From fef76a6c18105c0a307e82c9249bec00b8e0497e Mon Sep 17 00:00:00 2001 From: Graham Inggs Date: Tue, 25 Dec 2018 11:21:02 +0000 Subject: [PATCH 1/1] Import openorienteering-mapper_0.8.4.orig.tar.gz [dgit import orig openorienteering-mapper_0.8.4.orig.tar.gz] --- .github/ISSUE_TEMPLATE.md | 14 + 3rd-party/clipper/CMakeLists.txt | 125 + 3rd-party/clipper/License.txt | 24 + 3rd-party/clipper/License.txt.6.1.3a | 26 + 3rd-party/clipper/copyright | 127 + 3rd-party/clipper/download/README.txt | 1 + 3rd-party/qbezier/CMakeLists.txt | 38 + 3rd-party/qbezier/LICENSE.GPL | 674 + 3rd-party/qbezier/README.txt | 8 + 3rd-party/qbezier/src/mapper_qbezier.cpp | 5 + 3rd-party/qbezier/src/private/qbezier_p.h | 276 + 3rd-party/qbezier/src/private/qdatabuffer_p.h | 147 + 3rd-party/qbezier/src/private/qmath_p.h | 73 + 3rd-party/qbezier/src/private/qnumeric_p.h | 201 + 3rd-party/qtsingleapplication/CMakeLists.txt | 43 + 3rd-party/qtsingleapplication/copyright | 37 + .../qtsingleapplication/src/QtLockedFile | 1 + .../src/QtSingleApplication | 1 + .../qtsingleapplication/src/qtlocalpeer.cpp | 204 + .../qtsingleapplication/src/qtlocalpeer.h | 77 + .../qtsingleapplication/src/qtlockedfile.cpp | 193 + .../qtsingleapplication/src/qtlockedfile.h | 97 + .../src/qtlockedfile_unix.cpp | 115 + .../src/qtlockedfile_win.cpp | 211 + .../src/qtsingleapplication.cpp | 347 + .../src/qtsingleapplication.h | 105 + .../src/qtsingleapplication.pri | 17 + .../src/qtsinglecoreapplication.cpp | 149 + .../src/qtsinglecoreapplication.h | 71 + .../src/qtsinglecoreapplication.pri | 10 + CMakeLists.txt | 272 + COPYING | 674 + INSTALL.md | 159 + README.md | 46 + android/AndroidManifest.xml | 64 + android/CPackConfig.cmake.in | 112 + android/Mapper.pro.in | 62 + android/res/drawable-hdpi/icon.png | Bin 0 -> 11851 bytes android/res/drawable-ldpi/icon.png | Bin 0 -> 3896 bytes android/res/drawable-mdpi/icon.png | Bin 0 -> 5831 bytes android/res/drawable-xhdpi/icon.png | Bin 0 -> 17055 bytes android/res/values/strings.xml | 5 + .../mapper/MapperActivity.java | 252 + cmake/EnableSanitize.cmake | 44 + cmake/FindPROJ4.cmake | 152 + cmake/FindPolyclipping.cmake | 156 + code-check-wrapper.sh | 113 + doc/api/CMakeLists.txt | 96 + doc/api/Doxyfile.in | 53 + doc/api/api-docs-commit.sh | 38 + doc/api/api-docs-repository.sh | 36 + doc/api/api-docs.sh.in | 39 + doc/api/extra/code_overview.h | 111 + doc/api/extra/mainpage.h | 46 + doc/api/versionfilter.sh.in | 36 + doc/coding-style.xml | 39 + doc/licensing/CMakeLists.txt | 241 + doc/licensing/arch-licensing.cmake | 27 + doc/licensing/debian-licensing.cmake | 33 + doc/licensing/fedora-licensing.cmake | 36 + doc/licensing/licensing-html.qdocconf | 28 + doc/licensing/licensing.css | 50 + doc/licensing/licensing.html | 77 + doc/licensing/licensing.qdocconf | 35 + doc/licensing/licensing.qrc | 11 + doc/licensing/linux-distribution.cmake | 117 + doc/licensing/opensuse-leap-licensing.cmake | 1 + doc/licensing/opensuse-licensing.cmake | 43 + .../opensuse-tumbleweed-licensing.cmake | 1 + doc/licensing/src/apache-2.0.qdoc | 219 + .../src/gcc-runtime-library-exception.qdoc | 86 + doc/licensing/src/gdal-licensing.qdocinc | 268 + doc/licensing/src/gpl-3.0.qdoc | 702 + doc/licensing/src/lgpl-2.1.qdoc | 528 + doc/licensing/src/lgpl-3.0.qdoc | 189 + doc/licensing/src/licensing.qdoc | 265 + doc/licensing/src/qt-licensing.qdocinc | 1587 + doc/licensing/src/trademarks.qdocinc | 29 + doc/licensing/superbuild-licensing.cmake | 89 + doc/licensing/ubuntu-licensing.cmake | 1 + doc/man/Mapper.1 | 96 + doc/manual/CMakeLists.txt | 275 + doc/manual/Doxyfile-html.in | 53 + doc/manual/Doxyfile-pdflatex.in | 44 + doc/manual/Manual.qhcp.in | 19 + doc/manual/footer.html | 6 + doc/manual/header.html | 16 + doc/manual/install-html.cmake.in | 29 + doc/manual/pages/android-app.md | 170 + doc/manual/pages/android-pc.md | 25 + doc/manual/pages/android-requirements.md | 35 + doc/manual/pages/android-storage.md | 51 + doc/manual/pages/attachment/scribble_1024.png | Bin 0 -> 5733 bytes doc/manual/pages/attachment/scribble_2048.png | Bin 0 -> 21316 bytes doc/manual/pages/color_dock_widget.md | 53 + doc/manual/pages/colors_symbols.md | 15 + doc/manual/pages/course_design.md | 74 + doc/manual/pages/crt_files.md | 66 + doc/manual/pages/edit_menu.md | 92 + doc/manual/pages/faq.md | 91 + doc/manual/pages/file_menu.md | 84 + doc/manual/pages/find_objects.md | 111 + doc/manual/pages/georeferencing.md | 73 + doc/manual/pages/grid.md | 38 + .../pages/images/Android_UI_explanation.png | Bin 0 -> 256857 bytes doc/manual/pages/images/color_dock_widget.png | Bin 0 -> 97165 bytes .../pages/images/color_editor_desktop.png | Bin 0 -> 35336 bytes .../images/color_editor_professional.png | Bin 0 -> 51197 bytes doc/manual/pages/images/course_design.png | Bin 0 -> 138556 bytes doc/manual/pages/images/find_objects.png | Bin 0 -> 17724 bytes doc/manual/pages/images/georeferencing.png | Bin 0 -> 73351 bytes doc/manual/pages/images/grid_settings.png | Bin 0 -> 55073 bytes doc/manual/pages/images/main_window.png | Bin 0 -> 129388 bytes doc/manual/pages/images/new_map.png | Bin 0 -> 37671 bytes doc/manual/pages/images/query_editor.png | Bin 0 -> 36275 bytes .../pages/images/symbol_dock_widget.png | Bin 0 -> 25049 bytes .../pages/images/symbol_dock_widget_menu.png | Bin 0 -> 44031 bytes .../pages/images/symbol_replace_dialog.png | Bin 0 -> 71612 bytes doc/manual/pages/images/tag_editor.png | Bin 0 -> 7271 bytes doc/manual/pages/images/template_adjust.png | Bin 0 -> 31360 bytes .../images/template_image_positioning.png | Bin 0 -> 30339 bytes .../pages/images/template_setup_window.png | Bin 0 -> 26243 bytes doc/manual/pages/images/touch_cursor.png | Bin 0 -> 915 bytes doc/manual/pages/index.md | 71 + doc/manual/pages/main_window.md | 21 + doc/manual/pages/map_menu.md | 69 + doc/manual/pages/map_parts.md | 26 + doc/manual/pages/mapping-introduction.md | 97 + doc/manual/pages/new_map.md | 30 + doc/manual/pages/object_tags.md | 33 + doc/manual/pages/reference.md | 17 + doc/manual/pages/settings.md | 75 + doc/manual/pages/symbol_dock_widget.md | 113 + doc/manual/pages/symbol_replace_dialog.md | 28 + doc/manual/pages/symbols_menu.md | 44 + doc/manual/pages/template_adjust.md | 20 + doc/manual/pages/templates-index.md | 13 + doc/manual/pages/templates.md | 94 + doc/manual/pages/templates_menu.md | 28 + doc/manual/pages/toolbars.md | 360 + doc/manual/pages/tools_menu.md | 10 + doc/manual/pages/view_menu.md | 111 + doc/manual/postprocess-pdflatex.cmake.in | 32 + doc/manual/postprocess-qhp.cmake.in | 32 + doc/manual/preprocess-markdown-html.cmake.in | 200 + .../preprocess-markdown-pdflatex.cmake.in | 252 + doc/manual/style.css | 27 + doc/openorienteering.png | Bin 0 -> 13651 bytes doc/tip-of-the-day/tips_de.txt | 4 + doc/tip-of-the-day/tips_en.txt | 16 + doc/tip-of-the-day/tips_fr.txt | 16 + doc/tip-of-the-day/tips_ru.txt | 14 + doc/tip-of-the-day/tips_uk.txt | 16 + examples/CMakeLists.txt | 42 + examples/README.md | 5 + examples/autosave-example.qrc | 6 + examples/complete map.omap | 4910 + examples/examples.qrc | 6 + examples/forest sample.omap | 754 + examples/overprinting.omap | 242 + examples/src/complete map.xmap | 74858 ++++++++++++++++ examples/src/forest sample.xmap | 20504 +++++ examples/src/overprinting.xmap | 7125 ++ images/about.png | Bin 0 -> 1901 bytes images/arrow-down.png | Bin 0 -> 1187 bytes images/arrow-left.png | Bin 0 -> 1200 bytes images/arrow-right.png | Bin 0 -> 1219 bytes images/arrow-thin-downright.png | Bin 0 -> 450 bytes images/arrow-thin-upleft.png | Bin 0 -> 441 bytes images/arrow-up.png | Bin 0 -> 1193 bytes images/close.png | Bin 0 -> 1853 bytes images/colors.png | Bin 0 -> 267 bytes images/compass.png | Bin 0 -> 2033 bytes images/control.png | Bin 0 -> 318 bytes images/copy-coords.png | Bin 0 -> 1306 bytes images/copy.png | Bin 0 -> 723 bytes images/cursor-crosshair.png | Bin 0 -> 148 bytes images/cursor-cut.png | Bin 0 -> 1070 bytes images/cursor-delete.png | Bin 0 -> 814 bytes images/cursor-draw-circle.png | Bin 0 -> 677 bytes images/cursor-draw-path.png | Bin 0 -> 622 bytes images/cursor-draw-point.png | Bin 0 -> 261 bytes images/cursor-draw-rectangle.png | Bin 0 -> 398 bytes images/cursor-draw-text.png | Bin 0 -> 676 bytes images/cursor-fill.png | Bin 0 -> 832 bytes images/cursor-georeferencing-add.png | Bin 0 -> 211 bytes images/cursor-georeferencing-move.png | Bin 0 -> 268 bytes images/cursor-hollow.png | Bin 0 -> 210 bytes images/cursor-invisible.png | Bin 0 -> 81 bytes images/cursor-paint-on-template.png | Bin 0 -> 723 bytes images/cursor-rotate.png | Bin 0 -> 756 bytes images/cursor-scale.png | Bin 0 -> 661 bytes images/cut.png | Bin 0 -> 2087 bytes images/delete.png | Bin 0 -> 1198 bytes images/draw-circle.png | Bin 0 -> 1258 bytes images/draw-freehand.png | Bin 0 -> 1997 bytes images/draw-path.png | Bin 0 -> 1209 bytes images/draw-point-gps.png | Bin 0 -> 1530 bytes images/draw-point.png | Bin 0 -> 362 bytes images/draw-rectangle.png | Bin 0 -> 284 bytes images/draw-text.png | Bin 0 -> 1284 bytes images/georeferencing.png | Bin 0 -> 256 bytes images/gps-distance-rings.png | Bin 0 -> 970 bytes images/gps-temporary-clear.png | Bin 0 -> 834 bytes images/gps-temporary-path.png | Bin 0 -> 896 bytes images/gps-temporary-point.png | Bin 0 -> 1258 bytes images/grid.png | Bin 0 -> 224 bytes images/group.png | Bin 0 -> 1747 bytes images/help.png | Bin 0 -> 2231 bytes images/magnifying-glass.png | Bin 0 -> 2196 bytes images/map-parts.png | Bin 0 -> 1060 bytes images/mapper-help.png | Bin 0 -> 2952 bytes images/mapper-icon/Mapper-128.png | Bin 0 -> 24549 bytes images/mapper-icon/Mapper-16.png | Bin 0 -> 1917 bytes images/mapper-icon/Mapper-24.png | Bin 0 -> 2889 bytes images/mapper-icon/Mapper-256.png | Bin 0 -> 61581 bytes images/mapper-icon/Mapper-32.png | Bin 0 -> 4051 bytes images/mapper-icon/Mapper-48.png | Bin 0 -> 5831 bytes images/mapper-icon/Mapper-512.png | Bin 0 -> 143738 bytes images/mapper-icon/Mapper-96.png | Bin 0 -> 17055 bytes images/mapper-icon/Mapper-large.psd | Bin 0 -> 2932779 bytes images/mapper-icon/Mapper-small.psd | Bin 0 -> 1388453 bytes images/mapper-icon/Mapper.icns | Bin 0 -> 278110 bytes images/mapper-icon/Mapper.ico | Bin 0 -> 323550 bytes images/mapper-icon/README.txt | 4 + images/mapper-icon/mapper-help.xcf | Bin 0 -> 6244 bytes images/mapper.png | Bin 0 -> 3188 bytes images/minus.png | Bin 0 -> 377 bytes images/move-to-gps.png | Bin 0 -> 2249 bytes images/move.png | Bin 0 -> 1840 bytes images/new.png | Bin 0 -> 1008 bytes images/open-orienteering.png | Bin 0 -> 45070 bytes images/open.png | Bin 0 -> 1176 bytes images/paint-on-template-settings.png | Bin 0 -> 1266 bytes images/paste.png | Bin 0 -> 1027 bytes images/pencil.png | Bin 0 -> 1106 bytes images/plus.png | Bin 0 -> 646 bytes images/point-handles-2x.png | Bin 0 -> 1640 bytes images/point-handles-4x.png | Bin 0 -> 3206 bytes images/point-handles.png | Bin 0 -> 878 bytes images/print-mode-raster.png | Bin 0 -> 1686 bytes images/print-mode-separations.png | Bin 0 -> 1073 bytes images/print-mode-vector.png | Bin 0 -> 2932 bytes images/print.png | Bin 0 -> 917 bytes images/redo.png | Bin 0 -> 1502 bytes images/rotate-map.png | Bin 0 -> 1886 bytes images/save.png | Bin 0 -> 890 bytes images/settings.png | Bin 0 -> 2544 bytes images/svg/delete.svg | 219 + images/svg/map-parts.svg | 143 + images/svg/move-to-gps.svg | 451 + images/symbol_point_explanation.png | Bin 0 -> 3441 bytes images/symbols.png | Bin 0 -> 252 bytes images/tag-selector.png | Bin 0 -> 661 bytes images/templates.png | Bin 0 -> 2630 bytes images/text-align-baseline.png | Bin 0 -> 1259 bytes images/text-align-bottom.png | Bin 0 -> 1263 bytes images/text-align-hcenter.png | Bin 0 -> 1336 bytes images/text-align-left.png | Bin 0 -> 1284 bytes images/text-align-right.png | Bin 0 -> 1268 bytes images/text-align-top.png | Bin 0 -> 1272 bytes images/text-align-vcenter.png | Bin 0 -> 1224 bytes images/three-dots.png | Bin 0 -> 347 bytes images/title.png | Bin 0 -> 46905 bytes images/tool-boolean-difference.png | Bin 0 -> 618 bytes images/tool-boolean-intersection.png | Bin 0 -> 549 bytes images/tool-boolean-merge-holes.png | Bin 0 -> 417 bytes images/tool-boolean-union.png | Bin 0 -> 911 bytes images/tool-boolean-xor.png | Bin 0 -> 791 bytes images/tool-connect-paths.png | Bin 0 -> 1191 bytes images/tool-convert-to-curves.png | Bin 0 -> 1557 bytes images/tool-cut-hole.png | Bin 0 -> 2373 bytes images/tool-cut.png | Bin 0 -> 2124 bytes images/tool-cutout-physical-inner.png | Bin 0 -> 1930 bytes images/tool-cutout-physical.png | Bin 0 -> 1928 bytes images/tool-distribute-points.png | Bin 0 -> 331 bytes images/tool-duplicate.png | Bin 0 -> 916 bytes images/tool-edit-line.png | Bin 0 -> 964 bytes images/tool-edit.png | Bin 0 -> 876 bytes images/tool-fill-border.png | Bin 0 -> 1532 bytes images/tool-fill.png | Bin 0 -> 2435 bytes images/tool-gps-display.png | Bin 0 -> 970 bytes images/tool-measure.png | Bin 0 -> 1574 bytes images/tool-rotate-pattern.png | Bin 0 -> 1679 bytes images/tool-rotate.png | Bin 0 -> 1616 bytes images/tool-scale.png | Bin 0 -> 740 bytes images/tool-simplify-path.png | Bin 0 -> 1552 bytes images/tool-switch-dashes.png | Bin 0 -> 348 bytes images/tool-switch-symbol.png | Bin 0 -> 227 bytes images/tool-touch-cursor.png | Bin 0 -> 1167 bytes images/undo.png | Bin 0 -> 1601 bytes images/view-show-all.png | Bin 0 -> 1256 bytes images/view-zoom-in.png | Bin 0 -> 2168 bytes images/view-zoom-out.png | Bin 0 -> 2164 bytes images/window-new.png | Bin 0 -> 671 bytes iwyu-mapper.imp | 29 + iwyu-qt.imp | 1174 + make_iwyu_qt_imp.sh | 75 + packaging/CMakeLists.txt | 470 + packaging/custom_install.cmake.in | 218 + packaging/linux/Mapper.desktop | 29 + packaging/linux/openorienteering-mapper.xml | 53 + packaging/src/CMakeLists.txt | 36 + packaging/src/Mapper_Source.cmake.in | 64 + packaging/translations.cpp | 14 + packaging/windows/custom.nsi.in | 79 + resources.qrc | 132 + src/CMakeLists.txt | 346 + src/core/autosave.cpp | 143 + src/core/autosave.h | 92 + src/core/autosave_p.h | 69 + src/core/crs_template.cpp | 140 + src/core/crs_template.h | 325 + src/core/crs_template_implementation.cpp | 294 + src/core/crs_template_implementation.h | 135 + src/core/georeferencing.cpp | 918 + src/core/georeferencing.h | 695 + src/core/image_transparency_fixup.h | 95 + src/core/latlon.cpp | 36 + src/core/latlon.h | 138 + src/core/map.cpp | 2673 + src/core/map.h | 1895 + src/core/map_color.cpp | 364 + src/core/map_color.h | 846 + src/core/map_coord.cpp | 463 + src/core/map_coord.h | 1192 + src/core/map_grid.cpp | 237 + src/core/map_grid.h | 178 + src/core/map_part.cpp | 376 + src/core/map_part.h | 285 + src/core/map_printer.cpp | 1373 + src/core/map_printer.h | 589 + src/core/map_view.cpp | 579 + src/core/map_view.h | 548 + src/core/objects/boolean_tool.cpp | 1085 + src/core/objects/boolean_tool.h | 273 + src/core/objects/object.cpp | 3305 + src/core/objects/object.h | 1302 + src/core/objects/object_mover.cpp | 304 + src/core/objects/object_mover.h | 116 + src/core/objects/object_operations.h | 131 + src/core/objects/object_query.cpp | 780 + src/core/objects/object_query.h | 285 + src/core/objects/symbol_rule_set.cpp | 418 + src/core/objects/symbol_rule_set.h | 202 + src/core/objects/text_object.cpp | 569 + src/core/objects/text_object.h | 414 + src/core/path_coord.cpp | 529 + src/core/path_coord.h | 294 + src/core/renderables/renderable.cpp | 738 + src/core/renderables/renderable.h | 486 + .../renderables/renderable_implementation.cpp | 796 + .../renderables/renderable_implementation.h | 150 + src/core/storage_location.cpp | 306 + src/core/storage_location.h | 140 + src/core/symbols/area_symbol.cpp | 887 + src/core/symbols/area_symbol.h | 326 + src/core/symbols/combined_symbol.cpp | 393 + src/core/symbols/combined_symbol.h | 124 + src/core/symbols/line_symbol.cpp | 2076 + src/core/symbols/line_symbol.h | 382 + src/core/symbols/point_symbol.cpp | 725 + src/core/symbols/point_symbol.h | 160 + src/core/symbols/symbol.cpp | 842 + src/core/symbols/symbol.h | 478 + src/core/symbols/symbol_icon_decorator.cpp | 108 + src/core/symbols/symbol_icon_decorator.h | 94 + src/core/symbols/text_symbol.cpp | 583 + src/core/symbols/text_symbol.h | 193 + src/core/virtual_coord_vector.cpp | 40 + src/core/virtual_coord_vector.h | 244 + src/core/virtual_path.cpp | 845 + src/core/virtual_path.h | 425 + src/fileformats/file_format.cpp | 88 + src/fileformats/file_format.h | 339 + src/fileformats/file_format_registry.cpp | 102 + src/fileformats/file_format_registry.h | 103 + src/fileformats/file_import_export.cpp | 178 + src/fileformats/file_import_export.h | 266 + src/fileformats/native_file_format.cpp | 432 + src/fileformats/native_file_format.h | 84 + src/fileformats/ocad8_file_format.cpp | 2880 + src/fileformats/ocad8_file_format.h | 52 + src/fileformats/ocad8_file_format_p.h | 241 + src/fileformats/ocd_file_export.cpp | 51 + src/fileformats/ocd_file_export.h | 62 + src/fileformats/ocd_file_format.cpp | 65 + src/fileformats/ocd_file_format.h | 66 + src/fileformats/ocd_file_import.cpp | 2174 + src/fileformats/ocd_file_import.h | 465 + src/fileformats/ocd_types.cpp | 46 + src/fileformats/ocd_types.h | 872 + src/fileformats/ocd_types_v10.h | 33 + src/fileformats/ocd_types_v11.h | 159 + src/fileformats/ocd_types_v12.h | 99 + src/fileformats/ocd_types_v8.h | 454 + src/fileformats/ocd_types_v9.h | 215 + src/fileformats/xml_file_format.cpp | 1028 + src/fileformats/xml_file_format.h | 79 + src/fileformats/xml_file_format_p.h | 94 + src/gdal/CMakeLists.txt | 63 + src/gdal/gdal_manager.cpp | 394 + src/gdal/gdal_manager.h | 136 + src/gdal/gdal_settings_page.cpp | 253 + src/gdal/gdal_settings_page.h | 68 + src/gdal/mapper-osmconf.ini | 123 + src/gdal/ogr_file_format.cpp | 1455 + src/gdal/ogr_file_format.h | 69 + src/gdal/ogr_file_format_p.h | 278 + src/gdal/ogr_template.cpp | 544 + src/gdal/ogr_template.h | 117 + src/global.cpp | 53 + src/global.h | 34 + src/gui/about_dialog.cpp | 198 + src/gui/about_dialog.h | 68 + src/gui/autosave_dialog.cpp | 198 + src/gui/autosave_dialog.h | 141 + src/gui/color_dialog.cpp | 797 + src/gui/color_dialog.h | 160 + src/gui/configure_grid_dialog.cpp | 280 + src/gui/configure_grid_dialog.h | 102 + src/gui/file_dialog.cpp | 127 + src/gui/file_dialog.h | 95 + src/gui/georeferencing_dialog.cpp | 772 + src/gui/georeferencing_dialog.h | 333 + src/gui/home_screen_controller.cpp | 163 + src/gui/home_screen_controller.h | 88 + src/gui/main_window.cpp | 1256 + src/gui/main_window.h | 575 + src/gui/main_window_controller.cpp | 88 + src/gui/main_window_controller.h | 139 + src/gui/map/map_dialog_rotate.cpp | 169 + src/gui/map/map_dialog_rotate.h | 78 + src/gui/map/map_dialog_scale.cpp | 147 + src/gui/map/map_dialog_scale.h | 73 + src/gui/map/map_editor.cpp | 4102 + src/gui/map/map_editor.h | 796 + src/gui/map/map_editor_activity.cpp | 39 + src/gui/map/map_editor_activity.h | 97 + src/gui/map/map_editor_p.h | 87 + src/gui/map/map_find_feature.cpp | 262 + src/gui/map/map_find_feature.h | 90 + src/gui/map/map_widget.cpp | 1361 + src/gui/map/map_widget.h | 598 + src/gui/map/new_map_dialog.cpp | 317 + src/gui/map/new_map_dialog.h | 138 + src/gui/modifier_key.cpp | 105 + src/gui/modifier_key.h | 118 + src/gui/print_progress_dialog.cpp | 79 + src/gui/print_progress_dialog.h | 94 + src/gui/print_tool.cpp | 382 + src/gui/print_tool.h | 131 + src/gui/print_widget.cpp | 1412 + src/gui/print_widget.h | 345 + src/gui/select_crs_dialog.cpp | 145 + src/gui/select_crs_dialog.h | 99 + src/gui/settings_dialog.cpp | 231 + src/gui/settings_dialog.h | 107 + src/gui/symbols/area_symbol_settings.cpp | 577 + src/gui/symbols/area_symbol_settings.h | 124 + src/gui/symbols/combined_symbol_settings.cpp | 281 + src/gui/symbols/combined_symbol_settings.h | 73 + src/gui/symbols/line_symbol_settings.cpp | 791 + src/gui/symbols/line_symbol_settings.h | 195 + .../symbols/point_symbol_editor_widget.cpp | 1057 + src/gui/symbols/point_symbol_editor_widget.h | 229 + src/gui/symbols/point_symbol_settings.cpp | 97 + src/gui/symbols/point_symbol_settings.h | 63 + src/gui/symbols/replace_symbol_set_dialog.cpp | 709 + src/gui/symbols/replace_symbol_set_dialog.h | 124 + src/gui/symbols/symbol_properties_widget.cpp | 354 + src/gui/symbols/symbol_properties_widget.h | 113 + src/gui/symbols/symbol_setting_dialog.cpp | 506 + src/gui/symbols/symbol_setting_dialog.h | 186 + src/gui/symbols/text_symbol_settings.cpp | 605 + src/gui/symbols/text_symbol_settings.h | 135 + src/gui/task_dialog.cpp | 85 + src/gui/task_dialog.h | 115 + src/gui/text_browser_dialog.cpp | 147 + src/gui/text_browser_dialog.h | 90 + src/gui/touch_cursor.cpp | 221 + src/gui/touch_cursor.h | 112 + src/gui/util_gui.cpp | 351 + src/gui/util_gui.h | 323 + src/gui/widgets/action_grid_bar.cpp | 274 + src/gui/widgets/action_grid_bar.h | 128 + src/gui/widgets/color_dropdown.cpp | 174 + src/gui/widgets/color_dropdown.h | 79 + src/gui/widgets/color_list_widget.cpp | 505 + src/gui/widgets/color_list_widget.h | 101 + src/gui/widgets/compass_display.cpp | 121 + src/gui/widgets/compass_display.h | 82 + src/gui/widgets/crs_param_widgets.cpp | 107 + src/gui/widgets/crs_param_widgets.h | 57 + src/gui/widgets/crs_selector.cpp | 390 + src/gui/widgets/crs_selector.h | 194 + src/gui/widgets/editor_settings_page.cpp | 163 + src/gui/widgets/editor_settings_page.h | 76 + src/gui/widgets/general_settings_page.cpp | 427 + src/gui/widgets/general_settings_page.h | 98 + src/gui/widgets/home_screen_widget.cpp | 592 + src/gui/widgets/home_screen_widget.h | 220 + src/gui/widgets/key_button_bar.cpp | 307 + src/gui/widgets/key_button_bar.h | 151 + src/gui/widgets/mapper_proxystyle.cpp | 188 + src/gui/widgets/mapper_proxystyle.h | 99 + src/gui/widgets/measure_widget.cpp | 165 + src/gui/widgets/measure_widget.h | 63 + src/gui/widgets/pie_menu.cpp | 395 + src/gui/widgets/pie_menu.h | 172 + src/gui/widgets/segmented_button_layout.cpp | 79 + src/gui/widgets/segmented_button_layout.h | 84 + src/gui/widgets/settings_page.cpp | 35 + src/gui/widgets/settings_page.h | 98 + src/gui/widgets/symbol_dropdown.cpp | 186 + src/gui/widgets/symbol_dropdown.h | 114 + src/gui/widgets/symbol_render_widget.cpp | 1120 + src/gui/widgets/symbol_render_widget.h | 382 + src/gui/widgets/symbol_tooltip.cpp | 243 + src/gui/widgets/symbol_tooltip.h | 148 + src/gui/widgets/symbol_widget.cpp | 92 + src/gui/widgets/symbol_widget.h | 136 + src/gui/widgets/tag_select_widget.cpp | 379 + src/gui/widgets/tag_select_widget.h | 83 + src/gui/widgets/tags_widget.cpp | 269 + src/gui/widgets/tags_widget.h | 103 + src/gui/widgets/template_list_widget.cpp | 1340 + src/gui/widgets/template_list_widget.h | 161 + src/gui/widgets/text_alignment_widget.cpp | 186 + src/gui/widgets/text_alignment_widget.h | 77 + src/gui/widgets/text_browser.cpp | 57 + src/gui/widgets/text_browser.h | 54 + src/libocad/CMakeLists.txt | 34 + src/libocad/array.c | 133 + src/libocad/array.h | 113 + src/libocad/color.c | 78 + src/libocad/file.c | 460 + src/libocad/geometry.c | 145 + src/libocad/geometry.h | 111 + src/libocad/libocad.h | 1111 + src/libocad/ocad_object.c | 227 + src/libocad/ocad_symbol.c | 153 + src/libocad/path.c | 312 + src/libocad/setup.c | 44 + src/libocad/string.c | 167 + src/libocad/types.c | 90 + src/libocad/types.h | 50 + src/main.cpp | 201 + src/mapper_config.h.in | 46 + src/mapper_resource.cpp | 86 + src/mapper_resource.h | 47 + src/mingw/resources.rc.in | 50 + src/printsupport/CMakeLists.txt | 79 + src/printsupport/advanced_pdf_printer.cpp | 50 + src/printsupport/advanced_pdf_printer.h | 50 + src/printsupport/fork.sh | 39 + src/printsupport/patch.sh | 18 + src/printsupport/patches/devicecmyk.diff | 66 + src/printsupport/patches/enginetype.diff | 49 + src/printsupport/patches/headers.sed | 73 + src/printsupport/patches/papersize.diff | 25 + src/printsupport/patches/producer.diff | 19 + src/printsupport/printer_properties.cpp | 47 + src/printsupport/printer_properties.h | 73 + src/printsupport/printer_properties_win.cpp | 219 + src/printsupport/qt-5.2.1/advanced_pdf.cpp | 2642 + src/printsupport/qt-5.2.1/advanced_pdf_p.h | 337 + .../qt-5.2.1/printengine_advanced_pdf.cpp | 382 + .../qt-5.2.1/printengine_advanced_pdf_p.h | 152 + src/printsupport/qt-5.2.1/qfontsubset.cpp | 1264 + src/printsupport/qt-5.2.1/qfontsubset_agl.cpp | 280 + src/printsupport/qt-5.2.1/qfontsubset_p.h | 90 + src/printsupport/qt-5.5.1/advanced_pdf.cpp | 2693 + src/printsupport/qt-5.5.1/advanced_pdf_p.h | 323 + .../qt-5.5.1/printengine_advanced_pdf.cpp | 402 + .../qt-5.5.1/printengine_advanced_pdf_p.h | 138 + src/printsupport/qt-5.5.1/qfontsubset.cpp | 1259 + src/printsupport/qt-5.5.1/qfontsubset_agl.cpp | 280 + src/printsupport/qt-5.5.1/qfontsubset_p.h | 90 + src/qmake/mapper_config.h | 34 + src/sensors/compass.cpp | 610 + src/sensors/compass.h | 84 + src/sensors/gps_display.cpp | 477 + src/sensors/gps_display.h | 187 + src/sensors/gps_temporary_markers.cpp | 131 + src/sensors/gps_temporary_markers.h | 75 + src/sensors/gps_track.cpp | 650 + src/sensors/gps_track.h | 182 + src/sensors/gps_track_recorder.cpp | 106 + src/sensors/gps_track_recorder.h | 60 + src/settings.cpp | 329 + src/settings.h | 146 + src/templates/template.cpp | 990 + src/templates/template.h | 707 + src/templates/template_adjust.cpp | 877 + src/templates/template_adjust.h | 239 + src/templates/template_dialog_reopen.cpp | 171 + src/templates/template_dialog_reopen.h | 82 + src/templates/template_image.cpp | 728 + src/templates/template_image.h | 184 + src/templates/template_map.cpp | 232 + src/templates/template_map.h | 101 + .../template_position_dock_widget.cpp | 186 + src/templates/template_position_dock_widget.h | 70 + src/templates/template_positioning_dialog.cpp | 91 + src/templates/template_positioning_dialog.h | 62 + src/templates/template_tool_move.cpp | 131 + src/templates/template_tool_move.h | 73 + src/templates/template_tool_paint.cpp | 594 + src/templates/template_tool_paint.h | 172 + src/templates/template_track.cpp | 590 + src/templates/template_track.h | 131 + src/templates/world_file.cpp | 155 + src/templates/world_file.h | 65 + src/tools/cut_hole_tool.cpp | 265 + src/tools/cut_hole_tool.h | 99 + src/tools/cut_tool.cpp | 758 + src/tools/cut_tool.h | 154 + src/tools/cutout_operation.cpp | 157 + src/tools/cutout_operation.h | 95 + src/tools/cutout_tool.cpp | 195 + src/tools/cutout_tool.h | 116 + src/tools/distribute_points_tool.cpp | 181 + src/tools/distribute_points_tool.h | 136 + src/tools/draw_circle_tool.cpp | 323 + src/tools/draw_circle_tool.h | 86 + src/tools/draw_freehand_tool.cpp | 295 + src/tools/draw_freehand_tool.h | 88 + src/tools/draw_line_and_area_tool.cpp | 400 + src/tools/draw_line_and_area_tool.h | 145 + src/tools/draw_path_tool.cpp | 1248 + src/tools/draw_path_tool.h | 182 + src/tools/draw_point_gps_tool.cpp | 223 + src/tools/draw_point_gps_tool.h | 86 + src/tools/draw_point_tool.cpp | 364 + src/tools/draw_point_tool.h | 106 + src/tools/draw_rectangle_tool.cpp | 777 + src/tools/draw_rectangle_tool.h | 165 + src/tools/draw_text_tool.cpp | 490 + src/tools/draw_text_tool.h | 108 + src/tools/edit_line_tool.cpp | 627 + src/tools/edit_line_tool.h | 150 + src/tools/edit_point_tool.cpp | 957 + src/tools/edit_point_tool.h | 218 + src/tools/edit_tool.cpp | 278 + src/tools/edit_tool.h | 159 + src/tools/fill_tool.cpp | 543 + src/tools/fill_tool.h | 113 + src/tools/object_selector.cpp | 182 + src/tools/object_selector.h | 80 + src/tools/pan_tool.cpp | 89 + src/tools/pan_tool.h | 59 + src/tools/point_handles.cpp | 238 + src/tools/point_handles.h | 174 + src/tools/rotate_pattern_tool.cpp | 247 + src/tools/rotate_pattern_tool.h | 70 + src/tools/rotate_tool.cpp | 224 + src/tools/rotate_tool.h | 79 + src/tools/scale_tool.cpp | 165 + src/tools/scale_tool.h | 73 + src/tools/text_object_editor_helper.cpp | 918 + src/tools/text_object_editor_helper.h | 325 + src/tools/tool.cpp | 380 + src/tools/tool.h | 387 + src/tools/tool_base.cpp | 694 + src/tools/tool_base.h | 347 + src/tools/tool_helpers.cpp | 722 + src/tools/tool_helpers.h | 383 + src/undo/map_part_undo.cpp | 186 + src/undo/map_part_undo.h | 109 + src/undo/object_undo.cpp | 827 + src/undo/object_undo.h | 453 + src/undo/undo.cpp | 306 + src/undo/undo.h | 385 + src/undo/undo_manager.cpp | 520 + src/undo/undo_manager.h | 350 + src/util/backports.h | 52 + src/util/dxfparser.cpp | 605 + src/util/dxfparser.h | 161 + src/util/encoding.cpp | 83 + src/util/encoding.h | 57 + src/util/item_delegates.cpp | 266 + src/util/item_delegates.h | 251 + src/util/matrix.cpp | 224 + src/util/matrix.h | 202 + src/util/overriding_shortcut.cpp | 78 + src/util/overriding_shortcut.h | 84 + src/util/qasconst.h | 50 + src/util/qoverload.h | 86 + src/util/recording_translator.cpp | 53 + src/util/recording_translator.h | 59 + src/util/scoped_signals_blocker.cpp | 52 + src/util/scoped_signals_blocker.h | 117 + src/util/transformation.cpp | 393 + src/util/transformation.h | 86 + src/util/translation_util.cpp | 211 + src/util/translation_util.h | 175 + src/util/util.cpp | 208 + src/util/util.h | 312 + src/util/xml_stream_util.cpp | 323 + src/util/xml_stream_util.h | 775 + suppress.txt.in | 16 + symbol sets/10000/Course_Design_10000.omap | 497 + symbol sets/10000/ISMTBOM_10000.omap | 271 + symbol sets/10000/ISOM2000_10000.omap | 243 + symbol sets/10000/ISOM2017_10000.omap | 437 + symbol sets/10000/ISSkiOM_10000.omap | 288 + symbol sets/15000/Course_Design_15000.omap | 496 + symbol sets/15000/ISMTBOM_15000.omap | 271 + symbol sets/15000/ISOM2000_15000.omap | 243 + symbol sets/15000/ISOM2017_15000.omap | 437 + symbol sets/15000/ISSkiOM_15000.omap | 288 + symbol sets/20000/ISMTBOM_20000.omap | 271 + symbol sets/4000/Course_Design_4000.omap | 496 + symbol sets/4000/ISSOM_4000.omap | 446 + symbol sets/5000/Course_Design_5000.omap | 496 + symbol sets/5000/ISMTBOM_5000.omap | 271 + symbol sets/5000/ISSOM_5000.omap | 446 + symbol sets/5000/ISSkiOM_5000.omap | 288 + symbol sets/7500/ISMTBOM_7500.omap | 271 + symbol sets/CMakeLists.txt | 81 + symbol sets/ISOM2000-ISOM2017.crt | 150 + symbol sets/ISOM2000-ISSOM.crt | 126 + symbol sets/OSM-ISOM2000.crt | 172 + symbol sets/OSM-ISOM2017.crt | 172 + symbol sets/OSM-ISSOM.crt | 171 + symbol sets/README.md | 21 + symbol sets/src/Course_Design_10000.xmap | 10402 +++ symbol sets/src/ISMTBOM_15000.xmap | 7322 ++ symbol sets/src/ISOM2000_15000.xmap | 7192 ++ symbol sets/src/ISOM2017_15000.xmap | 8458 ++ symbol sets/src/ISSOM_5000.xmap | 7323 ++ symbol sets/src/ISSkiOM_15000.xmap | 7565 ++ test/AUTORUN_TESTS.cmake.in | 39 + test/CMakeLists.txt | 178 + test/TESTNAME-RUN.cmake.in | 36 + test/autosave_t.cpp | 259 + test/autosave_t.h | 101 + test/coord_xml_t.cpp | 1073 + test/coord_xml_t.h | 114 + .../issue-513-coords-outside-printable.omap | 115 + .../issue-513-coords-outside-printable.xmap | 117 + .../data/issue-513-coords-outside-qint32.omap | 1 + test/data/spotcolor_overprint.xmap | 2578 + test/data/templates/world-file.pgw | 6 + test/data/templates/world-file.png | Bin 0 -> 408 bytes test/data/templates/world-file.xmap | 92 + test/data/test_map.omap | Bin 0 -> 150014 bytes test/duplicate_equals_t.cpp | 137 + test/duplicate_equals_t.h | 42 + test/encoding_t.cpp | 71 + test/encoding_t.h | 49 + test/file_format_t.cpp | 532 + test/file_format_t.h | 66 + test/georeferencing_t.cpp | 225 + test/georeferencing_t.h | 77 + test/locale_t.cpp | 90 + test/locale_t.h | 48 + test/map_color_t.cpp | 322 + test/map_color_t.h | 51 + test/map_t.cpp | 414 + test/map_t.h | 63 + test/object_query_t.cpp | 392 + test/object_query_t.h | 55 + test/path_object_t.cpp | 484 + test/path_object_t.h | 58 + test/qpainter_t.cpp | 140 + test/qpainter_t.h | 92 + test/symbol_set_t.cpp | 850 + test/symbol_set_t.h | 94 + test/template_t.cpp | 171 + test/test_config.h.in | 27 + test/tools_t.cpp | 218 + test/tools_t.h | 39 + test/transform_t.cpp | 447 + test/transform_t.h | 87 + test/tst_qglobal.cpp | 163 + test/tst_qglobal.h | 44 + test/undo_manager_t.cpp | 247 + test/undo_manager_t.h | 80 + test/util_t.cpp | 124 + translations/CMakeLists.txt | 300 + translations/OpenOrienteering_cs.ts | 8349 ++ translations/OpenOrienteering_da.ts | 7742 ++ translations/OpenOrienteering_de.ts | 8104 ++ translations/OpenOrienteering_en.ts | 111 + translations/OpenOrienteering_eo.ts | 7855 ++ translations/OpenOrienteering_es.ts | 8276 ++ translations/OpenOrienteering_et.ts | 7724 ++ translations/OpenOrienteering_eu.ts | 7724 ++ translations/OpenOrienteering_fi.ts | 8190 ++ translations/OpenOrienteering_fr.ts | 8199 ++ translations/OpenOrienteering_he.ts | 7740 ++ translations/OpenOrienteering_hu.ts | 8218 ++ translations/OpenOrienteering_id.ts | 7879 ++ translations/OpenOrienteering_it.ts | 8021 ++ translations/OpenOrienteering_ja.ts | 8833 ++ translations/OpenOrienteering_lv.ts | 8186 ++ translations/OpenOrienteering_nb.ts | 8286 ++ translations/OpenOrienteering_nl.ts | 8429 ++ translations/OpenOrienteering_pl.ts | 8282 ++ translations/OpenOrienteering_pt_BR.ts | 7990 ++ translations/OpenOrienteering_pt_PT.ts | 7729 ++ translations/OpenOrienteering_ru.ts | 7956 ++ translations/OpenOrienteering_sv.ts | 8872 ++ translations/OpenOrienteering_template.ts | 7714 ++ translations/OpenOrienteering_uk.ts | 7950 ++ translations/OpenOrienteering_zh_CN.ts | 7846 ++ translations/future_translations.cpp | 30 + translations/locversion.plist.in | 14 + translations/map_symbols_cs.ts | 12290 +++ translations/map_symbols_da.ts | 11873 +++ translations/map_symbols_de.ts | 12038 +++ translations/map_symbols_eo.ts | 11841 +++ translations/map_symbols_es.ts | 12006 +++ translations/map_symbols_fi.ts | 12130 +++ translations/map_symbols_fr.ts | 12497 +++ translations/map_symbols_hu.ts | 12243 +++ translations/map_symbols_it.ts | 11892 +++ translations/map_symbols_nl.ts | 11839 +++ translations/map_symbols_pl.ts | 11839 +++ translations/map_symbols_ru.ts | 11895 +++ translations/map_symbols_sv.ts | 11940 +++ translations/map_symbols_template.ts | 11841 +++ translations/map_symbols_uk.ts | 12027 +++ translations/qt_eo.ts | 1818 + translations/qt_et.ts | 1808 + translations/qt_id.ts | 1816 + translations/qt_lv.ts | 2196 + translations/qt_nb.ts | 1927 + translations/qt_nl.ts | 1818 + translations/qt_template.ts | 1808 + translations/translate_text_files.sh | 70 + 833 files changed, 723254 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 3rd-party/clipper/CMakeLists.txt create mode 100644 3rd-party/clipper/License.txt create mode 100644 3rd-party/clipper/License.txt.6.1.3a create mode 100644 3rd-party/clipper/copyright create mode 100644 3rd-party/clipper/download/README.txt create mode 100644 3rd-party/qbezier/CMakeLists.txt create mode 100644 3rd-party/qbezier/LICENSE.GPL create mode 100644 3rd-party/qbezier/README.txt create mode 100644 3rd-party/qbezier/src/mapper_qbezier.cpp create mode 100644 3rd-party/qbezier/src/private/qbezier_p.h create mode 100644 3rd-party/qbezier/src/private/qdatabuffer_p.h create mode 100644 3rd-party/qbezier/src/private/qmath_p.h create mode 100644 3rd-party/qbezier/src/private/qnumeric_p.h create mode 100644 3rd-party/qtsingleapplication/CMakeLists.txt create mode 100644 3rd-party/qtsingleapplication/copyright create mode 100644 3rd-party/qtsingleapplication/src/QtLockedFile create mode 100644 3rd-party/qtsingleapplication/src/QtSingleApplication create mode 100644 3rd-party/qtsingleapplication/src/qtlocalpeer.cpp create mode 100644 3rd-party/qtsingleapplication/src/qtlocalpeer.h create mode 100644 3rd-party/qtsingleapplication/src/qtlockedfile.cpp create mode 100644 3rd-party/qtsingleapplication/src/qtlockedfile.h create mode 100644 3rd-party/qtsingleapplication/src/qtlockedfile_unix.cpp create mode 100644 3rd-party/qtsingleapplication/src/qtlockedfile_win.cpp create mode 100644 3rd-party/qtsingleapplication/src/qtsingleapplication.cpp create mode 100644 3rd-party/qtsingleapplication/src/qtsingleapplication.h create mode 100644 3rd-party/qtsingleapplication/src/qtsingleapplication.pri create mode 100644 3rd-party/qtsingleapplication/src/qtsinglecoreapplication.cpp create mode 100644 3rd-party/qtsingleapplication/src/qtsinglecoreapplication.h create mode 100644 3rd-party/qtsingleapplication/src/qtsinglecoreapplication.pri create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 INSTALL.md create mode 100644 README.md create mode 100644 android/AndroidManifest.xml create mode 100644 android/CPackConfig.cmake.in create mode 100644 android/Mapper.pro.in create mode 100644 android/res/drawable-hdpi/icon.png create mode 100644 android/res/drawable-ldpi/icon.png create mode 100644 android/res/drawable-mdpi/icon.png create mode 100644 android/res/drawable-xhdpi/icon.png create mode 100644 android/res/values/strings.xml create mode 100644 android/src/org/openorienteering/mapper/MapperActivity.java create mode 100644 cmake/EnableSanitize.cmake create mode 100644 cmake/FindPROJ4.cmake create mode 100644 cmake/FindPolyclipping.cmake create mode 100755 code-check-wrapper.sh create mode 100644 doc/api/CMakeLists.txt create mode 100644 doc/api/Doxyfile.in create mode 100755 doc/api/api-docs-commit.sh create mode 100755 doc/api/api-docs-repository.sh create mode 100755 doc/api/api-docs.sh.in create mode 100644 doc/api/extra/code_overview.h create mode 100644 doc/api/extra/mainpage.h create mode 100755 doc/api/versionfilter.sh.in create mode 100644 doc/coding-style.xml create mode 100644 doc/licensing/CMakeLists.txt create mode 100644 doc/licensing/arch-licensing.cmake create mode 100644 doc/licensing/debian-licensing.cmake create mode 100644 doc/licensing/fedora-licensing.cmake create mode 100644 doc/licensing/licensing-html.qdocconf create mode 100644 doc/licensing/licensing.css create mode 100644 doc/licensing/licensing.html create mode 100644 doc/licensing/licensing.qdocconf create mode 100644 doc/licensing/licensing.qrc create mode 100644 doc/licensing/linux-distribution.cmake create mode 100644 doc/licensing/opensuse-leap-licensing.cmake create mode 100644 doc/licensing/opensuse-licensing.cmake create mode 100644 doc/licensing/opensuse-tumbleweed-licensing.cmake create mode 100644 doc/licensing/src/apache-2.0.qdoc create mode 100644 doc/licensing/src/gcc-runtime-library-exception.qdoc create mode 100644 doc/licensing/src/gdal-licensing.qdocinc create mode 100644 doc/licensing/src/gpl-3.0.qdoc create mode 100644 doc/licensing/src/lgpl-2.1.qdoc create mode 100644 doc/licensing/src/lgpl-3.0.qdoc create mode 100644 doc/licensing/src/licensing.qdoc create mode 100644 doc/licensing/src/qt-licensing.qdocinc create mode 100644 doc/licensing/src/trademarks.qdocinc create mode 100644 doc/licensing/superbuild-licensing.cmake create mode 100644 doc/licensing/ubuntu-licensing.cmake create mode 100644 doc/man/Mapper.1 create mode 100644 doc/manual/CMakeLists.txt create mode 100644 doc/manual/Doxyfile-html.in create mode 100644 doc/manual/Doxyfile-pdflatex.in create mode 100644 doc/manual/Manual.qhcp.in create mode 100644 doc/manual/footer.html create mode 100644 doc/manual/header.html create mode 100644 doc/manual/install-html.cmake.in create mode 100644 doc/manual/pages/android-app.md create mode 100644 doc/manual/pages/android-pc.md create mode 100644 doc/manual/pages/android-requirements.md create mode 100644 doc/manual/pages/android-storage.md create mode 100644 doc/manual/pages/attachment/scribble_1024.png create mode 100644 doc/manual/pages/attachment/scribble_2048.png create mode 100644 doc/manual/pages/color_dock_widget.md create mode 100644 doc/manual/pages/colors_symbols.md create mode 100644 doc/manual/pages/course_design.md create mode 100644 doc/manual/pages/crt_files.md create mode 100644 doc/manual/pages/edit_menu.md create mode 100644 doc/manual/pages/faq.md create mode 100644 doc/manual/pages/file_menu.md create mode 100644 doc/manual/pages/find_objects.md create mode 100644 doc/manual/pages/georeferencing.md create mode 100644 doc/manual/pages/grid.md create mode 100644 doc/manual/pages/images/Android_UI_explanation.png create mode 100644 doc/manual/pages/images/color_dock_widget.png create mode 100644 doc/manual/pages/images/color_editor_desktop.png create mode 100644 doc/manual/pages/images/color_editor_professional.png create mode 100644 doc/manual/pages/images/course_design.png create mode 100644 doc/manual/pages/images/find_objects.png create mode 100644 doc/manual/pages/images/georeferencing.png create mode 100644 doc/manual/pages/images/grid_settings.png create mode 100644 doc/manual/pages/images/main_window.png create mode 100644 doc/manual/pages/images/new_map.png create mode 100644 doc/manual/pages/images/query_editor.png create mode 100644 doc/manual/pages/images/symbol_dock_widget.png create mode 100644 doc/manual/pages/images/symbol_dock_widget_menu.png create mode 100644 doc/manual/pages/images/symbol_replace_dialog.png create mode 100644 doc/manual/pages/images/tag_editor.png create mode 100644 doc/manual/pages/images/template_adjust.png create mode 100644 doc/manual/pages/images/template_image_positioning.png create mode 100644 doc/manual/pages/images/template_setup_window.png create mode 100644 doc/manual/pages/images/touch_cursor.png create mode 100644 doc/manual/pages/index.md create mode 100644 doc/manual/pages/main_window.md create mode 100644 doc/manual/pages/map_menu.md create mode 100644 doc/manual/pages/map_parts.md create mode 100644 doc/manual/pages/mapping-introduction.md create mode 100644 doc/manual/pages/new_map.md create mode 100644 doc/manual/pages/object_tags.md create mode 100644 doc/manual/pages/reference.md create mode 100644 doc/manual/pages/settings.md create mode 100644 doc/manual/pages/symbol_dock_widget.md create mode 100644 doc/manual/pages/symbol_replace_dialog.md create mode 100644 doc/manual/pages/symbols_menu.md create mode 100644 doc/manual/pages/template_adjust.md create mode 100644 doc/manual/pages/templates-index.md create mode 100644 doc/manual/pages/templates.md create mode 100644 doc/manual/pages/templates_menu.md create mode 100644 doc/manual/pages/toolbars.md create mode 100644 doc/manual/pages/tools_menu.md create mode 100644 doc/manual/pages/view_menu.md create mode 100644 doc/manual/postprocess-pdflatex.cmake.in create mode 100644 doc/manual/postprocess-qhp.cmake.in create mode 100644 doc/manual/preprocess-markdown-html.cmake.in create mode 100644 doc/manual/preprocess-markdown-pdflatex.cmake.in create mode 100644 doc/manual/style.css create mode 100644 doc/openorienteering.png create mode 100644 doc/tip-of-the-day/tips_de.txt create mode 100644 doc/tip-of-the-day/tips_en.txt create mode 100644 doc/tip-of-the-day/tips_fr.txt create mode 100644 doc/tip-of-the-day/tips_ru.txt create mode 100644 doc/tip-of-the-day/tips_uk.txt create mode 100644 examples/CMakeLists.txt create mode 100644 examples/README.md create mode 100644 examples/autosave-example.qrc create mode 100644 examples/complete map.omap create mode 100644 examples/examples.qrc create mode 100644 examples/forest sample.omap create mode 100644 examples/overprinting.omap create mode 100644 examples/src/complete map.xmap create mode 100644 examples/src/forest sample.xmap create mode 100644 examples/src/overprinting.xmap create mode 100644 images/about.png create mode 100644 images/arrow-down.png create mode 100644 images/arrow-left.png create mode 100644 images/arrow-right.png create mode 100644 images/arrow-thin-downright.png create mode 100644 images/arrow-thin-upleft.png create mode 100644 images/arrow-up.png create mode 100644 images/close.png create mode 100644 images/colors.png create mode 100644 images/compass.png create mode 100644 images/control.png create mode 100644 images/copy-coords.png create mode 100644 images/copy.png create mode 100644 images/cursor-crosshair.png create mode 100644 images/cursor-cut.png create mode 100644 images/cursor-delete.png create mode 100644 images/cursor-draw-circle.png create mode 100644 images/cursor-draw-path.png create mode 100644 images/cursor-draw-point.png create mode 100644 images/cursor-draw-rectangle.png create mode 100644 images/cursor-draw-text.png create mode 100644 images/cursor-fill.png create mode 100644 images/cursor-georeferencing-add.png create mode 100644 images/cursor-georeferencing-move.png create mode 100644 images/cursor-hollow.png create mode 100644 images/cursor-invisible.png create mode 100644 images/cursor-paint-on-template.png create mode 100644 images/cursor-rotate.png create mode 100644 images/cursor-scale.png create mode 100644 images/cut.png create mode 100644 images/delete.png create mode 100644 images/draw-circle.png create mode 100644 images/draw-freehand.png create mode 100644 images/draw-path.png create mode 100644 images/draw-point-gps.png create mode 100644 images/draw-point.png create mode 100644 images/draw-rectangle.png create mode 100644 images/draw-text.png create mode 100644 images/georeferencing.png create mode 100644 images/gps-distance-rings.png create mode 100644 images/gps-temporary-clear.png create mode 100644 images/gps-temporary-path.png create mode 100644 images/gps-temporary-point.png create mode 100644 images/grid.png create mode 100644 images/group.png create mode 100644 images/help.png create mode 100644 images/magnifying-glass.png create mode 100644 images/map-parts.png create mode 100644 images/mapper-help.png create mode 100644 images/mapper-icon/Mapper-128.png create mode 100644 images/mapper-icon/Mapper-16.png create mode 100644 images/mapper-icon/Mapper-24.png create mode 100644 images/mapper-icon/Mapper-256.png create mode 100644 images/mapper-icon/Mapper-32.png create mode 100644 images/mapper-icon/Mapper-48.png create mode 100644 images/mapper-icon/Mapper-512.png create mode 100644 images/mapper-icon/Mapper-96.png create mode 100755 images/mapper-icon/Mapper-large.psd create mode 100755 images/mapper-icon/Mapper-small.psd create mode 100644 images/mapper-icon/Mapper.icns create mode 100644 images/mapper-icon/Mapper.ico create mode 100644 images/mapper-icon/README.txt create mode 100644 images/mapper-icon/mapper-help.xcf create mode 100644 images/mapper.png create mode 100644 images/minus.png create mode 100644 images/move-to-gps.png create mode 100644 images/move.png create mode 100644 images/new.png create mode 100644 images/open-orienteering.png create mode 100644 images/open.png create mode 100644 images/paint-on-template-settings.png create mode 100644 images/paste.png create mode 100644 images/pencil.png create mode 100644 images/plus.png create mode 100644 images/point-handles-2x.png create mode 100644 images/point-handles-4x.png create mode 100644 images/point-handles.png create mode 100644 images/print-mode-raster.png create mode 100644 images/print-mode-separations.png create mode 100644 images/print-mode-vector.png create mode 100644 images/print.png create mode 100644 images/redo.png create mode 100644 images/rotate-map.png create mode 100644 images/save.png create mode 100644 images/settings.png create mode 100644 images/svg/delete.svg create mode 100644 images/svg/map-parts.svg create mode 100644 images/svg/move-to-gps.svg create mode 100644 images/symbol_point_explanation.png create mode 100644 images/symbols.png create mode 100644 images/tag-selector.png create mode 100644 images/templates.png create mode 100644 images/text-align-baseline.png create mode 100644 images/text-align-bottom.png create mode 100644 images/text-align-hcenter.png create mode 100644 images/text-align-left.png create mode 100644 images/text-align-right.png create mode 100644 images/text-align-top.png create mode 100644 images/text-align-vcenter.png create mode 100644 images/three-dots.png create mode 100644 images/title.png create mode 100644 images/tool-boolean-difference.png create mode 100644 images/tool-boolean-intersection.png create mode 100644 images/tool-boolean-merge-holes.png create mode 100644 images/tool-boolean-union.png create mode 100644 images/tool-boolean-xor.png create mode 100644 images/tool-connect-paths.png create mode 100644 images/tool-convert-to-curves.png create mode 100644 images/tool-cut-hole.png create mode 100644 images/tool-cut.png create mode 100644 images/tool-cutout-physical-inner.png create mode 100644 images/tool-cutout-physical.png create mode 100644 images/tool-distribute-points.png create mode 100644 images/tool-duplicate.png create mode 100644 images/tool-edit-line.png create mode 100644 images/tool-edit.png create mode 100644 images/tool-fill-border.png create mode 100644 images/tool-fill.png create mode 100644 images/tool-gps-display.png create mode 100644 images/tool-measure.png create mode 100644 images/tool-rotate-pattern.png create mode 100644 images/tool-rotate.png create mode 100644 images/tool-scale.png create mode 100644 images/tool-simplify-path.png create mode 100644 images/tool-switch-dashes.png create mode 100644 images/tool-switch-symbol.png create mode 100644 images/tool-touch-cursor.png create mode 100644 images/undo.png create mode 100644 images/view-show-all.png create mode 100644 images/view-zoom-in.png create mode 100644 images/view-zoom-out.png create mode 100644 images/window-new.png create mode 100644 iwyu-mapper.imp create mode 100644 iwyu-qt.imp create mode 100755 make_iwyu_qt_imp.sh create mode 100644 packaging/CMakeLists.txt create mode 100644 packaging/custom_install.cmake.in create mode 100644 packaging/linux/Mapper.desktop create mode 100644 packaging/linux/openorienteering-mapper.xml create mode 100644 packaging/src/CMakeLists.txt create mode 100644 packaging/src/Mapper_Source.cmake.in create mode 100644 packaging/translations.cpp create mode 100644 packaging/windows/custom.nsi.in create mode 100644 resources.qrc create mode 100644 src/CMakeLists.txt create mode 100644 src/core/autosave.cpp create mode 100644 src/core/autosave.h create mode 100644 src/core/autosave_p.h create mode 100644 src/core/crs_template.cpp create mode 100644 src/core/crs_template.h create mode 100644 src/core/crs_template_implementation.cpp create mode 100644 src/core/crs_template_implementation.h create mode 100644 src/core/georeferencing.cpp create mode 100644 src/core/georeferencing.h create mode 100644 src/core/image_transparency_fixup.h create mode 100644 src/core/latlon.cpp create mode 100644 src/core/latlon.h create mode 100644 src/core/map.cpp create mode 100644 src/core/map.h create mode 100644 src/core/map_color.cpp create mode 100644 src/core/map_color.h create mode 100644 src/core/map_coord.cpp create mode 100644 src/core/map_coord.h create mode 100644 src/core/map_grid.cpp create mode 100644 src/core/map_grid.h create mode 100644 src/core/map_part.cpp create mode 100644 src/core/map_part.h create mode 100644 src/core/map_printer.cpp create mode 100644 src/core/map_printer.h create mode 100644 src/core/map_view.cpp create mode 100644 src/core/map_view.h create mode 100644 src/core/objects/boolean_tool.cpp create mode 100644 src/core/objects/boolean_tool.h create mode 100644 src/core/objects/object.cpp create mode 100644 src/core/objects/object.h create mode 100644 src/core/objects/object_mover.cpp create mode 100644 src/core/objects/object_mover.h create mode 100644 src/core/objects/object_operations.h create mode 100644 src/core/objects/object_query.cpp create mode 100644 src/core/objects/object_query.h create mode 100644 src/core/objects/symbol_rule_set.cpp create mode 100644 src/core/objects/symbol_rule_set.h create mode 100644 src/core/objects/text_object.cpp create mode 100644 src/core/objects/text_object.h create mode 100644 src/core/path_coord.cpp create mode 100644 src/core/path_coord.h create mode 100644 src/core/renderables/renderable.cpp create mode 100644 src/core/renderables/renderable.h create mode 100644 src/core/renderables/renderable_implementation.cpp create mode 100644 src/core/renderables/renderable_implementation.h create mode 100644 src/core/storage_location.cpp create mode 100644 src/core/storage_location.h create mode 100644 src/core/symbols/area_symbol.cpp create mode 100644 src/core/symbols/area_symbol.h create mode 100644 src/core/symbols/combined_symbol.cpp create mode 100644 src/core/symbols/combined_symbol.h create mode 100644 src/core/symbols/line_symbol.cpp create mode 100644 src/core/symbols/line_symbol.h create mode 100644 src/core/symbols/point_symbol.cpp create mode 100644 src/core/symbols/point_symbol.h create mode 100644 src/core/symbols/symbol.cpp create mode 100644 src/core/symbols/symbol.h create mode 100644 src/core/symbols/symbol_icon_decorator.cpp create mode 100644 src/core/symbols/symbol_icon_decorator.h create mode 100644 src/core/symbols/text_symbol.cpp create mode 100644 src/core/symbols/text_symbol.h create mode 100644 src/core/virtual_coord_vector.cpp create mode 100644 src/core/virtual_coord_vector.h create mode 100644 src/core/virtual_path.cpp create mode 100644 src/core/virtual_path.h create mode 100644 src/fileformats/file_format.cpp create mode 100644 src/fileformats/file_format.h create mode 100644 src/fileformats/file_format_registry.cpp create mode 100644 src/fileformats/file_format_registry.h create mode 100644 src/fileformats/file_import_export.cpp create mode 100644 src/fileformats/file_import_export.h create mode 100644 src/fileformats/native_file_format.cpp create mode 100644 src/fileformats/native_file_format.h create mode 100644 src/fileformats/ocad8_file_format.cpp create mode 100644 src/fileformats/ocad8_file_format.h create mode 100644 src/fileformats/ocad8_file_format_p.h create mode 100644 src/fileformats/ocd_file_export.cpp create mode 100644 src/fileformats/ocd_file_export.h create mode 100644 src/fileformats/ocd_file_format.cpp create mode 100644 src/fileformats/ocd_file_format.h create mode 100644 src/fileformats/ocd_file_import.cpp create mode 100644 src/fileformats/ocd_file_import.h create mode 100644 src/fileformats/ocd_types.cpp create mode 100644 src/fileformats/ocd_types.h create mode 100644 src/fileformats/ocd_types_v10.h create mode 100644 src/fileformats/ocd_types_v11.h create mode 100644 src/fileformats/ocd_types_v12.h create mode 100644 src/fileformats/ocd_types_v8.h create mode 100644 src/fileformats/ocd_types_v9.h create mode 100644 src/fileformats/xml_file_format.cpp create mode 100644 src/fileformats/xml_file_format.h create mode 100644 src/fileformats/xml_file_format_p.h create mode 100644 src/gdal/CMakeLists.txt create mode 100644 src/gdal/gdal_manager.cpp create mode 100644 src/gdal/gdal_manager.h create mode 100644 src/gdal/gdal_settings_page.cpp create mode 100644 src/gdal/gdal_settings_page.h create mode 100644 src/gdal/mapper-osmconf.ini create mode 100644 src/gdal/ogr_file_format.cpp create mode 100644 src/gdal/ogr_file_format.h create mode 100644 src/gdal/ogr_file_format_p.h create mode 100644 src/gdal/ogr_template.cpp create mode 100644 src/gdal/ogr_template.h create mode 100644 src/global.cpp create mode 100644 src/global.h create mode 100644 src/gui/about_dialog.cpp create mode 100644 src/gui/about_dialog.h create mode 100644 src/gui/autosave_dialog.cpp create mode 100644 src/gui/autosave_dialog.h create mode 100644 src/gui/color_dialog.cpp create mode 100644 src/gui/color_dialog.h create mode 100644 src/gui/configure_grid_dialog.cpp create mode 100644 src/gui/configure_grid_dialog.h create mode 100644 src/gui/file_dialog.cpp create mode 100644 src/gui/file_dialog.h create mode 100644 src/gui/georeferencing_dialog.cpp create mode 100644 src/gui/georeferencing_dialog.h create mode 100644 src/gui/home_screen_controller.cpp create mode 100644 src/gui/home_screen_controller.h create mode 100644 src/gui/main_window.cpp create mode 100644 src/gui/main_window.h create mode 100644 src/gui/main_window_controller.cpp create mode 100644 src/gui/main_window_controller.h create mode 100644 src/gui/map/map_dialog_rotate.cpp create mode 100644 src/gui/map/map_dialog_rotate.h create mode 100644 src/gui/map/map_dialog_scale.cpp create mode 100644 src/gui/map/map_dialog_scale.h create mode 100644 src/gui/map/map_editor.cpp create mode 100644 src/gui/map/map_editor.h create mode 100644 src/gui/map/map_editor_activity.cpp create mode 100644 src/gui/map/map_editor_activity.h create mode 100644 src/gui/map/map_editor_p.h create mode 100644 src/gui/map/map_find_feature.cpp create mode 100644 src/gui/map/map_find_feature.h create mode 100644 src/gui/map/map_widget.cpp create mode 100644 src/gui/map/map_widget.h create mode 100644 src/gui/map/new_map_dialog.cpp create mode 100644 src/gui/map/new_map_dialog.h create mode 100644 src/gui/modifier_key.cpp create mode 100644 src/gui/modifier_key.h create mode 100644 src/gui/print_progress_dialog.cpp create mode 100644 src/gui/print_progress_dialog.h create mode 100644 src/gui/print_tool.cpp create mode 100644 src/gui/print_tool.h create mode 100644 src/gui/print_widget.cpp create mode 100644 src/gui/print_widget.h create mode 100644 src/gui/select_crs_dialog.cpp create mode 100644 src/gui/select_crs_dialog.h create mode 100644 src/gui/settings_dialog.cpp create mode 100644 src/gui/settings_dialog.h create mode 100644 src/gui/symbols/area_symbol_settings.cpp create mode 100644 src/gui/symbols/area_symbol_settings.h create mode 100644 src/gui/symbols/combined_symbol_settings.cpp create mode 100644 src/gui/symbols/combined_symbol_settings.h create mode 100644 src/gui/symbols/line_symbol_settings.cpp create mode 100644 src/gui/symbols/line_symbol_settings.h create mode 100644 src/gui/symbols/point_symbol_editor_widget.cpp create mode 100644 src/gui/symbols/point_symbol_editor_widget.h create mode 100644 src/gui/symbols/point_symbol_settings.cpp create mode 100644 src/gui/symbols/point_symbol_settings.h create mode 100644 src/gui/symbols/replace_symbol_set_dialog.cpp create mode 100644 src/gui/symbols/replace_symbol_set_dialog.h create mode 100644 src/gui/symbols/symbol_properties_widget.cpp create mode 100644 src/gui/symbols/symbol_properties_widget.h create mode 100644 src/gui/symbols/symbol_setting_dialog.cpp create mode 100644 src/gui/symbols/symbol_setting_dialog.h create mode 100644 src/gui/symbols/text_symbol_settings.cpp create mode 100644 src/gui/symbols/text_symbol_settings.h create mode 100644 src/gui/task_dialog.cpp create mode 100644 src/gui/task_dialog.h create mode 100644 src/gui/text_browser_dialog.cpp create mode 100644 src/gui/text_browser_dialog.h create mode 100644 src/gui/touch_cursor.cpp create mode 100644 src/gui/touch_cursor.h create mode 100644 src/gui/util_gui.cpp create mode 100644 src/gui/util_gui.h create mode 100644 src/gui/widgets/action_grid_bar.cpp create mode 100644 src/gui/widgets/action_grid_bar.h create mode 100644 src/gui/widgets/color_dropdown.cpp create mode 100644 src/gui/widgets/color_dropdown.h create mode 100644 src/gui/widgets/color_list_widget.cpp create mode 100644 src/gui/widgets/color_list_widget.h create mode 100644 src/gui/widgets/compass_display.cpp create mode 100644 src/gui/widgets/compass_display.h create mode 100644 src/gui/widgets/crs_param_widgets.cpp create mode 100644 src/gui/widgets/crs_param_widgets.h create mode 100644 src/gui/widgets/crs_selector.cpp create mode 100644 src/gui/widgets/crs_selector.h create mode 100644 src/gui/widgets/editor_settings_page.cpp create mode 100644 src/gui/widgets/editor_settings_page.h create mode 100644 src/gui/widgets/general_settings_page.cpp create mode 100644 src/gui/widgets/general_settings_page.h create mode 100644 src/gui/widgets/home_screen_widget.cpp create mode 100644 src/gui/widgets/home_screen_widget.h create mode 100644 src/gui/widgets/key_button_bar.cpp create mode 100644 src/gui/widgets/key_button_bar.h create mode 100644 src/gui/widgets/mapper_proxystyle.cpp create mode 100644 src/gui/widgets/mapper_proxystyle.h create mode 100644 src/gui/widgets/measure_widget.cpp create mode 100644 src/gui/widgets/measure_widget.h create mode 100644 src/gui/widgets/pie_menu.cpp create mode 100644 src/gui/widgets/pie_menu.h create mode 100644 src/gui/widgets/segmented_button_layout.cpp create mode 100644 src/gui/widgets/segmented_button_layout.h create mode 100644 src/gui/widgets/settings_page.cpp create mode 100644 src/gui/widgets/settings_page.h create mode 100644 src/gui/widgets/symbol_dropdown.cpp create mode 100644 src/gui/widgets/symbol_dropdown.h create mode 100644 src/gui/widgets/symbol_render_widget.cpp create mode 100644 src/gui/widgets/symbol_render_widget.h create mode 100644 src/gui/widgets/symbol_tooltip.cpp create mode 100644 src/gui/widgets/symbol_tooltip.h create mode 100644 src/gui/widgets/symbol_widget.cpp create mode 100644 src/gui/widgets/symbol_widget.h create mode 100644 src/gui/widgets/tag_select_widget.cpp create mode 100644 src/gui/widgets/tag_select_widget.h create mode 100644 src/gui/widgets/tags_widget.cpp create mode 100644 src/gui/widgets/tags_widget.h create mode 100644 src/gui/widgets/template_list_widget.cpp create mode 100644 src/gui/widgets/template_list_widget.h create mode 100644 src/gui/widgets/text_alignment_widget.cpp create mode 100644 src/gui/widgets/text_alignment_widget.h create mode 100644 src/gui/widgets/text_browser.cpp create mode 100644 src/gui/widgets/text_browser.h create mode 100644 src/libocad/CMakeLists.txt create mode 100644 src/libocad/array.c create mode 100644 src/libocad/array.h create mode 100644 src/libocad/color.c create mode 100644 src/libocad/file.c create mode 100644 src/libocad/geometry.c create mode 100644 src/libocad/geometry.h create mode 100644 src/libocad/libocad.h create mode 100644 src/libocad/ocad_object.c create mode 100644 src/libocad/ocad_symbol.c create mode 100644 src/libocad/path.c create mode 100644 src/libocad/setup.c create mode 100644 src/libocad/string.c create mode 100644 src/libocad/types.c create mode 100644 src/libocad/types.h create mode 100644 src/main.cpp create mode 100644 src/mapper_config.h.in create mode 100644 src/mapper_resource.cpp create mode 100644 src/mapper_resource.h create mode 100644 src/mingw/resources.rc.in create mode 100644 src/printsupport/CMakeLists.txt create mode 100644 src/printsupport/advanced_pdf_printer.cpp create mode 100644 src/printsupport/advanced_pdf_printer.h create mode 100755 src/printsupport/fork.sh create mode 100755 src/printsupport/patch.sh create mode 100644 src/printsupport/patches/devicecmyk.diff create mode 100644 src/printsupport/patches/enginetype.diff create mode 100644 src/printsupport/patches/headers.sed create mode 100644 src/printsupport/patches/papersize.diff create mode 100644 src/printsupport/patches/producer.diff create mode 100644 src/printsupport/printer_properties.cpp create mode 100644 src/printsupport/printer_properties.h create mode 100644 src/printsupport/printer_properties_win.cpp create mode 100644 src/printsupport/qt-5.2.1/advanced_pdf.cpp create mode 100644 src/printsupport/qt-5.2.1/advanced_pdf_p.h create mode 100644 src/printsupport/qt-5.2.1/printengine_advanced_pdf.cpp create mode 100644 src/printsupport/qt-5.2.1/printengine_advanced_pdf_p.h create mode 100644 src/printsupport/qt-5.2.1/qfontsubset.cpp create mode 100644 src/printsupport/qt-5.2.1/qfontsubset_agl.cpp create mode 100644 src/printsupport/qt-5.2.1/qfontsubset_p.h create mode 100644 src/printsupport/qt-5.5.1/advanced_pdf.cpp create mode 100644 src/printsupport/qt-5.5.1/advanced_pdf_p.h create mode 100644 src/printsupport/qt-5.5.1/printengine_advanced_pdf.cpp create mode 100644 src/printsupport/qt-5.5.1/printengine_advanced_pdf_p.h create mode 100644 src/printsupport/qt-5.5.1/qfontsubset.cpp create mode 100644 src/printsupport/qt-5.5.1/qfontsubset_agl.cpp create mode 100644 src/printsupport/qt-5.5.1/qfontsubset_p.h create mode 100644 src/qmake/mapper_config.h create mode 100644 src/sensors/compass.cpp create mode 100644 src/sensors/compass.h create mode 100644 src/sensors/gps_display.cpp create mode 100644 src/sensors/gps_display.h create mode 100644 src/sensors/gps_temporary_markers.cpp create mode 100644 src/sensors/gps_temporary_markers.h create mode 100644 src/sensors/gps_track.cpp create mode 100644 src/sensors/gps_track.h create mode 100644 src/sensors/gps_track_recorder.cpp create mode 100644 src/sensors/gps_track_recorder.h create mode 100644 src/settings.cpp create mode 100644 src/settings.h create mode 100644 src/templates/template.cpp create mode 100644 src/templates/template.h create mode 100644 src/templates/template_adjust.cpp create mode 100644 src/templates/template_adjust.h create mode 100644 src/templates/template_dialog_reopen.cpp create mode 100644 src/templates/template_dialog_reopen.h create mode 100644 src/templates/template_image.cpp create mode 100644 src/templates/template_image.h create mode 100644 src/templates/template_map.cpp create mode 100644 src/templates/template_map.h create mode 100644 src/templates/template_position_dock_widget.cpp create mode 100644 src/templates/template_position_dock_widget.h create mode 100644 src/templates/template_positioning_dialog.cpp create mode 100644 src/templates/template_positioning_dialog.h create mode 100644 src/templates/template_tool_move.cpp create mode 100644 src/templates/template_tool_move.h create mode 100644 src/templates/template_tool_paint.cpp create mode 100644 src/templates/template_tool_paint.h create mode 100644 src/templates/template_track.cpp create mode 100644 src/templates/template_track.h create mode 100644 src/templates/world_file.cpp create mode 100644 src/templates/world_file.h create mode 100644 src/tools/cut_hole_tool.cpp create mode 100644 src/tools/cut_hole_tool.h create mode 100644 src/tools/cut_tool.cpp create mode 100644 src/tools/cut_tool.h create mode 100644 src/tools/cutout_operation.cpp create mode 100644 src/tools/cutout_operation.h create mode 100644 src/tools/cutout_tool.cpp create mode 100644 src/tools/cutout_tool.h create mode 100644 src/tools/distribute_points_tool.cpp create mode 100644 src/tools/distribute_points_tool.h create mode 100644 src/tools/draw_circle_tool.cpp create mode 100644 src/tools/draw_circle_tool.h create mode 100644 src/tools/draw_freehand_tool.cpp create mode 100644 src/tools/draw_freehand_tool.h create mode 100644 src/tools/draw_line_and_area_tool.cpp create mode 100644 src/tools/draw_line_and_area_tool.h create mode 100644 src/tools/draw_path_tool.cpp create mode 100644 src/tools/draw_path_tool.h create mode 100644 src/tools/draw_point_gps_tool.cpp create mode 100644 src/tools/draw_point_gps_tool.h create mode 100644 src/tools/draw_point_tool.cpp create mode 100644 src/tools/draw_point_tool.h create mode 100644 src/tools/draw_rectangle_tool.cpp create mode 100644 src/tools/draw_rectangle_tool.h create mode 100644 src/tools/draw_text_tool.cpp create mode 100644 src/tools/draw_text_tool.h create mode 100644 src/tools/edit_line_tool.cpp create mode 100644 src/tools/edit_line_tool.h create mode 100644 src/tools/edit_point_tool.cpp create mode 100644 src/tools/edit_point_tool.h create mode 100644 src/tools/edit_tool.cpp create mode 100644 src/tools/edit_tool.h create mode 100644 src/tools/fill_tool.cpp create mode 100644 src/tools/fill_tool.h create mode 100644 src/tools/object_selector.cpp create mode 100644 src/tools/object_selector.h create mode 100644 src/tools/pan_tool.cpp create mode 100644 src/tools/pan_tool.h create mode 100644 src/tools/point_handles.cpp create mode 100644 src/tools/point_handles.h create mode 100644 src/tools/rotate_pattern_tool.cpp create mode 100644 src/tools/rotate_pattern_tool.h create mode 100644 src/tools/rotate_tool.cpp create mode 100644 src/tools/rotate_tool.h create mode 100644 src/tools/scale_tool.cpp create mode 100644 src/tools/scale_tool.h create mode 100644 src/tools/text_object_editor_helper.cpp create mode 100644 src/tools/text_object_editor_helper.h create mode 100644 src/tools/tool.cpp create mode 100644 src/tools/tool.h create mode 100644 src/tools/tool_base.cpp create mode 100644 src/tools/tool_base.h create mode 100644 src/tools/tool_helpers.cpp create mode 100644 src/tools/tool_helpers.h create mode 100644 src/undo/map_part_undo.cpp create mode 100644 src/undo/map_part_undo.h create mode 100644 src/undo/object_undo.cpp create mode 100644 src/undo/object_undo.h create mode 100644 src/undo/undo.cpp create mode 100644 src/undo/undo.h create mode 100644 src/undo/undo_manager.cpp create mode 100644 src/undo/undo_manager.h create mode 100644 src/util/backports.h create mode 100644 src/util/dxfparser.cpp create mode 100644 src/util/dxfparser.h create mode 100644 src/util/encoding.cpp create mode 100644 src/util/encoding.h create mode 100644 src/util/item_delegates.cpp create mode 100644 src/util/item_delegates.h create mode 100644 src/util/matrix.cpp create mode 100644 src/util/matrix.h create mode 100644 src/util/overriding_shortcut.cpp create mode 100644 src/util/overriding_shortcut.h create mode 100644 src/util/qasconst.h create mode 100644 src/util/qoverload.h create mode 100644 src/util/recording_translator.cpp create mode 100644 src/util/recording_translator.h create mode 100644 src/util/scoped_signals_blocker.cpp create mode 100644 src/util/scoped_signals_blocker.h create mode 100644 src/util/transformation.cpp create mode 100644 src/util/transformation.h create mode 100644 src/util/translation_util.cpp create mode 100644 src/util/translation_util.h create mode 100644 src/util/util.cpp create mode 100644 src/util/util.h create mode 100644 src/util/xml_stream_util.cpp create mode 100644 src/util/xml_stream_util.h create mode 100644 suppress.txt.in create mode 100644 symbol sets/10000/Course_Design_10000.omap create mode 100644 symbol sets/10000/ISMTBOM_10000.omap create mode 100644 symbol sets/10000/ISOM2000_10000.omap create mode 100644 symbol sets/10000/ISOM2017_10000.omap create mode 100644 symbol sets/10000/ISSkiOM_10000.omap create mode 100644 symbol sets/15000/Course_Design_15000.omap create mode 100644 symbol sets/15000/ISMTBOM_15000.omap create mode 100644 symbol sets/15000/ISOM2000_15000.omap create mode 100644 symbol sets/15000/ISOM2017_15000.omap create mode 100644 symbol sets/15000/ISSkiOM_15000.omap create mode 100644 symbol sets/20000/ISMTBOM_20000.omap create mode 100644 symbol sets/4000/Course_Design_4000.omap create mode 100644 symbol sets/4000/ISSOM_4000.omap create mode 100644 symbol sets/5000/Course_Design_5000.omap create mode 100644 symbol sets/5000/ISMTBOM_5000.omap create mode 100644 symbol sets/5000/ISSOM_5000.omap create mode 100644 symbol sets/5000/ISSkiOM_5000.omap create mode 100644 symbol sets/7500/ISMTBOM_7500.omap create mode 100644 symbol sets/CMakeLists.txt create mode 100644 symbol sets/ISOM2000-ISOM2017.crt create mode 100644 symbol sets/ISOM2000-ISSOM.crt create mode 100644 symbol sets/OSM-ISOM2000.crt create mode 100644 symbol sets/OSM-ISOM2017.crt create mode 100644 symbol sets/OSM-ISSOM.crt create mode 100644 symbol sets/README.md create mode 100644 symbol sets/src/Course_Design_10000.xmap create mode 100644 symbol sets/src/ISMTBOM_15000.xmap create mode 100644 symbol sets/src/ISOM2000_15000.xmap create mode 100644 symbol sets/src/ISOM2017_15000.xmap create mode 100644 symbol sets/src/ISSOM_5000.xmap create mode 100644 symbol sets/src/ISSkiOM_15000.xmap create mode 100644 test/AUTORUN_TESTS.cmake.in create mode 100644 test/CMakeLists.txt create mode 100644 test/TESTNAME-RUN.cmake.in create mode 100644 test/autosave_t.cpp create mode 100644 test/autosave_t.h create mode 100644 test/coord_xml_t.cpp create mode 100644 test/coord_xml_t.h create mode 100644 test/data/issue-513-coords-outside-printable.omap create mode 100644 test/data/issue-513-coords-outside-printable.xmap create mode 100644 test/data/issue-513-coords-outside-qint32.omap create mode 100644 test/data/spotcolor_overprint.xmap create mode 100644 test/data/templates/world-file.pgw create mode 100644 test/data/templates/world-file.png create mode 100644 test/data/templates/world-file.xmap create mode 100644 test/data/test_map.omap create mode 100644 test/duplicate_equals_t.cpp create mode 100644 test/duplicate_equals_t.h create mode 100644 test/encoding_t.cpp create mode 100644 test/encoding_t.h create mode 100644 test/file_format_t.cpp create mode 100644 test/file_format_t.h create mode 100644 test/georeferencing_t.cpp create mode 100644 test/georeferencing_t.h create mode 100644 test/locale_t.cpp create mode 100644 test/locale_t.h create mode 100644 test/map_color_t.cpp create mode 100644 test/map_color_t.h create mode 100644 test/map_t.cpp create mode 100644 test/map_t.h create mode 100644 test/object_query_t.cpp create mode 100644 test/object_query_t.h create mode 100644 test/path_object_t.cpp create mode 100644 test/path_object_t.h create mode 100644 test/qpainter_t.cpp create mode 100644 test/qpainter_t.h create mode 100644 test/symbol_set_t.cpp create mode 100644 test/symbol_set_t.h create mode 100644 test/template_t.cpp create mode 100644 test/test_config.h.in create mode 100644 test/tools_t.cpp create mode 100644 test/tools_t.h create mode 100644 test/transform_t.cpp create mode 100644 test/transform_t.h create mode 100644 test/tst_qglobal.cpp create mode 100644 test/tst_qglobal.h create mode 100644 test/undo_manager_t.cpp create mode 100644 test/undo_manager_t.h create mode 100644 test/util_t.cpp create mode 100644 translations/CMakeLists.txt create mode 100644 translations/OpenOrienteering_cs.ts create mode 100644 translations/OpenOrienteering_da.ts create mode 100644 translations/OpenOrienteering_de.ts create mode 100644 translations/OpenOrienteering_en.ts create mode 100644 translations/OpenOrienteering_eo.ts create mode 100644 translations/OpenOrienteering_es.ts create mode 100644 translations/OpenOrienteering_et.ts create mode 100644 translations/OpenOrienteering_eu.ts create mode 100644 translations/OpenOrienteering_fi.ts create mode 100644 translations/OpenOrienteering_fr.ts create mode 100644 translations/OpenOrienteering_he.ts create mode 100644 translations/OpenOrienteering_hu.ts create mode 100644 translations/OpenOrienteering_id.ts create mode 100644 translations/OpenOrienteering_it.ts create mode 100644 translations/OpenOrienteering_ja.ts create mode 100644 translations/OpenOrienteering_lv.ts create mode 100644 translations/OpenOrienteering_nb.ts create mode 100644 translations/OpenOrienteering_nl.ts create mode 100644 translations/OpenOrienteering_pl.ts create mode 100644 translations/OpenOrienteering_pt_BR.ts create mode 100644 translations/OpenOrienteering_pt_PT.ts create mode 100644 translations/OpenOrienteering_ru.ts create mode 100644 translations/OpenOrienteering_sv.ts create mode 100644 translations/OpenOrienteering_template.ts create mode 100644 translations/OpenOrienteering_uk.ts create mode 100644 translations/OpenOrienteering_zh_CN.ts create mode 100644 translations/future_translations.cpp create mode 100644 translations/locversion.plist.in create mode 100644 translations/map_symbols_cs.ts create mode 100644 translations/map_symbols_da.ts create mode 100644 translations/map_symbols_de.ts create mode 100644 translations/map_symbols_eo.ts create mode 100644 translations/map_symbols_es.ts create mode 100644 translations/map_symbols_fi.ts create mode 100644 translations/map_symbols_fr.ts create mode 100644 translations/map_symbols_hu.ts create mode 100644 translations/map_symbols_it.ts create mode 100644 translations/map_symbols_nl.ts create mode 100644 translations/map_symbols_pl.ts create mode 100644 translations/map_symbols_ru.ts create mode 100644 translations/map_symbols_sv.ts create mode 100644 translations/map_symbols_template.ts create mode 100644 translations/map_symbols_uk.ts create mode 100644 translations/qt_eo.ts create mode 100644 translations/qt_et.ts create mode 100644 translations/qt_id.ts create mode 100644 translations/qt_lv.ts create mode 100644 translations/qt_nb.ts create mode 100644 translations/qt_nl.ts create mode 100644 translations/qt_template.ts create mode 100755 translations/translate_text_files.sh diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..3f386a6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,14 @@ +### Steps to reproduce +1. +2. +3. + +### Actual behaviour +Tell us what happens + +### Expected behaviour +Tell us what should happen instead + +### Configuration +Mapper Version: +Operating System: diff --git a/3rd-party/clipper/CMakeLists.txt b/3rd-party/clipper/CMakeLists.txt new file mode 100644 index 0000000..bd4b9c7 --- /dev/null +++ b/3rd-party/clipper/CMakeLists.txt @@ -0,0 +1,125 @@ +# +# Copyright 2013-2015 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +project(Clipper) + +cmake_minimum_required(VERSION 2.8.3) + +# Configuration options + +set(CLIPPER_VERSION_DEFAULT 6.4.2) +if(CLIPPER_VERSION MATCHES "^5") + unset(CLIPPER_VERSION CACHE) # source incompatible +endif() +set(CLIPPER_VERSION ${CLIPPER_VERSION_DEFAULT} CACHE STRING + "Version number of the Clipper library") +mark_as_advanced(CLIPPER_VERSION) + + + +message(STATUS "Configuring Clipper library ${CLIPPER_VERSION}") + +if (NOT ${CLIPPER_VERSION} STREQUAL ${CLIPPER_VERSION_DEFAULT}) + message(WARNING + "The Clipper library version is different from the current recommended version " + "(${CLIPPER_VERSION} vs. ${CLIPPER_VERSION_DEFAULT}).") +endif() + +# Optionally use externally provided clipper source dir (e.g. Debian packaging) +set(CLIPPER_SOURCE_DIR_DEFAULT) +set(CLIPPER_SOURCE_DIR "${CLIPPER_SOURCE_DIR_DEFAULT}" CACHE STRING + "The Clipper library source directory to be used instead of a download") +mark_as_advanced(CLIPPER_SOURCE_DIR) + +if(CLIPPER_SOURCE_DIR) + # Expand variables like @PROJECT_SOURCE_DIR@ + string(CONFIGURE "${CLIPPER_SOURCE_DIR}" EXPANDED_SOURCE_DIR @ONLY) + set(CLIPPER_SOURCE + SOURCE_DIR "${EXPANDED_SOURCE_DIR}" + ) +else() + set(CLIPPER_MD5SUMS + # Schema: VERSION:MD5 + 6.1.3a:4dcd043ce48de59714f07bd3ec7ac62b + 6.4.2:100b4ec56c5308bac2d10f3966e35e11 + ) + foreach(line ${CLIPPER_MD5SUMS}) + if(${line} MATCHES "^${CLIPPER_VERSION}:") + string(REPLACE "${CLIPPER_VERSION}:" "" CLIPPER_MD5 ${line}) + break() + endif() + endforeach() + if(NOT CLIPPER_MD5) + message(FATAL_ERROR + "Unknown MD5 sum for Clipper library ${CLIPPER_VERSION}. " + "Edit ${PROJECT_SOURCE_DIR}/CMakeLists.txt, " + "or specify the correct CLIPPER_MD5 value at the command line.") + endif() + set(CLIPPER_SOURCE + DOWNLOAD_DIR ${PROJECT_SOURCE_DIR}/download + URL "http://sourceforge.net/projects/polyclipping/files/clipper_ver${CLIPPER_VERSION}.zip/download" + URL_MD5 ${CLIPPER_MD5} + ) +endif() + +set(CLIPPER_LICENSE_FILE "${PROJECT_SOURCE_DIR}/License.txt") +if(EXISTS "${CLIPPER_LICENSE_FILE}.${CLIPPER_VERSION}") + set(CLIPPER_LICENSE_FILE "${CLIPPER_LICENSE_FILE}.${CLIPPER_VERSION}") +endif() +file(GLOB CLIPPER_LICENSE_FILES License.txt*) +add_custom_target(clipper-licenses + COMMENT "This target makes Qt Creator show all sources in the project tree." + SOURCES ${CLIPPER_LICENSE_FILES} +) + +# External project definition + +include(ExternalProject) +ExternalProject_Add( + Clipper + ${CLIPPER_SOURCE} + CONFIGURE_COMMAND + # Check that the license hasn't changed. + ${CMAKE_COMMAND} -E compare_files /License.txt "${CLIPPER_LICENSE_FILE}" + COMMAND + # Force source file timestamp update. + ${CMAKE_COMMAND} -E copy /cpp/clipper.cpp "${CMAKE_CURRENT_BINARY_DIR}/clipper.cpp" + BUILD_COMMAND "" + INSTALL_COMMAND "" +) +ExternalProject_Get_Property(Clipper SOURCE_DIR) + + +# The actual library build + +set(CMAKE_CXX_CLANG_TIDY "") +set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "") + +if(CMAKE_COMPILER_IS_GNUCXX) + string(REPLACE "-Wpedantic" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +endif() + +set(CLIPPER_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/clipper.cpp") +set_source_files_properties(${CLIPPER_SOURCES} PROPERTIES GENERATED TRUE) + +add_library(polyclipping STATIC ${CLIPPER_SOURCES}) + +target_include_directories(polyclipping PUBLIC ${SOURCE_DIR}/cpp) + +add_dependencies(polyclipping Clipper) diff --git a/3rd-party/clipper/License.txt b/3rd-party/clipper/License.txt new file mode 100644 index 0000000..3d94797 --- /dev/null +++ b/3rd-party/clipper/License.txt @@ -0,0 +1,24 @@ +Boost Software License - Version 1.0 - August 17th, 2003 +http://www.boost.org/LICENSE_1_0.txt + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/3rd-party/clipper/License.txt.6.1.3a b/3rd-party/clipper/License.txt.6.1.3a new file mode 100644 index 0000000..36f45df --- /dev/null +++ b/3rd-party/clipper/License.txt.6.1.3a @@ -0,0 +1,26 @@ +The Clipper Library (including Delphi, C++ & C# source code, other accompanying +code, examples and documentation), hereafter called "the Software", has been +released under the following license, terms and conditions: + +Boost Software License - Version 1.0 - August 17th, 2003 +http://www.boost.org/LICENSE_1_0.txt + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the Software covered by this license to use, reproduce, +display, distribute, execute, and transmit the Software, and to prepare +derivative works of the Software, and to permit third-parties to whom the +Software is furnished to do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including the +above license grant, this restriction and the following disclaimer, must be +included in all copies of the Software, in whole or in part, and all derivative +works of the Software, unless such copies or derivative works are solely in the +form of machine-executable object code generated by a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL +THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY +DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/3rd-party/clipper/copyright b/3rd-party/clipper/copyright new file mode 100644 index 0000000..a12b934 --- /dev/null +++ b/3rd-party/clipper/copyright @@ -0,0 +1,127 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Clipper +Upstream-Contact: Angus Johnson +Source: http://sourceforge.net/projects/polyclipping + +Files: * +Copyright: Copyright 2010-2013 Angus Johnson +License: boost + The Clipper Library (including Delphi, C++ & C# source code, other accompanying + code, examples and documentation), hereafter called "the Software", has been + released under the following license, terms and conditions: + . + Boost Software License - Version 1.0 - August 17th, 2003 + http://www.boost.org/LICENSE_1_0.txt + . + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the Software covered by this license to use, reproduce, + display, distribute, execute, and transmit the Software, and to prepare + derivative works of the Software, and to permit third-parties to whom the + Software is furnished to do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, including the + above license grant, this restriction and the following disclaimer, must be + included in all copies of the Software, in whole or in part, and all derivative + works of the Software, unless such copies or derivative works are solely in the + form of machine-executable object code generated by a source language processor. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL + THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY + DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Files: Documentation/Scripts/SyntaxHighlighter/* +Copyright: Copyright (C) 2004-2010 Alex Gorbatchev. +License: MIT or GPL + +Files: Documentation/Scripts/SyntaxHighlighter/scripts/shBrushCpp.js +Copyright: + Copyright (C) 2004-2010 Alex Gorbatchev. + Copyright 2006 Shin, YoungJin +License: MIT or GPL + +Files: Documentation/Scripts/SyntaxHighlighter/tests/js/jquery-1.4.2.js +Copyright: + Copyright 2010, John Resig + Copyright 2009, 2010 The Dojo Foundation +License: MIT + +Files: Documentation/Scripts/SyntaxHighlighter/tests/js/qunit.js +Copyright: + Copyright (c) 2009 John Resig, Jörn Zaefferer + Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com +License: MIT or GPL, and BSD + +Files: debian/* +Copyright: Copyright 2003-2011 Bas Wijnen +License: GPL-3+ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + The latest version of the GPL can be found in + /usr/share/common-licenses/GPL. + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + . + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License: GPL + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 1 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the latest version of the GPL can be found in + /usr/share/common-licenses/GPL + +License: BSD + All rights reserved. + . + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + . + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rd-party/clipper/download/README.txt b/3rd-party/clipper/download/README.txt new file mode 100644 index 0000000..5703953 --- /dev/null +++ b/3rd-party/clipper/download/README.txt @@ -0,0 +1 @@ +This directory contains downloaded Clipper source archives. diff --git a/3rd-party/qbezier/CMakeLists.txt b/3rd-party/qbezier/CMakeLists.txt new file mode 100644 index 0000000..5a75cc4 --- /dev/null +++ b/3rd-party/qbezier/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Copyright 2014 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +# qbezier_p.h and the private headers it depends on may be missing +# in some installations of Qt. +# +# The implementation of the functions declared in qbezier_p.h are available in +# the QtGui library. +# +# The headers need to be reviewed when switching to newer versions of Qt. +# (Last review: Qt 5.3.0 beta.) + + +set(QBEZIER_SRCS + src/private/qbezier_p.h # from qtbase/src/gui/painting/ + src/private/qdatabuffer_p.h # from qtbase/src/gui/painting/ + src/private/qmath_p.h # from qtbase/src/gui/painting/ + src/private/qnumeric_p.h # from qtbase/src/corelib/global/ +) + +add_custom_target(QBezier + SOURCES ${QBEZIER_SRCS} +) diff --git a/3rd-party/qbezier/LICENSE.GPL b/3rd-party/qbezier/LICENSE.GPL new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/3rd-party/qbezier/LICENSE.GPL @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/3rd-party/qbezier/README.txt b/3rd-party/qbezier/README.txt new file mode 100644 index 0000000..7418ead --- /dev/null +++ b/3rd-party/qbezier/README.txt @@ -0,0 +1,8 @@ +The files in src/private/ are available under the GPL v3 (cf. LICENSE.GPL) and +were taken unmodified from the follwing files in +qt-everywhere-opensource-src-5.2.0.tar.gz: + +qtbase/src/gui/painting/qbezier_p.h +qtbase/src/gui/painting/qdatabuffer_p.h +qtbase/src/gui/painting/qmath_p.h +qtbase/src/corelib/global/qnumeric_p.h diff --git a/3rd-party/qbezier/src/mapper_qbezier.cpp b/3rd-party/qbezier/src/mapper_qbezier.cpp new file mode 100644 index 0000000..a2bdb90 --- /dev/null +++ b/3rd-party/qbezier/src/mapper_qbezier.cpp @@ -0,0 +1,5 @@ +// This file is part of OpenOrienteering. + +#include "private/qbezier_p.h" + +#define MAPPER_QBEZIER_VERSION 0x050200 diff --git a/3rd-party/qbezier/src/private/qbezier_p.h b/3rd-party/qbezier/src/private/qbezier_p.h new file mode 100644 index 0000000..3257fdc --- /dev/null +++ b/3rd-party/qbezier/src/private/qbezier_p.h @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBEZIER_P_H +#define QBEZIER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qpoint.h" +#include "QtCore/qline.h" +#include "QtCore/qrect.h" +#include "QtCore/qvector.h" +#include "QtCore/qlist.h" +#include "QtCore/qpair.h" +#include "QtGui/qtransform.h" +#include + +QT_BEGIN_NAMESPACE + +class QPolygonF; + +class Q_GUI_EXPORT QBezier +{ +public: + static QBezier fromPoints(const QPointF &p1, const QPointF &p2, + const QPointF &p3, const QPointF &p4); + + static void coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d); + + inline QPointF pointAt(qreal t) const; + inline QPointF normalVector(qreal t) const; + + inline QPointF derivedAt(qreal t) const; + inline QPointF secondDerivedAt(qreal t) const; + + QPolygonF toPolygon(qreal bezier_flattening_threshold = 0.5) const; + void addToPolygon(QPolygonF *p, qreal bezier_flattening_threshold = 0.5) const; + void addToPolygon(QDataBuffer &polygon, qreal bezier_flattening_threshold) const; + + QRectF bounds() const; + qreal length(qreal error = 0.01) const; + void addIfClose(qreal *length, qreal error) const; + + qreal tAtLength(qreal len) const; + + int stationaryYPoints(qreal &t0, qreal &t1) const; + qreal tForY(qreal t0, qreal t1, qreal y) const; + + QPointF pt1() const { return QPointF(x1, y1); } + QPointF pt2() const { return QPointF(x2, y2); } + QPointF pt3() const { return QPointF(x3, y3); } + QPointF pt4() const { return QPointF(x4, y4); } + + QBezier mapBy(const QTransform &transform) const; + + inline QPointF midPoint() const; + inline QLineF midTangent() const; + + inline QLineF startTangent() const; + inline QLineF endTangent() const; + + inline void parameterSplitLeft(qreal t, QBezier *left); + inline void split(QBezier *firstHalf, QBezier *secondHalf) const; + + int shifted(QBezier *curveSegments, int maxSegmets, + qreal offset, float threshold) const; + + QBezier bezierOnInterval(qreal t0, qreal t1) const; + QBezier getSubRange(qreal t0, qreal t1) const; + + qreal x1, y1, x2, y2, x3, y3, x4, y4; +}; + +inline QPointF QBezier::midPoint() const +{ + return QPointF((x1 + x4 + 3*(x2 + x3))/8., (y1 + y4 + 3*(y2 + y3))/8.); +} + +inline QLineF QBezier::midTangent() const +{ + QPointF mid = midPoint(); + QLineF dir(QLineF(x1, y1, x2, y2).pointAt(0.5), QLineF(x3, y3, x4, y4).pointAt(0.5)); + return QLineF(mid.x() - dir.dx(), mid.y() - dir.dy(), + mid.x() + dir.dx(), mid.y() + dir.dy()); +} + +inline QLineF QBezier::startTangent() const +{ + QLineF tangent(pt1(), pt2()); + if (tangent.isNull()) + tangent = QLineF(pt1(), pt3()); + if (tangent.isNull()) + tangent = QLineF(pt1(), pt4()); + return tangent; +} + +inline QLineF QBezier::endTangent() const +{ + QLineF tangent(pt4(), pt3()); + if (tangent.isNull()) + tangent = QLineF(pt4(), pt2()); + if (tangent.isNull()) + tangent = QLineF(pt4(), pt1()); + return tangent; +} + +inline void QBezier::coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d) +{ + qreal m_t = 1. - t; + b = m_t * m_t; + c = t * t; + d = c * t; + a = b * m_t; + b *= 3. * t; + c *= 3. * m_t; +} + +inline QPointF QBezier::pointAt(qreal t) const +{ + // numerically more stable: + qreal x, y; + + qreal m_t = 1. - t; + { + qreal a = x1*m_t + x2*t; + qreal b = x2*m_t + x3*t; + qreal c = x3*m_t + x4*t; + a = a*m_t + b*t; + b = b*m_t + c*t; + x = a*m_t + b*t; + } + { + qreal a = y1*m_t + y2*t; + qreal b = y2*m_t + y3*t; + qreal c = y3*m_t + y4*t; + a = a*m_t + b*t; + b = b*m_t + c*t; + y = a*m_t + b*t; + } + return QPointF(x, y); +} + +inline QPointF QBezier::normalVector(qreal t) const +{ + qreal m_t = 1. - t; + qreal a = m_t * m_t; + qreal b = t * m_t; + qreal c = t * t; + + return QPointF((y2-y1) * a + (y3-y2) * b + (y4-y3) * c, -(x2-x1) * a - (x3-x2) * b - (x4-x3) * c); +} + +inline QPointF QBezier::derivedAt(qreal t) const +{ + // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * t^2) * p2 + t^2 * p3) + + qreal m_t = 1. - t; + + qreal d = t * t; + qreal a = -m_t * m_t; + qreal b = 1 - 4 * t + 3 * d; + qreal c = 2 * t - 3 * d; + + return 3 * QPointF(a * x1 + b * x2 + c * x3 + d * x4, + a * y1 + b * y2 + c * y3 + d * y4); +} + +inline QPointF QBezier::secondDerivedAt(qreal t) const +{ + qreal a = 2. - 2. * t; + qreal b = -4 + 6 * t; + qreal c = 2 - 6 * t; + qreal d = 2 * t; + + return 3 * QPointF(a * x1 + b * x2 + c * x3 + d * x4, + a * y1 + b * y2 + c * y3 + d * y4); +} + +inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const +{ + Q_ASSERT(firstHalf); + Q_ASSERT(secondHalf); + + qreal c = (x2 + x3)*.5; + firstHalf->x2 = (x1 + x2)*.5; + secondHalf->x3 = (x3 + x4)*.5; + firstHalf->x1 = x1; + secondHalf->x4 = x4; + firstHalf->x3 = (firstHalf->x2 + c)*.5; + secondHalf->x2 = (secondHalf->x3 + c)*.5; + firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5; + + c = (y2 + y3)/2; + firstHalf->y2 = (y1 + y2)*.5; + secondHalf->y3 = (y3 + y4)*.5; + firstHalf->y1 = y1; + secondHalf->y4 = y4; + firstHalf->y3 = (firstHalf->y2 + c)*.5; + secondHalf->y2 = (secondHalf->y3 + c)*.5; + firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5; +} + +inline void QBezier::parameterSplitLeft(qreal t, QBezier *left) +{ + left->x1 = x1; + left->y1 = y1; + + left->x2 = x1 + t * ( x2 - x1 ); + left->y2 = y1 + t * ( y2 - y1 ); + + left->x3 = x2 + t * ( x3 - x2 ); // temporary holding spot + left->y3 = y2 + t * ( y3 - y2 ); // temporary holding spot + + x3 = x3 + t * ( x4 - x3 ); + y3 = y3 + t * ( y4 - y3 ); + + x2 = left->x3 + t * ( x3 - left->x3); + y2 = left->y3 + t * ( y3 - left->y3); + + left->x3 = left->x2 + t * ( left->x3 - left->x2 ); + left->y3 = left->y2 + t * ( left->y3 - left->y2 ); + + left->x4 = x1 = left->x3 + t * (x2 - left->x3); + left->y4 = y1 = left->y3 + t * (y2 - left->y3); +} + +QT_END_NAMESPACE + +#endif // QBEZIER_P_H diff --git a/3rd-party/qbezier/src/private/qdatabuffer_p.h b/3rd-party/qbezier/src/private/qdatabuffer_p.h new file mode 100644 index 0000000..45132fb --- /dev/null +++ b/3rd-party/qbezier/src/private/qdatabuffer_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDATABUFFER_P_H +#define QDATABUFFER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qbytearray.h" + +#include + +QT_BEGIN_NAMESPACE + +template class QDataBuffer +{ +public: + QDataBuffer(int res) + { + capacity = res; + if (res) + buffer = (Type*) malloc(capacity * sizeof(Type)); + else + buffer = 0; + siz = 0; + } + + ~QDataBuffer() + { + if (buffer) + free(buffer); + } + + inline void reset() { siz = 0; } + + inline bool isEmpty() const { return siz==0; } + + inline int size() const { return siz; } + inline Type *data() const { return buffer; } + + inline Type &at(int i) { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; } + inline const Type &at(int i) const { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; } + inline Type &last() { Q_ASSERT(!isEmpty()); return buffer[siz-1]; } + inline const Type &last() const { Q_ASSERT(!isEmpty()); return buffer[siz-1]; } + inline Type &first() { Q_ASSERT(!isEmpty()); return buffer[0]; } + inline const Type &first() const { Q_ASSERT(!isEmpty()); return buffer[0]; } + + inline void add(const Type &t) { + reserve(siz + 1); + buffer[siz] = t; + ++siz; + } + + inline void pop_back() { + Q_ASSERT(siz > 0); + --siz; + } + + inline void resize(int size) { + reserve(size); + siz = size; + } + + inline void reserve(int size) { + if (size > capacity) { + if (capacity == 0) + capacity = 1; + while (capacity < size) + capacity *= 2; + buffer = (Type*) realloc(buffer, capacity * sizeof(Type)); + } + } + + inline void shrink(int size) { + capacity = size; + if (size) + buffer = (Type*) realloc(buffer, capacity * sizeof(Type)); + else { + free(buffer); + buffer = 0; + } + } + + inline void swap(QDataBuffer &other) { + qSwap(capacity, other.capacity); + qSwap(siz, other.siz); + qSwap(buffer, other.buffer); + } + + inline QDataBuffer &operator<<(const Type &t) { add(t); return *this; } + +private: + int capacity; + int siz; + Type *buffer; +}; + +QT_END_NAMESPACE + +#endif // QDATABUFFER_P_H diff --git a/3rd-party/qbezier/src/private/qmath_p.h b/3rd-party/qbezier/src/private/qmath_p.h new file mode 100644 index 0000000..90920f4 --- /dev/null +++ b/3rd-party/qbezier/src/private/qmath_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMATH_P_H +#define QMATH_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +static const qreal Q_PI = qreal(3.14159265358979323846); // pi +static const qreal Q_2PI = qreal(6.28318530717958647693); // 2*pi +static const qreal Q_PI2 = qreal(1.57079632679489661923); // pi/2 +static const qreal Q_MM_PER_INCH = 25.4; + +inline int qIntSqrtInt(int v) +{ + return static_cast(qSqrt(static_cast(v))); +} + +QT_END_NAMESPACE + +#endif // QMATH_P_H diff --git a/3rd-party/qbezier/src/private/qnumeric_p.h b/3rd-party/qbezier/src/private/qnumeric_p.h new file mode 100644 index 0000000..dba5d4d --- /dev/null +++ b/3rd-party/qbezier/src/private/qnumeric_p.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNUMERIC_P_H +#define QNUMERIC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qglobal.h" + +QT_BEGIN_NAMESPACE + +#if !defined(Q_CC_MIPS) + +static const union { unsigned char c[8]; double d; } qt_be_inf_bytes = { { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } }; +static const union { unsigned char c[8]; double d; } qt_le_inf_bytes = { { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f } }; +static inline double qt_inf() +{ + return (QSysInfo::ByteOrder == QSysInfo::BigEndian + ? qt_be_inf_bytes.d + : qt_le_inf_bytes.d); +} + +// Signaling NAN +static const union { unsigned char c[8]; double d; } qt_be_snan_bytes = { { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 } }; +static const union { unsigned char c[8]; double d; } qt_le_snan_bytes = { { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f } }; +static inline double qt_snan() +{ + return (QSysInfo::ByteOrder == QSysInfo::BigEndian + ? qt_be_snan_bytes.d + : qt_le_snan_bytes.d); +} + +// Quiet NAN +static const union { unsigned char c[8]; double d; } qt_be_qnan_bytes = { { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 } }; +static const union { unsigned char c[8]; double d; } qt_le_qnan_bytes = { { 0, 0, 0, 0, 0, 0, 0xf8, 0xff } }; +static inline double qt_qnan() +{ + return (QSysInfo::ByteOrder == QSysInfo::BigEndian + ? qt_be_qnan_bytes.d + : qt_le_qnan_bytes.d); +} + +#else // Q_CC_MIPS + +static const unsigned char qt_be_inf_bytes[] = { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 }; +static const unsigned char qt_le_inf_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f }; +static inline double qt_inf() +{ + const unsigned char *bytes; + bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian + ? qt_be_inf_bytes + : qt_le_inf_bytes); + + union { unsigned char c[8]; double d; } returnValue; + memcpy(returnValue.c, bytes, sizeof(returnValue.c)); + return returnValue.d; +} + +// Signaling NAN +static const unsigned char qt_be_snan_bytes[] = { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 }; +static const unsigned char qt_le_snan_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f }; +static inline double qt_snan() +{ + const unsigned char *bytes; + bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian + ? qt_be_snan_bytes + : qt_le_snan_bytes); + + union { unsigned char c[8]; double d; } returnValue; + memcpy(returnValue.c, bytes, sizeof(returnValue.c)); + return returnValue.d; +} + +// Quiet NAN +static const unsigned char qt_be_qnan_bytes[] = { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }; +static const unsigned char qt_le_qnan_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf8, 0xff }; +static inline double qt_qnan() +{ + const unsigned char *bytes; + bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian + ? qt_be_qnan_bytes + : qt_le_qnan_bytes); + + union { unsigned char c[8]; double d; } returnValue; + memcpy(returnValue.c, bytes, sizeof(returnValue.c)); + return returnValue.d; +} + +#endif // Q_CC_MIPS + +static inline bool qt_is_inf(double d) +{ + uchar *ch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + return (ch[0] & 0x7f) == 0x7f && ch[1] == 0xf0; + } else { + return (ch[7] & 0x7f) == 0x7f && ch[6] == 0xf0; + } +} + +static inline bool qt_is_nan(double d) +{ + uchar *ch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + return (ch[0] & 0x7f) == 0x7f && ch[1] > 0xf0; + } else { + return (ch[7] & 0x7f) == 0x7f && ch[6] > 0xf0; + } +} + +static inline bool qt_is_finite(double d) +{ + uchar *ch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0xf0) != 0xf0; + } else { + return (ch[7] & 0x7f) != 0x7f || (ch[6] & 0xf0) != 0xf0; + } +} + +static inline bool qt_is_inf(float d) +{ + uchar *ch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + return (ch[0] & 0x7f) == 0x7f && ch[1] == 0x80; + } else { + return (ch[3] & 0x7f) == 0x7f && ch[2] == 0x80; + } +} + +static inline bool qt_is_nan(float d) +{ + uchar *ch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + return (ch[0] & 0x7f) == 0x7f && ch[1] > 0x80; + } else { + return (ch[3] & 0x7f) == 0x7f && ch[2] > 0x80; + } +} + +static inline bool qt_is_finite(float d) +{ + uchar *ch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0x80) != 0x80; + } else { + return (ch[3] & 0x7f) != 0x7f || (ch[2] & 0x80) != 0x80; + } +} + +QT_END_NAMESPACE + +#endif // QNUMERIC_P_H diff --git a/3rd-party/qtsingleapplication/CMakeLists.txt b/3rd-party/qtsingleapplication/CMakeLists.txt new file mode 100644 index 0000000..47c09d4 --- /dev/null +++ b/3rd-party/qtsingleapplication/CMakeLists.txt @@ -0,0 +1,43 @@ +# +# Copyright 2012-2015 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +find_package(Qt5Widgets REQUIRED) +find_package(Qt5Network REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_CXX_CLANG_TIDY "") +set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "") + +set(QSINGLEAPP_SRCS + src/qtlocalpeer.cpp + src/qtsingleapplication.cpp + src/qtsinglecoreapplication.cpp +) + +add_library(QtSingleApplication STATIC + ${QSINGLEAPP_SRCS} +) + +target_include_directories(QtSingleApplication + INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src" +) + +target_link_libraries(QtSingleApplication + PUBLIC Qt5::Widgets + PUBLIC Qt5::Network +) diff --git a/3rd-party/qtsingleapplication/copyright b/3rd-party/qtsingleapplication/copyright new file mode 100644 index 0000000..0f44fc0 --- /dev/null +++ b/3rd-party/qtsingleapplication/copyright @@ -0,0 +1,37 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Qt Solutions Component: Single Application +Source: http://code.qt.io/cgit/qt-solutions/qt-solutions.git/tree/qtsingleapplication + +Files: src/* +Copyright: 2013 Digia Plc and/or its subsidiary(-ies). +License: BSD-3-clause + +License: BSD-3-clause + This file is part of a Qt Solutions component. + . + You may use this file under the terms of the BSD license as follows: + . + "Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." diff --git a/3rd-party/qtsingleapplication/src/QtLockedFile b/3rd-party/qtsingleapplication/src/QtLockedFile new file mode 100644 index 0000000..16b48ba --- /dev/null +++ b/3rd-party/qtsingleapplication/src/QtLockedFile @@ -0,0 +1 @@ +#include "qtlockedfile.h" diff --git a/3rd-party/qtsingleapplication/src/QtSingleApplication b/3rd-party/qtsingleapplication/src/QtSingleApplication new file mode 100644 index 0000000..d111bf7 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/QtSingleApplication @@ -0,0 +1 @@ +#include "qtsingleapplication.h" diff --git a/3rd-party/qtsingleapplication/src/qtlocalpeer.cpp b/3rd-party/qtsingleapplication/src/qtlocalpeer.cpp new file mode 100644 index 0000000..f73b584 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtlocalpeer.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qtlocalpeer.h" +#include +#include +#include + +#if defined(Q_OS_WIN) +#include +#include +typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); +static PProcessIdToSessionId pProcessIdToSessionId = 0; +#endif +#if defined(Q_OS_UNIX) +#include +#include +#include +#endif + +namespace QtLP_Private { +#include "qtlockedfile.cpp" +#if defined(Q_OS_WIN) +#include "qtlockedfile_win.cpp" +#else +#include "qtlockedfile_unix.cpp" +#endif +} + +const char* QtLocalPeer::ack = "ack"; + +QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) + : QObject(parent), id(appId) +{ + QString prefix = id; + if (id.isEmpty()) { + id = QCoreApplication::applicationFilePath(); +#if defined(Q_OS_WIN) + id = id.toLower(); +#endif + prefix = id.section(QLatin1Char('/'), -1); + } + prefix.remove(QRegExp("[^a-zA-Z]")); + prefix.truncate(6); + + QByteArray idc = id.toUtf8(); + quint16 idNum = qChecksum(idc.constData(), idc.size()); + socketName = QLatin1String("qtsingleapp-") + prefix + + QLatin1Char('-') + QString::number(idNum, 16); + +#if defined(Q_OS_WIN) + if (!pProcessIdToSessionId) { + QLibrary lib("kernel32"); + pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); + } + if (pProcessIdToSessionId) { + DWORD sessionId = 0; + pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); + socketName += QLatin1Char('-') + QString::number(sessionId, 16); + } +#else + socketName += QLatin1Char('-') + QString::number(::getuid(), 16); +#endif + + server = new QLocalServer(this); + QString lockName = QDir(QDir::tempPath()).absolutePath() + + QLatin1Char('/') + socketName + + QLatin1String("-lockfile"); + lockFile.setFileName(lockName); + lockFile.open(QIODevice::ReadWrite); +} + + + +bool QtLocalPeer::isClient() +{ + if (lockFile.isLocked()) + return false; + + if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) + return true; + + bool res = server->listen(socketName); +#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) + // ### Workaround + if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { + QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); + res = server->listen(socketName); + } +#endif + if (!res) + qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); + QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection); + return false; +} + + +bool QtLocalPeer::sendMessage(const QString &message, int timeout) +{ + if (!isClient()) + return false; + + QLocalSocket socket; + bool connOk = false; + for(int i = 0; i < 2; i++) { + // Try twice, in case the other instance is just starting up + socket.connectToServer(socketName); + connOk = socket.waitForConnected(timeout/2); + if (connOk || i) + break; + int ms = 250; +#if defined(Q_OS_WIN) + Sleep(DWORD(ms)); +#else + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, NULL); +#endif + } + if (!connOk) + return false; + + QByteArray uMsg(message.toUtf8()); + QDataStream ds(&socket); + ds.writeBytes(uMsg.constData(), uMsg.size()); + bool res = socket.waitForBytesWritten(timeout); + if (res) { + res &= socket.waitForReadyRead(timeout); // wait for ack + if (res) + res &= (socket.read(qstrlen(ack)) == ack); + } + return res; +} + + +void QtLocalPeer::receiveConnection() +{ + QLocalSocket* socket = server->nextPendingConnection(); + if (!socket) + return; + + while (socket->bytesAvailable() < (int)sizeof(quint32)) + socket->waitForReadyRead(); + QDataStream ds(socket); + QByteArray uMsg; + quint32 remaining; + ds >> remaining; + uMsg.resize(remaining); + int got = 0; + char* uMsgBuf = uMsg.data(); + do { + got = ds.readRawData(uMsgBuf, remaining); + remaining -= got; + uMsgBuf += got; + } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); + if (got < 0) { + qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); + delete socket; + return; + } + QString message(QString::fromUtf8(uMsg)); + socket->write(ack, qstrlen(ack)); + socket->waitForBytesWritten(1000); + socket->waitForDisconnected(1000); // make sure client reads ack + delete socket; + emit messageReceived(message); //### (might take a long time to return) +} diff --git a/3rd-party/qtsingleapplication/src/qtlocalpeer.h b/3rd-party/qtsingleapplication/src/qtlocalpeer.h new file mode 100644 index 0000000..1b533b1 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtlocalpeer.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTLOCALPEER_H +#define QTLOCALPEER_H + +#include +#include +#include + +#include "qtlockedfile.h" + +class QtLocalPeer : public QObject +{ + Q_OBJECT + +public: + QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); + bool isClient(); + bool sendMessage(const QString &message, int timeout); + QString applicationId() const + { return id; } + +Q_SIGNALS: + void messageReceived(const QString &message); + +protected Q_SLOTS: + void receiveConnection(); + +protected: + QString id; + QString socketName; + QLocalServer* server; + QtLP_Private::QtLockedFile lockFile; + +private: + static const char* ack; +}; + +#endif // QTLOCALPEER_H diff --git a/3rd-party/qtsingleapplication/src/qtlockedfile.cpp b/3rd-party/qtsingleapplication/src/qtlockedfile.cpp new file mode 100644 index 0000000..c142a86 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtlockedfile.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtlockedfile.h" + +/*! + \class QtLockedFile + + \brief The QtLockedFile class extends QFile with advisory locking + functions. + + A file may be locked in read or write mode. Multiple instances of + \e QtLockedFile, created in multiple processes running on the same + machine, may have a file locked in read mode. Exactly one instance + may have it locked in write mode. A read and a write lock cannot + exist simultaneously on the same file. + + The file locks are advisory. This means that nothing prevents + another process from manipulating a locked file using QFile or + file system functions offered by the OS. Serialization is only + guaranteed if all processes that access the file use + QLockedFile. Also, while holding a lock on a file, a process + must not open the same file again (through any API), or locks + can be unexpectedly lost. + + The lock provided by an instance of \e QtLockedFile is released + whenever the program terminates. This is true even when the + program crashes and no destructors are called. +*/ + +/*! \enum QtLockedFile::LockMode + + This enum describes the available lock modes. + + \value ReadLock A read lock. + \value WriteLock A write lock. + \value NoLock Neither a read lock nor a write lock. +*/ + +/*! + Constructs an unlocked \e QtLockedFile object. This constructor + behaves in the same way as \e QFile::QFile(). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile() + : QFile() +{ +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Constructs an unlocked QtLockedFile object with file \a name. This + constructor behaves in the same way as \e QFile::QFile(const + QString&). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile(const QString &name) + : QFile(name) +{ +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Opens the file in OpenMode \a mode. + + This is identical to QFile::open(), with the one exception that the + Truncate mode flag is disallowed. Truncation would conflict with the + advisory file locking, since the file would be modified before the + write lock is obtained. If truncation is required, use resize(0) + after obtaining the write lock. + + Returns true if successful; otherwise false. + + \sa QFile::open(), QFile::resize() +*/ +bool QtLockedFile::open(OpenMode mode) +{ + if (mode & QIODevice::Truncate) { + qWarning("QtLockedFile::open(): Truncate mode not allowed."); + return false; + } + return QFile::open(mode); +} + +/*! + Returns \e true if this object has a in read or write lock; + otherwise returns \e false. + + \sa lockMode() +*/ +bool QtLockedFile::isLocked() const +{ + return m_lock_mode != NoLock; +} + +/*! + Returns the type of lock currently held by this object, or \e + QtLockedFile::NoLock. + + \sa isLocked() +*/ +QtLockedFile::LockMode QtLockedFile::lockMode() const +{ + return m_lock_mode; +} + +/*! + \fn bool QtLockedFile::lock(LockMode mode, bool block = true) + + Obtains a lock of type \a mode. The file must be opened before it + can be locked. + + If \a block is true, this function will block until the lock is + aquired. If \a block is false, this function returns \e false + immediately if the lock cannot be aquired. + + If this object already has a lock of type \a mode, this function + returns \e true immediately. If this object has a lock of a + different type than \a mode, the lock is first released and then a + new lock is obtained. + + This function returns \e true if, after it executes, the file is + locked by this object, and \e false otherwise. + + \sa unlock(), isLocked(), lockMode() +*/ + +/*! + \fn bool QtLockedFile::unlock() + + Releases a lock. + + If the object has no lock, this function returns immediately. + + This function returns \e true if, after it executes, the file is + not locked by this object, and \e false otherwise. + + \sa lock(), isLocked(), lockMode() +*/ + +/*! + \fn QtLockedFile::~QtLockedFile() + + Destroys the \e QtLockedFile object. If any locks were held, they + are released. +*/ diff --git a/3rd-party/qtsingleapplication/src/qtlockedfile.h b/3rd-party/qtsingleapplication/src/qtlockedfile.h new file mode 100644 index 0000000..a0afcd7 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtlockedfile.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTLOCKEDFILE_H +#define QTLOCKEDFILE_H + +#include +#ifdef Q_OS_WIN +#include +#endif + +#if defined(Q_OS_WIN) +# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) +# define QT_QTLOCKEDFILE_EXPORT +# elif defined(QT_QTLOCKEDFILE_IMPORT) +# if defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# endif +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) +# elif defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTLOCKEDFILE_EXPORT +#endif + +namespace QtLP_Private { + +class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile +{ +public: + enum LockMode { NoLock = 0, ReadLock, WriteLock }; + + QtLockedFile(); + QtLockedFile(const QString &name); + ~QtLockedFile() override; + + bool open(OpenMode mode) override; + + bool lock(LockMode mode, bool block = true); + bool unlock(); + bool isLocked() const; + LockMode lockMode() const; + +private: +#ifdef Q_OS_WIN + Qt::HANDLE wmutex; + Qt::HANDLE rmutex; + QVector rmutexes; + QString mutexname; + + Qt::HANDLE getMutexHandle(int idx, bool doCreate); + bool waitMutex(Qt::HANDLE mutex, bool doBlock); + +#endif + LockMode m_lock_mode; +}; +} +#endif diff --git a/3rd-party/qtsingleapplication/src/qtlockedfile_unix.cpp b/3rd-party/qtsingleapplication/src/qtlockedfile_unix.cpp new file mode 100644 index 0000000..976c1b9 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtlockedfile_unix.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qtlockedfile.h" + +bool QtLockedFile::lock(LockMode mode, bool block) +{ + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if (mode == NoLock) + return unlock(); + + if (mode == m_lock_mode) + return true; + + if (m_lock_mode != NoLock) + unlock(); + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; + int cmd = block ? F_SETLKW : F_SETLK; + int ret = fcntl(handle(), cmd, &fl); + + if (ret == -1) { + if (errno != EINTR && errno != EAGAIN) + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + + m_lock_mode = mode; + return true; +} + + +bool QtLockedFile::unlock() +{ + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if (!isLocked()) + return true; + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = F_UNLCK; + int ret = fcntl(handle(), F_SETLKW, &fl); + + if (ret == -1) { + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + m_lock_mode = NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() +{ + if (isOpen()) + unlock(); +} + diff --git a/3rd-party/qtsingleapplication/src/qtlockedfile_win.cpp b/3rd-party/qtsingleapplication/src/qtlockedfile_win.cpp new file mode 100644 index 0000000..5e21262 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtlockedfile_win.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtlockedfile.h" +#include +#include + +#define MUTEX_PREFIX "QtLockedFile mutex " +// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS +#define MAX_READERS MAXIMUM_WAIT_OBJECTS + +#if QT_VERSION >= 0x050000 +#define QT_WA(unicode, ansi) unicode +#endif + +Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) +{ + if (mutexname.isEmpty()) { + QFileInfo fi(*this); + mutexname = QString::fromLatin1(MUTEX_PREFIX) + + fi.absoluteFilePath().toLower(); + } + QString mname(mutexname); + if (idx >= 0) + mname += QString::number(idx); + + Qt::HANDLE mutex; + if (doCreate) { + QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, + { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); + return 0; + } + } + else { + QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, + { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + if (GetLastError() != ERROR_FILE_NOT_FOUND) + qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); + return 0; + } + } + return mutex; +} + +bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) +{ + Q_ASSERT(mutex); + DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); + switch (res) { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + return true; + break; + case WAIT_TIMEOUT: + break; + default: + qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); + } + return false; +} + + + +bool QtLockedFile::lock(LockMode mode, bool block) +{ + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if (mode == NoLock) + return unlock(); + + if (mode == m_lock_mode) + return true; + + if (m_lock_mode != NoLock) + unlock(); + + if (!wmutex && !(wmutex = getMutexHandle(-1, true))) + return false; + + if (!waitMutex(wmutex, block)) + return false; + + if (mode == ReadLock) { + int idx = 0; + for (; idx < MAX_READERS; idx++) { + rmutex = getMutexHandle(idx, false); + if (!rmutex || waitMutex(rmutex, false)) + break; + CloseHandle(rmutex); + } + bool ok = true; + if (idx >= MAX_READERS) { + qWarning("QtLockedFile::lock(): too many readers"); + rmutex = 0; + ok = false; + } + else if (!rmutex) { + rmutex = getMutexHandle(idx, true); + if (!rmutex || !waitMutex(rmutex, false)) + ok = false; + } + if (!ok && rmutex) { + CloseHandle(rmutex); + rmutex = 0; + } + ReleaseMutex(wmutex); + if (!ok) + return false; + } + else { + Q_ASSERT(rmutexes.isEmpty()); + for (int i = 0; i < MAX_READERS; i++) { + Qt::HANDLE mutex = getMutexHandle(i, false); + if (mutex) + rmutexes.append(mutex); + } + if (rmutexes.size()) { + DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), + TRUE, block ? INFINITE : 0); + if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { + if (res != WAIT_TIMEOUT) + qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); + m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky + unlock(); + return false; + } + } + } + + m_lock_mode = mode; + return true; +} + +bool QtLockedFile::unlock() +{ + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if (!isLocked()) + return true; + + if (m_lock_mode == ReadLock) { + ReleaseMutex(rmutex); + CloseHandle(rmutex); + rmutex = 0; + } + else { + foreach(Qt::HANDLE mutex, rmutexes) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } + rmutexes.clear(); + ReleaseMutex(wmutex); + } + + m_lock_mode = QtLockedFile::NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() +{ + if (isOpen()) + unlock(); + if (wmutex) + CloseHandle(wmutex); +} diff --git a/3rd-party/qtsingleapplication/src/qtsingleapplication.cpp b/3rd-party/qtsingleapplication/src/qtsingleapplication.cpp new file mode 100644 index 0000000..d8c1131 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtsingleapplication.cpp @@ -0,0 +1,347 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qtsingleapplication.h" +#include "qtlocalpeer.h" +#include + + +/*! + \class QtSingleApplication qtsingleapplication.h + \brief The QtSingleApplication class provides an API to detect and + communicate with running instances of an application. + + This class allows you to create applications where only one + instance should be running at a time. I.e., if the user tries to + launch another instance, the already running instance will be + activated instead. Another usecase is a client-server system, + where the first started instance will assume the role of server, + and the later instances will act as clients of that server. + + By default, the full path of the executable file is used to + determine whether two processes are instances of the same + application. You can also provide an explicit identifier string + that will be compared instead. + + The application should create the QtSingleApplication object early + in the startup phase, and call isRunning() to find out if another + instance of this application is already running. If isRunning() + returns false, it means that no other instance is running, and + this instance has assumed the role as the running instance. In + this case, the application should continue with the initialization + of the application user interface before entering the event loop + with exec(), as normal. + + The messageReceived() signal will be emitted when the running + application receives messages from another instance of the same + application. When a message is received it might be helpful to the + user to raise the application so that it becomes visible. To + facilitate this, QtSingleApplication provides the + setActivationWindow() function and the activateWindow() slot. + + If isRunning() returns true, another instance is already + running. It may be alerted to the fact that another instance has + started by using the sendMessage() function. Also data such as + startup parameters (e.g. the name of the file the user wanted this + new instance to open) can be passed to the running instance with + this function. Then, the application should terminate (or enter + client mode). + + If isRunning() returns true, but sendMessage() fails, that is an + indication that the running instance is frozen. + + Here's an example that shows how to convert an existing + application to use QtSingleApplication. It is very simple and does + not make use of all QtSingleApplication's functionality (see the + examples for that). + + \code + // Original + int main(int argc, char **argv) + { + QApplication app(argc, argv); + + MyMainWidget mmw; + mmw.show(); + return app.exec(); + } + + // Single instance + int main(int argc, char **argv) + { + QtSingleApplication app(argc, argv); + + if (app.isRunning()) + return !app.sendMessage(someDataString); + + MyMainWidget mmw; + app.setActivationWindow(&mmw); + mmw.show(); + return app.exec(); + } + \endcode + + Once this QtSingleApplication instance is destroyed (normally when + the process exits or crashes), when the user next attempts to run the + application this instance will not, of course, be encountered. The + next instance to call isRunning() or sendMessage() will assume the + role as the new running instance. + + For console (non-GUI) applications, QtSingleCoreApplication may be + used instead of this class, to avoid the dependency on the QtGui + library. + + \sa QtSingleCoreApplication +*/ + + +void QtSingleApplication::sysInit(const QString &appId) +{ + actWin = 0; + peer = new QtLocalPeer(this, appId); + connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::messageReceived); +} + + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a GUIenabled are passed on to the QAppliation constructor. + + If you are creating a console application (i.e. setting \a + GUIenabled to false), you may consider using + QtSingleCoreApplication instead. +*/ + +QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) + : QApplication(argc, argv, GUIenabled) +{ + sysInit(); +} + + +/*! + Creates a QtSingleApplication object with the application + identifier \a appId. \a argc and \a argv are passed on to the + QAppliation constructor. +*/ + +QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) + : QApplication(argc, argv) +{ + sysInit(appId); +} + +#if QT_VERSION < 0x050000 + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a type are passed on to the QAppliation constructor. +*/ +QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) + : QApplication(argc, argv, type) +{ + sysInit(); +} + + +# if defined(Q_WS_X11) +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, + and \a cmap are passed on to the QApplication constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, visual, cmap) +{ + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) +{ + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be \a appId. \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) +{ + sysInit(appId); +} +# endif // Q_WS_X11 +#endif // QT_VERSION < 0x050000 + + +/*! + Returns true if another instance of this application is running; + otherwise false. + + This function does not find instances of this application that are + being run by a different user (on Windows: that are running in + another session). + + \sa sendMessage() +*/ + +bool QtSingleApplication::isRunning() +{ + return peer->isClient(); +} + + +/*! + Tries to send the text \a message to the currently running + instance. The QtSingleApplication object in the running instance + will emit the messageReceived() signal when it receives the + message. + + This function returns true if the message has been sent to, and + processed by, the current instance. If there is no instance + currently running, or if the running instance fails to process the + message within \a timeout milliseconds, this function return false. + + \sa isRunning(), messageReceived() +*/ +bool QtSingleApplication::sendMessage(const QString &message, int timeout) +{ + return peer->sendMessage(message, timeout); +} + + +/*! + Returns the application identifier. Two processes with the same + identifier will be regarded as instances of the same application. +*/ +QString QtSingleApplication::id() const +{ + return peer->applicationId(); +} + + +/*! + Sets the activation window of this application to \a aw. The + activation window is the widget that will be activated by + activateWindow(). This is typically the application's main window. + + If \a activateOnMessage is true (the default), the window will be + activated automatically every time a message is received, just prior + to the messageReceived() signal being emitted. + + \sa activateWindow(), messageReceived() +*/ + +void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) +{ + actWin = aw; + if (activateOnMessage) + connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); + else + disconnect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); +} + + +/*! + Returns the applications activation window if one has been set by + calling setActivationWindow(), otherwise returns 0. + + \sa setActivationWindow() +*/ +QWidget* QtSingleApplication::activationWindow() const +{ + return actWin; +} + + +/*! + De-minimizes, raises, and activates this application's activation window. + This function does nothing if no activation window has been set. + + This is a convenience function to show the user that this + application instance has been activated when he has tried to start + another instance. + + This function should typically be called in response to the + messageReceived() signal. By default, that will happen + automatically, if an activation window has been set. + + \sa setActivationWindow(), messageReceived(), initialize() +*/ +void QtSingleApplication::activateWindow() +{ + if (actWin) { + actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); + actWin->raise(); + actWin->activateWindow(); + } +} + + +/*! + \fn void QtSingleApplication::messageReceived(const QString& message) + + This signal is emitted when the current instance receives a \a + message from another instance of this application. + + \sa sendMessage(), setActivationWindow(), activateWindow() +*/ + + +/*! + \fn void QtSingleApplication::initialize(bool dummy = true) + + \obsolete +*/ diff --git a/3rd-party/qtsingleapplication/src/qtsingleapplication.h b/3rd-party/qtsingleapplication/src/qtsingleapplication.h new file mode 100644 index 0000000..049406f --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtsingleapplication.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTSINGLEAPPLICATION_H +#define QTSINGLEAPPLICATION_H + +#include + +class QtLocalPeer; + +#if defined(Q_OS_WIN) +# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) +# define QT_QTSINGLEAPPLICATION_EXPORT +# elif defined(QT_QTSINGLEAPPLICATION_IMPORT) +# if defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# endif +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) +# elif defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTSINGLEAPPLICATION_EXPORT +#endif + +class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication +{ + Q_OBJECT + +public: + QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); + QtSingleApplication(const QString &id, int &argc, char **argv); +#if QT_VERSION < 0x050000 + QtSingleApplication(int &argc, char **argv, Type type); +# if defined(Q_WS_X11) + QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); + QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); + QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); +# endif // Q_WS_X11 +#endif // QT_VERSION < 0x050000 + + bool isRunning(); + QString id() const; + + void setActivationWindow(QWidget* aw, bool activateOnMessage = true); + QWidget* activationWindow() const; + + // Obsolete: + void initialize(bool dummy = true) + { isRunning(); Q_UNUSED(dummy) } + +public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); + void activateWindow(); + + +Q_SIGNALS: + void messageReceived(const QString &message); + + +private: + void sysInit(const QString &appId = QString()); + QtLocalPeer *peer; + QWidget *actWin; +}; + +#endif // QTSINGLEAPPLICATION_H diff --git a/3rd-party/qtsingleapplication/src/qtsingleapplication.pri b/3rd-party/qtsingleapplication/src/qtsingleapplication.pri new file mode 100644 index 0000000..6f2bced --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtsingleapplication.pri @@ -0,0 +1,17 @@ +include(../common.pri) +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +QT *= network +greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets + +qtsingleapplication-uselib:!qtsingleapplication-buildlib { + LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME +} else { + SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp + HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h +} + +win32 { + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT + else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT +} diff --git a/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.cpp b/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.cpp new file mode 100644 index 0000000..d1440e2 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qtsinglecoreapplication.h" +#include "qtlocalpeer.h" + +/*! + \class QtSingleCoreApplication qtsinglecoreapplication.h + \brief A variant of the QtSingleApplication class for non-GUI applications. + + This class is a variant of QtSingleApplication suited for use in + console (non-GUI) applications. It is an extension of + QCoreApplication (instead of QApplication). It does not require + the QtGui library. + + The API and usage is identical to QtSingleApplication, except that + functions relating to the "activation window" are not present, for + obvious reasons. Please refer to the QtSingleApplication + documentation for explanation of the usage. + + A QtSingleCoreApplication instance can communicate to a + QtSingleApplication instance if they share the same application + id. Hence, this class can be used to create a light-weight + command-line tool that sends commands to a GUI application. + + \sa QtSingleApplication +*/ + +/*! + Creates a QtSingleCoreApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc and \a + argv are passed on to the QCoreAppliation constructor. +*/ + +QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) + : QCoreApplication(argc, argv) +{ + peer = new QtLocalPeer(this); + connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleCoreApplication::messageReceived); +} + + +/*! + Creates a QtSingleCoreApplication object with the application + identifier \a appId. \a argc and \a argv are passed on to the + QCoreAppliation constructor. +*/ +QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) + : QCoreApplication(argc, argv) +{ + peer = new QtLocalPeer(this, appId); + connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleCoreApplication::messageReceived); +} + + +/*! + Returns true if another instance of this application is running; + otherwise false. + + This function does not find instances of this application that are + being run by a different user (on Windows: that are running in + another session). + + \sa sendMessage() +*/ + +bool QtSingleCoreApplication::isRunning() +{ + return peer->isClient(); +} + + +/*! + Tries to send the text \a message to the currently running + instance. The QtSingleCoreApplication object in the running instance + will emit the messageReceived() signal when it receives the + message. + + This function returns true if the message has been sent to, and + processed by, the current instance. If there is no instance + currently running, or if the running instance fails to process the + message within \a timeout milliseconds, this function return false. + + \sa isRunning(), messageReceived() +*/ + +bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) +{ + return peer->sendMessage(message, timeout); +} + + +/*! + Returns the application identifier. Two processes with the same + identifier will be regarded as instances of the same application. +*/ + +QString QtSingleCoreApplication::id() const +{ + return peer->applicationId(); +} + + +/*! + \fn void QtSingleCoreApplication::messageReceived(const QString& message) + + This signal is emitted when the current instance receives a \a + message from another instance of this application. + + \sa sendMessage() +*/ diff --git a/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.h b/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.h new file mode 100644 index 0000000..b87fffe --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTSINGLECOREAPPLICATION_H +#define QTSINGLECOREAPPLICATION_H + +#include + +class QtLocalPeer; + +class QtSingleCoreApplication : public QCoreApplication +{ + Q_OBJECT + +public: + QtSingleCoreApplication(int &argc, char **argv); + QtSingleCoreApplication(const QString &id, int &argc, char **argv); + + bool isRunning(); + QString id() const; + +public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); + + +Q_SIGNALS: + void messageReceived(const QString &message); + + +private: + QtLocalPeer* peer; +}; + +#endif // QTSINGLECOREAPPLICATION_H diff --git a/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.pri b/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.pri new file mode 100644 index 0000000..d2d6cc3 --- /dev/null +++ b/3rd-party/qtsingleapplication/src/qtsinglecoreapplication.pri @@ -0,0 +1,10 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +HEADERS += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h +SOURCES += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp + +QT *= network + +win32:contains(TEMPLATE, lib):contains(CONFIG, shared) { + DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport) +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1e9b2b7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,272 @@ +# +# Copyright 2012, 2013, 2014 Thomas Schöps +# Copyright 2012-2018 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +cmake_minimum_required(VERSION 3.0 FATAL_ERROR) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include(FeatureSummary) + +if(CCACHE_PROGRAM) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") +endif() + +# Project declaration + +project(Mapper VERSION 0.8.4 LANGUAGES CXX C) + +if(Mapper_VERSION_DISPLAY) + message(STATUS "Custom version display string: \"${Mapper_VERSION_DISPLAY}\"") +elseif(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE MATCHES "Release|MinSizeRel|RelWithDebInfo") + set(Mapper_VERSION_DISPLAY "${CMAKE_BUILD_TYPE} ${Mapper_VERSION}") +else() + set(Mapper_VERSION_DISPLAY "${Mapper_VERSION}") +endif() + +set(Mapper_COPYRIGHT "(C) 2012-2018 The OpenOrienteering developers") + +set(android_manifest "${PROJECT_SOURCE_DIR}/android/AndroidManifest.xml") +file(READ "${android_manifest}" current) +if(NOT current MATCHES " android:versionName=.${Mapper_VERSION_MAJOR}\\.${Mapper_VERSION_MINOR}\\.${Mapper_VERSION_PATCH}. ") + set(android_version_name "${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}") + math(EXPR android_version_int "${Mapper_VERSION_MAJOR} * 10000 + ${Mapper_VERSION_MINOR} * 100 + ${Mapper_VERSION_PATCH}") + string(REGEX REPLACE "( android:versionName=.)[0-9]*\\.[0-9]*\\.[0-9]*(. )" "\\1${android_version_name}\\2" output "${current}") + string(REGEX REPLACE "( android:versionCode=.)[0-9]*(. )" "\\1${android_version_int}\\2" output "${output}") + file(WRITE "${android_manifest}" "${output}") +endif() + +if(${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${PROJECT_SOURCE_DIR}) + message(AUTHOR_WARNING "In-source builds are discouraged for development.") +endif() + +# Build configuration options + +if(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING + "Type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + FORCE) +endif(NOT CMAKE_BUILD_TYPE) + +if(APPLE) + SET(Mapper_MACOSX_VERSION "10.7" CACHE STRING + "Minimum Mac OS X version to build for, recommended: 10.7") +endif() + +option(Mapper_DEBUG_TRANSLATIONS "Debug missing translations" OFF) + +# Used for some Linux distributions which do not provide the polyclipping lib. +option(Mapper_BUILD_CLIPPER "Build the Clipper package from source" OFF) + +option(Mapper_USE_GDAL "Use the GDAL library" ON) + +if(CMAKE_BUILD_TYPE MATCHES Release|MinSizeRel|RelWithDebInfo) + set(Mapper_DEVELOPMENT_BUILD_DEFAULT OFF) +else() + set(Mapper_DEVELOPMENT_BUILD_DEFAULT ON) +endif() +option(Mapper_DEVELOPMENT_BUILD "Configure development build (loading resource from the build directory)" ${Mapper_DEVELOPMENT_BUILD_DEFAULT}) +mark_as_advanced(Mapper_DEVELOPMENT_BUILD) + +option(Mapper_AUTORUN_SYSTEM_TESTS "Run the system tests as part of the Mapper_Test target" ${Mapper_DEVELOPMENT_BUILD}) +option(Mapper_AUTORUN_MANUAL_TESTS "Run the system tests as part of the Mapper_Test target" OFF) +mark_as_advanced(Mapper_AUTORUN_SYSTEM_TESTS Mapper_AUTORUN_MANUAL_TESTS) + +if(ANDROID OR APPLE OR WIN32) + set(mapper_package_default ON) +else() + set(mapper_package_default OFF) +endif() +if(Mapper_USE_GDAL) + set(mapper_package_gdal ${mapper_package_default}) +else() + set(mapper_package_gdal OFF) +endif() +if(NOT ANDROID) + set(mapper_package_assistant ${mapper_package_default}) +else() + set(mapper_package_assistant OFF) +endif() +option(Mapper_PACKAGE_PROJ "Include all required Proj components in the packages" ${mapper_package_default}) +option(Mapper_PACKAGE_GDAL "Include all required GDAL components in the packages" ${mapper_package_gdal}) +option(Mapper_PACKAGE_QT "Include all required Qt components in the packages" ${mapper_package_default}) +option(Mapper_PACKAGE_ASSISTANT "Include Qt Assistant in the packages" ${mapper_package_assistant}) +mark_as_advanced(Mapper_PACKAGE_PROJ Mapper_PACKAGE_GDAL Mapper_PACKAGE_QT Mapper_PACKAGE_ASSISTANT) + + +# Migration of legacy configurations (before 0.7, affects developers) + +if(Mapper_BUILD_DOXYGEN) + unset(DOXYGEN_EXECUTABLE CACHE) +endif() +unset(Mapper_BUILD_DOXYGEN CACHE) +unset(Mapper_BUILD_GDAL CACHE) +unset(Mapper_BUILD_PROJ CACHE) +unset(Mapper_BUILD_QT CACHE) + + +# Installation configuration + +set(Mapper_PACKAGE_NAME "openorienteering-mapper" CACHE STRING + "The package name" +) + +# These value are used for the DESTINATION parameter of the install command +# and must not be empty. +if(WIN32 AND BIN_INSTALL_DIR) + set(MAPPER_RUNTIME_DESTINATION "${BIN_INSTALL_DIR}") + set(MAPPER_LIBRARY_DESTINATION "${LIB_INSTALL_DIR}") + set(MAPPER_DATA_DESTINATION "${SHARE_INSTALL_DIR}/${Mapper_PACKAGE_NAME}") + set(MAPPER_ABOUT_DESTINATION "${SHARE_INSTALL_DIR}/doc/${Mapper_PACKAGE_NAME}") +elseif(WIN32) + set(MAPPER_RUNTIME_DESTINATION .) + set(MAPPER_LIBRARY_DESTINATION .) + set(MAPPER_DATA_DESTINATION .) + set(MAPPER_ABOUT_DESTINATION "doc") +elseif(APPLE) + set(MAPPER_RUNTIME_DESTINATION .) + set(MAPPER_LIBRARY_DESTINATION .) + set(MAPPER_DATA_DESTINATION "Mapper.app/Contents/Resources") + set(MAPPER_ABOUT_DESTINATION "Mapper.app/Contents/Resources/doc") +elseif(ANDROID) + set(MAPPER_RUNTIME_DESTINATION "libs/${CMAKE_ANDROID_ARCH_ABI}") + set(MAPPER_LIBRARY_DESTINATION "libs/${CMAKE_ANDROID_ARCH_ABI}") + set(MAPPER_DATA_DESTINATION "assets") + set(MAPPER_ABOUT_DESTINATION "assets/doc") +else() # LINUX and alike + set(MAPPER_RUNTIME_DESTINATION "bin") + set(MAPPER_LIBRARY_DESTINATION "lib/${Mapper_PACKAGE_NAME}") + set(MAPPER_DATA_DESTINATION "share/${Mapper_PACKAGE_NAME}") + set(MAPPER_ABOUT_DESTINATION "share/doc/${Mapper_PACKAGE_NAME}") +endif() + +if(CMAKE_CROSSCOMPILING) + message(STATUS "Crosscompiling, host: ${CMAKE_HOST_SYSTEM_NAME}, target: ${CMAKE_SYSTEM_NAME}") +endif() + + +# Build definitons + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) + +if(APPLE AND Mapper_MACOSX_VERSION) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -mmacosx-version-min=${Mapper_MACOSX_VERSION}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${Mapper_MACOSX_VERSION}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -mmacosx-version-min=${Mapper_MACOSX_VERSION}") +endif() + +if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Wextra") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wpedantic") +endif() + +add_custom_target(Mapper_prerequisites + SOURCES # Extra files to be shown in IDE + INSTALL.md + README.md +) +set(Mapper_prerequisites_FOUND TRUE) + +if(Mapper_BUILD_CLIPPER) + add_subdirectory(3rd-party/clipper) + add_feature_info(Mapper_BUILD_CLIPPER 1 "version: ${CLIPPER_VERSION}") + add_library(Polyclipping::Polyclipping ALIAS polyclipping) +else() + find_package(Polyclipping 6.1.3 MODULE REQUIRED) +endif() + +find_package(PROJ4 CONFIG QUIET) +if(NOT TARGET PROJ4::proj) + set(PROJ4_FOUND false) + find_package(PROJ4 MODULE REQUIRED) +endif() + +if(Mapper_USE_GDAL) + find_package(GDAL REQUIRED) +endif() + + +include("TestBigEndian") +test_big_endian(big_endian) +if(big_endian) + add_definitions(-DMAPPER_BIG_ENDIAN) +endif() + + +if(ANDROID OR big_endian) + add_definitions(-DNO_NATIVE_FILE_FORMAT) +endif() + +if(UNIX AND NOT APPLE) +# set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib/${Mapper_PACKAGE_NAME}/lib") + set(CMAKE_INSTALL_RPATH "${MAPPER_LIBRARY_DESTINATION}/lib") +endif() + +add_definitions(-D_USE_MATH_DEFINES -DUNICODE) + +if(Mapper_DEVELOPMENT_BUILD) + add_definitions(-DMAPPER_DEVELOPMENT_BUILD) + include(EnableSanitize) + enable_sanitize(address undefined) + configure_file(suppress.txt.in suppress.txt COPYONLY) +else() + add_definitions(-DQT_NO_DEBUG -DQT_NO_DEBUG_OUTPUT -DQT_NO_WARNING_OUTPUT -DNDEBUG) +endif() + +# Subdirectories + +add_subdirectory("doc/manual") +add_subdirectory("examples") +add_subdirectory("symbol sets") +add_subdirectory("translations") +add_subdirectory("3rd-party/qbezier") +if(NOT ANDROID) + add_subdirectory("3rd-party/qtsingleapplication") +endif() +if (Mapper_USE_GDAL) + add_subdirectory("src/gdal") +endif() +add_subdirectory("src/libocad") +if(NOT ANDROID) + add_subdirectory("src/printsupport") +endif() +add_subdirectory("src") +add_subdirectory("packaging") +add_subdirectory("doc/licensing") + +if(CMAKE_CROSSCOMPILING) + add_custom_target(TEST_WARNING ALL + COMMENT "Crosscompiling, skipping all tests") + add_dependencies(TEST_WARNING Mapper) +else() + enable_testing() + add_subdirectory("test") +endif() + +add_subdirectory("doc/api") +add_subdirectory("packaging/src") + +feature_summary(WHAT ALL) + +string(TOUPPER "${CMAKE_BUILD_TYPE}" type) +foreach(lang CXX C) + foreach(i "" _${type}) + message(STATUS "CMAKE_${lang}_FLAGS${i}: ${CMAKE_${lang}_FLAGS${i}}") + endforeach() +endforeach() diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..6626111 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,159 @@ +## General + +This document is about building OpenOrienteering Mapper from source code. + +The general build process prerequisites are: + - A supported platform: + - Linux. Ubuntu 16.04 is known to work. + Linux is also used to cross-compile for Windows and Android. + - OS X Yosemite (10.10). OS X Mountain Lion (10.8) or newer may work. + (Qt supports Mac OS X Lion (10.7) for deployment only until Qt 5.6.) + - CMake >= 3.1. + CMake is available from https://cmake.org/. + - A supported C++ compiler toolchain. C++14 is mandatory. + +Mapper has a number of direct and indirect dependencies on third-party +components. Direct dependencies are: + - Qt >=5.3 + https://www.qt.io/download-open-source/ + - Clipper library (aka libpolyclipping) >= 6.1.3.a + http://www.angusj.com/delphi/clipper.php + - PROJ.4 Cartographic Projections Library >= 4.8 + https://proj4.org/ + - GDAL Geospatial Data Abstraction Library + https://www.gdal.org/ + - ZLib Compression Library + https://zlib.net/ + +When building for Linux, you may use the distributions' packages. +However, openSUSE is known to lack the Clipper library. + +For target systems other than desktop Linux, the recommended way to deal +with the dependencies is to use the OpenOrienteering superbuild project +(https://github.com/OpenOrienteering/superbuild). See below for details. + +The recommented integrated development environment (IDE) is Qt Creator +which is available from https://www.qt.io/download-open-source/. + + +## Getting the Source + +Download a zip or tar.gz source code archive from + +https://github.com/OpenOrienteering/mapper/releases + +and unpack it, or checkout the source code with git: + + +``` +git clone https://github.com/OpenOrienteering/mapper.git +``` + + +## Compiling for Linux (without OpenOrienteering superbuild) + +The standard g++ (>= 4.9) compiler from a recent distribution should work. Make +sure that the required development and tool packages are installed. For a Ubuntu +or Debian system, install: +``` +cmake \ +doxygen \ +libcups2-dev \ +libgdal-dev \ +libpolyclipping-dev \ +libproj-dev \ +qt5-default \ +qtbase5-dev qtbase5-private-dev qtbase5-dev-tools \ +qttools5-dev qttools5-dev-tools libqt5sql5-sqlite \ +zlib1g-dev +``` + +When not using Qt Creator, open a terminal, and create a build directory, e.g. +as subdirectory build in the source directory, and change to that directory. +From the build directory, configure and build like this: + +``` +cmake PATH/TO/SOURCE_DIR +``` + +When building on openSUSE, you may want to add -DMapper_BUILD_CLIPPER=1. This +will make the build download and build the Clipper library (libpolyclipping) +which is not (yet) provided by this distribution. + +Now you may start the build process by running + +``` +make +``` + + +## Compiling with OpenOrienteering superbuild + +The OpenOrienteering superbuild project +(https://github.com/OpenOrienteering/superbuild) +takes care of downloading toolchains and sources, unpacking and patching +sources, and building the binaries with respect to all known dependencies, +using parallel jobs as much as possible. Superbuild will even create packages +for Mapper when you build an openorienteering-mapper-...-package target. + +If you want to do development on the Mapper project for macOS, Windows, or +Android, you can use the results (install directory, toolchain directory) from +the superbuild for building the Mapper CMake project, for example by using +the toolchain file from the superbuild directory, or by setting +CMAKE_PREFIX_PATH to point to the superbuild installation directory. + +For convenient development in Qt Creator, it is possible to create a Kit which +uses the corresponding toolchain and installation directories from the +superbuild. When building Mapper with this Kit, it will find all dependencies +it needs. + +For setting up Kits, see the Qt Creator documentation: + +https://doc.qt.io/qtcreator/creator-targets.html + +Starting with Qt Creator 4.3, it might become possible to simply open the +superbuild's openorienteering-mapper build directory as a regular project +with no further toolchain configuration. + + +## Cross-Compiling on Linux for Android + +In addition to the general build process prerequisites, you need: + - CMake >= 3.7 + - the Android SDK + - the Android NDK +The OpenOrienteering superbuild project will download and install this software +when it creates an android toolchain. + +Qt 5.6 requires at least API Level 9 to work. Apps built for the armeabi-v7a +ABI require at least an emulator image of API level 14. + +The build of OpenOrienteering Mapper for Android is done by CMake, too. +However, the cmake-generated build creates a qmake project in +BUILD_DIR/packaging/Mapper. While the cmake-generated build only used the +deployment settings generated by qmake for this project, you this project is +suitable for convenient deplyoing and debugging Mapper in Qt Creator. + +Note that release APKs need to be signed, and the signing key cannot change for +replacing an installed app without loosing the data (maps) stored for this app. +To facilitate development, debug builds of Mapper use a different namespace and +name. + + +## Binary Packages and Distribution + +Even under open source licenses, distributing and/or using code in source or +binary form creates certain legal obligations, such as the distribution of the +corresponding source code and build instructions for GPL licensed binaries, +and displaying copyright statements and disclaimers. + +For OpenOrienteering Mapper, this is solved by either using (not distributing) +the Linux distributors' build systems and packages, or by packaging with +OpenOrienteering superbuild. OpenOrienteering superbuild collects all +third-party downloads, patches and control scripts, so that they can be made +available together with the release binaries. + +Packages for macOS and Windows are built using CPack which comes with CMake. +Android APKs are build in the same way, although not using a CPack generator. +These packages bundle all 3rd-party components (Qt binaries and translations, +PROJ.4 and GDAL binaries and data, etc.). diff --git a/README.md b/README.md new file mode 100644 index 0000000..c04412f --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# OpenOrienteering Mapper + +![Mapper Screenshot](https://www.openorienteering.org/mapper-manual/pages/images/main_window.png) + +OpenOrienteering Mapper is an orienteering mapmaking program and provides +a free and open source alternative to existing commercial software. +OpenOrienteering Mapper runs on Android, Windows, Mac OS X and Linux. + + - [Mapper Homepage](https://www.openorienteering.org/apps/mapper/) + - [Manual](https://www.openorienteering.org/mapper-manual/) + - [Downloads](https://github.com/OpenOrienteering/mapper/releases) + - [OpenOrienteering Blog](https://www.openorienteering.org/) + + +## Reporting Issues and Asking for Help + +Issues and possible improvements can be posted to our public [Ticket system](https://github.com/OpenOrienteering/mapper/issues). +Please make sure you provide all relevant information about your problem or idea. + + +## Contributing + +### Translating + +Translations can be edited online on [Weblate](https://hosted.weblate.org/projects/openorienteering/mapper/). You can register/login with your Github account. Find out more about translation in our [wiki](https://github.com/OpenOrienteering/mapper/wiki/Translation). + + +### Writing Documentation + +The Mapper manual lives in its [own repository](https://github.com/OpenOrienteering/mapper-manual) +which contains all information for you to get started. + + +### Writing Code + +For building Mapper from source see [`INSTALL.md`](https://github.com/OpenOrienteering/mapper/blob/master/INSTALL.md). +Pull requests are very welcome. + + - [Issue tracker](https://github.com/OpenOrienteering/mapper/issues) + - [API documentation](https://www.openorienteering.org/api-docs/mapper/) + - [Developer wiki](https://github.com/OpenOrienteering/mapper/wiki) + + +## License + +Mapper is licensed under the [GNU GENERAL PUBLIC LICENSE Version 3](https://www.gnu.org/licenses/gpl.html). diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 0000000..0ea7d5e --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/CPackConfig.cmake.in b/android/CPackConfig.cmake.in new file mode 100644 index 0000000..4229a18 --- /dev/null +++ b/android/CPackConfig.cmake.in @@ -0,0 +1,112 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +execute_process(COMMAND "${CMAKE_COMMAND}" -E echo "Creating APK package") + +# For Android, we don't use a CPack generator, +# but run androiddeployqt from this file. +set(CPACK_GENERATOR "") + +set(destdir "@CMAKE_CURRENT_BINARY_DIR@/Mapper/install") +set(KEYSTORE_URL "@KEYSTORE_URL@") +set(KEYSTORE_ALIAS "@KEYSTORE_ALIAS@") + +if(EXISTS "${destdir}") + FILE(REMOVE_RECURSE "${destdir}") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" + "-DCMAKE_INSTALL_PREFIX=${destdir}" + "-DCMAKE_INSTALL_DO_STRIP=1" + -P cmake_install.cmake + WORKING_DIRECTORY "@PROJECT_BINARY_DIR@" + RESULT_VARIABLE result +) +if(result) + message(FATAL_ERROR "Installation failed: ${result}") +endif() + +# Create an androiddeployqt configuration file via qmake project. +execute_process( + COMMAND "$" + WORKING_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@/Mapper" + RESULT_VARIABLE result +) +if(result) + message(FATAL_ERROR "Creating APK configuration failed: ${result}") +endif() + +# Copy the Mapper runtime from the CMake project to the qmake project. +file(INSTALL + DESTINATION "@CMAKE_CURRENT_BINARY_DIR@/Mapper" + FILES "$" +) + +set(sign ) +set(result ) +set(final_message ) +$<$,$>,$,$>: +if("@CMAKE_HOST_UNIX@") + execute_process(COMMAND "${CMAKE_COMMAND}" -E echo "Checking if we are running in a terminal") + execute_process(COMMAND tty RESULT_VARIABLE result) + if(result) + message(SEND_ERROR "Not running in a terminal, signing disabled.") + else() + set(sign --sign "${KEYSTORE_URL}" "${KEYSTORE_ALIAS}") + endif() +endif() +if(NOT "@CMAKE_HOST_UNIX@" OR result) + set(final_message "The build created an unsigned APK. To sign the APK, run: + jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore '${KEYSTORE_URL}' /path/to/install-release-unsigned.apk ${KEYSTORE_ALIAS} + zipalign 4 /path/to/install-release-unsigned.apk outfile.apk" + ) +endif() +> + +execute_process( + COMMAND androiddeployqt + --output "${destdir}" + --deployment "bundled" + --gradle + --verbose +$<$,$>: + --release + $<$,$>: + ${sign} + > +> + WORKING_DIRECTORY "@CMAKE_CURRENT_BINARY_DIR@/Mapper" + RESULT_VARIABLE result +) +if(result) + message(FATAL_ERROR "Running androiddeployqt failed: ${result}") +endif() +$<$,$>,$,$>: +configure_file( + "@CMAKE_CURRENT_BINARY_DIR@/Mapper/install/build/outputs/apk/install-release-signed.apk" + "@PROJECT_BINARY_DIR@/@CPACK_PACKAGE_FILE_NAME@.apk" + COPYONLY +) +> + +if(final_message) + message(STATUS "${final_message}") +endif() + diff --git a/android/Mapper.pro.in b/android/Mapper.pro.in new file mode 100644 index 0000000..d7aca3d --- /dev/null +++ b/android/Mapper.pro.in @@ -0,0 +1,62 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +# This qmake project file is to create a configuration for androiddeployqt. + +!android:error("This project is for Android only.") + +TEMPLATE = app +TARGET = Mapper + +ANDROID_PACKAGE_SOURCE_DIR = "@PROJECT_SOURCE_DIR@/android" + +EXPECTED_VERSION = @Mapper_VERSION_MAJOR@\.@Mapper_VERSION_MINOR@\.@Mapper_VERSION_PATCH@ +!system(grep "$$EXPECTED_VERSION" "$$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml") { + error(The version name in AndroidManifest.xml does not match $$EXPECTED_VERSION.) +} + +CONFIG(debug, release|debug) { + # Don't let (unsigned) debug packages replace (signed) release packages, + # or the developer may loose maps. + system(cp -a "$$ANDROID_PACKAGE_SOURCE_DIR" "$$OUT_PWD/") + ANDROID_PACKAGE_SOURCE_DIR = $$OUT_PWD/android + system(sed -i -e $$shell_quote(//%%INSERT_APP_NAME%% --> dev/) "$$ANDROID_PACKAGE_SOURCE_DIR/res/values/strings.xml") +} + + +versionAtLeast(QT_VERSION, 5.12) { + system(sed -i -e $$shell_quote(s/minSdkVersion=\"[0-9]*\"/minSdkVersion=\"16\"/) "$$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml") +} + + +# Additional configuration from packaging/custom_install.cmake +include(mapper_libs.pri) + + +# For the generated .pro file to be used in Qt Creator, +# we must install everything and replace the dummy libMapper.so. +INSTALLS += everything + +everything.path = / +everything.extra = \ + "@CMAKE_COMMAND@" --build "@PROJECT_BINARY_DIR@" && \ + "@CMAKE_COMMAND@" -E copy "$" "$$OUT_PWD/" && \ + "@CMAKE_COMMAND@" -E chdir "@PROJECT_BINARY_DIR@" \ + "@CMAKE_COMMAND@" "-DCMAKE_INSTALL_PREFIX=$(INSTALL_ROOT)" -P cmake_install.cmake diff --git a/android/res/drawable-hdpi/icon.png b/android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ed28510966760e2f541878604462bbc38ca72cdd GIT binary patch literal 11851 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 FV1i1aOl|-G literal 0 HcmV?d00001 diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..83b482a6454dd82e2212a4d0b0ceae8a0155aecd GIT binary patch literal 3896 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 literal 0 HcmV?d00001 diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml new file mode 100644 index 0000000..af51c8d --- /dev/null +++ b/android/res/values/strings.xml @@ -0,0 +1,5 @@ + + + + OpenOrienteering + diff --git a/android/src/org/openorienteering/mapper/MapperActivity.java b/android/src/org/openorienteering/mapper/MapperActivity.java new file mode 100644 index 0000000..410b223 --- /dev/null +++ b/android/src/org/openorienteering/mapper/MapperActivity.java @@ -0,0 +1,252 @@ +/* + * Copyright 2013 Thomas Schöps + * Copyright 2014 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +package org.openorienteering.mapper; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.location.GpsStatus; +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.location.LocationListener; +import android.os.Build; +import android.os.Bundle; +import android.os.CountDownTimer; +import android.os.Looper; +import android.os.SystemClock; +import android.provider.Settings; +import android.view.Gravity; +import android.view.Surface; +import android.widget.Toast; + + +/** + * Contains Android Java code for Mapper. + */ +public class MapperActivity extends org.qtproject.qt5.android.bindings.QtActivity +{ + private static MapperActivity instance; + + private String yes_string; + private String no_string; + private String gps_disabled_string; + + private static Toast toast; + private static CountDownTimer toast_reset; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + instance = this; + } + + /** Call setIntent, as recommended for singleTask launch mode. */ + @Override + public void onNewIntent(Intent intent) + { + setIntent(intent); + } + + /** Returns the data string from the intent, and resets the intent. */ + public String takeIntentPath() + { + String result = ""; + Intent intent = getIntent(); + if (intent != null) + { + String action = intent.getAction(); + if (action == Intent.ACTION_EDIT || action == Intent.ACTION_VIEW) + { + result = intent.getDataString(); + } + setIntent(null); + } + return result; + } + + // Static methods to be called from C++ + + /** Checks if GPS is enabled in the Android settings and if not, prompts the user to enable it. + * The dialog box works asynchronously, so the method cannot return the result. */ + static void checkGPSEnabled() + { + LocationManager locationManager = (LocationManager) instance.getSystemService(LOCATION_SERVICE); + boolean enabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + if (!enabled) + { + instance.runOnUiThread(new Runnable() { + public void run() { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which){ + case DialogInterface.BUTTON_POSITIVE: + Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); + instance.startActivity(intent); + break; + + case DialogInterface.BUTTON_NEGATIVE: + //No button clicked + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(instance); + builder.setMessage(instance.gps_disabled_string) + .setPositiveButton(instance.yes_string, dialogClickListener) + .setNegativeButton(instance.no_string, dialogClickListener) + .show(); + } + }); + } + } + + public static void setTranslatableStrings(String yes_string, String no_string, String gps_disabled_string) + { + instance.yes_string = yes_string; + instance.no_string = no_string; + instance.gps_disabled_string = gps_disabled_string; + } + + public static void showToast(final String message, final int duration) + { + instance.runOnUiThread(new Runnable() { + public void run() { + if (toast_reset != null) + toast_reset.cancel(); + + if (toast == null) + { + toast = Toast.makeText(instance, "", Toast.LENGTH_SHORT); + toast.setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 4); + } + + toast.setText(message); + toast.show(); + + if (duration <= 0) + return; + + toast_reset = new CountDownTimer(duration, 500) { + public void onTick(long millisUntilFinished) + { + toast.show(); + } + + public void onFinish() { + toast.cancel(); + } + }; + toast_reset.start(); + } + } ); + } + + public static void hideToast() + { + instance.runOnUiThread(new Runnable() { + public void run() { + if (toast_reset != null) + toast_reset.cancel(); + if (toast != null) + toast.cancel(); + } + } ); + } + + /** Locks the current display orientation. + * While a native "locked" mode comes in API level 18, + * this method tries to determine and lock the current orientation + * even on devices with lower API level. On these devices, the screen + * may be temporary in reverse orientation. + */ + public static void lockOrientation() + { + // ActivityInfo.SCREEN_ORIENTATION_LOCKED == 14 comes with API level 18 + if (Build.VERSION.SDK_INT >= 18) + { + instance.setRequestedOrientation(14); + return; + } + + int orientation = instance.getResources().getConfiguration().orientation; + int rotation = instance.getWindowManager().getDefaultDisplay().getRotation(); + + if (orientation == Configuration.ORIENTATION_PORTRAIT) + { + if (rotation == Surface.ROTATION_180) + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); + else + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + else if (orientation == Configuration.ORIENTATION_LANDSCAPE) + { + if (rotation == Surface.ROTATION_180) + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + else + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + + // If we read another value now, then we must reverse the rotation. + // Maybe this can occasionally return the old value (i.e. the value + // before the requested rotation takes effect). + int new_rotation = instance.getWindowManager().getDefaultDisplay().getRotation(); + if (new_rotation != rotation) + { + // first try didn't lock the original rotation, retry reverse. + if (orientation == Configuration.ORIENTATION_PORTRAIT) + { + if (new_rotation == Surface.ROTATION_180) + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + else + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); + } + else if (orientation == Configuration.ORIENTATION_LANDSCAPE) + { + if (new_rotation == Surface.ROTATION_180) + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + else + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + } + } + } + + /** Unlocks the display orientation + * by setting the requested orientation to unspecified. + */ + public static void unlockOrientation() + { + instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + } + + /** Returns the display's current rotation. + */ + public static int getDisplayRotation() + { + return instance.getWindowManager().getDefaultDisplay().getRotation(); + } +} diff --git a/cmake/EnableSanitize.cmake b/cmake/EnableSanitize.cmake new file mode 100644 index 0000000..ab2c127 --- /dev/null +++ b/cmake/EnableSanitize.cmake @@ -0,0 +1,44 @@ +# +# Copyright 2016 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +if(NOT COMMAND check_cxx_compiler_flag) + include(CheckCXXCompilerFlag) +endif() + +macro(enable_sanitize) + foreach(option ${ARGV}) + if(CMAKE_CROSSCOMPILING) + break() # out of macro + endif() + if(option STREQUAL "NO_RECOVER") + set(full_option "-fno-sanitize-recover=all") + else() + set(full_option "-fsanitize=${option}") + endif() + if(NOT CMAKE_CXX_FLAGS MATCHES "${full_option}") + string(MAKE_C_IDENTIFIER ${option} option_id) + check_cxx_compiler_flag("${full_option}" SANITIZE_${option_id}) + if(SANITIZE_${option_id}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${full_option}") + endif() + endif() + endforeach() + +endmacro() + diff --git a/cmake/FindPROJ4.cmake b/cmake/FindPROJ4.cmake new file mode 100644 index 0000000..093deaa --- /dev/null +++ b/cmake/FindPROJ4.cmake @@ -0,0 +1,152 @@ +#.rst: +# FindPROJ4 +# -------- +# +# Find the proj includes and library. +# +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines :prop_tgt:`IMPORTED` target ``PROJ4::proj``, +# if Proj.4 has been found. +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables: +# +# :: +# +# PROJ4_INCLUDE_DIRS - where to find proj_api.h, etc. +# PROJ4_LIBRARIES - List of libraries when using libproj. +# PROJ4_FOUND - True if libproj found. +# +# :: +# +# PROJ4_VERSION - The version of libproj found (x.y.z) +# PROJ4_VERSION_MAJOR - The major version of libproj +# PROJ4_VERSION_MINOR - The minor version of libproj +# PROJ4_VERSION_PATCH - The patch version of libproj +# PROJ4_VERSION_TWEAK - always 0 +# PROJ4_VERSION_COUNT - The number of version components, always 3 +# +# Hints +# ^^^^^ +# +# A user may set ``PROJ4_ROOT`` to a libproj installation root to tell this +# module where to look exclusively. + +#============================================================================= +# Copyright 2016 Kai Pastor +# +# +# This file was derived from CMake 3.5's module FindZLIB.cmake +# which has the following terms: +# +# Copyright 2001-2011 Kitware, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * The names of Kitware, Inc., the Insight Consortium, or the names of +# any consortium members, or of any contributors, may not be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Search PROJ4_ROOT exclusively if it is set. +if(PROJ4_ROOT) + set(_PROJ4_SEARCH PATHS ${PROJ4_ROOT} NO_DEFAULT_PATH) +else() + set(_PROJ4_SEARCH) +endif() + +find_path(PROJ4_INCLUDE_DIR NAMES proj_api.h ${_PROJ4_SEARCH} PATH_SUFFIXES include) +mark_as_advanced(PROJ4_INCLUDE_DIR) + +if(PROJ4_INCLUDE_DIR AND EXISTS "${PROJ4_INCLUDE_DIR}/proj_api.h") + file(STRINGS "${PROJ4_INCLUDE_DIR}/proj_api.h" PROJ4_H REGEX "^#define PJ_VERSION [0-9]+$") + + string(REGEX REPLACE "^.*PJ_VERSION ([0-9]).*$" "\\1" PROJ4_VERSION_MAJOR "${PROJ4_H}") + string(REGEX REPLACE "^.*PJ_VERSION [0-9]([0-9]).*$" "\\1" PROJ4_VERSION_MINOR "${PROJ4_H}") + string(REGEX REPLACE "^.*PJ_VERSION [0-9][0-9]([0-9]).*$" "\\1" PROJ4_VERSION_PATCH "${PROJ4_H}") + set(PROJ4_VERSION "${PROJ4_VERSION_MAJOR}.${PROJ4_VERSION_MINOR}.${PROJ4_VERSION_PATCH}") + set(PROJ4_VERSION_COUNT 3) +endif() + +# Allow PROJ4_LIBRARY to be set manually, as the location of the proj library +if(NOT PROJ4_LIBRARY) + set(PROJ4_NAMES proj) + set(PROJ4_NAMES_DEBUG projd) + if(WIN32 AND DEFINED PROJ4_VERSION_MAJOR AND DEFINED PROJ4_VERSION_MINOR) + list(APPEND PROJ4_NAMES proj_${PROJ4_VERSION_MAJOR}_${PROJ4_VERSION_MINOR}) + list(APPEND PROJ4_NAMES projd_${PROJ4_VERSION_MAJOR}_${PROJ4_VERSION_MINOR}) + endif() + find_library(PROJ4_LIBRARY_RELEASE NAMES ${PROJ4_NAMES} ${_PROJ4_SEARCH} PATH_SUFFIXES lib) + find_library(PROJ4_LIBRARY_DEBUG NAMES ${PROJ4_NAMES_DEBUG} ${_PROJ4_SEARCH} PATH_SUFFIXES lib) + include(SelectLibraryConfigurations) + select_library_configurations(PROJ4) +endif() + +# handle the QUIETLY and REQUIRED arguments and set PROJ4_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PROJ4 + REQUIRED_VARS + PROJ4_LIBRARY + PROJ4_INCLUDE_DIR + VERSION_VAR + PROJ4_VERSION +) + +if(PROJ4_FOUND) + set(PROJ4_INCLUDE_DIRS ${PROJ4_INCLUDE_DIR}) + + if(NOT PROJ4_LIBRARIES) + set(PROJ4_LIBRARIES ${PROJ4_LIBRARY}) + endif() + + if(NOT TARGET PROJ4::proj) + add_library(PROJ4::proj UNKNOWN IMPORTED) + set_target_properties(PROJ4::proj PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${PROJ4_INCLUDE_DIRS}") + + if(PROJ4_LIBRARY_RELEASE) + set_property(TARGET PROJ4::proj APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(PROJ4::proj PROPERTIES + IMPORTED_LOCATION_RELEASE "${PROJ4_LIBRARY_RELEASE}") + endif() + + if(PROJ4_LIBRARY_DEBUG) + set_property(TARGET PROJ4::proj APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(PROJ4::proj PROPERTIES + IMPORTED_LOCATION_DEBUG "${PROJ4_LIBRARY_DEBUG}") + endif() + + if(NOT PROJ4_LIBRARY_RELEASE AND NOT PROJ4_LIBRARY_DEBUG) + set_property(TARGET PROJ4::proj APPEND PROPERTY + IMPORTED_LOCATION "${PROJ4_LIBRARY}") + endif() + endif() +endif() diff --git a/cmake/FindPolyclipping.cmake b/cmake/FindPolyclipping.cmake new file mode 100644 index 0000000..ec64ff9 --- /dev/null +++ b/cmake/FindPolyclipping.cmake @@ -0,0 +1,156 @@ +#.rst: +# FindPolyclipping +# -------- +# +# Find the polyclipping includes and library. +# +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines :prop_tgt:`IMPORTED` target ``Polyclipping::Polyclipping``, +# if Polyclipping has been found. +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables: +# +# :: +# +# POLYCLIPPING_INCLUDE_DIRS - where to find clipper.hpp, etc. +# POLYCLIPPING_LIBRARIES - List of libraries when using libpolyclipping. +# POLYCLIPPING_FOUND - True if libpolyclipping found. +# +# :: +# +# POLYCLIPPING_VERSION - The version of libpolyclipping found (x.y.z) +# POLYCLIPPING_VERSION_MAJOR - The major version of libpolyclipping +# POLYCLIPPING_VERSION_MINOR - The minor version of libpolyclipping +# POLYCLIPPING_VERSION_PATCH - The patch version of libpolyclipping +# POLYCLIPPING_VERSION_TWEAK - The tweak version of libpolyclipping +# POLYCLIPPING_VERSION_COUNT - The number of version components +# +# Hints +# ^^^^^ +# +# A user may set ``POLYCLIPPING_ROOT`` to a libpolyclipping installation root +# to tell this module where to look exclusively. + +#============================================================================= +# Copyright 2016 Kai Pastor +# +# +# This file was derived from CMake 3.5's module FindZLIB.cmake +# which has the following terms: +# +# Copyright 2001-2011 Kitware, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * The names of Kitware, Inc., the Insight Consortium, or the names of +# any consortium members, or of any contributors, may not be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Search POLYCLIPPING_ROOT exclusively if it is set. +if(POLYCLIPPING_ROOT) + set(_POLYCLIPPING_SEARCH PATHS ${POLYCLIPPING_ROOT} NO_DEFAULT_PATH) +else() + set(_POLYCLIPPING_SEARCH) +endif() + +find_path(POLYCLIPPING_INCLUDE_DIR NAMES clipper.hpp ${_POLYCLIPPING_SEARCH} PATH_SUFFIXES include include/polyclipping) +mark_as_advanced(POLYCLIPPING_INCLUDE_DIR) + +# Allow POLYCLIPPING_LIBRARY to be set manually, as the location of the polyclipping library +if(NOT POLYCLIPPING_LIBRARY) + set(POLYCLIPPING_NAMES polyclipping) + set(POLYCLIPPING_NAMES_DEBUG polyclippingd) + find_library(POLYCLIPPING_LIBRARY_RELEASE NAMES ${POLYCLIPPING_NAMES} ${_POLYCLIPPING_SEARCH} PATH_SUFFIXES lib) + find_library(POLYCLIPPING_LIBRARY_DEBUG NAMES ${POLYCLIPPING_NAMES_DEBUG} ${_POLYCLIPPING_SEARCH} PATH_SUFFIXES lib) + include(SelectLibraryConfigurations) + select_library_configurations(POLYCLIPPING) +endif() + +if(POLYCLIPPING_INCLUDE_DIR AND EXISTS "${POLYCLIPPING_INCLUDE_DIR}/clipper.hpp") + file(STRINGS "${POLYCLIPPING_INCLUDE_DIR}/clipper.hpp" POLYCLIPPING_H REGEX "^#define CLIPPER_VERSION \"[^\"]*\"$") + + string(REGEX REPLACE "^.*CLIPPER_VERSION \"([0-9]+).*$" "\\1" POLYCLIPPING_VERSION_MAJOR "${POLYCLIPPING_H}") + string(REGEX REPLACE "^.*CLIPPER_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" POLYCLIPPING_VERSION_MINOR "${POLYCLIPPING_H}") + string(REGEX REPLACE "^.*CLIPPER_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" POLYCLIPPING_VERSION_PATCH "${POLYCLIPPING_H}") + set(POLYCLIPPING_VERSION "${POLYCLIPPING_VERSION_MAJOR}.${POLYCLIPPING_VERSION_MINOR}.${POLYCLIPPING_VERSION_PATCH}") + set(POLYCLIPPING_VERSION_COUNT 3) + + # only append a TWEAK version if it exists: + set(POLYCLIPPING_VERSION_TWEAK 0) + if( "${POLYCLIPPING_H}" MATCHES "CLIPPER_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+)") + set(POLYCLIPPING_VERSION_TWEAK "${CMAKE_MATCH_1}") + set(POLYCLIPPING_VERSION "${POLYCLIPPING_VERSION}.${POLYCLIPPING_VERSION_TWEAK}") + set(POLYCLIPPING_VERSION_COUNT 4) + endif() +endif() + +# handle the QUIETLY and REQUIRED arguments and set POLYCLIPPING_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Polyclipping + REQUIRED_VARS + POLYCLIPPING_INCLUDE_DIR + POLYCLIPPING_LIBRARY + VERSION_VAR + POLYCLIPPING_VERSION +) + +if(POLYCLIPPING_FOUND) + set(POLYCLIPPING_INCLUDE_DIRS ${POLYCLIPPING_INCLUDE_DIR}) + + if(NOT POLYCLIPPING_LIBRARIES) + set(POLYCLIPPING_LIBRARIES ${POLYCLIPPING_LIBRARY}) + endif() + + if(NOT TARGET Polyclipping::Polyclipping) + add_library(Polyclipping::Polyclipping UNKNOWN IMPORTED) + set_target_properties(Polyclipping::Polyclipping PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${POLYCLIPPING_INCLUDE_DIRS}") + + if(POLYCLIPPING_LIBRARY_RELEASE) + set_property(TARGET Polyclipping::Polyclipping APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(Polyclipping::Polyclipping PROPERTIES + IMPORTED_LOCATION_RELEASE "${POLYCLIPPING_LIBRARY_RELEASE}") + endif() + + if(POLYCLIPPING_LIBRARY_DEBUG) + set_property(TARGET Polyclipping::Polyclipping APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(Polyclipping::Polyclipping PROPERTIES + IMPORTED_LOCATION_DEBUG "${POLYCLIPPING_LIBRARY_DEBUG}") + endif() + + if(NOT POLYCLIPPING_LIBRARY_RELEASE AND NOT POLYCLIPPING_LIBRARY_DEBUG) + set_property(TARGET Polyclipping::Polyclipping APPEND PROPERTY + IMPORTED_LOCATION "${POLYCLIPPING_LIBRARY}") + endif() + endif() +endif() diff --git a/code-check-wrapper.sh b/code-check-wrapper.sh new file mode 100755 index 0000000..675788e --- /dev/null +++ b/code-check-wrapper.sh @@ -0,0 +1,113 @@ +#!/bin/sh -e + +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +# This is a wrapper for code quality tools supported by CMake. +# +# It adds these benefits over direct use of the tools: +# +# - It provides a pattern for filenames on which the tools are to be +# applied. This limits the noise in relevant diagnostic output and +# cuts build times by skipping files which are not of interest. +# - It allows changing pattern and arguments without forcing a complete +# rebuild of all sources handled by this compiler (which is always +# triggered by changes to the CMake variables). +# +# To use this wrapper, set its full path as the program for each check, +# with the actual tool as the first argument, e.g. +# +# CMAKE_CXX_CLANG_TIDY="/path/to/code-check-wrapper.sh;clang-tidy" +# CMAKE_CXX_INCLUDE_WHAT_YOU_USE="/path/to/code-check-wrapper.sh;iwyu" +# +# Any other parameter is ignored. So modifying extra parameters can still +# be used to force a full re-run. + + +PROGRAM=$1 +shift + +ENABLE_CLANG_TIDY=true +ENABLE_IWYU=true + +PATTERN=" \ + combined_symbol.cpp \ + configure_grid_dialog.cpp \ + file_dialog.cpp \ + file_import_export.cpp \ + georeferencing_dialog.cpp \ + gdal_manager.cpp \ + gps_display.cpp \ + key_button_bar.cpp \ + /map.cpp \ + map_editor.cpp \ + map_find_feature.cpp \ + map_widget.cpp \ + object_mover.cpp \ + object_query.cpp \ + ogr_file_format.cpp \ + ogr_template.cpp \ + overriding_shortcut.cpp \ + print_widget.cpp \ + renderable.cpp \ + renderable_implementation.cpp \ + symbol_rule_set.cpp \ + tag_select_widget.cpp \ + template_image.cpp \ + template_tool \ + text_brwoser_dialog \ + undo_manager.cpp \ + /util.cpp \ + /util_gui.cpp \ + world_file.cpp \ + xml_file_format.cpp \ + \ + ocd \ + src/tools/ \ + settings \ + \ +" + +PATTERN=$(echo "${PATTERN}" | sed -e "s/^ *//;s/ *\$//;s/ */\\\\\\|/g") + +if echo "$@" | grep -q "${PATTERN}"; then + case "${PROGRAM}" in + *clang-tidy*) + if ${ENABLE_CLANG_TIDY}; then + "${PROGRAM}" \ + -checks=-*,modernize-*,cert-*,misc-*,readability-*,-cert-err58-cpp,-readability-implicit-bool-cast,-readability-braces-around-statements,-readability-else-after-return \ + "$@" \ + || exit 1 + fi + ;; + *iwyu*) + if ${ENABLE_IWYU}; then + "${PROGRAM}" \ + -Xiwyu --mapping_file=${0%/*}/iwyu-mapper.imp \ + -Xiwyu --check_also=*_p.h \ + -Xiwyu --max_line_length=160 \ + "$@" \ + || exit 1 + fi + ;; + *) + "${PROGRAM}" "$@" || exit 1 + esac +else + true; +fi diff --git a/doc/api/CMakeLists.txt b/doc/api/CMakeLists.txt new file mode 100644 index 0000000..661044a --- /dev/null +++ b/doc/api/CMakeLists.txt @@ -0,0 +1,96 @@ +# +# Copyright 2012-2015 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +message(STATUS "Configuring ${PROJECT_NAME} API documentation") + +set(API_SRCS + extra/code_overview.h + extra/mainpage.h +) + +set(TAG_FILES + qtcore.tags + qtgui.tags + qtwidgets.tags + qtnetwork.tags + qtprintsupport.tags + qtsensors.tags + qtpositioning.tags +) + +find_program(DOXYGEN_EXECUTABLE NAMES doxygen + DOC "The path of the doxygen executable") +mark_as_advanced(DOXYGEN_EXECUTABLE) + +find_program(GIT_EXECUTABLE NAMES git + DOC "The path of the git executable") +mark_as_advanced(GIT_EXECUTABLE) + +set(API_DOCS_REPOSITORY "https://github.com/OpenOrienteering/api-docs.git" CACHE STRING + "The git repository where to push the api docs (only used on initial clone)") +set(API_DOCS_BRANCH "gh-pages" CACHE STRING + "The git branch where to push the api docs") +set(API_DOCS_PATH "mapper" CACHE STRING + "The path where to push the api docs") + +if(DOXYGEN_EXECUTABLE MATCHES NOTFOUND) + message(STATUS "doxygen executable not found.") +else() + set(url http://doc.qt.io/qt-5/) + foreach(file ${TAG_FILES}) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${file}.cmake" + "file(DOWNLOAD \"${url}${file}\" \"${CMAKE_CURRENT_BINARY_DIR}/${file}\")\n") + add_custom_command(OUTPUT ${file} + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/${file}.cmake" + COMMENT "Downloading ${file} from ${url}") + endforeach() + + configure_file(api-docs.sh.in api-docs.sh @ONLY) + configure_file(Doxyfile.in Doxyfile @ONLY) + configure_file(versionfilter.sh.in versionfilter.sh @ONLY) + add_custom_target(api-docs + COMMAND "${CMAKE_CURRENT_BINARY_DIR}/api-docs.sh" "${DOXYGEN_EXECUTABLE}" + COMMENT "Generating API documentation" + DEPENDS ${TAG_FILES} + SOURCES ${API_SRCS} + api-docs.sh + ) +endif() + +if(GIT_EXECUTABLE MATCHES NOTFOUND) + message(STATUS "git executable not found.") +elseif(TARGET api-docs) + add_custom_target(api-docs-repository + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/api-docs-repository.sh" "${API_DOCS_REPOSITORY}" "${API_DOCS_BRANCH}" "${API_DOCS_PATH}" + COMMENT "Preparing API docs repository" + SOURCES api-docs-repository.sh + ) + + add_custom_target(api-docs-commit + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/api-docs-commit.sh" "${API_DOCS_PATH}" "${CMAKE_CURRENT_BINARY_DIR}" + COMMENT "Committing changed API docs" + SOURCES api-docs-commit.sh + ) + add_dependencies(api-docs-commit api-docs api-docs-repository) + + add_custom_target(api-docs-push + COMMAND git push + COMMENT "Pushing changed API docs to ${API_DOCS_REPOSITORY}" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/repository" + ) +endif() diff --git a/doc/api/Doxyfile.in b/doc/api/Doxyfile.in new file mode 100644 index 0000000..d6334ca --- /dev/null +++ b/doc/api/Doxyfile.in @@ -0,0 +1,53 @@ +# +# Copyright 2014, 2015, 2018 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +PROJECT_NAME = "@PROJECT_NAME@" +PROJECT_NUMBER = "@Mapper_VERSION@" +PROJECT_BRIEF = "API documentation" +PROJECT_LOGO = "@CMAKE_CURRENT_SOURCE_DIR@/../openorienteering.png" + +QUIET = YES + +INPUT = "@PROJECT_SOURCE_DIR@/src" +INPUT += "@PROJECT_SOURCE_DIR@/android/src" +INPUT += "@PROJECT_SOURCE_DIR@/test" +INPUT += "@PROJECT_SOURCE_DIR@/doc/api/extra" +RECURSIVE = YES +STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@" +FILE_VERSION_FILTER = "@CMAKE_CURRENT_BINARY_DIR@/versionfilter.sh" +SOURCE_BROWSER = NO +VERBATIM_HEADERS = NO + +INCLUDE_PATH += "@PROJECT_SOURCE_DIR@/src/printsupport/qt-5.5.1" +PREDEFINED = QT_PRINTSUPPORT_LIB QT_SENSORS_LIB QT_LOCATION_LIB +PREDEFINED += Qt5Core_VERSION=5.5 Qt5PrintSupport_VERSION=5.5 +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = YES +BUILTIN_STL_SUPPORT = YES +EXTRACT_ALL = YES +HAVE_DOT = YES +GENERATE_TAGFILE = oomapper.tags +TAGFILES += qtcore.tags=http://doc.qt.io/qt-5/ +TAGFILES += qtgui.tags=http://doc.qt.io/qt-5/ +TAGFILES += qtwidgets.tags=http://doc.qt.io/qt-5/ +TAGFILES += qtxml.tags=http://doc.qt.io/qt-5/ +TAGFILES += qtnetwork.tags=http://doc.qt.io/qt-5/ +TAGFILES += qtprintsupport.tags=http://doc.qt.io/qt-5/ + +GENERATE_LATEX = NO +HTML_TIMESTAMP = NO diff --git a/doc/api/api-docs-commit.sh b/doc/api/api-docs-commit.sh new file mode 100755 index 0000000..773a3d4 --- /dev/null +++ b/doc/api/api-docs-commit.sh @@ -0,0 +1,38 @@ +#!/bin/sh -e +# +# Copyright 2015 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +# Usage: api-docs-commit.sh PATH [PROJECT_BINARY_DIR] + +for I in "repository/${1:-.}"/*; do + if [ -e "$I" -a "${I##*/}" != "README.md" ]; then + rm -R "$I" + fi +done + +for I in html/*; do + if [ "${I##*.}" != "md5" -a "${I##*.}" != "map" ]; then + cp -R "$I" "repository/${1:-.}/" + fi +done + +cd repository +git add -A "${1:-.}" +git commit -m "Update${1:+ in $1}" + +echo "Now call 'cd \"${2:-[CURRENT_BINARY_DIR]}/repository\" && git commit' to publish the updated API docs." diff --git a/doc/api/api-docs-repository.sh b/doc/api/api-docs-repository.sh new file mode 100755 index 0000000..df0d613 --- /dev/null +++ b/doc/api/api-docs-repository.sh @@ -0,0 +1,36 @@ +#!/bin/sh -e +# +# Copyright 2015 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +# Usage: api-docs-repository.sh REPOSITORY BRANCH PATH + +if [ ! -f repository.txt -o "$(cat repository.txt)" != "$1" ]; then + if [ -d repository ]; then + rm -Rf repository + fi + echo "$1" > repository.txt +fi + +if [ ! -d repository ]; then + git clone --depth 5 "$1" -b "$2" repository + cd repository +else + cd repository + git checkout "$2" "${3:-.}" + git pull --rebase +fi diff --git a/doc/api/api-docs.sh.in b/doc/api/api-docs.sh.in new file mode 100755 index 0000000..2600b2b --- /dev/null +++ b/doc/api/api-docs.sh.in @@ -0,0 +1,39 @@ +#!/bin/sh -e +# +# Copyright 2015 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +# Usage: api-docs.sh DOXYGEN_EXECUTABLE + +SOURCE_DIR=$(echo "@PROJECT_SOURCE_DIR@/" | sed -e "s/\//\\\\\//g") +DOC_DIR=$(echo "@CMAKE_CURRENT_BINARY_DIR@/" | sed -e "s/\//\\\\\//g") + +test -d html || mkdir html + +for I in html/*; do + rm -Rf "$I" +done + +"$1" Doxyfile 2>&1 \ +| sed -e "s/${SOURCE_DIR}//g" > html/doxygen-warnings.txt + +sed -i -e " + s/\( ([^( ]* rev ......., [A-Za-z0-9 ]*)\)\(<\/a>\)/\2\1/ # Footer of class documentation + s/\([^( ]*\) rev \(.......\), /\2<\/tt><\/a> on / + s/\"${DOC_DIR}/\"/g + s/\"${SOURCE_DIR}/\"/g +" html/*.html diff --git a/doc/api/extra/code_overview.h b/doc/api/extra/code_overview.h new file mode 100644 index 0000000..80bd654 --- /dev/null +++ b/doc/api/extra/code_overview.h @@ -0,0 +1,111 @@ +/* + * Copyright 2012, 2013 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +/** + +\page code_overview Code overview + +\date 2013-08-31 +\author Kai Pastor +\author Thomas Schöps + +\todo Review and update the code overview. + +\section core Core + +Map is a (too) huge class which represents a complete map. It contains dynamic arrays of: + +- Colors (MapColor) +- Symbols (Symbol) +- Templates (Template) +- Parts (MapPart) which in turn contain objects (Object). + +It also keeps track of the currently selected map objects and contains an UndoManager object. + +Furthermore, it has a MapRenderables renderables whose purpose will be explained soon, +and contains the settings for the georeferencing and for printing / exporting the map. + +Most features of a map are represented by Object. All map objects have got +coordinates, which are stored as a dynamic array of MapCoord. +A PointObject has got a single coordinate. +A TextObject has got either one coordinate (representing a single anchor point) +or two coordinate (representing a text box). For a text box, the first +coordinate gives the center of the box, and the second one its width and height. +For a PathObject, the coordinates are interpreted as a path, +i.e. a line consisting of polygonal and/or cubic bezier curve segments. + +To define paths with different kind of segments, the MapCoord objects store +extra flags: + +- curve start: if set, the coordinate is the first of 4 which define a bezier curve. +- dash point: used for line symbols, can be equivalent to an OC*D dash point or corner point +- hole point: if set, a break in the path starts at this coordinate and ends at the next one. + This is different from OC*D's hole points for areas: in OC*D, the first point of a hole in an area + is marked as a hole point, in OO Mapper the last point of the previous area is marked as hole point. + This is done for consistency with hole points for paths, so there are no hacks needed + for combined objects with lines, areas and holes in it. +- close point: if a path is closed and thus the last coordinate is at the same position + as its first one, the last coordinate is marked as close point. + +For path objects, the coordinates are also processed into another form +which approximates the path using only straight line segments: a vector of PathCoord. +These coordinates also contain information about the cumulative path length, +the parameter value if generated from a bezier curve, and from which segment +of the original coordinates they were generated. + + +\section rendering Rendering + +The process to display map objects works like this: + +- An Object is created, set up and added to a layer of a map. +- object->update(true) can be called to force an update of the object, + otherwise it is updated the next time the map is drawn. +- This update (re)generates the ObjectRenderables, a set of Renderable which + belong to the object. Instances of Renderable are the individual elements + that make up the visualization of the object, such as dots in a dotted line. + The power of a single renderable depends on the actual subclass, + ranging from a simple dot to a line of text. + The constraint is that every renderable uses just one map color. +- The ObjectRenderables are inserted into the MapRenderables of the map, + where they are sorted by their color priority. +- MapRenderables::draw() goes through all Renderables in order, + thus painting them with the correct color priority. + + +\section gui GUI + +The topmost application windows are provided by the MainWindow class. +It is just responsible for the window itself; every MainWindow also has a +subclass of MainWindowController which is responsible for the content. +Currently, this can be the HomeScreenController or MapEditorController, +but in the future there could also be a controller for course setting, for example. + +The MapEditorController has as most important members + +- a Map object which contains colors, symbols, objects etc., +- a MapView object which defines the view properties (zoom level, position offset, rotation), +- a MapWidget responsible for drawing, +- a MapEditorTool current_tool, for example the path drawing tool, +- a MapEditorActivity editor_activity. Activities are used to display stuff + that is common to multiple tools. Example: the TemplateAdjustActivity displays + the pass point positions when adjusting a template. + + +**/ diff --git a/doc/api/extra/mainpage.h b/doc/api/extra/mainpage.h new file mode 100644 index 0000000..c85984e --- /dev/null +++ b/doc/api/extra/mainpage.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012 Thomas Schöps + * Copyright 2013-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +/** + +\mainpage OpenOrienteering Mapper Developer Documentation + + +\section pages Contents + +- \subpage code_overview +- Class index +- Warnings issued by doxygen + + +\section links Web Links + +- OpenOrienteering Mapper repository on GitHub +- OpenOrienteering Mapper issues +- OpenOrienteering Mapper developer wiki +- Qt 5 documentation +- CMake documentation +- Doxygen documentation + +
+ +Qt is a trademark of The Qt Company Ltd. + + **/ diff --git a/doc/api/versionfilter.sh.in b/doc/api/versionfilter.sh.in new file mode 100755 index 0000000..9c82041 --- /dev/null +++ b/doc/api/versionfilter.sh.in @@ -0,0 +1,36 @@ +#!/bin/sh -e +# +# Copyright 2015 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +FILE=$(echo "${1#@PROJECT_SOURCE_DIR@/}" | sed -e "s/\//\\\\\//g") + +if [ "$1" != "${1#@PROJECT_SOURCE_DIR@/}" ]; then +{ + cd $(dirname "$1") + { + git log -n 1 "${1##*/}" \ + || echo >&2 " at file $1" + } \ + | sed -e " + : start + N + /\\nDate: /! b start + s/commit \(.......\).*Date: *[a-zA-Z]* \([a-zA-Z]*\) \([0-9]*\) ..:..:.. \(20..\).*/$FILE rev \1, \3 \2 \4/ + q" +} +fi diff --git a/doc/coding-style.xml b/doc/coding-style.xml new file mode 100644 index 0000000..1171082 --- /dev/null +++ b/doc/coding-style.xml @@ -0,0 +1,39 @@ + + + + + + CodeStyleData + + true + false + false + true + false + true + false + false + true + false + true + false + true + true + false + true + false + true + false + 4 + true + false + 1 + false + 4 + + + + DisplayName + OpenOrienteering + + diff --git a/doc/licensing/CMakeLists.txt b/doc/licensing/CMakeLists.txt new file mode 100644 index 0000000..de3bff8 --- /dev/null +++ b/doc/licensing/CMakeLists.txt @@ -0,0 +1,241 @@ +# +# Copyright 2014-2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +message(STATUS "Configuring ${PROJECT_NAME} licensing documentation") + + +# Show all available providers in Qt Creator +set(known_providers ) +file(GLOB provider_files "${CMAKE_CURRENT_LIST_DIR}/*-licensing.cmake") +foreach(file ${provider_files}) + get_filename_component(name "${file}" NAME) + string(REPLACE "-licensing.cmake" "" provider "${name}") + list(APPEND known_providers "${provider}") + file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.stamp" + INPUT "${file}" + ) +endforeach() + +# Select default provider +if(ANDROID OR APPLE OR WIN32) + set(default_provider OFF) +elseif(UNIX) + execute_process( + COMMAND sh -c ". /etc/os-release; echo $ID" + OUTPUT_VARIABLE system + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(system) + set(default_provider "${system}") + else() + set(default_provider NOTFOUND) + endif() +endif() + +set(LICENSING_PROVIDER "${default_provider}" CACHE STRING + "Provider for 3rd-party licensing information (${known_providers};OFF)" +) + + +# Software and data used directly by Mapper +set(third_party_components + libpolyclipping + proj + qtbase + qtimageformats + qttranslations +) +if(TARGET Qt5::AndroidExtras) + list(APPEND third_party_components qtandroidextras) +endif() +if(TARGET Qt5::Positioning) + list(APPEND third_party_components qtlocation) +endif() +if(TARGET Qt5::Sensors) + list(APPEND third_party_components qtsensors) +endif() +if(TARGET Qt5::Serialport) + list(APPEND third_party_components qtserialport) +endif() +if(TARGET QtSingleApplication) + list(APPEND third_party_components qtsingleapplication) +endif() +if(TARGET mapper-gdal) + list(APPEND third_party_components gdal) +endif() +if(TARGET printsupport AND WIN32) + list(APPEND third_party_components zlib) +endif() +if(Mapper_PACKAGE_ASSISTANT) + list(APPEND third_party_components qttools) +endif() + + +# Licences used directly by Mapper +set(common_license_names + GPL-3 +) + +# Required, because linked from About dialog +set(explicit_copyright_GPL-3 + "GPL-3.txt" + "${PROJECT_SOURCE_DIR}/COPYING" + "common-licenses" +) +set(explicit_copyright_qtsingleapplication + "qtsingleapplication.txt" + "${PROJECT_SOURCE_DIR}/3rd-party/qtsingleapplication/copyright" + "3rd-party" +) +if(Mapper_BUILD_CLIPPER) + set(explicit_copyright_libpolyclipping + "libpolyclipping-${CLIPPER_VERSION}.txt" + "${PROJECT_SOURCE_DIR}/3rd-party/clipper/copyright" + "3rd-party" + ) +endif() + + +if(LICENSING_PROVIDER) + message(STATUS "Licensing information provided by: ${LICENSING_PROVIDER}") + set(third_party_copyright ) + set(common_licenses ) + include("${LICENSING_PROVIDER}-licensing.cmake") +else() + set(licensing_warning " +This build is a configuration without an external provider of third-party \ +licensing and copyright information. It may not fulfill requirements for \ +redistribution of third-party components." + ) + message(WARNING "${licensing_warning}") + set(third_party_copyright "
  • ${licensing_warning}
  • ") + set(common_licenses ) +endif() + + + +function(add_copyright var name path) + if(name AND path MATCHES "file:///") + string(REPLACE "file:///" "/" file "${path}") + if(NOT EXISTS "${file}") + message(FATAL_ERROR "No such license file: ${file}") + endif() + list(APPEND ${var} "
  • ${name}
  • ") + elseif(name AND path STREQUAL "-" AND ARGN) + list(APPEND ${var} "
  • ${name}: ${ARGN}
  • ") + elseif(name AND path STREQUAL "-") + list(APPEND ${var} "
  • ${name}
  • ") + elseif(name AND path AND ARGN) + set(target ${ARGN}) + string(REPLACE ".txt" "" basename "${name}") + list(APPEND ${var} "
  • ${basename}
  • ") + install(FILES "${path}" + DESTINATION "${MAPPER_ABOUT_DESTINATION}/${target}" + RENAME "${name}" + ) + # Generate output for manual tests from build dir. + # As a side effect, make source visible in Qt Creator. + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${target}") + file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${target}/${name}" + INPUT "${path}" + ) + elseif() + message(FATAL_ERROR "Cannot create link for ${name}, ${path}, ${target}") + endif() + set(${var} "${${var}}" PARENT_SCOPE) +endfunction() + + + +foreach(entry ${package_names}) + set(entry "explicit_package_${entry}") + string(REPLACE ":" ";" entry ${entry}) + set(${entry}) +endforeach() + +list(SORT third_party_components) +list(REMOVE_DUPLICATES third_party_components) +foreach(basename ${third_party_components}) + if(LICENSING_PROVIDER AND NOT DEFINED explicit_copyright_${basename}) + if(explicit_package_${basename}) + set(package ${explicit_package_${basename}}) + else() + set(package ${basename}) + endif() + # Exact match + string(CONFIGURE "${copyright_pattern}" pattern @ONLY) + file(GLOB path ${pattern}) + if(NOT path) + # Pattern match + string(REPLACE "/${package}/" "/${package}*/" pattern "${pattern}") + file(GLOB raw_path ${pattern}) + set(path ) + foreach(item ${raw_path}) + if(NOT item MATCHES "${package}.*-" + OR item MATCHES "${package}[0-9]*-[0-9]") + list(APPEND path ${item}) + endif() + endforeach() + endif() + if(NOT path) + message(SEND_ERROR "No license file for ${basename} (${pattern})") + elseif(path MATCHES ";") + message(SEND_ERROR "Multiple matches for ${basename}: ${path}") + elseif(system_copyright_dir AND path MATCHES "^${system_copyright_dir}/") + set(explicit_copyright_${basename} "${basename}" "file://${path}") + else() + string(REGEX REPLACE "-[0-9]*$" "" name ${basename}) + set(explicit_copyright_${basename} "${name}.txt" "${path}" "3rd-party") + endif() + endif() + if(explicit_copyright_${basename}) + add_copyright(third_party_copyright ${explicit_copyright_${basename}}) + endif() +endforeach() + +list(SORT common_license_names) +list(REMOVE_DUPLICATES common_license_names) +foreach(basename ${common_license_names}) + if(NOT DEFINED explicit_copyright_${basename}) + string(CONFIGURE "${common_license_pattern}" pattern @ONLY) + file(GLOB path ${pattern}) + if(NOT path) + message(SEND_ERROR "No license file for ${basename} (${pattern})") + elseif(path MATCHES ";") + message(SEND_ERROR "Multiple matches for ${basename}: ${path}") + elseif(system_copyright_dir AND path MATCHES "^${system_copyright_dir}/") + set(explicit_copyright_${basename} "${basename}" "file://${path}") + else() + set(explicit_copyright_${basename} "${basename}.txt" "${path}" "common-licenses") + endif() + endif() + if(explicit_copyright_${basename}) + add_copyright(common_licenses ${explicit_copyright_${basename}}) + endif() +endforeach() + + +string(REPLACE ";" "\n" third_party_copyright "${third_party_copyright}") +string(REPLACE ";" "\n" common_licenses "${common_licenses}") +configure_file(licensing.html "${CMAKE_CURRENT_BINARY_DIR}/licensing.html" @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/licensing.html" + DESTINATION "${MAPPER_ABOUT_DESTINATION}" +) diff --git a/doc/licensing/arch-licensing.cmake b/doc/licensing/arch-licensing.cmake new file mode 100644 index 0000000..0a2deb6 --- /dev/null +++ b/doc/licensing/arch-licensing.cmake @@ -0,0 +1,27 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +# Based on Arch Extra as of 2017-04. + +include("linux-distribution.cmake") + +set(system_copyright_dir "/usr/share/licenses") +set(copyright_pattern + "${system_copyright_dir}/@package@/LICENSE" +) diff --git a/doc/licensing/debian-licensing.cmake b/doc/licensing/debian-licensing.cmake new file mode 100644 index 0000000..373c505 --- /dev/null +++ b/doc/licensing/debian-licensing.cmake @@ -0,0 +1,33 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +# Based on Debian testing as of 2017-04. +# Also used for Ubuntu. + +include("linux-distribution.cmake") + +# A 3rd-party Mapper dependency may consist of multiple DEB packages +# sharing the same copyright file. We reference it just once. +set(package_names + gdal:libgdal +) + +set(system_copyright_dir "/usr/share") +set(copyright_pattern "${system_copyright_dir}/doc/@package@/copyright") +set(common_license_pattern "${system_copyright_dir}/common-licenses/@basename@") diff --git a/doc/licensing/fedora-licensing.cmake b/doc/licensing/fedora-licensing.cmake new file mode 100644 index 0000000..3a7e83c --- /dev/null +++ b/doc/licensing/fedora-licensing.cmake @@ -0,0 +1,36 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +# Based on Fedora 25. + +include("linux-distribution.cmake") + +set(explicit_copyright_gdal + "gdal" + "file:///usr/share/doc/gdal-libs/LICENSE.TXT" +) + +set(system_copyright_dir "/usr/share") +set(copyright_pattern + "${system_copyright_dir}/doc/@package@/COPYING" + "${system_copyright_dir}/doc/@package@/COPYRIGHT" + "${system_copyright_dir}/licenses/@package@/COPYING" + "${system_copyright_dir}/licenses/@package@/Copyright.txt" + "${system_copyright_dir}/licenses/@package@/LICENSE" +) diff --git a/doc/licensing/licensing-html.qdocconf b/doc/licensing/licensing-html.qdocconf new file mode 100644 index 0000000..234cb7e --- /dev/null +++ b/doc/licensing/licensing-html.qdocconf @@ -0,0 +1,28 @@ +# +# Copyright 2014 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +HTML.stylesheets = licensing.css + +HTML.headerstyles = \ + " \n" + +# HTML.nonavigationbar = "true" + +macro.break.HTML = "
    " +macro.copyright.HTML = "©" +macro.reg.HTML = "®" diff --git a/doc/licensing/licensing.css b/doc/licensing/licensing.css new file mode 100644 index 0000000..37bb290 --- /dev/null +++ b/doc/licensing/licensing.css @@ -0,0 +1,50 @@ +/* + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +body { + font-family: Arial, Helvetica; +} + +body:first-child { + font-size: 200%; +} + +blockquote { + margin-left: 20px; +} + +#buildversion { + display:none; +} + +.toc .level1 { +} + +.toc .level2 { + margin-left: 10px; +} + +.toc .level3 { + margin-left: 20px; +} + +.clearfix { + clear: both +} diff --git a/doc/licensing/licensing.html b/doc/licensing/licensing.html new file mode 100644 index 0000000..1591e5c --- /dev/null +++ b/doc/licensing/licensing.html @@ -0,0 +1,77 @@ + + + + + + Licensing | OpenOrienteering Mapper + + + + +
    +

    OpenOrienteering Mapper Licensing

    +
    + + + +

    + OpenOrienteering Mapper +

    +

    + OpenOrienteering Mapper is a free software for drawing orienteering maps.
    + https://www.openorienteering.org/apps/mapper/ +

    +

    + @Mapper_COPYRIGHT@ +

    +

    + This program is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. +

    +

    + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (GPL) + for more details. +

    + +

    + Third-party components +

    +
      +@third_party_copyright@ +
    + +

    Full Text of Common Licenses

    +
      +@common_licenses@ +
    + +

    + Trademarks +

    +

    + Qt and the Qt logo are trademarks of The Qt Company Ltd. +

    +

    + All other company, product, or service names may be trademarks or service + marks of others and are the property of their respective owners. +

    + + + diff --git a/doc/licensing/licensing.qdocconf b/doc/licensing/licensing.qdocconf new file mode 100644 index 0000000..3d90d80 --- /dev/null +++ b/doc/licensing/licensing.qdocconf @@ -0,0 +1,35 @@ +# +# Copyright 2014 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +project = OpenOrienteering Mapper +description = Information on Licensing + +naturallanguage = en_US +sourceencoding = UTF-8 +outputencoding = UTF-8 +outputformats = HTML + +sourcedirs = src +sources.fileextensions += *.qdoc + +# exampledirs = +# headerdirs = +# imagedirs = +# excludedirs = + +include(licensing-html.qdocconf) diff --git a/doc/licensing/licensing.qrc b/doc/licensing/licensing.qrc new file mode 100644 index 0000000..3fc58a7 --- /dev/null +++ b/doc/licensing/licensing.qrc @@ -0,0 +1,11 @@ + + + html/licensing.html + html/apache-2-0.html + html/gcc-runtime-library-exception-3-1.html + html/gpl-3-0.html + html/lgpl-2-1.html + html/lgpl-3-0.html + html/style/licensing.css + + diff --git a/doc/licensing/linux-distribution.cmake b/doc/licensing/linux-distribution.cmake new file mode 100644 index 0000000..7f7bb37 --- /dev/null +++ b/doc/licensing/linux-distribution.cmake @@ -0,0 +1,117 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +# Linux distributions provide packages of programs, shared libraries and data +# which Mapper just uses, not distributes. Most dependencies needed by Mapper +# have no attribution requirements for pure usage. +# +# Commented-out items either +# - are components which explicitly need attribution, or +# - are transitive dependencies which were checked and are recorded here for +# reference, but do not need to be handled explicitly unless added by some +# provider file, or +# - were not checked yet (marked by '?'). +# +# Based on CMakeLists.txt component names and +# - Debian jessie +# - Debian stretch (testing) as of 2017-04 +# - openSUSE Leap 42.3 +# basename[:package_name] + +list(APPEND easy_dependencies + libpolyclipping + proj + qtbase + qtimageformats + qttranslations + # qtandroidextras ? + # qtlocation ? + # qtsensors ? + # qtserialport ? + # qtsingleapplication : Linked statically, attribution required! + # gdal dependencies + # libarmadillo4 + # libatomic1 + # libcurl[3]:curl + # libdap11:libdap # not used here + # libdapclient3:libdap # not used here + # libdapserver7:libdap # not used here + # libepsilon1:libepsilon + # libexpat:expat + # libfreexl1 + # libgeos_c:geos + # libgeotiff:libgeotiff2 + # libgif:libgif6 + # libhdf4 + # libhdf5:libhdf5-10 + # libjasper1 + # libjpeg:libjpeg-turbo + # libjson-c3 + # libkml0 + # libkmlbase1:libkml + # libkmldom1:libkml + # libkmlengine1:libkml + # libmysqlclient + # libnetcdf[7] # not used here + # libodbc:unixODBC + # libodbcinst:unixODBC + # libogdi3.2 # not used here + # libopenjp2 + # libpcre + # libpng16 + # libpoppler + # libpq + # libproj* + # libqhull7 + # libspatialite + # libsqlite3 + # libsz2 + # libtiff + # liblzma5:xz + # liburiparser1 + # libwebp + # libxerces-c + # libxml2 + # libz + zlib + # qttools ? +) + +foreach(dependency ${easy_dependencies}) + if(dependency MATCHES ":") + string(REGEX REPLACE ".*:" "" package ${dependency}) + string(REGEX REPLACE ":.*" "" dependency ${dependency}) + endif() + if(NOT explicit_copyright_${dependency}) + set(explicit_copyright_${dependency} "${dependency}" "-") + if(dependency MATCHES "^qt") + find_package(Qt5Core) + list(APPEND explicit_copyright_${dependency} "${Qt5Core_VERSION}") + elseif(dependency STREQUAL "libpolyclipping") + find_package(Polyclipping) + list(APPEND explicit_copyright_${dependency} "${POLYCLIPPING_VERSION}") + elseif(dependency STREQUAL "proj") + find_package(PROJ4) + list(APPEND explicit_copyright_${dependency} "${PROJ4_VERSION}") + elseif(dependency STREQUAL "zlib") + find_package(ZLIB) + list(APPEND explicit_copyright_${dependency} "${ZLIB_VERSION_STRING}") + endif() + endif() +endforeach() diff --git a/doc/licensing/opensuse-leap-licensing.cmake b/doc/licensing/opensuse-leap-licensing.cmake new file mode 100644 index 0000000..e4d052b --- /dev/null +++ b/doc/licensing/opensuse-leap-licensing.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/opensuse-licensing.cmake") diff --git a/doc/licensing/opensuse-licensing.cmake b/doc/licensing/opensuse-licensing.cmake new file mode 100644 index 0000000..ddf3fbb --- /dev/null +++ b/doc/licensing/opensuse-licensing.cmake @@ -0,0 +1,43 @@ +# +# Copyright 2017, 2018 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +# Based on openSUSE Leap 42.3. + +include("linux-distribution.cmake") + +find_file (GDAL_LICENSE_TXT + NAMES LICENSE.TXT + PATHS /usr/share/licenses/gdal-devel + /usr/share/doc/packages/gdal-devel + NO_DEFAULT_PATH +) +set(explicit_copyright_gdal + # Copy! Package libgdal* does not include this file. + "gdal.txt" + "${GDAL_LICENSE_TXT}" + "3rd-party" +) + +set(system_copyright_dir "/usr/share/doc/packages") +set(copyright_pattern + "${system_copyright_dir}/@package@/COPYING" + "${system_copyright_dir}/@package@/COPYRIGHT" + "${system_copyright_dir}/@package@/Copyright.txt" + "${system_copyright_dir}/@package@/LICENSE" +) diff --git a/doc/licensing/opensuse-tumbleweed-licensing.cmake b/doc/licensing/opensuse-tumbleweed-licensing.cmake new file mode 100644 index 0000000..e4d052b --- /dev/null +++ b/doc/licensing/opensuse-tumbleweed-licensing.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/opensuse-licensing.cmake") diff --git a/doc/licensing/src/apache-2.0.qdoc b/doc/licensing/src/apache-2.0.qdoc new file mode 100644 index 0000000..bb79e6f --- /dev/null +++ b/doc/licensing/src/apache-2.0.qdoc @@ -0,0 +1,219 @@ +// This file is part of OpenOrienteering. + +/*! + +\page apache-2.0 + +\title Apache License + Version 2.0, January 2004 \break + \l http://www.apache.org/licenses/ + +\section1 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +\list 1 + \li Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + \li Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + \li Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + \li Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + \list + \li (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + \li (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + \li (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + \li (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + \endlist + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + \li Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + \li Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + \li Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + \li Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + \li Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. +\endlist + + END OF TERMS AND CONDITIONS + +\section1 APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +\code + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +\endcode + +\hr + +\e Reference: \l{http://www.apache.org/licenses/LICENSE-2.0} + +*/ diff --git a/doc/licensing/src/gcc-runtime-library-exception.qdoc b/doc/licensing/src/gcc-runtime-library-exception.qdoc new file mode 100644 index 0000000..f87454a --- /dev/null +++ b/doc/licensing/src/gcc-runtime-library-exception.qdoc @@ -0,0 +1,86 @@ +// This file is part of OpenOrienteering. + +/*! + +\page gcc-runtime-library-exception-3.1 + +\title GCC Runtime Library Exception + +\section1 GCC RUNTIME LIBRARY EXCEPTION + + Version 3.1, 31 March 2009 + + Copyright (C) 2009 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies of this + license document, but changing it is not allowed. + + This GCC Runtime Library Exception ("Exception") is an additional + permission under section 7 of the GNU General Public License, version + 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that + bears a notice placed by the copyright holder of the file stating that + the file is governed by GPLv3 along with this Exception. + + When you use GCC to compile a program, GCC may combine portions of + certain GCC header files and runtime libraries with the compiled + program. The purpose of this Exception is to allow compilation of + non-GPL (including proprietary) programs to use, in this way, the + header files and runtime libraries covered by this Exception. + +\section2 0. Definitions. + + A file is an "Independent Module" if it either requires the Runtime + Library for execution after a Compilation Process, or makes use of an + interface provided by the Runtime Library, but is not otherwise based + on the Runtime Library. + + "GCC" means a version of the GNU Compiler Collection, with or without + modifications, governed by version 3 (or a specified later version) of + the GNU General Public License (GPL) with the option of using any + subsequent versions published by the FSF. + + "GPL-compatible Software" is software whose conditions of propagation, + modification and use would permit combination with GCC in accord with + the license of GCC. + + "Target Code" refers to output from any compiler for a real or virtual + target processor architecture, in executable form or suitable for + input to an assembler, loader, linker and/or execution + phase. Notwithstanding that, Target Code does not include data in any + format that is used as a compiler intermediate representation, or used + for producing a compiler intermediate representation. + + The "Compilation Process" transforms code entirely represented in + non-intermediate languages designed for human-written code, and/or in + Java Virtual Machine byte code, into Target Code. Thus, for example, + use of source code generators and preprocessors need not be considered + part of the Compilation Process, since the Compilation Process can be + understood as starting with the output of the generators or + preprocessors. + + A Compilation Process is "Eligible" if it is done using GCC, alone or + with other GPL-compatible software, or if it is done without using any + work based on GCC. For example, using non-GPL-compatible Software to + optimize any GCC intermediate representations would not qualify as an + Eligible Compilation Process. + +\section2 1. Grant of Additional Permission. + + You have permission to propagate a work of Target Code formed by + combining the Runtime Library with Independent Modules, even if such + propagation would otherwise violate the terms of GPLv3, provided that + all Target Code was generated by Eligible Compilation Processes. You + may then convey such a combination under terms of your choice, + consistent with the licensing of the Independent Modules. + +\section2 2. No Weakening of GCC Copyleft. + + The availability of this Exception does not imply any general + presumption that third-party software is unaffected by the copyleft + requirements of the license of GCC. + +\hr + +\e Reference: \l{http://gcc.gnu.org/onlinedocs/libstdc++/manual/license.html} + +*/ diff --git a/doc/licensing/src/gdal-licensing.qdocinc b/doc/licensing/src/gdal-licensing.qdocinc new file mode 100644 index 0000000..261f2c7 --- /dev/null +++ b/doc/licensing/src/gdal-licensing.qdocinc @@ -0,0 +1,268 @@ +\omit +// This file is part of OpenOrienteering. +\endomit + + \note Depending on the actual package, a particular copy of OpenOrienteering Mapper + may not contain code covered by one or more of the licenses listed here. + +In general GDAL/OGR is licensed under an MIT/X style license with the +following terms: + +\quotation +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +\endquotation + + +\section3 gdal/frmts/gtiff/tif_float.c + +\quotation +Copyright (c) 2002, Industrial Light & Magic, a division of Lucas +Digital Ltd. LLC + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +\list +\li Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +\li Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +\li Neither the name of Industrial Light & Magic nor the names of +its contributors may be used to endorse or promote products derived +from this software without specific prior written permission. +\endlist +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + +\section3 gdal/frmts/hdf4/hdf-eos/* + +\quotation + Copyright (C) 1996 Hughes and Applied Research Corporation + + Permission to use, modify, and distribute this software and its documentation + for any purpose without fee is hereby granted, provided that the above + copyright notice appear in all copies and that both that copyright notice and + this permission notice appear in supporting documentation. +\endquotation + + +\section3 gdal/frmts/pcraster/libcsf + +\quotation +Copyright (c) 1997-2003, Utrecht University +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +\list +\li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +\li Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +\li Neither the name of Utrecht University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. +\endlist +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + +\section3 gdal/frmts/grib/degrib/* + +The degrib and g2clib source code are modified versions of code produced +by NOAA NWS and are in the public domain subject to the following +restrictions: + +\quotation +http://www.weather.gov/im/softa.htm + +DISCLAIMER The United States Government makes no warranty, expressed or +implied, as to the usefulness of the software and documentation for any +purpose. The U.S. Government, its instrumentalities, officers, employees, +and agents assumes no responsibility (1) for the use of the software and +documentation listed below, or (2) to provide technical support to users. + +http://www.weather.gov/disclaimer.php + + The information on government servers are in the public domain, unless +specifically annotated otherwise, and may be used freely by the public so +long as you do not 1) claim it is your own (e.g. by claiming copyright for +NWS information -- see below), 2) use it in a manner that implies an +endorsement or affiliation with NOAA/NWS, or 3) modify it in content and +then present it as official government material. You also cannot present +information of your own in a way that makes it appear to be official +government information.. + + The user assumes the entire risk related to its use of this data. NWS is +providing this data "as is," and NWS disclaims any and all warranties, +whether express or implied, including (without limitation) any implied +warranties of merchantability or fitness for a particular purpose. In no +event will NWS be liable to you or to any third party for any direct, +indirect, incidental, consequential, special or exemplary damages or lost +profit resulting from any use or misuse of this data. + + As required by 17 U.S.C. 403, third parties producing copyrighted works +consisting predominantly of the material appearing in NWS Web pages must +provide notice with such work(s) identifying the NWS material incorporated +and stating that such material is not subject to copyright protection. +\endquotation + + +\section3 port/cpl_minizip* + +This is version 2005-Feb-10 of the Info-ZIP copyright and license. +The definitive version of this document should be available at +ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. + +\quotation +Copyright (c) 1990-2005 Info-ZIP. All rights reserved. + +For the purposes of this copyright and license, "Info-ZIP" is defined as +the following set of individuals: + + Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, + Jean-loup Gailly, Hunter Goatley, Ed Gordon, Ian Gorman, Chris Herborth, + Dirk Haase, Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, + David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, + Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, + Kai Uwe Rommel, Steve Salisbury, Dave Smith, Steven M. Schweda, + Christian Spieler, Cosmin Truta, Antoine Verheijen, Paul von Behren, + Rich Wales, Mike White + +This software is provided "as is," without warranty of any kind, express +or implied. In no event shall Info-ZIP or its contributors be held liable +for any direct, indirect, incidental, special or consequential damages +arising out of the use of or inability to use this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: +\list 1 +\li Redistributions of source code must retain the above copyright notice, + definition, disclaimer, and this list of conditions. + +\li Redistributions in binary form (compiled executables) must reproduce + the above copyright notice, definition, disclaimer, and this list of + conditions in documentation and/or other materials provided with the + distribution. The sole exception to this condition is redistribution + of a standard UnZipSFX binary (including SFXWiz) as part of a + self-extracting archive; that is permitted without inclusion of this + license, as long as the normal SFX banner has not been removed from + the binary or disabled. + +\li Altered versions--including, but not limited to, ports to new operating + systems, existing ports with new graphical interfaces, and dynamic, + shared, or static library versions--must be plainly marked as such + and must not be misrepresented as being the original source. Such + altered versions also must not be misrepresented as being Info-ZIP + releases--including, but not limited to, labeling of the altered + versions with the names "Info-ZIP" (or any variation thereof, including, + but not limited to, different capitalizations), "Pocket UnZip," "WiZ" + or "MacZip" without the explicit permission of Info-ZIP. Such altered + versions are further prohibited from misrepresentative use of the + Zip-Bugs or Info-ZIP e-mail addresses or of the Info-ZIP URL(s). + +\li Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip," + "UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its + own source and binary releases. +\endlist +\endquotation + + +\section3 gdal/ogr/ogrsf_frmts/dxf/intronurbs.cpp + +This code is derived from the code associated with the book "An Introduction +to NURBS" by David F. Rogers. More information on the book and the code is +available at: + + http://www.nar-associates.com/nurbs/ + +\quotation +Copyright (c) 2009, David F. Rogers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +\list +\li Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +\li Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +\li Neither the name of the David F. Rogers nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. +\endlist +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +\endquotation + + +\section3 gdal/alg/thinplatespline.cpp + +IEEE754 log() code derived from: +@(#)e_log.c 1.3 95/01/18 + +\quotation +Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + +Developed at SunSoft, a Sun Microsystems, Inc. business. +Permission to use, copy, modify, and distribute this +software is freely granted, provided that this notice +is preserved. +\endquotation diff --git a/doc/licensing/src/gpl-3.0.qdoc b/doc/licensing/src/gpl-3.0.qdoc new file mode 100644 index 0000000..c023adf --- /dev/null +++ b/doc/licensing/src/gpl-3.0.qdoc @@ -0,0 +1,702 @@ +// This file is part of OpenOrienteering. + +/*! + +\page gpl-3.0 + +\title GNU General Public License + +\chapter GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <\l{http://fsf.org/}> \break + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +\section1 Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + +\section1 TERMS AND CONDITIONS + +\section2 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +\section2 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + +\section2 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + +\section2 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + +\section2 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +\section2 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + +\list +\li a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + +\li b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + +\li c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + +\li d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. +\endlist + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +\section2 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + +\list +\li a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + +\li b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + +\li c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + +\li d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + +\li e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. +\endlist + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +\section2 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + +\list +\li a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + +\li b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + +\li c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + +\li d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + +\li e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + +\li f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. +\endlist + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + +\section2 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +\section2 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +\section2 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +\section2 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +\section2 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + +\section2 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +\section2 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +\section2 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +\section2 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +\section2 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + +\section1 How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + +\quotation +\code + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +\endcode +\endquotation + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + +\quotation +\code + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. +\endcode +\endquotation + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<\l{http://www.gnu.org/licenses/}>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<\l{http://www.gnu.org/philosophy/why-not-lgpl.html}>. + +\hr + +\e Reference: \l{http://www.gnu.org/licenses/gpl-3.0.en.html} + +*/ diff --git a/doc/licensing/src/lgpl-2.1.qdoc b/doc/licensing/src/lgpl-2.1.qdoc new file mode 100644 index 0000000..f8d9c6c --- /dev/null +++ b/doc/licensing/src/lgpl-2.1.qdoc @@ -0,0 +1,528 @@ +// This file is part of OpenOrienteering. + +/*! + +\page lgpl-2.1 + +\title GNU Lesser General Public License + + GNU LESSER GENERAL PUBLIC LICENSE \break + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. \break + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \break + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + +\section1 Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +\section1 GNU LESSER GENERAL PUBLIC LICENSE +\section1 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +\list +\li a) The modified work must itself be a software library. + +\li b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + +\li c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + +\li d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) +\endlist + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +\list +\li a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + +\li b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + +\li c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + +\li d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + +\li e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. +\endlist + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +\list +\li a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + +\li b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. +\endlist + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +\section2 NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + +\section1 How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + +\quotation +\code + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +\endcode +\endquotation + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + +\quotation + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 \break + Ty Coon, President of Vice +\endquotation + +That's all there is to it! + +\hr + +\e Reference: \l{http://www.gnu.org/licenses/lgpl-2.1.en.html} + +*/ diff --git a/doc/licensing/src/lgpl-3.0.qdoc b/doc/licensing/src/lgpl-3.0.qdoc new file mode 100644 index 0000000..053c067 --- /dev/null +++ b/doc/licensing/src/lgpl-3.0.qdoc @@ -0,0 +1,189 @@ +// This file is part of OpenOrienteering. + +/*! + +\page lgpl-3.0 + +\title GNU Lesser General Public License Version 3 + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + +\section2 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + +\section2 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + +\section2 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + +\list +\li a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + +\li b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. +\endlist + +\section2 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + +\list +\li a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + +\li b) Accompany the object code with a copy of the GNU GPL and this license + document. +\endlist + +\section2 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + +\list +\li a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + +\li b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + +\li c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + +\li d) Do one of the following: + + \list + \li 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + \li 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + \endlist + +\li e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) +\endlist + +\section2 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + +\list +\li a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + +\li b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. +\endlist + +\section2 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +\hr + +\e Reference: \l{http://www.gnu.org/licenses/lgpl-3.0.en.html} + +*/ diff --git a/doc/licensing/src/licensing.qdoc b/doc/licensing/src/licensing.qdoc new file mode 100644 index 0000000..fca4032 --- /dev/null +++ b/doc/licensing/src/licensing.qdoc @@ -0,0 +1,265 @@ +/* + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +/*! + +\page licensing +\title OpenOrienteering Mapper Licensing + +\section1 OpenOrienteering Mapper + +\e {OpenOrienteering Mapper is a free software for drawing orienteering maps.} \break +\l https://www.openorienteering.org + +Copyright (C) 2012-2015 Kai Pastor, Thomas Schöps + +This program is free software: you can redistribute it and/or modify +it under the terms of the \l{GNU General Public License} as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +\l{GNU General Public License} (GPL) for more details. + + + +\section1 Third-party licenses, copyright notices, conditions and disclaimers + +OpenOrienteering Mapper includes, uses and/or is bundled with various +third-party material. +This document provides licensing information, copyright notices, conditions +and disclaimers for binary distributions of OpenOrienteering Mapper. +If you want to distribute or modify OpenOrienteering Mapper it is your +responsibility to ensure that you have met the licensing requirements of this +program and of any third-party material you are using or distributing. + +\note Depending on the actual package, a particular copy of OpenOrienteering +Mapper may not contain code covered by one or more of the licenses listed here. +The following information reflects the versions which are part of the releases +for Windows, OS X and Android. Linux packages will use the distribution's +packages for most 3rd-party-components. These 3rd-party packages come with +independent documentation on copyright and license terms. + + +\section2 Clipper library + +\e {The Clipper Library has been designed to perform boolean operations on + polygons, and polygon and polyline offsetting.} \break +\l http://www.angusj.com/delphi/clipper.php + +\quotation +Copyright (C) 2010-2014 Angus Johnson + +The Clipper Library (including Delphi, C++ & C# source code, other accompanying +code, examples and documentation), hereafter called "the Software", has been +released under the following license, terms and conditions: + +Boost Software License - Version 1.0 - August 17th, 2003 \break +\l http://www.boost.org/LICENSE_1_0.txt + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the Software covered by this license to use, reproduce, +display, distribute, execute, and transmit the Software, and to prepare +derivative works of the Software, and to permit third-parties to whom the +Software is furnished to do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including the +above license grant, this restriction and the following disclaimer, must be +included in all copies of the Software, in whole or in part, and all derivative +works of the Software, unless such copies or derivative works are solely in the +form of machine-executable object code generated by a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL +THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY +DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +\endquotation + + +\section2 GDAL - Geospatial Data Abstraction Library + +\e {GDAL is an open source X/MIT licensed translator library for raster and + vector geospatial data formats.} +\break +\l http://www.gdal.org/ + +\include gdal-licensing.qdocinc + + +\section2 GNU Standard C++ Library v3 + +\e {The GNU Standard C++ Library v3 is an ongoing project to implement the + ISO 14882 Standard C++ library as described in chapters 17 through 27 and + annex D.} \break +\l http://gcc.gnu.org/libstdc++/ + +\quotation + Copyright (C) 2014 Free Software Foundation, Inc. + + This library is free + software; you can redistribute it and/or modify it under the + terms of the \l{GNU General Public License} as published by the + Free Software Foundation; either version 3, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the \l{GCC Runtime Library Exception}, version + 3.1, as published by the Free Software Foundation. +\endquotation + + +\section2 PROJ.4 package + +\e {A cartographic projection software.} \break +\l http://trac.osgeo.org/proj/ + +\quotation +Copyright (c) 2000, Frank Warmerdam + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +\endquotation + + +\section2 Qt Solutions Component: Single Application + +\e {The QtSingleApplication component provides support for + applications that can be only started once per user.} \break +\l http://code.qt.io/cgit/qt-solutions/qt-solutions.git/ + +\quotation + Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +\endquotation + +\quotation + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + +\list + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + \li Neither the name of Digia Plc and its Subsidiary(-ies) nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. +\endlist + + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + +\section2 Qt Toolkit + +\e {Qt is a C++ toolkit for cross-platform application development. + Qt is The Qt Company Ltd product developed as an open source project.} +\break +\l http://qt.io + +For version, copyright and license terms of Qt see menu item Help > About Qt. +OpenOrienteering Mapper uses Qt under the terms of the +\l{GNU Lesser General Public License Version 3}. + +\include qt-licensing.qdocinc + + +\section2 Zlib Data Compression Library + +The 'zlib' compression library provides in-memory compression and +decompression functions + +\quotation + (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + +\list 1 + \li The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + \li Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + \li This notice may not be removed or altered from any source distribution. +\endlist +\endquotation + + +\section1 Trademarks + +\include trademarks.qdocinc + + +\section1 Full Text of Selected Licenses + +\list + \li \l{Apache License}, version 2.0 + \li \l{GCC Runtime Library Exception}, version 3.1 + \li \l{GNU General Public License} (GPL), version 3.0 + \li \l{GNU Lesser General Public License} (LGPL), version 2.1 + \li \l{GNU Lesser General Public License Version 3} (LGPLv3) +\endlist + +*/ diff --git a/doc/licensing/src/qt-licensing.qdocinc b/doc/licensing/src/qt-licensing.qdocinc new file mode 100644 index 0000000..422c04c --- /dev/null +++ b/doc/licensing/src/qt-licensing.qdocinc @@ -0,0 +1,1587 @@ +\omit +/* + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ +\endomit + + + +\section2 Licenses Used in Qt 5.5.1 + +This section lists other licenses used in Qt or in libraries that are supplied +alongside Qt modules, not provided under the +\l{GNU Lesser General Public License Version 3}. + +\note Depending on the actual package, a particular copy of OpenOrienteering Mapper + may not contain code covered by one or more of the licenses listed here. + + + +\section3 at-spi and at-spi2 + +This license applies to +\c{qtbase/src/3rd-party/atspi2/}. +In OpenOrienteering Mapper, this code is used under the terms of the +\l{GNU General Public License}, version 3. + +\quotation + Copyright 2010, 2011 Novell, Inc. \break + Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +\endquotation + + + +\section3 Big5 Text Codec + +This license applies to parts of some files in +\c{qtbase/src/corelib/codecs/}. + +\quotation + Copyright (C) 2000 Ming-Che Chuang \break + Copyright (C) 2002 James Su, Turbolinux Inc. \break + Copyright (C) 2002 Anthony Fok, ThizLinux Laboratory Ltd. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 Big5-HKSCS Text Codec + +This license applies to parts of some files in +\c{qtbase/src/corelib/codecs/}. + +\quotation + Copyright (C) 2000 Ming-Che Chuang \break + Copyright (C) 2001, 2002 James Su, Turbolinux Inc. \break + Copyright (C) 2002 WU Yi, HancomLinux Inc. \break + Copyright (C) 2001, 2002 Anthony Fok, ThizLinux Laboratory Ltd. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 brg_endian.h + +This license applies to +\c{qtbase/src/3rdparty/sha3/brg_endian.h}. + +\quotation + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + +\list 1 + \li source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + \li binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + \li the name of the copyright holder is not used to endorse products + built using this software without specific written permission. +\endlist + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. +\endquotation + + + +\section3 Clucene Core Library + +This license applies to files in +\c{qttools/src/assistant/3rdparty/clucene}. + +\quotation + Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team + + The CLucene Core Library uses a dual license strategy for the source code. + These licenses are the GNU Lesser General Public License (LGPL) and the Apache + License (Version 2.0). Users can choose the license they wish to distribute + their software under. This means that you do not need to abide by *both* + licenses, but rather than you can choose the license which most suits your + needs. + + To rephrase this and to make it perfectly clear: \break + CLucene is distributed under the \l{GNU Lesser General Public License} (LGPL) \break + *or* \break + the \l{Apache License}, Version 2.0 + + However, we are an open source project, and we encourage users to use the LGPL + license and participate fully in the free software community. Dual licensing + of the CLucene source code provides open and free access to the technology both + for the GPL community and for other developers or companies that cannot use the + GPL. + + You can freely modify, extend, and improve the CLucene source code. The only + question is whether or not you must provide the source code and contribute + modifications to the community. The GNU and Apache licenses allow different + ranges of flexibility in this regard, but in the end, regardless of the license + used, we highly recommend that you submit any bugs, incompatibilities or + added features. + + Note that this same license does *not* apply to the CLucene Contributions + package. You should read the COPYING file in that directory or package for + more information. +\endquotation + + + +\section3 Cocoa Platform Plugin + +This license applies to parts of some files in +\c{qtbase/src/plugins/platforms/cocoa/}. + +\quotation + Copyright (C) 2007-2008, Apple, Inc. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + +\list +\li Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +\li Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +\li Neither the name of Apple, Inc. nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 Drag and Drop + + Copyright 1996 Daniel Dardailler. + + Permission to use, copy, modify, distribute, and sell this software for any + purpose is hereby granted without fee, provided that the above copyright + notice appear in all copies and that both that copyright notice and this + permission notice appear in supporting documentation, and that the name of + Daniel Dardailler not be used in advertising or publicity pertaining to + distribution of the software without specific, written prior permission. + Daniel Dardailler makes no representations about the suitability of this + software for any purpose. It is provided "as is" without express or implied + warranty. + + Modifications Copyright 1999 Matt Koss, under the same license as above. + + + +\section3 Easing Equations + +This license applies to +\c{qtbase/src/3rdparty/easing/easing.cpp}. + +\quotation + Copyright \copyright 2001 Robert Penner \break + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +\list + \li Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + \li Neither the name of the author nor the names of contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 EUC-JP Text Codec + +This license applies to parts of some files in +\c{qtbase/src/corelib/codecs/}. + +\quotation + Copyright (C) 1999 Serika Kurusugawa. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS". + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 EUC-KR Text Codec + +This license applies to parts of some files in +\c{qtbase/src/corelib/codecs/}. + +\quotation + Copyright (C) 1999-2000 Mizi Research Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 forkfd + +This license applies to files in +\c{qtbase/src/3rdparty/forkfd/}. + +\quotation + Copyright (C) 2014 Intel Corporation \break + Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +\endquotation + + + +\section3 FreeBSD strtoll and strtoull + +This license applies to files in +\c{qtbase/src/3rdparty/freebsd/}. + +\quotation + Copyright (c) 1992, 1993 \break + The Regents of the University of California. All rights reserved. + + Copyright (c) 2011 The FreeBSD Foundation \break + All rights reserved. \break + Portions of this software were developed by David Chisnall + under sponsorship from the FreeBSD Foundation. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + \li Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 GBK Text Codec + +This license applies to parts of some files in +\c{qtbase/src/corelib/codecs/}. + +\quotation + Copyright (C) 2000 TurboLinux, Inc. Written by Justin Yu and Sean Chen. \break + Copyright (C) 2001, 2002 Turbolinux, Inc. Written by James Su. \break + Copyright (C) 2001, 2002 ThizLinux Laboratory Ltd. Written by Anthony Fok. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 HarfBuzz + +This license applies to files in +\c{qtbase/src/3rdparty/harfbuzz} and +\c{qtbase/src/3rdparty/harfbuzz-ng}. + +\quotation + Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) \break + Copyright \copyright 2010,2011,2012 Google, Inc. \break + Copyright \copyright 2012 Mozilla Foundation \break + Copyright \copyright 2011 Codethink Limited \break + Copyright \copyright 2008,2010 Nokia Corporation and/or its subsidiary(-ies) \break + Copyright \copyright 2009 Keith Stribley \break + Copyright \copyright 2009 Martin Hosken and SIL International \break + Copyright \copyright 2007 Chris Wilson \break + Copyright \copyright 2006 Behdad Esfahbod \break + Copyright \copyright 2005 David Turner \break + Copyright \copyright 2004,2007,2008,2009,2010 Red Hat, Inc. \break + Copyright \copyright 1998-2004 David Turner and Werner Lemberg + + Permission is hereby granted, without written agreement and without + license or royalty fees, to use, copy, modify, and distribute this + software and its documentation for any purpose, provided that the + above copyright notice and the following two paragraphs appear in + all copies of this software. + + IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + + THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +\endquotation + + + +\section3 IAccessible2 IDL Specification + +This license applies to files in +\c{qtbase/src/3rdparty/iaccessible2/}. + +\quotation + Copyright (c) 2007, 2010 Linux Foundation \break + Copyright (c) 2006 IBM Corporation \break + Copyright (c) 2000, 2006 Sun Microsystems, Inc. \break + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + \li Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + \li Neither the name of the Linux Foundation nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 ISO 2022-JP (JIS) Text Codec + +This license applies to parts of some files in +\c{qtbase/src/corelib/codecs/}. + +\quotation + Copyright (C) 1999 Serika Kurusugawa. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS". + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 JasPer library + +This license applies to files in +\c{qtimageformats/src/3rdparty/jasper/}. + +\quotation + JasPer License Version 2.0 + + Copyright (c) 2001-2006 Michael David Adams \break + Copyright (c) 1999-2000 Image Power, Inc. \break + Copyright (c) 1999-2000 The University of British Columbia + + All rights reserved. + + Permission is hereby granted, free of charge, to any person (the + "User") obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + +\list 1 + \li The above copyright notices and this permission notice (which + includes the disclaimer below) shall be included in all copies or + substantial portions of the Software. + + \li The name of a copyright holder shall not be used to endorse or + promote products derived from the Software without specific prior + written permission. +\endlist + + THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS + LICENSE. NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER + THIS DISCLAIMER. THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS + "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO + EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. NO ASSURANCES ARE + PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE + THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY. + EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS + BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL + PROPERTY RIGHTS OR OTHERWISE. AS A CONDITION TO EXERCISING THE RIGHTS + GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE + ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY. THE SOFTWARE + IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL + SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES, + AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL + SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH + THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH, + PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH + RISK ACTIVITIES"). THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY + EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES. +\endquotation + + + +\section3 LibTIFF library + +This license applies to most files in +\c{qtimageformats/src/3rdparty/libtiff/}. + +\quotation + Copyright (c) 1988-1997 Sam Leffler \break + Copyright (c) 1991-1997 Silicon Graphics, Inc. \break + Copyright (C) 2005, Andrey Kiselev \break + Copyright (c) 2009 Frank Warmerdam \break + Copyright (c) 2010, Andrey Kiselev + + Permission to use, copy, modify, distribute, and sell this software and + its documentation for any purpose is hereby granted without fee, provided + that (i) the above copyright notices and this permission notice appear in + all copies of the software and related documentation, and (ii) the names of + Sam Leffler and Silicon Graphics may not be used in any advertising or + publicity relating to the software without the specific, prior written + permission of Sam Leffler and Silicon Graphics. + + THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + OF THIS SOFTWARE. +\endquotation + + +This license applies to some files in +\c{qtimageformats/src/3rdparty/libtiff/}. + +\quotation + Copyright (c) 1996-1997 Sam Leffler \break + Copyright (c) 1996 Pixar + + Permission to use, copy, modify, distribute, and sell this software and + its documentation for any purpose is hereby granted without fee, provided + that (i) the above copyright notices and this permission notice appear in + all copies of the software and related documentation, and (ii) the names of + Pixar, Sam Leffler and Silicon Graphics may not be used in any advertising or + publicity relating to the software without the specific, prior written + permission of Pixar, Sam Leffler and Silicon Graphics. + + THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + IN NO EVENT SHALL PIXAR, SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + OF THIS SOFTWARE. +\endquotation + + +This license applies to some files in +\c{qtimageformats/src/3rdparty/libtiff/}. + +\quotation + Copyright (c) 1997 Greg Ward Larson \break + Copyright (c) 1997 Silicon Graphics, Inc. + + Permission to use, copy, modify, distribute, and sell this software and + its documentation for any purpose is hereby granted without fee, provided + that (i) the above copyright notices and this permission notice appear in + all copies of the software and related documentation, and (ii) the names of + Sam Leffler, Greg Larson and Silicon Graphics may not be used in any + advertising or publicity relating to the software without the specific, + prior written permission of Sam Leffler, Greg Larson and Silicon Graphics. + + THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + IN NO EVENT SHALL SAM LEFFLER, GREG LARSON OR SILICON GRAPHICS BE LIABLE + FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + OF THIS SOFTWARE. +\endquotation + + +This license applies to some files in +\c{qtimageformats/src/3rdparty/libtiff/port}. + +\quotation + Copyright (c) 1987, 1993 + The Regents of the University of California. All rights reserved. \break + Copyright (c) 1987, 1993, 1994 + The Regents of the University of California. All rights reserved. \break + Copyright (c) 1989, 1993 + The Regents of the University of California. All rights reserved. \break + Copyright (c) 1990, 1993 + The Regents of the University of California. All rights reserved. \break + Copyright (c) 1992, 1993 + The Regents of the University of California. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + \li Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 Native Style for Android + +This license applies to +\c{qtbase/src/3rdparty/android/extract}. + +\quotation + Copyright (C) 2005 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +\endquotation + + + +\section3 PCRE library + +This licence applies to files in +\c{qtbase/src/3rdparty/pcre/} (the basic library functions) and +\c{qtbase/src/3rdparty/pcre/sljit/} (stack-less just-in-time compiler). + +\quotation +\code + THE BASIC LIBRARY FUNCTIONS + --------------------------- + + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk + + University of Cambridge Computing Service, + Cambridge, England. + + Copyright (c) 1997-2015 University of Cambridge + All rights reserved. + + + PCRE JUST-IN-TIME COMPILATION SUPPORT + ------------------------------------- + + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + + Copyright(c) 2010-2015 Zoltan Herczeg + All rights reserved. + + + STACK-LESS JUST-IN-TIME COMPILER + -------------------------------- + + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + + Copyright(c) 2009-2015 Zoltan Herczeg + All rights reserved. + + + THE C++ WRAPPER FUNCTIONS + ------------------------- + + Contributed by: Google Inc. + + Copyright (c) 2007-2012, Google Inc. + All rights reserved. +\endcode + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + +\list + \li Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + \li Neither the name of the University of Cambridge nor the name of Google + Inc. nor the names of their contributors may be used to endorse or + promote products derived from this software without specific prior + written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 pixman + +This license applies to the files in +\c{qtbase\src\3rdparty\pixman\}. + +\quotation + Copyright \copyright 2009 Nokia Corporation + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +\endquotation + + + +\section3 Poly2Tri + +This license applies to files in +\c{qtlocation/src/3rdparty/poly2tri/}. + +\quotation + \span {} {Poly}2Tri Copyright (c) 2009-2010, \span {} {Poly}2Tri Contributors \break + \l http://code.google.com/p/poly2tri/ + + All rights reserved. \break + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + +\list + \li Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + \li Neither the name of \span {} {Poly}2Tri nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 QCrashHandler class + +This license applies to parts of +\c{qtbase/src/corelib/kernel/qcrashhandler.cpp}. + +\quotation + Copyright (c) 1998 by Bjorn Reese + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. +\endquotation + + + +\section3 QImage smooth scaling + +For smooth scaling, the \c{QImage::transformed()} functions use code based on +smooth scaling algorithm by Daniel M. Duley. + +\quotation + Copyright (C) 2004, 2005 Daniel M. Duley + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 QKeyMapper class on X11 platforms + +This license applies to parts of +the internal QKeyMapper class on X11 platforms. + +\quotation + Copyright 1985, 1987, 1998 The Open Group + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of The Open Group shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from The Open Group. +\endquotation + + + +\section3 QtCLucene Library + +This license applies to files in +\c{qttools/src/assistant/clucene/}. + +\quotation + Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team. \break + All rights reserved. + + Portion Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). \break + All rights reserved. + + This file may be used under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation and + appearing in the file LICENSE.LGPL included in the packaging of this file. + Please review the following information to ensure the GNU Lesser General + Public License version 2.1 requirements will be met: \break + \l{http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html}. +\endquotation + + + +\section3 qtmain Library + +This license applies to files in +\c{qtbase/src/winmain/}. + +\quotation +Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). \break +Contact: \l http://www.qt-project.org/legal + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +\list +\li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +\li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. +\li Neither the name of Digia Plc and its Subsidiary(-ies) nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. +\endlist + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 QUrl::fromUserInput + +This license applies to parts of \c{qtbase/src/corelib/io/qurl.cpp}. + +\quotation + Copyright (C) Research In Motion Limited 2009. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + +\list + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + \li Neither the name of Research In Motion Limited nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY Research In Motion Limited ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Research In Motion Limited BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 Secure Hash Algorithms (SHA) + +This license applies to files in +\c{qtbase/src/3rdparty/rfc6234/}. + +\quotation + Copyright (c) 2011 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + +\list + \li Redistributions of source code must retain the above + copyright notice, this list of conditions and + the following disclaimer. + + \li Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + \li Neither the name of Internet Society, IETF or IETF Trust, nor + the names of specific contributors, may be used to endorse or + promote products derived from this software without specific + prior written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 Shift-JIS Text Codec + +This license applies to parts of some files in +\c{qtbase/src/corelib/codecs/}. + +\quotation + Copyright (C) 1999 Serika Kurusugawa. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + +\list 1 + \li Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS". ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 TSCII Text Codec + +This license applies to parts of some files in +\c{qtbase/src/corelib/codecs/}. + +\quotation + Copyright (c) 2000 Hans Petter Bieker. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + +\list 1 + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +\endquotation + + + +\section3 Unicode Character Database Data Files + +This license applies to files in +\c{qtbase/util/unicode/data/}. + +\quotation + Copyright \copyright 1991-2014 Unicode, Inc. All rights reserved. + Distributed under the Terms of Use in + http://www.unicode.org/copyright.html. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of the Unicode data files and any associated + documentation (the "Data Files") or Unicode software and any + associated documentation (the "Software") to deal in the Data + Files or Software without restriction, including without + limitation the rights to use, copy, modify, merge, publish, + distribute, and/or sell copies of the Data Files or Software, + and to permit persons to whom the Data Files or Software are + furnished to do so, provided that (a) the above copyright + notice(s) and this permission notice appear with all copies + of the Data Files or Software, (b) both the above copyright + notice(s) and this permission notice appear in associated + documentation, and (c) there is clear notice in each modified + Data File or in the Software as well as in the documentation + associated with the Data File(s) or Software that the data or + software has been modified. + + THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT + WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED + IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES + OR SOFTWARE. + + Except as contained in this notice, the name of a copyright + holder shall not be used in advertising or otherwise to promote + the sale, use or other dealings in these Data Files or Software + without prior written authorization of the copyright holder. +\endquotation + + +\section3 WebP codec + +This license applies to files in +\c{qtimageformats/src/3rdparty/libwebp/}. + +\quotation + Copyright (c) 2010, Google Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + +\list + \li Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + \li Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + \li Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. +\endlist + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endquotation + + + +\section3 xcb + +This license applies to most files in +\c{qtbase/src/3rdparty/xcb/}. + +\quotation + Copyright \copyright 2008-2009 Julien Danjou \break + Copyright \copyright 2008 Bart Massey + Copyright \copyright 2008 Ian Osgood + Copyright \copyright 2008 Jamey Sharp + Copyright \copyright 2008 Josh Triplett + Copyright \copyright 2008 Ulrich Eckhardt + Copyright \copyright 2008 Arnaud Fontaine + Copyright \copyright 2007-2008 Vincent Torri + Copyright \copyright 2007 Bart Massey + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the authors or + their institutions shall not be used in advertising or otherwise to + promote the sale, use or other dealings in this Software without + prior written authorization from the authors. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: +\endquotation + + +This license applies to +\c{qtbase/src/3rdparty/xcb/xcb-util-renderutil/util.c}. + +\quotation + Copyright \copyright 2000 Keith Packard + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation, and that the name of Keith Packard not be used in + advertising or publicity pertaining to distribution of the software without + specific, written prior permission. Keith Packard makes no + representations about the suitability of this software for any purpose. It + is provided "as is" without express or implied warranty. + + KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +\endquotation + + + +\section3 xkb + +The following is a list of copyright notices and license statements in +\c{qtbase/src/3rdparty/xkbcommon}. + +\quotation + Copyright \copyright 2009-2012 Daniel Stone \break + Copyright \copyright 2012 Ran Benita \break + Copyright \copyright 2010, 2012 Intel Corporation \break + Copyright \copyright 2008, 2009 Dan Nicholson \break + Copyright \copyright 2010 Francisco Jerez + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + \hr + + + Copyright 1985, 1987, 1988, 1990, 1998 The Open Group + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the authors or their + institutions shall not be used in advertising or otherwise to promote the + sale, use or other dealings in this Software without prior written + authorization from the authors. + + \hr + + Copyright (c) 1993, 1994, 1995, 1996 by Silicon Graphics Computer Systems, Inc. + + Permission to use, copy, modify, and distribute this + software and its documentation for any purpose and without + fee is hereby granted, provided that the above copyright + notice appear in all copies and that both that copyright + notice and this permission notice appear in supporting + documentation, and that the name of Silicon Graphics not be + used in advertising or publicity pertaining to distribution + of the software without specific prior written permission. + Silicon Graphics makes no representation about the suitability + of this software for any purpose. It is provided "as is" + without any express or implied warranty. + + SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON + GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH + THE USE OR PERFORMANCE OF THIS SOFTWARE. + + \hr + + Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation, and that the name of Digital not be + used in advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR + ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + + \hr + + Copyright (C) 2011 Joseph Adams + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +\endquotation + + + +\section3 Additional Credits and Acknowledgements + +This software is based in part on the work of the Independent JPEG Group. + +Portions of this software are copyright \copyright 2012 The FreeType Project +(\l{http://www.freetype.org}). All rights reserved. + +We are very grateful to the following projects which are used in or distributed +alongside this software, and to the people behind these projects: + +\list +\li The \l{http://zlib.net/ zlib} Data Compression Library + (Jean-loup Gailly, Mark Adler) +\li The \l{http://www.libpng.org/pub/png/ libpng} PNG reference library + (Glenn Randers-Pehrson et al.) +\li The \l{http://www.sqlite.org/ sqlite} database engine +\endlist + diff --git a/doc/licensing/src/trademarks.qdocinc b/doc/licensing/src/trademarks.qdocinc new file mode 100644 index 0000000..19a6915 --- /dev/null +++ b/doc/licensing/src/trademarks.qdocinc @@ -0,0 +1,29 @@ +\omit +/* + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ +\endomit + + +Qt and the Qt logo are trademarks of The Qt Company Ltd. + +Digia and the Digia logo are trademarks of Digia Plc and/or +its subsidiaries in Finland and other countries. + +All other company, product, or service names may be trademarks or service marks +of others and are the property of their respective owners. diff --git a/doc/licensing/superbuild-licensing.cmake b/doc/licensing/superbuild-licensing.cmake new file mode 100644 index 0000000..bd6508a --- /dev/null +++ b/doc/licensing/superbuild-licensing.cmake @@ -0,0 +1,89 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +find_file(LICENSING_COPYRIGHT_DIR + NAMES copyright + PATH_SUFFIXES share/doc + DOC "Path to directory with 3rd-party component copyright files." +) + +find_file(LICENSING_COMMON_DIR + NAMES common-licenses + PATHS "${LICENSING_COPYRIGHT_DIR}" + NO_DEFAULT_PATH + DOC "Path to directory with common license files." +) + +if(NOT LICENSING_COPYRIGHT_DIR OR NOT LICENSING_COMMON_DIR) + message(STATUS "LICENSING_COPYRIGHT_DIR: ${LICENSING_COPYRIGHT_DIR}") + message(STATUS "LICENSING_COMMON_DIR: ${LICENSING_COMMON_DIR}") + message(FATAL_ERROR "Both LICENSING_COPYRIGHT_DIR and LICENSING_COMMON_DIR must be set") +endif() + + +# Based on OpenOrienteering superbuild as of 2017-04 +list(APPEND third_party_components + libjpeg-turbo + liblzma + libpcre3 + libpng + libtiff +) +if(NOT APPLE) + list(APPEND third_party_components + libcurl + libexpat + libsqlite + ) +endif() +if(NOT ANDROID AND NOT APPLE) + list(APPEND third_party_components + zlib + ) +endif() +if(ANDROID OR MINGW) + list(APPEND third_party_components + gnustl + ) +endif() + +list(APPEND common_license_names + Apache-2.0 + Artistic + BSD + GFDL-1.3 + GPL-1 + GPL-2 + LGPL-2 + LGPL-2.1 + LGPL-3 +) + + +# Map component names to package names +set(package_names + libcurl:curl + libexpat:expat + libpcre3:pcre3 + libsqlite:sqlite3 + libtiff:tiff +) + +set(copyright_pattern "${LICENSING_COPYRIGHT_DIR}/@package@*.txt") +set(common_license_pattern "${LICENSING_COMMON_DIR}/@basename@.txt") diff --git a/doc/licensing/ubuntu-licensing.cmake b/doc/licensing/ubuntu-licensing.cmake new file mode 100644 index 0000000..126ef62 --- /dev/null +++ b/doc/licensing/ubuntu-licensing.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/debian-licensing.cmake") diff --git a/doc/man/Mapper.1 b/doc/man/Mapper.1 new file mode 100644 index 0000000..76f3b52 --- /dev/null +++ b/doc/man/Mapper.1 @@ -0,0 +1,96 @@ +.TH MAPPER 1 2017-05-12 OpenOrienteering + +.SH NAME +Mapper \- orienteering map drawing software + +.SH SYNOPSIS +.B Mapper +.RI [ FILES ] + +.SH DESCRIPTION +OpenOrienteering Mapper is an orienteering map drawing software. +.PP +It comes with predefined symbol sets implementing the IOF standards: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +ISOM2000 (forest) (1:15000, 1:10000) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +ISOM2017 (forest) (1:15000, 1:10000) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +ISSOM2007 (sprint) (1:5000, 1:4000) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +ISMTBOM2010 (mountain bike) (1:20000, 1:15000, 1:10000, 1:7500, 1:5000) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +ISSkiOM2014 (ski) (1:15000, 1:10000, 1:5000) +.RE +.PP +But it is easy to implement other symbol sets. + +.SH OPTIONS +This program takes map file names as options. + +.SH AUTHOR +This manual page was written by Kai Pastor . + +.SH "REPORTING BUGS" +Report bugs in the OpenOrienteering ticket system: +.br + + +.SH COPYRIGHT +Copyright \(co 2017 The OpenOrienteering developers +.br +License GPLv3: GNU GPL version 3 . +.br +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. + +.SH "SEE ALSO" +Extended documentation for +.B Mapper +is available from the program's help menu and also online at: +.br + diff --git a/doc/manual/CMakeLists.txt b/doc/manual/CMakeLists.txt new file mode 100644 index 0000000..72b92e2 --- /dev/null +++ b/doc/manual/CMakeLists.txt @@ -0,0 +1,275 @@ +# +# Copyright 2012, 2013, 2014 Thomas Schöps +# Copyright 2012-2018 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +message(STATUS "Configuring ${PROJECT_NAME} user manual") + + +set(Mapper_MANUAL_QTHELP_default ON) +if(ANDROID) + set(Mapper_MANUAL_QTHELP_default OFF) +endif() +option(Mapper_MANUAL_QTHELP + "Provide the manual as Qt Help collection" + ${Mapper_MANUAL_QTHELP_default} +) + + +set(Mapper_MANUAL_PDF_default OFF) +option(Mapper_MANUAL_PDF + "Provide the manual as PDF file" + ${Mapper_MANUAL_PDF_default} +) + + + +# +# General target +# +add_custom_target(Mapper-manual ALL) + + + +# +# Creating doxygen input for HTML generation +# +configure_file(preprocess-markdown-html.cmake.in preprocess-markdown-html.cmake @ONLY) +add_custom_target(Mapper-manual-markdown-html + COMMAND "${CMAKE_COMMAND}" -P preprocess-markdown-html.cmake + BYPRODUCTS preprocess-markdown-html.stamp + SOURCES preprocess-markdown-html.cmake.in + COMMENT "Preprocessing Markdown for HTML output" +) + + + +# +# Doxygen HTML generation; creating input for optional Qt Help generation +# + +find_program(DOXYGEN_EXECUTABLE + NAMES doxygen + DOC "The path of the doxygen executable" +) +if(NOT DOXYGEN_EXECUTABLE) + message(FATAL_ERROR "doxygen executable not found.") +endif() + +set(Mapper_COMPRESSED_HELP "Mapper ${Mapper_VERSION_DISPLAY} Manual.qch") +set(Mapper_HELP_COLLECTION "Mapper ${Mapper_VERSION_DISPLAY} Manual.qhc") +set(Mapper_HELP_NAMESPACE "openorienteering.mapper-${Mapper_VERSION}.help" + CACHE INTERNAL "The namespace where the current version's help will be located." +) +if(NOT MANUAL_SECTIONS) + if(ANDROID) + set(MANUAL_SECTIONS_DEFAULT ANDROID) + elseif(APPLE) + set(MANUAL_SECTIONS_DEFAULT MACOS) + elseif(UNIX) + set(MANUAL_SECTIONS_DEFAULT LINUX) + elseif(WIN32) + set(MANUAL_SECTIONS_DEFAULT WINDOWS) + else() + set(MANUAL_SECTIONS_DEFAULT OTHER) + endif() + set(MANUAL_SECTIONS "${MANUAL_SECTIONS_DEFAULT}" CACHE STRING + "Conditional manual sections to be enabled") +endif() +message(STATUS "Conditional manual sections: ${MANUAL_SECTIONS}") + +set(DOXYFILE_HTML_EXTRA ) +if(ANDROID) + set(DOXYFILE_HTML_EXTRA " +FILE_PATTERNS = android* toolbar* +USE_MDFILE_AS_MAINPAGE = android-app.md +") +endif() + +configure_file(Doxyfile-html.in Doxyfile-html @ONLY) +configure_file(postprocess-qhp.cmake.in postprocess-qhp.cmake @ONLY) +add_custom_command( + OUTPUT "html/index.qhp" + COMMAND "${CMAKE_COMMAND}" -E remove_directory html + COMMAND "${DOXYGEN_EXECUTABLE}" Doxyfile-html + COMMAND "${CMAKE_COMMAND}" -P postprocess-qhp.cmake + DEPENDS preprocess-markdown-html.stamp + "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile-html" + "${CMAKE_CURRENT_BINARY_DIR}/postprocess-qhp.cmake" + "${DOXYGEN_EXECUTABLE}" + header.html + footer.html + style.css + COMMENT "Running doxygen for HTML output" +) +add_custom_target(Mapper-manual-HTML + DEPENDS "html/index.qhp" + # Sources to be listed in Qt Creator + SOURCES Doxyfile-html.in + header.html + footer.html + install-html.cmake.in + postprocess-qhp.cmake.in + style.css +) +add_dependencies(Mapper-manual-HTML Mapper-manual-markdown-html) + + + +if(Mapper_MANUAL_QTHELP) + # + # Qt Help generation + # + + find_package(Qt5Core 5.3 REQUIRED) + if (Qt5Core_VERSION VERSION_LESS 5.12) + set(qcollectiongenerator_name qcollectiongenerator) + else() + set(qcollectiongenerator_name qhelpgenerator) + endif() + + # Qt provides a broken Qt5::qcollectiongenerator when crosscompiling. + if (CMAKE_CROSSCOMPILING AND NOT TARGET Qt5::qcollectiongenerator) + find_program(Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE + NAMES ${qcollectiongenerator_name}-qt5 ${qcollectiongenerator_name} + DOC "The path of the Qt Help collection generator executable" + ) + add_executable(Qt5::${qcollectiongenerator_name} IMPORTED) + set_target_properties(Qt5::${qcollectiongenerator_name} PROPERTIES + IMPORTED_LOCATION ${Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE} + ) + elseif(NOT Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE) + find_package(Qt5Help REQUIRED) + set(Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE Qt5::${qcollectiongenerator_name}) + endif() + + # Reproducible builds need a modifications to the help collection file. + set(source_date_commands ) + find_program(SQLITE3_EXECUTABLE NAMES sqlite3 + DOC "The path of the sqlite3 executable" + ) + if (DEFINED ENV{SOURCE_DATE_EPOCH} AND SQLITE3_EXECUTABLE) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/timestamp.sql" + "update 'SettingsTable' set Value='$ENV{SOURCE_DATE_EPOCH}' where Key='CreationTime';\n" + "delete from 'SettingsTable' where Key='LastRegisterTime';\n" + ) + set(source_date_commands + COMMAND ${SQLITE3_EXECUTABLE} "${Mapper_HELP_COLLECTION}" < "timestamp.sql" + ) + elseif(DEFINED ENV{SOURCE_DATE_EPOCH}) + message(FATAL_ERROR + "By setting SOURCE_DATE_EPOCH, a reproducible build was requested. " + "sqlite3 is needed for reproducible builds but cannot be found." + ) + endif() + + configure_file(Manual.qhcp.in Manual.qhcp @ONLY) + add_custom_command( + OUTPUT "${Mapper_HELP_COLLECTION}" + "${Mapper_COMPRESSED_HELP}" + COMMAND "${Qt5Help_QCOLLECTIONGENERATOR_EXECUTABLE}" Manual.qhcp + -o "${Mapper_HELP_COLLECTION}" + ${source_date_commands} + MAIN_DEPENDENCY "html/index.qhp" + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/Manual.qhcp" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMENT "Running qcollectiongenerator for Qt Help output" + ) + add_custom_target(Mapper-manual-Qt-Help + DEPENDS "${Mapper_HELP_COLLECTION}" + SOURCES Manual.qhcp.in + ) + add_dependencies(Mapper-manual-Qt-Help Mapper-manual-HTML) + add_dependencies(Mapper-manual Mapper-manual-Qt-Help) + + install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/${Mapper_HELP_COLLECTION}" + "${CMAKE_CURRENT_BINARY_DIR}/${Mapper_COMPRESSED_HELP}" + DESTINATION "${MAPPER_ABOUT_DESTINATION}" + ) +else() + # + # Direct installation of manual HTML files + # + + add_dependencies(Mapper-manual Mapper-manual-HTML) + + configure_file(install-html.cmake.in install-html.cmake @ONLY) + install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install-html.cmake") +endif() + + +if(Mapper_MANUAL_PDF) + # + # PDF file generation + # + + find_program(PDFLATEX_EXECUTABLE + NAMES pdflatex + DOC "The path of the pdflatex executable" + ) + if(PDFLATEX_EXECUTABLE) + find_program(MAKE_EXECUTABLE + NAMES make + DOC "The path of the make executable" + ) + if(NOT MAKE_EXECUTABLE AND CMAKE_HOST_WIN32) + set(MAKE_EXECUTABLE make.bat) + endif() + endif() + + set(Mapper_PDF_manual "Mapper ${Mapper_VERSION_DISPLAY} Manual.pdf") + string(REPLACE "-" "_" Mapper_VERSION_DISPLAY_PDFLATEX "${Mapper_VERSION_DISPLAY}") + + configure_file(Doxyfile-pdflatex.in Doxyfile-pdflatex @ONLY) + configure_file(preprocess-markdown-pdflatex.cmake.in preprocess-markdown-pdflatex.cmake @ONLY) + configure_file(postprocess-pdflatex.cmake.in postprocess-pdflatex.cmake @ONLY) + add_custom_target(Mapper-manual-markdown-pdflatex + COMMAND "${CMAKE_COMMAND}" -P preprocess-markdown-pdflatex.cmake + BYPRODUCTS preprocess-markdown-pdflatex.stamp + Doxyfile-pdflatex-input.txt + SOURCES preprocess-markdown-pdflatex.cmake.in + ) + add_custom_command( + OUTPUT "${Mapper_PDF_manual}" + COMMAND "${DOXYGEN_EXECUTABLE}" Doxyfile-pdflatex + COMMAND "${CMAKE_COMMAND}" -P postprocess-pdflatex.cmake + COMMAND "${MAKE_EXECUTABLE}" -C pdflatex + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "pdflatex/refman.pdf" "${Mapper_PDF_manual}" + COMMAND "${CMAKE_COMMAND}" -E remove "pdflatex/refman.pdf" + MAIN_DEPENDENCY preprocess-markdown-pdflatex.stamp + DEPENDS Doxyfile-pdflatex + Doxyfile-pdflatex-input.txt + postprocess-pdflatex.cmake + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMENT "Running pdflatex for PDF output" + ) + add_custom_target(Mapper-manual-PDF + DEPENDS "${Mapper_PDF_manual}" + SOURCES Doxyfile-pdflatex.in + postprocess-pdflatex.cmake.in + ) + add_dependencies(Mapper-manual-PDF Mapper-manual-markdown-pdflatex) + add_dependencies(Mapper-manual Mapper-manual-PDF) + + install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/${Mapper_PDF_manual}" + DESTINATION "${MAPPER_ABOUT_DESTINATION}" + ) +endif() + diff --git a/doc/manual/Doxyfile-html.in b/doc/manual/Doxyfile-html.in new file mode 100644 index 0000000..77208ad --- /dev/null +++ b/doc/manual/Doxyfile-html.in @@ -0,0 +1,53 @@ +# +# Copyright 2014, 2015, 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +PROJECT_NAME = "Mapper" +PROJECT_NUMBER = "@Mapper_VERSION_DISPLAY@" +PROJECT_BRIEF = "User Manual" +PROJECT_LOGO = "@CMAKE_CURRENT_SOURCE_DIR@/../openorienteering.png" + +QUIET = YES + +INPUT = "@CMAKE_CURRENT_BINARY_DIR@/markdown-html" +STRIP_FROM_PATH = "@CMAKE_CURRENT_BINARY_DIR@/markdown-html" +IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/pages/images" +IMAGE_PATH += "@CMAKE_CURRENT_SOURCE_DIR@/../../images" +ENABLE_PREPROCESSING = NO +USE_MATHJAX = NO + +GENERATE_HTML = YES +DISABLE_INDEX = YES +GENERATE_TREEVIEW = NO +HTML_TIMESTAMP = NO +SEARCHENGINE = NO +HTML_HEADER = "@CMAKE_CURRENT_SOURCE_DIR@/header.html" +HTML_FOOTER = "@CMAKE_CURRENT_SOURCE_DIR@/footer.html" +HTML_STYLESHEET = "@CMAKE_CURRENT_SOURCE_DIR@/style.css" +HTML_EXTRA_FILES = "@CMAKE_CURRENT_SOURCE_DIR@/pages/attachment/scribble_1024.png" +HTML_EXTRA_FILES += "@CMAKE_CURRENT_SOURCE_DIR@/pages/attachment/scribble_2048.png" + +GENERATE_QHP = YES +QCH_FILE = "../@Mapper_COMPRESSED_HELP@" +QHP_NAMESPACE = "@Mapper_HELP_NAMESPACE@" +QHP_VIRTUAL_FOLDER = manual + +GENERATE_LATEX = NO + +ENABLED_SECTIONS = @MANUAL_SECTIONS@ + +@DOXYFILE_HTML_EXTRA@ diff --git a/doc/manual/Doxyfile-pdflatex.in b/doc/manual/Doxyfile-pdflatex.in new file mode 100644 index 0000000..c10f4e5 --- /dev/null +++ b/doc/manual/Doxyfile-pdflatex.in @@ -0,0 +1,44 @@ +# +# Copyright 2014, 2015, 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +PROJECT_NAME = "OpenOrienteering Mapper User Manual" +PROJECT_NUMBER = "@Mapper_VERSION_DISPLAY_PDFLATEX@" +PROJECT_BRIEF = "User Manual" +PROJECT_LOGO = "@CMAKE_CURRENT_SOURCE_DIR@/../openorienteering.png" + +QUIET = YES + +@INCLUDE = "@CMAKE_CURRENT_BINARY_DIR@/Doxyfile-pdflatex-input.txt" +STRIP_FROM_PATH = "@CMAKE_CURRENT_BINARY_DIR@/markdown-pdflatex" +IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/pages/images" +IMAGE_PATH += "@CMAKE_CURRENT_SOURCE_DIR@/../../images" +ENABLE_PREPROCESSING = NO +USE_MATHJAX = NO + +GENERATE_HTML = NO + +GENERATE_QHP = NO + +GENERATE_LATEX = YES +LATEX_OUTPUT = pdflatex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_TIMESTAMP = NO diff --git a/doc/manual/Manual.qhcp.in b/doc/manual/Manual.qhcp.in new file mode 100644 index 0000000..2ccacef --- /dev/null +++ b/doc/manual/Manual.qhcp.in @@ -0,0 +1,19 @@ + + + + @CMAKE_CURRENT_SOURCE_DIR@/../../images/mapper-help.png + OpenOrienteering Mapper Help + qthelp://@Mapper_HELP_NAMESPACE@/manual/index.html + + + + + html/index.qhp + @Mapper_COMPRESSED_HELP@ + + + + @Mapper_COMPRESSED_HELP@ + + + diff --git a/doc/manual/footer.html b/doc/manual/footer.html new file mode 100644 index 0000000..7325a1c --- /dev/null +++ b/doc/manual/footer.html @@ -0,0 +1,6 @@ +
    + + + diff --git a/doc/manual/header.html b/doc/manual/header.html new file mode 100644 index 0000000..0f51ad9 --- /dev/null +++ b/doc/manual/header.html @@ -0,0 +1,16 @@ + + + + + + OpenOrienteering $projectname: $title + + + +
    + +
    + OpenOrienteering $projectname $projectnumber +
    + diff --git a/doc/manual/install-html.cmake.in b/doc/manual/install-html.cmake.in new file mode 100644 index 0000000..206524d --- /dev/null +++ b/doc/manual/install-html.cmake.in @@ -0,0 +1,29 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/html/index.qhp" index_qhp) +string(REGEX MATCHALL "[^<]*" matches "${index_qhp}") +foreach(match ${matches}) + string(REGEX REPLACE ".*" "" match "${match}") + string(REGEX REPLACE ".*" "" match "${match}") + file(INSTALL + DESTINATION "${CMAKE_INSTALL_PREFIX}/@MAPPER_ABOUT_DESTINATION@/manual" + FILES "@CMAKE_CURRENT_BINARY_DIR@/html/${match}" + ) +endforeach() diff --git a/doc/manual/pages/android-app.md b/doc/manual/pages/android-app.md new file mode 100644 index 0000000..ea9605e --- /dev/null +++ b/doc/manual/pages/android-app.md @@ -0,0 +1,170 @@ +--- +title: The Mapper App for Android +authors: + - Kai Pastor + - Thomas Schoeps +keywords: Android +edited: 3 February 2018 +redirect_from: + - /android/ + - '/Android Manual/' + - /mapper-manual/pages/android-index.html +--- + +## Preliminary remarks + +The Android version of OpenOrienteering Mapper is not as self-descriptive as +the desktop version, so please read this first for important instructions. + +[Device requirements and recommendations](android-requirements.md){: .subpage} + +[Preparing a map on the PC](android-pc.md){: .subpage} + +[Storing Maps and Templates on Android Devices](android-storage.md){: .subpage} + +{% if doxygen %} +**Note:** The [online version](https://www.openorienteering.org/mapper-manual/android/) of this documentation may contain additions and corrections. +{% endif %} + + +## Using the Mapper app + +On startup, the app shows a list of map files available from the [supported storage locations](android-storage.md). To start editing a map, touch its filename. + +![ ](images/Android_UI_explanation.png) + +The map editor is slightly different from the desktop version. This page focuses on specific features of the mobile version. To find out about the other symbols, see [Toolbars](toolbars.md) in the desktop application's manual. + +#### ![ ](../mapper-images/arrow-thin-upleft.png) Hide the top bar + +Using this button, you may hide the top bar, leaving more screen for the map. A similar button will remain visible for restoring the top bar again. + + +#### ![ ](../mapper-images/compass.png) Compass display toggle + +When this item is activated, a compass is shown in the top-left corner of the screen. The red line indicates the north direction, while the black-white lines show the up direction of your device. If both are aligned, the device is aligned with magnetic north. When the needle is close to the north direction, a green circle appears which quickly shows how well the device is aligned: the larger it is, the better the alignment is. The compass works three-dimensionally, so you do not need to hold the device in a flat pose for it to work. + +Note: the usefulness of the compass depends on the presence of a gyroscope, as mentioned in the device recommendations section. + +Attention: the compass is very sensitive to magnetic materials. Android tries the eliminate the influence of local magnetic fields, but this requires calibration. To do this, move the device around in the shape of an 8 while rotating it and while the compass is active. If the local influence changes, re-calibration is required. + + +#### ![ ](../mapper-images/tool-gps-display.png) Location display toggle + +If the currently active map work has valid georeferencing, this control will be enabled and will show a dot at your current position as obtained from GPS or other positioning services. In addition, in case an accuracy estimate (HDOP) is available, this shows a circle around the dot which indicates the positioning accuracy. The chance of your real position being in the circle is approximately 70%. + +While this is active, your track is automatically recorded into a GPX file in the directory of your map, named " - GPS-.gpx". This file is included as a template in the map. You can simply show it using the template visibility control button to view your track. + +Note that track recording might be interrupted while the screen is off or the app is in the background to save battery. Keep the app active if you want to ensure continuous track recording. + + +#### ![ ](../mapper-images/gps-distance-rings.png) Distance rings toggle + +This button is active if location display is active. When activated, this will display rings in 10 meter and 20 meter distance from the current location which can be used to do rough distance estimates. + + +#### ![ ](../mapper-images/rotate-map.png) Automatic north alignment toggle + +When this toggle is active, the map is automatically rotated to north using the device's internal compass. The update frequency is 1 update per second. This is quite low on purpose to save battery, but you probably still need some replacement batteries when using this mode. While interacting with the map and for another 1.5 seconds after that, the map is not rotated to allow editing without unwanted rotations. If GPS position display is activated at the same time, a line starting from the GPS position and going in the direction of the up direction of your device is displayed which shows your viewing direction (assuming you are holding the device upright). + + +----- + +#### ![ ](../mapper-images/map-parts.png) Map parts selector + +This button opens a popup for changing the active map part. + + +#### ![ ](../mapper-images/tool-touch-cursor.png) Touch cursor toggle + +This symbol activates a helper cursor which allows slow, but precise editing with fingers only. The cursor consists of a circular area and a pointer above the circle. + +![ ](images/touch_cursor.png) + +Touching the map anywhere outside the circle moves the pointer to this position. Touching the circle simulates a real touch at the pointer position above the circle. + + +#### ![ ](../mapper-images/templates.png) Template control + +This shows a list of all opened templates. Touching a template toggles its visibility. + + +#### ![ ](../mapper-images/close.png) Close button + +This closes the active map and returns to the map selection screen. + + +#### ![ ](../mapper-images/three-dots.png) Overflow button + +Depending on the screen size of your device, some of the symbols available not fit onto the screen. They are instead placed in a list which is shown on touching the overflow button. If all symbols fit on the screen, this button is unused. + + +----- + +#### ![ ](../mapper-images/view-zoom-out.png) Zoom out / Zoom to fixed + +A short press will zoom out by one step. A long press will open a menu where you can jump to a fixed zoom (cf. screenshot). + + +#### ![ ](../mapper-images/move-to-gps.png) Move to location + +When positioning services are enabled, this will move the map so that your current location is in the middle of the screen. + + +#### ![ ](../mapper-images/gps-temporary-point.png) Mark temporary position + +This records a single point at your current location to act as a drawing help. Note that this marker is not saved in the map. + + +#### ![ ](../mapper-images/gps-temporary-path.png) Record temporary trace + +This symbol is enabled when the location display is active. It records a temporary trace of the location, which is intended to act as a guideline for drawings. Using the trace directly is rarely useful because of the inaccurateness of location services. +Note that the temporary trace is not saved in the map. + + +#### ![ ](../mapper-images/gps-temporary-clear.png) Clear temporary markers + +This removes the temporay traces and position markers. (They will also be discarded when closing the map file.) + + +#### ![ ](../mapper-images/pencil.png) Paint on template + +By this tool, mapper supports free-hand drawing onto image templates. There is +a small selection of colors for drawing. White is used for corrections to +previous drawings. There is also a dedicated undo/redo feature. + +This tool is an convenient alternative when you want to do only (or mostly) +drafting in the field, but draw the final version of the map at home on a PC. +It does not create new map objects but only alters the template you draw on. +This may help to deal with low processing power or battery runtime on mobile +devices. + +Please note that direct drawing onto base map images is *not* recommended: +First, the eraser tool would also erase the base map. Second, these images +usually are in the lossy JPEG format which will introduce visible artifacts +when saving the file and loading it repeatedly. + + +#### ![ ](../mapper-images/paint-on-template-settings.png) Paint-on-template settings + +This button allows to select an image template for scribbling, +or to add a new one covering the vicinity of your position. + + +----- + +#### ![ ](../mapper-images/draw-freehand.png) Free-hand draw tool + +Allows to draw paths free-handedly. + + +#### ![ ](../mapper-images/draw-point-gps.png) Draw at location tool + +Sets a point object at your current location. Selecting this button first enters an averaging mode. While it is active, the input from the location service is averaged to get a more accurate estimate. Touch the map display anywhere to finish the averaging. + + +#### Symbol selector + +Displays the active symbol, and opens the symbol selection screen on short press. + +A long press of the symbol selector opens a popup menu which allows to read the symbol name and description, and to toggle the visibility and the protection state of the symbol. diff --git a/doc/manual/pages/android-pc.md b/doc/manual/pages/android-pc.md new file mode 100644 index 0000000..4b09aaf --- /dev/null +++ b/doc/manual/pages/android-pc.md @@ -0,0 +1,25 @@ +--- +title: Preparing a Map on the PC +--- + +The Android app cannot be used to create new maps because it would be cumbersome to setup the base maps there. Instead, maps should be prepared with templates and possibly georeferencing using the PC version of Mapper first and then transferred to the mobile device for surveying. + +If you want to use live GPS display, the map must be georeferenced. There are several options how to do this: + + +## Georeferencing when you create a new map + +The first thing to do after creating a new map should be to load a georeferenced template. If you do not have one, you may use for example an OpenStreetMap export in case there is sufficient data at the map's location. To do so, go to https://www.openstreetmap.org, locate the map region, and click 'Export'. Then select an area which covers all of your templates and save the export as .osm file. This file can then be loaded into Mapper as a georeferenced template. + +On loading the first georeferenced template, Mapper will show the georeferencing dialog. This may look a bit intimidating at first, but when loading a template, usually little needs to be changed there. First, the projection type should be checked. If you do not know which projection to use, just select UTM. Second, the magnetic declination should be entered to align the map with magnetic north. You may simply use the lookup button to look it up on the internet for the place of the template you loaded. However, be aware that the lookup only uses a model which may deviate from the real declination. It may be advantageous to check this in the terrain. All remaining values in the georeferencing dialog are already initialized using the newly loaded template, so you can then finish the dialog. + +Then load all remaining templates and finish preparing the map. Non-georeferenced templates will need to be moved to match the location of the georeferenced templates. + + +## Georeferencing when you use an existing map + +In this case, the easiest way is to first create a new map with the same symbol set as the existing map. The old map can be selected as the symbol set source in the new map dialog to do this. Then load a georeferenced template as detailed in the paragraph above (use e.g. OpenStreetMap if you do not have a georeferenced template). + +After that, load the existing map as template in the new map. Then use the alignment tools for templates to move the existing map to match with the new georeferenced template. When you are finished, import the existing map template into the new map file using the corresponding action in the template dock widget, and you should be done. + +Attention: especially for old maps, the whole existing map may be locally distorted. In this case it will not be possible to match the old map and the georeferenced template and the only way to georeference the map is to do a tedious undistortion. Mapper does not provide a tool for this. diff --git a/doc/manual/pages/android-requirements.md b/doc/manual/pages/android-requirements.md new file mode 100644 index 0000000..683a19e --- /dev/null +++ b/doc/manual/pages/android-requirements.md @@ -0,0 +1,35 @@ +--- +title: Device Requirements and Recommendations +keywords: Android +--- + +## Android version + +The minimum version for running the app is 2.3. Write access to SD cards may require Android 4.4 (cf. [Storage locations](android-storage.md)) + + +## Water-proofing + +For obvious reasons, water-proof devices may be advantageous. + + +## Stylus + +For doing any serious work, a stylus is a must-have (because of precision & speed, and because of temperature during winter). You should preferably look for devices which come with an adapted stylus such as the Samsung Galaxy Note series, as general styluses for capacitive touch screens have rather wide tips. + + +## Screen size + +There is a compromise between ease of handling (better on smaller devices) and overview (better on larger devices). + + +## Battery + +It is very desirable to use a device with replaceable battery. It may be possible to use an external battery pack for devices with integrated battery. + + +## Sensors + +OpenOrienteering Mapper can use integrated GPS receivers. However, their accuracy may be low, so it may be advantageous to connect to an external GPS receiver. There is no direct support for external devices in Mapper, but some third party apps bridge the gap. For example the [Bluetooth GPS app](https://play.google.com/store/apps/details?id=googoo.android.btgps&rdid=googoo.android.btgps) provides a "mock" location provider which can be used when Android is put in developer mode. + +The app can use a magnetometer and accelerometer as a compass. Almost all modern devices should contain these sensors. If a gyroscope is also available, it will be used to improve the compass stability. However, these sensors are typically not accurate enough to be used for measurements. They can be used for convenience when not relying on a compass, but for accurate measurements, an external compass is recommended. diff --git a/doc/manual/pages/android-storage.md b/doc/manual/pages/android-storage.md new file mode 100644 index 0000000..c2c3cdc --- /dev/null +++ b/doc/manual/pages/android-storage.md @@ -0,0 +1,51 @@ +--- +title: Storing Maps and Templates on Android Devices +keywords: Android +--- + +## Storage locations + +Android organizes storage and permissions different than PCs. +Especially on SD cards, write access for apps is restricted. + +You can choose the following locations for storing data which you want to edit and to display in OpenOrienteering Mapper: + + - Folder "OOMapper" at the top level of the primary storage volume. On most devices, the primary storage volume is a part of the built-in storage. + Files in this area are stored permanently, until you decide to remove them. + Unfortunately, there is usually only limited capacity available - this memory is expensive when buying the device. + + - Path "Android/data/org.openorienteering.mapper/files" on arbitrary storage volumes such as SD cards (since Mapper 0.6.5). + This is the only convenient way to store map data on cheap extra memory cards. + However, you need to be aware of the fact that these folders are removed by Android when you uninstall the Mapper app. + So please carefully save your changes to a PC. + +OpenOrienteering Mapper for Android will create these folders when they are missing, and scan them for map files. It will display warnings when you open maps from the non-permanent app-specific folders or from read-only locations. (Top-level "OOMapper" folders on secondary storage volumes are scanned, but cannot be written to.) + + +## File transfer + +You can transfer files from and to a PC via a USB cable. Android supports multiple file transfer protocols. + +- MTP is the preferred method now. There is hardly any interference for apps while the device is connected to the PC. + Via MTP, Android will only show files which are known to its Media Scanner. + +- Mass Storage makes the storage unavailable for apps for the duration of the connection with the PC. + (Android also needs to terminate apps which are stored on the volume which is provided as mass storage.) + Unlike MTP, mass storage does not depend on the media scanner, so all files are always visible. + +Note that after mapping, you might want to transfer back not only the modified map but also GPX tracks and templates you painted on. + + +## Data loss prevention and recovery + +*Remember to keep backups and to verify transferred files.* + +If a file appears to be corrupted after transfer back to PC, or you cannot find +a new file such as a recorded GPX track, a reboot of the Android device could +solve the issue. After the reboot, Android's media scanner will take notice of +new files and changed file sizes. However, Mapper v0.8.3 is expected to bring a +solution to this issue. + +In some situations, the Mapper app might not be able to properly save data and +shutdown as quickly as requested by the Android operating system, for example +when you start other apps or when the device runs out of power. diff --git a/doc/manual/pages/attachment/scribble_1024.png b/doc/manual/pages/attachment/scribble_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..2de8cacee616aa60b0305b4ec91f47c6302974a1 GIT binary patch literal 5733 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7&zE~RK2WrGmv6S@^*J&_z!{$_AZ~yz#t~& z>EaktaqI1^je-pZJS+#ro#nsWpSseePlKt_Jm}BE-YivFpvHtPr$MF}-2W@m_URAh``6> z0LeFi2nK>o;!IQknbiOy2%CsxC5;SM06Sz57>?vs;+UkkoH!F3NDT`}!l9Aj5Z4R> z!$}Efuv$`FPKx2AgaueFtqdn2yNo6g^uXd^P-6fB;tKacQ*(f88G>~Yk^-;>2arz* z*Lfs_1ti>PWH^z|MY5d)my=>RQaBRFq?O@FUL}r6oO5X!i!?Qylz@h)9Zez(K!6l( zgCL1GFc^TMg_H^vy%<*j>c`P?ZU9RWXSf1b?I19mIOigHjTD!YVmJw5G1|Dm8dyk? zM370VSOmv7tqjNNYe-_iZY7PH>W~!BDi*;FR9YF1)z^^3fZa;sn#+)2A;EB@a3qpRBg3)# z8jmQE&ZTKAQeybOWnq`n7=S~UH*0_-!vRLKJMR4Zmo3^_2jY6V`njxgN@xNAr~a)2=YKS~=Tves9y) z)(6KPzo-mVpB&cLtv!5JZe5qeeczQz62)GUVD{qh+|XPmy|Gq)y7=YQPX9P;^iu4+ zB$=!i8zV0{3A~uK5hv~MZhQyAPYSvej7S9Z2yR0JfB8>5B2W+^iIB>lpjkvnAwnt= zh>%1`HG=?Z05yObKn+v_P=oDH4c-h?8wPEzbHDmtzdd-}#QxZ>)?BcWam;1cZ;2E( z@|C=ZdQz*vC_FsC44xvj1^gra5&xKI04M;3EeC}|D?z*3_Hy{RI|(!*}NDc~RR zkNC$#13&>N00p3+8mP(T4Ku^c=(ZdFEKh*I(jF%T6o5jaftrLuRQ`M?l*0Hx@9gCA zt&#S3H>2fGyKTI&OGa%M>WQ}Nrs3m{mb7Aj-#0rKKqs0a0n8q}o6DoZUZ}9Aeo&DR z#fFEZQAS6EgcuqWV8ld7&A@VW6&3bGNOOo#1E>Mi0BT?w*gR@*yW`%uq(_a#GnZ58 r|FC@K%7r+i&!YEhAFhts`Iy@3g7F>uXR4=fEZMixk)GmISO4fA&zPkU literal 0 HcmV?d00001 diff --git a/doc/manual/pages/color_dock_widget.md b/doc/manual/pages/color_dock_widget.md new file mode 100644 index 0000000..e1375d8 --- /dev/null +++ b/doc/manual/pages/color_dock_widget.md @@ -0,0 +1,53 @@ +--- +title: Color Window +authors: + - Peter Hoban + - Thomas Schoeps +keywords: Colors +edited: 26 February 2013 +--- + +This window can be shown by clicking the menu item Symbols -> Color window. + +![ ](images/color_dock_widget.png) + +#### Introduction + +Colors on orienteering maps are defined in the orienteering map standards, ISOM and ISSOM, as colors from the Pantone Matching System (PMS). The recommended way to print maps is to use these spot colors to print each color layer separately onto the map sheet. This way colors printed later will overlap colors which are printed earlier. The color system in OpenOrienteering Mapper follows this analogy: each map file contains a prioritized list of available colors, where colors higher in the list overlap lower colors. + +Colors can be managed with the buttons at the bottom of the list. To edit a color, double click its entry in the color list. + +### Color editor + +Today there are different methods for printing maps and the color editor in OpenOrienteering Mapper is adjusted to that, so there are different settings for "desktop" and "professional" printing. + +#### Desktop printing + +![ ](images/color_editor_desktop.png) + +The first page addresses "desktop" printing, i.e. printing maps with cheap home laser printers. These devices use the **RGB color space** to define colors, where each color consists of a given amount of red, green and blue (this is not to be confused with how the colors are actually printed; it is just how the PC tells the printer which color to print). So on the desktop page, you can define how the RGB value for a color should be calculated by OpenOrienteering Mapper: + + + - **Evaluate spot colors**: the spot colors referenced by this color will define its RGB color. This is only possible if this color references at least one spot color (see later) + - **Evaluate CMYK color**: the CMYK value of this color will be converted to a RGB value (see later) + - **Custom RGB color**: you can enter the RGB value directly + + +Note that while defining RGB colors is enough for normal desktop printing, using the [overprinting simulation](view_menu.md#overprinting) effect requires to define spot color printing options also. + +#### Professional printing + +![ ](images/color_editor_professional.png) + +This page is divided into two parts. On the left are the settings for **spot color printing**: + + - If the first option is activated, this color entry represents a spot color and will thus define a color layer. Enter the spot color name in the text field below the option. + - If the second option is activated, this color entry represents a color which is printed as a percentage of a spot color (screen) or a mixture of multiple spot colors (overprint). Choose the referenced spot colors and their amounts below. + +When multiple colors overlap each other on the map and [overprinting simulation](view_menu.md#overprinting) is activated, the program simulates the effect of overprinting real spot colors where the colors are darkened when printed on top of each other. This can be very useful to increase map legibility, however it is not desired in all situations. Sometimes a color on top of another should erase the lower color completely. This can be set by activating the "**Knockout**" option. + +On the right side of the professional page, there are the options for **CMYK color printing**. These are similar to the RGB color options: + + - **Evaluate spot colors**: the spot colors referenced by this color will define its CMYK color. This is only possible if this color references at least one spot color + - **Evaluate RGB color**: the RGB value of this color will be converted to a CMYK value + - **Custom process color**: you can enter the CMYK value directly diff --git a/doc/manual/pages/colors_symbols.md b/doc/manual/pages/colors_symbols.md new file mode 100644 index 0000000..5bdb457 --- /dev/null +++ b/doc/manual/pages/colors_symbols.md @@ -0,0 +1,15 @@ +--- +title: Colors and Symbols +edited: 26 November 2015 +--- + +Modifying map colors and symbols + +[Color Window](color_dock_widget.md){: .subpage} +Color system explanation and working with colors + +[Symbol Pane](symbol_dock_widget.md){: .subpage} +Types of symbols and how to create a new symbol + +[Symbol Replacement Dialog](symbol_replace_dialog.md){: .subpage} +Replacing the current symbol set by another, useful for updating the symbol set version diff --git a/doc/manual/pages/course_design.md b/doc/manual/pages/course_design.md new file mode 100644 index 0000000..b83e902 --- /dev/null +++ b/doc/manual/pages/course_design.md @@ -0,0 +1,74 @@ +--- +title: Using the Course Design Symbol Set +keywords: Courses, Symbol sets +--- + +[Templates]: templates.md +[IOF Homepage]: https://orienteering.org + +OpenOrienteering Mapper doesn't have a sophisticated course planning feature. +The Course Design symbol set provides basic support for designing maps with +courses and control desriptions. It contains the overprinting symbols from the +ISOM 2000 and the control descriptions from IOF's "International Specification +for Control Descriptions - 2004". See the [IOF Homepage] for details. + +![ ](images/course_design.png) + + +## Loading a base map + +Open the base map as a template in the background. See [Templates] for details. + + +## Drawing the course + +For simple courses which consist only of start, controls, finish and free +navigation you may use symbol 799 Simple Orienteering Course. For more complex +courses use the individual overprinting symbols. The labels for the controls +have to be created explicitly. + + +## Creating the control description sheet + +The Course Design symbol set contains the individual pictograms as well as +symbols for the different sections which together form the control description +sheet. The pictograms' names indicate the column of the control description sheet where +they are to be used. + +The control description symbols are meant to be used with a grid of horizontal +and vertical lines spaced 3.0 millimeters. By pressing the Shift key while +creating a new element, you may easily align all the parts of the description. + + +## About the control description sheet + +The control description sheet starts with a heading which consists of blocks for +the event title, classes (optional), and (in a single row) course code, course +length and and height climb. The course length is given in kilometres rounded to +the nearest 0.1 km. The height climb is given in metres rounded to the nearest +5 m. + +The heading is followed by lines describing the start location, the individual +controls, and special routes. The description of start and individual controls +uses eight colums which are refered to as A...H. + +| Column | Description | +| ------ | ----------- | +| A | Control number. Numbering of controls is in the sequence they are to be visited, unless the description is for a Score competition. | +| B | Control code. The control code should be a number greater than 30. | +| C | Which of any similar feature. This column is used when there is more than one similar feature within the control circle; e.g. south eastern. | +| D | Control feature. The feature, as shown on the map, at the centre of the circle deÞ ning the control site; e.g. clearing; boulder. The description of each control is based on the International Specification for Orienteering Maps (ISOM 2000). | +| E | Appearance. Further information on the nature of the feature if it is required; e.g. overgrown; ruined. In certain circumstances also used for a second control feature where the description requires this. | + | F | Dimensions / Combinations. Dimensions of the feature should be given where the size of the control feature on the map is symbolic rather than to scale. Also used for the two combination symbols (crossing; junction). | +| G | Location of the control flag. Position of the control flag with respect to the feature; e.g. west corner (outside); south foot. | +| H | Other information. Other information that may be of importance to the competitor; e.g. radio control; refreshments. | + + +## Specifications for Trail Orienteering + +There are two variations in the use of the columns when using IOF Control Descriptions for Trail Orienteering. + +| Column | Description | +| ------ | ----------- | +| B | Number of control flags. This column is used to denote the number of control flags visible at this control; e.g. A-C equals three control flags to choose from; A-D equals four control flags to choose from. | +| H | Direction of observation. This column is used to denote the direction in which to view a feature. For example an arrow pointing north indicates that the competitor should be on a path/track to the south of the control circle. | diff --git a/doc/manual/pages/crt_files.md b/doc/manual/pages/crt_files.md new file mode 100644 index 0000000..09515a3 --- /dev/null +++ b/doc/manual/pages/crt_files.md @@ -0,0 +1,66 @@ +--- +title: CRT Files +authors: + - Kai Pastor +edited: 21 January 2018 +--- + +## Introduction + +Cross Reference Table (CRT) files provide rules for assigning and replacing symbols. +They are used in the following situations: + + - replacing a map's symbol set (menu Symbols > Replace symbol set...), + - replacing symbols within the current map (menu Symbols > Load CRT file...), + - importing geospatial vector data (menu File > Import...). + +To facilitate the automatic lookup of a suitable CRT file, CRT files may be placed in the symbol set folder +and named following this pattern: + +SOURCE_ID-TARGET_ID.crt + +where SOURCE_ID is a symbol set ID (menu Symbols > Symbol set ID...) or an import driver name (e.g. OCD, OSM, DXF), +and TARGET_ID is another symbol set ID (either of this map or of the replacement symbol set). +Examples: + + - OSM-ISOM2017.crt will be used when importing data through the OSM driver into an ISOM2017 map. + - ISOM2000-ISOM2017.crt will be used when replacing the symbols of an ISOM2000 map with an ISOM2017 symbol set. + + +## CRT file syntax + +Empty lines are ignored. Lines starting with a # character are considered comments, i.e. they are ignored, too. +Every other line is a replacement rule and consists of two parts. + +The first part of the rule is the target symbol number. It determines which symbol will be assigned to matching objects. + +The second part of the rule, separated from the first part by white space, determines the objects this rule applies to. +This can be given either as a symbol number or as a [object query](find_objects.md#advanced-query-language). +When using object queries, more specific rules need to follow more general rules. + +Example from OSM-ISOM2017.crt: + + - 406 natural = wood + - 408 natural = wood AND (wood:age = young OR wood:age = very_young) + - 404 natural = wood AND wood:density = sparce + - 408 natural = wood AND wood:density = dense + +Example from ISOM2000-ISOM2017.crt: + + - 102 102 + - 103 103 + - 101.1 104 + - 102.1 105 + + + +## CRT file editing + +CRT files may be edited with any text editor, e.g. notepad in Windows. +However the .crt filename extension is commonly associated with certificate files. +You will probably have to select an "All files" filter in the editor's Open-file dialog, +or temporarily change the extension to .txt. + +When Mapper displays the symbol replacement dialog, modified symbol assignments can be saved to a CRT file via the "Symbol mapping" button. +Note that this dialog shows the assigned symbol in the second column, while in the CRT file, assigned symbols are recorded at the beginning of the line. + diff --git a/doc/manual/pages/edit_menu.md b/doc/manual/pages/edit_menu.md new file mode 100644 index 0000000..9201a6b --- /dev/null +++ b/doc/manual/pages/edit_menu.md @@ -0,0 +1,92 @@ +--- +title: Edit Menu +authors: + - Peter Hoban + - Thomas Schoeps + - Kai Pastor +keywords: Menus +edited: 21 January 2018 +--- + +#### ![ ](../mapper-images/undo.png) Undo +**Ctrl+Z** + +This most important function reverses the last change made to the map. Repeating 'Undo' will undo earlier changes, one step at a time. **Attention:** While changes to map objects can be undone this way, not all map changes are covered by the undo system. This includes changes to colors, symbols or templates. + + +#### ![ ](../mapper-images/redo.png) Redo +**Ctrl+Y, Ctrl+Shift+Z** + +When you have taken Undo too far this will reverse the process — again one step at a time. + + +--- + +#### ![ ](../mapper-images/cut.png) Cut +**Ctrl+X** + +This removes the selected object(s) and moves them to the clipboard. + + +#### ![ ](../mapper-images/copy.png) Copy +**Ctrl+C** + +This copies the selected object(s) to the clipboard. + + +#### ![ ](../mapper-images/paste.png) Paste +**Ctrl+V** + +This inserts the map object(s) on the clipboard into the map. They will be centered at the view midpoint. **Note:** When copying objects between different maps, their symbols and colors may be copied too in order to be able to display the objects in the new map in the same way as in the source map. + + +#### ![ ](../mapper-images/delete.png) Delete +**Del** + +This action deletes the selected object(s). + + +--- + +#### Find... + +Opens the ["Find objects" dialog](find_objects.md) which can be used to find objects by contained text, symbol name or tag keys and values. + + +#### Find next + +Finds the next object matching the last query from the ["Find objects" dialog](find_objects.md). + + +--- + +#### Select all +**Ctrl+A** + +This selects all objects in the current map part. + + +#### Select nothing +**Ctrl+Shift+A** + +After this action, no object is selected. + + +#### Invert selection +**Ctrl+I** + +This action inverts the selection in the current map part. + + +#### Select all objects with selected symbols + +After this action, the selection consists of all objects in the current map part which have the selected symbol(s). + + +--- + +#### Clear undo / redo history + +Clears the history of undo and redo steps, i.e. all undo and redo steps will be deleted. This reduces the file size for map file formats where the last undo and redo steps are stored, e.g. the omap format. + + diff --git a/doc/manual/pages/faq.md b/doc/manual/pages/faq.md new file mode 100644 index 0000000..b1f1c8b --- /dev/null +++ b/doc/manual/pages/faq.md @@ -0,0 +1,91 @@ +--- +title: Frequently Asked Questions (FAQ) +authors: + - Peter Hoban + - Kai Pastor + - Thomas Schoeps + - Libor Pechacek +keywords: FAQ +edited: 5 December 2017 +--- + +* TOC +{:toc} + + +## Navigation + +### How do I move the map? + +The easiest way is to hold the middle mouse button while moving the mouse. But you can also press F6 and move the mouse, or use the corresponding toolbar button, or use the cursor keys. + +### How to zoom in and out? + +You can use the mouse wheel, press F7/F8 or +/- keys, or click the zoom buttons in the toolbar. + + +## Drawing + +### I want to start drawing a map, but have no idea how to. + +Start by reading the [short introduction to o-mapping](mapping-introduction.md). + +### How do I add points to an existing path or delete points from it? + +With the path object selected using the edit tool, hold Ctrl and click a free spot on the path to create a new point there. You can even move the mouse in the same action to set the position of the new point. To delete a point, hold Ctrl and click an existing point. + +### How do I change line segments from curved to straight and vice versa? + +With the [line edit tool](toolbars.md#tool_edit_line), select the object you want to edit and hold Ctrl while clicking the line segment. This switches the segment between curved and straight. + + +## Advanced editing + +### How can I cut out a part of a map to take it for a new map? + +First you save the map to the new file. Then draw some closed line or area (symbol doesn't matter) arround the border of the part which you want to keep. Now when this border object is finished and selected, click on the "Cutout" icon or menu item, then press the enter key. + + +## File formats + +### What are the advantages of the .omap format? + +The .omap format is OpenOrienteering Mapper's native format. It is XML-based, which means for example that it is relatively easy for other developers to access the information in map files. Additionally, it is platform independent, error-resistant and makes it possible to keep a high compatibility between different versions of the format. + +### What is the difference between .xmap and .omap format? + +The .xmap format is nearly identical but more verbose, even suitable for direct manual editing. The .omap format is tuned towards minimum size and fast processing. +If you want to keep you data in a line-based version control system such as git, you must use the .xmap format. + +### Can Mapper read and write .ocd files? + +Yes, Mapper reads .ocd files from different version (8 .. 12). But currently only version 8 .ocd files can be written. Beware that some details are neither loaded from nor saved to .ocd files, and some additional inaccuracies might occur. + + +## Mobile devices + +### Will Mapper be available for Android? + +An Android version is available. It already has a special user interface, but it needs a lot of work. See the [instructions for the Android app](android-app.md). + +### Will Mapper be available for iPhone / iPad? + +Probably not, due to distribution issues. Technically, a build should be feasible. + +### How do I hide/protect a symbol in the mobile UI? + +Tap-and-hold on the Symbol selector button. Context menu with hide and protect options will appear. Hidden and protected symbols are marked with usual signs in the symbol list. + + +## Experienced mappers' corner + +### What are the most notable differences to OCAD? + +* Whole objects can be moved by dragging the dashed magenta bounding box when [**Edit objects** tool](toolbars.md#tool_edit_point) is selected. +* There is a [single tool for drawing straight lines and Bézier curves](toolbars.md#tool_draw_path). +* [Another tool](toolbars.md#tool_draw_circle) combines circle and ellipse drawing. +* Addition and removal of path points (vertices) can be achieved via [**Edit objects** tool](toolbars.md#tool_edit_point) using various combinations of keyboard modifiers. See the status line at the bottom edge of the window for dynamically updated help. See also [Drawing](#drawing). +* Dash points can be added and removed using the [**Edit objects** tool](toolbars.md#tool_edit_point) after pressing **Ctrl-Space** on keyboard. +* Corner points do not exist per se. Move the Bézier curve handles independently while holding keyboard **Shift** key. +* Edge lines of double line symbols (roads et al) are inactive and cannot be snapped on. Mapper does not support cutting out parts of the side lines. +* Partial map export is done via a tool. See [Advanced editing](#advanced-editing). diff --git a/doc/manual/pages/file_menu.md b/doc/manual/pages/file_menu.md new file mode 100644 index 0000000..2a8ced3 --- /dev/null +++ b/doc/manual/pages/file_menu.md @@ -0,0 +1,84 @@ +--- +title: File Menu +authors: + - Peter Hoban + - Kai Pastor + - Thomas Schoeps +keywords: Menus +edited: 29 November 2015 +--- + +These controls provide standard file management tools. + +#### ![ ](../mapper-images/new.png) New +**Ctrl+N** + +This shows the dialog to create a [new map](new_map.md). + + +#### ![ ](../mapper-images/open.png) Open... +**Ctrl+O** + +Shows the dialog to open an existing map file. Currently supported formats are .omap, .xmap or .ocd (versions 6 to 12). + + +#### Open recent + +This shows a list of recently opened files, recognizing that a file to be opened will often have been previously opened in the recent past. + + +#### ![ ](../mapper-images/save.png) Save +**Ctrl+S** + +Saves the currently opened file. OCD files will be saved in version 8 format. + + +#### Save as... +**Ctrl+Shift+S** + +Shows the file dialog for choosing a file name and file format to save the currently opened map. + + +--- + +#### Import... + +Permits the import of maps or data in other formats. Currently the following formats are supported: .omap, .xmap, .ocd (version 6 to 11), .gpx, .osm, .dxf. + + +#### Export as... + +Permits the export of the currently opened map to another format. Currently pdf and raster image export are supported. + +For raster images, Mapper may additionally create world files. A world file is a small text file which defines the image's georeferencing relative to the map's projected coordinate reference system. Note that these files contain only six numerical parameters. They do not contain an identification of the projection, coordinate system or datum they refer to. The filename is derived from the image's filename, but with a modified filename extension usually ending with letter 'w'. + + +--- + +#### ![ ](../mapper-images/print.png) Print +**Ctrl+P** + +Shows the print dialog to print the currently opened map. + + +--- + +#### Settings + +Opens the [settings dialog](settings.md). (On OS X, the settings are in the usual place instead.) + + +--- + +#### ![ ](../mapper-images/close.png) Close +**Ctrl+W, Ctrl+F4** + +Closes the current program window. + + +#### Exit +**Ctrl+Q** + +Exits the program, which will close all open map windows. + + diff --git a/doc/manual/pages/find_objects.md b/doc/manual/pages/find_objects.md new file mode 100644 index 0000000..b2f84c5 --- /dev/null +++ b/doc/manual/pages/find_objects.md @@ -0,0 +1,111 @@ +--- +title: Find objects +authors: + - Mitchell Krome + - Kai Pastor +keywords: Tagging +edited: 21 January 2018 +--- + +The "Find objects" dialog allows to find and select objects in the current map part based on their tags, text, or symbol name. +It is opened from the View menu. + +![Find objects dialog](images/find_objects.png) + + +## Querying objects + +The main element of the "Find objects" dialog is an input field for the text you want to find. +The text is looked for in the following *textual properties*: + + - in the displayed text of text objects, + - in the name of symbols assigned to objects, + - in the values and keys of object tags. + + +## Advanced query language + +With just a few keywords, you may enter complex queries which connect subqueries +for general textual properties and for particular key-value combinations. +The following expressions are supported: + +| Expression | Matching objects | +| ---------- | ---------------- | +| word | having "word" in any textual property | +| "some words" | having the phrase "some words" in any textual property | +| expr1 AND expr2 | matching both expression expr1 and expr2 | +| expr1 OR expr2 | matching expression expr1 or expr2 (or both) | +| key = value | having a tag with the given key and value | +| key ~= word | having a tag with the given key and a value containing "word" | +| key != word | having a tag with the given key and a value different from "word", or not having a tag with this key | + +AND has precedence over OR. You may use parentheses to nest operators in another way. + +You may use double quotes to deal with keys and values containing white space or the special words and signs. +Inside double quotes, the backslash character is used to escape the special meaning of double quotes and backslash. + +**This query language may be used in the second column of [CRT files](crt_files.md).** + + +## Buttons + +The button "Find next" will select the next object matching the query. +The button "Find all will select all matching objects. +The main windows's status line will temporarily display the number of selected objects. +(It may also indicate that the query is invalid.) + +The "Query editor" button will replace the input field with a table for entering object tag queries. +Pressing the button again will restore the input field and make it show the query from editor view. + + +## Query editor {#query-editor} + +![Query editor](images/query_editor.png) + + +### Editor Layout + +Each row in the editor represents a single condition in the query and its relation to the other conditions. + + +### Relation + +The relation column specifies a logical operator describing how this row is related to the row above. Therefore the first row has no relation. + +There are two possible relations: **and** or **or**. **and** relations take precedence over **or** relations. Within the editor the **and** relation is indented slightly to show this precedence. + +Taking the precedence into account, the query in the screenshot above would map to the following textual query: + +**(highway = "residential" AND source ~= "bing") OR ("highway" = "primary" AND "source" ~= "bing")** + + +### Key + +The key is the name of the tag which this condition applies to. This field cannot be left empty. +(Otherwise the query is invalid.) + + +### Value + +This field specifies a value that the actual tag's value is compared to. +It can be left empty. + + +### Comparison + +The comparison defines how the actual value of the tag is compared to the value that was specified in the editor. + +| Comparison | Description | +| ---------- | ----------- | +| is | The tag specified must exist for the object, and that tag's value must exactly match the specified value. | +| is not | If the tag specified exists for the object, then its value must not much the specified value. If the tag doesn't exist, the condition is true. | +| contains | The tag specified must exist for the object, and its value must contain the specified value. If an empty value is specified, any value of the tag will match. This can be used to test for the existence of a particular tag. | + + +### Extra buttons for the query editor + +The ![ ](../mapper-images/plus.png) and ![ ](../mapper-images/minus.png) buttons are used to add or delete a row. +Added rows appear below the currently selected row. + +The ![ ](../mapper-images/arrow-up.png) and ![ ](../mapper-images/arrow-down.png) buttons move the selected row up or down. + diff --git a/doc/manual/pages/georeferencing.md b/doc/manual/pages/georeferencing.md new file mode 100644 index 0000000..29ed9e6 --- /dev/null +++ b/doc/manual/pages/georeferencing.md @@ -0,0 +1,73 @@ +--- +title: Georeferencing +authors: + - Peter Hoban + - Thomas Schoeps +keywords: Georeferencing +edited: 25 February 2013 +--- + + - [Introduction](#introduction) + - [Map coordinate reference system](#map-coordinate-reference-system) + - [Reference point](#reference-point) + - [Map north](#map-north) + - [Related functions](#related-functions) + - [Further reading](#further-reading) + +#### Introduction + +Georeferencing of a map is the best way for aligning templates (such as base maps or aerial imagery) and GPS tracks. In short, to georeference a map means to establish a known relationship between the paper coordinates of the map and the coordinates of a geographic coordinate reference system. This way, data which is known in a geographic coordinate reference system (such as GPS coordinates) can be transformed to map coordinates and thus displayed on the map, and vice versa the map can be transformed to geographic coordinates and e.g. be displayed on a world map. More information is available on [Wikipedia](https://en.wikipedia.org/wiki/Georeferencing). + +Georeferencing properties are set in a dialog which is available from the menu **Map > Georeferencing...**. The dialog is divided in three sections: Map coordinate reference system, Reference point and Map north. + +![Georeferencing dialog](images/georeferencing.png) + +#### Map coordinate reference system + +This section defines in which kind of projected coordinates (real-world metric cartesian coordinates) the reference point relating map and geographic coordinates is defined. Projected coordinates are transformed to map coordinates by scaling and rotation. You should thus choose the coordinate reference system in which you know the real-world coordinates of a point on the map. In case you just want to load some GPS tracks, you can also just safely use UTM, which is widely used world-wide. Other choices are: + + - **Gauss-Krüger**: this is similar to UTM and widely used in Germany, but is being superseded by UTM. + - **From Proj.4 specification**: projections are internally handled by the [PROJ.4 Cartographic Projections library](https://proj4.org/), so coordinate reference systems can also be given in its internal specification format. Examples may be found at [http://www.remotesensing.org/geotiff/proj_list/ (Internet Archive)](https://web.archive.org/web/20160802172057/http://www.remotesensing.org/geotiff/proj_list/) and [http://spatialreference.org/](http://spatialreference.org/). When selecting this option, the specification field will be pre-filled with the specification of the previously selected coordinate reference system. + - **Local**: this enables you to use local projected coordinates without a mapping to global geographic coordinates. + +Depending on the selected coordinate reference system more settings may show up. For example, for UTM the zone number must be given in addition. + +#### Reference point + +Settings in this section define the reference point, which is the point for which coordinates in all of the involved coordinate systems are known. Thus it acts as the anchor between the different coordinate reference systems. + +In case the georeferencing dialog is triggered by loading a georeferenced template in a map which is not georeferenced yet, these settings are probably already pre-filled with sensible values (assuming that no other map objects exist yet), so they do not need to be changed in this case. + +The **Map coordinates** field shows the map paper coordinates of this point. To change them, use the **Select...** button. The georeferencing dialog will then be hidden until you select a point on the map (left mouse click) or cancel the selection process (another mouse button). Changing the reference point on the map will not affect the other sections. + +The next set of coordinates gives the reference point east-west and north-south position in **projected coordinates**, for example in UTM or Gauss-Krüger coordinates. Unless working with local coordinates, changing easting or northing will update the geographic coordinates. Easting and northing are given in meters. + +The third set of coordinates gives the reference point position in **geographic coordinates**. Note that after the selection of a coordinate reference system other than local, projected and geographic coordinates are linked together, so changing one will also change the other. Geographic coordinates specify a location on the planet's surface by +latitude and longitude. Latitude and longitude are measured in decimal degrees. Also note that according to convention, the first coordinate here is the latitude (northing) and the second the longitude (easting). + + - The **latitude** specifies the north-south position of the reference point as an angle relative to the equatorial plane. Negative values indicate the southern hemisphere. + - The **longitude** specifies the east-west position as angle relative to a prime meridian. Negative values indicate a position west of the prime meridian. + +The **Datum** field shows the datum the geographic coordinates refer to. This is especially relevant for the longitude. + +The second last line in the Reference point section contains hyperlinks for opening the reference point in OpenStreetMap or in the World of O Maps directory. + +The last option in this section determines which coordinates will be recalculated and which stay equal when changing the coordinate reference system. + + +#### Map north + +In the **Declination** field the angle between true north and magnetic north at the position of the map has to be entered to make magnetic north be at the top. This can be looked up from an online accessible model as soon as the reference point geographic coordinates are entered, however it should be checked with a precise compass if accuracy is required. + +**Grivation** determines the rotation which moves the magnetic north to the top of the map. Grivation is composed of magnetic declination (the angle between true north and magnetic north) and grid convergence (the angle between true north and grid north). + + +#### Related functions + +The (mouse) cursor position of the map editor can be displayed in map coordinates, projected coordinates or geographic coordinates (decimal or as degrees/minutes/seconds, DMS). The coordinates of the cursor on the map sheet are discussed [here](view_menu.md#coorddisplay). + +#### Further reading + + - Wikipedia: [Geographic coordinate systems](https://en.wikipedia.org/wiki/Geographic_coordinate_system) + - Richard Knippers (2009): [Geometric Aspects of Mapping](https://kartoweb.itc.nl/geometrics/) + - Larry Simons (2005): [Magnetic Declination & Grid Convergence](http://www.threelittlemaids.co.uk/magdec/explain.html) diff --git a/doc/manual/pages/grid.md b/doc/manual/pages/grid.md new file mode 100644 index 0000000..057da04 --- /dev/null +++ b/doc/manual/pages/grid.md @@ -0,0 +1,38 @@ +--- +title: Map Grid +authors: + - Peter Hoban + - Thomas Schoeps +keywords: Map +edited: 25 February 2013 +--- + +![Grid icon](../mapper-images/grid.png) The map grid can be activated by clicking the corresponding button in the [view toolbar](toolbars.md#view-toolbar). Settings for the grid can be changed by clicking the arrow to the right of this button. + +### Grid settings + +![ ](images/grid_settings.png) + +#### Show grid + +Toggles the grid's visibility. + +#### Snap to grid + +If activated, holding the Shift key while drawing or editing objects will snap not only to existing objects, but also to intersections of grid lines if the grid is visible. + +#### Line color + +The line color can be adjusted here. + +#### Display + +Here the display can be constrained to horizontal or vertical lines only. + +#### Alignment + +Here you can choose whether the vertical grid lines should align with magnetic north (which is assumed to be identical with the up direction of the paper), the grid north or true north directions. The latter work only if the map is [georeferenced](georeferencing.md). If grid north is selected, in addition the origin of the grid will be shifted to the grid coordinates origin. Furthermore, it is possible to define an offset angle which is added to the north angle to calculate the grid orientation. + +#### Positioning + +The spacing of the grid lines can be specified here. Normally, at the coordinate system origin there will always be a grid line intersection. Using the offset values, it can be shifted away from the coordinate system origin. diff --git a/doc/manual/pages/images/Android_UI_explanation.png b/doc/manual/pages/images/Android_UI_explanation.png new file mode 100644 index 0000000000000000000000000000000000000000..ba35dfd14cb5dcbb67159d662f8886f642fdb006 GIT binary patch literal 256857 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

    JXpw65(c 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Π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ʽPGiJCBoWl0l59OWDXFl*bfwu52E-9=^BVzB zB7#MPfi}sjfiG#kJw!NGE^-Qy|$EXe(P9$=~P{6Wq71(31D{)7Fy)qsBjKdhG)Eq;n7(qD+a zD=lLLd(MY*`*K%b=Gl%XGoJQ;ffJOkMZqT|tbX+#UB;5|B-?ALmVl8Yn~mwvukYx{ zmZ42hEgXR~;YAKa-Tl&~q}5GgA&iXwvX(mFVCbjBbHDPX5BOV8OLn|A<;i@<%zX5I zsP}(l8wvuP&$ZlEJ@Xj23hx27v9YyHw{BMv}J z3jOHMQilc+t!Tg;xnbwroL_lP=Z#whv~9i@)YZRNP;A%ZI;%*i=tsi zlW^K~7R51bweKNn9@C3EvrMm@?6JJ;YaSg0EV@r`Ec_A8y-+G)ou^9bm-LZs ze!9l~@L{iQL-FCk$DdZu&A*w|B5?>TiTlCA5jzrU2bHq2GK42V{>ae+P&vTx21=6+ zb#!_GJI7hrJ@R=`R3Nhb$q(Ace|<$S{KfK~V9KaLI^`*5*q?Z!K_EV6q@sxRhnc;v z#7qA5z5wfZ49G968q6B?YiCvzg7~5=tLH{Bb-suHcG^`1MrPA;C^@h{mf@-KRqXUD z9!IiPnQo31Atw*9+JUA-!_v3#wShEOc=j&fKwi?t?l@ET%87pvTq|E(6c-kTbaY7a zIn0K@=v$)}VhKDS-52*%7@jrG;gmI6d>W@LrQa<%4`Pg5xlnz|4KaL22R?G7zJRUI4cUPx@YvM9xGG0aLiZ*(C0~r;dyByhc>OL&r9b>CE60BQ z8%@T*Y=8GLend`03=~g1W-0>%1Gl5(l(e+WYN!3Hf%?!(^@0s_#cSiLVrx*K%g@kn zHV;FkgZ>F18Bg(UqqAFB=i}57{=V?JnAzBVK!LRDDFl&F7Ze-l=;#QNE5f$YTvZSB zJp|z0hjM8@nEK*VAI^qq3!HJdq>p@VhIN8*U?2cBMQkJs)l+E*stFh6_~PO)Bns*) zI|SHwZa)4at~=B|{Fh9?g&rUixG(d2tvEghw*&J;MfB8SmGm}HJ7E>l9p>I66F62y zedait7aW7{R}>fba9GgC?hOYolBe@!4S|f)9QBd?G>0L3+J1{Pm4=zr+}%=YK{w{B zv#ItoA|Xe(Z2)4mv9?AaT_mA`)EbgM;;5pqUTH!Zpt?ZmV+Ji^pg*`kJmzx!sr24) z;S1981%LM*aB4K?Aic>Q`zF{3vBRovT;_oZKVsDu;9YMfd(ukfKMA&9wU8cI523QW=)R zIi0DS>;2h-vgH}3yD%}%>htHX`JEQ8c_N9tZ|ZT3+jHtv@jM>Bk1aT6tB^}hW#*VB zDQwjEIq)ahIJbN)2i^mh&!IIiL#_v7=e>Q46j1Pm@VuaB$MfhB`Rf&_`xENz!kTV+!WYjozSObNDJ0gud|Q^e61w-XfDkQD$4);Wd4IGfVa&ac zmCJecx#2tBOdC73)9=b+N0Xe9tb=`wm}lUAI@V&~i32rFT$qCT24Yoi6Vp~ADhT{>2-<5!L;;|4o4Hi2xJgM>>szK1WUHkyaw zvQUwdLPeNp2kT$=YB!a;lEonFK<+NkK#>Y?w=JkXzZH|u?0eF(v<7^z)xCf#A!uA= zKK1^!v0)09OMdz`1Xr=$W`-{P3^0FSOBj;zi{D{lP_ahqvRM(XdqWBj{??Iq?EGlV zOWPYzj*lqdCu9q<-Nycx-!Ay?^lQMM0_sb+i{t4dQd5bMZjWRh-dz*l+NXg+@REq1 z{PXrpKIA*gI}x%4ve&J-a^@kHq9DjI())noDsolu%0-ySNnEe3LAWVt~Q^*C~N|$So&Mze09BTj8p!JVl^}W1bLcNmkh0&!8 z{T*!3a64vShsP;|loYl$V=Z^_Or)zQMQEsJTHetkUbXVniw?HCcXMz{K`T4)&ovh| zMwbdk)V_cRqlbUpTPmm6O9pS!k)i0o^-b`tV>$MHkW+zx`fo5u?D zlwRb}gvnEgjV!;yK$~wd;H6jKD)mn^55ezOPmnL{eZfq~NQhO}G9;(vr{SA7F+u;> zkIY#0#$p9_ok)gPJ(q%K5$8^!vyy=G_8s(?THR=&-4DT#&K>}!R;Y{75}Z(y>z|1< z-=P=x8yb18zuuN`In2KEdo4zY#RE3dVcu(?88TyU1#Wn4KG(>|$oP*Y zH15bG{Q2X;qijBu$qT#>?zL&i{|PYBNFWw0X^y(r=%R&c>*LAJ)(A*j5C~t%lQVP< z4gSrG`o@?t?2$6e7ZVHrecyJqYai8)fvP|DR|OrjE%28$&liMg0;5KHkx>Uip6sx1 z;XOuw-gF!L_N@nL0K@4;814{Y&Xv)29^_R5-2xn~_koxOge|@j zhilL#FomQUX{HvIlxP?j3;;bso`VENFP~FpWMteYJ_(*gk66;H)CR_=iQf?sLWBpB zjs@IaZH^uUnn4lLAO9T#RazL3ZvlMTsr+a`T0?`NGwe1K1#Z39z8P|) z0g~;3j+n#XXM{jz)`f}KT{%L(7w`!;DB4%!#Q40flGe}Q9}OWC4v6qg(9T2pH5?gA zkU=8jCAiQFPqf_ZCrZ^2dLdGJ4MIZ%$ti$O1UC{CGe4k1y8^vWt6vju+ZL;tBN0=6 zNMH8BA`ZD*NhV5xDf|<Ft>)R(`x>Huz8#M2pRbA!dtv7YX_IN$M$xH(lW{_9o?(X`CxB8<+ASr*F+ zvjO9Gj=1^k=!rIqN}CmxVvgvNZ%8I!O4Dj$_y62Hf6ny%cKu-iQN@`pLChT4-=#Pm zSIMTqFq3yK9dA1oRO<<1Y`<5EpClh_Yh2~EEG+FhnJVhvurQ)!N)Dz{|NQOIu}3d` zyZdr4?^q%+3$~m+#lw_Zo(WE#t=Pvr{Zr0C4+Sk#9Ggc7a zT6V%zj>+#@xiNFW#+_gt&66cwJRZ_(Njq-YvC5aU#%J!Dwfc@n|N0i(e4i;=&9mN1&GV4XIPWDhY+};TSC@bGj0uv5Z5Tkp5vq_FGPlDB z#{lXmAaCA(SWOckFFK<)19({;s{Y+{Re$$5P5FxlYK=7B+_}uto?S1`1C=zBU>>vu zm$=`G6+Zi+McHdKUV{#wz1wKhZAF>lrfr`;KR|hwEga}X|0N+2VJAZ8X@5lOHmB$A zyl9*OK+!Pyfg0(BK3epmLr7a^Th-5it$BYxbzu$YLgoj$0G|KgV}em=T@^r-~6Ww%BDzL8T#(@UMHd5Q|a-FNU{lCktzT{%Sl^@ovTN$E$&7)!q0d|dZ0&uM$2?5gH1=~i>SmiDerYpvMqaZEWDR=0p zdPA%~o%!nD1LFe1y3Ow4M>q{~_BsJn{`+Uv^Wto}QP0n&Bm80X3c`>gLa6{+uTMa- zi0fVDv{np&XT@PNR#;8R=t+a}6(r1%zsYd!&zW-cA>(R*^Y#}(GlAtA1e=ot8$YTW zA)q1`jRwA8oAE=$U=k59{pY(13}w(9WMvHy=@@>7LMx~4Ed`eaX8YM>b$GYg#m@79 zRiJ1{r&^4ARcITVI^ZVeV^a3wh93bZe`4G@S$=o&S z0){|#2g^4#41}N$058XM9YE6Bmq6D9e?i`)HwYqd zF4gq)zW@yB1*Hwrd)e99ftK4a|0*^WFn|3a7j&L;Xe5;>0@2iJZ`}H$_S2XI>ZEij z|MvE<29f1ETd$>@;uE-ZdAZFc>GW)_C~mb)vD|S_SPkxdgNc<$GfKSqJ;FWU+2eDS z(B>%89n#@V^rUo-A6^vNLJ6j&CNgn^S~B~m@o4GJX9|PDTRiXgDpu=xw`M5khk6Z+ z&5X9>bRDP|#t&3j{4z< z3QU?UqdYpJWx7O^dnvzs#3p0z{mP`9*cr99p2U=<5fX}rJE$qPG*|I7O?mLa zoW;WgFNy7Gy?%|}Q2G$#pcVIL({O7ZS!R3&lIZWwS(x3L(u5uQ;goqE-==o*{EX<7 zm0M;Orb8wRSCk(uE2s4%J^tpvZ`0{b?9%JpTSh5Bjn8 zlhZ<8^i=9k|JMNo7=P~Zo>PZ__^j)NiSYvPpeRH>wTVSGHAZBi9No-6w?Hpqj=Zy1 z!i)u%XN5`oYkSoj9Zy;QOk@ckjz}Swgarh=P#KX^35LkGpl?B%2N1UZm#q8anbiOf8M)A(Po2I>OB z=!ABrDa3DJD50ao^>!kTd(fuqgHFRIkoLXmw^%`P6#!3=mnqL;nZ@@8xDe4xfozN!K$uXQCgtX; zg0(EnKm__JY1WXeqoX6179jhp2#Kz4fZZH&mK$%#xvj9F$r(0bp7NU{$GL|T3QH0J z{L4|InVA`6(j3Czx1W0-14uF`S(w2F93-Ll8Oq1U$4}vO4*+QU<@a(YPR$|0L59SW z(@a{CL>IsXNT>jKE$P}2q`<9e*%bs*ojkDLVZmkP)jj==V6+c{s{ko}iM2)6a4WpT>wgQEC}qmYST2oE0`MC$yJ2L}hxFn{p@X5`HP2NAgyz!2-p zm(q#~C50!#6ZvH>|1TD`duB@TF;>gy{4nbWx1vpwP; zWa7&R#SJt{Ff|ZdE#6jC6xP)xCaDPCEE&(ZUic7D3+SA7f~v>H(UFmfDGbQktNUJ{ zqKN(Mo3N1!6zS*)0;W39sQ|zW5+K;Fp}3Ta5)y&VhOl#h#>DBLwT%wc!dA}Eb0Vr5 z>fR_RSB~;a;g2=dyk4^!(ag-*J7O8j%Xy_ikup37+rGIXpfo9xRB$fi#E zcGSFA{F0JqD7J?`wt#CEh@vQlkKvx<@aUu zTh1i{>HLmt`g(7gQT~q$@X*xl_;6gob(f(8Q#ttW-A|RlT&b*s_WACZ8E}xqKhRmJ7ee8 zx}JKPHfGeOE6FF4Jc1UBNg}3XbVq8(;z;&~&xYb!y!ncQ;pv!YQf_C#f3M%elSWgM zi+FYO@7(mlV%Kb$kF8jl zO}ki<8+dDH4=%$iL&L>@Mg#?c&>;%um-79T=Ze?SB2ps+bFNb>l#}3wMFz~@1T!Ck zXcB+ctR7k9Q-8MTYlr?OTy7sP{yc^Q*lZlv1VZjpV9Gg+*fJz$fSL&FA7nHKFmsXv z07rIU?oqY-!A8VHTe&EPbj~ks&jc8+EG#VPh+}W-A1wH$$kNrn6CCmX5*^_9PTkm{ z{=_?F7|b?+q&!H3#|(N2)DncQ4$LzS;tRC&jVfyb^3o6@0q@$wQ{T{{UF!s0&Gl$K z6;a)l)ixbpr7L4HMK-N#N%b3t}^?znpn_+gjOkJ29qLZ=m6g1g}O;iN_^&T_|7 zy$@lif_w~EHhx?!hv*~Q9zsmnPC*GFnZE7)4r%oLeT@npCRAWgdOzT?;}nuBmMt^v zLLUb~W9j&s&+Ao3f?)`g^T30~L(_wq+z*EADk4V{#^#JICzT71h~+j{Y-MSwDd4JY zv1$4Fv|#6|vCE|9eDYy6)*Qae92wMa0krTxn&NyeE@NnDJs7<%=Hu?duP?E8Br&+S1O-{JmUhznd53h~+$3XEvl{*O=ya<`C!ASL zT7oB0LaQ>krFYkCGSrs+a(_#AUaD#5tUSYMkYhr=^}9iUtYvF^zOFKU3Xx1Q@tW(d z4FM*r;GX~C{o5i(I+6?D9>4r^!KRdGO?bMsakzgr_t@)v@%r$U`g-d&kLXG0%`9HU zU@I=MvC^y7Byv8!8kc%%lZlSxn@mn)T^WW>U*vxlHYK{{-g>G-!D`}z0R6rM1D;nPTW$mf`o%$t%-m{N3(O-VFlB~J6+>&U5@*gC&< zoG-5B0My`D_YeEqHrXZcR+`mA5{MWyQ|}UJXrj@SF?Z$HR=rRU?;#Az!#irpn$b2= z)mp$czS<^uuQN41GA*y5z-d`z36V)%TVE131x(1Bpis`^3L~3`5=iVj(?W{ud!{y% z-AXA2Aa(Ei-!+DYJb26xfF;C2=PaW2DJZke)$T(&uSK>dC$3DTDAy287^FE;%E~wp z_&^m3#s)rwdO*K4e6iC2tlTiby@8?g;R8w9KnlK~D==E`VBdK0&H+*`*}!-UQeZej z^H~ZnB-w+~Moo*kjDypy@Kq%_ZH4ijmFbB0Ui?GASaO7}1nuA0guVIOYrgpble$XoW`8~wEj&y|}!7;Ax z4?b@x`8O8mE_N)wD~E?BdisL^XpoeQN4-f zI@1*?#;1w}?!h5KG;!Gh|4!#YNheFREOvGJ75dbedZ_?(RjJ){12E0dxtj>#=|kbP zRKxYqs6J#;K9b3V z@O$wJMWl!o7E}LVro}T?Bf5a|xk@FIu`ZE==PP5ko_$Z|CDh7mA2-nAwNRk%+=+a_ zX7&0r!@7#4=d*Y*(WC2M+YVJ>0T%6V>t$@--#_BSsiX9-QJio*);KDTtIYEAZRQ;; z&bi0V?~4CSC8vA7zQ;vQg>hsEPlMU*F|rfPk9agG1F-724QD9)>WHIL0LGsD$w zOz_J1R<{6gWE-)fOCa9R{q0t1JVh)709Z@f7|%zq!7dhd*)obcgDa>duL z{a;pA4DXg^K24xfvtRSnnf<%KYi#VZ?rmjwL2r|>Hh)lWK&qb{RakybPqg7zNmP?= zsaR)tahi4&C$`{Da9i|}R-aAj^_01(k=D4^WsawAtyBA7AEXbH|=z zg_=GJEM~b;bNN1+_1>LOI(N$v#)u)bO}@J_Gma`GoP>enC-|?!X*rd2UzQzB9F*a*7`j;Dre))3 z30(;P?VGHqs^vV9OXIfLzG=}+@nM`oKL2Hv&80q-8~)8!tVWX~KXIDbN>e=rwflBg zmz=mQq{*cTb0v0n@<-_=&*eO>De3-kZ#(hex0E07&`3=Btk4_x>yOK0nuwRuJjG*o zFzZV?znZ+h!jSqA>rZ|k(BlO8^g--Lc}_pAtqsT$PUa7Qvraw`IQe^tz|LG9W}lQ{(6m zcQ=w6Z%$<5<-I?zfifD+78#xx!i*Vy{l$YT{d|5#xx!h$4)WNtv8+o86hp?q?I3J> z_3S%kVxNq(w0!-k+0zJ=xG3nI?l%8>EI$<<6F_B}yf@Y*=)qE>!T%wPfWWpoZ zB|JGOwsV<>MDfg^>5p}yw9Mqkm4l!w zq$qT0omNy*E_(|-)mCSJ;2wj4NP zA!fg%{xvt4&A-;tm&z7f$!0lNqfRdYtrRLk<|tW={PaRo`*0bx?Z^NfiJNz@(7}f3 zo%@-M<3W9Sw<-;ZWa`Q*HU_gdauQKyoHl4doP?Y?0%8|;49ab%b+yP#e(u^caf{?^ zS11W*kQTO#$-DB`%vIgU#e7@c1VWWD&@ z@u+9T)U=zl_*^;aOb+T8nQvB}>{zg5$jf(y1U{tjfz_5SdVeu*Pl9$U*oB#?tvDek zDeTRhb*bG>{l(u;?1nU1pX~2EAqtm16rCA$qh!;*rVy$t%|sj=Senmmr&_P2W-|3g41tW_EdTiN{GvcLhx{Ytzc_uP_9m zgg2{=NQYM|So@3xO{9cdXif<|xmAZ-NAZl|X22~eHiUXoKRz>~pr&x2?0PfJii;)Q zZun?x0g+qIORD*2YB?6Q9&6(pH^tusmPWj}Wets$Cq9JV?x0=Env~=z&bQW&WZ2%a z-*vFkXpQvbxzX`xH3Jhk@G}!t<~#!8-kIjf>8Fd! z-8xbpn%K5Avva>-u<;*blNgjlkvJfTLc_dq*(XVuW+bil*(#wX6TQOfW{Z^Bc`VA6 z;hN>5YQPt!-0G*B&$37ae0cKWmFn?}_(nn-KF2X{Y?Mi|;nNcS9-mo^WQ!mq`oT^A zFirS5-|IoQc^j2%4_3zd`=Y8mL=$MrW-czj@@gnBH79wT)FdR@*>)q#MrKA;^i+G(I;68{>+x#(?597=WI$sI-S zfg*9v%d1Xrhm)Ou+(z?0+^cpu-j_+f-A@&dlcc8v?zB9qp(x;w-bxVMPI(6c%t?aBDpO~X;#x5Y=$-{FAvoLDMt`uViec=KGcm z8iXkigltI?H))0(DelQ$K?%DwElIy4i<+wmqG(T6-?wRc8aJ~X+z(JNTJP!=I&q*h6LWraR# zrWT}F==mKtUFK$*J*@_DU-D}AVY1YPJfT)enLucFI?$vfW`!vcwyb8d zno;{LiTCkoY0pSxa6;1xb*^tQM@lkNFj8RM!G47q))CT~SJnOsj^y+X-&i&-g@+Jd zIeOGgbU!_vC($c1`SBKd1>cLAG(;tP8SDIfzAb7S`|hTrPWu-6P0Wc5xF7Ws@LrJI za}$^8&d<6Zmz+;Kdn0QWKWJ*DQEiTw5%aP6Z%Ax_lKmy`o-?^hh!&iZ|w9;ay23) zjz)4&7oH0y(jU#m&n3y~ZJfA=7^A#+-fOc_SR{af{ee2ZX}vG_N!t{kLZZc!97-F! zXZ17jmKpN&lKT3yYOCmf-$xev)t{fd>?k;r;xGR8p99R2eXPFxJ{J`BqzH!PpT;sRYB;46>adGEf@Fn9>; zXtAw{meHQX$rgY*b7X8xk_Jzn#h!k}iNCMEAME~p2lRfUq4B;Xna1h}cp8IhB=_jq zo0&)nF}J6!VT2!g%{V~@1)Zb&mWAVEV_=rapQA#rAXp);IOR?#^Lg)N_2gWGxeZ_# z=;nZ#`vp zU7)=N%EFC#mP#8k;86q5`nK-TGd02pXK*TEKykE;%Z zeNfYZa~>vD+{V55 z0U-|DYQy0~G&JCF4dHwlMV%+$G#2vQr6-F4$?z8{lMpR(vKhm@l006ROHpe;uh^;* zdtc$1g^hrt4!1$0gB(y(zI?&G8IKj#$-~E&n2?}iyomQp|DxiuEVUBnTBZ=4HCf?| zV)a0f7a5xJa*{|nlcVe@eJsLjI=fh z(aIDRYjV73Pc1FOcw^a#qiP-I_;{4*fH$?h?N!tfB9mWRn*c;J3f$hOdqRj7?HE3^ zzJUR(BWNUo72S}l0J#mWi_;E(7eOQuD>FO$zgcY?2|PqO1vI!mRNZYQLK!5x0-GZV zIXMjI4P8&9ADy0tf&@0C`RY?RtWSoZee@Y`NJ78|8Dj>Y6zm5|D=0o6hk7g;v_#wTs9l=tuXU;}x3 zqgO1>_SNymglGolXS*pzRX_*rA<0v@g1BlA$K?rgmkb~KJDd!%4h|2%&n#=mLz+6d z*e1kRbb?Z-MpG++JSI`1wMIravGmw$8X8zi{3nJkF0L4bw_gmzhQ033v8vC21wzbb zUc3;3mq;6s;io8WYoml!wtjGTzZ@+4Fqh&g91z^)d+$0(Y5Sk+BgSJ;3bunIC`nj zM}U$S;fz8NoM7o4PxJIitgzEvv_ROW@S~lfw@W^De|=%H+f)4|+u!Vtr8oN+B{3eF zZy=^J<9KC%CD%{ju#Ezr4g_++Y(&NrYIhK zt$dKqso8dbtGw6#HRRZX%OZoNK4ZwzGC9|qv1n~g9@W$V-Z1EDU0u0BaHFM_Ux_KaYJ!2Bk*BB+XIDH!2*!H@2_S5 zPk6eqws9w_+iYW}8r{iaMz{QiKKQCLCiQcuZ#Kt{Z-O306atDX@CVH6XW(K)P)gv1 zg3m_mj6Id_r(l;Kfh|>AaBS~kT~D;PnoJcG?4&J;ePF8T%$VVb@%({*4ERQdvsPaQ z5PETO4$v-_D!N}%@lBkS_I=5>?Zt&y1_WAc&6a;)ev`>RqV*3@=-B`^;A7V!&!YQaaGsy zu{J&^f7OK))M)P~tl$QrNT77_30ZCKW^C@BWAIZxNqmrirl7E0Vv9x2X+3->uNpRy52Op!V8RUw zXrZ0>eU)kFUP%Uyj;aBw2Aq&GmNl0QFJYDE&-ZxmCpOf=*Qv#ShMwfGKk}KiC;Y%r zY0r0E$andcNzFIePq>6LzHT8ehH!0MToQk#63a4jVJow`@yKmDVlAgK%(DmT82cmy zoURA_I57E3dz3&T+Nd3#g|^{g%ozP90#k&g3_l98=| zkrZQw6410@M|W~;?Cq6kvgmocG~6Hpv1qeXdMx>Ki?h@9ep%WNd(BY{j}}6Dx7zGS zapUFby?~Ah%Y#H)3oZo+NH{n+$%+hEuR73QN!77wn3w?NaCv*C-lAR)G}k{b4}Wy3 zS85Bm{~`pr$^ciBVXS1{%cmr6Zf+wZBc^e_FebX@LaLW~N9K;v)Ps8uBlA<{ z{;A1Fz11#H9m?5T`)$;ERWys3Od4sV9C?65+x|gsc(K;a( z`k&KQcQhSWYq&S1%{k9JcYowenUt81GZH^vP&D$pw2bnQz+)zkq7Zb$1kzYf3^^Fy z0C^V7@`7zL-Q3;V_KM4N*|_-m<=<-NLsrz&)5Gc^eOitO(xK(2ibeQCLqqU;BD-l} z$-pZ_Lu0&^`2Nf^d>IBzU_Bv1(oPNbH0LHmxN85zUHy%7^YjFARjA$U#3+yQ*d|Zd zZR(%%O|HN{nA$38YRUlHS-8J<%x1RqKBOr!d@3y5wAM;`^R=_@H|f9it8*soY+;o= ze%8MB(}2rsxHA$OTi5^nIlq!n7-iPMnQ$SBd)dI+I-m8HPghe59W635HbQ)4&vNN? z&PAtZyA)$e^BdmkrzG~xm*W>bQ~#n5l_EARNEbLNZ-^c!xAjUlz)qD^2u+g^I(B^&f}{{*W7GH)!VdlQAi6@CG$QZta%zd@hNvWd(c>m zj*bTVGC2Oc%d;-xz$m3E zq8@%ICT55Uqm#oLcv}>>u3&x-iVE-xg5v~iHah~0yQs0G@O%BH0iCt|Ox;=}Ut8TN<}FVNJhnW>hph}k8`NKlAyE;|Qe~^=0&Zwna zWLUN09?8#;WUB{$B!f}%RSO|-KiL*Z@_(02A4^vn92sp%7dxZAx}AtpZ2IIIOEO_h zkajl*1B)e5rD4&%@6y%U9N4bL_$hvZb2vnGeVlhylfy=d^eQ=mujY1oR`+!&U81^a z_r0=#)HfG80Ef57%J@)5`HdJ@8nuF-|GdYX1Gt8ltLz~jhTsW)F|>4n({GWu^fzYd zgY9)aLYY`!(52Uyk2fKJjsC$)-uSxW%G;Wc;h`x}rkH6yM>up^R)R zH954#PE!k?(9MdD!)V23UB+t7QDrBAoWH_U7V8z`N-QNcWlWLA+E29HjWOnb|K>P9 z*;}vAz1(hXF!^W*5+fgwHqh7YOv!a9LRkfc}y zI%aS%Cjg0HSioNl|KGvXZ+J>>Lmm8^gV@hX{*)5sSO|7@nq2XzwzuZ~jAh3j|Af{s@8y&Ql>J=!J&-W$sr)ZC-vz;?VJ9A zi63Z_wkb~h!Vf#D4cT1}H`EZVaGgcJ<@v<$-EOlxGHRj|ThwvA*V41I>G30IBJ-f@z4V8`35E?2*GH_wm}hYc{ph7X?iuLfke(V$ePq<+_se%0FqwRb0B>XM#=bt z`yv6>tMJ~{A1(qeO%y8gZxq zd*dk07dl8DxrGUgiGA*qj?Gu^*}=JEV=q%4`6H6(Mh{o3_cq0&2R4Ot+i$KWP~d4< zXYy4ijOvf)jMfL+>rKkG4iD9i-%R9YS&Nj=3ai6aoT1k#bMj&yvMB2lAs58^g06yt ztxNU&fHyrfEKKj6^(aHcglE*sDSjU;kZr?R;C%@&H3GiT_{2mPDA&Eggaz&c2x43r z*kDfqSF>W#nAV+v<|=McQQSeko%D>1JoUHnDkV0ywr0y6ul{$hG|{dY5z2_3&wTx( z6L6#hZsXuEB?aDD5E21pROm$!n!!u#iLA{fZ1)f@8>SLvt?HM z_p$1zb3UfKVzR)i4f^!B+WV=qrmx|$}r*bYTqKk;(gz` zf~@O2$>r^0a?I6feUZ;)yE zFZdD35X!uNzN8{dwVt(f!HAgic*6h^YkPb81oUjcM92X2iEGC=>JQ2 zS!17)Alf}J&kCsyxZFZmwCfOQ3&9ISiBZbjoDOu?W>^0%DFj?X;I;Q7i{6riN`R0I z@rZ!$1Hp&_H*Bp;`>iGn zCRv)NQi(6C;HrxgnBkSJw$)R10xy-6m1|wsRlu*SZ>2lxVN1;uLYd6WOvtJdMmdWK z6wF3%h_*3C_wKwtqSet-e~TTqB+v<`;6GQHmtbQFTXSrzqpJ%I;6yN$aC}ox&{-$i zN@BYpA4eh3ff#xrbEoEZWXE|TyYnYGFwATxkk8Nh>H4(vs* zRUE`#1eZ$K+5wGy4QVdIL~B~i>%qan+K>3hdDIABZEo85`1l~@H!K$bWOk?;mD-?a zSO(&ve4?~4RSDdhnk+J+xsCJIKgpHHQA*3p3*e^dx9GFrLZ%LYVDkl69dZXZ*#hnX z07(EiBYma*w@h6JhUJbhF#BSNB3aLlO zpy1T4w~A>~G2ZKFljR}nUO(W3EykW^{(ErdR;-O}<;MBh83{G@Ghg5AC(ITCL9oFf zJBRhUm;1FIgabjJzgtGZxsUz2PM4y{$(v(p=JKW423bg4JP7H_t0?KN z;(z3UrCIjto{PXpz_06Yga^C}f7<=>XsPt&$o<+2p6hUTyn!qiLTKKTH+gy2WYnM* z22?YZP0wX?Q@8=Q01h~9Qa{YS0o%s^dad=X+jfiPp-;2-l+G8gG~gmh8>qFNWXV=p zx5VktKZ}83GJ7X-NAxWZmq79FK`Z^U${=)v$`D+;uoV(X$jI{5-|FVK0X7F@Rv0rN0FepY zDL>FZJ@cPph|G6acC8-Zz=pA>B-nMKxzVGE|l1a3*Nrn zOuUfuAri%XIo{~o-=UIG%9VumL1cN(fz8g9@zJa4X4dG5x zDq9Zr%l_9~3pyLV+T9N8d|K2y(QNlEk`h!>cxbSQFcAm#`*l!=LJb3iN=Q9FeL^ue zH;21rxvt7?S55-2g6CFTFXZ@|<&(GO4PX!we5$@W;Vtx~329GHB(Xr*o@A5u1tF!N0C+yJ--3jc z6dB!=#umM#+B!v*S)9jQZ)8+H$&y(NN#+BC&di~rxg9GnFA^wZc*rcBoZ{8rx^>fj zY5$`vWMcmZyeAPM7ECRQqgT+8PWMk=uOKR=67$b4kc%6en;QWCiHAb&>pDi`gM-*m zJi@yj9~nUzd@X)arxDnsoWJN-VFQb7elQBx6kOMRX`t5{5S6GB0rBmEyG)f99vYN5 z!(b(3iGGb&8K&py>+7@i)~mmjhCmM)D6HbDN6U_Q;!wlgU0w+u9m8+2R<=P+uvg8R zJ;p&o_E=saQ5vo)cXxLRA&+SA%ZP+n3vW#i=A_9iZSdwD4|vQs*r^k)iO7)gZqX|w zLgd=rbp)^g6&3XY?4sXSL<>JReu{7B`se>9 ze;wNXw*~`KNf0RjL|A9o8Rx(TR~<#z;VFtn33e()>d=sYn96~TszzA((pWSX;vBFo z0AWJLRzUOv?*RLfs%gU)kW4K#Tx`ttHhHh@5%2KU@;t(4!MqO354k7!B0^0ePUFPV)lJUw*DuS4?jycv8AfEF@6ZM{>98u&?zl?V2h=c0EQ^}pX?5aRd2OEg0{ zi_hpFr{yULKyz>}ZSL$a5MmkCZd@5&_+OzXUCy_k_}`azT^euN+nL|{AEb%gwqzd= zhXa3FsCps?)|zQu+n6Iu%OLZF7U+PyBpzC;akWFvNs`QM`Ul2j**qUNoB8We$U(c^Zog&$D`vM=f3aP z>-BtI*Y&)v@yR0{7;-bu?m)hch|Agl`vt3Xu(uqj#_Q9j!~0C5D3SJV+lEV>perQh zq-4>(e$s8n>8EqAA1ya@kespAQwy8%iTpb#qGo+l(*PI_j7BmjYz3Y@k1D(q!B7l| zHjCS}V>L&?Zt@eW4ySu%7!{w{r@Fd2Hb!$A5_|~>iOo8Zvhs4Ut4AiPp=2txoH_%T z8h`*s=H_ppjXnMycKl`(yjgA+JC6=E9i2n}{2G*(M|Xy6yXp+=4^X$yz=U{lS@RWi zIFfuKy6Z0*w<9f2ii?byS~d7Y9jaU9Nd1#EMjil%yHK5}t5YG?b{;z`wT8abP^qX! zLu{Jc;>YLI=aE5D7~DhwGXHTP)}*i%n)<_^Hb+;$Mw_e0d@C2P9QFX~O-``D0NHrc z!X^yDE5apd2KT((qsbF5*i#CJT8tLHp57Tx73OFe;ls6NPcU?;W%W@Ubb{up<{7D_ zSQ&(cIZS%(9Le}XT2ZYxd_Av~smc)ozlTtiz$Jj%5$X>ZlfM-&*Qzl7UoX1%&dQ>5 zc-iu}bMM2?YOv71vU1D*%uiH^Zg72a_d)^vh+(lgWs~>2?26Mp`*h!Sx-(|=6+}zD z8=G}%Py~FRFLB~tq*?}b<16lxnsHqq0cD=YQ5)vs#*?>#%2u4*lhAUNphk0(*Z-^&2Nk)pZf zx!;C8TF;WG`gA$+!tW|y9j(*FY2>gRKR`mC&aBxav5u5OMvU4Cw-O8{Q&|DPi`|hp)T-6f2f3Cs+Z-B$99GEDHq;`|75B_yx zvmzwVV0x#co4#&sJZw>fzJE(}nv)RcPFa(YBF#eoC%>g1dt1viV-;D;2?XrT+)5O9 zVQpWHPTUgyl;W4SNo1-tE;DZkrRLc-mrzINhO%R&B8(?FZu!4zg)wO$tT)#$ucR|8sjwby4U;dS z-kBP7U%NNVAbJ?)?btlm%2)8l&-Ux8MIY~71b^g~-#k>%ZJM>~9Um8~5Rn5c|_;FF(jdQ*)ZAK%?4NY}5w(9GsbRa|}@W0lGa>6bpC1^L5-~O7t`^ ziU6}*#C4a)#g-sv0T@G67{;{u(JFDq4p+NyiS9ODLNHp9)G**$f!l;aNZ`ijjZDn~ zXxa8WQ(&Gk8B+F2pscFf%H%2rv@NMCk81hnHsNFEqqkWtyusuogw>%NKYM0BXdakr zl&s_mb$P|ro8f^s)QXen*(itI2`~W>C7Yk)?HTo0s`jGJz1lJ%GRKlFxL2@ zhm?$omq_iPiUjr`cOaK?DM85PZ&fIE9valypw4+f7Lh1XpM!EAi9sjC3yy)Tk(=oI z{JjB7N>x?gVaB)M09{oD`#cI7W#qUOJ?ayk57s-RN9Lo$rRIXVPyA@_x$l#zxJTf% z+`Y8=Uciw=ep5wC!7vULF;$VfYEr2Ud?y9E7$>HzYXvy9qQT(i=}#Q_&!me=djWaX5o-88*RIpzv*ve5E!x`vWgpWgWqV z>DeZXU{K2W8A8!xXJL%<@@kc?`K(tEbDY;L+SuZ<ja?B0wn3hBqW+^QcQnJ9w3)$9jw6K zKZQU-hDou2ks2 zxqgj+&>A3I&WC2(_qUTU=CSs5K6m7Gp$$}jTsr6|eJ_3vhR~8wPC?N8nwpwIF%gS> z1^stgnlIq`P(jG?UaOtrE2~s^Rec7NcOE}fuEc1ELcxqJ?cq7qamk`cwr>V$NoBSgCx-SwUm_f`cDbwN*?#HF7*>;@ zSA>eI!mqJzp@nLqS~gZ0!^;iOaOGgweFgNIbVzXltVDdy5OW0>TnBG&Ky);IjCU3v z9Y8=CFl2z7e+9-tz-@y8l0D5pmon&dvz(RydOiTH@}Ce$N5I|{mxWM@xO427QXk9af z11>P|0<`HGb)JGSR0-XmR*hbF6O9#eoKf#2f-NoTQSa_JPkh zeG9FUu5y%Eolpl~dAaV9yy?yoU_dKP1`TI3_qrPmFQ2=c)2cwmtcmgPYl~~lmKM(X z7Zez?f+hdzi>beRIuzpOtyH01IP9C73*OLfmsf!=M3U?f)EFu~sPMbbT@r2>aat7c z_(jz1HG6$`WTfIKanjh(&<}}x1phcd%SaF>vd>-z&VWWZxt&|A>Vm9Wz`kJN0H_BQ z=xKZjpbaA#V{k10ItQ^b0I|S=@8gv%?d~)xy09aYDSj{h1BMkky#@KD<%!xv1m6Oe zcR1Ch%QQ`Z{D!G+h#;vvIX*y44j|V8HZVsWrf3UguM;PUskug~_1}^G!GgJcvmY(_+aKchj%#Unwk@o? zOHc=Rf1Q*-D+|EgLUJ!>XJ?^oh2j@x@1O_=Ob?`j;6)ER06;v55g;%bNYE-o(AW)F z?mZJRY#Qg5%dPUo{+VhW;3(z8Z|{l{ur>A*ZwzZ&#=|B2dLv7)ai8+0aLWCJ3=sz9 z586M&=dp_D9LjVYS7BYE=F-r0T47(Oh zJ4}z`2VdHsWmb0#-C`OlwtBPl_WQ`Zok;z2y2QcqSv%B^ezLFPzA7wXxs(FUHZ-cuZ=_PbItm*L962x#c=8 z;oemWw3upe%k`-~moFvOCKPRZr8HtMwCg(7q;B>_k)j(%D70Ct3i~D}yA{1R$YDL{ z9gGaR{!V+HPoD(Lvf-+(8X?X_)#yh4iT59vAwL004z2rFR-~MRIW^1l@i}o~0?yyS z&=5yDf)ov`6HqG%4AI{&30^TABhL}m78j};C>|=czu1ou8~uz*QzO+R?Q zfXD!k1I#EGRo-~u9tFmLb`$x@Xl44?_?e|*DvPxkJ0Vu)oU`_OWD?|f*G3b~^-8XE zu%%{MP@f0x2m0$&cw}bQ1!`++5o>PeYA8PeHUTqZfRdlxw?fZ)cz6W1%~BoKC2P7T z6F^C?RLW{|Dst+Z_$xj@ zF_K^`QeEKB$CfVDn~%<9e#of)YcKZk@WD@p+$^mtPhwe#PxrOohP}tvB>QB58 z7CcEIo6nr=$rFl6_h7UF6F&it1PuS?C^c{s0dS0%qa!&100IgFAq@9PkH6ZunHwlV zNmW%Sovi|^JvFXEo74o)B6~s;S3Wp|mD^pSASSEl?BsY*- zB?>84ozXCYDh31rU?L!Egt`Q}DI}>87#MGT^?uEre)!i0Z05iu0_yy@j~8?}!0;GD z=#tuO7S^Ly@_WMf_*oB9+1veiH_YS3qC6A&o=jQPhVyCNYGKh!{^oQ)aURO-w;>^y z32=Xdx^LMz5<3qM+}x&H?f@$gn)=+Xz7ENw%UJ+U18g}oMEyAI=AFdO5&xM;&M~yu zL#QJ@dBmNJ3CbwvJdQEzGh!siYt7#L@R=}L2Fgt(88k)an_L!V%lJQzz79{^7$eT;| zn8+@a3@@2s^cS<@D=p&sbc$BXQzkkn@4=oqDv z^AbS*tu1bD?glVA;S&05op3%fqpdxJ<~<#=J}WtfF!C)U--aO@|Kz3zu7t8^rH;dF8;_4pkjm37**gW=z{2>uW&@Q&)au&bSULuOgp*Km4Rxg zuTNcHw>v1^zujT7P$9dJ$oAlP>B59?QU+(miwe;WW5YK(2jn+UBK{U}a~rIE?POVn zvk}Pz^ii1T=^oZFQIGRQ*uXhbDHBRRUuyYcXR<#I!j(pGek#906RY0QkhQ+1u2ImK zqv8tVM#a#Q72?aaX3H>NV^KXpW*h+9B0hEeB5kbWxvQydJGcn=qTb2PS zS(C;7T%Fddmwn}Pzq0|O+goN;u;K({$TP_4@;MsxL7>q@o4R33xpiqsZGxV1+V<&X zJU|2My=%ug29RQ>`TT@rI9wT(oT&LD9kz#HoC|deFwS6wiD7kW+>gM{Q{TLqRa|^I zd$U*W4uiCzzJ7zH{ZqEkZTDLEq^rPIWUB1_=vMzMJO+gi5+Dl(UIaKmE6Zdy0bUKM zJM|wIl7eXo<{VhMh?nj#Q`tR97v^cgRDJuy5}qis!@c}~?c?9*yp7{ORn{7`{GdY) zdDO$(nJW(>ACTa;&HmgT92fxdDR6MH?8cB~eJmV^3}i^nMl&lK*rY>&SK=1pa}btM zG)RxR%D=3f5dUHM2*^1@2yWZB4DL_zrkU7Q9BZv?~i z<{s?kHCvdgV((RY1RS1GJ^ID4Z9wj2`qC5Bs6F>;6YDxQh)2&het%vhI8r+~$sUWv zutsAEdExeibR2(EAy{^_pC84DJ_`rL5u4B+H}*9grV zD^EPri}@QXNkWr4({KCgtJih&pjP0Pr2JlXlQ*muw9OJu*}K~bjtMZ}12U7NmZRpx zpe!DfRAGF{I+iMo=iv`v@Hgn$&`ogK1sP^f^Hozg$t>sdDtcpsWWK%)^0RLqo zP`692xtfLr-6)B~JX$yqMYT6(uwYb;1|tM!ERA06Eg-o3B+b!vf&Xme)e0)@Od z=^brO{$qXWFs?_mLvj+O7YjC~70yoBl(0w305Hl;U88RXm(ND{h?k!#_*+>cC&8{S z78D|{dnA5ReB4j=aSu1UO;&39PX6e|V#S!ZoP%=) zgrEVO3{u|7X~-KzVhoYYX0RDwPpscdTpB8F9>HjLwdR>dL0i*3s(a#+EH0ADL>Blo zp^;Y}ic|_h&|(z$&1xDk#>pp_T*Qc`HF-HtNhrt5`xc+~XQmG#i(sIap~u1TW2Obi z+J+_TtwVg}q|VD!xM$nx=!0<5=gufg>fiUN)9(4fI)Yw^2wo@x)Iw5B5;eKp&nquS zlyLn4@7E*Y?V+&Y`+|Y$5l?fOKMLvTuhtpWI0Qg zwmROeT!5jTC=U~9uAuduV3B5LyCS%?fu%ZBSl+yph`(zmNs0?t2((OU##v9?T%zk zfD$$ak^eiQyoLpP915Zr3WH;xNTSNC_r?}#lK4t6-|pJm^V`qg5OcZ8C)Dw6j%ar8 zOycUvE#8)D?(WAU-6;>S{B`TZ2s2-IFun@V7!n)-E*^++EyCS_&2q5kqSmA#6v7h| z-IZ1v9O;mL%DL;!F{RFa#ploJh^J#^J|Lptwg^?n8+hdylp7=)xaC-@LNNujs^8z3P)gg=Vy(GpFnNqd0#BdU%4HN1)T~&iJ)Zy z8{7QbVna2Db&#+dB`CX?e91T=7N0jLBXZK~( zlp5d&rPo!a@D!Q66X;T_V({U_saJx_PD1Z?=G=vdKR24C@vT;lMa@?=@H!qfKoTs? z@torE%cFxGBx&dqFn`crX6#z}vM4Gu_ib-=$ex1N7Iv5|Cz`hcPBvEj}u*sx1<}kpvcbdf#DuEnvIF zQH4^Q+IcBu(wd_d`Hf`FHJ3W8RZdsJ{+p||0&e4nk5M_-?u=QVi_P<61}hA?3{_2A zl$Mk!V_cF*uPIgT5!1|#_80hKXXo;YN=%PX$K~BS>#K^OtIxx*nfTQaF57XHNJ#WW z1qyJ+C696#E3GMTU!-o6=@!5lO{tgEj6*v5tnV!dSn6mXVCvb zL-rX|FIen9kRZX>&nS=Y>0UeLPD9uENyoCFI3s^Yu@{b0w$WKj@B^x{u$2gYf28E^jywAz+vw zAxoo39+eCmQt!lsLJnpIae37OSpFw4h31Xy(qz658_50w>Y)+Sq5zQV5)M(--7ikp4#_BBt|^ypP>}_S!9e zc1uZ8ysoz;TEX_5*<4dV+ z_w$P)rRMlbSX8}}&n1u$DRIBCAY7Ud{TkmRh9eU1QU0aaQ~9M#Wykb6d~W{dGv=dY zyy{HrsF1O#QHJa+DLI8=$96;S&EuOXuBLH44un=}yc<(yRTuV?0dBlO;&wm6C;YlA zz&maQE5n~(gGbpbSmv!z8HI(C-Ir&R&oZQbUad0XsV&4SBS=7*;4mfun=D{hHNP$oxaHouK<{SuCm#wMYP^B@{pcAQ^$g zS^PAmZ(Z}BkzHTXqfGNov`m^s!aTbQd2bnk2S=62j+w)AIxFU&CTIyEKfGmeTesA9 zb!FvC(lcm#A%C;ocnCNeNDZt1NI`X~bEx&$0YL+tEv?hG?{itREHs%0sIF#8wlmpE zp|4e`g+GWibAR+LXHIQ22D}oowsz2nc)wuRHK|GIVsL(1?LuNR3s6 zqbV8QZ*g%J5OX-8{UIGe>s1ET?J+BK4n$@G5(X#|ODy(V$uo4fmD_PyRNP2TeQvJW z(Gb*)P?bqB7+%~_EU_&LbScvM_1R^6Itt(I`3bEb?T;JF`gS%H(n*x>MOGnY zv{q!Iywz;JM$e08Iu%xyu}$#o_Uo+KF!j~(Xba3={nc3XOpNWVvbtVa;dS;uc?)7w zImx$aG9}uvS^qn? zz_R`HfKyeTYEy({Ff*BuSHclU6*2UUX%erI_7ssaGjYyXCuH$W!>!LF009+HK%mvC zqeYu?PTR%-LJs{5D7!!?#~7KGmlq6!Btv)tK#dG?Ao0mt7K@uB+UC^fs|vMfFjkX! z7j_eN6SbbGZW`)AdyLLb2UH$mC_se<%`PM^G@-)1AyW#0fP<;HjvLO)@%3;9(K9I| zO+gqD)3uP9%{>@-_4Pdqf6CJu*LAeB2j#~oRkB$o^Tnlyws?0BlRr)f*J4-RTlJg= zfCrhk0*CKYLox^jS*V6-p;c%H12)EZGUV6Y}9|(bv z>H%1lgv7-5g-zg~^PZ|A!y|t4h0r4dc?ju+}pXFNY9IIoQ=7=wRuh}X?(u&frvKeE#0D9<)i+zGI0Tch1a3eqN|VmA~04f z*p+?XX=d}Awk3!3b9>mOem%N1Dl*Wuuc~aU(Znt1se6fo20K*A)>&9fE)~&n?0?~S zvGTJkPI60Pg6Ty|`KSF)y8U;YKbLa7We7CSHC;C4+8zN^8}T`Yi9zAx-PtAYBPYg5 z@5C6-6vwjfH{$`qv5-lwk0G)h z$^Z`jl93F?k;x-KJpi}Q9r)|@-;?r3MqrDl}VBsM`P%nEyvOruo@7z1#TYNM9 z481?R7N#PK2V29qL-$gMEN6~d2Re6a70!2MNt;k+MY^%7(uA4)+*c-j$nRuI$g?DR zPwYo3-HokJhRSbKy4(#fuja4ua~9oBWDff69^D%EI;(=}HLn&0n>5aCBfONuuNow& z%u_q~reRR-9g9V$KH&LQtEHvU|3q)pv2Wn!driK(^2tF4+RmCmAoK#B1TnTmZvA1= zs&{M|4^I*$t^&K}%P)p_7a0+D|FnTZ{T5jtF|p^0L#h$H zLB;rrqA{)@uvy1o*b5^ZvM^cDsK2KENFQ{WfKB#KJxjF4iO*c^ix1_P+q)l$QQFRw!J zK8vK1fRR5UJAj54qL0uVtgC(D92_!#+u`V8niN!&}FJOvH z%>fGJufTf2v>gPL<{|hS3~Rvd50A)G_5hgjM%sL-ScbV9$WS=~k|RnY*uCJZ!!%J9 zvNZrdRg$1Izv%|^4V9b8!`S(IaCpEhOW(-IejT6D{ZyrClFx-4XH$cf8st%VYa0`n zCp?*g2gxN#n|3|Lv`emwQCyY`TzcCY>_K^N_^ukqg^hXqSaPeiuHdpX+79=|SgZ+A z?OUwiB01X|9J5Co!t0xKq5r6CQC<}fRy3Tf#Vhme4Bt!TI0opAntt+B9vbmN-M~r6 z(z@=C1$H0$=u5(DXH<0-UY~4mF)%U?=>QwZKiM&!dc;nXoGj3-hym9BZPxlXp;hi= zDgAHvQr)M{-aH&z5Gk%4Df>AJ94Dx8$-~;f*kVt~EegtN2)KoTF)$Agxz0bc?wWtQ zsq$hZ(wuLL7e6(&v9B#zlw%1p!`bxq2z~ZqecE%LWIu&aD`4sY#(}*X6pwRIkC-+G5W`jhf`8aAwO3AT zR>8tCw@*0Kr~7N{tX5JmdE|8qWiI7jaLga~4gF3~HhKj&9!E2zs)&{0q4>FsvlH(r zYs@me?y^he4OrgM<yk^eYsi$Z#c=tKJyoWH2RH0{PWM%~SA)kX#>eGpN#D)RSYBNDm-vmBgQ#A?%IIarxkz(Oz8yYZgI!__MoINh1ccP7 zn;8oP*HAQzf7fCHirJ|Si{Q$X{7;JqhH-$?7T-+b%0s8i4h|`wI($F;AOM_q-x^SZ@CP;UVo%q@ zMRW=RjJQ!K8TKmjV~CeSuDS&t%gd*#>{vr60gUhc>DaU@;v50j5@w3r zp~2BQFgt*21buN+yt;|{Mi`CIBP*-Lh}%`)T@a(c?S5?Im>@Yr`kI8s5DvROUa(1UR5N)s+)4nA+C1>vH4sUFgaT>k6WTIP zj|B5_3>4&xXdZdQ-)~JHGMeYZf=>E#l<1JoJJNV(`w3qs!P$;jS zk`Rwq-k`7b+RviE^jyN#lLM9wGz~CRVsvD^LwlRt7Mqp8=cJwya0-OkyM-E zHXpbG>n;#BsiRenh=ier+=b*XKn=QrYyb;~SqvoW87k3EP^&`y#kTJsBa`_?k{&Sd z;bCPi-kkn%{Gr8dI1Qjc&w!z1M3e+uFB0kp6Yij90Xc#}`v4u?%L?^ix>w?jf2wsS z?f|>{ezA&2xaYyk)(O%Z!ZK&hTu6|X6vLETCOkS7)Mnp(;U$a#3bj!TmV*r{syN9a z0kj`5CGil%GhojPTRzYN3OYJ8Q0q9Nmfseazy1q~6OgsT5CEJf2GlT_h=^1`Er0~w z;FH{_$n;)wW}DfZWj$WIwy$_;bMl1eO_l}JGmzkLN?c-L^aV86;Qa#l7mN>>P1boW zja8CoVz&BtJFm*?yTsbL_-lmzB7^IL2u|SE0s8}_`7O9?NuoXzf}Ga^#9(1Sin0+z zEJ2yNcV5%)V0-N*k?U}!7jaHI`(uTamNoo0k4%NWY1atL+}o>P@=6a}zErMHy}$G{ zQRvg>4foov_^ioQCN-<~XA-ieb}W^)40g{wz4FxUQh_|(eG$@3b4}xWea80&)o)dk ze=eXz(~%Ou9K9H{3>a!*Ppz&XmcAR2y}r`zCfON5M!4 z4W}sRVPS>JNpVmRDxkFoo#3g0E2pQY2(#@;=umyG-BJZ}C(0T)UzOHYtR^5KJUpfA_@Z8lgM98beY*ib8XPA0q332+Tf7UzoR=tn2 zQ>Vp~OeJ<;km6I?Q4%~WTUT?A(sBz5G9lExvglAkV_S+odj0C0h5ENAbm- z?;lMq#O)Z!DyN;O#NP}wENmhMIdIql_?+6u(zNIbv&liwpgwsL=}>=~ zdx*NYI~EG;@`U>$2Z*%XAWv=&h5K~b3I|p}(&dK-fLK0Pl`!?%oym#FmWAW3afIW9u zB%n{@)TwHLlO2j_7>`3tv?u>9{>rkrmZoIhxKJN;ylZ$FqeO|PikIm~zqlXMX8+1% z(YdKcWxDF#!GhVv-WT|5ICa49^k{jD1T`r}@*MWn(-;V6K4CU?-7woJwi$d>ana@` zNRb{q@v1opo8)=&jP^$8yUs49Z@SaN$p&AnEn0ZZM9pP&h!w_kDz&Y9R#{x;B?;)% zP}RM<>{k|^{o_wqvEf#f9uXxP96e2#@rTVkM>;iT>bUs|Z% z%gV|euDsPuNqQflki7@1au;IeV5%m5b05!N?55k?JDxP>3n6!+6Nr4o9T01T7~0nBDZ2(z@U!x;IG@P`O}fVTDQ=u`uiie zCx_LOJ%c-Zh|&hwj1DGl1!zNm^dmPNmIwPMjYFfC9StC#Yth7W~L zmNg2~;o^2-ep}hUR>(yYV<``5&f2**vQ^?cHo$)F-j8u~GQWR(Bg~4;zOPJ>)`Z#`x4p%WoOiiQB3(e1eNl zD*lG@#uOW6)%dhmmbh&Z#l%8p?2%Yz({q>MNpI2>I_?TTuax1R;n(iTJ^JqxLVu9( zlf<%AiMzYad)2KeyjrtXC0x0fEEtvgfSFRc}9b3->@y2nl zU+>ijR#))N@OL(3sxhOlkSp6_Nw^lnCK0PXF@CmA#k|~MyJm~fM~S6c_fKrprBs$n zOEMb&cZc|1y2OndWU0L1s#?kGCy5{&jIFWHt$b*!{&5Wb=zn+WseEItXUfN~Azq`{ zyk&l!C5%SP(LbjFryO?MKO{?JBmeu@i>uM7&JLFg4#BgSiaLGURIVDf;2{R&L59EU z!m2|NSLrx!Y4c3L_aV`I%^hTn@&wdzsRv#dH%oOu+rC)6DYU76E)d&65YUwdoNQWRPA)p|Lgh+RH7>KmejkL6Mmk3CAr*zk%^FB*| z=Ztgy|9i){V~@SJEY|wg`@QcIbIxae`aV;T!o?!PLP0^nm63j;jDmuSfPWdUVZbYF z-^vo;e;99`N)wCLlgH1U#?~ht-H0T}>vyKpc$D7}T}u_c^Mycm z`7Y5)AJLb4etTK(Wy6`S9VL6+dH?x6Lk%P9J!LsDhR4Q_uO$t0iDA*6ADq*??ffc+ zDlP3)o48Kz=qga;$|>O5-PO7IjzH|%-H-qMp9E_88yO^jUz*j=5bV~`M0NW2Yb~#O zEd=!g=7!pTZ&oD9Au;0XRC$oXIB)y!E%!gOjUZ6b_k*#^xr3uKzTVOLd+E;^SgYYJ zXj^TeXb5NYl^7|iC2qy{uKzyM;JVozj(mk}+H2~Y7x|7+Pr~$`krms&HKFbEeK?TH zSZ_CBs4qW$AK=sNk`uX6N0)ecvj_X{6~21j$?see8NFB%Kx_Ht_0H`e!Lde@?pzbq zrBhGg_P@(Gk)j^2KXyB)7n=K{x6v5I=Zcbl*AU-5XzJuWPLa|Fgg7Rua;oru-fV+= z9uwhtyWCKaL*M?rX^~Ku4?3B@@-TL=#hL1mN|ES~37ou;k z;L~?DE{*E%&n%4!T$U#suQwX(rDFZ(swU~9?~kPN1v-T(=$SEw7sHjb8qQLL_FQgu z{JWDKxu`3~`1Cb`sK-)UHlb^i=DXNE)HMt9o@5>L8L8}VkEGw@Q-!P&|2-aBp(q|& z+w#DNI%u~;Mtk!VyJFksoyAE%NWFKoalX)WSyTy88Tnz&xiMZ6{-4iu54MGiSncdC z2x>bz+AqXAo(N~#rJuR-NFN>I@u-tDnH0&Z@09OK$K?riPYdi7I>rVPHAsTot^2_g}DhPU5-POZSVKqQFlrXHL%_Z*pFaY@W}D5ov7=eb8%B-0m8;d}y)mgAyl5+7>TR zQ0Vh7Zb*ij+9U1XZwPs=-<_$yvhS=SBBD`h(mJ2nZzJ!>&KouDaM=*Ba#<_vyvj9J zVi9gy>C+&1HcptRuy}Obf?LpfV`PcjZn2lzz0EPIv-()uW_kOD>ScKdUy(siW@EP9 z>NZoT2vs*`%9L8Y=1k{Q3^>-Vs5!=6#s<8vlUlC9e(zs^*%?zg;udA3*58Y$<_3_tj5#yU811Q3$ zmpR83?6cDayBMQ%?slG0FG8Q1<)z$dG~4Y&(HT@UH9D5X)-s-Y&>axrS@!;VXgd4J zxnrhwK5N}&fzOo>pWf(ND6FZnBr?dmNMgM59F@HEaCgqNrN2o?xU-wpOwTE@u-ses zvfAp3zHB7j*-v!Y@zK@<#=o(*8&)Ibu{&UF5_~q-5hctbbtjQqBvD6rPWORDloUa} z^L?r%nfK0AkxFiEs7gvo)%yndf}GOw@_LKwd^qdV!NK$aSHCSVvy@xU(W#_&|K^Tj zlZ!dAUz;5+|9T>Lj{^%66H}wi#J9FqxVF~*?H1ABm^f}*{o2jpyjqNkjtIus3%)*I z^k#d#X6Iy{;tgU%lnTqiC&sO%gIRX*2c`RYb$)P-)eh?Q>%Lr;i}{>&&YYfI@z_gK zHLl(F0K%oBDTeSS*n{o9G&w4_BygXQ+WvG{6s^b*HpITK!8k(D9Dr@aW&Ret`E=v6evsF=^Wj$x?=;#=!b*B#`8)%uDx+pN- zE!3SefL&mCUCE`Er?qf#ej3L5TI1l^zuNhsDw|orVSRp3ZG4GO(gQNr%F(`)^L0vd zpuJW**0SwWsjfdUs)b_n8j36@ViB6 zdrWpsRvjW&SL3>`Gt$#gn1O{{+GmO>=L|MxwTmxXyoVDWt@|?l#E*tpjeg{{F38Ro%tS@bKbvW5y zj%ifXMaa@b8`!-56;Uxk@)*PLuV{$BVIW4N@?W)BNsjo$;!&031ydT`-^2Sw_@lUx zX-tRen?!qr1Y(n$tjpV5DNUAml2A_^uP$K2^=pWL&u4|CEpRKHc`8r~)S^)t0tW zobYVbxqS;utNqnM!_uHvWfr}e?Q3zk*r6PenZGmU=oNa_I)%gXmn9iZ1tx^D`tmPD z^R(;m804b)ahb1+h-I zZ)-HRr)U>(>^9~0z+ARy?QlBH0*k#yX&ZAxFLHz}szW(seJ{ zxqXNqf%@V7k5cCLJw<~v%(aejOlr3$8XEk@E5>eI2J8M)oZ8PbB>Vy*Pw>sgs)_08 z>4js@jf42Rsz;u*Rh1v;O1nDSh<^&w*_v{fyDbvI^g{4+P|zP~>1%LM-8piX=7!x# z38^ekPup8c&Bs^z%cuvl)%4m@eR=u#Zjh3;QoA0!F*5Q>W|$~<_74jqqLYtqj!E?B zk?~wpMMKneqwcUPmLqf;2LdV70+{rdI8vwMqy;faJqkw+ul z2|mU9WQM)XK?FLpsl?Ke!=0l$`Ht=%zS4YpsK49NookWqxqICg-}2SQ<&uq`GUXZUU_Rb=xKVa=k}UyumBheqyvt=Wu-7!zOKK|M zdKOcvbD8*#Z&$t0E_w`Ob-?s@v-~T&G3xqk$|@Zb-M~z5ZYi;##Y!R1bNhhB{NjeO zE~=JY)&NS80-d#Ih5L-eR#km{eJcnD5y9+Jomb6Qd~Ar1^}2;e*<_dPmJ}#He8S7{ zsHsF-A8)L`a&A1G$8vsJ(>OLLqFkCIfW{$E51bZGijMm zE39S^gr=ntr(*Wd03(yCRPbGC6s9d*REq;e5ppC%DTKnS|M%XIGv+eTs)h?Q(b8RW)bVP?X@c)>`wA zB=MuSV*_WwJEvET`u2!(Hr6xY060bB#a&z^-wo}5Uzm1pDajtT>8NwqAZxEXy=YpG zW~=na54)%3**cKwOC2v@|90MWOQo}jNm!VhigB%@g;L90ar{y4!$*${*1GH1-F{UK z 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 literal 0 HcmV?d00001 diff --git a/doc/manual/pages/images/course_design.png b/doc/manual/pages/images/course_design.png new file mode 100644 index 0000000000000000000000000000000000000000..7e763e72576fd7f852e651d8438dc3b2aefb45e6 GIT binary patch literal 138556 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 z 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 literal 0 HcmV?d00001 diff --git a/doc/manual/pages/images/find_objects.png b/doc/manual/pages/images/find_objects.png new file mode 100644 index 0000000000000000000000000000000000000000..376c4d1ec4bef3c7990fda1e83ab38dde419215d GIT binary patch literal 17724 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 literal 0 HcmV?d00001 diff --git a/doc/manual/pages/images/georeferencing.png b/doc/manual/pages/images/georeferencing.png new file mode 100644 index 0000000000000000000000000000000000000000..6216ffc48dadc41532fbbcefff70291363f4616f GIT binary patch literal 73351 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

    JK!Mw+IskSU8I?&6zX}< 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˖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

    +Drücken Sie beim Bearbeiten einer Karte die Tab-Taste, um schnell zwischen dem Bearbeitungstool und den Zeichentools zu wechseln. +Dies ist der dritte Tipp des Tages, der jedoch nur ein Platzhalter ist. Wenn Sie einen Tipp wissen, fügen Sie ihn doch hinzu ... diff --git a/doc/tip-of-the-day/tips_en.txt b/doc/tip-of-the-day/tips_en.txt new file mode 100644 index 0000000..66cc951 --- /dev/null +++ b/doc/tip-of-the-day/tips_en.txt @@ -0,0 +1,16 @@ +

    Welcome to OpenOrienteering Mapper!

    If this is the first time you use the program, please read this first.

    You might want to check out the settings and adjust the program to you. For detailed advice on using Mapper, read the help files.

    Quick start advice:
    • Create or load a map using the buttons on the left
    • Hold the middle mouse button to drag the map
    • Zoom using the mouse wheel, if available
    • Read the hints in the status bar at the bottom to learn how the tools work
    +

    Examples

    +While editing a map, press Tab to switch between the edit tool and the drawing tools quickly. +A map with few well chosen features will give a much better map than a map cluttered with many insignificant features.

    Eduard Imhof +Features that are not important for a competitor taking part in a sprint orienteering event should not be mapped. Examples of this are waste baskets, fire hydrants, parking meters and individual street lights as they clutter the map with unhelpful detail. +In the end, it is the mapmaker's task to produce precise and legible orienteering maps by applying the specifications and generalisation rules, such as selection, simplification and exaggeration. +Mapping multilevel structures (bridges, underpasses or underground buildings) is confusing. Generally only the main "running" level should be shown. However, underground passages (e.g. underpasses, lighted tunnels) or overpasses (e.g. bridges), which are important for competitors should be mapped. +All line widths and symbol dimensions must be kept strictly to their specified value. Certain minimum dimensions must also be observed. These are based on both printing technology and the need for legibility. +Minimum gap between two line symbols of the same colour, in brown or black: 0.15 mm, in blue: 0.25 mm. +Minimum gap between line symbols and area symbols of the same colour, in black: 0.15 mm. +Shortest dotted line: at least two dots. +Shortest dashed line: at least two dashes. +Smallest area enclosed by a dotted line: 1.5 mm (diameter) with 5 dots. +Smallest area of colour:
    • Blue, green, grey or yellow full colour: 0.5 mm2
    • Black dot screen: 0.5 mm2
    • Blue, brown, green or yellow dot screen: 1.0 mm2
    +All features plotting smaller than the minimum dimensions must be either exaggerated or omitted, depending on whether or not they are of significance to the competitor. +The sprint map format should not exceed DIN A4. diff --git a/doc/tip-of-the-day/tips_fr.txt b/doc/tip-of-the-day/tips_fr.txt new file mode 100644 index 0000000..4fb60f1 --- /dev/null +++ b/doc/tip-of-the-day/tips_fr.txt @@ -0,0 +1,16 @@ +

    Bienvenue dans OpenOrienteering Mapper !

    Si c'est la première fois que vous utilisez ce logiciel, prenez le temps de lire ceci.

    Il est possible d'accéder aux préférences et de personnaliser la configuration du logiciel. Pour des conseils détaillés sur l'utilisation de Mapper, consultez le manuel.

    Conseils pour commencer tout de suite:
    - Créer ou charger une carte avec les boutons à gauche
    - Maintenir appuyer la molette de la souris pour déplacer la carte
    - Pour zoomer, utiliser la molette de la souris, si disponible
    - Lire les conseils dans la barre d'état (en bas de l'écran) afin d'apprendre à utiliser les outils. +

    Exemples

    +Lors de l'édition d'une carte, appuyer sur Tab pour basculer rapidement entre l'outil d'édition et les outils de dessin. +Une carte avec peu de symboles bien choisis sera bien meilleure qu'une carte encombrée de symboles inutiles.

    Eduard Imhof +Les objets inutiles pour les compétiteurs dans une course d'orientation de sprint ne devraient pas être cartographiés. Par exemple les corbeilles de rue, les bouches d'incendie, les parcmètres, les révèrbères sont des détails inutiles qui encombrent la carte. +L'objectif du cartographe est de produire une carte précise et lisible en appliquant les principes spécifiques et généraux suivant : sélection, simplification et exagération. +Cartographier des structures multi-niveaux (pont, passages souterrains ou batîments souterrains) est source de confusion. En général, seul le "niveau principal de course" doit apparaître. Cependant les passages souterrains, tunnel éclairés ou les passages aériens comme les ponts qui sont utiles pour les orienteurs doivent être cartographiés. +Toutes les largeurs de lignes et les tailles des symboles doivent conserver leur valeur spécifiée. Certaines dimensions minimales doivent être respectées pour apparaître correctement à l'impression et permettre une bonne lisibilité de la carte. +Écart minimum entre deux symboles de ligne de la même couleur :
    en brun ou noir : 0,15 mm
    en bleu : 0,25 mm +Écart minimum entre deux symboles de surface de la même couleur : en noir : 0,15 mm +Pour les lignes en pointillés : au moins deux pointillés doivent apparaître +Pour les lignes en tirets : au moins deux tirets doivent apparaître +Les surfaces délimitées par des lignes tirets doivent avoir un diamètre au moins égal à 1,5 mm et être composées d'au moins 5 tirets +Les surfaces de couleurs doivent avoir un diamètre minimum :
    Bleu, vert, gris ou jaune en plein : 0,5 mm²;
    Pointillés noirs : 0,5 mm²;
    Bleu, marron, vert ou jaune en pointillés : 1,0 mm² +Tous les objets plus petits que les dimensions minimums doivent être exagérées (agrandies sur la carte) ou ne pas apparaître, en fonction de leur utilité ou non pour les orienteurs. +La taille d'une carte de sprint ne doit pas dépasser le format A4. diff --git a/doc/tip-of-the-day/tips_ru.txt b/doc/tip-of-the-day/tips_ru.txt new file mode 100644 index 0000000..1d8ed4d --- /dev/null +++ b/doc/tip-of-the-day/tips_ru.txt @@ -0,0 +1,14 @@ +

    Добро пожаловать в OpenOrienteering Mapper!

    Если вы вперые открыли эту программу, прочитайте это сначала.

    Возможно вы захотите ознакомиться с Параметрами и настроить программу под себя. Более подробно об использовании Mapper читайте в Справке.

    Советы по быстрому старту:
    • Создайте или загрузите карту с помощью кнопок слева
    • Удерживайте среднюю кнопку мыши для перемещения карты
    • Изменяйте масштаб с помощью колесика мыши, если возможно
    • Читайте подсказки в нижней части окна, чтобы понять как работает инструмент
    +

    Примеры

    +Во время редактирования карты, используйте Tab для быстрого переключения между инструментами редактирования и рисования. +A map with few well chosen features will give a much better map than a map cluttered with many insignificant features.

    Eduard Imhof +Объекты, которые не важны для спортсмена в спринт ориентировании, не отображаются. Примерами могут служить мусорные корзины, пожарные помпы, парковочные столбы и т.п. +Отображение многоуровневых конструкций (мосты, подземные переходы или прочее) сбивает с толку. Как правило только главный "беговой" уровень должен быть отображен. Однако, если такие объекты важны для участников, то они должны быть отображены. +Все толщины линий и размеры символов должны строго соответствовать указанным значениям. Минимальные размеры также должны соблюдаться. Они основаны, и на технологии печати и на потребности в удобочитаемости. +Промежуток между двумя линиями одного и того же цвета должен быть не менее 0,15 мм (для чёрного, коричневого) и 0,25 мм (для голубого). +Минимальные размеры точечных линий: минимум две точки. +Минимальные размеры пунктирных линий: минимум два штриха. +Минимальный размер площади, отображаемой точками: 1,5 мм диаметром (5 точек). +Минимальный размер площадей, отображаемых цветом:
    • голубой, зелёной, серой или жёлтой заливками: 0,5 мм2
    • чёрной точечной сеткой: 0,5 мм2
    • голубой, коричневой, зелёной или жёлтой точечной сеткой: 1,0 мм2
    +Все объекты меньших размеров должны быть или утрированны (обобщены) или пропущены (не нанесены на карту), в зависимости от того насколько они важны для ориентирования. +Размер карты для спринта не должен превышать формата DIN A4. diff --git a/doc/tip-of-the-day/tips_uk.txt b/doc/tip-of-the-day/tips_uk.txt new file mode 100644 index 0000000..063d56a --- /dev/null +++ b/doc/tip-of-the-day/tips_uk.txt @@ -0,0 +1,16 @@ +

    Вітаємо в OpenOrienteering Mapper!

    Якщо ви запустили цю програму вперше, вам можуть бути цікавими наші поради.

    Можливо ви захочете переглянути налаштування щоб налаштувати програму на свій смак. Детальні поради щодо використання Mapper можна знайти у вбудованій довідці.

    Швидкий старт:
    • Створіть або відкрийте карту за допомогою кнопок ліворуч
    • Утримуйте середню кнопку миші щоб переміщувати карту
    • Віддаляйте або наближайте карту за допомогою коліщатка на миші
    • Читайте підказки у рядку статусу в нижній частині вікна щоб дізнатися як можуть працювати інструменти
    +

    Зразки

    +Під час креслення карти, використовуйте Tab щоб швидко переключатися між інструментами креслення і редагування. +A map with few well chosen features will give a much better map than a map cluttered with many insignificant features.

    Eduard Imhof +Не варто зображати на спринтерській карті об'єкти, що є неважливими для учасника змагань. Прикладами таких об'єктів є сміттєві урни, пожежні гідранти, парковочні автомати та ліхтарні стовпи - це лише навантажує карту непотрібними деталями. +Кінцевою задачею картографа є випуск точної чіткої карти, застосовуючи при цьому специфікацію і прийоми генералізації, такі як вибіркове відображення, спрощення або перебільшення. +Зображення багаторівневих конструкцій (мостів, підземних переходів або інших споруд) спантеличує. У загальному випадку лише головний "біговий" рівень має бути зображений. Однак, проходи (підземні переходи, освічені тунелі, мости), що важливі для учасників змагань, мають бути показані. +Товщина ліній та розміри знаків повинні точно відповідати визначеним величинам. Також мають бути витримані певні мінімальні розміри. Ці вимоги забезпечують чіткість карти незалежно від технології друку. +Мінімальна відстань між двома лінійними знаками однакового кольору - коричневий або чорний: 0.15 мм, синій: 0.25 мм. +Мінімальна відстань між лінією та точковим об'єктом чорного кольору - 0.15 мм. +Найкоротша лінія з точок: мінімум дві точки. +Найкоротша лінія зі штрихами: мінімум два штрихи. +Мінімальна площа обнесена контуром: діаметр 1.5 мм і 5 точок. +Мінімальні площі для об'єктів певних кольорів:
    • Синій, зелений, сірий або повний жовтий: 0.5 мм2
    • Заповнення чорними точками: 0.5 мм2
    • Заповнення синіми, коричневими, зеленими чи жовтими точками: 1.0 мм2
    +Всі елементи креслення що менші за мінімальні дозволені, мають бути збільшені, або упущені, в залежності від того наскільки вони важливі для учасника змагань. +Формат карти для спринту не повинен перевищувати DIN A4. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..3ce5376 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# Copyright 2013-2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +set(Mapper_EXAMPLES + overprinting.omap + forest\ sample.omap + complete\ map.omap +) + +if(ANDROID) + # \todo Support overprinting simulation in the mobile app. + list(REMOVE_ITEM Mapper_EXAMPLES overprinting.omap) +endif() + +install( + FILES ${Mapper_EXAMPLES} + DESTINATION "${MAPPER_DATA_DESTINATION}/examples" +) + +foreach(_example ${Mapper_EXAMPLES} examples.qrc) + configure_file(${_example} ${_example} COPYONLY) +endforeach() + +if(Mapper_DEVELOPMENT_BUILD) + configure_file(overprinting.omap autosave-test.omap COPYONLY) + configure_file(complete\ map.omap autosave-test.omap.autosave COPYONLY) +endif() diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..1509ab4 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +The examples to be edited are stored in the src subdirectory in the verbose +.xmap format. The examples to be distributed are stored in the more compact +.omap format in this subdirectory. The test subdirectory builds (under CMake) +an executable (test) `symbol_set_t` which updates compressed files from a list +of known files in the src directory. diff --git a/examples/autosave-example.qrc b/examples/autosave-example.qrc new file mode 100644 index 0000000..3a43034 --- /dev/null +++ b/examples/autosave-example.qrc @@ -0,0 +1,6 @@ + + + complete map.omap + overprinting.omap + + diff --git a/examples/complete map.omap b/examples/complete map.omap new file mode 100644 index 0000000..28a4c06 --- /dev/null +++ b/examples/complete map.omap @@ -0,0 +1,4910 @@ + + + ++proj=utm +datum=WGS84 +zone=3232 N+proj=latlong +datum=WGS84 + +PURPLE + +BLACK +RED + + + + + + + + + + + + + + +BLUE + + + + +BROWN + + + + + + +GREEN + + + +YELLOW + + + + + + + +A line joining points of equal height. The standard vertical interval between contours is 2 or 2.5 m. To emphasize the 3-dimensional effect of the contour line image, contour lines shall be represented as continuous lines through all symbols, also building (526.1) and canopy (526.2). However, contour lines shall be cut out for better legibility, if they touch the following symbols: small earth wall (108.1), small knoll (112), small elongated knoll (113), small depression (115), pit or hole (116), prominent landform feature (118), step or edge of paved area (529.1). + +The relative height difference between neighbouring features must be represented on the map as accurately as possible. Absolute height accuracy is of less importance. It is permissible to alter the height of a contour slightly if this will improve the representation of a feature. This deviation should not exceed 25% of the contour interval and attention must be paid to neighbouring features. The smallest bend in a contour is 0.4 mm from centre to centre of the lines. +Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with symbol contour (101). +An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. +Slope lines should be drawn on the lower side of a contour line where it is necessary to clarify the direction of slope, e.g. along the line of a re-entrant or in a depression.0 0;0 -750; +Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. +A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, roads and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks shall be drawn with the symbol impassable cliff (201). The line width of very high earth banks may be 0.37 mm.0 750;0 0; +A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, roads and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks shall be drawn with the symbol impassable cliff (201). The line width of very high earth banks may be 0.37 mm.0 750;0 100; +Use this symbol to display the full extent of wide earth banks. +A small distinct earth wall, usually man made. The minimum height is 0.5 m. Larger earth walls should be represented with the symbols contour (101), form line (103) or earth bank (106). +An erosion gully or trench which is too small to be represented with the symbol earth bank (106), contour (101), index contour (102) or form line (103) is represented by a single line. The line width reflects the size of the gully. The end of the line is pointed. Minimum depth is 1 m. Minimum length is 3 mm on the map. +A small erosion gully or trench. Minimum depth is 0.5 m. +A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (101), index contour (102) or form line (103). The height of the knoll should be a minimum of 1 m from the surrounding ground. +A small obvious elongated knoll which cannot be drawn to scale with a contour (101), index contour (102) or form line (103). The maximum length should be 6 m and the maximum width 2 m. The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this shall be shown by contours. + +The symbol may not be drawn in free form or such that two elongated knoll symbols touch or overlap.0 -600 1;166 -600;300 -331;300 0 1;300 331;166 600;0 600 1;-166 600;-300 331;-300 0 1;-300 -331;-166 -600;0 -600; +A small shallow natural depression or hollow which cannot be represented by the symbol contour (101) or form line (103) is represented by a semicircle. The minimum diameter should be 2 m. The minimum depth from the surrounding ground should be 1 m. The symbol is orientated to north.475 -135 1;475 127;262 340;0 340 1;-262 340;-475 127;-475 -135; +A pit or hole with distinct steep sides which cannot be represented to scale with the symbol earth bank (106). The minimum diameter shall be 2 m. The minimum depth from the surrounding ground shall be 1 m. The symbol is orientated to north.-546 -409;-274 -409;0 217;274 -409;546 -409;0 841; +An area of pits or knolls, which is too complex to be represented in detail. The density of randomly placed dots may vary according to the detail on the ground. +An area of pits or knolls, which is too complex to be represented in detail. The density of randomly placed dots may vary according to the detail on the ground. +A small landform feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north.-600 -600;600 600;-600 600;600 -600; +An impassable cliff, quarry or earth bank [see symbol earth bank (106)]. Tags are drawn downwards, showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). + +The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags shall clearly extend over the bank line. Minimum height is 2 meters. + +<span style="color:magenta">It is forbidden to cross an impassable cliff! +Competitors violating this rule will be disqualified.</span>0 100;0 750; +An impassable cliff, quarry or earth bank [see symbol earth bank (106)]. Tags are drawn downwards, showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). + +The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags shall clearly extend over the bank line. Minimum height is 2 meters. + +<span style="color:magenta">It is forbidden to cross an impassable cliff! +Competitors violating this rule will be disqualified.</span>-450 0;450 0;270 0;270 750;-270 0;-270 750; +For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). +For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm).-450 0;450 0; +Use this symbol to display the full extent of a wide cliff. +A gigantic boulder, rock pillar or massive cliff shall be represented in plan shape without tags. +A small vertical rock face may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. Minimum height is 1 m. For passable rock faces shown without tags the end of the line may be rounded to improve legibility.0 50;0 750; +A small vertical rock face may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. Minimum height is 1 m. For passable rock faces shown without tags the end of the line may be rounded to improve legibility.-450 0;450 0;270 0;270 750;-270 0;-270 750; +Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. +Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good.-450 0;450 0; +Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. +Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the end of the line may be rounded to improve legibility.-300 0;300 0; +A rocky pit, hole or mineshaft which may constitute a danger to the competitor. The symbol is orientated to north.-546 -409;-274 -409;0 217;274 -409;546 -409;0 841; +A cave is represented by the same symbol as a rocky pit. In this case the symbol shall be orientated to point up the slope as indicated opposite. This symbol should generally not be used in urban areas. The centre of gravity of the symbol marks the opening. + +<span style="color:magenta">Controls may not be placed inside caves!</span>-546 -409;-274 -409;0 217;274 -409;546 -409;0 841; +A small distinct boulder. The minimum height is 1 m. Every boulder marked on the map shall be immediately identifiable on the ground. +A particularly large and distinct boulder. Gigantic boulders shall be represented in plan shape with the symbol gigantic boulder or rock pillar (202). +An area which is covered with so many blocks of stone that they cannot be marked individually is represented with randomly orientated solid triangles. The runnability is reduced and is indicated by the density of the triangles. A minimum of two triangles shall be used. The triangles can be enlarged by up to 20 %.150 452;150 -508;-299 55; +An area which is covered with so many blocks of stone that they cannot be marked individually is represented with randomly orientated solid triangles. The runnability is reduced and is indicated by the density of the triangles. A minimum of two triangles shall be used. The triangles can be enlarged by up to 20 %.180 542;180 -610;-359 66; +An area of stony or rocky ground which reduces runnability. The dots shall be randomly distributed with density according to the amount of rock. A minimum of three dots shall be used. +An area of stony or rocky ground which reduces runnability. The dots shall be randomly distributed with density according to the amount of rock. A minimum of three dots shall be used. +An area of soft sandy ground or gravel with no vegetation which reduces runnability. Where an area of sandy ground is open and has good runnability, it is represented with symbol open land (401), open land with scattered trees (402) or paved area (529). +An area of runnable rock without earth or vegetation. An area of rock covered with grass, moss or other low vegetation shall be represented according to its openness and runnability (401/402/403/404). +A water-filled pit or an area of water which is too small to be shown to scale. The symbol is orientated to north.-546 -409;-274 -409;0 217;274 -409;546 -409;0 841; +An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². + +<span style="color:magenta">It is forbidden to cross an impassable body of water! +Competitors violating this rule will be disqualified.</span> +An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². + +<span style="color:magenta">It is forbidden to cross an impassable body of water! +Competitors violating this rule will be disqualified.</span> +The bordering black line indicates that the feature cannot or shall not be crossed. +An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². + +<span style="color:magenta">It is forbidden to cross an impassable body of water! +Competitors violating this rule will be disqualified.</span> +An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². + +<span style="color:magenta">It is forbidden to cross an impassable body of water! +Competitors violating this rule will be disqualified.</span> +An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol impassable body of water (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. +An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. +An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol impassable body of water (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. +A crossable watercourse less than 2 m wide. +A natural or man-made minor water channel which may contain water only intermittently. +A marsh or trickle of water which is too narrow to be shown with symbol marsh (310). +A marsh which is impassable or which may constitute a danger to the competitor. The feature cannot or shall not be crossed. + +<span style="color:magenta">It is forbidden to cross an impassable marsh! +Competitors violating this rule will be disqualified.</span> +This symbol should not be used on its own. +This symbol should not be used on its own. +A crossable marsh, usually with a distinct edge. The symbol shall be combined with vegetation symbols to show runnability and openness. + +Minimum size: not less than 2 lines, 5 mm long. +-250 150;250 150;-250 -150;250 -150; +An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness. + +Minimum size: 4 dashes.-450 0;450 0;-450 0;450 0; +125 0;1025 0;-125 0;-1025 0;-450 -300;450 -300;-450 300;450 300; +Small well or fountain, which is at least 1 m high or at least 1 m in diameter. +The source of a stream with a distinct outflow. This symbol should generally not be used in urban areas. The symbol is orientated to open downstream.475 -135 1;475 127;262 340;0 340 1;-262 340;-475 127;-475 -135; +A small water feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north.-600 -600;600 600;-600 600;600 -600; +An area of cultivated land, lawn, field, meadow, grassland, etc. without trees, offering very good runnability. +An area of meadows with scattered trees or bushes, with grass or similar ground cover offering very good runnability. Areas smaller than 10 mm² at the maps scale are shown as open land (401). Symbols prominent large tree (418) and prominent bush or small tree (419) may be added. +An area of heath or moorland, a felled area, a newly planted area (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, i.e. heather or tall grass. This symbol may be combined with symbols undergrowth: slow running (407) and undergrowth: difficult to run (409) to show reduced runnability. +An area of rough open land with scattered trees or bushes. Areas smaller than 16 mm² in the map scale are either mapped as rough open land (403) or forest: easy running (405). Symbols prominent large tree (418) and prominent bush or small tree (419) may be added. +An area of typical open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. +An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. + +Minimum width 0.25 mm. + +An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, cut branches, etc.) which reduces running to ca. 60-80% of normal speed. This symbol shall not be combined with the symbol forest: slow running (406) or forest: difficult to run (408). +An area with dense trees or thicket (low visibility) which reduces running to ca. 20-60% of normal speed. + +An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. +An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced 1-20% of normal speed. + +Minimum width: 0.25 mm. + +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (406) to show the direction with good runnability. +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (408) to show the direction with good runnability. +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (410) to show the direction with good runnability. +Land planted with fruit trees or bushes. The dot lines may be orientated to represent the direction of planting. +Land planted with fruit trees or bushes, with a distinct direction of planting which reduces the runnability. The green lines shall be orientated to show the direction of planting.0 -650;0 650;0 -650;0 650; +The boundary of symbol cultivated land (seasonally out of bounds) (415) when not shown with other symbols (fence, wall, path, etc.) is represented with a black line. A permanent boundary between different types of cultivated land is also represented with this symbol. +Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. +A distinct forest edge or very distinct vegetation boundary within the forest. For indistinct boundaries, the area edges are shown only by the change in colour and/or dot screen. +A prominent single tree. +A bush or a tree with a trunk less than 0.5 m diameter. +A vegetation feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north.-600 -600;600 600;-600 600;600 -600; +An area of dense vegetation (trees or undergrowth) which is impassable or which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor. + +Minimum width: 0.4 mm. + +<span style="color:magenta">It is forbidden to cross impassable vegetation! +Competitors violating this rule will be disqualified.</span> + +An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). + +To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). +An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). + +To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). +An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). + +To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). +An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). + +To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). +A small unpaved footpath or track. Not to be used in urban areas. +A less distinct path or forestry extraction track. Not to be used in urban areas. +A distinct ride is a linear break in the forest (usually in a plantation), which does not have a distinct path along it. Where there is a path along a ride, the symbol small unpaved footpath or track (506.1) shall be used. Not to be used in urban areas. +A bridge is a structure spanning and permitting passage over a river, chasm, road or the like.125 200;125 670;125 -200;125 -670;-125 -200;-125 -670;-125 200;-125 670; +A bridge is a structure spanning and permitting passage over a river, chasm, road or the like.125 0;125 -345;-125 0;-125 -345; +A railway is a permanent track laid with rails on which locomotives, carriages or wagons can travel. If it is forbidden to cross or run along the railroad, the forbidden area around the railway shall be represented with symbol area with forbidden access (528.1). +A tramway is a public vehicle running regularly along certain streets, usually on rails. The track can be easily crossed by the competitor. Tramways are generally not represented. However, if they serve navigation or orientation, they can be represented. +Power line, cableway or skilift. The bars indicate the exact location of the pylons. + +<b>Note: When drawing this symbol, press space to toggle placing the pylon symbols at new nodes.</b>0 -300;0 300; +Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline. + +Very large carrying masts shall be represented in plan shape or with the symbol high tower (535). In this case, the cable lines can be left out (the map shows only the pylons). + +<b>Note: When drawing this symbol, press space to toggle placing the pylon symbols at new nodes.</b>0 -1120;0 1120; +An underpass or a tunnel is a passage running underneath the ground, especially a passage for pedestrians or vehicles, crossing under for instance a railroad or a road. + +<span style="color: magenta">If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with the symbol crossing point (708) or crossing section (708.1)!</span>-125 125;125 125;125 -125;-125 -125; +A stone wall or stone faced bank. This symbol shall be used only in non-urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol impassable wall (521.1). +A passable wall or retaining wall is a construction made of stone, brick, concrete etc., which can be passed. This symbol is suitable for urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol impassable wall (521.1). Wide walls shall be drawn in plan shape. +A passable wall or retaining wall is a construction made of stone, brick, concrete etc., which can be passed. This symbol is suitable for urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol (521.1). Wide walls shall be drawn in plan shape. +An impassable wall or retaining wall is a wall, which fulfil the function of an enclosure or solid barrier. It shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor due to its height. Very wide impassable walls shall be drawn in plan shape and represented with the symbol building (526.1). + +<span style="color:magenta">It is forbidden to cross an impassable wall! +Competitors violating this rule will be disqualified.</span> +An impassable wall or retaining wall is a wall, which fulfil the function of an enclosure or solid barrier. It shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor due to its height. Very wide impassable walls shall be drawn in plan shape and represented with the symbol building (526.1). + +<span style="color:magenta">It is forbidden to cross an impassable wall! +Competitors violating this rule will be disqualified.</span> +A passable fence is a barrier enclosing or bordering a field, yard, etc., usually made of posts and wire or wood. It is used to prevent entrance or to confine or mark a boundary. A railing is a fencelike barrier composed of one or more horizontal rails supported by widely spaced upright poles, usually it can be slipped through. + +If a fence or railing is higher than 2 m or very difficult to cross, it shall be represented with the symbol impassable fence or railing (524).0 0;375 650; +A passable fence is a barrier enclosing or bordering a field, yard, etc., usually made of posts and wire or wood. It is used to prevent entrance or to confine or mark a boundary. A railing is a fencelike barrier composed of one or more horizontal rails supported by widely spaced upright poles, usually it can be slipped through. + +If a fence or railing is higher than 2 m or very difficult to cross, it shall be represented with the symbol impassable fence or railing (524).0 0;375 650; +An impassable fence or railing, which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor because of its height. + +<span style="color:magenta">It is forbidden to cross an impassable fence or railing! +Competitors violating this rule will be disqualified.</span>450 0;825 650;-450 0;-75 650; +A crossing point is a gap or an opening in a fence, railing or wall, which can easily be crossed by a competitor. Small gaps or openings which can not easily be crossed by competitors, shall not be represented on the map and shall be closed during the competition.450 -700;450 700;-450 -700;-450 700; +A building is a relatively permanent construction having a roof. Buildings within symbol area with forbidden access (527.1) may just be represented in a simplified manner.Areas totally contained within a building shall be mapped as being a part of the building. + +The minimum gap between buildings and between buildings and other impassable features shall be 0.40 mm. The black screen percentage should be chosen according to the terrain. A dark screen gives a better contrast to passable areas, such as streets, stairways and canopies, while a light screen makes contours and course overprint more clearly visible (which can be important in very densely built up urban terrain and in steep urban terrain). The black screen shall be the same for the whole map. + +<span style="color:magenta">It is forbidden to pass through or over a building! +Competitors violating this rule will be disqualified.</span> +Do not use this symbol on its own! +Do not use this symbol on its own! +A building is a relatively permanent construction having a roof. Buildings within symbol area with forbidden access (527.1) may just be represented in a simplified manner.Areas totally contained within a building shall be mapped as being a part of the building. + +The minimum gap between buildings and between buildings and other impassable features shall be 0.40 mm. The black screen percentage should be chosen according to the terrain. A dark screen gives a better contrast to passable areas, such as streets, stairways and canopies, while a light screen makes contours and course overprint more clearly visible (which can be important in very densely built up urban terrain and in steep urban terrain). The black screen shall be the same for the whole map. + +<span style="color:magenta">It is forbidden to pass through or over a building! +Competitors violating this rule will be disqualified.</span>180 -180;180 180;-180 180;-180 -180;180 -180;180 180;-180 180;-180 -180;180 -180 18; +A canopy is a building construction (with a roof), normally supported by pillars, poles or walls, such as passages, gangways, courts, bus stops, gas stations or garages. + +Small passable parts of buildings which can not easily be crossed by competitors, shall not be represented on the map and shall be closed during the competition. +Do not use this symbol on its own! +Do not use this symbol on its own! +A pillar is an upright shaft or structure of stone, brick or other material, relatively slender in proportion to its height and any shape in section, used as a building support. Pillars smaller than 2 m × 2 m are generally not represented. + +Columns of pillars and pillars along buildings are not represented. However, if they are important for navigation and orientation, they can be represented.-250 -250;250 -250;250 250;-250 250;-250 -250 2; +An area with forbidden access such as a private area, a flower bed, a railway area etc. No feature shall be represented in this area, except very prominent features such as railways, large buildings, or very large trees. Road entrances shall be represented clearly. + +Areas with forbidden access totally contained within buildings shall be mapped as being a part of the building. + +<span style="color:magenta">It is forbidden to cross an area with forbidden access! +Competitors violating this rule will be disqualified.</span> +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). +A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. +A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. +A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm.0 220;0 -220; +A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm.0 440;0 -440; +A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under.-530 530;0 0;-530 -530; +An impassable pipeline (gas, water, oil, etc.) above ground level which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor because of its height. + +<span style="color:magenta">It is forbidden to cross an impassable pipeline! +Competitors violating this rule will be disqualified.</span>-530 530;0 0;-530 -530; +A high tower or large pylon. Very large towers shall be represented in plan shape with the symbol building (526.1). The symbol is orientated to north.-1050 0;1050 0;0 -1050;0 1050; +An obvious small tower, platform or seat. The symbol is orientated to north.-750 -382;750 -382;0 -382;0 1118; +Cairn, memorial, small monument or boundary stone more than 0.5 m high. Large massive monuments shall be represented in plan shape with the symbol building (526.1).0 0; +A fodder rack, which is free standing or attached to a tree. The symbol is orientated to north.-750 -48;0 -481;750 -48;0 -481;0 1019; +A man-made feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. +A man-made feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north.-600 -600;600 600;-600 600;600 -600; +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. + +<b>Note: this is a non-standard addition to the symbol set.</b>500 0;-1500 -600;-1000 0;-1500 600;500 0; +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Cut holes in the pattern to create these breaks. +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. + +<b>Note: this is a non-standard addition to the symbol set.</b>500 0;-1500 -600;-1000 0;-1500 600;500 0; +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Cut holes in the pattern to create these breaks. +At least three registration marks shall be placed within the frame of a map in a non-symmetrical arrangement. In addition, a colour check should be possible.0 -2000;0 2000;-2000 0;2000 0; +Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. +Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. +The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point.-3500 2021;0 -4041;3500 2021;-3500 2021 18; +The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. +The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. +Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. +A marked route is shown on the map with a dashed line. +The finish is shown by two concentric circles.0 0; +A boundary which it is not permitted to cross. Uncrossable boundaries shall be mapped by using the symbols: impassable cliff (201), impassable body of water (304.1), impassable marsh (309), impassable wall (521.1), impassable fence or railing (524) or impassable pipeline (534) and shall not be overprinted with symbol uncrossable boundary (707). This symbol is to be used only for last minute updates to the competition area, as excessive use of purple for indicating barriers is unfortunate. + +<span style="color:magenta">It is forbidden to cross an uncrossable boundary! +Competitors violating this rule will be disqualified.</span> +A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards. + +If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with symbol crossing point (708) or crossing section (708.1).-1500 725 1;-500 450;500 450;1500 725;-1500 -725 1;-500 -450;500 -450;1500 -725; +Acrossing section through or over a building, wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map as a linear object, according to the plan shape. + +If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with symbol (708) or (708.1).250 0;0 0 1;-600 0;-900 -150;-1500 -650;-250 0;0 0 1;600 0;900 -150;1500 -650; +Out of bounds areas are mapped with the symbol area with forbidden access (528.1). This symbol shall only be used for last minute updates to the competition map (e.g. for areas that may be dangerous for the competitors during the competition, or very late changes to the competition terrain). +An out-of-bounds area is shown with vertical stripes. A bounding line may be drawn if there is no natural boundary, as follows: +- a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, +- a dashed line indicates intermittent marking on the ground, +- no line indicates no marking on the ground. + +<span style="color:magenta">It is forbidden to cross an out-of-bounds area! +Competitors violating this rule will be disqualified.</span> +A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. +A dashed line indicates intermittent marking on the ground. +The location of a first aid post.-1500 0;1500 0;0 -1500;0 1500; +The location of a refreshment point which is not at a control or along the marked route.-1340 -1395;-820 1380;1340 -1395;820 1380;0 -1770 1;600 -1770;1200 -1620;1340 -1395 1;1200 -1170;600 -1020;0 -1020 1;-600 -1020;-1200 -1170;-1340 -1395 1;-1200 -1620;-600 -1770;0 -1770;-820 1380 1;-680 1680;680 1680;820 1380; +Obvious temporary constructions like platforms for spectators and speaker, closed area for spectators, outside restaurant areas, etc. shall be represented in plan shape. + +<span style="color:magenta">It is forbidden to enter a temporary construction or closed area! +Competitors violating this rule will be disqualified.</span> +This symbol provides a simple and quick way to make training courses. + +The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach.3041 0;3541 0;-3021 3500;3041 0;-3021 -3500;-3021 3500 18;-600 0;600 0;0 0; + + + + + + + + + + + + +The OpenOrienteering Logo.-12797 -557 1;-12755 -447;-12770 -420;-12873 -420 1;-12953 -420;-12973 -440;-12973 -520 1;-12973 -635;-12838 -663;-12797 -557 18;-933 2063 1;-933 1925;-920 1900;-851 1900 1;-780 1900;-770 1921;-781 2050 1;-789 2154;-814 2203;-863 2213 1;-920 2224;-933 2197;-933 2063;-933 2063 18;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 18;-4230 -3923 1;-4183 -3737;-4160 -3527;-4160 -3296;-4160 -1395;-5113 -1395;-5113 -3182 1;-5113 -3488;-5155 -3707;-5235 -3833 1;-5317 -3961;-5468 -4027;-5690 -4027 1;-5759 -4027;-5830 -4021;-5907 -4013 1;-5983 -4008;-6052 -4004;-6112 -3993;-6112 -1395;-7064 -1395;-7064 -4646 1;-6903 -4693;-6694 -4736;-6438 -4774 1;-6181 -4818;-5913 -4838;-5631 -4838 1;-5347 -4838;-5110 -4800;-4921 -4723 1;-4730 -4652;-4579 -4547;-4467 -4410 1;-4357 -4273;-4277 -4111;-4230 -3923 18;-13524 1118 1;-13605 1126;-13667 1137;-13708 1150;-13708 3722;-14662 3722;-14662 536 1;-14492 476;-14292 420;-14060 369 1;-13827 314;-13565 286;-13280 286 1;-13229 286;-13167 290;-13096 299 1;-13022 303;-12951 312;-12878 325 1;-12806 333;-12733 345;-12660 363 1;-12587 375;-12526 392;-12474 414;-12634 1201 1;-12719 1180;-12819 1158;-12936 1137 1;-13051 1112;-13174 1098;-13306 1098 1;-13366 1098;-13439 1105;-13524 1118 18;-11093 -200 1;-11204 -101;-11336 -52;-11490 -52 1;-11642 -52;-11777 -101;-11893 -200 1;-12004 -303;-12059 -441;-12059 -616 1;-12059 -791;-12004 -927;-11893 -1026 1;-11777 -1128;-11642 -1179;-11490 -1179 1;-11336 -1179;-11204 -1128;-11093 -1026 1;-10978 -927;-10920 -791;-10920 -616 1;-10920 -441;-10978 -303;-11093 -200 18;-11009 3722;-11963 3722;-11963 357;-11009 357;-11009 3722 18;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 18;-5220 1105 1;-5297 1110;-5365 1115;-5425 1125;-5425 3722;-6378 3722;-6378 472 1;-6217 425;-6007 382;-5751 344 1;-5495 301;-5227 280;-4945 280 1;-4658 280;-4422 318;-4235 395 1;-4042 467;-3890 572;-3781 709 1;-3669 845;-3591 1007;-3544 1195 1;-3497 1381;-3474 1592;-3474 1821;-3474 3722;-4427 3722;-4427 1937 1;-4427 1630;-4467 1411;-4549 1285 1;-4628 1156;-4780 1092;-5002 1092 1;-5070 1092;-5143 1097;-5220 1105 18;-2636 2346;-2636 -481;-1683 -634;-1683 357;-539 357;-539 1150;-1683 1150;-1683 2333 1;-1683 2535;-1648 2694;-1581 2813 1;-1508 2933;-1365 2993;-1151 2993 1;-1050 2993;-945 2984;-838 2966 1;-728 2946;-627 2918;-539 2884;-404 3626 1;-518 3673;-646 3712;-787 3748 1;-928 3780;-1102 3799;-1305 3799 1;-1566 3799;-1781 3764;-1951 3696 1;-2123 3624;-2259 3526;-2361 3402 1;-2463 3274;-2536 3121;-2579 2941 1;-2618 2763;-2636 2565;-2636 2346 18;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 18;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700;5923 1700 18;8718 1118 1;8637 1126;8575 1137;8534 1150;8534 3722;7581 3722;7581 536 1;7750 476;7950 420;8182 369 1;8415 314;8677 286;8962 286 1;9013 286;9075 290;9146 299 1;9220 303;9292 312;9365 325 1;9437 333;9510 345;9583 363 1;9655 375;9717 392;9768 414;9608 1201 1;9523 1180;9424 1158;9306 1137 1;9191 1112;9069 1098;8937 1098 1;8877 1098;8804 1105;8718 1118 18;10350 -200 1;10238 -303;10183 -441;10183 -616 1;10183 -791;10238 -927;10350 -1026 1;10465 -1128;10600 -1179;10752 -1179 1;10906 -1179;11038 -1128;11149 -1026 1;11264 -927;11323 -791;11323 -616 1;11323 -441;11264 -303;11149 -200 1;11038 -101;10906 -52;10752 -52 1;10600 -52;10465 -101;10350 -200 18;11233 3722;10279 3722;10279 357;11233 357;11233 3722 18;14726 709 1;14836 845;14916 1007;14963 1195 1;15010 1381;15033 1592;15033 1821;15033 3722;14080 3722;14080 1937 1;14080 1630;14039 1411;13958 1285 1;13876 1156;13725 1092;13504 1092 1;13435 1092;13363 1097;13287 1105 1;13210 1110;13142 1115;13082 1125;13082 3722;12129 3722;12129 472 1;12290 425;12499 382;12756 344 1;13012 301;13280 280;13562 280 1;13847 280;14083 318;14272 395 1;14463 467;14614 572;14726 709 18;-17051 -1307 1;-17299 -1307;-17525 -1349;-17729 -1434 1;-17931 -1521;-18102 -1639;-18247 -1794 1;-18392 -1951;-18505 -2139;-18587 -2355 1;-18669 -2579;-18708 -2820;-18708 -3085 1;-18708 -3350;-18669 -3591;-18587 -3808 1;-18502 -4027;-18387 -4211;-18242 -4365 1;-18092 -4518;-17917 -4638;-17717 -4723 1;-17512 -4808;-17291 -4851;-17051 -4851 1;-16808 -4851;-16586 -4808;-16386 -4723 1;-16182 -4638;-16006 -4518;-15861 -4365 1;-15716 -4211;-15603 -4027;-15523 -3808 1;-15442 -3591;-15401 -3350;-15401 -3085 1;-15401 -2820;-15440 -2579;-15517 -2355 1;-15593 -2139;-15703 -1951;-15848 -1794 1;-15993 -1639;-16168 -1521;-16373 -1434 1;-16574 -1349;-16800 -1307;-17051 -1307 18;-17051 -2126 1;-16834 -2126;-16668 -2210;-16552 -2383 1;-16433 -2557;-16373 -2792;-16373 -3085 1;-16373 -3380;-16433 -3610;-16552 -3777 1;-16668 -3946;-16834 -4034;-17051 -4034 1;-17269 -4034;-17437 -3946;-17557 -3777 1;-17675 -3610;-17736 -3380;-17736 -3085 1;-17736 -2792;-17675 -2557;-17557 -2383 1;-17437 -2210;-17269 -2126;-17051 -2126 18;-14662 -213;-14662 -4646 1;-14576 -4673;-14478 -4697;-14369 -4716 1;-14257 -4743;-14142 -4765;-14022 -4781 1;-13898 -4798;-13776 -4811;-13652 -4819 1;-13524 -4833;-13402 -4838;-13287 -4838 1;-13009 -4838;-12763 -4796;-12544 -4711 1;-12328 -4630;-12144 -4513;-11995 -4358 1;-11846 -4210;-11732 -4027;-11657 -3808 1;-11574 -3591;-11535 -3348;-11535 -3078 1;-11535 -2819;-11566 -2582;-11629 -2368 1;-11694 -2156;-11788 -1972;-11911 -1819 1;-12034 -1666;-12189 -1546;-12373 -1461 1;-12556 -1376;-12765 -1333;-13006 -1333 1;-13137 -1333;-13261 -1346;-13377 -1371 1;-13492 -1395;-13602 -1432;-13708 -1479;-13708 -213;-14662 -213 18;-13184 -2139 1;-12733 -2139;-12506 -2443;-12506 -3054 1;-12506 -3348;-12572 -3582;-12704 -3756 1;-12837 -3936;-13032 -4027;-13293 -4027 1;-13379 -4027;-13457 -4021;-13530 -4013 1;-13602 -4008;-13662 -4004;-13708 -3993;-13708 -2274 1;-13648 -2234;-13572 -2202;-13479 -2177 1;-13381 -2152;-13282 -2139;-13184 -2139 18;-9182 -1307 1;-9485 -1307;-9750 -1352;-9976 -1440 1;-10198 -1530;-10383 -1652;-10533 -1806 1;-10678 -1964;-10787 -2149;-10858 -2362 1;-10926 -2575;-10962 -2807;-10962 -3054 1;-10962 -3352;-10917 -3612;-10827 -3833 1;-10733 -4060;-10611 -4248;-10462 -4396 1;-10314 -4547;-10141 -4660;-9950 -4736 1;-9754 -4813;-9553 -4851;-9349 -4851 1;-8872 -4851;-8494 -4705;-8217 -4410 1;-7939 -4120;-7801 -3692;-7801 -3123 1;-7801 -3069;-7803 -3007;-7808 -2939 1;-7810 -2873;-7816 -2817;-7819 -2766;-9982 -2766 1;-9961 -2569;-9870 -2413;-9707 -2299 1;-9545 -2184;-9327 -2126;-9055 -2126 1;-8881 -2126;-8708 -2141;-8542 -2171 1;-8372 -2205;-8234 -2246;-8127 -2292;-7999 -1517 1;-8051 -1493;-8119 -1468;-8204 -1440 1;-8289 -1416;-8385 -1395;-8492 -1378 1;-8594 -1356;-8706 -1339;-8824 -1326 1;-8944 -1314;-9063 -1307;-9182 -1307 18;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418 18;-17051 -2126 1;-17269 -2126;-17437 -2210;-17557 -2383 1;-17675 -2557;-17736 -2792;-17736 -3085 1;-17736 -3380;-17675 -3610;-17557 -3777 1;-17437 -3946;-17269 -4034;-17051 -4034 1;-16834 -4034;-16668 -3946;-16552 -3777 1;-16433 -3610;-16373 -3380;-16373 -3085 1;-16373 -2792;-16433 -2557;-16552 -2383 1;-16668 -2210;-16834 -2126;-17051 -2126 18;-17064 -2593 1;-16944 -2591;-16842 -2808;-16838 -3077 1;-16834 -3347;-16928 -3567;-17049 -3569 1;-17170 -3571;-17271 -3354;-17275 -3084 1;-17280 -2815;-17185 -2595;-17064 -2593 18;-8496 3810 1;-8798 3810;-9062 3765;-9288 3677 1;-9510 3588;-9696 3466;-9845 3312 1;-9990 3154;-10098 2969;-10171 2756 1;-10240 2542;-10273 2311;-10273 2065 1;-10273 1766;-10230 1507;-10140 1285 1;-10045 1058;-9923 870;-9775 722 1;-9625 572;-9455 459;-9264 382 1;-9067 305;-8867 267;-8662 267 1;-8184 267;-7806 414;-7529 709 1;-7252 998;-7113 1426;-7113 1995 1;-7113 2050;-7116 2112;-7119 2180 1;-7124 2245;-7128 2301;-7132 2353;-9296 2353 1;-9273 2550;-9182 2704;-9020 2820 1;-8857 2934;-8640 2993;-8368 2993 1;-8193 2993;-8022 2978;-7855 2948 1;-7684 2912;-7546 2873;-7440 2826;-7311 3601 1;-7363 3626;-7431 3651;-7516 3677 1;-7603 3703;-7697 3724;-7804 3741 1;-7908 3763;-8017 3779;-8137 3793 1;-8257 3804;-8376 3810;-8496 3810 18;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700 18;1726 3810 1;1422 3810;1159 3765;933 3677 1;712 3588;524 3466;376 3312 1;231 3154;123 2969;49 2756 1;-19 2542;-52 2311;-52 2065 1;-52 1766;-9 1507;81 1285 1;177 1058;298 870;446 722 1;596 572;766 459;958 382 1;1154 305;1354 267;1559 267 1;2038 267;2416 414;2692 709 1;2968 998;3109 1426;3109 1995 1;3109 2050;3105 2112;3102 2180 1;3097 2245;3094 2301;3088 2353;926 2353 1;947 2550;1039 2704;1201 2820 1;1364 2934;1581 2993;1854 2993 1;2029 2993;2199 2978;2365 2948 1;2538 2912;2675 2873;2782 2826;2910 3601 1;2859 3626;2790 3651;2705 3677 1;2619 3703;2524 3724;2417 3741 1;2314 3763;2204 3779;2084 3793 1;1965 3804;1846 3810;1726 3810 18;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700 18;5463 3810 1;5160 3810;4895 3765;4668 3677 1;4446 3588;4262 3466;4112 3312 1;3967 3154;3858 2969;3787 2756 1;3719 2542;3684 2311;3684 2065 1;3684 1766;3729 1507;3819 1285 1;3911 1058;4033 870;4183 722 1;4332 572;4503 459;4695 382 1;4891 305;5091 267;5297 267 1;5773 267;6151 414;6428 709 1;6706 998;6844 1426;6844 1995 1;6844 2050;6843 2112;6837 2180 1;6834 2245;6829 2301;6826 2353;4663 2353 1;4683 2550;4775 2704;4938 2820 1;5100 2934;5318 2993;5590 2993 1;5765 2993;5936 2978;6103 2948 1;6272 2912;6411 2873;6518 2826;6646 3601 1;6594 3626;6526 3651;6441 3677 1;6356 3703;6259 3724;6152 3741 1;6051 3763;5939 3779;5821 3793 1;5701 3804;5581 3810;5463 3810 18;4663 1700;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700 18;17092 4924 1;16887 4924;16682 4905;16477 4867 1;16272 4832;16083 4785;15908 4725;16074 3926 1;16224 3986;16379 4032;16543 4067 1;16707 4101;16896 4119;17105 4119 1;17377 4119;17570 4059;17680 3939 1;17796 3819;17854 3666;17854 3479;17854 3357 1;17750 3404;17644 3441;17533 3466 1;17427 3487;17309 3498;17182 3498 1;16717 3498;16361 3361;16113 3087 1;15866 2811;15743 2424;15743 1930 1;15743 1683;15781 1458;15857 1259 1;15934 1053;16044 878;16189 733 1;16339 589;16521 478;16734 402 1;16947 320;17187 280;17457 280 1;17572 280;17689 286;17809 299 1;17931 307;18053 320;18173 337 1;18292 354;18405 375;18512 402 1;18624 422;18722 446;18806 472;18806 3299 1;18806 3849;18665 4257;18385 4522 1;18107 4790;17677 4924;17092 4924 18;17360 2730 1;17459 2730;17550 2718;17636 2691 1;17720 2666;17794 2636;17854 2603;17854 1080 1;17807 1072;17750 1065;17687 1060 1;17623 1052;17548 1047;17464 1047 1;17212 1047;17024 1130;16900 1297 1;16776 1464;16714 1675;16714 1930 1;16714 2463;16930 2730;17360 2730 18;-17051 2993 1;-17269 2993;-17437 2908;-17557 2736 1;-17675 2561;-17736 2326;-17736 2033 1;-17736 1738;-17675 1509;-17557 1342 1;-17437 1171;-17269 1085;-17051 1085 1;-16834 1085;-16668 1171;-16552 1342 1;-16433 1509;-16373 1738;-16373 2033 1;-16373 2326;-16433 2561;-16552 2736 1;-16668 2908;-16834 2993;-17051 2993 18;-17064 2526 1;-16944 2528;-16842 2311;-16838 2042 1;-16834 1772;-16928 1552;-17049 1550 1;-17170 1548;-17271 1765;-17275 2035 1;-17280 2304;-17185 2524;-17064 2526 18;-17051 3810 1;-17299 3810;-17525 3769;-17729 3684 1;-17931 3598;-18102 3479;-18247 3324 1;-18392 3168;-18505 2980;-18587 2763 1;-18669 2540;-18708 2298;-18708 2033 1;-18708 1768;-18669 1526;-18587 1310 1;-18502 1092;-18387 906;-18242 754 1;-18092 600;-17917 480;-17717 395 1;-17512 310;-17291 267;-17051 267 1;-16808 267;-16586 310;-16386 395 1;-16182 480;-16006 600;-15861 754 1;-15716 906;-15603 1092;-15523 1310 1;-15442 1526;-15401 1768;-15401 2033 1;-15401 2298;-15440 2540;-15517 2763 1;-15593 2980;-15703 3168;-15848 3324 1;-15993 3479;-16168 3598;-16373 3684 1;-16574 3769;-16800 3810;-17051 3810 18;-17051 2993 1;-16834 2993;-16668 2908;-16552 2736 1;-16433 2561;-16373 2326;-16373 2033 1;-16373 1738;-16433 1509;-16552 1342 1;-16668 1171;-16834 1085;-17051 1085 1;-17269 1085;-17437 1171;-17557 1342 1;-17675 1509;-17736 1738;-17736 2033 1;-17736 2326;-17675 2561;-17557 2736 1;-17437 2908;-17269 2993;-17051 2993 18;17090 5390 1;16864 5390;16623 5362;16391 5321 1;16389 5320;16389 5320;16387 5320 1;16169 5284;15963 5240;15758 5170 1;15555 5102;15409 4847;15450 4637 1;15450 4635;15450 4634;15450 4632;15538 4221 1;15549 4165;15526 4109;15478 4079 1;15431 4049;15369 4052;15324 4088 1;15245 4152;15138 4191;15033 4191;14080 4191 1;13930 4193;13775 4107;13692 3983 1;13669 3944;13625 3921;13580 3921 1;13534 3921;13492 3944;13466 3983 1;13385 4107;13233 4191;13083 4191;12131 4191 1;12001 4191;11866 4131;11781 4032 1;11755 4004;11719 3987;11680 3987 1;11642 3987;11606 4004;11581 4032 1;11496 4131;11364 4191;11234 4191;10281 4191 1;10048 4193;9815 3957;9815 3724;9815 1796 1;9815 1758;9798 1721;9770 1697 1;9741 1670;9703 1658;9665 1663 1;9608 1670;9553 1668;9498 1655 1;9433 1638;9341 1618;9227 1597 1;9221 1595;9217 1593;9210 1592 1;9161 1582;9142 1582;9157 1584 1;9118 1578;9078 1590;9048 1615 1;9018 1640;9001 1678;9001 1716 1;9001 1716;9001 3724;9001 3724 1;9001 3957;8768 4193;8535 4191;7582 4191 1;7424 4191;7262 4099;7183 3962 1;7162 3926;7125 3900;7082 3894 1;7040 3889;6999 3902;6969 3932 1;6935 3966;6899 3996;6855 4016 1;6768 4059;6679 4092;6574 4124 1;6467 4157;6364 4178;6253 4195 1;6244 4197;6236 4199;6226 4202 1;6107 4227;5991 4240;5870 4254 1;5738 4267;5601 4279;5461 4279 1;5121 4279;4808 4231;4520 4120 1;4514 4118;4506 4114;4498 4110 1;4220 3998;3977 3840;3778 3636 1;3717 3566;3663 3494;3609 3412 1;3571 3361;3503 3344;3445 3371 1;3389 3396;3357 3459;3370 3521 1;3404 3711;3293 3930;3120 4016 1;3034 4060;2945 4092;2837 4124 1;2732 4157;2628 4178;2517 4195 1;2508 4197;2499 4199;2490 4202 1;2372 4227;2256 4240;2134 4254 1;2001 4267;1866 4279;1726 4279 1;1386 4279;1071 4231;785 4120 1;778 4118;771 4114;761 4110 1;562 4029;383 3917;218 3782 1;186 3757;145 3748;105 3757 1;64 3765;32 3793;13 3829 1;-35 3929;-124 4013;-229 4054 1;-362 4109;-512 4164;-678 4204 1;-871 4251;-1073 4266;-1303 4266 1;-1606 4266;-1876 4227;-2127 4126 1;-2355 4031;-2561 3892;-2717 3706 1;-2719 3701;-2721 3699;-2719 3702 1;-2716 3707;-2718 3706;-2721 3700 1;-2724 3694;-2734 3667;-2764 3624 1;-2799 3577;-2859 3558;-2915 3576 1;-2969 3594;-3007 3646;-3006 3704;-3006 3724 1;-3006 3957;-3239 4193;-3473 4191;-4427 4191 1;-4577 4193;-4730 4107;-4814 3983 1;-4838 3944;-4880 3921;-4927 3921 1;-4972 3921;-5013 3944;-5038 3983 1;-5122 4107;-5274 4191;-5421 4191;-6377 4191 1;-6533 4191;-6696 4099;-6777 3962 1;-6796 3926;-6833 3900;-6875 3894 1;-6916 3889;-6958 3902;-6988 3932 1;-7021 3966;-7059 3996;-7101 4016 1;-7101 4017;-7104 4016;-7104 4016 1;-7191 4059;-7278 4092;-7384 4124 1;-7489 4157;-7592 4178;-7704 4195 1;-7714 4197;-7722 4199;-7729 4199 1;-7731 4201;-7733 4202;-7735 4204 1;-7849 4227;-7966 4240;-8088 4254 1;-8221 4267;-8355 4279;-8496 4279 1;-8836 4279;-9151 4231;-9437 4120 1;-9444 4118;-9450 4114;-9461 4110 1;-9737 3998;-9980 3840;-10181 3635 1;-10185 3631;-10185 3628;-10186 3624 1;-10218 3591;-10250 3536;-10300 3470 1;-10333 3425;-10393 3404;-10450 3423 1;-10505 3440;-10541 3493;-10541 3549 1;-10541 3549;-10541 3724;-10541 3724 1;-10541 3957;-10774 4193;-11008 4191;-11961 4191 1;-12194 4193;-12427 3957;-12427 3724;-12427 1796 1;-12427 1758;-12444 1721;-12472 1697 1;-12501 1670;-12539 1658;-12577 1663 1;-12634 1670;-12689 1668;-12744 1655 1;-12809 1638;-12901 1618;-13015 1597 1;-13021 1595;-13026 1593;-13032 1592 1;-13081 1582;-13101 1582;-13086 1584 1;-13124 1578;-13164 1590;-13194 1615 1;-13224 1640;-13240 1678;-13240 1716;-13240 3724 1;-13240 3957;-13474 4193;-13707 4191;-14660 4191 1;-14893 4193;-15126 3957;-15126 3724;-15126 3532 1;-15126 3476;-15164 3423;-15218 3406 1;-15273 3387;-15335 3408;-15368 3455 1;-15420 3526;-15463 3591;-15511 3643 1;-15517 3651;-15517 3652;-15518 3656 1;-15703 3851;-15933 4002;-16189 4110 1;-16458 4224;-16748 4279;-17051 4279 1;-17346 4279;-17629 4225;-17889 4120 1;-17895 4118;-17902 4114;-17909 4112 1;-18160 4005;-18392 3849;-18580 3651 1;-18586 3643;-18587 3641;-18588 3637 1;-18776 3434;-18919 3196;-19017 2931 1;-19017 2931;-19017 2933;-19019 2927 1;-19122 2648;-19176 2350;-19176 2033 1;-19176 1718;-19123 1419;-19019 1142;-19019 1139 1;-18913 875;-18770 642;-18587 446 1;-18407 241;-18054 35;-17897 -33 1;-17894 -33;-17891 -35;-17889 -37 1;-17628 -143;-17344 -200;-17051 -200 1;-16755 -200;-16468 -143;-16206 -33 1;-15946 76;-15707 235;-15518 433 1;-15470 487;-15425 560;-15368 639 1;-15335 685;-15273 705;-15218 687 1;-15164 669;-15126 617;-15126 559;-15126 538 1;-15126 435;-15088 329;-15023 247 1;-14983 198;-14983 129;-15023 80 1;-15088 -1;-15126 -108;-15126 -211;-15126 -1586 1;-15126 -1644;-15164 -1696;-15218 -1714 1;-15273 -1733;-15335 -1712;-15368 -1666 1;-15420 -1594;-15463 -1529;-15506 -1483 1;-15508 -1478;-15511 -1474;-15517 -1465 1;-15701 -1271;-15928 -1115;-16189 -1005 1;-16463 -889;-16753 -841;-17051 -841 1;-17341 -841;-17624 -888;-17889 -995 1;-17895 -997;-17902 -1001;-17909 -1003 1;-18165 -1113;-18394 -1273;-18578 -1463 1;-18582 -1468;-18582 -1470;-18584 -1474 1;-18586 -1476;-18587 -1479;-18588 -1483 1;-18776 -1684;-18919 -1924;-19019 -2192 1;-19122 -2472;-19176 -2770;-19176 -3085 1;-19176 -3401;-19122 -3698;-19019 -3973 1;-18915 -4238;-18774 -4473;-18586 -4675 1;-18582 -4680;-18580 -4682;-18580 -4684 1;-18577 -4686;-18573 -4688;-18572 -4690 1;-18384 -4883;-18152 -5043;-17897 -5151 1;-17894 -5153;-17891 -5154;-17889 -5156 1;-17628 -5263;-17344 -5317;-17051 -5317 1;-16755 -5317;-16468 -5263;-16204 -5150 1;-15943 -5038;-15707 -4885;-15518 -4684 1;-15468 -4631;-15425 -4560;-15368 -4481 1;-15335 -4433;-15273 -4413;-15218 -4431 1;-15164 -4449;-15126 -4502;-15126 -4560 1;-15126 -4560;-15126 -4648;-15126 -4648 1;-15126 -4840;-14978 -5038;-14794 -5093 1;-14694 -5123;-14598 -5144;-14499 -5165 1;-14499 -5165;-14477 -5168;-14452 -5173 1;-14340 -5198;-14217 -5219;-14085 -5240 1;-13922 -5266;-13842 -5266;-13682 -5281 1;-13547 -5294;-13415 -5306;-13286 -5306 1;-12964 -5306;-12660 -5253;-12380 -5145 1;-12373 -5143;-12371 -5143;-12369 -5143 1;-12101 -5041;-11864 -4883;-11665 -4681 1;-11657 -4671;-11653 -4668;-11649 -4665 1;-11536 -4548;-11441 -4412;-11358 -4260 1;-11334 -4218;-11289 -4192;-11242 -4192 1;-11193 -4192;-11148 -4218;-11125 -4260 1;-11028 -4434;-10923 -4593;-10791 -4726 1;-10600 -4917;-10372 -5065;-10128 -5165 1;-10125 -5165;-10124 -5166;-10118 -5168 1;-9872 -5263;-9613 -5317;-9350 -5317 1;-8783 -5317;-8251 -5120;-7882 -4730 1;-7846 -4691;-7812 -4641;-7767 -4581 1;-7733 -4538;-7676 -4519;-7622 -4536 1;-7569 -4553;-7532 -4600;-7529 -4656 1;-7526 -4846;-7376 -5039;-7192 -5093 1;-6999 -5148;-6777 -5186;-6518 -5226 1;-6513 -5227;-6510 -5229;-6505 -5231 1;-6226 -5276;-5933 -5306;-5630 -5306 1;-5305 -5306;-5020 -5259;-4754 -5153 1;-4499 -5054;-4271 -4908;-4106 -4706 1;-3948 -4513;-3839 -4282;-3777 -4034 1;-3719 -3803;-3694 -3557;-3694 -3294;-3694 -1395 1;-3694 -1161;-3927 -927;-4160 -927;-5113 -927 1;-5263 -927;-5419 -1011;-5502 -1136 1;-5525 -1174;-5569 -1198;-5613 -1198 1;-5660 -1198;-5701 -1174;-5727 -1136 1;-5808 -1012;-5960 -927;-6110 -927;-7063 -927 1;-7220 -929;-7380 -1021;-7459 -1158 1;-7479 -1194;-7516 -1219;-7558 -1224 1;-7599 -1231;-7641 -1216;-7671 -1186 1;-7706 -1151;-7744 -1121;-7790 -1100 1;-7898 -1046;-7976 -1023;-8071 -995 1;-8178 -963;-8281 -942;-8392 -924 1;-8400 -923;-8409 -921;-8417 -920 1;-8419 -918;-8419 -916;-8421 -916 1;-8527 -894;-8644 -874;-8775 -861 1;-8917 -846;-9054 -841;-9183 -841 1;-9523 -841;-9837 -888;-10125 -999 1;-10132 -1003;-10138 -1004;-10148 -1010 1;-10205 -1033;-10261 -1066;-10333 -1102 1;-10380 -1126;-10438 -1119;-10478 -1085 1;-10520 -1051;-10537 -996;-10520 -944 1;-10481 -826;-10453 -709;-10453 -616 1;-10453 -441;-10525 -246;-10637 -67 1;-10667 -20;-10665 40;-10633 84 1;-10575 164;-10541 262;-10541 359;-10541 559 1;-10541 617;-10505 669;-10450 687 1;-10393 705;-10333 685;-10300 639 1;-10233 547;-10171 461;-10103 392 1;-9914 202;-9685 53;-9442 -45 1;-9439 -46;-9435 -48;-9433 -50 1;-9432 -50;-9431 -50;-9429 -50 1;-9183 -144;-8927 -200;-8662 -200 1;-8096 -200;-7562 -1;-7194 386;-7194 386 1;-7192 387;-7194 385;-7194 388 1;-7192 392;-7192 389;-7192 390;-7143 453;-7079 538 1;-7046 581;-6988 598;-6935 581 1;-6882 566;-6846 519;-6843 463 1;-6837 273;-6688 79;-6505 25 1;-6313 -30;-6091 -67;-5824 -110 1;-5540 -156;-5246 -186;-4944 -186 1;-4619 -186;-4332 -142;-4066 -35 1;-3813 64;-3584 211;-3419 414 1;-3404 432;-3387 477;-3347 534 1;-3315 585;-3254 609;-3195 590 1;-3139 574;-3101 519;-3103 459 1;-3103 459;-3103 -482;-3103 -482 1;-3103 -693;-2915 -908;-2706 -941;-1753 -1091 1;-1501 -1129;-1215 -886;-1215 -633;-1215 -241 1;-1215 -166;-1155 -108;-1082 -108;-537 -108 1;-323 -106;-109 82;-79 292 1;-70 342;-35 384;11 399 1;60 415;111 402;147 367 1;333 190;549 49;786 -47 1;1037 -144;1294 -200;1559 -200 1;2126 -200;2658 -1;3027 388 1;3144 513;3239 654;3317 805 1;3338 848;3383 875;3432 875 1;3481 876;3526 852;3550 808 1;3642 654;3737 510;3853 392 1;4045 202;4273 53;4521 -47;4521 -47 1;4521 -48;4520 -46;4522 -47 1;4529 -50;4527 -50;4527 -50 1;4773 -144;5031 -200;5295 -200 1;5861 -200;6394 -1;6763 388 1;6802 432;6839 480;6882 538 1;6914 581;6969 600;7022 585 1;7074 572;7112 529;7119 476 1;7140 310;7271 152;7427 97 1;7615 32;7831 -23;8080 -81 1;8085 -82;8083 -82;8083 -82 1;8360 -146;8654 -178;8965 -178 1;9028 -178;9099 -173;9168 -166 1;9182 -165;9190 -163;9205 -161 1;9278 -156;9347 -148;9415 -136 1;9437 -133;9445 -133;9447 -133 1;9507 -125;9570 -116;9635 -103 1;9681 -95;9728 -112;9758 -146 1;9788 -181;9800 -230;9785 -274 1;9745 -394;9718 -512;9718 -616 1;9718 -878;9838 -1186;10043 -1369 1;10048 -1372;10051 -1374;10058 -1380 1;10243 -1538;10508 -1644;10751 -1644 1;10994 -1644;11257 -1542;11452 -1373 1;11458 -1367;11460 -1365;11466 -1364 1;11671 -1177;11789 -869;11789 -616 1;11789 -441;11717 -246;11606 -67 1;11576 -18;11579 44;11619 94 1;11644 132;11678 151;11716 155 1;11753 159;11791 147;11817 122 1;11871 77;11935 44;12001 25 1;12194 -30;12416 -67;12682 -110 1;12967 -156;13260 -186;13564 -186 1;13889 -186;14173 -142;14439 -35 1;14695 64;14923 211;15088 414 1;15181 529;15253 669;15316 817 1;15337 863;15381 893;15431 897 1;15481 900;15529 874;15555 830 1;15641 673;15739 530;15862 404 1;16061 212;16299 65;16570 -35 1;16849 -140;17147 -186;17457 -186 1;17585 -186;17713 -174;17847 -161 1;17854 -161;17842 -163;17865 -161 1;17990 -153;18110 -138;18237 -120 1;18355 -103;18475 -82;18593 -58 1;18605 -52;18617 -50;18628 -50 1;18743 -26;18848 -1;18940 25 1;19124 80;19273 279;19273 472;19273 3299 1;19273 3915;19111 4466;18724 4844 1;18718 4850;18716 4852;18709 4858 1;18305 5243;17742 5390;17090 5390 18;-1305 3799 1;-1102 3799;-928 3780;-787 3748 1;-646 3712;-518 3673;-404 3626;-539 2884 1;-627 2918;-728 2946;-838 2966 1;-945 2984;-1050 2993;-1151 2993 1;-1365 2993;-1508 2933;-1581 2813 1;-1648 2694;-1683 2535;-1683 2333;-1683 1150;-539 1150;-539 357;-1683 357;-1683 -634;-2636 -481;-2636 2346 1;-2636 2565;-2618 2763;-2579 2941 1;-2536 3121;-2463 3274;-2361 3402 1;-2259 3526;-2123 3624;-1951 3696 1;-1781 3764;-1566 3799;-1305 3799 18;10752 -52 1;10906 -52;11038 -101;11149 -200 1;11264 -303;11323 -441;11323 -616 1;11323 -791;11264 -927;11149 -1026 1;11038 -1128;10906 -1179;10752 -1179 1;10600 -1179;10465 -1128;10350 -1026 1;10238 -927;10183 -791;10183 -616 1;10183 -441;10238 -303;10350 -200 1;10465 -101;10600 -52;10752 -52 18;10279 3722;11233 3722;11233 357;10279 357;10279 3722 18;-11963 3722;-11009 3722;-11009 357;-11963 357;-11963 3722 18;7581 3722;8534 3722;8534 1150 1;8575 1137;8637 1126;8718 1118 1;8804 1105;8877 1098;8937 1098 1;9069 1098;9191 1112;9306 1137 1;9424 1158;9523 1180;9608 1201;9768 414 1;9717 392;9655 375;9583 363 1;9510 345;9437 333;9365 325 1;9292 312;9220 303;9146 299 1;9075 290;9013 286;8962 286 1;8677 286;8415 314;8182 369 1;7950 420;7750 476;7581 536;7581 3722 18;12129 3722;13082 3722;13082 1125 1;13142 1115;13210 1110;13287 1105 1;13363 1097;13435 1092;13504 1092 1;13725 1092;13876 1156;13958 1285 1;14039 1411;14080 1630;14080 1937;14080 3722;15033 3722;15033 1821 1;15033 1592;15010 1381;14963 1195 1;14916 1007;14836 845;14726 709 1;14614 572;14463 467;14272 395 1;14083 318;13847 280;13562 280 1;13280 280;13012 301;12756 344 1;12499 382;12290 425;12129 472;12129 3722 18;15729 3616 1;15737 3607;15749 3596;15758 3588 1;15784 3562;15799 3528;15799 3491 1;15799 3491;15799 3489;15799 3487;15794 3440;15767 3400 1;15769 3404;15763 3384;15737 3354 1;15711 3321;15671 3303;15623 3306 1;15549 3318;15499 3374;15499 3441 1;15499 3441;15499 3524;15499 3524 1;15501 3579;15533 3628;15585 3648 1;15634 3667;15691 3656;15729 3616 18;-14662 3722;-13708 3722;-13708 1150 1;-13667 1137;-13605 1126;-13524 1118 1;-13439 1105;-13366 1098;-13306 1098 1;-13174 1098;-13051 1112;-12936 1137 1;-12819 1158;-12719 1180;-12634 1201;-12474 414 1;-12526 392;-12587 375;-12660 363 1;-12733 345;-12806 333;-12878 325 1;-12951 312;-13022 303;-13096 299 1;-13167 290;-13229 286;-13280 286 1;-13565 286;-13827 314;-14060 369 1;-14292 420;-14492 476;-14662 536;-14662 3722 18;-6378 3722;-5425 3722;-5425 1125 1;-5365 1115;-5297 1110;-5220 1105 1;-5143 1097;-5070 1092;-5002 1092 1;-4780 1092;-4628 1156;-4549 1285 1;-4467 1411;-4427 1630;-4427 1937;-4427 3722;-3474 3722;-3474 1821 1;-3474 1592;-3497 1381;-3544 1195 1;-3591 1007;-3669 845;-3781 709 1;-3890 572;-4042 467;-4235 395 1;-4422 318;-4658 280;-4945 280 1;-5227 280;-5495 301;-5751 344 1;-6007 382;-6217 425;-6378 472;-6378 3722 18;17092 4924 1;17677 4924;18107 4790;18385 4522 1;18665 4257;18806 3849;18806 3299;18806 472 1;18722 446;18624 422;18512 402 1;18405 375;18292 354;18173 337 1;18053 320;17931 307;17809 299 1;17689 286;17572 280;17457 280 1;17187 280;16947 320;16734 402 1;16521 478;16339 589;16189 733 1;16044 878;15934 1053;15857 1259 1;15781 1458;15743 1683;15743 1930 1;15743 2424;15866 2811;16113 3087 1;16361 3361;16717 3498;17182 3498 1;17309 3498;17427 3487;17533 3466 1;17644 3441;17750 3404;17854 3357;17854 3479 1;17854 3666;17796 3819;17680 3939 1;17570 4059;17377 4119;17105 4119 1;16896 4119;16707 4101;16543 4067 1;16379 4032;16224 3986;16074 3926;15908 4725 1;16083 4785;16272 4832;16477 4867 1;16682 4905;16887 4924;17092 4924 18;1726 3810 1;1846 3810;1965 3804;2084 3793 1;2204 3779;2314 3763;2417 3741 1;2524 3724;2619 3703;2705 3677 1;2790 3651;2859 3626;2910 3601;2782 2826 1;2675 2873;2538 2912;2365 2948 1;2199 2978;2029 2993;1854 2993 1;1581 2993;1364 2934;1201 2820 1;1039 2704;947 2550;926 2353;3088 2353 1;3094 2301;3097 2245;3102 2180 1;3105 2112;3109 2050;3109 1995 1;3109 1426;2968 998;2692 709 1;2416 414;2038 267;1559 267 1;1354 267;1154 305;958 382 1;766 459;596 572;446 722 1;298 870;177 1058;81 1285 1;-9 1507;-52 1766;-52 2065 1;-52 2311;-19 2542;49 2756 1;123 2969;231 3154;376 3312 1;524 3466;712 3588;933 3677 1;1159 3765;1422 3810;1726 3810 18;-8496 3810 1;-8376 3810;-8257 3804;-8137 3793 1;-8017 3779;-7908 3763;-7804 3741 1;-7697 3724;-7603 3703;-7516 3677 1;-7431 3651;-7363 3626;-7311 3601;-7440 2826 1;-7546 2873;-7684 2912;-7855 2948 1;-8022 2978;-8193 2993;-8368 2993 1;-8640 2993;-8857 2934;-9020 2820 1;-9182 2704;-9273 2550;-9296 2353;-7132 2353 1;-7128 2301;-7124 2245;-7119 2180 1;-7116 2112;-7113 2050;-7113 1995 1;-7113 1426;-7252 998;-7529 709 1;-7806 414;-8184 267;-8662 267 1;-8867 267;-9067 305;-9264 382 1;-9455 459;-9625 572;-9775 722 1;-9923 870;-10045 1058;-10140 1285 1;-10230 1507;-10273 1766;-10273 2065 1;-10273 2311;-10240 2542;-10171 2756 1;-10098 2969;-9990 3154;-9845 3312 1;-9696 3466;-9510 3588;-9288 3677 1;-9062 3765;-8798 3810;-8496 3810 18;-12484 -146 1;-12454 -181;-12442 -230;-12457 -274 1;-12497 -394;-12524 -512;-12524 -616 1;-12524 -619;-12502 -670;-12491 -753 1;-12484 -794;-12496 -836;-12524 -866 1;-12554 -896;-12594 -911;-12640 -905 1;-12765 -882;-12886 -866;-13002 -866 1;-13009 -866;-13043 -876;-13102 -878 1;-13139 -879;-13174 -866;-13199 -841 1;-13225 -814;-13240 -781;-13240 -745 1;-13240 -745;-13240 -300;-13240 -300 1;-13240 -230;-13188 -171;-13119 -166 1;-13079 -161;-13064 -165;-13074 -166;-13056 -164;-13037 -161 1;-12964 -156;-12895 -148;-12827 -136 1;-12806 -133;-12797 -133;-12795 -133;-12795 -132;-12793 -132;-12795 -133 1;-12735 -125;-12673 -116;-12607 -103 1;-12561 -95;-12514 -112;-12484 -146 18;-14662 -213;-13708 -213;-13708 -1479 1;-13602 -1432;-13492 -1395;-13377 -1371 1;-13261 -1346;-13137 -1333;-13006 -1333 1;-12765 -1333;-12556 -1376;-12373 -1461 1;-12189 -1546;-12034 -1666;-11911 -1819 1;-11788 -1972;-11694 -2156;-11629 -2368 1;-11566 -2582;-11535 -2819;-11535 -3078 1;-11535 -3348;-11574 -3591;-11657 -3808 1;-11732 -4027;-11846 -4210;-11995 -4358 1;-12144 -4513;-12328 -4630;-12544 -4711 1;-12763 -4796;-13009 -4838;-13287 -4838 1;-13402 -4838;-13524 -4833;-13652 -4819 1;-13776 -4811;-13898 -4798;-14022 -4781 1;-14142 -4765;-14257 -4743;-14369 -4716 1;-14478 -4697;-14576 -4673;-14662 -4646;-14662 -213 18;-17051 3810 1;-16800 3810;-16574 3769;-16373 3684 1;-16168 3598;-15993 3479;-15848 3324 1;-15703 3168;-15593 2980;-15517 2763 1;-15440 2540;-15401 2298;-15401 2033 1;-15401 1768;-15442 1526;-15523 1310 1;-15603 1092;-15716 906;-15861 754 1;-16006 600;-16182 480;-16386 395 1;-16586 310;-16808 267;-17051 267 1;-17291 267;-17512 310;-17717 395 1;-17917 480;-18092 600;-18242 754 1;-18387 906;-18502 1092;-18587 1310 1;-18669 1526;-18708 1768;-18708 2033 1;-18708 2298;-18669 2540;-18587 2763 1;-18505 2980;-18392 3168;-18247 3324 1;-18102 3479;-17931 3598;-17729 3684 1;-17525 3769;-17299 3810;-17051 3810 18;5463 3810 1;5581 3810;5701 3804;5821 3793 1;5939 3779;6051 3763;6152 3741 1;6259 3724;6356 3703;6441 3677 1;6526 3651;6594 3626;6646 3601;6518 2826 1;6411 2873;6272 2912;6103 2948 1;5936 2978;5765 2993;5590 2993 1;5318 2993;5100 2934;4938 2820 1;4775 2704;4683 2550;4663 2353;6826 2353 1;6829 2301;6834 2245;6837 2180 1;6843 2112;6844 2050;6844 1995 1;6844 1426;6706 998;6428 709 1;6151 414;5773 267;5297 267 1;5091 267;4891 305;4695 382 1;4503 459;4332 572;4183 722 1;4033 870;3911 1058;3819 1285 1;3729 1507;3684 1766;3684 2065 1;3684 2311;3719 2542;3787 2756 1;3858 2969;3967 3154;4112 3312 1;4262 3466;4446 3588;4668 3677 1;4895 3765;5160 3810;5463 3810 18;-1065 2521 1;-1018 2518;-969 2518;-911 2508 1;-910 2508;-909 2506;-907 2505 1;-830 2489;-762 2473;-704 2450 1;-702 2450;-700 2450;-698 2450 1;-670 2439;-642 2426;-612 2422 1;-545 2409;-500 2350;-503 2283 1;-509 2203;-520 2131;-520 2067 1;-520 1973;-502 1875;-490 1763 1;-487 1725;-500 1688;-525 1660 1;-550 1633;-586 1616;-623 1616;-1106 1616 1;-1170 1630;-1217 1687;-1215 1750;-1215 2333 1;-1215 2360;-1211 2386;-1206 2405 1;-1198 2474;-1136 2527;-1065 2521 18;-11490 -52 1;-11336 -52;-11204 -101;-11093 -200 1;-10978 -303;-10920 -441;-10920 -616 1;-10920 -791;-10978 -927;-11093 -1026 1;-11204 -1128;-11336 -1179;-11490 -1179 1;-11642 -1179;-11777 -1128;-11893 -1026 1;-12004 -927;-12059 -791;-12059 -616 1;-12059 -441;-12004 -303;-11893 -200 1;-11777 -101;-11642 -52;-11490 -52 18;-17051 -1307 1;-16800 -1307;-16574 -1349;-16373 -1434 1;-16168 -1521;-15993 -1639;-15848 -1794 1;-15703 -1951;-15593 -2139;-15517 -2355 1;-15440 -2579;-15401 -2820;-15401 -3085 1;-15401 -3350;-15442 -3591;-15523 -3808 1;-15603 -4027;-15716 -4211;-15861 -4365 1;-16006 -4518;-16182 -4638;-16386 -4723 1;-16586 -4808;-16808 -4851;-17051 -4851 1;-17291 -4851;-17512 -4808;-17717 -4723 1;-17917 -4638;-18092 -4518;-18242 -4365 1;-18387 -4211;-18502 -4027;-18587 -3808 1;-18669 -3591;-18708 -3350;-18708 -3085 1;-18708 -2820;-18669 -2579;-18587 -2355 1;-18505 -2139;-18392 -1951;-18247 -1794 1;-18102 -1639;-17931 -1521;-17729 -1434 1;-17525 -1349;-17299 -1307;-17051 -1307 18;-7064 -1395;-6112 -1395;-6112 -3993 1;-6052 -4004;-5983 -4008;-5907 -4013 1;-5830 -4021;-5759 -4027;-5690 -4027 1;-5468 -4027;-5317 -3961;-5235 -3833 1;-5155 -3707;-5113 -3488;-5113 -3182;-5113 -1395;-4160 -1395;-4160 -3296 1;-4160 -3527;-4183 -3737;-4230 -3923 1;-4277 -4111;-4357 -4273;-4467 -4410 1;-4579 -4547;-4730 -4652;-4921 -4723 1;-5110 -4800;-5347 -4838;-5631 -4838 1;-5913 -4838;-6181 -4818;-6438 -4774 1;-6694 -4736;-6903 -4693;-7064 -4646;-7064 -1395 18;-11099 -1644 1;-11068 -1689;-11067 -1748;-11095 -1794 1;-11120 -1834;-11128 -1842;-11120 -1827 1;-11143 -1872;-11189 -1899;-11242 -1899 1;-11246 -1897;-11251 -1895;-11258 -1894 1;-11298 -1887;-11332 -1862;-11353 -1827 1;-11358 -1822;-11364 -1814;-11375 -1799 1;-11394 -1761;-11396 -1718;-11379 -1679 1;-11362 -1639;-11328 -1611;-11287 -1602 1;-11257 -1596;-11240 -1594;-11242 -1594 1;-11188 -1581;-11133 -1601;-11099 -1644 18;-9182 -1307 1;-9063 -1307;-8944 -1314;-8824 -1326 1;-8706 -1339;-8594 -1356;-8492 -1378 1;-8385 -1395;-8289 -1416;-8204 -1440 1;-8119 -1468;-8051 -1493;-7999 -1517;-8127 -2292 1;-8234 -2246;-8372 -2205;-8542 -2171 1;-8708 -2141;-8881 -2126;-9055 -2126 1;-9327 -2126;-9545 -2184;-9707 -2299 1;-9870 -2413;-9961 -2569;-9982 -2766;-7819 -2766 1;-7816 -2817;-7810 -2873;-7808 -2939 1;-7803 -3007;-7801 -3069;-7801 -3123 1;-7801 -3692;-7939 -4120;-8217 -4410 1;-8494 -4705;-8872 -4851;-9349 -4851 1;-9553 -4851;-9754 -4813;-9950 -4736 1;-10141 -4660;-10314 -4547;-10462 -4396 1;-10611 -4248;-10733 -4060;-10827 -3833 1;-10917 -3612;-10962 -3352;-10962 -3054 1;-10962 -2807;-10926 -2575;-10858 -2362 1;-10787 -2149;-10678 -1964;-10533 -1806 1;-10383 -1652;-10198 -1530;-9976 -1440 1;-9750 -1352;-9485 -1307;-9182 -1307 18;16428 5964 1;15493 5816;15156 5633;14905 5136 1;14842 5010;14767 4893;14739 4875 1;14686 4843;13829 4769;13588 4777 1;13511 4779;12673 4786;11726 4791;10003 4801;9412 4491;8810 4801;8189 4815 1;7703 4826;7501 4814;7261 4759 1;6957 4690;6950 4690;6521 4785 1;6170 4863;5974 4881;5488 4880;5457 4880 1;4811 4880;4563 4831;4074 4600 1;3912 4524;3745 4461;3703 4461 1;3660 4461;3540 4504;3437 4556 1;2881 4838;1858 4973;1148 4859 1;1005 4835;741 4765;560 4703 1;206 4579;201 4579;-280 4737 1;-640 4854;-1173 4915;-1564 4883 1;-1876 4858;-2085 4806;-2522 4644 1;-2687 4583;-2700 4585;-2962 4691 1;-3221 4796;-3260 4801;-3912 4799 1;-4286 4797;-4700 4791;-4832 4785 1;-4964 4779;-5368 4786;-5729 4802 1;-6286 4827;-6433 4820;-6697 4760 1;-7003 4690;-7012 4690;-7420 4779 1;-8263 4964;-9117 4926;-9699 4679 1;-10091 4513;-10177 4511;-10482 4666;-10748 4801;-12238 4801;-12541 4645;-12844 4489;-13138 4645;-13432 4801;-14922 4801;-15208 4663 1;-15529 4507;-15467 4502;-16152 4744 1;-16831 4984;-17621 4945;-18238 4642 1;-19010 4261;-19482 3685;-19691 2865 1;-19908 2013;-19801 1156;-19391 468 1;-19227 194;-19067 14;-18762 -238 1;-18460 -488;-18462 -552;-18772 -793 1;-19030 -993;-19347 -1393;-19507 -1720 1;-19700 -2113;-19789 -2552;-19789 -3099 1;-19788 -3688;-19727 -3966;-19489 -4450 1;-19192 -5057;-18572 -5590;-17912 -5806 1;-17374 -5984;-16546 -5988;-15770 -5633;-15447 -5485;-15239 -5576 1;-14740 -5794;-14092 -5908;-13352 -5909 1;-12569 -5909;-12118 -5779;-11532 -5381 1;-11433 -5314;-11315 -5259;-11270 -5259 1;-11225 -5259;-11047 -5352;-10875 -5465 1;-10015 -6029;-9078 -6079;-8023 -5616;-7813 -5524;-7443 -5648 1;-6638 -5917;-5482 -5996;-4840 -5824 1;-4236 -5663;-3768 -5339;-3467 -4873 1;-3148 -4378;-3090 -4073;-3064 -2779 1;-3052 -2196;-3026 -1687;-3006 -1649 1;-2959 -1560;-2787 -1559;-2312 -1644 1;-2114 -1679;-1835 -1709;-1691 -1709 1;-1229 -1712;-888 -1486;-654 -1024 1;-541 -799;-528 -788;-245 -681 1;187 -519;243 -518;668 -662 1;965 -763;1135 -795;1448 -809 1;2091 -837;2566 -704;3131 -337 1;3264 -250;3400 -179;3432 -179 1;3464 -179;3619 -259;3777 -358 1;4137 -581;4287 -644;4688 -741 1;5298 -888;5896 -821;6519 -534;6870 -372;7177 -478 1;7625 -633;8076 -733;8550 -781 1;9020 -829;9105 -876;9182 -1133 1;9308 -1554;9694 -1943;10095 -2129 1;10456 -2299;11045 -2279;11428 -2095 1;11880 -1878;12170 -1593;12329 -1108 1;12441 -766;12455 -756;12815 -779 1;14010 -855;14639 -730;15238 -297 1;15327 -232;15435 -179;15479 -179 1;15522 -179;15686 -258;15843 -355 1;16603 -824;17454 -922;18651 -679 1;19204 -567;19417 -469;19628 -230 1;19900 79;19908 145;19908 1951;19908 2068 1;19908 3651;19886 3934;19718 4401 1;19503 5000;19140 5402;18544 5703 1;18007 5974;17155 6079;16428 5964 18;17928 5561 1;18700 5363;19232 4845;19456 4071 1;19512 3877;19525 3576;19538 2141 1;19550 942;19541 396;19509 289 1;19438 50;19214 -170;18973 -238 1;18265 -438;17233 -498;16751 -367 1;16348 -257;15993 -62;15706 208;15444 456;15306 271 1;15116 17;14791 -194;14388 -327 1;14080 -428;13997 -439;13508 -436 1;13048 -433;12442 -364;12083 -274 1;12000 -253;11998 -259;12035 -436 1;12080 -647;12030 -945;11908 -1199 1;11636 -1762;10897 -2045;10292 -1817 1;9806 -1635;9468 -1148;9468 -632;9468 -407;9216 -431 1;8866 -464;8256 -399;7783 -280 1;7323 -163;7201 -108;7046 57;6934 175;6772 37 1;5907 -704;4382 -591;3607 271;3424 474;3360 377 1;3119 21;2522 -336;2023 -420 1;1440 -518;756 -394;341 -114;129 29;28 -91 1;-139 -289;-354 -379;-661 -379;-932 -379;-932 -509 1;-933 -750;-1017 -955;-1187 -1124 1;-1437 -1374;-1550 -1391;-2246 -1283 1;-2569 -1232;-2887 -1163;-2953 -1129 1;-3248 -975;-3372 -718;-3372 -258;-3372 65;-3550 -58 1;-3801 -230;-4065 -337;-4405 -402 1;-4781 -475;-5094 -474;-5671 -398 1;-6502 -289;-6766 -202;-6943 24;-7040 149;-7232 3 1;-7499 -201;-7690 -292;-8042 -381 1;-8755 -564;-9576 -395;-10120 47 1;-10278 175;-10291 178;-10328 108 1;-10358 53;-10348 -23;-10289 -176 1;-10246 -291;-10206 -465;-10202 -563 1;-10196 -705;-10182 -736;-10133 -716 1;-9610 -509;-8452 -550;-7836 -799 1;-7626 -884;-7587 -889;-7532 -839 1;-7372 -694;-7184 -659;-6575 -659;-6548 -659 1;-5984 -659;-5837 -684;-5685 -810 1;-5627 -859;-5602 -854;-5492 -772 1;-5374 -686;-5322 -678;-4739 -666 1;-4055 -652;-3888 -680;-3692 -845 1;-3432 -1064;-3432 -1066;-3434 -2479 1;-3436 -3540;-3448 -3805;-3503 -4029 1;-3693 -4791;-4168 -5277;-4922 -5483 1;-5261 -5575;-6112 -5566;-6698 -5463 1;-7272 -5363;-7461 -5289;-7610 -5109;-7726 -4968;-7879 -5088 1;-8814 -5825;-10355 -5685;-11073 -4798;-11235 -4596;-11494 -4860 1;-11783 -5155;-12156 -5370;-12572 -5482 1;-13108 -5626;-14450 -5523;-14960 -5299 1;-15058 -5256;-15175 -5160;-15239 -5069;-15350 -4913;-15611 -5103 1;-16440 -5704;-17556 -5733;-18389 -5176 1;-18763 -4926;-18970 -4689;-19174 -4279 1;-19544 -3536;-19541 -2623;-19167 -1859 1;-18698 -903;-17669 -417;-16549 -623 1;-16267 -675;-15788 -888;-15574 -1057;-15421 -1179;-15405 -589 1;-15394 -169;-15373 26;-15332 89 1;-15235 239;-15319 237;-15504 86 1;-16274 -546;-17402 -634;-18285 -132 1;-19617 624;-19853 2734;-18732 3863 1;-17912 4689;-16505 4772;-15566 4050;-15379 3907;-15333 4019 1;-15270 4170;-15105 4323;-14923 4399 1;-14712 4487;-13672 4487;-13461 4399 1;-13264 4317;-13137 4195;-13048 4001 1;-12982 3860;-12974 3728;-12973 2871;-12973 2823 1;-12972 1843;-12978 1874;-12782 1926;-12692 1950 1;-12692 3973;-12680 4038;-12439 4250 1;-12222 4440;-12067 4471;-11387 4455 1;-10804 4441;-10780 4438;-10624 4329 1;-10492 4238;-10413 4134;-10306 3908 1;-10303 3901;-10213 3963;-10106 4044 1;-9632 4408;-9047 4566;-8312 4528 1;-7825 4503;-7365 4416;-7096 4297 1;-6933 4226;-6914 4225;-6848 4284 1;-6702 4417;-6483 4457;-5908 4459 1;-5355 4461;-5138 4426;-5010 4317 1;-4958 4272;-4918 4279;-4753 4362 1;-4571 4454;-4518 4461;-3955 4460 1;-3267 4459;-3106 4414;-2910 4168;-2797 4027;-2623 4147 1;-2311 4362;-2052 4456;-1635 4505 1;-1021 4577;-348 4454;37 4200;186 4101;437 4238 1;800 4435;1069 4503;1588 4529 1;1962 4547;2134 4535;2508 4464 1;3072 4358;3355 4230;3495 4017 1;3552 3931;3607 3861;3617 3861 1;3627 3861;3710 3925;3802 4004 1;4053 4219;4484 4414;4868 4487 1;5440 4594;6391 4505;6861 4299;7033 4223;7198 4332 1;7357 4438;7382 4441;8005 4453 1;8611 4465;8658 4460;8826 4373 1;8923 4323;9058 4212;9126 4127;9248 3974;9260 2937;9272 1901;9360 1902 1;9541 1903;9543 1914;9556 2969;9568 3974;9690 4127 1;9758 4212;9892 4322;9989 4371 1;10149 4453;10221 4461;10767 4460 1;11260 4459;11397 4446;11528 4387 1;11682 4317;11694 4317;11848 4387 1;11979 4446;12117 4459;12611 4460 1;13162 4461;13230 4453;13396 4369;13577 4278;13732 4359 1;13871 4432;13961 4441;14551 4441;15214 4441;15197 4626 1;15173 4869;15240 5059;15408 5233 1;15568 5397;15799 5483;16366 5586 1;16835 5672;17538 5661;17928 5561;17928 5561 18;-13184 -2139 1;-13282 -2139;-13381 -2152;-13479 -2177 1;-13572 -2202;-13648 -2234;-13708 -2274;-13708 -3993 1;-13662 -4004;-13602 -4008;-13530 -4013 1;-13457 -4021;-13379 -4027;-13293 -4027 1;-13032 -4027;-12837 -3936;-12704 -3756 1;-12572 -3582;-12506 -3348;-12506 -3054 1;-12506 -2443;-12733 -2139;-13184 -2139 18;-13248 -2631 1;-13034 -2631;-12976 -2876;-12976 -3090 1;-12976 -3284;-13054 -3505;-13248 -3506;-13248 -2631 18;17360 2730 1;16930 2730;16714 2463;16714 1930 1;16714 1675;16776 1464;16900 1297 1;17024 1130;17212 1047;17464 1047 1;17548 1047;17623 1052;17687 1060 1;17750 1065;17807 1072;17854 1080;17854 2603 1;17794 2636;17720 2666;17636 2691 1;17550 2718;17459 2730;17360 2730 18;17409 2232;17409 1571 1;17201 1492;17180 1713;17180 1926 1;17178 2120;17192 2338;17409 2232 18; + + + +61856 99734 32;63500 98232 32;62139 96743 32;60495 98245 32;61856 99734 50; +54869 90874 32;63157 91162 32;63200 89924 32;66484 90038 32;66319 94805 32;58567 94536 32;58549 95067 32;54728 94934 32;54869 90874 50; +68204 75140 32;70579 75252 32;70681 73095 32;68306 72983 32;68204 75140 50; +64279 74690 32;64704 71464 32;66066 71643 32;65641 74869 32;64279 74690 50; +63586 65647 32;69726 66691 32;70222 63775 32;64082 62730 32;63586 65647 50; +70380 63085 32;72307 63386 32;72526 61980 32;70599 61679 32;70380 63085 50; +75286 57526 32;72411 57214 32;72366 57631 32;69314 57299 32;69333 57127 32;68714 57060 32;68643 57715 32;68836 57736 32;68661 59346 32;69687 59457 32;69649 59809 32;71999 60064 32;72037 59718 32;73324 59858 32;73296 60115 32;73858 60176 32;73895 59837 32;75021 59959 32;75286 57526 50; +71263 54445 32;71413 53007 32;70539 52916 32;70389 54354 32;71263 54445 50; +63012 54794 32;64362 45931 32;75005 47552 32;74599 50216 32;66676 49009 32;65732 55208 32;63012 54794 50; +76768 42623 32;77093 39347 32;74878 39127 32;74653 41397 32;72694 41203 32;72538 42776 32;75635 43088 32;75692 42516 32;76768 42623 50; +61912 37890 32;65430 38209 32;65471 37757 32;67532 37943 32;67293 40582 32;65189 40392 32;65124 41106 32;61650 40792 32;61912 37890 50; +71456 31230 32;76607 32518 32;77194 30168 32;72043 28880 32;71456 31230 50; +68419 35393 32;69069 31993 32;71512 32460 32;70850 35924 32;73371 36406 32;73072 37972 32;69565 37302 32;69877 35672 32;68419 35393 50; +64007 28914 32;67045 29751 32;67763 27146 32;64725 26308 32;64007 28914 50; +63907 25638 32;65482 26064 32;65900 24515 32;64325 24090 32;63907 25638 50; +61782 25613 32;63382 26276 32;64028 24716 32;62428 24053 32;61782 25613 50; +60019 21075 32;63119 22400 32;63771 20874 32;65074 21431 32;64025 23886 32;62216 23678;59417 22482 32;60019 21075 50; +71588 22291;68939 19601;69439 19142;69524 18831;68197 18258 32;67751 19292 32;65464 18305 32;64918 19569 32;63421 18923 32;64882 15539 32;70062 17775 32;70189 17853;73032 20935;71588 22291 18; +76923 18984 32;74908 18809 32;75055 17111;72954 16414 32;73427 14991 32;74813 15451 32;74629 16004 32;75136 16172;75156 15952 32;75409 15966 32;75573 14073 32;77092 14205 32;76824 17292 32;77068 17313 32;76923 18984 50; +71199 11651 32;74237 12664 32;75100 10076 32;72062 9063 32;71199 11651 50; +69561 4526 32;70361 4763 32;70524 4214 32;69724 3977 32;69561 4526 50; +78862 8076 32;75037 7864 32;75047 7691;73124 7126 32;73424 6103;71286 5476 32;71474 4837 32;70875 4661 32;71098 3901 32;73405 4578 32;73228 5182 32;75181 5715 32;75106 5968 32;75354 6041;78964 6241 32;78862 8076 50; +74401 4772;74464 2809;73438 2609;73401 4522;74401 4772 18; +75964 2046 32;79177 2209 32;79287 35 32;76074 -128 32;75964 2046 50; +73714 -417 32;70851 -1017 32;70669 -150 32;68902 -520 32;69084 -1387 32;66280 -1975 32;66098 -1108 32;65190 -1298 32;64948 -141 32;65859 50 32;65739 625 32;68546 1213 32;68665 646 32;70431 1016 32;70311 1587 32;73218 2196 32;73335 1637 32;74023 1781 32;74270 601 32;73533 447 32;73714 -417 50; +75308 -1430 32;75368 -4193 32;76143 -4176 32;76165 -5187 32;74287 -5228 32;74259 -3929 32;73944 -3936 32;73897 -1773 32;75308 -1430 50; +70145 -6834 32;79709 -6445 32;79833 -9490 32;70269 -9879 32;70145 -6834 50; +77916 -13426 32;77368 -21576 32;74409 -21377 32;74957 -13227 32;77916 -13426 50; +74045 -26455 32;64110 -25730 32;64327 -22757 32;74262 -23482 32;74045 -26455 50; +60239 -20728 32;59973 -24653 32;58782 -24573 32;59047 -20647 32;60239 -20728 50; +56809 -20516 32;56473 -25254 32;55010 -25150 32;55103 -23841 32;55374 -23860 32;55617 -20431 32;56809 -20516 50; +55536 -17617 32;67275 -18395 32;67468 -15485 32;55729 -14707 32;55536 -17617 50; +56934 -11039 32;61409 -9876 32;60903 -7933 32;58183 -8639 32;58084 -8257 32;57581 -8388 32;57680 -8770 32;56428 -9095 32;56934 -11039 50; +59400 -5670 32;60700 -5320 32;60859 -5911 32;59559 -6261 32;59400 -5670 50; +63501 -3481 32;65539 -2906 32;65796 -3816 32;63758 -4391 32;63501 -3481 50; +59713 -2006 32;62438 -1256 32;63047 -3471 32;60003 -4309 32;59833 -3690 32;60152 -3602 32;59713 -2006 50; +64251 1801 32;62026 1163 32;61814 1905 32;62157 2003 32;61822 3171 32;63704 3710 32;64251 1801 50; +70239 7052 32;63288 4927 32;62845 6377 32;64465 6872 32;64274 7496 32;69605 9126 32;70239 7052 50; +63401 4601 32;66063 5427 32;66320 4598 32;65010 4192 32;64931 4448 32;63578 4029 32;63401 4601 50; +59235 13231 32;60331 13700 32;59962 14562 32;58866 14094 32;59235 13231 50; +51251 -59310 32;51319 -62389 32;47991 -62463 32;47923 -59384 32;51251 -59310 50; +51909 -51306 32;52007 -56722 32;49887 -56760 32;49876 -56136 32;49622 -56141 32;49608 -55374 32;49867 -55369 32;49794 -51344 32;51909 -51306 50; +50967 -48176 32;52912 -48127 32;52946 -49463 32;51001 -49512 32;50967 -48176 50; +51417 -42485 32;51466 -46836 32;52072 -46829 32;52077 -47305 32;53001 -47295 32;52996 -46826 32;54155 -46813 32;54136 -45115 32;53597 -45121 32;53567 -42461 32;51417 -42485 50; +54777 -35256 32;54874 -39127 32;53794 -39154 32;53837 -40855 32;53518 -40863 32;53558 -42436 32;51653 -42484 32;51573 -39314 32;52067 -39302 32;51968 -35326 32;54777 -35256 50; +62406 -35931;62386 -37163;62118 -37426;62115 -37717;62405 -38013;62394 -38967;61415 -38977 32;61403 -37703 32;60350 -37713 32;60337 -36363 32;56054 -36404 32;56077 -38810 32;60363 -38768 32;60372 -39653 32;61581 -39641 32;61592 -40801 32;62949 -40788 32;62934 -38914;65247 -38941;65258 -37981;65516 -37729;65520 -37362;65268 -37104;65280 -35960;62406 -35931 18; +43003 -56988 32;42713 -60596 32;41283 -60481 32;41421 -58772 32;40482 -58696 32;40426 -59390 32;37774 -59177 32;37937 -57149 32;40339 -57342 32;40407 -56500 32;41227 -56566 32;41442 -56862 32;43003 -56988 50; +32729 -58785 32;34850 -58941 32;34783 -59853 32;35721 -59922 32;35732 -59766 32;37027 -59861 32;37158 -58080 32;35964 -57992 32;35931 -58439 32;34901 -58363 32;35010 -56888 32;33997 -56813 32;33964 -57264 32;32424 -57150 32;32326 -58475 32;31896 -58443 32;31848 -59094 32;32702 -59157 32;32729 -58785 50; +33591 -50014 32;41804 -50180 32;41811 -49847 32;43651 -49884 32;43709 -47017 32;41735 -46977 32;41729 -47263 32;37168 -47171 32;37268 -42219 32;35946 -42192 32;35915 -43722 32;35472 -43713 32;35404 -47097 32;33651 -47062 32;33591 -50014 50; +30666 -49189 32;30617 -50264 32;31935 -50324 32;31874 -51675 32;30560 -51615 32;30522 -52445 32;29058 -52378 32;29040 -52768 32;28442 -52741 32;28478 -51958 32;27848 -51929 32;27864 -51579 32;26419 -51513 32;26471 -50373 32;26143 -50358 32;25734 -49664 32;26491 -49697 32;26556 -48274 32;27320 -48309 32;27304 -48669 32;28527 -48725 32;28498 -49352 32;29881 -49415 32;29893 -49154 32;30666 -49189 50; +25504 -47722 32;26149 -48915 32;25011 -49530 32;24366 -48338 32;25504 -47722 50; +15048 -52091 32;14965 -55713 32;14730 -55708 32;14689 -57477 32;15682 -57500 32;15688 -57246 32;17533 -57288 32;17572 -55602 32;17867 -55609 32;17947 -52119 32;18445 -52130 32;18485 -50390 32;19027 -50402 32;19065 -48760 32;18449 -48746 32;18456 -48455 32;16214 -48404 32;16168 -50419 32;15631 -50407 32;15592 -52103 32;15048 -52091 50; +11157 -47883 32;4811 -47675 32;4769 -48975 32;11115 -49182 32;11157 -47883 50; +4719 -50638 32;4677 -51938 32;6647 -52002;6557 -55527 32;6837 -55534 32;6788 -57466 32;8623 -57513 32;8630 -57244 32;9905 -57277 32;9944 -55737 32;9517 -55726 32;9609 -52099;10954 -52143 32;10996 -50844 32;4719 -50638 50; +1498 -51399 32;1332 -54994 32;1801 -55016 32;1718 -56817 32;835 -56776 32;806 -57404 32;-27 -57366 32;1 -56756 32;-2187 -56655 32;-2099 -54759 32;-2679 -54732 32;-2663 -54381 32;-2401 -54393 32;-2343 -53131 32;-1476 -53171 32;-1394 -51402 32;-937 -51423 32;-850 -49531 32;-1270 -49512 32;-1192 -47826 32;2588 -48000 32;2512 -49647 32;2039 -49625 32;1956 -51420 32;1498 -51399 50; +-6884 -50669 32;-7021 -54257 32;-6485 -54277 32;-6554 -56092 32;-8165 -56031 32;-8158 -55842 32;-10144 -55766 32;-10082 -54149 32;-10821 -54121 32;-10750 -52260 32;-10916 -52254 32;-10851 -50558 32;-9231 -50620 32;-9157 -48685 32;-7247 -48758 32;-7256 -48990 32;-6330 -49025 32;-6393 -50688 32;-6884 -50669 50; +-12206 -53469 32;-12245 -54476 32;-12638 -54461 32;-12599 -53454 32;-12206 -53469 50; +-21216 -54315 32;-16474 -54296 32;-16472 -54794 32;-14987 -54788 32;-14998 -52066 32;-16631 -52073 32;-16633 -51558 32;-21227 -51577 32;-21216 -54315 50; +-18511 -50189 32;-17699 -50189 32;-17699 -49866 32;-18511 -49866 32;-18511 -50189 50; +-23641 -49534 32;-23612 -51499 32;-22603 -51484 32;-22613 -50815 32;-22221 -50809 32;-22240 -49513 32;-23641 -49534 50; +-29262 -51167 32;-26202 -51196 32;-26206 -51587 32;-24569 -51603 32;-24595 -54334 32;-26114 -54319 32;-26110 -53945 32;-29320 -53914 32;-29324 -54296 32;-30933 -54281 32;-30937 -54746 32;-32411 -54732 32;-32397 -53278 32;-32937 -53263 32;-32918 -51318 32;-33495 -51312 32;-33483 -50013 32;-32829 -50019 32;-32841 -51264 32;-32292 -51269 32;-32299 -51997 32;-30826 -52011 32;-30822 -51586 32;-29266 -51601 32;-29262 -51167 50; +-29686 -49599 32;-28874 -49599 32;-28874 -49276 32;-29686 -49276 32;-29686 -49599 50; +-26374 -48998; +-27300 -45487 32;-26809 -46006 32;-27211 -46387 32;-27702 -45868 32;-27300 -45487 50; +-34488 -50010 32;-34488 -51721 32;-34781 -51721 32;-34781 -51320 32;-35211 -51320 32;-35211 -50010 32;-34488 -50010 50; +-43316 -54752 32;-43267 -51965 32;-41881 -51989 32;-41887 -52345 32;-40132 -52376 32;-40125 -51984 32;-38567 -52011 32;-38560 -51613 32;-37012 -51640 32;-37006 -51300 32;-35642 -51324 32;-35654 -51996 32;-35427 -52000 32;-35475 -54737 32;-37001 -54710 32;-36995 -54361 32;-38682 -54331 32;-38689 -54743 32;-40276 -54715 32;-40283 -55124 32;-41853 -55096 32;-41847 -54778 32;-43316 -54752 50; +-39032 -50290 32;-38220 -50290 32;-38220 -49967 32;-39032 -49967 32;-39032 -50290 50; +-42926 -43887 32;-42955 -47133 32;-43131 -47131 32;-43145 -48697 32;-43551 -48693 32;-43558 -49421 32;-41851 -49436 32;-41843 -48579 32;-40473 -48591 32;-40458 -46957 32;-40208 -46959 32;-40181 -43945 32;-40717 -43940 32;-40703 -42344 32;-40999 -42341 32;-40987 -40964 32;-47598 -40904 32;-47610 -42223 32;-43434 -42261 32;-43449 -43882 32;-42926 -43887 50; +-46161 -43437 32;-46161 -44268 32;-46503 -44268 32;-46503 -43437 32;-46161 -43437 50; +-47217 -47660; +-47671 -37842 32;-41714 -37851 32;-41716 -39191 32;-47673 -39182 32;-47671 -37842 50; +-37699 -40257 32;-31201 -40288 32;-31203 -40813 32;-28892 -40824 32;-28894 -41324 32;-28104 -41328 32;-28110 -42528 32;-28920 -42524 32;-28925 -43537 32;-31077 -43527 32;-31075 -43095 32;-35626 -43074 32;-35622 -42265 32;-37708 -42255 32;-37699 -40257 50; +-24270 -37857 32;-21504 -37857 32;-21504 -40144 32;-22139 -40144 32;-22139 -42403 32;-22755 -42403 32;-22755 -44671 32;-24231 -44671 32;-24231 -45385 32;-25502 -45385 32;-25502 -42481 32;-24857 -42481 32;-24857 -40193 32;-24270 -40193 32;-24270 -37857 50; +-48913 -28668;-41461 -28654;-41466 -25986;-46521 -25995;-47199 -23235;-50061 -23938;-48913 -28668 18; +-34395 -31907 32;-34423 -35834 32;-35722 -35825 32;-35728 -36718 32;-37134 -36708 32;-37118 -34367 32;-36746 -34370 32;-36733 -32472 32;-35737 -32479 32;-35733 -31898 32;-34395 -31907 50; +-29644 -36196 32;-23983 -36206 32;-23979 -33606 32;-26007 -33602 32;-26009 -34798 32;-27356 -34796 32;-27355 -34153 32;-29640 -34149 32;-29644 -36196 50; +-23983 -37438 32;-22018 -37418 32;-22024 -36774 32;-22213 -36776 32;-22229 -35169 32;-23221 -35179 32;-23563 -35789 32;-23553 -36783 32;-23989 -36787 32;-23983 -37438 50; +-33113 -29429 32;-28457 -29451 32;-28465 -31062 32;-26257 -31071 32;-26261 -32492 32;-28453 -32483 32;-28454 -31994 32;-33124 -31972 32;-33113 -29429 50; +-14291 -20714 32;-19255 -20507 32;-19281 -21129 32;-23721 -20943 32;-23707 -20602 32;-24297 -20577 32;-24353 -21908 32;-24957 -21883 32;-25015 -23271 32;-19497 -23501 32;-19474 -22953 32;-14393 -23165 32;-14291 -20714 50; +-14129 -15640 32;-21789 -15225 32;-21815 -15710 32;-26865 -15437 32;-26904 -16151 32;-32349 -15856 32;-32246 -13950 32;-33032 -13907 32;-32961 -12602 32;-32171 -12645 32;-32218 -13505 32;-26674 -13805 32;-26638 -13149 32;-21525 -13426 32;-21493 -12837 32;-13999 -13243 32;-14129 -15640 50; +-40106 2232 32;-40090 -4398 32;-41348 -4401 32;-41364 2229 32;-40106 2232 50; +-34919 1617 32;-34919 -4356 32;-37518 -4356 32;-37518 1617 32;-34919 1617 50; +-42159 9067 32;-34430 9067 32;-34430 5984 32;-42200 5984 32;-42200 5652 32;-43334 5652 32;-43334 6377 32;-45505 6377;-45505 5984;-47122 5984 32;-47122 8362 32;-42159 8362 32;-42159 9067 50; +-25909 13095 32;-25909 1951 32;-29376 1951 32;-29376 3209 32;-28754 3209 32;-28754 13095 32;-25909 13095 50; +-17319 13127 32;-17306 2040 32;-20140 2037 32;-20154 13124 32;-17319 13127 50; +-8696 13160 32;-8683 2075 32;-11517 2072 32;-11531 13157 32;-8696 13160 50; +-54 13233 32;-41 2466 32;-2875 2463 32;-2889 13230 32;-54 13233 50; +-46315 56035 32;-45765 51235 32;-51193 50604 32;-51760 55404 32;-46315 56035 50; +-35856 65590 32;-35299 60791 32;-40738 60159 32;-41322 64957 32;-35856 65590 50; +-69777 80432 32;-54506 82485 32;-53319 73658 32;-56328 73252;-56227 72562;-57631 72373;-57701 73026;-68242 71610;-68155 70959;-69164 70823 32;-69088 70256 32;-72172 69841 32;-73959 83131 32;-73070 83251;-72996 82722;-71638 82904 32;-71597 82600 32;-70096 82802 32;-69777 80432 50; +-50694 74014 32;-49414 74190 32;-49501 74821 32;-50781 74645 32;-50694 74014 50; +-43501 80881 32;-43105 77503 32;-43395 77469 32;-43323 76852;-43031 76884;-42932 76080;-45033 75836;-45074 76158;-46198 76036;-46167 75705;-48388 75448 32;-48937 80251 32;-43501 80881 50; +-52033 84000 32;-44410 85003 32;-44238 83696 32;-51861 82693 32;-52033 84000 50; +-51240 85832 32;-44645 86676 32;-44813 87992 32;-51408 87149 32;-51240 85832 50; +-30841 88944 32;-31145 92982 32;-31365 92965 32;-31652 96771 32;-28648 96997 32;-28506 95118 32;-28175 95143 32;-28128 94522 32;-28474 94496 32;-28368 93095 32;-28050 93119 32;-27924 91448 32;-27605 91472 32;-27559 90856 32;-27879 90832 32;-27754 89177 32;-30841 88944 50; +-23486 93024 32;-23223 89138 32;-20135 89347 32;-20398 93233 32;-23486 93024 50; +-26154 97876 32;-21757 98153 32;-21570 95173 32;-23235 95068 32;-23215 94750 32;-24213 94687 32;-24232 94991 32;-25966 94882 32;-26154 97876 50; +-20034 97907 32;-19799 96027 32;-18750 96158 32;-18985 98038 32;-20034 97907 50; +-13987 97476 1;-14309 97449;-14564 97216;-14634 96917;-17097 96722 32;-16793 92830 32;-13959 93052 32;-13941 92816 32;-13317 92865 32;-13336 93104 32;-10523 93324 32;-10826 97211 32;-13255 97022 1;-13367 97313;-13661 97504;-13987 97476 18; +-836 91538 32;-4047 91196 32;-3961 90411 32;-6778 90108 32;-7080 92956 32;-4350 93246 32;-4435 94018 32;-1137 94373 32;-836 91538 50; +-1449 98595 32;-1338 97247 32;-811 97290 32;-922 98638 32;-1449 98595 50; +1286 94347 32;4649 94523 32;4785 91926 32;4463 91909 32;4537 90492 32;3162 90420 32;3087 91856 32;1421 91769 32;1286 94347 50; +1636 100765 32;10000 101470 32;9978 101731 32;14356 102100 32;14429 101238 32;17309 101481 32;17429 100063 32;17229 100046 32;17407 97938 32;14918 97728 32;14829 98784 32;12739 98608 32;12832 97506 32;11811 97420 32;11716 98542 32;10346 98426 32;10370 98136 32;8983 98020 32;9071 96973 32;8034 96886 32;7946 97929 32;5469 97720 32;5556 96683 32;4481 96592 32;4393 97635 32;1917 97426 32;1636 100765 50; +14591 94335 32;14826 90659 32;12658 90521 32;12608 91300 32;11475 91228 32;11416 92156 32;11044 92570 32;11020 92944 32;11343 93366 32;11294 94129 32;12012 94175 32;11990 94520 32;13697 94629 32;13719 94279 32;14591 94335 50; +47565 108673; +10807 81344 32;10848 80376 32;11859 80419 32;11818 81387 32;10807 81344 50; +-19444 85137 32;-10098 85773 32;-9868 82393 32;-19214 81757 32;-19444 85137 50; +-33088 83812 32;-22774 84807 32;-22462 81578 32;-24724 81360 32;-24698 81093 32;-25288 81036 32;-25316 81325 32;-29885 80884 32;-29859 80616 32;-30440 80560 32;-30465 80824 32;-32778 80601 32;-33088 83812 50; +-35454 97091;-34265 87247;-34569 87136;-37998 94823;-36297 97022;-35454 97091 18; +-23558 72896 32;-22576 65568 32;-27458 64914 32;-28440 72242 32;-23558 72896 50; +-27090 62286 32;-26768 59822 32;-25346 60008 32;-25668 62472 32;-27090 62286 50; +-20970 60848 32;-20657 58453 32;-19415 58615 32;-19728 61010 32;-20970 60848 50; +-11855 64598 32;-10856 64739 32;-10721 63788 32;-11720 63646 32;-11855 64598 50; +-10341 63847 32;-9432 63985 32;-9576 64935 32;-10485 64797 32;-10341 63847 50; +-11001 76228 32;-9593 66412 32;-1507 67572 32;-2915 77388 32;-11001 76228 50; +-13465 62772 32;-12345 54186 32;-13995 53970;-14224 53662;-14555 53897;-15048 53833 32;-16168 62419 32;-15638 62488;-15405 62760;-15116 62556;-13465 62772 50; +-5155 63847 32;-4035 55261 32;-5685 55045;-5914 54737;-6245 54972;-6738 54908 32;-7858 63494 32;-7328 63563;-7095 63835;-6806 63631;-5155 63847 50; +-3193 52256 32;4646 53293 32;5001 50607 32;-2838 49570 32;-3193 52256 50; +5504 53514 32;5891 50555 32;8264 50865 32;7877 53824 32;5504 53514 50; +-3285 50099 32;-3177 49346 32;-4000 49228 32;-4108 49981 32;-3285 50099 50; +6189 54714 32;6257 54167 32;5732 54101 32;5664 54648 32;6189 54714 50; +2006 40097 32;8711 43803 32;9990 41488 32;2350 37267 32;2006 40097 50; +2309 44818 32;2466 43782 32;3530 43943 32;3374 44979 32;2309 44818 50; +-94 49122 32;1468 37342 32;-1164 36993 32;-2726 48773 32;-94 49122 50; +9694 39471 32;10164 38586 32;11070 39067 32;10600 39952 32;9694 39471 50; +5698 37300 32;6168 36415 32;7074 36896 32;6604 37781 32;5698 37300 50; +-14708 50601 32;-6855 51651 32;-6798 51223;-6487 50987;-6721 50646;-6487 48898 32;-8135 48677;-8366 48354;-8696 48602;-14340 47847 32;-14708 50601 50; +-13812 47059 32;-12360 36344 32;-9608 36717 32;-10225 41270;-10003 41611;-10305 41858;-10761 45227;-10542 45539;-10833 45760;-11060 47432 32;-13812 47059 50; +-9198 39546 32;-5297 40093 32;-5241 39695;-4962 39462;-5164 39146;-4908 37322 32;-6595 37085;-6813 36782;-7122 37011;-8809 36774 32;-9198 39546 50; +-14624 34546 32;-13631 34702 32;-13784 35682 32;-14777 35526 32;-14624 34546 50; +-14016 29426 32;-12917 29569 32;-13048 30580 32;-14147 30437 32;-14016 29426 50; +-12368 17497 32;-11172 17568 32;-11112 16550 32;-12308 16479 32;-12368 17497 50; +-32743 -2599 32;-24228 -2540 32;-24218 -4037 32;-21508 -4018 32;-21518 -2525 32;-12780 -2465 32;-12779 -2679 32;-8850 -2652 32;-8858 -1420 32;-442 -1362 32;-422 -4270 32;-3541 -4291 32;-3543 -3992 32;-5772 -4007 32;-5770 -4293 32;-8847 -4315 32;-8849 -4025 32;-12771 -4052 32;-12763 -5250 32;-31947 -5382 32;-31956 -4005 32;-32733 -4010 32;-32743 -2599 50; +-6941 -23626 32;-10494 -23446 32;-10378 -21143 32;-9130 -21206 32;-9150 -21605 32;-6845 -21722 32;-6941 -23626 50; +-14433 -27965 32;-14671 -35372 32;-13424 -35412 32;-13186 -28005 32;-14433 -27965 50; +20202 -13306 32;-2415 -12929 32;-2409 -12585 32;-3908 -12560 32;-3914 -12903 32;-11608 -12775 32;-11654 -15564 32;21497 -16117 32;21586 -10769 32;20245 -10747 32;20202 -13306 50; +101 1035 32;92 -486 32;1537 -495 32;1546 1027 32;101 1035 50; +-9811 -27565 32;-10032 -35985 32;-7050 -36063 32;-7004 -34309 32;-6577 -34320 32;-6560 -33662 32;-6980 -33651 32;-6885 -30054 32;-6453 -30065 32;-6435 -29392 32;-6867 -29381 32;-6821 -27644 32;-9811 -27565 50; +-5276 -30497 32;-5415 -35654 32;-4138 -35688 32;-4000 -30531 32;-5276 -30497 50; +-12626 -40671; +-13646 -44401 32;-14155 -44384 32;-14209 -45979 32;-14721 -45962 32;-14783 -47803 32;-15847 -47767 32;-16027 -47899 32;-16342 -47543;-17659 -47499 32;-17607 -45971 32;-17113 -45988 32;-17057 -44322 32;-16553 -44339 32;-16480 -42159 32;-16717 -41850 32;-16700 -41358 32;-16451 -41054 32;-15360 -41091 32;-15351 -40830 32;-14468 -40860 32;-14477 -41118 32;-13536 -41150 32;-13646 -44401 50; +-9389 -40413 32;-9569 -45902 32;-11093 -45852 32;-11073 -45233 32;-10855 -45240 32;-10696 -40370 32;-9389 -40413 50; +-7592 -40468 32;-7786 -45957 32;-6531 -46001 32;-6517 -45608 32;-4583 -45676 32;-4601 -46192 32;-3036 -46247 32;-2981 -44689 32;-2701 -44699 32;-2655 -43407 32;-4652 -43337 32;-4633 -42794 32;-6377 -42733 32;-6299 -40514 32;-7592 -40468 50; +2336 -35864 32;-664 -35726 32;-642 -35257 32;-2333 -35180 32;-2208 -32457 32;-593 -32531 32;-613 -32967 32;2463 -33109 32;2336 -35864 50; +4389 -35934 32;7319 -36043 32;7335 -35572 32;10400 -35684 32;10504 -32896 32;7529 -32786 32;7509 -33334 32;4491 -33223 32;4389 -35934 50; +12817 -36513 32;14919 -36583 32;14938 -36010 32;16478 -36061 32;16457 -36709 32;17211 -36734 32;17232 -36085 32;17818 -36104 32;17871 -34504 32;15117 -34413 32;15099 -34964 32;12870 -34891 32;12817 -36513 50; +11697 -28715 32;11614 -30388 32;13184 -30466 32;13267 -28793 32;11697 -28715 50; +17158 -29047 32;20352 -29199 32;20273 -30861 32;18573 -30780 32;18526 -31767 32;17762 -31731 32;17810 -30726 32;17080 -30691 32;17158 -29047 50; +21498 -28763 32;21443 -29966 32;22308 -30006 32;22363 -28803 32;21498 -28763 50; +25081 -29517 32;25012 -31190 32;26607 -31256 32;26676 -29583 32;25081 -29517 50; +19454 -36761 32;22150 -36844 32;22163 -36423 32;24621 -36499 32;24687 -34348 32;23338 -34306 32;23367 -33352 32;22396 -33322 32;22356 -34635 32;19522 -34548 32;19454 -36761 50; +26823 -36983 32;28994 -37038 32;29005 -36608 32;31297 -36666 32;31340 -34970 32;29160 -34914 32;29150 -35326 32;26867 -35268 32;26823 -36983 50; +33486 -31649 32;33555 -30007 32;31903 -29938 32;31835 -31580 32;33486 -31649 50; +38294 -30288 32;40339 -30377 32;40266 -32050 32;38221 -31961 32;38294 -30288 50; +33729 -37186 32;35851 -37270 32;35873 -36731 32;38083 -36817 32;38148 -35208 32;36015 -35124 32;35998 -35611 32;33795 -35525 32;33729 -37186 50; +43768 -35344 32;43781 -35007 32;46004 -35092 32;45904 -37697 32;40648 -37495 32;40735 -35227 32;43768 -35344 50; +24999 -40165 32;31276 -40411 32;31228 -41656 32;32183 -41694 32;32124 -43163 32;31157 -43125 32;31136 -43672 32;25182 -43440 32;25200 -42952 32;21230 -42795 32;21191 -43788 32;17894 -43659 32;17908 -43321 32;14583 -43191 32;14615 -42427 32;12682 -42350 32;12736 -41018 32;14656 -41094 32;14673 -40649 32;15432 -40679 32;15448 -40288 32;17934 -40385 32;17920 -40776 32;21308 -40907 32;21286 -41428 32;25253 -41585 32;25268 -41177 32;24959 -41165 32;24999 -40165 50; +964 -21200 32;7739 -21462 32;7700 -22480 32;8470 -22510 32;8423 -23722 32;6977 -23666 32;6961 -24091 32;1557 -23881 32;1571 -23516 32;-942 -23419 32;-959 -23870 32;-4664 -23726 32;-4646 -23272 32;-5894 -23224 32;-5867 -22529 32;-6603 -22500 32;-6526 -20505 32;-6905 -20490 32;-6856 -19215 32;-5882 -19253 32;-5987 -21972 32;-4611 -22025 32;-4560 -20704 32;-859 -20848 32;-911 -22177 32;923 -22248 32;964 -21200 50; +12782 -21492 32;15736 -21611 32;15751 -21276 32;16898 -21321 32;16826 -23101 32;15677 -23055 32;15617 -24482 32;12666 -24364 32;12782 -21492 50; +17838 -21332 32;17926 -19122 32;17120 -19090 32;17032 -21300 32;17838 -21332 50; +17911 -24589 32;21147 -24765 32;21332 -21367 32;20226 -21307 32;20189 -21979 32;18059 -21863 32;17911 -24589 50; +21543 -21616 32;24388 -21772 32;24458 -20500 32;21613 -20344 32;21543 -21616 50; +23225 -23102 32;23112 -24919 32;25240 -25051 32;25290 -24247 32;24868 -24221 32;24931 -23208 32;23225 -23102 50; +28563 -22876 32;31594 -23052 32;31457 -25406 32;28426 -25230 32;28563 -22876 50; +34299 -22755 32;38483 -22990 32;38435 -23839 32;40748 -23969 32;40653 -25655 32;39222 -25575 32;39253 -25023 32;38367 -24973 32;38322 -25779 32;34164 -25546 32;34210 -24724 32;33565 -24688 32;33630 -23537 32;34253 -23572 32;34299 -22755 50; +40963 -25854 32;44434 -26030 32;44542 -23897 32;45508 -23946 32;45489 -24320 32;45952 -24343 32;45971 -23973 32;46552 -24002 32;46640 -22276 32;44627 -22174 32;44597 -22796 32;43804 -22757 32;43823 -22379 32;42011 -22287 32;41904 -24379 32;41039 -24334 32;40963 -25854 50; +45452 -18019 32;45314 -20445 32;43119 -20320 32;43139 -19965 32;42109 -19906 32;42202 -18276 32;43233 -18335 32;43247 -18090 32;44052 -18136 32;44063 -17940 32;45452 -18019 50; +38916 -20853 32;37734 -20798 32;37763 -20172 32;38945 -20227 32;38916 -20853 50; +26237 -10777 32;26069 -17909 32;23524 -17849 32;23691 -10717 32;26237 -10777 50; +30871 -10875 32;30704 -17958 32;28159 -17898 32;28325 -10815 32;30871 -10875 50; +33094 -11769 32;32945 -17682 32;34404 -17719 32;34553 -11806 32;33094 -11769 50; +33045 -17704 32;32967 -19884 32;34559 -19941 32;34584 -19252 32;35558 -19287 32;35539 -19815 32;36905 -19864 32;36920 -19449 32;38234 -19496 32;38271 -18477 32;34559 -18344 32;34580 -17759 32;33045 -17704 50; +35512 -13688 32;40967 -13610 32;40925 -10659 32;35470 -10737 32;35512 -13688 50; +49233 -10019 32;49917 -12893 32;50880 -12664 32;51156 -13824 32;48957 -14348 32;48670 -13143 32;47935 -13318 32;47261 -10488 32;49233 -10019 50; +48451 -5521 32;51971 -4397 32;52630 -6461 32;50326 -7197 32;50544 -7879 32;49328 -8267 32;48451 -5521 50; +45968 -4172 32;46457 -5570 32;45153 -6026 32;44664 -4628 32;45968 -4172 50; +47982 -6264 32;48168 -6870 32;47530 -7065 32;47344 -6459 32;47982 -6264 50; +50917 1387 32;54016 2424 32;54867 -121 32;54187 -348 32;54119 -146 32;51700 -955 32;50917 1387 50; +46758 -2955 32;48362 -2388 32;48643 -3183 32;47039 -3750 32;46758 -2955 50; +45937 -589 32;43904 -1322 32;44007 -1607;43536 -1833;43072 -866;41978 -1391;42818 -3141;44363 -2596;44553 -3123 32;46586 -2390 32;45937 -589 50; +55077 3996 32;56378 4436 32;55967 5650 32;54666 5210 32;55077 3996 50; +51261 5377 32;58050 8142 32;58162 7866 32;58879 8158 32;58763 8444 32;59728 8837 32;58664 11449 32;53556 9368 32;52362 12298 32;57513 14396 32;56879 15953 32;56985 15996 32;56637 16850 32;58215 17493 32;57536 19160 32;56469 18725 32;56338 19048 32;54698 18380 32;55322 16847 32;54409 16475 32;55013 14991 32;51017 13363 32;51455 12288 32;48874 11237 32;51261 5377 50; +43264 5095 32;40409 3697 32;39272 6018 32;42127 7416 32;43264 5095 50; +44682 8018 32;47204 9143 32;46423 10894 32;43901 9770 32;44682 8018 50; +49657 14033 32;53653 15802 32;52669 18023 32;48674 16253 32;49657 14033 50; +39076 12694 32;40220 9859 32;41164 10240 32;41485 9444 32;41096 9287 32;41647 7922 32;39790 7173 32;38861 9476 32;37763 9033 32;36677 11726 32;39076 12694 50; +46593 12763 32;43846 11599 32;44239 10670 32;43394 10312 32;42671 12020 32;43565 12398 32;43084 13534 32;45043 14363 32;45327 13693 32;46067 14006 32;46593 12763 50; +52547 23214 32;55118 24289 32;55875 22480 32;56270 22645 32;56541 21997 32;55126 21405 32;54965 21790 32;53545 21196 32;53158 22121 32;51595 21467 32;50692 23627 32;51720 24057 32;51943 23524 32;52347 23693 32;52547 23214 50; +48362 23781 32;48734 22911 32;48248 22703 32;47876 23574 32;48362 23781 50; +46661 20486 32;49046 21493 32;49429 20585 32;48871 20349 32;49244 19464 32;48313 19071 32;48449 18748 32;47617 18397 32;47418 18869 32;46966 18678 32;46673 19372 32;47062 19536 32;46661 20486 50; +41245 18756 32;44315 20057 32;44455 19727 32;45618 20220 32;45200 21206 32;45818 21468 32;46238 20476 32;45526 20175 32;46098 18825 32;45030 18373 32;45256 17839 32;42184 16538 32;41245 18756 50; +36660 17979 32;38117 14527 32;35396 13378 32;34938 14462 32;34651 14341 32;34307 15155 32;34588 15274 32;33937 16815 32;35789 17597 32;35422 18466 32;37099 19174 32;37461 18317 32;36660 17979 50; +27501 -2558 32;33171 -2500 32;33143 181 32;27473 122 32;27501 -2558 50; +28793 3765 32;28780 1111 32;27507 1118 32;27521 3772 32;28793 3765 50; +25710 4636 32;31420 4664 32;31407 7346 32;25697 7318 32;25710 4636 50; +27051 10968 32;27051 8355 32;25682 8355 32;25682 10968 32;27051 10968 50; +22267 11950 32;27991 11978 32;27978 14622 32;22254 14594 32;22267 11950 50; +4220 17026 32;4924 17105 32;4866 17630 32;4162 17552 32;4220 17026 50; +-1426 26520 32;-334 17934 32;-3095 17583 32;-4187 26169 32;-1426 26520 50; +-10510 20644 32;-4358 20672 32;-4345 17867 32;-5540 17862;-5836 17583;-6106 17859;-8637 17848;-8912 17564;-9259 17845;-10497 17839 32;-10510 20644 50; +-11768 28781;-1894 30081;-1781 29223;-1465 28981;-1420 28643;-1682 28300;-1558 27373;-6525 26709;-6746 26420;-7044 26648;-9529 26317;-9793 25972;-10122 26224;-11397 26055;-11768 28781 18; +7989 27813 32;9316 17983 32;8323 17849;8413 17271;7733 17173;7645 17757;6704 17630 32;5377 27460 32;7989 27813 50; +10173 20776 32;17418 20721 32;17398 18079 32;10153 18134 32;10173 20776 50; +18704 18979 32;19630 18965 32;19616 18023 32;18690 18037 32;18704 18979 50; +9869 24910 32;10809 25007 32;10902 24103 32;9962 24006 32;9869 24910 50; +24982 18010 32;25666 18010 32;25666 18587 32;24982 18587 32;24982 18010 50; +14854 37640 32;17840 32124 32;15498 30856 32;12512 36372 32;14854 37640 50; +11798 35867 32;12185 35165 32;11706 34900 32;11319 35602 32;11798 35867 50; +13983 31907;8175 28788 1;7902 28645;7444 28509;7139 28465;4899 28161;4744 29304;4387 29256;4330 29677;4690 29726;4541 30820;6374 31070 1;6850 31135;7091 31220;7512 31451;12683 34230;13983 31907 18; +19071 31935 32;15857 30161 32;20276 22033 1;20728 21192;20813 20872;20813 20363;20799 18385;23536 18385;23536 21082 1;23536 21579;23463 21851;23218 22284;19453 29037 32;20386 29552 32;19071 31935 50; +42757 27526;57250 33877;58010 30780;57153 30406;56995 30003;56578 30166;54392 29209;54205 28729;53735 28912;52246 28258;52074 27817;51651 27982;49455 27018;49306 26635;48879 26801;47393 26147;47223 25710;46806 25873;44747 24993;44592 24595;44178 24756;43012 24226;42757 27526 18; +45351 39345;45890 33344;45614 33319;45667 32738;44659 32293;44673 32003;44079 32015;42870 31506;42198 39027;45351 39345 18; +36914 36801 1;35494 35978;34698 35338;33823 33950;37643 31195;39285 32559;36914 36801 18; +62294 59503 32;66144 59952 32;66300 58616 32;62450 58167 32;62294 59503 50; +50218 106994 32;49208 106949 32;49178 107623 32;50188 107662 32;50218 106994 50; +50081 105547; +50533 105847 32;50229 105008 32;49606 105233 32;49918 106077 32;50533 105847 50; +50535 105846 32;50223 105002 32;49608 105229 32;49919 106073 32;50535 105846 50; +49396 103762; +49850 104061 32;49538 103217 32;48923 103444 32;49234 104288 32;49850 104061 50; +49848 104062 32;49544 103223 32;48921 103448 32;49233 104292 32;49848 104062 50; +49163 102300 32;48859 101461 32;48236 101686 32;48548 102530 32;49163 102300 50; +49165 102299 32;48853 101455 32;48238 101682 32;48549 102526 32;49165 102299 50; +48711 102000; +51183 107564 32;50879 106725 32;50256 106950 32;50568 107794 32;51183 107564 50; +50731 107264; +51185 107563 32;50873 106719 32;50258 106946 32;50569 107790 32;51185 107563 50; +45126 105235 32;45160 104461 32;44522 104433 32;44488 105207 32;45126 105235 50; +52567 106393;52056 106623;50928 104232;51636 102026;51535 101779;51787 101662;52432 103053;52309 103413;51608 103760;51541 104047; +51509 101807;50697 102157;50478 101685 1;50379 101447;50358 101179;50593 101080 16; +56290 106654;54435 107524;52848 107381;47927 110682;47562 111920;44832 114000 1;43496 115018;43059 115828;42197 117270 1;41634 118212;41247 119085;40150 119095 1;39570 119100;39066 119041;38713 118580 1;38540 118354;38363 118154;38222 117967; +41264 112609;41163 112294;41652 112137;41753 112474;41264 112609 18; +41028 114140;41478 115417;40000 115904; +40408 116307 32;40700 117171 32;40011 117404 32;39719 116540 32;40408 116307 50; +38990 115399;39365 115712;39338 116219;38877 116302;38507 115982;38512 115527;38990 115399 18; +37915 115333;38104 115679;38509 115696; +37014 115904;38371 117896; +39175 116227;39275 116555;39574 116718;39762 116659; +38693 113189; +38143 113941; +38536 114794; +39120 114110; +41094 115996; +40625 114131; +37782 115805; +39108 117040; +39466 115774; +38295 115720;38295 116068;38803 116513;39319 116386; +40970 115196; +46673 112120;46864 112452;44832 114000 1;43496 115018;43059 115828;42197 117270 1;41634 118212;41247 119085;40150 119095 1;39570 119100;39066 119041;38713 118580 1;38540 118354;38363 118154;38222 117967;37110 115812;37323 115673;38022 115254;36561 112818;36518 112339;41181 112264;41310 112635;41737 113866;46673 112120 18;40669 117181 32;40411 116362 32;39733 116576 32;39991 117395 32;40669 117181 50;39339 116261;39367 115722;38991 115397;38486 115537;38486 116003;38890 116328;39339 116261 18; +36880 115939;34037 111163 1;33956 110827;34074 110035;34487 109523;41143 109351;41456 110488;44543 109388;45634 109404;46618 112089;41737 113866;41310 112635;41776 112481;41649 112112;41181 112264;36518 112339;36561 112818;38022 115254;36880 115939 18; +33983 111136 1;33673 110584;33528 110144;33435 109517;34467 109494;34165 110113;34014 110732;33983 111136 18; +33983 111136 1;33673 110584;33528 110144;33435 109517;34467 109494;34165 110113;34014 110732;33983 111136 18; +31942 105976 32;35283 106207 32;35252 106659 32;31918 106429 32;31942 105976 50; +31912 106426 1;31656 106408;31481 106188;31502 105932;31867 101903;32327 101945; +30679 100089; +30763 99253; +28224 106250; +23724 105454 32;27242 105889 32;27287 105522 32;23769 105087 32;23724 105454 50; +25514 105308 32;27287 105543 32;27242 105892 32;25474 105668 32;25514 105308 50; +23781 105092 32;25543 105328 32;25522 105659 32;23738 105440 32;23781 105092 50; +29698 106547 1;29715 106467;29725 106384;29728 106299 1;29758 105469;29109 104771;28279 104741 1;27706 104720;27196 105023;26926 105486 16; +26780 105827 1;26746 105943;26727 106064;26722 106190 16; +11442 73470 1;11595 74160;11724 74521;11990 75192 1;12427 76294;13019 76819;13903 77609 1;15030 78616;15596 79338;16198 80724 1;16620 81696;16768 82279;17055 83300;19244 91090;19971 91566;22745 103820;22258 103926 16; +22430 103640; +22217 102484; +22251 103914;22717 103813;22307 101787;21898 102450;22251 103914 18; +29957 103129;23814 102618; +22997 97045;22933 97568;22156 98616;22775 101346;23267 101386;23357 100285;23545 100300;23684 98561;24183 98601 16; +24175 98547;24677 98597;24544 100113;24056 100068;24175 98547 18; +23680 98548 1;23712 98227;23722 97639;23402 97596;22950 97548;22156 98616;22775 101346;23267 101386;23357 100285;23545 100300;23655 98922;23664 98771;23680 98548 18; +22981 97064;22934 97572;23489 97675;23593 97826;23640 98564;24172 98612;24299 97152;22981 97064 18; +35205 107089;35245 106652;31935 106430;31959 105970;32316 101970;34181 102160;34189 101889;34222 101491 1;33971 101530;33731 101363;33681 101112 1;33633 100876;33766 100647;33984 100566;32725 99076;30538 98893;30223 102658;23915 102130;23744 105065;23895 105103;27287 105522 32;27242 105889 32;23724 105454 32;23697 105811;31872 106835;35205 107089 18; +33969 100612 1;33737 100680;33604 100924;33672 101156 1;33740 101389;33984 101522;34216 101454 1;34449 101385;34582 101142;34514 100909 1;34445 100677;34202 100544;33969 100612 18; +33655 101038;34561 101060; +34104 101470;34104 100612; +33485 101152 1;33425 100833;33597 100572;33900 100456; +39209 98935;39055 99583;36787 96979;36423 97301;36135 96970;36203 96347;35537 95582;35155 95919;32517 92754;31765 93372;25143 92597;25031 93551;23179 94180;22930 97063;22378 97020;22644 93818;24567 93117;24681 92093;31665 92868;32688 92021;34649 94391;35095 94022;36254 95423;36596 95140;37832 96634;37519 96893;38100 97596;38339 97396;39205 98458;38983 98662;39209 98935 18; +19064 87284; +22497 95396;21453 95306;21218 94195;23261 92544;23182 93622; +21566 94251; +22660 93821;22527 95376;21465 95295;21230 94184;23273 92533;23194 93611;22660 93821 18; +20672 92630;20414 91362;19707 90924;19583 90408;20784 90677;21137 92519;20672 92630 18; +20724 91587; +20672 92630;20414 91362;19707 90924;19583 90408;20784 90677;21137 92519;20672 92630 18; +21778 91441; +18036 82201 1;17721 82167;17304 82374;17382 82681 1;17516 83204;17599 83496;17749 84015;19209 89072;22228 89824 1;22415 89870;22556 89663;22571 89471;22695 87722 16; +20398 88882; +26761 92306 1;26776 92148;26784 92070;26799 91912 1;26843 91449;27240 91074;27703 91117 1;28133 91157;28494 91629;28432 92085;28377 92496; +27868 91791; +26761 92306 1;26776 92148;26784 92070;26799 91912 1;26843 91449;27240 91074;27703 91117 1;28133 91157;28494 91629;28432 92085;28377 92496;26761 92306 18; +28456 89739 1;28831 89838;29215 89615;29314 89240 1;29414 88865;29190 88480;28815 88381 1;28440 88282;28055 88505;27956 88880 1;27857 89255;28081 89640;28456 89739 18; +28624 89054; +32458 92187;31773 91382;32136 91106 1;32561 90817;32732 90910;32949 91113 1;33237 90886;33760 90732;34088 91060;34483 91520;33174 92571; +32862 91641; +32458 92187;31842 91405;32276 91048 1;32440 90909;32793 90808;32909 91111 1;32918 91136;33001 91122;33020 91105 1;33308 90838;33882 90901;34091 91110;34451 91559;33174 92571;32458 92187 18; +35087 94031 1;35249 93892;35330 93823;35492 93684 1;35661 93539;35903 93557;36048 93726 1;36184 93884;36166 94109;36008 94245;35571 94602; +35087 94031 1;35249 93892;35330 93823;35492 93684 1;35661 93539;35903 93557;36048 93726 1;36184 93884;36166 94109;36008 94245;35571 94602;35087 94031 18; +34669 95336;32223 97319;32124 99026;30538 98893;30223 102658;23915 102130;24090 100037;24090 100037;24536 100076;24663 98632;24211 98592;24341 97144;22933 97027;22933 97027;23179 94180;25031 93551;25143 92597;31765 93372;32517 92754;34669 95336 18; +35006 95740;32822 97585;32725 99076;32124 99026;32124 99026;32222 97313;34669 95336;35006 95740 18; +32156 99063;32708 99107; +41129 109358;41084 109066;33928 109105;33725 109162 1;33520 109229;33409 109252;33435 109518;41129 109358 18; +-45730 102340 1;-45095 100590;-44335 100382;-43065 100464;-33229 101315;-31707 102152;-20987 102981;-18485 104438;-7560 105479;-4626 104316;4505 105239;5369 106325;19118 107811;23563 108295;24372 108374 1;26057 108538;27132 108642;29033 108827 1;30281 108948;30912 109585;31588 110641 1;33510 113643;34321 115236;36169 118284 1;36805 119278;37205 119719;37931 120649 1;38345 121180;38663 121390;38916 122014 16; +42487 121983 1;43779 118804;45475 116661;47615 114999 1;48642 114201;49116 113746;50202 113030 1;50789 112643;51361 112834;51948 113221; +54473 111819;53870 110446;69549 103488;69308 102945 1;71358 101931;73151 99902;74558 97088;75152 96692;75688 96335 1;76312 93907;76420 92639;76669 90144;76636 88824;77079 85473 1;77113 85256;77273 85255;77461 85275 16; +32923 90694; +45150 108372 32;44261 108340 32;44231 108989 32;45119 109022 32;45150 108372 50; +44701 108688; +45152 108375 32;44270 108338 32;44233 108992 32;45124 109025 32;45152 108375 50; +23729 105836;23744 105455;23800 105058;23903 102598;23927 102106;24126 99153;24173 98590;23697 98566;23562 100296;23348 100296;23260 101399;22760 101352;22158 98628;22952 97549;23007 97081;22380 97025;22515 95406;21459 95303;21221 94184;20673 92621;20451 91383;19713 90922;19586 90407;19007 90303;19072 90478;19244 91090;19971 91566;22745 103820;22258 103926;22300 104157;22337 104315 1;22510 105064;23030 105649;23592 105788;23596 105789;23729 105836 18; +39484 106861 32;39537 105839 32;38647 105792 32;38593 106814 32;39484 106861 50; +39657 104066;39440 102541 16; +29943 103128 1;30181 103149;30354 102953;30375 102715; +39240 102577;39211 102087;39318 102115;39599 102569;39240 102577 18; +40256 102552;40225 103131;40851 103162; +40335 102584;40866 102615;40858 103100;40303 103037;40335 102584 18; +35331 104839;36496 104909;36637 102728;37629 101857; +35324 104792;36426 104854;36621 102696;37568 101727;36864 100976;35527 102165;35324 104792 18; +35370 105013 32;35298 105981 32;35703 106011 32;35775 105043 32;35370 105013 50; +36636 103559;39615 103731; +38066 103668; +38814 102120;39050 98729;39484 99247;39484 99247;39258 102148;38814 102120 18; +38842 101730;38341 101712;38157 101505;37702 101875;36886 100948;35504 102165;35251 106160;31981 105953;32310 101985;34169 102139;34222 101491 1;34470 101429;34607 101177;34557 100926 1;34509 100690;34204 100518;33984 100566;32725 99076;32725 99076;32822 97585;35006 95740;35155 95919;35537 95582;36201 96345;36147 96983;36443 97324;36797 96990;38994 99538;38842 101730 18; +37701 101964;38176 101549;38355 101728;38876 101747;38851 102102;38344 102066;38035 102338;37701 101964 18; +38905 102099;38241 102064;38001 102281;37690 101920; +39258 102474;38540 102433; +38819 102122;39269 102148; +38555 102262;38886 102283; +35148 107060;41461 107440;41616 107392;41753 107302;41797 107158;44134 107266;44441 106967;40678 106750;40875 103246;40337 103101;40141 102584;39613 102543;39841 105272;39531 105872;39510 106864;38580 106843;38631 105799;36605 105045;35757 105024;35706 106006;35303 105996;35230 106678;35148 107060 18; +36641 105091;38607 105800;39594 105830;39872 105332;39280 102145;38235 102094;37993 102277;37723 101992;36744 102759;36641 105091 18; +28456 89739 1;28831 89838;29215 89615;29314 89240 1;29414 88865;29190 88480;28815 88381 1;28440 88282;28055 88505;27956 88880 1;27857 89255;28081 89640;28456 89739 18; +41700 98861 32;41123 98177 32;40614 98607 32;41201 99292 32;41700 98861 50; +41167 98740; +41702 98859 32;41115 98173 32;40613 98602 32;41199 99288 32;41702 98859 50; +41703 99306; +40837 100828;39349 100738;39461 99184;40837 100828 18; +39502 100736;39594 99543; +39429 99567;39780 99582;39466 99201;39429 99567 18; +34698 95370;32517 92754;31765 93372;25143 92597;25031 93551;23179 94180;22930 97063;22378 97020;22644 93818;24567 93117;24681 92093;31665 92868;32688 92021;34649 94391;35095 94022;36254 95423;36596 95140;37832 96634;37536 96879;38120 97580;38342 97389;39213 98460;39003 98638;39449 99174 16; +38759 95852 1;38893 95736;39080 95772;39196 95906;40256 97174 32;39794 97560 32;39794 97560 32;38529 96047 32;38759 95852 18; +39670 96938; +38759 95852 1;38893 95736;39080 95772;39196 95906;40256 97174 32;39794 97560 32;39794 97560 32;38529 96047 32;38759 95852 18; +38400 97459;39179 96807; +42937 100904;42792 104329;44505 104403;44462 105239 32;45094 105271 32;45051 106109 32;44420 106077 32;44377 106910 32;44377 106910 32;40682 106720 32;40894 102582 32;39592 102501;39279 102091;39279 102091;39378 100723;39378 100723;42937 100904 18; +44515 104426;42780 104351;42927 100881;44692 100971 32;44515 104426 18; +44340 104423;44442 102530; +44428 101199; +43740 104043;43814 102493; +43265 104019;43339 102469; +47760 100951;46912 98822;45506 98769;46302 100889;47760 100951 18; +46105 99204; +47760 100951;46912 98822;45506 98769;46302 100889;47760 100951 18; +50782 99188;51410 100585;50581 100994;49919 99559 32;50782 99188 18; +49962 98870; +50712 101184;50422 101329;50696 102011;51477 101675;51363 101406;50893 101628;50712 101184 18; +41023 109062;41043 109320;41147 109341;41436 110458;41672 110411;44543 109388;45634 109404;46618 112089;46843 112453;46942 112392;47562 111920;47927 110682;52848 107381;52848 107381;52601 106374;52421 106459;52056 106623;51540 105529;50554 105837;51185 107584;50585 107770;50285 107026;50182 107667;49169 107656;49210 106964;45065 106105;45147 108380;45116 109010;44227 109000;44269 108349;44082 108328;44036 109074;41023 109062 18; +44082 108359;44279 108349;45178 108369;45085 106147;44424 106136;44382 106922;44136 107239;44103 107615;44082 108359 18; +45064 106116;49199 106974;50254 106964;50884 106695;50605 105816;49923 106075;48228 101661;47762 100937;46284 100896;44692 100958;44517 104431;45163 104488;45064 106116 18; +46906 98819;47761 100960;48229 101691;48853 101455 32;49165 102299 32;49165 102299 32;48549 102526 32;48919 103460;48922 103469;48977 103424;49538 103217 32;49850 104061 32;49850 104061 32;49234 104288 32;49567 105244;49697 105196;50223 105002 32;50535 105846 32;51540 105551;51477 105396;50928 104232;51636 102026;51592 101918;51351 101875;50697 102157;50478 101685 1;50379 101447;50358 101179;50593 101080;49919 99559 32;46906 98819 18; +37631 91207;37014 90511;36615 90853 32;34899 88855 32;37546 86625;37527 84837;37336 84839;37311 82722;36938 82738;36928 80299;37908 79738;38069 84497;38288 84833;38302 86806;37798 87196;37694 87487;36359 88591;38194 90722;37631 91207 18; +36618 90797 32;35427 89372 32;35795 89065 32;36986 90490 32;36618 90797 50; +36816 90637;35612 89201; +36943 82409;35765 82461; +37315 84828 32;35868 84869 32;35856 84466 32;37303 84425 32;37315 84828 50; +36137 84663; +37315 84828 32;35868 84869 32;35856 84466 32;37303 84425 32;37315 84828 50; +36475 87511;35979 86953 1;36265 86699;36604 86912;36920 87129; +36475 87511;35979 86953 1;36265 86699;36604 86912;36920 87129;36475 87511 18; +32713 80358 1;32242 80384;31881 80786;31906 81257 1;31932 81728;32335 82089;32806 82064 1;33277 82038;33638 81635;33612 81164 1;33586 80693;33184 80332;32713 80358 18; +32752 81212; +31824 79542 1;32040 78991;32679 78768;33230 79005 1;33539 79138;33755 79397;33831 79702;34263 79635;34325 78250 1;34153 78231;33979 78143;33995 77971;34077 77010;32153 76846;32081 77822 1;32066 77999;31742 77996;31565 77981;31421 79336;31824 79542 18; +32982 77847; +31824 79542 1;32040 78991;32679 78768;33230 79005 1;33539 79138;33755 79397;33831 79702;34263 79635;34325 78250 1;34153 78231;33979 78143;33995 77971;34077 77010;32153 76846;32081 77822 1;32066 77999;31742 77996;31565 77981;31421 79336;31824 79542 18; +35870 80815; +36925 82180 32;36925 80351 32;37359 80333 32;37359 82180 32;36925 82180 50; +37132 82170;37158 80258; +45368 77872;43249 78430;42970 77944 1;42796 77640;42447 77481;42139 77657;40334 78666;40271 78545;39007 79271;38924 79118;36877 80295 16; +38194 90722;38194 90722;37631 91207;37631 91207;37014 90511;36615 90853 32;34899 88855 32;37546 86625;37527 84837;37336 84839;37311 82722;36906 82739;36902 80299 16; +37875 80260;39114 79561;39119 79558;39216 79738;40478 79008;40549 79151;41462 78628;42904 81548;43365 81298;42074 78637 1;41997 78510;41966 78313;42093 78237 1;42199 78173;42160 78196;42266 78132 1;42436 78030;42629 78170;42711 78350;43031 78926;45403 78274;45397 77869;45191 77919;43249 78430;42970 77944 1;42796 77640;42447 77481;42139 77657;40334 78666;40271 78545;39007 79271;38924 79118;37839 79742;37875 80260 18; +42175 77899 1;42383 77765;42669 77854;42818 78130; +42019 77639 1;42411 77401;42888 77505;43093 77915;43280 78249;45381 77696; +36896 76899;36562 76983;36301 77420;35732 77080;35987 76653;35905 76329; +45364 77612;45192 76892; +44656 77779;44485 77062; +44839 76989;44940 77400; +44323 77129;43461 77104;43158 76730;41680 76911 1;41546 76992;41576 77215;41640 77323 1;41747 77501;41801 77593;41906 77768 16; +42992 77091; +44201 77489; +41912 77779 1;41803 77597;41749 77505;41640 77323 1;41567 77200;41561 76951;41700 76917;42671 76794;42574 77380;41912 77779 18; +42625 76801;42529 77374;42960 77682;43275 78126;44567 77804;44361 77136;43455 77110;43159 76730;42625 76801 18; +39429 75155;39918 75232;40182 75065;40555 75116;40889 74879;39468 74647; +39951 75580; +41192 75462;41665 75953;44656 75553;45093 74633;45183 73570; +37036 77338;36908 80269;37131 80149;38924 79118;39007 79271;40271 78545;40334 78666;41895 77793;41813 77614 1;41759 77522;41711 77441;41640 77323 1;41567 77200;41561 76951;41700 76917;42800 76774;43158 76730;43461 77104;44323 77129;44832 77695;45227 77595;45057 76968;44671 75563;41659 75956;41179 75441;40838 75371;40883 74869;40733 74990;40555 75116;40182 75065;39918 75232;39452 75159;39391 75392;39322 76210;39414 76222;39593 76883;38603 77151;38667 77387;37651 77663;37536 77240;37036 77338 18; +33435 109534 1;33400 109294;33574 109106;33935 109111;44051 109082;44129 107271;41780 107154 1;41767 107340;41596 107446;41412 107437;35238 107077;31843 106829;23719 105812 1;23110 105736;22523 105120;22337 104315;21900 102429;22306 101777 16; +38916 122014 1;38663 121390;38345 121180;37931 120649 1;37205 119719;36805 119278;36169 118284 1;34321 115236;33510 113643;31588 110641 1;30912 109585;30281 108948;29033 108827 1;26845 108614;25751 108508;23563 108295;23853 105829;31843 106829;35238 107077;41470 107443;41638 107388;41729 107293;41788 107152;44120 107270;44104 107633;44114 107634;44046 109038;33995 109112 1;33687 109108;33466 109238;33435 109372;33435 109517 1;33511 110027;33621 110414;33826 110838;34012 111158;36853 115904;38222 117967 1;38363 118154;38540 118354;38713 118580 1;39066 119041;39570 119100;40150 119095 1;41247 119085;41634 118212;42197 117270 1;42962 115991;43461 115045;44832 114000;47562 111920;47927 110682;52848 107381;54435 107524;55546 107031;56377 109332;53869 110459;54504 111840;51948 113221 1;51361 112834;50789 112643;50202 113030 1;49116 113746;48642 114201;47615 114999 1;45462 116671;43759 118830;42487 121983;38916 122014 18; +39840 105352;39657 104066 16; +56272 106659 1;56084 106262;55901 105888;56199 105566;57572 106078;56272 106659 18; +56588 106071; +56283 106659 1;56095 106262;55878 105888;56176 105566;57549 106066;56283 106659 18; +55846 103910;56265 104650;56630 103493;55846 103910 18; +57134 103225;57628 104132; +57621 104009;58936 103387; +59336 103200;60516 102640;59872 101534; +56197 105564;60769 103289;62157 104000;63382 103359; +63615 102794; +63398 103358 1;63210 102961;62993 102587;63291 102265;64652 102718;63398 103358 18; +63387 103358 1;63199 102961;63016 102587;63314 102265;64675 102742;63387 103358 18; +61944 99769;61458 100163;62063 100909 1;63061 100215;63550 99767;64339 98845 1;65218 97817;65653 97093;66462 95984 16; +63312 102262;67161 100291;68759 100664;68773 100662 1;71275 98947;72292 97285;73158 94328 1;73470 93265;73588 92794;73800 91706;73329 91268 1;73893 87726;73930 86419;74309 83045;74683 82841 1;74589 80982;74866 80057;75509 74442 1;75577 73861;75503 73535;75114 73098 16; +60373 100268; +60559 98355;60135 98708;60432 99032; +61352 100023;60701 99310; +59154 99762;58454 100147; +58052 100141;59048 99562; +60027 98593;59805 98362;52691 97383 1;52035 97288;51767 97439;51154 97686 1;50849 97809;50679 96888;50599 96568 16; +57644 99681;57339 99162;52455 98600;51194 99138 16; +52585 106350 32;51524 104037;51622 103743;52296 103433;52444 103052;51784 101620 32;51549 101728 32;51391 101385 32;50903 101610 32;50600 100952;51424 100573;51135 99916;52728 99179 32;53070 99920 32;52868 100013 32;53148 100620 32;53831 100305 32;54699 102188;56137 101405;55152 99594;55805 99258;57167 99410;57462 99848;57755 99675 32;58232 100496 32;59197 99971 32;59541 100603 32;59241 100766 32;59788 101772 32;55852 103912;56206 104681 32;52585 106350 50; +49975 99665;51014 99203;51286 99814; +51830 98667 1;51164 98710;50695 98766;50197 98321 1;50049 98380;49972 98404;49826 98469 1;49442 98639;49712 99140;49900 99516; +51830 98667 1;51164 98710;50695 98766;50197 98321 1;50049 98380;49972 98404;49826 98469 1;49442 98639;49712 99140;49900 99516;51830 98667 18; +42040 94433;43922 96689;46045 96234;46720 96677;48938 96328;49579 96771;50491 96595;50290 95588;51537 95339;51554 95798;52242 95848;52304 92548;49800 92439;49801 93299;45187 92994;43916 91498;44551 90982;43730 89972;41014 92180;41292 92522;40812 92912;42045 94429;42040 94433 18; +50491 96595;49579 96771;48949 96328;46628 96666;46021 96235;43922 96689;42040 94433;41626 94770;43438 96910;43254 97398;43926 97651;44119 97138;46081 96743;46203 97329;49580 96803;49667 97236;50583 97053;50491 96595 18; +52518 106392;52798 107383;54443 107500;56274 106660;56128 106338 1;56020 106060;55983 105800;56199 105566;56394 105466;60769 103289;62157 104000;63372 103364;63298 103167 1;63152 102842;63064 102535;63314 102265;63326 102269;62188 100992;59784 101787;56612 103513;56227 104691;52518 106392 18; +51151 99259;51390 99820;51646 99679;52728 99179 32;53070 99920 32;52868 100013 32;53148 100620 32;53831 100305 32;54699 102188;56137 101405;55152 99594;55805 99258;57167 99410;57462 99848;57680 99719;57508 99449;57339 99162;52455 98600;51507 99004;51200 99127;51151 99259 18; +59804 101809;62008 101115;61489 100135;61816 99709;60621 98374;60148 98695;59752 98339;52148 97369;50958 97766;50540 97065;49647 97246;49799 98459;49988 98402 1;50052 98377;50113 98355;50197 98321 1;50695 98766;51164 98710;51830 98667;52138 98735;52455 98600;57339 99162;57644 99681;57857 99851;58232 100496 32;59197 99971 32;59541 100603 32;59241 100766 32;59553 101340;59640 101518;59804 101809 18; +56321 109344;55488 107035;57583 106086;57418 106021;56199 105566;56401 105462;60769 103289;62157 104000;62681 103726;64701 102738;64521 102674;63291 102265;63526 102153;67161 100291;68759 100664;69311 102928;69443 103248;69549 103488;58742 108284;56321 109344 18; +43225 97382 1;42907 97272;42989 96918;42767 96665 1;42537 96403;42423 96272;42193 96010 1;42045 95841;42091 95680;42256 95527; +43019 96650; +42276 95764; +43225 97382 1;42907 97272;42989 96918;42767 96665 1;42537 96403;42423 96272;42193 96010 1;42045 95841;42091 95680;42256 95527;43437 96921;43225 97382 18; +41687 94010 32;41079 93278 32;40670 93618 32;41278 94350 32;41687 94010 50; +44698 100989;46301 100909;45514 98774;46906 98840;49937 99590;49624 98927;49639 98621;49835 98475;49682 97222;49595 96806;46206 97324;46075 96734;44108 97142;43933 97644;43175 97361;43109 97320 1;42924 97171;42957 96881;42767 96665 1;42537 96403;42423 96272;42193 96010 1;42045 95841;42091 95680;42256 95527;41623 94774;42024 94439;41674 94023;41288 94344;40654 93608;41076 93265;40756 92901;38876 95780;38946 95782 1;39037 95783;39129 95828;39196 95906;40256 97174 32;39798 97557;40615 98601;41115 98173 32;41702 98859 32;41199 99288;40748 100765;44698 100989 18; +39195 96816;38430 97479;38611 97720;39213 98460;39003 98638;39449 99174;40806 100773;41228 99322;40609 98572;39815 97559;39195 96816 18; +38588 95974;37409 94568;36626 95215; +40753 92916 1;40492 92595;40276 92088;40630 91875;41960 91000;43526 89710 1;43685 89579;43888 89591;44021 89748 1;44176 89930;44184 90120;44051 90319 16; +41454 89927;41867 90009;42712 89340 1;42889 89200;42868 88981;42728 88804 1;42586 88625;42311 88612;42132 88754; +41640 87206; +45776 90741 1;45426 90145;44691 89413;43990 89040;43210 88585 1;43038 88447;42437 88012;42442 87826;42477 87058;42877 87074;42943 85799 16; +40753 92916 1;40492 92595;40276 92088;40630 91875;41960 91000;43526 89710 1;43685 89579;43888 89591;44021 89748 1;44176 89930;44184 90120;44051 90319;43731 90008;41020 92209;41291 92492;40753 92916 18; +41454 89927;41867 90009;42712 89340 1;42889 89200;42846 88910;42706 88733 1;42564 88554;42260 88634;42081 88776;42390 89126;41454 89927 18; +44319 90607 1;44527 90491;44750 90495;44910 90671;45340 91287;45047 91529;44593 90982 16; +44319 90607 1;44527 90491;44802 90495;44962 90671;45340 91287;45029 91512;44606 90984;44319 90607 18; +45068 92862;45811 92166 1;46031 91979;46186 91941;46472 91963 1;46579 91969;46660 91979;46739 91985 1;47073 92010;47124 92200;47098 92534;47047 93182 16; +53704 96232 1;54077 96212;54363 95893;54343 95520 1;54323 95148;54005 94862;53632 94882 1;53259 94901;52973 95220;52993 95593 1;53013 95965;53331 96252;53704 96232 18; +53704 94676 1;54077 94656;54363 94337;54343 93964 1;54323 93592;54005 93306;53632 93326 1;53259 93345;52973 93664;52993 94037 1;53013 94409;53331 94696;53704 94676 18; +52188 92563;52217 91892;51619 91308; +50720 90645;50852 86753 32;50921 84730; +51640 91359;50669 90469; +47909 93167;47998 91767;47613 91745 1;47628 91519;47799 91276;48025 91286 1;49028 91331;49458 91356;50571 91405;51039 91878;51028 92470; +49076 81752;47494 81556 16; +47489 80619;47313 82174; +43083 83816; +42816 82682;42367 83002;42460 84578;43037 84867;44119 84269;44727 84114;44707 83795;43728 83311;42816 82682 18; +43529 81648;42074 78637 1;41997 78510;41966 78313;42093 78237 1;42199 78173;42160 78196;42266 78132 1;42436 78030;42629 78170;42711 78350;43013 78894;45422 78270;45367 77548;48034 77421;47740 80620;45566 80921;44383 81175;43529 81648 18; +42928 81543;43361 81311; +43072 82602;43104 81942;43520 81706; +42727 85843 1;42752 85254;43181 84761;43766 84447 1;44322 84148;45020 83978;45644 84071; +43842 83133;43565 81701;43113 81922;43082 82671;43503 82938;43842 83133 18; +47170 81981 1;45774 82178;45086 82378;43744 82809 1;43466 82898;43221 82592;43221 82300 1;43221 81992;43450 81896;43650 81662 1;44341 80850;44891 80562;45943 80391 1;46564 80290;47321 80287;47482 80711 16; +43744 82809;45442 82279;47206 81967;47482 80660;44303 81175;43497 81720;43744 82809 18; +45255 83075; +43724 83218;43703 82838;45315 82330;47378 81986;47517 82269;47433 82964 1;47269 83473;46983 83875;46396 84071 1;46036 84191;45823 84115;45448 84060 1;45123 84013;44761 83875;44416 83707;44042 83506;43724 83218 18; +42773 89273;43348 89893;42835 90279;41960 91000;40630 91875 1;40276 92088;40492 92595;40753 92916;38923 95778;38728 95871;38543 96045;39097 96738;38316 97395;38127 97570;37672 97078;37519 96893;37832 96634;36596 95140;36254 95423;35583 94612;35707 94491;36008 94245 1;36166 94109;36184 93884;36048 93726 1;35903 93557;35661 93539;35492 93684 1;35330 93823;35249 93892;35087 94031;34658 94394;33192 92608;33324 92450;34483 91520;34088 91060 1;33760 90732;33237 90886;32949 91113 1;32819 90992;32706 90910;32545 90925;34901 88870;36594 90820;37015 90502;37631 91200;38185 90738;38832 90153;39746 91272;41419 89938;41871 90005;42773 89273 18; +41370 87095;41304 87947;41813 88499;41844 87700;41359 87244;41370 87095 18; +41597 88318;41286 87981;41335 87349;41641 82687;41223 82696;41217 82425;42887 81530;41903 79446;41462 78628;40549 79151;40478 79008;39216 79738;39119 79558;37925 80249;38069 84497;38288 84833;38302 86774;37794 87184;37661 87514;36359 88591;38174 90699;38806 90139;39732 91270;39732 91270;42407 89135;41597 88318 18; +50672 90703 1;50599 90611;50437 90664;50320 90659 1;49362 90617;47818 90552;46860 90511; +48055 88909; +46635 90511 1;46679 89819;46716 89131;47077 88540 1;47301 88173;47527 88076;47822 87763 16; +50528 84006 1;50463 83671;50140 83452;49807 83517 1;49472 83581;49253 83905;49317 84238 1;49382 84573;49705 84791;50039 84728 1;50374 84663;50593 84340;50528 84006 18; +46389 84064;48062 84526;49467 84605; +48360 82545; +45448 84060 1;44515 83925;43283 83046;42997 82834 1;42742 82670;42345 82813;42351 83223 1;42357 83639;42326 84043;42333 84459 1;42336 84612;42525 84719;42663 84792 1;42851 84891;43114 84896;43310 84753 16; +47664 81741 1;47523 82793;47146 83756;46139 84092 16; +45250 84044;46472 84095; +47324 84825; +48591 88119 1;49368 88236;50092 87700;50208 86924 1;50324 86147;49789 85423;49012 85307 1;48236 85191;47512 85726;47396 86503 1;47279 87279;47815 88003;48591 88119 18; +49383 84411 1;49361 84365;49327 84291;49317 84238 1;49253 83905;49472 83581;49807 83517 1;49921 83495;50034 83506;50205 83574 1;50367 83658;50490 83812;50528 84006 1;50567 84205;50510 84441;50296 84599 1;50005 85076;49894 85537;50101 86056 16; +49084 82227 1;49455 82655;49424 82658;49844 83038 1;49982 83163;50130 83183;50162 83366; +47101 89893;47674 89907;48729 89926 1;49202 89947;49724 89535;49849 88950;50006 87928; +50553 90645;46827 90489;46866 89943;47091 89899;48750 89926 1;49223 89947;49724 89535;49849 88950;50016 87887;50075 87346 1;50140 87217;50185 87075;50208 86924 1;50251 86633;50204 86350;50086 86104;50076 85988 1;49904 85497;50018 85055;50296 84599;50377 84539 1;50505 84400;50567 84205;50528 84006 1;50490 83812;50367 83658;50205 83574;50162 83366 1;50130 83183;49982 83163;49844 83038 1;49424 82658;49455 82655;49084 82227;49079 81920;51152 82178 32;50836 84724 32;50860 84727;50910 85041;50852 86753 32;50744 89962;50553 90645 18; +43696 88687;44624 89289; +46241 89123;44629 88973; +44538 89399 1;44636 89489;44696 89533;44767 89590 1;45212 89947;45423 90157;45747 90709;46418 90516;46428 90205;46454 90037;46538 89154;44321 88942;44538 89399 18; +48798 86727; +47104 88430;47176 86603 1;47202 85951;47529 85526;48120 85248 1;48392 85120;48361 84602;48069 84529; +48268 85404;48412 85346 1;48601 85291;48805 85276;49012 85307 1;49483 85377;49866 85671;50067 86065; +48134 85340;48325 85381;48412 85346 1;48601 85291;48805 85276;49012 85307 1;49465 85374;49836 85649;50043 86020;50070 85972 1;49907 85487;50021 85050;50296 84599;49418 84470;48376 84639;48134 85340 18; +50044 86035;50088 86106 1;50204 86352;50251 86635;50208 86924 1;50092 87700;49368 88236;48591 88119 1;48356 88084;48144 87993;47964 87862 1;47615 87593;47408 87217;47383 86804 16; +47252 88140;47724 87648;47651 87542 1;47498 87333;47377 87134;47356 86877;47337 86808;47252 88140 18; +46733 83990;48094 84390;49454 84462;50224 83605;50190 83500;50162 83366 1;50130 83183;49982 83163;49844 83038 1;49424 82658;49455 82655;49084 82227;49079 81920;49099 81922;47811 81711;47370 83076;46733 83990 18; +46797 89913;46881 89011;47208 88329;47890 87835;48067 87930 1;48223 88025;48399 88090;48591 88119 1;49207 88212;49805 87865;50078 87342;50061 87478;50016 87887;49849 88950 1;49724 89535;49202 90009;48729 89988;47091 89950;46797 89913 18; +45861 87091;44670 87009; +45831 86957 1;46006 86887;46171 86690;46077 86527 1;45804 86054;44724 85590;44301 86393 1;44081 86810;43757 87338;44003 87533 1;44290 87760;44706 87848;45071 87872 1;45415 87895;45629 87820;45953 87697 1;46200 87603;46034 87355;45841 87245;45831 86957 18; +46036 86803 1;46101 86719;46129 86618;46077 86527 1;45804 86054;44724 85590;44301 86393 1;44081 86810;43757 87338;44003 87533 1;44290 87760;44675 87858;45040 87882 1;45384 87905;45598 87759;45953 87697 1;46127 87667;46106 87543;46012 87408 16; +43209 88542;43572 88144 1;43634 87872;43593 87782;43470 87532 1;43320 87228;43364 87097;43026 87063;42476 87053;42435 87863 1;42430 88048;42875 88308;43039 88394;43209 88542 18; +43177 88566;44151 89100;46625 89241;47032 88449;47170 85996;47591 85488;48150 85139;48252 84689;46597 84152;44847 84065;43483 84610;43069 85263;42960 85663;42874 87076;43200 87565;43177 88566 18; +47973 92932; +49503 92984; +49667 92193; +47909 93167;47998 91767;47613 91745 1;47628 91519;47799 91276;48025 91286 1;49028 91331;49458 91356;50571 91405;51039 91878;51028 92470;49828 92476;49806 93321;47909 93167 18; +46146 92768; +45068 92862;45811 92166 1;46031 91979;46186 91941;46472 91963 1;46579 91969;46660 91979;46739 91985 1;47073 92010;47124 92200;47098 92534;47047 93182;45201 92997;45068 92862 18; +46441 90492;45751 90717; +41564 84584;42102 84605;42205 84559;42282 84441;42662 84867;42939 85005;42852 85780;42939 85903;42886 87076;42478 87056;42446 87852 1;42441 88038;42831 88302;42996 88389;43215 88581;43990 89040 1;44325 89218;44416 89293;44705 89538 1;45212 89967;45415 90174;45754 90719;46432 90497;46884 90528;50373 90653;50584 90653;52057 91988;52021 92554;51019 92496;51030 92365;51039 91878;50571 91405 1;49458 91356;49028 91331;48025 91286 1;47799 91276;47628 91519;47613 91745;47998 91767;47910 93158;47048 93113;47066 92937;47098 92534 1;47124 92200;47073 92010;46739 91985 1;46660 91979;46579 91969;46472 91963 1;46186 91941;46031 91979;45811 92166;45068 92862;43912 91502;44572 90972;44698 91098;45029 91512;45340 91287;44947 90715 1;44787 90539;44527 90491;44319 90607;44072 90326;44093 90246 1;44173 90076;44128 89922;43992 89763 1;43859 89606;43685 89579;43526 89710;43262 89927;42725 89329 1;42882 89188;42849 88948;42713 88776 1;42571 88597;42274 88634;42095 88776;41818 88500;41849 87705;41357 87277;41564 84584 18; +43386 88361;43572 88144 1;43634 87872;43593 87782;43470 87532 1;43330 87248;43359 87115;43087 87071; +41616 82684;41565 82241;41216 82430;41223 82684; +41637 82677;42007 82626;41964 84622;41586 84586;41695 82873;41637 82677 18; +41971 84666;42000 82641;41644 82699;41579 82271;42958 81523;43364 81312;43510 81712;43089 81958;43038 82728;42493 82873;42327 84710;41971 84666 18; +41568 84596;41960 84603;41993 82651;41622 82665; +45900 85854; +56325 97898;56673 95024;54728 94936;54873 90886;56746 90944;56891 88331;55555 88215;55410 89521;51911 89158;52100 87822;51592 87764;51897 84875;51011 84745;50837 90378;52231 91931;52318 92614;52274 95894;51548 95807;51519 95386;50358 95590;50488 96577;51011 97651;52201 97317;56325 97898 18; +36958 76899;36712 75944;36421 75821;36566 75389;36312 74430 16; +33723 73116; +33363 72814;33002 72773;32961 73133 1;32951 73224;33011 73305;33102 73315;35378 73565; +33363 72814;33002 72773;32961 73133 1;32951 73224;33011 73305;33102 73315;35327 73559;35205 73037;33363 72814 18; +30423 72813; +30227 75005; +30460 75804;30702 73121;30917 72827;31040 71601 1;31069 71312;31026 71077;30719 71054 1;30587 71044;30396 71019;30184 71006; +30460 75804;30702 73121;30917 72827;31040 71601 1;31069 71312;31026 71077;30719 71054 1;30587 71044;30396 71019;30184 71006;29703 75317;30460 75804 18; +30597 71478; +33567 69433;33048 68687;33172 67735;34268 67877;33992 69503;33567 69433 18; +33624 69475;33049 68693;33172 67732;34220 67863 16; +34276 67397;33229 67260;33327 66523;33952 65943;34451 66001 16; +34284 67398;33229 67260;33333 66512;33953 65953;34432 66000;34284 67398 18; +31015 66767; +29991 67364; +34698 63475;34238 63423;33890 62991;34021 61757;34411 61807;34510 60964;35215 61051 16; +47457 65505;43155 64920;41882 66234;41597 69924; +39533 65118 32;40149 64376 32;40492 64658 32;39872 65405 32;39533 65118 50; +39624 67834; +38743 65487;40334 66212;41131 65889; +41328 71723;41365 71366; +41567 71977 32;41641 71172 32;41150 71127 32;41076 71932 32;41567 71977 50; +41016 71933;41092 71096; +40675 71578; +40058 71832;40971 70509 16; +39142 70383 32;39210 69481 32;39640 69518 32;39573 70420 32;39142 70383 50; +40058 71793 32;39246 71220 32;39482 70863 32;40304 71442 32;40058 71793 50; +43555 63267 32;44499 63381 32;44447 63812 32;43495 63701 32;43555 63267 50; +41061 67896; +39598 69978;43879 70359; +41435 71190;41651 70444; +41673 69630;42065 70073; +39474 70860;39785 70388; +38571 71044;39104 71399;39782 70398;39577 70377;39566 70423;38637 70367;38571 71044 18; +39509 70862;40312 71461;40982 70423;39867 70327;39509 70862 18; +40048 71840;41120 71917;41244 71137;41413 70532;40961 70506;40048 71840 18; +38330 69448;39634 69530;39634 69602;41317 69746;41543 66625;41554 66266;41061 65917;40383 66163;38741 65547;38330 69448 18; +38889 65218;40352 65844;40573 65772;39993 65295;39875 65413;39218 64869;38889 65218 18; +42948 71033; +43969 63754;43823 64908; +40194 65063;41820 66431; +39286 65144; +38670 65966;40283 66695; +36808 57918; +36541 56353; +37967 56993; +36758 58849 1;36239 58811;35849 58358;35887 57838 1;35905 57590;36018 57371;36188 57215 1;35819 57076;35565 56696;35596 56272 1;35634 55753;36086 55363;36606 55401 1;37043 55433;37389 55758;37464 56172 1;37627 56062;37829 56005;38043 56021 1;38562 56059;38952 56511;38914 57032 1;38876 57551;38424 57941;37904 57903 1;37859 57900;37814 57893;37771 57884;37768 57978 1;37730 58497;37278 58887;36758 58849 18; +36758 58849 1;36239 58811;35849 58358;35887 57838 1;35905 57590;36018 57371;36188 57215 1;35819 57076;35565 56696;35596 56272 1;35634 55753;36086 55363;36606 55401 1;37043 55433;37389 55758;37464 56172 1;37627 56062;37829 56005;38043 56021 1;38562 56059;38952 56511;38914 57032 1;38876 57551;38424 57941;37904 57903 1;37859 57900;37814 57893;37771 57884;37768 57978 1;37730 58497;37278 58887;36758 58849 18; +35159 64468;35263 63544;35498 63357;35710 61495;35898 61282;37607 61461;41480 59445;41559 58404;41745 58237;42929 58338;43078 58522;43070 58741;49131 59240 16; +35178 64894;34630 70154;34169 70112;33944 72251;35744 72469;36552 75395;36408 75836;36706 75949;36928 76862;37065 77368;37536 77240;37651 77663;38667 77387;38603 77151;39593 76883;39414 76222;39322 76210;39456 74619;40898 74842;40817 75367;41193 75425;41367 74298;42581 74490;42982 74939;43397 74605;43692 71386;41658 71200;41584 72012;40051 71872;39215 71295;39106 71440;38550 71056;38618 70309;39093 70352;39170 69506;38348 69431;38690 65693;38563 65678;38280 65301;35178 64894 18; +33998 61998;35094 62132;35235 61065;34514 60980;34422 61807;34033 61758;33998 61998 18; +34946 63497;34217 63434;33892 62981;34012 61977;35087 62104;34946 63497 18; +40919 57388;39702 57280;39786 56337;41933 56531;41848 57470 16; +40919 57388;39702 57280;39786 56337;41933 56531;41848 57470;40919 57388 18; +43706 57872;44619 57943; +43381 58077 32;44958 58216 32;44998 57761 32;43421 57622 32;43381 58077 50; +43377 57600;43457 56700;45073 56845;45038 57240;45739 57302;45684 58078 16; +44267 57230; +47630 54288; +47210 58414;47290 57493;47863 57543;47894 57185;49814 57350;49669 58840;49245 58799 16; +49130 57848; +51391 59890;49171 59452;49135 59911;50401 60159;50415 60025;51334 60201;51391 59890 18; +36562 76978;36302 77420;35724 77079;35978 76639;35588 75205;35801 75147;35220 73016;33262 72804;33639 69445;34039 69492;34741 63053;35009 63081;35285 60584;37406 60814;40801 58973;40942 57387;43397 57601;43355 58068;44955 58209;44971 58028;46681 58176;46662 58396;49074 58592;49256 58782;49221 59240;43071 58730;43085 58525;42929 58341;41745 58239;41561 58409;41480 59441;37608 61462;35888 61287;35718 61432;35498 63352;35263 63537;35159 64451;38345 64872;38572 65142;38987 64661;39208 64851;38771 65350;38725 65697;38563 65678;38280 65301;35164 64892;34641 70153;34180 70131;33964 72232;35742 72463;36552 75395;36408 75836;36706 75949;36928 76862;36562 76978 18; +53034 60078 32;53699 60205 32;53780 59778 32;53116 59649 32;53034 60078 50; +54410 65753;55538 60701; +53900 65677;55090 60304; +62435 58058;56486 57195;62327 30198;44380 22387 16; +63014 65111;62986 62820 1;62978 62160;62936 61826;62802 61179 1;62642 60408;61304 59309;60072 59057 1;58735 58784;58063 58739;56723 58480 1;56274 58393;56093 58025;56028 57573 1;55994 57334;56007 57206;56057 56969 1;56765 53607;57957 47962;58666 44600;59094 44345 16; +56725 57412;56555 58452; +54278 58287; +54561 55714; +54872 54073; +54782 59186;53250 58594;53442 57869;54510 57994 16; +50692 54408;50641 54883;51769 55000;51717 55504 1;51705 55619;51765 55732;51880 55744;53523 55915;53631 54734;50692 54408 18; +52150 55364; +50681 54407;50641 54883;51769 55000;51717 55504 1;51705 55619;51765 55732;51880 55744;53523 55915;53631 54734;50681 54407 18; +47179 53825 32;48173 53929 32;48087 54751 32;47093 54647 32;47179 53825 50; +47179 53825 32;48173 53929 32;48087 54751 32;47093 54647 32;47179 53825 50; +44661 54956;45084 54996 1;45216 55008;45379 54768;45391 54636; +45723 53668 32;44536 53563 32;44470 54189 32;45656 54312 32;45723 53668 50; +45717 53685 32;44532 53551 32;44470 54189 32;45656 54312 32;45717 53685 50; +53422 53887;43294 52835; +40196 53916 32;40116 54679 32;40872 54758 32;40948 53995 32;40196 53916 50; +40196 53916 32;40116 54679 32;40872 54758 32;40948 53995 32;40196 53916 50; +43377 57600;43457 56700;45073 56845;45038 57240;45739 57302;45684 58078;45014 58033;45006 57745;43377 57600 18; +43381 58077 32;44958 58216 32;44998 57761 32;43421 57622 32;43381 58077 50; +47210 58414;47290 57493;47863 57543;47894 57185;49814 57350;49669 58840;49245 58799;49079 58596;47210 58414 18; +54740 59174;53250 58594;53442 57869;54510 57994;54575 58522;54740 59174 18; +42411 54134;41396 54035;41318 54786 32;42332 54892 32;42411 54134 18; +53443 54681 32;50696 54391 32;50712 54186 32;43906 53470;43824 54264 32;42411 54122;41398 54005;40192 53879 32;40277 53135;43151 53406;43264 52840;53516 53891;53443 54681 50; +35171 60566;35568 56305;35598 56435 1;35633 56791;35867 57094;36188 57215 1;36018 57371;35905 57590;35887 57838 1;35849 58358;36239 58811;36758 58849 1;37278 58887;37730 58497;37768 57978;37771 57884 1;37814 57893;37859 57900;37904 57903 1;38424 57941;38876 57551;38914 57032 1;38937 56712;38799 56418;38568 56230;39776 56324;39683 57290;40935 57399;40809 58969;37416 60808;35171 60566 18; +40116 54657;39771 56356;39869 56344;41933 56531;41848 57470;42016 57481;43367 57598;43398 57363;43457 56700;45073 56845;45038 57240;45739 57302;45684 58078;45826 58102;46681 58176;46662 58396;47228 58442;47231 58174;47290 57493;47863 57543;47894 57185;49814 57350;49669 58840;49253 58800;49247 58898;49221 59240;49200 59455;49328 59483;51391 59890;51387 59912;51458 59782;53039 60094;53066 59912;53116 59649 32;53780 59778 32;53699 60203;53863 60257;54692 60421;54725 60254;55060 60320;55244 59924 1;55054 59742;54896 59476;54774 59177;54605 59122;53250 58594;53442 57869;54457 57988;54484 57885 1;54474 57709;54480 57547;54501 57408;54769 56047;51801 55738;51726 55606 1;51716 55574;51713 55539;51717 55504;51769 55000;50641 54883;50690 54429;50704 54283;50712 54186 32;48158 53917;48161 54041;48087 54751 32;47093 54647 32;47179 53825 32;47074 53803;45723 53661;45699 53900;45656 54312 32;44470 54189 32;44532 53604;44484 53531;43906 53470;43824 54264 32;42419 54125;42332 54892 32;41318 54786 32;41396 54035;40947 53968;40872 54758 32;40116 54657 18; +56732 44030;54206 56000;54759 56058 16; +50641 55187 1;50629 55320;50800 55569;50936 55585;51306 55628 16; +40476 51377;40401 52092;40058 51666;40476 51377 18; +40827 47149;42351 47310;42415 46703;40891 46570;40827 47149 18; +41147 43850;40873 46572;47621 47111;47849 46957;48142 43402;43999 43043;43671 43163;43359 43549;43213 43998;41147 43850 18; +43278 47378 1;43413 47166;43579 46982;43818 47058 1;44010 47118;43998 47318;44178 47408 1;44410 47524;44560 47486;44818 47508 1;44929 47517;44975 47378;44958 47268;44909 46898;42982 46743;42968 47358;43278 47378 18; +43278 47378 1;43413 47166;43579 46982;43818 47058 1;44010 47118;43998 47318;44178 47408 1;44410 47524;44560 47486;44818 47508 1;44929 47517;44975 47378;44958 47268;44909 46898;42982 46743;42968 47358;43278 47378 18; +47868 47768;48048 47632;48134 47438;48120 47178;47990 47000;47818 46968;47789 47101;46313 47012;44920 46899;44915 46941;44958 47268 1;44975 47378;44929 47517;44818 47508 1;44560 47486;44410 47524;44178 47408 1;43998 47318;44010 47118;43818 47058 1;43579 46982;43413 47166;43278 47378;47868 47768 18; +48179 43249;45649 43037; +41189 43832;43209 44007 1;43250 43511;43620 43006;44116 43047;45600 43170;45753 41308;44270 41197 1;42669 41066;41321 42226;41189 43832 18; +47710 47043;47767 46268; +47802 45601;47982 43421; +50738 47202;50671 47826;51702 47941;51345 51221;50677 51147;50698 50900;46421 50447 32;46491 49775 32;42648 49374 32;42763 48273 32;40846 48073 32;40318 53139;43202 53411;43264 52840;53483 53888;53649 52266 32;53400 52240 32;53582 50497 32;53833 50523 32;53987 49043 32;54196 49065 32;54284 48226 32;53987 48195 32;54058 47511 32;53817 47486 32;53934 46364 32;53632 46332 32;53710 45585 32;53256 45538 32;53281 45298 32;52386 45205 32;52418 44896 32;51479 44798 32;51308 44563 32;51043 44535 32;50794 44736 32;49957 44649 32;49887 45323 32;50091 45344 32;49973 46473 32;49768 46452 32;49702 47085 32;50738 47202 18; +50841 47901;50640 49751;51141 49811;51341 47961;50841 47901 18; +48994 41295; +45796 41243;47476 41354;47585 39723;49312 39867 16; +51390 41859;45780 41369; +50382 40266 32;50874 40300 32; +50075 40497 32;51164 40581 32;51203 40077 32;50108 39993 32;50075 40497 50; +50064 40496 32;51164 40581 32;51203 40077 32;50108 39993 48; +50113 39928;50064 40556 48; +50199 40552;50126 41507; +47525 41516;50095 41746;50250 40013;50359 40002;50420 39062;49830 39035;49764 39583;47686 39482;47525 41516 18; +46528 49723 1;46896 49760;47081 49778;47449 49815 1;47835 49854;48346 49230;48368 48843;48571 49079;48695 49327;48804 49848;48691 50300;48005 50190;46487 50040;46528 49723 18; +42648 49382;46498 49792;46632 49733 1;46934 49764;47117 49782;47449 49815 1;47834 49854;48344 49232;48368 48845;48274 48746 1;48086 48581;47871 48476;47671 48458;44490 48178 1;44320 48160;44201 48329;44183 48499;44140 48918;42708 48768;42648 49382 18; +51131 49820;51494 49854;51702 47941;51329 47901;51131 49820 18; +66274 98308 32;66917 97682 32;66455 97205 32;65815 97849 32;66274 98308 50; +66378 97770; +66277 98312 32;66919 97671 32;66460 97209 32;65817 97849 32;66277 98312 50; +65184 99618 32;65827 98992 32;65365 98515 32;64725 99159 32;65184 99618 50; +65288 99080; +65187 99622 32;65829 98981 32;65370 98519 32;64727 99159 32;65187 99622 50; +69283 98928 32;69150 98133 32;68443 98262 32;68590 99053 32;69283 98928 50; +68879 98600; +69287 98927 32;69143 98138 32;68451 98260 32;68594 99052 32;69287 98927 50; +69031 97510 32;68885 96714 32;68187 96838 32;68331 97636 32;69031 97510 50; +69026 97511 32;68892 96709 32;68180 96839 32;68328 97637 32;69026 97511 50; +68619 97180; +68752 96103 32;68610 95324 32;67926 95445 32;68067 96226 32;68752 96103 50; +68748 96104 32;68616 95319 32;67919 95446 32;68064 96227 32;68748 96104 50; +68348 95780; +67052 96144; +66692 95394;66997 94189;67630 93534; +56093 97806;56533 94986;57033 95014;58549 95067 32;58567 94536 32;66319 94805 32;66347 93994;66980 94258;66694 95386;66877 95430;66601 96095;65024 98011;63172 100097;62097 100860;61546 100182;61899 99687;61999 99603;63500 98232 32;62139 96743 32;60495 98245 32;60174 98626;59792 98329;56093 97806 18; +67590 94118; +69742 92663 32;69600 91884 32;68986 92085 32;69057 92786 32;69742 92663 50; +69738 92664 32;69606 91879 32;68979 92076 32;69054 92787 32;69738 92664 50; +69328 92340; +72954 88624;72752 88847 32;71200 90554;70676 90530 32;69575 91615 32;67616 93545 16; +73091 88759;73197 88610;72869 88596;73091 88759 18; +69291 102956;68740 100686;68887 100583 1;71306 98896;72305 97240;73158 94328 1;73470 93265;73588 92794;73800 91706;73329 91268 1;73489 90261;73611 89403;73711 88613;76636 88824;76669 90144 1;76420 92639;76312 93907;75688 96335;75152 96692;74558 97088 1;73151 99902;71358 101931;69308 102945;69291 102956 18; +62066 101079;63286 102279;63507 102162;67161 100291;68759 100664;68773 100662 1;71275 98947;72292 97285;73158 94328 1;73470 93265;73588 92794;73800 91706;73329 91268 1;73490 90254;73612 89391;73713 88597;72898 88686;72752 88847 32;71200 90554;70676 90530 32;69575 91615;69603 91900;69742 92663 32;69057 92786 32;69012 92344;67038 94225;66878 95415;66578 96125;68063 96224;67919 95446 32;68616 95319 32;68748 96104 32;69288 98935;68588 99055;66938 97665;66278 98325;65848 98975;65188 99625;64448 98875;62978 100325;62066 101079 18; +64408 98938;64705 99164;64797 99086;65365 98515 32;65827 98992;65915 98980;66325 98351;66236 98270;65815 97849 32;66455 97205 32;66917 97682;68588 99079;68569 98940;68443 98262 32;69150 98133;69040 97509;68961 97523;68328 97637 32;68180 96839 32;68892 96709;68757 96102;68043 96208;66495 96102;65342 97616;64408 98938 18; +56828 88325;56668 90965;63148 91162;63157 91162 32;63200 89924 32;66484 90038 32;66341 94159;66963 94324;66980 94258;67616 93545;67616 93545;67823 93341;69575 91615 32;70676 90530 32;71200 90554;72752 88847 32;72954 88624;72648 88546;68674 88528 32;68698 83097;67586 82959 32;67745 81676 32;54474 80027 32;53842 85117 32;55080 85268 32;55165 84423 32;57154 84622 32;57126 84905 32;57509 84943 32;57444 85591 32;57186 85565 32;56918 88295;56828 88325 18; +73418 82744; +77574 84016 1;77376 83992;77268 83804;77292 83606;78012 77623;80255 54163 1;80294 53756;80564 53595;80912 53380 16; +73713 88669;76627 88881;76650 88717;77079 85473 1;77113 85256;77273 85255;77461 85275;77574 84016 1;77376 83992;77268 83804;77292 83606;78012 77623;75166 77303 1;74770 80466;74608 81355;74683 82841;74309 83045 1;74118 84749;74013 85926;73884 87130;73713 88669 18; +69224 80491;69169 80884;68519 80800;68404 81740;69049 81819;69661 81895 32;69544 82832;73150 82848 32;73125 88548 32;68674 88528 32;68698 83097;67586 82959 32;67745 81676 32;54474 80027 32;53842 85117 32;55080 85268 32;55165 84423 32;57154 84622 32;57126 84905 32;57509 84943 32;57444 85591 32;57186 85565 32;56910 88325 32;55556 88190 32;55425 89496 32;51925 89146 32;52057 87823 32;51593 87777 32;51886 84855;50836 84724 32;51152 82178 32;49061 81918 32;49837 75672 32;72607 78500 32;72647 78181 32;74427 78574 32;74113 81099 32;69224 80491 18; +69080 81840;68410 81758;68528 80795;69201 80870 32;69080 81840 18; +69031 80922;68975 81400; +74308 78498;74926 73093 1;74575 72633;74169 72606;73596 72527 1;72341 72355;69826 71999;68571 71826 16; +73051 88741;73674 88685;74296 83028;74385 83004;74683 82841 1;74589 80982;74866 80057;75509 74442 1;75577 73861;75503 73535;75114 73098;74911 73223;74308 78498;74401 78568;74427 78574 32;74113 81099 32;69224 80491;69169 80884;69084 81807;69080 81840;69170 81834;69661 81895 32;69544 82832;73150 82848 32;73147 83469;73156 88548;73051 88741 18; +71182 70642; +70182 70492; +71582 70502;71182 70812;69972 70642;69802 70202; +71582 70502;71182 70812;69972 70642;69802 70202;71582 70502 18; +74141 69908; +76149 65965; +76008 67196; +75363 69996 1;75779 69667;76078 69448;76135 68921;76602 64840;76015 64767;75363 69996 18; +75372 69926 1;75788 69597;76078 69448;76135 68921;76601 64848;76015 64768;75372 69926 18; +74790 70452;75671 63922; +72501 70642 1;73409 70751;73938 70868;74781 70512 1;75054 70324;75124 70174;75391 69982;76031 64642;75651 63922;75121 63872;74945 64220;74444 65157;74911 66459;74048 67548 32;74614 68821 32;72721 70332;72501 70642 18; +72511 70652 1;73670 70801;74575 70836;75361 69972; +73486 69688;72479 70462;67572 69655;66052 69405;65835 69102;66330 66111 16; +75006 47682;77066 47992;77354 48395 32;75952 63927;75145 63847 32;74782 64526 32;74444 65157;74911 66459;74692 66735 32;74048 67548;74614 68821;74009 69286 16; +76598 64844;79858 30295; +77993 77671;75143 77381;75190 77114 1;75283 76360;75390 75481;75509 74442 1;75577 73861;75503 73535;75114 73098;74529 72638;69140 71874;69367 70036;69777 70121;69835 70287;69972 70642;71182 70812;71582 70502;71672 70404;72563 70644;72709 70676 1;73774 70802;74620 70786;75361 69972;75493 69893 1;75841 69616;76084 69391;76135 68921;76599 64862;77319 57364;79923 57632;78074 76920;77993 77671 18; +66328 65877;65893 69242;68131 69747;72479 70462;73331 69807;73595 69845;74140 69435;74536 68749;74090 67568;74882 66437;74696 65859;74444 65157;74945 64220;75121 63872;75651 63922;75872 63903;77314 48459;77060 47978;74967 47695;74627 50241;66679 49025;65746 55219;66312 58642;66199 60000;65715 61191;64574 62027;64391 62952;66328 65877 18; +81158 49716 1;80878 49448;80667 49243;80709 48858;81638 38822;81492 34640;81746 32306 1;82795 22667;83060 17821;83874 8159 1;84432 1532;84924 -1770;85166 -8416 1;85333 -13021;85368 -15325;85515 -19931 1;85579 -21919;85390 -26429;89498 -28983; +84659 -24816 1;85467 -26779;85931 -28158;87782 -29353;84677 -29143;84659 -24816 18; +89377 -32625;87929 -32521 1;86487 -32418;85481 -33659;85399 -35103;85275 -40155; +84659 -24816 1;85467 -26779;85931 -28158;87782 -29353;84677 -29143;84659 -24816 18; +75454 43228;77852 43527 32;77525 46740 32;71924 45955;71750 47054 16; +78453 39506;77077 39370; +72915 21155;73270 21151 1;73740 21614;74558 22440;75352 23299 1;76087 24093;76333 24600;76860 25545 1;77597 26868;77965 27530;78702 28853;78553 30114;79190 30190;78407 38509;73115 37868 16; +73078 38015;72806 41161; +76847 39312 1;76733 40669;76081 41473;74767 41834 1;74153 42003;73774 42013;73175 41797 1;72892 41695;72781 41443;72805 41143 1;72896 40008;72810 39423;73027 38305 1;73182 37506;73187 36947;73815 36430 1;74874 35558;76509 36141;76796 37482 1;76938 38143;76904 38638;76847 39312 18; +75098 36035; +76066 41112; +75675 64072;76024 64779;76591 64831;79072 38619;73193 37956;72861 41218;73122 41245;74653 41397 32;74878 39127 32;77093 39347 32;76768 42623 32;75692 42516 32;75635 43088 32;75580 43082;75740 43382;77834 43556;77503 46784;71885 45964;71798 47046;77102 47866;77381 48390;75932 63882;75675 64072 18; +77292 57419;79907 57691;79933 57530;80255 54163 1;80294 53756;80564 53595;80912 53380;81158 49716 1;80878 49448;80667 49243;80709 48858;81302 42448;81362 41801;81638 38822;81492 34640;81746 32306 1;81836 31479;81920 30688;81999 29926;79924 29592;79751 29721;79861 30227;78317 46624;77292 57419 18; +81083 12362 1;81251 8771;81389 6759;81485 3166 1;81643 -2754;81646 -6074;81746 -11995 1;81786 -14393;81782 -15738;81833 -18136 1;81847 -18803;81692 -19212;81320 -19766 1;81041 -20182;81001 -20513;81021 -21013 1;81109 -23234;81158 -24479;81246 -26700 1;81288 -27760;80691 -28839;79631 -28883;74795 -29081;56271 -27721;53558 -27544 1;52501 -27475;51704 -26569;51761 -25511 1;51792 -24936;51772 -24612;51752 -24037 1;51723 -23194;51665 -22703;51378 -21909 1;50372 -19123;49815 -19400;50302 -18151 1;50526 -17575;51629 -17167;52264 -16864 1;52558 -16724;52721 -16459;52766 -16136 1;53190 -13105;53697 -11640;54394 -8660 1;54716 -7282;54936 -6595;55517 -5304 1;56581 -2940;57150 -1775;58230 581 1;59005 2270;59409 3109;60302 4739 1;61395 6734;62053 7680;63448 9476 1;64989 11460;65967 12298;67877 13930 1;69399 15230;70213 15822;71665 17200 1;72909 18380;73519 18990;74624 20301 1;76392 22398;77328 23443;78879 25705 1;79245 26239;80131 26133;80151 25816;81083 12362 18; +77477 26061;76601 24444; +76630 19911 32;77599 21080 32;76949 21618 32;75988 20458 32;76630 19911 50; +74322 19337;74872 18795; +74976 19398; +76921 21637;76555 21943; +76903 21593;76537 21899; +76460 21114; +76874 21583;76542 21871;74344 19350;74902 18766;74929 19114;76874 21583 18; +76527 21928 1;75827 21093;75093 20261;74280 19338 16; +76603 24460 1;76235 23853;75888 23421;75440 22932; +79083 22526; +78577 23617; +79864 22084;79645 25372 1;79635 25536;79355 25655;79263 25520 1;78616 24572;77994 23739;77358 22943 33;77085 22600;76809 22263;76527 21928 16; +79782 22184;79607 25394;79380 25586;79180 25429;77452 23091;78241 22317;78757 21846;79076 21736;79330 21807;79782 22184 18; +72925 21362;72636 21364;72939 21082;72925 21362 18; +73520 21172;73678 21022;72641 20002;72342 20300;72927 21033;73520 21172 18; +73066 16488;72819 17121 1;72786 17219;72679 17265;72558 17267 1;72444 17270;72317 17233;72248 17165 1;71412 16337;69872 15035;69003 14241; +66872 12360;69003 14241 16; +64888 15337;65733 15703; +72204 19779;70294 17703; +70379 17615 1;69585 16849;69120 16489;68109 16048;65148 14765;64970 15177; +65990 15810;66915 16194; +65807 15521;67317 16158;67169 16510; +65755 15573;67290 16184;67160 16498;65650 15861;65755 15573 18; +64976 15140;67349 16160;67183 16509;70219 17801;70411 17652;70320 17558 1;69561 16831;69095 16478;68109 16048;65148 14765;65063 14961;64976 15140 18; +64048 14710;63291 16448; +64030 17496 32;64474 16432 32;62943 15793 32;62499 16856 32;64030 17496 50; +64430 16426 32;65164 14763 32;64290 14416;64248 14669 32;63646 16095 32;64430 16426 50; +79857 30306 1;79902 29824;79256 28510;79044 28178 1;78699 27638;78273 26985;77537 25995; +79376 30024;78870 28785; +78730 29971;79385 30032;78861 28811;78730 29971 18; +78537 38647;79055 38734;79870 30221;79737 29684 1;79548 29114;79191 28409;79044 28178 1;78699 27638;78273 26985;77537 25995;77304 26097;78410 28328 1;78501 28492;78598 28666;78702 28853;78553 30114;79190 30190;78565 36830;78420 38537;78537 38647 18; +79973 29651;80146 25876;79925 26037 1;79633 26137;79132 26074;78879 25705 1;77328 23443;76392 22398;74624 20301 1;73519 18990;72909 18380;71665 17200 1;70213 15822;69399 15230;67877 13930 1;67231 13378;66692 12918;66202 12473;63863 12274;63562 12697;63116 13003;62974 14238;64265 14433;65148 14765;68109 16048 1;69120 16489;69585 16849;70379 17615;72284 19691;72108 19860;72438 20206;72454 20206;72636 20015;73336 20693;73669 21023;73540 21152;75355 23018;75532 22856;75574 22897 1;75881 23246;76037 23420;76311 23795 1;76478 24023;76552 24144;76701 24384 1;77008 24880;77117 25155;77399 25666 1;77468 25792;77500 25856;77567 25983;77750 26284 1;78358 27115;78734 27692;79044 28178 1;79195 28415;79567 29150;79751 29727;79839 29766;79973 29651 18; +79900 22185;80425 22203;80151 25816 1;80131 26133;79245 26239;78879 25705 1;77328 23443;76392 22398;74624 20301 1;73519 18990;72909 18380;71665 17200 1;71422 16969;71196 16760;70983 16567;70613 16236 1;69728 15456;68993 14883;67877 13930 1;67477 13588;67118 13281;66786 12991;67090 12543;68892 14143;69264 14477 1;70155 15275;71490 16415;72248 17165 1;72317 17233;72444 17270;72558 17267 1;72679 17265;72786 17219;72819 17121;73066 16488;73478 16588;75055 17111;74908 18809;74791 18874;74322 19337;74431 19509 1;75184 20364;75870 21145;76527 21928;76820 22280 1;77001 22499;77180 22720;77358 22943 33;77994 23739;78616 24572;79263 25520 1;79355 25655;79635 25536;79645 25372;79801 23032;79813 22779;79900 22185 18; +72946 37863;69294 34508;60066 21233;62632 16841;64704 16643;69837 18568;72946 21332;73370 21249 1;73850 21725;74612 22498;75352 23299 1;76087 24093;76333 24600;76860 25545 1;77597 26868;77965 27530;78702 28853;78553 30114;79190 30190;78407 38509;73985 37973;72946 37863 18; +63668 12097 1;63909 12442;63448 12803;63075 12997;62853 14354;64272 14614; +66872 12360;65783 11399; +65783 11399 1;63904 9741;61457 6223;60162 3633 16; +63668 12097 1;62844 10917;60642 8243;59368 5883 16; +57930 3294 1;58442 4331;58819 4865;59368 5883; +48535 -15189;51425 -14576 33;52072 -11748;52648 -7364;55832 -704 1;56353 385;56709 950;57288 2023 16; +60162 3633 1;59460 2229;59158 1399;58497 -25 1;57553 -2059;57023 -3198;56079 -5232;55389 -6719;53893 -12696;68840 -8861 32;69084 -9814;70271 -9747 16; +66789 13026;67061 12669;65728 11558;63779 9313;63147 8405 1;61991 6853;60893 5095;60162 3633;59955 3212 1;59393 2050;59089 1251;58497 -25 1;57553 -2059;57023 -3198;56079 -5232;55389 -6719;53866 -12803;53405 -12686 1;53690 -11461;54014 -10286;54394 -8660 1;54716 -7282;54936 -6595;55517 -5304 1;56581 -2940;57150 -1775;58230 581 1;59005 2270;59409 3109;60302 4739 1;61395 6734;62053 7680;63448 9476 1;64504 10836;65296 11658;66305 12565;66789 13026 18; +63867 12324;66260 12533;65915 12208 1;65082 11431;64364 10656;63448 9476 1;62053 7680;61395 6734;60302 4739 1;59409 3109;59005 2270;58230 581 1;57150 -1775;56581 -2940;55517 -5304 1;54936 -6595;54716 -7282;54394 -8660 1;54014 -10286;53690 -11461;53405 -12686;51897 -12351 1;52479 -9594;53386 -5821;55832 -704 1;56353 385;56709 950;57288 2023;57447 1925;58101 3238;59358 5854;61431 9185;63867 12324 18; +54605 -14221; +53970 -15455; +55357 -15833; +55082 -17040;55135 -16152 32;55627 -16182 32;55576 -17034 16; +55382 -16595;55348 -17062; +55627 -16182 32;55577 -17010 32;55085 -16980 32;55135 -16152 32;55627 -16182 50; +53578 -17356 32;53645 -16339 32;54505 -16396 32;54438 -17413 32;53578 -17356 50; +53770 -12844;53105 -16521 1;53057 -16789;52811 -17053;52560 -17159;51753 -17500; +51054 -17752;67373 -18852; +51185 -19306; +51746 -18315;51665 -19528;50687 -19459 1;50582 -19207;50537 -19052;50537 -18783 1;50537 -18491;50668 -18303;50826 -18058 16; +54018 -17373;53995 -17721; +54676 -17429; +55330 -16966;55277 -17776; +55276 -17922 32;67398 -18725 32;67381 -18464 32;55297 -17601 32;55276 -17922 50; +67256 -19739; +57915 -20579;58055 -18472; +57279 -20218; +55670 -20396;55811 -18411; +55600 -20344; +56852 -20484;57737 -20537 16; +58101 -20559;58999 -20613 16; +59010 -20618 32;58740 -24631 32;56548 -24483 32;56818 -20470 32;59010 -20618 50; +58155 -20084;61271 -20290 1;61451 -20302;61505 -20580;61493 -20760;58129 -20546;58155 -20084 18; +55913 -18381;57838 -18517;57702 -20540;55765 -20404;55913 -18381 18; +55151 -18426;50801 -18133; +51914 -21690; +55438 -22789;52172 -22558; +55524 -25250 1;55512 -25369;55506 -25436;55494 -25555 1;55483 -25664;55527 -25741;55612 -25808 1;55916 -26049;56032 -26128;56368 -26303;56467 -25311;55524 -25250 18; +56493 -25285;56299 -27389;53571 -27186 1;53309 -27166;53030 -27039;52855 -26898 16; +55054 -25197;54930 -26932;52833 -26788; +55499 -25506 1;55491 -25677;55534 -25747;55612 -25808 1;55843 -25991;55965 -26080;56157 -26189; +52392 -25433;52350 -26259; +52359 -26212 1;52368 -26443;52703 -26777;52864 -26789; +55177 -25175;56489 -25285;56285 -27376;53460 -27173;53108 -27050;52854 -26908;55041 -27046;55177 -25175 18; +52609 -22712;52547 -23585;52090 -23568 1;51962 -22653;51799 -22143;51500 -21251;52286 -21303;52211 -22444 16; +52322 -23113; +52609 -22712;52547 -23585;52090 -23568 1;51962 -22653;51799 -22143;51500 -21251;52286 -21303;52211 -22444;52609 -22712 18; +51744 -18315;51665 -19528;50687 -19459 1;50582 -19207;50537 -19052;50537 -18783 1;50537 -18491;50650 -18331;50808 -18086;50809 -18243;51744 -18315 18; +50677 -19476;51689 -19520;51753 -18297;55119 -18531;55133 -18255;55697 -18311;55446 -22678;52197 -22443;52282 -21308;51436 -21247;50677 -19476 18; +64363 45872;71796 47076;72005 46011;77553 46762;77850 43447;75582 43080;72738 41196;73052 37864;68655 34461;65445 37759;64363 45872 18; +29894 88243 1;29547 86998;30558 86212;31485 86443 1;31824 86528;32027 86292;32374 86245 1;32933 86169;33417 86525;33558 87071 1;33699 87617;33332 88100;32806 88305 1;32576 88395;32339 88421;32201 88626 1;31608 89507;30199 89340;29894 88243 18; +32472 87293; +31115 87848; +30215 88168 1;29941 87186;30801 86529;31532 86711 1;31800 86778;31985 86555;32258 86518 1;32699 86458;33204 86652;33315 87082 1;33427 87513;32964 87968;32549 88130 1;32368 88201;32157 88259;32048 88421 1;31580 89116;30456 89033;30215 88168 18; +30216 88095; +33312 87034; +29894 88243 1;29547 86998;30558 86212;31485 86443 1;31824 86528;32027 86292;32374 86245 1;32933 86169;33417 86525;33558 87071 1;33699 87617;33332 88100;32806 88305 1;32576 88395;32339 88421;32201 88626 1;31608 89507;30199 89340;29894 88243 18; +30428 81724;29873 81650;29244 81884;29540 82378;29441 83587;30195 83661;30428 81724 18; +30146 84297;29992 85795;29367 86239 1;28927 86526;28295 86639;28023 86190;27742 85726;29286 84791;29367 84215;30146 84297 18; +29873 82131; +29700 84623; +28479 85894; +30146 84297;29992 85795;29367 86239 1;28927 86526;28295 86639;28023 86190;27742 85726;29286 84791;29367 84215;30146 84297 18; +30428 81724;29873 81650;29244 81884;29540 82378;29441 83587;30195 83661;30428 81724 18; +29253 81896;29528 82371;29303 84772;27753 85697;27571 85391;28990 84541;29178 82496;28941 82020;29253 81896 18; +27928 81153;27566 80724;25543 80564;25059 80975;25290 81280;24860 81712;24543 81413;24075 81810;23803 85215;24097 85391;24164 85431;24454 82057;25768 80967;27928 81153 18; +24543 85672;25320 86207;25345 85475;24235 84759; +24173 85438;24223 84759;25345 85487;25321 86189;24173 85438 18; +25309 86202;25250 87249;25543 87436;25465 88631;25264 88818;25051 88810;24771 89070;24347 88842;24384 88286; +25272 87251;24507 86868; +24433 86819;25272 87250;25309 86202;24532 85708;24433 86819 18; +25038 88336; +24483 86843;25248 87263;25507 87413;25543 87436;25465 88631;25264 88818;25051 88810;24771 89070;24347 88842;24383 88298;24483 86843 18; +24347 88842;22417 87550; +22398 87546;24360 88842;24372 88250;22460 86843;22238 87164;22386 87324;22398 87546 18; +22309 89828;22228 89809;22228 89824;19209 89072;18560 86877;18793 86839;18993 86955;19251 87126;19238 87468;22262 89570;22447 87608;22694 87741;22571 89471 1;22558 89634;22455 89808;22309 89828 18; +21191 94198;23267 92541;23197 93614;23350 93561;24567 93117;24681 92093;26760 92324;26780 92108 1;26786 92049;26792 91991;26799 91912 1;26843 91449;27240 91074;27703 91117 1;28133 91157;28494 91629;28432 92085;28377 92496;28560 92523;31665 92868;32437 92229;32224 91912;31773 91382;32136 91106 1;32433 90904;32606 90889;32757 90967;34894 88903;33447 87792 1;33309 88018;33079 88199;32806 88305 1;32576 88395;32339 88421;32201 88626 1;31608 89507;30199 89340;29894 88243 1;29891 88231;29887 88218;29884 88206;29219 88670 1;29327 88832;29368 89037;29314 89240 1;29215 89615;28831 89838;28456 89739 1;28081 89640;27857 89255;27956 88880;25485 88323;25465 88631;25264 88818;25051 88810;24771 89070;24347 88842;24188 88736;22729 87759;22680 87927;22571 89471 1;22556 89663;22415 89870;22228 89824;19209 89072;19619 90416;20784 90677;21137 92519;20672 92630;20640 92759;21191 94198 18; +33427 87819;34849 88918;34892 88857;35028 88746;36460 87540;36380 87404;35979 86953 1;36265 86699;36604 86912;36920 87129;36993 87091;37546 86625;37527 84837;37336 84839;37336 84809;37168 84832;35868 84869 32;35856 84466 32;37303 84425 32;37304 84452;37330 84364;37311 82722;36938 82738;36936 82209;36934 80264;37073 77394;36934 76897;36547 77008;36301 77420;35732 77080;34077 77010;33995 77971 1;33979 78143;34153 78231;34325 78250;34263 79635;33831 79702 1;33755 79397;33539 79138;33230 79005 1;32679 78768;32040 78991;31824 79542;31421 79336;29954 80965;29052 81294;29252 81881;29873 81650;30428 81724;30195 83661;29441 83587;29364 84233;29367 84215;30146 84297;29992 85795;29367 86239;30264 86741 1;30587 86453;31046 86333;31485 86443 1;31824 86528;32027 86292;32374 86245 1;32933 86169;33417 86525;33558 87071 1;33624 87328;33578 87571;33456 87777;33427 87819 18; +29283 81872;29100 81288;28867 81362;28283 81575;27910 81131;27674 81131;25768 80967;24454 82057;24222 84755;24329 84827;25345 85487;25321 86189;25301 86351;25250 87246;25323 87306;25507 87413;25543 87436;25482 88373;27943 88939 1;27947 88919;27951 88900;27956 88880 1;28055 88505;28440 88282;28815 88381 1;29009 88432;29163 88560;29252 88724;29908 88291 1;29903 88275;29899 88259;29894 88243 1;29705 87564;29920 87021;30305 86706;29426 86197;29367 86239 1;28927 86526;28295 86639;28023 86190;27742 85726;29286 84791;29367 84215;29424 83486;29528 82371;29253 81896;29283 81872 18; +14815 90792;16045 90896 32;16431 90928;19047 101772 1;19210 102446;19162 103096;18590 103487 1;18118 103809;17720 103979;17154 103897;15241 103685 32;14272 101994; +14937 83031 1;16201 87001;16684 89316;17695 93348 1;18525 96658;19009 98510;19768 101837 1;20006 102881;19606 103868;18644 104339 1;18101 104604;17751 104826;17152 104746;14622 104408;14179 103580;13926 103536;13968 103187;13746 102772;10255 102476;10155 103656;9673 103615;9773 102439;5083 102040;5000 103020;3805 102919;3893 101875;1982 101714;1904 102646;-10566 101461;-21513 100284;-21425 99305;-21733 98925;-26115 98594;-26204 99767;-28371 99603;-28290 98530;-30258 98381;-30338 99432;-35511 99038 1;-36732 98945;-37243 98055;-38465 97989 1;-39872 97913;-40660 97870;-42067 97794 1;-42863 97751;-43557 96881;-43264 96139;-42113 93224;-40476 92684;-39217 89588;-35699 81278 1;-34938 79479;-32648 78178;-31558 78105;-31712 79529;-27639 79900;-27519 78801;-27239 78832;-27277 79178;-22806 80075;-22700 79545 1;-21289 79842;-20491 80013;-19053 80127 1;-13258 80585;-10012 80910;-4206 81191 1;-2212 81288;-405 81608;1528 81109 1;5080 80193;6464 79714;9961 78609 1;11121 78242;12141 78442;12996 79307 1;14092 80415;14523 81520;14937 83031 18; +14619 104410;13816 104303;13917 103542;14197 103579; +14165 103992; +14619 104410;13816 104303;13917 103542;14197 103579;14619 104410 18; +13051 101987 32;13688 102041 32;13656 102414 32;13019 102360 32;13051 101987 50; +10483 101768 32;11120 101822 32;11088 102195 32;10451 102141 32;10483 101768 50; +12436 101961;11781 101896 1;11762 102076;11893 102238;12074 102258 1;12254 102277;12416 102146;12436 101965 1;12436 101964;12436 101963;12436 101961 18; +2118 100835 32;2755 100889 32;2723 101262 32;2086 101208 32;2118 100835 50; +4071 101028;3416 100963 1;3397 101143;3528 101305;3709 101325 1;3889 101344;4051 101213;4071 101032 1;4071 101031;4071 101030;4071 101028 18; +4686 101054 32;5323 101108 32;5291 101481 32;4654 101427 32;4686 101054 50; +8818 101406 32;9455 101460 32;9423 101833 32;8786 101779 32;8818 101406 50; +6469 101205 32;8051 101340 32;8019 101713 32;6437 101578 32;6469 101205 50; +10197 103083;9717 103042; +9936 103353; +4780 102769; +5041 102577;4561 102536;4524 102973 16; +138 101692;443 98377;844 97941;262 97406;-447 98122;-763 101589; +138 101692;443 98377;844 97941;285 97427;-396 98168;-741 101581;33 101658; +-621 100522;223 100605; +-499 99283;315 99363; +-212 100540; +-107 99318; +1657 102368; +1918 102176;1438 102135;1401 102572 16; +66 97095 32;1087 97173 32;1166 96139 32;145 96061 32;66 97095 50; +913 97915;965 97156; +1035 96127;1086 95447; +305 101676;759 101449;1073 97924; +305 101676;759 101449;1073 97924;572 98350;305 101676 18; +881 95272;5320 95577;5364 94942; +1885 97375 1;1944 96757;2541 96368;3159 96424;3059 97525; +1885 97375 1;1944 96757;2541 96368;3159 96424;3059 97525;1885 97375 18; +5584 96651 32;6880 96759 32;6790 97833 32;5494 97725 32;5584 96651 50; +5584 96651 32;6880 96759 32;6790 97833 32;5494 97725 32;5584 96651 50; +9100 96938 32;10730 97075 32;10640 98149 32;9010 98012 32;9100 96938 50; +9100 96938 32;10730 97075 32;10640 98149 32;9010 98012 32;9100 96938 50; +11944 97401;12138 95103 32;6398 94618;6689 90147; +4398 90453;4435 89852;6891 89980; +5684 95826;5950 91742;4507 91648; +5149 91566;5194 90877;4534 90834;4489 91530;5149 91566 18; +5154 91497;5194 90877;4534 90834 16; +5707 93136 32;5677 93607 32;5277 93581 32;5307 93110 32;5707 93136 50; +5646 93605;5277 93581 32;5307 93110 32;5677 93134 16; +10456 95590; +7372 95504; +5885 95806;11118 96204 1;11648 96240;11715 96847;11825 97367; +5188 94955;4669 94567;4805 91869;5712 91936;5638 93127;5540 93125;5307 93110 32;5277 93581 32;5607 93602;5545 94984;5188 94955 18; +4530 90834;4583 89979;6633 90057;6675 90354;6398 94618;12138 95103;11946 97375;11755 97027 1;11665 96613;11534 96232;11118 96204;5885 95806;6170 91549;5141 91479;5184 90877;4530 90834 18; +10373 98121;10355 98426;11734 98548;11812 97205;11742 96967 1;11652 96577;11514 96231;11118 96204;5885 95806;5479 95757;1274 95469;995 98923;821 100720;1614 100799;1876 97388;1906 97246 1;2029 96704;2585 96372;3159 96424;3059 97525;4406 97597;4476 96568;6884 96760;6796 97798;7965 97929;8035 96873;9108 96934;9432 96966;10730 97075 32;10640 98149 32;10500 98137;10373 98121 18; +-3945 99805;-4094 100952;-904 101367; +-4001 100902;-930 101277;-852 100230;-3818 99838;-4001 100902 18; +-10579 101443 32;-6725 101809 32;-6681 101342 32;-10535 100976 32;-10579 101443 50; +-6941 101539; +-8581 101400; +-10326 101216; +-10579 101443 32;-6725 101809 32;-6681 101342 32;-10535 100976 32;-10579 101443 50; +-5316 97173;-5586 99370;-9071 98908 32;-8703 96147 32;-5252 96607 32;-5327 97172 32;-5316 97173 18; +-4400 97253;-4731 99711;-5613 99600 32;-5327 97130;-4400 97253 18; +-4740 99668;-4420 97292;-1842 97636 32;-2043 99148;-1166 99261 32;-1273 100119 32;-4740 99668 18; +-5650 99376;-5856 100933; +-6526 99281;-6730 100818; +-6574 100641;-10156 100165; +-10217 100346;-9819 97143;-10772 97072; +-9854 95983;-9573 93874;-4719 94521;-4132 95128;-3059 95251;-3195 96438; +-8746 96254;-10067 96078; +-8136 94466; +-7734 94309;-7799 94793;-8443 94706; +-7795 96263 1;-7795 95905;-7972 95724;-8171 95426 1;-8337 95177;-8468 94998;-8424 94702;-8330 94721;-7799 94793;-7734 94309;-9462 94030;-9732 95949;-7795 96263 18; +-3006 96438;-3111 97450;-4463 97250;-5283 97127;-5240 96621;-7796 96281;-7797 96201 1;-7816 95883;-7984 95706;-8171 95426 1;-8337 95177;-8468 94998;-8424 94702;-8326 94722;-7799 94793;-7734 94309;-7494 94151;-4719 94521;-4132 95128;-3059 95251;-3132 95889;-3006 96438 18; +-5796 100352;-6500 100259; +-6224 100283; +-3998 99769;-4129 100982;-787 101427;-456 98147;259 97379;870 97841;1123 95163;-2855 95085;-3003 96463;-3016 96533;-3111 97450;-3142 97445;-2939 97490;-1842 97636 32;-2043 99148;-1166 99261 32;-1273 100119 32;-3804 99790;-3998 99769 18; +-11706 101329 32;-14033 101079 32;-13980 100589 32;-11653 100839 32;-11706 101329 50; +-15320 100940 32;-17941 100658 32;-17888 100168 32;-15267 100450 32;-15320 100940 50; +-19196 100522 32;-19738 100465 32;-19685 99975 32;-19143 100032 32;-19196 100522 50; +-19452 100247; +-17655 100448; +-15571 100657; +-13678 100875; +-11968 101058; +-15320 100940 32;-17941 100658 32;-17888 100168 32;-15267 100450 32;-15320 100940 50; +-19196 100522 32;-19738 100465 32;-19685 99975 32;-19143 100032 32;-19196 100522 50; +-11706 101329 32;-14033 101079 32;-13980 100589 32;-11653 100839 32;-11706 101329 50; +-17154 97527;-17242 98648;-15159 98811;-15075 97732; +-14962 97754 1;-14455 98172;-13670 98310;-13069 97877; +-11630 98042;-12951 97939;-13085 99651; +-14116 98845; +-15338 99604; +-13601 99600 1;-13776 99594;-13935 99718;-13951 99892;-13968 100067;-11613 100310 1;-11664 100050;-11766 99775;-12029 99750;-13601 99600 18; +-17353 98759;-17429 99633;-12240 100245;-11613 100310 1;-11664 100050;-11766 99775;-12029 99750;-12960 99661; +-11412 98182;-11386 97894; +-10921 97254;-10997 98230; +-12315 99240; +16507 90767;17051 90832 1;17254 91606;17465 92431;17695 93348 1;18525 96658;19009 98510;19768 101837 1;20006 102881;19606 103868;18644 104339 1;18101 104604;17751 104826;17152 104746;14622 104408;14179 103580;13926 103536;13968 103187;13746 102772;10255 102476;10202 103105;9727 103043;9717 103042;9771 102467;9773 102439;5083 102040;5037 102586;4880 102563;4561 102536;4524 102973;4309 102962;3805 102919;3893 101875;4089 100972;4686 101022;4680 101121;4654 101427 32;5291 101481 32;5323 101108 32;5321 101108;6469 101205 32;6437 101578 32;8019 101713 32;8051 101340;8791 101368;8812 101476;8786 101779 32;9423 101833 32;9455 101460;9900 101462;10000 101470 32;9978 101731 32;10475 101773;10473 101886;10451 102141 32;11088 102195 32;11120 101822;11799 101898;11795 102030 1;11833 102150;11940 102243;12074 102258 1;12254 102277;12416 102146;12436 101965 1;12436 101964;12436 101963;12436 101961;13070 101996;13042 102094;13019 102360 32;13656 102414 32;13688 102041;14378 102120;14610 102585;15241 103685 32;17154 103897 1;17720 103979;18118 103809;18590 103487 1;19162 103096;19210 102446;19047 101772;18109 97883;16507 90767 18; +3907 101907;4129 101006;4029 101160 1;3966 101271;3843 101339;3709 101325 1;3528 101305;3397 101143;3416 100963;3437 100965;2747 100883;2748 100975;2723 101262 32;2086 101208 32;2118 100835 32;1637 100784;835 100667;766 101367;759 101449;305 101676;-861 101565;-1249 101365;-4129 100982;-3998 99769;-4797 99726;-5611 99603;-5747 99381;-6426 99294;-6697 100627;-10385 100219;-10535 100976 32;-6681 101342 32;-6725 101809;-6212 101875;1347 102593;1397 102565;1412 102443;1438 102135;1918 102176;1954 102048;1982 101714;3558 101847;3907 101907 18; +-3115 97417;-3015 96457; +-10379 100368;-9969 97297;-13249 97018;-13282 97084 1;-13410 97340;-13685 97502;-13987 97476 1;-14309 97449;-14564 97216;-14634 96917;-17097 96722 32;-17275 97523;-17034 97543;-17109 98535;-15270 98678;-15193 97730;-14944 97745;-14929 97781 1;-14421 98178;-13657 98301;-13069 97877;-12957 98011;-13085 99651;-12917 99665;-12029 99750 1;-11773 99774;-11670 100035;-11617 100289;-17424 99632;-17899 100162;-17689 100189;-15267 100450 32;-15320 100940 32;-14031 101094;-14018 100938;-13980 100589 32;-11653 100839 32;-11706 101329 32;-11723 101327;-10583 101464;-10515 100964;-10379 100368 18; +-17353 98711;-17421 99612;-13967 100043;-13956 99946;-13951 99892 1;-13935 99718;-13776 99594;-13601 99600;-13155 99643;-13004 97847;-13161 97938 1;-13749 98295;-14481 98151;-14962 97754;-15089 97916;-15159 98811;-17353 98711 18; +-18848 96611;-17817 96692;-17508 92754;-17910 92722; +-17825 92651 32;-17915 93785 32;-18471 93741 32;-18381 92607 32;-17825 92651 50; +-18130 92930;-18171 93439; +-18337 92177;-17442 92247;-17382 89877; +-16859 89920;-16907 91827;-16518 91857; +-17829 89844 32;-16608 89948 32;-16495 88629 32;-17716 88525 32;-17829 89844 50; +-18921 96112;-18034 88400; +-20118 88302 32;-20160 88919 32;-20900 88869 32;-20858 88252 32;-20118 88302 50; +-20156 88438;-17715 88604; +-18292 96124; +-17934 93990; +-18848 96611;-17817 96692;-17508 92754;-17910 92722;-17912 93801;-18525 93737;-18848 96611 18; +-17825 92651 32;-17915 93785 32;-18471 93741 32;-18381 92607 32;-17825 92651 50; +-17786 91004; +-19082 98024;-19225 99172; +-19933 97949;-20067 99023; +-20259 98947;-20383 100405;-19752 100473;-19724 100340;-19685 99975 32;-19143 100032 32;-19196 100522 32;-19104 100543;-17973 100665;-17930 100552;-17893 100217;-17397 99638;-17418 99502;-17353 98764;-17372 98758;-17276 97523;-17256 97508;-17054 96741;-16825 92825;-16650 92816;-16518 91857;-16907 91827;-16859 89927;-17357 89928;-17388 90103;-17442 92247;-18362 92174;-18381 92607;-17825 92651 32;-17831 92727;-17663 92742;-17508 92754;-17817 96692;-18777 96617;-18962 98050;-19844 97963;-20259 98947 18; +-18376 92163;-17443 92250;-17373 89921;-17800 89851;-17748 88795;-18053 88761;-18376 92163 18; +-10558 93579;-9657 93690;-9867 96071;-8720 96293;-8800 96878;-9071 98908 32;-6462 99254;-6524 99775;-6697 100627;-10302 100228;-9830 97194;-10780 97033;-10558 93579 18; +-19813 96006 32;-19722 95278 32;-18960 95373 32;-19051 96101 32;-19813 96006 50; +-19394 95701; +-19813 96006 32;-19722 95278 32;-18960 95373 32;-19051 96101 32;-19813 96006 50; +-20258 91601 32;-19806 91631 32;-19919 93306 32;-20371 93276 32;-20258 91601 50; +-20144 93014; +-20048 91871; +-20258 91601 32;-19806 91631 32;-19919 93306 32;-20371 93276 32;-20258 91601 50; +-20066 90930;-19987 89755; +-20362 93458;-21169 93404; +-21148 93398;-21695 93361;-21737 93989;-25406 93744;-25024 88018; +-24829 87867;-28988 87589; +-29095 87387;-29210 89104; +-25850 91199; +-25885 91713; +-25579 93903;-26702 93828;-26396 89242;-25293 89316; +-26067 92901; +-26504 93546; +-25692 93590; +-25579 93903;-26702 93828;-26396 89242;-25293 89316;-25579 93903 18; +-21383 95126;-21577 98228; +-21549 95125;-21526 94765;-23530 94636; +-23190 94668;-21531 94761;-21550 95162;-23215 95057;-23190 94668 18; +-24600 94527;-25740 94456; +-26546 96871; +-26472 95785; +-26385 94762; +-26199 97908 32;-25971 94286 32;-26737 94238 32;-26965 97860 32;-26199 97908 50; +-26199 97908 32;-25971 94286 32;-26737 94238 32;-26965 97860 32;-26199 97908 50; +-27556 91123;-27268 91144;-27207 90310 1;-27170 89812;-27381 89508;-27716 89137; +-27556 91123;-27268 91144;-27207 90310 1;-27170 89812;-27381 89508;-27716 89137;-27852 90802;-27550 90824;-27556 91123 18; +-27642 92060; +-27655 92912;-27720 93780; +-27975 95749;-28039 96584; +-27988 92936;-27649 92962;-27771 94547;-28085 94523; +-28629 97057;-28677 97685;-27979 97738;-27851 96057;-28531 95404; +-28358 96292; +-28444 97303; +-28629 97057;-28678 97694;-27980 97747;-27851 96057;-28531 95404;-28629 97057 18; +-27988 92936;-27649 92962;-27771 94547;-28455 94500;-28360 93112;-28037 93121;-27988 92936 18; +-21373 100270;-21268 99240;-21650 98771;-26147 98443; +-26499 99438; +-28122 99325; +-30573 99106; +-28226 99186;-28166 98398;-30377 98230;-30435 99009; +-26155 99060 1;-26313 98855;-26606 98828;-26837 98959 1;-27077 99095;-27069 99459;-26947 99706;-26208 99762 32;-26155 99060 18; +-26947 99706 1;-27069 99459;-27056 99160;-26824 99011 1;-26593 98863;-26313 98855;-26155 99060;-26099 98326 32;-27025 98256 32;-27134 99692 32;-26947 99706 18; +-28796 97000;-28824 97506;-29968 97420 32;-30512 97379;-31887 98233;-32201 98207 48; +-34660 98208;-34563 97037;-35422 96967 16; +-32148 98211;-34643 98007 16; +-21276 100315;-20342 100403;-20203 98941;-20050 97873;-19799 95991;-19712 95262;-18931 95362;-18150 88724;-20081 88582;-20111 88946;-20136 89334;-20272 91592;-19803 91641;-19920 93294;-20401 93263;-21111 93455;-21677 93422;-21738 94076;-25600 93903;-26702 93828;-26724 94239;-25971 94286 32;-26006 94841;-25871 94888;-24232 94991 32;-24213 94687 32;-23491 94733;-23494 94638;-21526 94765;-21549 95125;-21739 98184;-26994 97851;-27026 98263;-26089 98337;-21650 98771;-21268 99240;-21303 99587;-21276 100315 18; +-26402 89252;-25261 89332;-25175 87962;-29024 87685;-29092 89067;-27722 89159;-27635 89229 1;-27347 89560;-27173 89854;-27207 90310;-27268 91144;-27556 91123;-27594 91329;-27605 91472 32;-27924 91448 32;-28035 92923;-27741 92953;-27708 93771;-27721 93894;-27771 94547;-28085 94523;-28139 94667;-28175 95143 32;-28506 95118 32;-28530 95433;-28473 95460;-28125 95794;-27891 96606;-27907 96788;-27979 97738;-28677 97685;-30552 97504;-31852 98350;-31913 99336;-31746 99325;-30349 99423;-30289 98706;-30377 98230;-28166 98398;-28301 99127;-28354 99379;-28371 99603;-27098 99699;-27001 98298;-26961 97802;-26737 94238;-26715 94163;-26684 93824;-26437 89488;-26402 89252 18; +-27106 99677;-27002 98296;-26120 98351; +-32704 98325 32;-34101 98211 32;-34145 98747 32;-32748 98861 32;-32704 98325 50; +-32761 98894;-32714 98323; +-34163 98778;-34119 98238; +-42143 97594; +-40428 97729; +-40021 96348; +-39984 94843; +-41983 96237; +-41810 94522; +-40934 93128; +-38639 97816; +-36937 98260; +-42032 98827;-42081 99506;-42723 99444 1;-42908 99426;-43025 99247;-43007 99062 1;-42990 98891;-42832 98773;-42661 98790;-42032 98827 18; +-40663 98963;-40700 99592;-39960 99654 1;-39807 99667;-39689 99535;-39676 99382 1;-39659 99185;-39812 99017;-40009 99000;-40663 98963 18; +-42802 94909;-41470 95008;-40470 92664; +-40285 92219;-39686 92462;-39296 93385;-38146 93852;-37582 92464;-37062 92675; +-31802 98336;-31871 99313;-32094 99298;-35511 99038 1;-36732 98945;-37243 98055;-38465 97989 1;-39872 97913;-40660 97870;-42067 97794 1;-42863 97751;-43557 96881;-43264 96139;-42777 94905;-42702 94916;-41470 95008;-40470 92664;-40309 92219;-40174 92264;-39686 92462;-39296 93385;-38146 93852;-37582 92464;-37062 92675;-37182 92993;-37998 94823;-36297 97022;-35454 97091;-35159 96988;-34563 97037;-34658 98179;-34104 98153;-34106 98277;-34145 98747 32;-34006 98758;-32748 98861 32;-32744 98808;-32685 98314;-31802 98336 18; +-42032 98827;-42081 99506;-42723 99444 1;-42908 99426;-43025 99247;-43007 99062 1;-42990 98891;-42832 98773;-42661 98790;-42032 98827 18; +-40663 98963;-40700 99592;-39960 99654 1;-39807 99667;-39689 99535;-39676 99382 1;-39659 99185;-39812 99017;-40009 99000;-40663 98963 18; +-42797 94895;-41488 95008;-40476 92714;-42125 93228;-42797 94895 18; +-37727 87650 32;-36682 85308 32;-34257 86390 32;-35301 88731 32;-37727 87650 50; +-35732 86451; +-36239 87586; +-34212 86107; +-34188 87303;-34107 86632; +-33745 86114;-33236 86175;-32955 83838; +-27219 87728;-27079 84378;-32977 83855;-33256 86158;-33736 85922;-34303 86611;-34268 87283;-35397 96941;-34977 97003;-34563 97037;-34648 98059;-31908 98250;-31484 98110;-30552 97504;-28828 97670;-28785 96976;-29257 96951;-31652 96771 32;-31365 92965 32;-31145 92982 32;-30841 88944 32;-29226 89066;-29029 87520;-27219 87728 18; +-33740 80722; +-33231 81999;-35113 81326 1;-34796 80579;-34463 80270;-33842 79747;-32744 79870;-32806 80561; +-34928 81301; +-33830 81708; +-32941 82103;-35113 81326 1;-34796 80579;-34463 80270;-33842 79747;-32744 79870;-32806 80561;-32941 82103 18; +-32942 80529;-33527 85984; +-40294 92224;-39683 92468;-39265 93402;-38166 93864;-37572 92468;-37101 92669;-35304 88726;-35541 88624;-37727 87650 32;-36682 85308 32;-34257 86390 32;-34584 87123;-34347 87204;-34290 86619;-34281 86584;-33736 85922;-33681 85949;-33649 85943;-33518 85533;-33131 82009;-33339 81960;-35113 81326 1;-34796 80579;-34463 80270;-33842 79747;-32744 79870;-31712 79530;-31561 78096 1;-32575 78186;-34906 79405;-35699 81278;-37062 84497;-40294 92224 18; +-39682 92442;-37710 87644; +-36686 85324;-35082 81364; +-27081 79615; +-22231 84883;-21844 80883; +-21108 84992;-20728 81064; +-20907 81209;-19157 81350;-19196 81757; +-18689 81255; +-22785 84682;-20903 84864; +-11227 82322;-11157 81303; +-9843 82384;-9780 81451;-10791 81383; +-11541 81325;-20858 80618;-20896 81098 16; +-16772 80938 32;-12847 81236 32;-12885 81733 32;-16810 81435 32;-16772 80938 50; +-17942 80849 32;-17086 80913 32;-17124 81410 32;-17980 81346 32;-17942 80849 50; +-11578 82061 1;-11971 81926;-12240 81685;-12294 81272;-11566 81321;-11578 82061 18; +-12146 81753; +-19104 81765;-11615 82259;-11591 82061;-11652 82034 1;-12004 81895;-12243 81659;-12294 81272;-12861 81222;-12855 81345;-12885 81733 32;-16810 81435 32;-16772 80938 32;-17093 80914;-17093 81000;-17124 81410 32;-17980 81346 32;-17942 80849 32;-17923 80850;-20880 80630;-20893 81099;-19129 81309;-19104 81765 18; +-10302 81879; +-10843 82324 32;-9840 82392 32;-9777 81458 32;-10780 81390 32;-10843 82324 50; +-6827 81748; +-8303 81622;-6333 81756; +-7563 86371 32;-6703 86430 32;-6658 85768 32;-7518 85709 32;-7563 86371 50; +-7588 86260;-10257 86079;-10237 85780; +-4840 83882; +-4975 85942; +-4876 84597; +-6690 86325;-4576 86468;-4529 85765 32;-4403 83905 32;-4364 83324;-3759 83365; +-5321 82426; +-3764 83206;-3671 81835;-6354 81652;-6641 85745 16; +-3764 83206;-3671 81835;-6354 81652;-6641 85745;-6678 86273;-4602 86401;-4390 83307;-3764 83206 18; +7779 87400;7770 86135;-3890 85295 32;-3760 83242 32;-1236 83401;-1208 83017;6065 83542;6106 82674;6727 82671;6728 82865;10420 82837;11395 83100;11388 83425;11726 83432;11719 83796;12026 83802;12228 84012;12218 84536;12010 84736;11747 84731;11692 87386;7779 87400 18; +-2081 81944;-1228 82006; +-2118 83289;-2069 82522;-808 82603;-367 83105; +-2118 83289;-2069 82522;-808 82603;-367 83105;-1206 83033;-1258 83422;-2118 83289 18; +-1254 82166;-659 82204;-859 81870;-1242 81845; +-1254 82166;-659 82204;-871 81858;-1242 81845;-1254 82166 18; +7636 80945;7661 80340;8043 79958;10301 80019;10906 80500; +10819 79810; +-358 81894 1;504 81947;1007 81859;1838 81623 1;1939 81940;1722 82357;1394 82412 1;885 82497;589 82411;74 82388 1;-147 82378;-216 82167;-321 81972;-358 81894 18; +1432 82015; +124 82128; +-358 81894 1;504 81947;1007 81859;1838 81623 1;1939 81940;1722 82357;1394 82412 1;885 82497;589 82411;74 82388 1;-147 82378;-216 82167;-321 81972;-358 81894 18; +2485 81446;6321 80360;6050 82038;3225 81890 1;2935 81875;2683 81820;2559 81557;2485 81446 18; +2077 83247 1;2099 82889;2399 82604;2756 82630;6074 82872; +5823 83308;5836 83022; +5614 82829;6067 82873;6041 83536;5579 83510;5614 82829 18; +4463 82760;4414 83426; +4445 82760;5649 82847;5597 83519;4410 83458;4445 82760 18; +2064 83248;4428 83431;4454 82786;4308 82743;2756 82630 1;2422 82606;2137 82854;2084 83180;2064 83248 18; +3154 81574; +5614 81268; +5886 82053;6127 80734;6875 80751; +6782 81189; +7838 81617; +8532 81253;8558 80608;10024 80667;9996 81366; +8567 80791;9998 80843; +7733 80936 32;8073 80950 32;8093 80474 32;7753 80460 32;7733 80936 50; +10464 81398 32;10804 81412 32;10824 80936 32;10484 80922 32;10464 81398 50; +8065 80927;8426 80942; +8097 80502;8440 80516; +10118 81342;10473 81356; +10485 80953;10129 80939; +2485 81446;6321 80360;6050 82038;3225 81890 1;2935 81875;2683 81820;2559 81557;2485 81446 18; +5588 83485;5618 82851; +12945 81400; +14213 84549; +10831 81392 32;11337 81413 32;11267 83070 32;10765 82962 32;10831 81392 50; +11315 82040 1;11308 82182;11436 82248;11568 82301 1;12497 82676;13020 82884;13950 83257 1;14184 83351;14481 83049;14412 82807;14061 81895 1;13513 80896;13297 80185;12377 79513 1;11723 79035;11130 78831;10353 79059 1;9259 79380;8631 79511;7544 79853;7492 80263 16; +11760 83436;11766 83157;14857 84413 1;15297 85599;15398 86314;15664 87551;14936 87501;14997 86021;12408 85830;12325 86952;12026 86929;11895 88676;11348 88635 16; +-9780 81441;-8279 81519;-6360 81667;-6657 85733;-7538 85689;-7555 86116;-10103 86021;-9780 81441 18; +-3677 81831;-3773 83245;-2116 83349;-2113 83209;-2069 82522;-808 82603;-424 83040;-328 83081;2089 83271;2098 83117 1;2175 82823;2444 82607;2756 82630;6067 82871;6093 82686;6756 82704;6756 82887;10394 82826;10795 82948;10839 81395;10737 81400;10467 81386;10118 81366;9874 81358;9888 80962;8667 80917;8654 81255;8407 81245;8421 80973;8065 80949;7832 80938;7442 80924;7485 80257;7520 79864;6307 80344;6290 80606;6054 81068;5889 82036;3018 81871;2882 81836 1;2743 81790;2630 81708;2559 81557;2485 81446;2520 81436;1838 81623 1;1939 81940;1722 82357;1394 82412 1;885 82497;589 82411;74 82388 1;-147 82378;-216 82167;-321 81972;-358 81894;-859 81870;-659 82204;-1254 82166;-2032 82088;-2058 81774;-3677 81831 18; +11315 82040 1;11308 82182;11436 82248;11568 82301 1;12497 82676;13020 82884;13950 83257 1;14184 83351;14481 83049;14412 82807;14061 81895 1;13513 80896;13297 80185;12377 79513 1;11723 79035;11130 78831;10353 79059 1;9259 79380;8631 79511;7544 79853;7492 80263;8059 79971;10246 80020;10935 80509;11356 81354;11315 82040 18; +10116 81343;10468 81328;10489 80927;10759 80933;10779 80625;10239 80186;8115 80143;7836 80414;7849 80471;8090 80475;8070 80896;8426 80914;8443 80485;10149 80550;10116 81343 18; +11284 87427;11037 90536 32;12591 90647; +15306 87143; +16588 90820;16342 89611;16058 89290;16021 90179;12061 89895;12115 89142;11280 89082; +11687 89425; +12034 89930 32;11260 89875 32;11316 89081 32;12090 89136 32;12034 89930 50; +11188 90442;16553 90791;16561 90686;16342 89611;16058 89290;16021 90179;12059 89920;11238 89861;11188 90442 18; +12386 85823;11776 85291;11697 87376;11435 87402;11322 88632;11889 88676;11895 88676;12026 86929;12325 86952;12394 86023;12386 85823 18; +11331 88637;11305 89082;12099 89152;12064 89893;16025 90181;16042 89283;16348 89606;16601 90836;17063 90862;17041 90698 1;16408 88111;15898 86049;14937 83031 1;14897 82887;14858 82746;14817 82609;14369 82696;14412 82807 1;14481 83049;14184 83351;13950 83257 1;13020 82884;12497 82676;11568 82301 1;11484 82267;11401 82228;11354 82167;11281 83043;11398 83086;11435 83431;11768 83438;11762 83160;11852 83192;14857 84413 1;15297 85599;15398 86314;15664 87551;14936 87501;14997 86021;12408 85830;12325 86952;12026 86929;12015 87077;11910 88690;11331 88637 18; +11771 85323;12382 85835;15003 86026;14929 87488;15663 87556;15643 87452 1;15392 86275;15285 85567;14857 84413;11766 83157;11760 83436;11765 83793;12036 83806;12234 84009;12240 84534;12049 84750;11783 84768;11771 85323 18; +14385 102117;15298 103733;15751 103741;17154 103897 1;17720 103979;18118 103809;18590 103487 1;19162 103096;19210 102446;19047 101772;16431 90928;16045 90896 32;14815 90792;14797 91112;14591 94335 32;13719 94279 32;13697 94629 32;11990 94520 32;12012 94175 32;11294 94129 32;11343 93366 32;11020 92944 32;11044 92570 32;11416 92156 32;11475 91228 32;12608 91300 32;12649 90659;11052 90516;11209 87410;7772 87393;7754 86102;-3901 85282;-3796 83467;-4354 83450;-4564 86381;-6650 86311;-6827 90096 32;-3961 90411 32;-4047 91196 32;-836 91538 32;-1137 94373 32;-4435 94018 32;-4350 93246 32;-7080 92956;-7217 94188;-4719 94521;-4132 95128;-3064 95250;1054 95262;5277 95524;5207 94957;5155 94930;4669 94567;4669 94562;1307 94372;1412 91773;3104 91834;3174 90429;4291 90464;4430 89819;6716 89958;6471 94643;12229 95149;12037 97452;12315 97462;12832 97506 32;12739 98608 32;14829 98784 32;14918 97728 32;17407 97938 32;17229 100046 32;17429 100063 32;17309 101481 32;14429 101238 32;14402 101553;14385 102117 18; +-27161 84391;-27296 87661;-24977 87895;-25310 93632;-21781 93891;-21769 93151;-22160 93114;-23486 93024 32;-23223 89138 32;-20135 89347 32;-20110 88956;-20251 88913;-20900 88869 32;-20858 88252 32;-20118 88302 32;-16509 88647;-16588 89982;-16850 89947;-16902 91823;-16518 91866;-16684 92826;-16064 92887;-13959 93052 32;-13941 92816 32;-13317 92865 32;-13336 93104 32;-10523 93324 32;-10554 93718;-9656 93765;-7143 94080;-7056 92945;-6751 90041;-6547 86458;-7969 86362;-10342 85969;-10403 85777;-19462 85127;-19200 81454;-20744 81350;-21111 84962;-22838 84822;-27161 84391 18; +-21733 80911 32;-20887 80993 32;-21251 84740 32;-22097 84658 32;-21733 80911 50; +-21890 81800;-20842 81902; +-22048 83228;-20980 83331; +-21379 81843; +-21516 83280; +-32776 79781;-32784 80601;-30518 80819;-30465 80824 32;-30440 80560 32;-29859 80616 32;-29885 80884 32;-25316 81325 32;-25288 81036 32;-24698 81093 32;-24724 81360 32;-24545 81377;-22456 81587;-22761 84579;-22316 84649;-21741 80933;-20877 80994;-20860 80610;-20582 80639;-11585 81295;-10630 81369;-9779 81477;-8279 81534;-6349 81644;-3653 81848;-2049 81786;-833 81877;-865 81371 1;-1759 81408;-2585 81300;-3612 81227;-4610 81171 1;-10166 80895;-13394 80574;-19053 80127 1;-20491 80013;-21289 79842;-22700 79545;-22806 80075;-27277 79178;-27239 78832;-27519 78801;-27639 79900;-28018 79865;-32345 79469;-32776 79781 18; +-893 81869;-369 81913;-160 81895;376 81902 1;856 81876;1271 81784;1838 81623 1;1842 81636;1846 81649;1849 81663;2534 81519;2485 81446;6321 80360;6318 80378;7535 79924;7544 79853 1;8631 79511;9259 79380;10353 79059 1;11130 78831;11723 79035;12377 79513 1;12420 79544;12461 79576;12501 79607;12779 79849 1;13377 80427;13603 81060;14061 81895;14412 82807 1;14413 82810;14414 82813;14414 82816;14830 82653 1;14445 81331;13998 80320;12996 79307 1;12141 78442;11121 78242;9961 78609 1;9405 78785;8887 78944;8394 79093;7883 79245 1;5531 79938;4031 80466;1242 81185 1;567 81359;-353 81353;-911 81373;-893 81869 18; +29981 80952;31421 79408;31555 78074;31565 77981 1;31742 77996;32066 77999;32081 77822;32153 76846;34077 77010;34071 77079;35730 77114;35770 77016;35987 76653;35905 76329;35582 75177;35800 75150;35355 73563;33922 73405;33102 73315 1;33011 73305;32951 73224;32961 73133;33002 72773;33273 72804;33629 69476;33537 69356;33049 68693;33172 67732;34207 67861;34258 67403;33951 67355;33229 67260;33327 66523;33952 65943;34424 65998;34703 63492;34592 63466;34217 63434;33892 62981;34012 61977;34013 61830;34021 61757;34411 61807;34510 60964;35215 61051;35294 60543;32284 60260;31445 65824;29658 65562;29488 66888;30086 66933;30000 67036;30391 67442;30322 68432;29499 68355;29237 70660;29817 71375;30175 70993;30300 71014 1;30465 71028;30611 71046;30719 71054 1;31026 71077;31069 71312;31040 71601;30917 72827;30804 72982;30725 73090;30702 73121;30460 75804;30076 79555;29981 80952 18; +28566 66791;28480 67577;29038 68031;28777 70709;29117 71128;28899 71302;29291 71764;29852 71311;29263 70589;29594 67380;30088 66945;28566 66791 18; +30016 67036;30391 67438;30310 68440;29499 68361; +29919 68197; +30016 67036;30391 67438;30310 68440;29499 68361;29615 67387;30016 67036 18; +28553 66779 32;29489 66895 32;29652 65549 32;28721 65427 32;28553 66779 50; +28739 59676;28985 60258;29614 60145;31838 60546;31115 65284;28454 64865;28381 65366;31441 65828 32;32267 60199 32;28739 59676 18; +28386 65333;28454 64865;31115 65284;31794 60537;29570 60171;28976 60284;28712 59715;26556 60343 32;25860 64952 32;28386 65333 18; +28726 59657 32;29177 59724 32;29376 58388 32;28925 58321 32;28726 59657 50; +28836 58943 1;27385 59106;26543 59282;25098 59067 1;24344 58955;23920 58895;23173 58746 1;22009 58513;21200 58135;20518 57046 1;20514 57041;20496 57001;20493 56996; +20379 60151;21469 58721 1;21043 58497;20795 58373;20431 58058 1;20415 58831;20399 59292;20379 60151 18; +20379 60151;21469 58721 1;21043 58497;20795 58373;20431 58058 1;20415 58831;20399 59292;20379 60151 18; +20379 60151;21469 58721 1;21043 58497;20795 58373;20431 58058 1;20415 58831;20399 59292;20379 60151 18; +23270 58752 1;23792 58689;24074 58394;24590 58493 1;24957 58563;25161 58615;25527 58690 1;25976 58782;26245 58578;26699 58641 1;27128 58701;27446 58714;27736 59036;27610 59088 1;26796 59178;26089 59214;25098 59067 1;24549 58985;24175 58931;23723 58850;23270 58752 18; +26823 58888; +23479 59345 1;23192 59734;22927 60023;22443 60023 1;22091 60023;21899 59900;21592 59727 1;21828 59419;21984 59262;22295 59036;23479 59345 18; +25120 61676; +25429 61022; +26562 60392;24255 60043; +20497 56997;20580 56483;19518 56311;19421 56828;20497 56997 18; +19322 58478;18250 58309;17802 58635;18886 58806;19322 58478 18; +13099 57896;17605 58644;15796 60146;12796 59674 16; +14827 54670;14045 54547;13597 56214;19415 57121;19463 56813; +13981 55987; +14827 54670;14045 54547;13597 56214;19415 57121;19463 56813;14660 56035;14827 54670 18; +13197 57791 32;14292 57972 32;14430 57138 32;13335 56957 32;13197 57791 50; +14328 57978 32;16089 58269 32;16232 57407 32;14471 57116 32;14328 57978 50; +17512 58471;18484 57764;16238 57398;16108 58244;17512 58471 18; +14270 59874;14533 58203; +14393 59035; +12664 60405 1;13634 60674;14227 60730;15227 60841 1;15768 60901;16105 60693;16535 60360 1;17001 60000;17236 59764;17707 59410 1;17961 59220;18172 59117;18484 59175;19804 59410;19817 57966;18484 57764;17546 58441;17432 58725;15801 60157;12796 59654;12664 60405 18; +12651 60409 1;13621 60678;14227 60730;15227 60841 1;15768 60901;16105 60693;16535 60360 1;17001 60000;17236 59764;17707 59410 1;17961 59220;18172 59117;18484 59175;19804 59410;19817 57966;18484 57764;17546 58441;17432 58725;15801 60157;12796 59654;12651 60409 18; +22481 59725; +17089 60465; +14696 61477; +22073 63340; +22160 65376; +19211 60404; +20926 61317; +25922 65129;23553 64771;23541 63229;24737 62575;26144 62809; +25922 65129;23553 64771;23541 63229;24737 62575;26144 62809;25922 65129 18; +18570 63558;19873 61458; +19943 62166 1;21248 63039;21554 64400;21143 65801; +19211 61721 1;17734 60720;15873 61384;14901 62787; +21316 66303 1;22431 66128;23790 65744;24914 65844;28628 66238; +18871 69286; +15934 69349; +13973 67264; +13220 65253; +13553 63316; +14145 64057 1;14540 64166;14974 64026;15120 63644; +16637 68421 1;16796 68095;16713 67727;16388 67565; +15860 67251 1;15540 66991;15218 67109;15019 67392; +21321 66215;17834 64908; +17809 64760;14417 62872; +14294 62859 1;13389 62298;12867 61929;12147 61144; +17081 65758;12973 72901; +16111 62863; +17149 62324; +20291 64897; +20175 63947; +19641 63197; +19022 62074 1;17720 61192;16064 61799;15207 63035; +19689 62479 1;20841 63250;21111 64451;20748 65688; +19689 62479 1;20841 63250;21111 64451;20748 65688; +17225 66056 1;17724 66324;18550 66145;18777 65561 16; +16507 64349 1;16256 64788;16335 65505;16752 65767 16; +18254 63673 1;17734 63334;17055 63416;16784 63887 16; +18990 65061 1;19165 64572;18976 64098;18603 63882 16; +16877 66100 1;17539 66555;18445 66387;18900 65724 1;19355 65061;19186 64156;18524 63701 1;17861 63246;16955 63414;16501 64077 1;16046 64740;16214 65646;16877 66100 18; +17877 65443; +18338 64623; +17483 64152; +20322 68711; +21494 67588; +15556 68149 1;13818 66975;13288 64686;14396 62905 1;14938 62033;15772 61404;16690 61112 1;17726 60781;18870 60879;19825 61537 1;21391 62616;22145 64381;21417 66138 1;20508 68333;17792 69442;15717 68285; +17889 61174;19376 61797;18225 63682;18613 63909;19817 61994;20584 62809;20824 62277;20440 61754;19298 61043;17889 61174 18; +16635 60901 1;17735 60508;18535 60486;19627 60901 1;19918 60376;19824 60001;19811 59401 16; +21700 63605 1;21395 62652;21081 62041;20378 61329 1;21043 60365;21416 59798;22293 59017 1;23634 59436;24431 59528;25826 59659 1;26973 59767;27623 59547;28762 59375 16; +16582 61093;16693 60880 1;17763 60507;18554 60493;19627 60901 1;19918 60376;19824 60001;19811 59401;19819 57962;20431 58058 1;20415 58831;20399 59292;20379 60151;21469 58721;22293 59017 1;21416 59798;21043 60365;20378 61329 1;21081 62041;21373 62583;21678 63536 1;21705 63619;21509 63691;21468 63614;20386 61844;19295 61181;17463 60945;16582 61093 18; +21139 67064 1;21365 66753;21793 66529;22171 66461; +21049 66954;21234 66947;21517 66707;21751 66577;22079 66420;21326 66435;21049 66954 18; +20661 67045 1;20303 66908;20254 66532;20425 66208; +20415 66213;21043 66440;20642 67029;20552 66990 1;20305 66830;20275 66521;20408 66242;20415 66213 18; +16020 68222;16639 68453;16662 68361 1;16804 68043;16705 67723;16388 67565;16020 68222 18; +15025 67427;15352 67746;15509 67868;15871 67266;15791 67201 1;15525 67029;15264 67108;15080 67315;15025 67427 18; +14424 63246;14223 63721;14127 64057;14215 64074 1;14590 64149;14983 64003;15120 63644;14424 63246 18; +12521 61096 1;12617 60465;12683 60113;12804 59486; +19804 59410;19817 57966;16230 57423 16; +28909 58282;28830 58919;28699 58959 1;28345 59000;28026 59041;27725 59075;27680 58978 1;27405 58712;27100 58697;26699 58641 1;26245 58578;25976 58782;25527 58690 1;25161 58615;24957 58563;24590 58493 1;24074 58394;23792 58689;23270 58752;23024 58715 1;21934 58478;21163 58084;20508 57031;20534 56982;28909 58282 18; +28909 58282;28830 58919;28699 58959 1;28345 59000;28026 59041;27725 59075;27680 58978 1;27405 58712;27100 58697;26699 58641 1;26245 58578;25976 58782;25527 58690 1;25161 58615;24957 58563;24590 58493 1;24074 58394;23792 58689;23270 58752;23024 58715 1;21934 58478;21163 58084;20508 57031;20534 56982;28909 58282 18; +28761 59381;28734 59686;27982 59928;26556 60343 32;26204 62673;25841 62759;24737 62575;23541 63229;23553 64771;25740 65102;26015 64975;28386 65333;28734 65426;28692 65915;28325 65906;24565 65514;23998 65531;23440 65610;22943 65697;22419 65802;21695 65924;21783 65697;21844 64694;21111 62740;20265 61309;21477 59669;21642 59756 1;21921 59913;22111 60023;22443 60023 1;22927 60023;23192 59734;23479 59345;23619 59367 1;24307 59510;24948 59577;25826 59659 1;26861 59756;27492 59587;28442 59426;28761 59381 18; +28764 59373;28832 58948;28386 58996 1;27196 59139;26387 59259;25098 59067 1;24344 58955;23920 58895;23173 58746 1;22009 58513;21200 58135;20518 57046 1;20514 57041;20496 57001;20493 56996;19447 56846;19410 57105;13582 56215;13277 56930;16234 57410;18513 57786;19817 57978;20436 58069;20520 58133 1;20838 58394;21078 58516;21469 58721;21444 58754;22261 59046 1;22271 59036;22282 59027;22293 59017 1;22970 59228;23508 59357;24054 59448;24314 59490 1;24769 59557;25246 59605;25826 59659 1;26769 59747;27376 59615;28195 59469;28764 59373 18; +13071 58077;12775 59508;15718 60002;17309 58719;13071 58077 18; +19629 60887;19740 60714;19833 60467;19858 60159;19821 59400;19529 59361;18484 59175 1;18172 59117;17961 59220;17707 59410 1;17236 59764;17001 60000;16535 60360 1;16105 60693;15768 60901;15227 60841 1;14227 60730;13656 60678;12686 60409;12635 60391;12524 61082;12901 61497;13420 61907;14162 62430;14397 62557;14697 62163;15437 61496;16461 60954;17368 60695;18231 60627;19040 60707;19629 60887 18; +19101 65065;20613 65632;20884 64602;20452 63214;19552 62524;18700 63745;19034 64417;19101 65065 18; +16696 63782;17448 63406;18367 63548;19058 62326;18633 62042;17510 61746;16548 61987;15740 62573;15419 63073;16696 63782 18; +15133 63638;16379 64310;16607 65914;15849 67277;15779 67193 1;15487 67012;15201 67133;15019 67392;14658 66969;14152 66210;13937 64872;14066 64051;14228 64077 1;14538 64134;14857 64042;15033 63804;15133 63638 18; +16402 67553;17149 66239;18944 65641;20418 66227;20374 66324 1;20274 66605;20339 66899;20624 67029;20215 67751;18845 68583;17291 68713;16618 68479;16659 68372;16662 68361 1;16787 68080;16725 67798;16490 67627;16402 67553 18; +12453 66111;13983 66043; +24592 66129;24311 68912;22502 70401;22498 71162; +24156 67263 1;23755 67315;23346 67513;23353 67917 1;23360 68315;23538 68560;23850 68807; +24452 67280;24156 67263 1;23755 67315;23346 67513;23353 67917 1;23360 68315;23538 68560;23850 68807;24176 69023;24311 68912;24432 67716;24452 67280 18; +22996 70735;24688 69487;24906 67228;25220 67263;25072 69592;23423 70813;22996 70735 18; +24802 66434;24831 66144; +22503 71180;23066 70700;24679 69487;24915 67245;24740 67228;24793 66434;24798 66180;24814 66069;24637 66091;24615 66187;24302 68911;22492 70417;22503 71180 18; +18985 70625;21658 70945;22093 71488; +18966 70947;19752 71027; +20335 71080;21116 71166; +19042 70479 1;19850 70091;20571 69845;21407 69448 1;21630 69342;21823 69287;22036 69411;22215 69516;22135 70583; +22555 71151;21943 70388;21134 70299;21078 70811; +21219 70838;21699 70881;22187 71498;22477 71226;21947 70418;21243 70388;21219 70838 18; +21818 69795; +20188 70384; +19087 70516;20942 70702;20998 70115;22003 70227;22120 70387;22159 70258;22215 69516;22036 69411 1;21823 69287;21632 69346;21407 69448 1;20583 69821;19915 70069;19121 70460;19087 70516 18; +22589 69888; +18626 71270;17894 68708; +19022 70471;18688 70431; +18646 70453;19034 70496;18960 71353;18368 71285;18436 70681;18646 70453 18; +18940 71358;21906 71681;22007 71624;21562 71036;19001 70787;18940 71358 18; +18076 68820;18608 70478;19044 70521;19271 70387 1;20000 70037;20635 69797;21407 69448 1;21632 69346;21823 69287;22036 69411;22215 69516;22159 70258;22120 70387;22525 70896;22500 70722;22502 70401;24188 69013;24042 68934;23850 68807 1;23538 68560;23360 68315;23353 67917 1;23346 67513;23755 67315;24156 67263;24452 67280;24636 66072;22857 66160;21339 66578;20484 67651;18983 68585;18076 68820 18; +13209 71829;15329 68218;13880 66325;12467 66412;12485 66534 1;12610 67976;12828 69385;13101 71112;13209 71829 18; +12431 65794;13783 65759;14098 63055;12423 61833;12413 61974 1;12354 63228;12354 64307;12403 65326;12431 65794 18; +21191 65327 1;20829 65279;20512 65207;20179 65082 1;19555 64848;20065 63831;19211 63277; +15328 62465 1;15701 62701;15848 62948;16279 63041 1;16914 63179;17358 62303;18495 62901; +17910 62799; +19572 63766; +29708 52233 1;29487 52197;29278 52347;29241 52568 1;29204 52789;29354 52999;29576 53035 1;29797 53072;30006 52922;30043 52701 1;30080 52479;29930 52270;29708 52233 18; +29640 52634; +27802 52372; +25964 52110; +24125 51848; +22287 51586; +27867 51967 1;27646 51931;27437 52081;27400 52302 1;27363 52523;27513 52733;27735 52769 1;27956 52806;28165 52656;28202 52435 1;28239 52213;28089 52004;27867 51967 18; +26029 51705 1;25808 51669;25599 51819;25562 52040 1;25525 52261;25675 52471;25897 52507 1;26118 52544;26327 52394;26364 52173 1;26401 51951;26251 51742;26029 51705 18; +24191 51446 1;23970 51410;23761 51560;23724 51781 1;23687 52002;23837 52212;24059 52248 1;24280 52285;24489 52135;24526 51914 1;24563 51692;24413 51483;24191 51446 18; +22353 51187 1;22132 51151;21923 51301;21886 51522 1;21849 51743;21999 51953;22221 51989 1;22442 52026;22651 51876;22688 51655 1;22725 51433;22575 51224;22353 51187 18; +18636 50480 1;18415 50444;18206 50594;18169 50815 1;18132 51036;18282 51246;18504 51282 1;18725 51319;18934 51169;18971 50948 1;19008 50726;18858 50517;18636 50480 18; +18570 50879; +16648 50555; +16714 50156 1;16493 50120;16284 50270;16247 50491 1;16210 50712;16360 50922;16582 50958 1;16803 50995;17012 50845;17049 50624 1;17086 50402;16936 50193;16714 50156 18; +15642 48243; +16740 45874; +17776 43703; +21328 49367;22212 49389; +21642 48941 1;21410 49009;21277 49253;21345 49485 1;21413 49718;21657 49851;21889 49783 1;22122 49714;22255 49471;22187 49238 1;22118 49006;21875 48873;21642 48941 18; +21777 49799;21777 48941; +31053 56194;29745 56008;29647 56655;30959 56855;31202 56893 32;31305 56236 32;31053 56196 32;31053 56194 18; +15269 51817;14966 53695;15968 53853;16207 52514;29511 54595;29352 55593;29204 55603;29154 55895;29299 55928;29197 56571;29068 56557;28994 56829;29149 56870;28919 58319;30685 58597 32;30959 56855;29647 56655;29745 56008;31053 56194;31343 54350 32;15269 51817 18; +28924 58287;29151 56857;29001 56834;29040 56552;29197 56570;29301 55916;29156 55891;29202 55596;29348 55619;29511 54595;16212 52492 32;15996 53860 32;14756 53664 32;14655 54306 32;14890 54343 32;14624 56035 32;19428 56792;19518 56311;20573 56477;20504 56961;28924 58287 18; +13110 51834;13893 49891;14880 50114;18409 42613;17750 42303;19088 40077 16; +14438 50028;18014 42428; +12413 57281;13437 53630; +11809 57794 1;11580 57740;11459 57715;11243 57668 1;11052 57627;10940 57420;10995 57232 1;11375 55932;11629 55215;12080 53938 1;12351 53173;12558 52729;12907 51996 1;12957 51891;13036 51791;13141 51841 1;13229 51883;13194 51992;13178 52088;12876 54013;11809 57794 18; +12753 52887; +12235 54750; +11643 56811; +11809 57794 1;11580 57740;11459 57715;11243 57668 1;11052 57627;10940 57420;10995 57232 1;11375 55932;11629 55215;12080 53938 1;12351 53173;12558 52729;12907 51996 1;12957 51891;13036 51791;13141 51841 1;13229 51883;13194 51992;13178 52088;12876 54013;11809 57794 18; +11809 57794 1;11580 57740;11459 57715;11243 57668 1;11052 57627;10940 57420;10995 57232 1;11375 55932;11629 55215;12080 53938 1;12351 53173;12558 52729;12907 51996 1;12957 51891;13036 51791;13141 51841 1;13229 51883;13194 51992;13178 52088;12876 54013;11809 57794 18; +12469 57270;13228 57411; +12598 56999;13276 57122; +11811 57821;13085 58082;13320 56931;13617 56207;14053 54549;14847 54697;14890 54392;14655 54305;14760 53668;14986 53668;15283 51819;31398 54369;32013 51363;22204 49339 1;22214 49538;22088 49724;21889 49783 1;21657 49851;21413 49718;21345 49485 1;21317 49390;21323 49292;21356 49206;18409 42613;17980 42411;17941 42583;14438 50028;13881 49895;13123 51844;13191 51895 1;13210 51948;13189 52021;13178 52088;12876 54013;11892 57500;11811 57821 18; +11740 59432 1;11467 59390;11313 59368;11040 59326 1;10772 59285;10542 59496;10501 59764;11640 59938;11740 59432 18; +10214 62912 32;11304 62956 32;11326 62405 32;10236 62361 32;10214 62912 50; +10273 66023 32;11399 65935 32;11355 65366 32;10229 65454 32;10273 66023 50; +10371 67283 32;11497 67195 32;11453 66626 32;10327 66714 32;10371 67283 50; +7595 67542 32;8721 67454 32;8677 66885 32;7551 66973 32;7595 67542 50; +7497 66282 32;8623 66194 32;8579 65625 32;7453 65713 32;7497 66282 50; +7418 64121 32;8469 64112 32;8464 63520 32;7413 63529 32;7418 64121 50; +8015 58374;8558 58466 1;8829 58512;8942 58766;8965 59040;7947 58876;8015 58374 18; +8350 56931 1;8546 56971;8644 56991;8832 57031 1;9117 57091;9406 56926;9467 56641;9546 56273;8546 56058;8350 56931 18; +8890 54923 1;9345 55042;9939 55121;10120 54687;11193 51931;10042 51747 1;9536 52924;9219 53592;8890 54923 18; +9529 54297; +8890 54923 1;9345 55042;9939 55121;10120 54687;11193 51931;10042 51747 1;9536 52924;9219 53592;8890 54923 18; +8890 54923 1;9345 55042;9939 55121;10120 54687;11193 51931;10042 51747 1;9536 52924;9219 53592;8890 54923 18; +10801 50399 1;11121 50440;11473 50483;11793 50529;11885 50353 1;11945 50238;11841 50114;11721 50065;11054 49789;10801 50399 18; +12301 47242 32;13121 47643 32;13354 47166 32;12534 46765 32;12301 47242 50; +13630 44523 32;14450 44924 32;14683 44447 32;13863 44046 32;13630 44523 50; +15149 41439;15978 41884 1;16088 41650;16024 41369;15795 41247;15364 41018;15149 41439 18; +16117 39536;16335 39100;17113 39489 1;16997 39720;16715 39808;16484 39693;16117 39536 18; +7544 61667 32;8531 61753 32;8578 61217 32;7591 61131 32;7544 61667 50; +10799 70408 32;11909 70235 32;11817 69640 32;10706 69813 32;10799 70408 50; +8150 71012 32;9198 70876 32;9123 70291 32;8075 70427 32;8150 71012 50; +11256 72888 32;12379 72666 32;12494 73250 32;11371 73472 32;11256 72888 50; +8900 74578;9862 73925;9763 73456;8727 73715;8900 74578 18; +9973 74800 1;10090 75579;9880 76125;9369 76725;9122 75442;9973 74800 18; +8234 77021;8172 76738;7691 76540;6951 77095;7173 77317;8234 77021 18; +8234 77021;8172 76738;7691 76540;6951 77095;7173 77317;8234 77021 18; +9591 75602; +9270 73900; +11874 73110; +11269 70026; +8629 70667; +8123 67226; +7975 65943; +10788 65671; +10899 66967; +7939 63833; +10764 62661; +8037 61465; +11171 59639; +8420 58677; +8938 56641; +10214 62912 32;11304 62956 32;11326 62405 32;10236 62361 32;10214 62912 50; +8015 58374;8558 58466 1;8829 58512;8942 58766;8965 59040;7947 58876;8015 58374 18; +7595 67542 32;8721 67454 32;8677 66885 32;7551 66973 32;7595 67542 50; +8150 71012 32;9198 70876 32;9123 70291 32;8075 70427 32;8150 71012 50; +11740 59432 1;11467 59390;11313 59368;11040 59326 1;10772 59285;10542 59496;10501 59764;11640 59938;11740 59432 18; +7544 61667 32;8531 61753 32;8578 61217 32;7591 61131 32;7544 61667 50; +8900 74578;9862 73925;9763 73456;8727 73715;8900 74578 18; +7418 64121 32;8469 64112 32;8464 63520 32;7413 63529 32;7418 64121 50; +10273 66023 32;11399 65935 32;11355 65366 32;10229 65454 32;10273 66023 50; +8350 56931 1;8546 56971;8644 56991;8832 57031 1;9117 57091;9406 56926;9467 56641;9546 56273;8546 56058;8350 56931 18; +11256 72888 32;12379 72666 32;12494 73250 32;11371 73472 32;11256 72888 50; +9973 74800 1;10090 75579;9880 76125;9369 76725;9122 75442;9973 74800 18; +10799 70408 32;11909 70235 32;11817 69640 32;10706 69813 32;10799 70408 50; +10371 67283 32;11497 67195 32;11453 66626 32;10327 66714 32;10371 67283 50; +7497 66282 32;8623 66194 32;8579 65625 32;7453 65713 32;7497 66282 50; +12251 72681 1;12080 71734;11963 71206;11814 70255; +11709 69653 1;11576 68704;11505 68171;11395 67219; +11231 65374 1;11148 64425;11059 63880;11107 62932; +11156 62377 1;11215 61403;11277 60855;11464 59897; +7700 61117 1;7761 60241;7908 59762;8058 58896; +7626 61660 1;7586 62378;7494 62779;7503 63498; +7491 64115 1;7508 64727;7516 65071;7552 65682; +7676 67521 1;7809 68655;7860 69302;8145 70408; +8243 70987 1;8419 72049;8559 72637;8724 73701; +6977 51467 1;8839 52036;9988 52514;11165 54066 33;11801 54905;12147 55481;12543 56456;12595 56648;12543 56980;13346 57119; +14619 60103 1;14761 60546;15167 60696;15631 60731 1;16399 60789;16971 60276;17741 60276 1;18239 60276;18378 60760;18440 61254; +11182 54066; +18392 60856; +6008 56653;3143 56295 32;2976 57631 32;5841 57989 48; +6200 58320;6915 58407 1;6553 60578;6524 61895;6469 64095; +5404 56575;5245 57848; +4201 56506;4050 57712; +5413 57271; +4236 57114; +5961 56653 32;5794 57990 32;3005 57641 32;3172 56304 32;5961 56653 50; +6502 65933;6600 67450; +6695 54827 32;7583 54938 32;7658 54335 32;6770 54224 32;6695 54827 50; +7890 53848 32;8282 50848 32;8916 50931 32;8524 53931 32;7890 53848 50; +3806 70539; +3143 69580; +2340 72302; +6056 72511; +6999 71517; +910 70906 1;664 70737;328 70800;160 71046 1;-9 71291;54 71627;299 71796 1;545 71964;881 71902;1050 71656 1;1218 71410;1156 71075;910 70906 18; +910 70906 1;664 70737;328 70800;160 71046 1;-9 71291;54 71627;299 71796 1;545 71964;881 71902;1050 71656 1;1218 71410;1156 71075;910 70906 18; +6705 67411 1;6879 69327;7415 71897;7964 74690;6991 74534;7136 73632;5649 73393;5381 75064;4675 74950 16; +6038 76435 32;6576 76525 32;6662 76013 32;6124 75923 32;6038 76435 50; +3570 75319 32;4546 75483 32;4633 74964 32;3657 74800 32;3570 75319 50; +2793 75982 32;3331 76072 32;3417 75560 32;2879 75470 32;2793 75982 50; +3648 77317 32;4186 77407 32;4272 76895 32;3734 76805 32;3648 77317 50; +4075 76541 32;4613 76631 32;4699 76119 32;4161 76029 32;4075 76541 50; +5061 76026 32;5599 76116 32;5685 75604 32;5147 75514 32;5061 76026 50; +1615 74944 32;2153 75034 32;2239 74522 32;1701 74432 32;1615 74944 50; +2021 76714; +1898 77566; +5151 74718; +5377 75817; +6346 76218; +4392 76314; +3964 77117; +3092 75765; +1932 74735; +-2415 73726;3648 74755; +-165 74110; +-993 77896; +3940 72946;7714 73587; +7157 73590;6996 74540;7940 74688;7841 74004;7730 73498;7157 73590 18; +2247 74505 1;2331 74119;1994 73722;1602 73668 1;1192 73612;906 73788;494 73755 1;-295 73692;-702 73600;-1487 73502 1;-1703 73475;-1882 73595;-1958 73799;2247 74505 18; +2237 74251 1;2178 73959;1906 73710;1602 73668 1;1225 73616;952 73761;590 73759 33;559 73759;527 73758;494 73755 1;227 73734;4 73709;-205 73682 33;-440 73651;-736 73609;-978 73573 33;-1155 73547;-1267 73529;-1487 73502 1;-1587 73490;-1696 73525;-1773 73569; +-2696 76398 32;-2458 74740 32;-1872 74824 32;-2110 76482 32;-2696 76398 50; +-6564 77359; +-2381 78104;-2147 76472; +-2905 77403;-3002 78073;-14342 77354;-14835 77761;-16032 77675;-16427 77182;-16253 75527;-11011 76256; +-14343 77328 1;-13803 77017;-13459 76749;-12838 76797 1;-12453 76827;-12237 76843;-11852 76873 1;-11430 76906;-11039 76670;-10962 76254; +-14861 77735;-14602 75724;-10950 76230;-11030 76456 1;-11178 76745;-11504 76900;-11852 76873 1;-12237 76843;-12453 76827;-12838 76797 1;-13459 76749;-13803 77017;-14343 77328;-14861 77735 18; +-16266 75489;-14574 75733;-14618 75850;-14859 77721;-14941 77753;-16032 77675;-16427 77182;-16274 75731;-16266 75489 18; +-16035 77669 1;-16444 77640;-16792 77299;-16741 76892;-16569 75438; +-16308 77096;-15916 77549;-14887 77611;-14389 77210; +-14375 77341;-3025 78069;-2902 77415;-10982 76243;-11003 76380;-11030 76456 1;-11178 76745;-11504 76900;-11852 76873 1;-12237 76843;-12453 76827;-12838 76797 1;-13438 76751;-13779 76999;-14288 77296;-14375 77341 18; +-14375 77341;-3025 78069;-2902 77415;-10982 76243;-11003 76380;-11030 76456 1;-11178 76745;-11504 76900;-11852 76873 1;-12237 76843;-12453 76827;-12838 76797 1;-13438 76751;-13779 76999;-14288 77296;-14375 77341 18; +-29859 60374;-31107 69630;-31354 71627 1;-31512 72856;-30780 74572;-29347 75500 1;-27197 76892;-25507 76772;-22997 77280 1;-20895 77706;-19710 78030;-17568 78145;-17028 78174;-14771 78297;-14302 77878;-3064 78621 1;-100 78818;1815 78174;4814 77529;5031 77735;5240 77689;5327 77421;6958 77113 16; +8235 77011;9364 76746; +-2993 78060;-3055 78622 1;-142 78814;1775 78192;4813 77546;5045 77730;5264 77702;5327 77421;6951 77095;7691 76540;8172 76738;8234 77021;8456 76959;9364 76746;9346 76603;9122 75442;9995 74778;9869 73896;8900 74578;8732 73741;7723 73469 1;7802 73870;7883 74278;7964 74690;6991 74534;7136 73632;5649 73393;5381 75064;4675 74950;-2388 73717;-2536 74753;-2316 74760;-1872 74824 32;-2110 76482 32;-2696 76398 32;-2993 78060 18; +10060 54782;9528 56283; +13101 58090;11793 57819;11569 57739 1;11466 57716;11371 57696;11243 57668 1;11052 57627;10940 57420;10995 57232 1;11375 55932;11629 55215;12080 53938 1;12321 53257;12512 52831;12797 52228;11182 51960;10120 54687 1;10101 54733;10077 54773;10049 54808;10006 54935;9528 56283;9515 56416;9467 56641 1;9406 56926;9117 57091;8832 57031 1;8644 56991;8546 56971;8350 56931;7410 56793;6003 56522;5793 58181;6242 58210;7026 58303;8026 58373;8280 58419;8558 58466 1;8829 58512;8942 58766;8965 59040;8813 59016;8054 58893;8037 59012 1;7895 59807;7758 60281;7700 61117;7722 61142;8578 61217 32;8531 61753 32;7634 61675;7619 61781 1;7576 62425;7495 62820;7503 63498;7555 63528;8464 63520 32;8469 64112 32;7502 64120;7495 64258 1;7509 64784;7519 65120;7552 65682;7625 65700;8579 65625 32;8623 66194;8679 66914;8721 67454 32;7699 67534;7683 67583 1;7811 68679;7865 69322;8145 70408;8273 70401;9123 70291 32;9198 70876 32;8253 70999;8268 71138 1;8433 72107;8567 72689;8724 73701;8822 73691;9763 73456;9862 73925;11377 73500;11256 72888 32;12237 72694;12229 72560 1;12070 71687;11957 71165;11814 70255;11587 70285;10799 70408 32;10706 69813 32;11714 69656;11698 69574 1;11572 68674;11502 68144;11395 67219;11306 67210;10371 67283 32;10301 66705;10258 66024;10229 65454 32;11236 65375;11207 65114 1;11133 64321;11064 63793;11107 62941;10976 62943;10214 62912 32;10236 62361 32;11157 62398;11162 62283 1;11219 61367;11283 60824;11464 59897;11328 59890;10501 59764 1;10542 59496;10772 59285;11040 59326 1;11313 59368;11467 59390;11740 59432;11739 59436;12832 59508;13101 58090 18; +12802 59496;11747 59410;11719 59538;11640 59938;11450 59909;11437 60036 1;11277 60880;11216 61414;11162 62283;11157 62398;11217 62401;11326 62405 32;11304 62956 32;11099 62948;11101 63073 1;11071 63849;11137 64363;11207 65114;11236 65375;11229 65376;11253 65374;11355 65366 32;11399 65935 32;10242 66036 32;10292 66713;10396 66709;11453 66626 32;11497 67195 32;11410 67202;11429 67507 1;11521 68288;11590 68804;11709 69653;11781 69646;11817 69640 32;11909 70235 32;11809 70251;11827 70340 1;11970 71238;12085 71763;12251 72681;12320 72678;12379 72666 32;12493 73247;13379 72894;13221 71947;13187 71815;13139 71347 1;12803 69232;12562 67591;12448 65832 1;12447 65822;12432 65823;12431 65813 1;12417 65598;12405 65380;12396 65159;12375 64491 1;12358 63691;12339 62838;12386 61885;12493 61087 1;12570 60581;12656 60263;12738 59830;12802 59496 18; +13219 71853 1;12881 69722;12612 68074;12472 66385 16; +12432 65830 1;12355 64639;12343 63387;12417 61885 16; +-16256 75510;-16562 75458;-16584 75565;-16741 76892 1;-16791 77295;-16451 77633;-16047 77668;-16137 77544;-16427 77182;-16263 75621;-16256 75510 18; +-16056 77665;-16065 78215;-15933 78234;-14771 78297;-14302 77878;-11554 78060;-3004 78636;-2930 78056;-3113 78066;-14342 77354;-14835 77761;-16056 77665 18; +17578 104752;23132 105569 1;22763 105305;22461 104852;22337 104315;21900 102429;22306 101777;22215 101478;19971 91566;19244 91090;19172 90835;19114 90628;19072 90478;19007 90303;18914 89917;17055 83300;15158 83734 1;16261 87288;16745 89560;17695 93348 1;17716 93431;17737 93514;17757 93595;18069 94834 1;18687 97287;19130 99041;19768 101837 1;20006 102881;19606 103868;18644 104339 1;18333 104491;18085 104628;17818 104703;17578 104752 18; +29148 59749;32254 60211;32245 60333;35272 60595;35586 56469;35595 56286 1;35595 56282;35596 56277;35596 56272 1;35634 55753;36086 55363;36606 55401 1;36651 55404;36695 55411;36738 55420;37314 52002;31951 51351;31336 54369;31326 54367;31050 56180;31303 56233;31224 56861;30980 56878;30926 57064;30685 58597 32;29369 58390;29148 59749 18; +19018 90371;19607 90436;19672 90397;19236 89084;18573 86942;18102 87008;18835 89669;19018 90371 18; +-18490 76733;-18558 77210;-20271 76966;-20203 76485; +-18099 75226 32;-18391 77262 32;-20425 76970 32;-20133 74934 32;-18099 75226 50; +-18341 76763;-18126 75261; +-20356 76475;-20141 74973; +-20453 77083;-21518 76942;-19777 63792;-19685 61052 16; +-20053 67087;-19871 65710; +-20135 74957;-20425 77091;-21548 76950;-19773 63763;-19694 61032;-19363 58599;-18042 58800;-20135 74957 18; +-23707 74049;-24683 73918; +-30771 73235;-29833 72809; +-30071 72681;-29814 70681;-30825 70570 1;-30928 71507;-31000 72251;-30769 73000 16; +-30619 73392 1;-30539 73567;-30441 73744;-30320 73926 1;-29067 75811;-26827 76167;-24805 76418 1;-24665 76435;-24587 76446;-24447 76467 1;-24099 76520;-23710 76383;-23657 76035 1;-23563 75418;-23510 75072;-23416 74455 1;-23367 74131;-23529 73859;-23855 73827;-29796 73026 16; +-29955 73637; +-30084 72699;-29813 70688;-30837 70583;-30912 71523 1;-30940 72047;-30916 72523;-30769 73000;-30084 72699 18; +-29794 73007;-29679 73040;-23864 73819 1;-23538 73851;-23367 74131;-23416 74455 1;-23510 75072;-23563 75418;-23657 76035 1;-23710 76383;-24099 76520;-24447 76467 1;-24587 76446;-24665 76435;-24805 76418 1;-26827 76167;-29067 75811;-30320 73926 1;-30441 73744;-30539 73567;-30619 73392;-29794 73007 18; +-29590 69164 32;-29443 68070 32;-30453 67935 32;-30600 69029 32;-29590 69164 50; +-29270 66586 32;-29018 64702 32;-29991 64572 32;-30243 66456 32;-29270 66586 50; +-29554 65019; +-29813 68585; +-29270 66586 32;-29018 64702 32;-29991 64572 32;-30243 66456 32;-29270 66586 50; +-29590 69164 32;-29443 68070 32;-30453 67935 32;-30600 69029 32;-29590 69164 50; +-26371 72507 32;-25529 72620 32;-25566 72898 32;-26408 72785 32;-26371 72507 50; +-25353 64906 32;-24511 65019 32;-24548 65297 32;-25390 65184 32;-25353 64906 50; +-16042 78236;-16024 77674;-16182 77645 1;-16523 77556;-16786 77249;-16741 76892;-16569 75438;-18127 75219;-18135 75324;-18341 76763;-18460 77323;-20434 77076;-20662 77061;-21548 76950;-19773 63763;-19694 61032;-20366 60927;-20970 60848 32;-22576 65568 32;-23558 72896 32;-25577 72626;-25543 72726;-25566 72898 32;-26408 72785 32;-26371 72507 32;-26620 72486;-28440 72242 32;-27462 64943;-29018 64702 32;-29270 66586 32;-30243 66456;-30453 67935 32;-29443 68070 32;-29590 69164 32;-30631 69023;-30862 70564;-29814 70681;-30012 72225;-30029 72629;-30026 72704;-29791 72995;-29721 73010;-29595 73051;-23864 73819 1;-23538 73851;-23367 74131;-23416 74455 1;-23510 75072;-23563 75418;-23657 76035 1;-23708 76373;-24077 76512;-24417 76471;-24666 76995 1;-24145 77074;-23594 77159;-22997 77280 1;-20895 77706;-19710 78030;-17568 78145;-17028 78174;-16521 78202;-16042 78236 18; +-24609 77011;-24369 76468;-25449 76334 1;-27287 76075;-29199 75613;-30320 73926 1;-30388 73823;-30450 73722;-30504 73621;-30633 73445;-30677 73347;-30792 73057;-30808 72974;-30815 72836 1;-30921 72414;-30937 71987;-30912 71523;-30837 70583;-30581 69031;-30600 69029 32;-30453 67935;-30224 66458;-30232 66371;-29991 64572;-30421 64544;-31107 69630;-31354 71627 1;-31512 72856;-30780 74572;-29347 75500 1;-27853 76468;-26580 76705;-25100 76930;-24609 77011 18; +-22404 57890 32;-25224 57517 32;-22878 39763 32;-20057 40136 32;-20080 40309;-19762 40351 32;-19889 41318 32;-20208 41276;-20347 42328;-20028 42370 32;-20230 43908 32;-20550 43866;-20693 44944;-20372 44986 32;-20499 45953 32;-20820 45911;-20854 46166;-20533 46208 32;-20660 47175 32;-20982 47132;-21121 48184;-20799 48227 32;-21001 49765 32;-21324 49722;-21467 50800;-21143 50843 32;-21270 51810 32;-21595 51767;-21632 52050;-21307 52093 32;-21434 53060 32;-21760 53017;-21899 54069;-21573 54112 32;-21775 55650 32;-22102 55607;-22245 56685;-21917 56728 32;-22044 57695 32;-22372 57652;-22404 57890 50; +-22369 57928;-22056 57971;-19663 40159;-20032 40108; +-22369 57928;-22057 57971;-19664 40159;-20032 40108;-22369 57928 18; +-18024 53979; +-16698 43249; +-16035 38172; +-18127 58615 1;-18489 58524;-18527 58095;-18476 57725 1;-18364 56907;-18157 56471;-18075 55649 1;-18050 55403;-17921 55160;-17674 55160;-18127 58615 18; +-18383 58436 1;-18504 58255;-18511 57976;-18476 57725 1;-18364 56907;-18157 56471;-18075 55649 1;-18064 55541;-18033 55434;-17981 55348; +-17871 53214 1;-17901 53083;-17908 52944;-17897 52801;-15146 32013;-15122 31907 1;-15080 31777;-14998 31666;-14891 31594 16; +-21403 39907;-20932 36424; +-20710 58407;-22087 58225;-22048 57931; +-22027 57678;-22079 58210;-18188 58812;-13656 25400;-19175 24563;-21306 40001;-19678 40174;-22027 57678 18; +-27131 62305 32;-27303 63618 32;-26420 63733 32;-26248 62420 32;-27131 62305 50; +-26985 62329;-27158 63655; +-27349 60849; +-27065 62039;-29628 61705; +-29496 61423;-29409 60652;-26794 59850; +-27318 63569;-29736 63230;-29582 62039; +-27281 63538;-26794 59899;-29379 60682;-29724 63224;-27281 63538 18; +-27304 63634;-29816 63298 32;-29641 61993 16; +-29565 61428;-29453 60588 32;-26799 59785 48; +-24888 54877;-27359 54551;-27852 58284;-25220 57529; +-25769 55802; +-30419 64563;-29992 64563;-28988 64720;-27453 64964;-27399 64922;-25368 65194;-25369 65028;-25353 64906 32;-24511 65019 32;-24548 65297 32;-24296 65338;-22576 65568;-22498 65549;-20963 60882;-20614 58422;-22088 58212;-22045 57986;-25246 57549;-27863 58300;-29469 60620;-26756 59809;-25334 59992;-25648 62469;-26258 62391;-26263 62537;-26420 63733 32;-27203 63631;-30228 63185;-30419 64563 18; +-30243 63224;-29828 63272;-29662 61994;-29580 61392;-29370 60585;-29322 60450;-29867 60393;-30243 63224 18; +-54524 27770 1;-54957 29584;-55234 30593;-55659 32409 1;-56302 35157;-56421 36759;-56596 39576 1;-56752 42092;-56777 43536;-56399 46029 1;-56053 48311;-55710 49581;-54931 51753 1;-54115 54030;-53583 55289;-52480 57442 1;-51705 58956;-51171 59758;-50194 61150 1;-49246 62502;-48590 63177;-47411 64333 1;-45909 65805;-44928 66490;-43197 67684 1;-42685 68037;-42192 68186;-42055 68792;-41758 70109;-37274 72978 1;-36934 73188;-36758 73484;-36793 73883 1;-36863 74068;-36910 74266;-36929 74473 1;-37045 75711;-36135 76808;-34897 76924 1;-33658 77040;-32561 76130;-32445 74891 1;-32377 74152;-32673 73464;-33186 73004 1;-33205 72986;-33311 72859;-33331 72842 16; +-33370 76491 1;-33602 76164;-33807 75853;-33623 75497 1;-33408 75082;-32956 75105;-32489 75113; +-32480 74336 1;-32887 74404;-33331 74389;-33483 74005 1;-33622 73654;-33636 73367;-33548 73044 1;-33522 72948;-33323 72896;-33305 72747;-32241 64765;-33327 63371;-28007 24759;-26550 23363;-25869 18111 1;-25715 16920;-26491 15603;-27691 15634;-29681 15685;-30421 16871;-34757 16869 1;-36651 16868;-37714 16868;-39608 16867 1;-42037 16866;-43386 16497;-45775 16058;-46045 14671;-49421 13825 1;-50599 13530;-51909 14034;-52204 15212 1;-52799 17588;-53270 18883;-53905 21249 1;-54012 21648;-53906 21931;-53696 22287 1;-53564 22511;-53555 22525;-53423 22748 1;-53131 23242;-53431 23757;-53582 24311;-54524 27770 16; +-34941 73552 1;-35461 73581;-35859 74026;-35830 74546 1;-35802 75067;-35356 75465;-34836 75436 1;-34316 75407;-33917 74962;-33946 74442 1;-33975 73921;-34420 73523;-34941 73552 18; +-34217 76867 1;-34368 76435;-34637 76157;-35089 76082 1;-35558 76004;-35862 76064;-36293 76265; +-36877 75192 1;-36569 75024;-36417 74757;-36441 74407 1;-36458 74155;-36561 73998;-36764 73848; +-33076 73647; +-33187 75683; +-35173 76436; +-36703 74499; +-34956 74851; +-35756 71942; +-38124 71375; +-41480 67682; +-42340 69427; +-33535 66983; +-39813 65383;-41418 65197; +-38275 65550;-35642 65855;-35404 63803; +-38289 65716;-38443 67013;-37411 67135;-37722 69758;-38650 69648;-38847 71314;-36367 72843 1;-35318 73489;-33726 72632;-33555 71412;-32757 65717;-33457 65619;-33379 65059;-35298 64792 16; +-40755 69887;-40587 68228; +-40005 70288;-39820 68457; +-41165 69250; +-40415 67968; +-39804 65187;-38233 65366;-38290 65763;-38310 65889;-38443 67013;-37411 67135;-37722 69758;-38650 69648;-38847 71314;-40939 70046;-40945 69982;-40770 68246;-39554 67941;-39352 65949;-39857 65898;-39825 65575;-39856 65562;-39804 65187 18; +-33093 65381; +-32458 74330;-32587 74352 1;-32962 74399;-33344 74356;-33483 74005 1;-33622 73654;-33636 73367;-33548 73044 1;-33526 72963;-33380 72913;-33325 72809;-33302 72872 1;-33263 72916;-33200 72991;-33186 73004 1;-32836 73318;-32586 73739;-32485 74209;-32458 74330 18; +-32484 75124;-32597 75111 1;-33025 75104;-33425 75114;-33623 75497 1;-33807 75853;-33602 76164;-33370 76491;-33264 76427 1;-32913 76141;-32649 75750;-32520 75296;-32484 75124 18; +-34953 75446;-34909 75437 1;-34885 75438;-34861 75437;-34836 75436 1;-34316 75407;-33917 74962;-33946 74442 1;-33975 73921;-34420 73523;-34941 73552 1;-35461 73581;-35859 74026;-35830 74546 1;-35807 74969;-35509 75311;-35118 75409;-34953 75446 18; +-34220 76886;-34238 76809 1;-34393 76410;-34658 76154;-35089 76082 1;-35558 76004;-35862 76064;-36293 76265;-36216 76336 1;-35865 76660;-35409 76876;-34897 76924 1;-34700 76942;-34507 76935;-34321 76904;-34220 76886 18; +-36898 75185;-36790 75139 1;-36541 74970;-36419 74722;-36441 74407 1;-36458 74155;-36561 73998;-36764 73848;-36825 73973 1;-36877 74132;-36913 74299;-36929 74473 1;-36949 74682;-36939 74887;-36903 75084;-36898 75185 18; +-34231 76865;-34251 76778 1;-34407 76397;-34669 76152;-35089 76082 1;-35528 76009;-35822 76057;-36211 76228;-36298 76255 1;-36551 75996;-36740 75678;-36846 75326;-36877 75192 1;-36569 75024;-36417 74757;-36441 74407 1;-36456 74184;-36538 74036;-36697 73901;-36790 73841 1;-36775 73555;-36873 73323;-37063 73139;-36698 72639;-36367 72843 1;-35342 73474;-33799 72671;-33569 71496;-33551 71421;-33132 71482;-33325 72809 1;-33380 72913;-33526 72963;-33548 73044 1;-33636 73367;-33622 73654;-33483 74005 1;-33344 74356;-32962 74399;-32587 74352;-32461 74339 1;-32434 74518;-32428 74703;-32445 74890 1;-32448 74926;-32452 74960;-32457 74994;-32489 75113 1;-32956 75105;-33408 75082;-33623 75497 1;-33790 75820;-33636 76106;-33433 76401;-33357 76500 1;-33578 76661;-33828 76783;-34097 76856;-34231 76865 18;-34953 75446;-34909 75437 1;-34885 75438;-34861 75437;-34836 75436 1;-34316 75407;-33917 74962;-33946 74442 1;-33975 73921;-34420 73523;-34941 73552 1;-35461 73581;-35859 74026;-35830 74546 1;-35807 74969;-35509 75311;-35118 75409;-34953 75446 18; +-37030 73167;-36646 72670;-36878 72528;-38847 71314;-38835 71210;-40937 69898;-40949 70018;-41418 69722;-41554 68932 1;-41649 68394;-41867 68067;-42319 67760 1;-43741 66794;-44548 66256;-45859 65144 1;-46306 64765;-46679 64432;-47020 64106;-47324 64418 1;-45870 65832;-44894 66513;-43197 67684 1;-42685 68037;-42192 68186;-42055 68792;-41758 70109;-37274 72978 1;-37252 72991;-37231 73005;-37211 73020;-37030 73167 18; +-41397 85580 1;-40377 83183;-39817 81832;-38920 79386 1;-38666 78694;-38754 78071;-38864 77342;-39934 77512; +-41338 77712;-42053 77782;-42803 77677; +-39933 77348;-41382 77566; +-42762 77507;-43052 77483; +-43071 77470 32;-43001 76869 32;-43289 76835 32;-43359 77437 32;-43071 77470 50; +-41208 85517;-41919 85424 32;-41827 84720 32;-41751 84145;-44228 83819; +-44228 85084;-44087 84009; +-41988 86947 1;-42603 88438;-42948 89275;-43563 90766 1;-44027 91892;-45115 92188;-46333 92198 1;-47315 92206;-47892 91962;-48740 91466 1;-49196 91199;-49322 90871;-49595 90419;-52166 86162;-51896 83888; +-52055 89948; +-50241 92757; +-56853 94519; +-59340 91022; +-60848 90814; +-68413 89168; +-69904 88924; +-68648 85062; +-68909 86877; +-64286 87540; +-59732 88046; +-59400 85691; +-63920 85019; +-64876 94038 32;-65034 93892 1;-65152 93955;-65288 93986;-65431 93976 1;-65847 93949;-66162 93590;-66134 93174 1;-66128 93088;-66109 93007;-66077 92931;-66640 92410 32;-66313 92055 32;-65759 92566 1;-65632 92498;-65486 92461;-65332 92471 1;-64917 92498;-64602 92858;-64629 93273 1;-64635 93369;-64659 93460;-64698 93543;-64549 93683 32;-64876 94038 50; +-60472 84361;-57931 84768; +-65080 83623;-62539 84030; +-69867 84536;-67326 84943; +-70435 88842;-67894 89249; +-65920 89952;-63379 90359; +-61405 90717;-58864 91124; +-69011 89056 32;-68382 84775 32;-68825 84710 32;-69454 88991 32;-69011 89056 50; +-64467 90187 32;-63539 83868 32;-63982 83803 32;-64910 90122 32;-64467 90187 50; +-59913 90946 32;-58982 84609 32;-59425 84544 32;-60356 90881 32;-59913 90946 50; +-65406 93173; +-64876 94038 32;-65034 93892 1;-65152 93955;-65288 93986;-65431 93976 1;-65847 93949;-66162 93590;-66134 93174 1;-66128 93088;-66109 93007;-66077 92931;-66640 92410 32;-66313 92055 32;-65759 92566 1;-65632 92498;-65486 92461;-65332 92471 1;-64917 92498;-64602 92858;-64629 93273 1;-64635 93369;-64659 93460;-64698 93543;-64549 93683 32;-64876 94038 50; +-62736 94494; +-64550 94383; +-51644 86198; +-51252 85805;-51943 85717; +-51386 87114;-51232 85794;-52003 85713;-52015 86170;-51386 87114 18; +-44846 87871;-43077 88096;-43002 87509 32;-42931 86948;-41874 87082; +-52025 85715;-44610 86649;-44802 87870;-43136 88070;-42909 86875;-41845 87067;-41252 85654;-42080 85532;-41897 84171;-44244 83935;-44436 85026;-51833 84066;-52025 85715 18; +-51445 87154;-43056 88215;-42871 86956;-42057 87166;-42228 87529 1;-42703 88681;-43033 89480;-43563 90766 1;-44027 91892;-45115 92188;-46333 92198 1;-47315 92206;-47892 91962;-48740 91466 1;-49196 91199;-49322 90871;-49595 90419;-51420 87398;-51445 87154 18; +-51741 82708;-50658 74656; +-50572 74021;-49934 69287 1;-48724 70263;-48067 70794;-46821 71724;-46917 72440 16; +-51542 66781 1;-49716 68611;-47958 70192;-45671 71828;-44781 72403;-42915 71985 1;-41690 72790;-40863 73069;-39800 74079 1;-38827 75003;-38423 75775;-38139 77086 1;-37953 77952;-37779 78530;-38104 79354;-44786 95616 1;-45261 96773;-46178 97514;-47426 97429;-49444 97291 16; +-42749 92268;-43712 92225;-44445 92941;-46155 93255;-47673 93133;-49138 92557;-50307 92348;-51563 92488;-53081 93238;-53780 97739; +-53780 97739;-46923 98280;-44690 97286;-42764 92267;-43712 92225;-44445 92941;-46155 93255;-47673 93133;-49138 92557;-50307 92348;-51563 92488;-53081 93238;-53735 97450;-53780 97739 18; +-49444 97291;-60008 96439;-66307 95408 1;-66912 95289;-67263 94521;-67197 93908;-66919 91718;-66532 91789 16; +-54925 94297 1;-55057 94915;-54927 95736;-54297 95789 1;-53950 95818;-53755 95835;-53408 95864; +-57127 96661 1;-56415 96728;-56063 96149;-55351 96217 1;-54650 96284;-54256 96322;-53555 96389; +-69872 93675;-70822 94132;-73499 93836; +-69959 92984; +-73290 92479; +-92629 96005;-89125 83520; +-91750 91081;-92750 90790; +-92646 94157;-93646 93866; +-69596 94589;-80305 93899 1;-81343 93832;-81937 93808;-82946 93553;-84661 93072; +-91089 91196;-85766 92749; +-91301 91599;-86350 93034 1;-85856 92967;-85478 92618;-85426 92109;-85377 91529;-85908 91159;-85735 88951;-85118 88729 1;-84789 87113;-84507 86216;-83872 84694;-83150 82923;-82346 83216 1;-82993 84795;-82987 84420;-83527 86039 1;-83872 87075;-84070 87669;-84206 88753 1;-84382 90151;-84508 90931;-84662 92331 1;-84719 92847;-84729 93300;-84303 93628 1;-82566 94135;-81470 94340;-79599 94445 1;-76055 94643;-74055 94818;-70511 95015 1;-69832 95053;-69341 94446;-69243 93774;-68922 91578 1;-68892 91372;-69099 91238;-69305 91208;-69459 92265;-73842 91626;-73636 90214;-74087 90148;-73211 83927 16; +-91259 91601;-91128 91164;-91031 91213;-85766 92749;-85885 92867 1;-86020 92953;-86178 93011;-86350 93034;-91130 91648;-91259 91601 18; +-84679 93076;-84650 93109 1;-84600 93304;-84497 93479;-84303 93628 1;-82566 94135;-81470 94340;-79599 94445 1;-76055 94643;-74055 94818;-70511 95015 1;-70144 95036;-69833 94868;-69608 94604;-69729 94580;-80305 93899 1;-81343 93832;-81937 93808;-82946 93553;-84557 93101;-84679 93076 18; +-53213 93155 1;-53579 93222;-53791 93247;-54163 93229; +-53978 92526 1;-53096 92338;-52636 92096;-51794 91773 1;-51087 91502;-50620 91423;-49870 91526 1;-49330 91600;-49006 91593;-48500 91798; +-54257 87639; +-55664 92833;-55125 92951;-54408 90147 1;-54043 88559;-53438 87764;-53212 86150 1;-53068 85119;-53520 83686;-54549 83524;-55664 92833 18; +-55664 92833;-55125 92951;-54408 90147 1;-54043 88559;-53438 87764;-53212 86150 1;-53068 85119;-53494 83677;-54523 83515;-55664 92833 18; +-66609 91774;-64191 92243;-64352 93279;-57394 94870;-57110 93772;-55499 94157 1;-55692 94932;-56029 95757;-56813 95694;-59989 95440;-59938 94803;-61501 94677;-61556 95361;-67017 94803 16; +-54937 94276;-54465 94386;-54157 93205 16; +-38719 77164;-38135 77102 1;-37951 77959;-37781 78535;-38104 79354;-38964 81447;-39164 81933;-43376 92184;-43711 92225;-43712 92225;-44445 92941;-46155 93255;-47673 93133;-49138 92557;-50307 92348;-51563 92488;-53077 93236;-53344 93178 1;-53635 93228;-53838 93245;-54163 93229;-53952 92520 1;-53087 92333;-52628 92093;-51794 91773 1;-51087 91502;-50620 91423;-49870 91526 1;-49350 91597;-49030 91593;-48554 91776;-47989 91864 1;-47483 92092;-47002 92203;-46333 92198 1;-45115 92188;-44027 91892;-43563 90766 1;-42972 89332;-42630 88503;-42058 87116;-41179 85066 1;-40293 82978;-39751 81652;-38920 79386 1;-38745 78908;-38732 78463;-38778 77994;-38719 77164 18; +-48598 91751;-49357 90904;-52306 86167;-52350 86283;-53981 92530;-53880 92504 1;-53061 92320;-52605 92084;-51794 91773 1;-51087 91502;-50620 91423;-49870 91526 1;-49436 91585;-49141 91592;-48778 91700;-48598 91751 18; +-53565 95853;-53146 93140;-53361 93181 1;-53643 93228;-53844 93244;-54163 93229;-54228 93479;-54465 94386;-54937 94276;-54948 94423 1;-55038 95021;-54885 95740;-54297 95789 1;-54093 95806;-53941 95819;-53780 95833;-53565 95853 18; +-56906 96699;-56730 96703;-53718 96946;-53683 96377 1;-54307 96317;-54694 96280;-55351 96217 1;-55943 96161;-56286 96551;-56793 96646;-56906 96699 18; +-55683 94106;-55685 94113;-55499 94157 1;-55692 94932;-56029 95757;-56813 95694;-59989 95440;-59938 94803;-61501 94677;-61556 95361;-67017 94803;-67139 94512 1;-67197 94312;-67218 94102;-67197 93908;-66919 91718;-66532 91789;-66250 91844;-64191 92243;-64352 93279;-57394 94870;-57110 93772;-56385 93945;-55683 94106 18; +-67028 94797;-67002 94845 1;-66847 95128;-66609 95349;-66307 95408;-60008 96439;-56998 96682;-56750 96637 1;-56266 96529;-55926 96163;-55351 96217 1;-54694 96280;-54307 96317;-53683 96377;-53683 96382;-53582 95851;-53780 95833 1;-53941 95819;-54093 95806;-54297 95789 1;-54885 95740;-55038 95021;-54948 94423;-54937 94276;-55499 94157 1;-55692 94932;-56029 95757;-56813 95694;-59989 95440;-59938 94803;-61501 94677;-61556 95361;-61690 95347;-66856 94815;-67028 94797 18; +-76008 91985; +-75228 93291 1;-77983 93042;-79508 92722;-82259 92437 1;-82707 92391;-82900 92125;-83236 91826 1;-83621 91483;-83864 91305;-84161 90884 1;-84286 90706;-83965 90330;-83794 90465 1;-83324 90835;-83144 91143;-82660 91494 1;-82119 91885;-81664 91821;-81003 91913 1;-78781 92222;-77521 92289;-75298 92594 1;-75092 92622;-74883 92736;-74896 92943 1;-74908 93130;-75041 93308;-75228 93291 18; +-83550 87168; +-82084 87011; +-84034 76532; +-86146 83421; +-89235 84099;-87498 84587; +-87668 85103;-80421 61579;-76374 47910;-71316 30194;-65172 8505;-59447 -11679;-54759 -28605;-49800 -48418;-48147 -55326;-47228 -58797;-46252 -61219; +-45691 -63260;-44075 -71981; +-89524 91166;-89436 90850 32;-88781 91033 32;-88869 91349 48; +-87309 91382; +-91382 91752;-88956 92433; +-91820 93629;-88652 94518; +-77361 51461;-72728 52857 32;-66987 62087 32;-76551 73000 32;-83003 70224; +-73679 61705 1;-72733 62257;-72446 62426;-71500 62978 1;-71121 63199;-70664 63226;-70381 62891 1;-70030 62476;-69834 62243;-69483 61828 1;-69153 61437;-69266 60969;-69509 60519 1;-70151 59329;-70512 58663;-71154 57473 1;-71522 56791;-72953 56747;-73138 57500 1;-73450 58770;-73625 59481;-73937 60751 1;-74013 61060;-73954 61545;-73679 61705 18; +-73749 63973 1;-74330 63689;-75223 63675;-75371 64304 1;-75547 65051;-75645 65469;-75821 66216 1;-75973 66862;-75818 67499;-75232 67811 1;-74678 68106;-74170 68178;-73627 67864 1;-72699 67328;-72322 66809;-71585 66032 1;-71290 65721;-71596 65214;-71969 65002 1;-72667 64605;-73028 64326;-73749 63973 18; +-72423 62350; +-70381 58826; +-73138 64217; +-75214 67794; +-77361 51461;-72728 52857 32;-66987 62087 32;-76551 73000 32;-83003 70224;-77361 51461 18; +-72175 69963;-73717 69756; +-83163 82922;-78743 72220;-76535 73231 1;-78128 75477;-79286 76599;-80458 79091;-82328 83203;-83163 82922 18; +-90079 88255;-89703 86917; +-90079 88255;-89703 86917; +-88051 84594 1;-88146 84922;-88093 85321;-87767 85420 1;-87472 85510;-87140 85382;-87051 85087 1;-85960 81467;-85242 79471;-84130 75857;-76711 51895 1;-76127 49978;-75343 48702;-73440 48088 1;-72567 47806;-71873 48673;-71417 49469 1;-70364 51308;-69639 52258;-68542 54071 1;-67722 55426;-67248 56178;-66387 57508 1;-64778 59994;-63890 61403;-62113 63772 16; +-88582 84391 1;-88626 85039;-88509 85871;-87881 86035 1;-87435 86152;-86698 85916;-86492 85504;-75934 52024 1;-75535 50760;-74867 50033;-73560 49687 1;-72236 49336;-71651 50921;-70891 52060 1;-69371 54340;-68622 55622;-67158 57939 1;-65593 60416;-64703 61832;-62936 64151 16; +-89594 85560 1;-88921 85805;-88640 86606;-87954 86812 1;-87269 87017;-86197 86495;-85931 85831;-75105 52329 1;-74917 51748;-74414 51220;-73931 50938 1;-72629 50178;-71668 51968;-71085 52927 1;-69643 55302;-68950 56703;-67471 59055 1;-66102 61233;-65312 62453;-63685 64446 16; +-90286 88098 1;-89148 88430;-88463 88559;-87322 88882 1;-86567 89096;-85929 88658;-85540 87976 1;-84303 85808;-84142 84329;-83316 81974;-73359 52712 16; +-89951 86854 1;-89611 86949;-89413 86978;-89078 87090 1;-88542 87269;-87299 87760;-86442 87043; +-90957 90399 1;-90070 90648;-89571 90781;-88687 91040 1;-87875 91278;-87409 91373;-86590 91583 1;-85333 91905;-83972 91508;-83530 90288 1;-83261 89544;-83213 89093;-83073 88314 1;-82961 87693;-82236 87484;-81618 87611 1;-80736 87792;-80256 87959;-79372 88129 1;-78850 88229;-78956 88932;-79056 89454 1;-79311 90784;-79449 91785;-79597 92840 1;-79704 93605;-79273 94424;-79437 95393 1;-79607 96396;-80316 97000;-80974 97776; +-79461 95405; +-80729 87797; +-76733 81155;-78879 79575; +-78619 78786;-78718 78763 1;-79068 78598;-79173 78168;-79070 77778 1;-78889 77094;-78851 76584;-78302 76138 1;-78009 75900;-77815 75663;-77458 75664;-77522 75782 1;-77827 76138;-78000 76485;-78268 77045 1;-78511 77552;-78538 77936;-78600 78459 1;-78605 78504;-78610 78549;-78614 78592;-78619 78786 18; +-76744 81032 1;-77115 80647;-77413 80327;-77336 79798 1;-77243 79158;-77119 78552;-76337 78601;-76744 81032 18; +-76914 89342;-76937 89326 1;-77062 85543;-80322 84664;-81651 84424 1;-82231 84319;-82633 84072;-82371 83322;-82262 83085 1;-81990 82816;-81640 82809;-81391 82471 1;-80918 81829;-80883 81311;-80361 80709 1;-79927 80208;-79515 80114;-79000 79697 1;-78605 79377;-78660 78964;-78600 78459 1;-78538 77936;-78511 77552;-78268 77045 1;-77908 76293;-77720 75925;-77152 75405 1;-76935 75206;-76718 75166;-76426 75202 1;-76055 75247;-75697 75534;-75772 75900;-76357 78600 1;-77121 78563;-77244 79164;-77336 79798 1;-77413 80327;-77115 80647;-76744 81032;-76749 81081;-76767 81142 1;-76805 81483;-76862 81672;-76862 82015 1;-76862 82279;-76719 82415;-76539 82608 1;-76153 83020;-76020 83323;-75641 83742 1;-75370 84042;-75423 84361;-75467 84763 1;-75654 86483;-75658 87491;-75729 89220 1;-75735 89358;-75992 89465;-76130 89465 1;-76375 89465;-76513 89465;-76758 89465 1;-76836 89465;-76911 89420;-76914 89342 18; +-73788 82631;-73926 83656;-73152 83760; +-73030 83284 32;-71684 83464 32;-71609 82901 32;-72954 82721 32;-73030 83284 50; +-71762 82901;-72199 86143; +-84331 89762 1;-84220 89970;-84106 90113;-83875 90157 1;-83692 90192;-83479 90164;-83430 89984;-83314 89584 1;-83216 89186;-83164 88819;-83073 88314 1;-83038 88120;-82943 87966;-82810 87850 1;-82775 87820;-82832 87769;-82875 87751 1;-83280 87580;-83836 87408;-84059 87787 1;-84155 88231;-84160 88384;-84206 88753 1;-84247 89078;-84285 89370;-84322 89644;-84331 89762 18; +-78965 88982 1;-78773 89336;-78812 89758;-78422 89858 1;-78194 89916;-77978 89673;-77990 89438 1;-78002 89205;-78013 89071;-78077 88846;-78129 88666 1;-78320 87576;-78215 87613;-79039 86692 1;-79751 85897;-82597 84618;-83464 85784;-83497 85950 1;-83507 85979;-83517 86009;-83527 86039 1;-83758 86731;-83922 87226;-84047 87802;-83923 87641 1;-83656 87465;-83213 87608;-82875 87751 1;-82855 87759;-82832 87775;-82817 87792;-82696 87765 1;-82403 87580;-81991 87534;-81618 87611 1;-80736 87792;-80256 87959;-79372 88129 1;-79032 88194;-78958 88515;-78976 88875;-78965 88982 18; +-79324 90777 1;-79259 90404;-79414 90066;-79669 90021 1;-79925 89977;-80184 90244;-80249 90617 1;-80314 90990;-80159 91328;-79903 91372 1;-79648 91416;-79388 91150;-79324 90777 18; +-73619 84308 1;-74312 85219;-75022 85695;-75102 86837 1;-75245 88878;-75236 90027;-75294 92072 1;-75304 92425;-75640 92669;-75992 92647 1;-76995 92584;-77209 92532;-78208 92421 1;-78751 92361;-78993 91913;-78905 91374; +-75219 89202; +-75582 86052;-74958 86150 1;-75033 86354;-75084 86578;-75102 86837 1;-75245 88878;-75236 90027;-75294 92072 1;-75300 92279;-75418 92449;-75586 92549;-75704 92540 1;-77682 92280;-78920 92203;-81003 91913 1;-81186 91888;-81353 91874;-81511 91860;-81812 91826 1;-82100 91784;-82367 91705;-82660 91494 1;-83144 91143;-83324 90835;-83794 90465 1;-83917 90368;-84118 90535;-84174 90703;-84294 89829 1;-84193 90001;-84080 90118;-83875 90157 1;-83692 90192;-83479 90164;-83430 89984;-83314 89584 1;-83216 89186;-83164 88819;-83073 88314 1;-83038 88120;-82943 87966;-82810 87850 1;-82806 87846;-82803 87842;-82801 87838;-82585 87704 1;-82305 87571;-81946 87544;-81618 87611 1;-80736 87792;-80256 87959;-79372 88129 1;-78953 88209;-78939 88679;-79001 89131;-78859 89240 1;-78767 89518;-78719 89782;-78422 89858 1;-78194 89916;-77978 89673;-77990 89438 1;-78002 89205;-78013 89071;-78077 88846;-78129 88666 1;-78320 87576;-78215 87613;-79039 86692 1;-79751 85897;-82597 84618;-83464 85784;-83378 85604 1;-83020 84585;-82941 84616;-82501 83586;-82449 83661 1;-82492 84151;-82133 84337;-81651 84424 1;-80322 84664;-77062 85543;-76937 89326;-76914 89342 1;-76911 89420;-76836 89465;-76758 89465 1;-76513 89465;-76375 89465;-76130 89465 1;-75992 89465;-75735 89358;-75729 89220 1;-75685 88154;-75667 87362;-75615 86508;-75582 86052 18; +-83104 70145;-79773 71601;-79893 71914;-83316 81974 1;-84142 84329;-84303 85808;-85540 87976 1;-85929 88658;-86567 89096;-87322 88882 1;-88463 88559;-89148 88430;-90286 88098;-89347 84135;-87496 84604;-83104 70145 18; +-84821 85878;-84320 84432; +-91150 91176;-85746 92767;-85648 92656 1;-85527 92508;-85448 92322;-85426 92109;-85377 91529;-85908 91159;-85735 88951;-85118 88729 1;-84789 87113;-84507 86216;-83872 84694;-83150 82923;-83043 82631;-78743 72220;-79761 71720;-79974 72151;-83316 81974 1;-84142 84329;-84303 85808;-85540 87976 1;-85929 88658;-86567 89096;-87322 88882 1;-88463 88559;-89148 88430;-90286 88098;-91150 91176 18; +-85762 92773;-84637 93096;-84683 92920 1;-84700 92737;-84685 92539;-84662 92331 1;-84508 90931;-84382 90151;-84206 88753 1;-84070 87669;-83872 87075;-83527 86039 1;-82987 84420;-82993 84795;-82346 83216;-83150 82923;-83872 84694 1;-84507 86216;-84789 87113;-85118 88729;-85735 88951;-85908 91159;-85377 91529;-85426 92109 1;-85448 92329;-85532 92519;-85659 92669;-85762 92773 18; +-73972 83139;-74052 83731;-73213 83873;-73235 84099;-74087 90148;-73636 90214;-73842 91626;-69459 92265;-69305 91208 1;-69099 91238;-68892 91372;-68922 91578;-69243 93774 1;-69289 94092;-69424 94396;-69625 94623;-69779 94577;-80305 93899 1;-81343 93832;-81937 93808;-82946 93553;-84557 93101;-84679 93076;-84687 92633 1;-84683 92535;-84673 92434;-84662 92331 1;-84550 91316;-84453 90627;-84342 89793;-84288 89874;-84174 90703;-84131 90926 1;-83847 91317;-83608 91495;-83236 91826 1;-82900 92125;-82707 92391;-82259 92437 1;-79508 92722;-77983 93042;-75228 93291 1;-75041 93308;-74908 93130;-74896 92943 1;-74883 92736;-75092 92622;-75298 92594 1;-75400 92580;-75501 92566;-75599 92553;-75482 92472 1;-75371 92371;-75299 92233;-75294 92072 1;-75236 90027;-75245 88878;-75102 86837 1;-75084 86578;-75033 86354;-74958 86150;-75021 86140;-75203 86112;-75582 86052;-75582 86057;-75573 85887 1;-75545 85540;-75511 85171;-75467 84763 1;-75423 84361;-75370 84042;-75641 83742 1;-76020 83323;-76153 83020;-76539 82608 1;-76719 82415;-76862 82279;-76862 82015 1;-76862 81672;-76805 81483;-76767 81142;-76752 81090;-73754 81570;-73972 83139 18; +-73746 81653;-72228 70085;-73781 69859;-76572 73069;-76581 73296 1;-78150 75499;-79297 76623;-80458 79091;-82328 83203;-81998 82896 1;-81787 82781;-81564 82706;-81391 82471 1;-80918 81829;-80883 81311;-80361 80709 1;-79927 80208;-79515 80114;-79000 79697 1;-78686 79443;-78656 79130;-78627 78759;-78769 78736 1;-79078 78555;-79168 78148;-79070 77778 1;-78889 77094;-78851 76584;-78302 76138 1;-78009 75900;-77815 75663;-77458 75664;-77275 75521 1;-77236 75483;-77195 75444;-77152 75405 1;-76935 75206;-76718 75166;-76426 75202 1;-76055 75247;-75697 75534;-75772 75900;-76339 78518;-76360 78740;-76744 81032;-76642 81252;-73746 81653 18; +-69771 80469 32;-54436 82530 32;-54571 83532 32;-69906 81471 32;-69771 80469 50; +-69625 94594;-67022 94804;-67093 94649 1;-67185 94410;-67223 94147;-67197 93908;-66919 91718;-66532 91789;-66164 91860;-64191 92243;-64352 93279;-62001 93816;-57411 94866;-57078 93755;-55499 94138;-55116 92953;-55258 92922;-55664 92833;-54549 83524;-55038 83469;-69906 81471 32;-69970 81863;-70096 82802 32;-71597 82600 32;-71638 82904 32;-71714 82894;-71997 83422;-73030 83284 32;-73728 83166;-73850 83602;-73204 83689;-73224 83986;-73235 84099;-74087 90148;-73636 90214;-73842 91626;-69459 92265;-69305 91208 1;-69099 91238;-68892 91372;-68922 91578;-69243 93774 1;-69281 94034;-69378 94285;-69522 94492;-69625 94594 18; +-37968 77915;-35909 78543;-34761 79913;-35085 80277 1;-35333 80584;-35547 80919;-35699 81278;-39217 89588;-40476 92684;-42113 93224;-43264 96139 1;-43483 96694;-43150 97321;-42635 97620;-46526 97336 1;-45733 97103;-45138 96474;-44786 95616;-38104 79354 1;-37935 78925;-37901 78562;-37933 78191;-37968 77915 18; +-47343 102112 1;-46920 101139;-47696 99845;-48756 99791 1;-50544 99700;-51541 99468;-53275 99023 1;-54372 98742;-55023 98742;-56154 98674 1;-63131 98255;-67030 97834;-74001 97320 1;-77287 97077;-79185 96668;-82375 96036;-88705 94683; +23987 105832;23679 108312;22769 108209;19118 107811;5369 106325;4505 105239;-4626 104316;-7560 105479;-18485 104438;-20987 102981;-31707 102152;-33229 101315;-35903 101084;-35754 99008 1;-35676 99021;-35595 99032;-35511 99038;-30338 99432;-30258 98381;-28290 98530;-28371 99603;-26204 99767;-26115 98594;-21733 98925;-21425 99305;-21513 100284;-10566 101461;1904 102646;1982 101714;3893 101875;3805 102919;5000 103020;5083 102040;9773 102439;9673 103615;10155 103656;10255 102476;13746 102772;13968 103187;13926 103536;13818 104312;14879 104442;17152 104746 1;17432 104783;17657 104755;17874 104687;22927 105396 1;23161 105627;23438 105777;23719 105812;23758 105817;23987 105832 18; +-35693 99020;-35865 101081;-36284 101051;-40739 100665;-40700 99592;-39960 99654 1;-39807 99667;-39689 99535;-39676 99382 1;-39659 99185;-39812 99017;-40009 99000;-40663 98963;-40632 97872 1;-39952 97908;-39324 97943;-38465 97989 1;-37430 98045;-36905 98692;-36028 98945;-35693 99020 18; +-40703 100659;-40624 99598;-40700 99592;-40663 98963;-40591 98967;-40562 97876 1;-40585 97874;-40609 97873;-40632 97872;-40779 97864 1;-41168 97843;-41581 97820;-42067 97794 1;-42323 97780;-42569 97681;-42774 97527;-43241 97492;-42901 98862;-42847 98827 1;-42792 98797;-42728 98783;-42661 98790;-42032 98827;-42081 99506;-42723 99444 1;-42802 99436;-42869 99399;-42918 99345;-43241 100458;-42683 100511;-40703 100659 18; +-43206 100449;-42877 99383 1;-42892 99371;-42906 99359;-42918 99345;-42954 99468;-42976 99255 1;-43002 99196;-43014 99130;-43007 99062 1;-42997 98962;-42939 98880;-42858 98833;-43197 97544;-46364 97265;-46657 97371 1;-46897 97427;-47153 97448;-47426 97429;-49444 97291;-49896 97255;-60008 96439;-66307 95408 1;-66623 95346;-66870 95107;-67024 94805;-67041 94779;-69570 94557;-69664 94665 1;-69883 94894;-70173 95034;-70511 95015 1;-74055 94818;-76055 94643;-79599 94445 1;-81470 94340;-82566 94135;-84303 93628 1;-84497 93479;-84600 93304;-84650 93109;-84679 93076;-85743 92767;-85807 92811 1;-85957 92929;-86144 93006;-86350 93034;-91301 91599;-91837 93674;-88714 94686;-88290 94772;-82375 96036 1;-79185 96668;-77287 97077;-74001 97320 1;-67030 97834;-63131 98255;-56154 98674 1;-55023 98742;-54372 98742;-53275 99023 1;-51541 99468;-50544 99700;-48756 99791 1;-47696 99845;-46920 101139;-47343 102112;-45730 102340 1;-45162 100774;-44493 100443;-43448 100449;-43206 100449 18; +-53950 78577;-52735 78740; +-52478 69067; +-53501 73660;-53404 72940; +-67425 71694;-67268 70524; +-61445 72498;-61286 71315; +-68212 71605 32;-57696 73018;-57612 72368;-56239 72566;-56313 73203;-53655 73561 32;-53492 72351 32;-68049 70395 32;-68212 71605 50; +-57239 68824; +-60641 68336; +-54325 69147; +-64139 67880; +-68039 67202; +-68929 67045; +-68562 64515; +-57198 68609 32;-60223 68203 32;-60018 66678 32;-57582 67005 32;-57688 67795 32;-57099 67874 32;-57198 68609 50; +-57072 67841 32;-56967 67056 32;-57564 66976 32;-57669 67761 32;-57072 67841 50; +-55538 68587 32;-55204 66103 32;-54291 66226 32;-54215 65665 32;-53747 65728 32;-53881 66723 32;-54340 66661 32;-54616 68711 32;-55538 68587 50; +-55238 66083 32;-54295 66210 32;-54217 65628 32;-55160 65501 32;-55238 66083 50; +-55435 68615;-55639 70132; +-57165 68657;-57333 69899; +-60644 68186;-60812 69428; +-64118 67690;-64286 68932; +-60261 68225;-67646 67233;-67976 69689;-68977 69555;-68710 67561;-70606 67307;-70793 68706;-72084 68533;-72258 69832; +-69077 70234;-68044 70392; +-69066 70237;-68048 70403;-68128 70934;-69128 70810;-69066 70237 18; +-73531 69688;-72285 69848;-72237 69677;-72084 68533;-70793 68706;-70606 67307;-68710 67561;-68977 69555;-67976 69689;-67646 67233;-69522 65136;-73531 69688 18; +-53707 65691;-53669 65424 1;-53744 65342;-53823 65258;-53904 65171 1;-53982 65087;-54174 65131;-54188 65245;-54244 65702 16; +-54562 68746;-54759 70219;-54254 70287;-54115 69249;-52707 69438;-52931 71139 1;-52262 71218;-51414 71074;-51320 70411;-51029 68038 1;-51925 67140;-52538 66588;-53303 65803;-53438 66816;-53893 66755 16; +-54345 63308 1;-53348 64789;-52754 65481;-51537 66788;-51188 66421;-51842 65654 1;-52518 64862;-51741 63837;-52427 63054 1;-52823 62602;-53415 62459;-53936 62758 1;-54132 62871;-54246 62969;-54337 63176 1;-54351 63207;-54364 63280;-54345 63308 18; +-49251 62400 1;-51626 63855;-48989 66545;-47305 64476; +-45315 65029; +-47478 62517; +-44216 58644; +-44026 57688; +-48352 64315; +-49438 64377; +-49327 63316; +-53805 63341; +-51634 66265; +-52100 66165;-51708 65810; +-52676 65310;-53234 64115;-52159 63613; +-52842 65344;-52734 65185;-53234 64115;-52159 63613;-52230 63365 1;-52276 63258;-52339 63154;-52427 63054 1;-52823 62602;-53415 62459;-53936 62758 1;-54132 62871;-54246 62969;-54337 63176 1;-54351 63207;-54364 63280;-54345 63308 1;-53932 63921;-53589 64399;-53241 64841;-52842 65344 18; +-52092 66173;-51734 65815;-51587 65953;-51188 66421;-51537 66788 1;-51676 66639;-51807 66497;-51931 66362;-52092 66173 18; +-52499 57456 1;-54787 58435;-53164 60979;-51327 59517; +-53042 58986; +-52684 58123; +-54459 53030;-55545 53375 1;-55319 52873;-55179 52509;-55335 51981 1;-55600 51084;-55808 50482;-56556 49921 1;-57061 49542;-57268 49036;-57148 48416 1;-57019 47753;-56892 47179;-56272 46910; +-56981 67045;-56523 63621 1;-56455 63108;-56111 62735;-55688 62436 1;-57338 59635;-58390 58036;-59328 54911 1;-59640 53872;-60293 53471;-61169 52832 16; +-54475 82533;-54335 82560 1;-54064 82599;-53880 82653;-53591 82591 1;-53333 82536;-53369 82212;-53382 81949 1;-53422 81133;-53331 80658;-53092 79876 1;-52857 79105;-52741 78540;-52592 77913;-52479 77452 1;-52426 77246;-52366 77029;-52296 76792 1;-51929 75560;-51867 74830;-51686 73557 1;-51565 72704;-52164 71879;-53021 71786;-53272 73640;-54475 82533 18; +-52098 84775;-52209 84738;-52223 84577 1;-52263 84207;-52365 83872;-52444 83413 1;-52647 82228;-52688 81508;-52427 80334 1;-52022 78510;-51669 77514;-51337 75675 1;-51252 75201;-51176 74765;-51107 74352;-51046 73986 1;-50787 72395;-50559 70501;-50338 68666;-50002 68979;-50651 73869;-51900 82727;-52098 84775 18; +-55549 94151;-54975 94286;-54907 94286;-54476 94379;-54130 93256;-53933 92534;-53957 92504;-53843 92002;-52350 86283;-52306 86167;-52240 85323;-52234 85280 1;-52137 84553;-52320 84136;-52444 83413 1;-52647 82228;-52688 81508;-52427 80334 1;-52056 78664;-51729 77689;-51422 76125;-51267 75284 1;-50867 73021;-50684 71588;-50406 69293;-51176 69238;-51320 70411 1;-51414 71074;-52262 71218;-52931 71139;-53021 71786 1;-52164 71879;-51565 72704;-51686 73557 1;-51867 74830;-51929 75560;-52296 76792 1;-52650 77984;-52729 78686;-53092 79876 1;-53099 79900;-53107 79924;-53114 79947;-53231 80366 1;-53365 80890;-53413 81317;-53382 81949 1;-53369 82212;-53333 82536;-53591 82591 1;-53912 82660;-54104 82586;-54430 82547;-54545 83496;-54372 83566 1;-53468 83855;-53077 85180;-53212 86150 1;-53438 87764;-54043 88559;-54408 90147;-54683 91221;-54792 91648;-55125 92951;-55264 92921;-55549 94151 18; +-54430 82547 1;-54104 82586;-53912 82660;-53591 82591 1;-53333 82536;-53369 82212;-53382 81949 1;-53422 81133;-53331 80658;-53092 79876 1;-52983 79518;-52899 79204;-52826 78908 16; +-52744 78564 1;-52614 78011;-52501 77484;-52296 76792 1;-51929 75560;-51867 74830;-51686 73557 1;-51565 72704;-52164 71879;-53021 71786;-53272 73640 16; +-49251 62400 1;-51626 63855;-48989 66545;-47305 64476;-49251 62400 18; +-52499 57456 1;-54787 58435;-53164 60979;-51327 59517;-52499 57456 18; +-48422 75407;-50543 75164; +-39496 75272 1;-40002 74714;-40302 74409;-40823 73978;-43056 72559;-44611 72954;-44835 75171; +-42772 73904; +-43784 73842; +-43870 74681; +-40695 74940; +-39509 75323;-42405 75445;-42754 75403;-42735 75119;-44281 74968;-44306 75215;-44708 75166; +-42466 75445 1;-42520 75330;-42545 75158;-42423 75123 1;-42078 75025;-41871 75030;-41524 74939 1;-41391 74904;-41262 74969;-41210 75096 1;-41166 75202;-41139 75263;-41123 75376;-42466 75445 18; +-44822 75140;-44333 75212;-44306 75215;-44281 74968;-42735 75119;-42754 75403;-42405 75445;-39509 75323;-39483 75225 1;-39947 74708;-40216 74365;-40823 73978;-43056 72559;-44611 72954;-44809 74914;-44822 75140 18; +-46124 75668 32;-45041 75801 32;-45087 76173 32;-46170 76040 32;-46124 75668 50; +-50380 73830;-47086 74211;-47038 73798; +-46908 73795 32;-46753 72457 32;-47961 72317 32;-48116 73655 32;-46908 73795 50; +-46913 74410;-46248 74487;-46147 73620;-46848 73539; +-46760 72826;-46113 72901;-46014 72050 1;-46808 71504;-47472 70982;-48409 70244 1;-49165 69648;-49622 69311;-50331 68668 1;-50650 71311;-50866 73064;-51337 75675 1;-51669 77514;-52022 78510;-52427 80334 1;-52688 81508;-52647 82228;-52444 83413 1;-52320 84136;-52137 84553;-52234 85280;-52339 86073;-52332 86213;-53981 92530 16; +-46725 72433;-46630 71613; +-46411 74075; +-46769 72827;-46633 71618;-46537 71681 1;-46366 71805;-46193 71927;-46014 72050;-46113 72901;-46608 72844;-46769 72827 18; +-46855 73543;-46929 74400;-46844 74418;-46248 74487;-46147 73620;-46735 73552;-46855 73543 18; +-50459 73748;-47118 74150;-46891 71733;-49927 69352;-50459 73748 18; +-50577 75156;-48344 75403;-48414 75675;-48937 80251 32;-43501 80881 32;-43105 77503 32;-41978 77649;-41349 77561;-39933 77348;-38783 77476;-38760 78239 1;-38742 78620;-38775 78992;-38920 79386 1;-39802 81791;-40358 83138;-41346 85461;-41879 85371;-41768 84113;-44236 83693;-51700 82707;-50577 75156 18; +-46007 72056;-44664 72928;-44786 75161;-44306 75205;-44288 74979;-42736 75109;-42753 75388;-42754 75403;-42405 75445;-39522 75324;-38644 77204;-39927 77334;-40537 77439;-41349 77561;-41478 77579;-42169 77614;-42762 77509;-43059 77491;-42910 76096;-45030 75808;-48406 75416;-50552 75163;-50491 74692;-49470 74796;-49357 74072;-46914 74386;-46243 74482;-46167 73792;-46147 73620;-46848 73539;-46775 72825;-46653 72838;-46113 72901;-46045 72314;-46007 72056 18; +-42511 75212 1;-42510 75147;-42486 75141;-42423 75123 1;-42078 75025;-41871 75030;-41524 74939 1;-41391 74904;-41262 74969;-41210 75096 1;-41201 75117;-41193 75136;-41186 75155; +-76625 89465;-76477 89465 1;-76370 89465;-76267 89465;-76130 89465 1;-75992 89465;-75735 89358;-75729 89220 1;-75658 87491;-75654 86483;-75467 84763 1;-75423 84361;-75370 84042;-75641 83742 1;-76020 83323;-76153 83020;-76539 82608 1;-76719 82415;-76862 82279;-76862 82015 1;-76862 81672;-76805 81483;-76767 81142;-76749 81081;-76744 81032;-76702 80779;-76360 78740;-76339 78518;-75772 75900 1;-75697 75534;-76055 75247;-76426 75202 1;-76718 75166;-76935 75206;-77152 75405 1;-77195 75444;-77236 75483;-77275 75521;-77458 75664 1;-77460 75664;-77463 75664;-77465 75664;-77599 75876 1;-77858 76200;-78023 76534;-78268 77045 1;-78511 77552;-78538 77936;-78600 78459 1;-78660 78964;-78605 79377;-79000 79697 1;-79515 80114;-79927 80208;-80361 80709 1;-80883 81311;-80918 81829;-81391 82471 1;-81552 82690;-81756 82770;-81954 82873; +-75228 93291 1;-77983 93042;-79508 92722;-82259 92437 1;-82707 92391;-82900 92125;-83236 91826 1;-83621 91483;-83864 91305;-84161 90884 1;-84286 90706;-83965 90330;-83794 90465 1;-83324 90835;-83144 91143;-82660 91494 1;-82119 91885;-81664 91821;-81003 91913 1;-78781 92222;-77521 92289;-75298 92594 1;-75092 92622;-74883 92736;-74896 92943 1;-74908 93130;-75041 93308;-75228 93291 18; +-41845 69753;-42167 70267;-42883 69797 1;-43065 69671;-42974 69412;-42848 69230;-42250 68366; +-42246 68366;-42115 68514;-41836 69744;-41895 69834;-42167 70267;-42883 69797 1;-43065 69671;-42974 69412;-42848 69230;-42313 68457;-42246 68366 18; +-39564 75313;-38700 77232;-38114 77164;-38147 77051 1;-38430 75761;-38836 74995;-39800 74079 1;-40438 73473;-40990 73130;-41595 72779;-42005 72542 1;-42288 72378;-42587 72200;-42915 71985;-44781 72403;-45052 72228;-45240 72107;-45671 71828 1;-47695 70380;-49305 68975;-50915 67402;-51189 69300;-50413 69370;-50345 68727 1;-50343 68707;-50340 68686;-50338 68666;-50002 68979;-49701 69217 1;-49294 69557;-48919 69842;-48409 70244 1;-47472 70982;-46808 71504;-46014 72050;-46019 72090;-44699 72973;-44525 72932;-43056 72559;-40823 73978 1;-40377 74347;-40093 74624;-39702 75046;-39564 75313 18; +-53752 66764;-54310 66685;-54607 68727;-54563 68805;-54584 68908;-54759 70219;-54254 70287;-54115 69249;-52707 69438;-52931 71139 1;-52262 71218;-51414 71074;-51320 70411;-51029 68038 1;-51925 67140;-52538 66588;-53303 65803;-53438 66816;-53673 66785;-53752 66764 18; +-54206 65639;-53717 65717;-53691 65578;-53669 65424 1;-53744 65342;-53823 65258;-53904 65171 1;-53982 65087;-54174 65131;-54188 65245;-54223 65531;-54206 65639 18; +-54079 65127;-53591 64394;-53518 64481 1;-53425 64605;-53333 64724;-53241 64841;-52842 65344;-52748 65444 1;-52399 65854;-52016 66273;-51537 66788;-51479 66844 1;-51272 67051;-51066 67255;-50860 67455;-50965 68110;-51102 67965 1;-51959 67110;-52559 66567;-53303 65803;-53438 66816;-53858 66760;-53652 65423;-53966 65135;-54079 65127 18; +-52805 65387;-52102 66164;-51707 65813;-51789 65716;-51842 65654 1;-52360 65047;-52025 64304;-52148 63637;-52334 63695;-53234 64115;-52762 65126;-52805 65387 18; +-37973 78034;-35993 78610;-35434 76804;-35617 76733 1;-36468 76348;-37021 75455;-36929 74473 1;-36910 74266;-36863 74068;-36793 73883 1;-36758 73484;-36934 73188;-37274 72978;-39551 71521;-39832 71341;-41758 70109;-41841 69742;-41941 69907;-42167 70267;-42883 69797 1;-43065 69671;-42974 69412;-42848 69230;-42250 68366;-42414 68201 1;-42630 68015;-42910 67882;-43197 67684 1;-44887 66518;-45862 65838;-47305 64436;-47388 64574 1;-48913 66278;-51116 64291;-49792 62837;-52221 63397;-52163 63598;-52135 63717 1;-52050 64363;-52339 65071;-51842 65654;-51789 65716;-51707 65813;-51648 65881;-51188 66421;-51537 66788;-51458 66865 1;-51258 67065;-51059 67262;-50860 67455;-50780 67533 1;-49215 69053;-47639 70420;-45671 71828;-44781 72403;-42915 71985 1;-41690 72790;-40863 73069;-39800 74079 1;-39713 74162;-39630 74243;-39552 74324;-39386 74501 1;-38712 75248;-38386 75963;-38147 77051;-38114 77164;-37973 78034 18; +-61374 63787;-57765 64596 16; +-65145 65119;-62250 63877 16; +-62250 63877;-61813 63689 32;-61374 63787 16; +-59649 64555;-59780 65141; +-60389 64270;-60606 65237; +-61147 64067;-61405 65221; +-62408 64295;-61975 65304; +-63202 64570;-62828 65441; +-63973 64919;-63691 65577; +-53565 72972;-53181 73015;-53170 72885;-53021 71786;-52902 71157;-52916 71024;-52707 69438;-54115 69249;-54254 70287;-54759 70219;-54562 68746;-54724 68696;-55296 68620;-55528 68592;-55159 65493;-54246 65628;-54216 65470;-54188 65245 1;-54177 65155;-54056 65109;-53966 65134;-53539 64452 1;-53790 64115;-54050 63746;-54345 63308 1;-54356 63292;-54356 63260;-54352 63230;-54458 63134 1;-54730 62767;-54961 62436;-55171 62116;-55688 62436 1;-56111 62735;-56455 63108;-56523 63621;-56981 67045;-57157 68639;-67643 67220;-67963 69695;-68984 69564;-68696 67583;-70598 67296;-70798 68691;-72098 68526;-72255 69817;-69071 70236;-53485 72350;-53565 72972 18; +-58087 52559; +-58645 51355; +-59256 52350; +-56761 48738; +-56150 49314; +-54964 52786; +-55438 46826; +-54812 44938; +-54180 50848; +-53020 52846; +-52149 56105; +-71477 54718;-72454 53148;-72425 53130;-72541 52816 1;-72336 52248;-71887 52120;-71372 51804 1;-70855 51488;-70419 51673;-69857 51537;-66749 49606;-65685 51420;-71477 54718 18; +-61307 53454; +-63034 49185; +-61973 48889; +-61652 50567; +-65674 46118; +-66390 44415; +-65650 43601; +-67007 42713; +-66654 40247; +-66637 39332 1;-67141 39325;-67555 39729;-67562 40233 1;-67569 40737;-67166 41151;-66662 41158 1;-66157 41165;-65743 40762;-65736 40258 1;-65730 39753;-66133 39339;-66637 39332 18; +-66637 39332 1;-67141 39325;-67555 39729;-67562 40233 1;-67569 40737;-67166 41151;-66662 41158 1;-66157 41165;-65743 40762;-65736 40258 1;-65730 39753;-66133 39339;-66637 39332 18; +-70099 50778; +-70370 50037;-72652 44621; +-69654 51431;-68581 53171; +-73255 44770; +-61221 38886 1;-59063 38913;-59067 36089;-61011 36042; +-60365 37542; +-74741 42648;-72420 43311;-72978 36419;-74741 42648 18; +-74741 42648;-72420 43311;-72978 36541;-74741 42648 18; +-75770 46312 1;-75218 46312;-74740 45910;-74571 45384;-73873 42802;-74706 42543;-75770 46312 18; +-76416 51599;-75875 49802 1;-75720 49317;-75960 48768;-76451 48633;-77323 51390;-76416 51599 18; +-74496 52210 1;-74276 51804;-74066 51527;-74165 51076 1;-74347 50246;-74447 49773;-74758 48982 1;-74825 48813;-74707 48702;-74653 48528 1;-74651 48521;-74170 47754;-73870 48515 1;-73450 49584;-73378 49818;-73118 50936 1;-72994 51470;-72797 52060;-72490 52192 1;-71990 52407;-71687 52044;-72281 52768;-72682 52873;-73101 52751;-74496 52210 18; +-76494 51633;-76478 51499;-75875 49802 1;-75829 49659;-75818 49511;-75837 49369 1;-75875 49086;-75632 48863;-75349 48825 1;-75126 48795;-74862 48714;-74781 48924;-74671 49210 1;-74426 49871;-74329 50329;-74165 51076 1;-74066 51527;-74276 51804;-74496 52210;-76494 51633 18; +-74419 44793 1;-73959 44868;-73846 45333;-73766 45792 1;-73604 46718;-73444 47307;-73741 48198 1;-73775 48301;-73861 48302;-73963 48339;-74088 48210 1;-74359 48056;-74651 48523;-74653 48528 1;-74700 48680;-74796 48784;-74775 48921;-74867 48819 1;-74985 48750;-75179 48802;-75349 48825 1;-75448 48838;-75542 48874;-75621 48928;-74419 44793 18; +-76433 48651;-76341 48672 1;-76066 48792;-75888 49052;-75841 49344;-75828 49208 1;-75800 49098;-75729 49004;-75633 48936;-75599 48852;-74629 45514;-74696 45663 1;-74920 46046;-75321 46312;-75770 46312;-76433 48651 18; +-73831 45475;-72465 45064;-70398 49970;-69672 51422;-69857 51537 1;-70419 51673;-70855 51488;-71372 51804 1;-71663 51982;-71932 52101;-72149 52272;-72239 52262 1;-72313 52251;-72398 52232;-72490 52192 1;-72797 52060;-72994 51470;-73118 50936 1;-73378 49818;-73450 49584;-73870 48515 1;-73896 48449;-73924 48394;-73952 48349;-73836 48297 1;-73793 48279;-73759 48254;-73741 48198 1;-73444 47307;-73604 46718;-73766 45792 1;-73784 45691;-73803 45589;-73827 45491;-73831 45475 18; +-66749 49606 1;-67917 47531;-68574 46272;-68955 43922 1;-69483 40668;-69604 38753;-69177 35484 1;-68924 33547;-68454 32521;-67894 30649 1;-66131 24755;-65023 21484;-63404 15548;-66858 14734;-73026 36373;-72949 36783;-72505 42263;-72472 42670;-72420 43311;-73895 42891;-73950 43086;-74419 44820;-74243 44847 1;-74023 44954;-73911 45181;-73840 45442;-73649 45420;-72465 45064;-70398 49970;-69672 51422;-66749 49606 18; +-65685 51420;-65142 52336 1;-65698 53536;-66230 54364;-67468 54831;-68614 53088;-65685 51420 18; +-66809 62094 1;-66292 61633;-66108 61079;-66267 60404 1;-66391 59878;-66356 59411;-65946 59059;-65156 58368;-68605 53094;-71460 54680;-66809 62094 18; +-60669 58982 1;-61410 58916;-61917 58748;-62396 58179 1;-63115 57326;-63425 56724;-63722 55649 1;-63885 55060;-64020 54702;-63931 54097;-60669 58982 18; +-65142 52336;-63931 54097 1;-64020 54702;-63885 55060;-63722 55649 1;-63425 56724;-63115 57326;-62396 58179 1;-61917 58748;-61410 58916;-60669 58982;-60633 59058;-60423 59305 1;-59556 60569;-58930 61200;-58252 62575 1;-57933 63222;-57817 63633;-57746 64351;-58248 64488;-61374 63787;-61891 63672;-62503 63985;-65033 65071;-65222 64925 1;-65835 63819;-66183 63202;-66784 62089;-66722 62013 1;-66272 61568;-66117 61040;-66267 60404 1;-66391 59878;-66356 59411;-65946 59059;-65156 58368;-67471 54827;-67395 54795 1;-66247 54345;-65734 53586;-65212 52485;-65142 52336 18; +-59598 56986; +-60928 52527 1;-63596 50546;-65251 49304;-66788 46358 1;-67958 44116;-68231 42604;-68367 40079 1;-68444 38639;-68307 37826;-68158 36391; +-64859 50536; +-64341 41431; +-63477 47206 1;-63377 47624;-63092 47956;-62663 47946 1;-62247 47936;-61877 47594;-61923 47181 1;-62180 44896;-62422 43625;-62613 41333 1;-62665 40714;-62634 39877;-63255 39877 1;-63688 39877;-63816 40383;-63847 40815 1;-64025 43309;-64059 44775;-63477 47206 18; +-63477 47206 1;-63377 47624;-63092 47956;-62663 47946 1;-62247 47936;-61877 47594;-61923 47181 1;-62180 44896;-62422 43625;-62613 41333 1;-62665 40714;-62634 39877;-63255 39877 1;-63688 39877;-63816 40383;-63847 40815 1;-64025 43309;-64059 44775;-63477 47206 18; +-63477 47206 1;-63377 47624;-63092 47956;-62663 47946 1;-62247 47936;-61877 47594;-61923 47181 1;-62180 44896;-62422 43625;-62613 41333 1;-62665 40714;-62634 39877;-63255 39877 1;-63688 39877;-63816 40383;-63847 40815 1;-64025 43309;-64059 44775;-63477 47206 18; +-76422 51243;-75867 49602; +-74683 45630;-73893 42916;-72388 43310;-72870 37290; +-67266 49928;-69306 51195; +-71178 54524;-71178 54524;-68359 52939 32;-66167 51707; +-68410 53393;-68410 53393;-65156 58368 32;-65946 59059 1;-66356 59411;-66391 59878;-66267 60404 1;-66135 60963;-66239 61438;-66571 61847; +-61258 63608 1;-62680 60985;-63900 59663;-65456 57117 1;-67179 54297;-68868 52119;-69843 49890 1;-71121 46969;-71775 45194;-72064 42019 1;-72358 38790;-72824 37306;-71793 33802;-66340 14977;-61634 -1325;-59401 -9263;-56335 -20161;-50257 -43932;-46400 -59172 1;-46186 -60019;-45749 -60376;-45092 -60952 16; +-67670 67288;-69582 65240;-66855 62057;-66740 62170 1;-66166 63233;-65820 63846;-65222 64925;-64956 65595;-57868 65009;-57794 64590;-56690 64698;-56998 67054;-60070 66659;-60218 68238;-65474 67535;-67670 67288 18; +-65061 65296;-65038 65388;-64956 65595;-57868 65009;-57823 64755;-61825 63883;-65061 65296 18; +-57842 64628;-56673 64785;-56651 64580;-56523 63621 1;-56455 63108;-56111 62735;-55688 62436 1;-56105 61729;-56481 61098;-56826 60500;-57199 59842 1;-58022 58361;-58662 56987;-59268 54987;-62539 56182;-60669 58982;-60470 59250;-60423 59305 1;-59556 60569;-58930 61200;-58252 62575 1;-57933 63222;-57817 63633;-57746 64351;-57842 64628 18; +-60858 52106 1;-60722 52204;-60562 51747;-60597 51583 1;-61076 49363;-61493 48379;-61661 46114 1;-61842 43674;-61989 42299;-61908 39853; +-59159 53844;-59316 53403 1;-59711 53209;-59984 53003;-60093 52576 1;-60190 52197;-60060 51931;-59991 51546;-60389 50149 1;-60659 48926;-60843 48252;-61019 47024 1;-61413 44284;-61345 42708;-61280 39941 16; +-54361 63265 1;-55398 61882;-55847 61012;-56726 59523 1;-57953 57445;-58373 56125;-59159 53844 1;-58197 53531;-57139 52999;-57333 52006 1;-57454 51388;-57771 50969;-58369 50772 1;-59039 50551;-59462 50764;-60111 51041 16; +-59156 53823;-58999 53790 1;-58084 53471;-57150 52944;-57333 52006 1;-57454 51388;-57771 50969;-58369 50772 1;-59039 50551;-59462 50764;-60111 51041;-60127 51067;-59991 51546 1;-60060 51931;-60190 52197;-60093 52576 1;-59984 53003;-59711 53209;-59316 53403;-59208 53708;-59156 53823 18; +-62522 56275;-59265 55090;-59351 54838 1;-59671 53853;-60314 53456;-61169 52832;-63397 50562;-66050 47836;-67444 44974;-68283 42346;-68357 40360;-68308 36375;-69275 36300 1;-69580 39093;-69438 40948;-68955 43922 1;-68909 44204;-68859 44471;-68805 44725;-68712 45135 1;-68311 46770;-67699 47918;-66749 49606;-66718 49660;-65685 51420;-65611 51545;-65142 52336;-65063 52451;-63931 54097;-63844 54228;-62536 56186;-62522 56275 18; +-68028 39317;-61897 39848;-61913 39998 1;-61983 42354;-61838 43723;-61661 46114 1;-61493 48379;-61076 49363;-60597 51583 1;-60564 51737;-60703 52148;-60832 52118;-61798 51778;-64969 49384;-67054 46337;-68066 42439;-68436 39762;-68028 39317 18; +-72505 48179; +-73054 50750; +-50138 58884;-49934 57130; +-50138 58884;-49934 57130; +-50866 58588;-50662 56834; +-50866 58588;-50662 56834; +-50066 56050;-49623 56101;-49682 56620; +-50755 56869;-50726 56614;-49660 56735; +-51139 57898; +-51065 58937;-49390 61493;-49353 61369;-49129 59445;-47920 59585;-47614 56939;-48665 56817;-48544 55776;-50009 55622;-50035 55784;-50066 56054;-49623 56105;-49696 56738;-49951 56702;-50726 56614;-50755 56869;-50995 58239;-51065 58937 18; +-35637 63787 32;-35487 62495 32;-35027 62548 32;-35177 63841 48; +-35382 63815;-35332 63388; +-35637 63787 32;-35487 62495 32;-35027 62548 32;-35177 63841 32;-35637 63787 50; +-46074 54312 32;-45924 53020 32;-45464 53073 32;-45614 54366 48; +-46074 54312 32;-45924 53020 32;-45464 53073 32;-45614 54366 32;-46074 54312 50; +-45819 54340;-45769 53913; +-50031 55845;-53029 55498; +-42414 49532 32;-42007 46176 32;-38874 46555 32;-39281 49911 32;-42414 49532 50; +-42280 50301 32;-42501 52107 32;-40154 52394 32;-39933 50588 32;-42280 50301 50; +-38459 44665; +-42420 44159; +-44706 47230; +-41373 47073; +-40100 48696; +-38634 52588 32;-38494 51450 32;-39387 51341 32;-39527 52479 32;-38634 52588 50; +-38695 51012 32;-38623 50417 32;-39570 50302 32;-39642 50897 32;-38695 51012 50; +-39149 50648; +-39007 51973; +-38634 52588 32;-38494 51450 32;-39387 51341 32;-39527 52479 32;-38634 52588 50; +-42414 49532 32;-42007 46176 32;-38874 46555 32;-39281 49911 32;-42414 49532 50; +-42280 50301 32;-42501 52107 32;-40154 52394 32;-39933 50588 32;-42280 50301 50; +-38237 53080 32;-43107 52489 32;-42289 45747 32;-38429 46215 32;-38881 49941 32;-37871 50064 32;-38237 53080 50;-38634 52588 32;-38494 51450 32;-39387 51341 32;-39527 52479 32;-38634 52588 50;-40154 52394 32;-39933 50588 32;-42280 50301 32;-42501 52107 32;-40154 52394 50;-39281 49911 32;-38874 46555 32;-42007 46176 32;-42414 49532 32;-39281 49911 50; +-34468 51263; +-34939 52519; +-42529 53321; +-40901 52742 1;-41219 52935;-41304 52848;-41345 53220 1;-41374 53485;-41073 53714;-40697 53758 1;-38887 53970;-37873 54088;-36064 54300 1;-35721 54340;-35480 54252;-35440 53909 1;-35401 53577;-35646 53362;-35978 53323 1;-36867 53219;-37369 53170;-38258 53069;-40901 52742 18; +-51905 49085; +-52410 47550; +-31833 46621; +-31361 45665;-34519 46331 1;-35077 46449;-35407 46513;-35975 46454;-38428 46213;-42289 45747 32;-43107 52489 1;-44109 55092;-44604 56577;-45518 59212 1;-46160 61063;-46522 62061;-47215 63917 1;-47797 63344;-48302 62769;-48956 61973 1;-49101 61797;-49237 61627;-49364 61465;-49129 59445;-47920 59585;-47614 56939;-48665 56817;-48544 55776 16; +-31377 46421;-31361 45665;-34519 46331 1;-35077 46449;-35407 46513;-35975 46454;-38427 46211;-38511 46278;-38542 46588;-38415 46613;-36164 46865 1;-35015 46994;-34346 46830;-33216 46581;-32007 46347;-31759 46376;-31377 46421 18; +-33989 61628; +-35231 64254;-32936 64556;-33853 63455;-31445 46409; +-34473 64360;-33776 63546; +-33550 71448;-33138 71522;-33118 71342;-32241 64765;-33327 63371;-33070 61506;-32895 60233;-30992 46420;-31456 46487;-33853 63455;-32936 64556;-35210 64289;-35313 64276;-35361 64755;-35260 64766;-33383 65067;-33453 65612;-32763 65726;-32817 66146;-33527 71210;-33550 71448 18; +-33667 65606; +-34705 65275; +-38806 71339;-38763 71366;-36367 72843 1;-35318 73489;-33726 72632;-33555 71412;-32757 65717;-33457 65619;-33379 65059;-35290 64793;-35298 64792;-35276 64795;-35431 66096;-38293 65751;-38320 65977;-38443 67013;-37411 67135;-37722 69758;-38650 69648;-38802 70933;-38806 71339 18; +-47307 64464;-46775 64355;-46726 64229 1;-45346 60699;-44779 58632;-43525 55013 1;-43187 54038;-42917 53520;-42594 52545;-43101 52442;-43107 52489;-43242 52840 1;-44155 55230;-44646 56698;-45518 59212 1;-46160 61063;-46522 62061;-47215 63917 1;-47263 63869;-47311 63822;-47358 63774;-47555 63574 1;-48004 63107;-48432 62611;-48956 61973 1;-49101 61797;-49237 61627;-49364 61465;-51077 58849;-51365 59425;-51266 59574 1;-50954 60059;-50615 60550;-50194 61150 1;-49246 62502;-48590 63177;-47411 64333 1;-47392 64352;-47373 64370;-47354 64389;-47307 64464 18; +-39825 65575;-39857 65898;-39352 65949;-39554 67941;-40770 68246;-40949 70018;-41418 69722;-41554 68932 1;-41649 68394;-41867 68067;-42319 67760 1;-43741 66794;-44548 66256;-45859 65144 1;-46200 64854;-46499 64592;-46771 64343 1;-45358 60743;-44792 58671;-43525 55013 1;-43187 54038;-42917 53520;-42594 52545;-38237 53080 32;-37871 50064 32;-38881 49941 32;-38479 46606;-36164 46865 1;-35015 46994;-34346 46830;-33216 46581;-32007 46347;-31408 46417 16; +-35245 64256;-32924 64570;-33840 63471;-33825 63254;-31456 46487;-31787 46373;-32007 46347;-33216 46581 1;-34346 46830;-35015 46994;-36164 46865;-38479 46606;-38881 49941 32;-37871 50064 32;-38237 53080;-38057 53092 1;-37288 53178;-36798 53227;-35978 53323 1;-35646 53362;-35401 53577;-35440 53909 1;-35480 54252;-35721 54340;-36064 54300 1;-37873 54088;-38887 53970;-40697 53758 1;-41073 53714;-41374 53485;-41345 53220 1;-41304 52848;-41219 52935;-40901 52742;-40870 52746;-41245 52711;-42594 52545 1;-42917 53520;-43187 54038;-43525 55013 1;-44792 58671;-45358 60743;-46771 64343 1;-46499 64592;-46200 64854;-45859 65144 1;-44548 66256;-43741 66794;-42319 67760 1;-41867 68067;-41649 68394;-41554 68932;-41418 69722;-40949 70018;-40770 68246;-39554 67941;-39352 65949;-39857 65898;-39828 65602;-41348 65409;-40729 60192;-35277 60785;-35460 62478;-35032 62547;-35245 64256 18; +-33423 46296;-56291 43387; +-51335 59480;-51047 58887;-51356 58383 1;-51545 58047;-51745 57690;-51966 57298 1;-53207 55101;-53659 53736;-54520 51364 1;-55031 49957;-55288 49150;-55606 47687 1;-55926 46212;-56090 45370;-56173 43863 1;-56264 42212;-56222 41053;-56137 39713;-56603 39688 1;-56753 42136;-56771 43573;-56399 46029 1;-56053 48311;-55710 49581;-54931 51753 1;-54115 54030;-53583 55289;-52480 57442 1;-52144 58099;-51853 58622;-51551 59117;-51335 59480 18; +-52800 55710 1;-52562 56205;-52291 56723;-51966 57298 1;-51631 57891;-51346 58406;-51069 58885;-51029 58537 16; +-54459 53030;-55545 53375 1;-55319 52873;-55179 52509;-55335 51981 1;-55600 51084;-55808 50482;-56556 49921 1;-57061 49542;-57268 49036;-57148 48416 1;-57019 47753;-56892 47179;-56272 46910;-54459 53030 18; +-56182 43692 1;-56179 43748;-56176 43805;-56173 43863 1;-56090 45370;-55926 46212;-55606 47687 1;-55288 49150;-55031 49957;-54520 51364 1;-53943 52955;-53549 54093;-52978 55332 16; +-49479 61538;-47086 64277;-45593 60131;-42830 52458;-42559 49472;-42188 45944;-35329 46684;-31382 45971;-31265 44864;-31614 44824;-32195 44235;-43027 41206;-54969 39997;-56326 40021;-56474 42958;-56376 44783;-55784 48312;-54772 51199;-53612 54505;-52083 57762;-50553 60130;-49479 61538 18; +-55704 62465;-55163 62159;-55303 61913 1;-55779 61172;-56165 60474;-56726 59523 1;-57953 57445;-58373 56125;-59159 53844;-59272 53526;-59316 53403 1;-59711 53209;-59984 53003;-60093 52576 1;-60190 52197;-60060 51931;-59991 51546;-60127 51067;-60111 51041;-60272 50561;-60389 50149 1;-60659 48926;-60843 48252;-61019 47024 1;-61413 44284;-61345 42708;-61280 39941;-61898 39855;-61913 39998 1;-61983 42354;-61838 43723;-61661 46114 1;-61493 48379;-61076 49363;-60597 51583 1;-60573 51693;-60638 51935;-60724 52053 1;-60742 52078;-60727 52111;-60754 52125 1;-60794 52145;-60823 52146;-60867 52138;-61142 52796;-61131 52864;-60912 53018 1;-60185 53544;-59638 53954;-59351 54838;-59265 55090;-59190 55356 1;-58348 58002;-57396 59552;-55999 61909;-55704 62465 18; +-52203 63420;-49839 62905;-49727 62769 1;-49598 62640;-49440 62516;-49251 62400;-49333 62295 1;-49608 61959;-49885 61590;-50194 61150 1;-50629 60531;-50976 60028;-51295 59528;-51455 59613 1;-53238 60861;-54734 58412;-52499 57456;-52546 57313 1;-53391 55657;-53897 54524;-54473 53001;-54577 53067;-55545 53375 1;-55319 52873;-55179 52509;-55335 51981 1;-55600 51084;-55808 50482;-56556 49921 1;-57061 49542;-57268 49036;-57148 48416 1;-57019 47753;-56892 47179;-56272 46910;-56267 46850 1;-56312 46590;-56355 46317;-56399 46029 1;-56756 43672;-56754 42253;-56620 39979;-61280 39942 1;-61345 42708;-61413 44284;-61019 47024 1;-60843 48252;-60659 48926;-60389 50149;-60131 51055;-59986 50987 1;-59406 50738;-58995 50565;-58369 50772 1;-57927 50918;-57638 51185;-57469 51565;-57366 51861 1;-57354 51908;-57343 51956;-57333 52006 1;-57139 52999;-58197 53531;-59159 53844 1;-58373 56125;-57953 57445;-56726 59523 1;-56291 60259;-55962 60844;-55615 61414;-55359 61826 1;-55079 62266;-54770 62718;-54377 63244;-54310 63119 1;-54223 62950;-54113 62860;-53936 62758 1;-53415 62459;-52823 62602;-52427 63054 1;-52340 63153;-52277 63255;-52231 63361;-52203 63420 18; +-33371 76509;-32449 78301 1;-32303 78241;-31627 78101;-31561 78096;-31717 79529;-27639 79900;-27519 78801;-27239 78832;-27277 79178;-22806 80075;-22700 79545 1;-21289 79842;-20491 80013;-19053 80127 1;-18230 80192;-17459 80254;-16724 80314;-16258 80351;-15999 78229;-16424 78209;-16521 78202;-17028 78174;-17568 78145 1;-19710 78030;-20895 77706;-22997 77280 1;-23594 77159;-24145 77074;-24666 76995;-25022 76943;-25100 76930 1;-26580 76705;-27853 76468;-29347 75500 1;-30751 74591;-31482 72925;-31363 71702;-33138 71521;-33325 72809 1;-33329 72817;-33334 72824;-33339 72831;-33225 72961 1;-33207 72982;-33192 72998;-33186 73004 1;-32673 73464;-32377 74152;-32445 74891 1;-32508 75565;-32862 76142;-33371 76509 18; +-33309 78752;-34194 76880 1;-34418 76930;-34655 76947;-34897 76924 1;-35107 76904;-35308 76856;-35496 76784;-36120 78709;-34846 80001 1;-34367 79483;-33804 79059;-33309 78752 18; +-34194 76880;-33309 78752 1;-32975 78546;-32673 78393;-32449 78301;-33371 76509 1;-33615 76685;-33894 76813;-34194 76880 18; +-39308 35432; +-45153 33513; +-51033 32641; +-33566 36219; +-33467 37995; +-55791 39557; +-55442 36155; +-55250 38057; +-55591 32080 1;-55918 32040;-56100 32018;-56427 31978 1;-56982 31911;-57425 31160;-57092 30711 1;-56636 30097;-56426 29812;-55970 29198 1;-55719 28860;-55264 28995;-54850 29071; +-55893 31394; +-56608 30925; +-55733 30024; +-59767 29679 1;-59363 29725;-59025 29625;-58804 29284 1;-58444 28730;-58238 28416;-57793 27927 1;-57483 27586;-57307 27336;-57250 26878 1;-57159 26141;-57976 25636;-58718 25669; +-58755 27557; +-57928 26804; +-57964 22739;-57510 20995;-56586 20467;-55135 14784;-63153 14710;-69225 35585;-69269 36294;-69389 39172;-61419 39863;-61000 35224;-60185 30511;-59346 27081;-57964 22739 18; +-61221 38886 1;-59063 38913;-59067 36089;-61011 36042;-61221 38886 18; +-59767 29679 1;-59363 29725;-59025 29625;-58804 29284 1;-58444 28730;-58238 28416;-57793 27927 1;-57483 27586;-57307 27336;-57250 26878 1;-57159 26141;-57976 25636;-58718 25669;-59269 27158;-59767 29679 18; +-55591 32080 1;-55918 32040;-56100 32018;-56427 31978 1;-56982 31911;-57425 31160;-57092 30711 1;-56636 30097;-56426 29812;-55970 29198 1;-55719 28860;-55264 28995;-54850 29071;-55591 32080 18; +-55015 33534; +-54129 35123;-55705 34905; +-54016 34309;-55592 34091; +-51446 37789 32;-52104 37693 32;-52150 38010 32;-51492 38106 32;-51446 37789 50; +-45480 38614 32;-46138 38518 32;-46184 38835 32;-45526 38931 32;-45480 38614 50; +-39774 40470 32;-40432 40374 32;-40478 40691 32;-39820 40787 32;-39774 40470 50; +-33633 41318 32;-34291 41222 32;-34337 41539 32;-33679 41635 32;-33633 41318 50; +-31614 44789 32;-32608 44652 32;-32580 44452 32;-36395 43925;-36422 44122 32;-38458 43842 32;-38431 43644;-42273 43114;-42300 43313 32;-43365 43167 32;-43211 42100 32;-44333 41945 32;-44305 41741 32;-48010 41231;-48034 41408 32;-50070 41128 32;-50046 40951;-53936 40416;-53960 40592 32;-55025 40446 32;-54573 37176 32;-53883 37271;-53912 37486;-52110 37734;-52149 38011 32;-51489 38102 32;-51451 37825;-49597 38081;-49568 37864 32;-48886 37958 32;-48915 38175;-48526 38228;-48496 38011 32;-47814 38105 32;-47844 38322;-46144 38556;-46184 38835 32;-45526 38931 32;-45485 38647;-43558 38913;-43528 38693 32;-42846 38787 32;-42992 39834 32;-42194 39944 32;-42224 40163;-40437 40409;-40478 40691 32;-39820 40787 32;-39778 40500;-37915 40757;-37885 40535 32;-37203 40629 32;-37233 40851;-36843 40904;-36813 40682 32;-36131 40776 32;-36161 40998;-34296 41255;-34337 41539 32;-33679 41635 32;-33637 41346;-31876 41589;-31845 41364 32;-31163 41458 32;-31614 44789 50; +-55835 35844 1;-55907 36399;-55954 36948;-56000 37669 1;-56138 39820;-56273 41136;-56207 43105 16; +-40430 40395;-40255 39127 32;-41963 38891 32;-42917 37632 32;-44715 37384 32;-44895 38689 48; +-46134 38506;-45956 37213 32;-50645 36566 32;-50825 37871 48; +-34293 41234;-34119 39973 32;-39078 39290 32;-39254 40564 48; +-52100 37696;-51920 36390 32;-55835 35844 48; +-39146 31003;-38971 29735 32;-40679 29499 32;-41633 28240 32;-43431 27992 32;-43611 29297 48; +-44850 29114;-44672 27821 32;-49361 27174 32;-49541 28479 48; +-33009 31842;-32835 30581 32;-37794 29898 32;-37970 31172 48; +-50816 28304;-50636 26998 32;-53749 26564 48; +-48832 37243; +-54554 31014; +-42377 39441; +-34613 40540; +-30983 41115; +-30617 38045; +-30440 35637; +-31751 35612; +-41423 37615; +-30608 40457;-32946 40135 32;-33122 41409 48; +-31174 41491;-30720 41553;-30568 40438; +-29951 35210;-29374 31033; +-50162 28403 32;-50820 28307 32;-50866 28624 32;-50208 28720 32;-50162 28403 50; +-44196 29228 32;-44854 29132 32;-44900 29449 32;-44242 29545 32;-44196 29228 50; +-30330 35403 32;-31324 35266 32;-31296 35066 32;-35111 34539;-35138 34736 32;-37174 34456 32;-37147 34258;-40989 33728;-41016 33927 32;-42081 33781 32;-41927 32714 32;-43049 32559 32;-43021 32355 32;-46726 31845;-46750 32022 32;-48786 31742 32;-48762 31565;-52652 31030;-52676 31206 32;-53741 31060 32;-53289 27790 32;-52599 27885;-52628 28100;-50826 28348;-50865 28625 32;-50205 28716 32;-50167 28439;-48313 28695;-48284 28478 32;-47602 28572 32;-47631 28789;-47242 28842;-47212 28625 32;-46530 28719 32;-46560 28936;-44860 29170;-44900 29449 32;-44242 29545 32;-44201 29261;-42274 29527;-42244 29307 32;-41562 29401 32;-41708 30448 32;-40910 30558 32;-40940 30777;-39153 31023;-39194 31305 32;-38536 31401 32;-38494 31114;-36631 31371;-36601 31149 32;-35919 31243 32;-35949 31465;-35559 31518;-35529 31296 32;-34847 31390 32;-34877 31612;-33012 31869;-33053 32153 32;-32395 32249 32;-32353 31960;-30592 32203;-30561 31978 32;-29879 32072 32;-30330 35403 50; +-32349 31932 32;-33007 31836 32;-33053 32153 32;-32395 32249 32;-32349 31932 50; +-38490 31084 32;-39148 30988 32;-39194 31305 32;-38536 31401 32;-38490 31084 50; +-48923 19010 32;-49581 18914 32;-49627 19231 32;-48969 19327 32;-48923 19010 50; +-37251 21691 32;-37909 21595 32;-37955 21912 32;-37297 22008 32;-37251 21691 50; +-31110 22539 32;-31768 22443 32;-31814 22760 32;-31156 22856 32;-31110 22539 50; +-42957 19835 32;-43615 19739 32;-43661 20056 32;-43003 20152 32;-42957 19835 50; +-29091 26010 32;-30085 25873 32;-30057 25673 32;-33872 25146;-33899 25343 32;-35935 25063 32;-35908 24865;-39750 24335;-39777 24534 32;-40842 24388 32;-40688 23321 32;-41810 23166 32;-41782 22962 32;-45487 22452;-45511 22629 32;-47547 22349 32;-47523 22172;-51413 21637;-51437 21813 32;-52502 21667 32;-52050 18397 32;-51360 18492;-51389 18707;-49587 18955;-49626 19232 32;-48966 19323 32;-48928 19046;-47074 19302;-47045 19085 32;-46363 19179 32;-46392 19396;-46003 19449;-45973 19232 32;-45291 19326 32;-45321 19543;-43621 19777;-43661 20056 32;-43003 20152 32;-42962 19868;-41035 20134;-41005 19914 32;-40323 20008 32;-40469 21055 32;-39671 21165 32;-39701 21384;-37914 21630;-37955 21912 32;-37297 22008 32;-37255 21721;-35392 21978;-35362 21756 32;-34680 21850 32;-34710 22072;-34320 22125;-34290 21903 32;-33608 21997 32;-33638 22219;-31773 22476;-31814 22760 32;-31156 22856 32;-31114 22567;-29353 22810;-29322 22585 32;-28640 22679 32;-29091 26010 50; +-31596 44818;-31261 44864;-31375 45692; +-31425 46505;-31403 46341;-31369 45718;-31345 45657;-31252 44864;-31604 44821;-31160 41501;-30733 41571;-30637 40480;-30384 38369;-29939 35201;-30262 35159;-29905 32332;-29076 32436;-29122 32851;-31016 46601;-31425 46505 18; +-30580 40475;-30361 38381;-30769 38404;-32290 38194 32;-32459 39404 32;-41855 38108 32;-42809 36849 32;-53709 35346 32;-53552 34210 32;-55492 33937;-55835 35844;-55674 35867;-51920 36390 32;-52099 37686;-50834 37885;-50806 37731;-50645 36566 32;-45956 37213 32;-46134 38506;-44895 38689 32;-44715 37384 32;-42917 37632 32;-41963 38891 32;-40255 39127 32;-40430 40395;-39225 40557;-39238 40446;-39078 39290 32;-34119 39973 32;-34293 41234;-33118 41409;-33094 41206;-32946 40135 32;-30811 40429;-30580 40475 18; +-30511 39452;-32087 39234; +-30398 38638;-31974 38420; +-56602 39755;-56140 39763;-56130 39605 1;-56092 39016;-56046 38389;-56000 37669 1;-55954 36948;-55907 36399;-55835 35844;-55790 35834;-55450 33984;-55500 33903;-55488 33863 1;-55183 32378;-54927 31328;-54607 30009;-55067 29978 1;-55253 30721;-55435 31454;-55659 32409 1;-56301 35152;-56421 36754;-56595 39562;-56602 39755 18; +-56412 40193;-34289 42496;-30555 42252;-29334 32865;-41617 31818;-53481 30318;-55121 30597;-55888 34331;-56377 38274;-56412 40193 18; +-51750 27283; +-53179 27436; +-54575 30466;-53652 26731;-52920 26833; +-50961 28271;-50787 27007; +-49389 28487;-49215 27223; +-45007 29101;-44833 27837; +-43459 29314;-43285 28050; +-39301 30979;-39127 29715; +-37821 31195;-37647 29931; +-33168 31836;-32994 30572; +-31681 32041;-31507 30777; +-29949 31275; +-30899 31509; +-35636 30732; +-39091 28302; +-35945 28771; +-33860 28894; +-29455 26957; +-32811 26807 1;-33107 26726;-33414 26900;-33496 27196 1;-33578 27492;-33404 27799;-33107 27881 1;-32811 27963;-32504 27789;-32422 27492 1;-32341 27196;-32515 26889;-32811 26807 18; +-32811 26807 1;-33107 26726;-33414 26900;-33496 27196 1;-33578 27492;-33404 27799;-33107 27881 1;-32811 27963;-32504 27789;-32422 27492 1;-32341 27196;-32515 26889;-32811 26807 18; +-32021 27980; +-31160 29989 32;-29238 30254 32;-29072 29051 32;-30994 28786 32;-31160 29989 50; +-31649 28654 1;-31669 28447;-31632 28243;-31439 28166 1;-31146 28049;-30934 27974;-30637 28079 1;-30177 28241;-29724 28412;-29695 28899;-31649 28654 18; +-29766 28631;-29766 28631 1;-29915 28340;-30274 28207;-30637 28079 1;-30934 27974;-31146 28049;-31439 28166 1;-31535 28204;-31593 28274;-31623 28360; +-53206 24684;-50941 25001 32;-51098 26137 32;-41525 27457 32;-40571 28716 32;-31899 29912 32;-31724 28641 32;-29041 29011 48; +-29324 31065;-31662 30743 32;-31838 32017 48; +-30282 35135;-29939 35182; +-29896 32315;-29553 32362; +-41323 23741; +-42310 23877; +-40065 25518; +-42853 26593; +-42101 28814; +-47309 28053; +-47019 26218 1;-46574 26420;-46335 26576;-45859 26687 1;-45564 26756;-45158 26509;-45242 26218 1;-45398 25680;-45538 25382;-45859 24923 1;-45991 24734;-46276 24649;-46439 24812 1;-46716 25089;-46850 25265;-47130 25539 1;-47322 25727;-47264 26107;-47019 26218 18; +-53742 26539 1;-53915 27173;-54093 27866;-54286 28675 1;-54778 30741;-55095 31944;-55503 33935;-53552 34210 32;-53709 35346 32;-42809 36849 32;-41855 38108 32;-32459 39404 32;-32291 38183 32;-30396 38444;-29556 32347 16; +-50965 25033 32;-52951 24758 32;-53099 25828 32;-51113 26102 32;-50965 25033 50; +-53300 25852;-53137 24670; +-49991 26256 1;-49764 25620;-49440 25352;-48997 24842 1;-48656 24450;-48355 24329;-47880 24118 1;-47632 24008;-47505 23749;-47584 23490 1;-47708 23081;-48211 23186;-48639 23194 1;-49360 23207;-49680 22644;-50401 22644 1;-51187 22644;-51642 22368;-52268 21894 1;-52623 21625;-52962 21723;-53402 21789;-53321 21930 1;-53189 22166;-53101 22324;-52957 22582 1;-52791 22879;-52757 23120;-52852 23446 1;-52982 23891;-53112 24335;-53223 24713;-53045 24707;-50941 25001 32;-51098 26137 32;-51001 26150;-49991 26256 18; +-53079 21754; +-55101 30062;-54607 30099;-54563 29827 1;-54476 29469;-54385 29089;-54286 28675 1;-54093 27866;-53915 27173;-53742 26539;-53628 26581;-50636 26998 32;-50816 28304;-49540 28481;-49361 27174 32;-44672 27821 32;-44850 29114;-43609 29284;-43431 27992 32;-41633 28240 32;-40679 29499 32;-38971 29735 32;-39143 30978;-37961 31110;-37794 29898 32;-35797 30173;-35586 30202;-32835 30581 32;-33009 31842;-31834 31987;-31662 30743 32;-29324 31065;-29205 30260;-31142 29976;-31006 28767;-31216 28711;-31724 28641 32;-31899 29912 32;-40571 28716 32;-41525 27457 32;-51098 26137;-53318 25816;-53204 24674;-53139 24427 1;-53049 24119;-52950 23783;-52852 23446 1;-52757 23120;-52791 22879;-52957 22582 1;-53119 22292;-53210 22129;-53372 21839 1;-53518 21577;-53452 21358;-53378 21078;-53872 21126 1;-53883 21166;-53894 21208;-53905 21249 1;-54012 21648;-53906 21931;-53696 22287 1;-53564 22511;-53555 22525;-53423 22748 1;-53131 23242;-53431 23757;-53582 24311;-54524 27770;-55101 30062 18; +-55185 30938;-29991 35195;-29363 30728;-40773 29123;-41645 27832;-53928 26262;-55185 30938 18; +-29044 25776;-28589 25837; +-29029 29056;-28581 25840;-30049 25655;-40817 24146;-40625 23065;-45510 22437;-46069 24757 1;-45987 24790;-45911 24849;-45859 24923 1;-45538 25382;-45398 25680;-45242 26218 1;-45158 26509;-45564 26756;-45859 26687 1;-46335 26576;-46574 26420;-47019 26218 1;-47195 26138;-47275 25919;-47234 25732;-47793 24070 1;-47820 24088;-47849 24104;-47880 24118 1;-48355 24329;-48656 24450;-48997 24842 1;-49440 25352;-49764 25620;-49991 26256;-50051 26250;-49617 26341;-41525 27457 32;-40571 28716 32;-31899 29912 32;-31724 28641 32;-29029 29056 18; +-45508 22628;-45986 24801 1;-46012 24783;-46040 24769;-46069 24757;-46142 24735 1;-46248 24712;-46359 24732;-46439 24812 1;-46716 25089;-46850 25265;-47130 25539 1;-47198 25606;-47235 25697;-47243 25791;-47889 24121;-47743 24032 1;-47588 23901;-47521 23696;-47584 23490 1;-47708 23081;-48211 23186;-48639 23194 1;-49360 23207;-49680 22644;-50401 22644 1;-51187 22644;-51642 22368;-52268 21894 1;-52623 21625;-52962 21723;-53402 21789;-53444 21654 1;-53485 21467;-53434 21290;-53378 21078;-53331 20896 1;-53082 19919;-52855 19067;-52643 18296;-52047 18372;-52502 21667 32;-51437 21813 32;-51413 21637;-47523 22172;-47547 22349 32;-46078 22551;-45508 22628 18; +-53130 18623 1;-53505 18525;-53814 18159;-53679 17795 1;-53410 17071;-53295 16642;-52912 15971 1;-52770 15723;-52631 15759;-52345 15753; +-54528 14403 1;-54263 14379;-54250 14382;-53986 14353 1;-53497 14299;-53364 13389;-53171 12368 1;-53101 11999;-53328 11753;-53701 11566 1;-53905 11464;-54075 11739;-54133 11960 1;-54345 12767;-54505 13209;-54700 14021 1;-54738 14180;-54691 14418;-54528 14403 18; +-54235 13920; +-53594 12883; +-53754 11995; +-52390 6808 1;-51975 6954;-51519 6736;-51372 6320 1;-51226 5905;-51444 5449;-51860 5303 1;-52275 5156;-52731 5374;-52877 5790 1;-53024 6206;-52806 6661;-52390 6808 18; +-52390 6808 1;-51975 6954;-51519 6736;-51372 6320 1;-51226 5905;-51444 5449;-51860 5303 1;-52275 5156;-52731 5374;-52877 5790 1;-53024 6206;-52806 6661;-52390 6808 18; +-49148 7144; +-41499 13180; +-45354 12657; +-28931 17317; +-30344 18800; +-34200 18608; +-36486 18329; +-35561 17858; +-39469 17806; +-43684 17526; +-44320 18897; +-43841 18390; +-47478 16392; +-50462 15363; +-49816 17932; +-49940 18804; +-49627 16178;-49357 14222; +-49698 16140;-49802 16891; +-51624 15258 1;-51257 14451;-50426 14154;-49556 14322; +-53170 17456; +-54528 14403 1;-54263 14379;-54250 14382;-53986 14353 1;-53497 14299;-53364 13389;-53171 12368 1;-53101 11999;-53328 11753;-53701 11566 1;-53905 11464;-54075 11739;-54133 11960 1;-54345 12767;-54505 13209;-54700 14021 1;-54738 14180;-54691 14418;-54528 14403 18; +-53130 18623 1;-53505 18525;-53814 18159;-53679 17795 1;-53410 17071;-53295 16642;-52912 15971 1;-52770 15723;-52631 15759;-52345 15753;-53130 18623 18; +-48958 19015;-48337 14517; +-48501 19077;-48258 17315;-48584 16884; +-52672 18069;-49466 18511; +-52632 18257 1;-52863 19091;-53086 19935;-53360 21010 1;-53440 21324;-53530 21556;-53372 21839 1;-53210 22129;-53119 22292;-52957 22582 1;-52791 22879;-52757 23120;-52852 23446 1;-52982 23891;-53112 24335;-53223 24713 16; +-49579 18937;-49544 18686 16; +-49493 18318;-49303 16939;-49701 16884;-49602 16167 16; +-49561 18911;-48951 18998;-48322 14445;-49308 14174;-49622 16172;-49692 16879;-49308 16957;-49451 18269;-49522 18754;-49561 18911 18; +-49175 14276;-49362 14224 1;-50375 13940;-51511 14350;-51805 15360 1;-52090 16341;-52318 17129;-52533 17899 16; +-51336 18469;-49553 18697;-49572 18926;-51348 18691;-51336 18469 18; +-52539 17877;-49473 18297;-49360 17431;-49308 16957;-49692 16879;-49622 16172;-49611 16104;-49467 14312;-49716 14296 1;-50453 14202;-51143 14448;-51521 15063;-51729 15151 1;-51758 15218;-51784 15287;-51805 15360 1;-51973 15937;-52121 16448;-52258 16928;-52539 17877 18; +-48976 19008;-48510 19073;-48484 18957;-48258 17315;-48538 16944;-48662 16962;-48976 19008 18; +-44032 19679;-43895 18696;-43489 18745;-43252 17024; +-42587 19880;-42475 19069; +-42648 19068;-42383 17149; +-42570 19903;-42204 17085;-43242 16928;-43268 17139;-43489 18745;-43895 18696;-44032 19679;-42570 19903 18; +-43225 17022 1;-44107 16876;-45008 16675;-46118 16432;-46375 15061;-48366 14503 16; +-37914 21593;-37610 19384;-38700 19234;-38433 17291; +-36709 21768;-36509 20316;-37053 20241;-36647 17296; +-38525 19252;-38254 17282; +-37916 21608;-36721 21783;-36690 21630;-36509 20316;-37053 20241;-36647 17296;-36633 17220;-38117 17220;-38396 19262;-38240 19297;-37610 19384;-37892 21435;-37916 21608 18; +-31835 19229;-31569 17300; +-31810 22459;-31367 19248;-31702 19202; +-29034 29018;-28400 24579;-26907 23160;-26178 17897 1;-26033 16924;-26852 16010;-27836 16001 1;-28454 15995;-28801 16027;-29419 16035;-30187 17282;-30528 17289 16; +-30688 21939;-30583 21181;-31026 21120;-30498 17283 16; +-28057 24240 1;-28294 23530;-28235 22999;-28146 22256;-30712 21905 16; +-29028 25768;-28590 25842;-28386 24565;-28043 24245;-28090 24135 1;-28288 23476;-28231 22962;-28146 22256;-30681 21909;-30583 21181;-31026 21120;-30498 17283;-31445 17301;-31698 19194;-31518 19227;-31367 19248;-31807 22435;-29334 22815;-29325 22614;-28627 22736;-29028 25768 18; +-28052 24216;-27912 24115;-26907 23160;-26178 17897 1;-26033 16924;-26852 16010;-27836 16001 1;-28454 15995;-28801 16027;-29419 16035;-30187 17282;-30528 17289;-31961 17319;-32042 17321 1;-35005 17299;-36666 17281;-39629 17296 1;-40765 17302;-41701 17237;-42586 17118;-43514 16972 1;-44305 16832;-45129 16649;-46118 16432;-46375 15061;-48366 14503;-48373 14812;-48670 16961;-48591 16952;-48538 16944;-48258 17315;-48484 18957;-48509 19068;-43827 20292;-38767 22211;-30636 23258;-28052 24216 18; +-31374 17307;-32042 17321 1;-33879 17307;-35216 17295;-36672 17292 16; +-38059 17291 1;-38547 17291;-39064 17293;-39629 17296 1;-40765 17302;-41701 17237;-42586 17118 16; +-53902 21169;-53390 21113;-52678 18318;-52545 17834;-52467 17633;-52258 16928 1;-52121 16448;-51973 15937;-51805 15360 1;-51791 15311;-51775 15264;-51757 15218;-51682 15054 1;-51270 14275;-50280 13975;-49382 14219;-48244 14537;-46375 15061;-46118 16432 1;-45052 16665;-44179 16860;-43330 17004;-42087 17179 1;-41346 17259;-40554 17301;-39629 17296 1;-39150 17294;-38706 17292;-38284 17291;-36576 17292 1;-35157 17296;-33839 17307;-32042 17321;-31484 17309;-30435 17287;-30187 17282;-29419 16035 1;-28801 16027;-28454 15995;-27836 16001 1;-26852 16010;-26033 16924;-26178 17897;-26702 21682;-26714 21765;-26907 23160;-28400 24579;-28580 25837;-28159 25860;-28007 24759;-26550 23363;-26252 21066;-26237 20947;-25869 18111 1;-25715 16920;-26491 15603;-27691 15634;-29681 15685;-30421 16871;-30784 16871;-31750 16870;-34757 16869 1;-36651 16868;-37714 16868;-39608 16867 1;-42037 16866;-43386 16497;-45775 16058;-46045 14671;-49421 13825 1;-50599 13530;-51909 14034;-52204 15212 1;-52707 17219;-53121 18455;-53620 20217;-53902 21169 18; +-29530 32450;-29061 32499;-29045 32290;-28152 25808;-28583 25799;-28590 25842;-28593 25841;-28627 26165;-29034 29018;-29236 30294;-29375 31166;-29530 32450 18; +-24683 53659 32;-27196 53327 32;-26565 48547 32;-24052 48879 32;-24683 53659 50; +-23918 47614 32;-26410 47285 32;-25795 42631 32;-23303 42960 32;-23918 47614 50; +-23140 41717 32;-25632 41388 32;-24540 33118 32;-23253 33288 16; +-23052 32327 32;-24404 32144 32;-23762 27392 32;-22410 27575 32;-23052 32327 50; +-22312 26392;-23599 26219;-23452 25129;-24502 23752;-24145 21100;-21622 21440; +-25230 57467;-27761 58204;-27290 54531; +-27132 53341;-26499 48553; +-26338 47287;-25724 42633; +-21968 24107 32;-24428 23775 32;-24080 21197 32;-21620 21529 48; +-23542 21897; +-22415 22663 1;-22194 22699;-22137 22913;-22084 23131 1;-21998 23484;-21937 23996;-22298 24038 1;-22494 24060;-22697 23866;-22685 23670 1;-22670 23411;-22763 22605;-22415 22663 18; +-22685 23668 1;-22671 23406;-22762 22605;-22415 22663 1;-22196 22699;-22138 22909;-22085 23125; +-26230 52054; +-26082 49253; +-25774 46786; +-24737 45035; +-25083 41075; +-22290 38408; +-22611 37643; +-24096 35868; +-23404 30045; +-22189 17432; +-21066 17012; +-25571 41398;-25165 38321 16; +-25013 37168;-24478 33119 16; +-25056 38262 32;-23560 38459 32;-23427 37451 32;-24923 37254 32;-25056 38262 50; +-25056 38262 32;-23560 38459 32;-23427 37451 32;-24923 37254 32;-25056 38262 50; +-26783 53227;-25985 53332; +-25982 47143;-25184 47248; +-27751 58202;-25172 57449;-24827 54883;-25123 54846;-27292 54560;-27751 58202 18; +-27129 53324;-24711 53657;-24094 48883;-24266 48851;-26462 48561;-27129 53324 18; +-26326 47289;-23910 47603;-23291 42971;-23560 42926;-25709 42642;-26326 47289 18; +-25558 41410;-23141 41724;-22871 39744;-21536 39910;-21046 36376;-21291 36337;-23590 36025 32;-23221 33290;-23460 33261;-24481 33126;-25558 41410 18; +-24339 32146;-23074 32321;-22454 27558;-23711 27410;-24339 32146 18; +-23600 26222;-22298 26401;-21983 24130;-24426 23810;-24401 23902;-23451 25142;-23600 26222 18; +-24394 23748;-21964 24062;-21655 21558;-24055 21249;-24394 23748 18; +-23888 20067;-21463 20363;-21176 18265;-20606 18346;-20533 17928;-19923 18013;-19657 16199;-21226 16160 1;-22327 16155;-23453 16672;-23601 17763;-23689 18415;-23888 20067 18; +-10916 15685;-21530 15665 1;-22702 15721;-23869 16448;-24030 17610;-24898 23872;-23850 25224;-28204 57568;-29623 58604;-29859 60374; +-29866 60446;-29325 60490;-27808 58300;-27275 54554;-24876 54894;-24710 53647;-27127 53350;-26448 48566;-24102 48880;-23953 47615;-26326 47309;-25697 42623;-23316 42955;-23167 41716;-25540 41402;-24462 33120;-23241 33299;-23081 32312;-24339 32145;-23696 27390;-22469 27575;-22308 26397;-23597 26218;-23468 25127;-24430 23823;-24055 21180;-21630 21468;-21499 20395;-23898 20063;-23851 19760;-23689 18415;-23601 17763 1;-23453 16672;-22327 16155;-21226 16160;-21183 16160;-21133 16169;-20134 16113;-20172 15636;-21565 15664 1;-22737 15720;-23862 16395;-24023 17557;-24659 22145;-24806 23210;-24898 23872;-23850 25224;-28204 57568;-29623 58604;-29811 60014;-29866 60446 18; +-33150 71560;-31379 71752;-31267 70927;-31107 69630;-29859 60374;-29745 59518;-29623 58604;-28204 57568;-23850 25224;-24898 23872;-24030 17610 1;-23908 16726;-23204 16094;-22358 15818;-27064 15749 1;-26233 16092;-25744 17143;-25869 18111;-26550 23363;-28007 24759;-33327 63371;-32241 64765;-33056 70876;-33150 71560 18; +-57964 22739;-57475 20862;-56586 20467;-55138 14795;-63156 14721;-69225 35585;-69269 36294;-69389 39172;-61419 39863;-61000 35224;-60185 30511;-59346 27081;-57964 22739 18; +-61292 39998;-56641 40097;-56603 39688 1;-56604 39704;-56605 39719;-56606 39735;-56591 39504 1;-56419 36732;-56297 35133;-55659 32409 1;-55633 32298;-55607 32189;-55582 32083;-55839 32050 1;-56032 32026;-56192 32007;-56427 31978 1;-56982 31911;-57425 31160;-57092 30711 1;-56636 30097;-56426 29812;-55970 29198 1;-55719 28860;-55264 28995;-54850 29071;-54815 28925;-54524 27770;-53582 24311 1;-53431 23757;-53131 23242;-53423 22748 1;-53555 22525;-53564 22511;-53696 22287 1;-53906 21931;-54012 21648;-53905 21249 1;-53894 21208;-53883 21166;-53872 21126;-53811 20903 1;-53571 20024;-53353 19291;-53141 18577;-53251 18581 1;-53568 18444;-53799 18119;-53679 17795 1;-53410 17071;-53295 16642;-52912 15971 1;-52770 15723;-52631 15759;-52345 15753;-52297 15545;-54740 15595;-56245 20999;-57697 21763;-58885 25711;-58588 25669 1;-57884 25696;-57164 26184;-57250 26878 1;-57307 27336;-57483 27586;-57793 27927 1;-58238 28416;-58444 28730;-58804 29284 1;-59025 29625;-59363 29725;-59767 29679;-59946 29684;-60909 35013;-61007 36074;-60776 36062 1;-59070 36321;-59154 38912;-61221 38886;-61378 39232;-61353 39800;-61292 39998 18; +-57678 21794;-56247 21029;-54742 15588;-54394 14400 16; +-42375 10646; +-46806 8710; +-45044 8675; +-43387 8692; +-41119 9425; +-38345 9442; +-36094 9442; +-34367 9407; +-34070 6302; +-44644 5639; +-44644 4679; +-47871 4819; +-33477 3842;-48412 3825; +-36513 4033;-36513 6007; +-40374 4021;-40374 6193; +-39798 1566;-39800 3553; +2190 -4523;2195 53;2212 12685 1;2213 13560;1238 14411;363 14412;-20139 14209;-36427 14189 1;-39657 14185;-41498 14096;-44668 13473 1;-46567 13100;-47611 12793;-49490 12332 1;-50688 12038;-50976 10566;-50624 9383 1;-49635 6061;-48887 4237;-47322 1144 1;-46482 -517;-45905 -1401;-44805 -2902 1;-43220 -5066;-42173 -6172;-40500 -8269 1;-39636 -9352;-38684 -10157;-37342 -9811 1;-34300 -9027;-32598 -8569;-29530 -7891 1;-27431 -7427;-26235 -7221;-24096 -7006 1;-20512 -6646;-18476 -6639;-14876 -6766 1;-10303 -6928;-7742 -7097;-3169 -7272 1;-2052 -7315;-1679 -7356;-561 -7383 1;1139 -7424;2190 -6333;2190 -4523 18; +-48242 4219 1;-48963 6244;-49423 7361;-50084 9406 1;-50120 9518;-50141 9582;-50177 9694 1;-50489 10660;-49968 11750;-48985 12005 1;-46697 12599;-45419 12993;-43079 13331 1;-41171 13607;-40086 13729;-38159 13724;-33768 13713;-33730 6230 1;-33729 6052;-33905 5942;-34083 5941;-34423 5939; +-35399 12739 32;-34709 12742 32;-34706 12049 32;-35396 12046 32;-35399 12739 50; +-35399 12739 32;-34709 12742 32;-34706 12049 32;-35396 12046 32;-35399 12739 50; +-35685 11756; +-37711 13447 1;-37685 12918;-37597 12558;-37483 11980 1;-37408 11596;-37160 11127;-36769 11124 1;-36471 11122;-36203 11120;-35949 11118 33;-35313 11113;-34769 11108;-34069 11107; +-16624 12666;-16623 13633;-23966 13561;-23870 1907; +-32640 3154;-32631 2046;-29373 2072; +-26007 13147;-26005 13579;-29630 13592; +-25882 1919 32;-24983 1919 32;-24989 13737 32;-25888 13737 32;-25882 1919 50; +-25882 1919 32;-24983 1919 32;-24989 13737 32;-25888 13737 32;-25882 1919 50; +-32597 2016;-29369 2051;-29376 2434;-29376 3209 32;-28754 3209 32;-28754 13095 32;-26054 13095;-26006 13286;-26005 13579;-29630 13592;-30102 13587;-32725 13561;-32659 5475;-32597 2016 18; +-29630 13592;-32725 13561;-32640 3154; +-23686 2118;-20141 2146; +-25809 914;-22913 914; +-29058 907;-32617 907;-32617 -2565; +-25786 907;-29058 907; +-15369 907;-22951 907; +-15381 914;-12019 914; +-40197 5746;-33489 5746; +-43822 6028;-43822 4104; +-43387 5392;-40612 5392; +-42751 3537;-42749 1575; +-41368 65008;-41417 65415; +-47312 2989; +-47841 3430 1;-46664 1149;-46054 -174;-44591 -2282 1;-43534 -3806;-42867 -4609;-41678 -6033;-41569 -6163;-40038 -5377;-40050 -4390 16; +-35210 2086; +-33779 3135; +-34507 3209; +-38899 3110; +-42057 3011; +-44660 3011; +-46252 1642; +-42785 -2726; +-41660 -3608; +-45280 -2748; +-44784 -3391; +-45547 -1858 1;-45595 -2281;-45726 -2659;-45442 -2975 1;-45255 -3183;-45169 -3319;-44962 -3507 1;-44760 -3691;-44522 -3741;-44273 -3629; +-45547 -1858 1;-45595 -2281;-45726 -2659;-45442 -2975 1;-45255 -3183;-45169 -3319;-44962 -3507 1;-44760 -3691;-44522 -3741;-44273 -3629;-45547 -1858 18; +-42949 1606;-42548 1607; +-37577 1793;-39471 1793; +-41358 -3158;-41919 -3160 32;-41921 -2479 32;-41360 -2477 48; +-41358 -3158;-41919 -3160 32;-41921 -2479 32;-41360 -2477 32;-41358 -3158 18; +-43351 -1218;-42827 -494;-42807 -670 1;-42799 -733;-42794 -798;-42792 -869 1;-42781 -1325;-42534 -1799;-42077 -1794;-41397 -1787;-41379 -2335;-42199 -2318 1;-42758 -2286;-43310 -2016;-43344 -1458 1;-43346 -1422;-43347 -1386;-43349 -1351;-43351 -1218 18; +-42821 -539;-43339 -1242;-43361 -1106 1;-43379 -944;-43430 -807;-43591 -730 1;-43817 -623;-43987 -487;-43998 -237 1;-44029 481;-44102 868;-44085 1577 1;-44081 1740;-44012 1906;-43850 1922 1;-43494 1958;-43271 1965;-42937 1836;-42968 1540 1;-43159 1170;-43247 877;-43150 457 1;-43071 116;-42948 -117;-42869 -386;-42821 -539 18; +-41656 -1790;-41656 -1790;-42077 -1794 1;-42534 -1799;-42781 -1325;-42792 -869 1;-42794 -798;-42799 -733;-42807 -670;-42827 -494;-42874 -369 1;-42953 -107;-43073 123;-43150 457 1;-43226 785;-43189 1035;-43078 1305; +-43259 1926;-43259 1926 1;-43445 1956;-43619 1945;-43850 1922 1;-44012 1906;-44081 1740;-44085 1577 1;-44102 868;-44029 481;-43998 -237 1;-43987 -487;-43817 -623;-43591 -730 1;-43430 -807;-43379 -944;-43361 -1106;-43339 -1242;-43346 -1418 1;-43345 -1431;-43345 -1445;-43344 -1458 1;-43315 -1940;-42898 -2207;-42425 -2292; +-34519 5948;-34239 5940;-34083 5941 1;-33905 5942;-33729 6052;-33730 6230;-33755 11166;-33893 11107 1;-34969 11108;-35660 11116;-36769 11124 1;-37160 11127;-37408 11596;-37483 11980 1;-37614 12645;-37711 13021;-37719 13698;-38060 13724;-38159 13724 1;-40086 13729;-41171 13607;-43079 13331 1;-45419 12993;-46697 12599;-48985 12005 1;-49968 11750;-50489 10660;-50177 9694 1;-50141 9582;-50120 9518;-50084 9406;-48860 5919;-47150 5966;-47122 7623;-47122 8362 32;-42159 8362 32;-42159 9067 32;-34430 9067 32;-34430 7083;-34519 5948 18; +-43560 6025;-43560 5572; +-45503 5964;-43788 5964;-43788 5563; +-45505 6377 32;-43350 6377 32;-43350 5993 32;-45505 5993 32;-45505 6377 50; +-33736 5533;-33718 4216; +-34879 1608;-33719 2280;-33736 3474; +-36201 5533;-33722 5533;-33734 4226;-36201 4238;-36201 5533 18; +-48922 6051;-47133 6026;-45517 5965;-43308 5977;-36449 6001;-36486 3731;-33747 3620;-33737 3275;-33723 2280;-34879 1608;-37535 1671;-37757 1782;-40237 1831;-41285 1745;-41371 -3992;-43130 -4264 1;-43597 -3672;-44041 -3075;-44591 -2282 1;-46054 -174;-46664 1149;-47841 3430;-48289 4350 1;-48497 4932;-48683 5440;-48860 5919;-48922 6051 18; +-20952 14227;-21002 13635;-23950 13573;-23910 2006;-17315 2023;-17350 889;-32790 994;-32807 1918;-24975 1930;-24974 13748;-32895 13695;-32912 14219;-25567 14201;-20952 14227 18; +-34918 171;-32735 183;-32846 13708;-33771 13690;-33721 6216;-33823 6002;-34005 5915;-33703 5585;-33706 5493;-33690 4259;-33687 4129;-33703 3503;-33718 3377;-33724 2281;-34897 1606;-34918 171 18; +-33706 3456;-34184 3450; +-32852 13681;-32861 14187;-33506 14193;-36427 14189 1;-39657 14185;-41498 14096;-44668 13473 1;-46567 13100;-47611 12793;-49490 12332 1;-50688 12038;-50976 10566;-50624 9383 1;-49635 6061;-48887 4237;-47322 1144 1;-46572 -338;-46032 -1202;-45143 -2437;-44847 -2831;-44273 -3629;-44105 -3842 1;-43963 -4030;-43824 -4211;-43688 -4386;-43159 -4227 1;-43615 -3648;-44053 -3058;-44591 -2282 1;-46029 -210;-46643 1103;-47781 3314;-47857 3397;-47900 3505;-48249 4165;-48289 4273;-48345 4507 1;-48531 5024;-48699 5484;-48860 5919;-48922 6051;-48957 6182 1;-49330 7194;-49667 8115;-50084 9406 1;-50120 9518;-50141 9582;-50177 9694 1;-50489 10660;-49968 11750;-48985 12005 1;-46697 12599;-45419 12993;-43079 13331 1;-41171 13607;-40086 13729;-38159 13724;-35168 13717;-33782 13727;-33752 13622;-32852 13681 18; +-21031 14215;-21043 15646;-21142 15655;-21565 15664 1;-22042 15687;-22512 15812;-22911 16031;-26716 15947 1;-26824 15867;-26940 15800;-27064 15749;-27497 15641 1;-27560 15635;-27625 15632;-27691 15634;-29681 15685;-30421 16871;-31505 16871;-32615 16870;-34757 16869 1;-36651 16868;-37714 16868;-39608 16867 1;-42037 16866;-43386 16497;-45775 16058;-46045 14671;-49421 13825 1;-50275 13611;-51198 13817;-51754 14395;-50748 10660 1;-50666 11447;-50271 12140;-49490 12332 1;-47611 12793;-46567 13100;-44668 13473 1;-41498 14096;-39657 14185;-36427 14189;-28174 14199;-27335 14205;-25567 14201;-22054 14221;-21031 14215 18; +-50382 -5664 1;-50250 -5726;-50093 -5682;-50025 -5553 1;-49730 -4996;-49617 -4654;-49445 -4048 1;-49327 -3633;-49416 -3323;-49654 -2963 1;-49861 -2650;-50155 -2550;-50530 -2568 1;-51092 -2595;-51427 -2788;-51838 -3172 1;-52123 -3438;-52144 -3770;-52048 -4147 1;-51939 -4572;-51736 -4824;-51345 -5023 1;-50984 -5207;-50527 -5039;-50370 -4665 1;-50198 -4258;-50177 -3706;-50592 -3555 1;-50850 -3461;-51052 -3541;-51283 -3691 1;-51397 -3765;-51384 -3917;-51320 -4036 1;-51265 -4138;-51131 -4106;-51024 -4061 1;-50895 -4007;-50669 -4008;-50678 -4147 1;-50692 -4371;-50726 -4640;-50950 -4653 1;-51285 -4672;-51596 -4490;-51653 -4159 1;-51717 -3791;-51612 -3470;-51295 -3271 1;-50928 -3040;-50545 -3017;-50185 -3259 1;-49759 -3546;-49801 -4117;-50049 -4566 1;-50221 -4877;-50311 -5055;-50481 -5368 1;-50539 -5475;-50492 -5612;-50382 -5664 18; +-49099 -4406 1;-49031 -4271;-48916 -4059;-48803 -4159 1;-48703 -4247;-48623 -4284;-48569 -4406 1;-48509 -4542;-48609 -4705;-48754 -4739 1;-48984 -4794;-49085 -4977;-49136 -5208 1;-49187 -5438;-49247 -5721;-49025 -5800 1;-48817 -5874;-48622 -5710;-48544 -5504 1;-48512 -5418;-48493 -5371;-48461 -5285 1;-48433 -5212;-48368 -5140;-48297 -5171 1;-48191 -5218;-48252 -5361;-48297 -5467 1;-48476 -5892;-48807 -6354;-49235 -6183 1;-49531 -6065;-49746 -5741;-49605 -5455 1;-49404 -5047;-49305 -4816;-49099 -4406 18; +-52278 -7997 1;-52045 -7531;-51861 -6922;-52313 -6662 1;-52661 -6462;-53123 -6597;-53298 -6959 1;-53415 -7200;-53477 -7339;-53551 -7596 1;-53615 -7819;-53436 -8088;-53228 -8189 1;-52876 -8359;-52453 -8347;-52278 -7997 18; +-55165 -8974 1;-55084 -9120;-54841 -9096;-54738 -8965 1;-54609 -8801;-54464 -8618;-54589 -8451 1;-54728 -8266;-55009 -8270;-55182 -8424 1;-55264 -8497;-55283 -8587;-55261 -8695 1;-55238 -8808;-55221 -8873;-55165 -8974 18; +-54319 -11382 1;-54207 -11519;-53927 -11471;-53874 -11303 1;-53766 -10962;-53581 -10635;-53839 -10387 1;-53993 -10240;-54082 -10158;-54258 -10038 1;-54407 -9935;-54707 -9988;-54703 -10169 1;-54693 -10636;-54649 -10979;-54319 -11382 18; +-54502 -5345 1;-54146 -5276;-53701 -5401;-53673 -5763 1;-53643 -6149;-53513 -6459;-53769 -6749 1;-53931 -6932;-54170 -7063;-54371 -6924 1;-54710 -6689;-55084 -6471;-54991 -6069 1;-54914 -5737;-54837 -5410;-54502 -5345 18; +-50382 -5664 1;-50250 -5726;-50093 -5682;-50025 -5553 1;-49730 -4996;-49617 -4654;-49445 -4048 1;-49327 -3633;-49416 -3323;-49654 -2963 1;-49861 -2650;-50155 -2550;-50530 -2568 1;-51092 -2595;-51427 -2788;-51838 -3172 1;-52123 -3438;-52144 -3770;-52048 -4147 1;-51939 -4572;-51736 -4824;-51345 -5023 1;-50984 -5207;-50527 -5039;-50370 -4665 1;-50198 -4258;-50177 -3706;-50592 -3555 1;-50850 -3461;-51052 -3541;-51283 -3691 1;-51397 -3765;-51384 -3917;-51320 -4036 1;-51265 -4138;-51131 -4106;-51024 -4061 1;-50895 -4007;-50669 -4008;-50678 -4147 1;-50692 -4371;-50726 -4640;-50950 -4653 1;-51285 -4672;-51596 -4490;-51653 -4159 1;-51717 -3791;-51612 -3470;-51295 -3271 1;-50928 -3040;-50545 -3017;-50185 -3259 1;-49759 -3546;-49801 -4117;-50049 -4566 1;-50221 -4877;-50311 -5055;-50481 -5368 1;-50539 -5475;-50492 -5612;-50382 -5664 18; +-49099 -4406 1;-49031 -4271;-48916 -4059;-48803 -4159 1;-48703 -4247;-48623 -4284;-48569 -4406 1;-48509 -4542;-48609 -4705;-48754 -4739 1;-48984 -4794;-49085 -4977;-49136 -5208 1;-49187 -5438;-49247 -5721;-49025 -5800 1;-48817 -5874;-48622 -5710;-48544 -5504 1;-48512 -5418;-48493 -5371;-48461 -5285 1;-48433 -5212;-48368 -5140;-48297 -5171 1;-48191 -5218;-48252 -5361;-48297 -5467 1;-48476 -5892;-48807 -6354;-49235 -6183 1;-49531 -6065;-49746 -5741;-49605 -5455 1;-49404 -5047;-49305 -4816;-49099 -4406 18; +-55165 -8974 1;-55084 -9120;-54841 -9096;-54738 -8965 1;-54609 -8801;-54464 -8618;-54589 -8451 1;-54728 -8266;-55009 -8270;-55182 -8424 1;-55264 -8497;-55283 -8587;-55261 -8695 1;-55238 -8808;-55221 -8873;-55165 -8974 18; +-52278 -7997 1;-52045 -7531;-51861 -6922;-52313 -6662 1;-52661 -6462;-53123 -6597;-53298 -6959 1;-53415 -7200;-53477 -7339;-53551 -7596 1;-53615 -7819;-53436 -8088;-53228 -8189 1;-52876 -8359;-52453 -8347;-52278 -7997 18; +-54502 -5345 1;-54146 -5276;-53701 -5401;-53673 -5763 1;-53643 -6149;-53513 -6459;-53769 -6749 1;-53931 -6932;-54170 -7063;-54371 -6924 1;-54710 -6689;-55084 -6471;-54991 -6069 1;-54914 -5737;-54837 -5410;-54502 -5345 18; +-54319 -11382 1;-54207 -11519;-53927 -11471;-53874 -11303 1;-53766 -10962;-53581 -10635;-53839 -10387 1;-53993 -10240;-54082 -10158;-54258 -10038 1;-54407 -9935;-54707 -9988;-54703 -10169 1;-54693 -10636;-54649 -10979;-54319 -11382 18; +-59015 64185 1;-59209 62728;-59831 62001;-60718 60829 1;-63344 57356;-64705 55319;-67010 51625 1;-68817 48728;-69582 46842;-70193 43483 1;-70691 40742;-70650 39128;-70415 36352 1;-70205 33866;-69810 32488;-69057 30110;-64621 15057;-63917 12681 1;-63706 11843;-63032 11477;-62427 11530 1;-61592 11603;-61133 11858;-60329 12097 1;-59081 12467;-58226 12542;-56996 12115 1;-55934 11747;-55465 11044;-55042 10003 1;-54771 9336;-54656 8937;-54554 8224 1;-54488 7759;-54485 7090;-54955 7090 1;-55355 7090;-55606 7458;-55635 7857 1;-55735 9209;-56383 11435;-58188 11120 1;-59756 10847;-60488 10855;-61986 10318 1;-62948 9973;-62935 8831;-62771 7823 16; +-55112 8084; +-55010 7313;-54776 5302; +-54579 8005 1;-54003 7472;-53753 6975;-53715 6191; +-55236 15606 1;-55143 15238;-55050 14995;-55175 14637 1;-55279 14338;-55264 14130;-55175 13826 1;-54884 12830;-54655 12292;-54364 11296 1;-54259 10934;-53828 10935;-53727 10572 1;-53415 9456;-53228 8833;-52956 7707 1;-52855 7289;-53080 7033;-53290 6658 1;-53559 6178;-53450 5738;-53191 5252 1;-53003 4899;-52700 4829;-52315 4722 1;-52006 4636;-51827 4473;-51698 4179 1;-50549 1551;-49856 86;-48379 -2372 1;-47158 -4404;-46430 -5548;-44827 -7294 1;-43556 -8679;-42878 -9494;-41765 -11009 1;-41363 -11556;-41571 -12114;-41124 -12625 1;-40690 -13120;-40447 -13398;-40014 -13893 1;-39570 -14400;-39000 -14271;-38546 -14771;-38110 -15378 16; +-40845 -11492 1;-40225 -11480;-39757 -11539;-39340 -11998 1;-39051 -12315;-39043 -12630;-39056 -13059 1;-39061 -13245;-39102 -13537;-39279 -13479 1;-39749 -13325;-39959 -13079;-40278 -12701 1;-40287 -12691;-41321 -11501;-40845 -11492 18; +-40623 -11838; +-39624 -12282; +-39464 -13170; +-40874 -11498 1;-42498 -9356;-43504 -8230;-45167 -6119 1;-45895 -5196;-46326 -4689;-46944 -3689 1;-48487 -1195;-49301 243;-50572 2887;-51694 5354; +-53503 11661;-52230 6845; +-57477 20981;-57674 21758;-57294 21589;-56247 21029;-54742 15588;-54394 14400;-54596 14396 1;-54675 14327;-54728 14139;-54700 14021 1;-54505 13209;-54345 12767;-54133 11960 1;-54075 11739;-53905 11464;-53701 11566 1;-53629 11602;-53563 11640;-53503 11681;-53458 11492;-52230 6845;-52583 6709 1;-52869 6509;-52999 6136;-52877 5790 1;-52731 5374;-52275 5156;-51860 5303 1;-51804 5323;-51751 5348;-51702 5379;-51639 5233;-50572 2887 1;-49301 243;-48487 -1195;-46944 -3689 1;-46326 -4689;-45895 -5196;-45167 -6119 1;-43504 -8230;-42498 -9356;-40874 -11498;-40954 -11700 1;-40850 -12043;-40285 -12694;-40278 -12701 1;-39959 -13079;-39749 -13325;-39279 -13479 1;-39271 -13482;-39263 -13484;-39255 -13485;-39520 -14231 1;-39695 -14159;-39864 -14064;-40014 -13893 1;-40447 -13398;-40690 -13120;-41124 -12625 1;-41571 -12114;-41363 -11556;-41765 -11009 1;-42878 -9494;-43556 -8679;-44827 -7294 1;-46430 -5548;-47158 -4404;-48379 -2372 1;-49856 86;-50549 1551;-51698 4179 1;-51827 4473;-52006 4636;-52315 4722 1;-52700 4829;-53003 4899;-53191 5252 1;-53450 5738;-53559 6178;-53290 6658 1;-53080 7033;-52855 7289;-52956 7707 1;-53228 8833;-53415 9456;-53727 10572 1;-53828 10935;-54259 10934;-54364 11296 1;-54655 12292;-54884 12830;-55175 13826 1;-55264 14130;-55279 14338;-55175 14637 1;-55050 14995;-55143 15238;-55236 15606;-56565 20459;-57477 20981 18; +-51879 1376 32;-50818 -869 32;-53004 -1902 32;-54065 343 32;-51879 1376 50; +-54988 -943; +-52731 377; +-52200 -795; +-51879 1376 32;-50818 -869 32;-53004 -1902 32;-54065 343 32;-51879 1376 50; +-57771 9943; +-59473 13485;-59281 12211; +-57030 13310;-57344 12019; +-55268 12089;-55896 11269; +-58028 16520; +-57138 15211; +-59319 15403; +-61186 15351; +-55530 -3822;-56507 -2181;-58060 -1763;-59196 -1030;-61282 6867 32;-62930 9321 32;-63455 11687 32;-62868 13094 32;-63557 15497 16; +-62283 14638;-61934 12894 1;-62167 12362;-62598 12087;-62493 11515; +-62825 11575;-62171 11619; +-62781 11995;-62165 11865; +-62584 12427;-62029 12124; +-62399 12704;-61813 12501; +-62245 12883;-61616 12994; +-62199 10122;-60941 8197; +-62296 10059;-61038 8134; +-62094 10191;-60836 8266; +-62832 12955;-62656 12842 1;-62524 12800;-62366 12800;-62177 12846 1;-60743 13194;-59861 13450;-58374 13423 1;-57251 13403;-56529 13169;-55670 12446 1;-55514 12315;-55427 12184;-55349 12042;-55836 11403;-55968 11509 1;-56239 11765;-56569 11967;-56996 12115 1;-58226 12542;-59081 12467;-60329 12097 1;-61133 11858;-61592 11603;-62427 11530 1;-62864 11492;-63140 11579;-63405 11806;-62832 12955 18; +-55853 11368;-55313 12031;-55220 11803 1;-55188 11747;-55153 11688;-55112 11626 1;-54774 11119;-54387 10945;-54187 10369 1;-53834 9353;-53814 8732;-53577 7683 1;-53481 7260;-53537 6870;-53705 6527;-53779 6704 1;-53879 7161;-54091 7519;-54452 7883;-54550 8192 1;-54551 8203;-54553 8214;-54554 8224 1;-54656 8937;-54771 9336;-55042 10003 1;-55212 10422;-55390 10787;-55611 11096;-55853 11368 18; +-54318 6954; +-54493 6098; +-63269 14630;-55138 14770;-55211 14518 1;-55273 14279;-55252 14088;-55175 13826 1;-54884 12830;-54655 12292;-54364 11296 1;-54259 10934;-53828 10935;-53727 10572 1;-53415 9456;-53228 8833;-52956 7707 1;-52855 7289;-53080 7033;-53290 6658 1;-53525 6238;-53471 5849;-53281 5432;-60460 4110;-61245 6797;-62868 9326;-63461 11839;-62815 13147;-63269 14630 18; +-65376 64636;-65374 64652 1;-65836 63819;-66154 63251;-66602 62426; +-57781 64048;-57781 64048 1;-57856 63505;-57981 63125;-58252 62575 1;-58930 61200;-59556 60569;-60423 59305;-60633 59058;-60669 58982;-60910 58621;-62539 56182;-62958 55554;-63931 54097;-64291 53574;-65028 52501;-65219 52207;-65685 51420; +-66760 49586;-66793 49527 1;-67934 47499;-68579 46242;-68955 43922 1;-69218 42301;-69380 41013;-69424 39724; +-49773 -1087;-52163 4513; +-49971 -1171;-52361 4429; +-43599 -9210 1;-44418 -8251;-45610 -6918;-46321 -5876 1;-47598 -4006;-48194 -3144;-49294 -1165; +-44474 -8506 1;-45293 -7547;-45787 -7031;-46498 -5989 1;-47775 -4119;-48379 -3244;-49479 -1265; +-51617 3640; +-50420 975; +-49063 -2589; +-46250 -5489; +-49016 -15376 32;-48633 -16202 32;-49451 -16581 32;-49834 -15754 32;-49016 -15376 50; +-50163 -15302; +-48646 -14759; +-47766 -12262; +-48621 -11424; +-47556 -13850; +-47748 -15751; +-52546 -11616; +-54064 -14286; +-47212 -18076;-48109 -17578; +-49873 -18752;-49379 -20072; +-52241 -19270;-52456 -21078; +-51893 -23714;-53655 -23121 32;-54318 -20417 32;-54064 -18106 32;-54489 -16703;-55641 -12899;-56688 -8206 32;-58345 -7683 32;-59217 -3844 32;-58310 -1681 16; +-62771 7823;-60406 -867 1;-60120 -1704;-59758 -2359;-58905 -2594 1;-58304 -2759;-57954 -2802;-57352 -2961 1;-57137 -3018;-56986 -3175;-56986 -3397 1;-56986 -3606;-57303 -3527;-57510 -3502 1;-58059 -3435;-58691 -3316;-58888 -3833 1;-59143 -4502;-58561 -5902;-58324 -6578 1;-57863 -7895;-58225 -7666;-57806 -9179;-55314 -18178 1;-55009 -19279;-54431 -19932;-53223 -20156 1;-51929 -20396;-51072 -20466;-49862 -19935 1;-48184 -19199;-47179 -18431;-46390 -16777 1;-45474 -14857;-45271 -13377;-45919 -11351 1;-46143 -10652;-46278 -10080;-46948 -9780 1;-47505 -9530;-48329 -9208;-49088 -9222 1;-49365 -9227;-49366 -8812;-49101 -8812 1;-47592 -8812;-45819 -9363;-45420 -10818 1;-45070 -12093;-44869 -12794;-44896 -14116 1;-44925 -15534;-45201 -16418;-45978 -17605 1;-47038 -19223;-48334 -20177;-50008 -21146 1;-50462 -21409;-50524 -20935;-51044 -21004 1;-51598 -21078;-51383 -21614;-51908 -21804 1;-53439 -22359;-53206 -23288;-53219 -24480;-53219 -24480 1;-53227 -25227;-52985 -26815;-52632 -28281;-50429 -37634;-49492 -41321 16; +-56725 -8195;-56794 -6206; +-60101 63913 1;-60162 62768;-60773 62234;-61433 61297 1;-64079 57541;-65669 55497;-68021 51551 1;-69747 48654;-70429 46776;-71031 43458 1;-71597 40338;-71743 38631;-71327 35488 1;-71154 34176;-70812 33303;-70439 32033;-65529 15082;-60936 -1028;-58686 -9106;-55777 -19551;-52969 -30231;-50724 -39435;-48577 -48244;-47763 -51581 16; +-58532 -6318;-56910 -6265;-56787 -8097;-58393 -7591;-58532 -6318 18; +-58794 -3229 1;-58506 -3855;-58315 -4202;-58148 -4870;-57834 -6297;-58550 -6353;-59213 -3788;-58794 -3229 18; +-58615 -2673 1;-58041 -2880;-57673 -3105;-57393 -3648 1;-57211 -4000;-57012 -4301;-56616 -4301 1;-56074 -4301;-55735 -4313;-55247 -4079 1;-55001 -3961;-54949 -3644;-55074 -3401 1;-55262 -3033;-55454 -3135;-55864 -3080;-56505 -2266;-58319 -1674;-58615 -2673 18; +-55528 -3143 1;-55320 -3227;-55193 -3169;-55074 -3401 1;-54949 -3644;-55001 -3961;-55247 -4079 1;-55735 -4313;-56074 -4301;-56616 -4301 1;-57012 -4301;-57211 -4000;-57393 -3648 1;-57601 -3244;-57858 -3016;-58213 -2840; +-57307 -6251;-57850 -6275 32;-57897 -6013;-58148 -4870 1;-58275 -4360;-58417 -4037;-58605 -3635; +-50679 -7657 1;-50559 -7177;-50497 -6745;-50853 -6401 1;-51190 -6076;-51458 -5889;-51708 -5493 1;-51887 -5209;-52074 -5158;-52406 -5109 1;-52605 -5079;-52976 -5143;-52877 -5319 1;-52682 -5665;-52442 -5778;-52092 -5964 1;-51519 -6268;-51269 -6731;-51219 -7378 1;-51201 -7615;-50737 -7887;-50679 -7657 18; +-49404 -5319 1;-48591 -3512;-48246 -4882;-48393 -5301 1;-48541 -5724;-48766 -6133;-49213 -6104 1;-49532 -6083;-49535 -5611;-49404 -5319 18; +-54046 -4534 1;-54093 -4092;-54779 -4255;-55180 -4446 1;-55912 -4795;-56242 -5517;-56087 -6313 1;-55978 -6876;-56042 -7313;-55529 -7570 1;-55278 -7696;-54917 -8048;-54761 -7814 33;-54554 -7502;-55878 -7296;-55599 -5982 1;-55487 -5453;-55363 -4925;-54831 -4830 1;-54508 -4772;-54011 -4860;-54046 -4534 18; +-53960 -4892 1;-53827 -4841;-53704 -4710;-53697 -4516 1;-53691 -4365;-53534 -4298;-53383 -4289 1;-53012 -4266;-52671 -4242;-52511 -3911 1;-52488 -3837;-52468 -3752;-52448 -3662 1;-52410 -3490;-52381 -3328;-52335 -3168 1;-52358 -2924;-52594 -2626;-52371 -2562 1;-52258 -2530;-52179 -2632;-52113 -2759 1;-52047 -2734;-51986 -2691;-51913 -2655 1;-51297 -2347;-50958 -2150;-50308 -1922 1;-49944 -1794;-49566 -1931;-49384 -2271 1;-48819 -3324;-48399 -3867;-47953 -4976 1;-47725 -5542;-47918 -6089;-48389 -6476 1;-48747 -6771;-49145 -6682;-49576 -6511 1;-50330 -6212;-50894 -6141;-51390 -5499 1;-51542 -5303;-51623 -5190;-51774 -4993 1;-51990 -4710;-52309 -4774;-52664 -4801 1;-52907 -4819;-53046 -4944;-53188 -5142 1;-53368 -5393;-53110 -5718;-52830 -5846 1;-52226 -6122;-51703 -6383;-51621 -7042 1;-51533 -7747;-51651 -8384;-52263 -8745 1;-52549 -8914;-52901 -9025;-53114 -8770 1;-53234 -8626;-53340 -8579;-53509 -8499 1;-53735 -8392;-53974 -8646;-54015 -8893 1;-54066 -9196;-54162 -9525;-53891 -9671 1;-53734 -9756;-53507 -9797;-53435 -9634 1;-53386 -9524;-53449 -9434;-53521 -9338 1;-53586 -9252;-53644 -9144;-53570 -9066 1;-53470 -8961;-53279 -8945;-53200 -9066 1;-52977 -9404;-52954 -9802;-53226 -10102 1;-53447 -10346;-53262 -10615;-53312 -10941 1;-53382 -11395;-53443 -11737;-53818 -12002 1;-54097 -12200;-54483 -11988;-54632 -11681 1;-54994 -10934;-55389 -10498;-55323 -9670 1;-55298 -9360;-55415 -9180;-55582 -8918 1;-55771 -8623;-55951 -8406;-55866 -8066 1;-55848 -7993;-55834 -7954;-55816 -7881 1;-55783 -7748;-55589 -7750;-55471 -7820 1;-55183 -7990;-54975 -8093;-54644 -8042 1;-54474 -8016;-54432 -7728;-54558 -7610 1;-54999 -7197;-55446 -6955;-55458 -6351 1;-55470 -5727;-55274 -5051;-54656 -4970 1;-54374 -4933;-54135 -4917;-53960 -4892 18; +-54308 -4377; +-51813 -2527; +-54500 -2719; +-50308 -1922 1;-50958 -2150;-51297 -2347;-51913 -2655 1;-51979 -2688;-52036 -2726;-52084 -2769;-52084 -2747 1;-52094 -2751;-52103 -2755;-52113 -2759 1;-52179 -2632;-52258 -2530;-52371 -2562 1;-52558 -2616;-52422 -2834;-52360 -3047;-52318 -3169 1;-52378 -3313;-52409 -3482;-52448 -3662 1;-52476 -3790;-52505 -3907;-52544 -4013;-52564 -4000 1;-52739 -4247;-53049 -4268;-53383 -4289 1;-53534 -4298;-53691 -4365;-53697 -4516 1;-53703 -4692;-53805 -4815;-53922 -4875;-53925 -4877 1;-54144 -4917;-54379 -4934;-54656 -4970 1;-55274 -5051;-55470 -5727;-55458 -6351 1;-55446 -6955;-54999 -7197;-54558 -7610 1;-54432 -7728;-54474 -8016;-54644 -8042 1;-54975 -8093;-55183 -7990;-55471 -7820 1;-55589 -7750;-55783 -7748;-55816 -7881 1;-55834 -7954;-55848 -7993;-55866 -8066 1;-55951 -8406;-55771 -8623;-55582 -8918 1;-55415 -9180;-55298 -9360;-55323 -9670 1;-55389 -10498;-54994 -10934;-54632 -11681 1;-54483 -11988;-54097 -12200;-53818 -12002 1;-53443 -11737;-53382 -11395;-53312 -10942 1;-53262 -10615;-53447 -10346;-53226 -10102 1;-52954 -9802;-52977 -9404;-53200 -9066 1;-53279 -8945;-53470 -8961;-53570 -9066 1;-53644 -9144;-53586 -9252;-53521 -9338 1;-53449 -9434;-53386 -9524;-53435 -9634 1;-53507 -9797;-53734 -9756;-53891 -9671 1;-54162 -9525;-54066 -9196;-54015 -8893 1;-53974 -8646;-53735 -8392;-53509 -8499 1;-53340 -8579;-53234 -8626;-53114 -8770 1;-52901 -9025;-52549 -8914;-52263 -8745 1;-51651 -8384;-51533 -7747;-51621 -7042 1;-51703 -6383;-52226 -6122;-52830 -5846 1;-53110 -5718;-53368 -5393;-53188 -5142 1;-53046 -4944;-52907 -4819;-52664 -4801 1;-52309 -4774;-51990 -4710;-51774 -4993 1;-51623 -5190;-51542 -5303;-51390 -5499 1;-50894 -6141;-50330 -6212;-49576 -6511 1;-49145 -6682;-48747 -6771;-48389 -6476 1;-47918 -6089;-47725 -5542;-47953 -4976 1;-48399 -3867;-48819 -3324;-49384 -2271 1;-49566 -1931;-49944 -1794;-50308 -1922 18;-49654 -2963 1;-49416 -3323;-49327 -3633;-49445 -4048 1;-49617 -4654;-49730 -4996;-50025 -5553 1;-50093 -5682;-50250 -5726;-50382 -5664 1;-50492 -5612;-50539 -5475;-50481 -5368 1;-50311 -5055;-50221 -4877;-50049 -4566 1;-49801 -4117;-49759 -3546;-50185 -3259 1;-50545 -3017;-50928 -3040;-51295 -3271 1;-51612 -3470;-51717 -3791;-51653 -4159 1;-51596 -4490;-51285 -4672;-50950 -4653 1;-50726 -4640;-50692 -4371;-50678 -4147 1;-50669 -4008;-50895 -4007;-51024 -4061 1;-51131 -4106;-51265 -4138;-51320 -4036 1;-51384 -3917;-51397 -3765;-51283 -3691 1;-51052 -3541;-50850 -3461;-50592 -3555 1;-50177 -3706;-50198 -4258;-50370 -4665 1;-50527 -5039;-50984 -5207;-51345 -5023 1;-51736 -4824;-51939 -4572;-52048 -4147 1;-52144 -3770;-52123 -3438;-51838 -3172 1;-51427 -2788;-51092 -2595;-50530 -2568 1;-50155 -2550;-49861 -2650;-49654 -2963 18;-48803 -4159 1;-48703 -4247;-48623 -4284;-48569 -4406 1;-48509 -4542;-48609 -4705;-48754 -4739 1;-48984 -4794;-49085 -4977;-49136 -5208 1;-49187 -5438;-49247 -5721;-49025 -5800 1;-48817 -5874;-48622 -5710;-48544 -5504 1;-48512 -5418;-48493 -5371;-48461 -5285 1;-48433 -5212;-48368 -5140;-48297 -5171 1;-48191 -5218;-48252 -5361;-48297 -5467 1;-48476 -5892;-48807 -6354;-49235 -6183 1;-49531 -6065;-49746 -5741;-49605 -5455 1;-49404 -5047;-49305 -4816;-49099 -4406 1;-49031 -4271;-48916 -4059;-48803 -4159 18;-53673 -5763 1;-53643 -6149;-53513 -6459;-53769 -6749 1;-53931 -6932;-54170 -7063;-54371 -6924 1;-54710 -6689;-55084 -6471;-54991 -6069 1;-54914 -5737;-54837 -5410;-54502 -5345 1;-54146 -5276;-53701 -5401;-53673 -5763 18;-52313 -6662 1;-51861 -6922;-52045 -7531;-52278 -7997 1;-52453 -8347;-52876 -8359;-53228 -8189 1;-53436 -8088;-53615 -7819;-53551 -7596 1;-53477 -7339;-53415 -7200;-53298 -6959 1;-53123 -6597;-52661 -6462;-52313 -6662 18;-54589 -8451 1;-54464 -8618;-54609 -8801;-54738 -8965 1;-54841 -9096;-55083 -9120;-55165 -8975 1;-55221 -8873;-55238 -8808;-55261 -8695 1;-55283 -8587;-55264 -8497;-55182 -8424 1;-55009 -8270;-54728 -8266;-54589 -8451 18;-54258 -10038 1;-54082 -10158;-53993 -10240;-53839 -10387 1;-53581 -10635;-53766 -10962;-53874 -11303 1;-53927 -11471;-54207 -11519;-54319 -11382 1;-54649 -10979;-54693 -10636;-54703 -10169 1;-54707 -9988;-54407 -9935;-54258 -10038 18; +-60601 4204;-53125 5611;-52903 5166;-51718 4624;-49917 972;-48437 -1742;-46586 -4580;-43946 -8034;-44538 -8454;-46191 -8429;-48190 -9959;-46907 -14351;-47672 -16745;-49473 -18373;-52434 -18373;-53667 -16276;-53741 -14845;-55173 -14104;-56628 -8183;-58355 -7640;-59170 -3889;-58380 -1619;-59219 -953;-60601 4204 18; +-54886 -14896 1;-54531 -15193;-54172 -15301;-54066 -15751 1;-53978 -16127;-54054 -16539;-54415 -16676;-54886 -14896 18; +-54537 -15151 1;-54318 -15304;-54136 -15452;-54066 -15751 1;-54021 -15945;-54019 -16148;-54077 -16318; +-66899 14843;-63544 15707;-63494 15065;-63137 14198;-62815 13147;-63461 11839;-62868 9326;-61245 6797;-60460 4110;-60353 3351;-59196 -1030;-58266 -1630;-59152 -3810;-58239 -7708;-56734 -8128;-56512 -8893;-60114 -9016;-66899 14843 18; +-54765 15636;-52287 15618;-52238 15348 1;-52227 15303;-52215 15258;-52204 15212 1;-52100 14795;-51868 14463;-51561 14220;-50717 10907;-50751 10672;-50748 10660;-50764 10315 1;-50761 10002;-50713 9683;-50624 9383 1;-49635 6061;-48887 4237;-47322 1144 1;-46696 -94;-46216 -901;-45556 -1853;-45573 -2051 1;-45626 -2397;-45682 -2708;-45442 -2975 1;-45255 -3183;-45169 -3319;-44962 -3507 1;-44763 -3688;-44530 -3739;-44285 -3634;-44166 -3765;-44105 -3842 1;-43988 -3996;-43874 -4146;-43761 -4292;-45814 -5300 1;-46203 -4803;-46532 -4355;-46944 -3689 1;-48487 -1195;-49301 243;-50572 2887;-51694 5354;-51587 5466 1;-51357 5675;-51263 6009;-51372 6320 1;-51501 6684;-51866 6896;-52233 6846;-52289 7068;-53458 11492;-53503 11681;-53324 11833 1;-53191 11978;-53130 12152;-53171 12368 1;-53364 13389;-53497 14299;-53986 14353 1;-54219 14379;-54256 14379;-54444 14396;-54569 15002;-54765 15636 18; +55495 60745;54495 65448;55701 65415;55753 67280;55520 67286;55648 72240;56755 72211;56748 71959;58504 71910;58483 71175;59214 71286;59256 70971;59630 71027;59147 74731;61751 75071;62468 69576;59436 69180;58440 68075;58328 62711;57441 62509;57374 61117;55495 60745 18; +55110 60288;53916 65757;54394 65825;54473 65484;55488 60713;55553 60376;55110 60288 18; +53927 65718;55105 60328;54725 60254;54692 60421;51437 59778;51356 60189;50415 60004;50388 60140;49129 59890;49186 59237;43071 58730;43085 58525;42929 58341;41745 58239;41561 58409;41480 59441;37608 61462;35888 61287;35718 61432;35498 63352;35263 63537;35159 64451;38345 64872;38598 65133;39002 64647;39515 65073;40093 64378;39836 64164;40261 63654;43287 62003;43747 62059;43606 63219;44459 63323;44605 62124;46782 62389;47406 63698;48787 63866;48671 64821;49026 64864;48895 65949;53287 66695;53849 66257;53927 65718 18; +47899 77413;48306 74326 32;52587 74844 32;54629 65459; +47377 70799; +46592 71741; +43829 70729;45372 71308;45255 72690;44292 72597 16; +45217 73580;44237 73490; +43729 71353 32;44071 71384 32;43962 72583 32;44623 72643 32;44576 73161 32;44277 73134 32;44202 73966 32;43498 73902 32;43729 71353 50; +43717 71397;44083 71418;43885 73611; +44081 73129;44036 73622; +43871 71371;43669 73587; +43477 73923;44233 73992;44310 73151; +43992 72538;44413 72576; +44277 73122;44322 72617; +41371 74306;41198 75466;41282 75556;41665 75953;44656 75553;45093 74633;45190 73578;44270 73517;44221 74004;43468 73942;43413 74627;42994 74929;42568 74485;41371 74306 18; +46493 74704; +45926 77103; +47671 76510; +47819 75182; +47928 73576; +48754 72812; +54166 66983;53375 67659;52149 67442;52002 68392;52732 68505;52223 71788;50313 71492;50451 70602;47492 70147;45971 71512;45752 74647;44883 76336;45058 76971 16; +46475 70076 1;46202 69909;45629 69114;45000 70364;45065 70433;45803 70723;46320 70256;46475 70076 18; +46223 69878 1;45951 69668;45572 69471;45159 70088; +41780 65885;43028 64603;43377 64646;43717 62108;43316 62038;40254 63678;39817 64158;40480 64646;40428 64742;41780 65885 18; +47811 63760 32;48755 63874 32;48703 64305 32;47751 64194 32;47811 63760 50; +47740 64206;47087 65131; +47034 65756;46903 66835;47510 66921;47095 69556;45803 70723;43885 69970 16; +47391 67588 1;45416 67880;45023 66101;45559 65565;47025 65774;46885 66847;47505 66925;47391 67588 18; +47039 67619 1;45728 67653;45253 66676;45354 66011; +45868 66664 1;46029 66879;46314 66938;46503 66796 1;46692 66653;46713 66364;46552 66149 1;46390 65933;46105 65874;45916 66016 1;45727 66159;45706 66449;45868 66664 18; +44074 71425;44000 72542;45246 72690;45382 71302;43827 70728;41829 70568;41650 71160;44074 71425 18; +48680 69574 32;50046 69785 32;50304 68109 32;48938 67898 32;48680 69574 50; +48680 69574;47721 69426;48130 66814;51669 67371;51526 68301;50836 68195;50687 69168;50153 69086 16; +49490 68859; +48680 69574 32;50046 69785 32;50304 68109 32;48938 67898 32;48680 69574 50; +51139 71598 32;51496 69298 32;52588 69467 32;52231 71767 32;51139 71598 50; +51139 71598 32;51496 69298 32;52588 69467 32;52231 71767 32;51139 71598 50; +48531 67350; +50511 68440; +50633 67542; +51375 67655; +52575 69454;53613 69628;54172 66994;54000 67100;53357 67650;52149 67442;52002 68392;52732 68505;52637 69116;52575 69454 18; +52575 69454;53613 69628;54172 66994;54000 67100;53383 67659;52149 67442;52002 68392;52732 68505;52637 69116;52575 69454 18; +53134 67840; +48806 66126;53301 66889; +44649 62146;44335 64763;47083 65138;47746 64187;47816 63777;47397 63707;46813 62407;44649 62146 18; +42124 69825;43903 69965;44994 70418;45032 70303 1;45648 69141;46207 69912;46475 70076;46465 70088;47070 69563;47393 67574;47348 67594 1;45410 67854;45027 66097;45559 65565;45570 65567;43275 65245;42176 66388;41923 69616;42124 69825 18; +49772 71439; +48180 74153 1;48273 73800;48632 73667;48995 73697 1;49771 73762;50206 73804;50981 73882 1;51230 73907;51547 73860;51561 73610 1;51584 73188;51465 72841;51770 72549 1;52025 72306;52165 72118;52227 71772;52572 69428;52638 69465;53582 69623;53664 69892;52587 74844 32;48306 74326 32;48180 74153 18; +52129 72093 1;52050 72260;51934 72393;51770 72549 1;51465 72841;51584 73188;51561 73610 1;51547 73860;51230 73907;50981 73882 1;50206 73804;49771 73762;48995 73697 1;48768 73678;48543 73723;48383 73846; +45372 77545;47827 77434;48259 74338;52564 74807;53181 71883;52117 71772;50313 71492;50451 70602;47492 70147;45971 71512;45752 74647;44883 76336;45058 76971;45372 77545 18; +47728 69428;48653 69563;48700 69447;48938 67898 32;50304 68109 32;50154 69081;50304 69109;50687 69168;50836 68195;51526 68301;51669 67371;48130 66814;47916 68178;47728 69428 18; +44891 76348;44958 76190;45752 74647;45971 71512;47492 70147;50451 70602;50313 71492;51125 71618;51168 71409;51496 69298 32;52536 69459;52591 69415;52732 68505;52002 68392;52149 67442;51669 67371;51526 68301;50836 68195;50687 69168;50209 69095;50136 69203;50046 69785 32;48680 69574 32;48369 69526;47721 69426;48130 66814;47480 66917;47510 66921;47095 69556;45803 70723;44007 70018;43818 70007;43828 70698;43886 70747;43975 70784;45372 71308;45255 72690;44616 72628;44616 72723;44576 73161 32;44277 73134 32;44248 73451;44343 73500;45217 73580;45090 74677;44637 75584;44802 76378;44891 76348 18; +47119 65093;47045 65877;46910 66833;47551 66931;48143 66827;51675 67387;52138 67462;53390 67662;54192 66983;54383 65830;53927 65774;53841 66255;53298 66681;48887 65947;49029 64880;48665 64825;48708 64325;47746 64214;47119 65093 18; +61625 75073;61361 77100; +64045 77433;64402 74712; +54651 65419;52644 74841;48335 74352;47480 81488;49190 81715;49940 75783;61420 77179;61542 74963;60447 74901;59147 74731;59630 71027;59256 70971;59214 71286;58483 71175;58504 71910;56748 71959;56755 72211;55648 72240;55520 67286;55753 67280;55701 65415;55274 65427;54651 65419 18; +59632 61086;59738 64676;59987 67806;61285 67878; +60213 66940;60049 64675;60048 64168; +58349 63911 32;58866 63900 32;58841 62690 32;58324 62701 32;58349 63911 50; +58843 66131 32;59262 66122 32;59280 66959 32;58861 66968 32;58843 66131 50; +58898 67858;59729 68780;59939 68591;59284 67863;58898 67858 18; +58843 66131 32;59262 66122 32;59280 66959 32;58861 66968 32;58843 66131 50; +58898 67858;59729 68780;59939 68591;59284 67863;58898 67858 18; +58859 66970;58877 67853; +59272 66964;59291 67857; +60157 69272;59437 69171;58442 68098;58407 66136;58835 66127;58878 67845;60157 69272 18; +59262 66283;59785 66272; +58511 60561;58807 61042;60001 61109;60134 60928; +58511 60561;58807 61042;60001 61109;60142 60919;59604 60728;58511 60561 18; +59889 57824;59721 58980; +66113 59946 1;66001 60957;65539 61856;64534 62019;64411 62772; +63586 65611;63052 65520;62959 62521; +62848 65013;62772 62543; +66016 71754;68979 72144; +68582 71921 1;68636 71591;68482 71196;68150 71156 1;67804 71114;67538 71402;67496 71748; +68582 71921 1;68636 71591;68482 71196;68150 71156 1;67804 71114;67538 71402;67496 71748;68582 71921 18; +68051 71687; +61771 75043;62351 75118; +61869 74289;62449 74364; +63375 76893; +64029 73328; +65657 71342; +61599 77165 1;62388 76999;63176 76876;63363 76091 1;63732 74542;63644 73621;63869 72045;64522 72464;63980 77387;61599 77165 18; +64979 71502;64868 70848;64584 70676 1;64037 70902;63824 71441;63869 72032;64585 72526;64671 71440;64979 71502 18; +64868 70848;66156 71011;66601 71717;64956 71474;64868 70848 18; +66549 71674;66165 71028;64900 70880 1;63533 69977;61514 67794;61434 65639;61266 61918 1;61244 61442;60922 61190;60474 61027 1;59996 60854;59761 60729;59257 60659 1;57835 60462;57124 60363;55702 60165 1;54842 60045;54370 58266;54501 57408 16; +63097 65614 1;63211 67309;64658 68427;65984 69488; +64591 70676;64470 70730 1;64007 70981;63827 71484;63869 72032;63846 72214 1;63650 73688;63719 74598;63363 76091 1;63181 76854;62431 76992;61664 77151; +61467 77130;61825 77154;62002 77080 1;62641 76937;63208 76741;63363 76091 1;63732 74542;63644 73621;63869 72045;63867 71763 1;63898 71282;64121 70867;64584 70676;64499 70592 1;63876 70111;63178 69420;62606 68621;62466 69567;61529 76846;61467 77130 18; +64047 77417;74323 78743;74881 73073;74131 72550;68810 71921;68757 72096;64901 71607;64047 77417 18; +63075 65553;66258 66133;65752 69291;65493 69094 1;64362 68175;63272 67170;63110 65760;63075 65553 18; +60113 69253;62593 69574;62657 68691 1;62640 68668;62623 68644;62606 68621;62467 68421 1;61890 67565;61470 66599;61434 65639;61266 61918 1;61244 61442;60922 61190;60474 61027 1;60341 60979;60226 60934;60120 60894;60079 61004;60001 61109;58807 61042;58511 60561;58284 60524 1;57426 60405;56761 60313;55702 60165 1;55495 60136;55311 60012;55152 59827;54969 60272;55536 60358;55487 60704;55819 60809;57374 61117;57441 62509;58328 62711;58522 62697;58841 62690 32;58866 63900 32;58349 63911 32;58358 64171;58400 66149;58559 66133;58835 66127;59262 66132;59279 66934;58856 66946;58877 67853;59291 67872;59931 68582;59939 68591;59729 68780;59722 68773;60113 69253 18; +56561 58428;56650 58463 1;56674 58469;56698 58475;56723 58480 1;57914 58710;58578 58771;59650 58974;59807 57818;56701 57364;56561 58428 18; +59752 58985;59941 59031 1;59984 59039;60028 59048;60072 59057 1;61304 59309;62642 60408;62802 61179 1;62936 61826;62978 62160;62986 62820;63014 65111;63070 65548;63601 65610;64082 62723;64304 62747;64471 62407;64534 62019 1;65539 61856;66001 60957;66113 59946;62281 59515;62429 58195;59912 57813;59752 58985 18; +66514 71685;67464 71845;67506 71688 1;67570 71369;67824 71116;68150 71156 1;68421 71189;68574 71459;68591 71736;69228 71808;69450 70056;65885 69353;65355 68982 1;64275 68093;63265 67113;63110 65760;63075 65553;62970 62320 1;62947 61944;62898 61642;62802 61179 1;62642 60408;61304 59309;60072 59057 1;58735 58784;58063 58739;56723 58480 1;56275 58393;56094 58027;56029 57577;54481 57808 1;54507 58724;54960 60062;55702 60165 1;57124 60363;57835 60462;59257 60659 1;59761 60729;59996 60854;60474 61027 1;60922 61190;61244 61442;61266 61918;61434 65639 1;61514 67794;63533 69977;64900 70880;66165 71028;66368 71370;66514 71685 18; +56413 57316;56010 57289 1;56014 57191;56030 57095;56057 56969 1;56568 54544;56895 52993;57304 51055;58688 44619;59183 44231;56386 57310;56413 57316 18; +56010 57289;56413 57316;56716 57390;56546 58470;56455 58378 1;56197 58220;56078 57921;56028 57573 1;56012 57461;56006 57374;56010 57289 18; +53671 52241;54362 52313;55866 44716; +53386 52229;54386 52315;55866 44716;52498 43951;52412 45197;53386 52229 18; +49638 47125 1;49244 47093;48858 46739;48890 46345;49142 43284;51113 43389;52518 43971;52419 44898;51471 44776;51340 44584;51052 44575;50808 44758;49928 44665;49861 45323;49647 45474;49569 46217;49771 46484;49638 47125 18; +55293 52306; +55625 50683; +56026 48886; +54753 53462;55276 53584;55982 50225;55440 50110;54753 53462 18; +54230 55979;54771 56032;55279 53553;54732 53426;54230 55979 18; +55429 50118;55984 50224;56394 48128;55887 48038;55429 50118 18; +55853 48064;56403 48169;56428 48025;57065 44942 1;57137 44585;56988 44267;56726 44053;55853 48064 18; +54494 57851;56038 57633;56016 57476 1;55998 57295;56014 57172;56057 56969 1;56765 53607;57957 47962;58666 44600;59172 44299;56781 44101 1;57008 44313;57132 44611;57065 44942;54487 57430;54494 57851 18; +51226 43428;51309 43470;52518 43971;52561 43965;55851 44713;55893 44518 1;55971 44156;55730 43760;55361 43733;51226 43428 18; +55852 44710;55865 44648;55893 44518 1;55969 44165;55742 43780;55388 43736; +54203 56004;53512 55942;53616 54749;53389 54714;53651 52236;54383 52324;55849 44786;55851 44785;55893 44518 1;55954 44236;55821 43933;55584 43801;55494 43734;56107 43780 1;56307 43811;56493 43886;56648 43993;56721 44054;54203 56004 18; +51235 43428;52508 43925;55867 44745; +49987 41616;51409 41736; +42321 39029;42204 40184;46065 40577;46807 32488;43804 31232;43595 31775 16; +49531 34932 1;49350 34922;49196 35061;49186 35242 1;49177 35422;49315 35577;49496 35586 1;49677 35596;49831 35457;49840 35276 1;49850 35096;49711 34942;49531 34932 18; +49900 39037;50421 39076;50578 36992;51676 37073;51741 36223;55006 36498;54516 41413;53131 41260;53049 41999;56628 42396 1;57177 42457;57756 42232;57851 41688;56610 41471;57449 37334;58655 37579;59220 34796;58144 34578;58583 32676;59683 32964;59784 32577;58129 32039;58213 31628;57895 31524 16; +50420 39076;50353 39970; +55633 40294; +57143 33826;56721 34789;43020 28779;42808 28534;42884 27579; +41752 27858;41682 28346;41080 28573;33743 25084; +56505 42378;56614 41881;55005 41680;55153 40258;54637 40203;54516 41413;53131 41260;53049 41999;56505 42378 18; +54708 39481;55223 39534;55546 36228;55040 36114;54708 39481 18; +55450 37205 1;55467 36816;56013 36857;56400 36900 1;56756 36939;56961 36911;57316 36961 1;57453 36980;57511 36646;57377 36612 1;56637 36425;56215 36352;55467 36202;55301 36569;55450 37205 18; +57962 36856; +58555 34998; +59157 32599; +49407 34538 1;49097 34584;48845 34833;48807 35159 1;48761 35549;49040 35902;49430 35948 1;49820 35994;50174 35715;50220 35324 1;50258 34992;50061 34686;49761 34575 16; +46333 39159;47179 39229;47031 40947;46133 40869;46333 39159 18; +47424 39229;47912 38749;49639 38871; +47397 39272;47912 38749;49657 38889;49587 39420;47397 39272 18; +50639 35957;49996 35909;49719 39580 32;47297 39397 32;47173 41036 32;45654 40921;45626 41291 16; +48706 38801;48916 36027 1;48924 35921;49000 35884;49064 35800; +49186 38008; +49256 36655; +48689 38793;49649 38862;49849 35914;49709 35926 1;49621 35951;49527 35959;49430 35948 1;49287 35931;49160 35873;49057 35788;48984 35889 1;48949 35927;48921 35965;48916 36027;48802 37528;48689 38793 18; +47704 38671; +46875 38609; +46399 39167;47166 39228; +46638 35973 1;47119 36004;47625 35715;47625 35233 1;47625 34755;47313 34484;47440 34024 1;47586 33495;48277 32956;47810 32667 1;47515 32484;47308 32455;46971 32371;46638 35973 18; +42739 29829;49044 32593 1;49675 32870;49671 33805;49574 34616; +49596 34535 1;49206 34489;48852 34768;48807 35159 1;48761 35549;49040 35902;49430 35948 1;49820 35994;50174 35715;50220 35324 1;50265 34934;49986 34581;49596 34535 18;49560 34939 1;49381 34915;49217 35041;49193 35220 1;49169 35398;49295 35563;49474 35587 1;49652 35610;49817 35485;49841 35306 1;49864 35127;49739 34963;49560 34939 18; +49993 34694 1;50434 34425;50965 34938;50511 35817;50024 35799;50075 35679 1;50152 35579;50204 35458;50220 35324 1;50245 35102;50165 34891;50020 34742;49993 34694 18; +50435 39076;49880 39027;50115 36029;50482 36029;50466 35769;50638 35344 1;50602 35245;50797 35283;50878 35331 1;50895 35341;50915 35348;50938 35352 1;51203 35402;51355 35430;51614 35478;51631 35776 1;51646 35947;51680 36157;51514 36201 1;51070 36319;50733 36496;50567 36905;50435 39076 18; +50552 36991;51668 37071;51736 36226;55018 36510;55009 36428;55039 36126;51610 35486;51617 35534;51631 35776 1;51646 35947;51680 36157;51514 36201 1;51072 36319;50736 36495;50569 36900;50552 36991 18; +50211 34623;50216 34623 1;50481 34611;50715 34857;50681 35270 32;50703 35282 1;50759 35283;50835 35305;50878 35331 1;50895 35341;50915 35348;50938 35352 1;51203 35402;51355 35430;51614 35478;51683 35500;53884 35910 32;55039 36126;55190 36148;55546 36228;55590 36227 1;56263 36361;56679 36436;57377 36612 1;57511 36646;57453 36980;57316 36961 1;56961 36911;56756 36939;56400 36900 1;56120 36869;55756 36839;55570 36972 33;55500 37023;55455 37098;55450 37205;55427 37448;55223 39534 32;54785 39489; +54705 40198;55153 40258 32;55005 41680 32;56614 41881 32;56524 42292; +45754 41368;45619 42935;48172 43268;49141 43323;49153 43268;50841 43386;56007 43772;56092 43779;56107 43780 1;56435 43831;56726 43999;56903 44237;59092 44324;59627 41918;57844 41687;57851 41688 1;57756 42232;57177 42457;56628 42396;53049 41999;53072 41789;53131 41260;54516 41413;55006 36498;51741 36223;51676 37073;50578 36992;50421 39076;50350 39979;51153 40058;51161 40572;50184 40546;50190 40669;50126 41507;51327 41654;51353 41856;45754 41368 18; +42814 30994; +50494 35849 1;50768 35536;51181 35626;51593 35684 1;53095 35897;53924 36108;55432 36277 1;56056 36347;56652 36289;56845 35692 1;57055 35042;57176 34575;57248 33896; +53533 36033; +46329 39163;47440 39274;47908 38731;48686 38793;48932 36017;49068 35733;50314 35573;55002 36350;54483 41421;55137 42247;56731 42404 1;57248 42428;57762 42198;57851 41688;56610 41471;57449 37334;58655 37579;59220 34796;58144 34578;58218 34257;57161 33933;56716 34771;55861 34421 1;55648 34620;55354 35151;55073 35028;49307 32494;42938 29915;42745 30201;42443 30451;42252 30488;42160 31397;42837 31453;43528 31774;43657 31613;43804 31232;46581 32393;47052 32391 1;47343 32461;47540 32499;47810 32667 1;48277 32956;47586 33495;47440 34024 1;47313 34484;47625 34755;47625 35233 1;47625 35715;47119 36004;46638 35973;46329 39163 18; +42302 39040;42203 40101;46015 40484;46755 32428;43795 31268;43597 31811;45633 32847;45596 33279;45880 33341;45349 39336;42302 39040 18; +42857 27576;57094 33857;56723 34736;42813 28597;42857 27576 18; +35450 18328;34853 18076; +44406 22411;37128 19067; +34798 18212;34415 19119;58663 29668;58811 28829;34798 18212 18; +56911 34790 1;57090 34868;57189 34913;57368 34991 1;57684 35130;58075 34918;58150 34581;58682 32208;57740 31938;56911 34790 18; +58562 32654;59685 32950;59771 32592;58649 32235;58562 32654 18; +57742 31982;58137 32068;58205 31642;57859 31507;57742 31982 18; +57162 34917;57254 34941 1;57289 34956;57326 34973;57368 34991 1;57600 35093;57873 35006;58031 34819; +58157 30625; +45673 25406;45920 24829; +50608 27519;50855 26942; +55528 29663;55789 29054; +57969 31087;58314 31198;58506 30404;56045 29331 16; +55404 29051;51135 27189 16; +50483 26904;46212 25041 16; +42974 24157;43151 23716;45563 24758 16; +41889 27971;42662 28314;42971 24162;42286 23852;41889 27971 18; +38595 23158;39125 21899; +42215 24508;42338 23358;39519 22127 16; +34980 23519;36167 20622; +38689 21764;36405 20767 16; +35897 20545;32575 19094;31571 21392;32086 21673 16; +39770 22600; +38209 21928; +33570 20563; +56603 41478;57849 41700;59607 41934;62141 30319;58810 28826;58135 32065;59776 32589;59683 32978;58567 32669;58477 33137;58144 34578;59220 34796;58655 37579;57449 37334;56771 40676;56603 41478 18; +57862 31517;58247 31628;58321 31224;57973 31110;57862 31517 18; +55874 29855 32;58049 30808 32;57944 31047 32;58303 31204 32;58515 30405 32;56094 29352 32;55874 29855 50; +50944 27685 32;55171 29541 32;55380 29064 32;51153 27208 32;50944 27685 50; +46028 25539 32;50236 27390 32;50453 26897 32;46245 25046 32;46028 25539 50; +42956 24163 32;45318 25237 32;45531 24768 32;43169 23694 32;42956 24163 50; +42287 23901;39316 22633 16; +38474 22274;36233 21318 16; +39106 23214 32;42203 24499 32;42280 23890 32;39333 22667 32;39106 23214 50; +35985 21869 32;38209 22819 32;38443 22254 32;36213 21323 32;35985 21869 50; +39346 22638;42288 23909;42344 23385;39543 22157;39346 22638 18; +38426 22268;38667 21744;36434 20794;36230 21324;38426 22268 18; +41057 23133; +33770 25093;41074 28559;41592 28411;41715 27881;34597 24710;33857 24883;33770 25093 18; +34910 22844;32215 21387 1;32449 20954;32958 20923;33395 21149;35207 22086;34910 22844 18; +32112 20312;32610 19292;33813 19815; +35897 20545;32575 19094;31571 21392;32086 21673;34804 23212;35897 20545 18; +32258 21335 1;32537 20951;32996 20942;33395 21149;34813 21882; +58499 30368;43135 23679;42957 24131;42288 23839;42332 23320;32556 19051;32829 18431;58650 29654;58499 30368 18; +22202 38142 32;21555 37788 32;19634 41298 32;31023 47530 32;31414 46815 32;20672 40937 32;22202 38142 50; +20669 42896; +20735 42497 1;20514 42461;20305 42611;20268 42832 1;20231 43053;20381 43263;20603 43299 1;20824 43336;21033 43186;21070 42965 1;21107 42743;20957 42534;20735 42497 18; +22298 43735; +22364 43336 1;22143 43300;21934 43450;21897 43671 1;21860 43892;22010 44102;22232 44138 1;22453 44175;22662 44025;22699 43804 1;22736 43582;22586 43373;22364 43336 18; +23803 44537; +23869 44138 1;23648 44102;23439 44252;23402 44473 1;23365 44694;23515 44904;23737 44940 1;23958 44977;24167 44827;24204 44606 1;24241 44384;24091 44175;23869 44138 18; +25493 45450; +25559 45051 1;25338 45015;25129 45165;25092 45386 1;25055 45607;25205 45817;25427 45853 1;25648 45890;25857 45740;25894 45519 1;25931 45297;25781 45088;25559 45051 18; +26899 46228; +26965 45829 1;26744 45793;26535 45943;26498 46164 1;26461 46385;26611 46595;26833 46631 1;27054 46668;27263 46518;27300 46297 1;27337 46075;27187 45866;26965 45829 18; +28408 46644 1;28187 46608;27978 46758;27941 46979 1;27904 47200;28054 47410;28276 47446 1;28497 47483;28706 47333;28743 47112 1;28780 46890;28630 46681;28408 46644 18; +28342 47043; +29962 47470 1;29741 47434;29532 47584;29495 47805 1;29458 48026;29608 48236;29830 48272 1;30051 48309;30260 48159;30297 47938 1;30334 47716;30184 47507;29962 47470 18; +29896 47869; +20099 42608;20547 41787; +22841 44104;23289 43283; +36761 49256; +35293 48441; +33491 47516; +35514 46374; +36578 44229; +35706 42833; +37965 45740; +38621 45241 1;38825 45495;38683 45947;38305 46250 1;37927 46553;37455 46593;37252 46339 1;37048 46085;37189 45633;37568 45330 1;37946 45027;38417 44987;38621 45241 18; +36622 43526 1;36261 43513;35958 43794;35944 44155 1;35930 44517;36212 44820;36573 44834 1;36934 44848;37238 44566;37252 44205 1;37265 43844;36984 43540;36622 43526 18; +35635 42305 1;35291 42336;35037 42639;35068 42983 1;35099 43328;35402 43582;35747 43551 1;36091 43520;36345 43216;36314 42872 1;36283 42528;35980 42274;35635 42305 18; +34044 44341 1;34125 44174;34464 44171;34801 44335 1;35138 44498;35346 44766;35265 44933 1;35185 45099;34846 45102;34508 44938 1;34171 44775;33963 44507;34044 44341 18; +33316 45895 1;33304 45719;33496 45563;33745 45546 1;33993 45529;34205 45658;34217 45834 1;34229 46009;34037 46166;33788 46183 1;33540 46200;33328 46071;33316 45895 18; +35919 45796 1;36167 45963;36189 46365;35968 46696 1;35746 47026;35365 47159;35117 46993 1;34869 46827;34848 46424;35069 46094 1;35291 45763;35671 45630;35919 45796 18; +34633 43318; +36576 40070; +36798 39009; +37650 38590; +38526 39145; +39389 39120; +40512 39873; +40611 41649; +42190 37209;41910 40345; +41795 40318 1;41297 40933;41248 41689;41696 42341;41848 42198 1;42418 41514;43297 41117;44270 41197;45507 41290;45533 40638;41795 40318 18; +40979 47178;41234 44074;41787 44119; +38131 47029;38587 47264 1;38920 46788;39291 46464;39870 46511 1;40263 46543;40539 46522;40845 46770;41141 43896 1;40478 43864;40284 44726;40327 45389 1;40343 45634;40268 45879;40031 45944 1;39347 46130;38751 45951;38131 47029 18; +38137 47017 1;37962 46959;37545 47103;37681 47369 1;37910 47816;37627 48188;37860 48633 1;37953 48810;38302 48656;38341 48460;38587 47264;38137 47017 18; +40565 46607 1;40357 46530;40141 46533;39870 46511 1;39291 46464;38920 46788;38587 47264 32;38560 47398;38341 48460 1;38302 48656;37953 48810;37860 48633 1;37627 48188;37910 47816;37681 47369 1;37552 47116;37944 46974;38131 47010 32;38162 46976 1;38771 45957;39359 46127;40031 45944 1;40268 45879;40343 45634;40327 45389 1;40295 44901;40392 44304;40710 44037; +32081 46240;41151 29887; +40988 30057;36658 28095 1;35892 27748;34963 27795;34549 28527 1;32620 31940;31593 33885;29663 37298 1;29043 38393;29610 39737;30712 40345;34177 42257; +31236 34451 1;31550 34620;31640 35062;31438 35438 1;31235 35813;30817 35981;30503 35812 1;30189 35643;30098 35201;30301 34825 1;30503 34449;30922 34282;31236 34451 18; +29043 33694;26329 32218;27576 29937 32;27729 30020 32;28284 29004 32;28118 28913 32;30336 24858 32;30490 24942 32;31041 23935 32;30892 23854 32;32065 21710 32;34754 23181 32;34681 23389;35237 23693;35998 21893;36908 22273;37318 22105;37496 22540;38169 22823;38085 23023;39062 23434;39145 23237;39904 23552;40309 23387;40477 23798;42191 24517;41859 27904;34630 24699;33858 24892;29043 33694 18; +28657 34473;25905 32921;26302 32193;29078 33702;28657 34473 18; +29136 33556;29937 33995; +28560 34607;29361 35046; +29911 33976;30352 34215;34151 27207;32948 26555; +33830 25324;33184 26503; +29152 33541;30349 34157;34038 27212;32915 26644;29152 33541 18; +29361 35046;29815 35295;28104 38414;26874 37739; +28547 34637 32;29770 35306 32;28080 38393 32;26857 37724 32;28547 34637 50; +36796 36754;36378 37507;32997 35613;33903 34029; +36774 36742;36356 37495;33014 35582;33903 34029;36774 36742 18; +31546 35027;32773 35694; +34329 38269;34612 37751; +33483 37806;33766 37289; +34648 38349;33977 39576;33386 39252;34059 38025; +34330 38224;33923 38969; +33908 39666;33338 39354;33338 39353;32101 38678 32;32806 37390 32;34614 38376 32;33908 39666 18; +33386 39252;32285 38648;32944 37447; +32835 37601;33282 37851 16; +33559 38006;33934 38216 16; +35350 38878; +34635 40099; +33989 41158; +33262 42523; +31621 40837;31067 41842; +32303 40981 32;33205 39336 32;31762 38545 32;31086 39778 32;31669 40098 32;31443 40510 48; +31087 39791;32543 40574;33209 39334;31772 38545;31087 39791 18; +32252 41013;32507 40547;31698 40102;31447 40564;32252 41013 18; +32535 40554 1;32763 40684;32892 40756;33120 40886 1;33355 41020;33692 40854;33856 40639;35217 38164;34774 37901;33849 39647;33213 39332;32535 40554 18; +32806 40708 1;32902 40762;32997 40816;33120 40886 1;33355 41020;33692 40854;33856 40639;35033 38499; +31063 35798;36072 38587;36503 37797;31544 35034;31063 35798 18; +31057 35798;33597 37226 16; +33902 37398;34437 37699 16; +34750 37875;36006 38581 16; +32383 38116 1;32159 37933;32017 37654;32167 37407 1;32305 37180;32060 36961;32186 36728;32315 36519;33568 37210;33327 37672;32790 37395;32383 38116 18; +33919 37413;33685 37876;34178 38153;34419 37678;33919 37413 18; +32173 37870 1;32093 37724;32075 37559;32167 37407 1;32294 37198;32097 36997;32162 36785; +31051 39754 1;30559 39505;30851 38806;31409 39180;31051 39754 18; +30837 39545;31089 39073; +25263 38881;33463 43373; +26919 37974;26312 39085; +26247 38878;28171 39931; +28218 39857;28472 39393;26776 38465;26538 38899;28218 39857 18; +31386 34570 1;31494 34691;31549 34857;31545 35035;31544 35058 16; +31092 35792 1;30957 35865;30807 35894;30667 35869 16; +30359 35700 1;30257 35589;30199 35437;30193 35272 16; +30532 34547 1;30699 34415;30902 34358;31089 34397 16; +42405 36930;41358 36837;41951 30192; +41827 33424;42731 33505; +42835 31438 32;42679 33176 32;42000 33115 32;42156 31377 32;42835 31438 50; +42660 33820 32;42409 36619 32;41698 36556 32;41949 33757 32;42660 33820 50; +42823 31447;42173 31389; +55738 34400;55715 34584 1;55523 34816;55294 35125;55073 35028;51744 33662;42959 29952;43003 29833 1;43023 29778;43038 29721;43048 29662 1;43097 29379;43023 29102;42864 28888;55738 34400 18; +41114 29093;40837 29760; +42221 28490 1;41670 28395;41145 28765;41049 29317 1;40954 29869;41324 30394;41876 30489 1;42428 30584;42953 30214;43048 29662 1;43143 29111;42773 28586;42221 28490 18; +41266 30233;41343 30122;41090 29286;40886 29733;40834 29842;41034 29968;41266 30233 18; +41312 30187 1;41404 30285;41516 30364;41643 30419 16; +42208 30491 1;42471 30449;42706 30305;42863 30094 16; +43029 29750 1;43036 29721;43043 29692;43048 29662 1;43118 29256;42961 28879;42639 28656;42664 28318 16; +41899 27970;41849 28494 1;41482 28569;41174 28844;41071 29220 16; +41863 28515 32;42624 28721 32;42672 28322 32;41902 27981 32;41863 28515 50; +36294 29464 1;36062 29168;35933 29001;35701 28705 1;35576 28545;35343 28453;35195 28591 1;35033 28742;34942 28827;34780 28978 1;34624 29124;34608 29383;34767 29525 1;34871 29618;34995 29584;35125 29533 1;35253 29483;35304 29670;35360 29795 1;35497 30099;35817 30330;36119 30188 1;36382 30064;36473 29693;36294 29464 18; +36669 28373 1;36808 28776;36757 29211;36390 29429 1;36524 29685;36528 29993;36294 30162 1;36064 30328;35804 30384;35570 30223; +35526 30319 1;35338 30588;35079 30727;34758 30659 1;34523 30609;34459 30144;34680 30048 1;34939 29935;35085 29874;35351 29778;35645 30122;35526 30319 18; +35491 30388;35421 30448 1;35245 30630;35023 30715;34758 30659 1;34523 30609;34459 30144;34680 30048 1;34823 29985;35063 29887;35177 29841; +35870 29666; +39614 30496; +37994 29607; +37637 30377; +38741 31250; +37540 31271;37026 30845;35472 31995;35821 32475; +37540 31271;37026 30845;35472 31995;35821 32475;37540 31271 18; +35969 31454 1;35531 31350;35144 31092;34818 31402 1;34044 32137;34185 32919;33762 33923;35734 32588;35289 31943;35969 31454 18; +32886 35779;30382 34459;34071 27254;33096 26600;33861 25206;41140 28599;41670 28426;39326 32596;37118 30795;35969 31454 1;35531 31350;35144 31092;34818 31402 1;34055 32126;34181 32896;33780 33880;33849 33990;32886 35779 18;36119 30188 1;36382 30064;36473 29693;36294 29464 1;36062 29168;35933 29001;35701 28705 1;35576 28545;35343 28453;35195 28591 1;35033 28742;34942 28827;34780 28978 1;34624 29124;34608 29383;34767 29525 1;34871 29618;34995 29584;35125 29533 1;35253 29483;35304 29670;35360 29795 1;35497 30099;35817 30330;36119 30188 18; +40299 34023 1;40615 34094;40803 33737;40867 33419 1;40969 32908;41102 32631;41126 32111 1;41133 31966;41082 31820;40941 31790 1;40774 31754;40701 31943;40632 32099 1;40398 32632;40211 32911;40040 33468 1;39970 33697;40066 33971;40299 34023 18; +39423 35393; +41549 42088 1;41316 41600;41340 41073;41603 40601; +41434 42845 1;41153 42845;40897 42942;40814 43211 1;40659 43713;40140 43738;39916 44214 1;39754 44558;39912 45079;40291 45113;40333 44957 1;40368 44589;40482 44228;40710 44037;41172 43909;41434 42845 18; +38755 49795;32389 46316;41258 29982;41751 30278;41197 36952;42208 37063;41851 40323;41795 40318 1;41327 40896;41256 41597;41620 42221;41708 42380 1;41585 42555;41481 42744;41399 42946;41434 42845 1;41153 42845;40897 42942;40814 43211 1;40659 43713;40140 43738;39916 44214 1;39754 44558;39912 45079;40291 45113;40333 44957 1;40368 44589;40482 44228;40710 44037;41172 43909;41189 43841;40840 47204;39581 47081;39038 47451;38755 49795 18;35968 46696 1;36189 46365;36167 45963;35919 45796 1;35671 45630;35291 45763;35069 46094 1;34848 46424;34869 46827;35117 46993 1;35365 47159;35746 47026;35968 46696 18;38305 46250 1;38683 45947;38825 45495;38621 45241 1;38417 44987;37946 45027;37568 45330 1;37189 45633;37048 46085;37252 46339 1;37455 46593;37927 46553;38305 46250 18;33788 46183 1;34037 46166;34229 46009;34217 45834 1;34205 45658;33993 45529;33745 45546 1;33496 45563;33304 45719;33316 45895 1;33328 46071;33540 46200;33788 46183 18;35265 44933 1;35346 44766;35138 44498;34801 44335 1;34464 44171;34125 44174;34044 44341 1;33963 44507;34171 44775;34508 44938 1;34846 45102;35185 45099;35265 44933 18;36573 44834 1;36934 44848;37238 44566;37252 44205 1;37265 43844;36984 43540;36622 43526 1;36261 43513;35958 43794;35944 44155 1;35930 44517;36212 44820;36573 44834 18;35747 43551 1;36091 43520;36345 43216;36314 42872 1;36283 42528;35980 42274;35635 42305 1;35291 42336;35037 42639;35068 42983 1;35099 43328;35402 43582;35747 43551 18; +30904 41443;28436 40091; +31495 41773;32520 42334; +30729 42846 32;31404 43215 32;31673 42723 32;30998 42354 32;30729 42846 50; +31672 42735 1;31946 42902;32225 43062;31987 43494; +32363 43710;32752 43365;33202 43593;32918 44068; +32789 43679; +31783 43155; +32799 43320;32354 43725;31970 43516;32070 43311;32057 43054;31926 42897;31870 42840;31900 42770;32799 43320 18; +33512 43054;28068 40071;28218 39857;28472 39393;26825 38492;26681 38405;27030 37995;28173 38649;30057 35229;35980 38562;33512 43054 18;32543 40574;33209 39334;31772 38545;31087 39791;32543 40574 18; +28679 34454;30210 35305;30572 34533;29080 33730;28679 34454 18; +42051 41988 1;41475 41659;41017 41539;40376 41709 1;38801 42126;37757 42292;36206 41796 1;35378 41531;35083 40808;34968 39947 1;34909 39503;34576 39206;34130 39162; +37358 42065; +16335 39100 32;17105 39483 32;17293 39105 32;16523 38722 32;16335 39100 50; +16568 39460; +15626 41424; +16335 39100 32;17105 39483 32;17293 39105 32;16523 38722 32;16335 39100 50; +22196 49448;37427 52641;37829 49221;32416 46363;31827 46036;31018 47537;23378 43378;22950 44163;22736 44043;23159 43265;20636 41879;20208 42666;19990 42550;20419 41764;19642 41320;20014 40612;19089 40101;17770 42316;18376 42613;21329 49229;21403 49115 1;21458 49034;21541 48971;21642 48941 1;21875 48873;22118 49006;22187 49238 1;22198 49277;22204 49316;22205 49354;22196 49448 18; +56390 57245;66667 58710;65882 54575;69161 33383;62104 23069;59686 22181;57218 27905;62301 30225;56390 57245 18; +20017 40585 32;19093 40079 32;19346 39618 32;20270 40124 32;20017 40585 50; +21616 37300 32;20692 36794 32;20945 36333 32;21869 36839 32;21616 37300 50; +22760 35213 32;21836 34707 32;22089 34246 32;23013 34752 32;22760 35213 50; +23850 33225 32;22926 32719 32;23179 32258 32;24103 32764 32;23850 33225 50; +25049 31142 32;24125 30636 32;24378 30175 32;25302 30681 32;25049 31142 50; +26415 28857 32;25491 28351 32;25744 27890 32;26668 28396 32;26415 28857 50; +28222 25871 32;27298 25365 32;27551 24904 32;28475 25410 32;28222 25871 50; +29702 23391 32;28778 22885 32;29031 22424 32;29955 22930 32;29702 23391 50; +30981 21107 32;30057 20601 32;30310 20140 32;31234 20646 32;30981 21107 50; +26415 28857 32;25491 28351 32;25744 27890 32;26668 28396 32;26415 28857 50; +22760 35213 32;21836 34707 32;22089 34246 32;23013 34752 32;22760 35213 50; +28222 25871 32;27298 25365 32;27551 24904 32;28475 25410 32;28222 25871 50; +21616 37300 32;20692 36794 32;20945 36333 32;21869 36839 32;21616 37300 50; +25049 31142 32;24125 30636 32;24378 30175 32;25302 30681 32;25049 31142 50; +29702 23391 32;28778 22885 32;29031 22424 32;29955 22930 32;29702 23391 50; +30981 21107 32;30057 20601 32;30310 20140 32;31234 20646 32;30981 21107 50; +23850 33225 32;22926 32719 32;23179 32258 32;24103 32764 32;23850 33225 50; +20017 40585 32;19093 40079 32;19346 39618 32;20270 40124 32;20017 40585 50; +30635 20625; +29381 22885; +27872 25395; +26070 28369; +24700 30639; +23493 32748; +22407 34721; +21260 36806; +19688 40099; +19991 39974;21474 37226; +21736 36755;22626 35141; +22862 34662;23716 33144; +23179 32258;24134 30639; +25162 30590;26236 28758; +26538 28306;28068 25777; +28327 25333;29536 23297; +29031 22424;30068 20578; +31083 20566;31977 18669;30872 18148;31452 16882 1;31526 16726;31687 16645;31844 16719;34511 11232;37376 5225;42560 -4791;42893 -6008;47681 -15309 1;48093 -16109;48720 -16420;49607 -16572;49564 -16053 1;49055 -15980;48620 -15824;48321 -15476; +28619 34432;30202 35310 16; +30563 34522;29027 33688 16; +23477 38300;24124 38649;23942 38987;24456 39264;24642 38919;25142 39188;30974 42377 32;30711 42857 32;32888 44049 32;31390 46788 32;20717 40950 32;22230 38183 32;23239 38735 32;23477 38300 18; +25449 38567;25108 39190;24655 38947;24455 39317;23904 39020;24099 38658;23438 38299;23737 37752 32;25449 38567 18; +23420 37524 32;24610 35352 32;24780 35445 32;25310 34475 32;25144 34384 32;25940 32930;28631 34448;26149 38984 32;23420 37524 50; +24799 35409 32;25296 34502 32;24826 34245 32;24329 35152 32;24799 35409 50; +27751 30018 32;28281 29050 32;27811 28793 32;27281 29761 32;27751 30018 50; +30525 24949 32;31055 23981 32;30579 23721 32;30055 24692 32;30525 24949 50; +23376 37537 32;24597 35307 32;23983 34971 32;22762 37201 32;23376 37537 50; +25113 34391 32;25929 32900 32;25335 32575 32;24519 34066 32;25113 34391 50; +26316 32196 32;27557 29925 32;27043 29644 32;25802 31915 32;26316 32196 50; +28093 28912 32;30318 24843 32;29859 24592 32;27634 28661 32;28093 28912 50; +30845 23857 32;31064 23457 32;30585 23195 32;30366 23595 32;30845 23857 50; +25113 34391 32;25929 32900 32;25335 32575 32;24519 34066 32;25113 34391 50; +23376 37537 32;24597 35307 32;23983 34971 32;22762 37201 32;23376 37537 50; +28093 28912 32;30318 24843 32;29859 24592 32;27634 28661 32;28093 28912 50; +26316 32196 32;27557 29925 32;27043 29644 32;25802 31915 32;26316 32196 50; +30845 23857 32;31064 23457 32;30585 23195 32;30366 23595 32;30845 23857 50; +29859 24592;30366 23595; +27043 29644;27634 28661; +19976 39973;20260 40127;21549 37801;23215 38702;23727 37746;22771 37191;23968 34964;24331 35167;24835 34262;24521 34088;25341 32552;25882 32866;26284 32203;26129 32094;25802 31915 32;27043 29644 32;27268 29767;27341 29652;27811 28793;27731 28714;27634 28661 32;29859 24592 32;30044 24693;30147 24522;30579 23721;30485 23660;30366 23595 32;30585 23195;29955 22930;29702 23391 32;29507 23284;28327 25333;28475 25410;28222 25871 32;28054 25779;26538 28306;26668 28396;26415 28857 32;26246 28764;25162 30590;25302 30681;25049 31142 32;24131 30639;23179 32258;23417 32388;24103 32764 32;23850 33225 32;23689 33137;22866 34656;22990 34740;23013 34752 32;22760 35213 32;22607 35129;21736 36755;21869 36839;21616 37300 32;21466 37218;19976 39973 18; +29032 22444;29954 22950;30580 23213;31076 23472;32079 21671;31591 21405;32540 19111;32873 18383;33097 17926;32158 17501;32375 17003;31844 16719 1;31687 16645;31526 16726;31452 16882;30872 18148;31977 18669;31083 20566;31234 20646;30981 21107 32;30057 20601 32;29032 22444 18; +34859 18179;32868 17338; +58816 28855;58645 29659;32857 18440;33104 17898;32151 17481;32358 17008;32906 17248 16; +34433 19119;34874 18090;32353 17004;32148 17492;33099 17898;32863 18456;34433 19119 18; +34507 18901;34678 18496; +34659 14272;34285 15157 32;33869 14981 32;34241 14101 48; +34412 14170;34213 14644; +35177 13534;35415 12988;36715 13553 32;38203 14200 32;42543 16087 32;42326 16586 16; +46742 19239;46066 18953; +45032 18375 32;46113 18832 32;46354 18263 32;45273 17806 32;45032 18375 50; +42738 16011;45467 17186 1;45826 17341;46471 17883;46340 18252; +42738 16011;45467 17186 1;45826 17341;46471 17883;46340 18252;42456 16638;42738 16011 18; +47594 18360;47868 17710; +46387 16209;42563 14562; +43198 13618;42829 14490; +42546 11957;41622 14083;35254 11315;36245 9406;36742 9622; +31828 16724;32327 17002;35492 18310;35794 17600;33919 16835;34597 15275;34295 15157;34634 14331;34894 14460;35492 13085;42679 16024;42889 16076;45467 17186 1;45826 17341;46471 17883;46340 18252;46116 18815;46770 19103;46953 18693;47398 18859;47834 17786;47276 17332;47381 16800;47023 15998;46849 16390;42644 14576;43612 12421;42652 12028;41684 14201;35185 11278;36215 9202;35630 8897;31828 16724 18; +39337 8321;37234 7473 32;39436 3058; +39436 3058;42634 -3312; +44938 -5534;44091 -5830;48454 -15428; +43851 -5722 1;43621 -5806;43536 -6099;43650 -6315 1;44292 -7532;44652 -8214;45294 -9431 1;45394 -9620;45490 -9683;45678 -9784; +43851 -5722 1;43621 -5806;43536 -6099;43650 -6315 1;44292 -7532;44652 -8214;45294 -9431 1;45394 -9620;45512 -9695;45700 -9796;43851 -5722 18; +49564 -16053 1;50498 -15976;51340 -15627;51557 -14715; +48294 -15360;51557 -14715;51526 -14829 1;51268 -15656;50458 -15979;49564 -16053;49418 -16029 1;48972 -15948;48591 -15790;48321 -15476;48294 -15360 18; +37829 5951 1;37604 5831;37584 5534;37706 5310;42443 -3351; +37829 5951 1;37604 5831;37584 5534;37706 5310;42443 -3351;37829 5951 18; +36146 9162;36978 7558;39267 8403;38903 9477;37780 9051;36645 11753;35436 11241;36146 9162 18; +36170 9174;37009 7546; +43002 -3826;44233 -3392; +44536 -3165 32;42996 -3709 32;42806 -3170 32;44346 -2626 32;44536 -3165 50; +44670 -3098;45146 -4420; +35613 8929;36163 9209;37061 7499;37812 5963;37700 5835 1;37603 5690;37614 5479;37706 5310;42443 -3351;42792 -3223;43001 -3685;44546 -3118;45025 -4496;44711 -4618;44929 -5360;43883 -5718;43772 -5763 1;43603 -5878;43550 -6126;43650 -6315 1;44292 -7532;44652 -8214;45294 -9431 1;45394 -9620;45490 -9683;45678 -9784;45761 -9811;48315 -15425;48377 -15537 1;48642 -15811;49002 -15953;49418 -16029;49564 -16053;49602 -16550;49350 -16522 1;48597 -16353;48052 -16029;47681 -15309;42893 -6008;42560 -4791;37376 5225;35732 8673;35613 8929 18; +36153 18609;57439 27961;60300 21052;63371 16411;64069 14631;62812 14422;63092 12851;63580 12607;63824 12188;61417 9222;58206 3709;58124 3206;57458 1910;56964 1618;54496 -3613;53361 -6969;52128 -10991;51535 -14445;48402 -15235;44182 -5883;45367 -5340;44577 -2478;41740 -1540;37249 7491;39519 8453;38903 12870;41617 14079;42727 11981;43640 12425;42801 14695;46848 16349;47070 15830;47391 16743;47243 17336;47860 17755;47527 18495;47206 19421;46010 19050;42062 17360;42494 16102;35425 13079;35301 13424;37325 14855;36153 18609 18; +47921 17602;47169 17285;47407 16721;47036 15808;46796 16376;46360 16192; +58096 3249;57412 1929; +55614 -14649; +57649 -14636; +59315 -14673; +59919 -14698; +61449 -14797; +63645 -15056; +59453 -11395 1;59538 -11784;59931 -12039;60316 -11937 1;60793 -11811;60960 -11475;61439 -11358;63906 -10741;63771 -10309;59453 -11395 18; +81041 -11904;69609 -12236;69012 -9905; +68054 -9244 1;68165 -9556;68228 -9732;68339 -10044 1;68493 -10475;68035 -10770;68177 -11205 1;68308 -11607;68703 -11666;68831 -12069 1;68997 -12591;69421 -13014;69966 -12969 1;72186 -12784;72785 -12496;75642 -12463 1;77555 -12441;78628 -12429;80539 -12340;81008 -11957;69584 -12216;68794 -8984;68054 -9244 18; +63706 -10260;68146 -9118;68260 -9667;63906 -10741;63706 -10260 18; +69433 -20429 32;72320 -20602 32;72479 -17947 32;69592 -17774 32;69433 -20429 50; +77662 -22762;73037 -22423;71492 -20634; +69462 -19080;67093 -18939; +70340 -16200; +74054 -17482; +74007 -19774; +73881 -21443; +74141 -22011; +63609 -18634;63435 -21210; +60265 -20702;63262 -20900; +63652 -21196;63354 -25709;64078 -25757; +67339 -17776;67392 -16888 32;67884 -16918 32;67833 -17770 16; +67639 -17331;67605 -17798; +67884 -16918 32;67834 -17746 32;67342 -17716 32;67392 -16888 32;67884 -16918 50; +67591 -17766;67518 -18875; +74962 -13221;74863 -12480 1;72652 -12552;71968 -12802;69966 -12969 1;69421 -13014;68997 -12591;68831 -12069 1;68703 -11666;68308 -11607;68177 -11205 1;68035 -10770;68493 -10475;68339 -10044 1;68290 -9905;68250 -9793;68211 -9684;68112 -9704;63906 -10741;63420 -10863;61439 -11358 1;60960 -11475;60793 -11811;60316 -11937 1;59931 -12039;59538 -11784;59453 -11395;53908 -12759;53200 -16622;53077 -16860;52934 -17053;52673 -17246;51938 -17543;55542 -17829;55752 -14697;67497 -15505;67362 -16899;67831 -16949;67719 -18898;69422 -19009;69594 -17738;72469 -17960;72284 -20748;71704 -20761;72975 -22315;74344 -22463;76480 -22460;76510 -22056;75631 -21969;75646 -21472;74416 -21379;74962 -13221 18; +69760 -20132;69884 -18102;72154 -18219;72018 -20267;69760 -20132 18; +-64342 72109;-64186 70948; +-63501 72222;-63345 71059; +-59039 72823;-58882 71656; +-65691 71927;-65536 70779; +-66497 71819;-66341 70658; +58171 -18458;63562 -18803;63377 -20913;63070 -20887;61470 -20782;61493 -20668 1;61485 -20498;61422 -20300;61271 -20290;58155 -20084;58171 -18458 18; +59445 -27083; +60108 -26507; +65014 -28014;58830 -27595;58396 -27570;58458 -26570 1;58470 -26376;58637 -26228;58831 -26240;65108 -26681;65014 -28014 18; +61260 -26448 1;61227 -26762;61452 -27055;61766 -27077;65061 -27308;65110 -26707;61260 -26448 18; +58375 -27589;58401 -27492;58458 -26570 1;58470 -26376;58637 -26228;58831 -26240;61262 -26411;61257 -26503 1;61254 -26796;61471 -27056;61766 -27077;65061 -27308;65055 -27429;65014 -28014;58375 -27589 18; +69191 -28288;69276 -26962;72227 -27151;72142 -28481;69191 -28288 18; +68600 -28249;65577 -28051;65676 -26731;68684 -26924;68600 -28249 18; +68184 -27586; +66762 -27507; +70060 -27655; +71700 -27751; +68600 -28249;65577 -28051;65676 -26731;68684 -26924;68600 -28249 18; +69191 -28288;69276 -26962;72227 -27151;72142 -28481;69191 -28288 18; +73745 -27439 32;74817 -27517 32;74752 -28414 32;73680 -28336 32;73745 -27439 50; +73744 -27456 32;74816 -27534 32;74752 -28414 32;73680 -28336 32;73744 -27456 50; +73468 -28632;74930 -28738; +73469 -28568 32;72686 -28518 32;72715 -28064 32;73534 -28116 48; +73561 -27691;72742 -27639 32;72770 -27205 32;73566 -27256 48; +73221 -28367; +73258 -27454; +73469 -28568 32;72686 -28518 32;72715 -28064 32;73534 -28116 32;73469 -28568 50; +73561 -27691;72742 -27639 32;72770 -27205 32;73566 -27256 32;73561 -27691 18; +72297 -26365 32;72820 -26403 32;72797 -26724 32;72274 -26686 32;72297 -26365 50; +68781 -26111 32;69304 -26149 32;69281 -26470 32;68758 -26432 32;68781 -26111 50; +65209 -25853 32;65732 -25891 32;65709 -26212 32;65186 -26174 32;65209 -25853 50; +64972 -26276;64080 -26222 32;64108 -25730 32;64964 -25782 16; +64108 -25730 32;64941 -25780 32;64911 -26272 32;64080 -26222 32;64108 -25730 50; +64524 -25979;64992 -26008; +69971 -26148 32;70804 -26198 32;70774 -26690 32;69943 -26640 32;69971 -26148 50; +70835 -26694;69943 -26640 32;69971 -26148 32;70827 -26200 16; +70387 -26397;70855 -26426; +74910 -28412 1;76636 -28400;77603 -28357;79328 -28329 1;80022 -28318;80418 -27617;80414 -26923;80340 -23543;79674 -23558 32;79637 -21903 32;80280 -21889;80250 -20520 1;80237 -19942;80446 -19608;80784 -19139 1;80931 -18935;80975 -18774;80970 -18523;80840 -11990; +80475 -23417 32;80443 -21994 32;79751 -22009 32;79783 -23432 32;80475 -23417 50; +74136 -25717;74189 -24829 32;74681 -24859 32;74630 -25711 16; +74681 -24859 32;74631 -25687 32;74139 -25657 32;74189 -24829 32;74681 -24859 50; +74388 -25707;74315 -26816; +74436 -25272;74402 -25739; +74915 -24737; +73519 -26916;77419 -27155;78283 -13156; +76157 -27149;76108 -27944; +75775 -27909;75609 -28262;74920 -28284;74977 -27320;75819 -27359;75775 -27909 18; +75811 -27891;76411 -27928; +78729 -27392; +77801 -28066;79047 -28038; +79955 -12378 1;80248 -12725;80135 -13072;80140 -13526 1;80160 -15338;80118 -16354;80079 -18165 1;80068 -18684;79806 -18942;79499 -19361 1;79093 -19916;79191 -20216;79174 -20903 1;79144 -22141;79033 -23011;79104 -24247 1;79126 -24640;79499 -24780;79499 -25173 1;79499 -26045;79528 -26534;79486 -27405 1;79472 -27690;79563 -27859;79721 -28096;80104 -27921 1;80308 -27654;80416 -27287;80414 -26923;80340 -23543;79674 -23558 32;79637 -21903 32;80280 -21889;80250 -20520 1;80237 -19942;80446 -19608;80784 -19139 1;80931 -18935;80975 -18774;80970 -18523;80844 -12192;79955 -12378 18; +59585 -11679 1;59752 -11897;60036 -12011;60316 -11937 1;60793 -11811;60960 -11475;61439 -11358;63906 -10741 32;64154 -10680;68214 -9678 33;68291 -9908;68314 -9973;68339 -10044 1;68493 -10475;68035 -10770;68177 -11205 1;68308 -11607;68703 -11666;68831 -12069 1;68997 -12591;69421 -13014;69966 -12969 1;72186 -12784;72785 -12496;75642 -12463 1;77335 -12444;78405 -12432;79947 -12364 32;80045 -12505 1;80222 -12811;80136 -13127;80140 -13526 1;80160 -15338;80118 -16354;80079 -18165 1;80068 -18684;79806 -18942;79499 -19361 1;79093 -19916;79200 -20355;79183 -21042 1;79180 -21160;79142 -21316;79132 -21662 1;79106 -22556;79045 -23226;79104 -24247 1;79126 -24640;79499 -24780;79499 -25173 1;79499 -26045;79528 -26534;79486 -27405 1;79479 -27541;79497 -27651;79534 -27755 16; +77819 -15750 32;77856 -15227 32;78177 -15251 32;78140 -15774 32;77819 -15750 50; +77575 -19798 32;77612 -19275 32;77933 -19299 32;77896 -19822 32;77575 -19798 50; +77173 -13340;76287 -13289 32;76315 -12796 32;77166 -12848 16; +76315 -12796 32;77144 -12844 32;77113 -13339 32;76287 -13289 32;76315 -12796 50; +77158 -13089;78384 -13168;78371 -13440; +76729 -13040;77198 -13076; +65809 -22580; +56240 -27709;56271 -27388;56309 -27276;56493 -25285;56561 -24421;58750 -24581;59989 -24643;60251 -20734;63206 -20920;63310 -20920;63600 -21213;63634 -21222;63348 -25703;64101 -25753;65218 -25838;65201 -25961;65186 -26174 32;65709 -26212 32;65732 -25891;68759 -26048;68773 -26222;68758 -26432 32;69281 -26470 32;69304 -26149;72266 -26344;72286 -26512;72274 -26686 32;72797 -26724 32;72820 -26403;74238 -26449;74185 -26903;73558 -27224;73357 -27243;72770 -27205 32;72742 -27639 32;73523 -27689;73521 -28100;73409 -28108;72715 -28064 32;72686 -28518 32;73464 -28568;73459 -28964;69166 -28655;69218 -28290;72142 -28481;72227 -27151;69276 -26962;58815 -26237;58732 -26248 1;58583 -26281;58468 -26410;58458 -26570;58396 -27570;58404 -27570;65008 -28002;65004 -28362;56240 -27709 18; +64970 -28364;64995 -28019;65118 -26637;65686 -26686;65671 -26796;65577 -28051;68600 -28249;68684 -26924;68795 -26890;69270 -26945;69268 -27081;69191 -28288;69263 -28293;69197 -28670;64970 -28364 18; +63306 -25717;63778 -18878;69239 -19262;69151 -20692;71454 -20814;73007 -22646;77386 -22891;77125 -26904;74473 -26677;74665 -24880;74159 -24827;74246 -23536;64301 -22751;64092 -25735;63306 -25717 18; +75647 -21473 32;76476 -21521 32;76445 -22016 32;75619 -21966 32;75647 -21473 50; +76061 -21717;76530 -21753; +76505 -22017;75619 -21966 32;75647 -21473 32;76498 -21525 16; +76490 -21766;77550 -21834; +76482 -22246;77515 -22313; +76486 -22500;76522 -21955; +75329 -21835;75527 -22124;76483 -22186; +74917 -13260;74621 -12162;80814 -11940;80900 -18800;80197 -20218;80222 -21785;79605 -21933;79740 -23512;80296 -23562;80296 -27387;79778 -28263;75571 -28349;75793 -27794;76410 -27165;77298 -27091;77644 -26905;78397 -13482;74917 -13260 18; +73437 -28967;73441 -28575;75003 -28566;78921 -28336 1;79052 -28334;79187 -28331;79328 -28329 1;80022 -28318;80418 -27617;80414 -26923;80340 -23543;80474 -23358;80443 -21994 32;80216 -21432;80197 -20218;80900 -18800;80814 -11940;81744 -11857 1;81744 -11903;81745 -11949;81746 -11995 1;81786 -14393;81782 -15738;81833 -18136 1;81847 -18803;81692 -19212;81320 -19766 1;81041 -20182;81001 -20513;81021 -21013 1;81109 -23234;81158 -24479;81246 -26700 1;81288 -27760;80691 -28839;79631 -28883;74795 -29081;73631 -28996;73437 -28967 18; +71024 -12165 32;70974 -10929 32;70188 -10961 32;70238 -12197 32;71024 -12165 50; +69838 -11397; +69594 -10246; +69978 -9827;70667 -10472;70684 -10944; +70536 -10359;72041 -10298; +72016 -9799 32;72656 -9773 32;72670 -10124 32;72030 -10150 32;72016 -9799 50; +77004 -9600 32;77644 -9574 32;77658 -9925 32;77018 -9951 32;77004 -9600 50; +72758 -11955;71043 -11994; +74788 -11187; +78221 -11210; +79117 -11015; +76443 -10657 32;77691 -10621 32;77710 -11278 32;76462 -11314 32;76443 -10657 50; +80819 -11889;80843 -8484; +80844 -7731;80868 -5276; +80893 -3265;80905 -970; +76156 -4244;79209 -4177; +76175 -5108;79229 -5040; +76128 -4253 32;79173 -4186 32;79191 -5003 32;76146 -5070 32;76128 -4253 50; +78793 -5007;78776 -4239; +78794 -4618; +77389 -4635; +77388 -5024;77371 -4256; +79154 -5044;79536 -5036;79565 -6419; +80239 -10677;79836 -10689;79872 -11886; +79865 -11887 32;79834 -10536 32;80697 -10516 32;80728 -11867 32;79865 -11887 50; +72020 -10484;72035 -10846;80733 -10487 16; +79824 -8528;80774 -8522;80706 -10496;72032 -10834;72032 -10155;72241 -10141;72670 -10124 32;72657 -9802;72883 -9773;76982 -9606;77009 -9734;77018 -9951 32;77658 -9925 32;77644 -9574;79829 -9501;79824 -8528 18; +69061 -9954;72035 -9806;72047 -10843;79721 -10528;79771 -11922;69605 -12194;69061 -9954 18; +80547 -8499;80533 -10492; +80561 -5290;80537 -7726; +80591 -1224;80580 -3261; +80811 -1157;75341 -1541; +73904 -2029;65789 -3844; +65415 -2920;65014 -1273; +76035 -36;74913 -93;74125 723; +79267 141;80872 222 32;80556 10042 32;77625 9065 32;76252 13651 32;75127 13538 32;75006 14769 32;74878 16063; +76495 13335 32;77156 13533 32;77483 12441 32;76822 12243 32;76495 13335 50; +77700 9409;80716 10414; +80255 12680;79737 20057; +80446 12704;80023 18729; +76069 20353;75035 19139;75053 18800; +77474 23024;78696 21881 1;78890 21700;79169 21689;79376 21855;79835 22223; +77106 14180 32;77731 14234 32;77283 19433 32;75106 19245 32;75143 18816 32;76918 18969 32;77061 17312 32;76838 17293 32;77106 14180 50; +76522 21900;77464 23043;77641 22906;78241 22317;78757 21846;79076 21736;79330 21807;79782 22184;79778 22252;80395 22267;81055 12753;80567 12715;80145 18730;79918 18720;80224 12691;77476 12464;77156 13533 32;76495 13335 32;76274 13576;76252 13651 32;75127 13538 32;75006 14769 32;74886 15982;75209 15955;75409 15966 32;75573 14073 32;77092 14205 32;77234 14191;77731 14234 32;77283 19433 32;75205 19254;76167 20350;76664 19931;77572 21109;76522 21900 18; +76747 12212;77611 12515;80286 12705;80355 12704;80576 12718;81034 12762;81106 11866 1;81264 8579;81394 6590;81485 3166 1;81643 -2754;81646 -6074;81746 -11995 1;81746 -11996;81746 -11998;81746 -11999;80991 -11965;80685 -8501;80650 -7673;80689 -5289;80697 -3222;80929 -1161;80807 252;80563 10005;77597 9080;76747 12212 18; +80913 148;80987 -1061;75424 -1504;65813 -3798;65159 -1306;74042 717;74992 -110;77385 -72;80913 148 18; +75251 -1627;80852 -1170;80753 -8635;79704 -8622;79507 -4909;79174 -4921;79161 -4218;75312 -4243;75251 -1627 18; +72242 17150;72772 17200;73451 15201;74894 15670;75141 13511;76239 13659;77645 9082;80555 10033;80827 287;75053 -133;73992 731;65282 -1046;65183 -1465;65652 -3735;65948 -3883;74041 -1860;75300 -5117;79519 -5043;79556 -6707;70279 -9816;69070 -9865;68799 -8903;53915 -12684;54382 -10742;55389 -6719;56079 -5232 1;57023 -3198;57553 -2059;58497 -25 1;59089 1251;59393 2050;59955 3212;60090 3486;60484 4253 1;61197 5576;62150 7067;63147 8405;63779 9313;65702 11355;69757 14953;72242 17150 18; +49501 -26594 1;49320 -27302;48378 -27802;47468 -27745 1;47140 -27725;46957 -27713;46629 -27693 1;39039 -27222;34783 -26960;27192 -26506 1;22464 -26223;19813 -26061;15084 -25791 1;10304 -25519;7621 -25423;2837 -25251 1;-3124 -25036;-6466 -24899;-12430 -24779 1;-14501 -24737;-15676 -24944;-17699 -25390 1;-19105 -25700;-19909 -25945;-21161 -26655 1;-22377 -27344;-23966 -27804;-24885 -26720 1;-27303 -23868;-28430 -22601;-30851 -19752 1;-32889 -17353;-33937 -15925;-36033 -13576 1;-36664 -12869;-36449 -11422;-35527 -11203 1;-31236 -10181;-28847 -9334;-24465 -8831 1;-21354 -8474;-19501 -8324;-16370 -8395 1;-10701 -8524;-7649 -8663;-2047 -8813 1;2347 -8930;4796 -9014;9190 -8919 1;13770 -8820;16304 -8942;20879 -8709 1;23766 -8562;25392 -8327;28185 -7632 1;31893 -6710;33913 -6017;37573 -4879 1;38798 -4498;40215 -4692;40647 -5541 1;42952 -10071;44342 -12565;46590 -17124 1;47502 -18974;48066 -19986;48945 -21852 1;49740 -23540;49967 -24772;49501 -26594 18; +32883 -9217 1;32313 -8493;31629 -8340;30708 -8300 1;29779 -8260;29259 -8215;28330 -8197;28336 -8058 1;32110 -7114;34264 -6452;37966 -5256 1;38914 -4950;39872 -5188;40332 -6072;41406 -8175 16; +40978 -13469;44091 -13453 32;41309 -8339 32;33050 -9354 32;33047 -10107 32;33250 -10111 32;33242 -11733 16; +35322 -16274; +36272 -16237; +36568 -14855; +37345 -14843; +38666 -14312 32;44599 -14227 32;44879 -14752 32;38674 -14841 32;38666 -14312 50; +44390 -14522; +39061 -14571; +38666 -14312 32;44599 -14227 32;44879 -14752 32;38674 -14841 32;38666 -14312 50; +34408 -17285;43698 -17174; +37572 -17063;37533 -14324;35096 -14359; +37528 -14370;35052 -14406;35041 -13685; +45405 -18950;47009 -19049 32;46009 -17146 32;43698 -17174; +48762 -23304 1;48572 -22627;48328 -22248;48067 -21595;45140 -21429;45196 -20441; +48762 -23304 1;49065 -24383;49141 -25079;48888 -26170 1;48735 -26830;48129 -27185;47453 -27144;43183 -26885 16; +40517 -7295; +40900 -7158;41362 -6941;41421 -7040 1;43281 -10676;44604 -13096;46590 -17124 1;47470 -18908;48025 -19913;48852 -21656;48408 -20735 1;48577 -21079;48753 -21445;48945 -21852 1;49677 -23407;49906 -24585;49595 -26178;48929 -25982 1;49129 -25008;49049 -24336;48773 -23342;48684 -23049 1;48507 -22522;48293 -22161;48067 -21595;45140 -21429;45196 -20441;45227 -19013;46954 -19048;46047 -17233;37582 -17085;37570 -16920;37533 -14263;38666 -14233 32;38674 -14841 32;44879 -14752 32;44599 -14227;41443 -8203;40900 -7158 18; +38666 -14312 32;44599 -14227 32;44879 -14752 32;38674 -14841 32;38666 -14312 50; +35110 -14373;35070 -13687;44312 -13561;44679 -14224;38616 -14337;35110 -14373 18; +34501 -13636;35019 -13611;35036 -14419;37545 -14369;37589 -17065;34431 -17109;34501 -13636 18; +41366 -6941;42967 -6137;43233 -6668;47681 -15309 1;48093 -16109;48720 -16420;49607 -16572;49564 -16053;49751 -16034 1;50609 -15926;51355 -15566;51557 -14715;51564 -14275;51931 -12139;53427 -12591 1;53420 -12623;53412 -12654;53405 -12686;53312 -13098 1;53113 -13993;52933 -14943;52766 -16136 1;52721 -16459;52558 -16724;52264 -16864 1;51629 -17167;50526 -17575;50302 -18151 1;49815 -19400;50372 -19123;51378 -21909 1;51489 -22216;51566 -22478;51620 -22732;51693 -23170 1;51725 -23431;51741 -23707;51752 -24037 1;51772 -24612;51792 -24936;51761 -25511 1;51730 -26082;51948 -26609;52322 -26983;49601 -26145 1;49903 -24569;49672 -23396;48945 -21852 1;48753 -21445;48577 -21079;48408 -20735;48852 -21656 1;48025 -19913;47470 -18908;46590 -17124 1;45986 -15899;45447 -14821;44938 -13813;44434 -12820 1;43490 -10963;42564 -9268;41491 -7171;41366 -6941 18; +51616 -30253; +50482 -34388; +49871 -38907; +49260 -43426; +48493 -48049; +48496 -29303;48453 -30603;49351 -30655 1;49388 -30054;49192 -29365;48592 -29312;48496 -29303 18; +48663 -31019;49290 -31048;49194 -31615;48645 -31569;48663 -31019 18; +48067 -29251;47884 -29687;47012 -29190;48067 -29251 18; +48112 -36086;48540 -36287;48496 -36557;48069 -36540;48112 -36086 18; +48073 -29257;48483 -29309; +49342 -30673;49283 -31066; +22830 -48486 1;23821 -50287;24174 -51459;25532 -53002; +40966 -29419 1;43271 -29557;44272 -29533;46564 -29811 1;47306 -29901;48124 -30779;48033 -31521;47402 -35838;45983 -35788 16; +45946 -36480 32;47438 -36537 32;47391 -37766 32;45899 -37709 32;45946 -36480 50; +45083 -37664 32;46622 -37723 32;46582 -38755 32;45043 -38696 32;45083 -37664 50; +46379 -38935;47279 -38984;47637 -39490;46774 -44856; +41851 -51258;41840 -52204;44306 -51764 1;45191 -51606;45764 -50999;45910 -50111;46774 -44856; +82291 -39873;82168 -33261 1;82142 -31870;80943 -30819;79552 -30843 1;77943 -30871;77040 -30869;75432 -30941 1;74161 -30998;73447 -30895;72175 -30867 1;71250 -30847;70731 -30835;69806 -30815 1;68591 -30788;67753 -31996;67819 -33209;67985 -36263; +66507 -36177;66350 -32652 1;66297 -31452;65488 -30471;64291 -30367 1;60065 -30000;57688 -29887;53457 -29582 1;52022 -29479;50750 -30394;50526 -31815 1;49427 -38783;48861 -42698;47803 -49672 1;47596 -51034;47457 -51794;47253 -53156;45639 -54802 16; +81995 30074;79831 29690;79744 26075 1;79968 26053;80142 25955;80151 25816;80975 13914;81004 13496;81083 12362 1;81251 8771;81389 6759;81485 3166 1;81643 -2754;81646 -6074;81746 -11995 1;81774 -13672;81780 -14834;81799 -16202;81812 -17052 1;81818 -17392;81825 -17751;81833 -18136 1;81847 -18803;81692 -19212;81320 -19766 1;81041 -20182;81001 -20513;81021 -21013 1;81109 -23234;81158 -24479;81246 -26700 1;81288 -27760;80691 -28839;79631 -28883;79346 -28895;79360 -30846 1;79423 -30845;79487 -30844;79552 -30843 1;80943 -30819;82142 -31870;82168 -33261;82291 -39873;85275 -40155;85399 -35103 1;85481 -33659;86487 -32418;87929 -32521;89377 -32625;89498 -28983 1;85390 -26429;85579 -21920;85515 -19931 1;85368 -15325;85333 -13021;85166 -8416 1;84924 -1770;84432 1532;83874 8159 1;83156 16678;82865 21453;82088 29071;81995 30074 18;84659 -24816 1;85467 -26779;85931 -28158;87782 -29353;84677 -29143;84659 -24816 18; +79404 -28883;79456 -30837;79157 -30850 1;77785 -30871;76904 -30875;75432 -30941 1;74161 -30998;73447 -30895;72175 -30867 1;71250 -30847;70731 -30835;69806 -30815 1;68591 -30788;67753 -31996;67819 -33209;67985 -36263;66507 -36177;66350 -32652 1;66297 -31452;65488 -30471;64291 -30367 1;60065 -30000;57688 -29887;53457 -29582 1;52022 -29479;50750 -30394;50526 -31815 1;50525 -31823;50524 -31831;50522 -31839;49186 -31613;49271 -31233;49317 -30924;49330 -30840;49367 -30336 1;49290 -29909;49208 -29406;48517 -29306;48085 -29249;47064 -29202;47313 -27742;47677 -27741;47926 -27730;48323 -27644;48869 -27400;49291 -27065;49503 -26597 1;49550 -26401;49590 -26212;49623 -26028;52197 -26845 1;52236 -26893;52278 -26939;52322 -26983;52579 -27201 1;52858 -27396;53193 -27520;53558 -27544;56271 -27721;65560 -28403;66821 -28498;69197 -28670;71138 -28812;74795 -29081;78159 -28943;79404 -28883 18; +61254 -31343;53228 -30890 1;52281 -30837;51662 -31679;51519 -32617;50926 -36438 32;51991 -36438; +61254 -31343 1;62668 -31423;63460 -31467;64874 -31547 1;65504 -31583;65946 -32124;65946 -32755;66097 -36176 16; +48878 -30078; +48965 -31300; +48112 -36086;48540 -36287;48496 -36557;48069 -36540;48112 -36086 18; +48644 -31204;49271 -31233;49175 -31800;48626 -31754;48644 -31204 18; +48496 -29303;48453 -30603;49351 -30655 1;49388 -30054;49192 -29365;48592 -29312;48496 -29303 18; +48086 -29251;47903 -29687;47031 -29190;48086 -29251 18; +51563 -39310;50419 -39338;50761 -36535; +50755 -36538;52020 -36538;52055 -39295;50424 -39312;50755 -36538 18; +51389 -42635;50018 -42651 32;49045 -48212 32;50308 -47412 32;51072 -46928;51105 -48137; +51095 -49676;48847 -49733; +48763 -49553;47863 -54426; +49824 -54204;48011 -54237; +49586 -55488;47528 -55526 32;46048 -61344; +48736 -31578;49192 -31597;50524 -31794;50474 -32146 1;49409 -38909;48844 -42809;47803 -49672 1;47596 -51034;47457 -51794;47253 -53156;46079 -54354;45639 -54802;46164 -51670;45934 -51510;46433 -50462;48246 -39224;47777 -38656;48032 -36786;48041 -36561;48249 -36547;48496 -36557;48540 -36287;48112 -36086;48736 -31578 18; +53412 -12671;53340 -12970 1;53130 -13903;52941 -14886;52766 -16136 1;52721 -16459;52558 -16724;52264 -16864 1;51629 -17167;50526 -17575;50302 -18151 1;49815 -19400;50372 -19123;51378 -21909 1;51665 -22703;51723 -23194;51752 -24037 1;51772 -24612;51792 -24936;51761 -25511 1;51704 -26569;52501 -27475;53558 -27544;56271 -27721;56299 -27389;53571 -27186 1;53367 -27170;53152 -27090;52985 -26988;52849 -26928;52854 -26912;52839 -26906 1;52656 -26822;52540 -26764;52424 -26599 1;52342 -26482;52272 -26393;52242 -26279;52235 -26244;52282 -25450;52507 -25455;52476 -26196 1;52488 -26313;52536 -26372;52604 -26468 1;52673 -26566;52735 -26611;52843 -26660;52846 -26663;54879 -26850;54984 -25159;55107 -23839;55366 -23833;55399 -22923;52599 -22705;52544 -23593;52102 -23569;52102 -23568;52090 -23568 1;51965 -22678;51808 -22171;51524 -21322;51490 -21237;50706 -19460;50687 -19459 1;50582 -19207;50537 -19052;50537 -18784 1;50537 -18531;50621 -18377;50746 -18182;50830 -18041;51107 -18054;51794 -17556;51730 -17391;52447 -17084 1;52698 -16978;52944 -16783;52992 -16515;53683 -12737;53412 -12671 18; +66162 -36162;66490 -36163;66346 -32578 1;66263 -31412;65463 -30469;64291 -30367 1;60065 -30000;57688 -29887;53457 -29582 1;52022 -29479;50750 -30394;50526 -31815 1;50235 -33657;49982 -35286;49747 -36815;49572 -37955 1;48998 -41712;48511 -45007;47803 -49672 1;47646 -50706;47528 -51393;47391 -52259;47281 -52969 1;47272 -53030;47263 -53092;47253 -53156;45400 -61193;45934 -61312;47510 -55485;49918 -55398;49848 -54246;47877 -54246;48697 -49710;51018 -49605;51018 -47145;48994 -48297;49936 -42557;51681 -42522;51594 -39294;51332 -39316;50419 -39338;50761 -36535;51366 -33170;51612 -32212 1;51851 -31451;52420 -30845;53228 -30890;60955 -31326;61592 -31362 1;62803 -31431;63577 -31474;64874 -31547 1;65425 -31578;65833 -31997;65926 -32523;66162 -36162 18; +46888 -29857;47055 -29251;47878 -29675;48073 -29257;48495 -29311;48459 -30584;49357 -30655;49288 -31059;48663 -31025;48651 -31575;48760 -31584;47788 -38651;48257 -39243;46417 -50551 1;46235 -51451;45556 -51961;44696 -52224;44634 -51680 1;45332 -51444;45783 -50884;45910 -50111;46774 -44856;47614 -39367;47244 -38935;46553 -38812;46615 -37714;46810 -37744;47391 -37766 32;47438 -36537 32;45946 -36480 32;45973 -35764;47404 -35752;47725 -33626;48033 -31521 1;48117 -30839;47671 -30218;47022 -30053;46888 -29857 18; +37852 -55758;43644 -54677;45424 -54956;44621 -57869; +42956 -57633;44837 -57784; +47260 -53153;45394 -61208; +45593 -54841;47268 -53122;45462 -60913;45396 -61191;43927 -61002;43998 -60744;42714 -60642;42967 -57676;44738 -57702;45593 -54841 18; +42749 -60636;43998 -60737;43928 -60998; +37852 -55758;36137 -56111; +35926 -57978 32;34914 -57903 32;34881 -58350 32;35893 -58425 32;35926 -57978 50; +36066 -58009;36219 -55935; +34886 -56890;34925 -56359;31955 -56968; +31955 -56968;29069 -57528; +29069 -57528;24058 -58424 32;22445 -58713 32;24024 -58851 32;24977 -58934 32;25873 -59012 32;41285 -60361; +36023 -57997;36171 -56060;43635 -54703;45412 -54950;44647 -57713;42957 -57614;41304 -60366;36961 -59946;36023 -57997 18; +34916 -56356;34724 -58606;35056 -58659;34951 -59740;22773 -58676;30764 -57141;34916 -56356 18; +45932 -51514;46211 -51723 1;45770 -52360;45291 -52633;44527 -52761;41065 -53376;40990 -52929; +44492 -52290;44562 -52787;44390 -52819;41065 -53376;40991 -52935;44492 -52290 18; +44471 -52299;44615 -52248 1;45133 -52101;45591 -51867;45921 -51518;46050 -51597;46167 -51679;46210 -51737 1;45790 -52322;45284 -52607;44562 -52732;44471 -52299 18; +41735 -51821;41805 -50949;37408 -50966;37408 -52536;41735 -51821 18; +37425 -51685; +37447 -53637 32;38172 -53496 32;38255 -53924 32;37531 -54065 32;37447 -53637 50; +31905 -54655 32;34570 -54173 32;34653 -54601 32;31989 -55083 32;31905 -54655 50; +23476 -53626;23825 -53382 1;24780 -54735;25640 -55391;27126 -56122;26564 -56270 1;25238 -55450;24428 -54954;23476 -53626 18; +26373 -55929; +25056 -55030; +23983 -53966; +25815 -52995; +27061 -53980; +28443 -54695; +29763 -55004; +31905 -54655 32;34570 -54173 32;34653 -54601 32;31989 -55083 32;31905 -54655 50; +37836 -53786; +48740 -31587;47777 -38656;48246 -39224;46433 -50462 1;46247 -51618;45267 -52160;44113 -52362 1;39328 -53200;36671 -53818;31886 -54656 1;31341 -54751;30829 -54790;30342 -54775 16; +30342 -54775 1;28644 -54720;27060 -53903;25780 -52516;25353 -52803 1;26794 -54380;28353 -55272;30500 -55344;30300 -54785 16; +30293 -54778 1;28595 -54723;27130 -53964;25780 -52534;25379 -52803 1;26785 -54380;28353 -55272;30500 -55344;30300 -54785;30293 -54778 18; +23476 -53626;23825 -53382 1;24780 -54735;25640 -55391;27126 -56122;26564 -56270 1;25238 -55450;24428 -54954;23476 -53626 18; +19577 -57853;26004 -56509 1;24698 -55623;24017 -54997;23056 -53745 1;22405 -52897;22279 -52266;21723 -51352 1;21537 -51046;21290 -50844;20934 -50883 1;20553 -50925;20451 -51282;20305 -51636 1;20006 -52362;19843 -52800;19811 -53585;19577 -57853 18; +22446 -53065 1;22093 -52349;21942 -51915;21487 -51259 1;21364 -51082;21202 -50992;20989 -51024 1;20787 -51054;20641 -51146;20571 -51338 1;20377 -51872;20224 -52149;20082 -52699 1;19983 -53081;19943 -53307;19943 -53702; +23609 -55982; +21684 -56007; +24712 -55723;24904 -56569;19743 -57656;19844 -55777; +21229 -57312 1;21199 -56960;21300 -56568;21649 -56510 1;22509 -56367;23149 -56583;23832 -56041;24599 -55445 1;25000 -55798;25445 -56130;26004 -56509;24955 -56718;24753 -56028;23472 -56478;23549 -56787;21229 -57312 18; +23514 -56787;23453 -56473;24718 -56063;24822 -56508;23514 -56787 18; +23514 -56787;23453 -56473;24718 -56063;24822 -56508;23514 -56787 18; +22421 -52960;22552 -52994;22599 -53050 1;22723 -53272;22867 -53499;23056 -53745 1;23601 -54454;24055 -54963;24585 -55432;24458 -55554;23832 -56041 1;23149 -56583;22509 -56367;21649 -56510 1;21300 -56568;21199 -56960;21229 -57312;21241 -57309;21082 -57374;19743 -57656;19836 -55925;19665 -55786;19813 -53684;19945 -53567 1;19953 -53251;19995 -53035;20082 -52699 1;20224 -52149;20377 -51872;20571 -51338 1;20641 -51146;20787 -51054;20989 -51024 1;21202 -50992;21364 -51082;21487 -51259 1;21844 -51774;22014 -52153;22242 -52638;22421 -52960 18; +26920 -59533;22726 -59212 1;21649 -59130;21033 -59184;19962 -59323 1;16631 -59754;14773 -60172;11438 -60679 1;8330 -61151;6665 -61828;3567 -62363 1;-189 -63012;-2296 -63524;-6103 -63709 1;-6416 -63724;-6594 -63700;-6903 -63651 16; +22793 -46842 1;23157 -47274;23459 -47682;23725 -48078 33;24052 -48571;24110 -48660;24360 -49087 33;24877 -49976;25461 -51126;26271 -52159 1;27212 -53359;28905 -54428;30796 -54187 1;31563 -54089;31993 -54034;32760 -53936; +24432 -48446;24165 -48591; +28672 -46418;25526 -46343 32;25534 -47776; +28672 -46418;30754 -46502;31085 -46877;31914 -46894;31896 -47522; +32089 -46724;32150 -43209; +24185 -48531;25476 -47746;25476 -46402;30605 -46472;31024 -46926;32088 -46804;32090 -46647;32150 -43209;32367 -38254;40802 -37316;45390 -37456;46646 -38939;47292 -39061;47571 -39514;45889 -50460;45272 -51323;44532 -51669;41646 -52384;32763 -54099;30852 -54180 1;30834 -54182;30815 -54185;30796 -54187 1;28905 -54428;27212 -53359;26271 -52159 1;25568 -51263;25035 -50278;24569 -49453;24185 -48531 18; +41667 -52396;32747 -54095; +31342 -44398; +28929 -44361; +26560 -44336; +27053 -43502 32;28239 -43548 32;28223 -43969 32;27037 -43923 32;27053 -43502 50; +27001 -43912;26223 -43882;26184 -44890;29151 -45005;29205 -43599; +31125 -43668 32;31070 -45081 32;31604 -45102 32;31659 -43689 32;31125 -43668 50; +31125 -43668 32;31070 -45081 32;31604 -45102 32;31659 -43689 32;31125 -43668 50; +26223 -43882;26184 -44890;29151 -45005;29205 -43599;28246 -43577;28235 -43953;26223 -43882 18; +22233 -42825;22169 -44436; +21196 -43798;21397 -44391;20638 -45150 1;19611 -44652;18952 -44415;17811 -44408;13451 -44204 16; +17837 -43667;17811 -44408 1;18952 -44415;19611 -44652;20638 -45150;21397 -44391;21196 -43798;17837 -43667 18; +20626 -45175;22768 -46904 1;23132 -47336;23459 -47682;23725 -48078 33;23869 -48294;23960 -48433;24039 -48556;25495 -47712;25507 -46318;30726 -46490;31096 -46848;31972 -46848;32090 -46662;32150 -43209;31158 -43098;31133 -43628;31294 -43675;31659 -43689 32;31604 -45102 32;31070 -45081 32;31124 -43697;29184 -43603;29198 -43775;29151 -45005;26184 -44890;26223 -43882;27001 -43912;27037 -43554;25743 -43462;25182 -43440 32;25200 -42952 32;21230 -42795 32;21191 -43788 32;21234 -43910;21397 -44391;20842 -44946;20626 -45175 18; +22832 -48489 32;22439 -48005 32;22798 -47714 32;23191 -48198 32;22832 -48489 50; +22127 -47620 32;21734 -47136 32;22093 -46845 32;22486 -47329 32;22127 -47620 50; +16792 -46797 1;18472 -46868;19672 -47119;20802 -48364;20457 -48677;20419 -48635 1;19414 -47524;18253 -47369;16760 -47246;16792 -46797 18; +22802 -48123; +22105 -47241; +20162 -48098; +19703 -48646 1;19150 -48317;18664 -48125;18162 -48013 33;17593 -47885;17004 -47861;16273 -47857 32;12865 -47786 32;12809 -50455 32;12205 -51034 32;12085 -56721 32;10700 -56692 32;10753 -54152 32;9584 -54128 16; +19727 -48535 1;20051 -48756;20357 -49066;20196 -49424;18950 -52138;18793 -57894; +16187 -48363;16412 -47858 1;17680 -47870;18525 -47964;19631 -48604;19835 -48613 1;20114 -48826;20339 -49106;20196 -49424;18950 -52138;18798 -57702;17520 -57860;17572 -55602 32;17867 -55609 32;17947 -52119 32;18445 -52130 32;18485 -50390 32;19027 -50402 32;19065 -48760 32;18449 -48746 32;18456 -48455 32;17176 -48426;16187 -48363 18; +22439 -48005;22127 -47620; +20187 -45866;20342 -45499;20638 -45150;20673 -45115;20964 -45406;22793 -46842 1;22843 -46901;22891 -46960;22939 -47018;25416 -50932;26736 -52795;28266 -53683;29610 -54176;32534 -54078;32880 -54077;41646 -52384;44532 -51669;44788 -51549;44789 -52194 1;44758 -52205;44727 -52214;44696 -52224;42078 -52735;40991 -52935;40131 -53095 1;37466 -53607;35180 -54079;31886 -54656 1;31341 -54751;30829 -54790;30342 -54775;29641 -54708 1;28210 -54482;26882 -53710;25780 -52516;25353 -52803;25038 -52401 1;24068 -51135;23689 -50046;22830 -48486;22916 -48421;23191 -48198 32;22798 -47714 32;22440 -48004;22149 -47647;22127 -47620;22270 -47504;22486 -47329 32;22093 -46845 32;21760 -47115;21556 -46957 1;20749 -46163;20064 -45735;19008 -45414;20187 -45866 18; +20457 -48677;20457 -48677 1;21384 -49705;21776 -50414;22351 -51673 1;22716 -52470;22960 -52909;23486 -53610; +18634 -45308;18365 -46966 1;19285 -47156;20059 -47545;20802 -48364;20470 -48665;20773 -49040 1;21482 -49886;21846 -50567;22351 -51673 1;22713 -52465;22957 -52903;23475 -53596;23603 -53537;23825 -53382 1;24780 -54735;25640 -55391;27126 -56122;27485 -57684;34986 -56277;34961 -57931;35158 -57921;35926 -57978 32;36121 -56080;43671 -54600;45695 -54945;46312 -51787;46116 -51853 1;45701 -52398;45237 -52642;44527 -52761;41065 -53376;40995 -52958;40771 -52976;40131 -53095 1;39469 -53222;38829 -53347;38192 -53472;38203 -53658;38255 -53924 32;37531 -54065 32;37447 -53637 32;37280 -53650 1;36408 -53820;35521 -53992;34565 -54171;34593 -54293;34653 -54601 32;31989 -55083 32;31905 -54655 32;31722 -54683 1;31238 -54758;30780 -54788;30342 -54775;30335 -54883;30500 -55344 1;28353 -55272;26794 -54380;25353 -52803;25074 -52449 1;24078 -51162;23699 -50066;22830 -48486;22679 -48300;22439 -48005 32;22358 -47905;22127 -47620;22052 -47527;21734 -47136 32;21666 -47067 1;20795 -46187;20088 -45733;18950 -45396;18634 -45308 18; +65933 -36163;64841 -36189;62416 -39225;60759 -39295;59991 -38091;56135 -38806;53850 -38928;53379 -39400;53013 -47285;52925 -48158;51878 -49484;51966 -51316;51198 -56741;51267 -59306;47987 -61697;46068 -61313;47551 -55538;49976 -55468;49907 -54142;47935 -54194;48667 -49728;51058 -49589;51023 -46919;49051 -48280;49976 -42593;51825 -42540;52053 -36468;51041 -36381;51038 -35713;51519 -32617 1;51662 -31679;52281 -30837;53228 -30890;61225 -31341;61606 -31363 1;62809 -31431;63582 -31474;64874 -31547 1;65425 -31578;65833 -31997;65926 -32523;66138 -35788;65933 -36163 18; +-44103 -67138; +-41919 -66509; +10924 -58534;15740 -58185; +15727 -58191;18789 -57746; +3336 -58833;10924 -58534; +13459 -60032; +2310 -61524 1;-269 -62051;-1696 -62441;-4282 -62936 1;-6671 -63393;-8048 -63442;-10476 -63581 1;-15162 -63850;-17775 -64219;-22462 -64471 1;-25572 -64638;-27310 -64811;-30418 -65012 1;-32102 -65121;-33047 -65227;-34710 -65518 1;-36005 -65745;-36736 -65852;-38025 -66111 1;-39322 -66372;-40217 -67608;-39944 -68902 1;-39803 -69570;-39724 -69945;-39579 -70612; +27565 -59310;26920 -59533;22726 -59212 1;21649 -59130;21033 -59184;19962 -59323 1;19629 -59366;19310 -59409;19004 -59452;18718 -59493 1;16117 -59865;14335 -60239;11438 -60679 1;8330 -61151;6665 -61828;3567 -62363 1;3481 -62378;3397 -62393;3313 -62407;2984 -62465 1;-417 -63064;-2498 -63517;-6103 -63692;-6488 -63706;-6803 -63645;-6347 -63454;-554 -62339;2259 -61796;2444 -61885 1;4502 -61639;5704 -61091;7598 -60597;8625 -60337 1;8899 -60262;9136 -60198;9483 -60186 1;10255 -60159;10687 -60106;11454 -60018 1;15241 -59584;17425 -59130;21022 -58528;21439 -58459 1;21503 -58448;21567 -58438;21632 -58427 1;22562 -58274;24825 -58024;25765 -57958;23464 -58566;23325 -58828;27565 -59310 18; +26910 -59534;10163 -62480;-3585 -65062;-37816 -67644;-37258 -70366;-39295 -70533;-39740 -68608;-39740 -68275;-39634 -67754;-39331 -67235;-39142 -66993;-38850 -66756;-37882 -66345;-35505 -65942;-32578 -65478;-30410 -65299;-23378 -64819;-15603 -64250;-13243 -64023;-7134 -63644;-6624 -63692 1;-6458 -63712;-6310 -63719;-6103 -63709 1;-2296 -63524;-189 -63012;3567 -62363 1;6665 -61828;8330 -61151;11438 -60679 1;14027 -60285;15725 -59946;17914 -59612;18523 -59521 1;18588 -59512;18653 -59502;18718 -59493;19004 -59452 1;19310 -59409;19629 -59366;19962 -59323 1;21033 -59184;21649 -59130;22726 -59212;25927 -59457;26910 -59534 18; +26001 -56510;26575 -56276;26311 -56114 1;25132 -55382;24366 -54868;23476 -53626;23425 -53529 1;22941 -52874;22701 -52439;22351 -51673 1;21846 -50567;21482 -49886;20773 -49040;20470 -48665;20344 -48554 1;19356 -47516;18216 -47366;16760 -47246;16762 -47221;16735 -47863 1;17780 -47889;18549 -48008;19495 -48527;19791 -48580 1;20089 -48797;20346 -49090;20196 -49424;18950 -52138;18793 -57894;19578 -57853;19577 -57853;19811 -53585 1;19843 -52800;20006 -52362;20305 -51636 1;20451 -51282;20553 -50925;20934 -50883 1;21290 -50844;21537 -51046;21723 -51352 1;22279 -52266;22405 -52897;23056 -53745 1;23928 -54882;24570 -55502;25658 -56270;26001 -56510 18; +25965 -56491;26601 -56269;27168 -56127;27501 -57614;25385 -57984;23947 -58132 1;23067 -58228;22146 -58342;21632 -58427 1;17662 -59080;15451 -59560;11454 -60018 1;10687 -60106;10255 -60159;9483 -60186 1;8977 -60204;8706 -60330;8213 -60445 1;5899 -60985;4666 -61639;2304 -61901;2304 -61210;2378 -61210 1;3165 -61039;3596 -60901;4376 -60704 1;4512 -60670;4245 -60461;4105 -60469 1;2855 -60538;1754 -60601;720 -60652;186 -59182 1;1266 -59117;2348 -59050;3397 -58993;10873 -58634;15808 -58264;18788 -57871;19564 -57845;20202 -57722;25933 -56524;25965 -56491 18; +-45557 -63379;-42362 -62638; +25765 -57958 1;24825 -58024;22562 -58274;21632 -58427 1;17662 -59080;15451 -59560;11454 -60018 1;10687 -60106;10255 -60159;9483 -60186 1;8977 -60204;8706 -60330;8213 -60445 1;5899 -60985;4666 -61639;2304 -61901;2304 -61210;2378 -61210 1;3165 -61039;3596 -60901;4376 -60704 1;4512 -60670;4245 -60461;4105 -60469 1;-451 -60719;-3023 -60904;-7578 -60636 1;-12853 -60326;-15816 -60026;-21099 -60118 1;-25865 -60201;-28579 -60130;-33313 -60685 1;-35761 -60972;-37145 -61034;-39516 -61706 1;-40601 -62014;-41512 -62264;-42386 -62501 16; +-44657 -64381 32;-43004 -63997 32;-42789 -64924 32;-44442 -65308 32;-44657 -64381 50; +-41335 -65919 1;-41541 -65117;-41610 -64649;-41644 -63822 1;-41658 -63485;-41658 -63295;-41644 -62958 1;-41630 -62619;-41219 -62482;-40892 -62391 1;-40044 -62156;-39624 -61773;-38782 -61516 1;-38577 -61454;-38432 -62274;-38313 -62453 1;-38095 -62780;-37935 -63041;-37548 -63106 1;-37165 -63170;-36980 -63461;-36869 -63834 1;-36716 -64349;-36758 -64667;-36758 -65204;-41335 -65919 18; +-35110 -60919 1;-33784 -60734;-33040 -60636;-31708 -60500 1;-30186 -60345;-29324 -60368;-27800 -60238 1;-27732 -60232;-27803 -60345;-27800 -60413 1;-27786 -60700;-27950 -61173;-28236 -61198 1;-29917 -61347;-30864 -61418;-32545 -61564 1;-33082 -61611;-33281 -62083;-33819 -62122 1;-34291 -62157;-34722 -61915;-34901 -61477 1;-34989 -61262;-35340 -60951;-35110 -60919 18; +-20781 -61837 1;-20615 -62472;-20698 -62939;-20678 -63595;-17562 -63356;-17588 -62654 1;-17562 -62004;-17471 -61711;-17039 -61225 1;-16727 -60874;-16567 -60622;-16603 -60153;-17237 -60122 1;-18416 -60097;-19474 -60093;-20900 -60118 1;-20883 -60791;-20952 -61186;-20781 -61837 18; +-13872 -62993 1;-13886 -62163;-14006 -61661;-14415 -60939 1;-14566 -60672;-14826 -60302;-14533 -60212;-14232 -60210 1;-12790 -60279;-11358 -60374;-9736 -60477 1;-9498 -60492;-9424 -60769;-9434 -61007 1;-9449 -61376;-9825 -61497;-10184 -61583 1;-10749 -61719;-11343 -61603;-11563 -62141 1;-11663 -62386;-11741 -62525;-11702 -62787;-13872 -62993 18; +-16626 -62671; +-14653 -62017; +-17799 -63370;-17865 -61709;-16668 -60105;-16494 -60120 1;-15818 -60137;-14955 -60189;-14308 -60218;-13700 -61997;-13832 -62997;-14781 -63091;-17799 -63370 18; +-10047 -60496;-9680 -60507 1;-9016 -60549;-8320 -60592;-7578 -60636 1;-5000 -60788;-3057 -60794;-1030 -60726 1;-803 -60718;-636 -60950;-661 -61176 1;-687 -61406;-852 -61556;-1080 -61595 1;-3932 -62089;-5530 -62589;-8425 -62589 1;-9804 -62589;-10618 -62656;-11988 -62812;-11993 -62726 1;-12005 -62513;-11934 -62366;-11845 -62149 1;-11625 -61611;-11031 -61727;-10466 -61591 1;-10107 -61505;-9731 -61384;-9716 -61015 1;-9709 -60848;-9743 -60661;-9847 -60560;-10047 -60496 18; +-1158 -60692;-618 -60711 1;810 -60657;2302 -60568;4105 -60469 1;4245 -60461;4512 -60670;4376 -60704 1;3596 -60901;3165 -61039;2378 -61210;2304 -61210;2304 -61407;-3949 -62820;-8328 -63344;-11888 -63553;-13946 -63780;-24763 -64670;-29265 -64861;-32931 -65215;-37446 -66029;-39148 -66622;-39963 -67510;-40111 -68694;-39765 -70644;-44083 -71976;-45712 -63488;-42430 -62550;-42126 -62430 1;-41331 -62214;-40494 -61984;-39516 -61706 1;-37145 -61034;-35761 -60972;-33313 -60685 1;-28579 -60130;-25865 -60201;-21099 -60118 1;-20599 -60109;-20120 -60104;-19658 -60102;-17725 -60113 1;-14309 -60166;-11624 -60398;-7578 -60636 1;-5241 -60773;-3426 -60792;-1598 -60743;-1158 -60692 18; +-691 -60966 1;-665 -61031;-653 -61104;-661 -61176 1;-687 -61406;-852 -61556;-1080 -61595 1;-3932 -62089;-5530 -62589;-8425 -62589 1;-8628 -62589;-8819 -62590;-9001 -62593;-9460 -62605 1;-10295 -62633;-10996 -62699;-11988 -62812;-12366 -62845;-14427 -63021;-15096 -63089;-15203 -63100;-18213 -63396;-18887 -63458;-21285 -63641;-21769 -63701;-24566 -63939;-25204 -63971;-26288 -64048 1;-29975 -64340;-32039 -64560;-35710 -65008;-36594 -65154;-36918 -65229;-41335 -65919 33;-41541 -65117;-41610 -64649;-41644 -63822 1;-41658 -63485;-41658 -63295;-41644 -62958 1;-41630 -62619;-41219 -62482;-40892 -62391 1;-40542 -62294;-40265 -62172;-40001 -62044; +-5414 -57917;-5479 -59452; +-5414 -57917;-5484 -59461; +-46167 -61470 1;-43576 -60955;-42125 -60652;-39518 -60224 1;-36465 -59723;-34746 -59434;-31659 -59225 1;-28729 -59027;-27082 -58958;-24145 -58905 1;-21240 -58852;-19611 -58922;-16706 -58967 1;-12987 -59025;-10910 -59284;-7193 -59411 1;-6707 -59428;-6191 -59433;-5654 -59429 16; +-5289 -59425 1;-2633 -59387;509 -59149;3397 -58993 16; +2501 -49521;3182 -49556 32;3235 -47244 33;1849 -46957;1083 -46357;7 -45438 32;-3587 -48857 32;-3692 -52975 32;-4652 -53830 32;-4844 -57930 32;-709 -58245;3548 -58210 32;3538 -58855; +-5010 -58037;-5034 -59406;-4924 -59419 1;-2348 -59366;640 -59142;3397 -58993;3441 -58271;-766 -58370;-5010 -58037 18; +-5030 -57967;-5640 -57930 1;-6432 -58156;-7335 -57987;-7975 -57468;-13228 -56791;-17759 -56386;-21585 -56242;-27683 -56149;-28076 -56707 1;-31927 -56758;-33765 -56732;-36978 -56916 1;-39791 -57077;-41460 -57092;-44272 -57282;-44334 -55975; +-16212 -56655;-14860 -56777; +-14895 -56883 1;-14249 -57167;-13926 -57545;-13220 -57563 1;-12607 -57578;-12276 -57270;-11667 -57336 1;-10706 -57441;-10174 -57593;-9207 -57615 1;-8781 -57625;-8471 -57725;-8126 -57476;-8369 -57417;-13228 -56791;-14880 -56643;-14895 -56883 18; +-13975 -57376 1;-14289 -57953;-14294 -58376;-14270 -59033;-14074 -59047 1;-11792 -59151;-9941 -59317;-7193 -59411 1;-6723 -59427;-6225 -59433;-5707 -59429;-5622 -57931;-5640 -57930 1;-6432 -58156;-7335 -57987;-7975 -57468;-8112 -57450;-8200 -57524 1;-8516 -57711;-8812 -57624;-9207 -57615 1;-10174 -57593;-10706 -57441;-11667 -57336 1;-12276 -57270;-12607 -57578;-13220 -57563 1;-13505 -57556;-13727 -57490;-13934 -57395;-13975 -57376 18; +-19456 -58910 1;-19296 -58598;-19096 -58439;-18758 -58343 1;-18174 -58178;-17804 -58175;-17205 -58273 1;-16888 -58325;-16737 -58632;-16743 -58953;-19456 -58910 18; +-22165 -56247 1;-21876 -56753;-21513 -57108;-20931 -57086 1;-20386 -57065;-19786 -56889;-19746 -56345;-22165 -56247 18; +-26488 -56194 1;-26910 -56555;-27551 -56899;-27273 -57380 1;-27055 -57757;-26628 -57933;-26226 -57764 1;-25394 -57415;-24734 -57176;-23923 -57572 1;-23350 -57852;-22893 -58245;-22946 -58881;-23223 -58892 1;-23517 -58895;-23823 -58899;-24145 -58905 1;-26210 -58942;-27637 -58987;-29320 -59080 1;-29738 -59103;-29155 -58408;-29279 -58008 1;-29433 -57509;-29522 -57221;-29785 -56770;-29664 -56726 1;-29172 -56720;-28646 -56715;-28076 -56707;-27683 -56149;-26819 -56162;-26488 -56194 18; +-41384 -60582;-41576 -57128; +-41384 -60582;-41576 -57097; +-42340 -60976;-45675 -58642 1;-46086 -58354;-46306 -58069;-46391 -57575;-48869 -42674;-49033 -41692;-48615 -41622;-48944 -39656; +-48654 -42114;-47599 -42070; +-40958 -58645; +-32839 -56778 1;-32691 -57760;-32769 -58306;-32865 -59294;-32753 -59307 1;-32406 -59278;-32043 -59251;-31659 -59225 1;-30830 -59169;-30068 -59114;-29386 -59076;-29418 -58993 1;-29454 -58798;-29149 -58316;-29244 -58008 1;-29398 -57509;-29417 -57186;-29680 -56735;-29728 -56749;-31464 -56744;-32839 -56778 18; +-40667 -57091;-40570 -57086 1;-39468 -57039;-38361 -56995;-36978 -56916 1;-35385 -56825;-34071 -56785;-32741 -56762;-32743 -56888 1;-32621 -57799;-32729 -58364;-32821 -59311;-33065 -59334 1;-35339 -59533;-36960 -59804;-39518 -60224 1;-39852 -60279;-40166 -60332;-40467 -60383;-40667 -57091 18; +-44288 -57288;-44127 -59336;-42277 -60693;-41623 -60582;-41796 -57153;-44288 -57288 18; +-41368 -57138;-40652 -57086;-40663 -57164;-40467 -60383;-41184 -60496;-41368 -57138 18; +-46421 -61377;-44293 -60941; +-40650 -57379;-40490 -60012; +-27381 -56150;-27434 -58994;-27223 -58984 1;-26294 -58950;-25323 -58926;-24145 -58905 1;-23823 -58899;-23517 -58895;-23223 -58892;-22859 -58889 1;-20696 -58873;-19154 -58929;-16706 -58967 1;-15776 -58982;-14949 -59009;-14173 -59043;-13877 -57441;-13871 -57423 1;-14203 -57283;-14484 -57064;-14895 -56883;-14880 -56643;-15047 -56628;-17759 -56386;-21585 -56242;-27228 -56156;-27381 -56150 18; +-8462 -57623;-8556 -57638 1;-8754 -57657;-8961 -57621;-9207 -57615 1;-10174 -57593;-10706 -57441;-11667 -57336 1;-12276 -57270;-12607 -57578;-13220 -57563 1;-13922 -57545;-14245 -57171;-14884 -56888; +-42190 -60587;-45453 -58314 1;-45792 -58078;-45951 -57824;-46012 -57415;-46160 -56420; +-46892 -56944;-46735 -57851 1;-46662 -58270;-46508 -58531;-46160 -58776;-43167 -60883; +-46320 -56051;-43198 -55742 32;-43180 -54789; +-48481 -42082;-46170 -55949; +-44608 -59971;-44842 -60921;-44324 -61094;-43226 -60872;-44608 -59971 18; +-47605 -52218;-48740 -52428;-48000 -55611;-47194 -58763;-46223 -61274;-44767 -60928;-44520 -60015;-46420 -58609;-46642 -57906;-47605 -52218 18; +-44344 -55969;-44274 -57287;-44126 -59171;-45722 -58116;-46054 -57470;-46228 -56728;-46307 -56179;-44344 -55969 18; +-5194 -57976;-4985 -57976;-5009 -59426;-5268 -59419;-5194 -57976 18; +243 -59164;805 -60645;215 -60676 1;-2298 -60791;-4479 -60818;-7578 -60636 1;-12853 -60326;-15816 -60026;-21099 -60118 1;-25865 -60201;-28579 -60130;-33313 -60685 1;-35761 -60972;-37145 -61034;-39516 -61706 1;-40601 -62014;-41512 -62264;-42386 -62501;-46066 -63380;-46596 -61555;-46288 -61493;-44166 -61086;-43770 -60993 1;-42420 -60728;-41214 -60502;-39518 -60224 1;-36465 -59723;-34746 -59434;-31659 -59225 1;-31586 -59220;-31515 -59215;-31443 -59211;-30975 -59180 1;-30420 -59143;-29909 -59112;-29421 -59085;-29072 -59067 1;-27494 -58983;-26108 -58940;-24145 -58905 1;-23823 -58899;-23517 -58895;-23223 -58892;-22946 -58881;-22387 -58886 1;-20462 -58882;-18972 -58932;-16706 -58967 1;-12987 -59025;-10910 -59284;-7193 -59411 1;-6707 -59428;-6191 -59433;-5654 -59429;-4798 -59416 1;-3319 -59382;-1709 -59295;-89 -59198;243 -59164 18; +-50362 -45946;-48704 -45527;-47588 -52244;-48669 -52488;-50362 -45946 18; +-51897 -39476;-49427 -38638;-49220 -39760;-48936 -41376;-49393 -41438;-48714 -45546;-50331 -45953;-51897 -39476 18; +-52964 -35502 1;-51792 -35275;-50961 -35396;-49789 -35624;-49705 -36870;-49407 -38668;-51921 -39527;-52964 -35502 18; +-49990 -30922;-40150 -30748 1;-41024 -32749;-41312 -34209;-40796 -36331;-49746 -36714; +-40848 -31288; +-49990 -30922;-40150 -30748 1;-41024 -32749;-41312 -34209;-40796 -36331;-49746 -36714;-49990 -30922 18; +-40229 -30729;-39716 -29931 32;-40090 -28777 32;-39000 -28845 32;-35444 -25708 32;-36027 -25028;-34410 -23641;-33829 -24318 32;-33463 -24004 1;-33186 -23766;-32946 -23467;-32796 -23148 33;-32572 -22671;-32549 -22151;-32905 -21736;-37022 -16938 32;-50195 -23150 1;-51024 -23499;-51039 -24463;-50882 -25349 1;-50505 -27481;-50293 -28677;-49916 -30809; +-35304 -25624;-33944 -24455; +-35427 -25501;-35776 -25074;-34450 -23896;-34013 -24324;-35427 -25501 18; +-49874 -30884;-50038 -30121 1;-50343 -28395;-50548 -27241;-50882 -25349 1;-51039 -24463;-51024 -23499;-50195 -23150;-37022 -16938 32;-32905 -21736 1;-32311 -22428;-32771 -23410;-33463 -24004;-33829 -24318 32;-34410 -23641;-36027 -25028;-35444 -25708 32;-39000 -28845 32;-40090 -28777 32;-39716 -29931 32;-40229 -30729;-49874 -30884 18; +-51077 -24831 1;-51914 -25190;-52843 -25786;-52542 -26646 1;-52332 -27245;-52112 -27665;-51530 -27919 1;-51113 -28101;-50852 -28143;-50449 -28355;-51077 -24831 18; +-54254 -30275;-50276 -29507;-49980 -30921;-49718 -36713;-52510 -37376;-54254 -30275 18; +-47728 -37816;-47623 -41933;-48251 -41907;-48661 -39508 1;-48784 -38787;-48538 -37992;-47824 -37833;-47728 -37816 18; +-35338 -50247;-35301 -48803 32;-35807 -48803;-36954 -49605 32;-38201 -49602 32;-42000 -49593; +-35475 -54600;-34649 -54614 32;-34598 -51700; +-48415 -42142;-42469 -42142;-41877 -49544;-37658 -49603;-36954 -49605 32;-35807 -48803;-35301 -48803 32;-35333 -50063;-35017 -50383;-34598 -51740;-34672 -54602;-36547 -54504;-36843 -53788;-43037 -54084;-43209 -55515;-46121 -56009;-48415 -42142 18; +-34495 -54744 1;-34529 -55257;-35054 -55484;-35568 -55508;-43072 -55885; +-43090 -54745;-38684 -54292;-35840 -54326;-35448 -54675;-34497 -54728;-34524 -54906 1;-34647 -55305;-35112 -55487;-35568 -55508;-43072 -55885;-43090 -54745 18; +-40947 -40924 1;-40951 -40524;-40420 -40532;-40023 -40479;-39185 -46289;-40171 -46254; +-40475 -48557;-39516 -48566;-39278 -48333; +-49335 -39723;-49079 -41286;-49515 -41354;-49373 -42184; +-48199 -41726;-48548 -39587; +-48944 -39656;-48662 -41343; +-49295 -42054;-48630 -41944; +-48900 -44439;-48235 -44329; +-48114 -49191;-47449 -49081; +-48509 -46806;-47844 -46696; +-46933 -56331;-46268 -56221; +-47328 -53946;-46663 -53836; +-47719 -51579;-47054 -51469; +-46828 -56965;-46163 -56855; +-46736 -57521;-46071 -57411; +-45979 -57883;-46553 -58247; +-45640 -58253;-46056 -58787; +-45640 -58253;-46056 -58787; +-45172 -58589;-45542 -59117; +-44700 -58920;-45070 -59448; +-44221 -59256;-44591 -59784; +-43730 -59600;-44100 -60128; +-43257 -59931;-43627 -60459; +-42773 -60270;-43143 -60798; +-47705 -39222;-47626 -40897; +-49246 -39743;-49703 -36881; +-48652 -39633;-48661 -39508 1;-48784 -38787;-48538 -37992;-47824 -37833;-47728 -37816;-47728 -37821; +-47724 -37677;-45081 -37681; +-42455 -37677;-40623 -37680;-40626 -39256; +-38283 -35809;-38209 -37031; +-38209 -37031;-37726 -43474; +-37982 -42505 1;-38235 -42615;-38446 -42783;-38426 -43058 1;-38409 -43296;-38159 -43463;-37920 -43450; +-38007 -42517 1;-38260 -42627;-38446 -42783;-38426 -43058 1;-38409 -43296;-38159 -43463;-37920 -43450;-38007 -42517 18; +-39760 -42302;-40979 -42328;-40947 -40924 1;-40951 -40524;-40420 -40532;-40023 -40479;-39760 -42302 18; +-40979 -42328;-39760 -42302;-39185 -46289;-40171 -46254;-41014 -43903;-40979 -42328 18; +-40422 -41427; +-40665 -42313;-39758 -42308; +-40509 -39253;-40166 -39239;-40022 -40468;-40229 -40502 1;-40581 -40541;-40950 -40590;-40947 -40924;-47660 -40869;-47722 -39204;-41726 -39216;-41701 -37674;-40517 -37649;-40509 -39253 18; +-40498 -37569;-40453 -37554;-40159 -39241;-40588 -39262;-40498 -37569 18; +-40598 -36489;-40451 -37556;-42326 -37649;-42343 -37824;-47696 -37823;-47813 -37831;-47824 -37833 1;-48538 -37992;-48784 -38787;-48661 -39508;-48636 -39655;-49257 -39751;-49720 -36846;-40598 -36489 18; +-29646 -46706 1;-29400 -46201;-29743 -45850;-30220 -45797 1;-30696 -45744;-31131 -45743;-31259 -46069 1;-31388 -46395;-31131 -46802;-30685 -46978 1;-30240 -47154;-29799 -47021;-29646 -46706 18; +-31242 -46496 1;-31565 -46707;-31844 -47025;-31687 -47377 1;-31496 -47804;-31300 -48215;-30832 -48215 1;-30495 -48215;-30182 -47967;-30196 -47630;-30213 -47046; +-31260 -46025;-31201 -45964 1;-31023 -45719;-30634 -45712;-30220 -45797 1;-29725 -45899;-29431 -46176;-29646 -46706 1;-29729 -46911;-29944 -47037;-30204 -47056;-30209 -47185;-30196 -47630 1;-30182 -47967;-30495 -48215;-30832 -48215 1;-31300 -48215;-31496 -47804;-31687 -47377 1;-31844 -47025;-31565 -46707;-31242 -46496;-31269 -46403 1;-31298 -46306;-31301 -46207;-31274 -46113;-31260 -46025 18; +-32561 -45615 1;-32797 -45615;-33069 -45656;-33233 -45796 1;-33363 -45906;-33426 -46078;-33355 -46339; +-31166 -45650;-30258 -45650;-30258 -45650 1;-29801 -45650;-29559 -45963;-29325 -46356; +-23546 -49530;-23546 -48395;-28776 -48393;-29307 -48912 32;-32663 -48875 32;-32688 -50220; +-14922 -54736;-14910 -55599 32;-13960 -55686 32;-13800 -49875 32;-16563 -49850;-21004 -48401 32;-22353 -48421 32;-22337 -49517; +-12691 -41913;-12270 -41933;-12218 -40276 32;-13200 -40255 32;-14665 -40224 32;-18866 -40136 32;-20471 -46574 32;-20000 -47481;-16882 -48474 32;-16493 -48598 32;-14382 -48633 32;-12395 -45987 32;-12290 -43008;-12726 -42993; +-12691 -41913;-12270 -41933;-12218 -40276 32;-13200 -40255 32;-14665 -40224 32;-18866 -40136 32;-20471 -46574 32;-20000 -47481;-16882 -48474 32;-16493 -48598 32;-14382 -48633 32;-12395 -45987 32;-12290 -43008;-12726 -42993;-12691 -41913 18; +-18238 -39474 1;-18222 -38930;-18245 -38458;-17794 -38154;-14993 -39190;-15080 -39535;-18238 -39474 18; +-11850 -39657 1;-11858 -39194;-11831 -38726;-11369 -38702 1;-11063 -38686;-10768 -38877;-10752 -39183;-9506 -39189;-9531 -39645;-11850 -39657 18; +-24468 -54260;-24480 -55062;-23074 -55050;-23061 -54507; +-15067 -55735 1;-18128 -55435;-19849 -55204;-22925 -55204; +-24639 -55204 1;-27478 -55213;-29068 -55310;-31906 -55401 1;-32070 -55406;-32161 -55409;-32325 -55414 1;-32921 -55433;-33551 -55330;-33560 -54734; +-33385 -51308;-33418 -54815; +-22307 -49558;-22282 -48423;-21812 -48413;-21004 -48401;-16563 -49850;-13800 -49875 32;-13960 -55686 32;-14927 -55739;-15198 -55722 1;-18178 -55428;-19893 -55204;-22925 -55204;-24726 -55204 1;-27511 -55215;-29097 -55311;-31906 -55401 1;-32070 -55406;-32161 -55409;-32325 -55414 1;-32921 -55433;-33551 -55330;-33560 -54734;-33459 -50990;-32769 -50077;-32667 -49086;-32663 -48875;-29307 -48912;-28776 -48393;-23546 -48395;-23546 -49530;-22307 -49558 18; +-35148 -48622;-32740 -48683;-32818 -50053;-33412 -51309;-33551 -54768;-33519 -54963 1;-33369 -55357;-32835 -55430;-32325 -55414 1;-32161 -55409;-32070 -55406;-31906 -55401 1;-29068 -55310;-27478 -55213;-24639 -55204;-22716 -55204 1;-19770 -55215;-18058 -55442;-15067 -55735;-13836 -55849;-13958 -56726;-14880 -56643;-15140 -56620;-17759 -56386;-21585 -56242;-27228 -56156;-27381 -56150;-27656 -56149;-27683 -56149;-28076 -56707 1;-28646 -56715;-29172 -56720;-29664 -56726;-29785 -56770;-30027 -56729 1;-32654 -56756;-34359 -56766;-36978 -56916 1;-38825 -57022;-40179 -57064;-41687 -57135;-41865 -57144 1;-42601 -57179;-43379 -57222;-44272 -57282;-44334 -55975;-42971 -55880;-35568 -55508 1;-35054 -55484;-34529 -55257;-34495 -54744;-34597 -51313;-35243 -49987;-35148 -48622 18; +-29408 -46910;-28968 -46916;-28900 -47674;-29607 -48323;-29650 -48751;-32800 -48672;-33210 -47399;-32704 -47154;-32285 -48271;-30069 -48288;-29711 -47878;-29408 -46910 18; +-28852 -48218 1;-28876 -47677;-28879 -47369;-28975 -46836 1;-29018 -46600;-29075 -46464;-29222 -46275; +-31153 -45548;-32565 -45523; +-33466 -46356;-32812 -48693; +-35107 -48649;-34697 -46782 1;-34729 -46150;-35129 -45572;-35762 -45561 1;-36470 -45549;-36868 -45541;-37576 -45529 1;-37802 -45525;-37870 -45816;-37847 -46041;-37498 -49478; +-36255 -47733; +-34929 -47800 1;-35528 -47807;-35800 -48184;-36395 -48253 1;-36684 -48287;-36947 -48080;-36980 -47791 1;-37021 -47425;-36952 -46926;-37320 -46910 1;-37525 -46901;-37738 -47045;-37721 -47250;-37688 -47612;-37498 -49478;-37233 -49604;-36954 -49605 32;-35807 -48803;-35301 -48803 32;-35092 -48579;-34957 -47968;-34929 -47800 18; +-37685 -45646;-36952 -45663; +-35513 -46056; +-34972 -46736; +-35168 -47824;-35168 -47824 1;-35612 -47908;-35885 -48194;-36395 -48253 1;-36684 -48287;-36947 -48080;-36980 -47791 1;-37021 -47425;-36952 -46926;-37320 -46910 1;-37360 -46908;-37400 -46912;-37438 -46921; +-33073 -47333;-32876 -47238;-32704 -47154 32;-32285 -48271 32;-30069 -48288 32;-29711 -47878;-29408 -46910 32;-28999 -46891 32;-28912 -47681;-29586 -48296; +-28838 -48289;-28857 -48106 1;-28877 -47632;-28886 -47331;-28975 -46836 1;-29018 -46600;-29075 -46464;-29222 -46275;-29590 -46223 1;-29545 -46359;-29556 -46522;-29646 -46706 1;-29742 -46903;-29950 -47029;-30201 -47052;-30207 -47266;-30196 -47630 1;-30182 -47967;-30495 -48215;-30832 -48215 1;-31300 -48215;-31496 -47804;-31687 -47377 1;-31844 -47025;-31565 -46707;-31242 -46496;-31259 -46432 1;-31302 -46310;-31305 -46185;-31259 -46069 1;-31193 -45901;-31045 -45820;-30853 -45788;-31114 -45569;-31268 -45546;-32565 -45523;-32742 -45624 1;-32926 -45642;-33110 -45691;-33233 -45796 1;-33347 -45892;-33409 -46036;-33375 -46245;-33469 -46347;-33150 -47486;-32805 -48709;-29332 -48801;-28838 -48289 18; +-30411 -46317; +-32714 -46296; +-34949 -47842;-34888 -47650;-34697 -46782 1;-34729 -46150;-35129 -45572;-35762 -45561 1;-36470 -45549;-36868 -45541;-37576 -45529 1;-37802 -45525;-37870 -45816;-37847 -46041;-37690 -47587;-36877 -48610;-35560 -48505;-34949 -47842 18; +-34533 -43063;-34536 -43631;-33410 -43637;-33412 -44087;-28895 -44107;-28892 -43532; +-34533 -43063;-34536 -43631;-33410 -43637;-33412 -44087;-28895 -44107;-28892 -43532;-31066 -43538;-31069 -43070;-34533 -43063 18; +-27617 -41261;-27617 -45858; +-28370 -41175;-27414 -41182; +-26989 -46285;-23577 -46297 32;-23573 -45265;-24219 -45262; +-26966 -46202;-26983 -46481;-27214 -46481;-27214 -46422;-26966 -46202 18; +27804 -44786;28588 -44817; +-38283 -35809 1;-38378 -34235;-38647 -33191;-37928 -31788 1;-37224 -30413;-36451 -29855;-35276 -28852 1;-33901 -27679;-33131 -27021;-31757 -25848 1;-31087 -25276;-30090 -25098;-29502 -25755;-25246 -30665; +-26429 -30922;-25016 -30930;-25251 -30669; +-26247 -32393;-23718 -32400; +-22379 -34995;-21611 -34995;-23831 -32330; +-22014 -36913;-20068 -36895; +-22154 -37393;-22149 -37873; +-21493 -39020;-20603 -39020;-22174 -45527;-23395 -46452; +-21241 -41621 1;-20873 -41710;-20681 -41873;-20770 -42241 1;-20846 -42557;-21143 -42604;-21459 -42528; +-21092 -42136; +-21241 -41621 1;-20873 -41710;-20681 -41873;-20770 -42241 1;-20846 -42557;-21143 -42604;-21459 -42528;-21241 -41621 18; +-21493 -39020;-20603 -39020;-22174 -45527;-23395 -46452;-23566 -45327;-24247 -45189;-24320 -44583;-22227 -40055;-21493 -39020 18; +-22174 -45527 1;-22401 -46340;-22653 -47240;-23497 -47237;-26454 -47226;-27239 -46409; +-26201 -46845; +-25189 -46854; +-24143 -46871; +-23087 -46714; +-22174 -45527 1;-22401 -46340;-22653 -47240;-23497 -47237;-26454 -47226;-27239 -46409;-23416 -46453;-22174 -45527 18; +-22969 -47062;-22389 -48283;-22389 -49505;-23437 -49517;-23546 -48456;-23546 -48395;-28776 -48393;-28855 -48157 1;-28876 -47653;-28883 -47348;-28975 -46836 1;-29018 -46600;-29075 -46464;-29222 -46275;-29453 -46153 1;-29653 -45861;-29883 -45650;-30258 -45650;-30258 -45650;-31086 -45650;-31153 -45548;-32565 -45523;-32631 -45561;-32742 -45624 1;-32926 -45642;-33110 -45691;-33233 -45796 1;-33347 -45892;-33409 -46036;-33375 -46245;-33466 -46344;-33454 -46399;-32812 -48693;-35108 -48666;-35082 -48533;-34957 -47968;-34929 -47800;-34869 -47567;-34697 -46782 1;-34729 -46150;-35129 -45572;-35762 -45561 1;-36470 -45549;-36868 -45541;-37576 -45529 1;-37802 -45525;-37870 -45816;-37847 -46041;-37498 -49478;-41832 -49455;-41832 -48579;-40475 -48604;-40450 -46988;-40191 -46951;-40203 -46248;-40055 -46258;-39185 -46289;-39761 -42295;-37933 -42226;-37946 -42497;-38025 -42525 1;-38269 -42635;-38446 -42790;-38426 -43058 1;-38409 -43296;-38159 -43463;-37920 -43450;-37724 -43052;-37687 -42226;-35664 -42226;-35590 -43089;-34516 -43052;-34534 -43274;-34536 -43631;-33410 -43637;-33412 -44087;-28895 -44107;-28892 -43532;-28928 -42521;-28151 -42521;-28126 -41325;-27682 -41300;-27707 -45865;-26436 -47234;-26078 -47227;-23497 -47237 1;-23334 -47238;-23193 -47204;-23070 -47145;-22969 -47062 18; +-37909 -40689;-38377 -33608;-38327 -32912 1;-38256 -32553;-38131 -32185;-37928 -31788 1;-37224 -30413;-36451 -29855;-35276 -28852 1;-33901 -27679;-33131 -27021;-31757 -25848 1;-31087 -25276;-30090 -25098;-29502 -25755;-25246 -30665;-25004 -30943;-26336 -30967;-26361 -32398;-23869 -32398;-21599 -34891;-22290 -34989;-22290 -38024;-24955 -45180;-23622 -45254;-23647 -46216;-27101 -46191;-27718 -45550;-27644 -41207;-28162 -41182;-28286 -41651;-37909 -40689 18; +-38379 -37086 1;-38862 -37173;-39235 -37599;-39146 -38081 1;-39069 -38496;-38713 -38763;-38291 -38744; +-38379 -37086 1;-38862 -37173;-39235 -37599;-39146 -38081 1;-39069 -38496;-38713 -38763;-38291 -38744;-38379 -37086 18; +-38686 -37483; +-38607 -38460; +-38836 -37266 1;-38933 -36434;-38926 -35955;-39023 -35123 1;-39250 -33180;-38992 -31791;-37701 -30321 1;-37220 -29773;-36955 -29455;-36405 -28976 1;-34731 -27516;-33755 -26699;-32079 -25241 1;-31334 -24593;-29988 -24320;-29189 -25139 1;-28349 -26104;-27896 -26661;-27072 -27640;-19706 -36392;-20156 -36771; +-25602 -29386 1;-25100 -28995;-24388 -29031;-23978 -29518 1;-23566 -30007;-23629 -30737;-24118 -31149; +-28601 -25821 1;-28403 -25623;-28085 -25772;-27903 -25985 1;-27735 -26182;-27698 -26480;-27895 -26648; +-28107 -26100; +-25121 -29468; +-24233 -29813; +-24183 -30702; +-40629 -36418;-40507 -34377 1;-40456 -33518;-40416 -33010;-40088 -32214 1;-39538 -30878;-39298 -29984;-38221 -29021 1;-36134 -27155;-34929 -26161;-32842 -24295 1;-31991 -23534;-31533 -22228;-32294 -21377 1;-35032 -18315;-36308 -16371;-39086 -13345; +-38342 -37086;-38432 -37097 1;-38583 -37133;-38722 -37201;-38836 -37294;-38858 -37074 1;-38942 -36357;-38959 -35876;-39048 -35110 1;-39275 -33167;-38992 -31791;-37701 -30321 1;-37220 -29773;-36955 -29455;-36405 -28976 1;-34731 -27516;-33755 -26699;-32079 -25241 1;-31334 -24593;-29988 -24320;-29189 -25139 1;-28349 -26104;-27896 -26661;-27072 -27640;-19706 -36392;-20156 -36771;-22316 -36851;-22316 -35050;-21946 -34995;-21611 -34995;-23831 -32330;-26412 -32336;-26313 -30930;-25104 -30954;-25638 -30213;-29502 -25755 1;-30090 -25098;-31087 -25276;-31757 -25848 1;-33131 -27021;-33901 -27679;-35276 -28852 1;-36451 -29855;-37224 -30413;-37928 -31788 1;-38647 -33191;-38378 -34235;-38283 -35809;-38342 -37086 18; +-36993 -16889;-36418 -16433 1;-35134 -18001;-33997 -19473;-32294 -21377 1;-31533 -22228;-31991 -23534;-32842 -24295 1;-34220 -25527;-35214 -26379;-36330 -27353;-36768 -27735 1;-37214 -28126;-37689 -28545;-38221 -29021 1;-39298 -29984;-39538 -30878;-40088 -32214 1;-40416 -33010;-40456 -33518;-40507 -34377;-40622 -36309;-40839 -36147 1;-41295 -34124;-40999 -32691;-40150 -30748;-39943 -30285;-39716 -29931 32;-40090 -28777 32;-39000 -28845 32;-35498 -25645;-36027 -25028;-34410 -23641;-33829 -24318 32;-33463 -24004 1;-33186 -23766;-32946 -23467;-32796 -23148 33;-32572 -22671;-32549 -22151;-32905 -21736;-36674 -17344;-36993 -16889 18; +-39770 -42346;-38000 -42272;-37836 -42009;-38084 -38700;-38379 -38744 1;-38762 -38725;-39074 -38467;-39146 -38081 1;-39202 -37779;-39076 -37499;-38856 -37310;-38850 -37150;-38858 -37074 1;-38942 -36357;-38959 -35901;-39048 -35135 1;-39275 -33192;-38992 -31791;-37701 -30321 1;-37220 -29773;-36955 -29455;-36405 -28976 1;-36151 -28755;-35914 -28549;-35688 -28352;-35442 -28140 1;-34297 -27150;-33408 -26397;-32079 -25241 1;-31467 -24709;-30449 -24429;-29662 -24801;-31971 -21954 1;-31739 -22744;-32167 -23691;-32842 -24295 1;-34929 -26161;-36134 -27155;-38221 -29021 1;-39298 -29984;-39538 -30878;-40088 -32214 1;-40416 -33010;-40456 -33518;-40507 -34377;-40629 -36418;-39770 -42346 18; +-38453 -17473;-39249 -15785;-38190 -15286; +-39555 -14221;-39340 -13475;-39242 -13486 1;-39176 -13490;-39132 -13437;-39104 -13363;-39006 -13432 1;-37972 -14562;-37147 -15543;-36372 -16490;-36924 -16987;-36993 -16889;-38427 -17478;-38539 -17290;-39249 -15785;-38190 -15286;-38322 -15083;-38546 -14771 1;-38845 -14441;-39195 -14385;-39526 -14240;-39555 -14221 18; +-41954 -16681; +-43176 -17256; +-40244 -17815; +-41378 -18129; +-48580 -19065; +-44878 -13185; +-44555 -8818; +-44433 -10161; +-43316 -10353; +-42461 -11033; +-45260 -17574; +-39394 -14941; +-45911 -12993; +-45126 -11963; +-43403 -12575; +-44694 -10692;-45471 -10951; +-44694 -10692;-45471 -10951; +-46246 -10292 1;-46692 -10241;-46881 -10163;-47248 -10421; +-46226 -10294 1;-46672 -10243;-46881 -10163;-47248 -10421; +-46726 -18633 1;-46535 -18376;-46332 -18132;-46023 -18213 1;-45873 -18252;-45827 -18586;-45916 -18713 1;-46086 -18956;-46220 -19123;-46469 -19285 1;-46616 -19381;-46685 -19233;-46825 -19126 1;-46988 -19001;-46974 -18891;-46862 -18719;-46726 -18633 18; +-53148 -19342; +-50163 -21183 1;-50397 -21251;-50641 -21246;-50722 -21328 1;-50848 -21454;-50740 -21701;-50805 -22072; +-51363 -22045 1;-51337 -21665;-51156 -21402;-51520 -21294 1;-51673 -21249;-51793 -21282;-51952 -21270; +-46873 -18878;-47079 -18993 1;-47880 -19821;-48796 -20443;-49921 -21094 1;-50006 -21143;-50081 -21169;-50149 -21177;-50280 -21212 1;-50474 -21252;-50654 -21260;-50722 -21328 1;-50848 -21454;-50740 -21701;-50805 -22072;-50608 -22129 1;-50209 -21962;-49815 -21806;-49266 -21471 1;-48225 -20835;-47278 -20148;-46489 -19385;-46873 -18878 18; +-54040 -19058 1;-53790 -20025;-53730 -21226;-52731 -21256;-51894 -21273 1;-51763 -21275;-51654 -21255;-51520 -21294 1;-51452 -21314;-51403 -21340;-51368 -21371;-50735 -21342 1;-50841 -21472;-50742 -21714;-50805 -22072;-50527 -22095 1;-50155 -21940;-49778 -21783;-49266 -21471 1;-48225 -20835;-47278 -20148;-46489 -19385;-46426 -19324 1;-46305 -19205;-46187 -19084;-46074 -18961 1;-45992 -18871;-46088 -19155;-46136 -19267 1;-46624 -20394;-47082 -20951;-47881 -21884;-48661 -22426;-50195 -23150 1;-50558 -23303;-50765 -23573;-50869 -23903;-52155 -23626;-53655 -23121 32;-54318 -20417 32;-54292 -20176;-54040 -19058 18; +-54964 -20123 1;-54378 -20475;-54137 -20770;-53464 -20890 1;-53108 -20954;-53012 -21513;-53237 -21797 1;-53697 -22378;-53853 -23078;-53923 -23816; +-55247 -14022;-53555 -14772;-53555 -16185;-52403 -18191;-49507 -18156;-47972 -16185;-48460 -9921;-46559 -8317;-44744 -8282;-44238 -8003;-44053 -8151 1;-43231 -9078;-42629 -9832;-41765 -11009 1;-41363 -11556;-41571 -12114;-41124 -12625 1;-40690 -13120;-40447 -13398;-40014 -13893 1;-39570 -14400;-39000 -14271;-38546 -14771;-38221 -15224;-39196 -15749;-38306 -17564;-48024 -22030;-47739 -21718 1;-47026 -20881;-46594 -20326;-46136 -19267 1;-46097 -19176;-46026 -18971;-46045 -18946;-45916 -18713 1;-45916 -18713;-45916 -18713;-45916 -18713 1;-45827 -18586;-45873 -18252;-46023 -18213 1;-46332 -18132;-46535 -18376;-46726 -18633;-46862 -18719 1;-46897 -18772;-46922 -18819;-46935 -18863;-47148 -19053 1;-47964 -19874;-48904 -20507;-50008 -21146 1;-50144 -21225;-50244 -21237;-50333 -21218;-50776 -21374;-51368 -21371 1;-51403 -21340;-51452 -21314;-51520 -21294 1;-51654 -21255;-51763 -21275;-51894 -21273;-52731 -21256 1;-53730 -21226;-53790 -20025;-54040 -19058;-54079 -18070;-55247 -14022 18; +-60253 -8930;-56576 -8757;-54109 -18084;-54306 -20354;-53591 -22994;-51864 -23562;-50827 -23784;-51039 -24823;-51208 -24889 1;-52007 -25253;-52827 -25831;-52542 -26646 1;-52332 -27245;-52112 -27665;-51530 -27919 1;-51113 -28101;-50852 -28143;-50449 -28355;-50454 -28326;-50298 -29511;-50507 -29552;-54229 -30270;-60253 -8930 18; +-43370 -71742;-44697 -64381 1;-44757 -64047;-44701 -63679;-44379 -63573 1;-43332 -63229;-42293 -63202;-42521 -62336 1;-42683 -61721;-42722 -61474;-42881 -60877 1;-42949 -60622;-44162 -60932;-44848 -60982 16; +-45779 -59088 1;-45545 -59803;-45089 -60357;-44050 -60251; +-42916 -60004;-34103 -59131 1;-33267 -59048;-26637 -58715;-26607 -58923 1;-26530 -59457;-26517 -59760;-26478 -60298 1;-26444 -60765;-34511 -61533;-38174 -62448 1;-41523 -63284;-40242 -62974;-42780 -63826 1;-43084 -63928;-43258 -63969;-43565 -64061 1;-43938 -64173;-43931 -64630;-43862 -65013;-42675 -71538; +-49851 -39984;-49193 -42560 16; +-47155 -50537;-45385 -57466 1;-45175 -58288;-44487 -58851;-43643 -58763 1;-39091 -58286;-36514 -58299;-31941 -58114 1;-26794 -57906;-23897 -57749;-18751 -57975 1;-14455 -58164;-12083 -58755;-7794 -59056 1;-7639 -59067;-7543 -59215;-7549 -59370 1;-7567 -59861;-7579 -60136;-7598 -60627 1;-7606 -60835;-7795 -60960;-8003 -60975 1;-15290 -61499;-19399 -61488;-26671 -62196 1;-31338 -62650;-33987 -62812;-38571 -63801 1;-39975 -64104;-40741 -64380;-42158 -64619 1;-42725 -64714;-43105 -65264;-42997 -65828;-41887 -71281 16; +-41050 -5068; +-43170 -4218;-41347 -3939;-41349 -4166;-41348 -4401 32;-40090 -4398;-40049 -4462;-40038 -5377;-41569 -6163;-41678 -6033 1;-42154 -5463;-42546 -4993;-42913 -4536;-43170 -4218 18; +-37385 -5713; +-38841 -7440; +-40321 -7514;-39088 -6613;-37903 -4898;-33647 -4849;-33684 -8217;-37496 -9216 1;-38170 -9393;-38743 -9240;-39211 -8723;-40321 -7514 18; +-40321 -7514;-39088 -6613;-37903 -4898;-33647 -4849;-33684 -8217;-37496 -9216 1;-38170 -9393;-38743 -9240;-39211 -8723;-40321 -7514 18; +-33191 -16173;-35659 -13212 1;-36094 -12690;-35777 -11716;-35116 -11559;-33494 -11174;-32982 -12546; +-33598 -12015 1;-33408 -12018;-33259 -12355;-33266 -12767 1;-33273 -13180;-33433 -13511;-33623 -13508 1;-33813 -13505;-33962 -13168;-33955 -12756 1;-33948 -12344;-33789 -12012;-33598 -12015 18; +-34844 -12607; +-33684 -14593; +-32920 -13915;-33046 -16218; +-33191 -16173;-35659 -13212 1;-36094 -12690;-35777 -11716;-35116 -11559;-33494 -11174;-32982 -12546;-33191 -16173 18; +-29588 -17825;-29640 -18610;-30565 -18837;-31743 -17511; +-31873 -17634;-33217 -16153; +-29478 -18734 1;-26536 -17861;-24849 -17465;-21823 -16953 1;-19826 -16615;-18684 -16421;-16659 -16465 1;-15068 -16500;-14176 -16507;-12585 -16516; +-12740 -16519;-12652 -9512; +-12469 -9488 32;-13591 -9473 32;-13584 -8934 32;-12462 -8949 32;-12469 -9488 50; +-12836 -9661;-13795 -9648;-13787 -9074 32;-16336 -9038; +-16317 -9038;-18864 -8962;-21388 -9048; +-21344 -9048;-23947 -9308; +-23895 -9317;-29091 -10171; +-29054 -10171;-32052 -10924 32;-32163 -12676; +-32277 -12837;-32080 -10937;-28995 -10123;-23814 -9210;-21075 -9037;-16214 -8963;-13771 -9062;-13771 -9580;-12661 -9728;-12735 -16464;-12958 -16514 1;-14325 -16506;-15197 -16497;-16659 -16465 1;-18684 -16421;-19826 -16615;-21823 -16953 1;-24849 -17465;-26536 -17861;-29478 -18734;-30574 -18858;-31842 -17663;-31967 -17530;-33207 -16164;-32854 -13875;-32277 -12837 18; +-29649 -21177 1;-30181 -21623;-30299 -22410;-29853 -22942 1;-29400 -23483;-28593 -23531;-28052 -23078; +-29014 -22795; +-28601 -25821 1;-28403 -25623;-28085 -25772;-27903 -25985 1;-27735 -26182;-27698 -26480;-27895 -26648;-28601 -25821 18; +-25602 -29386 1;-25100 -28995;-24388 -29031;-23978 -29518 1;-23566 -30007;-23629 -30737;-24118 -31149;-25602 -29386 18; +-29649 -21177 1;-30181 -21623;-30299 -22410;-29853 -22942 1;-29400 -23483;-28593 -23531;-28052 -23078;-29649 -21177 18; +-26755 -21108; +-28642 -20664; +-28630 -19850; +-25780 -19924; +-25669 -21996; +-25743 -22971; +-19161 -17649;-24677 -18628 32;-24754 -20460 32;-24795 -21427 32;-24814 -21871; +-24881 -23263;-24939 -24638 32;-25419 -25001;-24411 -26195 1;-24163 -26521;-23890 -26600;-23491 -26682 33;-23241 -26734;-23030 -26741;-22826 -26707 33;-22611 -26672;-22403 -26590;-22165 -26464 1;-21253 -25983;-20688 -25792;-19733 -25402 32;-19653 -23507 16; +-25633 -25013;-29060 -20846 1;-29403 -20430;-29324 -19601;-28800 -19473;-24822 -18496; +-25646 -24962;-29048 -20845 1;-29391 -20429;-29324 -19601;-28800 -19473;-24822 -18496;-25095 -24571;-25646 -24962 18; +-19251 -17640;-12799 -17590;-13045 -24212;-13628 -24213; +-9097 -17121;-11639 -17097;-11817 -24135; +-5812 -17182;184 -17305; +-5812 -17182;-9097 -17121; +-11698 -15599;-11706 -16123;20187 -16612;20199 -16116; +20200 -16058;20181 -16611;-11706 -16123;-11698 -15599;20200 -16058 18; +-11452 -9948;-11499 -12750; +-5411 -9302;-8446 -9191; +-11452 -9948 1;-11444 -9476;-11053 -9089;-10581 -9108;-8446 -9191; +-5411 -9302;-2273 -9414; +-2265 -8794;-2329 -9412;-5411 -9302;-10927 -9149;-11468 -9620;-11659 -15273;-11700 -15763;-11706 -16123;19300 -16598;19274 -17649;-5812 -17182;-11712 -17088;-11820 -24414;-13021 -24415;-12799 -17590;-18968 -17638;-24727 -18519;-25116 -18568;-28800 -19473 1;-29251 -19583;-29354 -20203;-29155 -20639;-30624 -19025;-29449 -18725 1;-26525 -17858;-24839 -17463;-21823 -16953 1;-19826 -16615;-18684 -16421;-16659 -16465 1;-15114 -16499;-14229 -16507;-12722 -16515;-12496 -9464;-12464 -9132;-12462 -8949 32;-13584 -8934;-19737 -8889;-29071 -10093;-32055 -10896;-32264 -12658;-32927 -12588;-33039 -12392;-33494 -11174;-33590 -10725 1;-30417 -9918;-28143 -9253;-24465 -8831 1;-22262 -8578;-20690 -8429;-18849 -8389;-17713 -8378 1;-17289 -8379;-16844 -8384;-16370 -8395 1;-10867 -8520;-7830 -8655;-2532 -8800;-2265 -8794 18; +-33460 -11197;-33546 -10728;-33832 -10786 1;-34367 -10921;-34928 -11060;-35527 -11203 1;-36449 -11422;-36664 -12869;-36033 -13576 1;-33982 -15875;-32934 -17291;-30981 -19599;-30939 -19649 1;-30910 -19683;-30880 -19717;-30851 -19752 1;-28581 -22424;-27223 -23976;-25078 -26519;-24436 -26060;-25361 -24975;-25734 -24898;-29069 -20862 1;-29188 -20718;-29225 -20498;-29246 -20303;-30500 -18913;-31783 -17655;-31985 -17511;-33207 -16164;-33404 -15917;-35659 -13212 1;-36094 -12690;-35777 -11716;-35116 -11559;-33858 -11260;-33460 -11197 18; +-31834 -5407;-31818 -7837; +-24246 -5308;-24238 -6517 1;-27156 -6838;-28788 -7124;-31648 -7787; +-21482 -5333;-21475 -6345 1;-18092 -6010;-16102 -6231;-12703 -6195;-12728 -5270; +-21482 -5333;-21475 -6345 1;-18092 -6010;-16102 -6231;-12703 -6195;-12728 -5270;-21482 -5333 18; +-24246 -5308;-24238 -6517 1;-27156 -6838;-28788 -7124;-31648 -7787;-31772 -5393;-24246 -5308 18; +-7394 -4905;-7383 -6484;-8857 -6406;-8869 -4926;-7394 -4905 18; +-6687 -6523;-6698 -4909;-2560 -4881;-2547 -6767;-6687 -6523 18; +-3374 -5800; +-5829 -5824; +-8572 -6142; +-8674 -4922;-8667 -5923; +-7394 -4905;-7383 -6484;-8857 -6406;-8869 -4926;-7394 -4905 18; +-6687 -6523;-6698 -4909;-2560 -4881;-2547 -6767;-6687 -6523 18; +-3579 -4319;-5745 -4334; +-3535 -4315 32;-5763 -4330 32;-5765 -4035 32;-3537 -4020 32;-3535 -4315 50; +-634 -7372;-615 -6837;-1994 -6794;-2007 -4892;-390 -4881;-443 -4270;-3541 -4291 32;-3644 -4316;-5755 -4330;-5930 -4294;-8847 -4315 32;-8849 -4025 32;-12771 -4052 32;-12763 -5250 32;-12727 -5303;-12703 -6195;-8857 -6400;-8869 -4926;-7394 -4905;-7383 -6484;-6687 -6523;-6698 -4909;-2560 -4881;-2547 -6767;-2562 -7288;-634 -7372 18; +-2537 -6782;-2525 -7300;-2877 -7284 1;-2971 -7280;-3068 -7276;-3169 -7272 1;-7742 -7097;-10303 -6928;-14876 -6766 1;-17512 -6673;-19309 -6652;-21489 -6788;-21475 -6345 1;-18092 -6010;-16102 -6231;-12703 -6195;-8880 -6346;-8837 -6416;-7371 -6469;-7310 -6442;-6647 -6529;-2537 -6782 18; +-24249 -5326;-21467 -5296;-21460 -6344;-21473 -6782;-21640 -6798 1;-22394 -6847;-23196 -6916;-24096 -7006 1;-26235 -7221;-27431 -7427;-29530 -7891 1;-32598 -8569;-34300 -9027;-37342 -9811 1;-38684 -10157;-39636 -9352;-40500 -8269 1;-41770 -6677;-42679 -5656;-43737 -4310;-43203 -4171;-41555 -6156;-40038 -5377;-40050 -4390;-40056 1678;-39380 1678;-37585 1674;-37542 -4367;-34924 -4340;-34890 283;-32787 301;-32717 -4009;-33665 -4858;-37905 -4884;-38886 -6321;-39088 -6613;-40321 -7514;-39211 -8723 1;-38743 -9240;-38170 -9393;-37496 -9216;-33684 -8217;-31964 -7805;-31633 -7797;-31531 -7760 1;-28743 -7116;-27116 -6834;-24238 -6517;-24241 -5998;-24249 -5326 18; +-33657 -4822;-32767 -3983;-31982 -4036;-31982 -7857;-33683 -8250;-33657 -4822 18; +-40845 -11492 1;-40225 -11480;-39757 -11539;-39340 -11998 1;-39051 -12315;-39043 -12630;-39056 -13059 1;-39061 -13245;-39102 -13537;-39279 -13479 1;-39749 -13325;-39959 -13079;-40278 -12701 1;-40287 -12691;-41321 -11501;-40845 -11492 18; +-45831 -5264;-43764 -4252;-43402 -4733 1;-42480 -5882;-41634 -6848;-40500 -8269 1;-39636 -9352;-38684 -10157;-37342 -9811 1;-34300 -9027;-32598 -8569;-29530 -7891 1;-28792 -7728;-28166 -7597;-27572 -7486;-26357 -7281 1;-25676 -7179;-24964 -7093;-24096 -7006 1;-20512 -6646;-18476 -6639;-14876 -6766 1;-10303 -6928;-7742 -7097;-3169 -7272 1;-2767 -7287;-2428 -7305;-2107 -7322;-2429 -8798;-2532 -8800 1;-7830 -8655;-10867 -8520;-16370 -8395 1;-16844 -8384;-17289 -8379;-17713 -8378;-18849 -8389 1;-20690 -8429;-22262 -8578;-24465 -8831 1;-28143 -9253;-30417 -9918;-33590 -10725;-34529 -10960 1;-34851 -11040;-35183 -11121;-35527 -11203 1;-36449 -11422;-36664 -12869;-36033 -13576 1;-33982 -15875;-32934 -17291;-30981 -19599;-30939 -19649 1;-30910 -19683;-30880 -19717;-30851 -19752 1;-30422 -20257;-30026 -20721;-29651 -21160;-29787 -21310 1;-30198 -21768;-30257 -22460;-29853 -22942 1;-29400 -23483;-28593 -23531;-28052 -23078;-27897 -23208 1;-27013 -24240;-26142 -25257;-25078 -26519;-26858 -27894;-27072 -27640 1;-27381 -27273;-27637 -26966;-27882 -26674;-27810 -26544 1;-27716 -26374;-27766 -26146;-27903 -25985 1;-28085 -25772;-28403 -25623;-28601 -25821;-28713 -25691 1;-28859 -25519;-29016 -25338;-29189 -25139 1;-29432 -24890;-29725 -24742;-30036 -24675;-31916 -22235 1;-31948 -21926;-32067 -21631;-32294 -21377 1;-33405 -20134;-34276 -19075;-35104 -18054;-35785 -17212 1;-35994 -16953;-36204 -16694;-36418 -16433;-36643 -16159 1;-37341 -15314;-38092 -14431;-39006 -13432;-39104 -13363;-39065 -13188 1;-39060 -13143;-39057 -13099;-39056 -13059 1;-39043 -12630;-39051 -12315;-39340 -11998 1;-39757 -11539;-40225 -11480;-40845 -11492 1;-40854 -11492;-40863 -11493;-40872 -11494;-40978 -11361 1;-42542 -9307;-43540 -8185;-45167 -6119 1;-45376 -5854;-45561 -5623;-45733 -5404;-45831 -5264 18; +-14697 -35400;-14739 -36702 1;-15267 -36706;-15707 -36662;-16044 -36257 1;-17935 -33985;-19009 -32723;-20895 -30447 1;-21220 -30055;-21132 -29631;-20974 -29147 1;-20791 -28586;-20427 -28340;-19892 -28091 1;-18650 -27513;-17891 -27316;-16560 -26992 1;-15739 -26792;-15255 -26722;-14410 -26748;-14448 -27934 16; +-16199 -26890 1;-15597 -27868;-15424 -28580;-15434 -29728 1;-15448 -31429;-15445 -32384;-15521 -34083 1;-15558 -34908;-15588 -35490;-16138 -36106;-16248 -36012 1;-17931 -33992;-18975 -32761;-20640 -30754;-18888 -27662 1;-18087 -27355;-17828 -27305;-16918 -27080;-16199 -26890 18; +-18889 -27653;-18925 -27727;-20640 -30754;-20785 -30580 1;-20821 -30536;-20858 -30492;-20895 -30447 1;-21220 -30055;-21132 -29631;-20974 -29147 1;-20791 -28586;-20427 -28340;-19892 -28091 1;-19714 -28008;-19546 -27933;-19385 -27865;-18889 -27653 18; +-14442 -27943;-14416 -26774;-14600 -26744 1;-15222 -26735;-15655 -26787;-16236 -26916;-16156 -26961 1;-15589 -27903;-15424 -28608;-15434 -29728 1;-15448 -31429;-15445 -32384;-15521 -34083 1;-15558 -34902;-15588 -35482;-16126 -36093;-16106 -36183 1;-16085 -36208;-16065 -36232;-16044 -36257 1;-15707 -36662;-15267 -36706;-14739 -36702;-14697 -35400;-14442 -27943 18; +-18010 -34661;-18441 -35030;-18624 -34819 1;-19715 -33559;-20327 -32854;-21411 -31592 1;-21767 -31182;-21718 -30722;-21535 -30214;-21483 -30126 16; +-21147 -31376; +-20062 -32647; +-18779 -34164; +-14948 -37158;-14985 -37676 1;-15532 -37676;-15924 -37718;-16354 -37380;-17465 -36214;-16983 -35844; +-15988 -37249; +-16982 -35846;-17063 -35905;-17465 -36214;-16354 -37380 1;-15924 -37718;-15532 -37676;-14985 -37676;-14948 -37158;-15062 -37167 1;-15548 -37168;-15927 -37049;-16262 -36663 1;-16469 -36425;-16667 -36197;-16856 -35978;-16856 -35978;-16982 -35846 18; +-21465 -30142;-21464 -30207 1;-21405 -30467;-21295 -30715;-21127 -30934 1;-19872 -32416;-18992 -33475;-17982 -34663;-18400 -34995;-18441 -35030;-18624 -34819 1;-19715 -33559;-20327 -32854;-21411 -31592 1;-21756 -31195;-21721 -30750;-21552 -30261;-21465 -30142 18; +-21465 -30142;-21464 -30207 1;-21405 -30467;-21295 -30715;-21127 -30934 1;-19872 -32416;-18992 -33475;-17982 -34663;-18400 -34995;-18441 -35030;-18624 -34819 1;-19715 -33559;-20327 -32854;-21411 -31592 1;-21756 -31195;-21721 -30750;-21552 -30261;-21465 -30142 18; +-16982 -35846;-17063 -35905;-17465 -36214;-16354 -37380 1;-15924 -37718;-15532 -37676;-14985 -37676;-14948 -37158;-15062 -37167 1;-15548 -37168;-15927 -37049;-16262 -36663 1;-16469 -36425;-16667 -36197;-16856 -35978;-16856 -35978;-16982 -35846 18; +-17585 -38895; +-16610 -39068; +-11308 -39203; +-13059 -53996;-13096 -55797;-7050 -56587 1;-6571 -56650;-6022 -56415;-6001 -55933; +-5998 -55952;-5970 -55423 32;-5812 -52381;-5302 -51735 32;-5744 -51712;-5692 -50710 32;-5192 -50736;-5145 -49817;-4890 -49830 32;-4829 -48618 32;-6186 -47680 32;-11528 -47421 32;-12367 -47964;-12910 -48729 32;-13059 -53996; +-13132 -55797;-12811 -48728;-12693 -48423;-12367 -47964;-11528 -47421 32;-6186 -47680 32;-4829 -48618 32;-4890 -49830 32;-5145 -49817;-5192 -50736;-5692 -50710 32;-5744 -51712;-5302 -51735 32;-5812 -52381;-5970 -55423 32;-5993 -55849;-6094 -56229 1;-6274 -56508;-6683 -56635;-7050 -56587;-12159 -55919;-13132 -55797 18; +-12978 -46766;-12176 -47708;-12354 -47956;-12367 -47964;-12910 -48729 32;-13059 -53996;-13098 -55057;-13132 -55797;-12159 -55919;-7050 -56587 1;-6683 -56635;-6274 -56508;-6094 -56229;-5993 -55849;-5970 -55423 32;-5812 -52381;-5302 -51735 32;-5744 -51712;-5692 -50710 32;-5192 -50736;-5145 -49817;-4890 -49830 32;-4832 -48675;-3591 -49001;-3692 -52975 32;-4652 -53830 32;-4844 -57930 32;-5019 -57971;-5093 -57963;-5640 -57930 1;-6432 -58156;-7335 -57987;-7975 -57468;-8516 -57398;-8805 -57361;-13228 -56791;-14016 -56720;-13936 -55747;-13812 -49813;-14417 -49826;-14442 -48654;-12978 -46766 18; +-20099 -37046;-20596 -39018;-21486 -39027;-21469 -37849;-22009 -37858;-22001 -37046;-20099 -37046 18; +-20102 -37047;-18997 -39977;-20424 -46501;-19930 -47537;-16574 -48573;-14280 -48623;-14304 -49758;-16426 -49782;-20991 -48376;-22546 -48302;-23060 -47148;-22764 -46908 1;-22467 -46574;-22315 -46033;-22174 -45527;-22149 -45425;-21443 -42501;-21359 -42548 1;-21082 -42591;-20838 -42523;-20770 -42241 1;-20681 -41873;-20873 -41710;-21241 -41621;-21213 -41546;-20603 -39020;-20102 -37047 18; +-9345 -40125 32;-11441 -40101 32;-11656 -46411 32;-9058 -46501 32;-9039 -45951 32;-9547 -45933 32;-9345 -40125 50; +-11185 -40673; +-11235 -42014; +-11285 -43355; +-11335 -44697; +-11385 -46038; +-10374 -46186; +-9345 -40125 32;-11435 -40108 32;-11656 -46411 32;-9058 -46501 32;-9039 -45951 32;-9547 -45933 32;-10857 -45821;-10671 -40367;-9356 -40437;-9345 -40125 50; +-9537 -45182 32;-9205 -45194 32;-9028 -40150 32;-9360 -40138 32;-9537 -45182 50; +-7735 -39680;-6225 -39733 1;-5520 -39758;-5057 -39855;-4450 -40182;-4241 -39824 1;-4888 -39464;-5328 -39344;-6077 -39306;-7717 -39244;-7735 -39680 18; +-3110 -41146 32;-2684 -41559 32;-2362 -41227 32;-2788 -40814 32;-3110 -41146 50; +-1810 -39805 32;-1384 -40218 32;-1062 -39886 32;-1488 -39473 32;-1810 -39805 50; +723 -42418 32;1149 -42831 32;1471 -42499 32;1045 -42086 32;723 -42418 50; +-697 -43882 32;-271 -44295 32;51 -43963 32;-375 -43550 32;-697 -43882 50; +609 -45154;914 -44823 1;1716 -45432;2207 -45841;3193 -46049;3091 -46532 1;2171 -46395;1403 -45898;609 -45154 18; +4232 -46744 32;4852 -46779 32;4883 -46231 32;4263 -46196 32;4232 -46744 50; +1884 -43579;2180 -43259 1;2769 -43706;3019 -43977;3598 -44136;3505 -44540 1;2788 -44392;2467 -44125;1884 -43579 18; +4372 -44659 32;5795 -44739 32;5818 -44337 32;4395 -44257 32;4372 -44659 50; +8137 -44437 32;12271 -44574 32;12257 -45030 32;8123 -44893 32;8137 -44437 50; +11072 -47067 32;10566 -47058 32;10576 -46526 32;11082 -46535 32;11072 -47067 50; +12715 -47051;13975 -47077 1;13975 -46765;13668 -46571;13356 -46571 1;13051 -46571;12719 -46720;12715 -47051 18; +14924 -44716 1;14915 -45210;15222 -45485;15716 -45485 1;15952 -45485;16265 -45382;16289 -45147 1;18102 -45205;18948 -45268;20163 -45865;20345 -45493;20208 -45426 1;18946 -44809;18146 -44826;16716 -44771;14924 -44716 18; +15626 -45022; +16798 -45022; +18229 -45072; +19537 -45380; +13316 -46855; +10838 -46824; +11728 -44776; +10445 -44739; +9038 -44677; +4563 -46499; +4689 -44444; +5517 -44523; +2705 -46185; +1335 -45481; +2772 -43993; +-337 -43918; +1095 -42439; +-1459 -39823; +-2755 -41168; +-5950 -39515; +-4618 -39873; +-9051 -37503 1;-9063 -37893;-8813 -38277;-8423 -38280 1;-8092 -38282;-7756 -38207;-7673 -37887;-7551 -37878;-7315 -37878 1;-6526 -37878;-6023 -37866;-5326 -38236;-5157 -37917;-5248 -37869 1;-5978 -37484;-6498 -37455;-7324 -37468;-9051 -37503 18; +-8323 -37815; +-6744 -37710; +-5697 -37885; +-18238 -39474 1;-18222 -38930;-18245 -38458;-17794 -38154;-14993 -39190;-15080 -39535;-18238 -39474 18; +-697 -43882 32;-271 -44295 32;51 -43963 32;-375 -43550 32;-697 -43882 50; +4372 -44659 32;5795 -44739 32;5818 -44337 32;4395 -44257 32;4372 -44659 50; +-1810 -39805 32;-1384 -40218 32;-1062 -39886 32;-1488 -39473 32;-1810 -39805 50; +723 -42418 32;1149 -42831 32;1471 -42499 32;1045 -42086 32;723 -42418 50; +-3110 -41146 32;-2684 -41559 32;-2362 -41227 32;-2788 -40814 32;-3110 -41146 50; +4232 -46744 32;4852 -46779 32;4883 -46231 32;4263 -46196 32;4232 -46744 50; +14924 -44716 1;14915 -45210;15222 -45485;15716 -45485 1;15952 -45485;16265 -45382;16289 -45147 1;18102 -45205;18948 -45268;20163 -45865;20345 -45493;20208 -45426 1;18946 -44809;18146 -44826;16716 -44771;14924 -44716 18; +-7735 -39680;-6225 -39733 1;-5520 -39758;-5071 -39868;-4446 -40191;-4254 -39807 1;-4906 -39451;-5328 -39344;-6077 -39306;-7717 -39244;-7735 -39680 18; +16792 -46797 1;18472 -46868;19672 -47119;20802 -48364;20457 -48677;20419 -48635 1;19414 -47524;18253 -47369;16760 -47246;16792 -46797 18; +-11850 -39657 1;-11858 -39194;-11825 -38701;-11363 -38677 1;-11057 -38661;-10768 -38877;-10752 -39183;-9506 -39189;-9531 -39645;-11850 -39657 18; +-9051 -37503 1;-9063 -37893;-8813 -38277;-8423 -38280 1;-8092 -38282;-7756 -38207;-7673 -37887;-7551 -37878;-7315 -37878 1;-6526 -37878;-6023 -37866;-5326 -38236;-5157 -37917;-5248 -37869 1;-5978 -37484;-6498 -37455;-7324 -37468;-9051 -37503 18; +1884 -43579;2180 -43259 1;2769 -43706;3019 -43977;3598 -44136;3505 -44540 1;2788 -44392;2467 -44125;1884 -43579 18; +8137 -44437 32;12271 -44574 32;12257 -45030 32;8123 -44893 32;8137 -44437 50; +11072 -47067 32;10566 -47058 32;10576 -46526 32;11082 -46535 32;11072 -47067 50; +609 -45154;914 -44823 1;1716 -45432;2207 -45841;3193 -46049;3091 -46532 1;1962 -46299;1403 -45898;609 -45154 18; +12715 -47051;13975 -47077 1;13975 -46765;13668 -46571;13356 -46571 1;13051 -46571;12719 -46720;12715 -47051 18; +4759 -47500;11233 -47712; +10907 -53989;10984 -52169; +9631 -52097;10975 -52159;10901 -54034;9569 -53997;9631 -52097 18; +4774 -47399;4235 -47382;4174 -49233;4734 -49251;4743 -48969; +4669 -50594;4700 -49624;4170 -49607;4160 -49926;3626 -51377;3608 -51957;4638 -51975; +3630 -51985 32;3600 -52919 32;4389 -52945 32;4419 -52011 32;3630 -51985 50; +4467 -48724; +4669 -50594;4700 -49624;4170 -49607;4160 -49926;3626 -51377;3608 -51957;4638 -51975;4669 -50594 18; +4774 -47399;4235 -47382;4174 -49233;4734 -49251;4743 -48969;4774 -47399 18; +3726 -52926;3625 -56043 32;2420 -56004 32;2460 -54775 32;2503 -53432 32;1413 -53397; +12873 -47766;16624 -47801;17636 -57292;17636 -57816;14897 -58217;10727 -58479;3591 -58828;3556 -58147;-945 -58199;-4801 -57868;-4696 -53803;-3650 -52895;-3527 -48935;84 -45463;1550 -46632;3225 -47225;3190 -49546;1341 -49494;1166 -53367;2510 -53489;2440 -55966;3626 -56036;3801 -52878;4430 -52008;4665 -51965;7094 -52001;9589 -54060;10775 -54112;10706 -56607;12066 -56659;12206 -51059;12817 -50431;12873 -47766 18; +4406 -52151;4905 -52167; +3348 -49741;2756 -51178;2669 -53263; +2071 -49642;3335 -49698;3322 -49803;2756 -51178;2669 -53263;1417 -53238;1485 -51382;1966 -51400;2071 -49642 18; +11237 -47596;12693 -47596;12864 -47841;12809 -50455 32;12205 -51034 32;12085 -56721 32;10700 -56692 32;10753 -54152 32;10910 -53928;10984 -52169;10961 -51920;10996 -50844 32;4719 -50638 32;4667 -50560;4719 -49636;4170 -49610;4167 -49688;4160 -49926;3626 -51377;3608 -51957;3719 -53147;3625 -56043 32;2420 -56004 32;2460 -54775 32;2499 -53550;2672 -53186;2756 -51178;3348 -49741;3381 -47139;4220 -47398;4231 -47509;4174 -49233;4734 -49251;4742 -49000;4937 -48980;11115 -49182 32;11157 -47883 32;11237 -47596 18; +-6377 -45627;-6412 -46622; +-8349 -45973 32;-8368 -46525 32;-6563 -46588 32;-6544 -46035 32;-8349 -45973 50; +-8348 -45949 32;-8368 -46525 32;-6563 -46588 32;-6543 -46011 32;-8348 -45949 50; +-6218 -46599 1;-5340 -46837;-4839 -47062;-3930 -47037 1;-3070 -47013;-2581 -46694;-2079 -46221 1;-1637 -45805;-1154 -45309;-532 -44668;-2585 -42454; +-2796 -43376;-2766 -42520;-3869 -41396 1;-4582 -40670;-5314 -40457;-6330 -40404; +-6303 -40341;-7653 -40293; +-6297 -40384;-6062 -40422 1;-5182 -40500;-4518 -40735;-3869 -41396;-2766 -42520;-2796 -43376;-3226 -43387;-4652 -43337 32;-4633 -42794 32;-6377 -42733 32;-6315 -40965;-6297 -40384 18; +-11664 -46416;-12627 -46391;-12929 -46872;-12232 -47779;-11534 -47443;-6229 -47640;-4749 -48825;-3651 -49133;-3602 -48837;-24 -45259;-532 -44668 1;-1154 -45309;-1637 -45805;-2079 -46221 1;-2581 -46694;-3070 -47013;-3930 -47037 1;-4839 -47062;-5340 -46837;-6218 -46599;-6625 -46586;-8359 -46524;-9093 -46506;-11664 -46416 18; +-2666 -42507;-2513 -42513;-532 -44668 1;-1154 -45309;-1637 -45805;-2079 -46221 1;-2581 -46694;-3070 -47013;-3930 -47037 1;-4839 -47062;-5340 -46837;-6218 -46599;-6243 -45603;-4479 -45616;-2888 -43420;-2666 -42507 18; +-12037 -40098;-11438 -40086;-11671 -46463;-12541 -46432;-12325 -46012;-12251 -42897;-12726 -42829;-12702 -42070;-12251 -41984;-12037 -40098 18; +-9028 -40140;-7609 -40171;-7814 -45964;-7910 -45988;-8349 -45973 32;-8368 -46525 32;-9087 -46522;-9057 -46470;-9039 -45951 32;-9547 -45933 32;-9521 -45193;-9427 -45186;-9205 -45194 32;-9046 -40673;-9028 -40140 18; +-18441 -35030;-17465 -36214; +-14916 -37163 1;-15484 -37193;-15889 -37092;-16262 -36663 1;-16520 -36367;-16766 -36082;-16996 -35815 16; +-17979 -34668 1;-18982 -33487;-19879 -32407;-21127 -30934 1;-21942 -29869;-21404 -28123;-20152 -27640 1;-17613 -26661;-16056 -26212;-13336 -26295 1;-6784 -26495;-3118 -26594;3432 -26837 1;11347 -27130;15748 -27446;23657 -27862 1;30907 -28244;35002 -28391;42249 -28825 1;44137 -28939;45694 -28984;47011 -29197 16; +-14985 -37676;-8935 -37934; +-5157 -37917 1;-3783 -38532;-2890 -38767;-1792 -39796; +-1384 -40218;748 -42424; +1458 -42492;2180 -43259; +3590 -44139 1;3852 -44213;4140 -44232;4412 -44248; +5705 -44311;8137 -44437; +12271 -44574;14924 -44716; +16762 -47222;13974 -47064; +12715 -47045;11067 -47063; +10566 -47058;4721 -46770; +4208 -46743 1;3783 -46722;3439 -46656;3032 -46532; +607 -45137;-263 -44284; +-693 -43878;-2701 -41539; +-3103 -41137 1;-3572 -40690;-3887 -40481;-4464 -40187; +-7735 -39672;-9540 -39637; +-11835 -39637;-15106 -39533; +-12248 -36590;-6598 -36751; +-13395 -35431;-13429 -36727;-12238 -36758; +-6623 -36909;-5536 -36926 1;-5094 -37511;-4616 -37790;-3889 -37887 1;-3174 -37982;-2381 -37936;-2132 -37055 16; +-4309 -37368; +-2988 -37429; +-5539 -36914 1;-5097 -37499;-4616 -37790;-3889 -37887 1;-3174 -37982;-2385 -37967;-2136 -37086;-5539 -36914 18; +-5306 -35649;-5337 -36796 32;-2075 -36885 32;-384 -36930 32;738 -36960 32;696 -38508; +891 -38465;882 -38753;625 -38478;891 -38465 18; +7251 -42671;7194 -44024; +590 -38486;-385 -39381;818 -40692;1329 -40714;1306 -41256 32;2323 -41299 32;2281 -42277 32;4143 -42356 32;4106 -43227 32;5823 -43300 32;5852 -42618;8289 -42721 32;8262 -43365 32;12178 -43530 32;12452 -43216 32;12463 -42944 32;12213 -42661 32;12316 -40234 32;8367 -40068 32;8311 -41403 32;5910 -41302;5947 -40442 32;4105 -40364 32;4142 -39489 32;1404 -39373;590 -38486 18; +5855 -43316;5828 -43968;5030 -43940 1;3858 -43904;3096 -43562;2291 -42710;-635 -39611;-385 -39381 16; +13338 -42355;13264 -44234; +8171 -43867;13084 -44029; +8225 -43394;8212 -43699; +17906 -43303;17857 -44401;17584 -44397;13451 -44204;12702 -44016;8227 -43869;8213 -43677;8225 -43394;8282 -42899;8289 -42721 32;5852 -42618;5850 -43433;5828 -43968;5030 -43940 1;3858 -43904;3096 -43562;2291 -42710;-635 -39611;-385 -39381;740 -38461;9875 -38907;18092 -39178;17906 -43303 18; +21734 -47136 1;21176 -46564;20699 -46153;20133 -45851 16; +20652 -45168;20359 -45508;20094 -45371 1;18897 -44810;18102 -44824;16716 -44771;14924 -44716;14545 -44695;12598 -44592;12271 -44574;11843 -44560;8139 -44437;7904 -44425;5945 -44323;5818 -44342;5308 -44308;4395 -44257 32;4260 -44238 1;4115 -44229;3967 -44215;3824 -44191;3595 -44147;3468 -44097 1;2978 -43934;2723 -43671;2180 -43259;2021 -43090;1458 -42492;1413 -42443;1045 -42086 32;741 -42400;472 -42138;-1243 -40364;-1407 -40195;-1140 -39967;-1062 -39886 32;-619 -39628;2291 -42710 1;2438 -42865;2583 -43003;2729 -43126;2788 -43174 1;3428 -43690;4100 -43911;5030 -43940;5828 -43968;5855 -43316;5851 -43147;5852 -42618;8289 -42721 32;8282 -42899;8243 -43236;8216 -43605;8212 -43699;8244 -43869;13066 -44028;13488 -44206;17811 -44408 1;18043 -44409;18254 -44420;18453 -44441;18394 -44435 1;19159 -44506;19730 -44717;20496 -45082;20652 -45168 18; +-1049 -39908;-586 -39655;-601 -39579;-385 -39381;-292 -39296;590 -38486;705 -38182;738 -36960;-2126 -36883;-2165 -37178 1;-2442 -37968;-3200 -37979;-3889 -37887 1;-4616 -37790;-5094 -37511;-5536 -36926;-5645 -36924;-5872 -36921;-6623 -36909;-6999 -36899;-11984 -36765;-12476 -36752;-13429 -36727;-13395 -35445;-13566 -35407;-14671 -35372;-14703 -35591;-14739 -36702 1;-14791 -36702;-14842 -36702;-14892 -36701;-14951 -37194;-14985 -37676;-14840 -37676;-14710 -37688;-8965 -37933;-9000 -37827 1;-9036 -37726;-9054 -37615;-9051 -37503;-7324 -37468 1;-6498 -37455;-5978 -37484;-5248 -37869;-5157 -37917;-4807 -38070 1;-3628 -38578;-2797 -38856;-1798 -39790;-1714 -39706;-1488 -39473 32;-1104 -39845;-1049 -39908 18; +-14856 -36713;-14917 -37169;-15040 -37165;-15062 -37167 1;-15548 -37168;-15927 -37049;-16262 -36663 1;-16469 -36425;-16667 -36197;-16856 -35978;-16856 -35978;-16982 -35846;-17063 -35905;-17465 -36214;-17595 -36056;-18441 -35030;-18368 -34969;-17982 -34663 1;-18136 -34482;-18287 -34304;-18436 -34127;-18536 -34009 1;-19333 -33066;-20106 -32140;-21127 -30934 1;-21295 -30715;-21405 -30467;-21464 -30207;-21465 -30142;-21507 -29928 1;-21554 -29374;-21387 -28795;-21054 -28343;-20822 -28810 1;-20882 -28909;-20932 -29020;-20974 -29147 1;-21132 -29631;-21220 -30055;-20895 -30447 1;-19009 -32723;-17935 -33985;-16044 -36257 1;-15769 -36587;-15426 -36677;-15022 -36697;-14856 -36713 18; +40966 -29419;39815 -29350;39756 -30336; +38413 -30279;38455 -29302 32;32934 -29006 32;32883 -29949; +31530 -28877;26976 -28659; +31879 -30064;31284 -30039;31326 -29045; +26688 -29706;27142 -29725;27178 -28858; +25073 -29636;23841 -29596 32;24344 -28442 32;22643 -28347 32;20940 -28251 32;16928 -28063; +31403 -26255;40520 -26762 32;40545 -26244; +16415 -25363;28331 -26070; +28331 -26070;31428 -26256; +17689 -41740;18800 -28218;19144 -28244;22701 -28402 32;24368 -28476 32;23859 -29603 32;25083 -29654;27015 -29720;27142 -29725;27178 -28858;27212 -28670;31324 -28885;31318 -29231;31284 -30039;31879 -30064;32829 -30106;32898 -29676;32934 -29006 32;38455 -29302 32;38413 -30279;39714 -30340;39768 -30128;39815 -29350;40606 -29397;41333 -29440 1;43384 -29555;44397 -29548;46564 -29811 1;47306 -29901;48124 -30779;48033 -31521;47402 -35838;45990 -35788;40585 -37592;32612 -38552;32488 -42204;17689 -41740 18; +32590 -26296;32956 -19875;34352 -17328;34840 -17280;43698 -17174;44211 -17168;46009 -17146 32;47009 -19049 32;45405 -18950;45186 -20624;45140 -21429;48067 -21595 1;48313 -22210;48544 -22582;48729 -23190;48854 -23649 1;49078 -24542;49113 -25200;48888 -26170 1;48735 -26830;48129 -27185;47453 -27144;43597 -26910;43190 -27075;40691 -26962;32590 -26296 18; +16928 -28063;13681 -27918; +11797 -28708;11845 -27757;6135 -27523; +3659 -27432;-1897 -27222;-1994 -29579; +13272 -28911;13831 -28938;13871 -28115; +6135 -27523;3633 -27423; +-1994 -29579;-2083 -32451; +9144 -35645;9084 -37284 32;834 -36972; +-2367 -35210;-2387 -35642 1;-2398 -35882;-2188 -36065;-1948 -36074;8952 -36476; +-3512 -36529;8902 -36955; +9038 -36475;8865 -36473;-1948 -36074 1;-2188 -36065;-2398 -35882;-2387 -35642;-2367 -35210;-2062 -31772;-1994 -29579;-1981 -29261;-1897 -27222;3659 -27432;9256 -27651;11845 -27757;11797 -28708;13476 -28921;13831 -28938;13871 -28115;14013 -27933;16928 -28063;17293 -28079;18877 -28150;18062 -39291;720 -38628;754 -36988;9007 -37215;9038 -36475 18; +-5150 -30499;-5055 -26948; +-5055 -26957;-1904 -27036;-2184 -32427;-2356 -35178;-2376 -35409;-2387 -35642 1;-2398 -35882;-2188 -36065;-1948 -36074;8952 -36476;8957 -37214;-5292 -36720;-5267 -35647;-4138 -35688 32;-4000 -30531 32;-5155 -30501;-5055 -26957 18; +-13284 -27985;-13249 -26876;-6753 -27083; +-13306 -26880;-13395 -35431;-13429 -36727;-12500 -36751;-6623 -36909;-5709 -36923;-5499 -36895;-5246 -26968;-6868 -26898;-13306 -26880 18; +40694 -26924;43177 -27046; +25813 -29527;25862 -28345;24550 -28291;24006 -29490;25813 -29527 18; +16625 -25196;16713 -23137; +8268 -23737;8208 -24929;15460 -25296;15492 -24493; +15666 -23063 32;16516 -23096 32;16497 -23584 32;15647 -23551 32;15666 -23063 50; +15716 -23555;16496 -23585; +1521 -23913 32;6983 -24126 32;6948 -25037 32;1486 -24824 32;1521 -23913 50; +1521 -23913 32;6983 -24126 32;6948 -25037 32;1486 -24824 32;1521 -23913 50; +-4545 -23725;-4573 -24447;-901 -24589; +-937 -24743;66 -24782;116 -23477; +-12019 -24246;-6985 -24369; +-5938 -23255;-5989 -24559;-7028 -24532; +-13619 -24380 1;-14832 -24387;-15513 -24566;-16705 -24790 1;-17842 -25004;-18505 -25145;-19574 -25507; +-24679 -18615;-23641 -18353;-19083 -17656;-18677 -17636;-12799 -17590;-13021 -23630;-13033 -23900;-13045 -24212;-13486 -24283;-13653 -24380 1;-14845 -24390;-15524 -24568;-16705 -24790 1;-17842 -25004;-18505 -25145;-19574 -25507;-19934 -25483 1;-20771 -25821;-21319 -26018;-22165 -26464 1;-22403 -26590;-22611 -26672;-22826 -26707 33;-23030 -26741;-23241 -26734;-23491 -26682 33;-23890 -26600;-24163 -26521;-24411 -26195;-25419 -25001;-24939 -24638 32;-24903 -23775;-24679 -18615 18; +-4660 -24591;77 -24765;112 -23457;-4625 -23291;-4660 -24591 18; +-24627 -26116;-25099 -26470;-24997 -26591;-24961 -26630 1;-24936 -26660;-24910 -26690;-24885 -26720 1;-24148 -27589;-22980 -27466;-21919 -27027;-21397 -26783 1;-21317 -26742;-21238 -26699;-21161 -26655 1;-19909 -25945;-19105 -25700;-17699 -25390 1;-15676 -24944;-14501 -24737;-12430 -24779 1;-6466 -24899;-3124 -25036;2837 -25251 1;6418 -25380;8822 -25466;11809 -25615;12364 -25643 1;13207 -25687;14101 -25735;15084 -25791 1;19813 -26061;22464 -26223;27192 -26506 1;28535 -26586;29774 -26661;30945 -26731;31366 -26757 1;36602 -27072;40544 -27315;46629 -27693 1;46957 -27713;47140 -27725;47468 -27745 1;48378 -27802;49320 -27302;49501 -26594 1;49551 -26398;49593 -26209;49627 -26026;48952 -25865 1;48934 -25964;48912 -26065;48888 -26170 1;48735 -26830;48129 -27185;47453 -27144;43597 -26910;43190 -27075;40691 -26962;39040 -26826;16487 -25516;16532 -23591;15656 -23560;15539 -25435;8103 -25046;8164 -23701;6992 -23652;6944 -25025;1473 -24813;1535 -23549;116 -23487;61 -24770;-4739 -24595;-4653 -23293;-5917 -23250;-5985 -24570;-12008 -24378;-12925 -24379;-13616 -24372;-13970 -24387 1;-14974 -24423;-15633 -24589;-16705 -24790 1;-17842 -25004;-18505 -25145;-19574 -25507;-20346 -25648 1;-20970 -25897;-21465 -26095;-22165 -26464 1;-22403 -26590;-22611 -26672;-22826 -26707 33;-23030 -26741;-23241 -26734;-23491 -26682 33;-23839 -26610;-24092 -26541;-24315 -26308;-24627 -26116 18; +-21114 -28419;-21995 -27058;-21743 -26945;-21397 -26783 1;-21317 -26742;-21238 -26699;-21161 -26655 1;-19909 -25945;-19105 -25700;-17699 -25390 1;-15748 -24960;-14585 -24752;-12647 -24775;-12286 -24782 1;-8776 -24853;-6172 -24931;-3383 -25027;-1831 -25082 1;-411 -25132;1102 -25188;2837 -25251 1;7621 -25423;10304 -25519;15084 -25791 1;19813 -26061;22464 -26223;27192 -26506 1;34783 -26960;39039 -27222;46629 -27693 1;46957 -27713;47140 -27725;47468 -27745 1;47480 -27746;47492 -27746;47503 -27747;47189 -29199;47031 -29190;46440 -29116 1;45249 -28970;43871 -28923;42249 -28825 1;35002 -28391;30907 -28244;23657 -27862 1;15748 -27446;11347 -27130;3432 -26837 1;-3118 -26594;-6784 -26495;-13336 -26295 1;-16056 -26212;-17613 -26661;-20152 -27640 1;-20437 -27750;-20685 -27926;-20889 -28144;-21114 -28419 18; +-20843 -28855;-21096 -28380;-20960 -28230;-20889 -28144 1;-20685 -27926;-20437 -27750;-20152 -27640 1;-17613 -26661;-16056 -26212;-13336 -26295 1;-6784 -26495;-3118 -26594;3432 -26837 1;11347 -27130;15748 -27446;23657 -27862 1;30907 -28244;35002 -28391;42249 -28825 1;43871 -28923;45249 -28970;46440 -29116;47031 -29190;47109 -29194;46974 -29828;39725 -29357;39655 -30343;38425 -30299;38415 -29362;32888 -28992;32814 -30053;31284 -29979;31358 -28869;27213 -28647;27139 -29634;25786 -29569;25815 -29474;25862 -28345;24550 -28291;13895 -27898;13821 -28860;11798 -28737;11823 -27824;-2034 -27044;-5135 -26973;-6808 -26902;-13310 -26933;-13397 -27933;-14483 -27933;-14442 -27748;-14410 -26748 1;-15255 -26722;-15739 -26792;-16560 -26992 1;-17891 -27316;-18650 -27513;-19892 -28091 1;-20211 -28239;-20469 -28387;-20664 -28600;-20843 -28855 18; +-6756 -26916;-5235 -26978; +16111 -17576;184 -17305; +16111 -17576;19464 -17649; +19322 -17713;18844 -25412;16661 -25326;16685 -23093;15661 -23019;15464 -25276;8144 -24943;8218 -23561;-5893 -23171;-5944 -23419;-5989 -24559;-7028 -24532;-7588 -24354;-11791 -24252;-11810 -23721;-11712 -17088;-9749 -17119;19322 -17713 18; +18324 85141; +18594 86902;18799 86863;18821 86740;18913 85374;18728 85250;18783 84698;17871 84133;17800 84154;18594 86902 18; +17784 84152;17863 84126;18063 82202;17933 82198 1;17638 82212;17312 82407;17382 82681 1;17488 83097;17563 83366;17664 83720;17784 84152 18; +16683 73193; +18027 77177;18254 76950;18254 76662;18547 76662;18547 77446;19337 77854;19143 79264;19807 79596;19516 82389;18069 82238;17871 84133;18783 84698;18728 85250;18913 85374;18814 86838;19251 87126;19238 87468;22254 89592;22406 87338;22180 87151;22462 86817;24369 88209;24535 85693;24097 85391;23803 85215;24075 81810;24544 81412;24829 81698;25317 81307;25063 80971;25543 80564;27566 80724;27870 81084;28283 81575;29954 80965;30419 75813;29690 75333;30120 71093;29810 71346;29291 71764;28899 71302;29117 71128;28777 70709;29038 68031;28480 67577;28562 66826;24829 66449;24752 67206;25234 67256;25055 69582;23412 70816;23013 70770;21935 71688;17442 71190;16846 76567;18027 77177 18; +18560 77494;18569 76629;18252 76629;18252 76941;17998 77203;18560 77494 18; +16853 76479;16403 77197; +18563 77489;18307 77985;18134 77896 16; +17651 77646;16973 77287 16; +18134 77896;17651 77646 16; +16755 77327;16128 78056;15312 78362; +16529 77624;16738 77354; +16681 76380;16838 76130;16825 76495;16681 76380 18; +16918 78291; +17254 80581; +17557 77793;17366 78904;16941 78922;16996 80156;17625 80144;17607 81050;16916 81075 1;16481 80026;16176 79446;15491 78540; +9833 73908;11448 73460;11648 74263;9957 74783;9833 73908 18; +13396 72790;12463 74400;12827 75871 1;13882 77033;14527 77394;15238 78199; +16945 78961 1;16731 78916;16589 78873;16384 78949 1;16204 79016;16099 79067;15964 79202;16022 79294 1;16372 79839;16612 80343;16916 81075;17607 81050;17625 80144;16996 80156;16955 79228;16945 78961 18; +15958 79220;15991 79176 1;16113 79060;16216 79012;16384 78949 1;16589 78873;16731 78916;16945 78961;16942 78950;16941 78922;17366 78904;17557 77793;16945 77481;16871 77512;15477 78548;15755 78875;15958 79220 18; +19160 82357;19465 79428;19795 79606;19511 82388;19160 82357 18; +16557 77244;16893 77475;16931 77442;18222 78059;18493 77466;16933 76652;16557 77244 18; +16051 77854 1;15786 77476;15595 77211;15626 76750 1;15646 76453;15985 76317;15891 76034 1;15802 75768;15721 75619;15533 75411 1;15481 75353;15616 75298;15694 75294 1;16042 75278;16399 75135;16428 74788 1;16454 74472;16090 74489;15814 74333 1;15588 74205;15507 73894;15521 73635 1;15526 73539;15522 73484;15527 73388 1;15535 73233;15742 73227;15897 73240 1;16393 73281;16695 73324;17187 73400;16862 76349;16542 77251;16051 77854 18; +17960 71223;17953 71128 1;17932 70943;17845 70816;17791 70607 1;17679 70178;17232 70078;16788 70074 1;16716 70073;16646 70077;16580 70085;16493 70111 1;16240 70326;16102 70646;15937 70952 1;15843 71125;15990 71334;16177 71396 1;16623 71545;16901 71518;17368 71568;17417 71204;17960 71223 18; +17880 70853 1;17847 70781;17816 70703;17791 70607 1;17679 70178;17232 70078;16788 70074 1;16716 70073;16646 70077;16580 70085;16493 70111 1;16240 70326;16102 70646;15937 70952 1;15843 71125;15990 71334;16177 71396 1;16462 71491;16679 71515;16920 71532; +13777 74503; +13672 73578; +15228 78164;16048 77885;16066 77824;15974 77745 1;15750 77421;15598 77165;15626 76750 1;15646 76453;15985 76317;15891 76034 1;15802 75768;15721 75619;15533 75411 1;15481 75353;15616 75298;15694 75294 1;16042 75278;16399 75135;16428 74788 1;16454 74472;16090 74489;15814 74333 1;15588 74205;15507 73894;15521 73635 1;15526 73539;15522 73484;15527 73388 1;15535 73233;15742 73227;15897 73240 1;16393 73281;16695 73324;17187 73400;17409 71586;18334 71324;17636 68890;15804 68489;12480 74421;12485 74488;12827 75871 1;13702 76834;14294 77247;14877 77819;15228 78164 18; +15756 73237 1;15639 73243;15533 73278;15527 73388 1;15522 73484;15526 73539;15521 73635 1;15507 73894;15588 74205;15814 74333 1;16090 74489;16454 74472;16428 74788 1;16399 75135;16042 75278;15694 75294 1;15616 75298;15481 75353;15533 75411 1;15721 75619;15802 75768;15891 76034 1;15924 76133;15904 76214;15863 76290;15779 76418 1;15709 76515;15635 76616;15626 76750 1;15604 77074;15692 77301;15838 77538; +18112 87067;18605 86980;18532 86728;17749 84015 1;17599 83496;17516 83204;17382 82681 1;17304 82374;17721 82167;18036 82201;19172 82321;19459 79460;19307 79346;19143 79264;19337 77854;18727 77539;18125 78047;17549 77742;17545 77863;17366 78904;16941 78922;16996 80156;17625 80144;17607 81050;16916 81075 1;16499 80069;16201 79495;15574 78651;15467 78569;15249 78183;14467 77433;14304 77294 1;13885 76925;13421 76525;12827 75871;12485 74488;12480 74421;13251 73046;13336 72872;12497 73226;11450 73462;11467 73580 1;11609 74200;11738 74557;11990 75192 1;12427 76294;13019 76819;13903 77609 1;14527 78167;14979 78637;15361 79181;15744 79794 1;15899 80072;16047 80377;16198 80724 1;16620 81696;16768 82279;17055 83300;17802 85959;18112 87067 18; +9976 74799;9856 73929; +15169 83808;15108 83574 1;15053 83396;14996 83215;14937 83031 1;14523 81520;14092 80415;12996 79307 1;12572 78878;12107 78613;11605 78499;11278 78457;11273 78448 1;10859 78407;10421 78463;9961 78609 1;9707 78689;9461 78766;9221 78840;8833 78967 1;6117 79830;4672 80298;1528 81109 1;788 81300;67 81371;-654 81380;-865 81371 1;-1759 81408;-2585 81300;-3612 81227;-4610 81171 1;-9027 80952;-11972 80704;-15838 80387;-16289 80345;-16067 78210;-14771 78297;-14302 77878;-11554 78060;-5967 78436;-4750 78510;-3064 78621 1;-376 78799;1780 78179;4798 77539;5043 77731;5258 77690;5327 77446;6968 77101;7173 77317;7966 77096;8235 77011;9245 76774;9369 76725 1;9569 76490;9723 76263;9829 76027 1;9974 75706;10030 75367;9994 74970;9981 74776;11556 74291;11642 74264;11835 74794 1;11881 74916;11932 75047;11990 75191 1;12427 76294;13019 76819;13903 77609 1;15030 78616;15596 79338;16198 80724 1;16595 81638;16749 82208;17005 83121;17088 83406;15169 83808 18; +3350 72787;3089 71985; +6744 67628 1;5478 69525;4678 70806;3529 72493 1;3161 73033;2622 73572;2405 74532 16; +6438 66993;1705 67411;1616 66068 1;328 65890;-583 65876;-1479 64420 16; +294 67806; +-520 68818; +750 67880 1;759 68265;764 68482;773 68867 1;778 69090;910 69326;1133 69311 1;1316 69299;1422 69111;1404 68929 1;1337 68261;1301 67887;1235 67219 1;1222 67085;1082 67024;948 67029 1;813 67034;747 67178;750 67313 1;755 67534;745 67659;750 67880 18; +-497 65827;-2322 65565 32;-2381 65974 32;-555 66234 48; +-2331 65765;-526 66026; +-936 66347;1238 66585; +-18672 64224;-1511 66683 32;-1637 67560; +-930 66459;-1318 66533;-3067 66276;-2431 65984;-895 66215;-930 66459 18; +-916 65746 1;-1091 65505;-1268 65364;-1566 65345 1;-2075 65313;-2359 65545;-2865 65479 1;-3331 65418;-3593 65384;-4059 65323 1;-4241 65299;-4348 65354;-4519 65423 1;-4841 65553;-4957 65599;-5279 65729; +-9068 65289;-5262 65835; +-4163 65727; +-3125 65849; +-5330 65981;-5299 65737 1;-5004 65618;-4826 65547;-4519 65423 1;-4348 65354;-4241 65299;-4059 65323 1;-3593 65384;-3331 65418;-2865 65479 1;-2359 65545;-2075 65313;-1566 65345 1;-1268 65364;-1091 65505;-916 65746;-1068 65745;-2322 65565 32;-2381 65974 32;-2502 66017;-3067 66276;-5330 65981 18; +-5330 65981;-5299 65737 1;-5004 65618;-4826 65547;-4519 65423 1;-4348 65354;-4241 65299;-4059 65323 1;-3593 65384;-3331 65418;-2865 65479 1;-2359 65545;-2075 65313;-1566 65345 1;-1268 65364;-1091 65505;-916 65746;-1068 65745;-2322 65565 32;-2381 65974 32;-2502 66017;-3067 66276;-5330 65981 18; +1701 67411;1239 67170;1267 66445;-587 66264; +750 67880 1;759 68265;764 68482;773 68867 1;778 69090;910 69326;1133 69311 1;1316 69299;1422 69111;1404 68929 1;1337 68261;1301 67887;1235 67219 1;1222 67085;1082 67024;948 67029 33;813 67034;747 67178;750 67313 1;755 67534;745 67659;750 67880 18; +2827 57777;2495 57736;2024 64652 1;1382 64562;1022 64512;381 64420 1;218 64397;105 64242;132 64080 1;155 63946;270 63868;405 63883;1068 63961;1207 62570;430 62492 1;319 62481;240 62363;251 62252 1;263 62135;373 61988;490 62000;1247 62078;1391 60507;561 60431 1;441 60420;356 60279;367 60159 1;379 60029;493 59934;623 59946;1436 60021;1623 57997;755 57917 1;661 57908;597 57751;606 57657 1;616 57545;721 57417;833 57428;1669 57510;1828 55899;825 55757;944 54838;7559 55851;7398 56776;5979 56517 16; +2017 58896; +1860 61164; +1703 63476; +632 64179; +1138 57714; +1608 58120;2453 58200; +2501 57805;1681 57417; +1380 55379; +2651 55564; +4082 55749; +5611 55984; +7067 56206; +7178 54577; +6499 56592;6316 55658; +854 55759;1797 55896;1790 56283;1669 57510;833 57428 1;721 57417;616 57545;606 57657 1;597 57751;661 57908;755 57917;1623 57997;1436 60021;623 59946 1;493 59934;379 60029;367 60159 1;356 60279;441 60420;561 60431;1391 60507;1247 62078;490 62000 1;373 61988;263 62135;251 62252 1;240 62363;319 62481;430 62492;1207 62570;1068 63961;405 63883 1;270 63868;155 63946;132 64080 1;105 64242;218 64397;381 64420 1;1022 64512;1382 64562;2024 64652;2495 57736;2827 57777;2981 57589;3143 56295 32;5876 56637;5983 56525;6080 56537;7410 56793;7420 56794;7429 56595;7559 55851;944 54838;878 55504;854 55759 18; +3142 56306;2988 56083;1790 55944;1790 56283;1669 57510;833 57428 1;721 57417;616 57545;606 57657 1;597 57751;661 57908;755 57917;1623 57997;1436 60021;623 59946 1;493 59934;379 60029;367 60159 1;356 60279;441 60420;561 60431;1391 60507;1247 62078;490 62000 1;373 61988;263 62135;251 62252 1;240 62363;319 62481;430 62492;1207 62570;1068 63961;405 63883 1;270 63868;155 63946;132 64080 1;105 64242;218 64397;381 64420 1;1022 64512;1382 64562;2024 64652;2495 57736;2827 57777;2981 57589;3142 56306 18; +3786 58376; +4902 58184;5518 58261; +6735 58715;2491 58154; +2475 58136;6763 58733;6841 58358;6200 58197;5773 58162;2488 57743;2475 58136 18; +-3615 63435;-2473 63409; +-3306 61156;-2337 61134; +-2978 58780;-2096 58760; +-2664 56503;-1844 56484; +-2129 64513; +-1839 59648; +-2358 59384;-2386 59838;-2256 59855;-1493 59903 1;-1364 59911;-1253 59813;-1245 59684 1;-1237 59553;-1337 59439;-1468 59431;-2230 59387;-2358 59384 18; +-2511 64116 1;-2705 64300;-2774 64572;-2636 64802 1;-2541 64960;-2447 65049;-2263 65049;-2125 64975 1;-1929 64879;-1748 64733;-1544 64566 1;-1493 64524;-1495 64472;-1491 64406 1;-1483 64277;-1581 64165;-1710 64157;-2333 64117;-2511 64116 18; +-3678 64959;-3547 64981;-2791 65081 1;-2617 65104;-2466 65092;-2326 65052;-2413 65023 1;-2506 64986;-2571 64910;-2636 64802 1;-2773 64573;-2706 64303;-2514 64119;-2631 64092;-2629 63979;-2511 62164;-1637 62216 1;-1508 62224;-1397 62126;-1389 61997 1;-1381 61866;-1481 61752;-1612 61744;-2482 61692;-2365 59848;-2339 59389;-2320 59129;-2317 59077;-2110 57553;-1209 57608 1;-1080 57616;-969 57518;-961 57389 1;-953 57258;-1053 57144;-1184 57136;-2054 57083;-1899 55888;-2427 55820;-2530 56608;-3589 64220;-3678 64959 18; +-3508 63641;-3692 64962;-2791 65081 1;-2269 65150;-1951 64900;-1544 64566 1;-1493 64524;-1495 64472;-1491 64406 1;-1483 64277;-1581 64165;-1710 64157;-2637 64098;-2604 63597 16; +-3192 61366;-3450 63225 16; +-2578 63197;-2511 62164;-1637 62216 1;-1508 62224;-1397 62126;-1389 61997 1;-1381 61866;-1481 61752;-1612 61744;-2482 61692;-2460 61351 16; +-2862 58991;-3133 60942 16; +-2434 60930;-2365 59848;-1493 59903 1;-1364 59911;-1253 59813;-1245 59684 1;-1237 59553;-1337 59439;-1468 59431;-2336 59381;-2317 59077;-2304 58980 16; +-2544 56708;-2801 58554 16; +-2246 58553;-2110 57553;-1209 57608 1;-1080 57616;-969 57518;-961 57389 1;-953 57258;-1053 57144;-1184 57136;-2054 57083;-2006 56714 16; +-1949 56276;-1860 55585;-1837 55406;-76 55636;46 54715;-2222 54391;-2484 56277 16; +-1318 54585 1;-1550 54653;-1683 54897;-1615 55129 1;-1547 55362;-1303 55495;-1071 55427 1;-838 55358;-705 55115;-773 54882 1;-842 54650;-1085 54517;-1318 54585 18; +-1183 55443;-1183 54585; +-1632 55011;-748 55033; +-395 55166; +-1982 54952; +-1949 56276;-1860 55585;-1837 55410;-76 55640;46 54715;-2222 54391;-2484 56277;-1949 56276 18; +8671 66894;8622 66197; +10327 66715;10275 66018; +-2351 73726;-2177 73766;3648 74755;4795 74969;5381 75064;5649 73393;5778 73414;7729 73541;7675 73228 1;7236 71006;6848 68987;6705 67411;6429 66994;1705 67420;1223 67161;1240 66694;-963 66443;-1333 66542;-2351 73726 18; +7718 73514;8761 73780;8696 73525 1;8543 72569;8409 71989;8243 70987;8152 71012;8150 71012 32;8075 70427 32;8140 70419;8133 70362 1;7861 69293;7808 68650;7679 67549;7620 67540;7595 67542 32;7551 66973 32;8677 66885 32;8680 66920;8619 66181;8446 66208;7497 66282 32;7453 65713 32;7575 65703;7548 65610;7516 64956 1;7508 64736;7502 64521;7495 64258;7500 64152;7437 64121;7418 64121 32;7413 63529 32;7505 63528;7502 63393 1;7503 62778;7578 62391;7619 61781;7634 61675;7648 61676;7554 61668;7544 61667 32;7591 61131 32;7701 61141;7705 61053 1;7765 60256;7899 59787;8037 59012;8054 58893;8073 58896;7974 58880;7947 58876;8015 58374;7989 58356;7045 58288;6974 58486;6835 58907 1;6548 60792;6520 62069;6469 64095;6513 66106;6579 67132;6713 67502 1;6861 69047;7236 71004;7661 73154;7718 73514 18; +6536 65965;6511 64029;6530 61703;6795 59359;6745 58711;2468 58142;2436 58603;2024 64652 1;1382 64562;1022 64512;381 64420 1;218 64397;105 64242;132 64080 1;155 63946;270 63868;405 63883;1068 63961;1207 62570;430 62492 1;319 62481;240 62363;251 62252 1;263 62135;373 61988;490 62000;1247 62078;1391 60507;561 60431 1;441 60420;356 60279;367 60159 1;379 60029;493 59934;623 59946;1436 60021;1623 57997;755 57917 1;661 57908;597 57751;606 57657 1;616 57545;721 57417;833 57428;1669 57510;1828 55899;825 55757;943 54849;42 54747;-76 55640;-1837 55410;-1860 55585;-1931 56132;-1911 56234;-1978 56739;-2018 56805;-2054 57083;-1184 57136 1;-1053 57144;-953 57258;-961 57389 1;-969 57518;-1080 57616;-1209 57608;-2110 57553;-2215 58324;-2211 58518;-2281 59015;-2318 59092;-2336 59381;-1468 59431 1;-1337 59439;-1237 59553;-1245 59684 1;-1253 59813;-1364 59911;-1493 59903;-2365 59848;-2424 60771;-2404 60884;-2432 61373;-2467 61461;-2482 61692;-1612 61744 1;-1481 61752;-1381 61866;-1389 61997 1;-1397 62126;-1508 62224;-1637 62216;-2511 62164;-2566 63007;-2545 63160;-2579 63662;-2611 63701;-2637 64098;-1710 64157 1;-1581 64165;-1483 64277;-1491 64406 1;-1492 64423;-1493 64438;-1494 64453;-1430 64498 1;-550 65877;351 65893;1616 66068;1705 67420;6438 66993;6536 65965 18; +-12714 57130;-12136 57205;-11975 55963;-11448 56031;-11262 56273;-11848 60755;-12141 60980;-12651 60913;-12498 59747;-13035 59677; +-12767 57589 1;-12367 57659;-12069 57863;-11729 57641;-11412 57423;-11262 56273;-11448 56031;-11975 55963;-12136 57205;-12714 57130;-12767 57589 18; +-12305 58060; +-12418 58941; +-12497 59743 1;-12509 59534;-12496 59302;-12697 59246 1;-12801 59217;-12862 59208;-12968 59185;-13008 59267;-13057 59646;-12497 59743 18; +-12532 60031;-11712 59604;-11710 59701;-11848 60755;-12141 60980;-12651 60913;-12562 60237;-12532 60031 18; +-10761 60759; +-10650 59723; +-10513 58378; +-10415 57569; +-10292 56776; +-9588 60130 1;-9492 60145;-9394 60091;-9379 59995 1;-9198 58835;-9123 58181;-8947 57021 1;-8913 56794;-9063 56685;-9292 56676 1;-9518 56667;-9712 56834;-9737 57058 1;-9771 57362;-9812 57520;-9835 57824 1;-9842 57921;-9792 57978;-9712 58033 1;-9552 58144;-9429 58197;-9453 58391 1;-9525 58970;-9540 59298;-9650 59871 1;-9670 59973;-9691 60114;-9588 60130 18; +-9553 60132 33;-9470 60146;-9392 60079;-9379 59995 1;-9198 58835;-9123 58181;-8947 57021 1;-8913 56794;-9063 56685;-9292 56676 1;-9518 56667;-9712 56834;-9737 57058 1;-9771 57362;-9812 57520;-9835 57824 1;-9842 57921;-9792 57978;-9712 58033 1;-9552 58144;-9429 58197;-9453 58391 1;-9525 58970;-9540 59298;-9650 59871 1;-9671 59980;-9690 60109;-9553 60132 50; +-12714 57130;-12136 57205;-11975 55963;-11448 56031;-11262 56273;-11848 60755;-12141 60980;-12651 60913;-12498 59747;-13035 59677;-12714 57130 18; +-3692 64949;-3766 65361 1;-3462 65401;-3224 65432;-2865 65479 1;-2359 65545;-2075 65313;-1566 65345 1;-1268 65364;-1091 65505;-916 65746;-1115 65738;-2322 65565 32;-2381 65974 32;-621 66225;-524 66270;1267 66445;1239 67170;1697 67419;1697 67299;1616 66068 1;328 65890;-583 65876;-1479 64420;-1508 64519 1;-1515 64536;-1526 64551;-1544 64566 1;-1748 64733;-1929 64879;-2125 64975;-2263 65049;-2403 65071 1;-2521 65095;-2648 65100;-2791 65081;-3515 64985;-3692 64949 18; +-20115 74817;-10920 76090;-10850 75172;-9593 66412 32;-1593 67560;-1615 67404;-1511 66683 32;-18672 64224;-20115 74817 18; +-4419 58213;-3841 58288;-3680 57046;-3309 57093;-3126 57336;-3712 61818;-3966 62047;-4356 61996;-4203 60830;-4740 60760; +-4198 59509; +-3997 61437; +-4207 60827;-3609 60905;-3610 61036;-3712 61818;-3966 62047;-4356 61996;-4207 60827 18; +-4760 60754;-3591 60928;-3447 59777;-3518 59762 1;-3676 59750;-3857 59800;-3936 59928 1;-4103 60199;-4339 60154;-4643 60094;-4760 60754 18; +-3839 58311;-3264 58386;-3245 58246;-3126 57336;-3309 57093;-3680 57046;-3839 58311 18; +-4393 58207;-3263 58377;-3403 59494;-3477 59475 1;-3669 59443;-3773 59347;-3832 59125 1;-3895 58886;-4215 58873;-4460 58846;-4393 58207 18; +-4637 60148;-3495 60283;-3346 58935;-4485 58791;-4637 60148 18; +-4978 62475;-3940 62610;-4233 64862;-5362 65310;-8991 64795;-9252 64376;-9470 64341; +-5833 64943; +-5371 65314;-6463 65159;-6444 65094 1;-6393 64811;-6195 64552;-5911 64586 1;-5562 64628;-5323 64706;-5013 64542 1;-4570 64308;-4350 64071;-4092 63653;-4236 64851;-5371 65314 18; +-7367 63603;-7497 65010;-6454 65158;-6442 65085 1;-6389 64806;-6192 64552;-5911 64586 1;-5562 64628;-5323 64706;-5013 64542 1;-4570 64308;-4350 64071;-4092 63653;-3950 62616;-4974 62487;-5153 63850;-6812 63646;-7096 63807;-7367 63603 18; +-10690 63767;-10382 63810; +-9415 65019;-9365 64694; +-12052 64639;-11995 64271; +-11958 63919;-11887 63422;-10980 63551; +-12704 65028;-12445 63218;-12184 62940;-11994 61613;-13278 61429; +-12432 61995; +-12458 63211 1;-12719 62844;-12981 62261;-12860 61860 1;-12806 61680;-12753 61700;-12639 61551;-12575 61530;-11994 61613;-12180 62937;-12458 63211 18; +-8877 63188;-9248 64378;-9488 64335;-9436 63947;-11023 63702;-11416 63667;-11374 63319;-11341 63086;-11797 63021;-11639 61911;-11302 61959;-11201 61969 1;-10715 62069;-10554 62370;-10119 62694 1;-9716 62993;-9347 63111;-8947 63176;-8877 63188 18; +-7486 65006;-7360 63611;-7852 63506;-7852 63310;-7965 63290 1;-8304 63248;-8604 63228;-8887 63186;-8896 63248;-9248 64378;-9233 64407;-8991 64795;-7704 64978;-7486 65006 18; +-7486 65006;-7360 63611;-7852 63506;-7852 63310;-7965 63290 1;-8304 63248;-8604 63228;-8887 63186;-8896 63248;-9248 64378;-9233 64407;-8991 64795;-7704 64978;-7486 65006 18; +-7665 62271 32;-8232 62197 32;-7983 60293 32;-7416 60367 32;-7665 62271 50; +-7167 58241 32;-7702 58171 32;-7462 56339 32;-6927 56409 32;-7167 58241 50; +-6775 54855 32;-7261 54792 32;-7463 56354 32;-6977 56417 32;-6775 54855 50; +-7216 58228 32;-7700 58165 32;-7977 60299 32;-7493 60362 32;-7216 58228 50; +-7717 62263 32;-8230 62196 32;-8367 63238 32;-7854 63305 32;-7717 62263 50; +-8372 63240;-7276 54809;-6780 54871; +-11284 61917;-11310 62091;-10054 62972;-8981 63295;-8902 63212;-8905 63183;-8877 63188;-8849 63192 1;-8698 63213;-8541 63228;-8377 63245;-7264 54810;-7338 54409;-9356 54082;-9781 54372;-10441 55853;-11284 61917 18; +-10172 56031 1;-10114 56042;-10052 56053;-9978 56066 1;-9804 56097;-9743 55872;-9716 55698 1;-9651 55270;-9653 55025;-9576 54599 1;-9529 54336;-9292 54149;-9027 54189 1;-8458 54274;-8142 54350;-7570 54416 1;-7500 54424;-7471 54437;-7435 54482; +-11049 54400 32;-11882 54291 32;-11839 53961 32;-11006 54070 32;-11049 54400 50; +-10945 53337 1;-11475 53203;-12163 53168;-12149 52621 1;-12145 52465;-12191 52243;-12035 52237 1;-11765 52227;-11625 52380;-11355 52368 1;-11124 52358;-11033 52335;-10762 52359;-10945 53337 18; +-14422 50686 1;-14351 50949;-14169 51109;-13899 51149 1;-13610 51192;-13355 51080;-13227 50817;-14422 50686 18; +-12660 50896 1;-12533 51222;-12342 51441;-11997 51498 1;-11551 51572;-11079 51546;-10898 51131;-12660 50896 18; +-9423 51315 1;-9330 51591;-9125 51727;-8839 51777 1;-8584 51822;-8344 51713;-8228 51481;-9423 51315 18; +-12074 52902 1;-12614 52908;-12905 52733;-13406 52533 1;-13976 52305;-14334 52245;-14946 52206;-14765 50627 32;-9320 51321;-9385 52560;-10772 52373;-10857 52352 1;-11058 52341;-11153 52359;-11355 52368 1;-11625 52380;-11765 52227;-12035 52237 1;-12191 52243;-12145 52465;-12149 52621 1;-12150 52678;-12144 52729;-12132 52775;-12074 52902 18; +-12074 52902 1;-12614 52908;-12905 52733;-13406 52533 1;-13976 52305;-14339 52240;-14951 52201;-14765 50627 32;-9320 51321;-9385 52560;-10772 52373;-10857 52352 1;-11058 52341;-11153 52359;-11355 52368 1;-11625 52380;-11765 52227;-12035 52237 1;-12191 52243;-12145 52465;-12149 52621 1;-12150 52678;-12144 52729;-12132 52775;-12074 52902 18; +-12151 52503 1;-12687 52440;-12951 52231;-13468 52077 1;-14020 51912;-14325 51818;-14899 51769; +-15105 53781;-14951 52197 1;-14339 52236;-13976 52305;-13406 52533 1;-12905 52733;-12614 52908;-12074 52902;-12050 52935 1;-11842 53191;-11348 53235;-10945 53337;-11003 54065;-11034 54066;-11839 53961 32;-11882 54291 32;-11049 54400 32;-11176 55514;-12465 55335;-12360 54182;-14014 53972;-14217 53670;-14587 53880;-15105 53781 18; +-11172 53285 1;-11652 53183;-12161 53092;-12149 52621 1;-12145 52465;-12191 52243;-12035 52237 1;-11765 52227;-11625 52380;-11355 52368 1;-11200 52361;-11108 52349;-10982 52348; +-3174 57675;-2687 57715;-2831 58528;-2898 59023;-3160 60907;-3231 61417;-3471 63182;-3546 63709;-3681 64989;-3740 65360;-4209 65316;-4224 64857;-3940 62610;-4978 62475;-4748 60774;-4203 60844;-4216 60927;-4356 61996;-3966 62047;-3712 61818;-3505 60231;-3174 57675 18; +-4173 65321;-4212 64854;-4385 64912;-5371 65314;-6463 65159;-6657 65126;-8991 64795;-9252 64376;-9465 64342;-9556 64944;-10476 64791;-10350 63818;-10725 63766;-10869 64730;-11860 64594;-11768 63905;-11999 63866;-11938 63329;-11380 63412;-11336 63085;-11785 63015;-11633 61907;-11310 61942;-11271 61680;-12004 61602;-12011 61736;-12180 62937;-12458 63211;-12695 64982;-9023 65453;-9005 65217;-5324 65741;-4393 65369;-4173 65321 18; +-13282 61431;-12005 61628;-11265 61714;-10140 53770;-10942 53675;-10980 53779;-10996 53983;-10997 54101;-11180 55504;-12478 55335;-12713 57138;-12589 57146;-12136 57205;-11975 55963;-11448 56031;-11262 56273;-11848 60755;-12141 60980;-12651 60913;-12498 59747;-13035 59677;-13282 61431 18; +-4189 56330;-3036 56480;-2753 54305;-9604 53414 1;-9869 53380;-10118 53559;-10153 53817;-11302 61959;-11639 61911;-11797 63021 16; +-4161 56339;-3020 56468;-3028 56416;-2753 54305;-6685 53794;-6681 53929;-6789 54863;-6648 54920;-6245 54972;-5914 54737;-5685 55045;-4035 55261 32;-4161 56339 18; +-8131 48683;-7169 47995;-5827 48194;-5827 48194 1;-5698 48213;-5588 48301;-5606 48439;-6235 52988;-10772 52373;-11180 55504;-12478 55335 16; +-9388 52543;-9342 51490;-9319 51515 1;-9209 51661;-9045 51741;-8839 51777 1;-8584 51822;-8344 51713;-8228 51481;-6853 51640;-6798 51223;-6487 50987;-6721 50646;-6487 48898 32;-8079 48685;-7903 48520;-7169 47995;-5827 48194;-5827 48194 1;-5698 48213;-5588 48301;-5606 48439;-6236 52991;-9388 52543 18; +-7887 46818 1;-7906 46982;-8080 47120;-8236 47097 1;-8505 47058;-8656 47035;-8925 46995 1;-9235 46949;-9467 46608;-9412 46300 1;-9354 45977;-9334 45796;-9276 45473 1;-9221 45164;-8930 44868;-8707 44902;-8154 44977; +-8433 46072; +-11061 47450;-10145 46736;-10857 45803; +-10770 45176;-10726 45182;-9951 44593;-9727 42954;-10345 42141; +-10770 45176;-10726 45182;-9951 44593;-9727 42954;-10345 42141;-10770 45176 18; +-11061 47450;-10145 46736;-10857 45803;-11061 47450 18; +-3485 50117;-3904 53289;4433 54391;4459 53307;-3181 52283;-2919 50190;-3485 50117 18; +-3661 51103;-3805 52202 32;-4241 52145 32;-4094 51027 48; +-3872 51073;-3970 51816; +-5016 52702 1;-4921 52193;-4599 51985;-4196 51660;-4193 51783;-4241 52145 32;-3805 52202;-3752 52289;-3875 53300;-5078 53146;-5016 52702 18; +-2906 50024;-3234 49980; +-4557 49418;-4070 49436;-4113 49973;-3477 50115;-3498 50214;-3620 51140;-4131 51077;-4236 51712;-4320 51759 1;-4614 51992;-4852 52183;-4968 52518;-4557 49418 18; +-3661 51103;-3805 52202 32;-4241 52145 32;-4094 51027 32;-3661 51103 18; +-2374 48856 32;-698 49078 32;-798 49834 32;-2474 49612 32;-2374 48856 50; +-3701 49280;-3591 48508; +-2763 48804;-3446 48714; +-2864 49528;-2809 49113;-3285 49050;-3317 49292; +-3214 49822;-2921 49863;-2799 49133;-3277 49058;-3312 49292;-3146 49320;-3214 49822 18; +-3526 49261;-3324 49285;-3309 49229;-3285 49050;-2809 49113;-2864 49528;-2471 49582;-2379 48869;-3457 48718;-3526 49261 18; +-3757 48521;-2116 35837;988 36364;864 37229;-1149 36968;-2757 48807;-3459 48715;-3757 48521 18; +-4550 49433;-4069 49455;-4001 49239;-3813 49263;-3753 48525;-2267 37259;-2854 37183;-4550 49433 18; +-4181 50043;-2992 50213;-3204 51711;-4824 51582;-4187 47003;-2571 47170;-2792 48768;-3419 48686;-3395 48541;-3790 48487;-3885 49178;-4060 49151;-4181 50043 18; +-9129 42177;-8266 42331;-8343 42409 1;-8348 42418;-8352 42429;-8354 42441 1;-8378 42558;-8403 42621;-8424 42738 1;-8473 43011;-8271 43164;-8309 43439 1;-8332 43609;-8346 43705;-8369 43875 1;-8388 44014;-8515 44111;-8654 44092 1;-8878 44062;-9004 44046;-9228 44015 1;-9310 44004;-9366 43928;-9355 43846;-9129 42177 18; +-8321 39694;-8575 41509;-5529 41935;-5272 40095; +-8321 39694;-8575 41509;-5529 41935;-5272 40095;-8321 39694 18; +-9061 41489;-8574 41514;-8315 39688;-9240 39559;-9274 39628;-9465 40988;-9189 41354;-9061 41489 18; +-5077 39581;-4399 39766;-4928 43572;-5047 43550 1;-5316 43494;-5355 43406;-5445 43176 1;-5547 42916;-5800 42814;-6077 42781 1;-6436 42738;-6641 42744;-7002 42720 1;-7125 42712;-7193 42623;-7271 42545;-7167 41706;-5529 41935;-5272 40095;-5237 39698;-5077 39581 18; +-9395 40463;-8452 40595; +-9126 42206;-8311 42363;-8216 42337 1;-8168 42331;-8115 42336;-8066 42345 1;-7831 42388;-7701 42398;-7464 42433 1;-7366 42448;-7304 42511;-7243 42574;-7129 41722;-8572 41495;-9100 41460;-9030 41560;-9126 42206 18; +-6560 37067;-5456 36244;-3945 36467;-4928 43570 1;-5088 43554;-5232 43535;-5299 43481 1;-5393 43405;-5374 43318;-5436 43172 1;-5545 42915;-5800 42814;-6077 42781 1;-6436 42738;-6641 42744;-7002 42720 1;-7209 42707;-7259 42464;-7464 42433 1;-7701 42398;-7831 42388;-8066 42345 1;-8183 42324;-8331 42325;-8354 42441 1;-8378 42558;-8403 42621;-8424 42738 1;-8473 43011;-8271 43164;-8309 43439 1;-8332 43609;-8346 43705;-8369 43875 1;-8388 44014;-8515 44111;-8654 44092 1;-8878 44062;-9004 44046;-9228 44015 1;-9310 44004;-9366 43928;-9355 43846;-9034 41560;-9465 40988;-9250 39593 16; +-7322 47327;-7072 46506;-6417 46602 1;-6245 46627;-6076 46521;-6051 46349;-5868 45109 1;-5479 44861;-5304 44599;-5248 44141;-5015 44171;-5438 47239 1;-5467 47451;-5687 47568;-5898 47534;-7322 47327 18; +-7579 45697;-7412 44494 1;-7543 44476;-7616 44466;-7747 44448 1;-7889 44428;-7989 44297;-7969 44155 1;-7944 43975;-7929 43874;-7904 43694 1;-7887 43570;-7772 43483;-7648 43500;-7330 43544;-7296 43298 1;-7279 43172;-7162 43084;-7036 43101 1;-6647 43155;-6429 43183;-6040 43235 1;-5882 43256;-5771 43401;-5792 43559 1;-5854 44026;-5889 44287;-5951 44754 1;-5975 44936;-6143 45064;-6325 45040;-6401 45615;-6436 45881 1;-6453 46007;-6569 46097;-6695 46080 1;-6961 46045;-7111 46024;-7377 45988 1;-7497 45972;-7603 45849;-7579 45697 18; +-7579 45697;-7412 44494 1;-7543 44476;-7616 44466;-7747 44448 1;-7889 44428;-7989 44297;-7969 44155 1;-7944 43975;-7929 43874;-7904 43694 1;-7887 43570;-7772 43483;-7648 43500;-7330 43544;-7296 43298 1;-7279 43172;-7162 43084;-7036 43101 1;-6647 43155;-6429 43183;-6040 43235 1;-5882 43256;-5771 43401;-5792 43559 1;-5854 44026;-5889 44287;-5951 44754 1;-5975 44936;-6143 45064;-6325 45040;-6401 45615;-6436 45881 1;-6453 46007;-6569 46097;-6695 46080 1;-6961 46045;-7111 46024;-7377 45988 1;-7497 45972;-7603 45849;-7579 45697 18; +-8772 48571;-9698 47380;-10889 48306; +-8772 48571;-9698 47380;-10889 48306;-8772 48571 18; +-11473 47377 32;-11588 48220 32;-13372 47977 32;-13257 47134 32;-11473 47377 50; +-13272 47127 32;-13388 47977 32;-13986 47896 32;-13870 47046 32;-13272 47127 50; +-13856 47101;-13957 47852; +-5371 44870; +-5618 45919; +-5803 47054; +-6673 46999; +-7322 47327;-7072 46506;-6417 46602 1;-6245 46627;-6076 46521;-6051 46349;-5868 45109 1;-5479 44861;-5304 44599;-5248 44141;-5015 44171;-5438 47239 1;-5467 47451;-5687 47568;-5898 47534;-7322 47327 18; +-10151 53826;-10962 53739;-10936 53633;-10772 52373;-6817 52909;-6646 52933;-6236 52991;-5606 48439 1;-5599 48387;-5610 48343;-5634 48306;-4419 48442;-5073 53147;-2771 53456;-2882 54288;-6685 53794;-8489 53550;-9510 53412 1;-9806 53374;-10055 53508;-10141 53769;-10151 53826 18; +-4430 48483;-5621 48357;-5696 48243 1;-5733 48218;-5779 48201;-5827 48194;-5827 48194;-7169 47995;-8116 48672;-8216 48564;-8366 48354;-8696 48602;-8763 48593;-8829 48498;-9698 47380;-10421 47942;-10607 48087;-10889 48306;-11580 48206;-11469 47404;-11080 47441;-10155 46738;-10852 45831;-10556 45553;-10769 45218;-9951 44591;-9853 43882;-9353 43900 1;-9339 43959;-9291 44006;-9228 44015 1;-9004 44046;-8878 44062;-8654 44092 1;-8553 44106;-8457 44058;-8406 43977;-7955 44056 1;-7959 44087;-7964 44119;-7969 44155 1;-7989 44297;-7889 44428;-7747 44448 1;-7616 44466;-7543 44476;-7412 44494;-7579 45697 1;-7603 45849;-7497 45972;-7377 45988 1;-7111 46024;-6961 46045;-6695 46080 1;-6569 46097;-6453 46007;-6436 45881;-6401 45615;-6325 45040 1;-6143 45064;-5975 44936;-5951 44754 1;-5915 44483;-5888 44281;-5860 44067;-5249 44147 1;-5305 44601;-5481 44862;-5868 45109;-6051 46349 1;-6076 46521;-6245 46627;-6417 46602;-7072 46506;-7322 47327;-5898 47534 1;-5687 47568;-5467 47451;-5438 47239;-5019 44199;-3848 44332;-4430 48483 18; +-9363 43932;-9873 43915;-9729 42968;-10344 42166;-10327 41874;-10030 41603;-10218 41289;-9930 39056;-9186 39152;-9463 40979;-9040 41568;-9354 43806;-9363 43932 18; +-8438 43997;-7967 44093;-7944 43979 1;-7931 43889;-7920 43807;-7904 43694 1;-7887 43570;-7772 43483;-7648 43500;-7330 43544;-7296 43298 1;-7279 43172;-7162 43084;-7036 43101 1;-6647 43155;-6429 43183;-6040 43235 1;-5882 43256;-5771 43401;-5792 43559 1;-5820 43771;-5843 43941;-5865 44106;-5256 44201 1;-5255 44192;-5254 44182;-5252 44173;-5018 44180;-5023 44250;-3867 44376;-3222 39864;-4394 39724;-4928 43570 1;-5088 43554;-5232 43535;-5299 43481 1;-5393 43405;-5374 43318;-5436 43172 1;-5545 42915;-5800 42814;-6077 42781 1;-6436 42738;-6641 42744;-7002 42720 1;-7209 42707;-7259 42464;-7464 42433 1;-7701 42398;-7831 42388;-8066 42345 1;-8183 42324;-8331 42325;-8354 42441 1;-8378 42558;-8403 42621;-8424 42738 1;-8473 43011;-8271 43164;-8309 43439 1;-8329 43588;-8342 43681;-8361 43816;-8438 43997 18; +-14409 50659;-14402 50749 1;-14320 50975;-14147 51112;-13899 51149 1;-13610 51192;-13355 51080;-13227 50817; +-12638 50886;-12636 50954 1;-12509 51248;-12321 51444;-11997 51498 1;-11551 51572;-11079 51546;-10898 51131; +-9414 51309;-9387 51404 1;-9282 51621;-9092 51733;-8839 51777 1;-8584 51822;-8344 51713;-8228 51481; +-3872 53115;4385 54224;4507 53314; +-2697 57728;-3171 57709;-3146 57487;-3126 57336;-3309 57093;-3680 57046;-3841 58288;-4419 58213;-4145 56335;-3597 56407;-3036 56480;-2753 54305;-2938 54281;-2884 53248;1147 53789;4576 54284;4391 55370;4268 55347;944 54838;930 54944;26 54867;42 54747;36 54722;-2221 54395;-2514 56256;-2582 56750;-2697 57728 18; +4704 52835 32;5571 52949 32;5789 51293 32;4922 51179 32;4704 52835 50; +4502 54392;4336 55378;4485 55380;7559 55851;7398 56776;7413 56818;8333 56958;8542 56068;9541 56273;10034 54842;9853 54955 1;9588 55069;9206 55006;8890 54923 1;8974 54582;9058 54285;9144 54011;9235 53732 1;9461 53070;9711 52517;10042 51747;8832 51574;8524 53931 32;7890 53848 32;6819 53725;6758 54205;6862 54236;7658 54335 32;7583 54938 32;6695 54827 32;5641 54685;5746 53559;5484 53516;5536 52944;4752 52841;4656 53298;4502 54392 18; +6695 54827 32;7583 54938 32;7658 54335 32;6770 54224 32;6695 54827 50; +6230 54775;6685 54841;6840 53718;5721 53595;5684 54047;6286 54147;6230 54775 18; +5628 49455; +5630 49055 1;5409 49053;5228 49230;5226 49451 1;5223 49672;5400 49853;5621 49856 1;5842 49858;6024 49681;6026 49460 1;6028 49239;5851 49058;5630 49055 18; +5075 50431;3392 50209; +2832 50145;755 49871; +1537 47576;1418 48481;3993 48820;4056 48341; +3836 48902 1;3806 49137;3858 49419;4093 49451 1;4262 49474;4489 49507;4516 49338 1;4560 49061;4585 48905;4629 48628 1;4644 48535;4596 48436;4503 48422;4172 48370;3836 48902 18; +1339 48220 1;1169 48201;957 48220;934 48390 1;913 48544;902 48631;881 48785 1;867 48889;939 48985;1043 48999 1;1552 49068;1840 49086;2347 49167 1;2458 49185;2547 49086;2561 48975;2587 48709;1344 48521;1339 48220 18; +1314 46922; +1445 46128; +3155 47934; +2544 46686; +1628 47605;1844 46032;2271 46091;2372 45358;3536 45518;3423 46343 1;3373 46707;3733 47024;4080 47145;3932 48331;3889 48681;1557 48377;1628 47605 18; +3933 48322;4080 47145 1;3733 47024;3373 46707;3423 46343;3536 45518;2372 45358;2271 46091;1844 46032;1629 47596; +1629 47565;977 47476; +103 47536;613 47603;397 49246;-105 49180;103 47536 18; +127 47550;606 47613;394 49221;-62 49161; +655 43537;1134 43600;682 47036;226 46976; +657 45262; +655 43537;1134 43600;682 47036;226 46976;655 43537 18; +952 41199;1588 42033;1468 42927;750 42831; +952 41199;1588 42033;1468 42927;750 42831;952 41199 18; +1950 40084;1813 41123;3298 43060;4304 43179; +8711 43826;8157 44827;7610 44984; +7661 44793;6329 44590;6180 45904;7316 46095;7932 45015;7661 44793 18; +7598 44802;4119 44315;4242 43359; +1994 40119;1864 40738;1813 41123;3298 43060;4231 43253;4237 43398;4119 44315;7598 44802;8031 44863;8157 44827;8622 43986;8577 43729;1994 40119 18; +4061 44822 32;5605 45038 32;5377 46669 32;3833 46453 32;4061 44822 50; +4061 44822 32;5605 45038 32;5377 46669 32;3833 46453 32;4061 44822 50; +7047 46598;6037 48447 1;5901 48695;5513 48551;5320 48344 1;5039 48041;4881 47872;4599 47570 1;4486 47449;4644 47183;4808 47202 1;5035 47228;5162 47243;5389 47269 1;5653 47300;5889 47120;5931 46857;5998 46419;7047 46598 18; +6510 46820; +6195 47394; +5838 47967; +7047 46598;6037 48447 1;5901 48695;5513 48551;5320 48344 1;5039 48041;4881 47872;4599 47570 1;4486 47449;4644 47183;4808 47202 1;5035 47228;5162 47243;5389 47269 1;5653 47300;5889 47120;5931 46857;5998 46419;7047 46598 18; +6324 44635;6180 45904;7316 46095;7879 45108; +3107 43702;3746 43799;3534 45199; +2755 43648;2308 43580;2190 44355; +1836 46023;1269 45944;1588 43654;1821 43686;1899 43116;2126 42943;2761 43543;2459 43765;2348 44844;2403 44912;2348 45356;2280 46072;1836 46023 18; +2427 44875;2367 45362 16; +1836 46023;1269 45944;1588 43654;1821 43686;1899 43116;2126 42943;2733 43517 16; +6874 48842 32;6513 48645 32;7106 47557 32;7467 47754 32;6874 48842 50; +7658 47448 32;7277 47241 32;7499 46833 32;7880 47041 32;7658 47448 50; +7559 47115; +6806 48540; +6874 48842 32;6513 48645 32;7106 47557 32;7467 47754 32;6874 48842 50; +7570 49244 32;8793 49912 32;8274 50861 32;6794 50664 32;7570 49244 50; +1131 39941 32;2004 40056 32;2187 38670 32;1314 38555 32;1131 39941 50; +4897 51180;5752 51293;5818 51112;5891 50555 32;6774 50670;6871 50523;7570 49244 32;7591 49255;7453 49143;6909 48846;6780 48791;6513 48645 32;7106 47557 32;7467 47754 32;7663 47438;7538 47383;7277 47241 32;7499 46833 32;7880 47041 32;9678 43748;8977 43365;8160 44812;7863 45108;7314 46085;6171 45902;5996 46417;6040 46426;7047 46598;6037 48447 1;5901 48695;5513 48551;5320 48344 1;5039 48041;4881 47872;4599 47570 1;4515 47480;4580 47311;4686 47237;4079 47154;3934 48317;4168 48376;4172 48370;4503 48422 1;4596 48436;4644 48535;4629 48628 1;4589 48882;4564 49034;4527 49272;5238 49359 1;5281 49183;5441 49053;5630 49055 1;5851 49058;6028 49239;6026 49460 1;6024 49681;5842 49858;5621 49856 1;5538 49855;5461 49829;5398 49785;5060 50331;4897 51180 18; +3834 48920;3834 48920 1;3809 49151;3864 49420;4093 49451 1;4262 49474;4489 49507;4516 49338 1;4560 49061;4585 48905;4629 48628 1;4644 48535;4596 48436;4503 48422;4172 48370;4146 48388; +2583 48752;2561 48975 1;2547 49086;2458 49185;2347 49167 1;1840 49086;1552 49068;1043 48999 1;939 48985;867 48889;881 48785 1;902 48631;913 48544;934 48390 1;957 48222;1164 48201;1333 48219; +4920 50578;5408 49819;5377 49769 1;5284 49695;5224 49580;5226 49451 1;5226 49410;5233 49371;5245 49334;4529 49239;4512 49358 1;4471 49504;4255 49473;4093 49451 1;3858 49419;3806 49137;3836 48902;3859 48866;2593 48743;2577 48817;2561 48975 1;2547 49086;2458 49185;2347 49167 1;1840 49086;1552 49068;1043 48999 1;939 48985;867 48889;881 48785 1;902 48631;913 48544;934 48390 1;957 48222;1164 48201;1333 48219;1618 47565;1834 46041;1279 45943;1588 43654;1821 43686;1899 43116;2126 42943;2752 43535;3316 43064;3298 43060;1813 41123;1864 40738;1994 40119;1797 40027;1156 39947;977 41261;1588 42038;1458 42932;736 42827;668 43531;1119 43611;699 47029;205 46973;137 47547;606 47615;408 49219;-696 49089;-801 49805;4920 50578 18; +4062 47204;4623 47284;4722 47217 1;4750 47204;4779 47199;4808 47202 1;5035 47228;5162 47243;5389 47269 1;5653 47300;5889 47120;5931 46857;5998 46419;6037 46426;6225 45911;6171 45902;6320 44637;4118 44304;4061 44822 32;5605 45038 32;5377 46669 32;3833 46453 32;3958 45558;3532 45545;3423 46343;3441 46562 1;3520 46825;3794 47040;4064 47139;4062 47204 18; +2427 44876;2374 45348;3534 45508;3537 45579;3953 45585;4074 44805;4135 44330;4234 43349;3148 43171;2738 43525;2688 43794;3524 43948;3370 44972;2427 44876 18; +14958 41825 32;15796 42265 32;15984 41887 32;15146 41447 32;14958 41825 50; +14145 44489; +12813 47215; +11365 50229; +13488 41153;8770 49903;9269 50175 32;14034 41451 32;13488 41153 18; +8787 49872;13471 41184;11641 40184 32;6909 48846 32;8787 49872 18; +11193 51931;11793 50528;11793 50529; +11054 49789;12301 47242; +12534 46765;13630 44523; +13863 44046;14958 41825; +10801 50399 1;11121 50440;11473 50483;11793 50529;11885 50353 1;11945 50238;11841 50114;11721 50065;11054 49789;10801 50399 18; +13630 44523 32;14450 44924 32;14683 44447 32;13863 44046 32;13630 44523 50; +15149 41439;15978 41884 1;16088 41650;16024 41369;15795 41247;15364 41018;15149 41439 18; +12301 47242 32;13121 47643 32;13354 47166 32;12534 46765 32;12301 47242 50; +14958 41825 32;15796 42265 32;15984 41887 32;15146 41447 32;14958 41825 50; +9264 50170;8814 49917;8283 50830;8913 50935;8839 51632;10017 51786;10089 51754;11193 51931;11775 50540;10800 50411;11059 49788;12304 47247;12586 46659;13630 44523;13902 43967;14958 41825;14034 41451 32;9264 50170 18; +11170 51992;12780 52258;12834 52150 1;12858 52100;12882 52049;12907 51996 1;12955 51895;13030 51799;13129 51836;13175 51711;13881 49895;14438 50028;17941 42583;17980 42411;17760 42308;15985 41446 1;16047 41578;16046 41740;15978 41884;15900 42057;15796 42265 32;14969 41831;14821 42104;13902 43967;13856 44061;13991 44109;14683 44447 32;14450 44924 32;13630 44523 32;13525 44737;12586 46659;12526 46784;12633 46813;13354 47166 32;13121 47643 32;12301 47242 32;12175 47510;11063 49780;11186 49844;11721 50065 1;11841 50114;11945 50238;11885 50353;11793 50529;11719 50675;11170 51992 18; +-18383 61381;-18494 62134;-18561 64120;-12701 64947;-12677 64845;-12458 63211;-12451 63204;-12492 63162 1;-12741 62793;-12976 62243;-12860 61860 1;-12806 61680;-12753 61700;-12639 61551;-12611 61542;-13268 61443;-13355 61929;-13465 62772 32;-15116 62556;-15405 62760;-15638 62488;-16168 62419 32;-15816 59717;-15731 59072;-15048 53833;-15101 53740;-14951 52197;-14946 52162;-14765 50627 32;-14373 47847;-13978 47884;-13824 47014;-12394 36327;-12970 36239;-13152 36154;-13175 36037;-13160 35932;-13468 35886;-14830 35957;-18383 61381 18; +34 914;-12019 914; +152 -545;158 -1378; +140 -1785;148 -2889; +439 -3751; +1308 -3759; +1619 -3393 1;1628 -3761;1625 -4139;1631 -4536 1;1652 -5992;918 -6885;-581 -6838;-1994 -6794;-2007 -4892;100 -4877;92 -3689;765 -3007;1619 -3393 18; +1619 -3393 1;1628 -3761;1625 -4139;1631 -4536 1;1652 -5992;918 -6885;-581 -6838;-1994 -6794;-2007 -4892;100 -4877;92 -3689;765 -3007;1619 -3393 18; +-14234 987;-32592 962;-32592 -2714;-24820 -4442;-21563 -4491;-12767 -2694;-8868 -2634;-8859 -1456;-425 -1370;99 -1370;186 937;-14235 888;-14234 987 18; +1557 1050;2196 1046;2195 53;2190 -4523 1;2190 -6333;1139 -7424;-561 -7383 1;-614 -7382;-665 -7380;-715 -7379;-731 -6833;-615 -6837;-472 -6840 1;954 -6841;1651 -5956;1631 -4536 1;1625 -4139;1628 -3761;1619 -3393;765 -3007;92 -3689;100 -4877;-559 -4882;-572 -4271;-443 -4270;-426 -3753;-442 -1362;88 -1362;127 -494;1557 1050 18; +-16459 12674;-16426 2012;-17246 2009; +-16739 13703;-24033 13593;-23893 2165;-20125 2235;-20173 13114;-17348 13104;-17246 2009;-16426 2012;-16459 12674;-16739 13703 18; +-11834 13678;-15436 13652;-15423 11802; +-15419 8897;-15410 6105; +-14797 2808; +-15411 6114;-15406 2558 32;-15406 2183 32;-11555 2188; +-15428 11818;-15419 8897; +-11834 13678;-8633 13701; +-8653 13901;-7850 13912;-7813 2040;-8633 2037; +-15485 2043;-8541 2037;-7813 2040;-7850 13912;-8653 13901;-15467 13820;-15485 2043 18; +-6719 4039;-6716 2188;1837 2202; +-6925 13755;-3714 13759; +-6734 13566;-6719 4039; +1623 5535;1624 4663; +1618 2387;1615 4676; +1618 5521;1610 12270 1;1610 12420;1587 12565;1546 12702 33;1360 13315;790 13761;116 13760;-50 13760 32;-2345 13757 32;-3725 13755; +-6720 2166;1638 2183;1610 11936;1610 12270 1;1610 12420;1587 12565;1546 12702 33;1360 13315;790 13761;116 13760;-50 13760 32;-2345 13757 32;-3725 13755;-6772 13786;-6720 2166 18; +-17390 1024;-17427 2011;-17068 2010;-16426 2012;-16459 12674;-16632 13674;-21099 13622;-21116 14208;-20139 14209;363 14412 1;1238 14411;2213 13560;2212 12685;2198 2019;1565 2211;1612 10412;1610 12270 1;1610 12420;1587 12565;1546 12702 33;1360 13315;790 13761;116 13760;-50 13760 32;-2345 13757 32;-3725 13755;-6809 13692;-6774 2002;-7813 2040;-7850 13912;-8653 13901;-15550 13779;-15498 1025;-17390 1024 18; +-15568 2037;-15603 990;2193 921;2198 2177;1652 2229;-7813 2159;-7813 2040;-8633 2037;-15568 2037 18; +23488 -17737;19464 -17649; +26077 -17754;28115 -17802; +30749 -17775;33010 -17828; +19162 -17670;33207 -17880;32632 -26307;18639 -25452;19162 -17670 18; +28299 -10788;28328 -9546;29613 -9576 1;30319 -9592;30930 -10125;30914 -10831; +26276 -10739;26330 -8450 1;25304 -8664;24735 -8776;23694 -8897;23654 -10667; +26276 -10739;26330 -8450 1;25304 -8664;24735 -8776;23694 -8897;23654 -10667;26276 -10739 18; +28299 -10788;28328 -9546;29613 -9576 1;30319 -9592;30930 -10125;30914 -10831;28299 -10788 18; +33281 -7193; +31813 -7489; +34490 -7354 1;35292 -7278;35887 -7222;36415 -6613 1;36914 -6037;37643 -5668;38290 -6070 1;38702 -6326;38886 -6983;38512 -7292 1;37277 -8313;36111 -8624;34539 -8316 1;34249 -8259;33968 -8217;33885 -7933 1;33832 -7749;33849 -7519;34033 -7465 1;34209 -7413;34307 -7371;34490 -7354 18; +37966 -5256 1;34264 -6452;32110 -7114;28336 -8058;28330 -8197 1;29259 -8215;29779 -8260;30708 -8300 1;31629 -8340;32313 -8493;32883 -9217;41421 -8165;40332 -6072 1;39872 -5188;38914 -4950;37966 -5256 18;38290 -6070 1;38702 -6326;38886 -6983;38512 -7292 1;37277 -8313;36111 -8624;34539 -8316 1;34249 -8259;33968 -8217;33885 -7933 1;33832 -7749;33849 -7519;34033 -7465 1;34209 -7413;34307 -7371;34490 -7354 1;35292 -7278;35887 -7222;36415 -6613 1;36914 -6037;37643 -5668;38290 -6070 18; +40962 -13427;43997 -13365;41234 -8369;33079 -9380;33042 -10108;33227 -10133;33252 -11811;34522 -11823;34502 -13710;35488 -13693;40962 -13427 18; +-2273 -9414;-765 -9474; +-769 -9312;2293 -9390; +6396 -9606;2269 -9556; +6396 -9606;8091 -9594; +8091 -9594;9541 -9599; +9541 -9599;12606 -9550; +15726 -9526;12606 -9550; +15726 -9526;17010 -9507; +17010 -9507;18811 -9477; +18811 -9477;20391 -9410;20378 -10705; +-11446 -12938;-11421 -9853;-11403 -9677 1;-11284 -9337;-10958 -9093;-10581 -9108;-8446 -9191;-1251 -9336;-668 -9315;2293 -9390;7075 -9654;17247 -9497;20335 -9427;20318 -13492;-11446 -12938 18; +26331 -8060 1;24533 -8435;23069 -8598;20879 -8709 1;16304 -8942;13770 -8820;9190 -8919 1;4796 -9014;2347 -8930;-2047 -8813 1;-2060 -8813;-2074 -8812;-2087 -8812;-2323 -8802;-2367 -9259;-720 -9313;2293 -9390;8209 -9494;16462 -9406;20405 -9354;20370 -10750;21609 -10750;21505 -16120;20199 -16116;20187 -16612;19202 -16600;19185 -17516;23512 -17673;23658 -10489;23694 -8897 1;24735 -8776;25304 -8664;26330 -8450;26331 -8060 18; +40895 -7190;41351 -6974;41347 -6910 1;41123 -6473;40890 -6018;40647 -5541 1;40215 -4692;38798 -4498;37573 -4879 1;34307 -5894;32347 -6556;29328 -7341;29054 -7412 1;28774 -7484;28485 -7557;28185 -7632 1;27487 -7806;26861 -7951;26271 -8073;26264 -8460;26338 -8442;26252 -10842;26079 -17664;28176 -17714;28302 -10646;28328 -9546;29613 -9576 1;30319 -9592;30930 -10125;30914 -10831;30693 -17714;32914 -17751;33160 -10139;32889 -9251;32802 -9118 1;32248 -8479;31586 -8338;30708 -8300 1;29779 -8260;29259 -8215;28330 -8197;28336 -8058 1;32110 -7114;34264 -6452;37966 -5256 1;38914 -4950;39872 -5188;40332 -6072;40768 -6926;40895 -7190 18; +11980 39455 32;14384 40769 32;14685 40219 32;12281 38905 32;11980 39455 50; +13916 40136; +12678 39490; +11980 39455 32;14384 40769 32;14685 40219 32;12281 38905 32;11980 39455 50; +12907 37796;15121 38994;18659 32456; +17785 32218;18400 32551; +12428 36391 32;11958 37260 32;12489 37547 32;12959 36678 32;12428 36391 50; +12952 36644 32;12467 37540 32;15005 38915 32;18406 32639 32;17795 32308 32;14879 37688 32;12952 36644 50; +12931 37780;11989 37269;12457 36406; +11599 36132;11194 36880; +12562 34193;12080 35054; +10111 34855 1;9373 34421;8925 34239;8175 33826 1;7239 33311;6641 33107;5584 32953;2879 32535;3281 29569; +9981 34997;9592 35823 1;9535 35943;9393 35994;9273 35938 1;9171 35890;9126 35768;9174 35666;9589 34783; +8006 33956;7612 34793 1;7555 34913;7413 34964;7293 34908 1;7191 34860;7146 34738;7194 34636;7609 33753; +5434 33117;5286 33993 1;5268 34099;5162 34169;5056 34151 1;4949 34133;4846 34026;4864 33919;5008 33062; +9981 34997;9592 35823 1;9535 35943;9393 35994;9273 35938 1;9171 35890;9126 35768;9174 35666;9589 34783;9981 34997 18; +8008 33954;7612 34793 1;7555 34913;7413 34964;7293 34908 1;7191 34860;7146 34738;7194 34636;7609 33753;8008 33954 18; +5434 33117;5286 33993 1;5268 34099;5162 34169;5056 34151 1;4949 34133;4846 34026;4864 33919;5008 33062;5434 33117 18; +10016 34987;11246 35615; +3243 29229;3308 28750;2212 28601;2326 27764; +3343 29236;3692 29287;3778 28696;3424 28644;3343 29236 18; +3892 28006 32;4882 28150 32;4798 28728 32;3808 28584 32;3892 28006 50; +4815 28716;3790 28550;3690 29274;3158 29213;3123 29549;3246 29825;2879 32535;5584 32953 1;6641 33107;7239 33311;8175 33826;10002 34972;11311 35644;11952 34990;12445 34096;6930 31146;4537 30813;4815 28716 18; +2957 30550;1858 30401;1923 29919;3020 30067; +2673 32684;2511 33738;2008 33661 1;1689 33613;1470 33314;1519 32995;1633 32251;2695 32414; +2673 32684;2511 33738;2008 33661 1;1689 33613;1470 33314;1519 32995;1633 32251;2695 32414;2673 32684 18; +2957 30550;1858 30401;1923 29919;3020 30067;2957 30550 18; +12900 33848 32;13650 34268 32;14535 32685 32;13785 32266 32;12900 33848 50; +11412 39051;10072 38340;9433 39545;10682 40208;10921 39756; +9511 39183;6704 37694; +7101 36598; +8250 38393;8712 37522;8294 37300;7835 38166;8250 38393 18; +10044 41469 32;10305 40996 32;8776 40153 32;8515 40626 32;10044 41469 50; +8048 40368 32;8309 39895 32;5392 38286 32;5131 38759 32;8048 40368 50; +4697 38518 1;4829 38279;4847 37958;4609 37824 1;4430 37723;4332 37644;4130 37606 1;3808 37545;3627 37510;3305 37449 1;3189 37427;3105 37489;3035 37584; +10044 41469 32;10305 40996 32;8776 40153 32;8515 40626 32;10044 41469 50; +8048 40368 32;8309 39895 32;5392 38286 32;5131 38759 32;8048 40368 50; +4697 38518 1;4829 38279;4847 37958;4609 37824 1;4430 37723;4332 37644;4130 37606 1;3808 37545;3627 37510;3305 37449 1;3189 37427;3105 37489;3035 37584;4697 38518 18; +8652 38781 32;8449 39163 32;8081 38968 32;8284 38586 32;8652 38781 50; +7797 38397 32;7621 38728 32;5478 37590 32;5654 37259 32;7797 38397 50; +5704 36118;5232 37007;2369 36513 16; +1546 36352;-2204 35741;-2432 37307 16; +1500 37304;1761 35290 1;1547 35255;1201 35261;1164 35475;1069 36115;-96 35919;23 35023;-446 34960;-563 35852;-2064 35609;-1969 35033 1;-1941 34863;-2070 34757;-2240 34729;-2544 34692;-2649 35578;-2309 35634;-2558 37222;-2852 37174;-5070 53124;-3902 53279;-3485 50117 16; +-2068 35639;-1969 35033 1;-1941 34863;-2074 34761;-2244 34733;-2525 34692;-2649 35578;-2309 35634;-2068 35639 18; +1472 37277 32;1609 36245 32;987 36162 32;850 37194 32;1472 37277 50; +1601 36537;2347 36659; +1639 36199;2416 36321; +1057 36216;1611 36307;1667 36014;1761 35290 1;1547 35255;1201 35261;1164 35475;1071 36099;1057 36216 18; +-575 35928;-91 35993;-89 35865;23 35023;-446 34960;-523 35546;-575 35928 18; +-5096 38866;-4306 38984;-4402 39796;-5070 39608;-4982 39481;-5135 39194;-5096 38866 18; +-7150 36995;-7975 35873;-8692 35764;-8843 36756; +-7150 36995;-7975 35873;-8692 35764;-8843 36756;-7150 36995 18; +-4847 37278;-4071 37391;-4312 38983;-5082 38868;-4847 37278 18; +-5017 37277;-5096 36305;-5176 36285;-5456 36244;-6560 37067;-5017 37277 18; +-5048 37264;-4075 37426;-3953 36466;-5117 36305;-5048 37264 18; +-3843 35855;-9468 35123;-10106 34303 1;-9907 34142;-9769 34067;-9631 33852;-9076 33926;-9202 34870;-7034 35159;-6911 34239;-6530 34290;-6652 35202;-4133 35539;-4043 34868 1;-4019 34693;-3859 34571;-3684 34594;-3843 35855 18; +-3807 35845;-9432 35113;-10070 34293 1;-9871 34132;-9733 34057;-9595 33842;-9040 33916;-9166 34860;-6998 35149;-6875 34229;-6494 34280;-6616 35192;-4097 35529;-4007 34858 1;-3983 34683;-3823 34561;-3648 34584;-3807 35845 18; +-9578 36699;-9551 36500;-10060 35831;-9965 35128;-10345 34629 1;-11028 35149;-11752 35198;-12533 34842;-12700 35058;-13076 35700 1;-13184 35875;-13230 36194;-13027 36224;-12404 36317; +-9578 36699;-9551 36500;-10060 35831;-9965 35128;-10345 34629 1;-11028 35149;-11752 35198;-12533 34842;-12700 35058;-13076 35700 1;-13184 35875;-13230 36194;-13027 36224;-12404 36317;-9578 36699 18; +-13877 34647;-13842 34424;-13520 34190;-13267 34406;-13470 35887;-14821 35936; +-13877 34647;-13842 34424;-13520 34190;-13267 34406;-13470 35887;-14821 35936;-14774 35557;-14052 35594;-13877 34647 18; +-10997 76099;-20134 74789;-18828 64936 32;-18724 64156 32;-15017 36197;-14861 35024 48; +-13388 30562;-13417 30778;-13280 30960 1;-12740 30520;-11988 30296;-11247 30427 1;-10265 30602;-9722 31218;-9480 32186;-670 33346 1;-337 33390;-107 33079;-53 32748;21 32125;-1133 31988;-898 30072;262 30214;327 29685;-832 29543;-620 27815;561 27960;628 27412;-814 27236;-892 27871;-1328 27817;-1274 27379;-1494 27352 16; +-13768 30531;-13817 30906;-13562 31237 1;-13796 31518;-13962 31849;-14022 32215 1;-14129 32861;-14176 33346;-13790 33874;-14206 34178;-14269 34584 16; +-13768 30531;-13817 30906;-13562 31237 1;-13796 31518;-13962 31849;-14022 32215 1;-14129 32861;-14176 33346;-13790 33874;-14206 34178;-14269 34584;-14684 34542;-14151 30463;-13768 30531 18; +-12864 29516;-12555 29556;-12690 30597;-12759 30634 1;-12947 30722;-13123 30832;-13280 30960;-13417 30778;-13389 30566;-13043 30589;-12864 29516 18; +-12658 30576;-12526 29554; +-1613 33191;-2495 32550;-3340 32988;-3606 32951;-4716 32001;-6517 32581;-1613 33191 18; +-1851 30119;-1579 30391;-1691 31303;-2465 31213;-10060 30328;-11435 29687;-12456 29579;-13986 29400;-13489 25830;-11430 26092;-11744 28814;-1851 30119 18; +-11638 32113 1;-11995 32060;-12327 32306;-12380 32663 1;-12433 33019;-12187 33351;-11830 33404 1;-11474 33457;-11142 33211;-11089 32855 1;-11036 32498;-11282 32166;-11638 32113 18; +-11731 32748; +-11638 32113 1;-11995 32060;-12327 32306;-12380 32663 1;-12433 33019;-12187 33351;-11830 33404 1;-11474 33457;-11142 33211;-11089 32855 1;-11036 32498;-11282 32166;-11638 32113 18; +-1549 27329;-1898 30094;-1834 30136;-1579 30391;-1691 31303;-2465 31213;-10060 30328;-11435 29687;-12456 29579;-12544 29569;-12617 30572 1;-12194 30404;-11718 30344;-11247 30427 1;-10265 30602;-9722 31218;-9480 32186;-6452 32568;-6323 32518;-4716 32001;-3606 32951;-3340 32988;-2495 32550;-1613 33191;-1337 33258;-670 33346 1;-337 33390;-107 33079;-53 32748;21 32125;-1133 31988;-898 30072;262 30214;327 29685;-832 29543;-620 27815;561 27960;628 27412;-814 27236;-892 27871;-1328 27817;-1274 27379;-1416 27362;-1549 27329 18; +-9879 39055 32;-9170 39151 32;-8940 37460 32;-9649 37364 32;-9879 39055 50; +-4419 39767;-3222 39896;-3207 39726;-2852 37174;-2558 37222;-2309 35634;-2649 35578;-2544 34692;-2483 34699;-2275 33135;-9480 32186 1;-9722 31218;-10265 30602;-11247 30427 1;-11289 30420;-11331 30413;-11374 30408;-11591 32122 1;-11261 32196;-11038 32514;-11089 32855 1;-11104 32958;-11143 33052;-11199 33131;-10106 34303 1;-9907 34142;-9769 34067;-9631 33852;-9076 33926;-9202 34870;-7034 35159;-6911 34239;-6530 34290;-6652 35202;-4133 35539;-4043 34868 1;-4019 34693;-3859 34571;-3684 34594;-3843 35855;-8616 35234;-8692 35765;-8692 35764;-7975 35873;-7150 36995;-6806 36792;-6595 37085;-6451 36986;-5456 36244;-3945 36467;-4286 38931;-4419 39767 18; +-9655 37361;-8949 37461;-8687 35777;-8600 35250;-8734 35218;-9468 35123;-10106 34303 1;-10097 34296;-10088 34289;-10080 34282;-11157 33062 1;-11169 33086;-11184 33109;-11199 33131;-11289 33235 1;-11429 33366;-11626 33434;-11830 33404 1;-12187 33351;-12433 33019;-12380 32663 1;-12327 32306;-11995 32060;-11638 32113 1;-11610 32117;-11582 32123;-11555 32131;-11343 30401;-11482 30397 1;-12145 30346;-12797 30567;-13280 30960;-13417 30778;-13388 30562;-13768 30497;-13781 30632;-13817 30906;-13562 31237 1;-13796 31518;-13962 31849;-14022 32215 1;-14129 32861;-14176 33346;-13790 33874;-14206 34178;-14269 34584;-13881 34632;-13858 34525;-13842 34424;-13520 34190;-13267 34406;-13470 35887;-13175 35940;-13139 35839 1;-13123 35789;-13101 35740;-13076 35700;-12700 35058;-12533 34842 1;-11752 35198;-11028 35149;-10345 34629;-9965 35128;-10060 35831;-9551 36500;-9578 36699;-9655 37361 18; +-7735 46820 1;-7764 47056;-8003 47282;-8240 47244 1;-8552 47194;-8712 47169;-9024 47119 1;-9351 47067;-9608 46633;-9572 46327;-9419 45411 1;-9363 45078;-8989 44715;-8699 44753;-8120 44830;-8161 45140;-8733 45052 1;-8928 45022;-9070 45206;-9100 45401 1;-9158 45778;-9217 45985;-9267 46363 1;-9295 46576;-9183 46792;-8971 46826 1;-8680 46873;-8516 46899;-8225 46946 1;-8124 46962;-8020 46885;-8011 46783;-7735 46820 18; +9644 43757 32;8944 43374 32;9775 41855 32;10475 42238 32;9644 43757 50; +2403 36394;2572 35417 1;2818 35459;3125 35546;3088 35793;2983 36495;2403 36394 18; +2984 36488;5159 36856;5586 36023;5852 36167;5316 37170;2367 36668;2572 35417 1;2818 35459;3125 35546;3088 35793;2984 36488 18; +18495 36807 32;17571 36301 32;17824 35840 32;18748 36346 32;18495 36807 50; +18495 36807 32;17571 36301 32;17824 35840 32;18748 36346 32;18495 36807 50; +18139 36313; +19782 33314; +20138 33808 32;19214 33302 32;19467 32841 32;20391 33347 32;20138 33808 50; +20138 33808 32;19214 33302 32;19467 32841 32;20391 33347 32;20138 33808 50; +20348 32281; +20704 32775 32;19780 32269 32;20076 31729 32;21000 32235 32;20704 32775 50; +20704 32775 32;19780 32269 32;20092 31701 32;21016 32207 32;20704 32775 50; +21916 29419; +22272 29913 32;21348 29407 32;21601 28946 32;22525 29452 32;22272 29913 50; +22272 29913 32;21348 29407 32;21601 28946 32;22525 29452 32;22272 29913 50; +23585 27518 32;22661 27012 32;22914 26551 32;23838 27057 32;23585 27518 50; +23229 27024; +23585 27518 32;22661 27012 32;22914 26551 32;23838 27057 32;23585 27518 50; +23047 26614;23661 25494;24702 26064;25459 24683; +25113 24171; +25469 24665 32;24545 24159 32;24798 23698 32;25722 24204 32;25469 24665 50; +25469 24665 32;24545 24159 32;24798 23698 32;25722 24204 32;25469 24665 50; +26851 22054 32;25927 21548 32;26180 21087 32;27104 21593 32;26851 22054 50; +26851 22054 32;25927 21548 32;26180 21087 32;27104 21593 32;26851 22054 50; +26495 21560; +28535 19036 32;27611 18530 32;27864 18069 32;28788 18575 32;28535 19036 50; +28535 19036 32;27611 18530 32;27864 18069 32;28788 18575 32;28535 19036 50; +28179 18542; +28283 19495 32;27359 18989 32;27612 18528 32;28536 19034 32;28283 19495 50; +27359 18989;26180 21087; +26015 21594;24886 23744; +22783 27084;21754 29043; +21509 29508;20253 31798; +19346 33382;17933 35908; +17658 36363;16527 38732; +20391 33347;20704 32775; +16120 30345 32;15767 30997 32;17858 32130 32;18211 31478 32;16120 30345 50; +17721 42356;16000 41517;15895 41314;15346 40999;16080 39549;16217 39579;16484 39693 1;16715 39808;16997 39720;17113 39489;17283 39099;16530 38729;17659 36360;17744 36396;18495 36807 32;18748 36346 32;17943 35905;18017 35758;19346 33382;19473 33444;20138 33808 32;20391 33347;20462 33217;20704 32775;20827 32551;21016 32207 32;20253 31789;20371 31584;21509 29508;21651 29573;22272 29913 32;22525 29452 32;21743 29024;21891 28782;22783 27084;22885 27135;23585 27518 32;23838 27057 32;23034 26617;23107 26505;23661 25494;24702 26064;25459 24683;25532 24551;25722 24204 32;24885 23746;24993 23540;26015 21594;26158 21675;26851 22054 32;27104 21593 32;26180 21087;26342 20799;27359 18989;27490 19061;28283 19495 32;28536 19034;28787 18577;30872 18148;31977 18669;31083 20566;30310 20140 32;30057 20601;29897 20882;29031 22424;28943 22584;28778 22885 32;29544 23305;29356 23599;28334 25321;28189 25253;27551 24904 32;27298 25365 32;28072 25789;27913 26014;26538 28306;25881 27965;25744 27890 32;25491 28351 32;26221 28760;26080 29024;25162 30590;24379 30176;24378 30175 32;24125 30636 32;24047 30786;23179 32258;23085 32428;22926 32719 32;23706 33146;22856 34666;22089 34246 32;21836 34707 32;22617 35135;21727 36761;20945 36333 32;20692 36794 32;21474 37222;19986 39969;19346 39618 32;19093 40079;17721 42356 18; +16117 39536;16335 39100;17113 39489 1;16997 39720;16715 39808;16484 39693;16117 39536 18; +14008 41476;14939 41877;15377 40995;16124 39545;16556 38675;17655 36368;17585 36316;17838 35827;17951 35897;19347 33376;19269 33332;19214 33302 32;19467 32841 32;20391 33347 32;20381 33364;20700 32774;20578 32706;19780 32269 32;20076 31729 32;20234 31816;21502 29503;21449 29462;21348 29407 32;21601 28946 32;21768 29037;22770 27072;22661 27012 32;22914 26551 32;23028 26613;23648 25499;23002 25141;20438 29529;19068 31946;18196 31492;17838 32147;18645 32657;14008 41476 18; +14022 41454;14414 40791;14307 40727;11980 39455 32;12093 39249;11422 38968;11125 39055;10611 39980;9677 39483;9695 39343;8630 38837;8591 38896;8449 39163 32;8081 38968 32;8267 38617;7797 38397 32;7621 38728 32;5478 37590 32;5654 37259 32;5664 37265;6162 36342;5847 36176;5316 37170;2367 36668;2572 35417;1761 35291;1500 37304;1320 38549;2166 38662;2323 37240;3021 37633;3078 37533 1;3139 37468;3212 37431;3305 37449 1;3627 37510;3808 37545;4130 37606 1;4332 37644;4430 37723;4609 37824 1;4847 37958;4829 38279;4697 38518;5124 38810;5181 38669;5392 38286 32;8309 39895 32;8048 40368 32;8526 40634;8571 40524;8776 40153 32;10305 40996 32;10044 41469 32;9799 41855;10471 42247;11640 40154;14022 41454 18; +15206 39226;14701 40231;12270 38905;12067 39275;11363 39010;10093 38350;9544 39164;8242 38424;8280 38336;8712 37522;8294 37300;7835 38166;7804 38171;6737 37610;7094 36894;6169 36389;5861 36179;5573 36048;5159 36856;2984 36488;3088 35793 1;3125 35546;2818 35459;2572 35417;2569 35435;1764 35315;1695 35281 1;1482 35258;1197 35282;1164 35475;1069 36115;-96 35919;23 35023;-446 34960;-563 35852;-2064 35609;-1969 35033 1;-1941 34863;-2070 34757;-2240 34729;-2544 34692;-2338 33125;-2199 33145;-670 33346 1;-337 33390;-107 33079;-53 32748;18 32149;1624 32268;1606 32404;1574 32636;1519 32995 1;1470 33314;1689 33613;2008 33661;2511 33738;2673 32684;3023 32650;5028 33002;4999 33114;4864 33919 1;4846 34026;4949 34133;5056 34151 1;5162 34169;5268 34099;5286 33993;5434 33117;5614 33070;6804 33255;7649 33681;7587 33800;7194 34636 1;7146 34738;7191 34860;7293 34908 1;7413 34964;7555 34913;7612 34793;8001 33967;8081 33915;9611 34748;9559 34846;9174 35666 1;9126 35768;9171 35890;9273 35938 1;9393 35994;9535 35943;9592 35823;9981 34997;10011 34981;11295 35647;11800 35912;12905 33863;13614 34258;11992 37281;12954 37805;15206 39226 18; +11118 39071;11371 39205;11518 38927;10014 38130;9521 39060;8251 38390;8712 37522;8294 37300;7836 38163;6759 37589 16; +10806 39671;11072 39819;10735 40425;9234 39590;9400 39292;8632 38864;8449 39192;8047 38968;8215 38667;7784 38426;7608 38740;5525 37573;5668 37318; +-21488 20374;-23910 20047 32;-23601 17763 1;-23453 16672;-22327 16155;-21226 16160;-19673 16172 32;-19863 17543 16; +-20576 18146;-18895 18374; +-19721 18127;-19459 16196; +-19063 17068 32;-18104 17198 32;-18038 16710 32;-18997 16580 32;-19063 17068 50; +-19283 16500;-19027 16535;-19131 17301;-19393 17265; +-19354 16517 32;-19055 16558 32;-19153 17272 32;-19452 17231 32;-19354 16517 50; +-20591 18325;-20534 17906; +-18916 18553;-18859 18134; +-19258 16162;-14595 16174 32;-14810 17869; +-15272 16732; +-14740 17837;-15209 21217;-16122 21550;-16159 21772; +-16322 21740;-16734 24798; +-16471 21732 1;-16404 21204;-16120 20956;-16088 20425 1;-16038 19598;-16330 19143;-16310 18315;-18309 18216;-18383 18648;-19198 24496;-16892 24808;-16471 21732 18; +-20778 36401 32;-23598 36028 32;-21252 18274 32;-18431 18647 32;-18454 18820;-18136 18862 32;-18263 19829 32;-18582 19787;-18721 20839;-18402 20881 32;-18604 22419 32;-18924 22377;-19067 23455;-18746 23497 32;-18873 24464 32;-19194 24422;-19228 24677;-18907 24719 32;-19034 25686 32;-19356 25643;-19495 26695;-19173 26738 32;-19375 28276 32;-19698 28233;-19841 29311;-19517 29354 32;-19644 30321 32;-19969 30278;-20006 30561;-19681 30604 32;-19808 31571 32;-20134 31528;-20273 32580;-19947 32623 32;-20149 34161 32;-20476 34118;-20619 35196;-20291 35239 32;-20418 36206 32;-20746 36163;-20778 36401 50; +-24341 32159;-24276 31678 32;-23700 27422; +-19212 24546;-13733 25266; +-16161 21819;-16492 21767;-16462 21672 1;-16382 21184;-16119 20935;-16088 20425 1;-16038 19598;-16330 19143;-16310 18315;-18309 18216;-18381 18639;-18909 18574;-18900 18432;-18859 18134;-19528 18077;-19406 17266;-19135 17318;-19092 17091;-18933 17086;-18104 17198 32;-18038 16710 32;-18997 16580 32;-19083 16515;-19284 16498;-19231 16201;-14678 16219;-14739 17868;-14758 17970;-15209 21217;-16122 21550;-16128 21586;-16161 21819 18; +-18393 18616;-18366 18552;-18309 18216;-16310 18315 1;-16330 19143;-16038 19598;-16088 20425 1;-16120 20953;-16401 21201;-16470 21723; +-14205 29164 1;-14636 28978;-14701 28506;-14637 28041 1;-14548 27396;-14543 27027;-14415 26388 1;-14333 25977;-14034 25818;-13995 25401;-13724 25426;-14205 29164 18; +-14266 29470 1;-14608 29576;-14771 29877;-14771 30235 1;-14771 30493;-14620 30613;-14475 30827;-14266 29470 18; +-17473 53801 1;-17810 53580;-17929 53202;-17897 52801;-15147 32020 1;-15106 31711;-14847 31471;-14537 31505;-17473 53801 18; +-14686 30514;-14695 30519 1;-14741 30438;-14771 30350;-14771 30235 1;-14771 29995;-14698 29780;-14546 29633; +-14476 28953;-14480 28955 1;-14662 28722;-14684 28380;-14637 28041 1;-14548 27396;-14543 27027;-14415 26388 1;-14358 26104;-14198 25940;-14091 25728; +-33774 13716;-37719 13698 1;-37711 13021;-37614 12645;-37483 11980 1;-37408 11596;-37160 11127;-36769 11124 1;-35614 11116;-34913 11107;-33758 11107;-33774 13716 18;-34709 12742 32;-34706 12049 32;-35396 12046 32;-35399 12739 32;-34709 12742 50; +-13205 21917 32;-14426 21755 32;-14863 25056 32;-13642 25218 32;-13205 21917 50; +-12560 16334;-13579 16365;-13710 17307; +-14393 21751;-13799 17276; +-13570 25291;-14183 29910 32;-14861 35024 16; +-13124 21925;-13570 25291 16; +-14388 21755;-13216 21915;-12513 16450;-13673 16499;-14388 21755 18; +-20236 15666;-20219 16111;-19669 16190;-19268 16277;-19207 16172;-14584 16172;-14749 17882;-14758 17968;-14758 17970;-15209 21217;-16122 21550;-16128 21586;-16161 21819;-16187 21815;-16572 24747;-14819 25000;-13659 16192;-12511 16216;-12400 15688;-20236 15666 18; +-12360 16163;-13124 21925 16; +-1093 23897;-427 23981;-761 26630;689 26813;756 26286;-277 26155;-16 24099;1038 24233;1815 18125;2204 18174;2387 16720 1;185 16681;-1050 16770;-3252 16818 1;-3465 16823;-3679 16945;-3679 17158 1;-3679 17386;-3679 17515;-3679 17743 1;-3679 17961;-3432 18019;-3217 18057 16; +-923 22688 32;363 22852 32;972 18064 32;-314 17900 32;-923 22688 50; +-2995 17564 1;-2963 17358;-2847 17306;-2638 17302 1;-1551 17280;-1003 17299;84 17310 1;494 17314;872 17655;816 18061; +-3495 20628 32;-3254 18732 32;-4354 18592 32;-4364 20517 32;-3495 20628 50; +-1249 26591;-1009 24704; +-1954 26449 32;-2063 27309 32;-3917 27074 32;-3808 26214 32;-1954 26449 50; +-6289 26728 1;-6254 26482;-6089 26235;-5844 26274 1;-5709 26296;-5617 26358;-5565 26484;-4510 26632;-4553 26963; +-6289 26728 1;-6254 26482;-6089 26235;-5844 26274 1;-5709 26296;-5617 26358;-5565 26484;-4510 26632;-4553 26963;-6289 26728 18; +-3594 21735;-5281 23054;-5570 25479;-5103 26076;-4235 26194 16; +-5833 22814;-5690 23016;-5701 23106;-5962 25264;-7975 25046 16; +-3594 21735;-5281 23054;-5570 25479;-5103 26076;-4235 26194;-3594 21735 18; +-8163 25054;-7827 22534; +-8264 24198 1;-8597 24088;-8820 23838;-8790 23575 1;-8760 23305;-8474 23108;-8114 23082 16; +-7686 23133 1;-7346 23240;-7117 23494;-7147 23760 1;-7178 24036;-7478 24237;-7852 24254 16; +-8798 23544 1;-8739 23133;-8321 22852;-7863 22917 1;-7406 22982;-7082 23368;-7140 23780 1;-7199 24191;-7617 24472;-8075 24407 1;-8533 24342;-8856 23956;-8798 23544 18; +-9462 23452 1;-9377 22834;-8643 22425;-7821 22537 1;-6999 22648;-6400 23239;-6485 23858 1;-6569 24476;-7304 24885;-8126 24773 1;-8948 24660;-9547 24069;-9462 23452 18; +-8359 24996;-9556 24843;-9836 24477;-10386 24898 1;-10426 25195;-10562 25391;-10831 25523;-10196 26220; +-12913 24671 1;-12853 24322;-12473 24099;-12066 24169 1;-11659 24241;-11380 24581;-11441 24930 1;-11501 25279;-11881 25502;-12288 25432 1;-12695 25360;-12974 25019;-12913 24671 18; +-12197 24384; +-11686 24623; +-12746 24960 1;-12609 25141;-12491 25255;-12266 25288 1;-12072 25317;-11899 25278;-11738 25165; +-12913 24671 1;-12853 24322;-12473 24099;-12066 24169 1;-11659 24241;-11380 24581;-11441 24930 1;-11501 25279;-11881 25502;-12288 25432 1;-12695 25360;-12974 25019;-12913 24671 18; +-10759 23249; +-11405 23755;-11283 22560 1;-11269 22427;-11151 22330;-11018 22344 1;-10753 22371;-10605 22387;-10340 22415 1;-10205 22429;-10108 22550;-10122 22685 1;-10172 23160;-10199 23427;-10248 23902 1;-10263 24050;-10395 24157;-10543 24142 1;-10809 24115;-10959 24100;-11225 24073 1;-11337 24062;-11418 23962;-11407 23850;-11405 23755 18; +-4485 20695 1;-4478 21501;-4860 22059;-5584 22413; +-12912 21079;-11290 21279;-11320 21519 1;-11334 21634;-11274 21758;-11159 21768 1;-10163 21855;-9602 21647;-8603 21611 1;-7987 21589;-7653 21756;-7041 21829 1;-6547 21887;-5956 21861;-5652 22235 16; +-10483 18366;-12629 18356; +-4516 20660;-4547 21234 1;-4675 21731;-4998 22104;-5500 22370;-5688 22194 1;-6001 21863;-6566 21885;-7041 21829 1;-7653 21756;-7987 21589;-8603 21611 1;-9602 21647;-10163 21855;-11159 21768 1;-11274 21758;-11334 21634;-11320 21519;-11290 21279;-12912 21079;-12568 18436;-10509 18488;-10483 20634;-4516 20660 18; +-13415 24086;-12712 23926;-12391 21125;-13033 21051;-13415 24086 18; +-10823 25530;-11793 25369 1;-11936 25437;-12110 25463;-12288 25432 1;-12695 25360;-12974 25019;-12913 24671 1;-12888 24528;-12841 24418;-12729 24328;-12713 23926;-13285 24056;-13489 25830;-11430 26092;-11333 26064;-10210 26212;-10823 25530 18; +-7129 26640;-7744 25849;-8763 25721;-9540 26325; +-7129 26640;-7744 25849;-8763 25721;-9540 26325;-7129 26640 18; +-3476 20640;-4365 20517;-4488 20842 1;-4525 21539;-4874 22038;-5500 22370;-5512 22528;-5833 22814;-5690 23016;-5701 23106;-5962 25264;-7825 25062;-7985 25077;-8384 25020;-9535 24846;-9556 24843;-9836 24477;-10386 24898 1;-10426 25195;-10562 25391;-10831 25523;-10196 26220;-9808 25980;-9507 26294;-8763 25721;-7744 25849;-7129 26640;-6772 26447;-6511 26726;-6301 26731;-6266 26624 1;-6205 26418;-6054 26241;-5844 26274 1;-5709 26296;-5617 26358;-5565 26484;-4510 26632;-4553 26963;-3908 27070;-3810 26231;-4344 26179;-5103 26076;-5570 25479;-5281 23054;-3649 21778;-3476 20640 18; +-10828 25526;-10942 25510;-11755 25375;-11751 25347 1;-11589 25257;-11472 25111;-11441 24930 1;-11380 24581;-11659 24241;-12066 24169 1;-12319 24125;-12562 24195;-12724 24338;-12724 24193;-12713 23926;-12727 23929;-12705 23865;-12391 21125;-11290 21279;-11320 21519 1;-11334 21634;-11274 21758;-11159 21768 1;-10163 21855;-9602 21647;-8603 21611 1;-7987 21589;-7653 21756;-7041 21829 1;-6578 21883;-6030 21864;-5713 22169;-5634 22206;-5486 22545;-5803 22856;-5690 23016;-5701 23106;-5962 25264;-7975 25046;-9561 24836;-9836 24477;-10386 24898 1;-10426 25195;-10562 25391;-10831 25523;-10828 25526 18;-6485 23858 1;-6400 23239;-6999 22648;-7821 22537 1;-8643 22425;-9377 22834;-9462 23452 1;-9547 24069;-8948 24660;-8126 24773 1;-7304 24885;-6569 24476;-6485 23858 18;-10248 23902 1;-10199 23427;-10172 23160;-10122 22685 1;-10108 22550;-10205 22429;-10340 22415 1;-10605 22387;-10753 22371;-11018 22344 1;-11151 22330;-11269 22427;-11283 22560;-11405 23755;-11407 23850 1;-11418 23962;-11337 24062;-11225 24073 1;-10959 24100;-10809 24115;-10543 24142 1;-10395 24157;-10263 24050;-10248 23902 18; +-4309 17838;-4305 16940;-4650 16947;-5531 17838; +-6190 17881;-5548 17239 1;-5677 17110;-5779 16959;-5962 16961 1;-6166 16963;-6264 17183;-6264 17387;-7430 17387;-7430 17134 1;-7430 17038;-7507 16961;-7603 16961 1;-7721 16961;-7769 17047;-7855 17128;-8634 17861; +-9324 17880;-9323 17812;-8723 17259 1;-8877 17100;-8992 16928;-9213 16936 1;-9423 16944;-9496 17183;-9496 17393;-10193 17393;-10193 16739;-10452 16739;-10570 17060 1;-10651 17279;-10892 17282;-11125 17282; +-11081 16881 1;-10981 16886;-10879 16833;-10872 16733 1;-10853 16486;-10953 16220;-11199 16190 1;-11703 16128;-12054 16158;-12562 16171; +-11081 16881 1;-10981 16886;-10879 16833;-10872 16733 1;-10853 16486;-10953 16220;-11199 16190 1;-11703 16128;-12054 16158;-12562 16171;-12361 16473;-11154 16592;-11081 16881 18; +-10451 16310;-10926 15680;-10155 15693;-9670 16316;-10451 16310 18; +-9680 16316;2231 16207; +-6776 16278;-6410 15851;-5808 15855;-5485 16278; +-2810 16247;-2444 15820;-1842 15824;-1519 16247; +920 16210;1286 15783;1888 15787;2211 16210; +-6776 16278;-6410 15851;-5808 15855;-5485 16278;-6776 16278 18; +-2810 16247;-2444 15820;-1842 15824;-1519 16247;-2810 16247 18; +920 16210;1286 15783;1888 15787;2211 16210;920 16210 18; +-10451 16310;-10926 15680;-10155 15693;-9667 16322;-10451 16310 18; +1594 15989; +-2157 15995; +-6129 16040; +-10299 15996; +-12496 18271;-10510 18284;-10485 17858;-9289 17852;-9263 17754;-8726 17259 1;-8880 17100;-8992 16928;-9213 16936 1;-9423 16944;-9496 17183;-9496 17393;-10193 17393;-10193 16739;-10452 16739;-10570 17060 1;-10651 17279;-10892 17282;-11125 17282;-11165 17458;-11172 17568 32;-12368 17497 32;-12496 18271 18; +-6190 17881;-5548 17239 1;-5677 17110;-5779 16959;-5962 16961 1;-6166 16963;-6264 17183;-6264 17387;-7430 17387;-7430 17134 1;-7430 17038;-7507 16961;-7603 16961 1;-7721 16961;-7769 17047;-7855 17128;-8634 17861;-6190 17881 18; +-4309 17838;-4305 16940;-4650 16947;-5531 17838;-4309 17838 18; +-8831 17613;-8315 17127; +-8776 17671;-8260 17185; +-8933 17450;-8459 17014; +-5801 17491;-5328 17050; +-5706 17690;-5190 17204; +-4316 17833;-3674 17836; +-4311 17397;-3687 17399; +-4309 16939;-3583 16942; +-12473 15690;-12333 15682;-10916 15685;-10795 15853;-10451 16310;-9670 16316;-9112 16311;2309 16206;2387 16720 1;185 16681;-1050 16770;-3252 16818 1;-3465 16823;-3679 16945;-3679 17158 1;-3679 17386;-3679 17515;-3679 17743 1;-3679 17961;-3432 18019;-3217 18057;-3266 18731;-4328 18595;-4309 17732;-4305 16940;-4650 16947;-5522 17829;-5843 17547;-5743 17437;-5533 17241;-5675 17106 1;-5756 17025;-5842 16960;-5962 16961 1;-6166 16963;-6264 17183;-6264 17387;-7430 17387;-7430 17134 1;-7430 17038;-7507 16961;-7603 16961 1;-7721 16961;-7769 17047;-7855 17128;-8629 17856;-8976 17522;-8831 17359;-8723 17259 1;-8733 17248;-8744 17237;-8754 17226;-8796 17184 1;-8918 17050;-9027 16929;-9213 16936 1;-9423 16944;-9496 17183;-9496 17393;-10193 17393;-10193 16739;-10452 16739;-10570 17060 1;-10651 17278;-10890 17282;-11123 17282;-11081 16880;-11081 16881 1;-10981 16886;-10879 16833;-10872 16733 1;-10853 16486;-10953 16220;-11199 16190 1;-11703 16128;-12054 16158;-12562 16171;-12473 15690 18; +-437 24057;-16 24099;1038 24233;1815 18125;2204 18174;2387 16720 1;185 16681;-1050 16770;-3252 16818 1;-3465 16823;-3679 16945;-3679 17158 1;-3679 17386;-3679 17515;-3679 17743 1;-3679 17961;-3432 18019;-3217 18057;-3067 17645;870 18181;298 22745;-928 22597;-1093 23897;-427 23981;-437 24057 18; +-16 24099;-437 24057;-761 26630;689 26813;756 26286;-277 26155;-16 24099 18; +3866 16732 32;3439 16673 32;3223 18249 32;3650 18308 32;3866 16732 50; +5383 27283;4975 27228; +4544 27166;3540 27030;4808 17640; +3519 27113;3263 27101;2320 26974;2388 26471;3449 26614;3721 24597;2659 24454;2723 23981;3786 24125;4109 21744;3012 21595;3086 21048;4178 21196;4514 18720 1;4337 18696;4237 18682;4060 18658 1;3812 18624;3638 18397;3672 18149;3869 16716;4439 16734;4408 17018;4778 17647;3519 27113 18; +-1143 31999;17 32122;17 32178;1621 32314;1633 32264;2731 32425;2981 30545;2877 30539;1858 30401;1923 29919;3020 30067;3103 29612;3117 29485;3200 28846;2099 28680;2212 27782;4842 28153;5807 28286;5929 27553;5380 27404;4972 27323;4998 27106;4550 27045;4520 27257;4247 27234;2320 26974;2388 26471;3449 26614;3721 24597;2659 24454;2723 23981;3786 24125;4109 21744;3012 21595;3086 21048;4178 21196;4514 18720 1;4337 18696;4237 18682;4060 18658 1;3812 18624;3638 18397;3672 18149;3869 16716;2386 16726;2204 18174;1815 18125;1038 24233;-16 24099;-277 26155;756 26286;689 26813;-761 26630;-427 23981;-1093 23897;-1439 26543;-1957 26476;-2055 27277;-1446 27358;-1274 27379;-1328 27817;-892 27871;-814 27236;628 27412;561 27960;-620 27815;-832 29543;327 29685;262 30214;-898 30072;-1143 31999 18; +20293 29461;22893 25029; +23697 23687;24292 22575 1;25154 20964;25579 20031;26412 18405 1;26481 18270;26459 18100;26381 17967 1;26308 17843;26186 17751;26045 17751;25496 17749; +22638 25100;21893 24684; +23628 23429;22855 22998; +19436 29034 32;20221 29471 32;22623 25161 32;21838 24724 32;19436 29034 50; +18662 17992 32;18662 17582;19024 17581;19026 17986 48; +19384 17986 32;19386 17581;25150 17584;25151 17950 16; +20763 18521;19615 18529; +20181 18361;20176 17593;19400 17589;19387 18003;19600 18007;19618 18356;20181 18361 18; +18663 18025 32;19038 18019 32;19031 17580 32;18656 17586 32;18663 18025 50; +23562 18465;24944 18625;24969 18008;25147 17973;25151 17579;20176 17589;20176 17650;20181 18361;20170 18361;23507 18378;23562 18465 18; +22860 22956;23773 23419;26438 18231;26253 17812;25686 17830;25661 18571;24970 18626;24892 18619;23562 18465;23536 18583;23536 21082 1;23536 21579;23463 21851;23218 22284;23193 22329;22860 22956 18; +18947 24437;18171 24011;19504 21440;20333 21869; +18163 25930;17281 25442;15743 28155;16663 28655; +14320 28409 1;13999 28965;13463 29069;12822 29084;13772 29559;13609 29885;13949 30055;14727 28644;14320 28409 18; +12593 24011; +11606 26552; +14898 24038; +20786 20812;18674 19895;18666 19032 16; +19648 19018;19662 19920;20789 20369; +19654 18679;19654 19907;20826 20381;20789 18673;19654 18679 18; +20783 20813;20802 20381;20721 20339;19654 19907;19654 18969;18661 19000;18667 19129;18674 19895;20783 20813 18; +18297 20583; +15186 18101;15181 17581;12326 17568;12330 18081 16; +17446 20755;17939 20751;17937 20666 1;17934 20651;17933 20635;17933 20619;17899 17581;16014 17579;16019 18095;17381 18103;17446 20755 18; +15186 18101;15181 17581;12326 17568;12330 18081;15186 18101 18; +18947 24437;18171 24011;19504 21440;20333 21869;18947 24437 18; +16714 24599; +18163 25940 32;17257 25448 32;16344 27128 32;17250 27620 32;18163 25940 50; +17262 27618 32;16319 27105 32;15748 28155 32;16691 28668 32;17262 27618 50; +16276 29368;15463 28926;15036 29712;15592 30014; +15810 30174;15573 30045; +15856 30187 32;16267 29433 32;15562 29049 32;15151 29803 32;15856 30187 50; +12875 29104 32;12555 29699 32;11958 29378 32;12278 28783 32;12875 29104 50; +12875 29104 32;12555 29699 32;11958 29378 32;12278 28783 32;12875 29104 50; +12931 27608; +12321 28367 1;13033 28747;14002 28430;14318 27687;13856 27442;14222 26788;13243 26242;12882 26888;12205 26509 1;11869 27110;11622 27994;12321 28367 18; +14009 31894 32;14483 31011 32;13048 30241 32;12574 31124 32;14009 31894 50; +11895 30765;12060 30457;11008 29893;11203 29530;9054 28376 1;8861 28273;8620 28345;8516 28538;8334 28440;8278 28410 1;8126 28329;7951 28463;7892 28625; +11895 30765;12060 30457;11008 29893;11203 29530;9054 28376 1;8861 28273;8620 28345;8516 28538;8334 28440;8278 28410 1;8126 28329;7951 28463;7892 28625;11895 30765 18; +5921 27550 32;7268 27732 32;7173 28435 32;5826 28253 32;5921 27550 50; +8009 27844 32;8473 27907 32;8760 25778 32;8296 25715 32;8009 27844 50; +8009 27844 32;8469 27906 32;8756 25777 32;8296 25715 32;8009 27844 50; +8426 24754 32;8886 24816 32;9395 21039 32;8935 20977 32;8426 24754 50; +8426 24754 32;8890 24817 32;9400 21046 32;8936 20983 32;8426 24754 50; +10162 20625;9002 20398;9203 18950;10149 18958;10162 20625 18; +9679 25033;9806 23802;11074 23933;10942 25210;10531 25168; +10282 20755;10289 21612;11811 21600; +11400 21721;11269 22707;11211 22707;11134 22697;10085 21798;11400 21721 18; +11392 21771;12657 22705;12575 22727 1;12257 22771;11930 22800;11528 22748;11256 22713;11392 21771 18; +14842 22746;15156 21709;11381 21790;11483 21838;12657 22705;12729 22705 1;13085 22651;13443 22590;13898 22613 1;14168 22626;14415 22658;14644 22711;14842 22746 18; +17423 20723 32;17465 21700 32;10402 21792 32;10310 20777 32;17423 20723 50; +11817 21781;17456 21696;17443 20772; +17455 20740;17944 20723;17975 20783 1;18035 20895;18154 20970;18291 20969 1;18503 20967;18672 20794;18670 20582;18797 20611;18910 21060;17592 23541;17372 23950;17318 23923 1;17248 23865;17169 23816;17081 23779 1;16820 23670;16537 23695;16308 23824;16261 23759 1;15834 23237;15382 22924;14826 22759;14857 22698;15153 21719;15221 21730;17456 21696;17447 21052;17455 20740 18; +16019 18095;16014 17579;17899 17581;17933 20619 1;17935 20815;18095 20971;18291 20969 1;18503 20967;18672 20794;18670 20582;18797 20611;18910 21060;17366 23965 1;17284 23889;17190 23825;17081 23779 1;16820 23670;16537 23695;16309 23822 1;15658 23002;14943 22665;13898 22613 1;12972 22567;12448 22866;11528 22748;11134 22697;10109 21818 16; +15942 24211 1;15741 24698;15952 25214;16400 25402 1;16424 25412;16447 25421;16474 25432;15152 27818;14760 27586 1;15087 26953;14723 26493;14189 26032 1;13803 25698;13361 25729;12889 25924 1;12776 25971;12689 26056;12583 25994 1;12356 25861;12219 25754;12112 25514 1;11885 25003;11862 24568;12139 24083 1;12598 23279;13457 23008;14354 23237 1;15051 23415;15427 23689;15942 24211 18; +15942 24211 1;15741 24698;15952 25214;16400 25402 1;16424 25412;16447 25421;16474 25432;15152 27818;14760 27586 1;15087 26953;14723 26493;14189 26032 1;13803 25698;13361 25729;12889 25924 1;12776 25971;12689 26056;12583 25994 1;12356 25861;12219 25754;12112 25514 1;11885 25003;11862 24568;12139 24083 1;12598 23279;13457 23008;14354 23237 1;15051 23415;15427 23689;15942 24211 18; +11042 25327;10961 26462;10431 26159;10536 25265;11042 25327 18; +10961 26462;10937 27498;11668 28336;11397 28807;9233 27638;9431 26021;10417 26143;10961 26462 18; +10551 25002;10411 26141;9431 26021;9233 27638;11397 28807;11667 28335;10951 27514;10937 27498;10961 26462;11028 25341 16; +14320 28409 1;13999 28965;13463 29069;12822 29084;13772 29559;13609 29885;13949 30055;14727 28644;14320 28409 18; +14009 31894 32;14483 31011 32;13048 30241 32;12574 31124 32;14009 31894 50; +12321 28367 1;13033 28747;14002 28430;14318 27687;13856 27442;14222 26788;13243 26242;12882 26888;12205 26509 1;11869 27110;11622 27994;12321 28367 18; +13787 32280;14511 32673;15479 30828;15780 30963;16134 30352;15091 29776;15506 29039;16260 29401;16669 28666;16556 28597;15743 28155;17281 25442;18163 25930;18951 24441;18867 24393;18171 24011;19504 21440;20323 21864;20445 21708 1;20639 21320;20735 21058;20780 20806;20629 20744;18674 19895;18666 19032;18648 17576;17902 17588;17899 17625;17933 20619 1;17933 20635;17934 20651;17937 20666;17939 20751;18036 20865 1;18101 20930;18191 20970;18291 20969 1;18503 20967;18672 20794;18670 20582;18797 20611;18910 21060;17592 23541;17372 23950;17318 23923 1;17248 23865;17169 23816;17081 23779 1;16820 23670;16537 23695;16308 23824;16261 23759 1;15865 23275;15447 22970;14944 22797;14593 22701 1;14378 22654;14147 22625;13898 22613 1;13122 22574;12629 22778;11948 22776;12573 23562 1;13048 23172;13691 23068;14354 23237 1;15051 23415;15427 23689;15942 24211 1;15741 24698;15952 25214;16400 25402 1;16424 25412;16447 25421;16474 25432;15991 26304;15914 26442;15152 27818;14760 27586 1;15087 26953;14723 26493;14189 26032 1;13803 25698;13361 25729;12889 25924 1;12776 25971;12689 26056;12583 25994 1;12525 25960;12473 25928;12426 25895;12205 26509;12882 26888;13243 26242;14222 26788;13856 27442;14318 27687 1;14021 28385;13149 28707;12454 28429;12285 28787;12875 29104 32;12949 29080 1;13534 29053;14020 28928;14320 28409;14727 28644;13949 30055;13609 29885;13048 30241 32;14483 31011 32;14014 31885;13787 32280 18; +12577 31116;13053 30269;13616 29899;13773 29563;12870 29118;12560 29702;11962 29380;12271 28788;12302 28788;12480 28455;12382 28398 1;12362 28388;12341 28378;12321 28367 1;11622 27994;11869 27110;12205 26509;12443 25925;12348 25835 1;12250 25753;12176 25657;12112 25514 1;11885 25003;11862 24568;12139 24083 1;12267 23858;12427 23675;12610 23532;12082 22774 1;12038 22775;11993 22776;11948 22776;11707 22766 1;11649 22762;11589 22756;11528 22748;11256 22713;11129 22699;10105 21798;9711 23692;11092 23951;11018 25357;10926 27516;11672 28343;11419 28799;9243 27644;9233 27638;9431 26021;10417 26143;10543 25018;8421 24759;8285 25721;8754 25777;8470 27911;7261 27750;7175 28435;7884 28620;7938 28535 1;8018 28419;8156 28345;8278 28410;8334 28440;8516 28538 1;8620 28345;8861 28273;9054 28376;11203 29530;11008 29893;12060 30457;11895 30765;12577 31116 18; +8884 24850;9625 24955;9766 23654;10124 21822;10186 20631;9008 20390;8916 20976;9088 21004;9400 21046 32;8884 24850 18; +21973 24564;23613 25557;23667 25497;24702 26064;25459 24683;25403 24629;24545 24159 32;24798 23698 32;24871 23738;24928 23665;24993 23540;26015 21594;25955 21563;25927 21548 32;26180 21087 32;26212 21030;26342 20799;27359 18989;27490 19061;28283 19495 32;28534 19037;28510 19020;27612 18528;27864 18069;26296 17859 1;26329 17891;26358 17927;26381 17967 1;26459 18100;26481 18270;26412 18405 1;25579 20031;25154 20964;24292 22575;23770 23551;22793 23066;21973 24564 18; +6666 17568;4933 17340;4951 17081;4766 17050;4797 16723;4853 16717;5197 16718;7695 17103;7598 17710;6666 17568 18; +9374 17957;9444 17373;8457 17221;8350 17822;9374 17957 18; +10094 18082;10143 17478;10208 17491;10684 17564;11491 17570;11497 18087;10094 18082 18; +10115 18078;10172 17485;10684 17564;11491 17570;11497 18087 16; +5363 27174;3666 26939;4960 17340;6666 17562;5363 27174 18; +4400 17008;4432 16721;3869 16716;3672 18149 1;3638 18397;3812 18624;4060 18658 1;4237 18682;4337 18696;4514 18720;4178 21196;3086 21048;3012 21595;4109 21744;3786 24125;2723 23981;2659 24454;3721 24597;3449 26614;2388 26471;2320 26974;4536 27273;4565 27044;4998 27104;4967 27327;5380 27387 16; +9359 17942;9430 17372;5197 16718;4795 16719;4758 17066 16; +-21096 15663;-21114 14223;-20744 14208;-20139 14209;363 14412 1;636 14412;920 14328;1180 14187;1879 15787;1286 15783;920 16210;876 16219;-1542 16241;-1562 16190;-1842 15824;-2444 15820;-2798 16233;-2875 16254;-5510 16278;-5557 16183;-5808 15855;-6410 15851;-6776 16278;-6976 16291;-9112 16311;-9670 16316;-9679 16316;-9755 16208;-10155 15693;-10884 15681;-19146 15669;-21096 15663 18; +28775 18579;37873 -467 1;38405 -1580;37788 -3004;36608 -3360 1;33415 -4324;31646 -4947;28413 -5769 1;27385 -6030;26816 -6207;25780 -6437;24571 -5425;23301 -5561 1;21105 -5796;19870 -5932;17663 -6017 1;14081 -6155;12069 -6110;8484 -6177 1;6663 -6211;5642 -6176;3821 -6165 1;3563 -5779;3568 -5531;3574 -5166 1;3616 -2764;3608 -1428;3636 974;3562 13963 1;3508 14853;2774 15794;1882 15788 16; +3821 -6165 1;4426 -7007;5113 -7451;6150 -7440;6830 -7440;8069 -6201 16; +7413 -6575; +6551 -6811; +5600 -6811; +4667 -6794; +31356 -3787 1;31428 -3631;31469 -3543;31542 -3387 1;31602 -3258;31792 -3278;31906 -3363 1;32071 -3486;32188 -3534;32313 -3697 1;32424 -3842;32461 -4105;32288 -4165 1;31990 -4267;31818 -4314;31511 -4386 1;31276 -4441;31254 -4007;31356 -3787 18; +30838 -3958; +31305 -4177 1;31286 -4050;31307 -3894;31356 -3787 1;31428 -3631;31469 -3543;31542 -3387 1;31602 -3258;31792 -3278;31906 -3363 1;32071 -3486;32188 -3534;32313 -3697 1;32331 -3721;32347 -3747;32361 -3776; +28208 -4099; +27388 -2773; +35045 -2925; +35211 -3231 1;35294 -3013;35206 -2644;34975 -2681 1;34268 -2795;33881 -2940;33222 -3222 1;32979 -3326;32906 -3761;33135 -3894;35211 -3231 18; +36188 -2864 1;35903 -2666;35626 -2287;35865 -2035 1;36059 -1831;36394 -1747;36580 -1957 1;36750 -2149;36936 -2269;36912 -2524;36188 -2864 18; +36668 -1425; +37311 -1354 1;37319 -1154;37347 -1024;37259 -844 1;37103 -523;37015 -344;36858 -23 1;36824 46;36767 98;36700 125 1;36632 153;36554 154;36482 120 1;36394 79;36335 3;36305 -86 1;36267 -194;36274 -322;36325 -429; +36464 795;35273 3243; +36180 1115 1;35919 960;35672 646;35859 406 1;35992 235;36038 31;36254 17;36655 122;36963 73;36593 875;36180 1115 18; +35294 1773; +31285 3008; +34382 3566; +33466 4875; +32707 4901; +34480 4917;33961 5981; +33584 6796;32893 8209; +33461 6099; +31795 10795 1;31483 10899;31163 11186;30962 10925 1;30787 10699;31154 10515;31338 10296 1;31625 9953;31645 9668;31863 9278 1;31982 9065;32400 9175;32473 9407;31795 10795 18; +28714 8871; +29573 10796; +30904 12302;30323 13504; +29732 13721; +29858 14781 1;29513 14660;29102 14382;28925 14702 1;28760 14999;28654 15319;28314 15322 1;26352 15337;25251 15278;23289 15278 1;23111 15278;23087 15501;23071 15679 1;23052 15890;23356 15896;23568 15897;29300 15924;29858 14781 18; +27625 16334; +25173 16343; +24327 16334; +20476 16271; +18551 16234; +10891 16382; +13395 16419; +14493 16382; +25699 8316 1;25118 7831;24504 7434;24549 6678 1;24614 5591;24896 5007;25265 3982;27053 3973;27056 4618; +26966 7357 1;27049 7617;27225 7801;27498 7811 1;28492 7849;29082 7750;30037 8029 1;30423 8142;30831 7785;30839 7383;26966 7357 18; +25692 8317;25710 4618;27056 4609;27053 3973;25265 3982 1;24896 5007;24614 5591;24549 6678 1;24506 7399;25017 7755;25568 8207;25692 8317 18; +23788 3555;22469 3141; +27486 1059 1;26763 778;26326 144;26431 -625 1;26542 -1441;26676 -1888;26893 -2683;27654 -5465 1;28421 -5273;29129 -5087;29811 -4899;29786 -2570 16; +24803 -223;24384 1199; +26437 -5744 32;26153 -4692 32;25649 -4828 32;25933 -5880 32;26437 -5744 50; +23793 3473;24037 2668;24009 2572 1;23843 2288;23622 2103;23706 1785;24159 -405;24054 -161 1;23777 362;23591 650;23426 1218 1;23286 1698;23171 1958;23069 2448 1;23003 2765;23002 2967;23095 3277;23793 3473 18; +25578 -4940;23223 3303; +23707 522 1;23917 -20;23684 -396;23820 -961;24675 -4843;24981 -5052;25469 -4939;24954 -2967;24126 60;23707 522 18; +24034 2677;24568 1228;24941 -180;26166 -4705;25641 -4843;24019 749;23644 2153;24034 2677 18; +23785 3482;24550 1230; +33176 221 1;33192 495;33172 859;32897 867 1;31292 915;30394 1012;28788 1041;28047 1115;27584 1118;27379 1014 1;26720 714;26331 105;26431 -625 1;26486 -1026;26546 -1339;26619 -1647 1;26635 -1715;26696 -1764;26764 -1750 1;27054 -1692;27453 -1732;27453 -1436;27480 90;33176 221 18; +34758 4719 1;33514 4180;32712 4039;31356 4030;28791 4004; +30692 -2508;30718 -4639 1;32502 -4140;34203 -3591;36436 -2827 1;37252 -2548;37682 -1370;37354 -697;34819 4554 16; +29017 3892;29007 1875; +28820 1903;29187 1901; +29224 4626;29226 4185; +31230 12063 1;30311 11646;29752 11281;28744 11261;27085 11242; +27059 9120;27676 9120;27676 10755;29071 10755;29071 11070; +25658 11917;25661 11444;27115 11452; +23058 11913;23082 7948;23725 3617; +24575 -5415;24810 -4952;25953 -5882 1;26121 -5842;26286 -5802;26450 -5762;24934 -153;24537 1268;23785 3482;23717 3677;23213 7229;23188 7402;23073 7887;23058 11913;25674 11964;25659 11829;25661 11444;26748 11450;27113 11421;29044 11116;29050 11008;29047 10755;27676 10755;27676 9120;27059 9120;27045 10971;25691 10950;25683 8342;25642 8268 1;25064 7786;24504 7431;24549 6678 1;24605 5738;24824 5174;25120 4376;25236 4063 1;25245 4037;25255 4009;25265 3982;27053 3973;27056 4618;29212 4647;29193 4259;29192 4165;28814 3806;27518 3802;27512 1118;27368 1009 1;26716 707;26332 101;26431 -625 1;26486 -1026;26546 -1339;26619 -1647 1;26623 -1664;26630 -1680;26639 -1694;26687 -1922 1;26747 -2153;26814 -2395;26893 -2683;27654 -5465;27886 -5407 1;28565 -5235;29198 -5068;29811 -4899;29786 -2570;30693 -2570;30718 -4639 1;32471 -4149;34143 -3610;36319 -2867;36490 -3396 1;33371 -4339;31606 -4957;28413 -5769 1;27385 -6030;26816 -6207;25780 -6437;24575 -5415 18; +36281 -2878;36438 -3415;36511 -3389 1;36543 -3379;36576 -3370;36608 -3360 1;37788 -3004;38405 -1580;37873 -467;30181 15636;29974 16069;28775 18579;27871 18096;26514 17930;26156 17720;25509 18024;25147 17973;25151 17579;22883 17584;22600 17583;19386 17581;19384 17986 32;19034 17990;19035 17829;19031 17580 32;18656 17586 32;18658 17693;17911 17681;17862 16724;28912 16793;31448 11539;31507 11416;34665 4874;34811 4520;34855 4502;34964 4254;37354 -697 1;37681 -1368;37255 -2541;36443 -2824;36281 -2878 18; +34665 4874;31280 11888 16; +28917 16821;22557 16753;22574 14598;22740 14596;27978 14622 32;27991 11978 32;25681 11967;25660 11836;25659 11829;25661 11444;26748 11450;27113 11421;28509 11201;31335 12145;28917 16821 18;29300 15924;29858 14781 1;29513 14660;29102 14382;28925 14702 1;28760 14999;28654 15319;28314 15322 1;26352 15337;25251 15278;23289 15278 1;23111 15278;23087 15501;23071 15679 1;23052 15890;23356 15896;23568 15897;29300 15924 18; +31396 12034;29071 11070;29071 10755;27676 10755;27676 9120;27263 9120;27053 9123;27016 8383;25692 8317;25696 7432;25672 7334;28966 7334;29064 4040;33271 4176;34900 4829;31396 12034 18;31795 10795;32473 9407 1;32400 9175;31982 9065;31863 9278 1;31645 9668;31625 9953;31338 10296 1;31154 10515;30787 10699;30962 10925 1;31163 11186;31483 10899;31795 10795 18;30839 7383;26966 7357 1;27049 7617;27225 7801;27498 7811 1;28492 7849;29082 7750;30037 8029 1;30423 8142;30831 7785;30839 7383 18; +31803 4718; +29743 3336; +34998 4817;32593 4089;29040 4003;28781 3139;28802 1041 1;30399 1011;31297 915;32897 867 1;33143 860;33185 567;33180 309;33074 203;27436 92;27449 -1484 1;27408 -1728;27037 -1695;26764 -1750 1;26734 -1756;26706 -1750;26682 -1736;26437 -1882;27522 -5719;37034 -2968;37799 -1389;37108 400;34998 4817 18;36180 1115;36593 875;36963 73;36655 122;36254 17 1;36038 31;35992 235;35859 406 1;35672 646;35919 960;36180 1115 18;36580 -1957 1;36750 -2149;36936 -2269;36912 -2524;36188 -2864 1;35903 -2666;35626 -2287;35865 -2035 1;36059 -1831;36394 -1747;36580 -1957 18;35211 -3231;33135 -3894 1;32906 -3761;32979 -3326;33222 -3222 1;33881 -2940;34268 -2795;34975 -2681 1;35206 -2644;35294 -3013;35211 -3231 18; +28745 18634;30882 18172;30962 17951;31452 16882 1;31526 16726;31687 16645;31844 16719;34511 11232;37376 5225;42560 -4791;42893 -6008;43005 -6226;41421 -7040;41362 -6941;41237 -6696 1;41047 -6325;40851 -5941;40647 -5541 1;40215 -4692;38798 -4498;37573 -4879 1;34307 -5894;32347 -6556;29328 -7341;29054 -7412 1;28774 -7484;28485 -7557;28185 -7632 1;27487 -7806;26861 -7951;26271 -8073;25780 -6437 1;26816 -6207;27385 -6030;28413 -5769 1;31646 -4947;33415 -4324;36608 -3360 1;37788 -3004;38405 -1580;37873 -467;32052 11719;28745 18634 18; +17093 14358 32;17340 12322 32;16235 12188 32;15988 14224 32;17093 14358 50; +17835 14424;18181 11569;18871 11179;20413 11389;20142 13967;19130 14578;17835 14424 18; +17155 10696;17691 10758;18956 10018;19073 8852;18592 7994;17408 8630;17155 10696 18; +18202 9313; +19194 12847; +16699 13257; +3821 -6165 1;4426 -7007;5113 -7451;6150 -7440;6830 -7440;8069 -6201;3821 -6165 18; +9218 -3440 32;10927 -3466 32;10916 -4171 32;9207 -4145 32;9218 -3440 50; +9218 -3440 32;10927 -3466 32;10916 -4171 32;9207 -4145 32;9218 -3440 50; +10046 -3806; +18595 7999;19601 7435;19937 4130;21167 4192;22467 3197; +18125 6237;18297 4358;17183 4259;17219 3859;19478 4062;19245 6655;16683 8161;16334 11084;16003 11232;15135 11130;15766 6011;18125 6237 18; +10291 13223;9603 13141; +8409 12286;8323 12964;8939 13047; +9625 13048;9402 13021;9278 14045;13167 14516;14217 13955;14549 11485;13466 11339; +5425 9904;5476 8995; +5250 9963;5304 8986; +5603 9919;5653 9009; +4440 9110; +4467 7984; +4755 11011; +4467 11753; +8021 13453; +9739 13697; +4008 12467;5386 12066;5464 12083;6487 12142;6462 12587 32;7661 12656 32;7686 12210;8363 12249;8377 12540;8323 12964;8879 13039;8888 13209;8792 13970;4035 13359;4008 12467 18; +9045 16387;9007 15203; +14607 15720;12302 15761; +12311 15757;4051 15688; +17824 16709 1;17589 15908;17223 15208;16393 15122 1;15928 15074;15665 15072;15198 15069 1;14834 15067;14673 14729;14570 14380; +14588 15714;16974 15709; +31123 12212;28912 16793;16570 16718;16577 16178;15850 16174;15845 16706;12737 16706;12740 16178;12013 16174;12011 16694;10716 16680;5324 15843;4025 15843;4033 13922;13230 15135;14747 14280;15087 11568;16082 11707;17111 11140;17155 10709 16; +17918 15777 1;19494 15810;20377 15709;21953 15709 1;22027 15709;22069 15613;22045 15542 1;21957 15283;21883 15147;21792 14889 1;21735 14726;21566 14726;21397 14691 1;21251 14660;21169 14538;21163 14389 1;21158 14257;21154 14182;21144 14050 1;21136 13935;20994 13927;20879 13920 1;20636 13905;20499 13892;20256 13896 1;20210 13897;20160 13906;20151 13951 1;20096 14242;20108 14411;20065 14704 1;20032 14931;19924 15155;19695 15160 1;18955 15175;18532 15127;17801 15246 1;17665 15268;17599 15429;17641 15561 1;17682 15692;17781 15774;17918 15777 18; +16826 14605; +15636 14432; +15839 12439; +17093 14358 1;17177 14541;17179 14803;16987 14864;15278 14672;15562 11927;15785 11666;16082 11707;16320 11576;16327 11588 1;16555 11754;16703 11951;16660 12230;16245 12184;15993 14241;17093 14358 18; +17835 14424;18181 11569;18871 11179;20413 11389;20142 13967;19130 14578;17835 14424 18; +17093 14358 32;17340 12322 32;16235 12188 32;15988 14224 32;17093 14358 50; +17155 10696;17691 10758;18956 10018;19073 8852;18592 7994;17408 8630;17155 10696 18; +16068 10716; +16330 8422; +16540 6878; +17377 6990; +18189 7052; +18939 6154; +19052 4828; +21092 7727; +22914 7857;23054 6863; +23046 11518 1;22739 11453;22486 11245;22497 10932 1;22510 10560;22731 10352;23046 10154;23046 11518 18; +20055 11320 1;20220 11035;20268 10844;20316 10518 1;20389 10026;20311 9500;20779 9331 1;21079 9223;21243 9151;21537 9026 1;21594 9002;21646 8998;21695 9035;21644 9141 1;21596 9214;21540 9286;21474 9359 1;21212 9646;21173 9901;21107 10284 1;20993 10948;20932 11321;20776 11976 1;20696 12312;20534 12475;20279 12709;20291 12552;20413 11389;20303 11374;20055 11320 18; +22855 6833 1;22876 6103;22827 5683;22969 4966 1;23089 4358;23094 4002;23257 3404;23728 3544;23709 3728;23256 6930;22855 6833 18; +21158 14273 1;21537 14132;21723 13824;21745 13421 1;21770 12958;21750 12623;21449 12270;21227 12043 1;21095 12696;21151 13208;21107 13974;21158 14273 18; +21107 13974;20156 13930;20279 12709 1;20534 12475;20696 12312;20776 11976 1;20932 11321;20993 10948;21107 10284 1;21173 9901;21212 9646;21474 9359 1;21931 8858;21894 8372;21984 7695 1;22086 6943;22198 6525;22220 5767 1;22226 5552;22133 5415;21967 5279;22133 4293;22150 4302 1;22545 4027;22729 3760;22865 3299;23249 3421;23236 3484 1;23093 4035;23084 4385;22969 4966 1;22827 5683;22876 6103;22855 6833;23122 7885;22686 9525 1;22545 10022;22360 10320;21936 10615 1;21570 10870;21475 11166;21343 11592 1;21066 12485;21161 13041;21107 13974 18; +20275 4808; +22888 8765;22686 9525 1;22545 10022;22360 10320;21936 10615 1;21570 10870;21475 11166;21343 11592 1;21066 12485;21161 13041;21107 13974;21135 14138;21158 14273;21207 14553 1;21246 14622;21310 14673;21397 14691 1;21566 14726;21735 14726;21792 14889 1;21883 15147;21957 15283;22045 15542 1;22069 15613;22027 15709;21953 15709 1;20377 15709;19494 15810;17918 15777 1;17781 15774;17682 15692;17641 15561 1;17599 15429;17665 15268;17801 15246 1;18532 15127;18955 15175;19695 15160 1;19924 15155;20032 14931;20065 14704 1;20079 14605;20088 14521;20094 14441; +17814 14442;17685 15324;17728 15275 1;17749 15260;17774 15250;17801 15246 1;18532 15127;18955 15175;19695 15160 1;19924 15155;20032 14931;20065 14704 1;20108 14411;20096 14242;20151 13951;20107 13988;19130 14578;17814 14442 18; +17915 17608;17933 16751;17761 16725;16570 16718;16577 16178;15850 16174;15845 16706;12737 16706;12740 16178;12013 16174;12011 16694;10716 16680;6602 16041;6151 15971;5324 15843;4025 15843;4033 13922;3562 13901;3562 13963 1;3509 14844;2789 15775;1909 15788;1947 15864;2211 16210;2401 16758;3438 16746;3425 16672;3820 16727;3931 16718;4439 16734;4408 17018;4423 17044;4764 17060;4776 16943;4797 16723;4853 16717;5197 16718;7695 17103;9427 17375;9199 18954;10105 18960;10105 18103;10173 17486;10297 17505;10684 17564;11491 17570;11497 18076;12338 18091;12329 17943;12326 17568;15181 17581;15186 18076;16039 18054;16018 17993;16014 17579;17915 17608 18; +22611 16755;16570 16718;16577 16178;15850 16174;15845 16706;12737 16706;12740 16178;12013 16174;12011 16694;10716 16680;6602 16041;6151 15971;5324 15843;4025 15843;4033 13924;4008 13921;4033 13922;4033 13924;10064 14717;13230 15135;14747 14280;15087 11568;15660 11648;15785 11666;15562 11927;15278 14672;16987 14864 1;17147 14813;17172 14623;17128 14455;17091 14358;17093 14358 32;17340 12322 32;16726 12247;16660 12230 1;16702 11954;16559 11759;16335 11594;16334 11568;17111 11140;17131 10944;17155 10696;17691 10758;18211 10472;18918 11185;18871 11179;18181 11569;17845 14343;17831 14444;17814 14442;17693 15272;17763 15625;17763 15729 1;17806 15760;17858 15776;17918 15777 1;19494 15810;20377 15709;21953 15709 1;22027 15709;22069 15613;22045 15542 1;21957 15283;21883 15147;21792 14889 1;21735 14726;21566 14726;21397 14691 1;21256 14661;21175 14547;21164 14405;21158 14273 1;21537 14132;21723 13824;21745 13421 1;21770 12958;21750 12623;21449 12270;21245 12061;21223 12064 1;21252 11916;21291 11760;21343 11592 1;21475 11166;21570 10870;21936 10615 1;22360 10320;22545 10022;22686 9525;23030 8231;23075 8146;23046 10154 1;22731 10352;22510 10560;22497 10932 1;22486 11234;22722 11439;23014 11511;23075 11514;23058 11950;22281 11949;22281 14593;22613 14601;22611 16755 18; +11945 13911;10753 13764 32;10804 13348;10320 13288 32;10610 10866;8540 10612;8452 12254 32;7686 12210;7661 12656 32;6462 12587 32;6487 12142;5422 12081 32;5480 10866;5352 10859 32;5404 9923 32;5809 9945;5928 7812;5604 7794 32;5656 6870 32;5979 6888;6000 6509;8752 6666 32;8608 9254;10781 9521;10990 7750 32;14090 8132 32;13945 9294;14066 9309 32;13965 10135 32;13558 10085;13234 12735;13585 12778 32;13480 13636 32;13129 13593;13090 13910 32;11961 13781;11945 13911 18; +13940 10118;13819 11026; +13767 10093;13652 11001; +13857 10996;14592 11086;15354 4865;14469 4757;14331 5891;11134 5502;11259 4475;10641 4410;10484 5768; +4386 4416; +9765 4946; +10875 5017; +14675 5350; +5665 7043 1;5169 7135;4330 7334;4391 6833;4733 4996;8632 5330;8472 6650;6000 6509;5979 6888;5656 6870;5665 7043 18; +8707 6629;8824 5661;9960 5798; +10431 7656;10575 6491 16; +10368 6525;11453 6659 32;12487 6786 16; +11128 7742;11191 7229;12949 7445;13093 6276;10443 5951; +14459 6723; +11787 5856; +12061 5632 32;12608 5697 32;12570 6014 32;12108 5958 32;12061 5632 50; +12610 5695;12572 6014; +11694 5575;11458 5950;10490 5815;10493 5697;10650 4401;11259 4475;11134 5502;11459 5542;11694 5575 18; +8645 6648;8722 6506;8824 5661;9875 5788;9978 5781;10135 4366;9542 4300;9420 5398;8644 5331;8626 5375;8472 6650;8645 6648 18; +10462 7609 32;9723 7518 32;9500 9336 32;10239 9427 32;10462 7609 50; +9721 7520;9752 7266;10336 7337; +9691 7665;8712 7545; +8650 9233;9496 9346;9723 7536;10456 7615;10242 9451;10770 9495;11053 7070;12820 7287;12929 6393;10460 6101;10639 4391;10142 4361;9967 5874;8890 5730;8755 6694;8650 9233 18; +15987 11228;15699 11184;15701 11132;16272 6501;18224 6688;18588 6310;18745 4417;19446 4487;19433 4565;19245 6655;16683 8161;16334 11084;15987 11228 18; +19439 4495;19478 4062;17219 3859;17183 4259;18297 4358;18125 6237;15766 6011;15135 11130;15688 11195;15712 11044;16272 6501;18224 6688;18588 6310;18745 4417;19439 4495 18; +12566 6087;12576 5966;12608 5697;12729 5696;14331 5891;14469 4757;15354 4865;14592 11086;13857 10996;13853 10772;13940 10118;13989 9740;14042 9306 32;13945 9294;14090 8132 32;11199 7776;11240 7291;12941 7474;13046 6148;12566 6087 18; +10367 13163;9581 13043;9402 13021;9278 14045;13167 14516;14217 13955;14549 11485;13466 11339;13403 11392;10367 13163 18; +4036 3791;4123 3792;4830 3853;4732 4996;4727 5029;4391 6833 1;4330 7334;5169 7135;5665 7043;5973 7795;5894 8450;5718 8451;4058 8315;4036 3791 18; +4062 8668;4244 8690;5480 8791;5458 9055;5414 10133;5397 10831;5493 10849;5397 12070;5303 12090;4008 12467;4036 12463;4062 8668 18; +8896 13065;8399 12943;8425 12271;8522 10956;8540 10612;10610 10866;10336 13155;9385 13022;9298 13883;9278 14045;13167 14516;14217 13955;14549 11485;13466 11339;13572 10081;13904 10116;13834 10971;13949 11007;14592 11086;15222 5940;15766 6011;15135 11130;16003 11232;16334 11084;16683 8161;19245 6655;19476 4088;19953 4131;19937 4130;19601 7435;18595 7999;18344 8127;17408 8630;17155 10696;17143 10825;17131 10944;17111 11140;16334 11568;16335 11594 1;16337 11596;16340 11597;16342 11599;16120 11686;16082 11707;15087 11568;14747 14280;13230 15135;7474 14376;6955 14308;4033 13924;4033 13922;4008 13921;4035 13359;8792 13970;8896 13065 18; +4035 787;4043 2112;4553 2154;4632 1155 16; +5570 1136;5480 2269; +5551 1154;11281 1679;11184 2740;11959 2811;12266 -536;10003 -741;10017 -61;9388 -48; +4295 1119 32;4285 -2381 32;9374 -2367 32;9382 500 32;5515 511;5517 1116 32;4295 1119 50; +9390 -1934;9982 -1936;9984 -1317;12308 -1102;12722 -5610;7940 -5668;4495 -5710 1;4235 -5713;4022 -5505;4019 -5245;4047 -3858 16; +5933 -4668; +5776 -2383;5780 -3726;4136 -3731;4135 -3298; +4133 -2417;4131 -2845; +4118 -2070;4111 798; +4301 -2057 32;3999 -2062 32;4007 -2439 32;4300 -2440 32;4301 -2057 50; +10330 1127; +6387 857; +7756 935; +9074 1031; +11625 2230; +4309 1625; +4032 762;4267 766;4272 1146;4616 1150;4559 2158;4032 2105;4032 762 18; +9983 -1922 1;10007 -2332;10107 -2879;10515 -2829 1;11474 -2712;12113 -2052;12286 -1102 1;12394 -507;12077 -178;11867 390 1;11755 693;11519 970;11239 808 1;10812 561;10374 276;10009 -55;9983 -1922 18; +11666 -5043; +5511 1137;5515 511;9382 500 32;9380 -40;9454 -49;10017 -61;10073 2 1;10424 309;10837 575;11239 808 1;11519 970;11755 693;11867 390 1;11998 35;12172 -227;12257 -519;12253 -394;11959 2811;11184 2740;11281 1679;5511 1137 18; +16348 834;16304 1409;16863 1452;16813 2101; +18420 1685;17846 1602;17772 2107;17385 2050; +16608 846;17033 908 16; +17597 990;18482 1118 16; +20003 2611 32;18768 2432 32;18837 1956;18406 1893 32;18756 -517;16686 -819;16562 812 32;15763 751;15725 1239 32;14568 1150 32;14606 663;13533 581 32;13625 -637;13423 -653 32;13486 -1481 32;13966 -1444;14131 -3607;13715 -3639 32;13783 -4526 32;14198 -4494;14235 -4975 32;16987 -4765 32;16790 -2177;18951 -1862;19207 -3630 32;22296 -3182 32;22127 -2020;22275 -1999 32;22146 -1107 32;21713 -1169;21357 1285;21792 1348 32;21663 2240 32;21228 2177;21171 2573 32;20031 2420;20003 2611 50; +17392 2172;17283 3313;21036 3671;22233 2682;22650 167;21589 -9; +13322 -1470;13395 -2441; +13497 -1526;13567 -2432; +13694 -1508;13753 -2421; +13581 -2408;13597 -2621;12950 -2671;12431 2880;13490 2979;13597 1836;16450 2103;16347 3205;16781 3254;16912 2098 16; +13197 2380; +16625 2685; +17594 2810; +16265 1455;16161 2053;16054 2066;13597 1836;13564 1825 1;13266 1692;13076 1454;13101 1128 1;13121 866;13280 709;13520 603;13735 596;14606 663;14568 1150 32;15725 1239 32;15763 751;16249 788;16265 1455 18; +16235 1407;16250 1541;16161 2053;16250 2084;16450 2103;16347 3205;16781 3254;16912 2098;16839 1487;16235 1407 18; +13602 1833;13535 1812 1;13254 1676;13077 1443;13101 1128 1;13121 866;13280 709;13520 603;13547 398;13625 -637;13423 -653 32;13486 -1481 32;13602 -2621;13319 -2642;12950 -2671;12431 2880;13490 2979;13567 2162;13602 1833 18; +18422 1745;17834 1681;17772 2107;17429 2056;17389 2206;17283 3313;21036 3671;22233 2682;22650 167;21589 -9;21206 2202;18422 1745 18; +21976 -1108;21857 -309; +22149 -1083;22037 -284; +22587 3131;22768 2993;23972 -4934;24671 -4841;25953 -5882 1;26121 -5842;26286 -5802;26450 -5762;24934 -153 16; +25574 -4922;25830 -5818 32;26328 -5676 32;26057 -4724 16; +24964 -5049;25509 -4918;25731 -5699;24964 -5049 18; +22207 -4224; +16979 -4912;17754 -4853;17805 -5529; +17900 -3718;16921 -3838; +17930 -3863;17961 -4117;18545 -4046; +16859 -2150;17705 -2037;17932 -3847;18665 -3768;18451 -1932;18979 -1888;19262 -4313;21029 -4096;21138 -4990;19816 -5146;19812 -5404;17794 -5531;17753 -4872;17038 -4877;16964 -4689;16859 -2150 18; +18577 -4858;19662 -4724 32;20696 -4597 16; +18640 -3727;18784 -4892 16; +19337 -3641;19400 -4154;21158 -3938;21302 -5107;19779 -5294;19820 -5648; +18671 -3774 32;17932 -3865 32;17709 -2047 32;18448 -1956 32;18671 -3774 50; +22076 -309;22705 -204;23439 -5002;21509 -5248 16; +14071 -3051;13931 -3054;12998 -3126;13224 -5601;13329 -5602;16481 -5563;17695 -5509;17761 -5363;17700 -4918;17098 -4883;16976 -4761;14228 -4970;14071 -3051 18; +22284 -3178;21979 -1137;22110 -1137;22135 -986;22043 -329;22133 -299;22705 -204;23439 -5002;21509 -5248;21360 -5202;21142 -3998;19475 -4120;19414 -3588;22284 -3178 18; +19884 4517;20085 4537;21486 4619;22139 4298;22230 4245 1;22570 3989;22738 3728;22865 3299;22977 3335;23094 3348;23103 3279;23095 3277 1;23002 2967;23003 2765;23069 2448 1;23171 1958;23286 1698;23426 1218 1;23519 897;23619 665;23738 430;23777 276 1;23853 -143;23705 -485;23820 -961;24675 -4843;23975 -4930;23807 -3846;22768 2993;22587 3131;22188 3410;21167 4192;19937 4130;19884 4517 18; +21739 5134;21980 5325;21975 5231;22133 4293;22139 4296;22064 4335;21486 4619;19898 4526;19851 5004;20048 5016;21739 5134 18; +18263 10444 1;18453 10569;18620 10698;18824 10598 1;19001 10511;19101 10463;19278 10376 1;19386 10323;19465 10262;19478 10142 1;19494 9991;19482 9905;19490 9753 1;19497 9618;19504 9503;19398 9420 1;19262 9314;19186 9254;19050 9148;18948 10012;18263 10444 18; +19197 10180; +19834 4997;19919 5008;20048 5016;21739 5134;21980 5325;22055 5359 1;22165 5471;22225 5594;22220 5767 1;22198 6525;22086 6943;21984 7695 1;21911 8245;21922 8669;21683 9078;21682 9062;21695 9035 1;21646 8998;21594 9002;21537 9026 1;21243 9151;21079 9223;20779 9331 1;20311 9500;20389 10026;20316 10518 1;20268 10844;20220 11035;20055 11320;19958 11327;18871 11179;18798 11220;18107 10537;18284 10431;18263 10444 1;18453 10569;18620 10698;18824 10598 1;19001 10511;19101 10463;19278 10376 1;19386 10323;19465 10262;19478 10142 1;19494 9991;19482 9905;19490 9753 1;19497 9618;19504 9503;19398 9420 1;19262 9314;19186 9254;19050 9148;19050 9079;19073 8852;18592 7994;18853 7854;19601 7435;19834 4997 18; +20589 12386 1;20673 12273;20735 12147;20776 11976 1;20932 11321;20993 10948;21107 10284 1;21173 9901;21212 9646;21474 9359 1;21540 9286;21596 9214;21644 9141;21695 9035;21748 8954 1;21921 8582;21918 8190;21984 7695 1;22086 6943;22198 6525;22220 5767 1;22223 5648;22196 5553;22143 5468;22068 5372;21978 5279;21975 5231;22133 4293;22139 4296;22209 4260 1;22443 4088;22598 3915;22713 3691; +23031 2986 1;23010 2814;23026 2656;23069 2448 1;23171 1958;23286 1698;23426 1218 1;23547 801;23679 535;23851 217 1;23913 101;23980 -22;24054 -161; +25821 -6433;26301 -8073;25840 -8159 1;24256 -8465;22867 -8608;20879 -8709 1;16304 -8942;13770 -8820;9190 -8919 1;4796 -9014;2347 -8930;-2047 -8813 1;-2060 -8813;-2074 -8812;-2087 -8812;-2323 -8802;-2508 -8804;-2162 -7324;-1942 -7315;-634 -7372;-228 -7376 1;1275 -7275;2190 -6212;2190 -4523;2195 53;2212 12685 1;2213 13337;1671 13976;1033 14260;1705 15796;2002 15783 1;2839 15709;3511 14813;3562 13963;3562 13901;3565 13394;3636 974 1;3626 103;3620 -628;3616 -1321;3611 -1977 1;3604 -2952;3596 -3914;3574 -5166 1;3568 -5531;3563 -5779;3821 -6165;3984 -6380 1;4551 -7082;5205 -7450;6150 -7440;6830 -7440;8069 -6201;9489 -6160 1;12476 -6117;14430 -6142;17663 -6017 1;19870 -5932;21105 -5796;23301 -5561;24571 -5425;25821 -6433 18; +3552 13926;4021 13956;4058 13377;4021 13309;4038 8678;5486 8809;5399 9917;5800 9917;5870 8469;4029 8321;4038 4543;3602 4517;3552 13926 18; +8895 13158;8792 13970;4035 13359;4038 8674;5480 8791;5458 9055 16; +9962 5927;10135 4366;9542 4300;9420 5398;4732 4996;4830 3853;4044 3790;4046 8315;5869 8463 16; +3616 4558;4044 4580;4038 4168;4036 3791;4123 3792;4830 3853;4733 4983;4832 5005;9420 5398;9542 4300;10135 4366;10135 4367;10143 4391;10648 4416;10650 4401;11259 4475;11205 4921;11180 5127;11134 5502;14331 5891;14469 4757;15354 4865;15217 5985;15750 6039;15845 6019;18125 6237;18297 4358;17183 4259;17219 3859;17283 3313;17392 2172;17452 2057;17723 2075;17865 1625;18420 1674;18760 -516;16662 -812;16551 823;16379 811;16354 1409;16878 1483;16915 2112;16900 2203;16781 3254;16347 3205;16450 2103;13597 1836;13490 2979;12431 2880;12950 -2671;13597 -2621;13581 -2408;13518 -1534;13949 -1497;14073 -3039;13912 -3055;12998 -3126;13227 -5633;12706 -5625;12710 -5448;12308 -1102;9984 -1317;9982 -1936;9390 -1934;9409 -66;9620 -53;10017 -61;10003 -741;12266 -536;11959 2811;11184 2740;11281 1679;7495 1332;7139 1299;5551 1154;4632 1134;4624 1254;4553 2154;4043 2112;4035 787;4010 -2073;4022 -2443;4247 -2427;5685 -2403;5728 -3687;4050 -3816;4044 -4020;4019 -5245 1;4021 -5455;4160 -5631;4350 -5689;4197 -6168 1;4075 -6167;3950 -6166;3821 -6165 1;3563 -5779;3568 -5531;3574 -5166 1;3599 -3717;3606 -2657;3614 -1516;3616 4558 18; +17695 -5509;16481 -5563;13223 -5602;12998 -3126;14080 -3042 16; +4323 -5681;4144 -6144;4467 -6170 1;5909 -6184;6890 -6207;8484 -6177 1;12069 -6110;14081 -6155;17663 -6017 1;19601 -5942;20790 -5828;22539 -5642;22674 -5628 1;22875 -5607;23083 -5584;23301 -5561;24571 -5425;24621 -5467;24839 -4981;24662 -4852;24600 -4852;23975 -4930;23807 -3846;23793 -3753;23687 -3055;22768 2993;22587 3131;22271 3347;21167 4192;19937 4130;19929 4208;19466 4196;19476 4088;17140 3856;17220 3313;17519 3336;21036 3671;22233 2682;22491 1123;22559 717;22650 167;21589 -9;21724 -1115;22099 -1063;22038 -330;22701 -208;23439 -4988;21452 -5253;19620 -5382;17825 -5493;17302 -5526;16481 -5563;13329 -5602;13224 -5601;13222 -5580;12503 -5612;7940 -5668;4323 -5681 18; +3517 28551;2526 28408;2610 27833; +3108 29551;3200 28846;2099 28680;2212 27782;2801 27865 16; +3550 27970;4842 28153 16; +3504 28696 32;3979 28764 32;4081 28058 32;3606 27990 32;3504 28696 50; +3586 27950 32;2450 27786 32;2381 28261 32;3517 28425 32;3586 27950 50; +3028 27872;2963 28328; +3314 27913;3249 28369; +-11198 22399 33;-11150 22359;-11086 22337;-11018 22344 1;-10753 22371;-10605 22387;-10340 22415 1;-10205 22429;-10108 22550;-10122 22685 1;-10172 23160;-10199 23427;-10248 23902 1;-10255 23975;-10291 24038;-10343 24081 33;-10397 24126;-10468 24150;-10543 24142 1;-10809 24115;-10959 24100;-11225 24073 1;-11281 24067;-11329 24040;-11362 23999 33;-11395 23959;-11412 23906;-11407 23850 1;-11276 22495;-11244 22439;-11198 22399 50; +-6378 64888 1;-6287 64699;-6123 64560;-5911 64586 1;-5562 64628;-5323 64706;-5013 64542 1;-4745 64400;-4558 64257;-4395 64074; +-7851 63305 1;-8047 63278;-8230 63260;-8403 63242 16; +-8840 63199 1;-9471 63136;-9542 63123;-10119 62694 1;-10587 62346;-10459 62286;-10929 62088 16; +-14826 -54891 32;-14022 -54887 32;-14019 -55628 32;-14823 -55632 32;-14826 -54891 50; +9110 -2374 1;9078 -3197;8177 -3575;7370 -3515 1;6710 -3466;6248 -3036;6102 -2390;5823 -2391;5797 -3751;4044 -3769;4045 -3952;4019 -5245 1;4022 -5505;4235 -5713;4495 -5710;7940 -5668;12712 -5609;12692 -5255;12312 -1141;12259 -1231 1;12054 -2111;11430 -2717;10515 -2829 1;10109 -2879;10008 -2337;9983 -1928;9400 -1937;9356 -2373;9110 -2374 18; +6102 -2390 1;6248 -3036;6710 -3466;7370 -3515 1;8177 -3575;9078 -3197;9110 -2374;6102 -2390 18; +-24477 -63943;-24629 -62010;-20633 -61694;-20516 -63604;-24477 -63943 18; +-20853 -60145;-27771 -60273 1;-27775 -60310;-27802 -60370;-27800 -60413 1;-27786 -60700;-27950 -61173;-28236 -61198 1;-29917 -61347;-30864 -61418;-32545 -61564 1;-33082 -61611;-33281 -62083;-33819 -62122 1;-34291 -62157;-34722 -61915;-34901 -61477 1;-34949 -61360;-35075 -61215;-35142 -61101;-35215 -60897 1;-36368 -61028;-37365 -61163;-38596 -61463;-38746 -61500;-38614 -61655 1;-38493 -61886;-38397 -62327;-38313 -62453 1;-38095 -62780;-37935 -63041;-37548 -63106 1;-37165 -63170;-36980 -63461;-36869 -63834 1;-36718 -64341;-36757 -64658;-36758 -65181;-35710 -65008 1;-32039 -64560;-29975 -64340;-26289 -64048;-24823 -63944;-24479 -63915;-24629 -62010;-20705 -61700 1;-20737 -61562;-20767 -61405;-20780 -61174 1;-20808 -60674;-20840 -60646;-20853 -60145 18; +-18988 -39976;-18754 -38787; +-18207 -38881;-18754 -38805;-19008 -39972;-20112 -37131;-20114 -36709;-19966 -36611;-19706 -36392;-24137 -31127;-23923 -30939 1;-23621 -30518;-23629 -29932;-23978 -29518 1;-24388 -29031;-25100 -28995;-25602 -29386;-26893 -27858;-25122 -26454;-24961 -26630 1;-24936 -26660;-24910 -26690;-24885 -26720 1;-24148 -27589;-22980 -27466;-21919 -27027;-21049 -28336 1;-21436 -28858;-21602 -29550;-21470 -30177;-21582 -30354 1;-21724 -30807;-21734 -31220;-21411 -31592 1;-20327 -32854;-19715 -33559;-18624 -34819;-18441 -35030;-18379 -35106;-17465 -36214;-17265 -36424;-16354 -37380 1;-15924 -37718;-15532 -37676;-14985 -37676;-14984 -37662;-14710 -37688;-8935 -37934;-8883 -38043 1;-8774 -38184;-8615 -38279;-8423 -38280 1;-8092 -38282;-7756 -38207;-7673 -37887;-7551 -37878;-7315 -37878 1;-6526 -37878;-6023 -37866;-5326 -38236;-5176 -37952;-4680 -38125 1;-3565 -38600;-2758 -38891;-1792 -39796;-1243 -40364;748 -42424;886 -42576;1149 -42831 32;1471 -42499 32;1616 -42660;2180 -43259;2129 -43314;1884 -43579 1;2467 -44125;2788 -44392;3505 -44540;3595 -44147;3824 -44191 1;4007 -44222;4196 -44235;4379 -44246;4385 -44423;4372 -44659 32;5795 -44739 32;5818 -44342;5945 -44323;8137 -44437;8132 -44614;8123 -44893 32;12257 -45030 32;12271 -44574;12598 -44592;14914 -44715;14940 -44919 1;15009 -45285;15295 -45485;15716 -45485 1;15952 -45485;16265 -45382;16289 -45147 1;17338 -45181;18064 -45216;18724 -45355;18445 -46983 1;17944 -46872;17399 -46823;16792 -46797;16760 -47246;16146 -47187;13974 -47064;13952 -46935 1;13875 -46707;13617 -46571;13356 -46571 1;13052 -46571;12720 -46719;12715 -47049;12593 -47046;11067 -47063;11075 -46920;11082 -46535 32;10576 -46526 32;10566 -47058;10325 -47046;4872 -46777;4842 -46767;4858 -46671;4883 -46231 32;4263 -46196 32;4232 -46744 32;4268 -46746;4052 -46733 1;3702 -46705;3399 -46643;3051 -46538;3114 -46424;3193 -46049 1;2207 -45841;1716 -45432;914 -44823;609 -45154;427 -44960;-263 -44284;-228 -44250;51 -43963 32;-375 -43550 32;-686 -43870;-834 -43713;-2701 -41539;-2569 -41440;-2362 -41227 32;-2788 -40814 32;-3110 -41146 32;-3269 -40983 1;-3653 -40638;-3957 -40445;-4464 -40187;-4395 -40090;-4254 -39807 1;-4906 -39451;-5328 -39344;-6077 -39306;-7717 -39244;-7735 -39680;-8208 -39663;-9463 -39638;-9543 -39642;-9521 -39468;-9506 -39189;-10752 -39183 1;-10768 -38877;-11076 -38680;-11382 -38696 1;-11830 -38719;-11853 -39160;-11850 -39615;-12280 -39623;-15106 -39533;-15047 -39404;-14993 -39190;-17794 -38154 1;-18071 -38341;-18169 -38590;-18207 -38881 18; +-18748 -38799;-18988 -40006;-12158 -40150;-11452 -40124;-10213 -40115;-9035 -40158;-7648 -40210;-6296 -40245;-5397 -40350;-4307 -40838;-2649 -42399;-625 -44597;-573 -44667;-58 -45252;1076 -46264;2376 -46979;3039 -47049;4120 -47406;4766 -47398;11152 -47639;12755 -47632;16778 -47725;16796 -47281;16762 -47222;14077 -47070;13975 -47056 1;13975 -47063;13975 -47070;13975 -47077;12843 -47054;12715 -47045;11202 -47062;11072 -47067 32;10659 -47060;10420 -47051;4872 -46777;4723 -46727;4721 -46770 32;4282 -46745;4035 -46739 1;3723 -46720;3512 -46673;3239 -46594;3091 -46532 1;2235 -46405;1511 -45966;776 -45307;607 -45137;-53 -44490;-333 -44233;-705 -43864;-2661 -41586;-2684 -41559 32;-3004 -41248;-3113 -41128 1;-3514 -40747;-3803 -40540;-4244 -40302;-4450 -40182 1;-5057 -39855;-5520 -39758;-6225 -39733;-7502 -39688;-7735 -39672;-9463 -39638;-9610 -39645;-11634 -39656;-11831 -39639;-14928 -39552;-18210 -39492;-18210 -39484;-18221 -38869;-18748 -38799 18; +-34953 75446;-34909 75437 1;-34885 75438;-34861 75437;-34836 75436 1;-34316 75407;-33917 74962;-33946 74442 1;-33975 73921;-34420 73523;-34941 73552 1;-35461 73581;-35859 74026;-35830 74546 1;-35807 74969;-35509 75311;-35118 75409;-34953 75446 18; +3594 83351;3829 78922;9430 77657;9430 79860; +3704 83347;6001 83499;6036 82632;6774 82597;7477 80910;7489 80265;7934 79785;9352 79844;9423 77676;3798 78941;3704 83347 18; +-8233 53585;-8329 54305 1;-8097 54344;-7868 54382;-7570 54416 1;-7384 54438;-7353 54626;-7282 54799;-6770 54866;-6647 53800;-8233 53585 18; +-8329 54305;-8233 53585;-9510 53412 1;-9874 53365;-10139 53588;-10157 53974;-10475 55977 1;-10281 56012;-10172 56031;-9978 56066 1;-9804 56097;-9743 55872;-9716 55698 1;-9651 55270;-9653 55025;-9576 54599 1;-9529 54336;-9292 54149;-9027 54189 1;-8755 54230;-8541 54268;-8329 54305 18; +41229 47215;41152 48105;40846 48073 32;40502 51376;40058 51666;40401 52092;40272 53204;40116 54675;39791 56344;38603 56259;38386 56113 1;38282 56062;38166 56030;38043 56021 1;37829 56005;37627 56062;37464 56172 1;37405 55848;37180 55578;36878 55453;36666 55411;37783 49216;38773 49768;39027 47420;39593 47081;41229 47215 18; +48114 43686;49098 43757;49942 44659;49872 45327;50094 45366;49983 46490;49771 46484;49702 47085 32;50738 47202;50671 47826;51702 47941;51345 51221;50677 51147;50698 50900;46421 50447 32;46491 49775 32;42648 49374 32;42763 48273 32;41157 48105;41224 47213;41083 47202;41060 46522;47575 46897;48114 43686 18; +41230 47224;39582 47089;39024 47445;38763 49761;32422 46329 16; +54487 57430;57065 44942 1;57183 44357;56706 43874;56107 43780;49149 43259;49102 43835 16; +48105 43744;49108 43837;49153 43321;48146 43240;48105 43744 18; +48168 43148;48117 43750 16; +49841 44658;50753 44749;51043 44551;51266 44559;51457 44807;52468 44899;52567 43962;51242 43415;49129 43266;49104 43871;49841 44658 18; +13788 60561; +-28774 122236 1;-29487 122231;-30069 122805;-30074 123518 1;-30079 124231;-29505 124812;-28792 124817 1;-28079 124822;-27498 124248;-27493 123536 1;-27488 122823;-28062 122241;-28774 122236 18; +-26895 124286;-25138 122498;-24000 123616;-24576 124203 1;-25103 123685;-25542 123631;-26078 124178;-26527 124635;-26895 124286 18; +-28733 126000;-27664 126000;-25823 124159 1;-25558 123894;-25129 123894;-24864 124159 1;-24585 124438;-24585 124889;-24864 125168 1;-25660 125964;-26106 126410;-26902 127206 1;-27058 127362;-27181 127468;-27401 127468 1;-28348 127468;-28880 127468;-29827 127468 1;-29922 127468;-29999 127391;-29999 127296;-28733 126000 18; +-35225 129007;-32156 129007;-28742 125593 1;-28618 125469;-28591 125176;-28904 125176 1;-29650 125176;-30067 125176;-30813 125176 1;-31207 125176;-31481 125250;-31759 125529;-35225 129007 18; +-34258 126602;-33171 126602;-31927 125358 1;-31871 125302;-31909 125164;-31989 125164 1;-33002 125164;-33570 125164;-34583 125164 1;-34783 125164;-34905 125242;-35046 125383 1;-35898 126235;-36375 126712;-37227 127564 1;-37507 127844;-37507 128297;-37227 128577 1;-36925 128879;-36455 128866;-36153 128564;-34258 126602 18; +96915 -144926;96914 142524 32;-102705 142524 32;-102704 -144926 32;96915 -144926 18; +-94724 99498;29307 113498;38500 125237;45571 125803;49531 117317;74846 105013;77816 98932;91676 -31463;87716 -42777;78947 -41363;77816 -34857;72159 -34857;66302 -44060;56502 -43843;52359 -67385;26620 -63708;-47487 -75871;-48878 -61311;-52432 -52082;-86725 72900;-89598 78252;-95147 91437;-94724 99498 18;-87750 85332;-87944 85220;-88019 84989;-88000 84582;-87663 84664;-87700 85032;-87750 85332 18;-51203 55455 32;-49647 55455 32;-49647 60864 32;-51203 60864 32;-51203 55455 50;-50724 14979 32;-50024 14979 32;-50024 15742 32;-50724 15742 32;-50724 14979 50;-50688 1358 32;-50051 1358 32;-50051 -442 32;-50688 -442 32;-50688 1358 50;-13135 -24391 32;-12709 -24391 32;-12709 -8940 32;-13135 -8940 32;-13135 -24391 50;-13058 16186 32;-12722 16186 32;-12722 21066 32;-13058 21066 32;-13058 16186 50;-13064 29485 32;-12693 29485 32;-12693 30722 32;-13064 30722 32;-13064 29485 50;-13247 97034 32;-12628 97034 32;-12628 100163 32;-13247 100163 32;-13247 97034 50;-13097 58412 32;-12734 58412 32;-12734 61512 32;-13097 61512 32;-13097 58412 50;24437 65027 32;24755 65027 32;24755 69959 32;24437 69959 32;24437 65027 50; +62093 -43270;62093 -44202; +24593 -63582;24593 -64514; +-12907 -69552;-12907 -70484; +-50415 -56514;-50415 -57446; +-87907 75943;-87907 75011; +-61777 6671;-59865 -675 1;-59704 -1366;-59397 -1821;-58748 -2106 1;-58038 -2418;-57546 -2345;-56847 -2682 1;-56552 -2824;-56556 -3104;-56410 -3397 1;-56291 -3636;-56214 -4093;-56480 -4077 1;-56850 -4055;-57052 -3981;-57405 -3868 1;-57746 -3759;-58103 -4103;-58103 -4461 1;-58103 -5022;-57921 -5446;-57440 -5735 1;-56833 -6099;-57117 -6738;-57143 -7445 1;-57171 -8196;-57248 -8784;-57090 -9364;-54760 -17861 1;-54461 -18952;-53410 -19207;-52278 -19382 1;-50704 -19636;-49098 -18950;-47816 -17606 1;-46157 -15867;-46149 -13999;-46467 -11617 1;-46602 -10606;-47251 -9928;-48260 -9778 1;-49255 -9630;-49794 -9485;-50215 -10431 1;-50528 -11135;-49568 -11505;-49620 -12349 1;-49697 -13596;-52184 -13647;-52081 -11757 1;-52027 -10780;-52107 -10172;-51505 -9401 1;-50918 -8650;-50295 -8533;-49359 -8355 1;-48108 -8118;-47247 -8285;-46149 -8931 1;-44572 -9859;-44469 -11385;-44208 -13196 1;-43662 -16985;-46000 -19475;-49266 -21471 1;-50314 -22111;-50798 -22096;-51830 -22761 1;-52384 -23118;-52428 -23955;-52332 -24607 1;-51945 -27234;-51696 -28526;-51066 -31105 1;-50488 -33471;-50377 -34918;-49862 -37298;-49324 -39793 16; +-48266 -43193;-44969 -55600 1;-44770 -56350;-44658 -56771;-44459 -57521 1;-44403 -57731;-44126 -57609;-43909 -57599 1;-37936 -57328;-34589 -57125;-28611 -57007 1;-28172 -56999;-27797 -57000;-27550 -56637 1;-27347 -56339;-26998 -56491;-26637 -56489 1;-22731 -56463;-20534 -56558;-16644 -56908 1;-14125 -57135;-12753 -57589;-10241 -57885 1;-8135 -58133;-6949 -58248;-4832 -58374 1;-3352 -58462;-2528 -58619;-1046 -58670 1;-695 -58682;-321 -58841;-313 -59193 1;-299 -59779;-292 -60108;-279 -60694 1;-271 -61060;-683 -61180;-1045 -61235 1;-4468 -61755;-6395 -62109;-9856 -62230 1;-13334 -62352;-15274 -62620;-18737 -62963 1;-24631 -63547;-27958 -63634;-33847 -64260 1;-36788 -64573;-38425 -64883;-41324 -65469 1;-41971 -65600;-42155 -65883;-42027 -66530;-41134 -71044 16; +-90081 87295 1;-88904 87663;-88866 87631;-87777 87896 1;-86608 88180;-85808 87191;-85477 86452;-74252 52606 1;-74077 52075;-73143 51784;-72928 51898 1;-72514 52117;-72271 52360;-72088 52657 1;-69677 56571;-68581 58935;-65929 62690 1;-65359 63497;-65039 63950;-64469 64757 16; +-58129 64311 1;-58237 63013;-58757 62349;-59461 61252 1;-61129 58653;-62367 57397;-63977 54762 1;-65401 52432;-66406 51244;-67653 48815 1;-68965 46259;-69382 44645;-69627 41783 1;-70167 35481;-69198 33882;-68259 30659;-63792 15201;-63391 13475 1;-63213 12954;-62713 12716;-62177 12846 1;-60743 13194;-59861 13450;-58374 13423 1;-57251 13403;-56529 13169;-55670 12446 1;-55374 12197;-55327 11948;-55112 11626 1;-54774 11119;-54387 10945;-54187 10369 1;-53834 9353;-53814 8732;-53577 7683 1;-53363 6737;-53908 5958;-54763 5502 1;-55535 5090;-56383 5488;-56892 6200 1;-57257 6711;-57329 7272;-56996 7805 1;-56763 8177;-56738 8505;-56874 8922 1;-57021 9373;-57429 9567;-57904 9567 1;-58992 9567;-59431 8764;-60468 8433 1;-61332 8157;-61963 7532;-61777 6671 16; +23449 -54909 1;24004 -55081;24327 -55630;24170 -56134 1;24013 -56638;23436 -56907;22881 -56735 1;22326 -56562;22003 -56013;22160 -55509 1;22316 -55005;22893 -54736;23449 -54909 18; +73320 -54693;N +69062 -61008;77555 -61008;73312 -68970;69062 -61008 18; +85399 110654;N +81141 104339;89634 104339;85391 96377;81141 104339 18; +-85692 22052;N +-89950 15737;-81457 15737;-85700 7775;-89950 15737 18; + + +-90770 -101448;Construction area, forbidden access! +Street / asphalt, with / without traffic +Big signs, playground items, etc. +-94509 -62288 32;-91828 -62288 32;-91828 -59664 32;-94509 -59664 32;-94509 -62288 50; +-97187 -18612 32;-94506 -18612 32;-94506 -15988 32;-97187 -15988 32;-97187 -18612 50; +-94509 -33067 32;-91828 -33067 32;-91828 -30443 32;-94509 -30443 32;-94509 -33067 50; +-94509 -18612 32;-91828 -18612 32;-91828 -15988 32;-94509 -15988 32;-94509 -18612 50; +-94509 -22221 32;-91828 -22221 32;-91828 -19597 32;-94509 -19597 32;-94509 -22221 50; +-97187 -69541 32;-95398 -69541 32;-95398 -66917 32;-97187 -66917 32;-97187 -69541 50; +-94217 -80160 32;-92083 -80160 32;-92083 -78042 32;-94217 -78042 32;-94217 -80160 50; +-95807 -93036; +-93145 -93018; +-90746 -80314;Building; Canopy +Street / asphalt, with / without traffic +Private/flowerbed, forbidden access! +Meadow; Meadow with trees; Forest +Light; strong thicket +Thicket, impassable +Tree; Special tree +Path; Track; Stairs +Fence, forbidden to pass! +Fence, passable +Wall, forbidden to pass! +Small wall, passable +Hedge, forbidden to pass! +Light; strong undergrowth +Hedges, passable +Countours; Hill +Rough/semi-open area +Sandy ground +Cultivation boundary; Step +Stone; Rock face +Well; Small erosion gully + + +-94509 -76769 32;-91828 -76769 32;-91828 -74145 32;-94509 -74145 32;-94509 -76769 50; +-94509 -65897 32;-91828 -65897 32;-91828 -63273 32;-94509 -63273 32;-94509 -65897 50; +-93630 -69541;-91828 -69541 32;-91828 -66917 32;-93630 -66917 16; +-96905 -80131 32;-94837 -80131 32;-94837 -78071 32;-96905 -78071 32;-96905 -80131 50; +-97187 -22221 32;-94506 -22221 32;-94506 -19597 32;-97187 -19597 32;-97187 -22221 50; +-95397 -69541 32;-93622 -69541 32;-93622 -66917 32;-95397 -66917 32;-95397 -69541 50; +-97186 -70537 32;-97186 -73161 32;-91827 -73161 32;-91827 -70537 32;-97186 -70537 50; +-97187 -62288 32;-94506 -62288 32;-94506 -59664 32;-97187 -59664 32;-97187 -62288 50; +-95825 -57271; +-96926 -52501 1;-96327 -53305;-95862 -53873;-95644 -54852; +-95060 -52368 1;-94445 -53213;-94272 -53817;-94000 -54826; +-97176 -39024;-91837 -39024; +-97176 -35418;-91837 -35418; +-97187 -76769 32;-94506 -76769 32;-94506 -74145 32;-97187 -74145 32;-97187 -76769 50; +-92735 -52616;-92735 -54560; +-92735 -52616;-92735 -54560; +-97015 -24865 1;-96587 -25062;-96095 -25420;-95626 -25727; +-97003 -23702 1;-95974 -23917;-95416 -24580;-94390 -24352; +-96651 -12597;-95063 -14610; +-93963 -12572;-92375 -14585; +-95703 -24177; +-93450 -24790 1;-93191 -25215;-92761 -25425;-92488 -25260 1;-92215 -25094;-92204 -24615;-92462 -24190 1;-92721 -23764;-93151 -23554;-93424 -23720 1;-93697 -23885;-93708 -24364;-93450 -24790 18; +-97187 -65897 32;-94506 -65897 32;-94506 -63273 32;-97187 -63273 32;-97187 -65897 50; +-97187 -33067 32;-94506 -33067 32;-94506 -30443 32;-97187 -30443 32;-97187 -33067 50; +-97176 -50002;-91837 -50002; +-97176 -46314;-91837 -46314; +-97176 -42701;-91837 -42701; +-95238 -27135;-93674 -29044; +-93744 -27135;-92180 -29044; +-97012 -24218 1;-96005 -24698;-95019 -25545;-94318 -25362; +-96856 -27135;-95292 -29044; +-93188 -57246; +-93964 -5333;-92452 -7384; +-97183 -97983 32;-94502 -97983 32;-94502 -95359 32;-97183 -95359 32;-97183 -97983 50; +-91824 -101608;-97183 -101608;-97183 -98984;-91824 -98984;-91824 -101608 18; +-94070 -8966;-92558 -11017; +-95847 -6305; +-95844 -9873; +-97183 -101608 32;-94502 -101608 32;-94502 -98984 32;-97183 -98984 32;-97183 -101608 50; +-94505 -101608 32;-91824 -101608 32;-91824 -98984 32;-94505 -98984 32;-94505 -101608 50; +-94505 -97983 32;-91824 -97983 32;-91824 -95359 32;-94505 -95359 32;-94505 -97983 50; +-99428 -85496;Legend +-99743 -107017;Special Signatures + + +-108808 -113533 32;-108808 -149753 32;102997 -149753 32;102997 -113533 32;-108808 -113533 50; +-108808 -113533 32;-108808 -149753 32;102997 -149753 32;102997 -113533 33;102997 -107318;103133 -103770;103115 -102260 1;101634 -102168;99386 -102232;97055 -102227 1;78346 -102190;36945 -113533;13658 -113533;-108808 -113533 50; +59183 -130917;May 2013 +59183 -123479;Thomas Schöps +-98032 -141948;Orienteering map +24405 -130921;Standing: +-100736 -132215;Garching +-100438 -131472;Garching +24405 -123483;Cartography: +59183 -136073;2.5 m +59183 -141376;1 : 4,000 +24405 -141380;Scale: +24405 -136077;Contours: +76149 131570;Organizer, host, mapper, +land owners and administration +do not assume any liability! +-75609 131433; +-108809 147382 32;-108809 139763 32;102996 139763 32;102996 147382 32;-108809 147382 50; +-29900 132587;-26214 132587;-23147 129520 1;-23039 129412;-23179 129177;-23332 129176;-26912 129153;-29983 132185 1;-30093 132294;-30091 132589;-29900 132587 18;-29256 132147;-23843 129647;-26750 129616;-29256 132147 18; +102997 139965 32;102996 147382 32;-108810 147387 32;-108806 139970 33;-108807 134438;-109023 130448;-108862 128687 1;-107176 128658;-105495 128666;-102866 128662 1;-84159 128623;-42757 139968;-19467 139967;102997 139965 50; +-95213 128871;-95019 129525;-94453 130392;-93799 130869;-94258 131311;-94913 132089;-95195 133026;-95231 133821;-95036 134723;-94718 135306;-94223 135766;-93498 136084;-92402 136172;-91501 135978;-91129 135695;-90493 136031;-89132 136102;-88460 135748;-87771 136084;-86392 136066;-85773 135783;-85066 136049;-83970 136119;-82555 135978;-81760 136049;-78931 136048;-78472 135801;-77570 136119;-75997 136084;-75396 135836;-74388 136101;-73133 136119;-72090 135713;-71118 136102;-69827 136155;-68643 136031;-67246 136049;-66327 135819;-65355 136084;-60669 136119;-60369 136808;-59626 137144;-58248 137321;-57152 136967;-56356 136419;-55967 135606;-55914 134705;-55914 130338;-78896 125070;-95125 125565;-95213 128871 18; +-20091 135644;-49858 135630;-49858 132699;-49443 132699;-49293 132430;-49280 131652;-47718 130071;-43302 130083;-43299 130083;-43278 125783 1;-43394 125565;-43439 125417;-43434 125170 1;-43423 124571;-43079 124035;-42484 123964 1;-42460 123961;-42438 123961;-42428 123939 1;-42365 123801;-42273 123699;-42122 123701 1;-41963 123703;-41840 123778;-41784 123926 1;-41777 123945;-41754 123936;-41734 123939 1;-41135 124023;-40796 124577;-40809 125182 1;-40814 125404;-40830 125539;-40928 125739;-40922 130033;-40917 130033;-40100 130042;-40077 125801 1;-40193 125583;-40238 125435;-40233 125188 1;-40222 124589;-39878 124053;-39283 123982 1;-39259 123979;-39237 123979;-39227 123957 1;-39164 123819;-39072 123717;-38921 123719 1;-38762 123721;-38639 123796;-38583 123944 1;-38576 123963;-38553 123954;-38533 123957 1;-37934 124041;-37595 124595;-37608 125200 1;-37613 125422;-37629 125557;-37727 125757;-37719 130095;-37624 130216;-37623 131207;-36642 131657;-36227 132082;-36218 133257;-36094 133381;-35342 133386;-35339 132979;-35029 132674;-34118 132681;-33837 132966;-32396 132976;-32377 130280;-32093 129471 1;-32225 129147;-32253 128931;-32212 128584 1;-32154 128088;-31923 127658;-31431 127571 1;-31410 127567;-31445 127540;-31456 127521 1;-31516 127416;-31395 127274;-31274 127271 1;-31157 127268;-31073 127432;-31130 127534 1;-30854 127619;-30632 127677;-30512 127940 1;-30412 128158;-30366 128293;-30205 128471 1;-30131 128553;-30079 128617;-30087 128727 1;-30094 128827;-30130 128894;-30212 128952 1;-30316 129025;-30300 129131;-30337 129252;-30411 129477;-30130 130277;-30149 132308;-28692 132322;-28355 132665;-27955 132669;-27656 132376;-27616 128434;-27430 128239;-27438 127889;-27330 127776;-27337 127471 1;-27343 127224;-27152 127024;-26905 127008;-26899 126783;-26749 126633;-26618 126783;-26618 126989 1;-26283 127001;-26110 127417;-26121 127752;-26017 127880;-26017 128283;-25811 128508;-25831 131855;-25620 132068;-25622 132447;-25401 132671;-25079 132673;-24872 132469;-24854 129999;-24625 129773;-24622 129318 1;-24616 128430;-23974 127846;-23117 127612;-23117 127272;-23227 127139 1;-23350 126990;-23299 126748;-23130 126653 1;-23117 126646;-23117 126633;-23117 126618 1;-23112 126405;-23012 126180;-22799 126176;-22803 125973;-22574 125782;-22388 125964;-22384 126167;-22362 126176 1;-22160 126193;-22079 126411;-22083 126613 1;-22083 126625;-22072 126629;-22061 126635 1;-21938 126706;-21859 126798;-21862 126940 1;-21865 127096;-21961 127206;-22110 127254;-22110 127621 1;-21193 127748;-20726 128455;-20490 129350;-20503 129813;-20259 130007;-20271 132150;-20077 132419;-20091 135644 18; +-23551 129615;-26814 129502;-29539 132352;-23551 129615 18; +-30664 135205;-28383 132918 1;-28305 132840;-28179 132840;-28101 132918 1;-28023 132995;-28023 133121;-28101 133199 1;-28992 134090;-29492 134590;-30383 135481 1;-30459 135557;-30582 135557;-30658 135481 1;-30732 135407;-30732 135286;-30664 135205 18; +-35052 130658;-31782 130658;-33709 132585 1;-34002 132878;-34002 133352;-33709 133645 1;-33460 133894;-33056 133894;-32807 133645 1;-31544 132382;-30836 131674;-29573 130411 1;-29228 130066;-29577 129262;-30065 129262 1;-32132 129262;-33292 129262;-35359 129262 1;-35604 129262;-35761 129338;-35934 129511 1;-37787 131364;-38825 132402;-40678 134255 1;-40989 134566;-40989 135072;-40678 135383 1;-40415 135646;-39989 135646;-39726 135383;-35052 130658 18; +-35070 136617;Munich SprintCup +-53727 113748;www.ol-gruenwald.de +-53661 117691;www.ol-landshut.de +-54952 117697;Landshut and Freising: +-96409 113730;Munich: +-96266 109471;Orienteering in ... + + + + + + + + + + + + + + + + diff --git a/examples/examples.qrc b/examples/examples.qrc new file mode 100644 index 0000000..39ad28d --- /dev/null +++ b/examples/examples.qrc @@ -0,0 +1,6 @@ + + + complete map.omap + forest sample.omap + + diff --git a/examples/forest sample.omap b/examples/forest sample.omap new file mode 100644 index 0000000..fc4a45e --- /dev/null +++ b/examples/forest sample.omap @@ -0,0 +1,754 @@ + + + + + +PURPLE +BLACK + + + +BLUE + +BROWN + + + + + + +GREEN + + + +YELLOW + + + + + + +A line joining points of equal height. The standard vertical interval between contours is 5 metres. The smallest bend in a contour is 0.25 mm from centre to centre of the lines. +Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with a normal contour line. +An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. +Slope lines may be drawn on the lower side of a contour line, e.g. along the line of a re-entrant or in a depression. They are used only where it is necessary to clarify the direction of slope.0 0;0 -750; +Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. +A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm.105 0;105 750;0 750;0 -135;-105 0;-105 750; +A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm.-555 0;555 0;450 0;450 750;-450 0;-450 750; +The line width of very high earth banks may be 0.25 mm.105 0;105 750;0 750;0 150;-105 0;-105 750; +The line width of very high earth banks may be 0.25 mm.-555 0;555 0;450 0;450 750;-450 0;-450 750; +Use this symbol to display the full extent of wide earth banks. +Distinct earth wall. Minimum height is 1 m. +A small or partly ruined earth wall shall be shown with a dashed line. Minimum height is 0.5 m. +An erosion gully or trench which is too small to be shown by symbol 106 is shown by a single line. The line width reflects the size of the gully. Minimum depth 1 m. The end of the line is pointed. +A small erosion gully or trench. Minimum depth 0.5 m. +A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (diameter of mound less than ca. 5 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. The symbol may not touch a contour line. +A small obvious elongated knoll which cannot be drawn to scale with a contour (length less than 12 m and width less than 4 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this must be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols overlap. The symbol may not touch a contour line.0 -600 1;165 -600;300 -331;300 0 1;300 332;165 600;0 600 1;-165 600;-300 332;-300 0 1;-300 -331;-165 -600;0 -600 3; +Small shallow natural depressions and hollows (minimum diameter 2 m) which cannot be shown to scale by contours are represented by a semicircle. Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol, which is orientated to north. Symbol 116 is used for man-made pits.600 -382 1;600 -51;332 217;0 217 1;-331 217;-600 -51;-600 -382; +Pits and holes with distinct steep sides which cannot be shown to scale by symbol 106 (minimum diameter 2 m). Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol which is orientated to north.231 -453;525 -453;0 747;-525 -453;-231 -453;0 75;231 -453 2; +An area of pits or knolls which is too intricate to be shown in detail. The density of randomly placed dots may vary according to the detail on the ground. +The size of the dots may vary. +This symbol can be used for a special small land form feature. The definition of the symbol must be given in the map legend.-600 -600;600 600;-600 600;600 -600; +An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line.90 150;90 750;0 150;0 750;-90 150;-90 750; +An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line.-540 0;540 0;450 0;450 750;-450 0;-450 750; +For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). +Use this symbol to display the full extent of a wide cliff. +In the case of unusual features such as rock pillars or massive cliffs or gigantic boulders, the rocks shall be shown in plan shape without tags. +A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.90 75;90 750;0 75;0 750;-90 75;-90 750; +A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.-540 0;540 0;450 0;450 750;-450 0;-450 750; +Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. +Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good.-450 0;450 0; +For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. +For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.-262 0;262 0; +Rocky pits, holes or mineshafts which may constitute a danger to the runner. Location is the centre of gravity of the symbol, which is orientated to north.262 -465;525 -465;0 735;-525 -465;-262 -465;0 135;262 -465 2; +A cave is represented by the same symbol as a rocky pit. In this case the symbol should be orientated to point up the slope as indicated opposite. The centre of gravity of the symbol marks the opening.262 -465;525 -465;0 735;-525 -465;-262 -465;0 135;262 -465 2; +A small distinct boulder (minimum height 1 m). Every boulder marked on the map should be immediately identifiable on the ground. To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). +To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). +A particularly large and distinct boulder. For gigantic boulders symbol 202 should be used. +An area which is covered with so many blocks of stone that they cannot be marked individually is shown with randomly orientated solid triangles with sides of ratio 8:6:5. A minimum of two triangles should be used. The going is indicated by the density of the triangles. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%.150 452;150 -509;-300 54;150 452 2; +To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%.180 542;180 -610;-360 65;180 542 2; +A small distinct group of boulders so closely clustered together that they cannot be marked individually. The symbol is an equilateral triangle orientated to the north. To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm).-600 347;600 347;0 -693;-600 347 2; +To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm).-750 434;750 434;0 -865;-750 434 2; +Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. +Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. +An area of soft sandy ground or gravel with no vegetation and where running is slow. Where an area of sandy ground is open but running is good, it is shown as open land (401/402). +A runnable area of rock without earth or vegetation is shown as bare rock. An area of rock covered with grass, moss or other low vegetation is shown as open land (401/402). +Large areas of water are shown with dot screen. Small areas of water should be shown with full colour. A black bank line indicates that the feature cannot be crossed. +A black bank line indicates that the feature cannot be crossed. + +Where the lake or pond is smaller than 1 mm² on the printed map, the bank line is omitted. +A water-filled pit or an area of water which is too small to be shown to scale. Location is the centre of gravity of the symbol, which is orientated to north.231 -453;525 -453;0 747;-525 -453;-231 -453;0 75;231 -453 2; +A crossable watercourse, minimum 2 m wide. The width of watercourses over 5 m wide should be shown to scale. +A crossable watercourse (including a major drainage ditch) less than 2 m wide. For better legibility a ditch in a marsh should be drawn as a crossable watercourse (305). +A natural or man-made minor water channel which may contain water only intermittently. +A marsh or trickle of water which is too narrow to be shown with symbol 310 (less than ca. 5 m wide). +A marsh which is uncrossable or dangerous for the runner. A black line surrounds the symbol. +A black line surrounds the symbol. + +A crossable marsh, usually with a distinct edge. The symbol should be combined with vegetation symbols to show runnability and openness. Where a small marsh area should be combined with either 403/404 it is permitted to use 401/402 to improve legibility. +-375 -225;375 -225;-375 225;375 225; +An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness.-675 0;675 0;-675 0;675 0; +-675 -450;675 -450;-675 450;675 450;-1537 0;-187 0;188 0;1537 0; +Wells and captive springs, which are clearly visible on the ground. +The source of a stream with a distinct outflow. The symbol is orientated to open downstream.600 -382 1;600 -51;332 217;0 217 1;-331 217;-600 -51;-600 -382; +A special small water feature. The definition of the symbol must always be given in the map legend.-600 -600;600 600;-600 600;600 -600; +Cultivated land, fields, meadows, grassland, etc. without trees, offering easy running. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. +Meadows with scattered trees or bushes, with grass or similar ground cover offering easy running. Areas smaller than 10 mm at the maps scale are shown as open land (401). Individual trees may be added (418, 419, 420). If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. +Heath, moorland, felled areas, newly planted areas (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, heather or tall grass. Symbol 403 may be combined with symbols 407 and 409 to show reduced runnability. +Where there are scattered trees in rough open land, areas of white (or green) should appear in the tone. Such an area may be generalised by using a regular pattern of large white dots in the yellow screen. Areas smaller than 16 mm in the maps scale are shown as rough open land (403). Individual trees may be added (418, 419, 420). +Typically open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. +An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. +An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 60-80% of normal speed. This symbol may not be combined with 406 or 408. +An area with dense trees or thicket (low visibility) which reduce running to ca. 20-60% of normal speed. +An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. +An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced to ca. 0-20% of normal speed. +Line of minimum width for symbol 410. +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. +Land planted with fruit trees or bushes. The dot lines may be orientated to show the +direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. +The green lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used.0 -975;0 975;0 -975;0 975; +The boundary of cultivated land when not shown with other symbols (fence, wall, path, etc.) is shown with a black line. A permanent boundary between different types of cultivated land is also shown with this symbol. +Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. +A distinct forest edge or very distinct vegetation boundary within the forest. +Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend.-600 -600;600 600;600 -600;-600 600; +Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. +Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. +A road with two carriageways. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. +A road under construction may be shown with broken lines. +Road wider than 5m. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. +A road under construction may be shown with broken lines. +Road 3-5 m wide. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. +A road under construction may be shown with broken lines. +A maintained road suitable for motor vehicles in all weather. Width less than 3 m. +A track or poorly maintained road suitable for vehicles only when travelling slowly. Width less than 3 m. +A large path, or old vehicle track, which is distinct on the ground. +A small path or (temporary) forest extraction track which can be followed at competition speed. +A less distinct small path or forestry extraction track. +A distinct ride, less than ca. 5 m wide. A ride is a linear break in the forest (usually plantation) which does not have a distinct path along it. Where there is a path along a ride, symbols 507 or 508 should be used in place of symbol 509. +A footbridge with no path leading to it. +Note: if the stream is wider than 0.25mm, adjust this symbol so it extends 0.5mm over both sides of the stream!0 -937;0 938; +A railway or other kind of railed track (tramway, truckway, etc.).0 -788;0 788; +Power line, cableway or skilift. The bars indicate the exact location of the pylons.0 -555;0 555; +Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline.0 -1125;0 1125; +A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it.-450 -780;0 0;15 0;-15 0;0 0;450 -780; +A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it.450 -780;0 0;-450 -780; +A stone wall or stone-faced bank. +A ruined stone wall may be shown by a dashed line. +A stone wall higher than ca 1.5 m, not crossable to the average orienteer. +A wooden or wire fence less than ca. 1.5 m high.0 0;375 650; +A ruined fence may be shown with a dashed line.0 0;375 650; +A boarded or wire fence higher than ca 1.5 m, not crossable to the average orienteer, eg. deer fence.450 0;825 650;-450 0;-75 650; +All ways through or over high fences or walls must be indicated. The symbol may also be used for a gate through or stile over a stone wall (519) or a fence (522) or a pipeline (534).450 -750;450 750;-450 -750;-450 750; +A building is shown with its ground plan so far as the scale permits. +-375 -375;375 -375;375 375;-375 375;-375 -375 2; +Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. +Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. +Areas which are permanently forbidden to the runner are shown as out of bounds. The screen is superimposed on the normal map detail. A bounding line may be drawn if there is no natural boundary (see 709). +A bounding line may be drawn if there is no natural boundary (see 709). +An area of hard standing used for parking or other purposes. + + +The ground plan of a ruin is shown to scale, down to the minimum size shown opposite. Very small ruins may be drawn with a solid line. +Very small ruins may be drawn with a solid line. +Very small ruins may be drawn with a solid line.480 480;480 -480;-480 -480;-480 480;480 480 2; +A firing range is shown with a special symbol to indicate the need for caution. Associated buildings are individually marked.2550 -1050;2550 1050;2550 0;-375 -889;0 0;-375 890;2550 0 2; +A distinct grave marked by a stone or shrine. Location is at the centre of gravity of the symbol, which is orientated to north. A cemetery is shown by using grave symbols as space permits.0 -645;0 855;-525 -193;525 -193; +A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under.0 0;-531 -531;0 0;-531 531; +A pipeline which cannot be crossed.0 0;-531 -531;0 0;-531 531; +A high tower or large pylon, standing above the level of the surrounding forest. Location is at the centre of gravity of the symbol.-1050 0;1050 0;0 -1050;0 1050; +An obvious shooting platform or seat, or small tower. Location is at the centre of gravity of the symbol.0 -393;0 1107;-750 -393;750 -393; +Cairn, memorial stone or boundary stone (or a trigonometric point in some countries) more than 0.5 m high.0 0; +A fodder rack which is free standing or built on to a tree. Location is at the centre of gravity of the symbol. For land access reasons these may be omitted.0 -483;0 1017;-750 -49;0 -483;750 -49; +Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. +Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend.-600 -600;600 600;600 -600;-600 600; +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. + +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. + +At least three registration marks must be placed within the frame of a map in a non-symmetrical position. In addition, a colour check should also be possible.0 -2000;0 2000;-2000 0;2000 0; +Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. +Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. +The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point.-3500 2021;0 -4041;3500 2021;-3500 2021 18; +The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. +The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. +Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. +A marked route is shown on the map with a dashed line. +The finish is shown by two concentric circles.0 0; +A boundary which it is not permitted to cross. +A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards.-1500 725 1;-500 450;500 450;1500 725;-1500 -725 1;-500 -450;500 -450;1500 -725; +An out-of-bounds area, see also symbol 528, is shown with vertical stripes. +A bounding line may be drawn if there is no natural boundary, as follows: +- a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, +- a dashed line indicates intermittent marking on the ground, +- no line indicates no marking on the ground. +A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. +A dashed line indicates intermittent marking on the ground. +An area presenting danger to the competitor is shown with cross-hatched diagonal lines. +A route which is out-of-bounds is shown with crosses.1061 -1061;-1061 1061;1061 1061;-1061 -1061; +The location of a first aid post.-1500 0;1500 0;0 -1500;0 1500; +The location of a refreshment point which is not at a control.-1340 -1395;-820 1380;1340 -1395;820 1380;0 -1770 1;600 -1770;1200 -1620;1340 -1395 1;1200 -1170;600 -1020;0 -1020 1;-600 -1020;-1200 -1170;-1340 -1395 1;-1200 -1620;-600 -1770;0 -1770;-820 1380 1;-680 1680;680 1680;820 1380; +This symbol provides a simple and quick way to make training courses. + +The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach.3041 0;3541 0;-3021 3500;3041 0;-3021 -3500;-3021 3500 18;-600 0;600 0;0 0; + +The OpenOrienteering Logo.-12797 -557 1;-12755 -447;-12770 -420;-12873 -420 1;-12953 -420;-12973 -440;-12973 -520 1;-12973 -635;-12838 -663;-12797 -557 18;-933 2063 1;-933 1925;-920 1900;-851 1900 1;-780 1900;-770 1921;-781 2050 1;-789 2154;-814 2203;-863 2213 1;-920 2224;-933 2197;-933 2063;-933 2063 18;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 18;-4230 -3923 1;-4183 -3737;-4160 -3527;-4160 -3296;-4160 -1395;-5113 -1395;-5113 -3182 1;-5113 -3488;-5155 -3707;-5235 -3833 1;-5317 -3961;-5468 -4027;-5690 -4027 1;-5759 -4027;-5830 -4021;-5907 -4013 1;-5983 -4008;-6052 -4004;-6112 -3993;-6112 -1395;-7064 -1395;-7064 -4646 1;-6903 -4693;-6694 -4736;-6438 -4774 1;-6181 -4818;-5913 -4838;-5631 -4838 1;-5347 -4838;-5110 -4800;-4921 -4723 1;-4730 -4652;-4579 -4547;-4467 -4410 1;-4357 -4273;-4277 -4111;-4230 -3923 18;-13524 1118 1;-13605 1126;-13667 1137;-13708 1150;-13708 3722;-14662 3722;-14662 536 1;-14492 476;-14292 420;-14060 369 1;-13827 314;-13565 286;-13280 286 1;-13229 286;-13167 290;-13096 299 1;-13022 303;-12951 312;-12878 325 1;-12806 333;-12733 345;-12660 363 1;-12587 375;-12526 392;-12474 414;-12634 1201 1;-12719 1180;-12819 1158;-12936 1137 1;-13051 1112;-13174 1098;-13306 1098 1;-13366 1098;-13439 1105;-13524 1118 18;-11093 -200 1;-11204 -101;-11336 -52;-11490 -52 1;-11642 -52;-11777 -101;-11893 -200 1;-12004 -303;-12059 -441;-12059 -616 1;-12059 -791;-12004 -927;-11893 -1026 1;-11777 -1128;-11642 -1179;-11490 -1179 1;-11336 -1179;-11204 -1128;-11093 -1026 1;-10978 -927;-10920 -791;-10920 -616 1;-10920 -441;-10978 -303;-11093 -200 18;-11009 3722;-11963 3722;-11963 357;-11009 357;-11009 3722 18;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 18;-5220 1105 1;-5297 1110;-5365 1115;-5425 1125;-5425 3722;-6378 3722;-6378 472 1;-6217 425;-6007 382;-5751 344 1;-5495 301;-5227 280;-4945 280 1;-4658 280;-4422 318;-4235 395 1;-4042 467;-3890 572;-3781 709 1;-3669 845;-3591 1007;-3544 1195 1;-3497 1381;-3474 1592;-3474 1821;-3474 3722;-4427 3722;-4427 1937 1;-4427 1630;-4467 1411;-4549 1285 1;-4628 1156;-4780 1092;-5002 1092 1;-5070 1092;-5143 1097;-5220 1105 18;-2636 2346;-2636 -481;-1683 -634;-1683 357;-539 357;-539 1150;-1683 1150;-1683 2333 1;-1683 2535;-1648 2694;-1581 2813 1;-1508 2933;-1365 2993;-1151 2993 1;-1050 2993;-945 2984;-838 2966 1;-728 2946;-627 2918;-539 2884;-404 3626 1;-518 3673;-646 3712;-787 3748 1;-928 3780;-1102 3799;-1305 3799 1;-1566 3799;-1781 3764;-1951 3696 1;-2123 3624;-2259 3526;-2361 3402 1;-2463 3274;-2536 3121;-2579 2941 1;-2618 2763;-2636 2565;-2636 2346 18;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 18;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700;5923 1700 18;8718 1118 1;8637 1126;8575 1137;8534 1150;8534 3722;7581 3722;7581 536 1;7750 476;7950 420;8182 369 1;8415 314;8677 286;8962 286 1;9013 286;9075 290;9146 299 1;9220 303;9292 312;9365 325 1;9437 333;9510 345;9583 363 1;9655 375;9717 392;9768 414;9608 1201 1;9523 1180;9424 1158;9306 1137 1;9191 1112;9069 1098;8937 1098 1;8877 1098;8804 1105;8718 1118 18;10350 -200 1;10238 -303;10183 -441;10183 -616 1;10183 -791;10238 -927;10350 -1026 1;10465 -1128;10600 -1179;10752 -1179 1;10906 -1179;11038 -1128;11149 -1026 1;11264 -927;11323 -791;11323 -616 1;11323 -441;11264 -303;11149 -200 1;11038 -101;10906 -52;10752 -52 1;10600 -52;10465 -101;10350 -200 18;11233 3722;10279 3722;10279 357;11233 357;11233 3722 18;14726 709 1;14836 845;14916 1007;14963 1195 1;15010 1381;15033 1592;15033 1821;15033 3722;14080 3722;14080 1937 1;14080 1630;14039 1411;13958 1285 1;13876 1156;13725 1092;13504 1092 1;13435 1092;13363 1097;13287 1105 1;13210 1110;13142 1115;13082 1125;13082 3722;12129 3722;12129 472 1;12290 425;12499 382;12756 344 1;13012 301;13280 280;13562 280 1;13847 280;14083 318;14272 395 1;14463 467;14614 572;14726 709 18;-17051 -1307 1;-17299 -1307;-17525 -1349;-17729 -1434 1;-17931 -1521;-18102 -1639;-18247 -1794 1;-18392 -1951;-18505 -2139;-18587 -2355 1;-18669 -2579;-18708 -2820;-18708 -3085 1;-18708 -3350;-18669 -3591;-18587 -3808 1;-18502 -4027;-18387 -4211;-18242 -4365 1;-18092 -4518;-17917 -4638;-17717 -4723 1;-17512 -4808;-17291 -4851;-17051 -4851 1;-16808 -4851;-16586 -4808;-16386 -4723 1;-16182 -4638;-16006 -4518;-15861 -4365 1;-15716 -4211;-15603 -4027;-15523 -3808 1;-15442 -3591;-15401 -3350;-15401 -3085 1;-15401 -2820;-15440 -2579;-15517 -2355 1;-15593 -2139;-15703 -1951;-15848 -1794 1;-15993 -1639;-16168 -1521;-16373 -1434 1;-16574 -1349;-16800 -1307;-17051 -1307 18;-17051 -2126 1;-16834 -2126;-16668 -2210;-16552 -2383 1;-16433 -2557;-16373 -2792;-16373 -3085 1;-16373 -3380;-16433 -3610;-16552 -3777 1;-16668 -3946;-16834 -4034;-17051 -4034 1;-17269 -4034;-17437 -3946;-17557 -3777 1;-17675 -3610;-17736 -3380;-17736 -3085 1;-17736 -2792;-17675 -2557;-17557 -2383 1;-17437 -2210;-17269 -2126;-17051 -2126 18;-14662 -213;-14662 -4646 1;-14576 -4673;-14478 -4697;-14369 -4716 1;-14257 -4743;-14142 -4765;-14022 -4781 1;-13898 -4798;-13776 -4811;-13652 -4819 1;-13524 -4833;-13402 -4838;-13287 -4838 1;-13009 -4838;-12763 -4796;-12544 -4711 1;-12328 -4630;-12144 -4513;-11995 -4358 1;-11846 -4210;-11732 -4027;-11657 -3808 1;-11574 -3591;-11535 -3348;-11535 -3078 1;-11535 -2819;-11566 -2582;-11629 -2368 1;-11694 -2156;-11788 -1972;-11911 -1819 1;-12034 -1666;-12189 -1546;-12373 -1461 1;-12556 -1376;-12765 -1333;-13006 -1333 1;-13137 -1333;-13261 -1346;-13377 -1371 1;-13492 -1395;-13602 -1432;-13708 -1479;-13708 -213;-14662 -213 18;-13184 -2139 1;-12733 -2139;-12506 -2443;-12506 -3054 1;-12506 -3348;-12572 -3582;-12704 -3756 1;-12837 -3936;-13032 -4027;-13293 -4027 1;-13379 -4027;-13457 -4021;-13530 -4013 1;-13602 -4008;-13662 -4004;-13708 -3993;-13708 -2274 1;-13648 -2234;-13572 -2202;-13479 -2177 1;-13381 -2152;-13282 -2139;-13184 -2139 18;-9182 -1307 1;-9485 -1307;-9750 -1352;-9976 -1440 1;-10198 -1530;-10383 -1652;-10533 -1806 1;-10678 -1964;-10787 -2149;-10858 -2362 1;-10926 -2575;-10962 -2807;-10962 -3054 1;-10962 -3352;-10917 -3612;-10827 -3833 1;-10733 -4060;-10611 -4248;-10462 -4396 1;-10314 -4547;-10141 -4660;-9950 -4736 1;-9754 -4813;-9553 -4851;-9349 -4851 1;-8872 -4851;-8494 -4705;-8217 -4410 1;-7939 -4120;-7801 -3692;-7801 -3123 1;-7801 -3069;-7803 -3007;-7808 -2939 1;-7810 -2873;-7816 -2817;-7819 -2766;-9982 -2766 1;-9961 -2569;-9870 -2413;-9707 -2299 1;-9545 -2184;-9327 -2126;-9055 -2126 1;-8881 -2126;-8708 -2141;-8542 -2171 1;-8372 -2205;-8234 -2246;-8127 -2292;-7999 -1517 1;-8051 -1493;-8119 -1468;-8204 -1440 1;-8289 -1416;-8385 -1395;-8492 -1378 1;-8594 -1356;-8706 -1339;-8824 -1326 1;-8944 -1314;-9063 -1307;-9182 -1307 18;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418 18;-17051 -2126 1;-17269 -2126;-17437 -2210;-17557 -2383 1;-17675 -2557;-17736 -2792;-17736 -3085 1;-17736 -3380;-17675 -3610;-17557 -3777 1;-17437 -3946;-17269 -4034;-17051 -4034 1;-16834 -4034;-16668 -3946;-16552 -3777 1;-16433 -3610;-16373 -3380;-16373 -3085 1;-16373 -2792;-16433 -2557;-16552 -2383 1;-16668 -2210;-16834 -2126;-17051 -2126 18;-17064 -2593 1;-16944 -2591;-16842 -2808;-16838 -3077 1;-16834 -3347;-16928 -3567;-17049 -3569 1;-17170 -3571;-17271 -3354;-17275 -3084 1;-17280 -2815;-17185 -2595;-17064 -2593 18;-8496 3810 1;-8798 3810;-9062 3765;-9288 3677 1;-9510 3588;-9696 3466;-9845 3312 1;-9990 3154;-10098 2969;-10171 2756 1;-10240 2542;-10273 2311;-10273 2065 1;-10273 1766;-10230 1507;-10140 1285 1;-10045 1058;-9923 870;-9775 722 1;-9625 572;-9455 459;-9264 382 1;-9067 305;-8867 267;-8662 267 1;-8184 267;-7806 414;-7529 709 1;-7252 998;-7113 1426;-7113 1995 1;-7113 2050;-7116 2112;-7119 2180 1;-7124 2245;-7128 2301;-7132 2353;-9296 2353 1;-9273 2550;-9182 2704;-9020 2820 1;-8857 2934;-8640 2993;-8368 2993 1;-8193 2993;-8022 2978;-7855 2948 1;-7684 2912;-7546 2873;-7440 2826;-7311 3601 1;-7363 3626;-7431 3651;-7516 3677 1;-7603 3703;-7697 3724;-7804 3741 1;-7908 3763;-8017 3779;-8137 3793 1;-8257 3804;-8376 3810;-8496 3810 18;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700 18;1726 3810 1;1422 3810;1159 3765;933 3677 1;712 3588;524 3466;376 3312 1;231 3154;123 2969;49 2756 1;-19 2542;-52 2311;-52 2065 1;-52 1766;-9 1507;81 1285 1;177 1058;298 870;446 722 1;596 572;766 459;958 382 1;1154 305;1354 267;1559 267 1;2038 267;2416 414;2692 709 1;2968 998;3109 1426;3109 1995 1;3109 2050;3105 2112;3102 2180 1;3097 2245;3094 2301;3088 2353;926 2353 1;947 2550;1039 2704;1201 2820 1;1364 2934;1581 2993;1854 2993 1;2029 2993;2199 2978;2365 2948 1;2538 2912;2675 2873;2782 2826;2910 3601 1;2859 3626;2790 3651;2705 3677 1;2619 3703;2524 3724;2417 3741 1;2314 3763;2204 3779;2084 3793 1;1965 3804;1846 3810;1726 3810 18;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700 18;5463 3810 1;5160 3810;4895 3765;4668 3677 1;4446 3588;4262 3466;4112 3312 1;3967 3154;3858 2969;3787 2756 1;3719 2542;3684 2311;3684 2065 1;3684 1766;3729 1507;3819 1285 1;3911 1058;4033 870;4183 722 1;4332 572;4503 459;4695 382 1;4891 305;5091 267;5297 267 1;5773 267;6151 414;6428 709 1;6706 998;6844 1426;6844 1995 1;6844 2050;6843 2112;6837 2180 1;6834 2245;6829 2301;6826 2353;4663 2353 1;4683 2550;4775 2704;4938 2820 1;5100 2934;5318 2993;5590 2993 1;5765 2993;5936 2978;6103 2948 1;6272 2912;6411 2873;6518 2826;6646 3601 1;6594 3626;6526 3651;6441 3677 1;6356 3703;6259 3724;6152 3741 1;6051 3763;5939 3779;5821 3793 1;5701 3804;5581 3810;5463 3810 18;4663 1700;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700 18;17092 4924 1;16887 4924;16682 4905;16477 4867 1;16272 4832;16083 4785;15908 4725;16074 3926 1;16224 3986;16379 4032;16543 4067 1;16707 4101;16896 4119;17105 4119 1;17377 4119;17570 4059;17680 3939 1;17796 3819;17854 3666;17854 3479;17854 3357 1;17750 3404;17644 3441;17533 3466 1;17427 3487;17309 3498;17182 3498 1;16717 3498;16361 3361;16113 3087 1;15866 2811;15743 2424;15743 1930 1;15743 1683;15781 1458;15857 1259 1;15934 1053;16044 878;16189 733 1;16339 589;16521 478;16734 402 1;16947 320;17187 280;17457 280 1;17572 280;17689 286;17809 299 1;17931 307;18053 320;18173 337 1;18292 354;18405 375;18512 402 1;18624 422;18722 446;18806 472;18806 3299 1;18806 3849;18665 4257;18385 4522 1;18107 4790;17677 4924;17092 4924 18;17360 2730 1;17459 2730;17550 2718;17636 2691 1;17720 2666;17794 2636;17854 2603;17854 1080 1;17807 1072;17750 1065;17687 1060 1;17623 1052;17548 1047;17464 1047 1;17212 1047;17024 1130;16900 1297 1;16776 1464;16714 1675;16714 1930 1;16714 2463;16930 2730;17360 2730 18;-17051 2993 1;-17269 2993;-17437 2908;-17557 2736 1;-17675 2561;-17736 2326;-17736 2033 1;-17736 1738;-17675 1509;-17557 1342 1;-17437 1171;-17269 1085;-17051 1085 1;-16834 1085;-16668 1171;-16552 1342 1;-16433 1509;-16373 1738;-16373 2033 1;-16373 2326;-16433 2561;-16552 2736 1;-16668 2908;-16834 2993;-17051 2993 18;-17064 2526 1;-16944 2528;-16842 2311;-16838 2042 1;-16834 1772;-16928 1552;-17049 1550 1;-17170 1548;-17271 1765;-17275 2035 1;-17280 2304;-17185 2524;-17064 2526 18;-17051 3810 1;-17299 3810;-17525 3769;-17729 3684 1;-17931 3598;-18102 3479;-18247 3324 1;-18392 3168;-18505 2980;-18587 2763 1;-18669 2540;-18708 2298;-18708 2033 1;-18708 1768;-18669 1526;-18587 1310 1;-18502 1092;-18387 906;-18242 754 1;-18092 600;-17917 480;-17717 395 1;-17512 310;-17291 267;-17051 267 1;-16808 267;-16586 310;-16386 395 1;-16182 480;-16006 600;-15861 754 1;-15716 906;-15603 1092;-15523 1310 1;-15442 1526;-15401 1768;-15401 2033 1;-15401 2298;-15440 2540;-15517 2763 1;-15593 2980;-15703 3168;-15848 3324 1;-15993 3479;-16168 3598;-16373 3684 1;-16574 3769;-16800 3810;-17051 3810 18;-17051 2993 1;-16834 2993;-16668 2908;-16552 2736 1;-16433 2561;-16373 2326;-16373 2033 1;-16373 1738;-16433 1509;-16552 1342 1;-16668 1171;-16834 1085;-17051 1085 1;-17269 1085;-17437 1171;-17557 1342 1;-17675 1509;-17736 1738;-17736 2033 1;-17736 2326;-17675 2561;-17557 2736 1;-17437 2908;-17269 2993;-17051 2993 18;17090 5390 1;16864 5390;16623 5362;16391 5321 1;16389 5320;16389 5320;16387 5320 1;16169 5284;15963 5240;15758 5170 1;15555 5102;15409 4847;15450 4637 1;15450 4635;15450 4634;15450 4632;15538 4221 1;15549 4165;15526 4109;15478 4079 1;15431 4049;15369 4052;15324 4088 1;15245 4152;15138 4191;15033 4191;14080 4191 1;13930 4193;13775 4107;13692 3983 1;13669 3944;13625 3921;13580 3921 1;13534 3921;13492 3944;13466 3983 1;13385 4107;13233 4191;13083 4191;12131 4191 1;12001 4191;11866 4131;11781 4032 1;11755 4004;11719 3987;11680 3987 1;11642 3987;11606 4004;11581 4032 1;11496 4131;11364 4191;11234 4191;10281 4191 1;10048 4193;9815 3957;9815 3724;9815 1796 1;9815 1758;9798 1721;9770 1697 1;9741 1670;9703 1658;9665 1663 1;9608 1670;9553 1668;9498 1655 1;9433 1638;9341 1618;9227 1597 1;9221 1595;9217 1593;9210 1592 1;9161 1582;9142 1582;9157 1584 1;9118 1578;9078 1590;9048 1615 1;9018 1640;9001 1678;9001 1716 1;9001 1716;9001 3724;9001 3724 1;9001 3957;8768 4193;8535 4191;7582 4191 1;7424 4191;7262 4099;7183 3962 1;7162 3926;7125 3900;7082 3894 1;7040 3889;6999 3902;6969 3932 1;6935 3966;6899 3996;6855 4016 1;6768 4059;6679 4092;6574 4124 1;6467 4157;6364 4178;6253 4195 1;6244 4197;6236 4199;6226 4202 1;6107 4227;5991 4240;5870 4254 1;5738 4267;5601 4279;5461 4279 1;5121 4279;4808 4231;4520 4120 1;4514 4118;4506 4114;4498 4110 1;4220 3998;3977 3840;3778 3636 1;3717 3566;3663 3494;3609 3412 1;3571 3361;3503 3344;3445 3371 1;3389 3396;3357 3459;3370 3521 1;3404 3711;3293 3930;3120 4016 1;3034 4060;2945 4092;2837 4124 1;2732 4157;2628 4178;2517 4195 1;2508 4197;2499 4199;2490 4202 1;2372 4227;2256 4240;2134 4254 1;2001 4267;1866 4279;1726 4279 1;1386 4279;1071 4231;785 4120 1;778 4118;771 4114;761 4110 1;562 4029;383 3917;218 3782 1;186 3757;145 3748;105 3757 1;64 3765;32 3793;13 3829 1;-35 3929;-124 4013;-229 4054 1;-362 4109;-512 4164;-678 4204 1;-871 4251;-1073 4266;-1303 4266 1;-1606 4266;-1876 4227;-2127 4126 1;-2355 4031;-2561 3892;-2717 3706 1;-2719 3701;-2721 3699;-2719 3702 1;-2716 3707;-2718 3706;-2721 3700 1;-2724 3694;-2734 3667;-2764 3624 1;-2799 3577;-2859 3558;-2915 3576 1;-2969 3594;-3007 3646;-3006 3704;-3006 3724 1;-3006 3957;-3239 4193;-3473 4191;-4427 4191 1;-4577 4193;-4730 4107;-4814 3983 1;-4838 3944;-4880 3921;-4927 3921 1;-4972 3921;-5013 3944;-5038 3983 1;-5122 4107;-5274 4191;-5421 4191;-6377 4191 1;-6533 4191;-6696 4099;-6777 3962 1;-6796 3926;-6833 3900;-6875 3894 1;-6916 3889;-6958 3902;-6988 3932 1;-7021 3966;-7059 3996;-7101 4016 1;-7101 4017;-7104 4016;-7104 4016 1;-7191 4059;-7278 4092;-7384 4124 1;-7489 4157;-7592 4178;-7704 4195 1;-7714 4197;-7722 4199;-7729 4199 1;-7731 4201;-7733 4202;-7735 4204 1;-7849 4227;-7966 4240;-8088 4254 1;-8221 4267;-8355 4279;-8496 4279 1;-8836 4279;-9151 4231;-9437 4120 1;-9444 4118;-9450 4114;-9461 4110 1;-9737 3998;-9980 3840;-10181 3635 1;-10185 3631;-10185 3628;-10186 3624 1;-10218 3591;-10250 3536;-10300 3470 1;-10333 3425;-10393 3404;-10450 3423 1;-10505 3440;-10541 3493;-10541 3549 1;-10541 3549;-10541 3724;-10541 3724 1;-10541 3957;-10774 4193;-11008 4191;-11961 4191 1;-12194 4193;-12427 3957;-12427 3724;-12427 1796 1;-12427 1758;-12444 1721;-12472 1697 1;-12501 1670;-12539 1658;-12577 1663 1;-12634 1670;-12689 1668;-12744 1655 1;-12809 1638;-12901 1618;-13015 1597 1;-13021 1595;-13026 1593;-13032 1592 1;-13081 1582;-13101 1582;-13086 1584 1;-13124 1578;-13164 1590;-13194 1615 1;-13224 1640;-13240 1678;-13240 1716;-13240 3724 1;-13240 3957;-13474 4193;-13707 4191;-14660 4191 1;-14893 4193;-15126 3957;-15126 3724;-15126 3532 1;-15126 3476;-15164 3423;-15218 3406 1;-15273 3387;-15335 3408;-15368 3455 1;-15420 3526;-15463 3591;-15511 3643 1;-15517 3651;-15517 3652;-15518 3656 1;-15703 3851;-15933 4002;-16189 4110 1;-16458 4224;-16748 4279;-17051 4279 1;-17346 4279;-17629 4225;-17889 4120 1;-17895 4118;-17902 4114;-17909 4112 1;-18160 4005;-18392 3849;-18580 3651 1;-18586 3643;-18587 3641;-18588 3637 1;-18776 3434;-18919 3196;-19017 2931 1;-19017 2931;-19017 2933;-19019 2927 1;-19122 2648;-19176 2350;-19176 2033 1;-19176 1718;-19123 1419;-19019 1142;-19019 1139 1;-18913 875;-18770 642;-18587 446 1;-18407 241;-18054 35;-17897 -33 1;-17894 -33;-17891 -35;-17889 -37 1;-17628 -143;-17344 -200;-17051 -200 1;-16755 -200;-16468 -143;-16206 -33 1;-15946 76;-15707 235;-15518 433 1;-15470 487;-15425 560;-15368 639 1;-15335 685;-15273 705;-15218 687 1;-15164 669;-15126 617;-15126 559;-15126 538 1;-15126 435;-15088 329;-15023 247 1;-14983 198;-14983 129;-15023 80 1;-15088 -1;-15126 -108;-15126 -211;-15126 -1586 1;-15126 -1644;-15164 -1696;-15218 -1714 1;-15273 -1733;-15335 -1712;-15368 -1666 1;-15420 -1594;-15463 -1529;-15506 -1483 1;-15508 -1478;-15511 -1474;-15517 -1465 1;-15701 -1271;-15928 -1115;-16189 -1005 1;-16463 -889;-16753 -841;-17051 -841 1;-17341 -841;-17624 -888;-17889 -995 1;-17895 -997;-17902 -1001;-17909 -1003 1;-18165 -1113;-18394 -1273;-18578 -1463 1;-18582 -1468;-18582 -1470;-18584 -1474 1;-18586 -1476;-18587 -1479;-18588 -1483 1;-18776 -1684;-18919 -1924;-19019 -2192 1;-19122 -2472;-19176 -2770;-19176 -3085 1;-19176 -3401;-19122 -3698;-19019 -3973 1;-18915 -4238;-18774 -4473;-18586 -4675 1;-18582 -4680;-18580 -4682;-18580 -4684 1;-18577 -4686;-18573 -4688;-18572 -4690 1;-18384 -4883;-18152 -5043;-17897 -5151 1;-17894 -5153;-17891 -5154;-17889 -5156 1;-17628 -5263;-17344 -5317;-17051 -5317 1;-16755 -5317;-16468 -5263;-16204 -5150 1;-15943 -5038;-15707 -4885;-15518 -4684 1;-15468 -4631;-15425 -4560;-15368 -4481 1;-15335 -4433;-15273 -4413;-15218 -4431 1;-15164 -4449;-15126 -4502;-15126 -4560 1;-15126 -4560;-15126 -4648;-15126 -4648 1;-15126 -4840;-14978 -5038;-14794 -5093 1;-14694 -5123;-14598 -5144;-14499 -5165 1;-14499 -5165;-14477 -5168;-14452 -5173 1;-14340 -5198;-14217 -5219;-14085 -5240 1;-13922 -5266;-13842 -5266;-13682 -5281 1;-13547 -5294;-13415 -5306;-13286 -5306 1;-12964 -5306;-12660 -5253;-12380 -5145 1;-12373 -5143;-12371 -5143;-12369 -5143 1;-12101 -5041;-11864 -4883;-11665 -4681 1;-11657 -4671;-11653 -4668;-11649 -4665 1;-11536 -4548;-11441 -4412;-11358 -4260 1;-11334 -4218;-11289 -4192;-11242 -4192 1;-11193 -4192;-11148 -4218;-11125 -4260 1;-11028 -4434;-10923 -4593;-10791 -4726 1;-10600 -4917;-10372 -5065;-10128 -5165 1;-10125 -5165;-10124 -5166;-10118 -5168 1;-9872 -5263;-9613 -5317;-9350 -5317 1;-8783 -5317;-8251 -5120;-7882 -4730 1;-7846 -4691;-7812 -4641;-7767 -4581 1;-7733 -4538;-7676 -4519;-7622 -4536 1;-7569 -4553;-7532 -4600;-7529 -4656 1;-7526 -4846;-7376 -5039;-7192 -5093 1;-6999 -5148;-6777 -5186;-6518 -5226 1;-6513 -5227;-6510 -5229;-6505 -5231 1;-6226 -5276;-5933 -5306;-5630 -5306 1;-5305 -5306;-5020 -5259;-4754 -5153 1;-4499 -5054;-4271 -4908;-4106 -4706 1;-3948 -4513;-3839 -4282;-3777 -4034 1;-3719 -3803;-3694 -3557;-3694 -3294;-3694 -1395 1;-3694 -1161;-3927 -927;-4160 -927;-5113 -927 1;-5263 -927;-5419 -1011;-5502 -1136 1;-5525 -1174;-5569 -1198;-5613 -1198 1;-5660 -1198;-5701 -1174;-5727 -1136 1;-5808 -1012;-5960 -927;-6110 -927;-7063 -927 1;-7220 -929;-7380 -1021;-7459 -1158 1;-7479 -1194;-7516 -1219;-7558 -1224 1;-7599 -1231;-7641 -1216;-7671 -1186 1;-7706 -1151;-7744 -1121;-7790 -1100 1;-7898 -1046;-7976 -1023;-8071 -995 1;-8178 -963;-8281 -942;-8392 -924 1;-8400 -923;-8409 -921;-8417 -920 1;-8419 -918;-8419 -916;-8421 -916 1;-8527 -894;-8644 -874;-8775 -861 1;-8917 -846;-9054 -841;-9183 -841 1;-9523 -841;-9837 -888;-10125 -999 1;-10132 -1003;-10138 -1004;-10148 -1010 1;-10205 -1033;-10261 -1066;-10333 -1102 1;-10380 -1126;-10438 -1119;-10478 -1085 1;-10520 -1051;-10537 -996;-10520 -944 1;-10481 -826;-10453 -709;-10453 -616 1;-10453 -441;-10525 -246;-10637 -67 1;-10667 -20;-10665 40;-10633 84 1;-10575 164;-10541 262;-10541 359;-10541 559 1;-10541 617;-10505 669;-10450 687 1;-10393 705;-10333 685;-10300 639 1;-10233 547;-10171 461;-10103 392 1;-9914 202;-9685 53;-9442 -45 1;-9439 -46;-9435 -48;-9433 -50 1;-9432 -50;-9431 -50;-9429 -50 1;-9183 -144;-8927 -200;-8662 -200 1;-8096 -200;-7562 -1;-7194 386;-7194 386 1;-7192 387;-7194 385;-7194 388 1;-7192 392;-7192 389;-7192 390;-7143 453;-7079 538 1;-7046 581;-6988 598;-6935 581 1;-6882 566;-6846 519;-6843 463 1;-6837 273;-6688 79;-6505 25 1;-6313 -30;-6091 -67;-5824 -110 1;-5540 -156;-5246 -186;-4944 -186 1;-4619 -186;-4332 -142;-4066 -35 1;-3813 64;-3584 211;-3419 414 1;-3404 432;-3387 477;-3347 534 1;-3315 585;-3254 609;-3195 590 1;-3139 574;-3101 519;-3103 459 1;-3103 459;-3103 -482;-3103 -482 1;-3103 -693;-2915 -908;-2706 -941;-1753 -1091 1;-1501 -1129;-1215 -886;-1215 -633;-1215 -241 1;-1215 -166;-1155 -108;-1082 -108;-537 -108 1;-323 -106;-109 82;-79 292 1;-70 342;-35 384;11 399 1;60 415;111 402;147 367 1;333 190;549 49;786 -47 1;1037 -144;1294 -200;1559 -200 1;2126 -200;2658 -1;3027 388 1;3144 513;3239 654;3317 805 1;3338 848;3383 875;3432 875 1;3481 876;3526 852;3550 808 1;3642 654;3737 510;3853 392 1;4045 202;4273 53;4521 -47;4521 -47 1;4521 -48;4520 -46;4522 -47 1;4529 -50;4527 -50;4527 -50 1;4773 -144;5031 -200;5295 -200 1;5861 -200;6394 -1;6763 388 1;6802 432;6839 480;6882 538 1;6914 581;6969 600;7022 585 1;7074 572;7112 529;7119 476 1;7140 310;7271 152;7427 97 1;7615 32;7831 -23;8080 -81 1;8085 -82;8083 -82;8083 -82 1;8360 -146;8654 -178;8965 -178 1;9028 -178;9099 -173;9168 -166 1;9182 -165;9190 -163;9205 -161 1;9278 -156;9347 -148;9415 -136 1;9437 -133;9445 -133;9447 -133 1;9507 -125;9570 -116;9635 -103 1;9681 -95;9728 -112;9758 -146 1;9788 -181;9800 -230;9785 -274 1;9745 -394;9718 -512;9718 -616 1;9718 -878;9838 -1186;10043 -1369 1;10048 -1372;10051 -1374;10058 -1380 1;10243 -1538;10508 -1644;10751 -1644 1;10994 -1644;11257 -1542;11452 -1373 1;11458 -1367;11460 -1365;11466 -1364 1;11671 -1177;11789 -869;11789 -616 1;11789 -441;11717 -246;11606 -67 1;11576 -18;11579 44;11619 94 1;11644 132;11678 151;11716 155 1;11753 159;11791 147;11817 122 1;11871 77;11935 44;12001 25 1;12194 -30;12416 -67;12682 -110 1;12967 -156;13260 -186;13564 -186 1;13889 -186;14173 -142;14439 -35 1;14695 64;14923 211;15088 414 1;15181 529;15253 669;15316 817 1;15337 863;15381 893;15431 897 1;15481 900;15529 874;15555 830 1;15641 673;15739 530;15862 404 1;16061 212;16299 65;16570 -35 1;16849 -140;17147 -186;17457 -186 1;17585 -186;17713 -174;17847 -161 1;17854 -161;17842 -163;17865 -161 1;17990 -153;18110 -138;18237 -120 1;18355 -103;18475 -82;18593 -58 1;18605 -52;18617 -50;18628 -50 1;18743 -26;18848 -1;18940 25 1;19124 80;19273 279;19273 472;19273 3299 1;19273 3915;19111 4466;18724 4844 1;18718 4850;18716 4852;18709 4858 1;18305 5243;17742 5390;17090 5390 18;-1305 3799 1;-1102 3799;-928 3780;-787 3748 1;-646 3712;-518 3673;-404 3626;-539 2884 1;-627 2918;-728 2946;-838 2966 1;-945 2984;-1050 2993;-1151 2993 1;-1365 2993;-1508 2933;-1581 2813 1;-1648 2694;-1683 2535;-1683 2333;-1683 1150;-539 1150;-539 357;-1683 357;-1683 -634;-2636 -481;-2636 2346 1;-2636 2565;-2618 2763;-2579 2941 1;-2536 3121;-2463 3274;-2361 3402 1;-2259 3526;-2123 3624;-1951 3696 1;-1781 3764;-1566 3799;-1305 3799 18;10752 -52 1;10906 -52;11038 -101;11149 -200 1;11264 -303;11323 -441;11323 -616 1;11323 -791;11264 -927;11149 -1026 1;11038 -1128;10906 -1179;10752 -1179 1;10600 -1179;10465 -1128;10350 -1026 1;10238 -927;10183 -791;10183 -616 1;10183 -441;10238 -303;10350 -200 1;10465 -101;10600 -52;10752 -52 18;10279 3722;11233 3722;11233 357;10279 357;10279 3722 18;-11963 3722;-11009 3722;-11009 357;-11963 357;-11963 3722 18;7581 3722;8534 3722;8534 1150 1;8575 1137;8637 1126;8718 1118 1;8804 1105;8877 1098;8937 1098 1;9069 1098;9191 1112;9306 1137 1;9424 1158;9523 1180;9608 1201;9768 414 1;9717 392;9655 375;9583 363 1;9510 345;9437 333;9365 325 1;9292 312;9220 303;9146 299 1;9075 290;9013 286;8962 286 1;8677 286;8415 314;8182 369 1;7950 420;7750 476;7581 536;7581 3722 18;12129 3722;13082 3722;13082 1125 1;13142 1115;13210 1110;13287 1105 1;13363 1097;13435 1092;13504 1092 1;13725 1092;13876 1156;13958 1285 1;14039 1411;14080 1630;14080 1937;14080 3722;15033 3722;15033 1821 1;15033 1592;15010 1381;14963 1195 1;14916 1007;14836 845;14726 709 1;14614 572;14463 467;14272 395 1;14083 318;13847 280;13562 280 1;13280 280;13012 301;12756 344 1;12499 382;12290 425;12129 472;12129 3722 18;15729 3616 1;15737 3607;15749 3596;15758 3588 1;15784 3562;15799 3528;15799 3491 1;15799 3491;15799 3489;15799 3487;15794 3440;15767 3400 1;15769 3404;15763 3384;15737 3354 1;15711 3321;15671 3303;15623 3306 1;15549 3318;15499 3374;15499 3441 1;15499 3441;15499 3524;15499 3524 1;15501 3579;15533 3628;15585 3648 1;15634 3667;15691 3656;15729 3616 18;-14662 3722;-13708 3722;-13708 1150 1;-13667 1137;-13605 1126;-13524 1118 1;-13439 1105;-13366 1098;-13306 1098 1;-13174 1098;-13051 1112;-12936 1137 1;-12819 1158;-12719 1180;-12634 1201;-12474 414 1;-12526 392;-12587 375;-12660 363 1;-12733 345;-12806 333;-12878 325 1;-12951 312;-13022 303;-13096 299 1;-13167 290;-13229 286;-13280 286 1;-13565 286;-13827 314;-14060 369 1;-14292 420;-14492 476;-14662 536;-14662 3722 18;-6378 3722;-5425 3722;-5425 1125 1;-5365 1115;-5297 1110;-5220 1105 1;-5143 1097;-5070 1092;-5002 1092 1;-4780 1092;-4628 1156;-4549 1285 1;-4467 1411;-4427 1630;-4427 1937;-4427 3722;-3474 3722;-3474 1821 1;-3474 1592;-3497 1381;-3544 1195 1;-3591 1007;-3669 845;-3781 709 1;-3890 572;-4042 467;-4235 395 1;-4422 318;-4658 280;-4945 280 1;-5227 280;-5495 301;-5751 344 1;-6007 382;-6217 425;-6378 472;-6378 3722 18;17092 4924 1;17677 4924;18107 4790;18385 4522 1;18665 4257;18806 3849;18806 3299;18806 472 1;18722 446;18624 422;18512 402 1;18405 375;18292 354;18173 337 1;18053 320;17931 307;17809 299 1;17689 286;17572 280;17457 280 1;17187 280;16947 320;16734 402 1;16521 478;16339 589;16189 733 1;16044 878;15934 1053;15857 1259 1;15781 1458;15743 1683;15743 1930 1;15743 2424;15866 2811;16113 3087 1;16361 3361;16717 3498;17182 3498 1;17309 3498;17427 3487;17533 3466 1;17644 3441;17750 3404;17854 3357;17854 3479 1;17854 3666;17796 3819;17680 3939 1;17570 4059;17377 4119;17105 4119 1;16896 4119;16707 4101;16543 4067 1;16379 4032;16224 3986;16074 3926;15908 4725 1;16083 4785;16272 4832;16477 4867 1;16682 4905;16887 4924;17092 4924 18;1726 3810 1;1846 3810;1965 3804;2084 3793 1;2204 3779;2314 3763;2417 3741 1;2524 3724;2619 3703;2705 3677 1;2790 3651;2859 3626;2910 3601;2782 2826 1;2675 2873;2538 2912;2365 2948 1;2199 2978;2029 2993;1854 2993 1;1581 2993;1364 2934;1201 2820 1;1039 2704;947 2550;926 2353;3088 2353 1;3094 2301;3097 2245;3102 2180 1;3105 2112;3109 2050;3109 1995 1;3109 1426;2968 998;2692 709 1;2416 414;2038 267;1559 267 1;1354 267;1154 305;958 382 1;766 459;596 572;446 722 1;298 870;177 1058;81 1285 1;-9 1507;-52 1766;-52 2065 1;-52 2311;-19 2542;49 2756 1;123 2969;231 3154;376 3312 1;524 3466;712 3588;933 3677 1;1159 3765;1422 3810;1726 3810 18;-8496 3810 1;-8376 3810;-8257 3804;-8137 3793 1;-8017 3779;-7908 3763;-7804 3741 1;-7697 3724;-7603 3703;-7516 3677 1;-7431 3651;-7363 3626;-7311 3601;-7440 2826 1;-7546 2873;-7684 2912;-7855 2948 1;-8022 2978;-8193 2993;-8368 2993 1;-8640 2993;-8857 2934;-9020 2820 1;-9182 2704;-9273 2550;-9296 2353;-7132 2353 1;-7128 2301;-7124 2245;-7119 2180 1;-7116 2112;-7113 2050;-7113 1995 1;-7113 1426;-7252 998;-7529 709 1;-7806 414;-8184 267;-8662 267 1;-8867 267;-9067 305;-9264 382 1;-9455 459;-9625 572;-9775 722 1;-9923 870;-10045 1058;-10140 1285 1;-10230 1507;-10273 1766;-10273 2065 1;-10273 2311;-10240 2542;-10171 2756 1;-10098 2969;-9990 3154;-9845 3312 1;-9696 3466;-9510 3588;-9288 3677 1;-9062 3765;-8798 3810;-8496 3810 18;-12484 -146 1;-12454 -181;-12442 -230;-12457 -274 1;-12497 -394;-12524 -512;-12524 -616 1;-12524 -619;-12502 -670;-12491 -753 1;-12484 -794;-12496 -836;-12524 -866 1;-12554 -896;-12594 -911;-12640 -905 1;-12765 -882;-12886 -866;-13002 -866 1;-13009 -866;-13043 -876;-13102 -878 1;-13139 -879;-13174 -866;-13199 -841 1;-13225 -814;-13240 -781;-13240 -745 1;-13240 -745;-13240 -300;-13240 -300 1;-13240 -230;-13188 -171;-13119 -166 1;-13079 -161;-13064 -165;-13074 -166;-13056 -164;-13037 -161 1;-12964 -156;-12895 -148;-12827 -136 1;-12806 -133;-12797 -133;-12795 -133;-12795 -132;-12793 -132;-12795 -133 1;-12735 -125;-12673 -116;-12607 -103 1;-12561 -95;-12514 -112;-12484 -146 18;-14662 -213;-13708 -213;-13708 -1479 1;-13602 -1432;-13492 -1395;-13377 -1371 1;-13261 -1346;-13137 -1333;-13006 -1333 1;-12765 -1333;-12556 -1376;-12373 -1461 1;-12189 -1546;-12034 -1666;-11911 -1819 1;-11788 -1972;-11694 -2156;-11629 -2368 1;-11566 -2582;-11535 -2819;-11535 -3078 1;-11535 -3348;-11574 -3591;-11657 -3808 1;-11732 -4027;-11846 -4210;-11995 -4358 1;-12144 -4513;-12328 -4630;-12544 -4711 1;-12763 -4796;-13009 -4838;-13287 -4838 1;-13402 -4838;-13524 -4833;-13652 -4819 1;-13776 -4811;-13898 -4798;-14022 -4781 1;-14142 -4765;-14257 -4743;-14369 -4716 1;-14478 -4697;-14576 -4673;-14662 -4646;-14662 -213 18;-17051 3810 1;-16800 3810;-16574 3769;-16373 3684 1;-16168 3598;-15993 3479;-15848 3324 1;-15703 3168;-15593 2980;-15517 2763 1;-15440 2540;-15401 2298;-15401 2033 1;-15401 1768;-15442 1526;-15523 1310 1;-15603 1092;-15716 906;-15861 754 1;-16006 600;-16182 480;-16386 395 1;-16586 310;-16808 267;-17051 267 1;-17291 267;-17512 310;-17717 395 1;-17917 480;-18092 600;-18242 754 1;-18387 906;-18502 1092;-18587 1310 1;-18669 1526;-18708 1768;-18708 2033 1;-18708 2298;-18669 2540;-18587 2763 1;-18505 2980;-18392 3168;-18247 3324 1;-18102 3479;-17931 3598;-17729 3684 1;-17525 3769;-17299 3810;-17051 3810 18;5463 3810 1;5581 3810;5701 3804;5821 3793 1;5939 3779;6051 3763;6152 3741 1;6259 3724;6356 3703;6441 3677 1;6526 3651;6594 3626;6646 3601;6518 2826 1;6411 2873;6272 2912;6103 2948 1;5936 2978;5765 2993;5590 2993 1;5318 2993;5100 2934;4938 2820 1;4775 2704;4683 2550;4663 2353;6826 2353 1;6829 2301;6834 2245;6837 2180 1;6843 2112;6844 2050;6844 1995 1;6844 1426;6706 998;6428 709 1;6151 414;5773 267;5297 267 1;5091 267;4891 305;4695 382 1;4503 459;4332 572;4183 722 1;4033 870;3911 1058;3819 1285 1;3729 1507;3684 1766;3684 2065 1;3684 2311;3719 2542;3787 2756 1;3858 2969;3967 3154;4112 3312 1;4262 3466;4446 3588;4668 3677 1;4895 3765;5160 3810;5463 3810 18;-1065 2521 1;-1018 2518;-969 2518;-911 2508 1;-910 2508;-909 2506;-907 2505 1;-830 2489;-762 2473;-704 2450 1;-702 2450;-700 2450;-698 2450 1;-670 2439;-642 2426;-612 2422 1;-545 2409;-500 2350;-503 2283 1;-509 2203;-520 2131;-520 2067 1;-520 1973;-502 1875;-490 1763 1;-487 1725;-500 1688;-525 1660 1;-550 1633;-586 1616;-623 1616;-1106 1616 1;-1170 1630;-1217 1687;-1215 1750;-1215 2333 1;-1215 2360;-1211 2386;-1206 2405 1;-1198 2474;-1136 2527;-1065 2521 18;-11490 -52 1;-11336 -52;-11204 -101;-11093 -200 1;-10978 -303;-10920 -441;-10920 -616 1;-10920 -791;-10978 -927;-11093 -1026 1;-11204 -1128;-11336 -1179;-11490 -1179 1;-11642 -1179;-11777 -1128;-11893 -1026 1;-12004 -927;-12059 -791;-12059 -616 1;-12059 -441;-12004 -303;-11893 -200 1;-11777 -101;-11642 -52;-11490 -52 18;-17051 -1307 1;-16800 -1307;-16574 -1349;-16373 -1434 1;-16168 -1521;-15993 -1639;-15848 -1794 1;-15703 -1951;-15593 -2139;-15517 -2355 1;-15440 -2579;-15401 -2820;-15401 -3085 1;-15401 -3350;-15442 -3591;-15523 -3808 1;-15603 -4027;-15716 -4211;-15861 -4365 1;-16006 -4518;-16182 -4638;-16386 -4723 1;-16586 -4808;-16808 -4851;-17051 -4851 1;-17291 -4851;-17512 -4808;-17717 -4723 1;-17917 -4638;-18092 -4518;-18242 -4365 1;-18387 -4211;-18502 -4027;-18587 -3808 1;-18669 -3591;-18708 -3350;-18708 -3085 1;-18708 -2820;-18669 -2579;-18587 -2355 1;-18505 -2139;-18392 -1951;-18247 -1794 1;-18102 -1639;-17931 -1521;-17729 -1434 1;-17525 -1349;-17299 -1307;-17051 -1307 18;-7064 -1395;-6112 -1395;-6112 -3993 1;-6052 -4004;-5983 -4008;-5907 -4013 1;-5830 -4021;-5759 -4027;-5690 -4027 1;-5468 -4027;-5317 -3961;-5235 -3833 1;-5155 -3707;-5113 -3488;-5113 -3182;-5113 -1395;-4160 -1395;-4160 -3296 1;-4160 -3527;-4183 -3737;-4230 -3923 1;-4277 -4111;-4357 -4273;-4467 -4410 1;-4579 -4547;-4730 -4652;-4921 -4723 1;-5110 -4800;-5347 -4838;-5631 -4838 1;-5913 -4838;-6181 -4818;-6438 -4774 1;-6694 -4736;-6903 -4693;-7064 -4646;-7064 -1395 18;-11099 -1644 1;-11068 -1689;-11067 -1748;-11095 -1794 1;-11120 -1834;-11128 -1842;-11120 -1827 1;-11143 -1872;-11189 -1899;-11242 -1899 1;-11246 -1897;-11251 -1895;-11258 -1894 1;-11298 -1887;-11332 -1862;-11353 -1827 1;-11358 -1822;-11364 -1814;-11375 -1799 1;-11394 -1761;-11396 -1718;-11379 -1679 1;-11362 -1639;-11328 -1611;-11287 -1602 1;-11257 -1596;-11240 -1594;-11242 -1594 1;-11188 -1581;-11133 -1601;-11099 -1644 18;-9182 -1307 1;-9063 -1307;-8944 -1314;-8824 -1326 1;-8706 -1339;-8594 -1356;-8492 -1378 1;-8385 -1395;-8289 -1416;-8204 -1440 1;-8119 -1468;-8051 -1493;-7999 -1517;-8127 -2292 1;-8234 -2246;-8372 -2205;-8542 -2171 1;-8708 -2141;-8881 -2126;-9055 -2126 1;-9327 -2126;-9545 -2184;-9707 -2299 1;-9870 -2413;-9961 -2569;-9982 -2766;-7819 -2766 1;-7816 -2817;-7810 -2873;-7808 -2939 1;-7803 -3007;-7801 -3069;-7801 -3123 1;-7801 -3692;-7939 -4120;-8217 -4410 1;-8494 -4705;-8872 -4851;-9349 -4851 1;-9553 -4851;-9754 -4813;-9950 -4736 1;-10141 -4660;-10314 -4547;-10462 -4396 1;-10611 -4248;-10733 -4060;-10827 -3833 1;-10917 -3612;-10962 -3352;-10962 -3054 1;-10962 -2807;-10926 -2575;-10858 -2362 1;-10787 -2149;-10678 -1964;-10533 -1806 1;-10383 -1652;-10198 -1530;-9976 -1440 1;-9750 -1352;-9485 -1307;-9182 -1307 18;16428 5964 1;15493 5816;15156 5633;14905 5136 1;14842 5010;14767 4893;14739 4875 1;14686 4843;13829 4769;13588 4777 1;13511 4779;12673 4786;11726 4791;10003 4801;9412 4491;8810 4801;8189 4815 1;7703 4826;7501 4814;7261 4759 1;6957 4690;6950 4690;6521 4785 1;6170 4863;5974 4881;5488 4880;5457 4880 1;4811 4880;4563 4831;4074 4600 1;3912 4524;3745 4461;3703 4461 1;3660 4461;3540 4504;3437 4556 1;2881 4838;1858 4973;1148 4859 1;1005 4835;741 4765;560 4703 1;206 4579;201 4579;-280 4737 1;-640 4854;-1173 4915;-1564 4883 1;-1876 4858;-2085 4806;-2522 4644 1;-2687 4583;-2700 4585;-2962 4691 1;-3221 4796;-3260 4801;-3912 4799 1;-4286 4797;-4700 4791;-4832 4785 1;-4964 4779;-5368 4786;-5729 4802 1;-6286 4827;-6433 4820;-6697 4760 1;-7003 4690;-7012 4690;-7420 4779 1;-8263 4964;-9117 4926;-9699 4679 1;-10091 4513;-10177 4511;-10482 4666;-10748 4801;-12238 4801;-12541 4645;-12844 4489;-13138 4645;-13432 4801;-14922 4801;-15208 4663 1;-15529 4507;-15467 4502;-16152 4744 1;-16831 4984;-17621 4945;-18238 4642 1;-19010 4261;-19482 3685;-19691 2865 1;-19908 2013;-19801 1156;-19391 468 1;-19227 194;-19067 14;-18762 -238 1;-18460 -488;-18462 -552;-18772 -793 1;-19030 -993;-19347 -1393;-19507 -1720 1;-19700 -2113;-19789 -2552;-19789 -3099 1;-19788 -3688;-19727 -3966;-19489 -4450 1;-19192 -5057;-18572 -5590;-17912 -5806 1;-17374 -5984;-16546 -5988;-15770 -5633;-15447 -5485;-15239 -5576 1;-14740 -5794;-14092 -5908;-13352 -5909 1;-12569 -5909;-12118 -5779;-11532 -5381 1;-11433 -5314;-11315 -5259;-11270 -5259 1;-11225 -5259;-11047 -5352;-10875 -5465 1;-10015 -6029;-9078 -6079;-8023 -5616;-7813 -5524;-7443 -5648 1;-6638 -5917;-5482 -5996;-4840 -5824 1;-4236 -5663;-3768 -5339;-3467 -4873 1;-3148 -4378;-3090 -4073;-3064 -2779 1;-3052 -2196;-3026 -1687;-3006 -1649 1;-2959 -1560;-2787 -1559;-2312 -1644 1;-2114 -1679;-1835 -1709;-1691 -1709 1;-1229 -1712;-888 -1486;-654 -1024 1;-541 -799;-528 -788;-245 -681 1;187 -519;243 -518;668 -662 1;965 -763;1135 -795;1448 -809 1;2091 -837;2566 -704;3131 -337 1;3264 -250;3400 -179;3432 -179 1;3464 -179;3619 -259;3777 -358 1;4137 -581;4287 -644;4688 -741 1;5298 -888;5896 -821;6519 -534;6870 -372;7177 -478 1;7625 -633;8076 -733;8550 -781 1;9020 -829;9105 -876;9182 -1133 1;9308 -1554;9694 -1943;10095 -2129 1;10456 -2299;11045 -2279;11428 -2095 1;11880 -1878;12170 -1593;12329 -1108 1;12441 -766;12455 -756;12815 -779 1;14010 -855;14639 -730;15238 -297 1;15327 -232;15435 -179;15479 -179 1;15522 -179;15686 -258;15843 -355 1;16603 -824;17454 -922;18651 -679 1;19204 -567;19417 -469;19628 -230 1;19900 79;19908 145;19908 1951;19908 2068 1;19908 3651;19886 3934;19718 4401 1;19503 5000;19140 5402;18544 5703 1;18007 5974;17155 6079;16428 5964 18;17928 5561 1;18700 5363;19232 4845;19456 4071 1;19512 3877;19525 3576;19538 2141 1;19550 942;19541 396;19509 289 1;19438 50;19214 -170;18973 -238 1;18265 -438;17233 -498;16751 -367 1;16348 -257;15993 -62;15706 208;15444 456;15306 271 1;15116 17;14791 -194;14388 -327 1;14080 -428;13997 -439;13508 -436 1;13048 -433;12442 -364;12083 -274 1;12000 -253;11998 -259;12035 -436 1;12080 -647;12030 -945;11908 -1199 1;11636 -1762;10897 -2045;10292 -1817 1;9806 -1635;9468 -1148;9468 -632;9468 -407;9216 -431 1;8866 -464;8256 -399;7783 -280 1;7323 -163;7201 -108;7046 57;6934 175;6772 37 1;5907 -704;4382 -591;3607 271;3424 474;3360 377 1;3119 21;2522 -336;2023 -420 1;1440 -518;756 -394;341 -114;129 29;28 -91 1;-139 -289;-354 -379;-661 -379;-932 -379;-932 -509 1;-933 -750;-1017 -955;-1187 -1124 1;-1437 -1374;-1550 -1391;-2246 -1283 1;-2569 -1232;-2887 -1163;-2953 -1129 1;-3248 -975;-3372 -718;-3372 -258;-3372 65;-3550 -58 1;-3801 -230;-4065 -337;-4405 -402 1;-4781 -475;-5094 -474;-5671 -398 1;-6502 -289;-6766 -202;-6943 24;-7040 149;-7232 3 1;-7499 -201;-7690 -292;-8042 -381 1;-8755 -564;-9576 -395;-10120 47 1;-10278 175;-10291 178;-10328 108 1;-10358 53;-10348 -23;-10289 -176 1;-10246 -291;-10206 -465;-10202 -563 1;-10196 -705;-10182 -736;-10133 -716 1;-9610 -509;-8452 -550;-7836 -799 1;-7626 -884;-7587 -889;-7532 -839 1;-7372 -694;-7184 -659;-6575 -659;-6548 -659 1;-5984 -659;-5837 -684;-5685 -810 1;-5627 -859;-5602 -854;-5492 -772 1;-5374 -686;-5322 -678;-4739 -666 1;-4055 -652;-3888 -680;-3692 -845 1;-3432 -1064;-3432 -1066;-3434 -2479 1;-3436 -3540;-3448 -3805;-3503 -4029 1;-3693 -4791;-4168 -5277;-4922 -5483 1;-5261 -5575;-6112 -5566;-6698 -5463 1;-7272 -5363;-7461 -5289;-7610 -5109;-7726 -4968;-7879 -5088 1;-8814 -5825;-10355 -5685;-11073 -4798;-11235 -4596;-11494 -4860 1;-11783 -5155;-12156 -5370;-12572 -5482 1;-13108 -5626;-14450 -5523;-14960 -5299 1;-15058 -5256;-15175 -5160;-15239 -5069;-15350 -4913;-15611 -5103 1;-16440 -5704;-17556 -5733;-18389 -5176 1;-18763 -4926;-18970 -4689;-19174 -4279 1;-19544 -3536;-19541 -2623;-19167 -1859 1;-18698 -903;-17669 -417;-16549 -623 1;-16267 -675;-15788 -888;-15574 -1057;-15421 -1179;-15405 -589 1;-15394 -169;-15373 26;-15332 89 1;-15235 239;-15319 237;-15504 86 1;-16274 -546;-17402 -634;-18285 -132 1;-19617 624;-19853 2734;-18732 3863 1;-17912 4689;-16505 4772;-15566 4050;-15379 3907;-15333 4019 1;-15270 4170;-15105 4323;-14923 4399 1;-14712 4487;-13672 4487;-13461 4399 1;-13264 4317;-13137 4195;-13048 4001 1;-12982 3860;-12974 3728;-12973 2871;-12973 2823 1;-12972 1843;-12978 1874;-12782 1926;-12692 1950 1;-12692 3973;-12680 4038;-12439 4250 1;-12222 4440;-12067 4471;-11387 4455 1;-10804 4441;-10780 4438;-10624 4329 1;-10492 4238;-10413 4134;-10306 3908 1;-10303 3901;-10213 3963;-10106 4044 1;-9632 4408;-9047 4566;-8312 4528 1;-7825 4503;-7365 4416;-7096 4297 1;-6933 4226;-6914 4225;-6848 4284 1;-6702 4417;-6483 4457;-5908 4459 1;-5355 4461;-5138 4426;-5010 4317 1;-4958 4272;-4918 4279;-4753 4362 1;-4571 4454;-4518 4461;-3955 4460 1;-3267 4459;-3106 4414;-2910 4168;-2797 4027;-2623 4147 1;-2311 4362;-2052 4456;-1635 4505 1;-1021 4577;-348 4454;37 4200;186 4101;437 4238 1;800 4435;1069 4503;1588 4529 1;1962 4547;2134 4535;2508 4464 1;3072 4358;3355 4230;3495 4017 1;3552 3931;3607 3861;3617 3861 1;3627 3861;3710 3925;3802 4004 1;4053 4219;4484 4414;4868 4487 1;5440 4594;6391 4505;6861 4299;7033 4223;7198 4332 1;7357 4438;7382 4441;8005 4453 1;8611 4465;8658 4460;8826 4373 1;8923 4323;9058 4212;9126 4127;9248 3974;9260 2937;9272 1901;9360 1902 1;9541 1903;9543 1914;9556 2969;9568 3974;9690 4127 1;9758 4212;9892 4322;9989 4371 1;10149 4453;10221 4461;10767 4460 1;11260 4459;11397 4446;11528 4387 1;11682 4317;11694 4317;11848 4387 1;11979 4446;12117 4459;12611 4460 1;13162 4461;13230 4453;13396 4369;13577 4278;13732 4359 1;13871 4432;13961 4441;14551 4441;15214 4441;15197 4626 1;15173 4869;15240 5059;15408 5233 1;15568 5397;15799 5483;16366 5586 1;16835 5672;17538 5661;17928 5561;17928 5561 18;-13184 -2139 1;-13282 -2139;-13381 -2152;-13479 -2177 1;-13572 -2202;-13648 -2234;-13708 -2274;-13708 -3993 1;-13662 -4004;-13602 -4008;-13530 -4013 1;-13457 -4021;-13379 -4027;-13293 -4027 1;-13032 -4027;-12837 -3936;-12704 -3756 1;-12572 -3582;-12506 -3348;-12506 -3054 1;-12506 -2443;-12733 -2139;-13184 -2139 18;-13248 -2631 1;-13034 -2631;-12976 -2876;-12976 -3090 1;-12976 -3284;-13054 -3505;-13248 -3506;-13248 -2631 18;17360 2730 1;16930 2730;16714 2463;16714 1930 1;16714 1675;16776 1464;16900 1297 1;17024 1130;17212 1047;17464 1047 1;17548 1047;17623 1052;17687 1060 1;17750 1065;17807 1072;17854 1080;17854 2603 1;17794 2636;17720 2666;17636 2691 1;17550 2718;17459 2730;17360 2730 18;17409 2232;17409 1571 1;17201 1492;17180 1713;17180 1926 1;17178 2120;17192 2338;17409 2232 18; + + + +69180 53168 1;69435 53137;69705 53115;70028 53115 1;71160 53123;71678 53512;72750 53895 1;73958 54345;74580 54570;75870 54750 1;76800 54893;77460 54863;78090 55575 1;78675 56250;79028 56685;79148 57570 1;79200 58012;79260 58245;79185 58673 1;79065 59325;79095 59670;79230 60315 1;79320 60802;79320 61162;79725 61433 1;80370 61875;80768 62205;81548 62130 1;82448 62063;82815 61500;83730 61515 1;84795 61530;85328 61635;86408 61613 1;87367 61605;87908 61455;88725 60930 1;89160 60660;89250 60270;89265 59753 1;89280 59250;89115 58995;88830 58575 1;88418 58005;88110 57802;87585 57330 1;86760 56610;86265 56325;85568 55470 1;84998 54810;84548 54585;84165 53790 1;83798 53055;83708 52620;83625 51795 1;83505 50700;83175 50190;82770 49170 1;81855 46980;81068 46020;80310 43770 1;80055 43050;79905 42570;80227 41873 1;80288 41738;80348 41610;80408 41490; +69210 50662 1;70268 50715;70867 50573;71850 50992 1;72825 51435;73275 51742;74310 52073 1;75188 52373;75645 52485;76530 52815 1;77227 53085;77828 53085;78390 52575 1;78795 52200;78720 51780;78750 51210 1;78780 50302;78600 49815;78165 48990 1;77678 48090;77310 47700;76905 46755 1;76178 45053;75780 44130;75727 42270 1;75690 41213;75803 40545;75938 39488; +69210 46020 1;69480 45923;69705 45750;69908 45450 1;70110 45150;70200 44963;70208 44595 1;70215 44025;70005 43755;69870 43193 1;69675 42525;69518 42135;69218 41603; +89265 73830 1;89040 73545;88575 73223;88387 73530 1;88178 73875;88058 74242;88350 74512 1;88620 74783;89025 75135;89250 74813 1;89468 74483;89520 74123;89265 73830 18; +69158 62535 1;69255 62512;69360 62505;69488 62512 1;70035 62565;70418 62700;70710 63173 1;71048 63750;71025 64140;71430 64673 1;72488 66120;73155 66795;74610 67852 1;75338 68393;75878 68355;76770 68573 1;77617 68790;77977 69105;78788 69450 1;79515 69780;80025 69713;80648 70230 1;81015 70560;81278 70673;81525 71093 1;81698 71400;81885 71602;82245 71595 1;82710 71595;82890 71265;83145 70875 1;83318 70605;83408 70470;83588 70193 1;83790 69885;84240 69840;84510 70095 1;85410 70950;85808 71445;86768 72233 1;87105 72525;87135 72960;86910 73335 1;86745 73583;86655 73733;86685 74010 1;86730 74415;86895 74610;87210 74873 1;87825 75420;88005 75893;88770 76253 1;89190 76463;89550 76665;89948 76395 1;90367 76102;90660 75833;90668 75315 1;90668 74573;90218 74242;90270 73492 1;90315 72563;90398 71970;91050 71295 1;91598 70710;91830 70320;92550 69930 1;93330 69525;93885 69713;94770 69870 1;95378 69990;95700 70125;96330 70095 1;96960 70073;97320 69713;97590 69135 1;97958 68303;98130 67875;98430 66990 1;98595 66473;98895 66075;99450 66075 1;99990 66075;100260 66285;100688 66615 1;101085 66938;101325 67170;101580 67492; +118417 37920 1;118410 38063;118380 38213;118328 38370 1;118087 38993;118058 39495;117465 39773 1;117158 39923;116977 39983;116648 39975 1;116085 39960;115306 39791;115276 39235 1;115216 38718;115180 38397;115188 37880 1;115188 37647;115233 37370;115285 37190 16; +115709 37870 1;115822 35231;118914 37371;117405 38895 1;117106 39197;116122 39241;115920 38864 1;115769 38587;115717 38117;115709 37870 18; +69150 64305 1;69563 64448;69893 64695;70290 65070 1;71025 65813;71355 66225;72090 66975 1;72727 67643;73020 68003;73748 68573 1;74280 69000;74528 69293;75188 69495 1;76020 69765;76530 69593;77325 69953 1;78225 70373;78615 70740;79568 71055 1;80325 71310;80760 71483;81330 72053 1;81675 72413;81818 72803;82328 72810 1;82665 72825;82785 72555;82988 72270 1;83227 71933;83400 71790;83685 71475 1;83925 71190;84345 71220;84630 71475 1;85028 71850;85298 71970;85665 72390 1;85988 72773;86175 73193;85928 73613 1;85725 73950;85770 74220;85928 74573 1;86137 75060;86205 75352;86588 75735 1;86940 76110;88180 76923;88676 77100; +123225 70193 1;123585 69495;123690 69105;124005 68370 1;124193 67950;124335 67598;124455 67260; +124477 61650 1;124260 61403;124012 61170;123788 60810 1;123045 59693;122715 59085;121785 58110 1;120637 56925;119948 56430;118590 55492 1;117360 54660;116925 53963;115650 53213 1;114878 52785;114405 52755;113610 52395 1;113167 52215;112928 52162;112485 52012 1;112087 51885;112005 51600;111705 51315 1;111330 50970;111045 50805;110565 50738; +69158 66120 1;70035 66585;70410 67028;71168 67733 1;71970 68505;72390 68873;73305 69495 1;73770 69810;74040 69930;74565 70133 1;75637 70553;76268 70575;77227 71213 1;77985 71730;78495 71745;79365 72030 1;79680 72150;79800 72315;80048 72533 1;80430 72893;80588 73170;81090 73350 1;81578 73545;81795 73800;82328 73815 1;82755 73830;83010 73635;83288 73290 1;83558 72960;83580 72653;83970 72450 1;84420 72225;84930 72690;85005 73193 1;85065 73695;85080 73950;85028 74453 1;84990 74790;85178 74955;85365 75233 1;85695 75765;85808 76080;86288 76470 1;86820 76920;87038 77250;87690 77490 1;88125 77670;88448 77843;88793 77963; +90428 77963 1;90945 77745;91283 77288;91710 76733 1;92460 75750;92745 75113;92887 73875 1;92977 72975;93375 72473;94148 71970 1;94358 71843;94530 71895;94785 71895 1;95528 71903;96098 72030;96750 71655 1;97215 71385;97425 71280;97770 70830 1;98137 70335;98460 70193;98805 69653 1;99368 68835;99630 69180;99870 69533 1;100455 70170;100815 70373;101430 70890 1;101970 71370;102398 71385;103065 71655 1;103935 72023;104408 72150;105345 72315 1;106118 72450;106598 72690;107310 72352 1;107962 72120;108308 71955;109005 71963 1;110018 71985;110655 71970;111465 72578 1;111870 72893;111953 73185;112148 73658 1;112230 73867;112245 74010;112470 74190; +112470 74190 1;113730 74700;114465 74805;115830 74730 1;116505 74700;116917 74760;117525 74430; +118403 72773 1;118950 72383;119340 71955;119962 71685 1;120623 71408;121058 71445;121762 71258 1;122370 71100;122663 70973;123218 70665 1;123818 70335;124102 69915;124448 69495; +124485 55560 1;123878 55058;123225 54563;122370 53970 1;121335 53280;120968 52733;119910 52095 1;118748 51412;118170 51052;116910 50610 1;116258 50393;115905 50340;115230 50250 1;114735 50205;114698 49650;114225 49575 1;113220 49410;112740 49200;111727 49110 1;110265 48990;109628 48233;108172 48278 1;107595 48278;106748 48525;106005 48375 1;105525 48285;105225 48412;104805 48173 1;104108 47790;103837 47430;103087 47152 1;102420 46920;102278 46793;101768 46545; +96285 77977 1;96990 77573;97290 77123;98070 76650 1;98887 76163;99315 75653;100290 75690 1;101137 75742;101528 76200;102390 76155 1;102945 76133;103215 75900;103785 75930 1;104325 75975;104587 76080;105105 76230 1;105990 76492;106448 76665;107370 76650 1;107730 76650;107895 76523;108270 76455 1;108525 76365;108503 76088;108503 75810 1;108495 75203;108383 75188;108637 74625 1;108818 74220;109028 73823;109477 73808 1;109995 73800;110333 73733;110753 74048 1;111000 74242;111098 74400;111188 74708 1;111308 75180;111315 75158;111428 75623 1;111488 75893;111555 76155;111833 76178 1;112080 76208;112208 76193;112425 76230; +102600 78008 1;102758 77933;102915 77858;103087 77775 1;103920 77370;104348 77040;105285 77010 1;106365 76980;106875 77565;107970 77475 1;108360 77453;108585 77355;108870 77070 1;109328 76613;109005 76110;109050 75450 1;109087 74745;109170 74378;109890 74378 1;110520 74393;110715 74640;110835 75262 1;110970 75998;111113 76230;111368 76928 1;111503 77310;111975 77130;112328 77333 1;112920 77685;113250 77887;113903 78038; +116400 78045 1;116535 78000;116678 77940;116843 77873 1;117637 77543;118163 77310;118973 77018 1;119670 76778;119648 75585;120068 75315 1;121350 74498;122227 74490;123570 73305; +112425 76230 1;113490 76433;114360 76590;115425 76770 1;116648 76995;117413 77085;118628 76575 1;118695 76545;118485 76448;118575 76178 1;119385 73665;121815 74250;123698 72578; +112605 75833 1;114165 76102;115297 76320;116887 76433 1;117480 76485;117795 76463;118387 76335 1;118328 76185;118178 76020;118200 75848 1;118553 73005;121020 74085;122753 72668; +112065 74940 1;112163 75173;112358 75218;112605 75270 1;113805 75465;114405 75653;115628 75735 1;116505 75803;117015 75960;117893 75833 1;117953 75750;117810 75262;117848 75165 1;118680 72945;120518 73215;122558 72225 1;123270 71880;123750 71760;124440 71588; +111975 74288 1;112065 74617;112253 74805;112590 74873 1;113655 75015;114188 75180;115268 75210 1;116190 75255;116655 75352;117585 75233; +124508 52380 1;123735 52012;123098 51855;122205 51315 1;121118 50670;120525 50400;119325 50010 1;118508 49762;118095 49530;117488 48930 1;116887 48360;116475 48113;115665 47955 1;113558 47558;112320 47633;110542 47483; +119858 78060 1;120615 77745;121297 77460;122250 76950 1;122783 76673;122962 76275;123060 75675 1;123188 74887;123337 74242;124095 73965; +124508 49733 1;124073 49598;123623 49433;123060 49223 1;121620 48720;120990 48255;119580 47700 1;117150 46785;115725 46980;113137 46980; +123428 34110 1;123525 34253;123623 34410;123727 34590 1;123998 35145;124095 35565;124560 35858; +124523 43883 1;124208 43995;123870 44108;123465 44250 1;122137 44723;121448 44910;120068 45150 1;119010 45345;118477 45443;117428 45615 1;117098 45675;116850 45825;116528 45893 1;116318 45953;116408 46275;116625 46290 1;116828 46320;116940 46260;117150 46290 1;118883 46223;120038 46088;121410 46133 1;122025 46148;123188 46193;123465 46313; +124523 34110;124560 34193; +124530 44453 1;124065 44550;123765 44648;123270 44835 1;122400 45150;121928 45195;121087 45533 1;120848 45630;120660 45720;120922 45750 1;122415 45938;123428 45938;124523 45930; +123465 46313 1;123885 46530;124215 46748;124523 46957; +124433 71933 1;124260 71977;124087 72023;123922 72075; +124425 72308;124305 72352; +69240 38070 1;70613 38243;71858 38415;73665 38580 1;75585 38760;75908 38355;77400 38415 1;79485 38505;80453 38595;82485 38850 1;83205 38948;83580 38948;84293 39120 1;85125 39330;85650 39510;86137 40215 1;86843 41258;87262 41730;88043 42720 1;88538 43365;89115 43740;89925 43770 1;93262 44070;94875 44228;98235 44820 1;99458 45098;100065 45188;101235 45660 1;102810 46215;103493 47115;105225 47160 1;107340 47220;108435 47115;110505 46620 1;112575 46125;113655 46148;115785 46050 1;118568 45930;119925 45743;122715 45630 1;123428 45608;123983 45623;124530 45593; +69128 68242 1;69308 68363;69495 68490;69698 68625 1;73380 71183;74985 72203;78578 74783 1;80258 76043;81083 76725;82590 77948; +103875 57915; +116363 62985 1;114788 62738;112905 62512;111352 62183 1;110310 61965;108975 61800;107873 61568; +74175 70159; +79778 53235 1;81008 53250;82470 53730;84465 54450 1;86250 55103;87623 55125;89205 54060 1;90383 53265;91125 52838;92483 52380 1;94335 51840;95235 52020;97365 52230 1;97823 52275;98220 52320;98595 52365; +109912 65477;106598 63218 32;107783 61733; +107137 74783 1;107453 73980;107460 73492;107962 72773 1;108240 72375;108555 72262;109058 72233 1;109410 72225;109650 72323;109898 72367 1;110145 72405;110408 72390;110723 72488 1;111143 72645;111337 72870;111630 73208 1;111795 73410;111810 73560;111893 73808 1;111998 74175;112073 75045;112080 75405; +69195 48818 1;69338 48570;69488 48308;69645 48030; +83918 39030 1;84930 38970;85658 38850;86468 39465 1;87083 39945;87518 40027;88298 40005 1;89693 39975;90390 40058;91793 40110 1;93105 40162;94538 40350;95858 40320; +87727 42300 1;86985 41318;88560 40088;89798 40020; +102909 57261 1;100884 56076;98760 55335;96285 53693 1;95048 52890;94515 52545;93480 51938; +103515 59340; +69195 47918 1;69293 47955;69390 47993;69495 48030; +69120 74693;69383 74865 32;69120 72308;69120 74693 18; +69293 54375 1;69255 54443;69218 54510;69180 54578; +98715 77992;99255 77775 32;98858 76800 32;97913 77168;98235 77992;98715 77992 18; +74115 38700 1;73852 40298;73815 41198;72975 42570 1;71633 44760;70973 45870;69645 48060; +79605 53550 1;77198 55988;75975 57090;73665 59610 1;72218 61185;71602 61943;70125 63480 1;69720 63908;69360 64283;69143 64733; +69645 48060 1;70913 48683;71558 48938;72825 49500 1;74303 50168;75158 50332;76485 51270 1;77550 52035;78225 52238;79185 53130 1;79313 53258;79433 53378;79545 53490 33;80258 54203;80640 54742;81248 55710 1;81923 56820;82463 57270;83543 57990 1;84773 58823;85313 59393;86678 59970 1;88073 60570;88958 60750;90458 60450 1;92415 60068;93488 60255;95498 60255 1;96413 60240;96795 60195;97688 60225 1;98685 60262;99060 60270;100178 60495 1;101903 60840;102045 60870;103095 60668 1;103477 60608;103485 60668;103890 60758 1;104520 60908;104895 61207;105547 61215 1;106133 61230;106387 61253;107003 61343 1;107348 61403;107610 61485;107887 61568; +74025 39420 1;74700 41183;75150 42030;76125 43650 1;76898 44948;77310 45608;78405 46650 1;79515 47723;81060 48203;82485 48810; +79605 53370 1;80010 52973;80190 52755;80595 52350 1;81023 51923;81045 51555;81225 50970 1;81413 50348;81510 50040;81705 49410 1;81803 49080;81683 48878;81585 48540; +69188 54360 1;69840 54495;70433 54315;70988 54293 1;71648 54278;72435 54427;72968 54353 1;73688 54262;74025 54128;75045 53910 1;76635 53610;77205 53580;79275 53310; +69240 37838 1;69750 37568;70463 37065;70838 36750 1;71558 36158;73110 34868;74227 33953; +76845 38490 1;77003 37665;77100 37253;77265 36420 1;77445 35460;77617 34733;77670 33960; +73703 52598; +75953 54615; +112530 47799; +103395 60720 1;103598 59880;104025 59588;104505 58860 1;105053 58035;105570 57585;105735 56595 1;105945 55515;105675 55537;104805 54780 1;104303 54345;103898 54218;103635 53610 1;103440 53190;103643 52478;104115 52440; +102262 52545 1;102593 52515;102803 52537;102945 52605 33;103080 52665;103163 52762;103230 52883 33;103290 53010;103343 53160;103425 53332 33;103493 53475;103583 53633;103725 53798; +69195 48825 1;69270 48660;69338 48473;69398 48248 1;69428 48113;69330 48023;69195 47955;69203 48818;69195 48825 18; +124455 64207 1;123540 64485;122693 64688;121455 64710 1;119295 64755;117623 64643;116385 62880 1;115575 61740;115500 60945;115335 59550 1;114975 56640;118995 53700;122415 53370 1;123135 53295;123840 53325;124500 53430; +109485 57600 1;109178 57173;108878 56655;108488 56475 1;107685 56115;106740 55845;105465 55380; +113595 48600 1;113415 48825;112830 49245;112688 49433 1;112245 50010;112110 50400;112035 51248 1;111945 52110;111870 52650;112193 53370; +113535 53213 1;112935 53273;112530 53220;112125 53295 1;111773 53363;111525 53528;111180 53603 1;110790 53685;110265 53655;109725 53610 1;109380 53588;109305 53512;109065 53730 1;108885 53895;108712 54030;108593 54158 1;108240 54540;107730 54405;106350 55680; +112860 49238;115005 49260; +95835 51690; +91545 52650 1;90788 51840;90375 51375;89355 50940 1;88275 50490;87818 50070;86715 49680 1;86355 49560;86137 49650;85785 49530 1;84315 49058;84105 48900;82575 48750 1;82200 48720;81840 48983;81735 49290; +77385 55830 1;77273 57983;77273 59063;77085 61200 1;77033 61733;76988 62003;77085 62520 1;77183 63052;77100 63435;77498 63795 1;78203 64463;78525 64838;79313 65415 1;80115 66015;80550 66330;81518 66585 1;82148 66758;82365 66795;83010 66623 1;83512 66495;83798 66578;84390 66615 1;85095 66668;85350 66225;85808 65580 1;86213 64995;86475 64710;86768 64035 1;87008 63473;87008 63090;87233 62588; +74887 58373 1;75143 58927;75308 59235;75285 59835 1;75255 60503;75323 60840;75255 61492 1;75165 62318;75668 62707;75593 63525 1;75555 63893;75420 64073;75480 64425 1;75510 64650;75563 64845;75780 64898 1;76185 64898;76530 64665;76568 64290 1;76613 63832;76718 63525;77137 63330; +78533 64695 1;78945 64065;79117 63630;79793 63270 1;80753 62760;81278 62550;82102 61830 1;82583 61410;82673 61058;82988 60495 1;83227 60060;83581 59405;83792 59083; +83483 59820 1;83678 59985;83602 60262;83843 60360 1;84270 60548;84510 60660;84983 60615 1;85223 60593;85418 60555;85523 60330 1;85643 60060;85710 59918;85867 59655;85605 59430 33;85102 59137;84698 58830;84203 58463 32;84165 58665 33;83858 59010;83663 59295;83475 59603 32;83483 59820 18; +86408 64755 1;86588 65775;86723 66270;86873 67290 1;86933 67740;86940 68025;86723 68415 1;86348 69075;85973 69413;86078 70155; +86648 70290 1;86663 70050;86622 68946;86599 68758; +109455 41640 1;108285 41723;107408 41730;106253 41693 1;105323 41670;104887 41573;103995 41760 1;103193 41933;102758 42060;101955 41910 1;99480 41468;98205 41138;95843 40290; +95798 40305 1;96210 40185;96983 40110;96765 39510 1;96248 38115;95820 37373;95925 35880 1;95970 35145;96038 34575;96128 34020; +115028 34088 1;115448 34245;115890 34418;116415 34650 1;116790 34823;117023 34823;117375 35040 1;117758 35295;118140 35445;118395 35820 1;118740 36345;118943 36848;118905 37283; +109373 40613 1;107348 40545;106343 40582;104333 40582 1;102345 40582;101363 40418;99398 40185;96878 39855; +114840 40207 1;114675 39143;114255 37838;114825 36930 1;115403 36008;115643 35573;116385 34770; +80025 43350;78458 42855; +81231 42023; +79148 39570; +75518 39030 1;76680 39015;77258 39015;78428 39015 1;78900 39015;79088 39098;79492 39360 1;80137 39810;80685 40170;81105 40628 1;82073 41640;82673 42128;83992 42585 1;84788 42870;85185 43103;85815 43673 1;86198 44025;86490 44078;86985 44235 1;87413 44385;87623 44483;88073 44588 1;88508 44700;88748 44693;89205 44670 1;90120 44640;90578 44570;91500 44622 1;94053 44791;94018 44802;97910 45470 1;99009 45659;99116 45737;100278 45985 1;100496 46037;101150 46127;101578 46202; +69870 41093; +74992 49965 1;75248 49643;75420 49537;75653 49185 1;75803 48953;75495 48623;75262 48465 1;74955 48278;74625 48045;74378 48300 1;74003 48698;73845 48923;73463 49305 1;73365 49403;73680 49530;73762 49425 1;73973 49140;74018 48818;74378 48765 1;74528 48750;74760 48832;74708 48975 1;74573 49328;74625 49552;74453 49875;74992 49965 18; +74933 49920;74753 50310 33;75225 50520;75690 50745;76223 51098 32;76523 51090 1;76658 50985;76665 50873;76748 50715 1;76815 50573;76635 50423;76477 50400 1;76260 50378;76148 50430;75938 50400 1;75795 50385;75735 50213;75803 50085 1;76020 49673;76380 49508;76852 49545 1;77265 49590;77528 49628;77828 49920 1;77955 50055;78188 50213;78278 50040 1;78352 49890;78563 49980;78637 50137 1;78735 50363;78720 50528;78930 50655 1;79283 50880;79785 50768;80205 50753 1;80400 50753;80708 50558;80648 50363 1;80550 50078;80078 49957;79823 49785 1;79718 49725;79710 49485;79838 49485 1;79995 49485;80123 49552;80227 49425 1;80483 49103;80468 48848;80693 48495 1;80768 48375;80843 48218;80723 48135 1;80558 48037;80355 47910;80242 48060 1;80123 48218;80063 48285;79958 48435 1;79860 48578;79680 48630;79553 48525 1;78968 48075;78623 47925;77992 47550 1;77738 47408;77475 47280;77273 47475 1;77108 47640;77063 47753;76943 47940 1;76793 48173;76545 48105;76373 48300 1;75810 48930;75533 49253;74977 49875;74933 49920 18;78803 49290 1;78720 49358;78660 49350;78570 49380 1;78480 49418;78413 49343;78383 49253 1;78323 49103;78405 49012;78488 48863 1;78585 48668;78727 48623;78930 48533 1;79028 48495;79133 48615;79133 48720 1;79125 48998;78975 49117;78788 49313;78803 49290 18; +69158 66180 1;69248 66180;69315 66165;69428 66150 1;69570 66143;69623 66023;69683 65880 1;69773 65655;69825 65393;70073 65400 1;70253 65408;70350 65393;70538 65370 1;70710 65355;70703 65145;70733 64965 1;70762 64748;70793 64635;70838 64410 1;70852 64305;70838 64192;70733 64155 1;70583 64117;70710 63930;70718 63780 1;70725 63608;70740 63525;70748 63345 1;70748 63188;70950 63052;70823 62955;70568 63015 33;70433 63165;70283 63315;70125 63480 1;69975 63637;70320 64200;70193 64350 1;70050 64508;69413 64328;69308 64492 1;69248 64582;69195 64673;69143 64762;69150 66188;69158 66180 18; +70485 59445;70710 60015; +93188 51375; +81518 41640 1;81435 41843;81390 42113;81188 42060 1;80865 41993;80498 41850;80378 42150 1;80137 42713;79755 42855;79523 43410 1;79425 43635;79238 43793;79387 43980 1;79500 44138;79620 44273;79733 44430 1;79808 44550;79800 44678;79718 44790 1;79613 44940;79496 45231;79645 45328 1;79781 45426;79688 45503;79838 45585 1;79913 45638;80085 45390;80137 45300 1;80175 45218;80227 45128;80333 45135 1;80640 45180;80805 45240;81113 45165 1;81443 45090;81608 45015;81953 44985 1;82102 44978;82298 45135;82223 45270 1;82102 45480;82012 45593;82028 45825 1;82028 46005;82110 46103;82253 46200 1;82387 46305;82500 46320;82673 46275 1;82755 46260;82808 46245;82898 46260 1;83010 46290;83078 46388;83078 46500 1;83070 46635;83085 46733;83198 46800 1;83303 46875;83348 46980;83483 46965 1;83678 46950;84345 47213;84473 47063 1;84600 46912;84570 46703;84570 46500 1;84570 46305;83963 46073;83798 45960 1;83708 45908;83633 45908;83543 45945 1;83415 45998;83325 46050;83213 45990 1;83003 45893;82883 45773;82852 45540 1;82823 45345;82785 45218;82898 45045 1;82995 44895;83130 44850;83318 44865 1;83610 44895;83805 45015;84053 44850 1;84248 44723;84450 44655;84458 44415 1;84458 44183;84315 44078;84143 43920 1;83648 43500;83348 43373;82838 42975 1;82545 42758;82448 42585;82268 42270 1;82065 41925;81930 41707;81518 41640 18; +90218 53325 1;90188 53130;90083 53018;90038 52823 1;90008 52710;90045 52515;90023 52395 1;89977 52200;89805 52133;89573 51930 1;89063 51495;88733 50985;88313 50460; +69180 54270;69203 54233 1;69233 54150;69218 54075;69180 54000;69188 54262;69180 54270 18; +69180 52860 1;69240 52785;69293 52703;69338 52582 1;69495 52080;69518 51810;69637 51293 1;69653 51233;69563 51203;69503 51188 1;69390 51165;69285 51158;69188 51158;69188 52867;69180 52860 18; +69188 53783 1;69278 53768;69345 53723;69360 53655 1;69383 53573;69308 53483;69180 53423;69188 53783 18; +69218 48938 1;69668 48773;69893 48683;70358 48518 1;70403 48503;70283 48480;70238 48457 1;69983 48353;69795 48398;69608 48203;69218 48938 18; +74640 51518 1;74475 51690;74453 51923;74580 52043 1;74700 52155;74933 52117;75090 51938 33;75255 51773;75278 51540;75158 51427 1;75030 51308;74798 51345;74640 51518 18; +71977 51068 1;71835 51150;71775 51203;71678 51323 1;71558 51473;71423 51540;71242 51503 1;71093 51473;71018 51420;70867 51427 1;70658 51443;70477 51570;70477 51773 1;70455 52373;70425 52665;70433 53258 1;70433 53408;70485 53543;70628 53558 1;70740 53573;70808 53617;70928 53588 1;71070 53558;71145 53468;71198 53318 1;71400 52740;71400 52418;71678 51863 1;71715 51787;71805 51787;71887 51818 1;72075 51893;72165 51953;72367 51998 1;72428 52020;72495 52028;72555 52012 33;72600 51998;72645 51968;72683 51908 1;72735 51810;72765 51758;72818 51653 1;72848 51593;72855 51518;72833 51457 33;72825 51420;72795 51383;72758 51353 1;72548 51240;72443 51195;72233 51082 1;72113 51030;72008 50940;71933 51023; +72098 49343 1;71963 49425;71925 49508;71798 49582 1;71678 49658;71678 49883;71813 49927 1;72038 50018;72173 50040;72428 50018 1;72623 50003;72727 50010;72923 50048 1;73035 50078;73230 49995;73268 49883;72098 49343 18; +72098 51908 32;71992 51870 33;71963 51855;71925 51840;71887 51818 1;71805 51787;71715 51787;71678 51863 1;71400 52418;71400 52740;71198 53318 1;71160 53408;71123 53475;71070 53528 32;71183 53558 33;71370 53543;71550 53520;71558 53332 1;71558 53265;71588 53228;71588 53153 1;71588 53040;71490 53003;71498 52883 1;71505 52770;71565 52688;71678 52643 1;71850 52575;71963 52500;72008 52313 1;72030 52177;72068 52110;72098 51983;72098 51908 50; +70823 48802 32;70770 48908 33;70200 50115;69983 50858;69788 52207 1;69720 52650;69600 52927;69803 53318 1;69998 53715;70185 53887;70553 54128 32;71783 54188 32;71820 54075 33;72128 53280;72233 52830;72578 52012 32;72510 52020 33;72465 52028;72413 52020;72367 51998 1;72255 51975;72180 51953;72105 51915 32;72090 52012 33;72060 52125;72030 52193;72008 52313 1;71963 52500;71850 52575;71678 52643 1;71565 52688;71505 52770;71498 52883 1;71490 53003;71588 53040;71588 53153 1;71588 53228;71558 53265;71558 53332 1;71550 53550;71303 53543;71093 53565 32;70890 53595 33;70793 53610;70725 53573;70628 53558 1;70485 53543;70433 53408;70433 53258 1;70425 52665;70455 52373;70477 51773 32;70515 51623 33;70575 51510;70710 51443;70867 51427 1;70935 51427;70988 51435;71040 51450 32;71242 51503 33;71423 51540;71558 51473;71678 51323 1;71700 51293;71723 51262;71753 51240 32;71963 51037 32;71925 50887 33;71873 50745;71775 50640;71633 50512 1;71580 50483;71498 50528;71498 50588 1;71490 50685;71498 50738;71498 50828 1;71498 50903;71430 50963;71363 50948 1;71145 50918;71033 50903;70823 50858 1;70740 50843;70688 50768;70718 50693 1;70755 50580;70725 50512;70748 50393 1;70755 50348;70800 50318;70852 50318 1;70935 50325;70973 50340;71063 50348 1;71145 50363;71190 50250;71168 50168 1;71108 49988;71048 49898;71033 49703 1;70995 49410;71048 49253;71183 48983;70823 48802 50; +70440 52448 33;70433 52695;70425 52935;70433 53258 1;70433 53408;70485 53543;70628 53558 1;70740 53573;70808 53617;70928 53588 1;71070 53558;71160 53468;71213 53318 1;71325 52995;71385 52762;71453 52508 32;71475 52380 1;71063 52313;70838 52298;70433 52223;70440 52448 50; +69158 60825 1;69428 60720;69713 60660;70088 60675 1;70650 60713;70980 60765;71445 61095 1;71948 61463;72180 61853;72165 62475 1;72150 63105;72270 63443;72570 63990 1;72915 64680;73050 65085;73568 65655 1;74040 66180;74325 66405;74828 66915 1;75180 67283;75450 67492;75968 67492 1;76575 67500;76898 67523;77490 67695 1;78015 67860;78435 67703;78810 68115 1;78990 68325;79117 68385;79365 68490 1;79785 68693;80025 68760;80408 69053 1;80738 69323;80985 69413;81165 69795 1;81375 70283;81637 70643;82170 70650 1;82650 70665;82898 70343;83130 69915 1;83288 69623;83475 69540;83625 69233 1;83768 68925;84038 68790;84390 68813 1;84990 68865;85245 69180;85688 69615 1;86227 70155;86438 70492;87045 70950 1;87600 71385;87810 71865;88508 71933 1;88875 71970;89078 71850;89408 71655 1;89887 71363;89820 70913;90165 70455 1;90525 69983;90750 69765;91245 69413 1;91838 69000;92130 68700;92850 68550 1;93428 68445;93727 68400;94328 68430 1;95145 68483;95535 68670;96368 68693 1;96705 68715;96870 68490;97087 68213 1;97808 67290;97815 66615;98310 65535 1;98558 64973;99060 64815;99690 64815 1;100245 64815;100598 64973;100988 65393 1;101745 66240;102105 66690;102810 67590 1;103200 68115;103440 68363;103988 68715 1;104550 69083;104887 69330;105570 69315 1;105990 69315;106238 69375;106628 69173 1;107137 68925;107355 68693;107887 68453 1;108525 68160;108915 68063;109628 68093 1;110475 68145;110940 68295;111645 68775 1;112395 69300;112860 69443;113565 70035 1;113925 70350;114255 70335;114750 70290 1;115425 70245;115920 70223;116370 69690 1;116790 69195;116850 68790;116925 68130 1;117000 67500;117255 67245;117488 66630 1;117705 66045;117780 65715;117825 65070 1;117840 64883;117855 64793;117870 64590 1;117900 62715;116137 62302;114630 61170 1;113528 60360;112958 59963;111765 59295 1;110460 58575;109860 58125;108608 57293 1;107768 56753;107340 56512;106508 55950 1;105870 55545;105660 55140;104970 54855 1;103785 54375;103260 53992;102090 53490 1;101115 53093;100538 53070;99645 52515 1;98887 52043;98595 51683;97845 51195 1;96975 50640;96600 50153;95588 49950 1;95220 49890;95070 49733;94725 49575 1;93727 49133;93548 48420;92625 47835 1;91995 47445;91620 47325;91088 46815 1;90465 46245;90308 45705;89528 45412 1;89153 45285;88988 45218;88710 45098; +69173 58680;69758 58988 1;69930 58950;70020 58927;70200 58875 1;70320 58845;70418 58883;70508 58965;70867 58455 1;70852 58433;70852 58395;70852 58358 1;70838 57983;70838 57795;70808 57420 1;70793 57338;70695 57338;70628 57285 1;70515 57188;70455 57143;70320 57150 1;70193 57165;70163 57105;70065 57113 1;69998 57113;69938 57120;69900 57165 1;69780 57308;69698 57360;69578 57488 1;69383 57698;69262 57885;69165 58073;69173 58680 18; +69158 59453 1;69352 59325;69548 59220;69742 59137 1;69893 59078;69848 58568;70200 58523 1;70575 58478;70725 58860;70935 58957 1;71220 59093;71438 59100;71693 59130 1;71850 59153;72068 58860;72300 58905 1;72623 58980;72735 59348;72960 59475 1;73102 59550;73268 59685;73455 59783 1;73913 60052;74055 60285;74018 60802 1;73943 61508;73590 61613;73538 62302 1;73500 62753;73605 62985;73778 63383 1;74010 63953;73875 64380;74295 64823 1;75045 65633;75510 66300;76620 66300 1;77775 66300;78705 66180;79418 67102 1;79748 67545;80168 67463;80580 67823 1;80955 68175;81345 68175;81578 68625 1;81727 68933;81908 69120;82260 69143 1;82568 69165;82650 68865;82815 68580 1;83258 67845;83700 67320;84578 67305 1;85290 67305;85575 67673;86220 67980 1;87165 68460;87660 68895;88740 68903 1;89617 68925;90188 68880;90855 68303 1;91680 67590;92160 66983;93255 66983 1;94065 66983;94477 66945;95295 66983 1;96488 67043;97260 66300;97695 65183 1;98085 64185;98730 63555;99818 63503 1;100770 63473;101318 63735;102060 64343 1;102345 64598;102495 64815;102667 65063; +70867 58455;70508 58965 1;70538 58988;70560 59010;70590 59033 1;70733 59198;70935 59115;71130 59010 1;71258 58943;71333 58740;71318 58590 1;71295 58470;71085 58410;70965 58425 1;70920 58440;70875 58492;70867 58455 18; +69173 59348;69270 59468 1;69420 59250;69488 59048;69758 58988;69165 58680;69173 59348 18; +69150 64508;69158 64500 32;69150 64508 18; +69165 61440 1;69240 61403;69233 61290;69233 61185 1;69210 61005;69180 60855;69158 60720;69165 61448;69165 61440 18; +69158 59595 1;69195 59558;69233 59520;69270 59468;69165 59340;69165 59603;69158 59595 18; +69165 61440 1;69240 61403;69233 61290;69233 61185 1;69210 61005;69180 60855;69158 60720; +75105 54075 1;74992 54405;74887 54555;74843 54893 1;74828 54990;74858 55110;74955 55095 1;75075 55088;75300 55110;75390 55050 1;75465 55005;75450 54878;75480 54765 1;75548 54450;75525 54293;75600 53970;75105 54075 18; +74813 55433 1;74813 55582;74880 55688;75023 55740 1;75173 55800;75262 55800;75405 55883 1;75540 55980;75645 56033;75683 56190 1;75698 56287;75742 56370;75848 56370 1;75960 56370;75968 56242;76028 56137 1;76080 56033;76133 55988;76230 55898 1;76328 55808;76305 55710;76320 55575 1;76320 55508;76223 55500;76155 55492 1;75645 55455;75398 55373;74895 55410;74813 55433 18; +76283 55523 1;76403 55170;76553 55035;76770 54720 1;76770 54510;76440 54728;76253 54810 1;75930 54953;75810 55140;75683 55455;76283 55523 18; +77295 53948 1;77100 54218;76995 54345;76845 54630;76733 54765 1;76545 55043;76403 55185;76298 55478;76313 55643 1;76305 55748;76305 55823;76230 55898 1;76133 55988;76080 56033;76028 56137 1;75968 56242;75960 56370;75848 56370 1;75742 56370;75698 56287;75683 56190 1;75645 56033;75540 55980;75405 55883 1;75315 55838;75248 55815;75180 55800;74918 55688 1;74843 55635;74813 55568;74813 55470;74835 54945 1;74835 54930;74835 54915;74843 54893 1;74880 54593;74970 54443;75068 54180 32; +76898 54525;76770 54720 33;76620 54938;76508 55065;76410 55238 32;76448 55305 1;76673 55305;76793 55328;77010 55403 1;77070 55433;77130 55463;77183 55410 1;77453 55148;77640 55058;77865 54750 1;77895 54713;77963 54705;77955 54645 1;77865 54278;77588 54188;77273 53985;76898 54525 18; +69195 47310 1;69270 47280;69262 47160;69203 47123;69210 47295;69195 47310 18; +69203 46635 1;69233 46688;69262 46748;69293 46815 1;69315 46890;69323 46928;69367 46995 1;69405 47070;69450 47115;69533 47100 1;69735 47078;69840 47040;70058 47033;70433 46463;70125 46313;69458 45818;69203 45570;69210 46643;69203 46635 18; +69765 44475 1;69818 44130;69840 43943;70005 43620;70020 43530 33;70012 43463;69945 43410;69878 43425 1;69788 43455;69735 43440;69653 43463 1;69570 43493;69585 43568;69540 43628 1;69405 43815;69338 43928;69323 44152 1;69308 44303;69293 44378;69300 44520 32;69323 44595 1;69352 44610;69360 44640;69398 44633;69765 44475 18; +69810 44460 1;69908 44415;70005 44385;70140 44310 1;70185 44287;70193 44228;70178 44168 1;70110 43965;70050 43845;70028 43658 32;70020 43530 32;69990 43470 32;69788 44025 32;69735 44483;69810 44460 18; +69218 43140;69285 43103;69233 43050 32;69225 43133;69218 43140 18; +69203 45593 1;69495 45923;69870 46170;70335 46418 32;70425 46448 1;70500 46305;70553 46230;70635 46080 1;70515 45983;70455 45930;70328 45840 1;70275 45810;70283 45743;70320 45683 1;70358 45630;70373 45600;70418 45540 1;70470 45473;70425 45375;70350 45323 1;70178 45225;70110 45150;69968 45015 1;69818 44888;69788 44753;69780 44550;69765 44475 32;69398 44633 1;69360 44640;69352 44610;69323 44595;69300 44520 33;69293 44378;69308 44303;69323 44152 1;69323 44100;69330 44055;69338 44018 32;69345 43935 1;69293 43957;69248 43980;69210 44003;69210 45600;69203 45593 18; +69600 43448;69218 43073; +70935 43680 1;70958 43763;71025 43800;71108 43785 1;71183 43777;71220 43710;71227 43628 1;71242 43313;71250 43155;71295 42840 1;71303 42765;71318 42713;71280 42645 1;71198 42525;71123 42495;71018 42398 1;70965 42360;70883 42435;70883 42495 1;70875 42623;70838 42713;70733 42765 1;70620 42825;70545 42818;70455 42893 1;70395 42945;70448 43027;70515 43058 1;70643 43125;70748 43133;70815 43253 1;70890 43410;70860 43515;70943 43673;70935 43680 18; +72150 40838;72968 40718; +72473 39323;73290 39412; +70755 38490 1;70710 38655;70703 38738;70680 38902 1;70665 38985;70710 39060;70793 39068 1;70852 39082;70890 39082;70958 39082 1;71040 39082;71115 39143;71130 39225 1;71137 39360;71115 39435;71123 39563 1;71123 39683;71108 39758;71168 39855 1;71265 40035;71295 40170;71483 40253 1;71558 40290;71640 40283;71640 40193 1;71633 39990;71498 39878;71468 39668 1;71438 39495;71505 39390;71625 39248 1;71678 39180;71760 39188;71850 39203 1;71992 39240;72075 39218;72218 39277 1;72367 39353;72435 39457;72458 39623 1;72495 39945;72495 40110;72473 40425 1;72465 40523;72555 40628;72645 40590 1;72765 40537;72825 40500;72945 40433 1;73020 40395;73065 40350;73080 40260 1;73155 39660;73245 39360;73313 38753;70755 38490 18; +71715 42308 1;71708 42338;71715 42368;71745 42368 1;71873 42405;71933 42450;72075 42473 1;72150 42488;72188 42495;72248 42435 1;72398 42293;72428 42203;72623 42105 1;72727 42053;72750 41970;72795 41850 1;72840 41715;72878 41640;72780 41527 1;72713 41468;72645 41453;72563 41468 1;72345 41520;72240 41543;72038 41603 1;71910 41648;71850 41723;71828 41843 1;71783 42030;71753 42128;71715 42308 18; +71887 40973 1;71940 40890;71828 40845;71775 40770 1;71655 40628;71648 40440;71625 40253;71588 40260 33;71550 40275;71512 40275;71483 40253 1;71295 40170;71265 40035;71168 39855 1;71108 39758;71123 39683;71123 39563 1;71115 39435;71137 39360;71130 39225 1;71115 39143;71040 39082;70958 39082 1;70890 39082;70852 39082;70793 39068 1;70762 39068;70740 39060;70725 39045 32;70680 39015 1;70538 39240;70477 39360;70380 39593 1;70328 39720;70305 39803;70350 39923 1;70500 40350;70613 40553;70860 40935 1;70905 41010;70980 41018;71063 40995 1;71393 40920;71565 40965;71910 40988;71887 40973 18; +71895 40912;72473 40515 32;72473 40425 33;72495 40110;72495 39945;72458 39623 1;72435 39457;72367 39353;72218 39277 1;72075 39218;71992 39240;71850 39203 1;71760 39188;71678 39180;71625 39248 1;71505 39390;71438 39495;71468 39668 1;71498 39878;71633 39990;71640 40193 1;71640 40223;71625 40245;71617 40260 32;71625 40320 33;71648 40485;71663 40650;71775 40770 1;71798 40808;71828 40830;71858 40860;71895 40912 18; +74992 41880 1;74835 41978;74738 42037;74663 42195 1;74588 42353;74610 42480;74715 42615 1;74873 42840;75150 42660;75390 42525;74992 41880 18; +74693 38873 1;74925 38963;75008 39090;75210 39240 1;75323 39330;75413 39383;75555 39345 1;75653 39330;75660 39180;75600 39098 1;75533 39015;75510 38955;75503 38843;74693 38873 18; +75540 40043 1;76695 40020;77280 39975;78443 40088 1;78653 40110;78750 40140;78968 40155 1;79102 40170;79148 40245;79275 40298 1;79665 40478;80438 41018;80820 41213; +75578 41010 1;75503 41100;75465 41152;75398 41235 1;75338 41310;75293 41430;75383 41468 1;75450 41513;75480 41543;75563 41565 1;75645 41603;75705 41588;75795 41550 1;76012 41468;76117 41400;76335 41287 1;76425 41243;76500 41265;76590 41310 1;76800 41430;76905 41483;77130 41580 1;77198 41618;77265 41595;77333 41535 1;77490 41393;77558 41310;77730 41168 1;77835 41078;77933 41108;78075 41078 1;78308 41040;78383 40830;78420 40590 1;78443 40433;78435 40350;78450 40178 1;78450 40118;78383 40080;78323 40073 1;78030 40073;77887 40050;77602 40050 1;77355 40050;77227 40027;76988 40035 1;76905 40043;76830 40103;76852 40178 1;76883 40313;76905 40380;76898 40515 1;76883 40658;76867 40800;76740 40815 1;76380 40875;76200 40860;75848 40912 1;75727 40935;75637 40912;75570 40995;75578 41010 18; +75653 40433 1;75863 40448;75975 40448;76193 40448 1;76305 40448;76387 40402;76448 40298 1;76523 40162;76575 40095;76650 39945 1;76680 39878;76620 39810;76560 39758 1;76485 39705;76410 39675;76335 39713 1;76095 39832;75975 39893;75720 39945 1;75615 39968;75540 40005;75518 40103 1;75495 40193;75480 40238;75473 40328 1;75458 40402;75548 40440;75623 40433;75653 40433 18; +80940 48060 1;81038 47895;81150 47858;81255 47693 1;81398 47468;81383 47295;81390 47018 1;81390 46748;81367 46553;81165 46380 1;80955 46215;80775 46110;80535 46200 1;80265 46313;80168 46448;79980 46658 1;79883 46770;79883 46995;80033 47018 1;80205 47055;80295 47003;80475 46980 1;80588 46973;80745 47063;80700 47168 1;80580 47430;80573 47580;80445 47835;80940 48060 18; +78788 49313 1;78975 49117;79125 48998;79133 48720 1;79133 48615;79028 48495;78930 48533 1;78727 48623;78585 48668;78488 48863 1;78405 49012;78323 49103;78383 49253 1;78413 49343;78480 49418;78570 49380 1;78660 49350;78720 49358;78803 49290;78788 49313 18; +80835 52140 1;81015 52223;81098 52305;81300 52343 1;81390 52365;81480 52343;81525 52253 1;81585 52117;81555 51975;81435 51885 1;81308 51802;81248 51750;81120 51668;80835 52140 18; +82253 53228 1;82463 53310;82680 53265;82740 53108 1;82800 52965;82680 52770;82470 52680 33;82268 52598;82043 52658;81990 52800 1;81923 52950;82050 53145;82253 53228 18; +84458 54353 1;84608 54420;84690 54427;84855 54473 1;84975 54518;85035 54548;85163 54585 1;85598 54720;85838 54735;86295 54795 1;86318 54802;86363 54818;86363 54787 1;86288 54495;86145 54383;86055 54082 1;86033 54023;85935 54068;85890 54105 1;85748 54225;85673 54285;85515 54375 1;85440 54420;85387 54435;85313 54412 1;85110 54367;84990 54383;84810 54285 1;84645 54203;84653 54037;84488 53970 1;84293 53903;84173 54045;84023 54173;84458 54353 18; +88260 54473 1;88575 54367;88703 54248;89003 54090 1;89033 54075;89070 54015;89040 54008 1;88905 54000;88838 53970;88718 54008 1;88658 54030;88590 54090;88568 54030 1;88523 53955;88515 53910;88485 53820 1;88440 53730;88305 53783;88238 53850 1;87998 54098;87983 54308;87870 54623;88260 54473 18; +82643 48832 1;82425 49073;82178 49140;82148 49455 1;82117 49688;82117 49875;81915 49973 1;81810 50025;81727 49965;81645 50033 1;81615 50055;81608 50070;81600 50100 1;81548 50250;81518 50325;81503 50475 1;81488 50543;81398 50715;81458 50745 1;81578 50820;81698 50768;81848 50783 1;82178 50828;82328 50865;82635 51015 1;82793 51105;82958 51300;83115 51398 1;83168 51443;83280 51210;83325 51150 1;83393 51068;83460 50970;83565 51008 1;83760 51082;83865 51143;83992 51308 1;84105 51465;84195 51525;84367 51608 1;84615 51742;84773 51742;85058 51795 1;85553 51893;85793 51945;86295 52035 1;87083 52193;87473 52320;88238 52568 1;88448 52643;88620 52755;88628 52973 1;88628 53078;88613 53175;88710 53213 1;88852 53287;88943 53213;89108 53205 1;89280 53205;89385 53078;89550 53145 1;89738 53235;89850 53258;90008 53393;90510 53123;91395 52680;90578 51810;90270 51563;89798 51210;89258 51000;88860 50820;87750 50250;87435 50100;86700 49778;86363 49710;85920 49665;84825 49305;84352 49177;83617 48998;82643 48832 18;88860 52350 1;88635 52238;88583 52005;88650 51758 1;88680 51637;88823 51637;88950 51660 1;89265 51728;89393 51870;89633 52088 1;89745 52200;89738 52365;89640 52478 1;89453 52703;89160 52552;88920 52395;88860 52350 18; +88920 52395 1;89160 52552;89453 52703;89640 52478 1;89738 52365;89745 52200;89633 52088 1;89393 51870;89265 51728;88950 51660 1;88823 51637;88680 51637;88650 51758 1;88583 52005;88635 52238;88920 52395 18; +81293 54188 1;81143 53963;81045 53820;80798 53723 1;80693 53685;80580 53700;80535 53798 1;80468 53925;80445 54068;80565 54150 1;80783 54323;80910 54383;81150 54525 1;81210 54570;81338 54578;81338 54495 1;81338 54345;81367 54225;81255 54128;81293 54188 18; +81203 54713;82095 54735; +81180 56423 33;81218 56378;81270 56332;81323 56280 32;81390 56205 1;81173 55950;81083 55718;80918 55425 1;80880 55380;80805 55410;80753 55425 1;80633 55470;80565 55552;80617 55658 1;80753 55988;80918 56175;81135 56453;81180 56423 50; +81367 56242 1;81083 56505;80940 56730;80783 57075 1;80723 57203;80948 57203;81083 57188 1;81413 57165;81563 57023;81840 56835;81367 56242 18; +81608 57008 1;81795 57255;81915 57412;81938 57713 1;81945 57915;81908 58058;82073 58162 1;82268 58305;82380 58365;82620 58418 1;82943 58500;83130 58470;83430 58628 1;83512 58680;83588 58770;83535 58845 1;83453 58950;83430 59018;83325 59078 1;83288 59108;83333 59160;83378 59175 1;83453 59213;83490 59258;83580 59273 1;83925 59340;83933 58883;84150 58590 1;84165 58568;84098 58590;84083 58568 1;83955 58463;83887 58410;83753 58320 1;82935 57818;82553 57503;81863 56835;81795 56865 33;81735 56910;81683 56948;81630 56985 32;81608 57008 18; +79560 57285; +80573 57308 1;80303 57600;80115 57885;80160 58275; +107977 61328;109290 59745 32;109778 56753 32;111743 56580 32;111345 53918 32;113438 53565 32;115530 48675 32;118710 48150;118643 47520 32;119880 47445;121268 47355 32; +95288 77985;95070 77535;94470 77835 32;94538 77985;95288 77985 18; +96922 77460;96352 75968 32;95693 76223 32;95565 75900 32;94553 76298 32;94890 77175 32;95798 76830 32;96150 77760 32;96922 77460 18; +102600 78008;102600 77453 32;101512 77453;101512 78008;102600 78008 18; +103118 76590;102930 77602 32;104265 77835 32;104453 76830 32;103118 76590 18; +124433 72585;122693 69503 32;122078 69855 32;121935 69608 32;121073 70102 32;123158 73793 32;122880 73958 32;123480 75023 32;124433 74490;124440 72585;124433 72585 18; +124440 69053;124297 69143 32;124208 69000 32;123833 69240 32;124448 70208;124448 69053;124440 69053 18; +124455 65903;124005 66300 32;124455 66818;124462 65903;124455 65903 18; +119250 65843;119258 66113 32;118950 66120 32;119003 67515 32;119573 67485 32;119550 66637 32;120488 66600 32;120488 66405 32;120870 66390 32;120900 66983 32;121590 66953 32;121575 66173 32;121080 66188 32;121058 65235 32;120383 65250 32;120405 65805 32;119250 65843 18; +114968 65355;116408 65280 32;116430 65843 32;117210 65813 32;117158 64995 32;116768 65025 32;116700 63945 32;116070 63990 32;116070 64155 32;115665 64177 32;115680 64537 32;114698 64598 32;114705 64883 32;114330 64912 32;114413 66608 32;115028 66578 32;114968 65355 18; +113137 65588;113085 66248 32;113542 66278 32;113565 66030 32;113783 66038 32;113820 65640 32;113137 65588 18; +114848 69855;115837 69293 32;116325 69668 32;116250 70065 32;116933 70215 32;117075 69593 32;116933 69555 32;117120 68685 32;116483 68565 32;116430 68887 32;115462 68625 32;115283 68213 32;114518 68670 32;114683 69053 32;114518 69150;114848 69855 18; +124493 54315;123960 54375 32;123983 54495 32;123120 54600;123255 55733 32;124140 55620 32;124118 55350 32;124493 55305;124500 54315;124493 54315 18; +120195 53985;120712 58133 32;121178 58088; +120660 58148;121012 58110;120945 57510 32;120578 57540; +120885 54488;120945 54945 32;121080 54915 32;121193 55680 32;121672 55605 32;121628 55223 32;121905 55170 32;121935 55373 32;122520 55283 32;122408 54398 32;121635 54503 32;121628 54383 32;120885 54488 18; +119970 55710;118658 55875 32;118635 55710 32;118012 55785 32;118102 56528 32;118268 56512 32;118403 57585 32;119400 57457 32;119265 56408 32;120038 56310 32;119970 55710 18; +119917 59033;119955 59430 32;119265 59505 32;119363 60315 32;120090 60225 32;120060 59850 32;120998 59738 32;120915 58920 32;119917 59033 18; +116977 59768;118170 59558 32;118028 58770 32;117533 58867 32;117488 58658 32;116422 58853 32;116535 59535 32;115973 59640 32;116093 60375 32;117053 60218 32;116977 59768 18; +117195 62955;117068 63120 32;117368 63353;117727 62895;118268 62805;118073 61515 32;118425 61470 32;118335 60893 32;118028 60945 32;117968 60570 32;117233 60690 32;117292 61088 32;117180 61117 32;117233 61448 32;116977 61492 32;117195 62955 18; +122400 63923;123428 63795 32;123300 62760 32;124313 62617 32;124208 61800 32;122760 61980 32;122813 62408 32;122212 62475 32;122400 63923 18; +124470 59617;122917 59820;123075 61088 32;123938 60968 32;123893 60518 32;124477 60443;124477 59617;124470 59617 18; +121568 59512;124485 59160; +124485 56677;121178 57105; +69135 68858 1;71648 70628;73065 71580;75713 73440 1;77543 74738;78413 75458;80175 76845 1;80723 77288;81060 77535;81510 77940;82133 77948;82193 77685;79598 75593;74040 71685;69135 68370;69135 68873;69135 68858 18; +82410 77948;82260 77685 32;82140 77948;82410 77948 18; +81503 77933 1;81060 77535;80723 77288;80175 76845 1;78413 75458;77543 74738;75713 73440 1;73058 71580;71648 70628;69128 68850;69128 72615;69352 74865;73883 77925;81503 77940;81503 77933 18; +120083 39735;120938 39900 32;121118 38902 32;120270 38738 32;120083 39735 18; +119220 43928;115898 44108 32;112658 44033 32;109500 43140 32;109493 41123 32;109485 39743 32;109485 38348 32;110948 38340 32;114120 38318 32;114450 40470 32;115852 41730 32;115852 42428;116325 42428;116325 41738;118500 40335 32;118605 37103 32;122085 38655 32;124553 37553; +110438 64005;111825 64005 32;111825 64073 32;112635 64073 32;112635 63435 32;112080 63435 32;112080 63052 32;111120 63052 32;111120 63255 32;110438 63255 32;110438 64005 18; +110385 60015;110415 60323 32;110078 60360 32;110137 60795 32;109733 60840 32;109808 61358 32;110422 61275 32;110385 60908 32;110685 60870 32;110715 61103 32;111623 60990 32;111495 59880 32;110385 60015 18; +112837 61170;113550 61088 32;113550 61103 32;113535 60953 32;113790 60915 32;113828 61238 32;114458 61162 32;114375 60495 32;114098 60525 32;113993 59655 32;113430 59715 32;113453 59925 32;112703 60008 32;112837 61170 18; +111300 59115;112208 59010 32;112005 57270 32;111098 57367 32;111300 59115 18; +113648 58455;114465 58365 32;114428 58050 32;114810 57998 32;114735 57345 32;114563 57353 32;114488 56662 32;113453 56775 32;113648 58455 18; +114930 58658;113153 58867 32;113190 59213 32;114915 59018 32; +114615 56235;114398 54188 32;113010 54345 32;113070 54930 32;113618 54885 32;113768 56325 32;114615 56235 18; +115845 55103;115928 55740 32;116573 55643 32;116498 55012 32;115845 55103 18; +115792 53550;115860 54533 32;116775 54465 32;116805 54735 32;117360 54690 32;117278 53445 32;115792 53550 18; +119738 51653;118508 51735 32;118523 51975 32;117983 52012 32;118050 53025 32;118890 52965 32;118875 52710 32;119797 52643 32;119738 51653 18; +115628 50670;115785 51968 32;115387 52020 32;115433 52455 32;116520 52328 32;116475 52028 32;117128 51953 32;116940 50512 32;115628 50670 18; +116258 50453;116955 50558 32;117053 49905 32;116355 49793 32;116258 50453 18; +117265 49184;117337 50190 32;118448 50100 32;118383 49101 32;117265 49184 18; +119663 51090;120413 51398 32;120345 50783 32;119850 50588 32;119663 51090 18; +121275 52890;121538 52912 32;121590 52425 32;121313 52387 32;121275 52890 18; +119595 49245;120863 49245 32;120863 48698 32;120443 48698 32;120443 48510 32;120593 48510 32;120593 47955 32;119310 47955 32;119310 48488 32;119715 48488 32;119715 48735 32;119595 48735 32;119595 49245 18; +121748 50153;121770 51173 32;123060 51128 32;123038 50115 32;121748 50153 18; +124508 50063;124050 50010;123938 51045 32;124508 51105;124515 50063;124508 50063 18; +124515 46845;123540 46823;123518 47738 32;124523 47760;124523 46845;124515 46845 18; +124523 46245 1;123135 46343;122685 46275;121245 46313;121272 47476; +91035 50423; +81653 48435;82755 46448 1;82883 45675;82545 45270;82598 44475; +81203 60323; +82369 75957; +83640 59115 1;83333 59078;83175 59213;82883 59137 1;82545 59063;82380 59010;82050 58957 1;81495 58875;81173 58658;80910 58170 1;80790 57960;80715 57863;80633 57637 1;80565 57473;80408 57398;80227 57420 1;79852 57473;79643 57480;79313 57653 1;79155 57742;79035 57998;79193 58080 1;79620 58328;79808 58508;80273 58695 1;80655 58860;80895 58988;81090 59355 1;81240 59662;81383 59798;81690 59948 1;82163 60188;82380 60345;82860 60563;83640 59115 18; +87345 62145 1;87405 61965;87503 61912;87645 61770 1;88020 61395;88283 61260;88545 60795 1;88568 60758;88560 60698;88515 60683 1;87675 60533;87248 60443;86483 60068 1;86280 59978;86168 59948;85988 59828;85800 59760 33;85688 59963;85620 60105;85523 60330 1;85418 60555;85223 60593;84983 60615 1;84840 60630;84720 60630;84608 60623 32;84398 60525 1;84173 60660;84098 60758;83850 60825 1;83543 60915;83378 60893;83063 60870 1;82973 60870;82875 60840;82852 60930 1;82845 61012;82755 61058;82808 61103 1;83010 61328;83040 61470;83115 61762 1;83183 62093;83198 62273;83115 62588 1;83085 62700;83295 62783;83378 62700 1;83625 62453;83715 62242;84053 62115 1;84615 61912;85050 62490;85215 63068 1;85260 63240;85328 63323;85477 63420 1;85905 63713;85973 64035;86183 64508 1;86205 64567;86288 64628;86325 64575 1;86460 64373;86520 64268;86625 64035 1;86655 63960;86700 63893;86655 63818 1;86588 63728;86505 63728;86415 63668 1;86250 63578;86160 63443;86190 63248 1;86213 63090;86303 63037;86393 62895 1;86483 62753;86430 62617;86340 62468 1;85943 61875;85928 61440;85965 60720 1;85965 60570;86325 60713;86318 60863 1;86265 61650;86528 62137;87120 62655;87345 62145 18; +80123 63098 33;80018 63158;79913 63330;79800 63390 1;79170 63728;79058 64073;78705 64635 32;78510 64770 33;78735 64973;78983 65183;79313 65415 1;79538 65595;79740 65745;79935 65873 32;80250 65970 1;80768 65678;81098 65528;81383 64995 1;81420 64912;81323 64823;81233 64808 1;80783 64770;80550 64733;80123 64598 1;80055 64582;80055 64463;80123 64425 1;80408 64283;80528 64170;80820 64028 1;80933 63975;80985 63900;81015 63773 1;81038 63660;81038 63600;81060 63480 1;81068 63375;81083 63300;81030 63203 1;80985 63143;80918 63143;80843 63158 1;80708 63188;80655 63233;80535 63270 1;80408 63323;80325 63203;80250 63090;80123 63098 50; +82163 66180 1;82530 66173;82725 66165;83085 66053 1;83205 66015;83235 65910;83258 65775 1;83310 65438;83325 65220;83565 64957 1;83693 64815;83700 64695;83843 64552 1;83910 64485;83918 64410;83887 64313 1;83843 64200;83798 64148;83775 64028 1;83753 63953;83640 63968;83580 64012 1;83378 64155;83213 64185;82988 64102 1;82883 64073;82830 64155;82755 64223 1;82455 64485;82193 64628;81810 64523 1;81720 64500;81623 64530;81608 64613 1;81548 64845;81488 64980;81555 65205 1;81660 65573;81788 65730;81960 66068 1;81990 66143;82035 66188;82117 66180;82163 66180 18; +84135 65730 1;84600 65835;84915 65948;85335 65708 1;85403 65670;85403 65535;85328 65498 1;84908 65340;84780 65040;84645 64613 1;84608 64500;84465 64387;84383 64470 1;84068 64770;83933 65025;83940 65453 1;83940 65573;83940 65715;84060 65723;84135 65730 18; +74843 63832 1;74850 64035;74948 64200;75060 64192 1;75165 64192;75262 64012;75248 63810 33;75248 63615;75143 63450;75038 63457 1;74925 63457;74835 63637;74843 63832 18; +79140 65190 1;78855 64965;78773 64733;78428 64635 1;78180 64575;78068 64492;77865 64343 1;77490 64080;77543 63675;77145 63457 1;76958 63367;76830 63578;76703 63735 1;76635 63818;76658 63915;76718 63998 1;76808 64148;76898 64207;76958 64373 1;76980 64448;77003 64500;76958 64552 1;76898 64620;76890 64665;76883 64740 1;76860 64845;76980 64860;77063 64920 1;77475 65273;77633 65520;78075 65835 1;78218 65948;78330 65955;78518 65948 1;78900 65940;79080 65783;79418 65580;79140 65190 18; +79913 65992 1;79598 66165;79433 66255;79178 66495 1;79080 66585;79020 66727;79117 66818 1;79500 67193;79725 67335;80130 67680 1;80205 67755;80318 67852;80385 67762 1;80708 67313;80685 66960;81068 66548;79913 65992 18; +76050 66030 1;76485 65633;76815 65393;76905 64785 1;76913 64710;76867 64635;76793 64582 1;76725 64552;76628 64537;76560 64560 1;76403 64643;76448 64800;76395 64950 1;76313 65145;76283 65250;76148 65385 1;75998 65535;76058 65700;75945 65858 1;75923 65895;75915 65933;75915 65963 1;75930 66038;75960 66075;76012 66038;76050 66030 18; +69143 67950;72930 70605 1;74602 70965;73965 69128;74866 68433 1;74939 68377;74625 67815;74535 67793 1;74198 67733;74018 67725;73688 67770 1;73508 67800;73425 67883;73253 67883 1;73050 67883;72960 67845;72765 67838 1;72713 67838;72675 67793;72690 67740 1;72727 67500;72750 67373;72758 67125 1;72758 67005;72668 66960;72578 66878 1;72390 66735;72315 66645;72150 66473 1;72060 66390;72075 66308;72075 66188 1;72075 65865;72090 65648;71887 65385 1;71843 65332;71760 65370;71723 65423 1;71543 65693;71602 65895;71573 66210 1;71558 66330;71543 66398;71468 66473 1;71190 66735;70980 66825;70613 66848 1;70538 66855;70455 66833;70440 66900 1;70358 67155;70290 67290;70117 67485 1;70005 67613;69810 67755;69653 67710 1;69420 67665;69293 67710;69135 67635;69143 67943;69143 67950 18;70658 67200 1;70815 67073;70988 67012;71040 67073 1;71093 67125;71003 67290;70852 67425 33;70695 67560;70500 67635;70455 67583 1;70403 67515;70477 67320;70635 67178;70658 67200 18;72083 69983 1;72240 69840;72383 69773;72413 69555 1;72435 69360;72315 69210;72128 69150 1;71715 69030;71498 69015;71085 68918 1;70943 68887;70852 68798;70852 68655 1;70852 68512;71010 68468;71160 68468 1;71573 68483;71783 68588;72158 68768 1;72420 68903;72637 68933;72893 68775 1;73073 68663;73223 68633;73433 68700 1;73800 68828;73920 69036;73965 69426 1;74002 69786;73782 69624;73602 69924 1;73422 70224;73292 70023;72908 70230 1;72763 70308;72615 70193;72503 70313;72083 69983 18; +79673 75315 1;79718 75120;79748 74992;79658 74805 1;79613 74723;79635 74663;79673 74565 1;79718 74430;79680 74295;79560 74220 1;79403 74130;79283 74175;79110 74205 1;78998 74227;78923 74213;78833 74145 1;78000 73590;77550 73358;76748 72765 1;76665 72713;76605 72765;76515 72765 1;76425 72773;76380 72788;76298 72818; +79673 75315 1;79718 75120;79748 74992;79658 74805 1;79613 74723;79635 74663;79673 74565 1;79718 74430;79680 74295;79560 74220 1;79403 74130;79283 74175;79110 74205 1;78998 74227;78923 74213;78833 74145 1;78000 73590;77550 73358;76748 72765 1;76665 72713;76605 72765;76515 72765 1;76425 72773;76313 72818;76230 72848;79673 75315 18; +75428 72300 1;75248 71813;74963 71655;74550 71340 1;74453 71273;74363 71333;74265 71385; +75428 72300 1;75248 71813;74963 71655;74550 71340 1;74453 71273;74280 71378;74183 71430;75428 72300 18; +82815 77798 1;82838 77633;82785 77512;82658 77400 1;82253 77070;82073 76867;81630 76598 1;81383 76455;81405 76193;81473 75915 1;81495 75818;81525 75720;81443 75660 1;81090 75428;80903 75308;80505 75188 1;80363 75150;80265 75120;80153 75203 1;80025 75293;79988 75367;79898 75480;82815 77798 18; +79898 75480 1;79988 75367;80025 75293;80153 75203 1;80265 75120;80363 75150;80505 75188 1;80903 75308;81090 75428;81443 75660 1;81525 75720;81495 75818;81473 75915 1;81405 76193;81383 76455;81630 76598 1;82073 76867;82253 77070;82658 77400 1;82785 77512;82838 77633;82815 77798; +86280 77955;86595 76823;88125 77093;89985 76545 32;90015 73095 32;88875 72420;87465 72203 32;89168 68550;90420 66930 32;92348 65805 32;97688 64012 32;98828 66390;98977 70193 32;99893 75285; +83078 77940 1;83040 77468;83003 77137;83078 76590 1;83137 76095;83168 75840;83220 75338; +86512 76725 1;86415 76695;86325 76680;86265 76748 1;86108 76920;86108 77055;85965 77227 1;85905 77295;85973 77400;86055 77415 1;86168 77445;86227 77475;86348 77505;86512 76725 18; +87398 76463 1;87690 76448;87593 76117;87480 76095 1;87173 76058;86970 76080;86670 76043 1;86610 76043;86535 76050;86535 76110 1;86528 76245;86565 76313;86580 76440 1;86588 76553;86723 76545;86835 76538 1;87060 76530;87173 76500;87405 76463;87398 76463 18; +87788 76178 1;87788 76298;87870 76395;87990 76395 1;88313 76395;88560 76515;88800 76283 1;88920 76163;88875 75908;88710 75855 1;88508 75803;88380 75848;88208 75953 1;88073 76035;87983 76028;87855 76095;87788 76178 18; +87068 75180 1;86955 75023;86948 74895;86805 74768 1;86760 74745;86715 74753;86678 74768 1;86640 74798;86610 74835;86602 74865 1;86580 74925;86543 74985;86528 75038 1;86505 75113;86512 75173;86595 75195 1;86760 75285;86843 75352;87038 75352 1;87068 75352;87083 75330;87083 75285 1;87090 75255;87075 75218;87068 75180 18; +87293 75030 1;87488 74850;87578 75458;87803 75293 1;87893 75233;87975 74400;87900 74318 1;87713 74123;87698 73973;87548 73755 1;87503 73703;87435 73605;87398 73658 1;87248 73845;87180 73943;87045 74137 1;86955 74265;86835 74348;86887 74490 1;86977 74745;87090 74843;87240 75068;87293 75030 18; +88223 75090 1;88898 75262;89250 75323;89903 75570 1;90023 75623;90030 75315;89925 75240 1;89685 75083;89558 74992;89415 74745 1;89363 74663;89273 74655;89183 74678 1;88838 74790;88590 74685;88238 74648 1;88185 74648;88223 74715;88223 74760;88223 75090 18; +87218 75090 1;87345 75300;87443 75443;87690 75473 1;87878 75503;88043 75480;88140 75293 1;88283 75023;88313 74887;88230 74633 1;88178 74490;88058 74333;87923 74393;87218 75090 18; +88695 69360 1;88463 69278;88343 69240;88110 69173 1;88028 69158;87930 69113;87908 69188 1;87848 69352;87855 69450;87855 69615 1;87848 69953;87945 70178;88223 70358;88695 69360 18; +86227 66795 1;86423 66795;86520 66855;86715 66818 1;86880 66788;86775 66570;86790 66390 1;86790 66315;87024 66274;87069 66192 1;87174 66004;86948 65918;86820 65813 1;86685 65708;86453 65708;86393 65865 1;86258 66195;86120 66278;86083 66624;86227 66795 18; +89145 60818 1;89070 61177;88973 61328;88980 61688 1;88980 61778;89018 61778;89100 61808 1;89273 61898;89400 61867;89595 61800 1;90135 61628;90518 61523;91020 61800 1;91170 61890;91275 61875;91448 61860 1;91613 61853;91695 61838;91867 61830 1;91928 61830;91973 61800;92010 61740 1;92145 61530;92258 61433;92340 61185 1;92370 61088;92393 60998;92505 60975 1;93053 60893;93315 60742;93878 60742 1;93960 60742;94050 60735;94065 60645 1;94073 60593;94080 60563;94102 60503; +91965 60390 1;92108 60533;92198 60623;92430 60630 1;92610 60637;92753 60518;92850 60345;91965 60390 18; +92288 60113 1;92175 59948;92078 59835;91883 59820 1;91583 59805;91433 59753;91140 59783 1;90975 59805;90930 60023;90953 60180;92288 60113 18; +95493 60343 1;95415 60757;95610 61119;95880 61082 1;96120 61052;96212 60605;96242 60357;95493 60343 18; +95475 60542 1;95505 60812;95610 61119;95880 61082 1;96120 61052;96158 60804;96188 60556; +87000 60270 1;87030 61200;86760 61733;87120 62580 1;87255 62910;87593 62873;87953 62895 1;88523 62940;88965 62760;89363 63173 1;89783 63623;90098 63938;90713 63908 1;91117 63893;91305 63750;91695 63600 1;91852 63540;91867 63383;91867 63203 1;91852 62948;91867 62685;92108 62580 1;92400 62460;92595 62625;92850 62423 1;93068 62250;93165 62153;93330 61920 1;93525 61650;93645 61478;93968 61380 1;94433 61245;94583 60878;94703 60398 1;94755 60180;94260 60188;94140 60367;94102 60503 33;94080 60563;94073 60593;94065 60645 1;94050 60735;93960 60742;93878 60742 1;93315 60742;93053 60893;92505 60975 1;92393 60998;92370 61088;92340 61185 1;92258 61433;92145 61530;92010 61740 1;91973 61800;91928 61830;91867 61830 1;91695 61838;91613 61853;91448 61860 1;91275 61875;91170 61890;91020 61800 1;90518 61523;90135 61628;89595 61800 1;89400 61867;89273 61898;89100 61808 1;89018 61778;88980 61778;88980 61688 1;88973 61328;89070 61177;89145 60818 32;89078 60720 1;88313 60675;87915 60615;87210 60323;87000 60270 18; +88223 61200 33;87923 61508;87675 61965;87420 62438 1;87315 62625;87180 62693;87128 62850 32;87210 62970 1;87398 63075;87518 63090;87705 63203 1;87810 63278;87885 63353;87878 63480 1;87848 63698;87818 63795;87765 63998 1;87720 64133;87915 64177;88058 64170 1;88238 64162;88335 64035;88380 63855 1;88477 63427;88530 63218;88590 62775 1;88620 62505;88673 62340;88560 62085 1;88410 61778;88410 61448;88395 61103;88223 61200 50; +91140 64050 1;91170 63998;91140 63953;91140 63885;91140 63832 33;91012 63878;90878 63908;90713 63908 1;90098 63938;89783 63623;89363 63173 32;89265 63075 1;89213 62648;89295 62430;89333 61995;89318 61867 33;89250 61867;89175 61853;89100 61808 1;89018 61778;88980 61778;88980 61688 1;88973 61328;89070 61177;89145 60818 32;88875 60713 1;88740 60690;88620 60585;88545 60690 1;88470 60787;88485 60870;88440 60975;88395 61103 33;88410 61448;88410 61778;88560 62085 1;88673 62340;88620 62505;88590 62775 1;88575 62865;88560 62940;88553 63015 32;88538 63082 33;88492 63345;88448 63540;88380 63855 1;88335 64035;88238 64162;88058 64170 1;87968 64177;87863 64162;87803 64125 32;87742 64102 1;87668 64268;87660 64365;87683 64537 1;87698 64695;87765 64815;87923 64852 1;88163 64920;88290 64942;88545 64950 1;88770 64965;88725 65302;88958 65355 1;89130 65400;89235 65265;89415 65302 1;89790 65393;90000 65453;90383 65370 1;90510 65348;90585 65302;90660 65190 1;90908 64800;90938 64552;91117 64117;91140 64050 18; +87548 67688 1;88477 66870;88988 66405;90188 66060; +91898 62258 1;92018 62220;91950 62025;91860 61927;91762 61838 33;91650 61845;91575 61860;91448 61860 1;91275 61875;91170 61890;91020 61800 1;90518 61523;90135 61628;89595 61800 1;89498 61838;89423 61860;89348 61867 32;89310 62183 1;89303 62228;89325 62258;89370 62258 1;89753 62258;89940 62220;90330 62190 1;90983 62145;91313 62302;91980 62273;91898 62258 18; +107783 61703;106455 61305;105885 62887 32;106530 63255;107783 61703 18; +106583 63278;105885 62887 32;106455 61305; +99023 52508 1;98977 52830;98948 52988;98910 53302 1;98895 53378;98955 53445;99030 53445 1;99645 53475;99960 53408;100583 53325 1;100658 53318;100762 53355;100748 53423 1;100583 54023;100508 54315;100387 54915 1;100365 55012;100410 55140;100508 55125 1;100665 55103;100740 55058;100898 54998 1;101070 54938;101085 54780;101167 54608 1;101400 54098;101505 53828;101625 53265 1;101670 53033;101580 52898;101475 52673;99023 52508 18; +98273 54457 1;98378 54660;98445 54780;98430 54998 1;98422 55058;98468 55088;98528 55095 1;98843 55185;98977 55328;99218 55552 1;99248 55590;99308 55530;99315 55478 1;99330 55223;99360 55095;99398 54832 1;99405 54773;99390 54540;99330 54533 1;99172 54525;99113 54653;98977 54578 1;98753 54465;98625 54465;98385 54465;98273 54457 18; +99262 55545 1;99712 55635;99968 55785;100417 55658 1;100515 55635;100583 55575;100613 55470 1;100658 55298;100733 55223;100748 55035 1;100748 54885;100538 55080;100395 55035 1;100065 54953;99765 54585;99518 54443 1;99420 54390;99405 54503;99315 54555;99262 55545 18; +98565 54585 1;98738 54105;98813 53873;98895 53385 1;98813 53378;98723 53370;98625 53370 1;98393 53370;98273 53355;98063 53423 1;97755 53520;97643 53738;97545 54037 1;97508 54143;97493 54195;97462 54300 1;97440 54360;97448 54435;97508 54435;97538 54435 1;97868 54443;97958 54548;98295 54555 1;98393 54563;98468 54578;98565 54585 18; +99615 53423;99540 54465 32;99600 54503 33;99810 54660;100065 54945;100350 55028 32;100387 54915 33;100508 54315;100583 54023;100748 53423 1;100762 53355;100658 53318;100583 53325 1;100215 53378;99953 53423;99683 53445;99615 53423 18; +98977 52650;98977 52808 1;98955 52965;98933 53108;98910 53302;98835 53378 1;98775 53378;98700 53370;98625 53370 1;98393 53370;98273 53355;98063 53423 1;97755 53520;97643 53738;97545 54037 1;97508 54143;97493 54195;97462 54300 1;97440 54360;97448 54435;97508 54435;97538 54435 1;97868 54443;97958 54548;98295 54555;98415 54570 1;98475 54578;98528 54585;98602 54585 1;98880 54608;99008 54690;99233 54623 1;99360 54585;99413 54548;99540 54480 1;99600 54450;99608 54420;99615 54345 1;99630 53978;99660 53933;99667 53558 1;99667 53498;99667 53415;99608 53415 32;99743 53445 1;99990 53423;100238 53378;100583 53325 1;100613 53325;100650 53332;100680 53340 33;100725 53363;100755 53385;100748 53423 1;100718 53512;100695 53595;100672 53677;100628 53865 1;100538 54210;100470 54480;100387 54915 1;100365 55012;100410 55140;100508 55125 1;100665 55103;100740 55058;100898 54998 1;101070 54938;101085 54780;101167 54608 1;101400 54098;101505 53828;101625 53265 1;101655 53093;101610 52973;101542 52823 32; +93480 52238;93908 53168 32;94965 53228 32;95242 52927 32;94725 52155;93480 52238 18; +98415 53355 1;98258 53205;98198 53063;97988 53003 1;97913 52988;97905 52867;97965 52815 1;98085 52710;98160 52635;98183 52470;98047 52305 33;97830 52283;97523 52358;97283 52328 1;95175 52125;94463 51915;92595 52455 32;92325 52530 33;91867 52695;91523 52853;91163 53018 32;91170 53078 33;91215 53175;91238 53250;91298 53355 1;91320 53423;91365 53453;91440 53453 1;91583 53453;91658 53438;91808 53438 1;91883 53438;91935 53505;91943 53580 1;91943 53662;91950 53723;91958 53783 32;91845 53820 1;91455 54098;91433 54443;91373 54908 1;91335 55170;91313 55177;91373 55433 1;91403 55613;91815 55740;91852 55860 1;91928 56093;91823 56438;91770 56753 1;91703 57060;91695 57375;91710 57623 1;91755 58245;91958 58725;92198 59460;92273 59512 32;92992 59168 32;93090 59340 32;93210 59393 1;93600 59655;93773 59820;94193 60037 1;94335 60120;94575 60255;94613 60082;94650 60023 33;94665 59798;94650 59490;94867 59490 32;95063 59378 33;95333 59355;95475 59325;95835 59235 1;96413 59108;96750 58845;96968 58313 1;97215 57698;97268 57345;97297 56677 32;97283 56535 33;97283 56378;97238 56302;97260 56137 1;97260 56078;97275 56018;97290 55965 32;97283 55740 1;97297 55440;97313 55283;97320 54975;97320 54818 33;97335 54668;97380 54578;97425 54412 32;97462 54300 33;97493 54195;97508 54143;97545 54037 1;97643 53738;97755 53520;98063 53423 1;98160 53393;98235 53378;98318 53378 32;98415 53355 18; +95310 60120;97208 60075 32;97215 59963 33;97268 59550;97283 59302;97403 58845 1;97545 58275;97740 57938;97770 57345 1;97778 57105;97792 57098;97613 56933 1;97485 56828;97350 56775;97297 56655 32;97290 56738 33;97260 57367;97208 57720;96968 58313 1;96750 58845;96413 59070;95835 59198 1;95393 59310;95273 59355;94830 59400 32;94867 59490 32;94988 59520 33;95137 59595;95205 59805;95265 60008;95310 60120 18; +98573 52365 1;99278 52440;99938 52515;100815 52590 1;102773 52515;103672 52568;105645 52350 1;106800 52223;107385 51990;108405 51420 1;109658 50723;109965 50213;111165 49410 1;112042 48818;112868 48608;113925 48473 1;114683 48383;115185 48173;115815 47730 1;116408 47318;116760 46883;117308 46620 1;117727 46425;117983 46162;118185 45990; +89753 53745 1;89895 53805;89963 53865;90128 53873 1;90262 53887;90398 53940;90473 53820 1;90548 53693;90443 53580;90360 53445;89753 53745 18; +89963 57480; +84833 56933 1;84810 57060;84855 57188;84983 57218 1;85380 57330;85620 57353;85943 57608 1;86153 57780;86288 57818;86512 57975 1;86588 58035;86678 57938;86708 57840 1;86805 57480;86790 57278;86813 56895 1;86820 56707;86828 56610;86798 56423 1;86768 56295;86670 56198;86543 56213 1;85898 56295;85605 56483;85005 56723 1;84893 56768;84833 56783;84810 56895;84833 56933 18; +84818 58733;84930 58762 33;84960 58320;84870 58028;85140 57668 1;85238 57540;85283 57473;85380 57338 32;84810 57052 33;84593 57308;84548 57480;84323 57713 1;84262 57773;84195 57855;84135 57795 1;83933 57630;83775 57623;83580 57450 1;83528 57412;83453 57383;83415 57435 1;83370 57495;83340 57525;83310 57563 32;83280 57623 32;84053 58148;84818 58733 18; +85890 59340 1;85943 59025;85973 58823;86198 58560 1;86355 58380;86438 58260;86558 58065 32;86550 57998 33;86288 57818;86153 57780;85943 57608 1;85755 57465;85590 57398;85418 57338 32;85380 57338 33;85283 57473;85238 57540;85140 57668 1;84878 58020;84953 58298;84930 58718 32;84923 58808 32;85875 59423;85890 59340 18; +87878 54787;86820 55020 32;86378 55005 32;84975 54742 32;84533 54585 32;84053 54405 32;84023 54495 33;83745 55440;83910 56250;84780 56828 32;84900 56768 33;84930 56753;84960 56745;85005 56723 1;85605 56483;85898 56295;86543 56213 1;86670 56198;86768 56295;86798 56423 1;86813 56558;86820 56648;86813 56760 32;86805 56992 33;86790 57315;86790 57518;86708 57840 1;86685 57908;86633 57975;86580 57990 32;86565 58043 33;86445 58253;86363 58373;86198 58560 1;85958 58845;85943 59063;85875 59423 32;86558 59753 32;87240 60030 32;87270 59948 33;87315 59790;87413 59640;87570 59685 1;88035 59850;88283 59895;88755 60075 1;88905 60143;88980 60262;89003 60420 32;89438 60427 32;89445 60315 33;89453 59835;89430 59558;89385 59033 1;89355 58808;89303 58665;89415 58455 1;89543 58215;89685 58073;89835 57832 1;89880 57750;89865 57615;89775 57578 1;89295 57398;88920 57488;88492 57218 1;88313 57113;88290 56955;88260 56745 1;88133 56010;87975 55635;87923 54915;87878 54787 18; +90735 52568 1;91140 52537;91358 52455;91710 52238 1;92250 51908;92468 51653;93008 51300; +81742 56325;82470 57060 32;83235 57630 32;83310 57563 33;83340 57525;83370 57495;83415 57435 1;83453 57383;83528 57412;83580 57450 1;83775 57623;83933 57630;84135 57795 1;84195 57855;84262 57773;84323 57713 1;84548 57480;84593 57308;84810 57052 32;84810 56843 32;84900 56768 33;84930 56753;84960 56745;85005 56723 1;85605 56483;85898 56295;86543 56213 1;86580 56213;86625 56220;86655 56235 32;86843 54998 32;86348 54983 32;84983 54735 32;84555 54585 32;83295 54150 32;82875 54015 32;81548 53603 32;81495 53490 32;81495 53610 33;81495 53670;81503 53715;81518 53775 1;81578 54158;81690 54330;81720 54705 1;81735 54975;81630 55110;81630 55373 1;81623 55537;81765 55598;81765 55755 1;81758 55943;81750 56040;81750 56205;81742 56325 18; +84833 56933 1;84810 57060;84855 57188;84983 57218 1;85380 57330;85620 57353;85943 57608 1;86153 57780;86288 57818;86512 57975 1;86588 58035;86678 57938;86708 57840 1;86805 57480;86790 57278;86813 56895 1;86820 56707;86828 56610;86798 56423 1;86768 56295;86670 56198;86543 56213 1;85898 56295;85605 56483;85005 56723 1;84893 56768;84833 56783;84810 56895; +93023 59205;97297 56655 1;97283 56625;97275 56588;97283 56535 1;97283 56378;97238 56302;97260 56137 1;97275 55995;97297 55898;97425 55808 1;97538 55733;97620 55755;97755 55718 1;97883 55688;97950 55523;97890 55403 1;97800 55245;97680 55223;97515 55140 1;97455 55117;97403 55088;97365 55050 32;97320 54975 32;97320 54818 33;97335 54668;97380 54578;97425 54412 32;97462 54300 33;97493 54195;97508 54143;97545 54037 1;97643 53738;97755 53520;98063 53423 1;98160 53393;98235 53378;98318 53378 32;98415 53355 1;98258 53205;98198 53063;97988 53003 1;97913 52988;97905 52867;97965 52815 1;98070 52725;98145 52650;98167 52515 32;98175 52425 32;97305 52335 32;96683 52268 32;95385 52140 32;94778 52103 32;94725 52155 32;95242 52927 32;94965 53228 32;93908 53168 32;93525 52343 32;93503 52260 32;93413 52260 32;92955 52343 32;91680 52762 32;91133 52980 33;91208 53130;91223 53213;91298 53355 1;91320 53423;91365 53453;91440 53453 1;91583 53453;91658 53438;91808 53438 1;91883 53438;91935 53505;91943 53580 1;91958 53835;92003 53955;92085 54195 1;92175 54495;92310 54608;92483 54863 1;92520 54923;92475 54998;92415 55020 32;92880 59205;93023 59205 18; +97275 56625;92977 59175 32;93015 59220 32;93083 59332 32;93510 60045 32;94635 60105 32;94650 60023 33;94665 59798;94650 59490;94867 59490 32;95018 59385 33;95318 59355;95460 59332;95835 59235 1;96413 59108;96750 58845;96968 58313 1;97208 57713;97260 57360;97290 56723;97275 56625 18; +103433 60600;103823 59543 1;103658 59475;103515 59400;103387 59512 1;103163 59700;103133 59768;103005 60023 1;102885 60255;102503 60503;102292 60668;103433 60600 18; +78615 77933 1;78630 77843;78645 77753;78668 77655 1;78810 76950;78945 76605;78990 75870 1;79005 75293;78990 74385;79545 74535 1;80250 74730;80528 75060;81030 75593 1;81375 75975;81727 76020;82245 76012 1;82590 76012;82867 76050;83085 75773 1;83220 75600;83528 75683;83625 75870 1;83835 76343;84000 76553;84367 76913 1;84795 77363;85073 77640;85463 77955; +92175 77970 1;92438 77730;92678 77483;92948 77093 1;93480 76305;93885 76005;94568 75315 1;95205 74655;95775 74625;96645 74295 1;97260 74063;97650 73815;98265 73590 1;98505 73515;98505 73275;98190 72953 1;97988 72773;97950 72510;98085 72270 1;98198 72083;98348 72060;98505 71895 1;98715 71670;98887 71520;99210 71535 1;99458 71550;99540 71730;99750 71895 1;100320 72352;100545 72660;101167 73050 1;101775 73455;102060 73695;102727 73995 1;103725 74460;104295 74790;105405 74715 1;106140 74670;106590 74730;107205 74310; +73275 77528 1;73403 76950;73688 76523;73710 75795 1;73718 75030;73230 74730;72750 74130 1;72165 73433;71685 73043;71685 72135 1;71685 71715;71715 71430;72008 71115 1;72637 70425;73500 70575;74370 70950 1;75135 71303;75578 71415;76268 71910 1;76770 72285;76995 72540;77588 72735 1;78578 73073;79095 73245;80025 73733 1;80940 74220;81338 74670;82365 74835 1;82740 74903;83010 74775;83227 74453 1;83385 74213;83670 74010;83925 74175 1;84240 74385;84367 74602;84450 74970 1;84570 75593;84570 75990;85005 76433 1;85538 76980;85778 77303;86408 77730 1;86535 77820;86648 77895;86760 77963; +91343 77963 1;91755 77565;91898 77250;92325 76793 1;92790 76290;92955 75975;93225 75330 1;93510 74655;93660 74265;94208 73755 1;94680 73320;95108 73410;95708 73170 1;96420 72893;96690 72563;97268 72053 1;97958 71445;98318 71130;98910 70410 1;99008 70283;99188 70305;99330 70395 1;99870 70800;100118 71040;100688 71415 1;101528 71970;101940 72270;102810 72795 1;103470 73200;103905 73185;104670 73335 1;105540 73515;105998 73575;106890 73470 1;107280 73350;107528 73313;107813 73005; +92970 77970 1;93548 77288;93953 76793;94748 76095 1;95738 75225;96540 75210;97808 74790 1;98385 74602;98820 74505;99105 73950 1;99225 73710;99278 73590;99390 73335 1;99488 73095;99840 73095;100050 73253 1;100650 73733;100935 73995;101625 74333 1;102555 74813;103080 74963;104108 75173 1;104977 75360;105420 75585;106305 75510 1;106837 75473;107130 75435;107625 75210 1;107993 75038;108045 74730;108172 74333 1;108360 73733;108480 73148;109110 73058 1;109688 72983;110025 72998;110558 73245 1;110970 73448;111248 73583;111428 74003 1;111623 74475;111600 74745;111667 75248 1;111690 75480;111743 75675;111968 75742 1;112208 75818;112335 75803;112605 75833; +113160 53250;113565 52305; +113468 53550;113820 52403 1;113587 52268;113393 52320;113145 52403 1;112980 52463;112935 52620;112913 52785 1;112860 53093;112868 53250;112852 53558;113468 53550 18; +113520 53198;113828 52410 33;113550 52215;113250 52373;113145 52403 1;112980 52463;112935 52620;112913 52785 1;112890 52920;112875 53025;112868 53123 32;112852 53250 32;113137 53205;113520 53198 18; +114255 50985;112950 51758; +112583 51037;114893 49957; +114533 49335 1;114435 49500;114398 49590;114270 49725 1;114188 49815;114090 49808;113985 49778 1;113602 49680;113408 49575;113018 49582 1;112785 49590;112605 49628;112508 49823 1;112268 50280;112230 50543;112125 51037 1;112087 51195;112350 51188;112500 51128 1;113378 50798;113738 50438;114623 50093 1;114743 50048;114825 50048;114930 49950 1;115140 49748;115238 49582;115245 49283 1;115245 49170;115102 49140;114990 49140;114533 49335 18; +111225 53512;112005 53363 1;112193 53302;112020 53198;112035 53010 1;112035 52875;112012 52853;111990 52710 1;111878 52155;111938 51825;112155 51293 1;112208 51165;112403 50963;112080 50940 1;111750 50918;111563 51075;111375 51338 1;111068 51773;111210 52117;111225 52643 1;111233 53093;110955 53287;110625 53573;111225 53512 18; +112868 53168 33;112875 53055;112883 52935;112913 52785 1;112935 52620;112980 52463;113145 52403 1;113363 52335;113542 52283;113738 52365 32;113880 52380 1;114292 51465;114563 51023;114863 50055 1;114893 49957;114698 50048;114608 50085 1;113940 50408;113618 50588;112950 50887 1;112703 51000;112560 51075;112297 51098 1;112133 51120;112073 51278;112035 51427 1;111953 51698;111960 51848;111938 52117 1;111885 52575;112005 52815;112155 53242;112868 53168 50; +118710 48150 32;118643 47520 32;119760 47453 32;120030 47355 1;119962 47048;119723 46912;119415 46845 1;119100 46785;118890 46853;118643 47040 1;118065 47475;117705 47595;117060 47895 1;116797 48023;116700 48203;116663 48480;118710 48150 50; +115178 49207 1;115238 49207;115290 49177;115297 49110 1;115305 48975;115395 48923;115440 48787 1;115462 48705;115583 48585;115500 48578 1;115095 48555;114840 48795;114675 49162;115178 49207 18; +106943 55170 33;107385 54832;107708 54660;107962 54533 32;108098 54375 1;107977 54113;107917 53940;107685 53768 1;107580 53700;107535 53610;107550 53483 1;107558 53348;107587 53258;107528 53130 1;107422 52943;107205 53018;106988 53033 1;106553 53078;106365 53265;105938 53302 1;105593 53340;105383 53280;105098 53460 1;104910 53580;104962 53873;105135 54000 1;105368 54188;105510 54255;105803 54338 1;106297 54488;106462 54802;106725 55253;106943 55170 50; +108262 56348 1;108705 56430;108938 56370;109387 56385 1;109538 56393;109635 56220;109605 56063 1;109500 55628;109440 55403;109297 54975 1;109223 54780;109133 54623;108922 54593 1;108743 54570;108653 54653;108518 54758 1;108300 54930;108255 55073;108045 55238 1;107933 55328;107828 55373;107813 55508 1;107775 55748;107775 55867;107760 56100;108262 56348 18; +107685 56242 1;107145 56558;106733 56528;106328 56985 1;106185 57150;106148 57255;106058 57450 1;105938 57698;105908 57870;105690 58020 1;105547 58117;105495 58238;105518 58403 1;105547 58778;105465 59145;105113 59242 1;104775 59340;104587 59280;104265 59385 1;104033 59468;103973 59617;103845 59820 1;103672 60090;103613 60248;103508 60540 1;103470 60637;103560 60720;103658 60735 1;104100 60840;104333 60818;104783 60923 1;104977 60975;105068 61028;105278 61035 1;105540 61050;105720 61103;105945 60945 1;106538 60533;106845 60315;107295 59738 1;107595 59348;107738 59137;107925 58673 1;108045 58373;108180 58193;108495 58073 1;108720 57990;108953 58035;109035 57802 1;109102 57585;109178 57473;109178 57233 1;109163 56910;108900 56760;108593 56655 1;108413 56603;108337 56543;108165 56483;107685 56242 18; +106508 56880 1;106305 56550;106230 56363;105960 56085 1;105885 56018;105833 56198;105833 56287 1;105788 56940;105765 57323;105405 57855 1;105285 58028;105240 58133;105098 58275 1;104730 58643;104618 58898;104318 59310 1;104273 59378;104385 59445;104460 59430 1;104887 59385;105278 59453;105473 59063;106508 56880 18; +107078 56040;106680 55935 32;105915 55635 32;105960 56085 33;106178 56318;106268 56490;106410 56723 32;106440 56783 32;106508 56880 32;107738 56265;107078 56040 18; +107520 56332 33;107055 56558;106688 56580;106328 56985 1;106185 57150;106148 57255;106058 57450 1;105938 57698;105908 57870;105690 58020 1;105547 58117;105495 58238;105518 58403 1;105547 58778;105465 59145;105113 59242 1;104858 59318;104693 59302;104498 59332; +107768 55935 33;107775 55800;107783 55688;107813 55508 1;107828 55373;107933 55328;108045 55238 1;108255 55073;108315 54915;108533 54742 1;108585 54705;108495 54765;108547 54735; +109365 55230 1;109665 54870;109875 54735;110212 54390 1;110288 54315;110273 54143;110167 54120 1;109883 54068;109755 53985;109500 53865 1;109275 53768;109125 53835;108900 53903 1;108675 53978;108630 54128;108473 54293 1;108420 54345;108330 54353;108352 54412 1;108398 54578;108443 54660;108533 54802;109365 55230 18; +111698 56490 1;111585 55853;111608 55598;111518 54998 1;111443 54578;111675 54008;111255 53963 1;111105 53955;111083 54135;111038 54270 1;110843 54818;110745 55110;110378 55545 1;110190 55762;110040 55845;109928 56100 1;109868 56228;110040 56318;110175 56325 1;110602 56370;110820 56393;111225 56550;111698 56490 18; +109477 57503 1;109515 57555;109620 57585;109643 57518 1;109688 57345;109710 57255;109725 57075 1;109733 56933;109875 56760;109740 56715 1;109410 56625;109230 56648;108900 56633;109477 57503 18; +106628 61133 1;106898 60855;107040 60645;107430 60585 1;107760 60540;107955 60533;108233 60330 1;108420 60188;108570 60180;108818 60188 1;108983 60195;109028 60045;109133 59903 1;109268 59707;109275 59565;109313 59318 1;109403 58650;109605 58328;109583 57645 1;109575 57548;109462 57548;109395 57473 1;109313 57405;109275 57308;109178 57330;109155 57443 33;109125 57563;109080 57660;109035 57802 1;108953 58035;108720 57990;108495 58073 1;108180 58193;108045 58373;107925 58673 1;107738 59137;107595 59348;107295 59738 1;106845 60315;106538 60533;105945 60945 1;105870 60998;105795 61028;105727 61043 32;105870 61140;106628 61133 18; +105945 60945 33;106538 60533;106845 60315;107295 59738 1;107595 59348;107738 59137;107925 58673 1;108045 58373;108180 58193;108495 58073 1;108720 57990;108953 58035;109035 57802 1;109073 57683;109110 57600;109133 57503; +108023 61365;108818 60188 33;108570 60180;108420 60188;108233 60330 1;107955 60533;107760 60540;107430 60585 1;107055 60645;106905 60840;106658 61095 32;106635 61162 32;107970 61463;108023 61365 18; +112875 53603;112875 53250 33;112598 53258;112358 53258;112125 53295 1;111833 53355;111608 53475;111345 53558 32;111172 53670 1;111195 53783;111218 53828;111248 53933;112875 53603 18; +109395 55290 33;109470 55545;109523 55748;109605 56063 1;109635 56220;109538 56393;109387 56385 1;109065 56378;108855 56408;108608 56393; +109598 57525 1;109448 57383;109320 57188;109148 57285 1;108840 57457;108428 57780;108675 58020 1;108922 58275;109095 58350;109425 58478;109598 57525 18; +109365 61425;109410 60930 32;109762 60908; +109883 61253;109898 61508; +109470 60983;109433 61718 32;109830 61740 32;109770 60968;109470 60983 18; +111983 62662;111990 63525 32;112538 63503 32;112830 62873; +112073 62520;112050 63465 32;112485 63435 32;112830 62662;112073 62520 18; +116640 63818;116670 64567 32;117698 64537; +116730 63555;116730 64500 32;117885 64440;116730 63555 18; +122198 65310;122265 66060 32;122723 66015 32;122648 65273 32;122198 65310 18; +121718 65093;121748 65535 32;122625 65468 32;122745 65033; +121792 64860;121823 65460 32;122558 65408 32;122723 64800;121792 64860 18; +124485 56730;121238 57135; +121628 59468;123300 59235 1;123255 58973;123210 58800;123000 58635 1;122745 58455;122550 58433;122250 58455 1;121920 58485;121733 58635;121560 58912;121628 59468 18; +123068 57008 1;123225 57195;123390 57262;123637 57225 1;123870 57195;123990 57090;124133 56887;123068 57008 18; +121462 57908 1;121672 57675;121778 57495;121762 57173;121358 57218;121462 57908 18; +121560 58912 1;121733 58635;121920 58485;122250 58455 1;122550 58433;122745 58455;123000 58635 1;123210 58800;123255 58973;123300 59235; +123132 57085 1;123289 57272;123390 57262;123637 57225 1;123870 57195;123926 57205;124069 57002; +121564 57793 1;121774 57560;121778 57661;121762 57339; +124485 56745;121320 57165;121560 59505 32;124485 59145;124493 56745;124485 56745 18; +124058 54427;123990 53738; +122415 54832;122795 54795;122693 53723 16; +121538 54427;121500 53895; +124493 53610;124050 53498;124133 54383 32;124500 54360;124500 53610;124493 53610 18; +121568 53617;121643 54840 32;122738 54750 32;122580 53498;121568 53617 18; +121635 53048;121620 52845 32;121380 52823 32;121365 51787 32;119820 51030 32;119887 50693 32;119025 50430 32;117938 50468 32;117637 50108 32;116955 50130 32;116790 50595 32;116903 51218 32;117555 51555 32;118297 51570 32;118590 51810; +120922 53198;120915 52110 32;119723 51540 32;119460 51728; +121568 53400;121538 52898 32;121358 52853 32;121328 51750 32;120113 51120 32;119813 50677 32;119093 50423 32;117953 50460 32;117840 50363 32;117637 50108 32;116955 50130 32;116790 50595 32;116903 51218 32;117555 51555 32;118297 51570 32;118553 51787 32;119468 51765 32;119678 51570 32;121005 52140 32;121005 53453;121568 53400 18; +118095 54555;117090 54623 32;117120 55335; +118950 54878;119047 55957; +119602 55808;119955 55785 32;119910 55238 32;119558 55260 32;119602 55808 18; +119640 55328;119565 54563; +118283 54637;117165 54705 32;117225 55478;118283 54637 18; +119910 55283;119858 54473 32;119573 54593 32;119648 55343;119910 55283 18; +119010 54608;119123 55838 32;119610 55778 32;119468 54360;119010 54608 18; +116753 55568;115020 55740 32;115058 56153 32;116273 56033; +115485 56130;115553 57000; +117023 55628;115087 55793 32;115118 56115 32;115545 56085 32;115672 57278;117023 55628 18; +115125 58718;113212 58912 32;113235 59130 32;115140 58898;115125 58718 18; +114998 60488;114308 60585 32;114352 61088 32;115080 60990; +117135 57098;116640 57630 32;116145 57600; +116318 61973;116512 61710 32;117045 61650; +115253 60540;114375 60637 32;114398 61005 32;115320 60878;115253 60540 18; +115988 57503;116618 57555 32;117030 57113 32;116528 56670;115988 57503 18; +116925 63300;117210 62963 32;117023 61695 32;116565 61762 32;116183 62310;116925 63300 18; +124493 54427;124080 54465 32;123990 53760 32;122670 53670 32;122783 54795 32;121590 54908 32;121508 53873 32;120600 54158 32;120908 57143 32;124493 56760;124500 54427;124493 54427 18; +124477 59153;121545 59543 32;121395 58380 32;120360 58463 32;120075 55853 32;119018 55890 32;118935 54923 32;117810 55695 32;116693 56730 32;117083 57105 32;116693 57563 32;116010 57608 32;115658 58748 32;115808 60600;116227 62048;116468 61785 32;117045 61650 32;117330 62992 32;117262 63300 32;117922 63870 32;119415 64335 32;122408 64335 32;124470 63818;124485 59153;124477 59153 18; +100193 40162 1;99727 39803;99480 39630;98970 39330 1;98505 39060;98227 38895;97988 38415 1;97380 36720;96795 35790;97028 34020; +110587 34073 1;110715 34155;110850 34238;111000 34320 1;111630 34703;111998 34815;112672 35115 1;112792 35175;112845 35295;112808 35423 1;112448 36510;112245 37103;112365 38235; +91136 40984;91114 41531 32;91586 41554 32;91601 41006 32;91136 40984 18; +100184 42976;101369 43231 32;101466 42758 32;100274 42503 32;100184 42976 18; +109995 39923;111188 39878 32;111150 39045 32;110310 39082 32;110318 39420 32;109973 39443 32;109995 39923 18; +121193 58320;121365 58298 32;121260 57255 32;121080 57278;121193 58320 18; +116385 44813; +116280 46785 1;114150 46890;113070 46860;111000 47332 1;110190 47520;109792 47610;108990 47798 1;108810 47843;108758 47940;108630 48060; +99750 44610 1;100170 44700;100613 44805;101167 44933 1;101925 45120;102315 45293;102968 45728 1;103433 46035;103553 46125;104025 46193 1;104895 46328;105398 46380;106328 46388 1;108885 46433;110063 45938;112620 45720; +109185 48300 1;109628 48338;109852 48405;110295 48353 1;110393 48345;110520 48315;110505 48210 1;110453 47978;110295 47835;110055 47798 1;109740 47753;109448 47707;109283 47970 1;109208 48090;109110 48158;109155 48278;109185 48300 18; +107280 48968 1;107483 48960;107640 48848;107633 48728 1;107633 48615;107498 48473;107295 48488 33;107100 48495;106905 48653;106913 48765 1;106913 48885;107085 48983;107280 48968 18; +107738 47759;105855 47856; +92348 50498 1;92183 50475;92123 50378;91965 50378 1;91860 50385;91808 50490;91755 50573 1;91590 50828;91598 50992;91470 51255 1;91403 51383;91500 51510;91635 51563 1;91785 51630;91928 51630;92040 51503 1;92250 51255;92265 51060;92348 50738 1;92370 50648;92430 50520;92340 50490;92348 50498 18; +93578 52223 1;93540 52020;93510 51637;93308 51668 1;93038 51713;92910 52170;92850 52425;93578 52223 18; +84203 50093 1;84285 50242;84495 50145;84623 50025 1;85110 49552;85230 49207;85665 48683 1;85755 48578;85710 48383;85575 48330 1;85170 48195;84945 48195;84540 48082 1;84405 48052;84308 48052;84210 48143 1;84045 48300;84000 48405;83843 48563 1;83678 48728;83640 48923;83730 49133 1;83880 49523;84015 49695;84188 50070;84203 50093 18; +83498 48870 33;84225 49193;84660 49365;85793 49650 1;86153 49740;86325 49695;86685 49815 1;87788 50205;88230 50565;89310 51015 1;90113 51360;90585 51653;91110 52193 32;91305 52320 1;91643 52185;91778 52043;92093 51840 1;92242 51750;92235 51600;92235 51412;92190 51270 33;92153 51353;92108 51427;92040 51503 32;91980 51555 33;91883 51630;91755 51623;91635 51563 1;91500 51510;91403 51383;91470 51255 1;91568 51052;91583 50903;91673 50723 32;91710 50610 1;91418 50498;91260 50468;90960 50385 1;89820 50108;89242 49995;88080 49912 1;87968 49905;87878 49830;87885 49718 1;87893 49560;87870 49478;87863 49313 1;87840 49095;87690 48983;87488 48908 1;86940 48720;86655 48668;86108 48518 1;85762 48427;85755 48090;85433 47940 1;85223 47850;85110 48105;84893 48082 1;84690 48068;84578 47993;84398 48060 1;83918 48255;83745 48480;83355 48795;83498 48870 50; +83610 44588 1;84308 44760;84698 44768;85410 44625; +88328 44423 1;87803 43800;87360 43508;87233 42698; +95670 46875 1;95588 46770;95550 46710;95475 46598 1;95438 46568;95400 46545;95363 46553 1;95318 46553;95280 46568;95250 46598 1;95130 46710;95025 46733;94973 46875 1;94943 46943;94935 47003;94935 47048 1;94935 47115;94943 47175;95003 47220 1;95063 47303;95085 47363;95183 47400 1;95288 47460;95378 47430;95498 47370 1;95573 47332;95617 47295;95700 47235 1;95730 47220;95753 47183;95753 47138 1;95760 47108;95745 47070;95738 47018 1;95708 46973;95693 46943;95670 46890;95670 46875 18; +94950 46957 1;94733 47152;94455 47190;94313 47363 1;94253 47445;94313 47745;94403 47783 1;94523 47843;94867 47820;94995 47768 1;95175 47700;95205 47543;95325 47378;94950 46957 18; +102893 47858 1;103012 47895;103020 47978;103028 48037 1;103073 48248;103118 48465;103313 48540 1;103545 48653;103733 48555;103965 48443 1;104167 48360;104295 48420;104520 48480 1;104678 48533;104850 48488;104910 48653 1;104948 48773;104993 48930;105135 48960 1;105278 48998;105323 48945;105443 48848 1;105547 48765;105683 48653;105788 48555; +103755 47100 1;103417 46853;103215 46777;102840 46598 1;102637 46508;102525 46500;102315 46455 1;101873 46380;101633 46500;101212 46628 1;100823 46748;100620 46868;100320 47138 1;100238 47213;100387 47348;100500 47332 1;100643 47318;100718 47250;100860 47280 1;101610 47460;101970 47595;102720 47805 1;103178 47940;103380 47505;103762 47213;103755 47100 18; +105741 43279;105210 46148 32;103665 45893;102345 45045;97583 44123 32;92226 43370 32;89579 43106;88851 42674 32;88282 41889;88302 41376;89016 40811;89808 40565;92475 40692 32;97361 41391;97728 41725;101055 42329 32;105741 43279 18; +98346 43880;98429 43490 32;98031 43422 32;97949 43797 32;98346 43880 18; +97299 43761;97382 43356 32;96969 43281 32;96887 43679 32;97299 43761 18; +95595 34020;95400 35513;95438 36923 32;95460 37560;96165 39255;95850 39795 32;88463 39450 32;88497 37321 32;88508 36630;88538 35287;88628 34755;88755 33998; +92595 37245;93075 37253 32;93098 36405 32;92617 36405 32;92595 37245 18; +96122 41205;96225 40418 32;90075 40125 32;90009 40568 32;92498 40628;96122 41205 18; +93698 39735;93713 40110 32;95895 40125 32;95858 39840;93698 39735 18; +109140 44588 1;109118 44813;109118 44918;109125 45135 1;109125 45293;109245 45412;109403 45412 1;109590 45412;109688 45428;109883 45420 1;109988 45420;110063 45285;110025 45180 1;109950 45008;109860 44948;109733 44813;109140 44588 18; +109140 44753 1;109118 44978;109118 44918;109125 45135 1;109125 45293;109245 45412;109403 45412 1;109590 45412;109688 45428;109883 45420 1;109988 45420;110063 45285;110025 45180 1;109950 45008;109980 45053;109852 44918; +106343 43290 1;106320 43425;106350 43537;106470 43598 1;107220 44025;107580 44295;108390 44618 1;108547 44685;108750 44655;108795 44483 1;108803 44430;108712 44460;108667 44430 1;108345 44258;108165 44198;107828 44055 1;107663 43995;107587 43950;107438 43868;106343 43290 18; +106335 43380 1;106335 43478;106373 43553;106470 43598 1;107220 44025;107580 44295;108390 44618 1;108503 44670;108637 44670;108720 44603; +108488 44633;109148 44948 32;109125 44595 32;108727 44430;108488 44633 18; +109635 46050 1;109792 45863;109935 45803;110190 45743 1;110422 45698;110535 45593;110685 45398 1;110768 45293;110753 45203;110753 45060 1;110745 45015;110685 45037;110640 45023 1;110310 44955;110145 44925;109823 44835;109868 44955 33;109928 45015;109980 45082;110025 45180 1;110063 45285;109988 45420;109883 45420 1;109688 45428;109590 45412;109403 45412 1;109343 45412;109297 45405;109253 45375 32;109020 45435 33;108840 45405;108675 45330;108533 45330 1;108323 45338;108150 45495;107962 45495 1;107587 45503;107190 45398;106695 45398 1;106553 45398;106470 45338;106387 45225 1;106305 45135;106350 45113;106320 44993 1;106283 44873;106005 44873;105983 44993 1;105945 45173;105968 45383;105983 45563 1;105990 45728;106073 45855;106238 45870 1;106538 45915;106688 45923;106995 45945 1;107243 45968;107393 46080;107520 46298 32;107805 46268;109635 46050 18; +110378 45660 1;110903 45638;111165 45593;111698 45540 1;111825 45533;112080 45540;112005 45435 1;111900 45308;111803 45270;111645 45233 1;111262 45150;111083 45060;110700 45023;110378 45660 18; +105225 46245;106380 46313 32;107520 46298 33;107393 46080;107243 45968;106995 45945 1;106688 45923;106538 45915;106238 45870 1;106073 45855;105990 45728;105983 45563 1;105968 45383;105960 45285;105998 45105 1;106020 44985;106283 44985;106320 45105 1;106350 45225;106305 45308;106387 45398 1;106470 45510;106575 45495;106718 45495 1;107212 45495;107587 45503;107962 45495 1;108150 45495;108323 45338;108533 45330 1;108675 45330;108840 45405;109020 45435 32;109110 45412 32;109193 45330 33;109148 45277;109125 45210;109125 45135 1;109118 45068;109140 44993;109140 44940 32;108698 44693 32;108593 44655 32;108525 44655 33;108480 44655;108428 44640;108390 44618 1;107633 44325;107273 44070;106620 43688 32;106575 43643 33;106283 43755;106087 43800;105825 43920 32;105675 43950 32;105180 46193;105225 46245 18; +110475 43515 1;110348 43980;110303 44265;110205 44730 1;110160 44925;110393 45000;110587 45045 1;110775 45098;110873 45120;111075 45120 1;111218 45120;111405 45248;111435 45090 1;111503 44662;111585 44332;111683 43860;110475 43515 18; +109148 43770; +106815 41858;106417 41910 33;106515 42120;106590 42225;106733 42420 1;106883 42660;106995 42690;107273 42735 1;107498 42780;107678 42457;107917 42488 1;108172 42533;108195 42930;108458 42893 1;108660 42870;108735 42848;108833 42818 32;109058 42750 1;109223 42698;109223 42525;109238 42345 1;109245 42113;109058 42023;108870 41880;106815 41858 18; +103305 42098 33;103583 42412;104280 42615;104955 42720 1;104977 42728;105000 42735;105023 42735 32;105180 42683 1;104948 42308;104723 42203;104430 41873;103305 42098 50; +105458 41880 1;105578 42180;105533 42375;105720 42638 1;105803 42773;105900 42832;106058 42840 1;106462 42870;106635 43050;107025 43193 1;107625 43433;107873 43703;108458 43980 1;108615 44063;108840 43868;108885 43695 1;108975 43298;108990 43095;109118 42698 1;109163 42548;108878 42660;108727 42630 1;107783 42465;107348 42233;106455 41918;105458 41880 18; +109448 41498;109477 40680 32;107775 40620 32;107730 41588;109448 41498 18; +97508 40680 1;97433 40418;97395 40283;97297 40020 1;97230 39863;97020 39900;96870 39983 1;96698 40080;96428 40275;96593 40373;97508 40680 18; +99803 41287 1;99998 41310;100087 41363;100290 41385 1;101340 41535;101843 41820;102915 41835 1;103035 41843;103238 41835;103200 41715 1;103073 41332;102795 41018;102510 40793 1;102315 40658;102128 40673;101948 40620 1;101355 40440;100800 40590;100073 40418 1;99863 40373;99750 40410;99540 40388 1;99180 40358;99023 40223;98670 40223 1;98363 40223;98325 40545;98167 40800;99803 41287 18; +106852 41565 1;106845 41310;106778 41183;106665 40950 1;106613 40853;106590 40748;106485 40740 1;106290 40733;106200 40725;106012 40725 1;105765 40725;105630 40635;105413 40718 1;105308 40763;105285 40845;105270 40950 1;105240 41152;105255 41258;105248 41460;106852 41565 18; +106635 41543;107797 41550 32;107843 40650 32;106485 40703;106635 41543 18; +102278 40650;103148 41760 32;104348 41460 32;105278 41445 32;105390 40718 32;103215 40740 32;102683 40703;102278 40650 18; +103598 42698 1;103628 42615;103620 42563;103650 42465 1;103695 42308;103485 42270;103335 42203 1;103012 42068;102803 42105;102465 42158 1;102240 42195;102135 42255;101925 42308;103598 42698 18; +117540 45810 1;114900 45548;113212 45443;110587 44902 1;109238 44633;107730 43890;107063 43560 1;106095 43065;106170 42923;104955 42720 1;104220 42600;103455 42368;103245 42000; +119198 43943;124530 43718; +103035 45832 1;103538 45908;103913 45968;104550 45975 1;105608 45990;106125 45803;107167 45555 1;108075 45345;108518 45000;109470 45015 1;110520 45030;110985 44580;112005 44273 1;112890 44010;113318 43815;114210 43553 1;115095 43290;115568 43170;116505 43155 1;117450 43140;117930 43125;118868 42930 1;119850 42743;120315 42533;121245 42135 1;122310 41670;123068 41543;123690 40553 1;124073 39938;124530 39645;124545 38925; +124538 38925 1;124530 38310;124462 37845;123968 37613 1;123225 37290;122685 37365;122145 36773 1;121575 36165;121545 35670;121245 34890 1;121118 34590;121042 34328;120938 34095; +110880 45870 1;111315 45638;111690 45428;112305 45233 1;113955 44700;114788 44370;116505 44093 1;117780 43890;118440 43905;119685 43553 1;120750 43260;121260 43013;122288 42593 1;123248 42218;123818 41933;124538 41453; +124545 37350 1;124365 37268;124170 37178;123945 37050 1;123210 36660;122580 36503;122348 35693 1;122198 35183;122190 34912;121988 34410 1;121935 34298;121883 34193;121830 34095; +113865 46080 1;114053 45975;114248 45855;114495 45705 1;115215 45285;115590 45000;116415 44820 1;117465 44603;118020 44550;119100 44423 1;121328 44160;122288 43695;124448 42795;124538 42773; +124545 36488 1;123570 36015;122925 35468;122648 34350 1;122618 34260;122587 34178;122558 34103; +74145 38430 1;73950 38055;73710 37950;73350 37725 1;73073 37568;72870 37613;72563 37650 1;72315 37688;72135 37478;72128 37230 1;72105 37058;72053 37043;71813 37035 1;71655 37035;71588 37140;71512 37268 1;71445 37373;71378 37463;71265 37433 1;71078 37388;70980 37395;70808 37335 1;70665 37298;70305 37268;70283 37628 1;70260 37950;70538 38078;70703 38070;74145 38430 18; +70770 38093;70538 37148 32;69503 37890;70770 38093 18; +70335 37748 1;70313 37703;70298 37658;70305 37605 1;70313 37463;70560 37193;70808 37335 1;70965 37418;71078 37388;71265 37433 1;71378 37463;71445 37373;71512 37268 1;71588 37140;71655 37035;71813 37035 1;72053 37043;72105 37058;72128 37230 1;72135 37478;72315 37688;72563 37650 1;72870 37613;73073 37568;73350 37725 1;73665 37928;73890 38033;74078 38318; +74310 33945 1;73943 34118;73718 34238;73395 34530 1;72367 35460;71655 36075;70838 36750 1;70748 36825;70650 36900;70538 36983;70448 37125;70560 37320 32;70688 37335 33;70725 37328;70762 37328;70808 37335 1;70980 37395;71078 37388;71265 37433 1;71378 37463;71445 37373;71512 37268 1;71588 37140;71655 37035;71813 37035 1;72053 37043;72105 37058;72128 37230 1;72135 37478;72315 37688;72563 37650 1;72870 37613;73073 37568;73350 37725 1;73643 37912;73860 38018;74040 38265 32;74175 38483 32;76815 38385 32;76913 38108 33;77033 37515;77288 37140;77430 36443 1;77610 35483;77790 34740;77850 33960;74310 33960;74310 33945 18; +77558 35498 1;77738 35527;77835 35535;78023 35573 1;78158 35603;78315 35603;78420 35498 1;78563 35355;78600 35198;78570 34988 1;78503 34628;78593 34545;78450 34200 1;78413 34118;78233 34103;78143 34095 1;78023 34095;77948 34103;77835 34103;77558 35498 18; +79913 33975 1;79875 34027;79838 34080;79800 34140 1;79718 34260;79545 34230;79448 34133 1;79328 34035;79238 34020;79133 33968;77820 33968;77813 34125 1;78045 34118;78038 34103;78120 34103 1;78210 34110;78338 34155;78375 34238 1;78518 34582;78503 34628;78570 34988 1;78600 35198;78548 35340;78405 35483 1;78323 35565;78233 35580;78135 35565 32;77918 35543 32;77483 35408 1;77423 35408;77385 35415;77355 35453;77355 35828 1;77963 35888;78262 35948;78878 35978 1;79088 35993;79215 35970;79410 36060 1;79545 36128;79635 36203;79658 36353 1;79695 36705;79725 36878;79740 37223 1;79740 37380;79852 37568;80003 37508 1;80205 37440;80273 37305;80378 37110 1;80505 36863;80565 36735;80685 36473 1;80753 36308;80865 36263;81015 36158 1;81218 36015;81330 35963;81548 35828 1;81690 35745;81893 35707;81968 35858 1;82043 36015;82005 36113;82088 36263 1;82125 36360;82193 36510;82290 36465 1;82425 36412;82583 36270;82583 36113 1;82583 35902;82500 35805;82418 35603 1;82313 35370;82230 35250;82245 34988 1;82245 34920;82193 34868;82125 34860 1;81727 34838;81525 34793;81135 34777 1;80985 34777;80865 34815;80723 34793;80573 34763 33;80400 34680;80348 34673;80250 34598 1;80130 34523;80145 34245;80242 34140 1;80280 34095;80310 34043;80340 33998 32;80288 33975;79913 33975 18; +81360 34770 1;81352 34808;81383 34838;81420 34838 1;81600 34868;81698 34883;81893 34868 1;81960 34868;81983 34823;82035 34770 1;82117 34688;82125 34568;82058 34463 1;81938 34298;81727 34373;81548 34463 1;81413 34537;81390 34650;81383 34800;81360 34770 18; +83250 35798 1;83400 35933;83595 35963;83670 35873 1;83745 35790;83693 35603;83543 35468 33;83393 35340;83198 35303;83130 35393 1;83048 35483;83100 35670;83250 35798 18; +83063 33983 1;83108 34058;83145 34140;83168 34253 1;83168 34298;83175 34373;83220 34350;83258 34365 1;83363 34305;83468 34275;83573 34343 1;83633 34388;83708 34425;83760 34365 1;83828 34290;83805 34185;83738 34103 1;83693 34058;83648 34013;83602 33975;83055 33983;83063 33983 18; +81765 33983;81908 34380 1;81968 34388;82012 34418;82058 34463 1;82125 34568;82117 34688;82035 34770 32;81953 34853 33;82005 34860;82065 34860;82125 34860 32;82230 34890 1;82275 34725;82395 34688;82530 34575 1;82658 34470;82762 34455;82935 34470 1;83108 34493;83063 34703;83175 34838 1;83235 34920;83333 34912;83423 34860 1;83528 34793;83355 34673;83265 34575 1;83205 34515;83168 34448;83213 34373;83258 34365 32;83220 34350 1;83175 34373;83168 34298;83168 34253 1;83145 34140;83108 34058;83070 33975;81765 33983 18; +83625 36870 1;83588 36968;83483 37043;83573 37088 1;83753 37200;83910 37260;84090 37373 1;84180 37440;84315 37455;84398 37373 1;84540 37238;84488 37095;84593 36915 1;84713 36705;84840 36608;85080 36548 1;85275 36510;85463 36540;85538 36345 1;85590 36195;85628 36113;85650 35948 1;85658 35850;85628 35775;85553 35715 1;85477 35670;85425 35670;85343 35685 1;84990 35768;84803 35843;84450 35783 1;84308 35760;84165 35730;84098 35850 1;83873 36233;83783 36435;83625 36840;83625 36870 18; +83520 37035 1;83453 37185;83415 37268;83378 37418 1;83348 37515;83430 37643;83528 37628 1;83760 37598;83895 37620;84113 37515 1;84173 37485;84240 37463;84300 37410;83520 37035 18; +84367 37365 1;84863 37283;85268 37343;85560 36923 1;85643 36803;85650 36683;85583 36540 1;85538 36473;85463 36473;85387 36473 1;85095 36495;84915 36510;84705 36698 1;84495 36885;84465 37050;84367 37298;84367 37365 18; +81278 35955 1;81383 36480;81518 36713;81727 37200 1;81773 37320;81900 37373;82012 37313 1;82253 37193;82365 37118;82598 36960 1;82718 36878;82830 36863;82965 36923 1;83168 37020;83408 37043;83617 37133 1;83640 37148;83595 37215;83648 37035 1;83753 36503;83963 36428;84113 35895 1;84120 35850;84090 35828;84075 35783 1;83963 35580;83880 35385;83648 35385 1;83512 35385;83445 35408;83318 35408 1;83280 35408;83235 35400;83242 35363 1;83280 35168;83295 35055;83242 34860 1;83175 34658;83137 34478;82935 34418 1;82658 34350;82477 34493;82290 34695;81278 35955 18; +88395 35685 1;88170 35730;88050 35895;88035 36113 1;87998 36450;87983 36615;87960 36945 1;87945 37103;88012 37193;88125 37290 1;88215 37380;88283 37388;88395 37440;88395 35685 18; +88931 37484;89374 37484 32;89374 36689 32;88931 36689 32;88931 37484 18; +89900 36396;90252 35946 32;89712 35533 32;89367 35983 32;89900 36396 18; +84262 37410 1;84675 37658;84908 37740;85365 37912 1;85477 37957;85613 37928;85650 37808 1;85748 37463;85778 37275;85845 36915 1;85852 36840;85740 36758;85680 36803;85598 36848 33;85590 36870;85575 36900;85560 36923 1;85275 37328;84893 37290;84420 37358 32;84352 37320;84262 37410 18; +83617 33983;83738 34103 1;83805 34185;83828 34290;83760 34365 1;83708 34425;83633 34388;83573 34343 1;83468 34275;83363 34305;83258 34365;83220 34350 32;83190 34448 33;83190 34493;83220 34537;83265 34575 1;83355 34673;83528 34793;83423 34860 1;83378 34890;83340 34898;83303 34905 32;83242 34860 33;83288 35040;83280 35145;83250 35303 32;83220 35355 33;83273 35348;83340 35363;83408 35385 32;83445 35400 33;83505 35400;83565 35385;83648 35385 1;83880 35385;83963 35580;84075 35783 32;84098 35850 33;84165 35730;84308 35760;84450 35783 1;84803 35843;84990 35768;85343 35685 1;85425 35670;85477 35670;85553 35715 1;85628 35775;85658 35850;85650 35948 1;85628 36113;85590 36195;85538 36345 1;85523 36383;85508 36405;85492 36428 32;85455 36480 33;85500 36480;85545 36495;85583 36540 1;85613 36623;85628 36690;85620 36758 32;85770 36780 1;85943 36638;86048 36510;86137 36353 32;86288 36075 1;86325 36008;86280 35933;86220 35880 1;85838 35573;85673 35378;85410 34965 1;85343 34868;85485 34733;85605 34748 1;86003 34815;86175 35033;86565 35168 1;86678 35213;86790 35040;86813 34912 1;86835 34770;86730 34635;86850 34537 1;86925 34478;87000 34425;87068 34380;86678 33998;83617 33983 18; +88193 33998 1;88185 34058;88178 34118;88163 34193 1;88133 34328;88133 34448;88245 34523 1;88358 34605;88425 34628;88545 34703;88665 33998;88193 33998 18; +86303 36000 1;86400 36090;86558 36150;86625 36037 1;86790 35753;86768 35662;86880 35340 1;86918 35228;86783 35220;86625 35152;86565 35108 33;86175 34973;86003 34815;85605 34748 1;85485 34733;85343 34868;85410 34965 1;85673 35378;85838 35573;86220 35880 1;86235 35902;86250 35918;86265 35940 32;86303 36000 18; +86498 33998;86933 34455 1;87030 34402;87203 34313;87255 34207;87285 34125 33;87285 34080;87285 34035;87270 33990;86498 33998 18; +87210 33998 1;87293 34050;87428 34073;87473 33990;87210 33998 18; +87465 34005;87345 34043 32;87285 34043 33;87285 34073;87285 34103;87285 34125 32;87255 34207 1;87218 34283;87165 34313;87105 34335 32;87023 34410 33;86970 34448;86910 34493;86850 34537 1;86730 34635;86835 34770;86813 34912 1;86798 34988;86760 35063;86708 35115 32;86625 35152 33;86783 35220;86918 35228;86880 35340 1;86768 35662;86790 35753;86625 36037 1;86558 36150;86400 36090;86303 36000 32;86288 36075 32;86137 36353 33;86048 36510;85943 36638;85770 36780 33;85793 36810;85845 36870;85845 36915 1;85838 36938;85838 36960;85830 36975 32;85823 37020 33;85770 37320;85733 37500;85650 37808 1;85613 37928;85477 37957;85365 37912 1;84930 37755;84698 37673;84323 37455 32;84240 37455 33;84195 37478;84150 37500;84113 37515 1;84098 37523;84090 37530;84075 37530 32;84023 37553 33;83850 37620;83723 37605;83528 37628 1;83430 37643;83393 37508;83423 37410 1;83445 37313;83468 37253;83498 37178 32;83505 37095 33;83325 37035;83130 37005;82965 36923 1;82920 36908;82890 36900;82852 36893 32;82800 36870 32;82290 36465 33;82193 36510;82125 36360;82088 36263 1;82005 36113;82043 36015;81968 35858 1;81893 35707;81690 35745;81548 35828 1;81330 35963;81218 36015;81015 36158 1;80865 36263;80753 36308;80685 36473 1;80565 36735;80505 36863;80378 37110 1;80273 37305;80205 37440;80003 37508 1;79852 37568;79740 37380;79740 37223 1;79725 36878;79695 36705;79658 36353 1;79635 36203;79545 36128;79410 36060 1;79215 35970;79088 35993;78878 35978 1;78360 35955;78068 35910;77625 35858 32;77550 35858 32;77483 36120 32;76898 38453 32;85230 39015;85650 38378 32;86025 37695 32;86288 37200;87180 37365;88395 37440 33;88283 37388;88215 37380;88125 37290 1;88095 37275;88073 37253;88058 37230 32;88020 37193 33;87975 37125;87945 37050;87960 36945 1;87983 36615;87998 36450;88035 36113 1;88043 35918;88148 35760;88343 35700 32;88433 35662 32;88598 34695 32;88448 34650 33;88380 34613;88320 34582;88245 34523 1;88133 34448;88133 34328;88163 34193 1;88178 34118;88185 34050;88193 33990;87450 33998;87465 34005 18; +82245 36488;82800 36900 32;82875 36900 33;82905 36900;82935 36915;82965 36923 1;83115 36998;83295 37035;83460 37080 32;83535 37043 33;83535 36998;83595 36938;83625 36870;83625 36840 1;83783 36435;83873 36233;84098 35850 32;84075 35783 33;83963 35580;83880 35385;83648 35385 1;83573 35385;83520 35393;83468 35400 32;83543 35468 33;83693 35603;83745 35790;83670 35873 1;83602 35955;83453 35940;83318 35858 32;83250 35798 33;83100 35670;83048 35483;83130 35393 1;83145 35370;83175 35355;83213 35348 32;83250 35303 33;83280 35145;83288 35040;83242 34860 32;83175 34838 33;83063 34703;83108 34493;82935 34470 1;82762 34455;82658 34470;82530 34575 1;82395 34688;82275 34725;82230 34890 32;82245 34988 33;82230 35250;82313 35370;82418 35603 1;82500 35805;82583 35902;82583 36113 1;82583 36270;82425 36412;82290 36465;82245 36488 18; +69352 34590 1;69608 34628;69758 34748;69998 34635 1;70110 34582;70133 34463;70110 34328 1;70073 34178;69990 34095;69848 34035 1;69750 34005;69690 33975;69600 33998 1;69488 34035;69428 34058;69330 34088;69352 34590 18; +69233 37740 1;69285 37710;69345 37673;69413 37635 1;69525 37568;69727 37395;69600 37350;69233 37290;69240 37740;69233 37740 18; +69248 36248;69555 36713 32;69975 37373 32;70208 37230 33;70448 37058;70673 36885;70838 36750 1;71655 36075;72367 35460;73395 34530 1;73718 34238;73943 34125;74295 33953;69255 33938;69248 36248 18; +82305 36465 33;82470 36375;82583 36248;82583 36113 1;82583 35902;82500 35805;82418 35603 1;82313 35370;82230 35250;82245 34988 1;82245 34965;82230 34935;82215 34912;82253 34823 1;82313 34718;82410 34673;82530 34575 1;82658 34470;82762 34455;82935 34470 1;83108 34493;83063 34703;83175 34838 1;83183 34860;83198 34868;83265 34973 1;83288 35108;83265 35213;83213 35348 1;83175 35355;83145 35370;83130 35393 1;83048 35483;83100 35670;83250 35798;83318 35858 1;83453 35940;83602 35955;83670 35873 1;83745 35790;83693 35603;83543 35468;83468 35400 1;83520 35393;83573 35385;83648 35385 1;83880 35385;83963 35580;84075 35783;84098 35850 33;83873 36233;83783 36435;83625 36840;83625 36870 1;83595 36938;83535 36998;83535 37043; +80123 33975;80235 34118 1;80378 33953;80558 33960;80783 33990 1;80977 34027;81000 34200;81135 34343 1;81218 34448;81367 34380;81465 34283 1;81548 34207;81668 34103;81585 34013;81518 33968;80123 33975 18; +81893 34373;81608 34095 33;81600 34162;81518 34230;81465 34283 1;81367 34380;81218 34448;81135 34343 1;81000 34200;80977 34027;80783 33990 1;80558 33960;80378 33953;80235 34118 32;80213 34178 33;80137 34298;80137 34530;80250 34598 1;80348 34673;80400 34680;80573 34763 32;80723 34793 1;80865 34815;80985 34777;81135 34777 1;81210 34785;81293 34808;81390 34777 33;81398 34650;81420 34537;81548 34463 1;81630 34425;81727 34380;81810 34373;81893 34373 18; +79125 33968 1;79238 34020;79328 34035;79448 34133 1;79545 34230;79718 34260;79800 34140 1;79838 34080;79875 34027;79913 33968;79117 33975;79125 33968 18; +81540 33990;81585 34013 1;81608 34043;81615 34065;81608 34095 32;81645 34133 32;81893 34373 32;81938 34388;81855 33975;81540 33983;81540 33990 18; +95670 46875 1;95588 46770;95550 46710;95475 46598 1;95438 46568;95400 46545;95363 46553 1;95318 46553;95280 46568;95250 46598 1;95130 46710;95025 46733;94973 46875 1;94943 46943;94935 47003;94935 47048 1;94935 47115;94943 47175;95003 47220 1;95063 47303;95085 47363;95183 47400 1;95288 47460;95378 47430;95498 47370 1;95573 47332;95617 47295;95700 47235 1;95730 47220;95753 47183;95753 47138 1;95760 47108;95745 47070;95738 47018 1;95708 46973;95693 46943;95670 46890; +99667 53460;99720 52590 32;99000 52523 32;98977 52650 32;98977 52808 1;98955 52965;98933 53108;98910 53302;98835 53378 32;98933 53460;99667 53460 18; +100313 75218 1;100283 74850;100215 74670;100170 74295 1;100080 73658;101325 72360;101813 72060;107265 71753 32;108390 67440; +101520 67823; +103268 65910; +97365 37207; +96413 35798; +113805 35933; +115455 35190; +115335 36938; +116745 38303; +118477 35010; +118868 76673 1;118118 75915;117443 75473;117480 74408 1;117503 73643;117818 73238;118387 72713 1;119093 72060;119415 71588;120337 71288; +106417 70725 1;106515 70793;106628 70830;106718 70748 1;106868 70620;106710 70433;106620 70253 1;106373 69795;106605 69473;106553 68955 1;106523 68730;106477 68602;106320 68430 1;105998 68108;105840 67950;105540 67605 1;105375 67425;105075 67688;105023 67920 1;104940 68273;104985 68475;105113 68805 1;105285 69278;105383 69503;105608 69945 1;105803 70350;106050 70455;106410 70725;106417 70725 18; +108352 67560;108398 67463 32;110212 65933 32;110265 65843 1;110183 65783;110160 65730;110078 65670 1;110025 65640;109973 65602;109935 65640 1;109733 65828;109628 65918;109433 66098 1;108975 66525;108683 66653;108143 66945 1;108015 67020;107917 67170;108015 67275 1;108105 67380;108165 67418;108270 67508;108352 67560 18; +99090 70635 1;99780 70628;100148 70598;100815 70380 1;100973 70328;101063 70238;101108 70065 1;101243 69525;101438 69262;101445 68700 1;101445 68408;101363 68250;101205 67995 1;101123 67875;101070 67815;101063 67665 1;101055 67575;100792 67553;100830 67635 1;100898 67830;100883 67943;100958 68130 1;101025 68318;101137 68445;101040 68610 1;100883 68873;100800 69038;100530 69173 1;100238 69323;100058 69345;99743 69330 1;99450 69323;99300 69315;99015 69293;99090 70635 18; +98880 67477 1;99255 67553;99578 67658;99848 67373 1;100042 67163;100155 67073;100358 66863 1;100455 66758;100688 66810;100718 66953 1;100770 67215;100718 67365;100815 67605 1;100837 67688;100980 67680;101025 67605 1;101153 67403;101355 67178;101565 67313 1;101843 67508;102053 67605;102383 67515 1;102608 67455;102743 67313;102765 67073 1;102788 66803;102727 66630;102555 66413 1;102405 66240;102405 66090;102420 65858 1;102428 65625;102578 65453;102818 65415 1;103268 65363;103665 65340;103920 65723 1;104093 66000;104250 66120;104310 66435 1;104348 66660;104438 66915;104663 66878 1;104880 66848;105030 66720;105075 66495 1;105120 66210;105352 65985;105645 66030 1;106012 66098;106410 66120;106553 65768 1;106703 65393;106725 64957;106373 64762 1;105262 64133;104670 63608;103613 62970 1;103350 62813;103110 62828;103178 62528 1;103365 61695;103395 61740;103590 60900 1;103605 60832;103568 60750;103500 60750 1;103260 60750;103148 60690;102915 60690 1;102840 60690;102795 60735;102773 60795 1;102637 61148;102547 61418;102195 61515 1;100808 61912;100163 62242;98775 62603 1;98573 62655;98348 62662;98288 62468 1;97995 61515;97800 61170;97305 60720 1;97245 60668;97173 60451;97098 60451 1;96896 60458;96861 60334;96831 60521 1;96741 60994;96870 61043;97073 61470 1;97395 62175;97417 62595;97477 63367;97568 63383 33;97703 63360;97845 63375;98010 63427 1;98153 63495;98205 63600;98265 63742 32;98265 63795 1;98355 64095;98385 64245;98490 64537 1;98558 64755;98498 64973;98303 65063 1;98235 65093;98205 65160;98235 65220 1;98422 65685;98580 65887;98783 66345;98880 67477 18; +102893 60750;100808 60826 32;98828 60262 32;97425 60255 32;98212 62775 32;98633 62887 32;102390 61733 32;102855 61020;102893 60750 18; +98903 67500 1;99225 67553;99428 67620;99727 67477 1;99915 67395;100005 67260;100028 67043 1;100080 66518;99900 66255;99818 65723 1;99705 65033;99608 64635;99150 64095 1;98940 63863;98715 63795;98408 63832 1;98115 63878;97958 63855;97688 63960;97733 64117 32;98828 66390;98850 67117 32;98903 67500 18; +104865 66503;108323 67425 32;109028 64950 32;105900 62873 32;106433 61260 32;105150 61373 32;104633 61207 32;103500 60795 32;102690 63427 32;104917 64852;104865 66503 18; +105150 66270 1;105120 66413;105083 66593;105225 66615 1;106448 66878;107047 67065;108248 67418 1;108300 67440;108360 67410;108383 67350 1;108615 66435;108720 65977;109005 65070 1;109028 64995;109028 64920;108960 64867 1;107790 64088;107235 63653;106020 62955 1;105953 62925;105885 62873;105915 62798 1;106095 62280;106163 62010;106387 61500 1;106417 61418;106305 61358;106215 61350 1;105780 61335;105555 61433;105128 61350 1;104925 61320;104820 61298;104633 61223 1;104175 61065;103995 60840;103523 60773 1;103440 60765;103350 60765;103343 60840 1;103200 61658;103283 61485;103110 62287 1;103020 62677;103403 62903;103748 63105 1;104318 63457;104738 63720;105420 64155 1;105637 64290;105637 64793;105547 65033 1;105352 65520;105300 65775;105150 66270 18; +90503 50235;92340 50512; +103223 53100 1;102600 53100;102292 53123;101678 53175;101633 53175 33;101633 53205;101625 53235;101625 53265 1;101528 53707;101445 53963;101303 54293 32;101265 54323 1;102248 54240;102735 54165;103710 53992;103223 53100 18; +113940 41393;113933 41850 32;114735 41850 32;114743 41400 32;113940 41393 18; +118080 37358; +116430 41678;116512 41618 32;118500 40335 32;118462 39060 32;118433 38865 1;118028 38633;117803 38438;117667 37995 1;117375 37073;117540 36480;117922 35588;117983 35513 33;117818 35332;117630 35213;117375 35040 1;117105 34875;116903 34838;116655 34755 32;116385 34770 33;115643 35573;115403 36008;114825 36930 1;114623 37253;114547 37613;114533 37995 32;114533 38183 32;114585 38393 1;114968 38385;115148 38543;115493 38723 1;116055 39030;116430 39390;116430 40027 1;116422 40695;116363 41018;116318 41678;116430 41678 18; +114727 37230 1;114480 37200;114337 37253;114120 37358 1;113775 37530;113783 37853;113745 38220 1;113738 38280;113805 38287;113865 38280 1;114105 38273;114225 38348;114473 38370;114727 37230 18; +115943 35348 1;115268 35348;114930 35565;114352 35895 1;114210 35978;114233 36150;114308 36285 1;114473 36600;114570 36750;114788 37027;115943 35348 18; +111968 34073;112058 34148 1;112440 34388;112658 34455;113063 34665 1;113167 34725;113363 34703;113363 34575 1;113363 34395;113378 34215;113318 34073;111968 34073 18; +124545 37508;122010 38648 32;118598 37140 32;118508 40358 32;116333 41738;116265 42375;115868 42353 32;115837 41738 32;114420 40470 32;114075 38340 32;109523 38348 32;109448 43155 32;112613 44033 32;119220 43943 32;124530 43695;124553 37508;124545 37508 18; +118823 34080 1;118920 34230;119003 34298;119153 34410 1;119265 34508;119445 34485;119528 34350 1;119587 34245;119640 34170;119685 34088;118815 34095;118823 34080 18; +120653 34103 1;120750 34193;120848 34148;120953 34095;120667 34103;120653 34103 18; +123465 34110 1;123585 34170;123712 34253;123848 34350 1;123990 34485;124238 34568;124433 34500 1;124477 34485;124523 34478;124560 34470;124568 34110 32;123465 34110 18; +119288 35280;119813 34095; +114990 34073 1;115417 34238;115875 34418;116415 34650 1;116580 34733;116723 34777;116858 34815 32;116903 34658 1;117045 34433;117158 34253;117262 34080;114990 34088;114990 34073 18; +97598 39953 32;99398 40185 1;99653 40223;99893 40245;100118 40275 32;100193 40162 33;99727 39803;99480 39630;98970 39330 1;98565 39098;98303 38948;98085 38595 32;98003 38483 1;97875 39045;97725 39308;97508 39832;97598 39953 50; +96098 36323;96158 36930 32;97223 36323 33;97065 35783;96975 35250;96998 34605 32;96188 34793;96098 36323 18; +97028 34027 1;96795 35790;97380 36728;97988 38415 1;98227 38895;98505 39060;98970 39330 1;99480 39630;99727 39803;100193 40162;102675 40402;102750 40733;103163 40748;106050 40763;106500 40740;109470 40553;109530 38370;112365 38235 1;112245 37103;112448 36510;112808 35423 1;112845 35295;112792 35175;112672 35115 1;111998 34815;111630 34703;111000 34320 1;110843 34230;110700 34148;110565 34065;97020 34027;97028 34027 18;107258 40433 1;107512 40260;107640 40148;107970 40148 1;108255 40148;108473 40207;108645 40440;107258 40433 18; +96765 34020 1;96637 35100;96743 36075;97140 37268 1;97455 38243;97665 38655;98348 39270; +88755 34005;88628 34755 32;88538 35287;88508 36630 32;88463 39450 32;95850 39795 32;96165 39255;95460 37560;95438 36923 32;95400 35513;95595 34020;88755 34005 18; +117540 74992 1;117637 74873;117533 74393;117630 74265 1;119033 72435;120172 72390;122137 71633 1;123060 71288;123705 71033;124448 70613; +120113 71183 1;121343 70762;122333 71168;123225 70193; +108510 40305 33;108360 40185;108188 40148;107970 40148 1;107715 40148;107580 40215;107415 40328; +96930 44738 1;97740 44655;98258 44745;99165 44655 1;100215 44550;100755 44483;101805 44595 1;102810 44715;103320 44865;104325 44790 1;105248 44745;105727 44670;106590 44310 1;107595 43890;108180 43875;109245 43575 1;110227 43313;110775 43402;111750 43073 1;112868 42705;113445 42563;114570 42135 1;115440 41805;115890 41610;116828 41535 1;118028 41430;118620 41190;119828 41190 1;120885 41190;121417 40823;122310 40230 1;122708 39975;122940 39803;123165 39375 1;123375 38985;123337 38460;122925 38273 1;122288 37995;121980 37815;121305 37613 1;120915 37500;120727 37260;120630 36870 1;120435 36210;120450 35850;120210 35213 1;120128 35010;120038 34853;119933 34710; +90105 44213 1;90345 44145;90593 44055;90885 43935 1;91688 43605;92025 43260;92887 43095 1;93727 42930;94170 42983;95025 42953 1;96578 42915;97358 42953;98910 42810 1;100350 42690;101085 42623;102510 42293 1;103515 42060;104010 41790;105045 41730 1;106665 41655;107475 41655;109110 41550 1;109898 41513;110205 41085;110925 40733 1;111765 40335;112320 40313;113047 39713 1;113310 39503;113408 39315;113468 38970 1;113618 38055;113468 37523;113850 36652 1;114158 35940;114345 35580;114870 34995 1;114915 34935;114990 34943;115065 34890 1;115245 34770;115050 34380;114825 34395 1;114345 34425;114098 34380;113625 34350 1;113265 34335;113033 34230;112800 34073; +92753 44152 1;93060 44078;93383 44033;93810 44010 1;94598 43995;94988 43853;95790 43853 1;97515 43853;98385 44010;100125 43995 1;101400 43980;102000 43575;103260 43343 1;104505 43125;105105 42825;106380 42743 1;107648 42675;108300 42900;109575 42743 1;110160 42675;110408 42450;110977 42263 1;111675 42045;112140 42210;112778 41820 1;113288 41513;113528 41325;113977 40905 1;114435 40485;114908 40635;115538 40703 1;116348 40800;116768 40853;117578 40703 1;118230 40582;118635 40260;118860 39623 1;119145 38745;119205 38273;119378 37343 1;119475 36743;119610 36285;119205 35813 1;119137 35745;119070 35678;119010 35618; +83528 41888 1;83828 41603;84270 41430;84727 41152 1;85380 40763;85620 40425;86295 40065 1;87248 39570;87900 39773;88980 39705 1;90458 39623;91215 39713;92700 39465 1;94005 39240;94643 39218;95820 38588 1;96518 38228;95715 37493;95753 36698 1;95798 35662;95760 35513;95865 34545 1;95887 34328;95903 34155;95910 34020; +81375 40148 1;81495 40027;81630 39893;81788 39750 1;82800 38820;83438 38475;84690 37875 1;85800 37343;86445 37170;87390 36353 1;88155 35693;88875 35332;88905 34313 1;88905 34200;88898 34095;88890 33998; +76305 38633 1;76508 38363;76785 38123;77108 37815 1;77760 37185;78225 37005;78810 36293 1;79320 35662;79575 35213;79568 34395 1;79560 34245;79553 34103;79538 33968; +103898 66593 1;104055 66788;104078 66818;104295 67065 1;104685 67545;105120 67605;105735 67620 1;106560 67665;106935 67283;107655 66863 1;108285 66495;108630 66195;109365 66195 1;110400 66195;110865 66660;111765 67170 1;112350 67515;112920 68003;113408 67530 1;113925 67043;113828 66443;113648 65730 1;113325 64590;112875 64110;112208 63135 1;111375 61943;110340 62025;109065 61313 1;107520 60465;106650 60165;105330 58995 1;105053 58762;104805 58530;104542 58328; +69173 56070 1;69390 56018;69623 55973;69908 55935 1;70538 55860;70845 55680;71490 55710 1;72120 55755;72420 55920;73050 56070 1;73830 56273;74175 56490;74925 56790 1;75870 57180;76545 57953;76305 58935 1;76058 59955;75885 60443;75645 61455 1;75458 62220;75518 62685;75810 63412 1;75960 63802;75990 64125;76365 64290 1;76995 64590;77355 64673;78045 64830 1;78735 65010;79230 65040;79665 65610 1;79958 66000;80160 66233;80625 66352 1;81030 66465;81225 66585;81645 66555 1;82238 66525;82568 66330;82988 65895 1;83460 65393;83918 65363;84608 65310 1;85005 65295;85215 65190;85628 65213 1;86205 65250;86475 65423;87045 65550 1;87810 65745;88215 65805;89010 65775 1;90120 65745;90668 65543;91770 65310 1;92887 65085;93518 65175;94590 64733 1;95280 64455;95730 64530;96345 64110 1;96908 63735;97221 63426;97596 62849 1;98106 62046;98370 61662;98865 60845 1;99255 60207;98985 59393;98408 58912 1;97665 58320;97200 58162;96390 57675 1;95475 57143;95115 56730;94185 56250 1;93135 55733;92550 55590;91568 54953 1;90795 54465;90405 54203;89730 53595 1;88770 52740;88148 52485;87270 51555 1;86730 50992;86580 50625;86145 49973 1;85755 49395;85530 49110;85028 48630 1;84308 47963;83970 47565;83565 46673 1;83198 45885;83190 45390;83190 44513 1;83190 43838;83078 43470;83123 42848; +88515 77033 1;88898 77250;89198 77430;89730 77430 1;90240 77445;90555 77235;90870 76815 1;91148 76433;91440 76253;91448 75773 1;91448 75555;91538 75443;91530 75210 1;91500 74775;91245 74580;91290 74130 1;91328 73590;91358 73305;91605 72810 1;92145 71745;92708 71123;93885 70830 1;94380 70710;94658 70905;95130 71093 1;95438 71220;95610 71340;95948 71310 1;96465 71280;96795 71153;97125 70733 1;97800 69885;98018 69360;98625 68453 1;98835 68130;98940 67950;99165 67635 1;99308 67440;99608 67515;99765 67695 1;100215 68213;100575 68333;101085 68790 1;101715 69383;102000 69742;102705 70230 1;103297 70650;103635 70815;104348 70995 1;105225 71220;105705 71333;106628 71250 1;107227 71205;107490 70913;107985 70553 1;108547 70155;108908 69923;109605 69930 1;110445 69953;110805 70425;111428 70995 1;111945 71483;112208 71730;112605 72315 1;113070 72990;113490 73290;114285 73470 1;114968 73635;115320 73755;116025 73755 1;116580 73762;117113 73583;117653 73433; +92258 51225 1;92183 51188;92100 51143;92025 51090 1;91867 51015;91748 50933;91635 50850; +90975 49973 1;90623 49343;90255 49080;89625 48675 1;88635 48052;88208 47640;87308 46890 1;86565 46298;86063 45863;85845 45037 1;85778 44707;85740 44535;85740 44190; +115725 36315 1;115943 35828;116198 35543;116715 35543 1;117405 35565;117735 36120;118065 36735 1;118102 36818;118140 36893;118178 36968; +118178 35055 1;117960 34928;117735 34793;117488 34590 1;117233 34410;117045 34238;116865 34080; +86858 68370;87218 68723 32;86723 69218 32;87023 69503 1;87435 69518;87720 69540;88095 69270 1;88125 69255;88117 69203;88163 69203 1;88373 69218;88455 69330;88658 69405;89250 68325 1;88575 68325;88238 68250;87570 68250 1;87525 68250;87473 68220;87473 68258 1;87458 68340;87413 68393;87338 68393 1;87188 68408;87120 68393;86977 68385 1;86933 68385;86887 68378;86858 68370 18; +102165 68318 1;102450 68700;102720 68940;103208 69270 1;103837 69720;104220 69915;104985 70073 1;105765 70245;106230 70343;106988 70073 1;108030 69705;108450 69083;109568 69030 1;110685 68992;111285 69435;112148 70155 1;112958 70845;113160 71453;114090 71970 1;114727 72345;115125 72533;115868 72473 1;116917 72405;117727 72352;118328 71475 1;118845 70703;119258 70425;119970 69795 1;120660 69180;121058 68925;121710 68250 1;122205 67740;122235 67260;122265 66533 1;122295 65475;122070 64823;121365 64012 1;120818 63390;120578 63052;120008 62453 1;119400 61845;118928 61710;118365 61050 1;117848 60465;117525 60233;117068 59595 1;116340 58605;115808 58230;114825 57495 1;113738 56693;112995 56610;111825 55935 1;110310 55065;109417 54810;108030 53753 1;107595 53445;107438 53190;106965 52950 1;105788 52373;105398 51465;104085 51375 1;103238 51323;102825 51060;102090 50655 1;100890 50010;100358 49500;99045 49155 1;97898 48855;97350 48533;96428 47790 1;96158 47588;95948 47423;95730 47243; +98520 45975 1;98355 45945;98167 45915;97973 45885 1;97620 45848;97425 45848;97095 45960 1;96563 46148;96323 46283;95798 46463 1;95475 46575;95303 46598;94973 46582 1;93908 46537;93375 46508;92318 46388; +101768 46545 1;101655 46478;101633 46380;101618 46320; +85950 41993 1;85808 41948;85665 41902;85470 41850 1;84960 41730;84713 41603;84293 41295 1;83963 41070;83790 40973;83468 40740; +85913 42752; +86526 42070 1;87306 41425;87727 41280;88785 41332 1;90090 41415;90758 41475;92048 41250 1;94035 40920;95018 40605;97050 40455 1;99390 40290;100575 40590;102930 40433 1;104550 40335;105360 39990;106808 39233 1;108165 38520;109005 38303;110047 37155 1;110865 36248;110880 35123;110393 34065; +94943 47078 1;94410 47258;94035 47318;93750 47790 1;93645 47963;93653 48165;93810 48278 1;94005 48435;94260 48488;94433 48300 1;94762 47940;95033 47835;95295 47408;94943 47078 18; +95010 46673 1;94538 46290;94065 45930;93405 45473 1;93165 45323;92970 45188;92775 45082; +73852 57473; +70102 55927 1;70073 56040;70193 56100;70305 56130 1;70425 56175;70583 56198;70620 56070 1;70688 55793;70718 55658;70793 55373 1;70823 55253;70725 55155;70613 55103 1;70477 55050;70335 55125;70283 55253;70102 55927 18; +69150 64508;69158 64500 32;69150 64508 18; +85298 38888 1;86010 37688;86498 36900;86992 35588; +86303 36000 1;86400 36090;86558 36150;86625 36037 1;86790 35753;86768 35662;86880 35340 1;86918 35228;86783 35220;86625 35152;86535 35145 33;86145 35010;86003 34815;85605 34748 1;85485 34733;85343 34868;85410 34965 1;85673 35378;85838 35573;86220 35880 1;86235 35902;86250 35918;86265 35940 32;86303 36000 18; +86895 39652 1;87240 39953;87548 39893;88005 39938 1;88185 39960;88440 40027;88463 39840 1;88500 39435;88463 39248;88455 38820 1;88455 38738;88500 38625;88418 38603 1;87975 38505;87742 38505;87300 38468 1;86805 38430;86760 39045;86663 39525;86895 39652 18; +97794 60918; +107123 51255 1;107633 51165;107880 51023;108308 50715 1;109058 50183;109538 50048;110220 49418 1;110340 49305;110355 49200;110393 49028; +110955 50512 1;110738 50543;110648 50648;110475 50775 1;109808 51270;109470 51503;108765 51923 1;108518 52073;108488 52065;108248 52223; +108630 50258 1;108420 50205;108188 50160;107925 50130 1;106935 50040;106417 50115;105488 49815 1;104490 49515;103995 49320;103050 48915 1;101955 48465;101655 47700;100538 47310 1;100425 47273;100253 47543;100058 47453 1;99878 47370;99953 47168;99840 47085 1;99428 46823;99053 46650;98663 46440 1;98505 46365;98370 46335;98243 46448 1;98100 46582;98033 46755;97845 46718 1;97650 46688;97575 46553;97500 46365 1;97455 46283;97462 46260;97395 46193; +104393 52957 1;105540 52943;106178 53025;107250 52590; +97462 60128 1;97493 59505;97493 59168;97508 58545 1;97508 58223;97643 58088;97718 57765 1;97950 56693;98145 56100;97913 55020; +91463 56378; +94425 57052 1;94568 57173;94545 57203;94733 57225 1;95025 57270;95220 57233;95468 57052 1;96413 56355;96690 55710;97118 54608 1;97178 54457;96840 54457;96705 54540 1;96195 54855;95963 55088;95385 55215 1;94920 55320;94845 55710;94538 56055 1;94223 56408;94223 56633;94425 57052 18; +96465 53850 1;96428 53393;96555 53137;96398 52703 1;96352 52598;96233 52598;96120 52613 1;95835 52650;95700 52688;95423 52718 1;95265 52740;95137 52808;95123 52957 1;95070 53445;95183 53693;95190 54173 1;95190 54367;95108 54683;95295 54630 1;95835 54495;96480 54473;96488 53910;96465 53850 18; +101031 60459 1;101358 60278;100718 59408;100170 59302 1;99075 59108;98535 58973;97440 58875;97493 59190 33;97485 59483;97477 59760;97462 60128 32;97635 60225 33;97650 60225;97665 60225;97688 60225 1;98685 60262;99060 60270;100178 60495 1;100275 60518;100379 60350;100469 60365 32;101031 60459 18; +82598 44520 1;83033 44588;83438 44287;83505 43860 1;83573 43433;83663 43433;82838 42960 1;82200 42593;82088 43223;81945 43620 1;81675 44423;82178 44460;82598 44520 18; +82950 44490;83617 44925 33;83768 44948;83895 44955;84053 44850 1;84248 44723;84450 44655;84458 44415 1;84458 44183;84315 44078;84143 43920 1;83903 43725;83715 43590;83520 43463 32;83535 43650 32;83145 44490;82950 44490 18; +79725 47610 1;80183 47152;80415 46912;80895 46463 1;81195 46185;81383 46043;81540 45660 1;81750 45143;81915 44895;82020 44340; +73358 41760 1;73140 41332;73133 40980;72900 40553 1;72803 40388;72630 40748;72623 40935 1;72608 41138;72645 41243;72637 41438 1;72623 41662;72765 41835;72623 42000 1;72038 42675;71708 42983;71145 43665 1;71040 43793;70958 43890;70800 43883 1;70650 43883;70575 43860;70433 43838 1;70298 43830;70268 43980;70238 44100 1;70178 44303;70020 44363;69825 44408 1;69653 44453;69548 44475;69435 44603 1;69180 44888;69420 44498;69458 44865;69765 45240;70260 45585 32;70823 45930;73358 41760 18; +69233 40185 1;69360 39540;69548 39053;69758 38250; +69225 38925 1;69495 38993;69750 39060;70110 39165 1;70253 39218;70403 39120;70433 38970 1;70477 38738;70515 38625;70538 38385;69233 38228;69233 38933;69225 38925 18; +69210 45143 1;69285 45188;69367 45240;69458 45293;69413 45075 1;69443 44933;69413 44850;69367 44707 1;69323 44588;69270 44468;69210 44348;69218 45143;69210 45143 18; +69240 37388 1;69360 37402;69473 37418;69637 37425 1;69803 37448;70065 37402;70012 37245 1;69938 37050;69855 36960;69758 36773 1;69698 36683;69683 36630;69645 36525 1;69600 36443;69668 36360;69758 36315 1;69878 36263;69938 36225;70073 36173 1;70170 36143;70193 36045;70185 35933 1;70163 35723;70140 35610;70140 35393 1;70133 35295;70088 35213;69990 35190 1;69765 35160;69668 35085;69450 35040 1;69345 35025;69255 34898;69330 34823 1;69398 34755;69398 34680;69495 34650 1;69518 34643;69480 34613;69473 34582 1;69413 34433;69405 34343;69360 34178 1;69345 34148;69360 34110;69330 34088;69240 34058;69240 37395;69240 37388 18; +69248 34050;69330 34088 33;69428 34058;69488 34035;69600 33998 1;69690 33975;69750 34005;69848 34035 1;69990 34095;70073 34178;70110 34328 1;70117 34380;70117 34425;70110 34463 33;70102 34537;70065 34605;69998 34635 1;69795 34733;69660 34665;69495 34650 33;69398 34680;69398 34755;69330 34823 1;69255 34898;69345 35025;69450 35040 1;69668 35085;69765 35160;69990 35190 1;70088 35213;70133 35295;70140 35393 1;70140 35610;70163 35723;70185 35933 1;70193 36045;70170 36143;70073 36173 1;69938 36225;69878 36263;69758 36315 1;69668 36360;69600 36443;69645 36525 1;69683 36630;69698 36683;69758 36773 1;69818 36900;69878 36983;69930 37080; +72540 56887 1;72855 56985;73020 56820;73050 56558 1;73050 56363;72953 56048;72637 55950 33;72330 55860;72083 55613;72053 56295 1;72045 56820;71985 56768;72540 56887 18; +72833 60473 1;72473 60255;72008 60090;72143 59693 1;72300 59205;72308 58935;72293 58418 1;72270 57758;72255 57375;72233 56730; +73980 59153 1;73358 58155;73125 57855;72773 56858; +72360 55950 1;72360 55373;72338 55080;72360 54495; +87585 73350 1;87675 73020;87818 72600;88148 72698 1;88725 72878;89078 72900;89528 73305 1;89850 73605;89940 73890;89948 74325 1;89948 74805;89970 75135;89648 75473 1;89348 75780;88965 75645;88590 75450 1;88065 75188;87923 74865;87563 74400 1;87308 74085;87465 73808;87585 73350 18; +87262 33998;87330 34043 33;87390 34050;87443 34043;87473 33990;87255 33998;87262 33998 18; +98565 54585 1;98880 54608;99008 54690;99233 54623 1;99360 54585;99413 54548;99540 54480 1;99600 54450;99608 54420;99615 54345 1;99630 53978;99660 53933;99667 53558 1;99667 53498;99667 53415;99608 53415 1;99315 53415;99128 53393;98895 53385 1;98813 53873;98738 54105;98565 54585 18; +70523 46298 1;69863 46035;69795 45443;69210 45082; +96839 27067;Forest map sample +123255 78068;100740 78000;86288 77955;86602 76852;88110 77108 32;89977 76500 32;90030 73163 32;88913 72428 32;87518 72195 32;89100 68573 32;90375 66968 32;92318 65775 32;97658 64043 32;98828 66338 32;99015 70133 32;99420 72615 32;99830 75218;100312 75150;100140 74078 32;101100 72630 32;101790 72075 32;107190 71760 32;108248 67448 32;108922 64867 32;106598 63195 32;107655 61920 32;111960 62662 32;111960 63435 32;112493 63525 32;112815 62798 32;116250 63278 32;116625 63810 32;116685 64575 32;117608 64537 32;118470 64845 32;120735 65085 32;121703 65018 32;121748 65505 32;122558 65415 32;122753 64927 32;124455 64477;124462 64477;124440 73020;124433 75435;124111 77128;124110 77273 32;123810 77820 32;123255 78068 18; +121553 59528;121439 58455; +116653 56713;117190 57143; +76454 69284 1;76718 68677;76558 67267;77219 67267 1;77998 67267;78614 67769;79287 68161 1;79838 68483;80015 68692;79983 69329 1;79944 70111;79815 70424;79400 71088 1;79109 71553;78866 71605;78394 71326 1;77756 70949;77405 70721;76734 70407 1;76325 70215;76274 69698;76454 69284 18; +77063 67349 1;76453 67626;75900 67627;75582 68217 1;75244 68844;74745 69296;75021 69953 1;75367 70774;75898 71195;76756 71433 1;77650 71681;78202 71510;79130 71510;77063 67349 18; +82742 72759 1;82742 73189;83015 74001;83319 73697 1;83634 73382;83828 73182;83969 72759 1;84262 71880;83434 70554;84205 70040 1;84646 69746;85256 68794;84782 68557 1;84017 68174;83147 68721;82922 69546 1;82591 70758;82742 71502;82742 72759 18; +105741 43279;105210 46148 32;103665 45893 32;102345 45045 32;97583 44123 32;92226 43370 32;89579 43106;88851 42674 32;88282 41889 32;88302 41376;89016 40811;89808 40565;92475 40692 32;97361 41391 32;97728 41725 32;101055 42329 32;105741 43279 18; +115695 62535 32;109928 61552 32;109860 60953 32;109373 60953 32;109320 61425 32;108135 61223;109253 59775;109823 56783 32;111727 56595 32;111330 53880 32;113468 53595 32;115485 48720 32;118740 48135 32;118710 47550 32;121255 47315;121230 46298;124515 46223;124523 46223;124500 53093;123450 52935 32;121650 53093 32;121613 52867 32;121387 52748 32;121364 51785;120337 51300 32;120278 50835 32;119025 50430 32;117915 50453 32;117608 50100 32;116873 50168 32;116925 51180 32;117585 51600 32;118268 51563 32;118598 51802 32;119370 51802 32;119723 51518 32;120908 52148 32;120893 53332 32;119422 53820 32;118193 54563 32;117128 54653 32;117128 55260 32;116828 55545 32;115005 55755 32;115087 56145 32;115433 56115 32;115575 57052 32;114968 58635 32;113145 58860 32;113198 59213 32;114908 59025 32;115087 60465 32;114292 60608 32;114345 61095 32;115110 61005 32;115695 62535 50; +69135 68858 1;71648 70628;73065 71580;75713 73440 1;77543 74738;78413 75458;80175 76845 1;80723 77288;81060 77535;81510 77940; + + + + + + + + + + + + + + + + diff --git a/examples/overprinting.omap b/examples/overprinting.omap new file mode 100644 index 0000000..7cfdcc4 --- /dev/null +++ b/examples/overprinting.omap @@ -0,0 +1,242 @@ + + + + + +PURPLE +BLACK + + + +BLUE + +BROWN + + + + + + +GREEN + + + +YELLOW + + + + + + +A line joining points of equal height. The standard vertical interval between contours is 5 metres. The smallest bend in a contour is 0.25 mm from centre to centre of the lines. +Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with a normal contour line. +An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. +Slope lines may be drawn on the lower side of a contour line, e.g. along the line of a re-entrant or in a depression. They are used only where it is necessary to clarify the direction of slope.0 0;0 -500; +Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. +A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm.70 0;70 500;0 500;0 -90;-70 0;-70 500; +A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm.-370 0;370 0;300 0;300 500;-300 0;-300 500; +The line width of very high earth banks may be 0.25 mm.70 0;70 500;0 500;0 100;-70 0;-70 500; +The line width of very high earth banks may be 0.25 mm.-370 0;370 0;300 0;300 500;-300 0;-300 500; +Use this symbol to display the full extent of wide earth banks. +Distinct earth wall. Minimum height is 1 m. +A small or partly ruined earth wall shall be shown with a dashed line. Minimum height is 0.5 m. +An erosion gully or trench which is too small to be shown by symbol 106 is shown by a single line. The line width reflects the size of the gully. Minimum depth 1 m. The end of the line is pointed. +A small erosion gully or trench. Minimum depth 0.5 m. +A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (diameter of mound less than ca. 5 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. The symbol may not touch a contour line. +A small obvious elongated knoll which cannot be drawn to scale with a contour (length less than 12 m and width less than 4 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this must be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols overlap. The symbol may not touch a contour line.0 -400 1;110 -400;200 -221;200 0 1;200 221;110 400;0 400 1;-110 400;-200 221;-200 0 1;-200 -221;-110 -400;0 -400 3; +Small shallow natural depressions and hollows (minimum diameter 2 m) which cannot be shown to scale by contours are represented by a semicircle. Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol, which is orientated to north. Symbol 116 is used for man-made pits.400 -255 1;400 -34;221 145;0 145 1;-221 145;-400 -34;-400 -255; +Pits and holes with distinct steep sides which cannot be shown to scale by symbol 106 (minimum diameter 2 m). Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol which is orientated to north.154 -302;350 -302;0 498;-350 -302;-154 -302;0 50;154 -302 2; +An area of pits or knolls which is too intricate to be shown in detail. The density of randomly placed dots may vary according to the detail on the ground. +The size of the dots may vary. +This symbol can be used for a special small land form feature. The definition of the symbol must be given in the map legend.-400 -400;400 400;-400 400;400 -400; +An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line.60 100;60 500;0 100;0 500;-60 100;-60 500; +An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line.-360 0;360 0;300 0;300 500;-300 0;-300 500; +For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). +Use this symbol to display the full extent of a wide cliff. +In the case of unusual features such as rock pillars or massive cliffs or gigantic boulders, the rocks shall be shown in plan shape without tags. +A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.60 50;60 500;0 50;0 500;-60 50;-60 500; +A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.-360 0;360 0;300 0;300 500;-300 0;-300 500; +Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. +Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good.-300 0;300 0; +For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. +For passable rock faces shown without tags the ends of the line may be rounded to improve legibility.-175 0;175 0; +Rocky pits, holes or mineshafts which may constitute a danger to the runner. Location is the centre of gravity of the symbol, which is orientated to north.175 -310;350 -310;0 490;-350 -310;-175 -310;0 90;175 -310 2; +A cave is represented by the same symbol as a rocky pit. In this case the symbol should be orientated to point up the slope as indicated opposite. The centre of gravity of the symbol marks the opening.175 -310;350 -310;0 490;-350 -310;-175 -310;0 90;175 -310 2; +A small distinct boulder (minimum height 1 m). Every boulder marked on the map should be immediately identifiable on the ground. To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). +To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). +A particularly large and distinct boulder. For gigantic boulders symbol 202 should be used. +An area which is covered with so many blocks of stone that they cannot be marked individually is shown with randomly orientated solid triangles with sides of ratio 8:6:5. A minimum of two triangles should be used. The going is indicated by the density of the triangles. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%.100 301;100 -339;-200 36;100 301 2; +To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%.120 361;120 -407;-240 43;120 361 2; +A small distinct group of boulders so closely clustered together that they cannot be marked individually. The symbol is an equilateral triangle orientated to the north. To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm).-400 231;400 231;0 -462;-400 231 2; +To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm).-500 289;500 289;0 -577;-500 289 2; +Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. +Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. +An area of soft sandy ground or gravel with no vegetation and where running is slow. Where an area of sandy ground is open but running is good, it is shown as open land (401/402). +A runnable area of rock without earth or vegetation is shown as bare rock. An area of rock covered with grass, moss or other low vegetation is shown as open land (401/402). +Large areas of water are shown with dot screen. Small areas of water should be shown with full colour. A black bank line indicates that the feature cannot be crossed. +A black bank line indicates that the feature cannot be crossed. + +Where the lake or pond is smaller than 1 mm² on the printed map, the bank line is omitted. +A water-filled pit or an area of water which is too small to be shown to scale. Location is the centre of gravity of the symbol, which is orientated to north.154 -302;350 -302;0 498;-350 -302;-154 -302;0 50;154 -302 2; +A crossable watercourse, minimum 2 m wide. The width of watercourses over 5 m wide should be shown to scale. +A crossable watercourse (including a major drainage ditch) less than 2 m wide. For better legibility a ditch in a marsh should be drawn as a crossable watercourse (305). +A natural or man-made minor water channel which may contain water only intermittently. +A marsh or trickle of water which is too narrow to be shown with symbol 310 (less than ca. 5 m wide). +A marsh which is uncrossable or dangerous for the runner. A black line surrounds the symbol. +A black line surrounds the symbol. + +A crossable marsh, usually with a distinct edge. The symbol should be combined with vegetation symbols to show runnability and openness. Where a small marsh area should be combined with either 403/404 it is permitted to use 401/402 to improve legibility. +-250 -150;250 -150;-250 150;250 150; +An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness.-450 0;450 0;-450 0;450 0; +-450 -300;450 -300;-450 300;450 300;-1025 0;-125 0;125 0;1025 0; +Wells and captive springs, which are clearly visible on the ground. +The source of a stream with a distinct outflow. The symbol is orientated to open downstream.400 -255 1;400 -34;221 145;0 145 1;-221 145;-400 -34;-400 -255; +A special small water feature. The definition of the symbol must always be given in the map legend.-400 -400;400 400;-400 400;400 -400; +Cultivated land, fields, meadows, grassland, etc. without trees, offering easy running. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. +Meadows with scattered trees or bushes, with grass or similar ground cover offering easy running. Areas smaller than 10 mm at the maps scale are shown as open land (401). Individual trees may be added (418, 419, 420). If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. +Heath, moorland, felled areas, newly planted areas (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, heather or tall grass. Symbol 403 may be combined with symbols 407 and 409 to show reduced runnability. +Where there are scattered trees in rough open land, areas of white (or green) should appear in the tone. Such an area may be generalised by using a regular pattern of large white dots in the yellow screen. Areas smaller than 16 mm in the maps scale are shown as rough open land (403). Individual trees may be added (418, 419, 420). +Typically open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. +An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. +An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 60-80% of normal speed. This symbol may not be combined with 406 or 408. +An area with dense trees or thicket (low visibility) which reduce running to ca. 20-60% of normal speed. +An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. +An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced to ca. 0-20% of normal speed. +Line of minimum width for symbol 410. +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. +When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. +Land planted with fruit trees or bushes. The dot lines may be orientated to show the +direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. +The green lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used.0 -650;0 650;0 -650;0 650; +The boundary of cultivated land when not shown with other symbols (fence, wall, path, etc.) is shown with a black line. A permanent boundary between different types of cultivated land is also shown with this symbol. +Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. +A distinct forest edge or very distinct vegetation boundary within the forest. +Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend.-400 -400;400 400;400 -400;-400 400; +Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. +Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. +A road with two carriageways. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. +A road under construction may be shown with broken lines. +Road wider than 5m. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. +A road under construction may be shown with broken lines. +Road 3-5 m wide. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. +A road under construction may be shown with broken lines. +A maintained road suitable for motor vehicles in all weather. Width less than 3 m. +A track or poorly maintained road suitable for vehicles only when travelling slowly. Width less than 3 m. +A large path, or old vehicle track, which is distinct on the ground. +A small path or (temporary) forest extraction track which can be followed at competition speed. +A less distinct small path or forestry extraction track. +A distinct ride, less than ca. 5 m wide. A ride is a linear break in the forest (usually plantation) which does not have a distinct path along it. Where there is a path along a ride, symbols 507 or 508 should be used in place of symbol 509. +A footbridge with no path leading to it. +Note: if the stream is wider than 0.25mm, adjust this symbol so it extends 0.5mm over both sides of the stream!0 -625;0 625; +A railway or other kind of railed track (tramway, truckway, etc.).0 -525;0 525; +Power line, cableway or skilift. The bars indicate the exact location of the pylons.0 -370;0 370; +Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline.0 -750;0 750; +A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it.-300 -520;0 0;10 0;-10 0;0 0;300 -520; +A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it.300 -520;0 0;-300 -520; +A stone wall or stone-faced bank. +A ruined stone wall may be shown by a dashed line. +A stone wall higher than ca 1.5 m, not crossable to the average orienteer. +A wooden or wire fence less than ca. 1.5 m high.0 0;250 433; +A ruined fence may be shown with a dashed line.0 0;250 433; +A boarded or wire fence higher than ca 1.5 m, not crossable to the average orienteer, eg. deer fence.300 0;550 433;-300 0;-50 433; +All ways through or over high fences or walls must be indicated. The symbol may also be used for a gate through or stile over a stone wall (519) or a fence (522) or a pipeline (534).300 -500;300 500;-300 -500;-300 500; +A building is shown with its ground plan so far as the scale permits. +-250 -250;250 -250;250 250;-250 250;-250 -250 2; +Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. +Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. +Areas which are permanently forbidden to the runner are shown as out of bounds. The screen is superimposed on the normal map detail. A bounding line may be drawn if there is no natural boundary (see 709). +A bounding line may be drawn if there is no natural boundary (see 709). +An area of hard standing used for parking or other purposes. + + +The ground plan of a ruin is shown to scale, down to the minimum size shown opposite. Very small ruins may be drawn with a solid line. +Very small ruins may be drawn with a solid line. +Very small ruins may be drawn with a solid line.320 320;320 -320;-320 -320;-320 320;320 320 2; +A firing range is shown with a special symbol to indicate the need for caution. Associated buildings are individually marked.1700 -700;1700 700;1700 0;-250 -593;0 0;-250 593;1700 0 2; +A distinct grave marked by a stone or shrine. Location is at the centre of gravity of the symbol, which is orientated to north. A cemetery is shown by using grave symbols as space permits.0 -430;0 570;-350 -129;350 -129; +A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under.0 0;-354 -354;0 0;-354 354; +A pipeline which cannot be crossed.0 0;-354 -354;0 0;-354 354; +A high tower or large pylon, standing above the level of the surrounding forest. Location is at the centre of gravity of the symbol.-700 0;700 0;0 -700;0 700; +An obvious shooting platform or seat, or small tower. Location is at the centre of gravity of the symbol.0 -262;0 738;-500 -262;500 -262; +Cairn, memorial stone or boundary stone (or a trigonometric point in some countries) more than 0.5 m high.0 0; +A fodder rack which is free standing or built on to a tree. Location is at the centre of gravity of the symbol. For land access reasons these may be omitted.0 -322;0 678;-500 -33;0 -322;500 -33; +Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. +Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend.-400 -400;400 400;400 -400;-400 400; +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. + +Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. + +At least three registration marks must be placed within the frame of a map in a non-symmetrical position. In addition, a colour check should also be possible.0 -2000;0 2000;-2000 0;2000 0; +Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. +Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. +The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point.-3500 2021;0 -4041;3500 2021;-3500 2021 18; +The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. +The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. +Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. +A marked route is shown on the map with a dashed line. +The finish is shown by two concentric circles.0 0; +A boundary which it is not permitted to cross. +A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards.-1500 725 1;-500 450;500 450;1500 725;-1500 -725 1;-500 -450;500 -450;1500 -725; +An out-of-bounds area, see also symbol 528, is shown with vertical stripes. +A bounding line may be drawn if there is no natural boundary, as follows: +- a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, +- a dashed line indicates intermittent marking on the ground, +- no line indicates no marking on the ground. +A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. +A dashed line indicates intermittent marking on the ground. +An area presenting danger to the competitor is shown with cross-hatched diagonal lines. +A route which is out-of-bounds is shown with crosses.1061 -1061;-1061 1061;1061 1061;-1061 -1061; +The location of a first aid post.-1500 0;1500 0;0 -1500;0 1500; +The location of a refreshment point which is not at a control.-1340 -1395;-820 1380;1340 -1395;820 1380;0 -1770 1;600 -1770;1200 -1620;1340 -1395 1;1200 -1170;600 -1020;0 -1020 1;-600 -1020;-1200 -1170;-1340 -1395 1;-1200 -1620;-600 -1770;0 -1770;-820 1380 1;-680 1680;680 1680;820 1380; +This symbol provides a simple and quick way to make training courses. + +The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach.3041 0;3541 0;-3021 3500;3041 0;-3021 -3500;-3021 3500 18;-600 0;600 0;0 0; +The OpenOrienteering Logo.-12797 -557 1;-12755 -447;-12770 -420;-12873 -420 1;-12953 -420;-12973 -440;-12973 -520 1;-12973 -635;-12838 -663;-12797 -557 18;-933 2063 1;-933 1925;-920 1900;-851 1900 1;-780 1900;-770 1921;-781 2050 1;-789 2154;-814 2203;-863 2213 1;-920 2224;-933 2197;-933 2063;-933 2063 18;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 18;-4230 -3923 1;-4183 -3737;-4160 -3527;-4160 -3296;-4160 -1395;-5113 -1395;-5113 -3182 1;-5113 -3488;-5155 -3707;-5235 -3833 1;-5317 -3961;-5468 -4027;-5690 -4027 1;-5759 -4027;-5830 -4021;-5907 -4013 1;-5983 -4008;-6052 -4004;-6112 -3993;-6112 -1395;-7064 -1395;-7064 -4646 1;-6903 -4693;-6694 -4736;-6438 -4774 1;-6181 -4818;-5913 -4838;-5631 -4838 1;-5347 -4838;-5110 -4800;-4921 -4723 1;-4730 -4652;-4579 -4547;-4467 -4410 1;-4357 -4273;-4277 -4111;-4230 -3923 18;-13524 1118 1;-13605 1126;-13667 1137;-13708 1150;-13708 3722;-14662 3722;-14662 536 1;-14492 476;-14292 420;-14060 369 1;-13827 314;-13565 286;-13280 286 1;-13229 286;-13167 290;-13096 299 1;-13022 303;-12951 312;-12878 325 1;-12806 333;-12733 345;-12660 363 1;-12587 375;-12526 392;-12474 414;-12634 1201 1;-12719 1180;-12819 1158;-12936 1137 1;-13051 1112;-13174 1098;-13306 1098 1;-13366 1098;-13439 1105;-13524 1118 18;-11093 -200 1;-11204 -101;-11336 -52;-11490 -52 1;-11642 -52;-11777 -101;-11893 -200 1;-12004 -303;-12059 -441;-12059 -616 1;-12059 -791;-12004 -927;-11893 -1026 1;-11777 -1128;-11642 -1179;-11490 -1179 1;-11336 -1179;-11204 -1128;-11093 -1026 1;-10978 -927;-10920 -791;-10920 -616 1;-10920 -441;-10978 -303;-11093 -200 18;-11009 3722;-11963 3722;-11963 357;-11009 357;-11009 3722 18;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 18;-5220 1105 1;-5297 1110;-5365 1115;-5425 1125;-5425 3722;-6378 3722;-6378 472 1;-6217 425;-6007 382;-5751 344 1;-5495 301;-5227 280;-4945 280 1;-4658 280;-4422 318;-4235 395 1;-4042 467;-3890 572;-3781 709 1;-3669 845;-3591 1007;-3544 1195 1;-3497 1381;-3474 1592;-3474 1821;-3474 3722;-4427 3722;-4427 1937 1;-4427 1630;-4467 1411;-4549 1285 1;-4628 1156;-4780 1092;-5002 1092 1;-5070 1092;-5143 1097;-5220 1105 18;-2636 2346;-2636 -481;-1683 -634;-1683 357;-539 357;-539 1150;-1683 1150;-1683 2333 1;-1683 2535;-1648 2694;-1581 2813 1;-1508 2933;-1365 2993;-1151 2993 1;-1050 2993;-945 2984;-838 2966 1;-728 2946;-627 2918;-539 2884;-404 3626 1;-518 3673;-646 3712;-787 3748 1;-928 3780;-1102 3799;-1305 3799 1;-1566 3799;-1781 3764;-1951 3696 1;-2123 3624;-2259 3526;-2361 3402 1;-2463 3274;-2536 3121;-2579 2941 1;-2618 2763;-2636 2565;-2636 2346 18;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 18;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700;5923 1700 18;8718 1118 1;8637 1126;8575 1137;8534 1150;8534 3722;7581 3722;7581 536 1;7750 476;7950 420;8182 369 1;8415 314;8677 286;8962 286 1;9013 286;9075 290;9146 299 1;9220 303;9292 312;9365 325 1;9437 333;9510 345;9583 363 1;9655 375;9717 392;9768 414;9608 1201 1;9523 1180;9424 1158;9306 1137 1;9191 1112;9069 1098;8937 1098 1;8877 1098;8804 1105;8718 1118 18;10350 -200 1;10238 -303;10183 -441;10183 -616 1;10183 -791;10238 -927;10350 -1026 1;10465 -1128;10600 -1179;10752 -1179 1;10906 -1179;11038 -1128;11149 -1026 1;11264 -927;11323 -791;11323 -616 1;11323 -441;11264 -303;11149 -200 1;11038 -101;10906 -52;10752 -52 1;10600 -52;10465 -101;10350 -200 18;11233 3722;10279 3722;10279 357;11233 357;11233 3722 18;14726 709 1;14836 845;14916 1007;14963 1195 1;15010 1381;15033 1592;15033 1821;15033 3722;14080 3722;14080 1937 1;14080 1630;14039 1411;13958 1285 1;13876 1156;13725 1092;13504 1092 1;13435 1092;13363 1097;13287 1105 1;13210 1110;13142 1115;13082 1125;13082 3722;12129 3722;12129 472 1;12290 425;12499 382;12756 344 1;13012 301;13280 280;13562 280 1;13847 280;14083 318;14272 395 1;14463 467;14614 572;14726 709 18;-17051 -1307 1;-17299 -1307;-17525 -1349;-17729 -1434 1;-17931 -1521;-18102 -1639;-18247 -1794 1;-18392 -1951;-18505 -2139;-18587 -2355 1;-18669 -2579;-18708 -2820;-18708 -3085 1;-18708 -3350;-18669 -3591;-18587 -3808 1;-18502 -4027;-18387 -4211;-18242 -4365 1;-18092 -4518;-17917 -4638;-17717 -4723 1;-17512 -4808;-17291 -4851;-17051 -4851 1;-16808 -4851;-16586 -4808;-16386 -4723 1;-16182 -4638;-16006 -4518;-15861 -4365 1;-15716 -4211;-15603 -4027;-15523 -3808 1;-15442 -3591;-15401 -3350;-15401 -3085 1;-15401 -2820;-15440 -2579;-15517 -2355 1;-15593 -2139;-15703 -1951;-15848 -1794 1;-15993 -1639;-16168 -1521;-16373 -1434 1;-16574 -1349;-16800 -1307;-17051 -1307 18;-17051 -2126 1;-16834 -2126;-16668 -2210;-16552 -2383 1;-16433 -2557;-16373 -2792;-16373 -3085 1;-16373 -3380;-16433 -3610;-16552 -3777 1;-16668 -3946;-16834 -4034;-17051 -4034 1;-17269 -4034;-17437 -3946;-17557 -3777 1;-17675 -3610;-17736 -3380;-17736 -3085 1;-17736 -2792;-17675 -2557;-17557 -2383 1;-17437 -2210;-17269 -2126;-17051 -2126 18;-14662 -213;-14662 -4646 1;-14576 -4673;-14478 -4697;-14369 -4716 1;-14257 -4743;-14142 -4765;-14022 -4781 1;-13898 -4798;-13776 -4811;-13652 -4819 1;-13524 -4833;-13402 -4838;-13287 -4838 1;-13009 -4838;-12763 -4796;-12544 -4711 1;-12328 -4630;-12144 -4513;-11995 -4358 1;-11846 -4210;-11732 -4027;-11657 -3808 1;-11574 -3591;-11535 -3348;-11535 -3078 1;-11535 -2819;-11566 -2582;-11629 -2368 1;-11694 -2156;-11788 -1972;-11911 -1819 1;-12034 -1666;-12189 -1546;-12373 -1461 1;-12556 -1376;-12765 -1333;-13006 -1333 1;-13137 -1333;-13261 -1346;-13377 -1371 1;-13492 -1395;-13602 -1432;-13708 -1479;-13708 -213;-14662 -213 18;-13184 -2139 1;-12733 -2139;-12506 -2443;-12506 -3054 1;-12506 -3348;-12572 -3582;-12704 -3756 1;-12837 -3936;-13032 -4027;-13293 -4027 1;-13379 -4027;-13457 -4021;-13530 -4013 1;-13602 -4008;-13662 -4004;-13708 -3993;-13708 -2274 1;-13648 -2234;-13572 -2202;-13479 -2177 1;-13381 -2152;-13282 -2139;-13184 -2139 18;-9182 -1307 1;-9485 -1307;-9750 -1352;-9976 -1440 1;-10198 -1530;-10383 -1652;-10533 -1806 1;-10678 -1964;-10787 -2149;-10858 -2362 1;-10926 -2575;-10962 -2807;-10962 -3054 1;-10962 -3352;-10917 -3612;-10827 -3833 1;-10733 -4060;-10611 -4248;-10462 -4396 1;-10314 -4547;-10141 -4660;-9950 -4736 1;-9754 -4813;-9553 -4851;-9349 -4851 1;-8872 -4851;-8494 -4705;-8217 -4410 1;-7939 -4120;-7801 -3692;-7801 -3123 1;-7801 -3069;-7803 -3007;-7808 -2939 1;-7810 -2873;-7816 -2817;-7819 -2766;-9982 -2766 1;-9961 -2569;-9870 -2413;-9707 -2299 1;-9545 -2184;-9327 -2126;-9055 -2126 1;-8881 -2126;-8708 -2141;-8542 -2171 1;-8372 -2205;-8234 -2246;-8127 -2292;-7999 -1517 1;-8051 -1493;-8119 -1468;-8204 -1440 1;-8289 -1416;-8385 -1395;-8492 -1378 1;-8594 -1356;-8706 -1339;-8824 -1326 1;-8944 -1314;-9063 -1307;-9182 -1307 18;-9982 -3418;-8723 -3418 1;-8725 -3500;-8740 -3578;-8768 -3655 1;-8789 -3732;-8824 -3800;-8875 -3860 1;-8922 -3920;-8984 -3968;-9061 -4006 1;-9134 -4045;-9225 -4065;-9337 -4065 1;-9444 -4065;-9535 -4045;-9612 -4006 1;-9688 -3974;-9752 -3927;-9803 -3867 1;-9855 -3807;-9895 -3737;-9925 -3655 1;-9950 -3578;-9970 -3500;-9982 -3418 18;-17051 -2126 1;-17269 -2126;-17437 -2210;-17557 -2383 1;-17675 -2557;-17736 -2792;-17736 -3085 1;-17736 -3380;-17675 -3610;-17557 -3777 1;-17437 -3946;-17269 -4034;-17051 -4034 1;-16834 -4034;-16668 -3946;-16552 -3777 1;-16433 -3610;-16373 -3380;-16373 -3085 1;-16373 -2792;-16433 -2557;-16552 -2383 1;-16668 -2210;-16834 -2126;-17051 -2126 18;-17064 -2593 1;-16944 -2591;-16842 -2808;-16838 -3077 1;-16834 -3347;-16928 -3567;-17049 -3569 1;-17170 -3571;-17271 -3354;-17275 -3084 1;-17280 -2815;-17185 -2595;-17064 -2593 18;-8496 3810 1;-8798 3810;-9062 3765;-9288 3677 1;-9510 3588;-9696 3466;-9845 3312 1;-9990 3154;-10098 2969;-10171 2756 1;-10240 2542;-10273 2311;-10273 2065 1;-10273 1766;-10230 1507;-10140 1285 1;-10045 1058;-9923 870;-9775 722 1;-9625 572;-9455 459;-9264 382 1;-9067 305;-8867 267;-8662 267 1;-8184 267;-7806 414;-7529 709 1;-7252 998;-7113 1426;-7113 1995 1;-7113 2050;-7116 2112;-7119 2180 1;-7124 2245;-7128 2301;-7132 2353;-9296 2353 1;-9273 2550;-9182 2704;-9020 2820 1;-8857 2934;-8640 2993;-8368 2993 1;-8193 2993;-8022 2978;-7855 2948 1;-7684 2912;-7546 2873;-7440 2826;-7311 3601 1;-7363 3626;-7431 3651;-7516 3677 1;-7603 3703;-7697 3724;-7804 3741 1;-7908 3763;-8017 3779;-8137 3793 1;-8257 3804;-8376 3810;-8496 3810 18;-9296 1700;-8034 1700 1;-8039 1618;-8054 1539;-8079 1464 1;-8101 1387;-8137 1318;-8189 1259 1;-8236 1199;-8297 1150;-8374 1112 1;-8445 1073;-8537 1053;-8649 1053 1;-8755 1053;-8847 1073;-8924 1112 1;-9001 1145;-9065 1192;-9115 1252 1;-9167 1312;-9207 1381;-9237 1464 1;-9264 1539;-9282 1618;-9296 1700 18;1726 3810 1;1422 3810;1159 3765;933 3677 1;712 3588;524 3466;376 3312 1;231 3154;123 2969;49 2756 1;-19 2542;-52 2311;-52 2065 1;-52 1766;-9 1507;81 1285 1;177 1058;298 870;446 722 1;596 572;766 459;958 382 1;1154 305;1354 267;1559 267 1;2038 267;2416 414;2692 709 1;2968 998;3109 1426;3109 1995 1;3109 2050;3105 2112;3102 2180 1;3097 2245;3094 2301;3088 2353;926 2353 1;947 2550;1039 2704;1201 2820 1;1364 2934;1581 2993;1854 2993 1;2029 2993;2199 2978;2365 2948 1;2538 2912;2675 2873;2782 2826;2910 3601 1;2859 3626;2790 3651;2705 3677 1;2619 3703;2524 3724;2417 3741 1;2314 3763;2204 3779;2084 3793 1;1965 3804;1846 3810;1726 3810 18;926 1700;2187 1700 1;2183 1618;2168 1539;2142 1464 1;2121 1387;2084 1318;2032 1259 1;1986 1199;1924 1150;1847 1112 1;1776 1073;1685 1053;1572 1053 1;1466 1053;1375 1073;1298 1112 1;1221 1145;1156 1192;1106 1252 1;1054 1312;1014 1381;984 1464 1;958 1539;939 1618;926 1700 18;5463 3810 1;5160 3810;4895 3765;4668 3677 1;4446 3588;4262 3466;4112 3312 1;3967 3154;3858 2969;3787 2756 1;3719 2542;3684 2311;3684 2065 1;3684 1766;3729 1507;3819 1285 1;3911 1058;4033 870;4183 722 1;4332 572;4503 459;4695 382 1;4891 305;5091 267;5297 267 1;5773 267;6151 414;6428 709 1;6706 998;6844 1426;6844 1995 1;6844 2050;6843 2112;6837 2180 1;6834 2245;6829 2301;6826 2353;4663 2353 1;4683 2550;4775 2704;4938 2820 1;5100 2934;5318 2993;5590 2993 1;5765 2993;5936 2978;6103 2948 1;6272 2912;6411 2873;6518 2826;6646 3601 1;6594 3626;6526 3651;6441 3677 1;6356 3703;6259 3724;6152 3741 1;6051 3763;5939 3779;5821 3793 1;5701 3804;5581 3810;5463 3810 18;4663 1700;5923 1700 1;5919 1618;5904 1539;5878 1464 1;5856 1387;5821 1318;5769 1259 1;5723 1199;5661 1150;5585 1112 1;5511 1073;5419 1053;5308 1053 1;5201 1053;5110 1073;5033 1112 1;4957 1145;4893 1192;4841 1252 1;4790 1312;4750 1381;4720 1464 1;4695 1539;4675 1618;4663 1700 18;17092 4924 1;16887 4924;16682 4905;16477 4867 1;16272 4832;16083 4785;15908 4725;16074 3926 1;16224 3986;16379 4032;16543 4067 1;16707 4101;16896 4119;17105 4119 1;17377 4119;17570 4059;17680 3939 1;17796 3819;17854 3666;17854 3479;17854 3357 1;17750 3404;17644 3441;17533 3466 1;17427 3487;17309 3498;17182 3498 1;16717 3498;16361 3361;16113 3087 1;15866 2811;15743 2424;15743 1930 1;15743 1683;15781 1458;15857 1259 1;15934 1053;16044 878;16189 733 1;16339 589;16521 478;16734 402 1;16947 320;17187 280;17457 280 1;17572 280;17689 286;17809 299 1;17931 307;18053 320;18173 337 1;18292 354;18405 375;18512 402 1;18624 422;18722 446;18806 472;18806 3299 1;18806 3849;18665 4257;18385 4522 1;18107 4790;17677 4924;17092 4924 18;17360 2730 1;17459 2730;17550 2718;17636 2691 1;17720 2666;17794 2636;17854 2603;17854 1080 1;17807 1072;17750 1065;17687 1060 1;17623 1052;17548 1047;17464 1047 1;17212 1047;17024 1130;16900 1297 1;16776 1464;16714 1675;16714 1930 1;16714 2463;16930 2730;17360 2730 18;-17051 2993 1;-17269 2993;-17437 2908;-17557 2736 1;-17675 2561;-17736 2326;-17736 2033 1;-17736 1738;-17675 1509;-17557 1342 1;-17437 1171;-17269 1085;-17051 1085 1;-16834 1085;-16668 1171;-16552 1342 1;-16433 1509;-16373 1738;-16373 2033 1;-16373 2326;-16433 2561;-16552 2736 1;-16668 2908;-16834 2993;-17051 2993 18;-17064 2526 1;-16944 2528;-16842 2311;-16838 2042 1;-16834 1772;-16928 1552;-17049 1550 1;-17170 1548;-17271 1765;-17275 2035 1;-17280 2304;-17185 2524;-17064 2526 18;-17051 3810 1;-17299 3810;-17525 3769;-17729 3684 1;-17931 3598;-18102 3479;-18247 3324 1;-18392 3168;-18505 2980;-18587 2763 1;-18669 2540;-18708 2298;-18708 2033 1;-18708 1768;-18669 1526;-18587 1310 1;-18502 1092;-18387 906;-18242 754 1;-18092 600;-17917 480;-17717 395 1;-17512 310;-17291 267;-17051 267 1;-16808 267;-16586 310;-16386 395 1;-16182 480;-16006 600;-15861 754 1;-15716 906;-15603 1092;-15523 1310 1;-15442 1526;-15401 1768;-15401 2033 1;-15401 2298;-15440 2540;-15517 2763 1;-15593 2980;-15703 3168;-15848 3324 1;-15993 3479;-16168 3598;-16373 3684 1;-16574 3769;-16800 3810;-17051 3810 18;-17051 2993 1;-16834 2993;-16668 2908;-16552 2736 1;-16433 2561;-16373 2326;-16373 2033 1;-16373 1738;-16433 1509;-16552 1342 1;-16668 1171;-16834 1085;-17051 1085 1;-17269 1085;-17437 1171;-17557 1342 1;-17675 1509;-17736 1738;-17736 2033 1;-17736 2326;-17675 2561;-17557 2736 1;-17437 2908;-17269 2993;-17051 2993 18;17090 5390 1;16864 5390;16623 5362;16391 5321 1;16389 5320;16389 5320;16387 5320 1;16169 5284;15963 5240;15758 5170 1;15555 5102;15409 4847;15450 4637 1;15450 4635;15450 4634;15450 4632;15538 4221 1;15549 4165;15526 4109;15478 4079 1;15431 4049;15369 4052;15324 4088 1;15245 4152;15138 4191;15033 4191;14080 4191 1;13930 4193;13775 4107;13692 3983 1;13669 3944;13625 3921;13580 3921 1;13534 3921;13492 3944;13466 3983 1;13385 4107;13233 4191;13083 4191;12131 4191 1;12001 4191;11866 4131;11781 4032 1;11755 4004;11719 3987;11680 3987 1;11642 3987;11606 4004;11581 4032 1;11496 4131;11364 4191;11234 4191;10281 4191 1;10048 4193;9815 3957;9815 3724;9815 1796 1;9815 1758;9798 1721;9770 1697 1;9741 1670;9703 1658;9665 1663 1;9608 1670;9553 1668;9498 1655 1;9433 1638;9341 1618;9227 1597 1;9221 1595;9217 1593;9210 1592 1;9161 1582;9142 1582;9157 1584 1;9118 1578;9078 1590;9048 1615 1;9018 1640;9001 1678;9001 1716 1;9001 1716;9001 3724;9001 3724 1;9001 3957;8768 4193;8535 4191;7582 4191 1;7424 4191;7262 4099;7183 3962 1;7162 3926;7125 3900;7082 3894 1;7040 3889;6999 3902;6969 3932 1;6935 3966;6899 3996;6855 4016 1;6768 4059;6679 4092;6574 4124 1;6467 4157;6364 4178;6253 4195 1;6244 4197;6236 4199;6226 4202 1;6107 4227;5991 4240;5870 4254 1;5738 4267;5601 4279;5461 4279 1;5121 4279;4808 4231;4520 4120 1;4514 4118;4506 4114;4498 4110 1;4220 3998;3977 3840;3778 3636 1;3717 3566;3663 3494;3609 3412 1;3571 3361;3503 3344;3445 3371 1;3389 3396;3357 3459;3370 3521 1;3404 3711;3293 3930;3120 4016 1;3034 4060;2945 4092;2837 4124 1;2732 4157;2628 4178;2517 4195 1;2508 4197;2499 4199;2490 4202 1;2372 4227;2256 4240;2134 4254 1;2001 4267;1866 4279;1726 4279 1;1386 4279;1071 4231;785 4120 1;778 4118;771 4114;761 4110 1;562 4029;383 3917;218 3782 1;186 3757;145 3748;105 3757 1;64 3765;32 3793;13 3829 1;-35 3929;-124 4013;-229 4054 1;-362 4109;-512 4164;-678 4204 1;-871 4251;-1073 4266;-1303 4266 1;-1606 4266;-1876 4227;-2127 4126 1;-2355 4031;-2561 3892;-2717 3706 1;-2719 3701;-2721 3699;-2719 3702 1;-2716 3707;-2718 3706;-2721 3700 1;-2724 3694;-2734 3667;-2764 3624 1;-2799 3577;-2859 3558;-2915 3576 1;-2969 3594;-3007 3646;-3006 3704;-3006 3724 1;-3006 3957;-3239 4193;-3473 4191;-4427 4191 1;-4577 4193;-4730 4107;-4814 3983 1;-4838 3944;-4880 3921;-4927 3921 1;-4972 3921;-5013 3944;-5038 3983 1;-5122 4107;-5274 4191;-5421 4191;-6377 4191 1;-6533 4191;-6696 4099;-6777 3962 1;-6796 3926;-6833 3900;-6875 3894 1;-6916 3889;-6958 3902;-6988 3932 1;-7021 3966;-7059 3996;-7101 4016 1;-7101 4017;-7104 4016;-7104 4016 1;-7191 4059;-7278 4092;-7384 4124 1;-7489 4157;-7592 4178;-7704 4195 1;-7714 4197;-7722 4199;-7729 4199 1;-7731 4201;-7733 4202;-7735 4204 1;-7849 4227;-7966 4240;-8088 4254 1;-8221 4267;-8355 4279;-8496 4279 1;-8836 4279;-9151 4231;-9437 4120 1;-9444 4118;-9450 4114;-9461 4110 1;-9737 3998;-9980 3840;-10181 3635 1;-10185 3631;-10185 3628;-10186 3624 1;-10218 3591;-10250 3536;-10300 3470 1;-10333 3425;-10393 3404;-10450 3423 1;-10505 3440;-10541 3493;-10541 3549 1;-10541 3549;-10541 3724;-10541 3724 1;-10541 3957;-10774 4193;-11008 4191;-11961 4191 1;-12194 4193;-12427 3957;-12427 3724;-12427 1796 1;-12427 1758;-12444 1721;-12472 1697 1;-12501 1670;-12539 1658;-12577 1663 1;-12634 1670;-12689 1668;-12744 1655 1;-12809 1638;-12901 1618;-13015 1597 1;-13021 1595;-13026 1593;-13032 1592 1;-13081 1582;-13101 1582;-13086 1584 1;-13124 1578;-13164 1590;-13194 1615 1;-13224 1640;-13240 1678;-13240 1716;-13240 3724 1;-13240 3957;-13474 4193;-13707 4191;-14660 4191 1;-14893 4193;-15126 3957;-15126 3724;-15126 3532 1;-15126 3476;-15164 3423;-15218 3406 1;-15273 3387;-15335 3408;-15368 3455 1;-15420 3526;-15463 3591;-15511 3643 1;-15517 3651;-15517 3652;-15518 3656 1;-15703 3851;-15933 4002;-16189 4110 1;-16458 4224;-16748 4279;-17051 4279 1;-17346 4279;-17629 4225;-17889 4120 1;-17895 4118;-17902 4114;-17909 4112 1;-18160 4005;-18392 3849;-18580 3651 1;-18586 3643;-18587 3641;-18588 3637 1;-18776 3434;-18919 3196;-19017 2931 1;-19017 2931;-19017 2933;-19019 2927 1;-19122 2648;-19176 2350;-19176 2033 1;-19176 1718;-19123 1419;-19019 1142;-19019 1139 1;-18913 875;-18770 642;-18587 446 1;-18407 241;-18054 35;-17897 -33 1;-17894 -33;-17891 -35;-17889 -37 1;-17628 -143;-17344 -200;-17051 -200 1;-16755 -200;-16468 -143;-16206 -33 1;-15946 76;-15707 235;-15518 433 1;-15470 487;-15425 560;-15368 639 1;-15335 685;-15273 705;-15218 687 1;-15164 669;-15126 617;-15126 559;-15126 538 1;-15126 435;-15088 329;-15023 247 1;-14983 198;-14983 129;-15023 80 1;-15088 -1;-15126 -108;-15126 -211;-15126 -1586 1;-15126 -1644;-15164 -1696;-15218 -1714 1;-15273 -1733;-15335 -1712;-15368 -1666 1;-15420 -1594;-15463 -1529;-15506 -1483 1;-15508 -1478;-15511 -1474;-15517 -1465 1;-15701 -1271;-15928 -1115;-16189 -1005 1;-16463 -889;-16753 -841;-17051 -841 1;-17341 -841;-17624 -888;-17889 -995 1;-17895 -997;-17902 -1001;-17909 -1003 1;-18165 -1113;-18394 -1273;-18578 -1463 1;-18582 -1468;-18582 -1470;-18584 -1474 1;-18586 -1476;-18587 -1479;-18588 -1483 1;-18776 -1684;-18919 -1924;-19019 -2192 1;-19122 -2472;-19176 -2770;-19176 -3085 1;-19176 -3401;-19122 -3698;-19019 -3973 1;-18915 -4238;-18774 -4473;-18586 -4675 1;-18582 -4680;-18580 -4682;-18580 -4684 1;-18577 -4686;-18573 -4688;-18572 -4690 1;-18384 -4883;-18152 -5043;-17897 -5151 1;-17894 -5153;-17891 -5154;-17889 -5156 1;-17628 -5263;-17344 -5317;-17051 -5317 1;-16755 -5317;-16468 -5263;-16204 -5150 1;-15943 -5038;-15707 -4885;-15518 -4684 1;-15468 -4631;-15425 -4560;-15368 -4481 1;-15335 -4433;-15273 -4413;-15218 -4431 1;-15164 -4449;-15126 -4502;-15126 -4560 1;-15126 -4560;-15126 -4648;-15126 -4648 1;-15126 -4840;-14978 -5038;-14794 -5093 1;-14694 -5123;-14598 -5144;-14499 -5165 1;-14499 -5165;-14477 -5168;-14452 -5173 1;-14340 -5198;-14217 -5219;-14085 -5240 1;-13922 -5266;-13842 -5266;-13682 -5281 1;-13547 -5294;-13415 -5306;-13286 -5306 1;-12964 -5306;-12660 -5253;-12380 -5145 1;-12373 -5143;-12371 -5143;-12369 -5143 1;-12101 -5041;-11864 -4883;-11665 -4681 1;-11657 -4671;-11653 -4668;-11649 -4665 1;-11536 -4548;-11441 -4412;-11358 -4260 1;-11334 -4218;-11289 -4192;-11242 -4192 1;-11193 -4192;-11148 -4218;-11125 -4260 1;-11028 -4434;-10923 -4593;-10791 -4726 1;-10600 -4917;-10372 -5065;-10128 -5165 1;-10125 -5165;-10124 -5166;-10118 -5168 1;-9872 -5263;-9613 -5317;-9350 -5317 1;-8783 -5317;-8251 -5120;-7882 -4730 1;-7846 -4691;-7812 -4641;-7767 -4581 1;-7733 -4538;-7676 -4519;-7622 -4536 1;-7569 -4553;-7532 -4600;-7529 -4656 1;-7526 -4846;-7376 -5039;-7192 -5093 1;-6999 -5148;-6777 -5186;-6518 -5226 1;-6513 -5227;-6510 -5229;-6505 -5231 1;-6226 -5276;-5933 -5306;-5630 -5306 1;-5305 -5306;-5020 -5259;-4754 -5153 1;-4499 -5054;-4271 -4908;-4106 -4706 1;-3948 -4513;-3839 -4282;-3777 -4034 1;-3719 -3803;-3694 -3557;-3694 -3294;-3694 -1395 1;-3694 -1161;-3927 -927;-4160 -927;-5113 -927 1;-5263 -927;-5419 -1011;-5502 -1136 1;-5525 -1174;-5569 -1198;-5613 -1198 1;-5660 -1198;-5701 -1174;-5727 -1136 1;-5808 -1012;-5960 -927;-6110 -927;-7063 -927 1;-7220 -929;-7380 -1021;-7459 -1158 1;-7479 -1194;-7516 -1219;-7558 -1224 1;-7599 -1231;-7641 -1216;-7671 -1186 1;-7706 -1151;-7744 -1121;-7790 -1100 1;-7898 -1046;-7976 -1023;-8071 -995 1;-8178 -963;-8281 -942;-8392 -924 1;-8400 -923;-8409 -921;-8417 -920 1;-8419 -918;-8419 -916;-8421 -916 1;-8527 -894;-8644 -874;-8775 -861 1;-8917 -846;-9054 -841;-9183 -841 1;-9523 -841;-9837 -888;-10125 -999 1;-10132 -1003;-10138 -1004;-10148 -1010 1;-10205 -1033;-10261 -1066;-10333 -1102 1;-10380 -1126;-10438 -1119;-10478 -1085 1;-10520 -1051;-10537 -996;-10520 -944 1;-10481 -826;-10453 -709;-10453 -616 1;-10453 -441;-10525 -246;-10637 -67 1;-10667 -20;-10665 40;-10633 84 1;-10575 164;-10541 262;-10541 359;-10541 559 1;-10541 617;-10505 669;-10450 687 1;-10393 705;-10333 685;-10300 639 1;-10233 547;-10171 461;-10103 392 1;-9914 202;-9685 53;-9442 -45 1;-9439 -46;-9435 -48;-9433 -50 1;-9432 -50;-9431 -50;-9429 -50 1;-9183 -144;-8927 -200;-8662 -200 1;-8096 -200;-7562 -1;-7194 386;-7194 386 1;-7192 387;-7194 385;-7194 388 1;-7192 392;-7192 389;-7192 390;-7143 453;-7079 538 1;-7046 581;-6988 598;-6935 581 1;-6882 566;-6846 519;-6843 463 1;-6837 273;-6688 79;-6505 25 1;-6313 -30;-6091 -67;-5824 -110 1;-5540 -156;-5246 -186;-4944 -186 1;-4619 -186;-4332 -142;-4066 -35 1;-3813 64;-3584 211;-3419 414 1;-3404 432;-3387 477;-3347 534 1;-3315 585;-3254 609;-3195 590 1;-3139 574;-3101 519;-3103 459 1;-3103 459;-3103 -482;-3103 -482 1;-3103 -693;-2915 -908;-2706 -941;-1753 -1091 1;-1501 -1129;-1215 -886;-1215 -633;-1215 -241 1;-1215 -166;-1155 -108;-1082 -108;-537 -108 1;-323 -106;-109 82;-79 292 1;-70 342;-35 384;11 399 1;60 415;111 402;147 367 1;333 190;549 49;786 -47 1;1037 -144;1294 -200;1559 -200 1;2126 -200;2658 -1;3027 388 1;3144 513;3239 654;3317 805 1;3338 848;3383 875;3432 875 1;3481 876;3526 852;3550 808 1;3642 654;3737 510;3853 392 1;4045 202;4273 53;4521 -47;4521 -47 1;4521 -48;4520 -46;4522 -47 1;4529 -50;4527 -50;4527 -50 1;4773 -144;5031 -200;5295 -200 1;5861 -200;6394 -1;6763 388 1;6802 432;6839 480;6882 538 1;6914 581;6969 600;7022 585 1;7074 572;7112 529;7119 476 1;7140 310;7271 152;7427 97 1;7615 32;7831 -23;8080 -81 1;8085 -82;8083 -82;8083 -82 1;8360 -146;8654 -178;8965 -178 1;9028 -178;9099 -173;9168 -166 1;9182 -165;9190 -163;9205 -161 1;9278 -156;9347 -148;9415 -136 1;9437 -133;9445 -133;9447 -133 1;9507 -125;9570 -116;9635 -103 1;9681 -95;9728 -112;9758 -146 1;9788 -181;9800 -230;9785 -274 1;9745 -394;9718 -512;9718 -616 1;9718 -878;9838 -1186;10043 -1369 1;10048 -1372;10051 -1374;10058 -1380 1;10243 -1538;10508 -1644;10751 -1644 1;10994 -1644;11257 -1542;11452 -1373 1;11458 -1367;11460 -1365;11466 -1364 1;11671 -1177;11789 -869;11789 -616 1;11789 -441;11717 -246;11606 -67 1;11576 -18;11579 44;11619 94 1;11644 132;11678 151;11716 155 1;11753 159;11791 147;11817 122 1;11871 77;11935 44;12001 25 1;12194 -30;12416 -67;12682 -110 1;12967 -156;13260 -186;13564 -186 1;13889 -186;14173 -142;14439 -35 1;14695 64;14923 211;15088 414 1;15181 529;15253 669;15316 817 1;15337 863;15381 893;15431 897 1;15481 900;15529 874;15555 830 1;15641 673;15739 530;15862 404 1;16061 212;16299 65;16570 -35 1;16849 -140;17147 -186;17457 -186 1;17585 -186;17713 -174;17847 -161 1;17854 -161;17842 -163;17865 -161 1;17990 -153;18110 -138;18237 -120 1;18355 -103;18475 -82;18593 -58 1;18605 -52;18617 -50;18628 -50 1;18743 -26;18848 -1;18940 25 1;19124 80;19273 279;19273 472;19273 3299 1;19273 3915;19111 4466;18724 4844 1;18718 4850;18716 4852;18709 4858 1;18305 5243;17742 5390;17090 5390 18;-1305 3799 1;-1102 3799;-928 3780;-787 3748 1;-646 3712;-518 3673;-404 3626;-539 2884 1;-627 2918;-728 2946;-838 2966 1;-945 2984;-1050 2993;-1151 2993 1;-1365 2993;-1508 2933;-1581 2813 1;-1648 2694;-1683 2535;-1683 2333;-1683 1150;-539 1150;-539 357;-1683 357;-1683 -634;-2636 -481;-2636 2346 1;-2636 2565;-2618 2763;-2579 2941 1;-2536 3121;-2463 3274;-2361 3402 1;-2259 3526;-2123 3624;-1951 3696 1;-1781 3764;-1566 3799;-1305 3799 18;10752 -52 1;10906 -52;11038 -101;11149 -200 1;11264 -303;11323 -441;11323 -616 1;11323 -791;11264 -927;11149 -1026 1;11038 -1128;10906 -1179;10752 -1179 1;10600 -1179;10465 -1128;10350 -1026 1;10238 -927;10183 -791;10183 -616 1;10183 -441;10238 -303;10350 -200 1;10465 -101;10600 -52;10752 -52 18;10279 3722;11233 3722;11233 357;10279 357;10279 3722 18;-11963 3722;-11009 3722;-11009 357;-11963 357;-11963 3722 18;7581 3722;8534 3722;8534 1150 1;8575 1137;8637 1126;8718 1118 1;8804 1105;8877 1098;8937 1098 1;9069 1098;9191 1112;9306 1137 1;9424 1158;9523 1180;9608 1201;9768 414 1;9717 392;9655 375;9583 363 1;9510 345;9437 333;9365 325 1;9292 312;9220 303;9146 299 1;9075 290;9013 286;8962 286 1;8677 286;8415 314;8182 369 1;7950 420;7750 476;7581 536;7581 3722 18;12129 3722;13082 3722;13082 1125 1;13142 1115;13210 1110;13287 1105 1;13363 1097;13435 1092;13504 1092 1;13725 1092;13876 1156;13958 1285 1;14039 1411;14080 1630;14080 1937;14080 3722;15033 3722;15033 1821 1;15033 1592;15010 1381;14963 1195 1;14916 1007;14836 845;14726 709 1;14614 572;14463 467;14272 395 1;14083 318;13847 280;13562 280 1;13280 280;13012 301;12756 344 1;12499 382;12290 425;12129 472;12129 3722 18;15729 3616 1;15737 3607;15749 3596;15758 3588 1;15784 3562;15799 3528;15799 3491 1;15799 3491;15799 3489;15799 3487;15794 3440;15767 3400 1;15769 3404;15763 3384;15737 3354 1;15711 3321;15671 3303;15623 3306 1;15549 3318;15499 3374;15499 3441 1;15499 3441;15499 3524;15499 3524 1;15501 3579;15533 3628;15585 3648 1;15634 3667;15691 3656;15729 3616 18;-14662 3722;-13708 3722;-13708 1150 1;-13667 1137;-13605 1126;-13524 1118 1;-13439 1105;-13366 1098;-13306 1098 1;-13174 1098;-13051 1112;-12936 1137 1;-12819 1158;-12719 1180;-12634 1201;-12474 414 1;-12526 392;-12587 375;-12660 363 1;-12733 345;-12806 333;-12878 325 1;-12951 312;-13022 303;-13096 299 1;-13167 290;-13229 286;-13280 286 1;-13565 286;-13827 314;-14060 369 1;-14292 420;-14492 476;-14662 536;-14662 3722 18;-6378 3722;-5425 3722;-5425 1125 1;-5365 1115;-5297 1110;-5220 1105 1;-5143 1097;-5070 1092;-5002 1092 1;-4780 1092;-4628 1156;-4549 1285 1;-4467 1411;-4427 1630;-4427 1937;-4427 3722;-3474 3722;-3474 1821 1;-3474 1592;-3497 1381;-3544 1195 1;-3591 1007;-3669 845;-3781 709 1;-3890 572;-4042 467;-4235 395 1;-4422 318;-4658 280;-4945 280 1;-5227 280;-5495 301;-5751 344 1;-6007 382;-6217 425;-6378 472;-6378 3722 18;17092 4924 1;17677 4924;18107 4790;18385 4522 1;18665 4257;18806 3849;18806 3299;18806 472 1;18722 446;18624 422;18512 402 1;18405 375;18292 354;18173 337 1;18053 320;17931 307;17809 299 1;17689 286;17572 280;17457 280 1;17187 280;16947 320;16734 402 1;16521 478;16339 589;16189 733 1;16044 878;15934 1053;15857 1259 1;15781 1458;15743 1683;15743 1930 1;15743 2424;15866 2811;16113 3087 1;16361 3361;16717 3498;17182 3498 1;17309 3498;17427 3487;17533 3466 1;17644 3441;17750 3404;17854 3357;17854 3479 1;17854 3666;17796 3819;17680 3939 1;17570 4059;17377 4119;17105 4119 1;16896 4119;16707 4101;16543 4067 1;16379 4032;16224 3986;16074 3926;15908 4725 1;16083 4785;16272 4832;16477 4867 1;16682 4905;16887 4924;17092 4924 18;1726 3810 1;1846 3810;1965 3804;2084 3793 1;2204 3779;2314 3763;2417 3741 1;2524 3724;2619 3703;2705 3677 1;2790 3651;2859 3626;2910 3601;2782 2826 1;2675 2873;2538 2912;2365 2948 1;2199 2978;2029 2993;1854 2993 1;1581 2993;1364 2934;1201 2820 1;1039 2704;947 2550;926 2353;3088 2353 1;3094 2301;3097 2245;3102 2180 1;3105 2112;3109 2050;3109 1995 1;3109 1426;2968 998;2692 709 1;2416 414;2038 267;1559 267 1;1354 267;1154 305;958 382 1;766 459;596 572;446 722 1;298 870;177 1058;81 1285 1;-9 1507;-52 1766;-52 2065 1;-52 2311;-19 2542;49 2756 1;123 2969;231 3154;376 3312 1;524 3466;712 3588;933 3677 1;1159 3765;1422 3810;1726 3810 18;-8496 3810 1;-8376 3810;-8257 3804;-8137 3793 1;-8017 3779;-7908 3763;-7804 3741 1;-7697 3724;-7603 3703;-7516 3677 1;-7431 3651;-7363 3626;-7311 3601;-7440 2826 1;-7546 2873;-7684 2912;-7855 2948 1;-8022 2978;-8193 2993;-8368 2993 1;-8640 2993;-8857 2934;-9020 2820 1;-9182 2704;-9273 2550;-9296 2353;-7132 2353 1;-7128 2301;-7124 2245;-7119 2180 1;-7116 2112;-7113 2050;-7113 1995 1;-7113 1426;-7252 998;-7529 709 1;-7806 414;-8184 267;-8662 267 1;-8867 267;-9067 305;-9264 382 1;-9455 459;-9625 572;-9775 722 1;-9923 870;-10045 1058;-10140 1285 1;-10230 1507;-10273 1766;-10273 2065 1;-10273 2311;-10240 2542;-10171 2756 1;-10098 2969;-9990 3154;-9845 3312 1;-9696 3466;-9510 3588;-9288 3677 1;-9062 3765;-8798 3810;-8496 3810 18;-12484 -146 1;-12454 -181;-12442 -230;-12457 -274 1;-12497 -394;-12524 -512;-12524 -616 1;-12524 -619;-12502 -670;-12491 -753 1;-12484 -794;-12496 -836;-12524 -866 1;-12554 -896;-12594 -911;-12640 -905 1;-12765 -882;-12886 -866;-13002 -866 1;-13009 -866;-13043 -876;-13102 -878 1;-13139 -879;-13174 -866;-13199 -841 1;-13225 -814;-13240 -781;-13240 -745 1;-13240 -745;-13240 -300;-13240 -300 1;-13240 -230;-13188 -171;-13119 -166 1;-13079 -161;-13064 -165;-13074 -166;-13056 -164;-13037 -161 1;-12964 -156;-12895 -148;-12827 -136 1;-12806 -133;-12797 -133;-12795 -133;-12795 -132;-12793 -132;-12795 -133 1;-12735 -125;-12673 -116;-12607 -103 1;-12561 -95;-12514 -112;-12484 -146 18;-14662 -213;-13708 -213;-13708 -1479 1;-13602 -1432;-13492 -1395;-13377 -1371 1;-13261 -1346;-13137 -1333;-13006 -1333 1;-12765 -1333;-12556 -1376;-12373 -1461 1;-12189 -1546;-12034 -1666;-11911 -1819 1;-11788 -1972;-11694 -2156;-11629 -2368 1;-11566 -2582;-11535 -2819;-11535 -3078 1;-11535 -3348;-11574 -3591;-11657 -3808 1;-11732 -4027;-11846 -4210;-11995 -4358 1;-12144 -4513;-12328 -4630;-12544 -4711 1;-12763 -4796;-13009 -4838;-13287 -4838 1;-13402 -4838;-13524 -4833;-13652 -4819 1;-13776 -4811;-13898 -4798;-14022 -4781 1;-14142 -4765;-14257 -4743;-14369 -4716 1;-14478 -4697;-14576 -4673;-14662 -4646;-14662 -213 18;-17051 3810 1;-16800 3810;-16574 3769;-16373 3684 1;-16168 3598;-15993 3479;-15848 3324 1;-15703 3168;-15593 2980;-15517 2763 1;-15440 2540;-15401 2298;-15401 2033 1;-15401 1768;-15442 1526;-15523 1310 1;-15603 1092;-15716 906;-15861 754 1;-16006 600;-16182 480;-16386 395 1;-16586 310;-16808 267;-17051 267 1;-17291 267;-17512 310;-17717 395 1;-17917 480;-18092 600;-18242 754 1;-18387 906;-18502 1092;-18587 1310 1;-18669 1526;-18708 1768;-18708 2033 1;-18708 2298;-18669 2540;-18587 2763 1;-18505 2980;-18392 3168;-18247 3324 1;-18102 3479;-17931 3598;-17729 3684 1;-17525 3769;-17299 3810;-17051 3810 18;5463 3810 1;5581 3810;5701 3804;5821 3793 1;5939 3779;6051 3763;6152 3741 1;6259 3724;6356 3703;6441 3677 1;6526 3651;6594 3626;6646 3601;6518 2826 1;6411 2873;6272 2912;6103 2948 1;5936 2978;5765 2993;5590 2993 1;5318 2993;5100 2934;4938 2820 1;4775 2704;4683 2550;4663 2353;6826 2353 1;6829 2301;6834 2245;6837 2180 1;6843 2112;6844 2050;6844 1995 1;6844 1426;6706 998;6428 709 1;6151 414;5773 267;5297 267 1;5091 267;4891 305;4695 382 1;4503 459;4332 572;4183 722 1;4033 870;3911 1058;3819 1285 1;3729 1507;3684 1766;3684 2065 1;3684 2311;3719 2542;3787 2756 1;3858 2969;3967 3154;4112 3312 1;4262 3466;4446 3588;4668 3677 1;4895 3765;5160 3810;5463 3810 18;-1065 2521 1;-1018 2518;-969 2518;-911 2508 1;-910 2508;-909 2506;-907 2505 1;-830 2489;-762 2473;-704 2450 1;-702 2450;-700 2450;-698 2450 1;-670 2439;-642 2426;-612 2422 1;-545 2409;-500 2350;-503 2283 1;-509 2203;-520 2131;-520 2067 1;-520 1973;-502 1875;-490 1763 1;-487 1725;-500 1688;-525 1660 1;-550 1633;-586 1616;-623 1616;-1106 1616 1;-1170 1630;-1217 1687;-1215 1750;-1215 2333 1;-1215 2360;-1211 2386;-1206 2405 1;-1198 2474;-1136 2527;-1065 2521 18;-11490 -52 1;-11336 -52;-11204 -101;-11093 -200 1;-10978 -303;-10920 -441;-10920 -616 1;-10920 -791;-10978 -927;-11093 -1026 1;-11204 -1128;-11336 -1179;-11490 -1179 1;-11642 -1179;-11777 -1128;-11893 -1026 1;-12004 -927;-12059 -791;-12059 -616 1;-12059 -441;-12004 -303;-11893 -200 1;-11777 -101;-11642 -52;-11490 -52 18;-17051 -1307 1;-16800 -1307;-16574 -1349;-16373 -1434 1;-16168 -1521;-15993 -1639;-15848 -1794 1;-15703 -1951;-15593 -2139;-15517 -2355 1;-15440 -2579;-15401 -2820;-15401 -3085 1;-15401 -3350;-15442 -3591;-15523 -3808 1;-15603 -4027;-15716 -4211;-15861 -4365 1;-16006 -4518;-16182 -4638;-16386 -4723 1;-16586 -4808;-16808 -4851;-17051 -4851 1;-17291 -4851;-17512 -4808;-17717 -4723 1;-17917 -4638;-18092 -4518;-18242 -4365 1;-18387 -4211;-18502 -4027;-18587 -3808 1;-18669 -3591;-18708 -3350;-18708 -3085 1;-18708 -2820;-18669 -2579;-18587 -2355 1;-18505 -2139;-18392 -1951;-18247 -1794 1;-18102 -1639;-17931 -1521;-17729 -1434 1;-17525 -1349;-17299 -1307;-17051 -1307 18;-7064 -1395;-6112 -1395;-6112 -3993 1;-6052 -4004;-5983 -4008;-5907 -4013 1;-5830 -4021;-5759 -4027;-5690 -4027 1;-5468 -4027;-5317 -3961;-5235 -3833 1;-5155 -3707;-5113 -3488;-5113 -3182;-5113 -1395;-4160 -1395;-4160 -3296 1;-4160 -3527;-4183 -3737;-4230 -3923 1;-4277 -4111;-4357 -4273;-4467 -4410 1;-4579 -4547;-4730 -4652;-4921 -4723 1;-5110 -4800;-5347 -4838;-5631 -4838 1;-5913 -4838;-6181 -4818;-6438 -4774 1;-6694 -4736;-6903 -4693;-7064 -4646;-7064 -1395 18;-11099 -1644 1;-11068 -1689;-11067 -1748;-11095 -1794 1;-11120 -1834;-11128 -1842;-11120 -1827 1;-11143 -1872;-11189 -1899;-11242 -1899 1;-11246 -1897;-11251 -1895;-11258 -1894 1;-11298 -1887;-11332 -1862;-11353 -1827 1;-11358 -1822;-11364 -1814;-11375 -1799 1;-11394 -1761;-11396 -1718;-11379 -1679 1;-11362 -1639;-11328 -1611;-11287 -1602 1;-11257 -1596;-11240 -1594;-11242 -1594 1;-11188 -1581;-11133 -1601;-11099 -1644 18;-9182 -1307 1;-9063 -1307;-8944 -1314;-8824 -1326 1;-8706 -1339;-8594 -1356;-8492 -1378 1;-8385 -1395;-8289 -1416;-8204 -1440 1;-8119 -1468;-8051 -1493;-7999 -1517;-8127 -2292 1;-8234 -2246;-8372 -2205;-8542 -2171 1;-8708 -2141;-8881 -2126;-9055 -2126 1;-9327 -2126;-9545 -2184;-9707 -2299 1;-9870 -2413;-9961 -2569;-9982 -2766;-7819 -2766 1;-7816 -2817;-7810 -2873;-7808 -2939 1;-7803 -3007;-7801 -3069;-7801 -3123 1;-7801 -3692;-7939 -4120;-8217 -4410 1;-8494 -4705;-8872 -4851;-9349 -4851 1;-9553 -4851;-9754 -4813;-9950 -4736 1;-10141 -4660;-10314 -4547;-10462 -4396 1;-10611 -4248;-10733 -4060;-10827 -3833 1;-10917 -3612;-10962 -3352;-10962 -3054 1;-10962 -2807;-10926 -2575;-10858 -2362 1;-10787 -2149;-10678 -1964;-10533 -1806 1;-10383 -1652;-10198 -1530;-9976 -1440 1;-9750 -1352;-9485 -1307;-9182 -1307 18;16428 5964 1;15493 5816;15156 5633;14905 5136 1;14842 5010;14767 4893;14739 4875 1;14686 4843;13829 4769;13588 4777 1;13511 4779;12673 4786;11726 4791;10003 4801;9412 4491;8810 4801;8189 4815 1;7703 4826;7501 4814;7261 4759 1;6957 4690;6950 4690;6521 4785 1;6170 4863;5974 4881;5488 4880;5457 4880 1;4811 4880;4563 4831;4074 4600 1;3912 4524;3745 4461;3703 4461 1;3660 4461;3540 4504;3437 4556 1;2881 4838;1858 4973;1148 4859 1;1005 4835;741 4765;560 4703 1;206 4579;201 4579;-280 4737 1;-640 4854;-1173 4915;-1564 4883 1;-1876 4858;-2085 4806;-2522 4644 1;-2687 4583;-2700 4585;-2962 4691 1;-3221 4796;-3260 4801;-3912 4799 1;-4286 4797;-4700 4791;-4832 4785 1;-4964 4779;-5368 4786;-5729 4802 1;-6286 4827;-6433 4820;-6697 4760 1;-7003 4690;-7012 4690;-7420 4779 1;-8263 4964;-9117 4926;-9699 4679 1;-10091 4513;-10177 4511;-10482 4666;-10748 4801;-12238 4801;-12541 4645;-12844 4489;-13138 4645;-13432 4801;-14922 4801;-15208 4663 1;-15529 4507;-15467 4502;-16152 4744 1;-16831 4984;-17621 4945;-18238 4642 1;-19010 4261;-19482 3685;-19691 2865 1;-19908 2013;-19801 1156;-19391 468 1;-19227 194;-19067 14;-18762 -238 1;-18460 -488;-18462 -552;-18772 -793 1;-19030 -993;-19347 -1393;-19507 -1720 1;-19700 -2113;-19789 -2552;-19789 -3099 1;-19788 -3688;-19727 -3966;-19489 -4450 1;-19192 -5057;-18572 -5590;-17912 -5806 1;-17374 -5984;-16546 -5988;-15770 -5633;-15447 -5485;-15239 -5576 1;-14740 -5794;-14092 -5908;-13352 -5909 1;-12569 -5909;-12118 -5779;-11532 -5381 1;-11433 -5314;-11315 -5259;-11270 -5259 1;-11225 -5259;-11047 -5352;-10875 -5465 1;-10015 -6029;-9078 -6079;-8023 -5616;-7813 -5524;-7443 -5648 1;-6638 -5917;-5482 -5996;-4840 -5824 1;-4236 -5663;-3768 -5339;-3467 -4873 1;-3148 -4378;-3090 -4073;-3064 -2779 1;-3052 -2196;-3026 -1687;-3006 -1649 1;-2959 -1560;-2787 -1559;-2312 -1644 1;-2114 -1679;-1835 -1709;-1691 -1709 1;-1229 -1712;-888 -1486;-654 -1024 1;-541 -799;-528 -788;-245 -681 1;187 -519;243 -518;668 -662 1;965 -763;1135 -795;1448 -809 1;2091 -837;2566 -704;3131 -337 1;3264 -250;3400 -179;3432 -179 1;3464 -179;3619 -259;3777 -358 1;4137 -581;4287 -644;4688 -741 1;5298 -888;5896 -821;6519 -534;6870 -372;7177 -478 1;7625 -633;8076 -733;8550 -781 1;9020 -829;9105 -876;9182 -1133 1;9308 -1554;9694 -1943;10095 -2129 1;10456 -2299;11045 -2279;11428 -2095 1;11880 -1878;12170 -1593;12329 -1108 1;12441 -766;12455 -756;12815 -779 1;14010 -855;14639 -730;15238 -297 1;15327 -232;15435 -179;15479 -179 1;15522 -179;15686 -258;15843 -355 1;16603 -824;17454 -922;18651 -679 1;19204 -567;19417 -469;19628 -230 1;19900 79;19908 145;19908 1951;19908 2068 1;19908 3651;19886 3934;19718 4401 1;19503 5000;19140 5402;18544 5703 1;18007 5974;17155 6079;16428 5964 18;17928 5561 1;18700 5363;19232 4845;19456 4071 1;19512 3877;19525 3576;19538 2141 1;19550 942;19541 396;19509 289 1;19438 50;19214 -170;18973 -238 1;18265 -438;17233 -498;16751 -367 1;16348 -257;15993 -62;15706 208;15444 456;15306 271 1;15116 17;14791 -194;14388 -327 1;14080 -428;13997 -439;13508 -436 1;13048 -433;12442 -364;12083 -274 1;12000 -253;11998 -259;12035 -436 1;12080 -647;12030 -945;11908 -1199 1;11636 -1762;10897 -2045;10292 -1817 1;9806 -1635;9468 -1148;9468 -632;9468 -407;9216 -431 1;8866 -464;8256 -399;7783 -280 1;7323 -163;7201 -108;7046 57;6934 175;6772 37 1;5907 -704;4382 -591;3607 271;3424 474;3360 377 1;3119 21;2522 -336;2023 -420 1;1440 -518;756 -394;341 -114;129 29;28 -91 1;-139 -289;-354 -379;-661 -379;-932 -379;-932 -509 1;-933 -750;-1017 -955;-1187 -1124 1;-1437 -1374;-1550 -1391;-2246 -1283 1;-2569 -1232;-2887 -1163;-2953 -1129 1;-3248 -975;-3372 -718;-3372 -258;-3372 65;-3550 -58 1;-3801 -230;-4065 -337;-4405 -402 1;-4781 -475;-5094 -474;-5671 -398 1;-6502 -289;-6766 -202;-6943 24;-7040 149;-7232 3 1;-7499 -201;-7690 -292;-8042 -381 1;-8755 -564;-9576 -395;-10120 47 1;-10278 175;-10291 178;-10328 108 1;-10358 53;-10348 -23;-10289 -176 1;-10246 -291;-10206 -465;-10202 -563 1;-10196 -705;-10182 -736;-10133 -716 1;-9610 -509;-8452 -550;-7836 -799 1;-7626 -884;-7587 -889;-7532 -839 1;-7372 -694;-7184 -659;-6575 -659;-6548 -659 1;-5984 -659;-5837 -684;-5685 -810 1;-5627 -859;-5602 -854;-5492 -772 1;-5374 -686;-5322 -678;-4739 -666 1;-4055 -652;-3888 -680;-3692 -845 1;-3432 -1064;-3432 -1066;-3434 -2479 1;-3436 -3540;-3448 -3805;-3503 -4029 1;-3693 -4791;-4168 -5277;-4922 -5483 1;-5261 -5575;-6112 -5566;-6698 -5463 1;-7272 -5363;-7461 -5289;-7610 -5109;-7726 -4968;-7879 -5088 1;-8814 -5825;-10355 -5685;-11073 -4798;-11235 -4596;-11494 -4860 1;-11783 -5155;-12156 -5370;-12572 -5482 1;-13108 -5626;-14450 -5523;-14960 -5299 1;-15058 -5256;-15175 -5160;-15239 -5069;-15350 -4913;-15611 -5103 1;-16440 -5704;-17556 -5733;-18389 -5176 1;-18763 -4926;-18970 -4689;-19174 -4279 1;-19544 -3536;-19541 -2623;-19167 -1859 1;-18698 -903;-17669 -417;-16549 -623 1;-16267 -675;-15788 -888;-15574 -1057;-15421 -1179;-15405 -589 1;-15394 -169;-15373 26;-15332 89 1;-15235 239;-15319 237;-15504 86 1;-16274 -546;-17402 -634;-18285 -132 1;-19617 624;-19853 2734;-18732 3863 1;-17912 4689;-16505 4772;-15566 4050;-15379 3907;-15333 4019 1;-15270 4170;-15105 4323;-14923 4399 1;-14712 4487;-13672 4487;-13461 4399 1;-13264 4317;-13137 4195;-13048 4001 1;-12982 3860;-12974 3728;-12973 2871;-12973 2823 1;-12972 1843;-12978 1874;-12782 1926;-12692 1950 1;-12692 3973;-12680 4038;-12439 4250 1;-12222 4440;-12067 4471;-11387 4455 1;-10804 4441;-10780 4438;-10624 4329 1;-10492 4238;-10413 4134;-10306 3908 1;-10303 3901;-10213 3963;-10106 4044 1;-9632 4408;-9047 4566;-8312 4528 1;-7825 4503;-7365 4416;-7096 4297 1;-6933 4226;-6914 4225;-6848 4284 1;-6702 4417;-6483 4457;-5908 4459 1;-5355 4461;-5138 4426;-5010 4317 1;-4958 4272;-4918 4279;-4753 4362 1;-4571 4454;-4518 4461;-3955 4460 1;-3267 4459;-3106 4414;-2910 4168;-2797 4027;-2623 4147 1;-2311 4362;-2052 4456;-1635 4505 1;-1021 4577;-348 4454;37 4200;186 4101;437 4238 1;800 4435;1069 4503;1588 4529 1;1962 4547;2134 4535;2508 4464 1;3072 4358;3355 4230;3495 4017 1;3552 3931;3607 3861;3617 3861 1;3627 3861;3710 3925;3802 4004 1;4053 4219;4484 4414;4868 4487 1;5440 4594;6391 4505;6861 4299;7033 4223;7198 4332 1;7357 4438;7382 4441;8005 4453 1;8611 4465;8658 4460;8826 4373 1;8923 4323;9058 4212;9126 4127;9248 3974;9260 2937;9272 1901;9360 1902 1;9541 1903;9543 1914;9556 2969;9568 3974;9690 4127 1;9758 4212;9892 4322;9989 4371 1;10149 4453;10221 4461;10767 4460 1;11260 4459;11397 4446;11528 4387 1;11682 4317;11694 4317;11848 4387 1;11979 4446;12117 4459;12611 4460 1;13162 4461;13230 4453;13396 4369;13577 4278;13732 4359 1;13871 4432;13961 4441;14551 4441;15214 4441;15197 4626 1;15173 4869;15240 5059;15408 5233 1;15568 5397;15799 5483;16366 5586 1;16835 5672;17538 5661;17928 5561;17928 5561 18;-13184 -2139 1;-13282 -2139;-13381 -2152;-13479 -2177 1;-13572 -2202;-13648 -2234;-13708 -2274;-13708 -3993 1;-13662 -4004;-13602 -4008;-13530 -4013 1;-13457 -4021;-13379 -4027;-13293 -4027 1;-13032 -4027;-12837 -3936;-12704 -3756 1;-12572 -3582;-12506 -3348;-12506 -3054 1;-12506 -2443;-12733 -2139;-13184 -2139 18;-13248 -2631 1;-13034 -2631;-12976 -2876;-12976 -3090 1;-12976 -3284;-13054 -3505;-13248 -3506;-13248 -2631 18;17360 2730 1;16930 2730;16714 2463;16714 1930 1;16714 1675;16776 1464;16900 1297 1;17024 1130;17212 1047;17464 1047 1;17548 1047;17623 1052;17687 1060 1;17750 1065;17807 1072;17854 1080;17854 2603 1;17794 2636;17720 2666;17636 2691 1;17550 2718;17459 2730;17360 2730 18;17409 2232;17409 1571 1;17201 1492;17180 1713;17180 1926 1;17178 2120;17192 2338;17409 2232 18; + + + +-15193 -49784 1;-14217 -49499;-13415 -49441;-12989 -48519 1;-12566 -47603;-11592 -45828;-12581 -46029 1;-14699 -46461;-17951 -46822;-17192 -48845 1;-16847 -49781;-16020 -50025;-15193 -49784 18; +-18644 -49113 1;-16846 -47471;-15545 -46883;-13137 -46518 1;-12026 -46350;-11285 -46033;-10331 -46624 1;-9588 -47084;-8719 -47865;-8212 -47154 1;-7772 -46538;-7013 -45793;-7630 -45353 1;-9702 -43873;-11427 -43443;-13878 -44135 1;-16573 -44897;-18495 -45269;-19915 -47683 1;-20238 -48232;-20561 -48935;-20021 -49272 1;-19643 -49507;-19340 -49380;-18909 -49272 1;-18789 -49242;-18735 -49197;-18644 -49113 18; +-14392 -50181 1;-14381 -49674;-14435 -47077;-14246 -46608 1;-14027 -46065;-13484 -45210;-13406 -44631;-12944 -42978; +-12152 -46461 1;-12371 -47163;-12539 -47623;-12875 -48268 1;-13136 -48770;-13281 -49147;-13796 -49381; +-8047 -47393 1;-8861 -46910;-9236 -46594;-10115 -46243 1;-11056 -45866;-11873 -45554;-12649 -46207 1;-13681 -47074;-14003 -47947;-15310 -48274 1;-15927 -48429;-16204 -47931;-16820 -47771 1;-17810 -47514;-18814 -47421;-19319 -48310;-19769 -49119; +-7400 -46782 1;-8361 -45993;-8885 -45517;-10097 -45235 1;-11152 -44989;-12287 -44656;-13216 -45654 1;-13781 -46261;-13659 -46705;-14781 -47249 1;-15525 -47611;-16019 -46869;-16910 -46476 1;-17867 -46054;-18629 -46273;-19499 -46853;-20470 -47789; +-7310 -45847 1;-8637 -44962;-9328 -44361;-10905 -44121 1;-11609 -44013;-12029 -44131;-12649 -44481 1;-12883 -44612;-12993 -44687;-13225 -44822 1;-13381 -44913;-13594 -44817;-13638 -44642 1;-13729 -44278;-14245 -44136;-14537 -44373 1;-14929 -44690;-15109 -44875;-15544 -45128 1;-15969 -45376;-16271 -45501;-16749 -45379 1;-17381 -45219;-17745 -44985;-18367 -45181;-19049 -45397; +-16623 -46279; +-17072 -46764; +-14397 -45742 1;-14115 -45417;-13985 -45095;-14103 -44970 1;-14223 -44844;-14555 -44959;-14897 -45229 16; +-15275 -45591 1;-15547 -45911;-15670 -46224;-15554 -46346 1;-15439 -46467;-15127 -46365;-14799 -46118 16; +-11871 -51383 1;-12194 -50957;-12110 -50349;-11683 -50027 1;-11256 -49703;-10649 -49787;-10326 -50214 1;-10003 -50641;-10087 -51248;-10514 -51571 1;-10941 -51894;-11548 -51810;-11871 -51383 18; +-8669 -51265 1;-8967 -51745;-9599 -51893;-10079 -51595 1;-10559 -51297;-10707 -50665;-10409 -50184 1;-10111 -49703;-9479 -49556;-8998 -49854 1;-8518 -50153;-8370 -50784;-8669 -51265 18; +-9687 -48591 1;-9115 -48519;-8593 -48925;-8521 -49497 1;-8449 -50069;-8856 -50592;-9428 -50663 1;-10000 -50735;-10523 -50329;-10594 -49757 1;-10665 -49184;-10259 -48662;-9687 -48591 18; +-10193 -48866 1;-9783 -49241;-9754 -49877;-10129 -50288 1;-10504 -50698;-11141 -50727;-11551 -50351 1;-11961 -49977;-11990 -49340;-11615 -48929 1;-11240 -48519;-10603 -48491;-10193 -48866 18; +-9063 -47648 1;-8585 -47931;-8427 -48547;-8709 -49025 1;-8992 -49502;-9608 -49661;-10086 -49378 1;-10563 -49095;-10721 -48479;-10439 -48001 1;-10157 -47524;-9541 -47365;-9063 -47648 18; +-11816 -48004 1;-11539 -47531;-10931 -47373;-10458 -47651 1;-9986 -47928;-9827 -48536;-10105 -49008 1;-10382 -49481;-10990 -49639;-11463 -49361 1;-11935 -49084;-12093 -48477;-11816 -48004 18; +-17713 -51067;-15891 -51625;-15183 -49263;-17007 -49375;-17713 -51067 18; +-13406 -45295 1;-14143 -44522;-16113 -43986;-15247 -43361 1;-14461 -42794;-13298 -41094;-13201 -42059;-12979 -44291;-13406 -45295 18; +-18763 -51061;-18781 -41352; +-20529 -42915;-7231 -42840; +-11472 -46906 1;-11715 -47233;-12177 -47303;-12505 -47061 1;-12833 -46819;-12902 -46357;-12660 -46029 1;-12418 -45701;-11955 -45631;-11627 -45873 1;-11299 -46115;-11230 -46578;-11472 -46906 18; +-13281 -51435 1;-13605 -51008;-13521 -50401;-13093 -50078 1;-12667 -49755;-12059 -49839;-11737 -50265 1;-11414 -50692;-11498 -51299;-11925 -51623 1;-12351 -51945;-12959 -51861;-13281 -51435 18; +-25283 -38824; +-14902 -45736; +-44934 -51808;Click menu View > Overprinting simulation +or press F4 to toggle +overprinting preview. + + + + + + + + + + + + + + + + diff --git a/examples/src/complete map.xmap b/examples/src/complete map.xmap new file mode 100644 index 0000000..95c7c2e --- /dev/null +++ b/examples/src/complete map.xmap @@ -0,0 +1,74858 @@ + + + + + + + +proj=utm +datum=WGS84 +zone=32 + 32 N + + + + +proj=latlong +datum=WGS84 + + + + + + + PURPLE + + + + + + + + + + + + + + BLACK + + + + + + + RED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BLUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BROWN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GREEN + + + + + + + + + + + + + + + + + + + + + + + + + + + + YELLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A line joining points of equal height. The standard vertical interval between contours is 2 or 2.5 m. To emphasize the 3-dimensional effect of the contour line image, contour lines shall be represented as continuous lines through all symbols, also building (526.1) and canopy (526.2). However, contour lines shall be cut out for better legibility, if they touch the following symbols: small earth wall (108.1), small knoll (112), small elongated knoll (113), small depression (115), pit or hole (116), prominent landform feature (118), step or edge of paved area (529.1). + +The relative height difference between neighbouring features must be represented on the map as accurately as possible. Absolute height accuracy is of less importance. It is permissible to alter the height of a contour slightly if this will improve the representation of a feature. This deviation should not exceed 25% of the contour interval and attention must be paid to neighbouring features. The smallest bend in a contour is 0.4 mm from centre to centre of the lines. + + + + Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with symbol contour (101). + + + + An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. + + + + Slope lines should be drawn on the lower side of a contour line where it is necessary to clarify the direction of slope, e.g. along the line of a re-entrant or in a depression. + + + + + + + + + + + + + + + + + + + Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. + + + + + + + A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, roads and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks shall be drawn with the symbol impassable cliff (201). The line width of very high earth banks may be 0.37 mm. + + + + + + + + + + + + + + + + + + + + + + + + + A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, roads and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks shall be drawn with the symbol impassable cliff (201). The line width of very high earth banks may be 0.37 mm. + + + + + + + + + + + + + + + + + + + + + + + + + Use this symbol to display the full extent of wide earth banks. + + + + A small distinct earth wall, usually man made. The minimum height is 0.5 m. Larger earth walls should be represented with the symbols contour (101), form line (103) or earth bank (106). + + + + + + + + + + An erosion gully or trench which is too small to be represented with the symbol earth bank (106), contour (101), index contour (102) or form line (103) is represented by a single line. The line width reflects the size of the gully. The end of the line is pointed. Minimum depth is 1 m. Minimum length is 3 mm on the map. + + + + A small erosion gully or trench. Minimum depth is 0.5 m. + + + + + + + + + + A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (101), index contour (102) or form line (103). The height of the knoll should be a minimum of 1 m from the surrounding ground. + + + + A small obvious elongated knoll which cannot be drawn to scale with a contour (101), index contour (102) or form line (103). The maximum length should be 6 m and the maximum width 2 m. The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this shall be shown by contours. + +The symbol may not be drawn in free form or such that two elongated knoll symbols touch or overlap. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A small shallow natural depression or hollow which cannot be represented by the symbol contour (101) or form line (103) is represented by a semicircle. The minimum diameter should be 2 m. The minimum depth from the surrounding ground should be 1 m. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + A pit or hole with distinct steep sides which cannot be represented to scale with the symbol earth bank (106). The minimum diameter shall be 2 m. The minimum depth from the surrounding ground shall be 1 m. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + An area of pits or knolls, which is too complex to be represented in detail. The density of randomly placed dots may vary according to the detail on the ground. + + + + An area of pits or knolls, which is too complex to be represented in detail. The density of randomly placed dots may vary according to the detail on the ground. + + + + A small landform feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An impassable cliff, quarry or earth bank [see symbol earth bank (106)]. Tags are drawn downwards, showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). + +The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags shall clearly extend over the bank line. Minimum height is 2 meters. + +<span style="color:magenta">It is forbidden to cross an impassable cliff! +Competitors violating this rule will be disqualified.</span> + + + + + + + + + + + + + + + + + + + + + + + + + An impassable cliff, quarry or earth bank [see symbol earth bank (106)]. Tags are drawn downwards, showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). + +The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags shall clearly extend over the bank line. Minimum height is 2 meters. + +<span style="color:magenta">It is forbidden to cross an impassable cliff! +Competitors violating this rule will be disqualified.</span> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). + + + + For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). + + + + + + + + + + + + + + + + + + + Use this symbol to display the full extent of a wide cliff. + + + + A gigantic boulder, rock pillar or massive cliff shall be represented in plan shape without tags. + + + + A small vertical rock face may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. Minimum height is 1 m. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + + + + + + + A small vertical rock face may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. Minimum height is 1 m. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. + + + + Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. + + + + + + + + + + + + + + + + + + + Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. + + + + Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. For passable rock faces shown without tags the end of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + A rocky pit, hole or mineshaft which may constitute a danger to the competitor. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + A cave is represented by the same symbol as a rocky pit. In this case the symbol shall be orientated to point up the slope as indicated opposite. This symbol should generally not be used in urban areas. The centre of gravity of the symbol marks the opening. + +<span style="color:magenta">Controls may not be placed inside caves!</span> + + + + + + + + + + + + + + + + + + + + + + + A small distinct boulder. The minimum height is 1 m. Every boulder marked on the map shall be immediately identifiable on the ground. + + + + A particularly large and distinct boulder. Gigantic boulders shall be represented in plan shape with the symbol gigantic boulder or rock pillar (202). + + + + An area which is covered with so many blocks of stone that they cannot be marked individually is represented with randomly orientated solid triangles. The runnability is reduced and is indicated by the density of the triangles. A minimum of two triangles shall be used. The triangles can be enlarged by up to 20 %. + + + + + + + + + + + + + + + + + + + + An area which is covered with so many blocks of stone that they cannot be marked individually is represented with randomly orientated solid triangles. The runnability is reduced and is indicated by the density of the triangles. A minimum of two triangles shall be used. The triangles can be enlarged by up to 20 %. + + + + + + + + + + + + + + + + + + + + An area of stony or rocky ground which reduces runnability. The dots shall be randomly distributed with density according to the amount of rock. A minimum of three dots shall be used. + + + + An area of stony or rocky ground which reduces runnability. The dots shall be randomly distributed with density according to the amount of rock. A minimum of three dots shall be used. + + + + An area of soft sandy ground or gravel with no vegetation which reduces runnability. Where an area of sandy ground is open and has good runnability, it is represented with symbol open land (401), open land with scattered trees (402) or paved area (529). + + + + + + + + + + An area of runnable rock without earth or vegetation. An area of rock covered with grass, moss or other low vegetation shall be represented according to its openness and runnability (401/402/403/404). + + + + A water-filled pit or an area of water which is too small to be shown to scale. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². + +<span style="color:magenta">It is forbidden to cross an impassable body of water! +Competitors violating this rule will be disqualified.</span> + + + + + + + An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². + +<span style="color:magenta">It is forbidden to cross an impassable body of water! +Competitors violating this rule will be disqualified.</span> + + + + + + + The bordering black line indicates that the feature cannot or shall not be crossed. + + + + An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². + +<span style="color:magenta">It is forbidden to cross an impassable body of water! +Competitors violating this rule will be disqualified.</span> + + + + An area of deep water such as a lake, pond, river or fountain which may constitute a danger to the competitor or has forbidden access. The dark blue colour and the bordering black line indicates that the feature cannot or shall not be crossed. The minimum dimension is 1 mm². + +<span style="color:magenta">It is forbidden to cross an impassable body of water! +Competitors violating this rule will be disqualified.</span> + + + + An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol impassable body of water (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. + + + + An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. + + + + An area of shallow water such as a pond, river or fountain that can be crossed. The body of water shall be less than 0.5 m deep and runnable. If the body of water is not runnable it shall be represented with the symbol impassable body of water (304.1). If no other line symbol touches the border of the passable body of water, the border shall be represented with a blue line. + + + + + + + A crossable watercourse less than 2 m wide. + + + + A natural or man-made minor water channel which may contain water only intermittently. + + + + A marsh or trickle of water which is too narrow to be shown with symbol marsh (310). + + + + + + + + + + A marsh which is impassable or which may constitute a danger to the competitor. The feature cannot or shall not be crossed. + +<span style="color:magenta">It is forbidden to cross an impassable marsh! +Competitors violating this rule will be disqualified.</span> + + + + + + + This symbol should not be used on its own. + + + + + + This symbol should not be used on its own. + + + + A crossable marsh, usually with a distinct edge. The symbol shall be combined with vegetation symbols to show runnability and openness. + +Minimum size: not less than 2 lines, 5 mm long. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness. + +Minimum size: 4 dashes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Small well or fountain, which is at least 1 m high or at least 1 m in diameter. + + + + The source of a stream with a distinct outflow. This symbol should generally not be used in urban areas. The symbol is orientated to open downstream. + + + + + + + + + + + + + + + + + + + + + + + + A small water feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An area of cultivated land, lawn, field, meadow, grassland, etc. without trees, offering very good runnability. + + + + An area of meadows with scattered trees or bushes, with grass or similar ground cover offering very good runnability. Areas smaller than 10 mm² at the maps scale are shown as open land (401). Symbols prominent large tree (418) and prominent bush or small tree (419) may be added. + + + + + + + + + + An area of heath or moorland, a felled area, a newly planted area (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, i.e. heather or tall grass. This symbol may be combined with symbols undergrowth: slow running (407) and undergrowth: difficult to run (409) to show reduced runnability. + + + + An area of rough open land with scattered trees or bushes. Areas smaller than 16 mm² in the map scale are either mapped as rough open land (403) or forest: easy running (405). Symbols prominent large tree (418) and prominent bush or small tree (419) may be added. + + + + + + + + + + An area of typical open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. + + + + An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. + +Minimum width 0.25 mm. + + + + + + + An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, cut branches, etc.) which reduces running to ca. 60-80% of normal speed. This symbol shall not be combined with the symbol forest: slow running (406) or forest: difficult to run (408). + + + + + + An area with dense trees or thicket (low visibility) which reduces running to ca. 20-60% of normal speed. + + + + + + + An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. + + + + + + An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced 1-20% of normal speed. + +Minimum width: 0.25 mm. + + + + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (406) to show the direction with good runnability. + + + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (408) to show the direction with good runnability. + + + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol (410) to show the direction with good runnability. + + + + + + Land planted with fruit trees or bushes. The dot lines may be orientated to represent the direction of planting. + + + + + + + + + + Land planted with fruit trees or bushes, with a distinct direction of planting which reduces the runnability. The green lines shall be orientated to show the direction of planting. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The boundary of symbol cultivated land (seasonally out of bounds) (415) when not shown with other symbols (fence, wall, path, etc.) is represented with a black line. A permanent boundary between different types of cultivated land is also represented with this symbol. + + + + Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. + + + + + + + + + + A distinct forest edge or very distinct vegetation boundary within the forest. For indistinct boundaries, the area edges are shown only by the change in colour and/or dot screen. + + + + + + + + + + A prominent single tree. + + + + A bush or a tree with a trunk less than 0.5 m diameter. + + + + A vegetation feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An area of dense vegetation (trees or undergrowth) which is impassable or which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor. + +Minimum width: 0.4 mm. + +<span style="color:magenta">It is forbidden to cross impassable vegetation! +Competitors violating this rule will be disqualified.</span> + + + + + + + An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). + +To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). + + + + + + + + An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). + +To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). + + + + + + + + An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). + +To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). + + + + + + + + An unpaved footpath or rough vehicle track is a way for passing mainly by foot, without a smooth, hard surface. The density of the brown fill-in shall be the same as the density chosen for the symbol (529). + +To improve the legibility of this symbol in non-urban parts of the map, the line width shall, in the non-urban parts of the map, be increased from 0.07 mm to 0.14 mm, and the brown fill-in shall, in the non-urban parts of the map, be drawn darker, so that if (x)% brown is used in urban parts of the map, (x+20)% brown shall be used in the non-urban parts of the map. + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbols paved area (529) and step or edge of paved areas (529.1). + + + + + + + + A small unpaved footpath or track. Not to be used in urban areas. + + + + A less distinct path or forestry extraction track. Not to be used in urban areas. + + + + A distinct ride is a linear break in the forest (usually in a plantation), which does not have a distinct path along it. Where there is a path along a ride, the symbol small unpaved footpath or track (506.1) shall be used. Not to be used in urban areas. + + + + A bridge is a structure spanning and permitting passage over a river, chasm, road or the like. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A bridge is a structure spanning and permitting passage over a river, chasm, road or the like. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A railway is a permanent track laid with rails on which locomotives, carriages or wagons can travel. If it is forbidden to cross or run along the railroad, the forbidden area around the railway shall be represented with symbol area with forbidden access (528.1). + + + + + + + + + + + + + + + + + + + A tramway is a public vehicle running regularly along certain streets, usually on rails. The track can be easily crossed by the competitor. Tramways are generally not represented. However, if they serve navigation or orientation, they can be represented. + + + + + + + + Power line, cableway or skilift. The bars indicate the exact location of the pylons. + +<b>Note: When drawing this symbol, press space to toggle placing the pylon symbols at new nodes.</b> + + + + + + + + + + + + + + + + + + + + + + + + + Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline. + +Very large carrying masts shall be represented in plan shape or with the symbol high tower (535). In this case, the cable lines can be left out (the map shows only the pylons). + +<b>Note: When drawing this symbol, press space to toggle placing the pylon symbols at new nodes.</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + An underpass or a tunnel is a passage running underneath the ground, especially a passage for pedestrians or vehicles, crossing under for instance a railroad or a road. + +<span style="color: magenta">If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with the symbol crossing point (708) or crossing section (708.1)!</span> + + + + + + + + + + + + + + + + + + + + + + + + + + + A stone wall or stone faced bank. This symbol shall be used only in non-urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol impassable wall (521.1). + + + + + + + + + + A passable wall or retaining wall is a construction made of stone, brick, concrete etc., which can be passed. This symbol is suitable for urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol impassable wall (521.1). Wide walls shall be drawn in plan shape. + + + + A passable wall or retaining wall is a construction made of stone, brick, concrete etc., which can be passed. This symbol is suitable for urban areas. If such a wall is higher than 2 m, it shall be represented with the symbol (521.1). Wide walls shall be drawn in plan shape. + + + + An impassable wall or retaining wall is a wall, which fulfil the function of an enclosure or solid barrier. It shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor due to its height. Very wide impassable walls shall be drawn in plan shape and represented with the symbol building (526.1). + +<span style="color:magenta">It is forbidden to cross an impassable wall! +Competitors violating this rule will be disqualified.</span> + + + + An impassable wall or retaining wall is a wall, which fulfil the function of an enclosure or solid barrier. It shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor due to its height. Very wide impassable walls shall be drawn in plan shape and represented with the symbol building (526.1). + +<span style="color:magenta">It is forbidden to cross an impassable wall! +Competitors violating this rule will be disqualified.</span> + + + + A passable fence is a barrier enclosing or bordering a field, yard, etc., usually made of posts and wire or wood. It is used to prevent entrance or to confine or mark a boundary. A railing is a fencelike barrier composed of one or more horizontal rails supported by widely spaced upright poles, usually it can be slipped through. + +If a fence or railing is higher than 2 m or very difficult to cross, it shall be represented with the symbol impassable fence or railing (524). + + + + + + + + + + + + + + + + + + + + + + + + + A passable fence is a barrier enclosing or bordering a field, yard, etc., usually made of posts and wire or wood. It is used to prevent entrance or to confine or mark a boundary. A railing is a fencelike barrier composed of one or more horizontal rails supported by widely spaced upright poles, usually it can be slipped through. + +If a fence or railing is higher than 2 m or very difficult to cross, it shall be represented with the symbol impassable fence or railing (524). + + + + + + + + + + + + + + + + + + + + + + + + + An impassable fence or railing, which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor because of its height. + +<span style="color:magenta">It is forbidden to cross an impassable fence or railing! +Competitors violating this rule will be disqualified.</span> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A crossing point is a gap or an opening in a fence, railing or wall, which can easily be crossed by a competitor. Small gaps or openings which can not easily be crossed by competitors, shall not be represented on the map and shall be closed during the competition. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A building is a relatively permanent construction having a roof. Buildings within symbol area with forbidden access (527.1) may just be represented in a simplified manner.Areas totally contained within a building shall be mapped as being a part of the building. + +The minimum gap between buildings and between buildings and other impassable features shall be 0.40 mm. The black screen percentage should be chosen according to the terrain. A dark screen gives a better contrast to passable areas, such as streets, stairways and canopies, while a light screen makes contours and course overprint more clearly visible (which can be important in very densely built up urban terrain and in steep urban terrain). The black screen shall be the same for the whole map. + +<span style="color:magenta">It is forbidden to pass through or over a building! +Competitors violating this rule will be disqualified.</span> + + + + + + + Do not use this symbol on its own! + + + + Do not use this symbol on its own! + + + + A building is a relatively permanent construction having a roof. Buildings within symbol area with forbidden access (527.1) may just be represented in a simplified manner.Areas totally contained within a building shall be mapped as being a part of the building. + +The minimum gap between buildings and between buildings and other impassable features shall be 0.40 mm. The black screen percentage should be chosen according to the terrain. A dark screen gives a better contrast to passable areas, such as streets, stairways and canopies, while a light screen makes contours and course overprint more clearly visible (which can be important in very densely built up urban terrain and in steep urban terrain). The black screen shall be the same for the whole map. + +<span style="color:magenta">It is forbidden to pass through or over a building! +Competitors violating this rule will be disqualified.</span> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A canopy is a building construction (with a roof), normally supported by pillars, poles or walls, such as passages, gangways, courts, bus stops, gas stations or garages. + +Small passable parts of buildings which can not easily be crossed by competitors, shall not be represented on the map and shall be closed during the competition. + + + + + + + Do not use this symbol on its own! + + + + Do not use this symbol on its own! + + + + A pillar is an upright shaft or structure of stone, brick or other material, relatively slender in proportion to its height and any shape in section, used as a building support. Pillars smaller than 2 m × 2 m are generally not represented. + +Columns of pillars and pillars along buildings are not represented. However, if they are important for navigation and orientation, they can be represented. + + + + + + + + + + + + + + + + + + + + + + An area with forbidden access such as a private area, a flower bed, a railway area etc. No feature shall be represented in this area, except very prominent features such as railways, large buildings, or very large trees. Road entrances shall be represented clearly. + +Areas with forbidden access totally contained within buildings shall be mapped as being a part of the building. + +<span style="color:magenta">It is forbidden to cross an area with forbidden access! +Competitors violating this rule will be disqualified.</span> + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: black, brown 0%(white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (min.60lines/cm) (non-urban); the colour and the line width shall be the same as for the symbol unpaved footpath or track (506.1). + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + + A paved area is an area with a firm level surfaces such as asphalt, hard gravel, tiles, concrete or the like. It shall be bordered (or framed) by the symbol step or edge of paved area (529.1). Distinct differences within the paved area can be represented with the symbol step or edge of paved area (529.1), if they serve navigation. + +Where a paved road, footpath or track goes through a non-urban area, the brown fill-in shall be drawn darker, so that if (x)% brown is used in urban areas, (x+20)% brown shall be used in the non-urban areas, and the line width of the black outline shall be increased from 0.07 to 0.14 mm. The black border line can be omitted where it is logical (e.g. indistinct/gradual gravel-to-grass transitions). + +Colour: brown 0 (white), 10%, 20% or 30% (urban) / 20%, 30%, 40%, 50% (non-urban) (min. 60 lines/cm), black; the colour and the line width shall be the same as for symbol unpaved footpath or track (506.1). + + + + + + + + A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. + + + + A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. + + + + A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. + + + + + + + + + + + + + + + + + + + + + + + + + A step or an edge of a paved area. Steps of a stairway shall be represented in a generalized manner. Edges within paved areas are generally not represented, unless they serve navigation. The thickness of edge of paved areas shall be enlarged to 0.14 mm in non-urban areas to improve legibility. The thickness of step lines shall always be 0.07 mm. + + + + + + + + + + + + + + + + + + + + + + + + + A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under. + + + + + + + + + + + + + + + + + + + + + + + + + + An impassable pipeline (gas, water, oil, etc.) above ground level which shall not be crossed, due to forbidden access or because it may constitute a danger to the competitor because of its height. + +<span style="color:magenta">It is forbidden to cross an impassable pipeline! +Competitors violating this rule will be disqualified.</span> + + + + + + + + + + + + + + + + + + + + + + + + + + A high tower or large pylon. Very large towers shall be represented in plan shape with the symbol building (526.1). The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An obvious small tower, platform or seat. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cairn, memorial, small monument or boundary stone more than 0.5 m high. Large massive monuments shall be represented in plan shape with the symbol building (526.1). + + + + + + + + + + + + + + + A fodder rack, which is free standing or attached to a tree. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A man-made feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. + + + + A man-made feature which is significant or prominent. The definition of the symbol shall always be given in the map legend. The symbol is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. + +<b>Note: this is a non-standard addition to the symbol set.</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Cut holes in the pattern to create these breaks. + + + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. + +<b>Note: this is a non-standard addition to the symbol set.</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing shall be 30 mm on the 1:5 000 map so they represent 150 m on the ground. + +North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. Cut holes in the pattern to create these breaks. + + + + + + At least three registration marks shall be placed within the frame of a map in a non-symmetrical arrangement. In addition, a colour check should be possible. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. + + + + Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. + + + + + + + The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point. + + + + + + + + + + + + + + + + + + + + + The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. + + + + The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. + + + + + + + Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. + + + + A marked route is shown on the map with a dashed line. + + + + The finish is shown by two concentric circles. + + + + + + + + + + + + + + + A boundary which it is not permitted to cross. Uncrossable boundaries shall be mapped by using the symbols: impassable cliff (201), impassable body of water (304.1), impassable marsh (309), impassable wall (521.1), impassable fence or railing (524) or impassable pipeline (534) and shall not be overprinted with symbol uncrossable boundary (707). This symbol is to be used only for last minute updates to the competition area, as excessive use of purple for indicating barriers is unfortunate. + +<span style="color:magenta">It is forbidden to cross an uncrossable boundary! +Competitors violating this rule will be disqualified.</span> + + + + A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards. + +If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with symbol crossing point (708) or crossing section (708.1). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Acrossing section through or over a building, wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map as a linear object, according to the plan shape. + +If underpasses or tunnels etc. are to be used in a competition, they shall be emphasized with symbol (708) or (708.1). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Out of bounds areas are mapped with the symbol area with forbidden access (528.1). This symbol shall only be used for last minute updates to the competition map (e.g. for areas that may be dangerous for the competitors during the competition, or very late changes to the competition terrain). +An out-of-bounds area is shown with vertical stripes. A bounding line may be drawn if there is no natural boundary, as follows: +- a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, +- a dashed line indicates intermittent marking on the ground, +- no line indicates no marking on the ground. + +<span style="color:magenta">It is forbidden to cross an out-of-bounds area! +Competitors violating this rule will be disqualified.</span> + + + + + + A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. + + + + A dashed line indicates intermittent marking on the ground. + + + + The location of a first aid post. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The location of a refreshment point which is not at a control or along the marked route. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Obvious temporary constructions like platforms for spectators and speaker, closed area for spectators, outside restaurant areas, etc. shall be represented in plan shape. + +<span style="color:magenta">It is forbidden to enter a temporary construction or closed area! +Competitors violating this rule will be disqualified.</span> + + + + This symbol provides a simple and quick way to make training courses. + +The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The OpenOrienteering Logo. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + N + + + + + + + + + + + + + + + + + N + + + + + + + + + + + + + + + + + N + + + + + + + + + + + + + + + + + + + + + Construction area, forbidden access! +Street / asphalt, with / without traffic +Big signs, playground items, etc. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Building; Canopy +Street / asphalt, with / without traffic +Private/flowerbed, forbidden access! +Meadow; Meadow with trees; Forest +Light; strong thicket +Thicket, impassable +Tree; Special tree +Path; Track; Stairs +Fence, forbidden to pass! +Fence, passable +Wall, forbidden to pass! +Small wall, passable +Hedge, forbidden to pass! +Light; strong undergrowth +Hedges, passable +Countours; Hill +Rough/semi-open area +Sandy ground +Cultivation boundary; Step +Stone; Rock face +Well; Small erosion gully + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Legend + + + + + + Special Signatures + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + May 2013 + + + + + + Thomas Schöps + + + + + + Orienteering map + + + + + + Standing: + + + + + + Garching + + + + + + Garching + + + + + + Cartography: + + + + + + 2.5 m + + + + + + 1 : 4,000 + + + + + + Scale: + + + + + + Contours: + + + + + + Organizer, host, mapper, +land owners and administration +do not assume any liability! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Munich SprintCup + + + + + + www.ol-gruenwald.de + + + + + + www.ol-landshut.de + + + + + + Landshut and Freising: + + + + + + Munich: + + + + + + Orienteering in ... + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/src/forest sample.xmap b/examples/src/forest sample.xmap new file mode 100644 index 0000000..22f5a54 --- /dev/null +++ b/examples/src/forest sample.xmap @@ -0,0 +1,20504 @@ + + + + + + + + + + PURPLE + + + + + + + BLACK + + + + + + + + + + + + + + + + + + + + + + + + + + + + BLUE + + + + + + + + + + + + + + BROWN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GREEN + + + + + + + + + + + + + + + + + + + + + + + + + + + + YELLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A line joining points of equal height. The standard vertical interval between contours is 5 metres. The smallest bend in a contour is 0.25 mm from centre to centre of the lines. + + + + Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with a normal contour line. + + + + An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. + + + + Slope lines may be drawn on the lower side of a contour line, e.g. along the line of a re-entrant or in a depression. They are used only where it is necessary to clarify the direction of slope. + + + + + + + + + + + + + + + + + + + Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. + + + + + + + A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The line width of very high earth banks may be 0.25 mm. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The line width of very high earth banks may be 0.25 mm. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use this symbol to display the full extent of wide earth banks. + + + + Distinct earth wall. Minimum height is 1 m. + + + + + + + + + + A small or partly ruined earth wall shall be shown with a dashed line. Minimum height is 0.5 m. + + + + + + + + + + An erosion gully or trench which is too small to be shown by symbol 106 is shown by a single line. The line width reflects the size of the gully. Minimum depth 1 m. The end of the line is pointed. + + + + A small erosion gully or trench. Minimum depth 0.5 m. + + + + + + + + + + A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (diameter of mound less than ca. 5 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. The symbol may not touch a contour line. + + + + A small obvious elongated knoll which cannot be drawn to scale with a contour (length less than 12 m and width less than 4 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this must be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols overlap. The symbol may not touch a contour line. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Small shallow natural depressions and hollows (minimum diameter 2 m) which cannot be shown to scale by contours are represented by a semicircle. Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol, which is orientated to north. Symbol 116 is used for man-made pits. + + + + + + + + + + + + + + + + + + + + + + + + Pits and holes with distinct steep sides which cannot be shown to scale by symbol 106 (minimum diameter 2 m). Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol which is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + An area of pits or knolls which is too intricate to be shown in detail. The density of randomly placed dots may vary according to the detail on the ground. + + + + The size of the dots may vary. + + + + This symbol can be used for a special small land form feature. The definition of the symbol must be given in the map legend. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). + + + + Use this symbol to display the full extent of a wide cliff. + + + + In the case of unusual features such as rock pillars or massive cliffs or gigantic boulders, the rocks shall be shown in plan shape without tags. + + + + A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. + + + + Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. + + + + + + + + + + + + + + + + + + + For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. + + + + For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + Rocky pits, holes or mineshafts which may constitute a danger to the runner. Location is the centre of gravity of the symbol, which is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + A cave is represented by the same symbol as a rocky pit. In this case the symbol should be orientated to point up the slope as indicated opposite. The centre of gravity of the symbol marks the opening. + + + + + + + + + + + + + + + + + + + + + + + + A small distinct boulder (minimum height 1 m). Every boulder marked on the map should be immediately identifiable on the ground. To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). + + + + To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). + + + + A particularly large and distinct boulder. For gigantic boulders symbol 202 should be used. + + + + An area which is covered with so many blocks of stone that they cannot be marked individually is shown with randomly orientated solid triangles with sides of ratio 8:6:5. A minimum of two triangles should be used. The going is indicated by the density of the triangles. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%. + + + + + + + + + + + + + + + + + + + + + To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%. + + + + + + + + + + + + + + + + + + + + + A small distinct group of boulders so closely clustered together that they cannot be marked individually. The symbol is an equilateral triangle orientated to the north. To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm). + + + + + + + + + + + + + + + + + + + + + To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm). + + + + + + + + + + + + + + + + + + + + + Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. + + + + Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. + + + + An area of soft sandy ground or gravel with no vegetation and where running is slow. Where an area of sandy ground is open but running is good, it is shown as open land (401/402). + + + + + + + + + + A runnable area of rock without earth or vegetation is shown as bare rock. An area of rock covered with grass, moss or other low vegetation is shown as open land (401/402). + + + + Large areas of water are shown with dot screen. Small areas of water should be shown with full colour. A black bank line indicates that the feature cannot be crossed. + + + + A black bank line indicates that the feature cannot be crossed. + + + + + + + + + + Where the lake or pond is smaller than 1 mm² on the printed map, the bank line is omitted. + + + + A water-filled pit or an area of water which is too small to be shown to scale. Location is the centre of gravity of the symbol, which is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + A crossable watercourse, minimum 2 m wide. The width of watercourses over 5 m wide should be shown to scale. + + + + A crossable watercourse (including a major drainage ditch) less than 2 m wide. For better legibility a ditch in a marsh should be drawn as a crossable watercourse (305). + + + + A natural or man-made minor water channel which may contain water only intermittently. + + + + A marsh or trickle of water which is too narrow to be shown with symbol 310 (less than ca. 5 m wide). + + + + + + + + + + A marsh which is uncrossable or dangerous for the runner. A black line surrounds the symbol. + + + + + + A black line surrounds the symbol. + + + + + + + + + + A crossable marsh, usually with a distinct edge. The symbol should be combined with vegetation symbols to show runnability and openness. Where a small marsh area should be combined with either 403/404 it is permitted to use 401/402 to improve legibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Wells and captive springs, which are clearly visible on the ground. + + + + The source of a stream with a distinct outflow. The symbol is orientated to open downstream. + + + + + + + + + + + + + + + + + + + + + + + + A special small water feature. The definition of the symbol must always be given in the map legend. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cultivated land, fields, meadows, grassland, etc. without trees, offering easy running. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. + + + + Meadows with scattered trees or bushes, with grass or similar ground cover offering easy running. Areas smaller than 10 mm at the maps scale are shown as open land (401). Individual trees may be added (418, 419, 420). If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. + + + + + + + + + + Heath, moorland, felled areas, newly planted areas (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, heather or tall grass. Symbol 403 may be combined with symbols 407 and 409 to show reduced runnability. + + + + Where there are scattered trees in rough open land, areas of white (or green) should appear in the tone. Such an area may be generalised by using a regular pattern of large white dots in the yellow screen. Areas smaller than 16 mm in the maps scale are shown as rough open land (403). Individual trees may be added (418, 419, 420). + + + + + + + + + + Typically open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. + + + + An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. + + + + An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 60-80% of normal speed. This symbol may not be combined with 406 or 408. + + + + + + An area with dense trees or thicket (low visibility) which reduce running to ca. 20-60% of normal speed. + + + + An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. + + + + + + An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced to ca. 0-20% of normal speed. + + + + Line of minimum width for symbol 410. + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. + + + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. + + + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. + + + + + + Land planted with fruit trees or bushes. The dot lines may be orientated to show the +direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. + + + + + + + + + + The green lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The boundary of cultivated land when not shown with other symbols (fence, wall, path, etc.) is shown with a black line. A permanent boundary between different types of cultivated land is also shown with this symbol. + + + + Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. + + + + + + + + + + A distinct forest edge or very distinct vegetation boundary within the forest. + + + + + + + + + + Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. + + + + Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. + + + + A road with two carriageways. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. + + + + + + + + + + + + + + + + + + + A road under construction may be shown with broken lines. + + + + + + + + + + + + + + + + + + + Road wider than 5m. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. + + + + + + + + A road under construction may be shown with broken lines. + + + + + + + + Road 3-5 m wide. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. + + + + + + + + A road under construction may be shown with broken lines. + + + + + + + + A maintained road suitable for motor vehicles in all weather. Width less than 3 m. + + + + A track or poorly maintained road suitable for vehicles only when travelling slowly. Width less than 3 m. + + + + A large path, or old vehicle track, which is distinct on the ground. + + + + A small path or (temporary) forest extraction track which can be followed at competition speed. + + + + A less distinct small path or forestry extraction track. + + + + A distinct ride, less than ca. 5 m wide. A ride is a linear break in the forest (usually plantation) which does not have a distinct path along it. Where there is a path along a ride, symbols 507 or 508 should be used in place of symbol 509. + + + + A footbridge with no path leading to it. +Note: if the stream is wider than 0.25mm, adjust this symbol so it extends 0.5mm over both sides of the stream! + + + + + + + + + + + + + + + + + + + A railway or other kind of railed track (tramway, truckway, etc.). + + + + + + + + + + + + + + + + + + + + + + + + + Power line, cableway or skilift. The bars indicate the exact location of the pylons. + + + + + + + + + + + + + + + + + + + + + + + + + Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline. + + + + + + + + + + + + + + + + + + + + + + + + + + + + A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it. + + + + + + + + + + + + + + + + + + + + A stone wall or stone-faced bank. + + + + + + + + + + A ruined stone wall may be shown by a dashed line. + + + + + + + + + + A stone wall higher than ca 1.5 m, not crossable to the average orienteer. + + + + + + + + + + A wooden or wire fence less than ca. 1.5 m high. + + + + + + + + + + + + + + + + + + + + + + + + + A ruined fence may be shown with a dashed line. + + + + + + + + + + + + + + + + + + + + + + + + + A boarded or wire fence higher than ca 1.5 m, not crossable to the average orienteer, eg. deer fence. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All ways through or over high fences or walls must be indicated. The symbol may also be used for a gate through or stile over a stone wall (519) or a fence (522) or a pipeline (534). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A building is shown with its ground plan so far as the scale permits. + + + + + + + + + + + + + + + + + + + + + + + + + Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. + + + + Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. + + + + + + Areas which are permanently forbidden to the runner are shown as out of bounds. The screen is superimposed on the normal map detail. A bounding line may be drawn if there is no natural boundary (see 709). + + + + + + A bounding line may be drawn if there is no natural boundary (see 709). + + + + An area of hard standing used for parking or other purposes. + + + + + + + + + + + + + The ground plan of a ruin is shown to scale, down to the minimum size shown opposite. Very small ruins may be drawn with a solid line. + + + + Very small ruins may be drawn with a solid line. + + + + Very small ruins may be drawn with a solid line. + + + + + + + + + + + + + + + + + + + + + + A firing range is shown with a special symbol to indicate the need for caution. Associated buildings are individually marked. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A distinct grave marked by a stone or shrine. Location is at the centre of gravity of the symbol, which is orientated to north. A cemetery is shown by using grave symbols as space permits. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A pipeline which cannot be crossed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A high tower or large pylon, standing above the level of the surrounding forest. Location is at the centre of gravity of the symbol. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An obvious shooting platform or seat, or small tower. Location is at the centre of gravity of the symbol. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cairn, memorial stone or boundary stone (or a trigonometric point in some countries) more than 0.5 m high. + + + + + + + + + + + + + + + A fodder rack which is free standing or built on to a tree. Location is at the centre of gravity of the symbol. For land access reasons these may be omitted. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. + + + + Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. + + + + + + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. + + + + + + + + + At least three registration marks must be placed within the frame of a map in a non-symmetrical position. In addition, a colour check should also be possible. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. + + + + Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. + + + + + + + The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point. + + + + + + + + + + + + + + + + + + + + + The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. + + + + The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. + + + + + + + Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. + + + + A marked route is shown on the map with a dashed line. + + + + The finish is shown by two concentric circles. + + + + + + + + + + + + + + + A boundary which it is not permitted to cross. + + + + A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An out-of-bounds area, see also symbol 528, is shown with vertical stripes. +A bounding line may be drawn if there is no natural boundary, as follows: +- a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, +- a dashed line indicates intermittent marking on the ground, +- no line indicates no marking on the ground. + + + + + + A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. + + + + A dashed line indicates intermittent marking on the ground. + + + + An area presenting danger to the competitor is shown with cross-hatched diagonal lines. + + + + + + + A route which is out-of-bounds is shown with crosses. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The location of a first aid post. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The location of a refreshment point which is not at a control. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This symbol provides a simple and quick way to make training courses. + +The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The OpenOrienteering Logo. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Forest map sample + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/src/overprinting.xmap b/examples/src/overprinting.xmap new file mode 100644 index 0000000..ee3182e --- /dev/null +++ b/examples/src/overprinting.xmap @@ -0,0 +1,7125 @@ + + + + + + + + + + PURPLE + + + + + + + BLACK + + + + + + + + + + + + + + + + + + + + + + + + + + + + BLUE + + + + + + + + + + + + + + BROWN + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GREEN + + + + + + + + + + + + + + + + + + + + + + + + + + + + YELLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A line joining points of equal height. The standard vertical interval between contours is 5 metres. The smallest bend in a contour is 0.25 mm from centre to centre of the lines. + + + + Every fifth contour shall be drawn with a thicker line. This is an aid to the quick assessment of height difference and the overall shape of the terrain surface. Where an index contour coincides with an area of much detail, it may be shown with a normal contour line. + + + + An intermediate contour line. Form lines are used where more information can be given about the shape of the ground. They are used only where representation is not possible with ordinary contours. Only one form line may be used between neighbouring contours. + + + + Slope lines may be drawn on the lower side of a contour line, e.g. along the line of a re-entrant or in a depression. They are used only where it is necessary to clarify the direction of slope. + + + + + + + + + + + + + + + + + + + Contour values may be included to aid assessment of large height differences. They are inserted in the index contours in positions where other detail is not obscured. The figures should be orientated so that the top of the figure is on the higher side of the contour. + + + + + + + A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A steep earth bank is an abrupt change in ground level which can be clearly distinguished from its surroundings, e.g. gravel or sand pits, road and railway cuttings or embankments. The tags should show the full extent of the slope, but may be omitted if two banks are close together. Impassable banks should be drawn with symbol 201 (impassable cliff). The line width of very high earth banks may be 0.25 mm. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The line width of very high earth banks may be 0.25 mm. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The line width of very high earth banks may be 0.25 mm. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use this symbol to display the full extent of wide earth banks. + + + + Distinct earth wall. Minimum height is 1 m. + + + + + + + + + + A small or partly ruined earth wall shall be shown with a dashed line. Minimum height is 0.5 m. + + + + + + + + + + An erosion gully or trench which is too small to be shown by symbol 106 is shown by a single line. The line width reflects the size of the gully. Minimum depth 1 m. The end of the line is pointed. + + + + A small erosion gully or trench. Minimum depth 0.5 m. + + + + + + + + + + A small obvious mound or rocky knoll which cannot be drawn to scale with a contour (diameter of mound less than ca. 5 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. The symbol may not touch a contour line. + + + + A small obvious elongated knoll which cannot be drawn to scale with a contour (length less than 12 m and width less than 4 m). The height of the knoll should be a minimum of 1 m from the surrounding ground. Knolls larger than this must be shown by contours. The symbol may not be drawn in free form or such that two elongated knoll symbols overlap. The symbol may not touch a contour line. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Small shallow natural depressions and hollows (minimum diameter 2 m) which cannot be shown to scale by contours are represented by a semicircle. Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol, which is orientated to north. Symbol 116 is used for man-made pits. + + + + + + + + + + + + + + + + + + + + + + + + Pits and holes with distinct steep sides which cannot be shown to scale by symbol 106 (minimum diameter 2 m). Minimum depth from the surrounding ground should be 1 m. Location is the centre of gravity of the symbol which is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + An area of pits or knolls which is too intricate to be shown in detail. The density of randomly placed dots may vary according to the detail on the ground. + + + + The size of the dots may vary. + + + + This symbol can be used for a special small land form feature. The definition of the symbol must be given in the map legend. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An impassable cliff, quarry or earth bank (see 106) is shown with a 0.35 mm line and downward tags showing its full extent from the top line to the foot. For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). The tags may extend over an area symbol representing detail immediately below the rock face. When a rock face drops straight into water making it impossible to pass under the cliff along the water's edge, the bank line is omitted or the tags should clearly extend over the bank line. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For vertical rock faces the tags may be omitted if space is short, e.g. narrow passages between cliffs (the passage should be drawn with a width of at least 0.3 mm). + + + + Use this symbol to display the full extent of a wide cliff. + + + + In the case of unusual features such as rock pillars or massive cliffs or gigantic boulders, the rocks shall be shown in plan shape without tags. + + + + A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A small vertical rock face (minimum height 1 m) may be shown without tags. If the direction of fall of the rock face is not apparent from the contours or to improve legibility, short tags should be drawn in the direction of the fall. For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. + + + + Should be used if the direction of fall of the rock face is apparent from the contours and the legibility is good. + + + + + + + + + + + + + + + + + + + For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. + + + + For passable rock faces shown without tags the ends of the line may be rounded to improve legibility. + + + + + + + + + + + + + + + + + + + Rocky pits, holes or mineshafts which may constitute a danger to the runner. Location is the centre of gravity of the symbol, which is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + A cave is represented by the same symbol as a rocky pit. In this case the symbol should be orientated to point up the slope as indicated opposite. The centre of gravity of the symbol marks the opening. + + + + + + + + + + + + + + + + + + + + + + + + A small distinct boulder (minimum height 1 m). Every boulder marked on the map should be immediately identifiable on the ground. To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). + + + + To be able to show the distinction between boulders with significant difference in size it is permitted to enlarge this symbol by 20% (diameter 0.5 mm). + + + + A particularly large and distinct boulder. For gigantic boulders symbol 202 should be used. + + + + An area which is covered with so many blocks of stone that they cannot be marked individually is shown with randomly orientated solid triangles with sides of ratio 8:6:5. A minimum of two triangles should be used. The going is indicated by the density of the triangles. To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%. + + + + + + + + + + + + + + + + + + + + + To be able to show the distinction between boulder fields with a significant difference in boulder size it is permitted to enlarge the triangles by 20%. + + + + + + + + + + + + + + + + + + + + + A small distinct group of boulders so closely clustered together that they cannot be marked individually. The symbol is an equilateral triangle orientated to the north. To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm). + + + + + + + + + + + + + + + + + + + + + To be able to show the distinction between boulder clusters with significant difference in size it is permitted to enlarge this symbol by 25% (1.0 mm). + + + + + + + + + + + + + + + + + + + + + Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. + + + + Stony or rocky ground which affects going should be shown on the map. The dots should be randomly distributed with density according to the amount of rock. A minimum of three dots should be used. + + + + An area of soft sandy ground or gravel with no vegetation and where running is slow. Where an area of sandy ground is open but running is good, it is shown as open land (401/402). + + + + + + + + + + A runnable area of rock without earth or vegetation is shown as bare rock. An area of rock covered with grass, moss or other low vegetation is shown as open land (401/402). + + + + Large areas of water are shown with dot screen. Small areas of water should be shown with full colour. A black bank line indicates that the feature cannot be crossed. + + + + A black bank line indicates that the feature cannot be crossed. + + + + + + + + + + Where the lake or pond is smaller than 1 mm² on the printed map, the bank line is omitted. + + + + A water-filled pit or an area of water which is too small to be shown to scale. Location is the centre of gravity of the symbol, which is orientated to north. + + + + + + + + + + + + + + + + + + + + + + + + A crossable watercourse, minimum 2 m wide. The width of watercourses over 5 m wide should be shown to scale. + + + + A crossable watercourse (including a major drainage ditch) less than 2 m wide. For better legibility a ditch in a marsh should be drawn as a crossable watercourse (305). + + + + A natural or man-made minor water channel which may contain water only intermittently. + + + + A marsh or trickle of water which is too narrow to be shown with symbol 310 (less than ca. 5 m wide). + + + + + + + + + + A marsh which is uncrossable or dangerous for the runner. A black line surrounds the symbol. + + + + + + A black line surrounds the symbol. + + + + + + + + + + A crossable marsh, usually with a distinct edge. The symbol should be combined with vegetation symbols to show runnability and openness. Where a small marsh area should be combined with either 403/404 it is permitted to use 401/402 to improve legibility. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An indistinct or seasonal marsh or area of gradual transition from marsh to firm ground, which is crossable. The edge is generally indistinct and the vegetation similar to that of the surrounding ground. The symbol should be combined with vegetation symbols to show runnability and openness. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Wells and captive springs, which are clearly visible on the ground. + + + + The source of a stream with a distinct outflow. The symbol is orientated to open downstream. + + + + + + + + + + + + + + + + + + + + + + + + A special small water feature. The definition of the symbol must always be given in the map legend. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cultivated land, fields, meadows, grassland, etc. without trees, offering easy running. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. + + + + Meadows with scattered trees or bushes, with grass or similar ground cover offering easy running. Areas smaller than 10 mm at the maps scale are shown as open land (401). Individual trees may be added (418, 419, 420). If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. + + + + + + + + + + Heath, moorland, felled areas, newly planted areas (trees lower than ca. 1 m) or other generally open land with rough ground vegetation, heather or tall grass. Symbol 403 may be combined with symbols 407 and 409 to show reduced runnability. + + + + Where there are scattered trees in rough open land, areas of white (or green) should appear in the tone. Such an area may be generalised by using a regular pattern of large white dots in the yellow screen. Areas smaller than 16 mm in the maps scale are shown as rough open land (403). Individual trees may be added (418, 419, 420). + + + + + + + + + + Typically open runnable forest for the particular type of terrain. If no part of the forest is runnable then no white should appear on the map. + + + + An area with dense trees (low visibility) which reduces running to ca. 60-80% of normal speed. + + + + An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 60-80% of normal speed. This symbol may not be combined with 406 or 408. + + + + + + An area with dense trees or thicket (low visibility) which reduce running to ca. 20-60% of normal speed. + + + + An area of dense undergrowth but otherwise good visibility (brambles, heather, low bushes, and including cut branches) which reduces running to ca. 20-60% of normal speed. This symbol may not be combined with 406 or 408. + + + + + + An area of dense vegetation (trees or undergrowth) which is barely passable. Running reduced to ca. 0-20% of normal speed. + + + + Line of minimum width for symbol 410. + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. + + + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. + + + + + + When an area of forest provides good running in one direction but less good in others, white stripes are left in the screen symbol to show the direction of good running. + + + + + + Land planted with fruit trees or bushes. The dot lines may be orientated to show the +direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. + + + + + + + + + + The green lines may be orientated to show the direction of planting. If yellow coloured areas becomes dominant, a screen (75%) instead of full yellow may be used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The boundary of cultivated land when not shown with other symbols (fence, wall, path, etc.) is shown with a black line. A permanent boundary between different types of cultivated land is also shown with this symbol. + + + + Cultivated land which is seasonally out-of-bounds due to growing crops may be shown with a black dot screen. + + + + + + + + + + A distinct forest edge or very distinct vegetation boundary within the forest. + + + + + + + + + + Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. + + + + Symbols 418, 419 and 420 can be used for special small vegetation features. The definition of the symbol must be given in each case in the map legend. + + + + A road with two carriageways. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. + + + + + + + + + + + + + + + + + + + A road under construction may be shown with broken lines. + + + + + + + + + + + + + + + + + + + Road wider than 5m. The width of the symbol should be drawn to scale but not smaller than the minimum width. The outer boundary lines may be replaced with symbols 519, 521, 522 or 524 if a fence or wall is so close to the motorway edge that it cannot practically be shown as a separate symbol. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. + + + + + + + + A road under construction may be shown with broken lines. + + + + + + + + Road 3-5 m wide. The space between the black lines must be filled with brown (50%). A road under construction may be shown with broken lines. + + + + + + + + A road under construction may be shown with broken lines. + + + + + + + + A maintained road suitable for motor vehicles in all weather. Width less than 3 m. + + + + A track or poorly maintained road suitable for vehicles only when travelling slowly. Width less than 3 m. + + + + A large path, or old vehicle track, which is distinct on the ground. + + + + A small path or (temporary) forest extraction track which can be followed at competition speed. + + + + A less distinct small path or forestry extraction track. + + + + A distinct ride, less than ca. 5 m wide. A ride is a linear break in the forest (usually plantation) which does not have a distinct path along it. Where there is a path along a ride, symbols 507 or 508 should be used in place of symbol 509. + + + + A footbridge with no path leading to it. +Note: if the stream is wider than 0.25mm, adjust this symbol so it extends 0.5mm over both sides of the stream! + + + + + + + + + + + + + + + + + + + A railway or other kind of railed track (tramway, truckway, etc.). + + + + + + + + + + + + + + + + + + + + + + + + + Power line, cableway or skilift. The bars indicate the exact location of the pylons. + + + + + + + + + + + + + + + + + + + + + + + + + Major power lines should be drawn with a double line. The gap between the lines may indicate the extent of the powerline. + + + + + + + + + + + + + + + + + + + + + + + + + + + + A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A way under roads, railways, etc. which may be used by the runner. This symbol is used whether or not the tunnel has a track leading to it. + + + + + + + + + + + + + + + + + + + + A stone wall or stone-faced bank. + + + + + + + + + + A ruined stone wall may be shown by a dashed line. + + + + + + + + + + A stone wall higher than ca 1.5 m, not crossable to the average orienteer. + + + + + + + + + + A wooden or wire fence less than ca. 1.5 m high. + + + + + + + + + + + + + + + + + + + + + + + + + A ruined fence may be shown with a dashed line. + + + + + + + + + + + + + + + + + + + + + + + + + A boarded or wire fence higher than ca 1.5 m, not crossable to the average orienteer, eg. deer fence. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All ways through or over high fences or walls must be indicated. The symbol may also be used for a gate through or stile over a stone wall (519) or a fence (522) or a pipeline (534). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A building is shown with its ground plan so far as the scale permits. + + + + + + + + + + + + + + + + + + + + + + + + + Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. + + + + Houses and gardens and other built up areas. Roads, buildings and other significant features within a settlement must be shown. If all buildings cannot be shown, an alternative symbol (black line screen) may be used. + + + + + + Areas which are permanently forbidden to the runner are shown as out of bounds. The screen is superimposed on the normal map detail. A bounding line may be drawn if there is no natural boundary (see 709). + + + + + + A bounding line may be drawn if there is no natural boundary (see 709). + + + + An area of hard standing used for parking or other purposes. + + + + + + + + + + + + + The ground plan of a ruin is shown to scale, down to the minimum size shown opposite. Very small ruins may be drawn with a solid line. + + + + Very small ruins may be drawn with a solid line. + + + + Very small ruins may be drawn with a solid line. + + + + + + + + + + + + + + + + + + + + + + A firing range is shown with a special symbol to indicate the need for caution. Associated buildings are individually marked. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A distinct grave marked by a stone or shrine. Location is at the centre of gravity of the symbol, which is orientated to north. A cemetery is shown by using grave symbols as space permits. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A pipeline (gas, water, oil, etc.) above ground level which can be crossed over or under. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A pipeline which cannot be crossed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A high tower or large pylon, standing above the level of the surrounding forest. Location is at the centre of gravity of the symbol. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An obvious shooting platform or seat, or small tower. Location is at the centre of gravity of the symbol. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cairn, memorial stone or boundary stone (or a trigonometric point in some countries) more than 0.5 m high. + + + + + + + + + + + + + + + A fodder rack which is free standing or built on to a tree. Location is at the centre of gravity of the symbol. For land access reasons these may be omitted. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. + + + + Special man-made features are shown with these symbols. The definition of the symbols must be given in each case in the map legend. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. + + + + + + + + + Magnetic north lines are lines placed on the map pointing to magnetic north. Their spacing on the map should be 33.33 mm which represents 500 m on the ground at the scale of 1:15 000. For maps with other scales lines placing should be at intervals which represents a round number of meters (e.g. 50 m, 100 m, 250 m, 500 m) and the spacing should be between 20 mm and 40 mm on the map. North lines may be broken where they obscure small features such as boulders, knolls, cliffs, stream junctions, path ends, etc. In areas with very few water features, blue lines may be used. + + + + + + + + + At least three registration marks must be placed within the frame of a map in a non-symmetrical position. In addition, a colour check should also be possible. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. + + + + Spot heights are used for the rough assessment of height differences. The height is given to the nearest metre. The figures are orientated to the north. Water levels are given without the dot. + + + + + + + The start or map issue point (if not at the start) is shown by an equilateral triangle which points in the direction of the first control. The centre of the triangle shows the precise position of the start point. + + + + + + + + + + + + + + + + + + + + + The control points are shown with circles. The centre of the circle shows the precise position of the feature. Sections of circles should be omitted to leave important detail showing. + + + + The number of the control is placed close to the control point circle in such a way that it does not obscure important detail. The numbers are orientated to north. + + + + + + + Where controls are to be visited in order, the start, control points and finish are joined together by straight lines. Sections of lines should be omitted to leave important detail showing. + + + + A marked route is shown on the map with a dashed line. + + + + The finish is shown by two concentric circles. + + + + + + + + + + + + + + + A boundary which it is not permitted to cross. + + + + A crossing point through or over a wall or fence, or across a road or railway or through a tunnel or an out-of-bounds area is drawn on the map with two lines curving outwards. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An out-of-bounds area, see also symbol 528, is shown with vertical stripes. +A bounding line may be drawn if there is no natural boundary, as follows: +- a solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground, +- a dashed line indicates intermittent marking on the ground, +- no line indicates no marking on the ground. + + + + + + A solid line indicates that the boundary is marked continuously (tapes, etc.) on the ground. + + + + A dashed line indicates intermittent marking on the ground. + + + + An area presenting danger to the competitor is shown with cross-hatched diagonal lines. + + + + + + + A route which is out-of-bounds is shown with crosses. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The location of a first aid post. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The location of a refreshment point which is not at a control. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This symbol provides a simple and quick way to make training courses. + +The purple line will extend a bit into the finish symbol. This is a shortcoming of this simple approach. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The OpenOrienteering Logo. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Click menu View > Overprinting simulation +or press F4 to toggle +overprinting preview. + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/about.png b/images/about.png new file mode 100644 index 0000000000000000000000000000000000000000..749c825e0743a3d6341572de182ba101d64e2ae0 GIT binary patch literal 1901 zcmV-z2a@=SP);LPjufD2=|8-OE7BK$+UZa)$s?zGqO6geu5xXE(epV?3 zPt2bp`mX_eaj1ipqrK3K`0! zB5BXV4+4y}^uiv!PM7hq;bL0(k3Rq1k;G+KTz{?LTZh`@R5-mDyrG z3(~`JG^tdIY&Js}MQE*1N+Tv_rjjr>S~{?2-=Y2a@?PbnT#~R;>DE{8t!-Ss>-6{3 zuh(Czf9b(fm>UAo?OUixx&=kr_qQK6Cp_tjiFE( zSGn?@{Kn_|Vo<2DmBhjWhWJ$U*%kaD7 zCarddf*UYCeF#j9PGxa&W4OgBQiUo`b_DH}L8Y)^gOTxlW#u^EzafAH-Fs@Qo5hIPb5h>5@^h#0Ji z@KPSuQhZMUCr@S$YF9t~k)wx8LYqr*gdaiU@+vB_AYfe0Y}vzvZM2QCVH2ZMND?5{ zV8bRh?jUi1LNG~)QncO+fD+IA?$+B56^yOX@i!=xg)lZ`4pdk?zlBMJVkN^Sn(6vF zg;E6*Y$G;CIcX4!jeTsdfeoAJbcvu-6R~>NdTwnf|pG=msqOnm$)1xUH- zTT@fxrCR+w)`HRkR-rX$oiH@40EGy=xc*6WE+U;?VCKLq64o*P4q{@IcF>s$Dl^2z z#W#6!>45i(jeBO`(U-3UxN0w$J)C`fYHE*HZ>|wX;Jnq^7qtS`D$cInkFQSP?!O1? zuaIs26*pbLEso>n$FXh!zw5KSw7{A6&78Im=vE;2mOpUka%167XRn&`ci!B6Uokm) z?2xnY_p@ZO3fBdtL8-o@;8No__2fPd&CRj09TJ9JJ~p<@EzuvC&0Ij40IM@B^(Ogq zaGWdpFxDwi@HDTSTWlv*PA>ia+b`}m1*s$sw?w>&ByoWRV?f!r#Uoo}xp^l?jvQin zE1;JMI+f>&dxV#+jPl6+`*Bqaz|VjAH;SVZbmBCfMi*7dFniNv@%8h|r+DC32RZei zfvXEqDhSMPE-bD@d#n3U%E1^&3?vDd1eSL0pgK88vl|hFF$jnVE88I|Q)SzZapcGm z#%Jc}rl;BRUHoqUz_d~CkjrE!7IVV~dP@)eQ}mqx$+~XoyC;hls@{Az~9uoM24EQ)f3(O7YUd4k$3jAY%KmR@iR&>>aBV zu=2ItrXXpx*j!!VA+0X_bZvdTIxXpSBn+?BTki^0OLOTv#RHNR=qZCX3 z3IITW_Q2I3E+xJ8!Wfb4oI{=fYL2^FR5qxf$z00000NkvXXu0mjf)s2>+ literal 0 HcmV?d00001 diff --git a/images/arrow-down.png b/images/arrow-down.png new file mode 100644 index 0000000000000000000000000000000000000000..dce3f15ef5739854e5fcf3d8f03f8da077d2617e GIT binary patch literal 1187 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 literal 0 HcmV?d00001 diff --git a/images/arrow-thin-upleft.png b/images/arrow-thin-upleft.png new file mode 100644 index 0000000000000000000000000000000000000000..63c81ae4182acaa3da1d9b39fbf1226d6dbe1033 GIT binary patch literal 441 zcmV;q0Y?6bP)!BkcUmV*8S!T;kQxi*l_-Zq7p#@5=x_6qR~#70z7 zBu<(UmgDZc+nw3Gu&^_`!#>~4?rg$WOfkV{`{f}%V3BiP8IZtZ9GCV>{Qb|E8@$6g zei@i3$G_a-8h>_-`G#fA`J)O6{J=@6>SmqatFxV{Alh>Kqdh+b)cSh|Vr}FNp38Ebjvm z_>TA60Id;$H)U^M?z*uRA`bbfv_Qn618O6pkGduz`mS9qL=1U@Yan9icRUmkHFmQh zh$?^~h-!eoh^l}-i0Xht5lsMxAesU8MKlHMKul6fGrVmM*noI|S9pf!qX5_ j%tixr5T}^q5q{qRb?f~)r9}Q-00000NkvXXu0mjfx+b|w literal 0 HcmV?d00001 diff --git a/images/arrow-up.png b/images/arrow-up.png new file mode 100644 index 0000000000000000000000000000000000000000..afb307b18c130d44b130c57f64075744d703b791 GIT binary patch literal 1193 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 literal 0 HcmV?d00001 diff --git a/images/colors.png b/images/colors.png new file mode 100644 index 0000000000000000000000000000000000000000..862df465c16a660184cf3501b0c28bea32161019 GIT binary patch literal 267 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&kwQkx@X1e`;VjNJus_B%;JQKQ}iuuLQ_tU~n!iAf%++mGr9^TnhK3Vw^eYdsJ!4g zy1c@CBDcHx(TTpE1#Zj?lje&|`mufAUC)g(-koDO}i-8z_U+@fH{M_% zo#s}5KhslFc$HGsfq;sJ!>YS|g(Q#jk(Ep<~BL?%Y9YWCRgW z&onlAHztz`=v-2PmQd);)@1TWYgeprD__5R_X6Nj;1GmNcQ<-yh-%BiTU`zH^#tN^ zL~B6MB5d0F0)xZDqddO zX4NW+Lqll8;9h!|Wn0_rH5GSjdpm=}!}Rs`QWpy0H%;!O)7u>@JeAT1{eEWid9sDV z5|!)ntu-P-I2L2-$`v9jSE6;X^qru!t$^0HT0i^cuNcecna$@3`u$W|;btHak82T8 z_a-LT_2P>QB-dIa#ZvVpfPoJ_AU`~eH9n5d>*el8AC-U|l9hxs?HwJAOiUmmDiM!s zHv^9M4Rv+KY(CGxg9rTK@Zpk=fJmuw(Aa-xu3yKO%i)`tWN!2x-f)HQ#~+I! zqS4w8tE1L)?d_a-?>(Y{070M6>tw+LbS7dkC4f`C*da%>Mr&=$St0%4r=PN_rG@Fi z0r305FbD?x44yx4uh%TFN=kOLb?mZL01~m70y;g|Gqqq9i;^uAcS6F!agCrJxpF^6sZ-_U+%#=sH2Z-YnTAWgANGwD+uqdywa01)L<~O6cAq zj2iWP82vE?@srEtm^6drayhJ`kAK#8a?hwoYi#10a(+akwLGMNu>cvAt_Y4Jo7)Xq zv%V?9#j6+idFL)Z`R~WTJX#ED5*^IU%(Jekwwxav(6W6MbM0XTDnZNgIo;s+sxgfs zD_1vjSHDau|3hxizRZqo8@YWu<;rbahT8$<=jGuHh)9*sFjTCnioYH?vfzPo7K*Mt z-`mm17nkqw+}b*=+G!0Di+zB2wiL=o-#uivqGd z8s)3cK6B^2OT;$QPO%m`+LkjnH^+Pt<}6s92y&x$h@G1o+$tRfbcLG#yLgdAG>U)> zXR}41%hQv~9qdk}?#x+MA?WuLt*YX0fBsX&5Yg@ni$$Bv`ldQAT>h41V>NRYtX&o4 zi%Z|q+*AVzWV%gyAoZN z-67<(M)tyRNGx3BX&q}JyW zky591ojAehwQH~6u4N9rme4cbR%gFS!yY_;9h3)Dz0Lw!mR>Wcid_EA7 zsp)Al_wN_GQ>m-Ke-?v3$GgU9BsV*2jZREd zJ*w)bJ)9l_UMd|ksV5TzMSpM!z~S}bA&du?Pkw;xQ@|uN3?_PJE03r zv)Q*iTE=L*Bho=XPspH&pS|aiGox5Vd4uRZ(S%NO_K3&6jB*vF4dQX44?6SNDju(9 z4NDFJ=L;OXk;vd$@? F2>_|eakKyc literal 0 HcmV?d00001 diff --git a/images/copy-coords.png b/images/copy-coords.png new file mode 100644 index 0000000000000000000000000000000000000000..05e3deadfe93381fb212c15ca92ecafde4b7fc6a GIT binary patch literal 1306 zcmV+#1?BpQP)Uka zNTpIOKA(S8Et{OwR6BA)WoKbwp+>+k3@#Rn$(p7)lgXq&DJ6=cR+UxZiPN$RB zEGh`L6&04YVp1L+9#*p1EC&Fpst&~C@sO_TqOR*oBocA=^z_X6{r(Kcakqq2T?L}i zs2mD~)-E+UpU-pQaM(i#VSzxvV2s@w2{kSHme1!eyTC9EZfIyo&FAw)vpD*=rbX4& z#IuA_dk4hQ(YHmn&!0nuj{&n8y4{8vD1h| zB{VASARtKIxQvdr44yx*4YDkmAtwZiqAbmUWHM=s#^0YhhfriaWXDFVsbI^RwL#3J zfVV%MMt5Ke2cFtmcMa}VA^h&-S&V7zkR6`7G7YjD+@iY+Ec*kbp8^L$3rtSGa;+c`uyu((I6}4gq6|J zK*Nw}7+GV^G$uRO;-jOdVHKs8zHV)8&5n(Y)wL)Rl}tW2(zAGCH$i{zE&#yDg-LvM zViIn}cM~-}Sa!pb2r$Ora=8lY)~(CcQ^2isnETQM`g=E&`TY_0tzw*6-8;?qx|>IoQ^2%y)~Nyz_a_>!6>b917`()Nr>Y(baZs2su$Z3Xu;gtS2g@7NkN9Y&Teu&V?i1cVR(1p6Kiz{u!zWebHI9=g{9NfH47JkLWEMQak;MWC|X z+!KI{AFnI#A|u#-PbWBzgD8sST6ujlA;58*WvwJ;Wd(r42L~`Wd!ZKk*$7@bxCMeB zKomttk_1VTAc`WUrl-KbZsq}(%XRh2mA{vK7nF!oje^J1i07Vc!#6)%hr_kL4E-~E znxU*|#Ajc83P~znN+eN2E}w%#c9gTJP|SMj(WN$j?}K;Vq?FcdUTTBwx1lR=4ioXB z0$tt-^l#o;zMk*exf`YVhGF3Mk@MJn--E|q!yEQ5B(x%lRJx$l?%%rwt?m(UxeyNQ z-(H5B=Xp>{kw_$vNF*?NVFaAO-RSG>JGAV(z>W(_?B;nMM_wE(fAc&KK@dPGMLM0v z{QNw$SQMfpBo4pu@(w~sdPNEp3I#YE4wLnzkgO&x&+}l6A(bi~sDDjd#`MfI8|b_H z=fRzWdkG=B>0Pi~IeF^%SmffL-7CE}vPG`9G`C*ezwhZojg5^z;{R6u1(ozYNh!Vo Q@c;k-07*qoM6N<$f&gZ7Bme*a literal 0 HcmV?d00001 diff --git a/images/copy.png b/images/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..3348ee08fd8fcefa1b65a454e1ca6126267216f4 GIT binary patch literal 723 zcmV;^0xbQBP)LE$lq)T3AJjC7XEIa$68}xyYnVs4B|K|TZ zv)KUuI0yi?wwf26;b<|-g-z4=S+6zj#?8|Kz!{DfuQaY^sgI53`a+Um|ym8$`fH^ZGT_xM3LY>$S$|P=HpeCG`(` zy@kuG*%s;Mjr9tkaA0X^N$S7cc>y4sA|)ae3I(tKe->dd1zCjR6?5$Q2Yqpj0YB*JVj%3Zl+*(4i>dAVjPKoO6`RW$D_^?slwToN(B9C_oShT3-T4BA!GrZhjL7sz&RY)eR6 u7G6`dK2#+;?SSGVfrkuyA9@%VPTkaqQ{XxMqp1vNGJ~h9pUXO@geCx)2rPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyW2 z3jr^8xGqWn00XW`L_t(o!^M?PNF!$$#-AB==EG#2gw1G)`A0^wB_%o_nC8$zPf}VM z3WY)`fzmBS=wZQ&hg|lOlY*6^^rqBT-!nULJXj*cXCGLy#X zC0UHb)kKqhPBS0x$Nc7fzUTdb=49ZWMFW6g7_ixFcx^Ha1Bpc9Hg!hZ01U&x*x1IYbmrb`F1J(tl) z+W@ZnU?&C<)vQ}r_sq@Bp{J(@6B844g|S!+R;v}~=jWK5oNWCN-jo(W5D*H5Fh4(E zw{A9@-v#iK#bUV%W?5KR*qxb~dC)XKBgwK1j^hvr1Q3Zt006VIvk(N~aVQk}Skttx zFV{Pr&ZY73@wY4%%QsB}T$*q%H9b9zOeTZD!9j$>VThuL(a}-Jvivv4ag{(I5Q{`2 z?kZSB6vdAUg~Gq8s{YwhVjC?+qfw;OY3%OqVtIKP`}_MC9v&v^>+63>l4Pu|uKp;B zA|H>(U*z+7V`*t=rM9=LNr0z~7B@FH5ekLia=E}T3;>|7ukXK1CL{TLzRzqn+Z$f5 z_kBSS#B4TOT3lRw=iuN#2Y~MTic_&z#P;?!dV71Zx3`CAGztK}_s%R83M?x%{P z{7@>D;ujYe102VFq3ilHnx^9>lPT763QkW?K?s4OD5z8_kR%BJpsMNxA*AGVI=|I* z-KQwZZ>d!3IeGK77{>Ge0W8SI&0sjj9{>OV07*qoM6N<$f-$4&z5oCK literal 0 HcmV?d00001 diff --git a/images/cursor-delete.png b/images/cursor-delete.png new file mode 100644 index 0000000000000000000000000000000000000000..9f6f44a39eb22b60135a84eb52cfdc72cc30c5e1 GIT binary patch literal 814 zcmV+}1JV46P)Px%=t)FDR9M69mCH+1VHk&h?|05zoQpHr#F&Wpyfi43WeJI(=!gVC(4y-m+C(l| z20`SX5K)U(27!=-EediGZ4{28gqf?AW&zw1zuSJbyUJ@H;HvM*o55D)| zectnaz|U}GDGUJToWtkyWetLJ&bcH>JX@XMsWdb+WDfuUqR}YNhKdz{Y^hiQNPT2l z#VUYos8|D#X%%Y#GOc1=fJ~~`5Fk@3HU!9&icJ9kU?;o4wn8^EGn2U)kR)m4ZIC1> z)&8AT(A(R)UcroR#BCLvZ*47pT3hP@K!2WvDl2!rvRd=DIs*iNhKGk~D}@(3I_O(R zN2>@7mbqN_I-SnyRr{m8z0j7Q-_N|>`xk+N&F&Jm)2^$frFkMzKM6vK({2w0a&rUY zq0rbDAhHk)`UIjLmQ1$f7)IqjRjsgU+OrWLkv4!sUa#p8gd(RXHVQ<@v)hXs{r;N6 z4o7^y$J5P#AeNShR237+zy>s<+K6x&CMB+WOB03?GAV>mNJ2e@~qa9Dv#%+ z$XGS$I@6YyF&dAL-HOF7Jkhk-v?>6Ace4ClHjFp*9?ub%AnavCBoGM%kglqHBpMyP z^6AsLVO^j2r$w|`9)vI00~9G-EjEfd()g~ve_u@Lzqe=81&^^t=Ac2 zMFfBm!AvB;;&Cd~_1zP){C3DNKJN&?*S0p76}ZiaI*Eu3&iRroa~6*SvP?cz%|B|I zfrPG)jRTR5w&}J2=9`->7E!!LL|w!=>6*re!r`$S)6+c_hT-$6>K-Txc@?FwnDc|P z!1IqlY`we0?EoyRs@oxoXDysVS5*uyER0;5nz|e|%~7T(k8j#+cM24xUNcQ7K#l`2 z(z>@#`2E78s;bi;>grdnh?DiS}IOcE=0CY1J7y(`n05ivdrX6`aTtv?EZgPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyb& z4jUgu*2`l600JdRL_t(o!|j%_i{d~O#^2l?!4MD^TljL3DgUg#JPU zul5i^bk6x_P18OV3WW~|a{0Das})62g!Ot2 z005FCK^(_m+xC1qoqh>|;IZ9qKLY@~Ppg4FkH0`w)eDUAeYILWTPzlv*=)wP+wE5X zK!PCXilX3pyy*t6h%xFMbI?uGBYa;27`_3x_mesKK-e7I2=B?uFD65!RFsf!X&~lbRRZqF{{iRZ<cztX8YrTCG;BR4V7|_4<52ibtc-k0^e#FH_pb=-_+<>TW3N1a8bg00000 LNkvXXu0mjfDjFzW literal 0 HcmV?d00001 diff --git a/images/cursor-draw-path.png b/images/cursor-draw-path.png new file mode 100644 index 0000000000000000000000000000000000000000..efb5382a91590475f8dc2790836829b80424f685 GIT binary patch literal 622 zcmV-!0+IcRP)Px%C`m*?R9M69RXvLuQ53yzTt~-27!^~PbOB>wKmvh*R7tT|m^ATEC^m~#>JUgF zuAm!F_Pup1Ee z3h{P!NXb&yM0FAYWrft%jHYp;!B$3J%i#~|7}ffFmbDi(_m zP1AfI3Vm6!2o*y(^N~My#Ua!r5zmJQ>0$Z&X7K_D;gC(J^>yIjx z%6FdUK~WU&`Fs$KMqlRh`RqXmB$LS>Hk<8!xm<1n08mQ7@ApF}6grb6>E1!dkeYkKF8zn$C{=A%d+5hyJ0q)fvT#+ZntkOA3-1x;5m*1(==hT*}!JA0guOnwOUQ( zdHz}^*sLHRisA!G=`+JHKv5LvbUL)rXjBcuc%DckeqOEp1&PE4j*01PS^xk507*qo IM6N<$f>?Hq)$ literal 0 HcmV?d00001 diff --git a/images/cursor-draw-rectangle.png b/images/cursor-draw-rectangle.png new file mode 100644 index 0000000000000000000000000000000000000000..bcda7bc92b1d99284744c4f27d740c2dd5a00035 GIT binary patch literal 398 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Y)RhkE)4%c zaKYZ?lYt_f1s;*b3=G`DAk4@xYmNj^kiEpy*OmPa3oD0oGXKBFtk0enx%r@g;JdZ^v)9jCRB+zzLemP? zz*5eTwaw2e)slIHE*4+nW;|yZwp8wi;p1?FV+r@aKiO;Vt$Dba=fRXo?{>Z8WpFWN z?cmt+{dwjryQx9yE?1;|TO=MAO0@ASS&0bVcxC78B{?N%d7;dHxvP=cj$S|ii3c}C z&%H15@{h|>mV$E23mk9y7>tW67HK?*2sh35*L*4Q_+k|Ie%3%Q&v4n*sgXNOx~`Y5 m6+e2;l8wR9rG3)*7fdsx+iJ@V1^xpAi^0>?&t;ucLK6U{Jd^zZ literal 0 HcmV?d00001 diff --git a/images/cursor-draw-text.png b/images/cursor-draw-text.png new file mode 100644 index 0000000000000000000000000000000000000000..49d770dac692f5d60cd88faec423b5ccfcd7217b GIT binary patch literal 676 zcmV;V0$crwP)Px%UP(kjR9M69mOo1zK^VrLnZ5V+Zr#PCP((q9AXrFJosA-AktV^Ae1qgW*gIb# z1T11>AR#G}CLE->R4M!vDGu>gJa_N*c5im(-4xMqftX-CH5eb5VqpF}?=wGM1~{7v zj93y^tTP!XiUQXz0o*Wf!@z&fz?Dm!#Cigl5CXrNAFuuCDbBd(dg!JRUB5;o{(^x& z?ompK_4W0N5JE6Bj4?%{(HJPD_)-#>oSdu+AzAe*u3ImP!teL{2*dDI7>2L2ECT@F^Sm{sRP{UtejCBk z($c)Oc13HAFbtW9-a3x+=HTD}%nSf4K@iMeSOVp8`5rSbq-lycj^7I*ZV}OVuh;vS z=Q)^p!FAoc=jj8dYI}S8_RP%8j^}xgy4@~;qdVA+2-bE z2LMW=GB7bQF-t@bq9{V1=ZNDN#uzX&nE6McQp$(E@6Q700Dv=!%eS_+9LI5CX*Qe1ND`=4s}&;J==FL? zk_1r{eTbsy-B5wn_x+TJ)`$p1wBh^y3joPT476G;8XFtiDT;#fJm)OS+5lDn1jkFd zNs@%dnCDVT$;`RdnnnttQmPEV9UA2!#tZ-rU~n>&Qu?3xVtoT;WFPYqD9e=q0000< KMNUMnLSTZl(LK!o literal 0 HcmV?d00001 diff --git a/images/cursor-fill.png b/images/cursor-fill.png new file mode 100644 index 0000000000000000000000000000000000000000..730101a2cafb964d3149863c82dbfc149ceb0c9f GIT binary patch literal 832 zcmV-G1Hb%`Ci(+m z6ry1obP<>#gHK!Koo*BgyjguIn9tpT_l4&??~j)k z2u~a;OTY>%ECZZ#thxlS#=sf_|2+dMF0vTwB4C6NEcITf{cRLs_N)%AEuz)8h{zu> z5LQp=ep|LcP{jcY;p7#evls95J@hSG0sT~FE5N^gmBZcFi>7`;OeNCJO-cR^H+h5x`r4)=Y2!!D6I|tIz z(m^SOD2hl+O-FLd#``+GPE;L2O=Nt3XJ?09qtQT76f`z8q2^ExN=wQh2m&NY!ua?& z8csCESa)Wps%C%#Tu~GVBG-DSmAsMB=PVcuLQxc$OlC-u1g%yJy*1ceJ^R_T(R2__Vqgi`8lsbUK|$tEVO* z2s0?6q==}pmQyYDQ{TU_uI_7(=9#Ze6saEp{)15>%U@;p(9xn literal 0 HcmV?d00001 diff --git a/images/cursor-georeferencing-add.png b/images/cursor-georeferencing-add.png new file mode 100644 index 0000000000000000000000000000000000000000..3dae5bda894aaa7201993c7aa569abc3a49f237f GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}y`Cl6K7jMd?OH7t1Dp_fCzFcCY#i+8>lKqHL zg5-}{wpGn{uh}LrsC>JZ)*P(ye@mn2MA3<<4{pvW&bized{}jDQZC2Bt+Mx~uYGbo z#X(u+_9KTQHq!)Migs`*-4qut@OELy(y^T(7E=Bo+IL2?XsDi(@vR*VkwAwrc)I$z JtaD0e0szhGP>cWo literal 0 HcmV?d00001 diff --git a/images/cursor-georeferencing-move.png b/images/cursor-georeferencing-move.png new file mode 100644 index 0000000000000000000000000000000000000000..e35917a90746fc16039bcf45b8e389a6d1939399 GIT binary patch literal 268 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}$2?seLn02p zoo2{&*nr1%KX2>0rEx`q^KXV<)U-)6Sm^#kw#)bO5!ER!T3hNrFG;sml&)wo=wf%6 z#GyFDae|6#OW525-=2$n3=CeHTLLaJtO+q`sJakl$h;w7Bg2I-Fy>mYauN4}r6BxB zmw`d%ChPuIz2|GpnUyZxV-sBY);`S9D|79FS8jiHD6}7ZBfpWmp}p|Vz3pX>U(^LR z-ZZcipXcZ!E~_x7CGbW3zi>}Z9eTr{}zEW{R{kvQ1@0EUEdtMIcBL+`b KKbLh*2~7YL{b;}d literal 0 HcmV?d00001 diff --git a/images/cursor-hollow.png b/images/cursor-hollow.png new file mode 100644 index 0000000000000000000000000000000000000000..4fa2d9124c25bbfb974ab9a66eda2bbd9f0e6038 GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}J)SO(ArXh) zPTk3SKtX^dd|hMIikV^CxVRQ{lqNK${G2HKUe#RkVsD-R1G|%eG=ry;rmBzAB$uGT z*%y{vlTv43Nak@}62TOxIqiVWj^H^gN_Ac+1eI4H#JtwJnPVy;pnZHXx;hvu>&|M6ku6{1-oD!M< DMOR1! literal 0 HcmV?d00001 diff --git a/images/cursor-invisible.png b/images/cursor-invisible.png new file mode 100644 index 0000000000000000000000000000000000000000..9aef78259e068f6e64b2507753091b6c466b7072 GIT binary patch literal 81 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nR#^NA%Cx&(BWL^R}+@3CuAsp9} c6CBtX7=#!Y-*<*A0SYsCy85}Sb4q9e05_`;qyPW_ literal 0 HcmV?d00001 diff --git a/images/cursor-paint-on-template.png b/images/cursor-paint-on-template.png new file mode 100644 index 0000000000000000000000000000000000000000..7cf21ccf068cdd12320f3de2564e95bf61a21302 GIT binary patch literal 723 zcmV;^0xbQBP)Px%jY&j7R9M5!m%&RLQ4q$zr%fzE4}qWuy}AfR38ZdF4!a3LO_jzFD0s`ESEYv{ zJ+~nA4@fUkdhk#}FCu6cR1}ia2HG@-5?BZcApuLmMyXAfj0~M6~Q760U#oR)9JJff{2J@SthG0L6_or-Z}sPs8lLsg%vFTtE^}NTz;5s z#XkU6SaAiwY%8t+m~F+i0A^az5x^`fIs%ww#f<;}V6+M|Gc%4rATV{?V{|KBKA-RT zR>5HK-rCw)Zed~JYd9SK&^3T8%dNLTmSysIJXl#-0RYwufXn6jY`5DTnM}r}Y1$J} z6vsPWG9Hh&4!rQ202uaub#?V+B9Wk2EcV;y^O4{0|K4dYYad8>o<}N`V*NCf*Xx~% zMx)uu$;lxA*xA`(rBdmEBuSsH+kdAWB$Z0_)y+<))9!Y=6Vubv!`W;Wj4_;_pTFyL zcssyAw6wJJVsUYCyi_VtqtRf=Wb)hg_V&}8Vg?4_cDqHM=U?f%4pmi=PN(aqr>DYg zaRUJmMbRz@!W)j`pePCzi$$iY>f7VvW2Kv+v~M{aj!m!Ed%s*R)7jY>Qxrwo+uM8A z?Nr>C1VIRdLZN8AUPrA~W0_1w*LD4QFJpPX<&RHHOrTn=QZAQ+s;WUrlCq`%L?V$* zpU*dz&*$mnYDL zY1;e4!^4k^u`wbV0Wbo<)@ExHz|V`N0cZfw0sO-Mq(9)geT^BOi7fyC002ovPDHLk FV1jCLQU?G4 literal 0 HcmV?d00001 diff --git a/images/cursor-rotate.png b/images/cursor-rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..4b7d1aea0dc96213eb9fb0caffa6bc3eab21daa4 GIT binary patch literal 756 zcmV#U} zgG^BX0LB=D(P)$n0%MFZMNwF;PT+RN$H(OZ001bL%UBK-9RRshbO72PQmgn4Kn@jG z0HjuN1wd*Q*8)hXqAP$@D!KwlrQ${a001$&p!gR$hGC?e0g9qJZv#b9?e>eSV0Cr% zx(g-Uh>~B}4!Vm&=H})UQ&UrVr_Jv)$r!#=BGd)U~ktlnarC?rGl%~YAqZN zdqhz%IXS6!I2^MK!$3Nno=zr{p8)_iHa0XGjplu!P&jBb8a&5wA4Qqoot>R~G)?1d zHoMi!IX%rltJMUITV3m^RO;RShy4ko!Dx@gV(hI}akm^F9}7Hx&T6%~>4k*_`=9ld zl@&Y3aTCL~VRm$M^!(OuLAT`d`6|n@YlDM>Ff%i=9a>!MKrBC7wvUXAY=7f<7#J8> zE0s!BQ5I1s6tehyzN}WK8#}91K&aQZn$0E>1Yw%vzJkp*bZ~fBcpQtxPP?6<>;)i~ z%kc(-;jOm6U!@|+p^J-4_r+!X0ih&Vl}h!Rrs*f~c>GlKRlKycq;R=hdH~edX#N0F mMG8kpN7ZOF+JgW2OMU{0Gk|9@7WRk$0000T~HaGlfB)9?TQ0CZi)FjQ;+!cwsTSTAU+_zfTo6)ym2t9SuGTg6rY znkrreprztf09q>k2mk=E&nmFH(2d7qdOe^h%I0rS6lGoh{VEs?2HOs#IuQdou-$II zX|-DH-|S(6x`8aq$Apj$01OcEYqeU%K$>vZM5EE8cszc=aa_goJT|abuxk$a!zW(g zf#bMG+qRJ^K(pCo2}{n?sWc=KiL*>5!$^|E?6n7ESw7}C?uh64rwAeE?=LSq{eHhH zisG&7x=BLFyTxLmI*#+q5a#as7*hjgKV~NY@DLb)@B0oQ4(792#C6@r)oOVr2;yx%pFb#<%dBadH>PQR zHVi}cJa6p#z8biKZnw*Lp2ym@jp*7_ z4oH&3luD&f#bU9eY1((wGT&Z(xLQ$r1&u}n=kvKbnM`0hovMaz0QEWGIL@e|F4Iev)g|lM*|c00000NkvXXu0mjf+m9ae literal 0 HcmV?d00001 diff --git a/images/cut.png b/images/cut.png new file mode 100644 index 0000000000000000000000000000000000000000..217663b19c426af17532b2a6c49c5e8bd119911a GIT binary patch literal 2087 zcmV+?2-x?DP)Zdu$ZP9ml^jJGXbYcjr6fw@v(r4VYq#0HI(2_~?g@7cB8Fk&x9!=}gY_lG8mz1Mcv z*Vdb+^<$MewN0BgMaGMx<2BUQ)@E|SH>IVf#XMf;t5Zo{Q}fzffL5fWq{alS7bl7x z7r>^{cNdl{R9CHBZ3oB?ckbF*IGN;9N}}4GdlxVIs(tm!uh|t!w@%defdDl%I~Lni z+kzPdGnteGixw`jJ8bIPlL4sPU9;Zf@#N=Z=TIReN=r*MCsS*u1OQ6sWo1rRIOmX3 zA~*M8>To#b)b9G(GZQ7>xpQX@|AAGFd?NxdU__6QkF~!Kmg}IZ@;2QNC1Fo zT3A)K(xnjkhX0rTgz?0jn)=3^uN0|_*&w9^r4(n*Hbpq+C#M8Zy`}m%Ka{z#@2hrKtrR$;Sx+wtwfF6vz*45n= z3I=b15CYBxN**nBQi^qTbw4d08{5#(U{e+QpUal5&`i?;0H~@BjYk@FZd&UnD&cVf z{CL!?!-sTL9UW4`F!9*pr4GfWzAdFB@z_vkxH2^@HQDEz1|b9pA-H_G&59Y( zQ(L!I?;kJ6ga80gx%uUf48v$@yWDCKLLeoOk`Zcjm7UClZGsH@wa2a3w2k1cl@ zh5;!h6osJaY?EO`_zx!1|3Ux&h()dEPM>Kq7-Nu9A{L8bMcFDhW#kw2^?o0-IsaH% zvPd<}(XZZax8sw4AJwTuO;uIZ)h`WDRaMn4rPy=)#IbOEDhQ#NJ-bNdQu{!4*h_MA z@>qQ6hi?rb5C{yq-M$BYtWyR6fY3;Edv{M~Fcb;`04&SGyt(rnib9u{EGTtFA`wU_ zp=lZpA8FJr!~9-FMTPl)1MvI(QPZ-XYdmsD*E9`MN<<=2EL&Pe&Yk}ZLI^O%aH08} z!3F=e+$FfpWonDs<5}x@wY2CK=iAp<)?KHgyUOrgpqx_r>Vm>T zS7(2J_+oo|*Uez?`-%`r5pGsrYHvqcQWB&o63PJ%02>Do3Nf*)V<-U2MsT&OcO({z zt>LCwcztlN@;}YZH?Q^eMHd$ryBMY0{-P*PKM-Ia;3bs)u6V`_t)sU$JTx+L$}7Z- z?*jYFfru*b_ed-jxxFdfRXsl%!iX&*xy$8(VVaBpXVw6{&jN2MLge)Z0&Q2ix?&}B z=C~N}+kL>a`v5FTE0Plv835e4c{AkY{E2S>v3R=xmiXLmGpOrejCI^QbxdI$F2xRy zrda}57Jpt2+){*C+8YQ&34pBBRGUTVhWh{%O4m(KNzu9o2Ets39b=O>0GgsGug}cM zN$BkBiwe&7-U}c&-`myK7tPJcNKh!<-2iA~Aq4RHwZ6V+ZdR7l#@L-B+8qGSd7j(l zf*uYV0M3q0?q%%bjFc3ws;cM?1WZGS-`oqJ2=VKlK)_U06-ho{Qi7r!919_YXbS3j z)a&uUw5;6w06>TZ0B)yKAwcm9K*H~VwH`%j&qz*wWKKbW^W?d6`Vg>b9UuWD0Z5K} zIY4d@^5SQ0ZTigYY)7)s_lTR)js{@uGe82Nbb(vbY?M+E;LdV=rwrd??8BKkIp3V- z^^#M~&AP?8lHhimg}J#NhpOV_`SaoHoHx8+S#RA2r?|U}B>=I9u?<<2R?MF@ON*K& z+IxC}L0wlFV`9a^h0fuT5qY|$<-pU1@gx8q7htu(jBG{uv@kc<{ZM*3wK0Z~PzW8p zz0vMKfVW8L-!8<*0BjiDL*lMrv?Q&#S5vyO2-udHnyTbwW;&95J^(;Zf4}%|YiqF0 zvL1g0Xa!)7eFIt#6sIw^8>4?Rlu+6?#QFaBh1h!p2;NQ>cQVH180P@^62MbV>9fg< zJq6A)2p|NdXS+=ErLDlZ+pu!Hy&x`3?@3A@;K1md$}v}d`#wH0Vocjz_CHHMd&M+J RSE&F1002ovPDHLkV1jK^+$sP7 literal 0 HcmV?d00001 diff --git a/images/delete.png b/images/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..918153138e72e927283106d01f543b0ab9eb4713 GIT binary patch literal 1198 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 U1>%)jhX4Qo07*qoM6N<$f<^KxZ~y=R literal 0 HcmV?d00001 diff --git a/images/draw-freehand.png b/images/draw-freehand.png new file mode 100644 index 0000000000000000000000000000000000000000..4d17746191c50f3a1d7bc448838aac6f416491a5 GIT binary patch literal 1997 zcmV;;2Qv7HP)Y2UQxRA&{&fA?;7H`!%(lVx|a$+87PF@Yi^Am1SrN05eKkz@u8O6vGg%2Ww; z>Nw6QjHu)2w2sbLBXrtvf)xd^RuLpj14;x2%SQqs3E3osC7W!%*zda^_g??#CIp7I zK+Bmq|LvaVdEfUr&pB7%W5l;1Y+|y**>@)uN z^0T#H+K^E=duH%kW*Mn3mg37E5#`m*xc{2`5=gh{|xQGxl&6>bi;$hUHZHx3+$ z_B}Qcezn~6?Be1%FU_8k$eeWhGRb~{h`~%qO>r)Av7n!bU24g3=GcB+ zwrsuy6e7cC(M4WIj>Wh+cqKfn5!RhNN$O#5^nstiY8C4@t}AnE#0;WCt)!&ejD%!* zF8(&zIn=!CtrLN+M-lV`5qP28T)k+)yt2twn~;nS5gY0tky5$niK#dIm(JE53%>fO z?JRz@>L)p}t+t;pTU2aPl_X)`dw@!FkHigs;#NGORDK*c{YV04y>5SH#man9&=47D zMoC7wHJqfgPr9wB>Pv6_Cp`Gr2z+y`sb<;Yk|ow`s}K))i3fU#B~)6vB{>>b-rajF zaFCA~;s<-Z(V3g;{K?`4b4^MrN~Hf1stT98V)Tt9dc(=gt$YIUfdot@{lSgpEAvG` zAndz{Dktd~NYK$I4aXI6YyI(o$YUe0ZSADzS1emtVzF2RY1Bh9;wKnYXz7V1WLf#s z8^=aZ-{)_cKq>G#&<02oG76jlo(Bvc+J>9UvfOzt*AEwzJY`J914R9;C<+P}Nj{CIo0^paxQh?>lms3_s;bh})kR}t;}XB$zXbRi@O7Yh%!-AHxpJ<# zVN2!OBArf4=;nV>QW9<6I4#{#e;}rm|KnuBf6uA61BU?jrcIlutgOW8bP|n5@%#OR zLLsbHE2X8SxZQ4AT3TGd65s?dde)SOnbtujSXHU5%w_>lDW_si-aZl zSHC|I`pbl$>w)hBKXtp^LTzm=27`g#-d_BEKawON%QDGil29l_C=|lwa#2uFKy!0* z9*_kbxl03fhhxY5Wy`Ztq==1nlaf`MyWlh>o#oIfZy+DaBwg^GptyxRseiHA7--|lgWhF>m7fV)vH%? z=FAy@&Eo|0g06h(v?-Qk$d8(eB4lI|lr15Vz^(%gcg^@h;H`{|jLholY65}4ICy@3 zK9eU;rlFx>3_u1nRaI5Ym@$Knjt)jfMzGmz6c!fJ-rl}OytCtJH(9bRLMk#qY{bjk z2M+ePc6fhOcQmwb0=Nx01(8`#vk14Tr25>D92#lX37K>pp7{*ORFlK5uzjL5YnVzey3z*mbZ1!h9KIBT3RY0c|)Y z&#^tZ`jn?nTBw9Ve;61TFl^hlEw`zu$)?lkGN(?RDmtA`g2CW8xWQl`Cntx?moL-M z&;amP;0I~Id-p=Y>2!|k#bUA0-`@`q(E!t~^-43BmOWD{iegbX9G)Hs1YEK#TdY>A z)@(KlsZ@%AfdPhxhe;-r==FN+c01kO-5fo7l(f&k45R?v1cy}tE$|(;+pTxGTqufy z!{Olg@#Bn)jI?TiOhsjn*Xwn!Sg}GYiXw?b0!2|!)%TxK5Cn8O9a&jf6ciLtP*8x! z;~4{Q0-gg}fd~)-66qFH(`43Ny?S+u+wI0~w{!mdc`jVI0PvcSzM`GLt0g5RtY5#L zKp=q6=OdX+0-)7u?=qPB`g(5Oyb18nwCK7&Wa0|xc6?q zb#*lkhXYZ(37#=Zd9)BZmeqd@T9vAPB5N{dLnPkMk| f>0;t1v5@>9h4(JijaBcf00000NkvXXu0mjfoP*95 literal 0 HcmV?d00001 diff --git a/images/draw-path.png b/images/draw-path.png new file mode 100644 index 0000000000000000000000000000000000000000..b4adfba7c7a8cb096c5913657a8c0627d88fc172 GIT binary patch literal 1209 zcmV;q1V;ObP)Px(b4f%&R9M5cmqBPFWfaGM&CH}Q8|ab_p+j0BmF<*W>vpr1k}cE*TLi_UcxV-o zbx}OZ>OsYm;IXHQ;=x{o!s4Yz52mc`5>Z$dyVfjC+ilWq+L%dZ(&QW4Bpyt{c4fQC zI(_gRK6u~v{@;7=`@i>~J@Ehr@Hwyy`~@67wDQ1jz^6c-94EpTaOW`@4LKGNwlxm@m;k`SV~hKGlV#bPZz zt`z`p0xwKYPowKPo12>mApr1ty(o&(RIJr%Xqv|M_BKGR)ueioyd4Mx2!%qVQYnNG zsH)1qzyO9}Aj>ib2L}M?y3Y3YHVX?20Bbp4{e?gAeI`)&a6^!N93|NeadBuT=y?S}Cs;JtdHJ?za2;ItA3xuw6_(ZSKt zQFCuuR>R0gbyq*Nw2}j4G8t4=ZSHkl2atfPZF0$fL~222Mpad9U|;~#G^tc7`2Bur zwHm2Z>N2nk{Cb)m_<4DG8C6xAUdZKgOifJ@2m}DW0zNoR59EM%dU|>Y27{QUNx57` zmSrX;CP*X_*tY!&a0dACGyv=YFRiYw_DxPsB1sa(ViDW6@pwFpkB^hf<;Y|*W55L< z29(?ca0L7gTummEL?RIYN~IFTVv$Ov!szHIilUHACNBV&fm?OYxdH$n1DKX&y}ZA_ z-w_UnQ51z@u}G;@LI}a&;2@f&v9`7r0>Z#g_44crfGThssAjX-$kx`DG%_-R&*#Ik zEQAo0%Vk2L5W!%O)z#H!fOEhi@V6TP8lkua6fDbnF&>Z0V`F1znnpgK$8nq{h_h$U zl1L=Z0a+k^5+h1&CT#`y1-MZPvHRI{2_%ZS)@Eou;H#bMA zRBBK(4<0-9F-G8&Bn^q(dGfDPb!KA$I@P9LM| zo}M0es$mPV1l(O;U&rJ5_sYY=!K!(eD=2+OjX0CZhv zZ*T93-Lc%p>&F9u0N1WvqfjUui*dDDC7Db%yJJn$=<4cXc6JtE)2$H5=g*(VG);DP zb{?yw>pFhFpT)(+M(_BUTL3DlREjHCt{{XsHW^8hgx~L{ySp2J`T6tvb40sojZ2`1n>#)4N!FHs}rbS*y#5kT~#0rd|98QTateO XxJ}H%eiRJA00000NkvXXu0mjf2HYym literal 0 HcmV?d00001 diff --git a/images/draw-point-gps.png b/images/draw-point-gps.png new file mode 100644 index 0000000000000000000000000000000000000000..b66373044330ca0cd8391a6185ed8a043eab5e57 GIT binary patch literal 1530 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 literal 0 HcmV?d00001 diff --git a/images/gps-distance-rings.png b/images/gps-distance-rings.png new file mode 100644 index 0000000000000000000000000000000000000000..8b1a1a1ae41486a0544e5b3072edbafdee748195 GIT binary patch literal 970 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 UFoW|y!2kdN07*qoM6N<$f^7Xfy#N3J literal 0 HcmV?d00001 diff --git a/images/grid.png b/images/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..733c2313f8f9e3e0243172bbeecad8b33b03de86 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&kwILxzV#Zb$TNS)hTpLwh90mvdxS3j3^ HP6>URb{B^_F*d3 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 literal 0 HcmV?d00001 diff --git a/images/mapper-icon/Mapper-16.png b/images/mapper-icon/Mapper-16.png new file mode 100644 index 0000000000000000000000000000000000000000..1897cb7df96042e67de105716ccf39f145f9b9c3 GIT binary patch literal 1917 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 literal 0 HcmV?d00001 diff --git a/images/mapper-icon/Mapper-256.png b/images/mapper-icon/Mapper-256.png new file mode 100644 index 0000000000000000000000000000000000000000..0f50efee0614cbaeb921a979e99dc3847b917d19 GIT binary patch literal 61581 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 FV1i%}lzNktv& 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

    T?B0 z>{i?%#HFLIYP#M*Y0J*uL4T&B`Xr!aIOCRq%H7ZA>sJ(b=1mXIYa4jJB&`3_-?6^D z4w_GEKO0nWta5(zgNPd~i^@F`x-A3hbJNKw?LsaBUp_BDhe5=wn>H^zQ+gT4)I&VC zrWmKDE@1HK+M`@;a{yyX;Ptw>MHg1Uth1iBq~q|8=qjW4p=+qaqz8WPoSWSINjm z!q<&!XnMl&)V*76YG!P=MD^50%~@4g-A{YuN%w=*WuKM+7%0PCPa={7eD3luTmG=D znx6_FdhG&L!PjuMI5VafhpRhX&_{_tV9W*Y2bVAtQ zn->nfawKny;69NYy)qv@k5jvDy11L+6?(_5ex&Xc!ENX6ZZCrE`?dw2g718g|Du39 zMd?TdZRt4-5A2Vx2ABW(s7Ut=tx!Mxpg56#-7%f}(^%4d;)Jp<^n`Mhw@$&NZyrY{ z+C*p@@gO2FV*ZOVKS&2jZ{4es%oMvS*?O?_SOM~E?NTSW`cLYN`*D=KeV%r< z$B?DFWXfIpdN4X(bxEY6$BETn(X&#sqpAz%sQsmhVOBXcyXNNR)ou_@>Sh_UOqrLL z*G*&f82EUsL)u9FubQfT9a2^qO=}NJ1Y-h?FCj{Q3q4*(dMsa9zWbZ^7Mi}M9C%wr z;$T8Zs&VsI^cwR*qSHer33Bl|#qul2erJW2Mp1O3z;7=qeBC~+20DBbe~q{*ej9F& z8siQtXBft~=|H(pyglt%IMdtPbAi4tGWzJ&F0Yj&LJ$TD|m!kn+oBv5h;mIU3S#h!m>r@3%*3vTeUJ_%7CV# z6_GI3hgFQsJAAnDBz9ds_Srwp!0=xYwuxWH5fxl1LI{soa?TJ#L~LZPHDG&_uy_N0 z?_#1A@ykYflQOOw7@tCK^>zZ71eg04_EzLHe~ok_nix+{4X`^&$XTO=Beu5;E1+dA11`Ai=tgU`K? zAq5adCNU{DAbogTs6#&A`tnHZgZCW4c>teM-@*8h$s z*HS3lM-cpUK!vVDHN;aV;1Tbv*+tm86VWm56`Y27U`x7Z_q{0gj>sQMjAFR9*ihf@ z{R)PlxIdwnuhPa1s4E){R?mf>rVmZMeU>m2u$1R7c}@1tY6y26PKxw~TrVl$?kRm) z1=zVvhF9mp%Cb_-={h#PX8$;t8UwN(*X0&#W}5Gfl#WyTvt0rlhI18;ubEKxm~87+ zPkXIu;`^UXlLmGfD+Pzpax2)pY)?d9H8gOSRS9T!+76CYV6j*7>~j!Kj^L*gPiT-H z^?ERXivWz&6Wru{tJoh!w}R7h(rN|4(#9&8Q&4X-UQc-)RMcOF%0Rh-V*w>e<>H%4 zN^_+SBlPY%P?3Jzk4+>oNnBvyoNa9zWNjN=IHVp1&)=oPr8tkVxO35TrnBtM zOjqWQn)88Jhhniby1sq7*(&8^%TS$>f#{ z)-n%M*w1YOzrMBD6R^E-T);vW3NGz(p}#V9+MCrQON@`aFPdzWk1KK%H{#s105GA6 zF#Jx~>Q@k!>;s1!4W#LVSS2l!Y+!j42RClpZEbR`%_}mj#^p51#Rp4@D0_Sxy3*g+mP+9$2U1#tm^9i4V7+cV4q8d^;=Rt~T?(`^)r&AnZ@cO+x8SZt|3 zvE&z%hy4qsVDhY#H+=jF856M9t#6xr5;7Q0I{=%& z-J;_nFveJA0s7Dtc?kL9w(c7~VrFp`)X;e(WX{s=WpdfFZQG#!qb96ht;&>bnQvZ7 z!!qw>(Sck_#LG#)!9Ouk7^|}nU`BzG`FP;+W%W_Ngm9tVBIQ&jPa}lEmrQo-Mdcx9tA*pTsQ0W!ARfChP}H|7mIqbob0O) zd3ybJGv(KiiaZ7F!9O&sTLvzekLyA&pCPF}UcwIi`BkVprkKrEC*Y!Qc;eNwQY2z_ z3e?9oqi`*~Jpw*|YWU*^QUPtPIX{UC^5}laMFw@;@TobQUUS})k`$%AAr(c3-fXfwBH_!ibQa5)q43zmCe?^+#}b~bCEFs~CmDD2SYQ89&?d?yT?w(d;^ zk-~A4#kDcszec;}>S~=9j zAf0_@My#;OaSiPCJp0GH3AO#V3Y!CZA>-ZV} z>#mY9AZlu+ow@z^?SkWUnI)p^FRf$8edjwV=UV3nDtIbG=xs~@{jYO=A{^k2M0hTq zHywXm!7ye99wnqldNlg8liY5V2lk9h7eMcZ)kH_$mf<;f5F{~RsEt%If5*O9)`UyP zZA$&?;0gm?m@GwmI#aCx<}$QxwGOhz2^uNTsG%EMIJ=V~loyaQ0dF7nuQA#(u1*XF z4cweLTqbxp;C}8KjUs8Jv6|@H;z$2v0O)a*shR9X$m>mU9g%wJg5S|1->;I{5DM)M zY)6si8fc3vT$5WeQaiorI~4Q@o&RV3S0hap&Y~+%sLSc^geKpn-{Wg_tdvklR;y=@@xv&z@75-*vmBeky{v!3ww_u0DEiFC;WO$K}M_{j~z+B=&Krfjjjf3eySQ z<6s8-vvVO0^n?+j?;(?~O{jsWypMkDD`-6d-EE*B83x~%(uX83!RdHTgjc>@|8=;0 zY~Fc(RuOZbvDn(D*GQEFkeA1$RaTt+Z|8-yf=s8nr{1O5MQI7BA~M|-pL*WM+%g@X zoQL*NaUTqC^o@>;9zOnCB{8($!M~jqdbad}*3Kv`R+L!Tte+pMyd%ly9`iotg!!E% z@3V@Hp10?pi=WxKba9+);FPa!_<{MChQZ6#ixoypJ&pB*O5Cms#HyLmATct>(ud_y z{I5DxM4LY~shUzYw1hT{d^dna-#erY19S)=k|PejO5IvDS;AvgKRdZwArB5q%5Ii5!LoMY5y?<)*{&&qKK zTkrb3&o@3#T{w2Mci?YEb}3Q_wc}z7|_9t+GlV_EDqzXMQ)xQW--UTKDA8qS%8v^kP;hj^!%G| zp_X8Cnr{Q%cTm4zl!B`8d~F%uchJwf3uSO3sKFu8Kd@Q<8wkX^kle2DkQNdV&JTiGVe6o#=BRbzs{&rCZkCr)f=OvTJPkBe1dWR#*0Uyj@ z-aFx}_pq=|6M{WO)YOBk=hQX$P*Z^WRyegECp!rRMXz~&tkG5NEx=>?AOYHZg>1s{ z(jRw3yybKote!b6$u0v`KSYS|<8zMIt6tlFW>s>c8MJ-#iv)Y$*p_Bm#6l03b8qCQtH2>jRSk^SMXC@8{~uX>Xvxpj6Rk3DjzQ>h!G<4GEDBnV3~# zNsGK^A4NE1&ED;&k0WOiwD0we?%ap8r@c%DZZU4BQK|x^#URV@{u73n8e8T_g#(^jETh?NVSv!tYRqI2A9(Q=W&YGrQ|qOM|Thg zV-3N1`>c1Af4__AlyE8@jxAjY;23QGx0kLx6Z@+A%gZQ-uMrZDa*OkLzGiKCw-pqP zX{y;>!uwFPVc6;~25M%5?{@GK$ zj*XcKc)5LHJYZ3RgqGaPI2C5Zl=igAj$Io(O{@8AhE3sOeMvTom-==hxVvLMeig4b z*)3GA*2FpXB;Ppyy^fs)DXlw<@5k-+#P+w==)(}I6rI>XE(eTVDbQgK-76wr=u}XW z?Kpssa*k6G2he)>KkgpB(XRLXmTGZtzk9WTL(@(P(8Uu2;+#7X`=EfVUTyyK8E>ur zVoL0Zd6KEZ=3P85IY6shnjIGOjXXR=ZzbNT(rL)HatjB2i{;=J<@@4|EA(HlXmp3H-E zS1nmPEYae14{gK1HQqg=nHEW#iK}6n_mu-jPms-pPiD>MFe%LEmd*qP! zW)2)|hPdN>P%-iWd)UO7aOmTi-)l;KrbK{`Rl;)z7VQ)R(&X6aW?#%4icU8DdI-R$ zFA|K*0}7UM_MQ-&I_OSmG_4fVAfxk=S11=i==dX~sK@$!Wy(v5k(Mp5%F~%;>&EDn zCnVpaO{Y9eBYlJk8Nn9)cG@AUhXY@JPUqeIG`5q5o8!)GSP5E}KL}n|oxSKESsk5r zlWm0D>^DpdLX)Pv$9H-l3}?o9v)O#EhP*5L-_Mh^rM(~4^T2q|+568c_CYng|)lIVE9 ze0=wxP`#@4Ki>*Cx&KF|qe^HJ!Z+S7`URgjYGdD@*4FKG*n%{glqf=H%QJt=@ZIm^ zqF{dh!56Boiv}X@k*U-X!9CAN<*KUhkRKuZyq8{La>$9?_&(3twYGQ*Q=YoQooGAw zPaiUyeYHKNnu3JXbvs_$Ns6i0XnowllyH zO}g85rW*W^80Vz)ka&s+pP%&=R~(5@=gTDa7G+%A%%){aCKeR<&q24+MgDRP7z(Y6 zzE&3g`<}Tqa~EkD2Fg|RbTDEw`;^mllp zO2>l#rp%`_y>)6=cFJxt+3*{8@|YrD9a5T)@|><>W6poHKEc?op9kBm zny5QRF}a@Be5>NaI5=u^g$Tljsef3w0vfM|Z2(yU=~VsqTHW7ketGkeM&g_J)WVLm zO86S}wcjjQN%8Sm|b4AOgZ&oL|C-G?$52l;6 z-rJ3@P&t@KYX6nmpVMfkaH zbBm!_{~OETeKPYei_fP?JCEsV81k?I3OLd|A*-ugbA@-SV~{1=`aIaUS_o2 zJoOo?XlGRHGmNb{S!DakhS%Z`J#4SHoF3EHPzK$&ND-v{0ysA$0%B}F{lZ^p%wBI2 zrkIG7d+MremZjgO((Vs7p|-Ju!1B4;9YEx2H%G#sTE9F%ZRKq$*e%NsX9XWUc;xvFNp(9hUQs=ImCd~!49$)y9~cnDR%Qv+wu{MYOAWw9?32UvbTLp!ccdn-O(_JP-S#Y+GOAYuiC!e=SS( zCE}3z71R#+KWHkRtqMtsmx$02pxG`KUd*Jt69U&@{UhSo@Nf`f```WZ?P{T9T5) zdxoEv)l?|$?c#oJTeOq@&PLtkBM=lAcj{Q8I}Y#W_)HzbCzF8=mJ^W4Npig^#MF;%#;RR&U{Db{+>zZWNMh;ac(u>;zZp2y@3t(>6RDv?>FU*^M_X9u(+ z7qV}ye_~-<)@zF%_LOh5XG(_4Nih+LX2;&&$7i05RV>$^3czZ1wNnm@BX_qk?X=dQ zn^aY3cfc#8=fjC1qJmuQ1xHPN6%Uscm#=TUg!l%{SB;L@6nMHnX-1 z>;!#S02bQu?aR#$S->m4RCuCHTv4?D{modG==P9>>0>N*f6gO8LItGO? zZ@Bwg)MMY3W#6^_GKy&Mwg4Uz*Y7PRZDqmS5LAHJO6o{4Y+mxxSGTjP3TRI{V;q&w zJQ-2-zE)AS>zQQ4T#n;5aR%)l-1U)Nw<#|x`LX1So{Fget2Jil$IvJdSO(UEN3Z4y z)81V8$dv=WCi!i}2=Lmo9tj@#G1>x#h|=SIU2)IPwOyp~1J7X-N|e_Umt1K+KN|wp zS7KNqv21})N|v0n*;H&Z2MbY)JH89*saR&HHFFd+of_HdlEAE;o$K- z&trdxn#)J5rxH&iExO7*mNX?Oua1{=@fD5S!7o_NLjdcKaT&!uFU;>`#{~^=Ea8ai za^s+h>?}2pLuR`(u~_?2KqYF=T%&K6yc^e6tB5u`-|TxygpX`SP4Sac7>=mW-eAY^ zKlm{dp7<4qvk?Skn>OtZNA^rBZEz=y!Www)JwGX}Zy);f|G{d=PLn-f>EGP0z*b!3 z+9=VVMHM(KZ9jFczbXChFnIS3bNBs-z%6&8%oByL<8B0EpK*_QIc>jJm>gyn)vrFq z@9!hOI$6H07PeNfQ~$2p{N7Lnq7um3XXXdQ;J1wal&sEG4f>bf75V*j`k8*AB5Ka3 zP1Djxwok|Xj+$VpQUtJ4B}lzTNheBW9xaz34yz%m=GnL3G)3`4#wRv+9D01zZG6^` zNV~RoP?c`@dZuHLZX{m)*6>5DnTN^WA)Vwjcx&(G!*OgA_m;u&aRbdNEr~NpHwWtW zIGe~J13ewzbmrsA`;Ol3nBmn%c1i`^qo4b{$`I#<-d&h>kHNmq4MP*l)K5jQ| zBB-4|$@QmpxLn9>lo^fe=$}os`Tm5TU?My4H|y{%I{ddg zs1r7PHfpA(^BwR`rE=XAJ>HpX9b`^YDX=29RWv=bTr%O9W&5%)|B>Y~Q-9TY(zdea z^w+nc-}t<6n(xnHLZ?n;(C5o?v5?OfPrvmwnlqVvQ;S!;=9^{%b022KQyGi)@{zOump&-BJMkQ&|55*duAa)*%BwKm+N0(a!TvAe@Piy7`*2x!0n)f44&J~SesnMIzL%j4Zr}&w zSMGBbn>R9IjeBWQ5p%i=l45&d?s9q~n4~=w`&y@stn_O+zvEK#J0%$xP8|sp5hJ(* zYoB1u_xZAR;$xwcdaLwL+BYpH+eNnk;yY-(_RSd;R0;!b{hq07^SCo9`oE0MN&^0} zc(hpDzlcOmr$^3p_UmLF?}*OXT%D4Ap;u@fPyTEE62RwgM;A_k-1~<;RskaiWQJq{ zlR4((>CI{4$Q&o}3lHKth6C`tL;`kIRVr9XsIBLi@R|R+>cFE;KOK9KnT#1twdvlg zC~embCAL-J%x*b2{eduiAm!}UxKkU!Q|eJpFx(z1l+?L4WY~#wo9XC$YWS`AQ3WZg zFt!Z7R8O##dhc0lWgmNLQfGOhIwr=zyIDR)VO7%f#|8{wqSVgtky_>%FZ zj{*LA3LNt^2+PR1&|c-3DL3vvQf2Jc*~QYvu#Nb4M+&=mCm_5k4!ru)oSgNiaFDQM z;o85e2148gC(dVA3BsD+wxe{s*tYU^qAz*#@(|m{VDc*|CIeTRdjPk$7oluC`h|dN z+&I<1+s|hbfKa^~jHP1_0t3HA?EZ`Iyo=b8K(LAw{Xo-N^;*D&2j3u4Gviz^xvK)(KW3_4yB+Sl#|S(nIQD%``FG@RralovO0d- z#(JDB)ZV+I5B{c5ZuZr?G39rKG|c{w$|I}!BNOHcyND>9=O{N>`(3U?6v$sJbiaF~ zW3BGNyUq%;J!WaQ z16ydTgAUG)R}8!osBGcZfL*uzpgFzY#1-Kl69vkQL?yk{abpcs9sr;3gE2dYJGZ}* z4&8=$V15h6z5u^@@*S@IVKAw?)=wg)DPx(;&wLqR%$&!PUrTK6mhh7k)#uwt6r51v z^FaV5XWd5&NIIOS;(*-HR$hEjQa2M-kC65@_|pX7t5{0Ys{uZ`9PX0~Uc}+i=PkGQ z%>RS`)JAyou1uA8GoaBb>-Gi63W5nxc>6{np7!HUS=?!AWnd#alc*rSb^$n|nMF}i zQTEy12i1ovovWk3F}$>U zAM%A*)EYf=9n-9&SZ8@7k^iPIe2%Y+SNrP&3PiA%u59Y7c)|e=wz4qqizkq-3L;nu zxxgMVt?DanTU(#u2Mrz78ExdBgp5GUAqA2*uK<(($m;`jW~}8T=@mt-0RLT=2ct=$ zM?woVS0Z_SJ7_YkcT#Med8qSI(X&`p~0%_qA*aKiUBhT<7eSg7e_$ zM*koty6WPA%ZUIrnd13a*UZ%g+b_sOsa&7+M;{nH&?s;^e+nmsl7c3S2LZ0hw$F6i zyu^w1?O_j5jteh3qtxi@xN=d9=1gUdbmmgN{=QiyLMEPn@iw{U1)LN1BH8pGR?o+< zKF<3j@}Hxysp9**3IUwqbJ!>HoxHngYvN8T5APD0s>Hbb3o9r1(hS;Hkv+t63lY|ay`Vm9FM>&IZZY(mB3Pr98c=@=**ptg#0p@umB z=E&RuqXFiY7KF;UAVSwRdqVUaj_yiEDu`A@v;8&OrElj0)|ggume}~VT?t!BI+R2w zq!Qpm7i90Qi5yt?$(13_yw{%f@=Ay8Jg?*MRm2TMwhiW7kH8R!Qe-n$?=$vR9Z81H zpQFveb4H9SOE{<>o7+L*_&3udoB%4K=9ZQ(K{xMZ|2@{+mT?MtWWB$4s_NPz3$^b( zKp2sS?vz|rXrCP;MB2eR{Bgf{W^TIw2v8fZ=!Fy zMEgCK%uxr1Qhs&)H3=rL!Lu~sW)CA0M)6#)3D;Y;0;ha%b{|zLP3O7?L40!D|57jf zA81rTpxJAn<Eq%&4%XqB#E*a>fw$Wull zJdd9fEswlAgP?KEgTAfv-hR)gl1>oasFf8&dBvPcPUpORy?BTTr*`Qxh;xkzHz5Md zap0f0P6so9Z+BxI>6mtJmBu(iXLcZ@DU52c`S=wz%1@%5xmQ1HZk$}+rmt%0%H8E5 zVp-8`Khs`mf7|_aUh(vZCG=7p7t5g>=v0+tmL51^}Ui z=f!Yq!j@V>Z}u;hZ3U)869FIg?43nv&=ES6D}aW%c5ZQjZPk6c$~}j8D}b_2VFbab z6YQL*!wFy8{&G&1vue!(8m$$HkmR|%|d^>XQUXbfLu_Zaj7@m*Tm6pxhsbuA9 z`FR&U-xB|HfdR(gAx@+Pa*L1D0IbDrtgzR(i$+(zU; zo|(8><0Rq%C4oKJ>Wc9ENEA4~AqROaQKlz@GmfJj&C{AH<|{o2dmwS`wm>xbQi?~f z^l5Gz*|uFE;~-*_qVc7|ZD)fCdPD^GoQ~%TN0h)G32}&c+hO1I2Eq9o91fX#EE)Am z7I-uETF37S70P)y7mK>3Cj;B6ADOk>$c3sjV;$eB2&%*7sRjM*^XJK>cWY$M zLUZXjpVO4mW%HC=;E2!jk0w;mzRlFk7dp-aIHs10D*GwSS6S4;QoUKLLjd-0$IM`w zrKAvOH!z?Zej6L;uCg6*KV42e9C7nbvsxQ8oG3RCFeF#L1h@#yYM(?9py&2R$J)UIj=) zMR@+6CJ!u0A;&o!I@2m(@qQyyJi#YDLgUf$|6gFrJyM`Rv7nngwTjxHjUc-an!mn%?KNXNE0#{ z7?TC?QHB8y#$g-(jt+hNceZ7_SIUTkMRpZ-0OO(y8izexn=b(+i+9?n{ufzi;T3iG z?dzetLqa;GB}8&S>5vc+rKO~#q+wkw&^1q`Mhl>daYp-Mh~D-GAX5 z?|S#%&wf6uVbc}sD$k+_5BHTxluVreJ^3aYj+fS{v+-JP=+*f$Q-qZx9Z__}fm%Ku zR&ZCl%fwB^G0!Yut79QL>sR%qHhKwm&qL%+NN)rTymJHocT*!!WxYG_QYRg}L?$bn z?cJM#GrcyMwBU+wSZV*a3k1^;sSFKJuu!gFATKm?m#cQ$;jGS5Bw{|pR8T+^eLx)B zXm8LPHpH7-xa&3SeByTsg6JxB|L=5bni++MgYFasqr~RnbWH9I+j!TyuJ9XhR=hF&%aC+cRWgdq2z<#x;I-&tmcWEn&LH zn+a|pwGDlO%*JLMNkp2*%wBKkqcu_tR8$(jg!rX>v&8;0gy6O&6eKtY!4Fu&2{`lV z4a|dDivEK=61s8g*vqwEgLPvCD-6Bg@Zl9skP!m&-s}*=t-Q-tiM-yf5t38|H1g(Q zmv*-%wuWCliXG)c8SJ`YT)nt|xSVy^^?T~bess6UcYocZiiYn_a$NX{%%JT%_HHKy$AV`X16&cw*t+dCnc*$HxbTdU9nzow4Lvi$f20m8cFuKkK_mWy__|cM$uf3_rjyR0T=3Y95d9l_X5c{L z-NY6OZsY!*=rM#-%I_IY^9M0orKF6bx~CeM=sn^v>pq*jskCcT{oh%%rsuML8r?5f z@$Ikv{Bt;$evj>;e>R# zT&$u!hAHOS-%q|gs||xnTnE21t0N=)6iz<)qUN^^W&PK@%PQ>``Vz|wO@PPokyMb6 zi9z>i8{z(YeI0b7A(+ce1X2Aij%_mXSW0_a>ltfk-t5^hGl4*CV}fQ5STo7_?VMSt zK-|wBw`$L`{+9~WCQQQ8S3dz6$G7Be?+9SQ2T0jvmP1IsyiW%{a!?c`jOi^6mpn;Y z2-Fp9YW<=kNBS#kV}F(Lu+ThXD+HHe(}u{@7)?2kx=L$=Xz91P0Z=k?2ql7b0`#i5 z@`H?nxt}gEqPZvLN~FHi4j+8}p5VQTS`NhVDs_(%ZHx171FVE8SiOygr=mVFLpC-sg-r_Nct4A6HDESmG~d+Ufyh!5p$T&Yd(asWZ>#7a@^d(1OWsRHW zW~!A*bU0je{yny}SWnMs+`L`jP1&o?m5?A;_DnL)y2-inmeiqFkc?4LJuI|mdhM~{ zc*5n1K%0h zj6PD+620Gi3L*0adHqo$bp1TpiMpvd-f@e-oaC_@lR@l5!D7gZkS=@vo;H50p9$Dt z(CJjF1M~v?zFRE6(|KP zKkULZ=N$5874qkg)!%&omwSo~tO9G`%DY=~Yy?XU_It~f@g7B1j8~8RL1UZUebwuW z7MK`hE>uu>#W&v3IgGEH+m)GqTvJf#o850wffJ@8-D~4SWEIkC7As|-pWq+AiNoCl zo_vk{9vL@s8G#fP+5<%CaYS{bX-q_s>0!aX;Ft&HzUB*nZlg}zKWw}ryDKI?ZBVB4 zlSMD-So+mTj)*P=It4|{^bE)<>!NG^X5(E6rO%7wtLhH+;uTksh4hXW;Eh_(Prng! zz1KaNNvhJ0CR|ZEs-oqGD zOR9t#_iQfq`68nM7K1?iKfHYCoF$Us*zc|wp!=YW^Nx4il6ce&q%_rqD8ccX&1<}L zaaY-_DEfDw508!-AK($=ZWS)N=VAH2n6$@u{@D4cv|7}UoWUVtY=QHiWw{o`mJ$~p zo{+i2n&yoS%f=pBgVIbH0;2EqA4rrhla)DLq4u{!5OnlQ_%&&l{lVE1apw@l+-f(c zTn?YSFsNR1eq_OBaWo6A8h;-cQuq;PCum2(l{bawKBQUn0BoM*(9^*4sCTSTa3(iR zX(-(T%aKYbuD5d{74sg|)I&d4+mQNZbd(Jjy_~{}lVtdMy}f4Y$Sh^s?c%}pa{F~7 zC0~VrRVwatqe3(X833P{R5kQb9sCSF11nsb?Qi|LXhkn?AR07&wlDLM0T&*~9hSdY z@jNEHSFc=o#QppIP9@(A;3k6&B_)sS@2jLq@BduH}74)sGX?H6-uu@mA z)zUl8O3gU+61ALReY zo9lYSE1_r78s`b(^-HXb#|M>)bLI3!%h?2D$wb`0vgVbx@vxR)^OZ%l zTQkrr&h(}`&h(sh^}j_~>g+c#$cE*%{ew);ccFtLRP0H2$`PqNx%h_BzAOX`E9|*A583bEnjjz(T3l6_gx2r^| zlxX+2#?@yOx**%w3kt=w(+U4HG;vnwC*%uK+xV8nFZA6ii!+ewai4u&(eDK!efWuY z)YzZ%heU6(6dV={C6)Hot%`fF_I4YCswK-jD>mb7LS+4>UnuarnbmJXFW=^?&)Mj2X4pX}HJa}8s8?)r!UUjd1j&9E$iQThy$0Fw|GQuy6m$X zvV-NPH-u?W4r^ObXj`J}bxZ0PJH#t|?Ykq9Ai9#F0}$|ehFMVLn|g|30uRHk|G~Bq z>z_Q?4q9wzFKZ|D9{`Hn^T|`iqvWKJ-o|)w!nKvK5KrnJq5b^P(-Q2DLqi1<6LO7| z6n1Hp28GIwA*`hAHLGzeBF@8>65>%yMOD}z<*dE3^d2u0@lhN@a%j)v3LzW5h?*rn zim{~#nLx$!0p*WfN6xD*9uPNbeH`yqjlS1M1Gow$Mhs47tKbrqx*1UCz8eKM=Dl_d0>c=l4d77ZF;>lgKJ+)~ z$}(86;%?5@d1Z}L$xjUW{JoG5!D~-9yS0FV-d`ewr}bn-Z{|*A`PbmHurB-~5W@Gp zei6r=;Ky`ixy}Kw9JwLm!c#`zJTG z0`Kor(cC-r3bg)(XmF6bH^&CE=XuUrcKWoUtvAZK6`<|*AbG9nXm)g0BpNf#X1`SE zNpNt-Y^ADcHK@cDRS;Q-{EVE5&cGGJ?KZjMEbF9mJx|)d2j#OtwK<@Hm{k#?As=8h zp0C(wT8ag+A$KxiPniJjPI%y+a)bzW-tHccE}88lO#&A9j~HCFNQEutOx=1ZH$MK{>tdq)D4@QW0Pr$8$9*UW@q z4m^P0YjfmWTwhKxn9A%qj(Jgz7gfi;FA5e<2qX2?40^78t=f^xz-ghHzXa#pL)0I+ zfUiYNuEv3(uOne|!h00_SCQ_hZZCwSiO&r4<|;FYg_FBLX3}$Ep>e5(bO(Ywggm0f zE#NNcYyGw>$&R|Q(?TQJDJq^-hG3^+;JHB1?Yln9KUnEcoyo6L_A)$x00PPBoJ2dc45mA}VSz2vA?p~#^X z4ND~B?pFfHPqP4}LBEe_&tKQ%wyWTP7Ya&w-8rV_-xDWfl!$f0qjchQpruG^&V_pE zGY(ha2EqHZ4U+Qgo*BxCAWeVBI1|}!cvQ(8b?S*kk1+0c<4pSRQ?D68YfmA-#tAWiB26e{({|rs zqj|mQJrVvg9+)<3+JZ)Jb>rcP$rdeu`AainJ(8@JMmMmvXJRaqM_YY*zxL!72D;{2 z%Dnh-Io0PzQscCaXYS}OJOR5#)em%|&e>9tdY_(bx693PZQg0_;DSRzU=2h+c!va7 zWPbh6Wd9wS_&c1(`03>0Cb2|MHOpPRM;wn6_pxIwyY(!BR_4f>B(E%2uRi$QJfq_6 zN!*Fbm(D%tVov2lu+X7aoVz+5iTN(R!wZ{EdO*cuqF&3(lC7@*iq^SnScpEKQGQhgn?@O?!-Rg`xifOxW;>d&&7S(M{%6ux?R*#`&E9ITF zh0CJql1WjoQw7il09?7C&M?mpjUfgc$`o*%SHpWPDFWb&)or)IVA7dXv(@upf`5j- zMFdzkYUD>)@Q>-^Z2L`@p&?B@KxA};LL3;b*AM}&FK=hO8->mn9S4DBIvGW~?}clQ z?YfCP9QdA$D}MsA9cx&sNhtY029%6H@FW06FU4YVyM!>*PA?FXEJBkH;P>5lHwBAW z0uN>)OY8@_1uZFtriB!~>RM5%j@ZSu&}CQh(et~Du!YojcZ-fAk@4WEAReW{{!r|c03%~~>cvE0D>NY$bRIcgRR zuvofcA1O#)!jfd9JnVA{C9UweHxpBS*;-HXd-7c}HqtPAGwz2Bn-)tpdEPJ=mNsH{ zRb$^jN!64Edoz+RA%$<(Kp`Q!8#eIW<$j}yJ*L7_L<+Mh^_2RviJ2UZI6Zo1|tC*rY&)chd^76Fie--K@|_sS;~g^rbmAK`yZXSgZUk& z-$mdVq>3o%hkZr9`CRoM_Pm5JD?#XUCP|!SGVVINR^u`pvrHA$CQHN)pVV-5ybpHx zG_S_6p!etgI@&ub#HhIq&eX(fqWWGy0xA^LtnP@a*?J92`V!>Je`ixfmm+mvJJ8L|jp9hZ)@d2G9ZPsRSem$;BXUE-ZY&sm@kNjO z-CP*kP zCd^>z=d?et2cdAr|hBFVBBX`e;#mKbjN3*Hp~Q5V6>0WRC*4Qa3^GeYM;=PVq%5!a7&y!#n!l}w~Bwb z?gX8wlZO!op{YusBD16}m`vjUPzcFaoV}9!h_ED#g5LAf%^O;mrL^aRz!VM?o-hFN zMHDR zu#v`;ZL7${mZ^JWeBHEIIdcWJcik(xZE78`bbP=dW|LbL*_= zYqy0vOS}-a#M$nA|9}7RFo8R2p4@e_JN&>&+BqW_c?IUf1W90+;v_y zBvoFPLmS3S@|srW3zL*cc8r<-yX_JfV-yU-`!&?%9cjNvZvXDSh@$69bH`Q;-p0@M zxS#Q-u7e&mbW@)k{#o&t;geP=IL>3DJAn8E&wz7cNp8|tutyDXUE4QjAx~%a=915@ zAi$srSN#BPB^WIJ`jHM%fmPSXzrSLZ;zp$5%1CgA{RI4!r*0LRHg_aA9u2;Ruik&# zl+>9k3=)z%>UMTs*KoL7W->9oXKE1>rlH+E`0~;eGhxe~?Avrb`ssOCFuwZ5le!hL zRLDuEtVj#J5ZagDg{bOlol2b1HR5uj<*+e?1`(}N_zO9ZqO%PFmPOO2`O}=|EW8Cv z3ZbZi)^9&;j=9O|cnLipx)iMFsslXL4|7*k-<$+V=MYi?`4|va z#Nwc)+*-h=&tS#`_B_e=A;n8|80(+)j5%7e#MSKLR^EWN&zS`4$P9Q6*} zn~GdPDqcwa0HYNAuFpPIAKWag+A*EH-hIL*TP$LVnXFtO#XNfbQJvW%IpTlz>(930 z&h9%wN%xUi0Q1cG30Z6Xnh$-;*AhbispXPi@-^x!U7wEz4<}N@2fHnm^jXI-y+H^d zr{c1RNU4xV&YlE>-*M)*|1h=h(-=lHtyOQ1(%0wi%^T3Jm zC?aO7T$K1KoLHp}x$=eLr$GE}RlvQ5DH7sSovc>C?*b(t7Wh@pCk;1a+DMR^fj+1E z)@fhsOJcjh{Fdc>Q=B$fE{K;HP%q%m%O0OPSKYx9^W1qCpzJr76!pjVdQHrFUY=JP z$GVJ4le2-E3t~E%%MewO^YTL(gHNhrRdj{S)TU@sIZgyP-T`)0MA`5v+@+P;vA27o z^QE$LYVXOamPI;NPl78mbP0{Pg~UNGf2`v^>b%X)C&yLyl{? zEEK6k{ms1h@r{1Kc^YOEK^=Fu$B44J1^el8Y$#*wVgHXztZ+sH0#b0IP)S49XZ3OY zn|rzQT!9n1uS}?au$0&He6AM`Qlx)XFqE=H)?C>PBiKFLG zihZvyLzf*kg8r+YBYsa0X{mp7MJcs@PlqxX%0O`wZt!?%S>MQ*Uz3(?vJU133vw-q1WRJ7RzS7z+N43s6*2 zc%W!@{(YmAy7b5lD-AiX?%cDZGrv>ayw*E{9~bU7WmS)|e*p~3pjR4QC+I@JYrtE} z%3qHCp+L~YhnDL~wh+cWb7Qte&C(dRaIHB(iLfS4B#P^%`R7J~93H z9~;KVUf69+dT07?W0IH)wT6ySN4&<|L459C_T+Tm$?uoOhB@sRWj=NZ$6;m*!!RY8 z74%NQM^Epb7|c)XO1)oEZ%wQnN66o=mOX<}1+et9NbbE(nb-AOGPKodM1tY=@@Of5bEF$o=HpvQyfN;kc9e@s3KB zU7U}jIc%{&18*URGxFtD97GuV=S~6*^XK!ZC-r9+@dF07^{#n3Ig{$Ir{m;(fF@+< zsB7Go_f`TXyrywB>ibtXk6G6wuwKyO(x4R9DP%8Cll(!vs9B}XK%>}64P!x4$5^8xK-7ZfjgP0m4p3-FLp-C2x8o0v%PaKn=L>mq;AAak_kPunyKf zE-SDxhoGzYjQhdCCtMhpX~opxi`BnGd&omH1hWW2LMSVG^|;UE$HwG7qI>V%eMCn0 z)X+PB8$lXKo(I}L*-UO!5fh;f3QV5S-Mp|rwgleK!jS2 z_sLh(sHa!e`vcueG&~!>1u;vlpfTL^Ec;u`M)_P1(C6~A zXus-bFe2T1^d*NBo4lN))G#Qa}Ib-pWeCNPtR7?9KPR1vtrc&`l@)v8sNN@D*cdW)Ahh6FqM-?dxJ$7zLhStJT_NZYTaPf-e z%9+FYo{LC}KUNBA7^$}m0(M69Y@FlL#w5u7be`_}-)^%k0>VFWz_*xpR9^tB=44Mi zsM*c!a?!L|wfMUeDAHO!b=ZVSvVPpXF*#LPDDwUKiCqJN=HLz|`m>dzfaE%1F1FnA z!c!1&jBb4(?Q(DK+7@r7OLmv#X{pzhnLb;lvh*dluQ!R(4@S*?th{$V_|oS8nk8;N zq0dawze|Xr;LWqj-$C{6t zS;ZIp>vb%ZHSmmG*R>`p0D1iEOYpImZP@WV2F-P5=ZDR`V7890f-g(rX`HJ8dyi^&o3&Xc8&E7petXxNwe-lubgeOacC| zQh83RZ+Qv=yx0>AED&8^u!B5E1%Odky{Q5u3{eBP?+{^l{t7qI$#*=pYSgH*pR59HDe@_RZ$= zbVm;t%7AI~D>&Ni)`k#rxWD`&_I}s`FECbvqzfZVqx+6%?SU?l)#6#{zml^wq-P?L z&Pk28fVjo0yu6T4vH95PV;j7UT0Ps^K(A=~oM_ze;CR zC2BT4dIc@JOTas>+l!nut%yY8gJ(^JW8Jbk@a-&x3MIQ zE6KpVuK;4eDAdX~4XT>WJdXHg2s>;BRvuL*PN_LlPH9Q8yPS4Z(8a`EGr+%Qv#N*8}2Ryj#8zsvr^woQJUJ>5?*B;^uurg?L?M@6*m<)c>=L6*#34W=kdzq#^;;mMDL?h(r}?vfu#NcSp0 z>(Abk;U9LwW`GaQewS?Bw%R``%`$E#G(mAo@DB6J#klU zc8M4+QiZ6d8u1WcL$?^xPs|m)v>%jr=cv&}^T&}|XpM&>Ig>yBjay3CDPM_A&|1nG zclAIl)}XPDs$Va>m{WI8qO#pVjU!`j#gmqW?-km&TIHv4X(cm0p(BpGbu);Pc7^7N z18s`No@J6P4ylKl8A=TqP5Y`s%kliDH@?$(5Qi2nqW?8QMAC|dX)N`@=3dle++ly) zeK!QlRju9DqOVnaPS@>DmBLk3vqI9x@-&d5!ED;k*ixgfkH`qJ*XSUbp6RO+xK9R`|mI^W&nL< z#>VMwgy2U)ZO;z_6F-cj!-Vm055DhXSUK`%Yd6{M&$?5PqvbaorDgTZ#;Dy$tKpmK zYUT=Ci3g$5KMv@~H=*&HZW8D*BLK`G%n@+lwjmEx^e317Jz4n@A?FI%ut(cdE1k5r z$Dch?4nCQ8QbK5V+@n<@2D-Q4qXE|sIHL;1#puyhHk8jK#9p$~MxQ~Z4yP-zT^kmqN( zp@Xb%sY9V&dX31o!G$ihv^_O}D8|Yi1gGMplG^px3Dma$z1edxbd)XBJZ=M6A~;Ea z#z<^K1vmVeb<&@97dokSjWI`+3<*%4G4ayGG;^q#te*JMDx<0S`3QUW6ic@$G z7S@0HLXl;Qw*b6Qmb>=D$_wtz~AL-dl+3ys5lC{@moI|NZuADysk~M}! z>v~_Ai7YvVod?l3xa$olC$sbPEZYtc973Kyxof8o28G!qBnfR-{&@XV9~79UyaYBH z0pwC)^_(`s2#{mOX;&8BM^ULr0%BCyfg^=O%@ld_&;7y_f3$I|=!oV9CViz3v)Vxt z-R!Zlw{hvya_4=f={Dw}yQFyb6kVl^C$FDW7qLGi9k4PA>#?-keF&amW4*Yl2j%Q3 zRv-PmUiwDr`A`VvM*ZB^i?}W>1jV`XZwDUBR6={6GL|8ih?Pq2BD;b2ezbMNo%GQx zgEsiG=^)U*@olcB`iXCK-(+UoR7KBNkM!+`ao`~*E?yrKvfiGl&Qab#cIT)%3g$?S zr*iRe=D!+md2MTLCakap=kFZPT$xAxTl9jyZgXNhvPsY7}o5ILdbDi)-MpIQ2B;*(bkec*MB(G-=ivHT&1|`10@*UUTzGVHx@jj0c&aNDv!Jr+FsEXssZY0- z1uuT0*A)Vrd(E4>XLYJ{`aZL?mIzX0*Yg!coZoPw82}E{{xhFfChjRsRmXPj4)B|}STo#7T`9;4n9Lb`l{Pnlr zJldC;!{jmHr{7c9P6uCO#KE18Y-?S$j%QNsLLCot`419gkHq4PGJ^Ah;r2ry!o-mBr=m)0V&_`YB&Dja_fIJf?TTWwpl83+RaVD-W>z_ z0o@Xn4DkbY5I6^sKQdk-@^v=sk=%D)PJBw}+7}ZkdobSjhHn$_L)>nYG#qaGW5IFq zSo3~E6YDc*o*XU~kMl7G1&6?c$tv1?KG8NxUiN*xtRjgZa|I5o%KahU{CQiM568ao z9IsOz&c;TgDrPG`UtPDY#hWPA_DNp9sZWo~^L+kI{35H1$^{$l%j#briXSv1lJdDm z|Dbw5`0Grt5#uXD*uk3bSt~4rgYuv3ACf6{R@G-Mf6u6xIe)Q~$Jm%oi>AmOdsF}2 z+dOWk9cmrLAEQnR&rh8-d=AlC>)l>r(YY-;1sD7$vnv=Q)%oqr<`F}x(h!yk|M&>~4WwnM$lX>89zA6{L>;@3{rsyguS>?nJ zwO~B0K^{H)sfWbkX}C}KSgQ?R-6WLCy0d98u*ZBf&J81wz zwJO9e7{Bxv(hqYY zaaKgEMWDEwtGLhZ>=l31aQG*5wlj~VbXNk!4?0b1Piodu@jyC~1Qv%FXMs9J%bxCM zM^S*ecJC5;K-U|tpN}wBTU2Vy>6{fp9Z9S)#S9| zYvLf9SE+pswAXy&=81};Hy7fz3Liku&*c5ywNF25pn5v!jrS{4|8$JH-4Ga=Arbo2 zn3R*QIFF9zh;8^wo!SR_U|8z0wVu`Ie=jMyC>=`eQ30)N6F#aw@&;$l)M8iK$%FZ< z5UR&o%8${kB!M~UDnFEReo=g~UZiT*FtG#97#;oiPY(9inCS4g^N&(p7``n0+!KYGlC^`nlc;X( z68c>ux9zDkXh{ZFNe~L!vSZsgsqLlavU8mZj6-*PF{QG^5TtcZ)S-!>MzQz2Em?3aa}$>P}32 zAKZ-mj``b0mJc9U10S@$t7`LC|LE7_dNyuUHhCQgSsy?>8$JHT3`bp0;Q1<-u(F6G z!up$~ojTS+#nYi`Em`Ckx*Ym_eb)e6mhY9WGWtV$$9^b!Xgv@z>|y@45BA0zHU?n8 z4P^&la}!dJz2oNjO3P!Is5?VVCp4^u+TnL*+0JX^sTQtARPWT>F1M!w-}TZvYR?N0 ze9fTMd423Iqa;2uXk2kym{;W_U4+@zUK)@_xf{5RasS)SAXjs#PWcP zP)bE3=|HX13zkkK4doOLwp0H@I@6`x86f|w(do@(k4^Mr%Ws3&6JuZUo8yWDhDDdo zdh5lTc*STy$=^-=Hxr5e9Gd|NnP>egY>z3~$9)9Lv(KmiPK+CgZtxpU4yS>=!g;Em z$ti$E-C4@)Jah!xenUfLb`>x-;@zq^HR+tD^uU??b@BH7GZTKn&6Rkv$aDUR-`(m; zZbiVp$-%tef=d~wj{@uu<6>@{*naA>LsywbSpMiOT;){C*ek+cM-GyS={)YcK~3eC zeAd)Ykh&)T=*J5HVEhfQv+#iu zcfc2JgwGxA*~b|>#sFZtZ?GTUSpUulqS7w6=)wwK%{?J&}Fm?^=uKrSB7-{081JINdCaiY<$!7pr3*tQa0UJv!~r5?cXU z52}heTsRI8r}=a{H%4GBuyelqP~>QTqT8y-ezXkn1B42>y2h=1Q!*z(CyxdMPgAY@ ztpMo;391S+I`85bDD^(cYZAD1MfNq(Jckvxf+)Ml9{M_#ZGXQo$!AIB`+e7c#kpjj zy9_uD4-ttb7!e>K9`Yk3ifQ@Yb|z|KvA8gcGx;7?hZK8)by%w`Du9%D8dpU5Tx|0~ zX{PU-#k=bxJe*5>J0~%8h3WbFe!S=!6%h9RpX`GSV~4{12WV~$?WZQ9Pr(8X+`NvS z-82^Sz)#qKtiPXb$O?khcNptqiT>!$`}EXeuNf}|CKwFW+M}Nz!g@B)sVLtHD0ume zbdNLmM6{vdbJq6_7war%_~Y;B0z*sjctZXa=Me;srkcnT>b}^)>H&Db2VltkTe45&;3yn(nMd^bORbWzsdgYbuXQF@3YhJDm{g*2Im+1W!~w5VA&)6!u4-o8{6oVu-=(VH5h}e z%B9H<$mJ#GcV&!Q+FIG3OkcF*2y526iX6<@07BVa{v7_@XLVTkMDZR+vZV`~SE*JO zPU|N|gQ?>yZoB{9#D!v&4xxfP@vmLo`xQK%#rzY%A)p(R>OE1L0EF>%S-d4+H@x~5 z$|8DNv*&Lh2JP6m;=JUTI?}P@C`)WvprRX{;P^{?{vkt-fL&8^nCV{I6T5~*EY)NC z0Kh(Ec>3ebokE?(4b{*5R?r>f`9A6eC3k^yR{7YwgC@J z4Nr08$okcrn~9X$RIl4}>)~`<(LVYE0H<;Dzmac9DA4QH=)nC|C!c-y$d(^I^qlv($tUcb&6m{}k_CMR`WfD1yiFqVE{vDf%Mvy+bBo>up**^|qr zf0;ZCHdOnkQSqAniI%NJXbRxABYc!ij!n&Wsim`eFAC3JH?^m-N;i6_J>f#n?~9co z0U#&0)Bl~=TNZij>Qo**c26Ok&=*VY&4P5B53KO%;?1eu#tdMsnPK;)>gHt}f~dmp z3ZAKTGrYC>YEAUyqM;5vCHdPyq{?t4yi`wxFqxYWhiI=GVz#bAcUUIimPX|2P`z?&CyWZ9Q9WV;+r-C1_|f zY?rgcxOnwa;D{ox*N|x(OChHys*pke->hVg?Sjgdv}YAsyDhfA^=%n9 z)}PX0S%`jsv94e#2;aL>6$+ffGVbPXN#+pM%j~z?!*Gx^Dr$>+&QW z{_gBgeBbIaAHp^=t=%dPU=$!>m*bKDXCsO@C#kt94IIKnfeIg0p-#HL@T7H>9$;f) zEgZSKcb$|{``IY1aBC+GXDMQj?xn+kN2TpoD8CEausrQOMdlopJ-Gn1=08hekn>|a zhUM9mv+Ev6c>YTPYDDx6d}{#MQ5j_T5eQ$u=X18^N!}eCW5!yJ+KKlg=NDv}B@9a^ zE**0WzHgJnRl;V?IihIDqhPTqV>S6#$eJM$Swvqoi7our`XfCHGkjIVeLS_s6H-Zx z5Rm*Cr?v1^^5mw39q+Rc|DPD}*jPr1!!?SFXkA16t?H6O+6Wz-vxG2Rc!uIS;Nodg zXM5X2(O#TQY}LAAGlhTx-D`g+ATgAW+@-|B+6+)C)wA$q2B*?o$RO6z(H36J1Hy>s7FIaX8l)tx?qIAiTiI= zS6C^_B!~BDcl0%%&8dySd7fb_er*Eq$DCRj1SB@4w~JDvhUWSFk+IPT`1 z?CmWbP?E+G{KCQzM(Ex=#Nd{O9;&-HV0#zgX7&3D7fW7^XU9;*6H}S9_iC*##Y|t% zwvJ71ODWG_GYmi-i~>e{=q=x3U*Aq;$v?Mp%s6rJqfJ;0%52<=raBtk1BeP=bYDkqK6EU&!c3Gy)3BzVdF#GnXFIt>%}_TEj&<}0zD)@>ROjoI z$ef*9ws7xbLrpwl%}&lh`(;9HQ;LRcy59#+_?_ghY<+4ZJzm3)1K&@YRtpl`S;F`% zU9i!{K3&4Pfse%`(M@Hf-1BqE_gqN`m3Ed3 zyRvs?-VIJLGO5#6?pS=eyW78i4Uhr2r;tz_lt^uv{(_TV#fM1Sj)-BGLQW4BJ(3ys z2$wg2%1ErVQjWUDl%mZ1KUEp6OolYO@~@fF5;ADYaXJ4Sz_2QlR*^2~RSAeIuR=kk zNI~>mo*EXu&X}oVJgxgM-98bp+OTR7Mie>OV;3Am;jm(l{zA@g62lgq;By>%E+#NY z!fdn;crj26h`D*1%Qme=>opoa2<9YlR?eaT8n64pQIF;M%VKN7K1}oTpJb+R^G`n* zWj@g-(S|;G&3VoX$Io$J_WvRZWoEs*Em%$a`arZOdwEu}l;4ORYhJI{GS zqZkd0bpLgEujsq@`2chai^cXOUq;ZQA4`v(8h17L^s(yVs9cUvMFsx7V;2MW)HC5Y zGIn*!6e1ex6eW-|fHflS0o%!QK2<>c-oK1Sq35u=zRkJQG*-;K{;l3ee8qQVr2e-O zqyOrDtZWR|o?a^i1)j>GN#W8Rv(%U#&KP=G52VSg$)g(lJ_TKmEg(mZPkzT|hAjD$ z+ZJoPD{42(8(&KtRwv?BWuotQA0hEJD?pmvEZ_%CxT=#einuJUi(?hV_a;~p524hveonM8R5^f_R zd;SaUKoY-8Qvn!g;J)CxN(8tFlDbFl%0(04bGhHR2nwr#sL6X;G{>`cyFl#&BLXNW z0P*y(?CtBdu7*}}*EjF+oKZaG5Kg1a*}Ts5>rF*~t$DE$qbmjF2gIy#f8JVm$!25)h;j1yihmS#KWP%%4EQeVwZac=?HI z%)Ecv%s-0$s6^&pKMXLky({0xp2HG`NAnZ^cJzOUH1AE%T87zmtzOd=0M734>W)f$ zx;IWBRn?V&x~YCBsbAlq@klK1vu?g&?DOcGvi7wIXbla>H5*tdh?w?TM935N`&7gd zWMZ6Q+Z(+ArCSdMA44zh@ISxCIDmBK7A$Hl!p`tc`je`4gILMt}7lU8ZqT@ zszg{JBFegTnV*SuGSbr50!DP+a23H+Dm)9YsK<}h6%I;0Kg!BY>%dGZc=TENhFSw` zh~$*ll_fk9@N;#+u_rd|A!(~u(iwM@*6C7Njsf!=xH6EX(AVm*q{hGdJ9ha5S$u#L zDd0&5?8i+DVBWHGp#P*(3Ljal0&IHpj!C&s6PX=Pm1Qj3S|1&T$G`HKj(>W1ae2e< z{yi)@=Zak0){4;Qest^t1Br4z13D!51H^Z}Hk94Hey09zhIPG7{t42`t$tvJ`Y3tc640&zx0E@4@+NjZPVvxW&f@^SZY83u0vZh zJD-8P>3ve!-hDLd?+X5?rpzB4rN77qSizO&%t#^rNh|QPi6z1?(crBtTOcFnxYg`VMSK-|GJ>r3r<| zA83#)ucNdnf5WjAaIuMxET@pbom=R3+h19z{X4{lKJIxKoj=D^t;b>Fif>00&*XEYFjmdozIEei@POolIFf=0TM z;rp?+fDu}X40)uIHZyli=9<(egcC@3#03yms+YHmNvg&m$m|;{AA%6nW{6Z1S+9Tt zFkT`~MAJovwollADVhCS5uuDKbsmGSsha&ynE+tLweLx_TWtqTwc!DH`2YNVVMoF7 z&ZhH#YFZhq=m|QJ)qwFL{JcyX`8Y)bGAF zmGqawC2++^+)6^!B2cOCKdtnv0skiAU*Hh`9K#>X{trFfmGfY91XDyj;U61{z4Ns&>H-kbw0fF%GDI{pwd08)wTp`B>30eh@27eCgt z5|KcZR^x!~-)*iBlfFDKB?17{132aKw{-O01Fx)t$G-e&qq;TDpgq}3^4 zSJFD{MQnak2|z$LWIf~#l<7SXW$~V)3qc}3mdWoKj{!+px9-D}vYsBzQvgPr+;K`f+%9dr1)p9KC zaJ$tKn2gKD2XBExTVC!6{L3@@;p*T1du{BKR{Bo{1Zfq4cG-XPaZl$D$e=aR5G<2E zh$#uWfe^p`b&EE%ApSAizZ&)ZtNX(=d{0-tj~xe*l;;rtV*36nX8MDVZ|{4zorXYQ zY#`|`1bZa*1)2+Bocj>W4FleQ0=w2>z#s{*wOJ3(`qLBv0-sMMX%5J=w5~SO5sqSF zK(f3*S-cUx(LqVQ;RIRW1a!)RRGCWb(n>M&YsvUW0txbzY}J4#I&67d_%teLMi0xS zlI`W|iUFGK_$4C$3H!XD4BC(oiKyq!!;^(C@0S+iajb4_RX~8{7P#PQxBz86>t7;W z1(qz1xB?;yE=Y9Y6Cp=)xU1e7N@{69z>IF1Oa0i`OA7= z-gVVShTysHey-&&s@0mV{Pn+pnI|qP^!f4ZTv^f+Uv$6$Z8!UmmFPibOcAIIx!(wA z+}HAJ0w*x(hh5M&@3eH{UxTxM&isk_zx$r9oKM=lZ(}_^@1G?7ITeAqE8d}X0s;&C zRRUr|_S1cTJ@;R?**)H8a+wBhwC|+=h%e@tRqJ_vl0eql0L~1*vG0|-2hY7_DWgV> zb`?}n8^C_=?EJ>k%cSMY^Xx{PNpjtp-0Mj4)+5zI3OsxQGR*9`onR&)d^=U3sPxTX zRwl6>T}>d8q9#L<2LflvOswPT5UvCPoIxIJFTF&Jds7L(&n^*Zk4GiVF=Zc$=-l%h zqN4_A-?#Jk7SxH$A_&AS@cU6f#@`u_QAXlEU{wcW*FXDQl;N`%0hsO#=zs}guq2?#HG@9#pXr?2DpumABk;n6SrH;|3`lq$84*EUV%YQT%P{u@+} z9LW4BwfgC>1A(jS-#II{ikR5aiHf+u<13BA^|L;Y%;6WAeC&YAaGV1iT_Br z;#{)%Z6f|5=vN)NUsDB8hhH#%cVR*BS5AMQZ=Cfr2O!YOaXqkQ_%u8Ir3HGNK*|Ws zG)Vr}R08;TXpI0Z4pbvsT|q4cY0K-ZyU&~e_LVm!eW`D38l&O&{i+1lE0 z^je{8FksmHCU7xiL}8Wpz+uD#Guy$l?ti}H7RcEz0~nJ$xx_D=`FEpC=tqiqpe%T@ z4KIScF4HpsPB;yrgn}S*As8%82|lvejTrYFOmOSLw~Bi_F~N4k9%uPVIn@HUWe}gF z;K>8m6vzc>pDG(MJ^=+j?r&Z=J_)XHkXzzU1NV54F=4IyV}9|SQUSn>xeMSmAO7T+ zj`pTUZ-@K;?l)m{;7Hd?>k}L~7TT^cvRTJ%yE@?jj2zw%>+bqmN1uyX|E0h92kBXC zVkSTNSQ5;m*Cn%juorQ5>C+(21U|=nCavvYZt)L{0td$p&J{!I6a@ApsvnBsghR@}w^L*L5b2z={Mu zli_>1-_Hg=?4oDD2J8qPgR!8D-*5{WRt8u(4ssd@0c_R5CCQm;fN5oLb{&b%cqIQX zO#6lak{Rp1Gg>ETeU);L2IjzOLzv*kw=?@VmWQo`a`>+)yBiRv)_|1O_88Z%NAG-( zOnV5pX}r(K?wTM$6CnlL4@cxYZVzzl!X@Cjmc#lr^jX}yHynY;9gxfO4C*GB5Du}E z#tS{+1UbUG@o{z@jk?Cy2|~n7exhnaMC3~<3W|ifU!MEz5{DGvp7Om^Z>nwZnb*A+ z7F~E%N8uC~sP?RW3hw^@KLm%jZR$cLH z|6O}bvcvC*S` znv`aa5#DETH)VYjaA^I05}U!`fy>EU{56Co*WL4jh7wKlxr}M&4>!e$6{z@%gXM<=zJn zpZRZB#c)l1+{%GWSd2Cq1tmEGTFsB;Sc6y_K=;8LMx)*(8(z6|XFBmuCCz@Q^gp^g zJdg*v@?CD*7igD07yOliIN{I!3$VI+odqYu-p9HBsuldHQ$>JB{uBjWHDdjH1o*6+ zr>6$OKq4R?04?d;Fk^@s=(U@m0c~j=7ll2}`g`$&wN&N+OxW)WEJ@RIdd!N2fvnZ7 zX%XP_4aaQA$~4P}Vb9+`lLs{c(Yib`Q7$Tja}S@D`nY;j;@NzqEMbuK2TCjsgu#m| z_(R9gBO;#}A*@ivp=>~#rG95-{T9<2hnQ{Fs&aCG7#_l=7KR z5i{=Lz>fJB5Cs7oFf@q)waM^X*^L(n3RI4uU`i+ic~cR*X3Sd%*L?gR!t0&i@wcTf z&b3i^{L7z)mH+Z5P#rot?ts`$vfOwB^jJ-a#_O1^y5m2fUL7s`eV1p%&|Wx6 zkjDlC$lR}(u{q-e*oc*@x$li&jEs7o;rEHn?~%w)nGAf4!d{sw8l?YEK}uP>_xUvL zXC!kg;d=Y}`e@sU?eF=d(#M8BWKrw@peff!rF1-2TpeH_$5w(QrNyX463xzIki{(t zKJQ(!9tS;kLO?|Cw}FXsT%&)LB?MCL=d=|Z8{t^FU$jhmQY!%5W+(_FiGZOK{r~9z zpws1~b1#R>fBDZkuGhHB=0|UXJOAvxx-~GKrTwWU8#x{%!J_MP!MZj)2pjJECM17$ zd5-FZ@BA3_&ORacKDILXN&dc*frNBqX|sF-(z1W4WD{91(gAQ=1rf+!cJ9J-;vZ%H zmiR|OfJDI`+33o5`O^)}#6PzD<^H+m0EB<{&X@r^h8LLtpjtD+Ju)D2o&g60S_mdeQvZ`)vW?84NKJ(D=dRP0JUgj&&UN!n@wxAKk7Y8lJ*5>0)YF`nz+bhLY>vq z6~u9nW7!|hlr;!bpn=c#^i(v_5w{J28B6r#N=5%|su4z*2ZUSlHWLWkySL&$WCx6` z5#dw;xU{Zc%MAvakYFJnuBNac5oY*?Q=pO4%=vqcBOZGOs7TmIkaRFR97ZF6o)sA4 zZBax7_!ErWE1~f#Xr2iW5)aR_Tg+k0vJArj(!N*TKGqU zpD9lVq`=ppNdTeC>J6I#nsL5JCOP6(AgQoH;y)4lojE zCf`MZHO#$`B#G_e*@RE0^k)=)nQ+rq4;~``hflY2+~S1aM2v zz8Zo>r=Fie{F5z+e|L24TJ^~4n$7T2iGR)*;M~RM!l6Ob=r>Gu%>J>b&-<;l0CeA+ z0%a|7rBe1LfU{2PSQFXmIv^MWiA4VAafLxbOfV>t1~z?c zX$_eS5Ho*J*|~WRP>58gOa2B(F<`(~R4PUffP4K2+S@P|4T5%$G+*U-M1v*e7eg~hk zzQZ?&fZcO?`JT<{SrR!Jc)JdLE*BvB9t&Jsn=iI)UzO$_*a5Um2LSC#yq&)PieLUS zIP)#{2tRjssH5D zVCidrHupBQ-(JH`(ekw7l&R)wzUly9T_DeVS>Do&JGt8- zAZzf9{B;i`{7e-2~WahCf2IEc?5<0mti7IxhAi{E}Y#E1!n)jG3_d)|HF(~)VoDKw_nC3yzW0Iv?u>L1M=qSd%IQ-sR$Ff%0f0_;e zgKrvZILgu?HmA=eV3lSlq?!$U+}?k*Z4es~ba#BdSRbB?r#74YsD@Ge)4hMk^$&@E zdmrq|caJ3V)!P~->xZPxuS&@O0#p~g9qNM(Q`&DB);{*d8zjriQxe*cCt%B60&MJE z<8y(T{WpyH2HE<0*6vrYL!0|}cFx{p`yps5VSD`+cx=2BeMg*0D&bj8fs$_+iJYCA zz`!$^xBgC$Y!|+-edZn0) zo~9%E)c^J(10oi}G46b9aOB5qfU$K_CigxOQ2?#HE|X}`NC>MLuKONm^(HfCQ6iJg zV>Lsun34m~x>`e33XuY~QSjl#ejA?a(~wjEOl0b5dj9S3p?#iDt*t#}JGN%Nny zwnEH*plQN@O4}i*U3~6R>ifH9KQhYpowC3Fd;b$AIOxLiS+!4R(PTRdJ}C}5BW7t( zLs4;`=n>?8?RsG)?0V@**uD12W_8hVxMWTT00qFsEdk|O&inaah1m;FgO&gCNoZ6@ zy0$IDhxWqb|N7_f@||CWOFr-iu;h|Absgkett*eB-((2}y1k8Cp1e0M{G;;ys?K@q zhjM5CElqw@oZy$1{WrZgV~QZI_aDjpLM*UIzBYRQMYEp`|9@eM*$)!`Mq*jN{D5k)4}zRR+45>rSR1U`n(AiUP{g%dUf;{qJ9chyKs+!{ELhUHyU&Z(k2T`Cq>S zC!KQ{yyk;{1Sgz+LD#l>GJ<;&)Qji?bZdV$J$6U&6T7DumS1yY?(Dyv*pFrRXJr4b zTcAu4`r$T0yW=KV1jx}1;Ow@fj>7yU=Owa#aIAjo&e&hojQ#DpvrFdzz{)kXxXF)9 zG5*2F!p-SD@q8FL2u61wC2zw_v>_YX^alYb2=@mwcs@@I^PhFw&B6*bqgnnc8iQji;c|(m2Mm$UBcdEu4}u2k_?Juo$N;M=4HYBf%lre{K1mFJ(*iN) z2#y)Dy>Y;qI<_Lpo;`P1zL&3nGiLNzZGHpxC7X$J(8(i?o);1HxP@X*Wm~ov&82-2 zJ;uRo9&LUm9v02?A){r0;$hpL{RwP;`a#&WYGs%hj_i0%WBHtJ5CBJy z9)f+YK10szfB743gmT}ku4Mno=OWGWY)jzg&xt>l%>Jb-3Zx>2dn677aP=8shA-x& zTm1&rNbDyt|Ej^w0F8mJoCoW7HguOEjrhkU|9~d~!rOBP&VyRLMzi-=t{=4}`bJ=c zc?4_ednPl-y1dOMNsuO>{=ZwvF;zjSOZ*0445$axtM)xbfmr_XC=pk9oh&Zn1)(jB}tNx%u?nnvpwCfR(i6b!eiVV_%EG3sx;~tdAe(9=U#v zHW0$tgd}mPbzoWxq!>uTKN$G1J+C1E`s9FT_z*(#7KqfR0s?WL0sxxVXI%*upS#xz zh7DHy^KCZI(#bgP?#lOyQZp-_Jpf!YAO61V^n_EBPSvL-B9_%C3BW^)S&~w=*!MeSmoHKZOghf7bP&hNE#ZB)$cW zss>U7l#!7Ut8H&w_5w2UX_)*xe7J#Ns5p3NXkZhL*pX7?#^;&dJpxpum9Na4y>9?k zLZE{tj|6-4zch!B;EJE?k=tBUp*4g*-S^k7dQo|tzyC_5R|gE3i3{Jia=Lgyp*>AW+0f#3%t1PI6kZAM|w;MBkLF|8{`|p#?u#p8e@8`hAM|~xl zK;F7>f3^|E`hUu=9l1uWnirQkT*-^Pa}! z5D0sFdvq{k0vxI=kfWV8_wmiTB)z`eV_E_=>g97*jsN0$6tVKLbV8ofLZVL5pU&x5-&iP!$!7IOAKHrfpUnQ_qhjPB5$FiSodVnHp&`iI6*fT1m2`JN68NH{vABEg?nzdug=!`o8>Z_)r0S?!u~ zJO?+dhi}ySqOnPBd?bxzZPbgF#0xO2^^zUM(*t;h00;FZgftgVU^@C+Bg2_B@yobH?wb!SX*NbHTt^EPO>b|?BdX>--kSRI< zwx_S+pW|l#5f?y0zc>stm%W1&{_1Y&0HAL^$k7la>(7zsk5K@WJMZe!_k!7f-J1QV zHpD-eeLf8B<$X4CyjriH1aHu>4@9Kr&ZClS)}vw*64K1z`-X9j#|#C3OkX~7_jAX| zNWwlgJyg;Cb3sHl;E@hp>Sm&!u!b~D_A`H%wMdY%K(4_%Tt_4Pucu^)ciNqg$_Zrz zebUlr@G}a1W{N?V*Xgw$wGw>d3Gh6=??nv%$PPDBfQIC4oWJqd)+Z;Xeq{ zGO+51dMcqz=dTg)VebPDa5yWMG_ENO$-)AC@~07z_TpK6vC+IZAuz2{ICA6wYoUg45slZkWCB)JY#O9qX3CzO~P_jGa?;{}-GLa~3aax$ah?KAmL$?!p&ScO5Zf zA2<6?lK-3vf+VutrB}QWxF>B#1^I@Lmu3T03H((J)WC-D{!mxGKNS2i9`MSV{8-{& z5GVd20zAEYneN4dHt&&S^5a9j5da&=XfRZfr+kBijxfvB;xb%Gq4In{7Q5D#HXw4p z;Ig!G40@sygJ;s&&t8MFFQ{D4Vm{J>z1Ft(BEldzfruvI`CM66x-EdeYb?*R8(mLt zkAB>i#Hs30DhYD*Gg&?E502hxe*>J3sQ7lI&Svw}Rxns(WQSRtM^{j2BoXTmy}iBG zqX0|(8mRq+D+mA*sRjMLi{Jv;! z*u#%gm0M*pNQ>mLhC;=Svii86tlKh>p| zUK=~t(z5?p=ALuEA^?xC+mxAQ)4J2v9|3 zGW3m1u|#^JZgZG6ic$4*K^fZz?64SV^AkB01Dx>n%JG|E6D2*N;FHz>*(AH(0sODM zrux}LzvzCB>lg-xBSWxh<*l&xzHhJGRrGkN)E?IyfMmdt7J&4hbPBxr zkG~XV=s$sXBAkUp zSqHi2Cl+kSx~yDNZ%_QA`!oCJ!Tw#nCi7K%l_$}hdSLt3tkv%mpmEcuTfggX<{ivcI&! zhNnEOV#t7vAd9c5)vDp=^%#{s5xLhXn)_#BAFiE010d4OH{ef}!nP8?Onl!4gvecB zbwm^l!l1~+B4lzz3X0r%FhWSiwU3?s%xGiDo|bsm68wa6%I+SO{vkFApr~r&0(sic zEK9;StK9D{hj(m%_4j=TUU}q45LAa#L0cq~v2h0eE{$MM%CfMIW&WYz`3U2@}Tqmu#r118*tmfp8+$H5D$NwVuh16C-s^cFC{Ul9k;sJ(v|Lobb z6FrHf@o{8htLovg8rdG-w$f5iJ?Dr2E(sB&KTIzVfH(R$h}wph$9~UY3B#lLiGM2( zT!m+{;Xu$>U!xJpyW-qG@26n9W4=$le*HS?^_#;N7~r8&FuU<_pBvaThuCn2@aEe9 zLc8-3V41(mq{bJ}JbP5Z(V$4{!>WLuR3K?v&9X=4zUN0Hany6yLO@}rUMU-UV^2E* z3a;U6P_EYu!`~;Z|1xO+7;<4?0oY6~5K#}?8=mRimq_$S_vFcX*K9Tl7?rKmZdAHw zX1+Psa9-b(>3w^Cv3)O(KI(Rc$K#F43I;_+Jz&-QlI8lisZVm0Pqb9>v%j(M^S3|q z0Ia?Do3MB7Pg`DNMRTzr^);%?CI2G!!cztOUlkRAB3~Mg?VbO&UxO8IdM~{6qc6j{ zd%g~`QJ=8w9X_}RR{iMTV0CyKON*det(j!0dm_KL!`s&9{?T&j@`TgQ0>7s(6^x4o zC+!vcl*$JEPWRc4{?Uc4XU0rtb5sKOxiWzN&IPrL)DpdZkiD0K>bnAze^TPtehLJ= zmw-3$L-0l(N!<>_*r(RFCjJEu@o%*6LKq!r5axNNY)xspDd{Vtzd^z_ilRC8l{cev zK?L${7D|C0)AJ1boq&f3bGoljGx}^1Tp|M=kNW-i8e@|yEGGchugmQS&IEo&tDnd3 zOeQYKEf2|rhST8LDgX-$>HW|Z5+wTdQlL8PZ&bQR#6b7MfRV01BkR`?vgWM;klQ?!gDTbcVQUMj`4H= zaIBV|S+n7?5B(9G{j(o}=fCqg*c@i(SvB0E!a;e@>12!GjBDNji!ONmxU>i+V?XQg zj`f9)_kuGoP0het`75+i{!6ZlXV#n6)5QMIDF3JU1h_;&_azJZ`;7Fg${+wbEdbeb zHpp2Y3XyAGnC&ladq&DxZwIgXOn9fc#QNgqi1;Tnh<{ueK=6Kc_adEPo07g!qN~;F z#AJ`$`@rEh_`)|fy|ua}@PW45bxW<$lm!ESG^-!4`WTX+XF*>Bq+cdKd$8s8a;c)3 z|BB}7{qS!z-{Gkaw)`#8I3TXIM6$o8O#xPE4~=>B`aDB;MDGGml%rC-Z?+G`fV8lY zRs-y@EfUs;@Pr^^AHx;U$BhCPvxA-xC`dqj7uvM3mL@`YEy z$rrr=7GL}Zn0dm&JlDsjqU2P>x(N>erizR$F0)QN8Ls}VzlHN|_y|1voqw`K!SQMb zv}+Ocg(v3e*Sr(Xxb_#If5FnO9Me|s9m_h$x*XZIF*SZ}Roer%t6~q2))j2q zAJ|M^wITkksa->@4AU;aF+bGt*ZbNqJ!bxeXu}-fQa@D$G+{70&phYXq|4V_s&)i= z1X<#GzHdwPhIA;XcWwdzt#l`R>x8F?ahQ>Z_j`JJ?a34brsR!t`yRD;*os$IDLkJP z?+hD~odtVBU@vQzJ~P3f0T^2Wfv{zLY$ep{(pD7ox@e3s2qKq1Mdv6^5}3d%AVQs> zszJ$$>y&9mpSQ^v^9Xxh2_bA70V@{1=d?S7YGT@(K!BzxAad-}(+zHQ)Di3PoVowB zz&vT!%l{tTxd~R^`Bm8b*zLNq>P`-dHbMSa!9V$${za$4sh7P4mWHHCk#vC=JqTfPjde)O-JFnHFR zKLATEePdf~fvJ@JGbkR(VE(3EAo$ld=hU;Ja9ql^pAj!P3kQIs`Sd*7+Pd8UQReQX z_{m9uAc&I)=gjU;Apk;C_K&D62Z~#mDsN8HMe*TP4)ISl6aU!d55d2$e*x^<>>1X- zicDm*YadlbYPG;*+_sezSe)Fdcd-^bV1Xz`pPN_ zY9XMCk?57wb2qvIq+RcsGCLDIjPQ-=^E1$4hL4YjWsHDFmf#MI<20U~WG| zO-pwdN52V*36nX8MDV-@o-jV})CcB4h+cSxrGjfJno4BiE;lkBX?S{k9+LI+{f|8a;Z1` zyQ0_4L=s~ASX&@+!~O@op(OVU*eZI+}+(5GS#KE77_ z6F9((fta5-u5MQE{%!;`_w`%O>u1dN?|ku5SabWoTVh}Hb*yzp-;FE#oTX>L@+*HH zmR)&0%sKh=X~dsw6@c-`-pAr1n5!bN>>S-1xby>`fVKC09X34hU8oKnO+R7B89-56 z0M!PuDsakcuZMH5|0OMSG?CeQw}UWr0F)@slvDgiw`&$I0l(Z+sFdIQnr7=`clJLu zh|uIVnG#GmM8PcM9|ft~uxQTAgq}LT#`P62RRA@xue0BXsz%k4a6Qj%tjFj5lcYbV zB5=^Z5UMEjql{2bAZ0Ae$9}h+00;vEj|_V3vE{x#srczJJK@Lc#}3$-(ZP}iS{-Zz z0h#iIz*l0hGz7jz7?5*mooDg2ly0R&COD+K@B5_2XTS^B%Qn6U;Fv-8lzYSNVsRZG zO2PyM=*nj@{Ze?prlo)txdtj8>yreESi$g(qM#w{87}}d1qwgW$|d_f$TKj~!uY`Z`eo0VIGv#rA3vqS7jM;ubgR}>9 z#(!Ybs_asJHxr`7BfWFx!}6HGf>E`0ZI!PY14g^dq>2lj7x(U#Dx=!>KLHg4Mpd+oqxYM061xB+ilHL1<2b%V8uF>qU6^E^u+Gx`SWBj5t9=R@6Vq* zJD%ygbN@~YK#d*+RUhr_Aj?QC5W!zL{e8Z1*3TS(KpE!#i@WBVnSNsT8^DydZ*9i= z0&8X@DZ@9~@zVCBRorVirVr2Zwi*yg)sHp*IfG?@)mgs+n#1>OHeN3G=xp5x>ImZ{ zK;8r%Y3;M~o_6E2V|>PePgfm~Jm;HIzMjFyS$@msFvp`>t&wCfm-PKGcra~%Mi?OI zmH}>WSrB*|VIp@F5#NWw0gy#93-%awJ=3H2NtmeQCCc&aw#P}0?d>xkK*Xw2{9JR{ z-pkDT(||ViaCpqO{`B535%~%n-np?oSdRe;(%y>S(J8NaGn{eVFK9xa+tbf;J%2Sy zuuqB#z_I!w+8Iz3I*65CIQ^O%_3hBswXpHwTS73t6Y3*_lpUH2BFNT(fa*e&jPKpQ z?m2ikyv;oEWH|rE-+(jT^zN85;bafMh7ay7{N5RJ7v|3X3u9%X%-^Kh4{kJ}lztxiH|wi~Se0Q(+Ty?)I8Au{yQ4t~0|5$OW3 zW1)g(313%kSLEsUi*(@M^!V)>_y-bgJCPIob8GphObX0dvI5S$_7^lcx~G3$N7wY@ zZMJ_>1pq~|>Zzh}-?BY$>e+Dlhd&9IzVG*7`!f&1#z%e#`_}x_^!*^9YJkiP0=l{P zL;H8alQ;hl-5xmq9Um!;3@achy#Bb0kAd#YIJZ6N86kSkF2R{;3-$;!0h=~LGP>;A&7)vCI(^S zx*x!SL%S^TuQc-{II`0>Mn4b{%XGh)M)cSP5M_F?56;Se;B3Ad1SltKW!-x|-k2r( zUSu)LdiuU^%HV<&^^8#s&B;^Go&y2H!1t^qu-@}>iP-Zh1al$>YGa==Io_4Db#IBh z3Glul-<6_F9CZW&GueRPZ^VSmv0#Z{&sHu31j4-5%;%HipSI?$8)G5TP%!=hWg8Ue z;Ht!d2?c&6QsgtZ>j1&mW0!HZzE2g0NX*9_pEQ9|c8|X)2vE^vh6+!JEg|r~bn91A z`8py;`d$S1V*y2YY)-xWXW;C&egGDp_u7s@y^B~gWrKmzL|oJ-L-yb8;G*610Xh#N z@dR%}2X?{c$M1yAkK76aySEsr*B~ei{+(6`@bhDVKo~K5era5eaW|IQ=uocyzxy}Z zFBbv(R>nTbnfzAkNoy74F%3$N5HcTN(cJ#{4k}miFUtOdF=hV%N909e#6tKZ z(d%L6oD1=+wcA>kvtY^c_rSp5zr$dCK%cYsZdjq`-}xNm{yZz}NdeX^E}(V?>&2(d zg&T9Az?P{wyVN2&Ueq>V6*|nGSu4N@@DM@HGuFDAc!$P4>W(TJZ>2~3vx!A@hODcz-CC#j7P zZp-G!?toRd)IT*OOY1B%=wG8 z!r=Ko|EsWn!}IXU%G-3WzBY8UaCv`B6#)ta*4+6Yn!9ky6>puC$F@FP+yc1VGqYoHh3-56pA`FP690kJAmfBN{fVEWik1EK(x`zNIyk0%9QDqH-Tud5;Te6vE^+p= z*c8U=g_9P)9*(ZR2R4tMs|9{o%}+SQJ~oxnV4q?35-A*txPHCuJA-B9>$aI!if5yq zA2}2HK(qAR%dcpG9#$2U6i^_LmZ>an>~lllB;g@k7Xr6Y&8yhT3_J2+KVFg;kw=by zx>6t_UBVJ61$uV-efxU0_to3sdz9Hnms>MAV8$9f3LI!M)p~>ygNzx;IAfUvSoCaQ4r9 z08V}F&)P|diAqXeZ2`bB3k1fy{68sL5aGH{Jo6HLL#07v%Win&hp^{`$08fl30SZS z@!&uGftI_x;+H=SbH^|fTjZGKX$NVE(2?Qd0YKl(aj8tSa`tPg7(i!j2DhFutG}55 zm}~*aF=qdR!=tcs-=Q#LoRQdm4pbt+qFHlKhf)ZTFK_9$GPDSSb?W^WA-1~TyGPpk zM&-_wqCMYa{*u~c$iOd=RIlAWnx*$jM&S-Y*a8@-5BeT+0!q5CuYr@>1rU+^(y&^l5$8Q4oZc4Hjh2t1qY}GX}`nJU1S=?Z7lgBQ|8(2rLQA z+Xupw_hld>vLZx5fNYkl1aM0KO$>kMbKUPD4g6hR07YpU_*dO_v(5NL8*nHB{8=l& zD$wa~cn6$w{V&11WoJ)=`!Q+(OjvK@)%L~d7QyRp(6`}3d*PLbZ-G}H`k@Br2|3>3 zZO@vY!tHj^#*L&wXZpNv7jHXUijV&xNqx)^qi5<)!^Ue_nY;5 zW;>c&vgD>}Or@ zj*sdaTHtQbQv`QFePn2S_g4c-059M9AF%DId*OAz{ux+&;nm}I%!-&Yjn^+Mp3#Gy zp-u(F4!8sz?|-z=bcP=IS*K_|JR*MjHHtpP}MS9{Te^;Uy9QQ&6gM(Y(-~jfZ zW&F43s2iq%e{$^7mi;k^0AWYYA~wGv05WmTj2VUiD;iOi!1JSiHVNuzrac7=WfJef zc7fIm;Lo6SDw?Usl0WO`W3L}Xltc?A9D-5&!^i4%qiBeg1srO`pu_|PzLo>VaY#ez zGxobip4gB96-3gxCC^y#3g2^NbE({~&47HP{ZA!&k5mGIITZ9JpZGi6I&n+-90}Zqm}dqLtx}=uYO3&*Q|>xj*;+!HVnN>jc5w2mkSguj8!( zFew9qv8=0;6DDa7JDw*Uw+L{OVB1so!N!Nb4|`TUIqt_9CkF2SpC5x2Z~jHN?8ASo zD-@j^+irY^X#~ZI`ej9=K9TzSZq)`T8W1Fx6XwoN>~Mkr7=mYTWKYNI-@0c%RI!hr zS@H)9{5+lTzkc+JV+`aTk=cX;)NViB_u2W+5C8)$;fr(dRMIc` zmX(erbNstPq&j=_!u!3*()T=_t+N0TXYd{;`#P(yn977_RPzMQ5%%@<65%n>BY?*H z!1VDExTOH=3drP0AH|5s<DGa=Du$fBd)5Y+fS-H_*iM&S2f5T8ccQ5d;oAv z2!VM^iN|W2CTe>y3SRz(x9i)$o~^L{f$zemhkn@6h+L6n!~Nfey)XY1uKu0B(<6;j zsj^bE{GaM2E<~(C-);;rtrd`F5|okj$CjD168--K0ZI;f!z5(q;GhO@0#b(J;O}1To=>x_i0qkOaf?IUdooWC=O}! zo1oCwz;7~OpIG&t1@uz6VkQ7^of>3I&>Mm^j@g;#je}kzJbsE=0w&g@xq5Qx!;wBS zouI6fpilC{+WCOC$s?QNfOPzY&D;fL@t!8*ars}{{)X={YK8{jwW6Uj;>X+q7d|;S zFnxJ*9=PD2Cg;mzlTnZ%GE4t>+y2Z?VAbtkfkRu?wa)A_eluw=0kS1c+HUuSN4}1bHdGE(z>y!7u+8=*I zi~WqHMkQK2e{|<7aMxda7_R)a&x8TUjpJICDFWE7_DRX0YwK(GzSbr|kw-vG4md$3 zOjZnL^_0V`u_wNsSPMXcJhHQ+eK|Tf40{e7N$K}D_xusyaPIx4@7@7x_YP`nRPIBA zXmKHRX2l`F{%6HLUJNtex0bcO?^~6-xG{~{UL&Pz2yt5GS4BN}k8E(+!dI8!{YWH8 zOXmhCPweT>?eTl<>pWkXalG(;K;w-@V?bI7Uv5X}aloEltLA8cy<)ZSjR}#on)@!Z z-&BqwM!hZ5Hv)*p^n@iKCNueD$=>}tF5jCjwv_en?p2S&i?{qM9NhF$tV+`=Sk5zw z+-fRd@kQ6bMeqI?oVeoRwv$xjG0r!R7@#u#oeltwg}~48AS%>U)(f+R)2@1(z8&1O z8eYEV>)Ix`h#^s4cP&}3!lPgMQ#i1B4P5r2|JtctW{3M(X%F~`$Ld1^U7P>!?o~l$ zezvecQ^Z2x# zW#9Drb%~t_f4=7tKu7xfSXC&aN?+Ls@W>#rVm!ECnDOhXMEF@18UfS+f+p*E#6RGD z4TzC%Di~$*95CC3#9lMCU;+liXV3u%$w61N&=3h8VF1zFV|5c)bHG*RqhEss;AFPn z4D9jDJ`4Q!tbH0@{J|Gt|Hc>cT@OTh%A23nFJRf_KLZ!M^Ea$Ee>ap^j|KRrNQzHs zMZgIO0NVMlPk}Peq+76ecGbuJ3NHD@--nm)`cK$!|F_14G`RNeuW5j>UUt7u$0ZgFj zs_7x{VpfkJF6eW0re8M7ea1D9#_!6$mf6K^)#`mTZm7W(x1(IL%75tar-KIEr-rdL zE-_({_SYBTeP0=F0+9_X_&Au-zBT3XJbPn;pM`VcKiF}@gaMBLw{+i3;7h9n zq=9}~CcS^db2{VS8wLQeQ9jY4u3cn?Kl$2lU(tT|f*XGgW-mD-^V&QWfPakZnY2N| zq;mtD>`OZm;CPb+lM7A|yyk;CJ% zKxIaMF0dR+U{~Rhz5>6V+Q`waeYfuJL=nGgSuIdx5YzrM(zA&fGiRn;E(rargP=x^ z6yBFDyP5QtKH}$Q{`(Fcfq%T|TX5iT^f^*CpguaRzjIRokoFqS0flg9#+x!zE@68y z@Z*s?JMS)~9S`(~0H_6jgw%a{cIiV0Jp^s74d5H5zfbIW+=_ZbX>I;Wx#S#cNfQ84 zm+A5SIL2s39F4G!77{G;sGVoO0Nhq145V}g0!IdG)tU~duodB11wP*zA#snM34e-I z{`pR@5uX=1jvenPw4UPv~^%B=}urVcSgl&do&XOgATI)UHcDf(qF3) zKmHdp{{J0<|L~}B7WABQa&7pi{=SuJK8xI5H)Nh;LNo2LoX=u6#yrR<+F>BzgRmoT z+{?#>WpGiX$EfK^68EVPFG1<|ak;;@WcvH~JO%_Tq1)3FaVt>Gu(!9@NC^9;%#YIr zJa^9oL3SDB{(p}i_X~^$fmhNbMd7q9eA+%B!67#90XlAFA~UjR5Vt~rsv}vMdwMBf z*y=zQP~m+vCPR~+oi9Gz2EE&4@)>VUzTN=;+u_1@{wB;?cuHFU%`-tV4gSY@05B06 z{S#TSIMyc&iuqg+{s7N<>j&VaTmKDSzUynC8uhNM`@n`5bP({CPksr?eY3iL92iJ@ zX3Z)5-k}3Kb5$maZcmK7*CLS0V>g^t30N?9R&xl(1i*T=u<*~}fg#v?=xEy!Km7OQ zO8wfOapkf0UBkfBLebgv7?F`f72a*70uL5&?%=*C+AcdWJa0A39 zA)f^Jr=5H5YYrpu-*yx1eEwk`ZpcLR*vvuB`TcC*|7ZyOr#Aimi(Het<2(# zaB$pJ0H)_fDT|&tTTc$4s@5~#{72aK#NAyB0v`JOAHkbH@%b?m1G6p+GZvg&s5*hm za2Epr)xxPUYn3WR;6KUw&syfp?ss<1Apol4+5bqj23vO@u(rI(5kC$BP>|>8m)63+ zee2eyE4|C~u2+X4XjH?@ytnB$mK6G7a-8o)BYxZ>KvJCnVPQ-5k-`lS=zG-5H$8eI z@S`j_PS5irD_<&|)ni%SC($2MX)xwO;XA^NyJ9#CB&4HT3E^WEjoH2)(PLFVBm3)H z(w?BvKEp#0rY0f`VGsi)D=efUH$9$S@EtcpeSxLSKgRBNSo<6-@w9{s=E(_wDDzKm z3mn35@fPXC4;BH#9PvguK2>_-6dwiE!3zone zKmHG}`?~kS6F2=27~H#U%?K1SQWq*?p8u7Ko%%jJ?iK3Y@26GZSUnfFQq=-6V= z#7QJ4mP~MBjf|ACRRXRe@G+891u$9xNr~PpU>7k?McQky)W7Don>FyeF{lvv%KE~6 zLp$&n5xMW8cmEdjpR}a#wS1~y^N%OYnlLYb<8dr+DlLmHcs;!BZ@v#N{NM|)=8pf+ z62N0wHr)43IC1%TaK@Y7-Sut7aYrOK4j;^~dRHAd3L{4jLEoHtW6t!u{r)s*TwdnS z>WwR)IRrp?WH*46xwmE0j(t$AHM0BtA!s$i_1${kBXG|{k2irqa=c5>_KY0f2Q%lN zY{4uI{Mmk1u$mu`1D_u`{CQMr*Y67-uXwDJPg1~MBn8YHf{uL%_Q>=%a=qp?wOSQs z%$TA10c>Dn#{P)d=Ydh{BjCo(Xfzts&+j2kfpYj5_Vqn$x`XR7+yS#sXd#Ri$oO9T zEYGjl7K1yf;2sCw9yTH3c8TY-N8D`QmFh!<;NSWD!>M51F6(d68AyE` z&(q~sz6~yX=Wm1=|FXDJV9tL2M9BFb1Dwg?D^CjnjmMWV5y8aq7_i_NAGQTfx%66i z`17BHk%PO(w7#dl_BSy9%!^^p;^kf6*8YV{3IzbV3b12s7yw*3CNPgmvZ~Pg@}5u0 z*DsjcZ2zls)-XT|4(-Uj{oD5*)GmKnBYt?l7J}XPA9@-dK=4;_lu#Lretek>!EfY9 z7zCWKsOdKynenjVooNMVa-KHxFZ?POYO#@S6N0s zRy9zP7pnru?3Xm7rhyel1Z}46Te4x#10e00v&4^!p7rpk%Xu7U|*r z^0lpSnOYQOGd{;_nq4ylzx*kqC@5(~wP`mqi&J#CyNWPb|#jN+v z?(c7gTy6TZKTKI`xwhfJ;Q`opXdrXM4}r8=58&G?SHaqChcYkpq*d2Ub`-`dqHper zngO4@u(LrAX88hvk-$?Lg&Xa7hYQcWv!GBADE(U74wPr>7zM*FXqzMO!8|EpuFge5$Uhpx9+(R_`eFfS3j9Ok1vX4{aM<*f;M2; z<=4YS@BS#vUa}@ENiZcPD9@@w{#6a?x@0MI{s?sO$1*d_^S z$&0NEk}Pb)W%M$s>S>wYQprBg<#K);KM(&!wts~f`&a?sl00YWMzPOqFOKo?XW_Br z=McbIIgqy@xm~8#h~74XRm{#i-fWRdrMHB=Ld1^`*62h(+z<6ucxr z0sOtORzI$T0|)**4l>+;jJK2D%fT4~KW}%kEbs1XTE9LS93;<`c6Pi){%vdR+Xnpe z1_F8C-)Yw04db^7R*}tX@FS9mXjQwk5kGu>-@pibJp})wLnBRd*?BK|X^%6& z`_|T0*sPmb@C?K*AT#hXNMZn>34WC6^ZU3Gz^`F_eFk`b4ZbHipYK)}ct4HsPZT^b z_(!_^z6}(Tgg$pa(*V6)cAN*~^PYc2z(234POEL{6#P2@>q(gPs}!8;#)nR-RzYcu z8$Rj!9#hG9sMY)2ta7aTleLbi=EE?SNj?=<@;G6|4ekj0*isny6J$l>;?hFf>< zhan{WrHuG#b-x1xA^5)lwR*#rrdu6{R>2ED=9a_LvVDHsmFeZr~ znN}&tdVM?4Z&j{u^>?20F>e|Ecn1H1>zoo+zheym+^k-;0R80OVmvSAueN1c&Jrg5 z{wFWmNw7_;03h$IOy)}NPboOCrS0eKId}y2A0B8b`%B^+tlM=2zW?YdT^-011f~5p z-4edr4|o4|0YAwF;Pr5Tz*#+)_c`-VX8Mt~e!TbYKF@6eykQW<+pApzY1EHP{k$Ic zK36N;?<%=pY2RrQz#t2+s^~?MP55qf6F=6sWL!+$_D{? zDgY|SEh})y1p97(k;#l@Dx8$#v8`j3-RwqYJzm*=CmDV>wlhoo#w9JbE1_~`%jf6` zy}q-7=6`7SE04AweZ+D;X8)S>C*1(RxY<3sc^};V;gJx_?%&L zhR#6&gRDaWRNVLHl6|rzfD7yPb7t;t8?Osrk3jDd3z-bafdOarMz&YR_yEZOAX)Wr zzb7{s%F3{_ocNLzgRG3b(~LZCu+Z+gJm-D#9Pc)Re-eVfDtK$Po+%puBzIEMEufp1 z$oj=}26}vFyT@|Bie#~Qf`+VhCNHS1*25R~W@=01+pJ+gMYdhmr;6oYc|I4~Gy0Z$r>-4&^E%i(T@&Orh$oYiv%&KY@_sVgp- zyFGYj_4BcD`2a4%-xb_R5&*6F4w$wIKE|KJZG>c29^O~UeR01xH$Z3w-mL(x(|L8#SEilS_C#{@F)bL%3Kp@Vk@j4gvY&IlTbqTf z&rAkh89{++2lAZ)bT`3&tj1{*SxHE_CN&)J4>E3yjmd<0$^-qCK`A3X7L@bcEZDh*7MrELeWl$#MI!Nv^+-0VCVBsjD6 z%8~NL`x?+gAlJZ+!9cjZiUFGg0iOG(lD^_90+R)~y1=D-D)ZZYrR`Jh+&_;DQUIWX zBDY^3)9O!T_ldH-NC94z)dq0alcA>|+LeiufkBqYl=HW6?&G$pR(u0@8!~3@<0Z--Cqo z^z<^dv-9zI?Raln+H>KBUnHIrS?_5jB*Y+}{0vtNh}S3SP2qiy-h%*y?-yJFxccIA zW~$|mxsD2;v42zMzU|n15Dpv}jE(r={h`qseEGIV>l=6MRRuCM6+I-H#BAs>Ng2Ch zc0GeBf-mk5R|4EB0Iv^0-y!#DKrVc<}!)ijWEc258#@cCpJ zp9O)OR)k8M%XiEE32Yl!TU|0Bifbtb7ubT3RD5`jGY&Wb*2)% zvz|-&Of?y@{DMoBKpqkSz$iWE1_OEbchUt1S$+^MpnHkO()#^HJSIek+Xfx3XHqA_ z$N0o>{a;iE$5kAI^D?VqC0eXU#q#PiB|ua|Pe*Od0JfBsAJ7A-kX zzaLHi4)5?9+g^aQ@8HnULA7?vF5#B^F#tF;Ff{t-U;Or(S9a{Fwm6SeMtPD0c>bIF znsukNiD+04FQlkQ#0#Rz*&?>pIdjf zGobg?AmSf!(h)e@+i=bS>FiUG^0;tHwlZ;C(k7j<2wur`sf_nz{VP+})vkQp>buN( zeV)f#O>w1dPu^=*UdFu2_{==ltyafLr!Grp@y8vx`P15_EMjpUf1 zNcjR<{oM{g+Km?;7ju|XFj$#Lz|Ru^v^p2O7Vk?wCtF;Uyp-Tqr34?z3z;OqlC%!I z-{_w^@04hpl?e8}lDgl+)f%kdwp+L(e)#X+Lq~@``MIyJ*>~t@J>@e+n~NB4H}^O_ z2l(rWOi%;11I8Md!^iQ)SrL9-KtLVP-&`&9DNvB~8hlLBV|C})+Q5UJ)4@XGIWJL; zRVe_UBxuPtU?|&we%kwF4v>`@>lAI)-71J^2TXZ`hqUt>a-1`*UZYaLxk&aa+XNs* z0*$nF=6zqgZB#kF`zeJ{d6WRNBC4ziD-ZDJ1(>`i!*u&${Fm`OnOg+_BImgyT3iIu z09v-Fe0XgFP!H1GbCcWcIiI6}>f^uu;iZ00UztD+)X)J>RR=|)0s)O65NkK>P>qHZ z4Ez`fY}&o=@Spti|6MycJSy7+>S;=yE~yD92&hxpI|i5t=mf%Cb{?gQ027}^>4gDZ zM-Z4t65BFD_iIi@t|&lgI01uwE%mc8Sg1SOUWfP;k_-4E0rSc zfINCC?Z6q@1QM;T$TX4iQGk+EdsdDbCX1ONhxOZ3VNoKqQza^C%)5)}Z* zC=cg7QS$)5XmNs1XZhU)S5@%!6Im5#w{0G@mz&Ph0)P~NZdEz(GBT9pEm=`WwgGGv z;ODOYxsU8QG^pmx>J9oTWk1-vk*fr@eSm9<2#!NBtzcv~m0XPk-TC>l#6j zc3~4KS&CbU;B2}re#hZt!944yigIXnVkUPC(RRNM`$KCD}CKmpQZ9R_lQ_nR^nDi+)ZKu{CH@DQMRc zYW2BADg)h27*rQuCkj*o+65nZz9ZQp_@30&fKr>xU!+9ABF71n6@9jgYRBVXi7tTR zR0Z?#-pBF!oZ*$kQEdZ+4*jPzUcDFG`&o01)$w}^L&nk_H87QlF4UP3|f%{ zMatvJpg=f)r3keB*(WT}Ac~ppt#{t-J^l34aPBD!)URKEnKaD4^Z@e?%zY=^Ax>~fxqr>0Nm}aJAs2tY&(N&E#;!?lK#@JNv$&TJb<4!D{Q3- z*Q#8fx1XO^f4-N9mOw-HM z2mtbap2z^kECOm4*B~tr2->V)rBnml2>|%)fBVC8Dt$9__I&W*LI0U&o#^?ec$faWwa()?;1I{?`x=iWtAr;~&T#>)Csp!P3uNLk9u?FTf!DwOs-H#B#NPYE z4?oOctVi1#fh>RZo+s4O`TgR3SDz0hUj$$H-jCPZ`@{=}bM9Xq5~F;?)JK5Ty?qD7 znPrb9d#(cDHdY~;e=bARr{}UhgL74wp?RCBvk(n|x^gmnt`zWVeX0Vufr@)S+2?OS z`(A8TKeh-!w<7Yj1M@tUfjq#Hw~a6!nQAwG=S2Z}yw)$eaQ*1y^QR0xlydBZn82Ov zCTNex_PrV^4eA(uUwMMfcI_lpU>ijNfN!-fkrE6rvCjphp#OvgOAvU6hlhI}dE^mE zzd!xDfq?-TGy-q)oTI{oMGUJAm$=kmPcxwd0SoR;LN`N5J zUDe5~xe74qWZr=T&KwMs3F${5PQ+ufo=2bUZkt;{Y0!JY*?zZDkgT@I9N-K&A8R!s zk+)2r2fT{_d^;e_8$669`|U=Fe=>57ijFEyRNylu0)Qf!|3rMzWL?w@EVNZZG;{Ny;F3-)Q*dzM~9UIJEDR-c!Q*KYnlPlcdJP>>gx z$1*lI*#LW@wnH69`#A0az+~BR)gt>Ux=rmyHjc;TSm{)EW4uS?e80%?SgXLc-L}d$ z+mxJ~0nYHh{12Z!uT<%ksCj??{r7V);f;=tijk2Kq|Y~8iDnpmGq zxzB9*8kP4s02tvc8(RPf+63wY^cq)rZ-4>^2Iv~ZXZUp{PSpcf@|4T+wo2gi@zWFr zB{VblWg-|z1Lib^zoOYbJ@WXoM+Sy!8QCXa@TyJ*UnTHofF7bb{Sh)Is!=6ilq7jw@g2t_MZShIfU;ISfE ze<#0fHNn@cX#)3j05H8wFI~A{6v=+Nk@>W$I>aggvrkyCSW4;Ld+)slO{6RxIdY_g zuDUBuo zz8fldd4O{&^slGEe>wn|UXD{RF1m;=ZBLQul&o>rG*A!)Khr)xqp#PWId^_#-MV$& zzJ2@3A(-|YJ$kf^LBQEdPCWXJTkn2eO2wJD@38CfUoOQDIDL04?;oLzp9y_jIT)p2 z!IJGrV5dgE_bLSh9?kx{vb#*$V{sn_=#rD&C;0&)M@F5LEzTnLih4x>xoBWe zq^#db_CKDLyU75+)8IcH08B5_%YRgaEfV2J0>g{w^~;Yh2JY ztyb%=S4a9z=<~LH_pS%`fuj-_E|{$kQWlTp{k;V2M~TfpNMt@N&ks>~zu^FNH38`L z6mSd@U@f3oWse_kXM&TJ4W}7~wR6zR3(DPmpX%_rZe{!30#-Evzm4f-Islknrk7(5 z(vXpjcB67ImMTHod$bq8X{RnNS0(hVUcI_c1htZ1A6;|jW6x`)y%5YjD$}C`@BZ-q zHvAhieUj9z5%9aS_&omS&ebR9=w%A%1}R{kM@}~D?<`+}Jm6d;^UfPI6kS&*S^rqT zd^~&k)8Ie7Oa}nd%XDx6?MZpv3_#kwNB-5PKXu`Ezx%yX85-W6-8)}+cFl$giRoY# zzdgKRwvT;$1pLD!(aSx2mgNl~K4KRnKwKq&Kh2)hZBxjMjJ*?p>jp@UB?IpU@vd9U ze3+ouPJ{XMG93U+FVjoGi=UW)^~VB`nfN3FWj_d#!@_?l%c%1XL3W|cX>cTqmK38-KWZH*hdYKZ7m_7v4%k=X9*gM-5gkczpra1iL z`#-K!krunzcIhWo&w*kQoNkcaB%QtLSkVaJYwh2S&5hf0T;=Vux5?gS_fEgkeSx9atP+z6Xi*5-i(00306(|FdGovDgt0fCKnuKQN3Qwhmv|U|@WL z9u9`a)}}5bMyBSLcKl?Q9X(_umL~jU>g@8&@(!Y=7M4<8PNu3}3g3;rtc|%%$OHvQ z_&j*N2-ups7?OC{+Sobsc<__`7hRsO^Zzt6k&*lth>JBp*?$YAA+JOtYVTx9!p_Lb zV9X2xk#KM@g4ozOI6(9yEX*JlCg!i5g8{_G!_L9O!b$SKHnJ~nP9|nNDq<419C$$uys8ri$L@RNN>`rlo!b&!|;--PX)|Cge^RL10C=)eSGWM;Cp{ZGIC3)go$?|JZ3~j{hja_X` z?OdeA_{qNRFq&AJ@Nlz>Nw5erGmCu{aTX9rn3+?Ao10zytC5rSOQHWm4{Bt%%bK-^;ggDY+4>|$tVZ2CWZEx-8wH?F9}{}q=< z)XCJ)#op<=y}ixnzk4fY>SXC=Y9ire zZ%guD`sK0wKkye3=VB3NV`XDw<>35Jzqz@Y*+kiySwYO)%p%Mz5@i1i*W~|?&X~T0 zVfv48{C|w)f1185@IR;j+w{LK{`cfDwfmYePG3_)IVCj$3`}5JT1@!6$I6*6tOvpV zuZNBA;Qq4x`zXy)nd}lMaUv#4_h!mgi`G@*~ySmc6x4zC}-Y1VpSD*Ty3+ex?#9dw4 zzyC<>ul}6dao}n^Dpvl?E6jIB`poxv@BZuOSAK<7pq``G)zcp`O>#1bqtACbN}c~k z{36PT2vf@0fOJfLOij(5am^nrjC}Q)?r6>zCSW#H;)!^rF**&|v(caj*`@Hp{#Wl# zx07GMeg2mMx%UQ&UBSB}u?6KVmsj4C2}Y0?vruU+ud% zqZJ#_kR{uIog({D<WyQ;)~3{So(W2vc@h!n2Xg`ru!!50LGzo znE_)oToPv`DldWt4T3}ms9q&Nq8K7vnv?U*(6P4|MG^WDwn6L4R{d(|xLK1X)#7Wn z7XrCrjC~Y42X-_m0rI#8h^kYQoMk<;42w^ahdv~O4;c=X9$R)uWAya)@i{9|3aU^8 zGtuIE?X)rI`k8<(*P2uNn8)ooGc&99JxYgMVljdY2uEHy4e~_7=EGlaTDK{5r(3gA zTst0%Dz}W@)^IQt$g8{t<5P+NY~MelQU|SC22|GqV_X@Ow0Xe&BWgu-rX3+oJtI>0 z$i$j&++F)j8%W1yq zo&|Fnvt%d`QUx7dgmxcxYHCqv(p+2K5$Lm-5qTuO$QJpYa*!IuM)hqqSJ_hIZ+>KX z+Q|pa?Kf+E6h3}MqH%+99_m6u0 z_Vx93z(BDBx9*jVK*%5bs+o36CL;zyr6Ld^_R$z3FC(mt=y-zbgC(;*8^XE*xz@@` z!tQlR37e#@@|mv1nOR% zAi@cEc~PviS+6C8)vFW>dc!0;tbm)fj6bZ7GNEW_UGt9rvc`+*cW35`?r} z*V{t{*A&sjpDU$<;`vlR(GS>_O12YVEy$ zGr-(v00eRy&#o*7`ml$W%+L;)0AkVa>Qe@eb2LaO9w^?i>`p^&BRRKywzNoHoZ@`y z9iXJhjj@9=f4wTb4$1uX&r)Sh83n_mmO#a#iikRWNc6Lx%QTQH(!?WY;Y#V-FWF-i z{vM9h&TFOQfB$ab?T+*vZ%JqlB8W_l@zobFwAGQ!ynra)of6XsN!#GGd9}68LBw*4pewF7|wi*i=*${psliw9^*i z!^ZxLb%saINZpSimA$$;1gUMp=0ZtRRPQ4NoTYx98m8iM$m06<*?CGgV$Pp&j)VY#=ZA%ICQL_gupm* zE=6SCY*@&VmB-&BG;1CdI3NhM4{fX-w0e4v=eCMwxs%D4W=^|PU z?Gqt^TFkFqrF`2|K1$pIx9c^vV8v0n^gmwY&o zzpZ3eJ^nWSS4A|*WOK~aq*Ii^lds=X1e#|qTJzg6Qh&X8>zLUa&~_MRGZ!)LtLN!}tP<#&Wz+oN3YG{2UY`0|i6yl;KUG#T7zk z9vj~v=2*0>0xqT;q@HoajzK)u%!ynCY+7izDiUxuWRo+x!TC zSh%fLGdQ$lD8c318E&&@qU$VyOOvdu;O+gS>|)`5DvH5`Q<-IkFdsdFrQ)EyeW=qe z^wlh|SD9^LudrU8dx}K$oBR!U6QM|eo;!yuQi6ZRpR-aW41K0U}31- zkem|-5ymV4h?7gERuu5PSZmj>s;k>{-y2En_a%v_>6$bOef*WmVmUZt+yV_BL4zgu zitM94B^=!fsRm*55WOyeK;~j+n@9+)_yJUKLV>1IG1JHLFvQ{{wh;^IM2({(0AUfD zkT12-NlT+^r=TuXdAuRB<#WXR!$to6N)Sb-!)D5~?cHC(lEUvuEw^k(AtlQ)MCRVM z+x5%AOBB-wj_6b)-%aqHXuu0Sq^yTg(oYvh0+~4SVOXD((nJgR;*;M%!5IDx)>}QR zH$EBCY%2Ohn7F5@d{M&ANm#gdL_8XMJ=+j635ZMC+3W%{(`DG&mX+l=i#+ty>1-<$j=|f5%<(=NBs$Ih`H|EMRjj^c#9-93LR6u3QcY)88O82gR|d# zi$=Ph@}A$({MdVt(+A78INI`^@x$1LEYqU5d}83~fR1tNonfsUQH}%_w$P=K4 zxp$bXcqvngZ^MEAypBw!BtWkmMLP5Y-7X%3K}t86RxAYWyfR37){d>AM*^5#yFy^D z;ly8`BM4boYe(8PzaNx4wBz`+E-rX`3GH{F80xDOxn}a>GdQ@ei-a&4-<3qDre9TvHt z+Rj$T;+N~`LaJ;!qwbs}PXJ?3D+Sin0xqV+NX|onp~M`OyU>&p zXHp)9uViRDM?kHk;m$v#Yxckm7%5G#H(;fGmwoy?%y|Sw3I)v z=W2AFLvzyF%Hqngkzl)(*{hPo*BS-ji?Q4{TOA1M(lk!zRC+sPI)-)PCta3gVrqLR z`JB~rf)UpN z+4II=-R%HJx&kT#6%5O5FCCG~kV!-g>b1MhxIK}%Op*aouE?+g?0u4@R~{Jn*U?|& zv6K|Az@mnrkvD~)957C3C;0c=rra)7oY9opM8JSF8&!vhal@^FigzasmCVd45~sQK z9T$n5*bX;t@-D~*O?lY&QD)%ia+0_<)kTJ>Rk2u+4O!cgQR3~+th(pIE@&oWLb)Wr z8zWLeNdcA5eGnKJS_2?ChuWDj58T%tzdBBxn`1 zXhD>-&b#m+5+Fj6oU%$}HO2LOX2M%nVl8IDRnN350KRC5%PB?+zBQYzlooC3NkIp$ zp8!clk8iePYKD%wE*833vj4@^t1-h7Z`Dp^3v@TYPZ$ZvI`%FLS?LGMQl$nleTJ(W zUsnDq`6aAC{b=YF6SAM8-5q+N!>iQ~{mhF`GXv^?Cp=OLDaWJ!v@t5s5iVWlnvV-lQ{ z*D@u?5JJh1Uu}m)y0h!HQoB2RHom^N#(0K#K5gvvi z1KRX~>o0MwLTMFyHhADAtDg(FlPsdcSr5*xk^fM^93KJCTtqM|a-X7|g}3=|U2%hk z?%cg%rkZ>oN!!?ybsc?JE_00i#DF+9q$s%K!l`7yniK-=s$HG>xaSdVsL4u}YKx7- z*`8jDe+!b@O-a(?R=SY>?Anadu*Ay!^O%`u#MDi@XUVmKjiSKN{}jqC3K;nHO92x z5m3$fal+}+^-eGG;$WoA`h3qr$i)O0(_4o1@Zp=GlbD&YG)Z{Cna2@2Uv?%iS0c#l zIz?^xD#d6jb)XdUJ^kKZn|0l8c}UAo+8UU_h%%wtO?nzjtUDhFFZb-jhze zONKT6Nh5UK4Cg!KgbG>_T#t&A{EXX4QfYqfP?j`ipXt4f9Y{G#GNQ4s5k8k47ZaJE z=0x+sk|%;i^F>A8v{ed>WGnoy228$33f|Z-e-(QiP%&qf03FTY)qxyJm z%#Ma^bPQRSmHuLhZ*em1{CW3JQ7K5(-D-Ab7}yftYy%^01Up05n!xUQF&TN%v$9K4 z`bCniGjwsm`2EcQjZYk!IkAK9X6kgCohqReCnZTtwGS>uj%F|g{}9qPypZElm5++G zy@w$e9+`bmc|MElw~OeRf5Zw0zlEAtI^|@Sx7wKmD_N=?qP7nRS@5O^O8gjFr)o9! z&&4-_&f`S~wX{E42?(jotQw0kQr|_k2cB$!(+`Q)l^e=WLBnbU>odn(Ium+jD zxMd4!T!1-|oDC`<3pyW2ZtCg!naw;Z*UcZmt|9A~68nyL#wDU>4Af}(ccnp6#8Kuc zFl-KnZ&VxE;^so~sjV-*Jk)ih(E&*nPuCP>x5!cwS%wV%ti3-JYod{RQSD-``}3z$ zfFwH4j{0UQx+yln$k^~G?r2f-Vv(I7c(S{%DfsOj+UCkhkXO6MUicqB3 zpfT`f#ukvrgPEp;=(%rt#MOz#yXgZj|JquVYa`h|^{?&dg1GH-7A$pJDfpH?Qg@3psm(rIvq4()SoS>Bx(HCw#@|>dNlwmFh%ustW>d%qnKH0c`(u@{R`2s$ycr7&vnJJ2y(X4j9z~^8|av8)p zN%B1`(_RARg5)=!-6HOYn3lqVJU~||{A~ZdloH9vP#&)R7^w}h8q8`noRT6jihp-< zj1w49xhzX*3!4#3i#e|iSHC4yuowKqK>hc9dF%av8ZKhxMyydNn)} z=#~*Md zb$Te*AqS1an84H`-KkN0T7%t*LZem^D^_x;UC_g-s9{R#capi~kkt6KpoRBrXz$@1 zXQYF*8|{qI-G~r~6^)#AHmmb698T!aA6# zN~mTPwMxnGQYAL9@rL=JfPUKY5}rnAV1qhe0bX$&N%cs)D3rxGeRAIs2aK;lUA_%sV+u8F^USM`h>Npyklw3^Y*SMZ>pYe3b z*Rejq+8mC-CrVi8^Pu=^PEXXIw;wR4467dWv9ST@SdL>pT(Nc8pXX5fiPet^n=#+;$=%a(jhN{8{P;NIENssC{cKeJpFdj>Whig0r@;II4nJDrJ2C%a%0~iMt~n@N?C@LX76=Mt9(mXI2f#CM5;9z zINNLaXZr!9BYqcnoBX9i+Gn}A9<18^AsUojzp%QzsUcyT^8lF*m%d9w#DU#UrLd?Y zM;M)aE6t%m8M)EXu}q?n#fBV|9XQZWN>=41p>CH4v}m-@iyPY?un+@GJ*FuobydR6 z0iz<$b?Y+#UU-W__vfYUD+3hge0Y+wlBoR$8&N?~aG1toBL@K1e|}{8_a(S9+A_&k zDfCGu9;I&Oz|q{gPjGzwCnori-_TZOsxEvVefVN0Kg79_n_^w!P;A3nk|io=aGl}T zxIH6Ilu{{bdQIdEJDJPW%d^v5Adz23Y39Ra`aT_Wq_H-{3##2*{t=1M#bP1AChBq^ zLKiC(T5FfI^}AY{iKTSXcl3tqz)r5aLs^YWvcRgPy|_WYTE97@D-6!e*HZ2bL(iS@ zL(|UP{!5R?Bh<15O_`A#S zp0qWrt7*uBEV2yIrT59?_^`#7Ruy7yqRjUuiY?amNe{wI<&b0A+nf$t)H*c}gHwSH zt4DlmY<#eZA!;r=w+n)g&S!cL1Y{Hx^q6K3v{SK-sRI8Z_T%}ns?D@#4LCP_-nm%ws^1CnXwDf%LHd_=!vyeoR3_SJ%53sY zhkMZyEVmOJQGOjOjxM|55h0=({*O&winN0m2aJvEC1^D!9=seg@~S5Cb3Lsbfp{#P zl)H68*CU>6qs%w(2-g#=(?*LI{C6L6_DD}Rz(45rE!?O!_I($v`Mr7W=?Of<=ueJ& zmo0@tUR0er9uexQCW zWEl0!eR7=cQIwgTlBCq_ z70ca`+NbG{dE(Z{GDd8_ql0WTZV*5*5bGw){H3BwTu$&&68Z3oqXrek#9GgIgE;v+ ze$!0Z2hI>0>IXupk=>G3tECS6N(gw%4$y6ADt_QYM5dNcDdO(K>35<1$Y+q` z^|aBw|3{NiO|rQmtw7XXSCfzaK}>=4Z;q5VZfIr$tM6y1d!H_I44E0$1|yT*+myUY z%G`Of7hhj~3XTbxv{|H8-9{)Rad%85yFK}g3e(((Qg7ftilmpQ3iGka2GaBR8R{UC zi@b3{wk!Z2(!I8N-@#OAMEiGup5vG%4G}M)X6U|_m5_UF_yiwH-KHA{Po;bD`ISN= zCk}kWCrcF~6XA8Ac<+2Hxl^J&D`}Pz>mp@B00o`b8oc1=MLF7yE4eUH9QRrT0q1h+ z#WxxTI_L|z5|v*y@W@Hq_uq5EgjMUvkjVRGFOFL6JCM8|&Qh)O-B#`b5$>-6(@P^8 zLc3G!ORaCvK_T%YU~T|oTwc1ess1(uoMN9For=+@0b+B~H8}PFpcMu0pRjDC2L28q zfSvQ9g(Y|pwVQFWafTGWW)cP;V@DV0+WYqr5M>P{z}`=oUL4 z@E5m+@O#!bdAb9=h2=bk#(p!<)kiaT`<+s|6#K28()4ZN9vV)xFBVu#uqn5CP|$?U z89eP+ljG($MSTOS|OzhndzU;X^X`LE4dnxgmc~u4wE7166MIo7#EHe&5 z;mt?ZNhJzOJff0mZ-ZL;ElK-V8$%H<%Tl)2^)V!MlTk`lYsF`;mYIJlzulyxY>$?I zP#EVp4iWG@EEUh?S+oFQo0}KchI1)gtyY?dRzI)XfW01EBq8QOF&XZ(5)5td?ZLwU zM-9ABtmyFeA0hF(3ORsfWoIy6z0cwSh~Tm*@#iPY0%=SB8Y?rOzMy9JW%LxetYFK< z4N4*mS>L25xKN<{&`QZu;-yjlYBjDN<+qrNyNabBgdu~vk!aGX9 zT;rXANRR8#o)ML5;N*I>_-2@1!}c{~gOVfyN7q|DkPW#=qsaN2q|qJsqQTx99b3Po zMB*>frmLGvUtC%Zqgd_t0mTt$yMW>3XtearxYiR0=E439{k@=6V;I89CxInRi>2$# zmamA$#q3MA%dz@hnbrjrl^L)jiBmI87Fl}uFExN z$AwN$3mdCG>rW5p!NI|~5(9TGSg|(&M2*k2>H5=L%AL@*XgbgZU3BDAG==NL)0Et? zuHe%n#-tl$9N87jl=G><=|Y}&%0{%($~gb5pay% zNHW5Ids-SJyHPJuVV(lpQL!1IWx;)NR&+b8G5NOl6~r zt?pHh9my(-W@g1v7iuV1i*k2R6Vz#+iS(Bu4%1ruR?ovHD@$wJanpHQ+MTmkr`dyk z;^ul&-+-~Qk(4`OPU$(YOXNJWD_?J*fBN4Jf}{pELx04-gZ;>7#EOsXs@H5qH{qbV z73Qyrv?MCubCIFWQxL}SL-PW~O>%e{9J=}WBwhLVs%~I}xvs&b+ z=?Y_LjPMrZZ)n_(o#<)0tFw z3StxJ@y+N@KIt-U$f@k+{6vKlTeh#AJhueAsEM zd(r^idCDd;!8-WCN>B2YASFx21~<;8$T6?IM3*$wLoPqJtbClW*@53RX&uP5*-Cy) zv$z2hM7f`Z}&*=k4E{15xXdiaA@Vu>TO`SvSUnRmlJ|2jQ_8066?MGloq zyw{m+>(J8PTz05eH@J)P-6nIL@*5lgKiH9&Lr%9H2Mb#+g63Ly{^|84`L|8MnjhP| zy$x*^7U^h1_36c>SuMmAB*C}-orpsfXU}1SdZ*K0>4V3pXjxFjgo9Nb0AXyS5O4+f zzwNRgLz=?vU@?e0Ua{*9n1NIwlYMR>*%KeXiq@o4(0QdGs@o0 zcwf0E!#9tc!zYUB+p*?;a%;4~eFFXyvT`lbs{i7?Ikh5bW;AXgTe70SKs2pPfxC_ zPYnA-xD-@on{O~vqB)E?;4<=(&MXAKja-vhRoenX?E8k6#^{T0z2FmeaA5ZQ-hP-9 z3^E>y;Ou?Ql<89wSZY1E^(=JJmMk~$@s=tjk7DTYghH9=6?hDR!(cWYGl?m+#!SbQ zZJZ&#D*p8=cB_csFxQ<3T1~lK)9#zc{$-uP;8Fi*gy`aQ z&6^F(`-29N-xl=F^KE^;g26orVc@9)O`s_#}-)uzl9Q?5klSd)u)@Qz%S-McPW`sO~9PYI7^ z+*0hYXL}p^sX&_i>LJSmE+)2izA?Koy*4oiC1+YP3`uh~>jPQ*mHSv5kcpPU&GUSb znL4k?@bK|f{p)`I+RQ-!Pn9v&xvRMM?-29S-X5r!C2?0k`r_WrAn$q6!;LB@fFvhGncbXYsH>@o{ z8nc2Bu~o`$?pEOF1P^K_T+=g}>aCx*Pkm`Z3yX=(XUQO`KngdpYJGdg;cDLbQL&dQ zml4{a->!!pW)cQ|)HHPN%t|8t^#1y=h2rl6;r~32&}c>ZoW<|WVb^8|V*Gstfr*_O zaUd>DCQAJiiS-(#&JRET?cx<3(F{v%Yl4`s3};>RrKro|1W)d0ZEY=2+|}|cTMRcx z%Hnm@=cGSB@MzxeTzTM!jb~^V6P!X-hS;TD&R4Wt z6P(RnH@#q9H~p38$g3Gd&GkcYqc6i_LqdZ!S3w;^g5^jFq5lT{yjYlCbW zKLY@Qs!4_`jP#In!eg69cpM$72H})~A_MMw*hanU(?vdWXUSxK_CJ+WYC_?}q+Zs` z7)>1u(q4Hw5(RI_Fyk^{DTz|+pMr*Fjc8SaMmQWcZdG->f)2$WKrfVsPadb#fuHc;J(K3~*(FxPr^ zCpT?hwFCchwakSFfJlAJRVtpX7IQR4G1n=L?iN#K1kcq ziIGK!a882ekW*DBuk*U62>%)WT!28#yCy^8_T%l4`3f7R0%H7Ds}2rf3MU-;lbp z-fGO4fL$)RyTaD7`-F;b836)E4_$fXG(b6xBEKPG?D{N(ogJE;pX zn3l(Bo$uRnJ7JVP0}DNZ-$IEPT^TzMHB(}81q_ysdzyqdS?CLGQTk_$MLBQox)nLrA z6Mg2An}8+>Xsdi5%Xlqe-fsI*99~iULv%abO4sC*?R=2lOY~q@?2@8VB6|({ zRBJ5=O?omZ={iM`1$dAALL?V<7?&0gBcrvXa%Txyk$w7? zZ2rgp{h=50dRoAn)bBbvUp86!4>2%7m>XNPjc~xKsn~FlhwMXf#DC-4Z55ZRz`^^` z;~XB4G?K?}TIqA(ve!?%BwwA?YA#=>r5%G&JBGra7k2}Rw!fp5BaW&rC}1&c#nhe@ z?OxV3t(3pqh+>Q?Uw9y&mLt+ON9^oHLuWybqWMRvgmEKOWK1l0yyV-4NKP*p@^O3j zbCv!(#G9dNg~QOwHw^6+l{B0g3yZjEUXYKj7q!vkGlY__${S<-QDm>jT6XLQXxtY$-)9 zev*oG8)C^wY}@Ogx^`^Ik)F-RjoUfC@qL5~GlBD$SLTB?>(T*-!(J=W&doREK zHIVz!i#SSICH-@|w9&=htTEltp6;Rh7DmSo#msMa*!Mx1rx0n2!9W8ZMg`+Qg!^@_ zQ>m!b3IWf(HkD`&8UA|^Awy$*Amwmcs{BSjVoc9bHjr&sr~NKcYr&iPc&-CE={MtK zdx>R~vatC%IKhVvH*$?9PR+j#$9Kp_^jWe@-&|!|VR0@y;r+cqW({2c{A}u9XVfI? z{-wMT086cM7K6ugiZFXbYkyb>bW}Q5#Vl&pqkR&Y-0jrAtA(@Szq0m8KFa>*wEA3x zlaNiMCtQu%5GgCMpJF&jc>mP1fF;rk!EZN_H01$SN5?oLGC0M9K^|JTHy&s=*CgTo zEXz4$zwTGnjXx#pWa{^Fq8c1}eO3(voTfrPm(S3%BZP}KL;6b^Yw!jKQ5uoGZu zP;TVhn1_B7#iwcHbL+LGrf_~bq5C7$=}E8lGSoSY%>8rIv&DTVVG)vwKqdl|OHJ1$ z*e@6Aq_yF7{ZP5bo3IbOr)9I5AHSbEKh9HK585OC%bDlCIJ!#Q=9g(1(u+D%PRE#d zf%Y0OE3pJKm&fv@@1~~W>ll-Bu%8ro!mpgs^#}B(;SKl_D(xEDqQK(?N9qX+?_F`@ z6dJv`peJkv_{@TXgz}JtxO#45sY|Xz2gD6Z5a6oGFan_K#j79S&))?RrNVB5t_nU4iKM$PLO~6?@3n%8f!lFE{O6;41~1${ zVo-g}fD?o~$mG+{Fk|KUkj0Zof}S>o7ubk&RJLC^zK7vj`sJC#)I~|#l#yx#4)L9M zj(T>4*Q7#o=QVX?yS@NDr_|sLH{V}!I+X^Ul6j$zl%+!S9VO_VspJWYk_yKLCi_@2 z{os=Qh=TC#vyOU2gX3=Zwj4jft^HAVZdnSU-T4{l)Rtbk zm0|8Ypaj$MnchV)6FDxP5c3lkei1Bv(;A-RJO~;D3VeRc$g#UI_)VK+D@g(rj-&%i z&)q6P^0L8VBEWVeYP6^0=e+$kOuOSrpYdvDAB&E`BtCyToqv z#^4f*j(Xzt@wU}PP;eA5)!$^@emW^=*niuDO@HuYG;)k%H20&mUp`};AvCa7p#hxq zhGEhQY`+~70Sb@h%h$s?t1FM-G#cJ!f8Dm#q`XaY1&30Esls@8ThAqo_bFL(w0Sw+<=O9h= z+$PI|E^0(K5H#aVs#|NJZ%$Whfe7d}!agh+RD6+lFkT$-UFdL`s>9IZ_D0 z2IH-Fp{62Vh)NUq;2O%VIVuzc%6F*WOoLT+y(-7SnSoz)m<=$yrQA=7#4&j2*YW z6m)F)mC43aGWQ{A>(b>Ce+~rJ2@#)j^W>?LW=S{ShSZy!Dn&Q&dTd0BHXz_DgSxlU zuEF>`|6)EuQH9H6h&$uGD;(~karBQ{S>6Wq9Fz8+;Sv|F61habsflidA8QKwO2X-u zEMd9)gmFRWo-kmS@&m^v4ECm|qv~@nFY|{?b9S?C{M){V&DgTh3LefUO`|pn%K>+i z1|TRTlhQX({^#&BzQSZA@83uKc(g=U={5IHJ9_Ilyq5eow9)kxEgZ(}dMWmk^M= zy7qby{^rLXZqi2JS^Ed>5435uiTh=y@tANxBV-9nR7I%tlIZ!2w*~GZ^o@ z%t!Vma-9v4z+kCJ?Nv>#)y-G!2-yM+RYAWA=V#WJ;qA?NpFe~(dl?lSqQvL(pexE+ zELlgoMa*KaT$l?ppE%y10ocAMB(dOX%P_Hz&@$+P&v_pMe*(q&hW~+8%?Hna>_LVV{PHRn{yuW2Qqv3EMLHbgt?g{r@P4Jt9Y}B7#c#?9L!VfpPgRJ<@w^y=IJD^1@q88=UADoah#%0+r}by zEUb(S9D+r=ZzqL+AL)=c4?>nNg0qy{y2Pjfvcu@Ga&Hi~E2%i1Xmaho#{ZG&_AKZE zr~7=xuJ*X%6>ARONrER=C_sA$1tHldkj?bxcakP?9W^^E3AhrGf$N{}M(J(Ja~L#i zGdMsiXNb;iHqaUOD1bU^3|)+Xc%p-t1;5Hm1Tc|D=kH2>D5rZY-$uAU{?ZJ+T3h&% zYnWtgo=5Darh+=;>Tqvb8kl5#g={DWTc$X$8F|8fS>Sd%oZmxN^cXFApLkoB@*~VT zzZ2i~M~4Za<9b>u5cK(#C+qNwV&iF63GRkwsRIb0Xvkjebcgl(~}Vi)1^xkeNK9jv=>0Te4b)z7o{h7Gl#HDM% z{=mooBi+O9*S09YzUee+Dw}ax#eo9VM0y2c@=D7VIq}Xx9l?sTJ3i~4xWM$&%!Oz< zx*~l&h;$y?$8%Xpq0({goUX@H)UAJ|^-X*kCxYB$qca#^#j`xAk=C3n`YYxK!4$)* zcL?RpYop55k*T;e-BBK$In;Bw`s@w-Q3~nHD^#WsorW9{%HZ#lYQxaepPzS=K%Bv|4;(kPGBeowBY9}-IQ^8WFbzBzld6mS<(-TKT7rHy`?WM;Hl`f`0{_LUcy|2t6B3My>nC zzuE6Fs2vYt^>B_esZjI|XtoQu=V9tcd0nwfhl~`$Pl=E=N|qXk%ld3E6*#UO@Pxl+Eu0NK3SPU1t!N3zd4=f{XlYu~1jeyG;A$*0A#d_+>&JfU_U;PnkW zS~gZOpT$IjzTT*s&ynZIET{Osfoi14ClzRU6BqW&(gDjbZE(dkAwA0W7>F_{`5b{O zZe%(i;ubbb@%^^EhU}7i$~pv@R`Cl?Wex(_`3olxBj({iosO;U&Zg^M<;MKEiXrA@ zt!JwNU6+h#ebghg+kA>{#|B?vMO+D{PI%9pxWnNbG0_zctKVJ1E2{0VeF4+IzGY+l z@Nvj$bJ?+J08iFFQF=etUXq>b>p*Y(&2hOJ)8u!92_Gge=PK+_n@@~}b=ZeQ{rI{Eg#Cr)SeElmD&Bo?b#GNE0^^{|`)Zb1>?Hbh=#iAHBIdU4M@L=8F6AAp1 za#HXXohBTazq7p+hP@?4(Vo(bd8ieA`;oyvufVLdWPMq8l9BK$3g|;vJoUlTDE$OP zOA;w%3O$w|ORkEbghcm=lecKD86638ef7svQFPluYHNy2kTFM+lR?Z41Z464Zt$7JruRH2q>b~Y z;n>>Y#Yn%-ebu~;xcd5k{mR5VdXKet`JV}w44&TCnpgpT74`NY5j4|-zO9ZL*zbUo zB2zUQ(XQXW7qK3&ywGDtcP$2b8QHTbw>_GK&6 zJg?p2U*(T%g`eP)laspE3)PykP$R;Zl$-ijluE_=Dv8u96}VWOABx^5zxWbI3m#v9 zPk|#hN_$dbgescKTr9jje0UGWo3j#p?RWEkPWag?b;o5Y)v_51MO1nmgGl_6sG!Ni zJV;RGPj|zfn(kgs{TJF+=HInfgnA6(r~f|vQQyhW zi6KJ<52`wRu=4#CpX??`*-pgg9b%90qaTR@Eb#%c{+SY<{?rQfI1;W5P{O}3p-)ya z|8Kt1=cEoFGI)&7??1bxt-XqVKB`N5v=@PoC#}j!K*_(9*#ECEp!fWYX&RAkm(&5= k!|^2ef5nbe|3`oU09^&T#tB)+N&o-=07*qoM6N<$f+Ji=p#T5? literal 0 HcmV?d00001 diff --git a/images/mapper-icon/Mapper-large.psd b/images/mapper-icon/Mapper-large.psd new file mode 100755 index 0000000000000000000000000000000000000000..6606ab81aace006d0f904655de37d59ccc822649 GIT binary patch literal 2932779 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

    Pg=AAqXQccC4Ms$2w6t3}w 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

    e+eGt_j@sgPP^4C~MUP`T?R#R*6TTiW{ zHsNcnnN&92g;t}-;F?FzpdDy?dMZ7E?n!r~JJ1HS9OfaRPy`|S%#I!io_zZSR@J)JK=H`hv1nL3qWNhEFavVP+yvh0%JS0UqJo0_yu94pyuAE;VL?K^ z6E5fcSUhw2TppLp<*DTt6ciPgmRD9ImL|l(sv(A^#=5GC;)2}l%(T>$L@~Z&+nAJ) z?1alXIX1Zw-{KV~s3oSPre$X578F-h)ipK&sliH`8fq&_3bNCaDdJ(6}1gb637-c)>RhgWhBLf2m60` z|Hen`#rX(dCcJXO<@_r4mGC78nwrm>_aFR&!();%@`@|#8bz!+BWkEB&C7_72>STO z>xuh4*W1i3ncFUIZjpDKa5>)zy~DZDE}*L4cD?8R#Ouw+posX4ywa)$5u?sD)s+`y z#E1L8dExGI)%om+qs$SRqsLF2j6CIp%lTC3DVdWej)SUx^u$@`t1j*@-uQ>dXB3pz zH8C0zVNFR+Qe?m@57*0Qj_ljHdE*9ly})tPX5rR^ZBDqHx5aJ~ZWV0aH(}IMBPPm*G z#x87HATxj7oLS6F_1W{5uG+Zk@cG*wZ-Zje3o08Wy5ffN+~n|&&t1Cv6-CS%jHci+fnnzkV;c~VJwU9A4?bx0*(lG8| zX4S)X?97!rPv7?RkIpErCHSJW2tSXjhc?U`Z`<9}NJmqhRTF5m)fVa|=sV$Z){oT} z>I$^mYOrb=>Y6%6X5DSa&);z9%99TfX+?EnU2#o8O6c1M=XWo67;0mxtDz(>DrShu+IH4$70?{zM@m^y*K+XGHBL7?1LJdB_?}-ou*!a*g_hhe zeDc~B1MOEGy!IlXg)c4ud@CmpF#j8$oEG6baP9e*IB9%8{ujQ0X1?6GFU@kb<^N0W zFY)EZ2fVm`@W1lWO4rhM%IbsHU$n^0jSobA*EkHe)NB1qe697m44%5$shN+IV0@tG z4V0jrmBC*nh=OUO-(`ry+Cw*9d?6X;i#o{b<`KTARRq5lwQ@TvyJ_I`{1Ts6)xvq} z$od(>x*4j<{GHRfnKLez=gdn8@&@P3;oXhYWPjllsI-%EB0E2-n)oUK&ZCaAMszn; zm-|o7>FY>338$*KhE$&0M>ozMNh%L5JvN%JJpxiErms7Ug#N|q<8o}{oKZbHXe#{5 zDx>-rYpyibq!1t16PxCZ>1Cp=DEK8-uy#XD<71Vw<|l=`bUV3u-k9E|I;~}XVMVTf zCZL%$nXqnMFs_f8j?zz7Qu+o~Lr|vtYvSeSCx3nAc52(g@qNwp+Q_y<3QqDu9o9%+ z=ugf>MQ8zVfe}3Obz!O+giY&R02<6p0g@XrP@8RDR`@Z)@Iv@{0XepMkSV)WUgY z`+sxFYMAz$w0!@SXa1xbH*?KZ6sN3)8PZ7_M?(Hj z&O5)8F0b8b&=gYb0~7LVB`rDnUEY;Y@29Oje9J31so+=6|0vHON=&QBsu_15G28LD z%gd0IqB`cUX#ay5rxDPcKvqUpPF|jErJ|%`-gn%Bt*7t42~91o|1%7y1keToS#-Ot z6kE4ZQB&8@>d<}Yw3U0EANodQls5bs8ATb=4Aq_{J-wn5kXkzWhV4x)EV~UHJ#*FWb9Y|`#phNC{|x?4b%f~$5v!uE zsikY!-lUUdw_g1QjhVh|>(OhEeZ!Iqs+!n8mQy@sO5loZl+`q}bPbF;np<}3*>CXh zF%xGm-*U+Lu1`QrMsbbk&k-b2=7@kKUA?W2o?!=5i!L_32iT3AFlFZaRa>3TyFBv? zP0T5+6aP7iWU7-uww00!QlO*XuDyvxm##hf+725tY1-Vyt2XR9a>3=v+n`u}rts%T z(x@&1q(fOv6Gd;-!NlCs+NMwc!NbQ+nm%vInoYY7oV@Jz^v##Zu~ zhzKR3f&yvRzN1;EF4o<9_Zu{H;MCQq3* za}J_jy?)d7J%^5;zjFKGvsZqBp|QzX`K48LLNWUfiLarCppfNJX-S2bzOJr_dEgL~ z#-yo;c+ryOE7z>wv~AZur=zEwuid`?#QVMf*QofEtb(%2+C~xc52a3GmqnFS)zC(T zH0x|_)4M;3c(TKc+4GmISi51<*6llY?>~6t_^ETwS8v^Mf9n1AQ*cCVQf6*pX;r;Y zBw_zC_Q3)<1tk?t9m5XhRyKVG+Kn7gbOZriQW=$SbO7=o*@K?$&qkh_REW&t0+-MZA0e zp<}1dUq+-ZZg=myKYsGu>*X8YPhY-9#3ZKWg%_#@W|-cxWtsS z%$&TU(#jeVF&1C{81(>JPDx#_y=BkAW2Vkqv2mx8pCl}*Q6WP9Lu*HJbUB*i+2H` z(Fu4tMP*fW4NW4k5NmERTlr6M7|^XW3_A51K4r1vzSB3{y?lcr<5S7vwWdMDru-99 zPtdxpkyZb(vsUjse%0OkLr83LW?l(ep_onj8xE72zotL6l#ZaaR{QRQCokOO~b@R#lkk~YcsuN1${t44oMoG`S&!{;d@ z`6W693*-7G_WR!;f18`tQkpW#1{VFtE!cGQx`*#Kj8L^$O2_^aLIa4-gC;K7cGBgA ze{_0&C3>Nle*&n>sCg#oTc1R4*o?pqcV--rK z{LSG%zl@4O=YbOzZ#{AQ z`NzoAT%va2`LRIuym@&2%)hPLq*b%ifN={qAG`6yH#9N3gtVDhB7m%@ZyZ|dFvR+o zl_L+(?}WO%@$l8h@4m)o7FO1ix=fbfWGQ@b)s#V(%>p;AYa$cOCq_!|M^qu>K-OKC;TN`Vrs(A{ z)Jjf9@H_C8hi^!wYmwYoo|_cv>v8SKmL(3O`t`8x)WJ|kL!~veT>k~0>v-tzU`AL4 z)#DeR`dYaoF3*WO+e>o#D{(MzJI~Vap7dU9%lOLO0s`}HDxv0+YFttYVY|wJ^@k5 zXnyDbzjF+y!-|CsM09+p@6#J6w=bVIqPImmjlW?9&<+i8Sh@SGo3}q!d!8UFGic->RMm_%!8V^)`lN7~B>Pylh-aWjqch$5JJyD_xf)-gdlqRjx&T7zn0sk6fJ%xK@o7Z0qNF|rp>t$snZmD4cpKEwetF_Oe)6dT9Yb84c>?3fQv zE(0~9*WXfDOKFjc({tF2RlCo)y!hA*lSpBbs+`!51U7xRjfsv*OL2HmEX`t#%*u-8 zIC)kiE32rgsi~{88l0v;vu#^eQ=-ACYpZD*nD?-ov}mK#rF-6=|10*` z4;~lyt(-c<+DJpGxn945szo4xN{PgcH8&JJYHAvqZMAju^bOh>8W|b4@6e&6q$AgX zYtI?$8X2}Twy+sAZsy7z$FDtn{UtInySTFcX9lXXV?R8;uxI(?L6!!pEe-DtGSE>* zPO5(*YkDlbwRN$oH|}6!X3?p07ptyayLGp*VR~>jhTXe$>uPP)#nQU>;L+0-IvzN8 z`^o#@nB*Kt!FCjRO{z?FPOR^v^ShQ#9AKfV+|sqYrOb%JsTH!I!i&(+YiC53_m*9{ zcE^@U{{gmx?d*mPA7L_LC+sh*iW7~ak9hQrR#Sbc6RZ2{Uss+%8)9u!$z_MN!iiwADr2~ zU`+3hnhMPWj4x3k5iJ(9P|*LhLvfjRw(i!m&w#;0M~ognY3lS@yiQ@osx@nvb=(@m zRV$V+Te5iJfLZr*d`{LP10*2F-$P+ko|j8srnR@B@3C%4QSX=AJ=*E|e>$Zo`#iBO5mUAS!JTIely?B28Qz(EtILx-FW9@w{M_pTk= zwrt+IWAC9;&bQn>q2WnL&nd#19g9?6n4~f@^3A;yo9B+OF;bOn7WR?qBC9}DBt)5L zV%EtLZwCcuH)8CBDbt~GSh2=&>&`vU44gcD?!v`O%oXEH7cZPUbLzzLBS(%NKYihv ztNV+${-NJfvWZHju|<}sJR|(oonsqk59?~EBGW9(pXw?g>a(`ZI z`j~l(mao~cWyfBpW6+pfxpCX=-h)SvO+21F@p$~`!M!`Kw{PCKcJ1cvJMPb3z7P0D zHa4-);}t1<=9sGiTwl)$2F!*n9BEsq>d_xZHL3c;@Nz>g_v|_rAXG-@Se1HR0?%sn)oiP!rNh@!i!R-J z^&dQJ)YyrW(dgzbSh9T8I!C_hoIZc$hU@)DP~0F7U&A6};t~>-lT%WX6BFWMq9Ve+ zeGOrQHNJ*N#>S^)KwCz%IIs*LZ?A#VVC##LLa^~jR!c@|^73+WvMk05Sp|70KpW&a z1q!dFt`(;s!2ftf%^|hT6p_4)0s_RXC`C~-NeB>OZsOl1r^K}}#z=wuXtdSR*3sqr zbVDO!;|}dRbTlzBH8V9cXU*iy%*{>B%vck)L#y`2#zsblhV9xh1|t0;y#{@;9-}MN z6Y2}~B>cTjuU4N0tzL)I9fJ7CAkn^k2gGGzX@&O1>y#a4&RMW%>5A1TrENQ*K0Jm> zK*|YvGhdWbcywF>)MeQ@$}oh;%^^k&$%*lC-`QA=?@*+rXTgS{ysBA|#ut?uQ(Kr6 zjJn`BdxT8~&DO0InO3N!N=>a9C8kY-GTVkx;!tP=D@-ml+%2nY#}{+^hck)4}gR8p!`R$g9KT2hps z_ai%tP3KZI(lRo0atn$|D=KSY(*dm-ixH!N(*=>M zLK&HwTXe=4YSW`vpMC=d4ze9GWazMABSwxIJ(?NAjp0UfqqJLw(}4r~_wU!YPoLhs zdi6y4v9NP1v7vfMaA)wVz;hGsrOo#S8@fkNwuh)E*F&af56l3%i@UPcY*((^5SyO8 z$e2B7u-!0>sFS8lpFMx^%C#68_8vHNgdZEAH@@YDE|YXDpS}c#M#d(lpco5F$||aB zYnghkwpDdSnbhQl%~Q`WC@LaWHMR8(LS8>BEk|(;%F)N|#I{8f2U!_uYqizHY)Xq% z5M4&6Nt@N7bXh%#K4ZY>b9#72Ps049Nq8Eg!-7wstEb26i<FqAxMv+OdY5 zkqM?!CZ^_?@Su92EC&r9GIYeq(c{KXoIGX9v}rSD&YClK4l|FN$Ia26JA2lQ>C>i8 zv7acMZ?&ktQ|Fk9n9G>gV=%G zpkcOy2ipxDK4SFP@e}PGX7Jq>Mw{&zZBCxOaOvvxo3~x>-hiVD=t(z3FOiprYW`ufHe3nx+^Vd+tr^zGHXv%6L} z4DV%OY}n2~+BFz6m^L;UamHMG3T=cjVN6*Qt|Ok?3((WB#<+Lr$kUl-ItMEi!Y>QD ziHYbZKW8j9qJHvAOc`?vi%u4uEW22DBQ`M@*oTc6HFmUAAK7>NRWE zZ*X*EH&Po}NA30N)~?1RY3Y(h3+B(AGkfL?l(ECqsgfy-edT1ac8E8rm|BM?I%y3G;!jD@#Dvd#tt7hp1d{(G6h?>Wcli~j$5|xK5+QN8O$VH z?mT$t@#NVHua~dhyz~9=IUo@G4$oXCuD=k~2z zHaV_ew`TRKmCKheTe@_~5_WOXBFSQEiDa>O5wlRZNVr(INW$Oi7Ez1YC8DL=65JPY z3uNcdn>%-oXtv#)xp;j`maRakVzUhO^~9O;m#@3refZ>s&nuMZ$It!&LBU_YhDDH# zmn1Th{K!YUCyiUW_ktC!BC)#@HKA}Dv9;023ArXY826!68MORHPL~zc6sAP@dR#ra zb?NlcgL?Pq-qji%w@pt;Z?0EUPp%i&Tc%InzWtc~%mB8(0NonfTYzqj?S*?EDV=Gi zbG=v|APai`{wz#_*`FA$m$)bO6T=SR&^B3H^T9*VI7f~~eI=WY(kaE}ZDb1%n{6l0 zoWpM0m8(~;vDe#OzjpQNmCMpe@UbI@4({g*di%DmTefW8%x)^$DA`19mTVGlWE_PX zg`0#MCH%c^BejX$EZV}sD3aYMyJ0PXKCGf9-597vcjN!&|<4nhsRoAqc zv*#^bvSRi6jaWmFT}zC$m#*Bn>Ee3lzWc++9?TQ&3Fp!F(ZdIK-CS?oxOU}|^ZB!< zv9>sN^ayi^bz%-y9$*hjU@|Ik;tnzgnEjFi%t0aOk^_?FXYoN6G|H*|kb={}g9rBS z+q;L|#qMNx*zVkgR&l@y!_!&k%h$1T@ObX?#t-|MVNtR1sHW-ZnOWJ{IX`~nj*=Y(bKz4!{fvpe)a8n%+ES_#YbJpxRa~DWkKPlP$hsZAT>0=KxR6qIxpnjUwJVn|U%cq-%wAy5SDs_fOW?&Mxxk&T zJI9y)d#phYvsSB*L=pyH=aN+#9b7#+-KE-q*i= zI)Bd0X$}t4X3U;FXCCQ%<}}Ub=E%;SH-Etbb|JTjSMU7=Vt3sGyWp?i`hLLna!?2x%7#&4Y-roB!GQsvKl;6U^O`IQJszP7-@nJ)Vci(l zN*C5u;zr$(xN)wG3v*lI!ng`Sm$*oppT({$Xp~$1oz`xyu6QM6X?vBu!d^DMa`oDc zn^^n0qXT&P#*gei$0nqvV;2@X&gf6Y4YKG#g+jIoeX5wkmMqyR_C$Buo@Jo!%}EOX z?DO#Exx;(5tY5PdJwCea)oa$RTf276nx@s<8rik$)^FIrI&vGCP25J#QDFlH;WcYk zuV&ZCuU&^QGB}eU)={1WA&dnKVI*iHi>{63fi0op)e39YN<&!$bDOy>+*Z?V+c6~V z*@wOp9h&oH6y$?PPd$BJzw<*e!m2qUD)xIko5bSuLpDh_AucvLGCVXSDA51Y2Vd0J zmp-Vmo-bao&x@W(o>MO*&&AJ}r^08#=fYp&vk%z& z#t+;dKEw*$o7gS{1cgR@PfC+6U@(Is!#r$K*jyBum}6L7Mp#=89e42i7Y}Y;IN`K+ z*LHN`TefZADcQ~KYTC)|;&#jJ*}He&{{2k{Wca>iw_w-Ko#RX!J$vTN>C;W8xYKfH&YnGY?mTmWL7&p< z{5gc2K7HyGdzw1Ko~5M$%moEL03j?O@&t1J0(uwfA`29K{v3N&fI#sn`g9A5b>=QI zmn<%0;JtqHmdhPzgkY2Mio9218IQ$%5=O-AAH*O79S&R84vV!C81-YhmYI>7l9Ukl zJtitLGCVBo+qbV@MIn{JP58jBD1-@Sf+WFJA(CJT?sXwzXayu8>{rn@?kgLj0JHor z0sj7<*-z|8oll?rp(Y6-RW<=N=|^E{1q_?SYo<^Mm)o~*-DAZf+Tmjd+dF)2k!2V9;55`LL2k_NGefe6sbd~ zLO=4P4h&T_HB2p6%hhO9SCNHSVSX-(DIIJ5q@)Bkp876{WnwC$*_eh{E~YM;i4o%e z8vZVhVR2_-MR9DbOiXli6ngJ)DohmG7REN1^u-fv8?x4ewT+N$qjHT;$gPzc(*EpQE9AcszOf?D>lq%=4ya>~rn~=gE3;-i#0D&3Os=5((5z0bUH1SO_4#_C5jR>5{YpT${IDXq>Sj4#MrHu7J?orgxbo|{Or`kn24`I zU(m(9fA{{quippB$NCT4N7S;{5#%AD$I+drb>K{8!%gh-weZ`+mmR*VWyGKHmU*|O>uEi zVIh+*%B#$6%A;_v%dN_*$*s#1=drnxy!w1LkIH3#2+$ERS!^bkrJ9|Slbcrv9Z3Zg zLojEHYR;|(BM}K3YAQ<$bFaTJ6b&nqNJ#_}!A7!C z!f2RrL^dLXjbfsy7zrO*$Nw*m7RPY0+;?L#{Y^rSV19+x4u707-c$;@1EC0xJuvQ) zKz+xrFi5`YYHF&hDzQRl%Ozz+r43~jrNXij{OU@XGEq5O#+Gs=TrpcDD&&f|VkKgF zRfR<~1_;z_^4_o-WYmdUQGHESc?p^dI+V1u6egKUVN;p3`cz4pB%R4%(j{qZD%<>@ z1pkQ;%BG9>5Ft+&X0Vw?{6sTXYUx!9y$7UWO2nrgb4^T%nz+_{`pNRMv4O2;>qNCh zHSiXy5!S*3rnU}iQ5I`bwn|vZRkbElDlAR$N+sdHCKwB>hFY|5Qkn5e3z&SSfGMmm zkQCMxNebBl7EiT!5`jwYLj2!YBrax4xKbT5VXMR(7;%$TFCv#Nd+VW5U>Qy#APbBp zu`s)l68bmRHr5N7M)?LL7glB@#me>MiC-^cAAkvKP8f0J!f4y1(Aa=iQ%CY4s!pi! zs%ffa>sUn2HY!0DSq%ckvT)i$`AZnFBpWlF1~DTkKuD#yR$LDv<~(9nM1Fj7#Iii* z+fP}je_{%8kqDbagS_jK4oP0gOlM(YB;-yB_XjUqN@`oSP|P zMu3EW`GqDXDgrj(Zxn18v;O}3_O=VdLbYMAIEy|sQ_@+22UI%OF zM53l3CJfLCm6TW35Q8oDMl(Et(`Us{*i@7h=4Pj-LfOb$%mfDqLLC+$@#g|`z61sZ zg+ITGgA}4M}>w!qwDwn?dw-Q-d>)b66kt#y}Vz( z#6B!Enm@@7{sV?56lS zedDIyt=q0{cX@T->-R9)Ktw5-dBqhqjgni<(Ij(*FrTieDEyJ082#b1|B1k3Di?0JCB1Zg|gKx_SSJ*ZY9bnB?q2Qh0Zo zCKCW zuGh{T-HXT<&T^PAdidZ0eX#-D&AN-klI~({)(vKQ@Qxh?0rTLlyM6zWvzJ}m;b{6T z8g62xRkaP!K(UXS z+LaI$^7+kkcb6+?j~&>#VdbLPQ^t=N+`m^>OAFKXM(y-kPN! zLBmE(oHl3iO2_T{kDj{*bsO+ZXP)FP%EP zXWNFAd=>`x>(RBdX$M1nT`f%wH5Fw^8(Kxxf|&B^8W@{cSapZb=%@(}vllL3w{`E4 zv)9}_e0&2V;EY#TQQO3NG-m@sB*NM<@)p9s_!)|Go_5-?Va0-(QzwibI;fA0Ri}3n%LTh5ri$X(Y ztb3x=Mo*ftka%idbn)={6q*3fDDhtcEW{0!h1rRbU*0{xckT3nt*how8#ioVuO4{W z9gW-RX=$h`w{8Wm5`5o|h10s!|5ycH>-z2RRytev=r?5aB7 z^~}@e^cQpHl7_0{?4pDTXl$sjqooOd7e02n2A{Jsqj}$H zr8cVY$tQNgCY`$WwjDWn&N9cnC$B#6{v4SCr+)U?&&W-iYRYm`q66POadSSpYyBdJ zF}A(Bm>TM8x7AcvQ*P55#R6xVDjYk?2|Bjk>m>S@-@U>=&%t zd-~Rs_g~}jLhE?5VKQNIM(k3{6rwh+7uB^Q`_x3G%tS&9$*~5|DGM9wtBbP|LO*!j zxpa8P$~luq^s_e6Q*SMkO_fjvvaOUg4a~as8!=_UhJ9ymKlA;DT3K2FrMQHB#f#uz z4M}WY8tb8zhGtem>0Sq$AjlxpZR@@%gDjdiJ^eVxyVtlmnAA|a<&yQTFe*!;PP4*H5=kUBO3!r z?fAI`damN!l*oYB53e2HwQAl(yWUnN`p8ixj{DY?Q_|4yXx)FL!{SXuj_+4^t&<^4 zOvGG<;*?=Jko|+Y>Wb1LOf%9`Q?Ly}tW&V}PbLI#7Z$@eu$BzuO^q0t^0QN-zPx^P z?c}btb0*pKvg`;iBAE<)Jxo`wjfP&wuKh+iEZGR(f~W5S!{gHPN~@dL*N}A;xc-dO~E-+b6fr?%S}yerTUg#=08H3W9W; ziLNW7fH!8+b-*Zx#g2PVU5EYu*YBAHRgLT$WbPU<+ClNunN*b*=VzxS#DoTZ@_qBl z8(UzW#PR-fa2Ri3PR>z)(QO@!Ohs@^f>%>`AR5~FgBzDj9ofgKy|$XNqD&f*2d{xP zZ4FGj4j4Ig(fU0nufc^PEHS&ZR?;Gm>_kkJtYeVH^93no(RO?P+pjo7!mO9sp|!&%}b|`8er8?N40e;Svn2hkkf%c71YVD z{YOrjzi#)5EBAf;qf+4|+$<02#c5}zn8%Sp{;jvi1Gk&kF2nNV;-xFFLC0|m0pFsN zGV@ET8^uJPo}{t5&2f=q|4U05L)@Fp&C0PmI{GLPbmSwXh4C!fMsG%fC#Pj;fM8gBl;)6owPD-A zQeRS>E_7yY74DQ|8SR1Zq3HbP zqi`h6Y1ikU!r@_0ybsWo@H;FJE3PZcO$zt7@^-&|_TaX4%NER@Wn$g5{8IvE2%5aj1KU0J+phw>~Vv7Shm+yRixvobV^S~ zQBAu;*Fh8JZ#)FYvT;-zrHf-M zbWM8& z?8M(_r2X90+mBwl=lMA-5pz>wkHD}Q$fzdVu@i${x}DjvbgEr<6GI(!e07V+)T3n; zHH^CSA2)CP!3(@ET6%6V&J@HUCuBp52(A2)5d7+ava=KVl~DtGkr}eNDFZWlU7X2c z-M!zi$@A9kI&tF>PSi^S7G=a|Q$tjl^ZoPl>qi{tj_G5rudUvul?*l22Pl_e&2bQ z=YAp4$=OA4Iv2Od6P9I0fAF}f=HxhU!oY5(h6Z{%+L%0Rzz|;#4t9F_9jy9|p0Q%b z$?H$v2FIqM0>N{NFI9D6c}C>h2WNLI8rRQU8!pSTbSysGsx7Z-V9|TzwB_55U4G#6 z8N)HUPI!?v7id#idc=G8i^_Y5ucUf#acz4_{w&Ulm-FxchEHm64`Wi|4l(1A#zR zN!x@}>0PH>p8AC*kn;hWbJV7xDEX`B%|q~Lx71aPz-Q65ZKKuRy8r0ueRlS4#}vTfEFX8vTbzCdX1RAa?cqzcoipQqjaQATDPG%Da7mc zkqxtkFVozjaHP%|Fl+z}+4X0#`X+0eG$?1`;RJ07uZEVNQTE6wrg*%>~a9Ab| z!)TT-s>)3WetFOO{QhVlMTzo+UqPwiSechb=QJ*>>zx6^Hlbs*D9yN#-5JBwb! zr!L)c?8>7zA?U!%@wWM11_?+Fe{=uruH{qhY)tjF(R|g^c}HBaex|_>19`0Mwcww^ zYG`9E+}@&V-=Py`FW-ESoJRI70cXAZOh8RhM(mfDR`;(Q-MM<+l+ku%UER@8565)E z4@^tX$ec8VwR_H@(}IsSf3OBpK>Vf~O4B2KJuW+Km^;C?J64#6tbwc{{7y~8rmR_x zd9HZ}=rB68H|9OWjM|%a>ekzK^wb3_w;kfw2W0Wuk_s~LCWU>n^1OHH_^$PfXHOY3 zbYSo9UCd3;=^7e!Fz?c9@c3CPw;jFu;FW(APRBq?AyWmok{}hCF`r)CIcWO#Jd;gs*RX9bIH1$M=!cPCq;oo@OBhr z;?RI&Tw_D5-ao%{`NY1Bs}|0jJZ6|}-=1BqEIW0!?Ao*6(D5^uZaR3*^{HRTcN`vz zLnnTwqC6))@bx`sr_IY|P8e@w&t?eA1K|(2Q)|d(8Q^$Lj!cQ~}BLB8HZ z^WwC_arM&qvm7Q*9E%8{g<7&^+riVozmL8G;3 zE?kqQ&zK35aPd5Le$Im21s&$kn=@zDteJ3OngvVh1&f!j*|_82@e4N}c>0FKq~(>> zwbTk$+)!PXpVc`rD)`f@r}u7NJaKUMHpkV=7cYbz_58&v)^360@{I?cenGs?H(cXW z_#`yd6lKSUee`y}dH&e`9h*16k7w2D)i47VZ^(4~;V83V9dT~ZSp$mW=54zV9659G z)`RD7{lhRY;5fVHritT_iGs0nW>R!$z-=Z45`;Bd0I9xO=@1 zib%}F@Q+nVv-H}syp-5rKkrAbSI?h1>a>6F9@yyaKLF2woI|;XP`n4IeUiO8`#^F! z41eNFH{AGR>7t<<|Wt(3(ZY z5fvt;JG^vt6j?=4c5-ycr`InY-MxMN%0=da3|2Ikg;#Q}=3eEl7+<~w>-r1M7cXAA zbougC828@CNn$uzu6e1$4+>=U$J+&VD#_1DPmBpe0=%C+y6<)yUJyXtxPABGGoN># zLn7kSe?VskliXy!_bi1gu;cnc`ccdi=&{0ExBi*f^DqrTvAb?H7g7YCD!=ijS)GKxshxH6)p^G8y*pk%?TK!CZ}ipDB$f; zrBbhQSlmNuC$g_nQC5`yBQp(7uCXyu&?mshCgyu09Kvv79PFQwU5r9Rs1EWgBpWKQ zmW2yw2CQXMa0*;%8d8@opO#7<)0k8)O+7s$LuzVUTFx()Bz#`4a59i8QA1HT)YVi~ zlorFl6b`l-l5`DX%h<9FL-f~Jnh}xYcyVzP)Ymm|@gv@+#Y_=foK%uiA}OYdgvDwl zaQc%Pv0^odsR=&|yoAN98h|3Ge_;MtMa)^tN+l&~gqL?~sprl47&Z|yhDmHIRAywq1Idc-W72=m-m|}-gkq6fikx&$JP*Q#4@CArL__{+6d|RO_Wkp%z>V&%`)frbO z+%0f*qRjDw9~@8XM49qb6Z|#D8TDiEt%$+=mmd1zzdroe7JB1vU;eo_zUa^!Uv21t zn927dtnfrAgpcqK#1|U+gO~grh-(o4*B19d2p!b?EeG-)2@*C0T!SHR2!7-X6(eyz z{%HPu{K+^cU;51mX+F&P)R`lNB$XDF8Ga;3Q7TsyaR_JMzPWV=G4(WK)l*m-tqP ztlrd9Un+_@*@}RZ_B!$SRIseJ{mIYepj3T1Gfgj_=ZmLg6vkXg%Hm|=&y7?_rULQ* zh_i9{Kl#E~e3LbeqBdq7qz|;gmk=?h-JW?c311wYc)vP@lc~NtMlKDXJC>0f<4XRo zxjzx#gaslylcP=$0JSKCOQ8f7XUpO^LD8{}8TfdzpyRP3{8x6?qN16X7FpC{iAJjy z!*UwmJRR*91wG=M5n1>Qm|*xD*aHhbjG?P}?hKB4N0_M#>0Bly5X_1Ty+SoJhaX$JbyG0^L!~BHKq{?5!y_Y!WB}qL-}itE*W1+B~tYG5X zsq#W_Qty(vVv3f( z^~sZy3DBjQ`RICsCg^}@#jGT*l%l)mKj-K)vr1CJowJ^Ef*ebpF^Qud6F%xdB3DMy zqiddWbm@R9gwca5@mys`sz@S7*@>XaqB}Gua^)xvi3dkF467!^HA&>bVW(jdiUWLf zysQ|ElxG50LD5T?M;tAjR71*Ot_aVf>BwVt0!M8WLnTLziRUUQYQ+NtjH^Yq=()^8 zPQWn^$RW7sY8?r*g|vK1JXb~0bC~;(YFkILHA(Ewae$3P(zD|^_-KKV+8M`HQ}j4E zSq&y`njWC@q8oZ6P1$iA)t|wq=V*hvIIf1GZR*@OI!CtwNufJe-RBsNE;2&0 zz(_|4Sa4F0zjL(|t)K70(P4^>Br#f<_c#eh$0|c!_IHk2#X^5d4T@o* z7f_U2G)ES=AFgoJQNl%eyTZqex=47UIciZMMfHyY3$@}hM@zbk5jQo*1uQf>0FQ*H zm{LWh6huUELddPWgt8Dwu)jd77rVgskB*Z=B-v3Mb-tLQRz-q~dKk$m6s5=bMWyA(%DtWZTY$|AWYBqo-VjXX1_hmwerleL(<&#&=2 zs-zl&4+7F%u{j_cdtrXhHYCt=(yn(68Hk6j)-K``Ky#lYt zMroRt1a;&ziG%t8a`Z-gw@OCQ{+TEnV<>e^ipG#O3zgf3A$V#!>YrDi5=vlnL5Og6;lR=0?J zAR!l2$VKHAnW56F7W3e!6psFkVTD$>0K^3z8w^@Vd(h;L6<{lex-CUA=$)%6>Rc#x z#JpyDksfq}n3jXrVTU5GmoP$!HW-5+3P;-p~=f%7C&| zj^0Xe4?)=rN{`ZF?7UG2TTtk;QTIQ6;}~=#ScXxn(Y;xhrEzoyjlM@$4AM42DlGwy zE|e|f#i11)Kzo?=6*%fJc($VZ?Aw5cVPM#S`#})8RhM$r95t~8gWep~UMcr0EPH4{ zE{2!u@f2kb?gwB=N#|&HD68pFOc{ZBNnvP;o@fatLI9#hg6Erj97P=gxRGjIK~eoc zicthTl32Qv2{w zBU?pLz3>o=gX*Y_1Vjr-y7AJ`p6d)y@QPVH?iix(7lj_{M<$NGw*_twC?i0TO{|1h zFEvIAYeZ5|?E3?Wut-c5d?GMWAeckI1c2}vgc&^KoD|Z)$qu2Y_Kks@fYM~a(Jcb8 zrepyVgo>&p0;xYpbaWLF6y1W7iI=5be1XBhcrbhnqo~zc7z-NZYLH7%LP42N5H!;U zZ3S6E>)S9IgFSTw!BfKUsq@-wv_q;l5IaFy0FpvkHCKVr>nEZ*jUYZCHF>h36m^wH ztO3Fkq;dt2PVtBxQp8Izmu7L)rEe59i08cm-YigDLFvx3-fLl{v>6alMj5-xm22 zkb7Vl8B9?HKZuYqfLsN|9+Z??B4nBrLfb#A;*vOlq^B1^C`U>kEIvGF0YKiMn1S+` z2d$BU1ok?W)dY2pBB*Hmgh3Q_kwYr!A+oeTot(d!8MJ?T>!2Slu%Gc@i;drPS&2HEJd{( zrfpBr{z*z%{Jg+7lt*3$vL&v*Jo1hdN!f${P%VcZuv$+FMKL@Eus;vH(+u7aTlb$p25EQ#N_!sY)dB?f@H!5iXNchUKtx26{vR;(^>^zz z^kZTxJWA!g09#L9o+}cHMnt^>#gdnIzeS#eqT7}=07)G~?;{1i2AdrZB!${iG&X-P z8gMUa;Lt0jw~_*>I!FisbUY6|3urkgai9$4p*LD&Vj!hIHUdkHKtBnYR1tpUg|FbD zdjL%Xr3#ceJoH2h6f*#Gp^!uCyN5{yfugQ7*D)Tu2G}r^ks4~E9W556R0Sv>K+)$r+KYcd>5dHybQ9D~WQ$;_NF-UPh3}>cVW1Y$>Gr*fj$M&QIO~)9&P;>nwI;{0#40B-gwyyFdqJp z4aces61iebs-}Wcq5#S;e#EMkX+hUgGW}Q%pQNMSLI#gENA+F^R(}Ha0$3k}wIKNL z-uq&EMDTW;PO3`wW zCCj#oB~it)tYS5bk|g+qD zr}O=T#WAB&y*RNC%N6v2>cG}}fj}R5S%@3)8A30&;`49t{5|Pfa~vbLRY&TK4=gnzk{3& z709XBEM3=ui?uO1-#!Emi;R$ z0=vhs78u!y^}$vn8+Z@$_QjFc`=(qU>@#0%0VBQ(KK%J!WJwBiCHpok=+FGU0qc;D zy^Y&X;af7{7jDJvxqmSt_fJml;p*gHxH?fxgu7aE3|U`678-%|_$*gdL9Bvm?}d3k zV3X^czeMJ99muR&A(uu+oEMwG&_99I(w4HtCUmv~@I&7Ix@(Wn+y3nYmS&$VV1Wfk z7};l+dy!rGk4NO<>~s4rPy*wpUyYSw9$BCS#^@i`E$9#blj|+C^2xpMq4wG23hytG zeRR%??6QyVk_*Jc&Wp{!p7oxGOMT1UVl%qZZLcmEPTr6<92Exr#CMNknfkGLEHrhd zjd*eT#+(T+bYoY;K%)Ckqh66WAmOX<=?_(^reLQ&fQ9p+J3blno)N~{^k3h)AM56| zM!Dv_46hFV{L^tCUinl1>!v+&)x6n!X>+==tysr=WRIi_s~O$1SgoJj{@Uu*uWdht zC45FM;U7chsU7QAKf7+*Nx6i-3>AtbfriPm5Kf(H1F?dE^UJZ`I>%5!`x%A`#LstM zC=Mfc>v=4!JCFsQ!fbsL5r{)R8G*P;Mj(#YU0PgMbz~m#yj{iOI@_je7Q`Q)WiZ6@ zstjs;6z__}pt+A0AmAmcI3gtz^WT%fE(A&j&M{CT?%jDwaib2)usCu+3k=d1z7KVG z$?(fR%J9ox-^J3Wgud(Sj}WwxWp&0Ut_6;azKvLoPXFMK5%I#I2jKUpScmL)wuaB!hj?nokxW8j#3%n z{NPzcIPbH{2#7w_zP%+a>3qk=Kp8Ew>Xfp>GgRNeL z`{P&6|4xI5NtJx#m%yZkw{RLpZ~Ef=eOQmTsHpCx4MIk{F6b(iR0VwaQ^cM zmcDZ+5KqSs9zyW0doO;ZTyML&@Gw?rgJ*uHAOJ7E7V+%EpU^X&ts8$4563=jRPpRf zJKrw!#*ZI-0v%rC*(F{^B>vC$;i>S$N);u)v@AmJ&bbp$f%n*%4m)AT03!C7znE3Dx4 zr>=$H@lFnG6b8eRdvQ=!|H8-ZoAJ}x2L1G%=kU1pubgTd;!=XK#o_;}C$Xce-|+dK zjojhz{pI6)-2COCw|=K>Y@zr+eEZ?83x+d~{(bLyZlP5D$we8-2Ojym8aXAf<9?pN3S z%ZEaCwE-k_!lL~n2lzW0+!E71H9lx^py|OoaX>@LMLY3M&S=jXVrqz~A?6|uXh^9c zrG|(aB5H`Zh=~7BpFqko?VrX0jRXH(IWQ-Ww$I^Amv*P|K$C-I&;v~lG#+Sjz`vK$ zc4>TmykWfQH4GA?Q)R0g^LJbKuB-D^lLqZJ+H6+xK@V^!kYC8a0 z99ZMPe`gNN+2`!bkd7j0cNz~gInZ=L!vhToH6+xKP(wlu2{k0tkWfQH4GA?Q)R0g^ zLJbKuB>b<1gxU_kf9L40Hg=5zb8T~N%aD#DX?GeAG&#_8LBj(L2{k0tkWfQH4GA?Q z)R0g^LJbKuB-D^lLqZJ+H6;A6g@oebe>xEqePURQiE;c3iwXP-iwORyYeYoxFI;@X zx7t6A0~!bZzvlpTlDbJ!NhX1?95 z7bf#rlQ7O3gnnLkKpYhdqEx3h7)>RX(z3E|Sm&(EthZYYqReWYFIy;Gu;@xm1*6fR z*X!_evqyy$zYc6Lm6TfT4ww6!XSsLIyUe@7Tj>#QkH_tHIUNqWt*q2iVm8T2hf&9d zDrSq#;r3P6)YTUnmIdYl%K|F`6%FS4hWh%tT7Pww&+BrcqNT)SEa-*qD5{m~3?_@s z<*NwE)0a*qb@&?E2P4@f zSHAcCANarrKe+rub01pvp%owUe_-nUfBfF}TyfcSE*T!}>ud@5E8OK}Al9H`Ri~lE z>aJ-WjL*LJ&p!UiPyhXA{^9D+&V6>-XIFgI|M!3K>A(HtCqDM)AAH{xGnvRpPaCQ_ zL9VG-(`d2#0zHx0|MBt9eC|tM|JJgruUg!+q;^+VMAOH4$e)#=Y zOeRKqTO0gdCkd8)xpcs zzy9z4{Hc$9=!)ssXm?AU&&6J4Rkx|kRnrk#@@Jp<%C$fH#jhS)`NWe?e&?yVr;wR z?YjBbzPo;I{j&8d*8A5y``ptjAGvqMkH7b|fBn>-|M7HUxUvU=)!lkffH zmwxn%CtiAe%eEamm+zX}wQSdlUH&Z_HoW@cZytMK#gCVN;jz2db0&|CXI0O1^=nzNA~a7^vZ7@ zzU#-|{`}wmPG$7$4Gd&I^x5TiKKa@%RGu#^zohD0J2#{1-K_c-@6J#3wboWT7nyZQ z)u&kX-0xO>_L2X%s`Fy6sCwk|x%sOXJ3W2k=vzBCz4Cukb3JPoe!J%8wOY*!Gy0Uq zq5nv5FO@!xLmG!9`f0lIAA-E5D}HH_G`pfnf+h(UyyQinQA14)HPz}tlLSo?G)dU0 z`3ahzp!o@!pTJ*BLG(>qJ1oXkHIET-P0c!J)&@e&61Pv3k z{p{a8W}sOI%{pk-L9-5;b);wd)GuAxg;x}AqC$Y6P z62FP&lJDTqz6C-176k2E5VW|7h6!5SM2nkfaT6_WqQy;9gGKM{32p4KC46kSk~%AZVKi3ud3Kit*F6M14=iEMlpi+ z<0#*Py6vbnjDOuI%g<)C-6DU(u^o5qDAgt|lN62OU5$9J_;ch9p?n1I`TMMG;z}_f z5-8h=HrmA?=$%BFeo0vhC9aTVBY4-0wg+%cV6+@f4{G;{DU`h&Z@XoyQ)u<$7&TkS zAZJRv4=qOVOlsT3yOGlJZVV-Ipy&z_60;(Pya?LA9M?&F_Q~f2YRzB-A3)gw`8$+< zgtD_JGk_LbKwT%=Ph$*MqGSLZNFet;Xl)cVShr97DN2o?Tnsr8&=kPP)jRSbkDLf< zU4fC0;pxL@xgY!;2hBO;42AA|DgC|Jbeu5OX4qZeV_OQDEU(e{J)4Fim!@KihmOSiqanwpTeB{EqL}n@N|_} zj{C2R|0Vuf{EhhEP>ZX?x5anGZK(0T#lNBDuZbJPt!Uu~;#$1>iuf#^=a9Z6z9_yS zJ}176vM-7|#rMSZ$X_mgB%T!C$MtG)4chprxCQr5iJzg3ABg+JFU3l6kN64hZWOoU z&3)n_S>s;ubMY(jgm?tk2T-BVNLFGiuF?UE*onJtsDaEn>HL6=mKK8}a_Uu2H-V2zU+uwut@s2~i_{ zBlLjShWqVekJu?rixWDVP9W!)IIZ*I?YvItoH~ongl9eO49GpF^XlvZ_loN< z59IibkcyizQ#W9ie+HTQF5Y}Yd<&BEZOFkFad$Ij^&6P;s~|Ps!u|K9z2;C3B0QLN1ux%#>FYvRlLU;Ft{O{xG zEr^29-FzXBPv=qoC_axNoklv0=LMuA5H8gQjm~kTdoI*lz?*$Y+mRj>>u|lR`0hH~ zp9Up|@VOJ|-eUd@sDBb_A#^W6d@ttTj(hfe1`})0;pT26{F<%q1m*^29Glsh7ySoT>wJuKT)<=O7TNSg%9K7qvc&#Pe_m7G6^j1w2C z&58B6=Pa#7$IsyYQQ6$JxZ4l0+JHANAiW6{7P>o&@5qSz@yxb%qTb?=+1Ym??gbtB zyIVkG)}f)rN#pCYNI!xO3EdtP+l{vz{YfNF@!R4Q?!FS);S_k~)5Gh(e zk!8Lp-)_P6zTbH_2fP$I(!C4m3^bQBspeftf_lGzA3dgAsrQ?4e@k(kx8k0>;j}CO zFBYIALU$X|<48{yOHtq{d#~VADF`{R5{YWS@ll2Lp#*7puvm-zkh2e8GEK~*G_u?Q zU>0kuT5H5JczQ(E{hoaLDz3i<#*i}?-^~I4I94IY^tb>lk~tg%C5DVRBA&B|^{PljSIUXGuDVp`3C|&ld?M@vk8r2ZAfkDsC_6ge*_V`;9D1 zJ>Moy;JOisEO;IkQFTFlppvjITiXw-yI7Cv#`cv??7%#j(KfBjT^AJUVWqim$Vp<; z6jO@jfg+A60ecuZgo<{sx7 zRy>9)%aG0ka+J(j3+t`G^`Roa7R!-HPKhc@ma|(`o=o41bROw(S?>ni?Ua0=j?PIf zWC_*kUFa7ia<2E{6|W0mF2((Np$D_nXvu>|kf&HrJfk8#k6fbv4YH$$kh5Bl9~{U@ z#a8OTPFQb6Bc*^dct%!Ri=r>dstOcYmE+=w9uU9AmE~V9vQw4Wf*iI+0o(vTifGMJ z-<0hrOu1O50BsgJWsX>i+<6LVjVwI}e@*DN6sMWe$YHB@ zPC9|&aFIZ+lL^UhqJ4tSEBGXClJVEdJ{~~s^K6+qwMtM%??K`mDm%rdIfHEGd0}8t zQgaZsh3+w=U6MKMog%wgwz^0ULEyMV8a6@7ICX@tgJLC0(+pEc_X)a1)HY&{G8Srh z^yAPa*+AVoAlURidB-QJESsdoXA>{ubC)FHXSmx0A9&Fw-H5xbm&_}z5bYGT=d8pF zO379*?&93vgBQtl0VS|hdiRjFY#DVrp^u_wydtCFt8 zC(0UyV4KiGn|3PrxKDDGtfr_xDXX*LEwU;Vgfhbgz=E6}s;Oek23eM@coOf(;cc=G zMU!<%XhCd|16TluNd}q5L{iE7w~A6juXdN9qMpXx%d+KFxZ`qbw=~m~Q{}nQRbiPq zL5Nz7q{N?210{}>JP9dWz$fSbcI2Oxinbkhlp$q%DA25PlYD;!`Lxp;@VS$dEbIP6 za54zy%s(KD3D{Z`ca4W2k%CooJ(6w6bas1g+4lTx@jQDhI_DhXA# z=@C4ik}|wq0V_pqr>sV(qEK^w$Z_&-qwJQmMXSTNw@Thp81EGvz!w0x3%W1=R=H~g zHH~vY)uAlxkT#F5!!}u-HEA3drA8S6);cf9?T4g$`vmURNeNgc+vi{&MlCMT=@8z9 z=gqh~SCk~q?N89Y(0z~d^e}E%Wu5p93Y|q#Zq|PI?JUINE2SU6<_;in=y0f%oRe-c zmBm(z{lzCXLpsSJI@oj4!C<(79AkCTPg#6U)*<%M7;wZyu`l9tzF6m1xF`R&OGCl_ z2qPyYU)d{#WC4kjNv)z-otJ%5cFEs6@%gqi)NF_4j$rw`*v2zbmg(rJTED~_(y^99 zqzrKo>yeIN7|O=5$RSkNxme`3;;S#=1wGIOIMYg74hsSb`=o4;p>#2*OWQ!v0*oY} zl-=P(WQlY5K$?vY)Cby zju5n2kbl&EejbowPCVFzSOzCYUWv<>W5ueHkyF zLorfJ2~di@7I$oaKlr#H$9+T?!7Cl8=NLF%iq&2=L%)Ks%Bdk4e0jXs~-?+hPaa41Kq@%D1*GrK7cbQL^ArUV!I6}trGiPEOpcul^cHgR@ONuAh$8k9gnXWe>Ii#YkF;2u4#G02FPuLm z4XjU@A(k-rQ8@y}-q;6uL6?g21gnF)=afJ8lC%r2Bm1y;8P`{l2rbHhQzSWoRNV7O zMZ7}}@r^l$1gHF2B(gtn}m?eJCO)`)R%iNw8I6)Q8=Os zmUdsLOEEi!0KXD4)*;&RbF0{dIuFTu%KlJ@$g~}Dn#dZeKiS5$2m3xMr;CEcR<;6y z7Iakp;{Yv=z5vf(fgx8KY@ckT>YRlSpeFG+-jD}-@%epxGD<5~jt|OgGVTtf6L?M8 zCkd3FZJ7IuvbzOuZouNuWRw%N7f6 zm!XhnPJ>WDUSr{1qmIjyAe;T zBuo)H6&Y-i>(D19A2=N5FA4|SqnklV=Jaz=+=gNxm3*U4UoTnCS_hH%`J*CukqWZ& z4zUK;RYp4 z+XPwp5)!Q*nN7I(frKY^!zXsd4H7OHS(2Dddao-whbnB#7C9`!DYbfW;%FI&$Il?K zHMYukm~=U*geZd2F=66J1v*mbLh`SZBin*I&eAFQhCoAzQRa9~aGq|F9RI4EDaLGR zp1y+ovw)!mX(XS4dMUR{EkrnP+a>Nk3$Z4~a8^0I-ICYGaQD1i{?jTP7aTB$N9NMm zdRh?6pFyIP-~ir0pTFG(*9E7*@U{3nB~_4S*mIa%JSr`w;F8os6fVjQr`H)IuCzE`fU=|_;dvr<;J;!)hOi97H{)t{4#%$uZ;QNV7J+`S=9)9WCIC_oQ{x<|34m~kd3Itnal85UbviyCU-MFn`{ z!Vbs_cw#NKvQw}wb&kNs_BY9XU%?&O_NrvhZMah#iHl%`!LU(wcPA=yl|mrks4q56 zt@?p%j`Ggwd{#Ec;jwWJfiVy?Myz=oOqGV5!}@-)MV2{<#Fke>Aj#9)A%0{6LuV9} zVjHW92)130o0TYG6ol19d?bmBejlZ-ART0KfCFKoC~X!` z)2M)it=ufFJD)i+YS=b;zqrlF-j^YrlYp{L+>H|JB%Ops>gZ~umyr(2y=ayv)#s#C zakT9HWvQLy8{-wsA$R75(#1t7fV?1=Sg=!u8A#t*Iki-g{TJG}4^OK8cX6jAh!#be z(U-(>+))r%S4|}?JI7C`Eyy>_C#{sc^+=CmmZ@ah#SigB|NAX`GLK`J7o6Xlo2IroOl+wWc+UVsl(SwMga*`9Z@}j% zK~~WZpfTPg73!C`qs%YJ$yf!r|OXzzeYMJp_=SxqB5SuevERQ zNcR`yq}?X-mq{m&f^|}YFO`{MvI%`Xfln@K*vozzJ3bH})KJtZ62)(tsuwWNYStf; zTT+yAa+m7OZf{2-FHbHu#bzkzEJQ`&JtvA_a$Z6=bKaC(rHUUc1|(H;6moh)zZ9%P zQ?WSZqE70<=IMM878An-%J3_A7 z0-(6j^CS}=l~VHz?g+Z;kWR?O6TOzVq+@j+??}&qqQT>Mzlg3_hdd%@7M0?pRO<(k zyBl*)6yy|=4s!IURKJaQq8yV0^V0TF{a=785m+9;J^N;>`(ICbpN2&eNDIo-}EY`gipZf$yNuHg6vrEqGR?fn2Bnya` z_g@gfpWsP(sceE998{={#g!Swf}(N)OLMi_I49Zmyx=rYB-J)6MU~>e2I&;LM0sh& zcr%UpNEoFYJc_hWkTl{4XNdDk8Yow%B^~>qTgu2#uYP^O4D46r?=J>M-;@HmvA7+) z0!0aI6dd|&Y=T@}%v5v3lHbs%3j_!~+MRg!VvvFDqHLn*f1O2eW2stRyb_puvVdODT z$^OW`jhKJ>0WU}!LSWv79JNwm-)E)xQ$h;Td{E#IAW!WZlP`P4W<2rG65D=JzUN@6 zHUtjB6lwki()prWc^~dc{W@8fhK24bH!7}^llubR{RZ9w6p-hk((RvWM|6dJ~I^l7(UlY9bk846Seysi_;ElmYDiWjOThhF=w$Ni?PZ|S@|02 z6Wx#J?NW)zmQBSar<(UWB~Uzve2)F33_E#61?S;aq-_`zbD20tH%pH4juT8Co`h5# z!ZTUKk?)oxrG=+FbJCwe8|suArRQmBP!w!&$Asw1s3CXuWS9>SbuXUQNOyiwSBqF2HSL4X3aPfh@*jA;6@?DB<6n&QBzdQ{$3 zlE8ll!Q4HknY&*#c0^FH=t*=U$y9ak16~jcYsRoF8XOZ5R_4%HakGqqlMf`m0Mn;ps_W1{%dA2=CMSr(bT8f_ zG=$a(wIqd;vrAB1`TiLxyu=yW18`Of#uH)--aUo1gHXSS8;?lE=4{fJBDrK3y)HH^ zw^dl2LPHee`jFyFp4|?rY0;MfcIW^q@#SQ^g7kBwS7q75fP)3yH>LgkHs)5D2y$VI zoI1k%BNC=Lj(Jf$DSiZ6#Us}%E zsZ_2bIV7iK=Y zKyNZa2vj}J`^}O78mK2_4Qe%Ig5K;7S z>5YaGP;nK?5*1#7>QOa_RxXbMIQP)G!$$Q+f;+=RQc@@S$c=*>DKhQ;Y$Y>AR_ zJ(QK>Q3${RY><6Ffjd%p1R|@JFnl^HHv>33b_frqRai=d!Cc}n9+j&57~U`#Oi@s9 zuGSkIKmG;*SGvno8`3}y5aizGEWL>rlp(&S(_NbGi(eg z5Cs>&M!nt+P8u;kR=ugzYO<9c6_4ZHQ?e}{mAwzONas4a*KtaiF%taGdctHjmX(|B zu2MrO>XC!5pe{#5x!fUX;M6^ecIX1I6}CqOG7$mvI=exyGwF<_mNKW+;&7CxW@y7z z6Rf@+Ni{*H5;X3WE3ape|0@3aObCY&7@;>AOonp1wZdJBIZ^h2d#MT?ZsDZdj|Vb@ z+PhJL^UKg9m@fy0Vkj*uvFe@fGP~c02DoCUm{U*YAp~sbr-DX{+O$sAUI$b}^@84^ zHj6)#jpScl9l`lZ%o)=}rO%u@GU^Uw@;}(~-+*a;)*&J1lqq5zN z#n}F`;%*Pypef)!8u(1@rs*LtlnR{@(yG@ROC6?HHkl2E_T3)3YF6d2WgU(!5<|r$*SW9hom%Ys8s`UAd$E1__GKx|VRr3UI4sM%R zgDb&=(6tRob;HTIMLtpEZU;9FdIR7@Z!Wi>18~7oYInHYm6e{RQo3PEZT1R6z;kGl z)3Q~(f$M`}Bd)(jIsoo0=*nRibPySn&SEt>T@GWJ&slD-blcrNe|4P?Qp46LIlq?1 zlb$wZj{?By*(w2Mm3STZE0GRJ2!9lJocMyU83?Rqg8)obIvx5_tHbN7vALYS3U9Tq z&|9S*nCUF|FP*4H%GTaHPaN?S!&beqHu>H>v@E)3%a zZwn5QFi$scpX`vx@)8oqahih^TX08fMz^~lT*%QGje1jw$>=OCtuC?p7Hs8}-g1xI zCOI zRa;k4RoUn~CTNCNN`uT9cosY?xF2y!2P-)KU}Xfxs%dK_o2*H*B6kZ!EEx z%FGsUyV7IzINarR)gG_E##7r|S5+NE!-N6K11Y>yBHt^rI!{{>3Vtq|U4y$V68XrG zJCWdL>J8>XNx4~XD=jgX*qvUFwYMKcrrLSF*aHD)(I2Ou za1d7K_{Ia6$APPEVD;x~8tK8dKQP(q5^M zkyY8v&!p|3ZmZq+b>b1VfS@^{#b1r|s_=vVdIRAWMom{d|E(LxY zo~Rgi3!(`9l~vdUr8ux3mZcbT4yp`*6F8MNbBWDpGFx3vr`=iQae1p9mAOtAVF=>oi}NT-xNgK45h(rI}bNrl4b{+tta6lFbO25y$Y>L`aiI~`@d zN@!YDb)&=MZ>sUs1nL3}Ex};haFEbQXd=?m8GBIPtM-44=YtRks*@Kjf@a{j-fA|N zmgua$a(hL=VRC!C)in(cUtP7gvaZ43*xu6A*wWvm{D>z}mm}a*^KYq^Nx^0=*|iJW z1Use{B+S5;8P1gJYViT5$Y3&>ZI%*iiN$3x+MLdEM~&A};d3_Dw=`APHr3;!sky$r z6&9D8_GMTO9!_RY33G~ef~tEqQZ*ETlRG&D7~cJ_8Ov=6o$j$<7_6QsC*3PC{?IUv4rOy^{u zgs<0;w#m7tG;`EkQgiMIAEZHv8|k3IdtNlsgTd5cv}P;(M3c!;R_3<3JRY~h?e*0) zHU<|O?M?OmhL)DL-kxAn*H9-0JV%iu*bwiiRkZEwhXTyA-NZjeI7m5)(pVGxNYZ}U z^%|_`8U+j)=hWi1nCup6RJo(v=5$rKDg&OX=K4U5v$npzuDhi%*x20O+utNm}s-1@wf5C(S9A;(GHoR+{z-)lFX)=Nnsom53yBkBp!fEYkTIwJSC7Qh!O2{ch} zHR&AXcDt+G<|(ySRC>Jb`i7bsudm73SRHI{YG~?d3G@#120Pl#my#w0_ied?W80K) z@@l_$7FQzF>lbKw5jk`)x$dTzV-bTVf;z3og7DJ4C@~3BslCi-hb-E>UbnYUQBvt| zZmg(iZgDm=2ATt%Jsq8$!R~?J$cW{*G(_~%?v*6GhC2e_Q&Qv^x_BLPNJetd&moZ^ zoV-TL0~Xn)GK;0WTvuxMRych%o(g+KwZE#qzB<_CcUIQ5w*}j~+d7i{9i8pN5$kcW z1=Z;tll>geX~9Xk*yV%BKQ57(LBE@1*OXWqG1e=HCV2dMONmL(5J7oGxdj~eR(dP> zzptXUsj8(T;B0E>=xGnOwYK#3wzl_mkGYNu4((3KOLCU(&Nk^4Q~xLd>i7fupu6~oAGi8_~JtOE% zQO?4M9vz zOIveKXIqD}eWZJ+cVM`qFFZQdGddD)KaS`dgB4Vf*OB=7fLs-y7hlEw8TsaZ+!aJ8 zJ$iWW#!@Uv98M=}hu!1%x$5gHt80UGHFfYrG87fQKDspjMe)lwtjBad_a* z)OyS9zM7_%%KG-&il*keU{7~rTVGGG!#UL6)ju@c9|{eO4y04Vv7X~nir-efQ->(p z$Kfw1tFaAFWC7!a3<#>#HeELIo?aIJq?ic6xF>m00BUnR(rSZu0>fsv*{q&QcXhSh zT?IF&s;0IczG{10dq+oGcc{6|+1%dW-4_b?_78+cW1;bx0Tm@6D077S#Z9=X{VCGM zzJDk`@58kq`e3T)f*Z|P=sQbwCNn+nYM;Nds>Fg2Ks4y?L8C3c6e*6#AS-9BGa zYeiKs&`{q9IBh7jmUK0>b@g;~^|*#YL*R2VG?Gb#qPawT7`Eq&Xb{#HSNbuOyBnoH zMY!YXGTQ2uVgNn`01gO;h?uz!-;?O5EcaHyX=|?aH`HSd)!tg)(t*#;ftJ?3j@G^b zS6~0=C`J?+Nhf2;*hFI7z+J(M3N&{04z@OT^t5#j3=H;$Tw|TX}qKr>h9_r2=xp^U6Fx4%p^t|3MWQGk>rr+gmkhgOB~YX;&}=V>|*z1 z_5q|91tZN=t`qPI;dmJf=5C|j=(L#}R)@oB!}`jHb#|4%rn;r8wyL46v$d@=I0PS{ zd$6m2Vr0nGGnxubj7_A5#v)M&(d?-Cgxunz!PaNz#_R*p4?$(as?xF7f@qvh|e>fRS z$6?i?x$#gunvRs7kbQ7X@dnx^B}b*VKp$a~gwKbO_onpM_u`J!JR+rnvqn6pL$;tF z(g9lyh}&a5;wmr0O2k#`_f`698>*Wc>uVb70&UIBz1;(YeZAd-;eoy$z-!c%9Ul#i zjVDG&Ly1IaJQNL=U8INJF>&-wDHT*Ja)4;_6nf-g6i(V3(&wOb9+vSnh7`_-u!L&b zHKVbl)PT@>xr3`^|f_Dco)sBE$uCxy?woXg98JjV*?ZM;gBmi5*-Vr zva#{-*hF+QXQ`AB%1VDN|=1=*jbmx=M^CF25mH8ub3&MB)ng zMWmx*0y>L-SSMmNhX9MU%xbe$I?5{>Dj~)dl{Gc>jlp1Dus7J#)6(71*45E9G};#) z9~_AdyM~haiHS%&IhKeGM`D>=)P6!LC*4Lc30E?adl>BWAbTRkob7ugJ>(#Li*1N$ z1PIPLOd&#$x{}fo#MG@Gzr)*H>8WlARMpfp6oS6yrpE5pV0TYXe_QMDKyM6IvUe;p z?wUv@BNHQ0=v6E{KAs3?Do#jzd>f4ueGf=?cOC91GPKiMp<~Ls2VP@~Z`5ppc`mW& z%F3~iV0XEyYrK_im%paAt`O;fO07N=8Rwk#Ia34~>HFS#X_wlh4n>%~ulJhJ8jdHpm#*9|8sl;i;R)*Vd_qv>3*k-@CvbNS2Y^-l;ZA7>t zI0$v=9qjDx9T>!H4~>qEkEZpBaHtTmhO^mFI2?;i#Isc=q^D1Ldse)HD^~>;ju&F4 z)>Hct_mWFcx(`&alM?jrmhPwgmJ4aKxm+W8Aw!8?S5{)Om6s#XaVVtgVJO{ZP4L?oR^L{qU;bR-&`%;)M)2!@?F z6I9jHl0+_4xVvxw!BI-73Ybubwn@rP;Es+TjnzuIm&4g5G%dzQ(4yhL-yFF6eXD$Z&UWSLg81U}R`$d>|2?i08(`$&rPGV>UCE z%tXhN*@^LVb~F`G(6bSJkWMP=en|_dyB$f)YQxW9 zbs6RSVtT79;RQF<*E9zFtzE&khCq8yPwRMJPj7c;UuYy08V!w%jl^IyKw5k(7LHE= z8>3^%TsT>)z5{{lN+KOMJ-G#;)SL7E6cPd8P|+plp%;PyaeN~%l%dlFT}&=)5yLVV z$~@i@=$;#>=dY+}Y_14kWzo{njwo4UXLozc5MU3I57RLgj)aF&W3XYDxh6v6;e0%r z0u+wLrs9b!T2H_u`;UmCvkZN^NY~XUMyf~=@qlW2UQ)(gjF=vq4Q50ctQK3jz0_9i zsH%s7_xPFvuxi1!=GNxsj!vj$XIFTrcWgKw9mQg9d^!R&j3vTR7t}JG&O$+>=|nys z3QxDuxuOrjP>T8{qC3!~ipfyUDQiE02fLt8FRF^6R9{+NVg)R^Tn;~WP<-Cn`szkl zukP0R1_T*KN7_cQ;OZFa=?jMwBcZX;@F4D$M03KJM^@Y{ZO02@QkhXKGYxtRqJdV3wAWbvntnbyZ%VQ)_du z4fefbu%m5o5Eg!{qkAZs9v>YY9*ZS%fP+}v6^X}Vp>Q;ripNuMLz`KNO}&s>7t}>lNtl zar21cP(Y%-pAkgR;-8ZiL{*x%db6$Eg{@I1R^Fbfii*nWs)m+^u3$s3HrO*b zG&0)L+SM^09te-1`_Mo*Jsyk4;^UZ~4A_#7W^(CBEF6ges9?aS2l#~?lmR-s_ai94 zQ^qTh^Sa<#gO>fgniuFaP7su1PZH4+o5O)+g~#o0@YmM|g2DQ#`ntw02xeb@TW8k5+v1l?j9!MKcqrR5%%*h{mFcWF(w&CEA+^OX4Tw zQhJTdQS5?&2`Knu618u=T z`&e)5Kv!FTPw&V;Pyg`9_;`2}eqTB{fgVG#%=A<)o?eJK@{wG20xP6QVj>iaO--DD z?c!I45%|9$uh+{J*#qdC-o<%2fmmhhJtjSX7hwnN_hH+?X)E(oR2ORcoVAVhm4Q}< zO@e(LfL%B>BZEW3BcmhH;ZSlUoD9V$(O)c+&LxtuxI2;0W@h6Pc!;N>iD-Co9Dq+M z=nLO3u9H2}Nc`v>lrV3jn2`YxY%klbhLTc?$7U`scUCxTz8asW1_r*dt)>>M!ma_t zzPiVUCwd2lx<&^_M@J%9F(nd_NHPL13n~%EzS9Ks*=MMy%>Vs`S`=++m zj{cG1o}S292*NNHi;cyip=2nU$W5ignW=Cx7IlXsIp|L!8I8o@Z_cDb$zqX^X zqsx64JWRT}6yqNw=){Rx&@I7HAf7SOTgpv1&;WPaQ&H}!@HSv~w5FmFVW41hOIKG< zZ);n3*Fb+q&)~oiEX+g-6CW9m#^U*SEIpY?L}Q6qIuSER`kZu*7<7uP-SpN7&6$X2ULeCG@Dm zi=ju25%dUw2RU_(1nW|LDkYa&&ww91F$MscbG8%R!_qofZW3MPf-XeHTS2tig6}^Yd7c1qUT?hQZ{hu-XgGA?(lltD5Q?>g$?X z5c@_{41w?7-u4be2>VAu@sZKV^jIb{Hj$1;;AEx~(2YXG7>{MAlI~PG8I8v?@rmpN z{FcPTM9j|bc~UOZ39h6_W*9KZ!n%kS2hXy)3yRk%!hCx;cc4aJ-CX(hMtJ!=kl}v_{`Di+~Bo$A) zCo@ConPe)OnU29)$FYF)oGhMN21)dcNe;sT8>LB58yEx}Zp%>+;4jD$XLL(Jo4|1o z8xGmH9IkRtWrf>^P1Qh4pr)z0wb38w>g?zo=xXX1=!-=LCVIo6@#)OSM0_G1&qu?t z6arnKZ91EoOy$8XcPxz9SA@jJ^AYIzVhye8y@CYWe z;e0lgNN2K`usD`3@#z#;;Lhc9aKKY>sPxoiERl-ms!k$!rjBXf3-o39hD*D*k!T@F z2TvPrEiQX$zKJ?3w!)?B4Rm9PJsKQ} z_4P;6V{qS~eUWf@`dz6|B9)&?W7!*vWT(=JWCr5po=GPrV(C~modZ;3HJ+NP<$jEG zvgqq?Qn$Nhz=mhu-j?TEsD7lGn88G}W}*{q9U?Ql;14d!~af;|Rc z#D;%CcZI>E!~UGbY%cdYtkq7dySCo##tAMz_F{s~!S0r>k)gis-tn>S!GYf4v5|48 zX*3y6XJWX_q?hK>WN12-h{v&XNF}4ODK{23iBu9y#~h?Hp-3d%a8e$WSA6A}1mYVb za@^FR4|HsiF6yfaM8W(PjF;oouDKMOv^Iwm2L@c8DxV))EM1+At#$o9-2+{HeLX!L zZNnk>@uLXQrL*Z!JeiF|GnwpE3S?laQ<+pcoy(?UU}7v~Ou+}3DdftsnG6s*9G%ED ztC0NDpyMFK@&=G_5*u`KTMk#oluw8f8U+K6Qx!}zW?15KtF7GUwEK8g28Ro3I@;h8 zbPe>i_B7WI_H_-8bdO~Qu?;Yun$5!easo3`sU&m);g>`rmC7We*>pTx0C>c~ODuVk z@hN~yA{NR9PhxE$V+|yT2JTweCI;-}-%)@d9x;1A<`8}gJVpx~HXPq6vw5ABRoKb~ z#MT7@*ck`}dYU^%gB?Td;lb{3xO*rx93CBna~KCmB(h7h>0~ZHH3h((Oh>aRC`2L` zjmA(K0gJH8|YcR~U9hMh6G_#vw|Wnu$1k*V#g@A~BiAx-&78!vv;g zbBSCel1j(i@oY4ojYi`UtT&~YGMR0Yt*7D`oZmAj^`qXtmY*wGQ}Y8&Vs80{Tw?@x?GhKEwIvFZF+ zM3R)rX0a$vrGR6pTr!bMC#Evl1gsNGy?bU7&OcU+5v&)p>G522c3ge!;e+U%=s?S~ zUmmR6gF90Cf>eQf(L6NW0=#f=tE}OOO&-n5aa`jnx38Z#of5 zCqk(_T;nApipuBFCn;21R^a9Yoe$uGF_{+fzFy|)I@_2yu(wY#;i zzbDc?Ha0Xo(A_;ir3^(TWBJ)kYI-`Ift^dw09Yn3%g6JRlgY_U3Qkih6Q7;+z_8@t z#D*cya1Wr;*{I=^wENd^V)5?{kVkV&FGcy$%cK>&K@v!LrM>t7e14pH$Fif$Q0jJJ zccKBCjt%wIwbiv9!9Z(!usMJTYiba?%Hw^*eS={v$itaTcqSKyQ?N82U6M=Zv&r;K zZpmyW_pZtCR1O|pB9Xa#CX?`lqqE8IWs@)$v2-+`$*1G7=~N84XRo&cF z6%5wkR8b(%HQbBI9UJO}qd69yjEs!t^Wn@)68JMUotwTg3pq_>DXlK1?B9WZKJ;m1G^YY?WL~(WuDp zg3-Y}-6lE-`oX=+p$T2st`^ z)+)cf0w;?b0@c-lj?TK)-p+pPCH4*W3=Z~ABnDF9;bDBOocEvF-UT1HW> z0li!SGKcz3=C6{A*E^aB?#3iN+ZdfJ`H5F%g4*luuYs z$v==m?>od=vQmdN#4RA|kX-5AEzO0800-2<5>(jAOI`L7hudDLv{WFx*j&?8?Ju;a zTUvWt8gW*ly*J#}gI)EZ0c>$Z;*;swDJWMao?n_uzw5o1<#HJ~wqRoFUAgI*G&Vmn zQ_%h^FOQ{`<~-nFHWdNevk2`Z^5alG$0JkI2v@ ze&j+YE$&J27YvKiH8+*mU1esc-Hn6oJRVnFAE>TtYQh1Bj;40FnM3fJM*D}yQlW8d zU`_(wW0{$3=JKWS=_N~-HVxfN!nRH3mSUwAUr6iY$<%zd zEFViGvHZ_umiQFWt3cHWv93rd$)|&`5AKXI!>#aJ=n}vSMnoSDUb(l@>26>sqNb^# zzPG2XwYRmUt*bEP7>kZYMrP8`#*|G0}%zq=CM{)adwBBn)S>5bv3s z0&Hb7`ONHeZpm~edqp|rm^VlguY2uhIrzuy?4ZKRt|nxe07sBmkc? z(B|w6oWSYKR6aK~4P%Bh zI~~OWerhr?-N-Z1%HL97vAGFP^t4XM$TdTg56VrM-N++EJuR2KCnVMy3;K`Xn7!TR za)95yN}ii?H~K4@VR!>|ef>CV+R@(M9~l@P#es!*3_-wTCcAVpJvp6S`d&a%YHBI2 zOQ)7hW1TdckLB_++1L!`ARC{4_cVA8({@EJlJKO`8HB2U50jJ8d~N~@(3yr)f-d(r zir)`F@m&SMV6fq@sQKV3{^S*hx^RZfQ;pD%+Xc+W-`JQ|-Gqog{1Yo7q&15DESAA+K2@?%{%X@&#Gt=2@7^VVQa57_; zwVi?oqrT{x-s)x$_Oz7n8*xXnDI97=LudSur~rf+Aj!HCYpK~aJ{V zX>V(6X>aK4Yj5f9Ztoo)ndrd=LL`=oPbRbZRQ@vXGX{9gU%n)l`@nQ^=8D{8ItRhX zVkMn|EMK0>UV$}FCX<@QBur&g6m_%r4$Eo7+2JjA~!HDsYl8SK? zY-t>RiF8&l6brr7&znAqGbQ*#%W}8RUJj$sSXI^7P#?r0rLOjt*5O{9`WqTf4v&pa zV4HqoA~`*ohp#x3pUtI~UN$xLE_jer=?ook*oN7qfL6pKrY~DMIW?J`2AaWw#bG93 zb5rnzmQG<^fv8F%lKnqiy$O^hTUMTz@6|j{IaXF>RnGG~&zX0=H*Um@h#NQJ&hwn} zRFzp%zSo1mvb~nQY-;WGpV%pX_$iNf{-H(EJ10!G zg?;bxJ9Squ{nNsBn@+FB_QnV8jrHyAPg`41r8_4Eh9~Am5p5QyrdJkbC;g5E*D9OB zK&$bwtEGS3ope(G=Yr{W-rPb|s}aWKDG`{aLJx5uLw;Oye%Yd5amK>TiLynC;;`9a4}>!bFk zPo8uQ_6`h<_m9qw&MXftu1?LZE<%-oxSWP#RrY!UKH?p6CqJX2dfe z6r0OolR+C2RF-FL8I8{?@!8kbZFWDDiR4}uH4??%!gtsj=YE?1vrd%9qFfeF`Il?l z!w(T+&dppu#P9!(b)CJldy!2;f8v$v7j8VbOD9Cb{d%xZTW9l=jwd}mgM*{P{e2_- zlWUX1I8hc6I2P>^{1K2sRn(wLp(*PU+k;3VIo*=S<<<-*!Nl)ZS3M4oPh?9V^19vb zSeG2DE9(zPihlmPYIku)9q{`$*Yx=Z7mxTSYaYX2uIYo~5uepQwD{m6lK%|Zr&pX_ ze4{kT)3bT$`t7^-Z_?^Rb^N5IwY#}xc%pN7uybN!;FBTm?85Z;G-v@mo$v*wU}rmJ z$#D2|zvA)vJhD#(6-fcF%PBb}zwDR2E)Rdhsd(W`s7xK845!ORBxKjy_{l-->b5)C zG0v&))WEOcuFo&65wY+O0LPzM4TWRu2#86}xp?Ig+}MR%^;d5-H9ol8^su$HrTI~J zYh(9hcL!ZhL(>p+qj**3upKy&?;Ntf967)4(RJD93ILTnit4ckyre<5$L9xK zk{UfoWfI5OZIcwVG>)v#Q}6PwEPL$ef9rm-l;l|UdS)+tQ2W5&Q}-|Uh@w~(Pgwje zMr!;P8%5+MF(GD&Bq@+5!qSzCH|lBby>b8M?Z*vG%^h9M?OmOn?Hygck7*Z~TppR3 z$7VgbwmvTmxfE|6-`q%B6-);AJoPGkN z;-iXkP!~Gw^=n?$=W#lHOXN`sDv+vs_M*7E^GEn-zeYAWcR`&G#`p=UZsEq;hkb;@ zrjWaP`C5I`?R#ylO--$xEnWQ&`-X8$4o?q`O-!Jb&#uqUJ7(w6V%J?R$-~2C%LYQk zjDTN}B+ZcBYCu+fUZ3hxRJZte9)}im5E%Vl9ySM4h4@j)r}b-2+3i?&xPVtKCv}L^ z8Cc-~|37@r@2~NsKh0+qb3be8<{#Gn=%4cs|BC1PoG3@*1>I_wF5kR;pbvyuN@} z5Bgkg!@cGs`sogjM^-^29uWywp{|@B-x^WcUhfqgi_h)}+QHCkt1jD0z<&AsQ;U}` zcJQy&$a)duzRC0cY%hz69bc-gNYDkMmKIt5uj)<&q`DwFVy|4mx_axXP^f7EhqQdu zf~WfNy<+2yI>skKEE^2t@dizjDylME#}6xBSssz?Dpd&lE+Nu(at$^K`9 zi~mSsp@PEaT@g|UJvND8vc6uA=CtBqFM+<6A+a5h>ohvw0#((wHncT1b#y!;b@q03wGU7A z4h{^>tuD-t&n_*^f|XY#_rj`=r|SY>Yo_7z=$d9|a05ZFBdpp<_%@Bb#fOHN0}>E9 zsKeLR^#K0{b&r=VxL!{&;h+d!v7t}FH+fgq0=DZPfUh}%c;`hF6rWRUErA-v?_aI0 zMg(}^N^Pa0YvPmI%6_y)eG3it0&b3rOnJF_i>~cEx0)a0xo(E)d^*_G+1@wW**!gq zNppT_eiBFh{Gx3UHqPc$WWu;Ad);bKRwT`*S(@9Y>8>-ahd&R=4!;T_Glcl#3rP+S zXMp4DgWlyRJIJ}K^)9!aH?@lXO>PEMF0Z(KcRyRvKjjmP?e%AM|B7GFo2`Gm_UV6? zpMQo;b@t9Pwp87+bE?gBYTu37KXxwgr*Q;-C1Q!apYXfA;CWmrs87&IxAy%kSQ}3DbY=F1DNd z4;ot0llh>hGT#9UUJ-Yjr|QuPoYJNFwW!$FRFYvg(&q_00QLnbreNpX5>HfI@)v zIs+lu<5mJ1KRf+CyIa%!GHee|)*oP1ic|8r{ccx1whslgwr2N1(t;;kz@26;Hs=G1 zg!|v%H9yw@&X;jM%}PLg@$p4C!hBqp7DEicl(ef~zjmLt%{#Xn8Xr7r?`rEA?CKh5 z@9Q7!8659kS)Q4g0z+Z0wk`T)54@?q?g$w^zwYxHGM`r0{657)-Uv`RYl;#JsIpHF z%I-6N3ss8;AYl&#oF2-o<<)>pT(Sd~-+J#myb8*O-R;JixCR21Jde&dkobrq1&Avz ze~j;cmw8Jf_HZoD-sxaQ50Yfv^_y32F?8WUXT|IrhFqCKGjq5Xu zE0ar0aQuErvAJB{0MwKtbfzr%`N+XwR5AG6suDCLFA>yG3=XbaRs%M>98ewJfOFOD zR%EJEKRe3rfOv5ca_apuX=2@Fw<%;0T#Ihsn*8L0x^M6?#S#8_!BdIq`zw6nUnbI= z52E^A4#4@57Hj(fzC^@5d-uwPD;F;_d*Rv{jegJsO;5Y%l{xFp_H+%550CUuEYO89 zy|6GlgK2ivE8_?BkpX;CK#eN0t_Fi1w;9$I4USOOcme)kz#}8LN-}ZP<5L`Nm&{pq zLmB!Z?!A&J^TchESJLVMOfX1m9(WH*F0^CMx{sy&Gd`W*G{0425`=+X#M8g5vG*b~ zhoiCKWMJMjf?GJ$K%IfI64%Dre;Pb@Qcx8v#=yg z`3S!d^k^V5r$7&>iWLs|0s8)+;gw($CD!KS9kH4q8w>JISNwk2?UsCsLY517l{5Jo zP|x9A^DAy|y~qR(S`H|~6dT?J)$Y~$K7dO;w^#q|Qog6Q4Dlkw!4|)ZBmE0?q8=0R z>i7BL^}KtE)9IZn*O5rCAu~5L+(q?%+}8B47yC8s<0DhUbHWWcHScjPE!&poT(Wna zxFk#du;o(1ey^e{LBAOV`I$brkbq`H{mwv8l{|j0ixOQjAQRMp6Ovrg!Fs$sn@49; zx%_r78roaeeW%OMxo{x9I5_P1xuxL`0Q;bI6icws@%? zZ~dA_ykZcy?1QonIhNrhy3Rkp=tvSTLJTxKf8-)aiDW0B;WumR66fcq`4X>4a8!(q zs^v!Ux5W7oJMi}fQ}u3L-Cg>9LAg}Lv{^kAqf5>nZae}`J?a|iZ6BTMo0wmlnV6lL zUz|nxb=U)(UvEHE4L-$;=pnypYK9&lodg1Ez;74hq{NyX?N@l|)8Jbs8gV8W)#WW;C(E?H+K;^0F;1|E9`c=PQfxGp=*z+cd z!xC88<ymkIPAB#c_okvPQkVrm+*xC!mUj{8udKKNpj+MAgi% z2>5*J>sK#dy92?}^5}NU!$lvh7mLZ!=&30FOSW&2Rc#62+|F zUeD=r+5%pzW4HoPdJhKs$W^J#ZiM_XrC@5EUD^z`_|#NyP9eI93tO;I3Um57f=rE7*IDTc0v zsPZHuAO}OTW%*2)BdSi5r4h-jh^na?kuvQ7U{1Vhg$md4_Aew{1&U`lz+x)1hem@zgo=ndvffs?q@jGoU#cneu zKd6yff48=d|C!G$R`5yfjR;Lxobhk+B~F4++5Wm_W2W64_s`WU@?~1W?%#fJ|8@ff z_>(6sPx=PoOovBl2xCPPv(qyujfns3XS*D9`2$Ljw*j~{qIw{p2Xxa^6jchDe$`Z@ zv(QLThtcyI7W{`B4a3JWWt0M-ydryj4x7{M13gM!uS1k!{(80>NJddStGp-BujX6T zX3mF6fB93DNF;rcGQUwLPW=BNX4rwW#H5Uim)KWVZeOF<{ccm&Ek0~ zQ~g6r>ln?aaQoX=TmUA!JL;DWSr+L;gL#YxeQ`4s5sA_t3&`Y0S&8aF4g8`Rs_O9z z(B+VbT{@@>hE?$d47U(E0!qN)qTKbYyXvW7Y_%%Ufm6aondr0`Yv&6PGaLS#MVt$r zq7wYmPx}OgSzC~(0wIg2EO0>0g2*&E+5|6MUA}yy@%H^kbf7V6p|b<8OAk=2e_(P3 zzY|5&{5q~=o6oT(D_}822?hgNKvyiy2nS8mcNTib@Skb(0W)anBGU&$x+L?rLJ$yY z#Lo$*T=b}ZU2*&6fXf#Oc-(G)f^7E$_+y8&!HIa|^4Xj|7i@%IU3bbZW$oP$Ygh0I z8^T}I#>9wZb3SkFpVT1P@A0!J3jeC^n&4Q0!&mPT^D;ZT%Zsmh#m-LitR7EOdW=zhxx1jCv; zDE5`&6Ps+(`gUDZ@LL;-sKA) z)(Lh&oDg9oJ(vB2?j&ISKdqf7QF;j&Be=}7+IB>ZW-26u^ss(hxc^|1fr7VDcOf&| z`???XJ{}nD9~v6#nOUA%b1p2}Y;e8nn4~1{nom=L5ugjTis1S}H5 z5RBZwAiky9xv8P$<#EVVtdR~kMIhKrlOmC@u7&+dG^~cgEKN=6OOUDrOBzq6_0I6c6x$qk{r~%vYsHFt7bf&II~=8I1mjgrfeA!Sx3pJa@*~8OJj@qRgi$B8YbDDgXSm5iEbLN!v*Un!xA+B>8Nx>*u%u>_6rY1za$HwZ5Y!TW?$U!gN>9c&Mq#Kx8bhjQ1NI;@9$5kH{lEV(=)ZhoK`$)iu z^>E6xcp-LcG1U)HI(${M8>nFfaP%}2S3(QQsb#~{S<_8Hz@3l4c zjkG`MYwz#t8Jio#(m3tXm*!S%3ux0W-E9wt0wE7L&8t~vG#rkEtZ>}W(&0!vNxCJn zX{Je}4+rIdsRd#t+s$u4QG%Av0&SpIKoKMdd_usCtjQ4#2n3T%-RF~C4Jy)cfP#r| zVyABcMh8s6Iz)OE07t|+@g)i!0ZG5}H(Z%`J7RUhX!Q2~w_ag9`YpbGn=e5nysZu7 zWR~Nl8%&ZBgW#AHdCDxTrqsO#%(Tds}8mcbOSWR>QH3 zp>WXrW<0E0o^aX<2Lmv)YAj;OdW2nUn!%tMjYMSA;4FE(6icedr3pz_auH)F4JAeP zQAfcJsR&06ur5SV$|aa1%0a(uhsJ98+rhIMSEU@4I%3}V!#y@^y2iwoQJ)GFV!J? z%&4M-GeJF)h(%IXI6u*Q* z288lKi+bGtkm~Yz5g>&`$;-7jxJWEmh;TFzE8$V7jXmCnVyFJS+K2xyYqg+Q#IKTx z>qQu0c;5|XRNZ`l2b(!9EsstgwLb3Y>h5VD8lIXQWSqmq)YSOwJiHj?q)$bqpmtz? zh9at-iK#l^LXU(~$*37JBU&I~CW5MI8oCuzSzV~ckV*kf^68Rd2D~Bu2RsmA0a=3_ z_2~F^WZu5R0~-W=;q!PJ+%|Y=j|2D=@UPmaLWI)x=)>A{X2GQh{!q~A0t$Y+2I0S_ z_N#bF|DLaZR@;RS@D!m5u)16K?%!x=XvFmR@a@yrkExux+s8(RM+T-A=O(8(esk!i zsG|`5e%+D`Q?-mx#E3=JSU6~k3Qfl+UJ=?cVrzq)q6D4`^O6SKas;Rgo!-YE(%*GXh>9z8MH7Tj z4Su+;MZ|g`s{#kFUA~NW^Ojf$ofxgBy=`4R9ep@p=7#&`7N%)1q+@Hzwq)~$sJg>I zZZj1zl9rYVMXgXgW`r^<$B={Rn63x-fEi0MgX}rO2w7$@sruQ5L5)gL@kxF)re{xV;}tnnR^m>6d# zqRNl)oiMC1iBrtE7i)thx_sg4HAZS*YrJ)pQ`Ex9+UBQkJ743VZ!VCg7X8t<}%S(N;eHNl8!{< zYBZ*wS#V>T6;Fo430<|~mZT>5+_DC###Ljg< zJ-M8aF9EfjnuM(imCr*CjGOT^+I5hzW@pU5WkD>KOr(UiaEWs2VN6B zi+2!IS|9dybo36p`-Ug_`={6DhURBimJmf(P@90JhTFvj$o3U7~ z5H_u7A`#)8!1c#-A>t}oghlkzpm>(+t1eYuvuhI{*5aU8lu#tk7bf_aU*zi>yfhIj zks#k`5%TXP#$aE%Ll$FFN$Zog{%*LwiN4dGu@NRzPERg5$7eh?gdgA98nhOIQ6v~K zbfQQsY82B})H2hlL@F7M#7y1P!x1YIF;qfKJYt68(TKsTAqS*`BnV6@5?haD$(m*< zEYJs6KsSX~*Ss!8_6Nx)c1^?H?{7f;umS#M*d5w~s9uOH+USRMVo9Rn|F9M#fAG`x zVzHzj=IgAkjWCIy5Uz%nH%3<0-)n4P%5Pg+Pd^hjdizfMyF2FAXC@geLRGoC1nmt! zqqvk1%0)KK2Mb0snKUa3C3CTKDrSTu5uJBIo;F2L1mBy{5YRXT4X7FUQW+mll#kp%|ec zmC9t};Y1`FjfJdyC>%{uW*V^wATX{aV#%rUfYG9o1(Ai)AbEqj-|h4? zN^bf)aD`Fdk@dh_>)7>H-aTKt*r=Z=ApfRTatrOGHkSaETHxmMC-D)c(sM(hcoJg$ z;@=YI_2U|=D!vn(k@)@7%X{?WwT@i zV+no;Q&;GI3v6u!m1vBDUWYP~{PDONG9gnSiCkVh6@J6QMC4T|^CSt95*L@R(dY7L zF1Lc9>Gz_fp@sW=(h8e{s3PpVS#oE&lyW^|1)eA~OP%Waq7c}6R zAo@7s7p`8vc=hZK6HvqyH-y)YRtCe)O)!0Bv}1j0d}?&vvotpgY(^{fV>tlI`Pk2y zOeA3?t;89JAePFc(&Y@tI$;%~p-eOks>`LqrWq#)SZW$fEBiSKsticAq6(1J)XAT^ z8Zcp_{Xy~*rvaA|4~0mDSxr&glGKPY9U!|pozUhoYj+3PAoG{b*ZqI9j6bV6D?VNQ z`StbR;F0`ko#4)YxK4oe^A(G^rcY>_VVd5%Hybb0=-k$FAC>H3-;vQwo)fGIp-T*dz{3+g`m9>K8m{>7QN{z&G$y7O(&!m%45|L?G@o-wa*%LKwq8`=V2AR@KMRmejCPn>}jaw03TO?+gv3x!pNd~QSDQ<;Q zN!Fu5QpYXmP7+hZ2%CN@Vfgt$MK|=C2A`cH?e}uTF>gdcdf-|LVK@4fu9?{U8fip@ zRkY)Or>Y9On4f_8Z2js-tmS7|%g@q?QDbWUG0PO+Fdv5jJ!h96Ub}gnHvYTK_nUir zQO}dC^ zxS0T`C-gAUo4?DSS^OGN0zjuw*a`$QadJ=8f?ZRIb3Pw&j5G^*t)VdB5S0}_RlY#- z5Q2)784* z?>=S>peF~l+y}KDPyyhvU?N{BWs{MxmCuAD znNli|iRV+{sGi9tvhk<^qZVdgCIw6iA@mr*NJtJx4798W?^Z$hp}=Af@EglS0P-s` zteXpAi%JBl&4)w61zYEA^da}E!oTH29+4=1JkB+r@19uQmpNgAP!k7GK=V)VT%J#I z5Vp~`H8?2dr?FC)LhSv_db)eBxwE~kr@7_nKzCRFlfi-RiTUZN3CGguigS_E?g56u zaY8LA!Du{ID#g;7SUj6Z7W1iSCLN6>Q_)Z|CCI2)JRB2iktGyBZOM;0ZjO^uU@FSf#tuZ&TFz z^O?;eW`4EqyK1ZaEq)bTsED8942owU-tb3t!VWHsn&O0>U!(^ab>Z#Brwk0Zarf4Z zTMxK{q2=+@E-~R7Kg-C-=-|{Mv&n3&G`F~s4o$COp&*!i`eY`xm9}D5IC+*zXA1Fn zK4wLe)FYWx&J4%QsFjVWk&HnZX+hGM7KDx!BGKw0Q-={TH9ZnEW62-|fgdg)q(D^Z z0)zOWq*NV642KLXzDx1A0*&d>Ysp3QrwbNN zpu{iL2~JKtit|Ma2Jc_-&x8=eEpy@&3ui)y7*;@cHC!rNpryH^znA839ASg~)01N> zjU3AMV$(@swt6(={KSRR}fvPM`8aFXz(-O;!UGNK?~t{!X5C| zS??htxv^5y9p?^lo{7AHmXFxj=MFLfCB6d8I`6otd5y(5sm~t4uh&qTI1hrq6NUI& z{PR2nhIn437YAQr{UiE}+q(Onw)GA(qjYF+ae8KrnSjfVQ`fo+zod(|fPSGzf&8&_ zvXllrXEK|qR5p@IL{f!ZAswakPUO=TXPo1oV2@jzKgb{aLCyLrf}9Yog_E%duMG~m+ZbT$A28&%letLkxERN$Aetw}=f=k94DhTv zTA1!Yf0vxZ*%u_+WYbZ$btN6nRnC&xcs5fiS&4KiN7=xh4rNNwa4H-M$5BEIHA(O` zEmJeu-ymun8^TyhG@Of)Y!zuKpowWxqfJL zd~$kZU}A1^ac*iE-?;y+d<$zIN*P6snv6#?pJX>ukyJjJj91dd?R2r2PGmFjY&n~y zAmT+wGm%s@mLQDB!UF_-6|h zmQ*pg``+zqO!I#v+6gCSbGFR5qW? zmD7=IF_AAOQ-xAC7A=)C*+L=~&L-lq2w0itkVvOv6i;zW;~;Am1Xwt#nqd@hDkgXc z5Qid1@q+3)8kZ3WA(NP@-;{k2>#%-!raijiZA1#^MQTpRIu1;`O@o|RcPXE)R1`md zJ7z`{@xM|x%xk0#hwDf#U2ST-%Eg5E=iA!bxNC=QzM;XX!9~aDoNazyq0!3kIK^+{ zR>Eq`^r!M+K6t#8%w`LfT(VrrWUW*smrm!hMQXu(A(IbglHs&jV3o0WC2GXslyYHJ z4dtRrIH3m6Qo@k{%83fQq`4)Eq z;2)L{K&pD&Pd}=e5Jigple%ALWk15#5Ao9<)clvf%y$C}juwL`84r5*0Swa9CyyTX zh}#~3x`X{w6AO&*TV1sKTnL8a#61*_2&zwlbd$^zY0~A=W;IvLRku^|LOKb;-%N(H z70O+}Qzm&9rSwjw*}s|*GBWX)9?nM;Gp2+fL=}S!1Rod*1mc<%fe_SUnnw%i2EaDp zk0@GL^6S_cAkpxbxO|QNFs2hgfIoBMTJ`f zs9r8|+vep9Vvs{~d()$byvC-1$*!T9;n9)d(TN!*n=&r|%3Je>E~5=6Q^D9-_$X11 zhb!CFM4?hF6sv`Bj>sBMW((WpYz$bRE#}hsRJsrbJDTx)R7>WqbT$}FX050u2r@lTc|_Dhjlv2h&6i9El0gF*&L@9K?ielKx%Z8+t|?#W^lOZsQ@5* zZIaRVQJpA4MH&_At4Ozh!b~KA^F`zphw^JR+V?m4>DzT9q95zx<*V1PaKp`m+xMSP zv32!4Z0jBz80a0D8eLl$S+dX1*}e2m`F&dWl>88aw@8+A8-;i_m#d`nySaS6l8z^m z$y6zwOcyKpSSCw)&6QF{EE9|76JY0fDwnr7p|NN@7!T?EnvQB(xS&WexEm{^1ZxCthxSGJ!HoMrS$&ovW11_>;J)$ ze;r!w97u`R{w-d$xYd-#d?ujZzI(U%Zev$#OV2=WPbU|$U_qO-*%xQ%_n()PX*;Z@ zerm|^EL>owTrB0nKR-bXXN@M6PVYh=A61~O~Rk$_eW15IfWx4 zWE85UXl8?;oKKa-2H!4cqM7nWDN!sW+4O0iN7_o~Qf5RD8X3-Z5Z;R|kSHY$(htQ& zG-gPV6zD1~)Ow&gRF+Srzzg}bAf>FOgJ5LJTrY(Z9cbhvr7=eS4S=(7{fL1Bc%F!1 z^3*xH6d+oZ4Bx6vbQ8pkfKLK)i)ZqyHLdr1`R*tAI;$JUO~LE~ronUD*1fwo?=~_K zy1&1p800N++#CGP+SMR+7m~F1weGmP!P;Vli9I zR*Qx0Y$_GamcUwMFcMcH8Ow)6;Sr4GQczqWxJ*NjCJi}~(4n(TgD4$IfR&H}unHTn z>Y!|u-GHAI)22mtl2>@2L9*K0K)n}j57Uf`{us6a{@OLC!TLWdeb2k11&MW5HztOA zUA}aiIWM=b-({|JYhx>eh(-r`xDd#;GCno8xHM}Qeess*2!-5{_$f7eQf%@4YO+!+ zRx{b1{Y>s4n=G-@i>xYHC{p=l%5ipjCSQyuGwE2S5RDg2;u}OcoQ)PtrGsGr2#(Ak zhrtrD%!0E4kXvy%fY62Z0riR>UFMa$p!`t2DSlN&c`Mf&0q;6~VA`=|N+%B%V23gP zQO#%}YNDTHslUeZgoFOKYfZ^Q(h(c}le%#(hq(Oi#T&O6Eq|x}PGiR-j&@tuAU#vV zW24MKM7VKymSj37Lb8H|Oto%iilK0>R4nb5Qyayz%1(7Vo8BsCOO;Zg$esptB;(mw zIuXtmGVwww8A*h#Tq0ggg)#}`t5`N+1&wGZF5)8MW0>3((PK#!i3F}a%JR|feO5F8 z$(GE3ndylKP+Eo?K?G#gh*GA}AL#LL^vU8Nk5#uUcN=xpPXp@+T0gz#Q2z?hkx9G;<{h`pj}4!0p(rquiW z$wEGr*nOT)ZRUzQo7MfTO1V;u?^MgBd~Pe7ufR@airGvwmpaRJMyMH*sSKoKHXQ*> zN5d45h~kM<#)`+-GNG_~W)4~guv#}VDLoWGG70LE8fB%Du;$8ms*n%JUl<#3Gc*yw z-MU8eqbQtJ->O?$S(mk0Vw0d2MV%xf*Lj8bTYM+dTJ6Hr+N&0WOKU~MPxHs3a1eE~ z;AwxMX4qigBM&hb`r7S#^$(iPxa8u=(a=mv!ZZUH3}p^`jP8bP3R`5^m-V7lmBMi5ir zLYpv;_%NXY$(RQQa@BM}#ZQ1&#J$QH7NjV)kI zzOYlt_E+REx!A4EzyZ2Cd@s5RQF4KCsLcxZ9nD|i0{wuL=l*05^VyvAGdE^zxVKd z^W(?eo$X@U&cMvlv~8M^=yNzf0`4#fPI6jAv2ZStIL$;8scbg0vyrcE6gRi>JLODi ztC-2%+&7+nx>%jc~=v z%+%1-(#!%g2E5)Skfhtn>Xab(W6G(__HE*Rfdf$9INGXgZ&lCATN^vYN|CfsE^n2J z#d0B^C}(2X92`S3pM>3xhf{e=!~)O+Hr35kESTrsrg%K`_ zz^?c)glSe-5hM!%K~{xf#cMRdmBIrHvBJeCTx7Z;6Hq1T+Q+pBAZSCuqX}Z+Tc6h8 zBIgU@R6Lizs67jThyHEt@*ga)7cX7n>T~Whz2DT<+T6ueokJ5teY4{Xh(L*2u#2h4 z>ki?{puWn)DI@!;!XHd#PR=DEkyipwR>qq z?hwwYKdBRY<`3#t1$RiL&ZU}=#}}{PxYX3j{b4PSI|pdm=pW?54|3!hWB4(I!@gq? zQgkaCiH zXP9nJAKDEM!q-Fr%O^udl~$Q1V25J$EYGbh-w=mGuu6h9`;FREz#pvpdfi0!THcj_)*Wx7{=Blp3zJ`y)j_KOjZ3_qV^p3K~$-9!7S+eH_=8Vv!^3&nDtdOdTq zy0=-Wly>&>J3E<;v)$43_Wn*OyHQByGSzB24ahHKGr3eQoX-mQ3(7AR<6!`9UI-xB z^jUln6o8-9^g3QYLnh|2^jzXbU53&$ z-+u6jmc8!5j;?7cdgib)e|2`z$K+SCtsK$)Y4quIjCY<-vh>qZ1sG5)@8*F3dz%{@ zg{?|9Up^=lHp`hxF_q0_Gud*bkcs9CltMg^crgk8NI=Xb(qz97g&`bA3?8wN4eCk2 zAmWf5gU5(r3B_;`2nNNL2q;*)08Bv(N&`n%h~l1ErA>6jc~-~;+|t1DciXs+hMS<4 z?tffsA{CKeWC0;g3fBD3Yi%V04vD^S@jwJL{uv&|`IZo)R2Ogqagzr(lQy;7Z+hDQ zxZ%k(M)={0Dg4KiOUq0#VWbUwqZWx;iIScuXYCJx=R0G{Sf_>}$f6YsTUxYse=fxoYL;VC(8mWn+J@uvsae?HxQjspdB-8->F5 zR*B8OyboZmMd1PRECLsH5E^0%4staFDH|QQ~_f*Z7mod%xKy$)0Pzv z;*wPY7}kTBrX}b-D+Vw#!T?6aj}#Ne8il!U9T$ERCRv(s?2aX5%bCg9sg+flOy+EN zKdxDDL{j+D-;4_oP+GVc1a$Z-zP?>2ptOJw=X%pyzWcd4?ndI?Rf=HTzxA*{!%tfJ zI+(@NJ2#7{G%+y)Z1r!_cLbw^9RBB^mj=I5KNnFwkORG`-hl@JhU`3vobp+ zuZ}M>d)MvK4O>Vkg;pw&&X-Hi4>n4rgL38h>DKa)Gv!EtqGFIT8mH+R9z z6*he`lSxrbfF~1`bRrY8(uEv|DnnTv!KjrF=hzr93b+8`B4(NtQ}BRlFw35Rt5<@= z2)NW3#d0vDDVilS7K17Z@mNe@p=GYg&IB*#!u&D=-sfBnwCtrBE^AtP@Nv!X{Y!NM zDF`M+po4$Tmq1HmD+mN7v^C+8{|kysfgK!#QE`B{gZJv?n>X$w5vt8QB1E9$q8^uZ?z7b0oGATSQxug{@7ovq!9N&0CRL)p@^+bs>BOFVC zCUp)C4g!ldBjlT~DTj=(7}~~YB^g21r_%>%XL5EEtgje-u*Ou^<>9Giq?DMxJ!RHIiAT%j)H2wlpsCXJsM1Tg;=UuMCXli}%XrR5bePp1W zS(tN+j)esmwYn2aCrK-k3FESklt9PZOkDGfuu4P+p#h7swYkYcGU>Xi;*j!+%UCDka6Rd~i? zVO;gWP|Qq=IDxYd0z!dA!SN;OmRF|p&wCl?3-hDbn_LTvZl+Ay*2E30GsE0#HaFQl zve^7F0+o3G@7H~W6@R(b#{1`Fh&7JHwaZs&R}r)5AdR|vJBPSmYJ6~Yb#8iP+Gng% zo&=(COj(fFNQuRbQg!!;1i627y#MUzF!TKIaI3tvdvvh7$#41Xqs{#t0>fseP%I|P zrSeX7BUjw2RcDY@s1Mm^^MUwh477x|CV>Pv(3$;gGyy{Ypb$RDeo3H z4+^E~R;f_U6-pcBYB^h|7D%gwe6dnSnML%}47ONmV&3W;b4DkoW~XLGMn~tTM#l$c z#>O8C^!V9B_!`UpsXBq3gii1mb^nqF;HdebnY@1M<_%ip8y@0g>t*U$KaX;JdYvoK zxT;D5r)Uxsb}Wsx4{)AKJT4dacoh50%=%i>+W5@M^2+=OSz&f~ggbJ%e`0KCh;Sf? za}f~5@fX4Hw`4xaBkKi%6u+Bw|b+Nc~o z+uk8c?C)-EvURHahug(MWv7zQma2fMYPyg}ZD)$PSZRY-kA{}UFj*+4GnQ#^#t zcOw$UKWAWS!9s>}k{$w8Pg()wP$W;8CVKi{ndY%>lYHEM=AE6v7u3Ww$oZ+cS-R_H zCWpB|VQGdT`kno)AJ>WDw&I!mQmxn&l)88x;!l3JMnDLkfpPk6{p2w>uyHB$#?4v_xsBdWa>BqI634tzuz3wM? z1_E`8vm!WY0W^e<)^(wdK8mZ?gk;>rT`NzzQP_I=C+CogxtnCk>38Gd^m#Qik}<91 z#%4MT-?+Jdv{yZObF}~L`R>Wd!S??C>CwxBodfEQ&7;G^QsuC^vAeU8C4T02D*P)u zd(~<_w^uBbuz742Q>YaAL@uArl_|(l{38%}F3R%bu`ISkM2Sc$7NfH!lq{e*(?x2= z@vG1Ym+K!jHa&XUH#9Ub2m~YMtZ?J3OJ8^D zVOa}@*}b_^y08HPI5^pU{_4%~&Z`$khcEVyj*m_b_tx17`_H#`kM?(WPY#Hdo7L?d z!oe;lVrMIdK2xYv3ndI8g;KnfOK&8?#S&6vE*(Xr%HsA4pT!rb#&P>)qgIk$#AMuv zhj5$XbizLY+0M8PTSRmD%~cystG;zHoql0zesa-Ht55UN{PYlm&-?lYN4aGNZ-0At zZ}-#ACSJVo4~Sj=+chWc*8~9}c&P8I{juO5f3r@4b7Fe=mFrATZhUyRv4y)}p7eHe zrSZ(d2sh+$qnz7?LlTuM5h-nCOZgIvF8+l8%<9cFWKGM*VEPB&jq_{wD{5Hxo)G_h5exQlFgjT7LPZ8Pe-MbH;2!T4qhGZ zJ%4$4_=<<{?D%y5#qq)WFE)vX<%8GzTbn$M&BMKNb!#73iDI#}QO#o6=w@4q^J@#@v{lNYCl@9jN*e}C`f+3Df47yB=F505G*&rhCh?QCsBQ&-D{ z9nJ~y0sg*ROjq`gHe%3Ll`X{D&1yWkiK0mXnu&#gQpvQHh!uDS@dP?1JAw{#>=-ds z4w(*r*5hP&D&r(@>ibvLrzh8JGb>Bt_VrO=w4G@l8lIVlI2j%79_r=(^1hD8Esr|7 z9^Yd_2!JhkhhN}#aV)+mwna^;s);tD9u@k9*dA}8D+H_%Ozye9cUC9k^Q8;#+U=V+ z?l-hI+->b|e>~LLJvcf(Grh1hwY0>nftM^}? zzIgH8tM^{*zkKh-;Y*O#_Rjv!=JRK}Z}txl2shiiyF1h+MAHqD=mt_3kb-oQ1CJIm z5dYv<%pSs!l#Ip-$wVQkrMBXcY|?6U!d)ZfR+l8 zN9TKM&*NOxcuH64J4|%E_26z>`~4@Kje`@N%rhMro0)Sg%_{+?{S!|gMqUgirHHjt z*?wLszTDZ~-+Q^cbNup)ubw?Wdbzi^dwO#8^7)Z?7zZ!6cQ^KqPqwxXo*!)Q>`|*# zUmR5m2YWk+f9V2MY6`6@pC~5N)j}qd%>%`iPhz;t2elL$3IT;cJ&czL*Ua(uK+x;cLSa(nx*lCQvb9_+z)W=jCiVr93=e#vL6g&f2v z-e@~{k0G`ko_V$6N-~qVC@QH*J)N*bI)=S>h2Fpf@;vh3$>Q4OH{J-9n+foyISc%WRZVSP9UIWBGI`Z=fAwF9CjLVvMbb zgp)-}mYHyY6_X}d2Bg~Nml411_VF3_%=q;5%s3%;f*dqD%5A;v{mmVZyZZXDSM~^# zC2C$vd(-`<#+JKeg_AATDWaH+IyKk zgd9FT!R-z%ZiQ1NCo}@a_*fwoPKl6N+}(Nc{^$2jPT#zIee&iDUwHrc+3Q!wM@P@z zoIYcRbbRjQ#o^Y=z0Fq#n=iMY9qm>2w?Je2JNxC$-Tlfg4<&cD0quWS&2OR?ArofG znT>p+u$jnjCOJy!QY=cla3W1RbTk4UMI;X<(L-D-0XIXWoJ;nl1)pnidYzj>21nO< zHaK?r2B&(a2D-X>o9T6BAe6XYtd)UX58E2$_Yc`mk?+yDg_CNBYMZwz|wkbGLr>DE* ztfSMt!@a#1N0c$pfZMxUdn7Be_D&fDTP~E!yXD;0?p`^Qsczw;OqA#j%xCi%PFX^@ zV&f^(%F*W!Y3iqOh2BlYkXGpWhT>sF?%JG_8+wGgb}QSJ=g)Uve)o&lU)+21-tl+8 ze0F;L-g_@U|Hap@Uw;10t2c*-FAq*Pj*fR;oE#jUygYhF@wdHou)n=~2=FVEwx92B z9&A>M`RaBaVkw=^rAoUvc&hwgKpX)1NM)d#C|+w5OKmWQ7bP2F@-nmN#06}g6}y)P zscBU3#fgP!us^qP<7*!6V;V&dSG*3ib~Fz*b@n`b1iA63wWa?4!}|KhM&>KuyvtMj zPg(SbwWlS@VUfGVGZCeMxHb3uunAWA>orCG+jS<7ND!=7uHL*`|Dd6@jh4_SZInop z!%Ma)F2-1<{=}`$P?arIfxSxl#o_k;$rpfq@4tBez4zXK|8wtu;k#bFI(_!y3*@hp z&%Jo{-kTSD&rV;m!;Vj1ygA-}w!ic8`7!M4=GO7n79n|;dXcgLekqTZzm-qIA8eyt zZ)Vb)@Jd+NvYRv=M+$lR;u#+2PNbptO`J*Mo&}W1wH22KkHC~|b!uXAjyobJhx0_hHj>zMXEiI+**if_9&FNWVO4nexVyb?aBOyFVs4HJYHK!Vn{ZIK0+8W& zOiR_`-t(gud;4sa_nsZU`r?1`{;M}HPQP$+^5*67oA=*4IzE1W`i9Vb^7hT^moE-p zKHJ^jJs^i2z1lBr0)4i3kgX~^TN}lV1EKG47RtqZ4oSBv;&pjrFOR(^5f5k0Ov21U zz^5SB68P{P-Y7E!4fnF-U3IP6xp-=6ePM2q8CA1Ps+yjk=baRwS&oZcdwG+G?jQHsazIDp;AmnlKC*rK`|jwV)2YK(ai2q#n{vJh1K=hg>|Qc zYfjf;8HdNGW(NjG1{cP-?0#r~%frEZT|KyPn%kNmHr6*c^815^M-BJyT<1}6i}fuY z$1kw_PuLJ5J)K{$#G(0tbNvqY4}U7?>3@xwX~8{S;Ige7H!*TQY;Awq%}}PHNxDk7 zznCFEcK`oJ)?0u@oxNehSaf%HcXx-1-J+r*V$j_%!@v;3(A^D!fC_eZcdfB&ckH_A z+B&@VGwlAq?|Z-NVuk_T^_=rN=Q&T@&wU3io)d)B;IiFyj!Q{OW2UDUWU=!z zbBpsCoZ`HkqWt`#Jn{uI)3S0{X_@KFlw@XBT0&xGT557qVr)`IbW}oo3UalvC~&64 zM#YD%Ly|uDe7&Ih>}f+}M2<*GTf#Pja@GX#fsM8{w52MA${>c}vO2%Sww0 zNk|~W3I>+{XMM=udm=#brlqo}a~raBB8Ck!nv z>)`f;Y(%pT=o*%XqO5^FvIWbggDh+Q9Q5m*^XnvsPJE4=i!&Dsu9gsu0>$kq|}ChPaqG%5;;(;3>Cb6{z6-i=Vcw z!YAU%r=;X^_{7*Qur0D+{gs0Tu9>m6k+qe%EiM2ku*_K!Krl7jtsxPsR)xmLCC4Nr z#Ub)$=42J*=9cEM*qJ%(;-Z55!mMm&L0&Q}a8^b^c6NMnra%%eF_V$bN{VBoq@_nC zQSA%6Cq5=QK6b+fI6fNzBwYvd5=Y=MEKP7ocxYreu<@&b#a#%@ERZ2HfdiN}7i1&z zXLx{x0z!*UZtk|OKF&6dUe5LoHumPEC8}j+YocXntgngnFakxFww!{Bs-`kHwdG{x zlq4i&WW>>sgq{vzlqjG@_k({Ck@n*+RKdp~@)Ud%YKXG<2k@eh{3So=>Ls46Kcepjm5c12y33o-XqOeLz)!Rt3}~mItqkPf1)K7oU=n$>A{B`FVw$ z+g+R;oZGZ0AFD1d4e9zTPN&`?|aO zyV_ZUvf0Yq-p0ntLL>ECRw`5qU{5Wo3DFYg2Q3Qya*Ydm{@m9mI~v1fX0W zhBa6d%!@M5j7i8!WU!K8L>T!jPHuK_8Fou~ac)L_9u5$<5VtookCB$n$j;7Trm(UZ zjQHqyMrw3wVrq7BbV^!WQbbHL<+0-6BEnUU+z=fe6}l=uVdeUWwGk03*9NT$N3eui zFrYFs02c!x8dMoT0Sn@McL!GpZM%at+sW1%=nz{|$oN`08EYGx0rLp{7)>p8=sqi< zRHX`wtE?m`CMPc{527k*QFtT5LL!rJfRK6l&M$(|Vzl#@W*(<|wm2QZRON@!zm{pmS zoy&rA%;6Mq^H`N-EH<|&zc8PbQ&^nKNY7xTXJ@76=4LU{VW+bbGjmcSV-aTNX2zu^ zMk5anW0M#glZ2#3N^IEr4GAgH$x&-UW8yZ1qPQHoY9j!bp{p0KK%WTUa^QaizXb?V z1Kel2dmts@25xpoJ1^YPPA+B^);8b>HnTJWr>?%Psiv}qwtUANP#lgE zB?#c5m=s(-Wi<^gZGBTCXPj#+)7p%tcy+0M0lNwU*4W&@RA1LfT~!wu33U~S5=hG`D1stZPDx5yQXE|Bf~b@O zIWU1>mON@M`oZtdILKcd79X%o5qT@@%qpbDQ8t_+h)_XMRasLu=gSsK!!E!Y z&7ZdtWOa)|A~%L285f(JniQ4E%}C8F^CD7^+tynO_!za+&6Dsyop^WZfW8-0O&$At~QM9tQG=-;OZfmA)h?Rjv ziJGQ@sya_rSWa11Q5Mv>;&P%==+hSz5eM`TeZE2yrvO&-3)_VfIb?C~@Q+g~e^s90 z=k2JRtwuZPBvj2LBq3?50M!aDZDVIA8z42^1AJVkqictC|tPI;25gC<`5YNnu zPsn3raI$i^Ib~RtQZBb7x3rR*S6Ikogx+WyhvvB&DSx0L@5_k4DxjE*k&WxQKY*bx@aC9fSgND0nmxjI^voCd%`GZY0IQ z$NS%y4sP-sLLx`3h*v$7J?Sy^ReoZ^Zi zPIXynSy5G0brFYCn48aqH&{@RmCi2B&tYcgrzd1)XXZ0;^3n@alQJ@rlhZP@va*wb z28xS{j!y#~C>lBUn1o1RXrnjAhpvi@ShqA}P1u^y<*R4UT@y5aI>@S^uNAN;z-xgI zB#^y4UH!c59H!e_gQ?Tj#mLM|+t}V%$H-Vy)5b_uT~%IPT|-t{U0qd0Mn+OeSyoC~ zPD&8u^`J$-;!OrJW74?B`GkqISl{@h(A)8eF0_yQrK50sFaOUd#Gn!`^;MnY|IJ?l zp|ss5O@^%&mylJ^K&!K!F}m{Yyt*R<5tz;Ke78e$maEh>Mc`Rljhr!CtWo4$pSZ8N3nQ1HrBPlT>2}>QH zogSN+78f3!m=+(G6dfCzgd-Rq5f+0)d=vtgFqE%QC!H5Ke;GOh0|S=^de2)p&D+s; zmd{kk%K@-zW##T-Yv*ET=HOstYzUn#bCef=Y}G~93tGu4N?`C-l9mw$#!*C78Vw>6 zxa=px01JuoCLuS-Pux)d)OZe+k{4I`D?*3xFS_g}@Nt5C3DyaG9LQ^=gzV_|#A5)4 zV=+l7Ib}Hw19LMS3rBZ*5P%U(7r4A-Yfvs(y&fru$jF5Bj5J=BReo-6L1AS{KDVTz zvbw6Gsid%yQ&P^YDy=NfFJt8v2g zY)V{OQe+HZbO~|jPl^qV+7PrcVL6Z(Hb{zHbAiGt_ep~J3c)fvqdP%;gpoI3yR7r%ed9GRn@g+3p=(tXRH! zRY)9A*NKeutn?&CNjWEvTUuUTRge3wtg5=EtfHPJHQb7l z^7@MM>hkK+^4ik!25w1BS$-KOk5yL8U}fawBXyXbmXpgwET5eUWC4rG$jXLSfE-$S zR%~QudK|hO;-V39tXdg?k*NGP8|$Da#D(_fDu?)o7*`7*oxXY_z)qo zM6gNps>LQmr!W&UGjfWu3fLu;MHLN|?7F(L#`=c(s@jT*@}i3B@)AxVr=*BeR+yJp zQp994SezpAkP6tW6m=FmH!CAGCkHjtjI7Meq>QBG%#_50l<=hV_2Il|WHf@8#{@3| zWnWMbD2S&nm=UlDvdW-h_i^@|g*=#x9U%>!Ezp6BUK2et106#>Qyn!;JvCh&C^bi&?|ur$cG6Ei2$!EtBUfNzMi3tsX0{6oN!fmp}}D3!qD}>!AM&r#-+w* zWyGYw+-B!jl+{)hS63ESH8nIg)^KZ@Dy!?N3##kboT5C$9K}V&#d(bUqJkVm99cz_ zFUzveD#%Mu&d$$C&dNagIWaLUEh3qb5E~hll7tRIAh2RKtN;h+y5PCQ%L#-gy7#B? zkg#-hgnO|0E6U5s$xBHnNlA%GD8P{x zLpDPM5<*DeOvYK_@n=sL_g9=9@^r4@brY|@_!l|l4ykYOZ-F-gb14P`dx@|(ZYd>g z9TOeM_FCI}yL-C?%$(x~2-GT6{=jCjHXIRoR%$$p$<56x=2n!}R@YaQbu?DhRoB;5 z*Ob@QSJc**ak#t+#S$(fms^WCB$r*tX0nSJc{#axoI+-1UQ$9XlUb0JoRXComzIIO zlbixv3UGQc>sJ7kv>N^5p#E6A3WUz+Z-=TBcmZ8Ob?pq1XpG9u-rm~I+yWp~V?(F} z7-*w^6*6gRx?1ueB~g%5krJ1LPAgjZghfTg!FCSpr7&cCaGewI4Dua(ukp3OMa-W3 z>3ieRI-k^#)~S-;J4Dr-AAB!y2fq|ZRg1BS>k*N`s>5MW)6@b}fTgvqgS(R#I#Y=& z6&UxG0G6#wj);s8PhliwmvFL5N@|*`Dry>QD(hNX>uVZnYpSYS>Psss3rg$C3c1Dk zxa+xaC0Y4-1%=sMc0QBI&SmNquv4>gle1AuWoD*jWG7~%#YU#ZgvM?_GsTKUYa-Tz zK7h9(X!g8iE5OS-Z4UTop;YSO=QM4ay|ae{+-y7Op_>>Q+gh0#8*1aK(lXT40netE zih{C=iZrA#RAr=P<-~+V#Ng`)Q_l}P9vEUAEZkG%Hop)cxB4m!5WjwNjK40FQ>DE_ zR{0K`pD40W_z!{O5=FH}MPEZ#-_+V15HV;(fH-!}f_#?tUPXU zbwy)GX>~(GXJbuuV`XJcby;OoU1ei^X;o8o0Xw&#ysVg0T*NKq6c!d00&?*_&S;ucuG zZrSq4$gt4JsI>U(;^d6tG*ryX>T1iXDjGT(n;Y8NtDCB8o2qM?nyPB6Yg${H3XAIt za!aZa!RN9Ia=1mzynJS1K_NSnQIuQ&za|c4Ee5LX@rgkHC8Wehqf1~TaY03bS$oAo z)XTwNg~mXrPKZJpdaJ*Up_n-3n}WouWhM3pjpU3_Lna&A^(Rc>J(uWU+7T|+~C z9j|4q!L+5LxuL$kqqDxUsj{%5wz#ymj9Xq_QBhFLW|kC`l^5n_mQ-eDva?ciQj2mj z+1Z(_?9AB297a6yhN)5O;{bt1Cr$8b+;~gDfw~OB7$D#en6_Z5pR2QvcYv3bt(UtE zrP6K8ps#3Rh9(Y_kdOk@1HX|nwL~i^DauOAN{UO%Az>^dIz>bT_YZ6(e@`HBMb54O z-@$#ux2q%krJ)rOm~a-aAN-8h@AyQvX1oSUi}a5`G^vJwuTDlxN9Fvzjz1B5t#ZU~?v!1hI_WTqArBY}~ZUtHbPQD5HPR^8U#)85)#-`Q1P-Pl-J z(a=&>URKsvRbE+L&f?b97v>kB*oW|nn~!B-W#yz4u^Aaz={d}Vq_jlTJU6b{n2-cXMrvBx>Ke%Y zp;8CsWmz=R%R+Py!h3))ib)Cy;*JJ~5Zql%a6Cl+#XmRW+l7|mF<#?Z8-!h=h~SI> zEy^c>7?_|*lcBLHEhet2rLLxHZfR%d;_L;52xwrSOFn2#DB3Bb*F`1+F_Dp7R-9j4 z&MBy9Z0T!htZnM;Y3gWeYpH8)Z!71Pw=`6@RM$1NRdTD5>d4QntSK$cEvm@NL~bmT zft)jg8D5x{mYbBGmJZ-sTvSZt#)QZXvBBYh&#&IF0_~N+fX|)ofz!IkvRxLEDc8g zN7R~#WUNVcUQTun2PdPtx~RFMy|bmOr=_j6t8<{Ox~{Xksk)`PwXwFPvZ}d&Q^~Ew zVpJ3tl~oiMSLQQH@(M~3HZk)uGQ$DnNzIOr&4|m%UXPMQ)VkGC%hoRrUWd-~h2ZL) z8#EV9@(ZA4>Eq(+2aKPyn+?K6J2y}~qcVk%$jH!0S4~S*5l2WY*~4gg=}s?QE71ryM$9!naQat%4Bl^ zhl}Fm#4$5c(~}d^fI;3Eu@+dIH4*4Hn6m<8PV?skg1*BK(zAY$R&s#~wwH^olZ~m3 zrL&2Nu>~qI+PZqWMkadj&SlgM6yT;{zevgw(WSVkqzGzJf`Yh;;p0-YIPuqb^5yft z2Z?TFnm5@PimPcw9^pk6hHTDjzA>ioi4uW7;n46V#KYnUP6qHzQUk(!n#ey`IG{&d(8wv_a*GNJxMjIq_B`h~M{GgG6JvZA8nQX*D`Z3tcs z2>Qm=;7M8uaR}sW=Yp1gs-L$f#6tZ1ZLPf>(P?92YiX)!WCJ}@6GJUs(3WT@s%glo z$VsWoh$%>+!XP6mC=CoBG*Br7z#>r?M5f~t-PBJ7I`Huczw32;Daik&E%X?R5)bDX zfe|h`4G7H`S{U(pFI>C=rlV782^u9rB3G?X3`eU)dVYR6yS%o5(^}uw*3{h9y=h=` zUuScDTW4QiN6V(}rUu+gO)YSD8)`X?Ewx1mp6bhyd9UJf%h-&v%0f1~ARVyD&{PHs zxzDuq8xuChgrVCxcy%OLF@hG%SiE@t$_0UQ7x;SldAoTyx=wTS^GC58+@=QBwiYJ( z2BvCi`X&adhUzLRN|1h5Q;<^A5Lc8DMI4R8GX>$ZFwuETL!hTkrE3DY9ZzRRFJH8PP@g&2T_VCmVw2M2 z5_9qkSR8IyMQv?Ob6I0|duRKm&HbAM?# zR@Rgkl~z<278ewj6aq4tox)B^VT`3lXC$JlYs1F%8-Xwh2}QFc1fqi0&GiLcJE&l$ zxw*PQYueq*AClZ2HnxrqX6Dw`W~L4{YH9|0dTI(Pis~9FYBKWjGBPUSLekJ35JYND z9C&)%rigiID@?>B$GzyP%Z3vVw}C zo~DJXouh}pyAKq~XG3>q18Ps95s}$|(4^!Pz_R9)Ha0Z2b@jG34GeY+4r4j`HV=38 z^>=i%b#-;Lwzbx`G`BU@H#IfZm2euGn=6XBrDf$MfNXKOEJjvdb__E*j&iK(WR zo~DL^sysMjmDFXWq0}ylO13n0nTU%*8c+~UEM>rHLEw?mGSG(jDzFXHy&-S}?^HDy z@BHq^CvtePHE4~JDLSI8m6)6w{54a3BTJ~mpuKhatfffeu3Q@)9s#I*N_u7{yn6&N zRdv`Kz3n|C-ECWjx_i0@`Zu+7^>ugl_ILEOcJRuHw&sSO zmYTNO>ZY>hy3(3jtN?D`^4$EQoa~~kr1TtkAu+)bN#P-p>qA4s)&+$HL+@`Dh?BtG z3i4K8KW|(mt{#CtwsuwyAmO(Kg$txM^k6OYjI{w7B6KSjLRm&0o$8XnH%KGmod_Y% zDN}Hf!1_^4`~3ek|9%1T$PVB|7w{ti^5xIrPyod_aiS1pE98_=G|)9PcDBKF;WTv` z_*CJYu8t3njY*75$v`r-fLp+=s;q8nYHV%q+1%gX+BZBjGCDLoxMj<5e^<}IKwk%( zkj~zg=K99QW?sE|ZADd0O+!fu(zZq1;tB>;`19FGSqX`$k zmH-?Cy4vaU1AGE}eFME+y*!*9Y#r>-y$cZu6GKBYLt_ne58$d$lT*}Cmr+)emlhL| zlLqx4G|NS%h$1Nh-;2!S_!qTjviP$mlRza`@QH+70ujFPy#%yhq`o8{%`alX92O|?y>mCZ<4mzU?{pk&0(O9y5;J_RM}b+Jq0 zB36XQgshENy>ulBT`Zlkc+NE6d2<5&y#n3*J*}Mm-OMaqU98N^q1vPjgRuS9@1OV{=nOYiE0VH_~U#b+yH1<=mn&c4;k# z3p7k-1~44y*|E`yLE&g%U4(ufFlQ}-NZ{h7(|x^XPxFRinWu|8H4l2)m>N6Vqy0(; ztp7-+LY`1VSxZ+z3Wr}wi9`WpL?%f{PeI^01?e8hDB<+uUf_cf9#ggp(>{0g zvIm#2E9iIRhd+bGq|OQ8;047b#N;)gePLs1=48U!x@tRm2Zn}5hevnp-!-&z*Wk9TquT}sHgE3j8{XWu zb*QnowY|TqskW`Pwz|57s(mGOCG6q?Z2q(qHY+_mlNp1gdf57qjc8W{?HJgS7A*r6 z5crOlg4P(w9an2>CwCijbY)suIXGFOw5bcGUa&K3XsIY^1An3*t)(F*At5G-@&aVQ zg+vgVqYV|0p6?Nm^{3G4As#)Y$!U-O*JE@D7veZ+8ZFUyVZ9rg?$n+}yTh-{7`=!`pU`ZrQPQ z>yDwVBi%!zLqprPZ0c!i?d=)rZR%>MZ)$C-YG`h1tSqQ5V-%O<<}h*#(o!>$5SZh& zer<5@*y@?K$#A`u&{^58|_z6FzI*(sa1(SkJO5ptD zYn6j6N`N;h9r3t?tc0qLuC5t4RGd(ngCyzP#jDqV^*1s$98pj{GT&vK!bZ%or*{BX zKNeu`z8#~ZyAKQvZr{0kX!~g2Xn$w_;K)crL;qk;R}V5?4PD3zR##NlHsBO63)!j6 z99B_s1fWkK93wjPh!rzehOLF(+`?IlK>dp=(9hS$E6~r?*3HJs(a{M-R9kC(T^mDn zU0ro`O=T@b1qB%yB~%h6X}~B*iwFw|Pnax>h-$)=DZl6vpqbytD%`^NVZ6u+kolvm z1fjQh0#q@et?-)wxC0TiL#zxn8Wn9FApfjAJzV`42Ks@Kefi>*$m(xQ&dJKnDkw+Q zzqG!-t-H6qxog+vUfy2!gS+?d+A*?i`}UoChWobc+_htHaC6`0!NINV?H#oZJ>6|> zt#u9ERVDRBB`i(}qo^=7Eju+lDv6N^CKz7iBoJ^0i7yQWrwb_U=RwbKI?vN_dZ3$! zOMtB%n(?iz^i3^|0i@P7Fwj*~R##V4)ldgSQBD%V&_YvWrEu_(?Vcipciu#-0{;da zFB3h%0-VMa4}kuKyhIQs;wLI6Q7lE1rzQC);7G*;P$LlvLKW;5R;I|TqO>&!(9ij6 zHmr+_Nk~m$TCb<0q%5PLt_7!987MSyNim_x;=;nf zC4%Y;4j}n+>_xP`7OaP4mP@>Y(^6J_;W`<^- z9u6K56I}x4<6tnPCdbAXWH4AI6`aa?PF-JDLwkGE)@?9@dk!AndEn52gZp;x+&(%y zxNGA9$3RhCvaRjrN!A9`OzSINJ)xa2f~~U zYrzZ$nhMB7FIwXPv0(`3cmxE1+Re?~0T@6>7aMyUBU2M2BQ)L{=_smeDXHlI@GU8; z40dmEDd9&CZ6De>w0Z03P|xr{Pv_t!#DI1!B6Jr@Egs%i?# zstO7cLgGA$2_h0aK`E+IU|(Vm@bfSodVG{RrAPc7)26`gHC~s1tfb)gKbNl#(OBAKJb3 z=$XUEjvqaC3}1)#?mx6^|Gq7ox9=Dk8s56CyJMh#U`tD5Ye#1}ik?LUdHF@Gq>OYH zYIq6JfDcBm$A*R++R8NF1@Usrc`D>ExAdk0%kvYHrLnptS68tJP5 zJ_+qlbhgM!D~Jl?QUiOyL{Smcdg(;dlqVj?Jn5W{-`{k-k^Lq@-0=($b>iXzJB3xA%Vp=p zWn_fKXCjvluFwTbA?66C8E7E+Pje6Op9|I<2M1?oQWLi{wE#^ydU+AYpmR)1RR;5w zmseAVz!$)MaAa|JPZUCmi=R2?jr%MoG2ojrf@ z)S<)25AQ#86ozYL&-QI2I|m0gj|_G+*VeSR)l}7^id)J~Ehx;%%gRiSO$V3Vy6_bt z5$l1eS_E;$MWAM!J`;`Jt}X~w+`Q~;T&(PD%*~9=jP(uG)O8dUwUm{V<)KV2D=R63 zKn;xT!tl{1B21$@3UeVR9Y%NjGQNhX5|oT0bwW2#%IXcjnu4d#Px?{}^N>$dMJc{< zDC4gQKm$w^6`drdAfw7NOS7@Icb|b?pCFJstcl(T5*cO&tDv~8s;IfOzN5Dfq5J6e zy+;lmKY9G@$z!K3Ts(99*ol)T5AQjC>=2&z;axk1M+dhI^loZvAAlQMU0sn^T8xYq zoL_JXq(sEW#I0Wy4tm>FVWH^dp0RMwf_XkOXH4~+DtgM2@@Ruofo2^{kB~6xjexw+GEBrd=Aj4UF&`oJ-_#%Br*Q}F7`{IjAb*9j z-n94s5fH+YN2{)=7*k$V6;y@Bw(ed&vlc?~b#-WHWNdPF93z*_VOO=))o$wO*)-7D zyLHR()XHFg3ee~F#Lwon`#--icIndwN($w72*wt88 zR#eEz%14uBMpi~#YRra|SO7*=uUd{K>OduAV!6?&7HvCr_U}x#z%<-G>kC+cmmp>$d&@+{&$Woh>yLja7xE z=#I;b1Q0blH5!cs38CO)3|$6siuquO_L=76JXNH}#lRFjLLG55=Pv6db+ zqbtgxVGT7?B}sT=LhwT-V~6)1+_!J< z*8YLf(ZNlwu3=NCbNG>RL=)eRFFUbn8Meb8ZMa z9KzA+$WG4A<|UStXalDJw27VP-H>6H-};Xhe!x8yhkoD&Xr^f`1btXv7)i z38q2Z-1Y%p){ZvjhUNw)I=ZN(Yw4&ep%Yj}N=a5sL{wt3Fxm7e!jzN5UVvHW8$HTB zzQdEJ7;H1&nh`A_UEde^yZ-?`(LSI>dW)xx@{fp=gshr|Ez``(&K_c{$9?` zE>7O|mYR+Tj12Wbi)CnzAXHylSst(oIT>jMq+xN!e+i*1jv@u+pvWDg4ZM$kxM?yJ zk5EqHUje`a{MnwvFQ^y_P%R{u#jtnOjG0E3fc8wCJ~tR-8p-Q6qUkU#ms`$lY^W)1 z?cOpxFtT;`$peQ@ojr5z^3^MsF5S3(>&BJq*DhYTeE!_gQ)f>dK6GUN!F^jshdMfY zyAgV~HzJ@G7(fC@mx`TV)20^lyo>mzitR!=t$X+WcyRys zd$+FNx^wf~rAucoo<4cx!2Yc}clHf!?QiVuXsK;#EGw@o$thyS=P{EQ$*~b@R;=F; z3h4nDqj@V91WcW}U}}J$hnKyJhohqx8Wt^$t>l5>Fi_Vu&{I;@ZOQ*`%YgxfBVvfTMr*Sdvx#KlY4g`-245` z^;;J&Ub%4M%-N$yc5U6Zy?<+eb6sy|O+#IAVP#q-rz|@y2mNVi2uJ|EBg8nDFPODx z$yDDN)2A-*a`E}_wV1kbLH~+ zYZuQRJ$zu-u{{G@Huv||_chnHHZ@dmOAFE3k(IhJ745HK(UDB3n-%Vv8| zTL>B)KOc8zXW+{%EX^!UQ25c+RM!Kl&qxF8B`P2(7Dv4c<=`mcP`bHUPuASjN`%uLT`=A*dT-h*wt zWyh{V`;MGAf9TxR^H*-)z4!R(eLD*+ zU0oHmN&4!lN<1{_DwBARs3^QVs$&2a1#?Os(Pq2|Jv3gipxsKi_~Q^Zlt&Z{w!tYv zGKz|N`gT^9P&Jsp*a&iiAnH%b;4rv_MU}NhZCm>H?%1;H;E_|OPhC54>EZ8p@BH!j z*~90*UMzh5;Kt*7H||`&a(4fTBZuJt4Q!)h+1_2wX{^Z4%+5<<rC)D=&p)j4<61m<+1* zxc>P!Kb=E5e*|oyRL3Z};!sdGhq-pD!N2eEaO#%O{T>JoxSE zweuIxA3M5t_l}WWBZz(m8}bmn)|Z39C?^YatC_Lk;b94pL35X`T7mv<5U$UjGi|D` zpOdqnoe6j>EessZ^bAaNk(2=GpW>LTlC-L_

    RX>_8Eqn^18<6oYMuvqRIQxzUBO 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𐌜mDCpF>X)>k!TDyvs5 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

    &{FywkMP> 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�K#v%oZ8Yg*aAe^vSH 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 v1*ik7gE@V4RY%7kTEtLj{| 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

    GpJfD-_%M#+ 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

    TMRS{Rot zH(wlvl(<$>ngQY4o3?wrfQT1}OOp(+ zvvH>K0$)A441Z-KnAYG~S92X9Iz4<w6q|Upoq5(l5fVb+x0SuKg{R>arUhvLzIKxN$>GUORjE;1m&|2PB} z4+J)cQ;W$%0TMDW--W?n_z(H3fw`qr7F_;ZzB&p9?d5Mhc<)`e1qb+TUcdIC7DyVq zqWsVMU-mcB?B%MCte6dIK((nXln+G_e$e1AZDC3g#987yWQcW>(KJuZIH{~Sk0~Cg z7uBz>(Q4;`0#o8JNPrLp2+ikr>-7KmtiPaKJkj#4BV!dR(|ui~2S5M=n8`{E2p4ph z983g(LD!>DNzREbsJBdtT@vOoU-%FEFZewn+8MJ}2j^P&8n6+xt~rjz_wK#pwjF`n zw`^K}>y0;D|2RL980FvWKl>>VbVz|7)&aF*!XVkv0D)I=f?Pn%Hi2+O4b*J(^B(7n zvvZs97YxL#t~x_HnUJuyR;ycxYvlhUZa9rGHK&gj0mLR0V&)$&EDMhr!WHyFHHKn~ zBrBz!fz10Jd-AC0&QymnQ)tM%?@<}Sk1;g80R9i|r^jc914aU_`x)k=i>5fD94T1} zu}AUcx;qZt6CB{LP`CvcuDj;y=lP){mVlOkIgb(h_GX9{$wX<&TukgIHZqwB(4&wE z#)iy?;(|;FrOK?+GXb~RIPc9XLKfR=wMLCz>&3WRMnUTJb8D-J5}9}o5+O$l&!5}9 zS>NQd;VWfdvv#oRb^OekFd1425~?3r#Q?4ZoqGLUjD$>jH`hXnTL5*L$zRK%{_|zP zXu$U#BLh8@rRG7<>1_@?h8@?_lk&qYn>TG(zxI}!Zv5VNzjNKUu3CNhWp815KZy=# z4Y=~5HeN!6e`u3Trl8wg?IbX16(~q7?~aLJNRO2==lo-n8Ck)`Zlh7F_o#E$>kSJV znb%CbB=K@)8s*f}AaQoLwrS>9OS>*07F0?yH;l)LsD^5wAn{EAXr{iYU%52HEfSba z-B*(VB64M{*Pu!!6U8WC>TeujqL}AH3^rVlBNJE6fM4kb9LjSq>9;Jp>BcqRyW#rp zeEZsKuU>uS<*Qb%TyYF1^mYuRSUm5CM(vX;EHT;V#2<8?N!7E398k~f{@nCLVQ&M5 zOtVvx!MoD@yY;rokS*ANKrygUZ}0#Mk46n-<`QP%G1T1u$WuoO&Yi_n_}uQYmKTBM7%Lfr? zVPZ5yZP_M6ONVN<&CReMP>=-9Poa?v<07MbMflKeM$xw-A?MF)O(G-unQt}83tU`_ zELD^TRjfdAw(053n>V_X{TCK&4WG;W)!Y4SgqUTyNnt+AjsVkvNSDvW5+b`V7Q>;3 zDPmd;#$3qOuyi6P?d@ZMdZ6dyZLIvrSJi2kax-PZ(RKb={i@N`3xPtgSh-^P@?}ex zEb-lv0U+b5$O55E+8o&xnZ(DOmFSz z%Gfe+sj*UDp)Z>P6pI(@zMl7p`8{L}>CEpoZTf~X?B#FE zVn+;@LPNPhS4jxxDiMZ~5Z(=Alg|YWgZVN@sInnqOt5EXg3_o!V635y5SLPMFy1f>&$iXaLi)&@@3@|(~g&wF`|&F{;;7?6xx zJCO~aoBv(rRUo1%Ux7qt+m38vQMCR7`>{iKTqPm8Q7D9N z;fSJFY7LI=n4&hw>8(0a;yiyj^d)?t%q&((+DHMU{zVIP#FZtCiE2O2$S2{!lb#{iHs(8I$pgWXnnbU(<%`wr z%jax9a86c9nkI);nQYaWG`eqx+9mfC)^wgjZEI1xYu({7Q;LMLE(1vi;ulOkaE9QDSpCzXbIP(khDw86TS)XzK+)#$Qv31+w;XC0Js1OqNway>wmb_TO%Co4p0x9GC&<*On)*00 z5MXs!i*vB!YDz;{QPPPo`wU$nLt;cyoDz`;B^_JkVBiy@uMDB40gNHi;;*b^CNCdi2TNWsLOfrIhv zdnYE?^LKSPkkNSzpJpc`l|B=hwzkHHJC^%i_0JZe_R4ZfB{h&T!8MB?o;2{m4%toV z`DB%DT<0v5)vU5OS6;UKdEy62rm^cwOpLgn^5XQ9N5>_n1k#=a{AO&-KRk)}SeHF_ zE{wG1%f-Y<6eY`G2(U$XPUO|PGFo4$i>_Ku6Eth@6gxTEWnkPd{4ssfF48C6u~c}c z7lMQXqS~EcEu&CZ_zT;PN#74e>O38pb1@jQ*c2I6PN=PTTgUPxzpY~kylU$JCz)U+ z8#mZEXfNmI%brZzF|=dSJtq*NMO~F{l$#8iuUBrs8#)X$~C`q1#pG+Dv(-)RX zSKS-O-Z!4J1b1U-b-Q-CxXJ(;1)GbkG>W$v%V26Azf91Q1@F*M;!0T?+;`yaRF3&Z zqh?HTWU>!+Hr85pa+|?T+wm(pmrweAYoD`sv4V>ig{UwoLo!}`CW&XaDB+>+XYCl> z;rR-s=EYr^5Hf@cOO}rjl|+9oEo60B_Z)%CB}8e8LKx`ruYX1+!SbC$y3<6~+cdf&nOa(3u^?bT|QnD?_OLl$N#ohB^d&z7tO zwrjL!uG!)zD|sqP^JV6mQc8+CD#HP2Cf`t&U(XQoI@{gNkWple>?NqN*8`T4aA%?; z-Zo#F!hE@)+Pdmp7quWS{&}w~V<2nj(M_GL~-l0$cxy z$8vX0y8DCk5D`X-Y>MDmIMbz+ic$Jkl=7j*(LSwr=>?#fMoc6vIXkiMZ?h!#v=5L5PI@PsGq!;^r5w3mvs%zchuRa8ywp>=l3Q@|Hp75A)^RZ&_|Fne4s ztdPkaq0jBA*g5HE`|F&>%pvd-g_QzzgmJL_LE0u+oB+_SEIzf=ElC+E(bpFxWCgzTy1sN=+ zMZ+b6brO=H?+ZD-?pProe>?(BBF$3PLmE2j#9oANOwDWfOO@US#%30`BY)CrC;m2=!!pA^I^R{>aAbj$%h|* z4NR~?Fz9SJ$P0%r?ocC*e@8Ax8- zs_6Iia1H>cD7ewGR5V;7CAmChBBA6a)1{PF+dphfA8klJ@zGzOdEy86ICoug=L0|e z-Jkz+*jhel@RVOL+LULrC(uBm3e=b`k*%r7D`(4F+qK+t<;M=t^>w3bhBw$wbRT$Q zHD6=Vq)q>><<~eHl23j7>a$P$;9lph>GEq{eCM#Wj6Yzq^K8C-c|6?(u*(5jt`KV= z__-vQEwwtX4X_zMV5T~#xK%i6jd{){S->h9B-v3+L8oJeYKt_{9`{DbE@*S8HcIl1}8qLn7xq%kG-k$my!sIGEf7so#RpzYZK+$&5=pp1_RH=dl0Cd|5w zO)BG0_U*38f@KM#Jc~PJ{J&5&V9=cNmIJq^UJt5QM-22am=YO+#?G3pjUY7kvIUngG6yc9{f;&71MVW2Ua~_%HWFQB39z= z(b|7~L+yT5BnDULkohHuG`H5(!%kvla$PG;MWjLcq5!kkWo@qzj8!;`Z4{8M*{RqTr!L+ zZa%M{@fPi@`0Wy1AwA{Z^wOr@X5*5<)~ehDJHpBr7iLSFNtxnb2sS8%83AA#oV}w8 zXroJV#CSv%*@;gtl=caFACnUkb(pq#{H2M>!UTui$Ci+BocWfdrx0oOPId^=M(b*U znuEKcuB=cqvU9T|zkw{U>Q`clF&_CvWis1AK`nEbWIB%orAnN?igb@#oUb#*S^Yu< zH8%^^<^h-^6lYD%FK=jZ^bJdez_>~q>F;VgQ&p6Sxuyw0annU2h3uf{8cJ1L@hlxD zuLS$Rx@NbO&k~>%QtF)&pfAxDJAG%pKnt8bKE1G_QT-MtTdGEws?=0nlAVai3yVHO zuv?*YNzA_~r{FhH?uYs$pja+)uPUF)9f8M_AH zjIb{ew)9uzzS}WMlyRkcScz*?zmYyIX|Rz$KU)lpG_WVw?Yd338gZjm!*-c9o*PJF zuad|X?GZ3ea81gK5r2xYncUI&*`?gV11&w+n}NPjro2cHELK)7p5Iixa+G^8d}kb# zg<&GK@$r3jBYt*bfqOD^CgT|$1_*?GMrY0YGbsRX76l|8kvrsNtwG(D)R2Y(`8 zV$~tEKv_n@@0NDtpFhhk&kWP7e-rQyw;Egy8Y%}$om%zVnb5OF4D8cW1(@W~BqPz8 zba}FgIIv*fOw6m4+eGpKU6L7|j<*R+e&smh7JXb6QmGIR)0d7cF&0n1G)sM)uj{F@ zAdLS>olPT4W|qQRp;R#kDxKhDB0gq(MoB-*Fr?+YLgXqXAXoNB*=E^CMsreuo^KQw zg_G!a(97mP!w`odR!m6-fKS4YGUDDe@faSniX?s%LjdHcPC=%Msol-|6WAG7+?XgW zQ)s>{xRFj0>e~Y$##k*4mPhgkuMS99)15_SM zo>+R7DUCfNX&!WeuFT9rYxD971aieJs)Y^EBrQz}bcX-G0_ZRIHX!B7t4?!+{+H3E zqf2J&lwE@b(!Y?SXHOO`zLx1h$A_L{es>=U z8Z`pnXq(+~UU_RCa@Ez9{MmZhDD371bg5Ls_f$5D_(D8Jd&iK=zMGGiu}~YBMYK6^ zLH=9cK>oNqJ{6R+^JI-sMJ1L*@!;ZB+CjD5K=_1K`4yDgO~KaME>l)ae5onf{7Ts> zg@j1+7Fguw>Ny^Opygf${~Fd7=%SxANwbkz3|98&4yIPe^-Y1f5%O&SGXZp;U2|?qO zgSc*r*_uO5W@CtdEZDT1o>E!CDbx|JO%<+_2?W^22ysW1vRG*=ex-+$lVM(UN>k2M zSAu$MOv6FXO6$I56+KE>&cx7%(Hkwrj6D=bFhX5RF^jmyZB$uDj zFQ(=B{XmMyu$ZVQIfo%q{_=A>< zC&}8akUzpHibwIMYyVjKWD#7(ta^+W1Kmy9@3a`WA98p-3A;2!UogQz~n@yMBrSB|jP^5E$_z}}J zo#oi@UwtP3QsNc(=JMrz-v0O$K9F5*KnOU@9tZ3%LxJe$!bKMrkx;IIi}#8P`l!@-O+LfS`fJ$FmZr z{9SV(LBxMVx5|`4t!bzw947RlBmxD3Llc7u(n-ev4sDqJ585zHybRBb+bJBFs45sR z))tZLsDy|OM?C)=E#l7Yh`F``$F7=veTqUImoF>Z0zVyBxtwT=`Sb!3&2WoD602d$ z6VTV3hBQQ&8+9=JBGfZ>HQXC!37GU#ebKzhVh_iUV^ z1;9~03|E9ySIbmyu#cb7NR}kgC{3D#qaLA6K>f8DpXmwm{|=9aMhxf?Bw*F}l-gu~ z6*Y4u4Yh112NcuP*X70Oin;E0LMW)g~w5Hbh( z3}F#q<^g9Pvt)vOlf7nt0?Y2m3?UXFYl;pTQ2mdaW?U04=bPL7{z|eR+Wv7812h83 z>1QbFOb*QO7Z6{%mGL{sJDcDSLbhH$Wjd}212|v+krwLtk{k(2ah6Yu2r7n&1n


    2s%Z0t&Rw-jgP&A|z#k%`t8Z(2igCgu@=L>LFr#wPd-a3I!d zjRc4Fp~0Df5X?D^fa}9tR&Xrj3HkKVZ%(@=UHX4qG5rAm)fu|~yO7|1u2z)9G)%?k zs0LAk9(I=iewshm!{?+gND6fnvS{83amfTRA(Tc-p(?hDh7~&{zO3y9)YnN}XYrPo zZKY@mt6@80npML!DWMZq3=tP}2mkYFR7Q9-I1*rS-SlukZcJ8ac{rp`Z~Koa*AyYx zP{@DY(B2I5|HbDGdZygdcn|<%2p*{*b+4VI6vlny%+{OWZ-#*;u$-gABX*Tf>A(ms zTL;#ii|D|={(A14|LylgE>bmN%VUbG_Ek=uRHj3;pc!OL_`I9+as$FklCp(kM|^C; z3cF`KQ(lL^^wvq&v`hcon2DS1?A=%rt-JIB&O&EhoTrn>!c+Cz5!9m!JaMO;8}*#wE*x_i>&Q9h?a zGQ)GGxhkf^?f3ZI<8ZslJ(#`()M59B4cClI`>jlHC4UMGF&$n$Q4`+>&gYB~e#;F) zH{T3!Su^SnxcRQhr{PcbG@U(KQd)UcE=klhO%@WXT1Dl}mKdQr02k35)==aHjUCr- zQZghCsU<`>q`iGgbC6g)#XTWT(%>Q8r&0r%ZK<0MivsO;k6Ofadp)|Ffxiy#(O3Ch zA(y`8UsRvefguZw753}KJH}nw-=~cM|G3f5H-hS$NNu-2q2=i>y>WBEm3Yes9xG#0 z4V;H{7(o${Uy~Po>b^uBMpeV_L^Ukhg>n69xiOR>{%gd`29@1R`+q_;4|JM*(@00I2%7kYPFe@MaKIkMqlJL#|M+Q+{O{OU8OSTAEuru8z(Sd}(+H z5+AUN%K$HJ;%z@_P{F*D9#YG{oW&SX6K7`C#GFUw*L^~+O#seb8~FyLi|WI4 z;FQ*ddER`B?`%J z=z}d&wR-bF$QT7g$nW&^KX(17Yw6}MH;OkS@t^I=M4elnYWI*^iK}?l%K?@ALP_OZ{gG-LzryZ(8n64YQs!iaW;$^ z#;{-aJksY%&U!D85ouAsMg=m_2+vl`HSucu*Kc1a@{$JoU(w7Ee&)1K))dJa7J0M8 zp2|7gcfppysbjoQv^p;%0(|L`|`V(I{SmM&!?|HtE~SVAqU6Nb&>Y) zm&V}+hYXO^P+Wp#3Y)HH-#+ScT)Q8oU1?}LfpJ-2Oc|*|)#Zf;|NVSUSHpmDmg}g&-i?B z726cK3=OR~_cGeBE8dy`jQRDWJNpT9oVPa)(s{fomi|^|C|*JNhQ-9UxBccNsTLQ_1kaFR z7UQw_(Zylxc8i)dIl5!uCtF8c`U5?tb@DTu{J#c_gewQW`_@e!bT2b8dWQ_%S3g{< zdvLil!fE~dZG#fpzPoRL*rg|YWkb|=6rw+8OYi*R?Ty)sNpIhOX>*Q_2v|oSTa@O* z*anCDx!WN3HGgBToOSFRj{S`hBf)#}(RXefaRptD?GLq$7+T*@t>cP;>eO4_G~}Af z(tp)IYo)EsEhnyyR{*``#zjT;hZ;v#A`6Yr&^IS4UoyCq0_XBc0AgQt_j&wYz3A=3 zF8!`cGaOiQOnByh=g_})8_xdSU$}7q7t68j=ObvLRxjc@Q|o9l@JIBX_l~$Gv-GW% zb6BLM2^p>XeVmfw#tVx{=q`skHUS9(iI>T-m6nfpY{@;7W%G=C^|K!ta_J8b2mEe- zwW0liWB=1u)^gK{D$GYaJeG*l;i#-@rmYFy!7^N*_ z{}IP8oHm@^<3D{vCusrgPiXwnfm&_AG=}_)h;=z{`gtXecY>$;-|ux9S=xq;KayAY zc2p{R;uMh5dv9p^a>h_KZK~O5%t37BX~2S%86RqzrSP1<=pt)0fzO< z7SL%Tgs@^EVW7kVM(eI`zokiXR-1|`EWtknHp7w#>#jmOXnMGn&36RRq=qh4c&w{TD$Y^bf-`-`zIo(%*K%RQ~_8 z#W{b*v3$cT>m)sSb2n#3Qt60AxdhZ%6G@tnjvTfQ`wJ_2v8ofraVi1p{%4M$URHv7 zVRb-rE=d)NtFx-)2N~;y6mP}yhuza2N5i+a3}W{G&P=6N9C6{4-hG~&cGf@EXz()hR{w;3Zr9fwC)+hBL}r;8+IpB%p4is z8z6_iVZ-ZVWbv=IEG|r8uRR)(#b^|DwdccAu_u)%V_G!#0(O>mMoBpuu=O}5UL%+2 zD8GLrr+!Q6G^ZZ(OAv(#YOisn?s(fLzWVUbAO6~hZ*>WbkB?iHo`1q{+Yn55lGI~| zyEn~%A#=l)4iyOsu`flUr1=TuwQpNQCVxGtc$rf#H7g_`F(uLMLIjc#Gt}o8sxbF` z{VvB%Pr0Ym(It3Ac}QUX$|hIh)_Z=E-Qt@v^rqiyucvUav6UDQ7fb65sPjvhE_qig zFaG*qR&Yaj!}?0)ce479)MD+vjogJbpW;-uU?j zKc{9X2P%-0|FaYO^5EW{Q6p^VS3hL9oWytjNkjKE_-dUrDvV4i@6g)1Mbor$kk$E# zF+Li~nt}A|PSWD1#57(;b#2B1&biY5%5mX+C~p&9l;C2=Zy~UDH2>hbKKj4U5={A} zh&7?|0v(3lv+0V<`Pv^I^9~s%QPBsnN;`?K-HnrkcxJ6$H#DjAOdy8*;n5XR0vtTO zXVJ_cpa8P&I@e10P2%GD1W+8R)>fl$sa3=|KC zgh(TVNREmxYzE7FO5KWoB)`!P5B0hd-}&oG!|xw7LWbV35g^?F@wQ8>8YWpVHcqKP z_SHs|koA6zCw)sJ`E6|LdYWd`ZPZ(h3rF@nH$N%5=Dl(oG3gGHa6j{N8SCYFhhjxR`vDqlA>$o1x?`Cm*7Vqy&%@gVk?D%m}vnH z@em~X=5d0@=^lFK&Al$in|86v*Tc1FlI`@UjGEk!95~D;Kfim_<-Fy=eSMNJRp7n^ zxC@e2oRTCJ4>+3Jt_+iq1zl88QP7~^H<%hpEr{>1PSS~V3)CHmlPO*Ek2yjwwf~bV zeVYEQ(I`5JWfvD3QMY0=hAylH4O zqI*U4#zE7?T~WyP)P8%L1tjQ27x{cNPDtK2fEp&%_Wns%;-)u#_Kzn*)N`FUD!DZpZNlHgfQ{ zA7t^1I`z1JRB|GxLk}i>^O47%%)K-!S!?=?4ebt&-P2%9QA!YaO5>ViYsKG?w2swe zp{U;8wnnL}`ih9fo;N&mHF-^{zar_(UQwyF`#AQ};}jH?-d$c{6J2Sqh1sbNimTc8 zWO4MnbEq2#>j=GlWNS75e)N>q%4xTD@vBKr97|}m{9zuU+g7X2oBDV;%vB=KWCHQ# z?BhP<)e@eO`sWPdyll>C=R0#SYhZI4m<;Vn z&iw!CunMu~4UeoA`byi4WNmEkel@DJp*~*Xa+M@5?aZY`-rsw*#BZpAtsFR(yo)9O zy4{YR-`o<+8ge-{fA*|bojH*QgFG5EQ9`05Jp?IE^pEg2DOlW5LGYe&m-8EZvk{A6 z`OeB88dWdyBShzM8fpgKUL~ca+K6P%f1I>h5}0$$!Ltsu%sU1s7mSVx4BR41G$r-4 zFadkj?dba1&Op`(>f@WW(kUhjiPVC547Cq?CbgI_^tZ}P!x(C<8du{P5J(r_kop5N zbn{7l*W!55Gft6Jb8xx+RKmWJ=2cCNjrG5em&aUn)@rwsmIrkw`%-+GV|xcxWE+mU z6+5>hc;=^MrG_h|b*H=Od)ue7c-?s8&pSfE684ujCKjZF0^?i6y=`)SN$^WEdxWLT zv2^kAgrj0~jdD>}avP^DiCvJvT=m6TZX=p4>Fml`#_3m{rNSQ`lL#@TVq9gDRT^-i z!1lWxHDBF4k>znYUiZ|%j5@R=hfSO&QTcxrKd);W0`PVzHlsZw2Nr_!$Q*jFU|(i5 z{VkrI3~-N^w_J&jsH(;`x%1?_iP_SjLHmexV!MsZ;vSxGFQ~N+a_TRW+wi#@@7X&x zz38d6havTE>L789UX(+tm%cWcX^yhFgr>q3jSc^dle1izt%{J!1rjDHSnbsuzo$z> zojQakr$PZSM~m~k2mYWvr{>F>yjc^t)t@S#uo+}j%Bbi@;dO`6yoD%zW1G~m{BVdk z4MV~yaJgNux6Tij2%FfMDHRShE^my;thM?truB5?jlw3gF5JOood4@`ewrS}rKDx^4W(U zQL6HSp&jBoLOl~N#;m*h?LnDYDzDf@_U#@t;vHcR7}INy>WWxxNdCjQgKA(W8&AdqO*a&?=CMHih4?`NkWYQVQTuqbL zmlnPG?I~75NY8#VP_Vj#&|l(~5mhpm9PJ*%(RYWOy-?2nQ+jUx#tLpbevjJLwHDu~ ze6pHIOvmoyhr>ln3Jap!*`j0kR;XmA7$?iL{=%;%tU0HImgq*hEXSs;tgLe67d`is zTfgvu{w(LtpLV*D0Q=BBm}alxc)%qY@Q;WB#c7~!tLMiYIXtogUQTdA=9LDMX z?xuk($1T6TYT8GaG|EUs-~Y{|l}+DVW1s%Roc>bG>1`#sE<$jgSXhGESWj{@W!?fa zt2cX%o!yLSOz3*cq_&##LkX$1;~C=}j|k7Kk_3b*M_3^EWx&+K3;Ohe^vgYaBS2pB zRDUq$4yaN2vkm>iHG)I?Nu!~?jmzi~mf0pP=Bh1Wu0La`Ojt&;oz-!{M2WX zo%%Femd$4Juby5xqd7CrGTH45uxD95NKWoOxW${`{AZR(7ZURdi|n^*zry2 zToQ|MdFul62D3~A1-=Ph`Fkpu-#at~>gN9WwtB5dYx7re&8C)HAX zCEn=_b7QzV3gV?uAc1g0!x5EANzPlabSueDpqed-+#dnF*_|k5$ll+XTEcBj^nA+|nq+Bg z7m)5wRhG2#d6Ls!sD!l#Erf=!CDlCvL-;0L!Vt}5(gx3ujMvuQ% zuOA#z(6$5Z)w9sh5<|G)f-TxRpkn;SdMme@v~W?@7L{CO7g$T=RRw#~ROf!*7qz!v zj$-3=B^HKeSqdZYC>;e{qRLhZy6FYItV3Cj*XbP|+r2TjwJBomqma;i!jN{RHEEGl>z3J6tHqL{H{`;2 z1Mw@~+(#~qc4G0qf~%!cZ9~7DG#=)rt8htFwaouBmV@j7x&%D$nM*ROmDZX)v-bm-i z{l?{`QBt&8X{R@~(tWk`I%&B6A>^ zBdO#;cs9Zp(l-HxLJVwxp9ez=QFyW(TfW~V%Fq|JO469(@$(kQf6mZ{-#bYQNYd?B z4Wqf1Rtl|Vnoqi*Iso;~ao3Gi@~l(6M^UM=NUqFwid6&D!APz(Vctro^nb_$DBDj^ zkkqyG&cPI#)sZxlC#hj6 zxD4MrL7Qj7HPkDIC^)*wuzhmewd6nqlNFn1#a@#*o1{V_iJd2%^VTn=ZaR02c*XJ_ z%#J12JuSj&L4u)?kr@egL2_D}-Bs7oqM-a)`rC>J(B;z5U|C=v5lo2cok)lUP^x+I zDHd2h0oZ%bH~_VOn?^!MEpIc&YPBnXe0bcI`ZN39hH-1mL02qBTb^c9v(86<@bx)G zne-o~ztzf^AWk+jDotUk=NBSS@~+$*nNcnunRq*A6%k)=P_(Hp~tFB*E~ z?S5%eeLwA++8EsqKC8JE@khXeToj7{4 zicM(+XFt!XLW4RZZ`pXO4NXh91G~*EQ@ker>f$G-wd;+gS7T1T%g}SLB~9ddeJ>e_ zAr3w^N>X%e$GGc;hvtZWU-P>*Y}oinyIq@aY99=S<`36s3c;9$K9a><( zkNTRd)rQBU#QKSG*NQFG;$>R}ih=&Z*914Ld)sl_IgM6UB!8>LZUEAj&uD4O&qeZ` zqPNT*tv96D-yBSlSV2C9hl0b_FBA$hunJlCN}roee^M?p3yjN5mi~?lml1V&{2<~k zS?Ou)OCESAal60facNt$Qfl{d9is{iJ#fic_p~c4=Ag6Zz$dZ-8`fU)Tjk1-6S6wZ zpMLz&A3UhVHlb>IKwJ6Yh=(l}KVli>aa@R~Ergr4M8N^XbjZeFIitcu>>XH$A(Z9V z^w>zh3jbsEmKP`?|2`_;|H*`aO$U&HQCbUYYQ0{0m34LVB$H}WUiqO1ANik0zVe=J z>XGm2dn2GVANr)2W5 zx&`sX7M|bC((kIc%JGA$*I7C|aic9itb=Q)G4z@{dHMaUAyP~qP{Ww?E`4&6oa)qu zQ0A(ge{Y0Pq?UB2;(Qi)IEC{qPsBd&5Xa?tp2@4bAc|Du#Np}XftlDd zbLij##tCQX-yUd>5rAH7gFL;cHT1IU=`8;{eanX4Rx?avhg(Y9$EMVI1DPv6{q&r~ zEvJnv!O;>Xv}J!)mW-kCvD6I@IXGtsy2_(<-i`6rYHa|`A}yVAT3jfVnOpR<`i_JzKwUSdA2mJQJGc<8cjDe=fePy(9vch9=HxVaMfkPu6pw{6c}WT*w8RnGA)@F zuEi&oOEl`D*U~)C{7ROzf)oCb)9ySPG;pK_+|sC03$>|dFh$9yeKzRwBdgPyY2TZZ z$(8d)wlHfc<63I+SL(Yh*HwfxxnI3w9fabENGPvfNl*+X@v>EjADUh%?4wSsTKiw( z#60n}JtJ}XG-M0+e#U@_I=$-DmxaPSgzS6;4`z|oKxWzx=A>;y>Hb+s zozT+s_n)DKRyf}xaw4X+e{f)29>-E0mKVfH@=A7a2*zJ_N+x-WXCQCG78BBD-Wy4C zs`dOJNbn$UzD86@PM%mw;Q|&{Cx3fefRpR~HczeIM%F9916|#lW2%GCWUk11Y;GfJ zDBZA^%u60fb53e0ugR4LV9|?~g)(^`i(fDR$(QlCms87IWBwD)p#xKBJ{1rYs`ouU zmk5fGwAW&xX6&S*JocEypnVVGe|T`(m;geDuU78_!ZZ-*ul%_S4AV@=SaIVE)yheW zWNw3Ch%TbimQQI*Z+zBP?t&qVm%tnD0Y!3)`~%J*UVn#(3eCZE?u3^I@*$7(UivDe zIj%3NE~)+^ol+Q($_vRj_4Nb`EzYeSB6>r?s>lPpCcyd>x6Y&E5oR-uF$Y+@AeSNUmXqW%xRPncNF{&( zUs)K+p5r4$88KD!pN=@1>1Ys&B#N$$bk ztG$Ds5%oZlRt;VzF{+~f-=Ds5nrm2_dCe!YrBXeobv2Ma5fSHbnzlU1UHrBl*Hd^( zjGB4X!CD8Cr{m#%>abuy8hEeA#{h4L!~ITvGGO)thE~zgRGRQr6boR23vNW76<%@6 z6X6NE{%H|bfY6m=SYmlCskh#QOZw_+!;t5yA%#P714~ASPyhDQZ`zcF@0RX*!+-z1 zPQp!<>zz&Y2GS>Eh1@|PbMs%Myb*zH;wYinQgOtcEm)hLa7wI|5EIIt43Xdy)|MXqLGV2Y6rEI3ZAPiwLPPrr5I!kBE7Vcx(1T*a&beiGnXQsk|x%&q{}WH6G(ywZ=zzErNJRE9=6HfbIIJ-EI?1Dg1>UUgNsN<%{=p(YvJSa77C zViLG^{&q4w5TgsykJZ!(=c#rs>$X&6jV@S5a+@pR110$+tSmk4^E zuTD&ulfr&9a=qle^{WO9r4UAC%mBG4y>&aUBOdUM8O>4dGi{|HqsRuV0ajF?ZKaU? z+1V5gg9Ii36t(_q-%G0t>9p7+VK?iE!l(sie)LcGEd!Oh96*7D8%z-?qe=miAXk;f z%mK>{h|7OX*w0C&rEwwA3&Toy8WbGu0nq%%@F>Q|N+C)#H4{ZP%ZPGUF}e2E=KY`B zr2RVD6;CpEJbh+%mBnhAEkNU4Z1&T<;vl#(dz~B}Cr2(tm=Z1Ci^dRPV`L4#q4!@2 zO|0aSCQrKU8GkAkS;P=cVTgJV7GBpVh(TMlaB9R7l>qLRjuCO`m9I0*KoFFC)F8>k z0lUA=O1E2$0|!69$<)$TMe@2Ttt;pvN+Uzf8?fK6li{&5gaqbhZ<^c}pZt4rOQAj| zKjr%-JemlP0zK5{*gr98j<1yAA8Lg??zDvv?`QthChL)dina_;l19KvNh=>#YSdqb z=lM?hB8rENR;4}|UKbHD>Fz{-M5{hIU)(gKMVCISZdxNfIIBD|H!%fHnmA^&hGL}9 z?3_Q7e+CVYjEP^TeiStF%^<(6^#ppg&zci76-=hhf+D(6F5P@k1Kb-+BT#hGPi7&K zhq?^E5L8`8x~J___OrkJ?w3Ax&s}%ibn_eE`1TJy_>(6Jnt8&Z?E){$0a_K8Qa@7a za+O_!wYjuo*Qc}aO7AA-j|?FAdWMl6icuyRr&OOAa3xP?dEi#$7V1qBZ0KE>Rm_^% zlXwNFs1R`pu?ytF7}eCP*Y7kZR*z4hMP43r6Eia;qR{Bqmxd3^4DjI8r$$R4fL|Tf zmL^^M-iKeP=H0UUP8uyYM}6t+ z`HF1FSyC-`CThS@Z*_|hj8+}o~}E#k57cg z!xL#I81v5367K%>**BJJ~?3U zKmki9t+aDpl4Nm7`VM{LMz5BZ8IvvkmP>F&GEi$@lmSUJ-!(NydK$!WzoKz|3scWZ z2E<*32Z4b=UcJ)WGtF-e2s+vSqrII}t2 zl4M;{m)Y$gaT)B{*n3q(ORl3Ay~3at`N}TvZh?jAOBIBC@A7S1w;MsZD1oGAi@hYI zNPs9_$S_BD)co~13dc1~nlowH2l3%0$0o>n=G>68M|Qxk zJfmEys8;M?F?pg=i&q0ZO9|KH_$ubT;^N#~2-^$58uQWfs||obj<_MF^vO~u$*)C} zqcl>^pV=%n+kzwslo>ulaGyX+Hmd&kfpkr~19T&Gn)CY4{FC?Voi?gursuiOeDM|f zpq7@XbV8oh5WJniuIVdnH!=MS)irjvs_iFQjv}!l!otZ}Ljn}WfcC(J;ci(ai5y+g zd-$Tsx2~pM>8aWdtnqPU9B63_AM~KLB#jkE=L;kcjfRpBnk0;OUisIjHu7pUk@f|Z z1YKFJIj_0@Zw;O(8u5g+fA?w2o{MUPpFRA+=xAUwZ3%-*Q3$vhW=je+B22N`fhwnE zvRs-lnb5o3E6G?`KW!aEuk<^h$Im*mOtq~F>5_xCOFXxiHm zoll(75}mL3)SphdP4LVrZ6^PXe0oq@_BrL++F5BhF0w#9<*a3bsDVe}z;etlyvoc) zK$%yTbzepUbfdvZOY%EQFL$Y*Ns^LH1e_7%o1GM%pw5&%(7CEpB~` z#I*&JWaQ}~Q4$4`5!>_ZUw-}L?_4vxFMZ3mWel?l^F>9F6|=ltj4kudU^~V(X36kd z!KmN~tAdmlB79FM629!{{!M0RL&DbWBwOhf=k6kC6+g-|%|ykIi|D^0hE=KCo8*0v zv-jz@Ck$%Z83v5_A1~3u+8XE9yFT>QpZ)4LfBWsvAO6Z`KlbiBcdVh(Cl#Q`Wm<2) zhZ}~pWxM9KEj!k%ToGCkW+nKt70f17{*gGnX6q8Ne+mE^f;fz?t1n-@%7{0CBv%SV zuyv*IbB6c{QnTBczcG_bMeZcOYAhX>Ny0R}<6M~2X_6$D8e%-4vOg)wBUs4+z^#-1 z1pn~A-?yYs(|#wVA;VaAE1Ab`hnVe^T{PEXiRBJt&{sXFV$g7=V zf=be&xy)Cb-SCm4~aURKLF!N+XuCxFAVbRIU~obc#Hh zmz^*&KR7@g>@qK>c8P-R0{xY$SDKlLJo5&1(7(cn0WESlL;J%*qb32ho3fYgE7l)a zepS;xP-i6e6#RBulHLd!?PKa?za-G{>H))spW&g8d9_tppLn`zZW`U4?p(EUg@VI1 zzPd_{R}|GL*XR^?-t5}M^#}}-(lX+fmV9QbUcTI^c$9k%btW>Plb@KE6Zn4nyF#;w zQWYXQ{xqjG3c=w;C9{cv3V4#}P7^{n>mIpA>(;bPi809);eDGtz1cc{4qAcu5=EUrLHUGg3_WAn<=d$@L*I^=A~W^cc+R!(#^zK zVNbKuLHDBBUr@q{AwEZHmprc*1H7brWu%Vg)nnW-_)fkEj{Cdf4XE9>uIfy$-;}y zv2cddHvB}!WLiO}q_8EuC?*}jLv@BP<9l=gRx}Rdg&Evzb}p0E8@x)RbDMNGTzr|f z&&&;qF>$nL2DO0VomfC=#g<<9g(_BXin(x9e32$x&^=pcXg?o!C1kC;@2#n*cTIP? z8Nn#oY{Hgk!@IUIq-{uf^AC!oX;j`xG}a$f8M^F$J@vbveg7WiuY^{tU9o(5TG|=_ zTGLRiDBEn;J32#Ma*GLYXBYe1ov>%O*-4*thl4jHq0S^h+R@o=c7(gao!VcfG9BEa z>07SA9h2QvG6itboYrS(FdAH)|?$&4#Vpct!+Xzj2Jotm-n& zDaUj?yrW%wo-3t1R}71Pj8xX6IS~h?cpGu4!+FUAr&Cu%V7{ zXSm%-BNyYb%{IM5uD4U)y>T)#VZ)Yf9vuF#k#m&aCIf~x7mtwxhvGUx(j{F@cvHRbN)qMkG8j(?Fo_}1-YYR z|CWhNCx{6RRvP|!fEVZHUT)W~q&47kbpO=l)!dqM**#BoSkpSiYBI*5WD^;b%{5*l zq2cKVuA`UWs^<8fUm*^=Z=Cb`z{>tbCLr-Vch1y|jh0)aE#H6u|K0{1x#M=L1cJ2* zcP4gq!`khE&Tz*nB$nS&czFebChG{bIoms)-#U&CmixtA$OR+4tlE0X3zmmDGWJOx z-fOD5E{!DP5X@2WsT8;7)0}HRlRZM!AK((fQ(kXoWQg`72A%aBc<3z@nZf=MoG`Ic z2t|zcnseVO2hWRprM?!nh3gqrW_1$!+4AO{0t+QYuG_?44|Z{>+;FfXqn)g%e73ew zv*6`*n^38({n?HF%!DmF6!sNJvbEo0{H2(iC3PsSLH2z*;k|8Oz)190J^Ph6t_p!O@pWuYkzR3&t>qOI<>xI@YOv&3*jj1ncvCsFv~b$e zQXE62x5UxUP#4H$cJd#4C{WADF>ab zm;axD%G?Oi(q83GIEMaw?T|L5IWxXgiqfOMY4{!n9c6bL&bI8wKJR$T7N=WFTe0o- zcYo%`Pvu_hw-C#EsTj=_MJ#eGnk&xu4lOM;?Vd_;gq7u$yiK?*+ztyfTn)rXatE5M zt*tB2z8-L_b8TU34>t>9VuO~pwnsdf&IcLnjp_ft-D@mdKjXk2UOS-i@@3_x4ikUl z(BfFQ?*;K^+9_SGcDJ2A^vth*^~;BU@TUt& zF7ez;v9yeV?&p@ry^h+?;pUqJZ0~t~!~o8WddfQr_WLQG-EyVfP&Ljd$fyaH8mBdY z-I}q~*br?)pVx(+^*3&|w$92cSgCHMmgGh|>ELFuN_#uX7#p;@RZ8sKDKNR!7N`eR z^ow_UG85k3bh(@ArF4&^$qFDD_y2MrrS)Xe>p1;EXOHI8Zu`w;8GMF&h=!8+Ao3&A zXx#TRPbybBx>;wSeoHQP!Rw<~Jrym4#+jL{Z3QYbbE$3Gw}`8c%4AT&r$-cu2nKAam;W7Xs5mUk+)yf^wnS1 z&h{yQ?L4*mA^;zjl9ZcCtExvXdZBhpbc}XFR;@zlY=8Rv_H^w;y5&^Ggy(2m)GZjp zovUG6xkr#RiC$`vkf=GCyKf>oY6>+wpS~s|!ewy+FL@ciAoQ#s3ftKC!5VIa0xdiHPwGTBQrC@pXs{eti%r8WW;6s zg|AFZZB#acJUUw#1IQ(sw0ymAYmeqw`>ooLnrr1&lhCBg>uaFAwY9|y5SubW}ZUmxWq?y{rD56Q~flH)8Z*h!P@r_AEI092Vxp-zLSYP+d=#0Nd zv8=Qg3fAv{b?ZqMyzpyd9^P3+L_rWmq#FO#QVLCh@J~GJD3nD|}@U z$SP@za4vqY%=;XB<~nLosh?%ySAa^6s9RAu7GCYUz}>jFNx>zht;X(0e5Grn9rVhH z5Z72#2G<*zr|_@&GfOi9l1Wp?MJ$t(C%E6|uI<$v zn;+{5g7+$pvG!Bi>z`~iWafhP?mrp;$U{M;i)N=@C?1~EULV_C?>tpm#h$Mns9JeR zFZC-ikoZCJFTu*Jg;1<)!BvJe9^6(Gjw!Pmf!fDll#f+R0#(?V z#Sj+EpGv1!uj=WD9jY;hiz5M^HLo^y7E5e_MZZ{Sa=*!9Y2$mKskHrLiQHL)(Pp{bFjpa?0VU*Ax7`m>`Mt6YB+MJ8UAq@ZA4f#{1uRHeQ+ zQd-ZHe|`V$z}xz}ei;@KQV`{(`LL=b{Q;EUlPwdys>U2U<;B`hMCazl=<+aoXkyKr zX93upRdzKd=|)!5vF$HeT%&rBVlkU{sQXlc3nz1%&2=@XArvobjzs;kNw4mS>qj&6 z8}^$R;&5AvrIEVEWDu@GDpG1-bhymG;u?5}%ar(LicUB^|4tC!{49Sds0^&MCQnO= z0Mr!G0j+-rZ%I(ggqR2uOTP|7{TO>EN@^Chn(5b2Z>{@VShGnwqwcqwS2*=1``yBT z4mYJ^WLgniStQVi;c9NIsS^pUovBN#udb~xeBVe$@>|MTEC-v1+$0VYk?V-8FZG>Vbd2WD06VD zAdO_8l+i&o9g9=7N&c3sxx}l`#dF$$-Lz~9*NQJ8MpbPa;a1Vt^)6@O54Z&RpIZ)h5RFP!!q|#du{gHdgr74zrTaHO{obo$R8-0x7eq&qB(F)aT*U@ZnvGkq zDq?0(!RV#h`dU7!YirrsU0qe3_x_AbV0tsI`_e_}V1pwrT`eF#T|WUEhy3yw*%JYJzgs%8WUdmWMsAj5MMOVYd)uN;l%$(%$5_O$ zeqnt_X0n)R&Wej-W*ywtI)M!TisaT~aGKauUEfe^)+fTjjMSK^4%ax!K9yx+TOHU| ze@60+|CFpvJ?m&Uq07KboFsW0$sAjlhohL|YO8u?7(MB-FdGW90p>CBjk(-}`=yjz zg==h0ilyTpA&?z)l3I&uDBNQ4oy3OJ7cHpqSo3ERetdxqf(mTSb@Zi)6_NpswPI-? zQd516Y^tr{ZfmM*tE*~ij(=^_OolTPutvs`kDTG&QC{_CQ<7XUcGqlQYoQgmzEU=# zzuXPiyY-=K1`*tkRvV&l$J8vlFwz@lRFj(hNwOTCwyt`Bv;<4iW_^s2Yc006Q;K$? zTxqS^g_R!*cj_8&E5-(vxXS`sK5AKK+K4QRjVlAA_J3wL|q?w7}2=hmJZ7U-3NTOQuZWZFS#)Wbp6Kk7cu z48rLsN6SR-*;|gJoUB{Mh{Uh^q~^W>{nt)E!b55xn=(`Yg)_!colBx+0|z7+cb8mZuzH?2D03%lY$s^tW6HUDRyW zW-o2SCae=1|8Ns=6vYbFR$r==U5M2uzazn{Kf_EX4oG+2{P$||>fzdO{W`H{z_nQ$ zGpz8Mo>b9Y+^&t+5wND`FHB$vUc;?>@;sVDzxQ0o<5t()E;S$v@L4s!mbIgjBc~K# zbqquTznDFjBF1&`)?8|5Ll=`Rw@U6eN#b<5)>@}rvRShXMy{#8SXo_JS^m|nA@0`C zT6)KZFIAXT%WBB%2qz0#SUC-)C29+Y1EocgW2sjZHSxjAC~DT~ypv&P|9MV&v4or3 znU2H0Lv^v2xL67AWrnQnSIO?`SbnxW$cX8kx5Lsd`$(IJ<2UCom>0v=<<@N^#u5Kn z+=@EsnHH!p`bF6N=$AH*WQ5XJ-?dkQM7eF{V$XV#{%+PtP3HO21q z%75QYx*~lvV_DXhkMpPb8=}3;nl$`S#mdU-P}q|!R<^DfR;H*S;PvS^?v>;)W}pD& zE|1=@ghr-q|0Gb7eVh|;wkCb?J{0u`{Y7eoru$Qh7Sd%Rw2&XA%F9zoHRD&j?65Zp zs~~L{(a{@WwbY1S5i5hGf`QtY86tBxT_)Up?%ah7<|U^Xq>y+XSZ1?!d3`mSy}J5R z6^q}`KK>>zM48M;`Om{>e8|Eg_{tQl7#vZD?lYwi7@900;od1rXBABbl%;xUn#hnZ zSx_P6&ps)Ael@5$GXKbQyr3$73RzlMSqS&*!}lq2$gHJdjJ3>R4tvvpA2Yp2qDGah zv`X7n{;sZe@#>J7wxZ(P`HNh#m?(Ilrh0ufo{4fwzzIWC)>NJN{*CGBgBhvmcl{fF zILn{E#L9QN)cNyu=zOzr6&@TBq*XuclnnG7{MarBDZIsPmqmauPc2zw(!EmCR{T%D z43(6HnF3yx7IdTI^!|H5|H0w&7%_X4KddBLz!nAxGNR6?1iZvW@s?>i|9bY^c?=o1 zh^!0jy6Q_fpEc<6Dn2e&9eQAMMn-yifNS?fw>yiilKOW>cC@5#p zc3}*4`p%zKoIMX$oIA_k6jm4i27O@?Yc3Jphez5BuP0*35PFHO1hANG=_!^$RdKFb z0P6!S^5$F&c9UOkeVu}P!I%K9%>AE*dz~FsMbZMD)Y-u+f!}XM#n~G-zdl#R9pzq? zeg`u0j54oDt*gdHRbM)H;rI`3Aq|@jNO3r)_;CeSeBC4E=fHdJ{Q0wI&SU=|l~x_9 z!MUkd8SQY32HIqt6q~WrMEKmn#mDaOlp+MC4J%z6GeGI# z*ecWNGr&K8UiiFHNProubYjmgndcJz@OI>Qn!8TPNawcY10pQl_MOtxXV0EFQ*q`@ z=v>m-a~Chb?dB!3I)!!45`GbzhMJctr;V#;vHUC*lw^yMkLQn%x%JkMnVR;|AvVT| z)20z+N_4H~ZU_IDyfX4jM$XWqjb)Q`idq!scMyeJ+qomuP|7W4EvVRe^@OypHg+&m z>|Cal=PHn3W$VpL;mYtO2ZTwjf;Oe!Mw3GUAxAi42FE;5m&|gS^LBg%U`C2G+i%Y^|s0v z%FZhpq2VSH599mj|6;?U{!M6O(M7DiwStC4%8Dx1CTPp?4X*V=CQw%TJD{fMkBw_HKX7p%3n zmde_<^lU))+R`_kId*oig4#DCNe8ZlnxFdJYpMOiV}Xe-uyvc zmpm~{ahj%h?<^H0e!uG1$G+lrX+OJ2(NY#^;220DK@mYbKi*keG!P%aDu*bgSyxjY zufp{?r2njWK71kd;w3o#{KfMZp8Lq=0N_LENhk~Xm`q>y&L>ZnpFDH=Ui}o?X+28o^vAP1oO^w z#gE)!^6=LLwwW=J?#%q)^Cv?mww^q7^7x4p$NAy(nTiV0uxHNU6No=SOn{fformkg z4a=pHL>iJjB&a1AvQXO&!Aqf@aO=gd;Q7s0MwE#ZT_Cu*>=yBZHa#OIG62iVquKee zBNxsyIyZfW5HDyX1C_SG{leV~hUJVXMD>XmMre`}*dbr9Uq5*(u(#~F#711U==xCsLAA5Z9J^t5zMVJ*cH^qr zt6pDQb17ae%yGk+GiHSb=p;D&#m{Y5*BpTyNMC>J4@!=oC_j1p*okAuPn917_6hXe zDf4uyn8u2;7tV_*G0FLaD3ONiwuq366G&~rVsjh!Ah=&v${7Ye_QV>uroC+h&98Cm zZZutph)*n_zIz++x949XxCzq`NYAIjFSbx^ev7g?Ps8k-GevijPSwAQ^O8wFc4np` zF_vzsg=qx!`e=^K0m8vtr5F6Ti7TbA9^y^(MK1biU%} zuh)1dzGKtklA%OFPC@lz*Y0sMip6&OA=M zo&GS})^v1Y z;Aa_*Qgln~{H(3$A`}o$i1aM^^Es2h)Jp^_ADyz|6!&=gMh?r zasuH#e(Y%JF|%xK>9Mk-Wu;{bKVmPyF$)zA%zfy5ik;sjA~x%j6z9{M)Vg+7;=N@N z1^<#^uIbjF&l=M74>XUmwVW!W4V^9Ojc?)N?`kvstxClY_)+f)cX9-G2O52LtPrmr2n?NofA_YOk2HN_`^rf3Wx#xIUNm+T>v2yd+Z6!w)Erbxl4P|9w z{7#A(h$LY8uzh0s>~L5pAtef!LAiaXLBv^{oroGVc^tM_+^;{nY*6NLEO=(N8{1VPF&$`IT%rYh!Y=7lu{f(Ho7J2+p$!LpZS3i?rW7f!7?ynjCWT7S*G6ei7}V{ zV;%^$DBvUq74VxQNo^TFBRK&v0fJohABqylk5i1(*QbZze|`vpU*o4v;RUQfD1Ft2 z4?M-KmzS0vJz7#!a;&7J+$=GVCKX%143#cDhCW0bPM$tRFn}FB@06I?ymXy7KxhRN zA{A;$2$S6x{|8@}e56&OlQ*yCRHB-hv?wc#X!=bBJxUk)ebbmc{q!B&`=+N1^`u2w zOr=0cL$M;mEu=Z^+`gK50vVJc6l*|)EQYW$UTsca4t}SIfsg?UfE+LYfFT+;-|~&a zC!xa8qh-Z>6qlA3mq3uBB7Ra_0$IwAmL5elzzoVfhE6-qofkLiLio}Kv5e%;L>d$w zq#3tO!k|$uHKkM~4WeWS#A((X(;R8P9+AeGF|~Sn6m~G6$2fxU*DVm_+FT#?E3^TG zP+q^S55hw`)OaPkpu@{Fj3Ox_T{hnBu1J?n!{p!mv5&5}*U zB}Lr7d=vq{xTvV)XhA{AQRq@qQd&}GmM5Gzg zO=(A72@(WQu#M7zIc zzJQ3wSZMTyVfB_PHa|$lOi=^2h^D{i7xc8%K_nu8TAW_5-TAuj6=DXC^2z0wfxe&+ z^d&__1%*(hu(+@QlE5C&;aGX;F%b;$l`xpfPq?s3GB7Gzs2m0H6U|l!?+a`*;T1wi z!r*1(!mH`qbL7dFfm3bGlF>XvtC1%z;cYNkwB{+noZzwK9BMJ%lT148BH=T+S@CKYwT!Ls6 zi)^4hmG1yK(2=C1ud1q^x8m#6dq679A}tC@DI^Up0?xDlCFmAQ6<|v#t2L%j`Vie#UpOml$U1MARtVa{rHRXy7o{<* zF6l*9LThU*^|>;A{nn5FA51a!8&j`S3E_{3x{-sX)k!%LqZJtC3gQW*60j9xCEgvQt?Z zubwz9Hg!FMvufji{SGpqHc%aC%Ch3ZWnxPU^Ybwy`9})OBg^xTE z26jesEc^9ny~?xtv94hPkVza3NB>W@z$ROtaw9Vyip+yhM4sJAMJg20sLUqbVV7l8 z&znsUoX^V2-E#)%&Gyk7>(^$y>*x8H2;6EkhnNyEqT!Nop_v~p3?FvnAIS?HaU9Oe zGmm8E7aYlhIfO?F3bDJzMYvw2I3@^8_@su!Y>`S8^q{&`BaiHr1KQN0y2Qk`NCIxD z&$6?O1wdlrlOw3Ezd1 z-9xAltJseJWrgIG7bA)I`5UbnnuBT$;fTKJ`$tshjjvS-*pkA+qWrx4qC1Q6HuCe4 znB2qpxp{do$)UU>@}F>tuuDElMMMj|L+nQShB)n%rB1@<6X~3VIA%?v6lm+|gO&Ph zQd>vYD1DJCJi#la0{Jq;F*V(_yNY04{EjJ}ozvaw{`c#KUHYebrJbxtX%fXLA#VUK z(Z*Cg8me5Us3M9Ls@N49yi)Y)rQe8RU9@&Ny?(>G*MDm_xrpLIBJ%uVyl-@H{t>iA zL4LAu#gV+jN6g$UhjNbO<{aju;K<=z{yUNjZTMZx5z40M=uxvQmE`3~k~k;H;gG9N zqU-@xETV{uYFcS}S9_PT!sMaERkw6362z4JI1=R4oL}jwq@iDfZN_8_ug@{`EL3q^ z^Q5nyI=x6vP|+hi#?Gu!79ik;g!`@$#Y(hdb*j)0D8cHbW06L)c^kMLYuB#}t#{q| zwHJv*@Vko&aX*TR3-Sp|it~@;=M^0~6wc2$3`KJD4j;iLgA_V^nOp z?+mZsx^De#UwHv<%%W9?H%+bO^RN1U(KM$T@siedOk#ZSwQ;j=*O~9!U|0q=rs7rR)_v>y%`$S-D(Z zY+7-Dvpf!>&0GnQrCB8XgJyY2mXWv7@+mbx>V2M3Z`hYm|Ja=_*O1G3M=^;IXd>R* zWqG>Bk97Ztea6F|_Au~ij1RhAoRSJr%)^8L}L-~i-#_Bh!{D|zPZ)-21E;a`9h;%7ZjVn^= zOfC-v7ch@dsTH5!?R8vv?!7_Rh|96@p+Payy`%=1Cy(1JVQ>JaKVTI^Sw)yYdQ2#@ z-Rd1tSt6kfI){RG<;9;`a}XC+MIDEamWGZlM;uVSr35<##d!tA=8)#WEXS^`_~Y9}5r@2_OlBiwGZ2R^XOShJ*Lmf+`!)w$I4-X*uNG@G9wkM{ zoiHXn&G3jVOJa;{8!brz(Y^%_-5#eln)IooI4ehs%@Ws9d=Sj>k%A*I$dQ6WT>Rnu z>;ri@hYsiL-vj{;9>~t->%slm2lnqfa4`G8!2<^m=0FVS6*`=$_=d5{MeG+T}~dLeYyMoeGp)F0MoaM+}q{S$Dn9+^`1fAVTDt zl?V|Y`7IIB)bRiL>UWkpqHEmvyG~EVqwny$SS#_tY9Dcw(^6L{vd81Kl8oh!;}PZ0b#`?A0GN{@@7 z6t6xQB(0$N+WmbfURmHN`>w^kQ?m@#i&ROd$lT2<)_b3p=G-q|JCh!nJRO-48GV$P zenYWj(#Vo1-!%`%q&PpX@JOzCIDE*7FL%hy(Q?gf?LhcI!omG}4rH4L98kpE8{WTh z-`@Q>2lhj!oSaatLIBE1QxPUGjT5fg>tB_k_tP5tQ~-MS&uqDu_V zL_%qk2ByWR(gsyV6jjDO&P$KKpU{Zyb~}GP81U8khp0t3=ueU6#DF4-YP=mnnAow= z$AIExR~a`aUhl=n-jqSbb=iN%DbDW9BPW3KU&79BqGRk2@{?U~Bquiqw=VnOp+oyH zJ=r-2_ayDzcX0pief!OQ>-X>7y=U*<-3RvVKd^uAK3E77j;=BDb|Q!H8&sBLb@}lV zrwOS=l;~$t5w(a?UERL>H-;>dM8&Az*0@+A$+|2?5_t>fg8^jmv$H?vTelCg?AfvH zk?tv91D423|K^l>p)0%5O?r_DpK*LwF_~ON(3a=h^1??pnouNdL!76L*MGSPtwIu^ zun12ClAs`R@lv|Pyb-D6<5IJxarYupRWZH_~gY&3<<=UW$J&DP@ws5?=lqu(r&l(=OVS+gnx75 z0<&n9IH6Dg5**4tn45DjXCJ2HKx+2B-TMw?A3V5s&w)KlUfj3m#ohb&?A~kc-nM7& zu3bV6*a2J2UqLR!2n!WSx`>xgepWjU4=4j{5-MM;%>LTVGHNdq8m!+ERiDZ`V+zAK z3r$HNEKY>eiU=W(v-{cmcXY91)^XQAhQSs|!5|Lo{ ziEr&RGiRtlE>kYZVwKz5^2i4;M6m$RJs+!T9?m!@E*6{syuCT1B@ZYD*t0vlXQg0& z@r6AvKL7mgJuketXV0$CzVy8~BiS|`{y$~s0Ul+U_5GPi=tUArAQMp$5%t~GU3Xny zSBjt_(xvxaLni?eQh+4%P9VKkdKaa)P^60VP9Oo2kf9}{%-qlSJI^E(fp_=2BYYwM7^Zri$f6WRY3>^+Ix#*ORNY$R1#XIook1?sG}awr^kLjBEJqds2#)#+3NZ0w8#V9#}D(HtZp1k%%flA=5E4$_%5*9)P9Gw732T6kdI&N zYSv8v&apIlD1ksfviCY0Caz z>Q%|fiSbnul9S>S6BA++_$?_}CAp@e1*sSV9RVVTjhp#}WPyO%QGxD0PIAxk0e>x! z-6SOlHf`OsmO{@aG#Vtg-ygNZrCK{ z4?SGisdhQ64`}y}a>=V*P^VCgfaD)S!rdiw4naXS4ZSfN6BI7+Q*1G1hBv?H=I{@n z{m|dFhqv_XJ^OKv#7o#hd3$9$zyHpiGk%}Ax)!dJV&d{R=G*dz>KoJw?xkBLuCN=i(@7y{9X z)vJiDwGB>E`_a9s{`6m$4*Vyl)oMSj!>O{j2kz3}&6gAUg8pv#`fq&qVTcv4y<601sg0L_MeIYoT?kZ^@(8% zgQg68!2teM{*ZcIZ_S!b>qw!b{IGV#DtR~0PF|6^A~hu`B{?=JF+L$7C9ZOOQbKHO zTwF|iVtj18E$Qvl7;vItNEszLxVmsSMwk!~ zW{mGEc=+m}Q;Cp2LDdTii{JF@V;0kiL2gCn+ing`eYYdg)`$0ab`b?UvGelaB3vE& zTCy?|zb$Z8`g_H{zhTejXSI0mCMlvI(VH;rq=Km_d{~0n)RoC8D-%+Zk`i!nJU1aR zHZCTrd}Lf~OiXM{Y@CX9OiW5hz{)#Iz_Xesuivl^6Cdu`ra{djUMs3x#@spi^5skF zs_XS0mKP{xj{f1Bc5Tni#dM>}3&X215)O^paOoP5r7a!0cksgV)M8t)(_RP`c zKPW1Rax#{EVSegrz{fv@ZCBN`7k2ISz0}X*5z9>kG8^#clFk4HxeZbMiqyE2)Py+v zd2(W0QfyRATx@hqTwJs%Ix12{zZ(-38y_1Nn~;p9OBT_PppUxCx{$H0-mg`wX3fG{ zBI4|u`pj$pMOJqTSyhHu?6zIT(@L#ckt%-urf2~n9g7xNFVCFg=Fr@KM~?M@993Zz z(Zd@Q-{iADd-bhFpP9}1t~O-c=JM9RrmQcuXv^ljJa1|IR+5wTAFoM8H~H#hb*nJq zsVkF`h~E;EQm|sEbX-hqd_rVcbX;sybX^`D9TOFWu*b*6BIy|Pq!j)oKKr6kE#51v zSy0R9QmdF3+v-$(yI0a#sU)$Oi)qa3w%lr1@<+SvAO1lc0v<(RtJYiplj1T+AupY$dMmzJ#|9Iq7r$Vw3M)N4~qW z%e(zjemnf8Hfbf?7b1(IV$5x;-{X5HCZoP$yi=26<@s@miG1;x*vP2ph_I+Ab9hX2 zWK1N|AFsoZfNf7oNlltLuu0t-PisDf)|XdXt5e?fyIF_MGOR<(%d8KZGlVTwdJxEB zG)12_$ELsWtN&p!UhMtA{KUsoD4FcK&ArWK%gN?sGqFN;8UZz%<=K%wU z`{{E&wt|2L0tL0Je;v4=8N$Sk`Wkep*86+7J@1SDJy5bV0=ZN&nU$+otWLpR6S)(! zB*cpVM8zer=J2K8 zKFaiuu=H{U^fu3jZG?Zr06f$YD9UxnSoDcov3`VA!rGee^jmr2_|e~v94sxv|G5qN zxf}HyNn}@}@yW>qF^PmJDG4bl*z$zrn7HJ4JX%a_RCr`~WMo))M0iwGq;q&!WE6-* zVh`ft6M`rF^l8n48pSoC^zviSA74k~h8xxaMoGZeQN8^RD5z(L%KR1V*E_<#9<=d`B2P9upE6SuKAO}P0aJ;|*J@WW|KPXz2;E+4JExe< zW`~Rd0qtt7YqCOx@J5zwWCl0k82et9xvX2yHCfEp%XI`<$1PDk@8(B* zzLmtrxUs2?xS*K3;aY9i_ebs28AKQ_P3rcgYhm@rHS+Org)Y@T=sth9%q$+0 zLb=Q;sFc1F>1y|c^?A5A%9N$1H= zba$Ua-IMc$o^W_Ei@Pf3U1@Sy{xM(@wP z$It7nSh0qLB{4OX3@s@oK9-~BqG+^KbF=hmy-v>lQO_wV3V%)wjR8nEM4iHe9@Dw!fRc z^luCW9-RHA&SH%?OdJt<91%`(d|Xl@0c~8YigqEsiHL~|jfz3omq&yx3tbkxbW-=P z+-%OK>dwwa=jzJ&DJuS?rm9u`{dQAUGM%%3zwN;1!YPz#gyZawIz@*bQ+MHlEasHR z{yN{D9_vG1o}DcS$hR-T(Y;>P<>e17#`Ko=&BkWy3i#%75m0EioNvV7%UwVlE>A#U zgeN!UAH{rn8+YH!zB^(Mh5n1KC2i*u$C!~l__J>8N^i0~H+B`ft;D|*>7*pYtHinq zvC;7fanVuH@ewick)e?h5y*ZB8W0>fy4`0r^PNr3)t&Qre16SJwd%FbWZOLV=7UOSiirk)>m8-Q*`S(3f*g2(j zr0ShwFy!a6x>8`!Y{u7(rd(%fSVxQ5RM$7-vE?Ri?!Mmql2KdhBhVlMC=e0I@TKo8 z5ZLNeZ0K{Et@)Q^6tRwuf$s1Q9W0qnO8Ik#@WXkSd}90P^Z(B;5%npSPoC__x&Po1An14H z-lPvVEC$~P2*HdT3m6nX%*NY8_0lsQ=Ge}5FVNqw)v5YX;HB%=q}QhdaK;60IZjY4 zVVeDH@~eAF-yy6+`tf?CH~73TN$p}|mWM@0`1Jg;cJ=Ba`Wm0!dep@81$0|Uh}DvA8Dh}_|U%&BpG zg&SV?b&8H|6RaWvuQ}s&2b;*Z&P{n>HvVI;ct;t<%m)v09uz^wv8>1ygDOOXQsQE= z^#cK>I=z$z(=Ml9*V$Zp)BNli7q;FJ9UM8Fc3|ThdVj8c6XN5OaDx)tMn#53llDY~ zM27qK`Py7m-Rf*~0u(HQlhL^pmnYS=8dcrC?jICSGQ4`_I-#JbP30g;l5}m`VZ%-s z52V=+ny{AY*k4Z^*ZabHSbYBCMOJj9AsjiA!wv)pkMs6qKSPG1x6Nv>6~5B=D*@*4 z`wz{=pRQ$FuHzc)Wf39MKE_JOqyo%Ys2Ch{{=H3s+Y@v3I`w=n=pDeTc5^Awf_AdT ziQ_EuVSDF7E_r#habdj0PP&yI^rAPk{bM$WOC+NfGzU3!GlIN$c`4#rr-o}@AP zsU5A!-1G4hkc`=R2SXBrA|~~kx~^_=dGw*vW1z{~n{!|MYw1#C?KKi}%VHS+&}^6| z*HH3-M0M*3}fHJ>UK5QN&?1Z@hkY&QzvWMP3yt5 zU3z%&nvP-hzs3VYG=(v8h|sX$Q7!6Lw@Znwy5?+fQqH-cK{&~OD%bEw_j!?|8Y=Ex z8Yk2p?KQM@D3MSsq)CDb?Gy{UUnXHcXIlcyiW3AICB&^uoP+_7>)iI@oFHbEXv6Vw%%d8?W6-C zVo?gj$r`NTVk5(W{q|kZueJ{jF)Fr-gp|?Yk@E+BRm<5K7{sco>g91OPV%eT1F+DX zf2{FYhp`JutZ_n8T8oz=1m!r`1Qe*GSt8N_$&X2e-MeMvD~(+c%tni$CFx?rA%>O( zCh;5j08+%ByH(5Pg062qFYxIohpQWs%n=EV!={O6aSTIoAYHi=McNZKsp_Tl0=*aOC z$G`xljqR*lrbDjNiaDckXML^gl=*saLy_VnC^w0&>tDOPmGhDJw( z&*}Y{QyzxXhJnzk!BTqcRHlBjK0b?=Y0I?`74~n?0K1rYx+#fNm|+%Bo~9228j#TG zqx>S%3}P(zmtSDID6sZW!;d3%#v`nqoUCf`) z+Gbjehw45ymzls`J4^0uLV4K}B(P`*h$i72=F^zt`v^GP2AvjmiBS09z>16jhYb1& z9LK5?i;yS^>t5{VtF6V#3vnxp`hG4-Q5~CUBI;6T2Huv?t6G6m#TtM4aaiEO#ledg zla(7nRhSB|jhs-Jp-M@?mYxeWY#3TVvq5ck5Z}vO+-?(sq7GD*uteV(NSgA;?`PCG z7PDV8Tvk^M$jRGcId0!3QF|gW8TY06N0zAR6FoLPSt)|}t7&BwtfZoXV};s1olV{T zvDw%@Tj*<6LK6F&N+x`Zd|CA?{KrB;=zOb(ZdS9y*PDcvrQLE62B^APwZz5zL9_-$ zN1R!Mm9~FdX@%;~Eyyzej0hg}?;4m*r>9_o&PX6qRL$yC)%A;?#>|?(aQ=ctix(|f zx=aN-QK*Xu4@GvUXvD=+p-ob$6;tRwLfZhKO=^n^enuuY#L+;jMsCRCM2lpbOy#pO zOMQvU8szVCU6()eZ8_NDgbCRv=sY2Omko1LwUbQdWpHGf;6v=EWjpx%{n}M>80&-E z_PJ2VbBDCyh@_97k84!*@IoF#AQ zR66*zHe+24fMZ3SY)+4UdQ* z*N;SgRNN=hwWH{bsaXY)jfN)_1-bVGNd?cd7pANPDO3>IMX7#w(o339hS4nvYdI3C z(oMHBZmY-6d6L=equ8ZGqzjOhT=&o7BM9Mp+Tmv1eof~;6jS3WevnBxD@s;`YO$sy zhb_85)AEMBVdz0SK(R8SXN+}>I6kXDVrd++oJtxiJ z$eEg3X&%7LU$|i5lEn*`mRTMW8Ws|&!W|={fJVjDmm~>f;5w~Yy>62XEkJt23o&!J zbMKyg2Ss#_*p5{=i4!{gyTr8WLPJXZc3kvhxEUFlqFH&OSpf3{*%8g6^MhttRpFn6 zhm%3SJT@DA1(iaoKZOtOJ$y_6rz3K+#{)NIHhvT>g9&cJ{FFPiy1l{R5TTSv3%XQ| zDU&^K>Uug<#Tg4Gly-`NrEE24X9T92%}G^r{otG4lcxm)1kIQ+d)C~!bLP#RH*dj$ zg^Ly}TC~h%`O?tvP^5>3NOTO+yRKq*Y^nMh;45_ zD4-=`Kx9xdi}83P9iYDF1faJ|0TS;0WdTU94x&y}g%2L620*iK&b zrDS3{IFj^K*UHQbocy0rE9OJkcBJD&K*s_z!1*1u!tAJv&i0xC$s#kL*yYSj!KZTm zQ&B7(AlMMdPw^}G$mIa)##i5YVmACj{tPE5<3T>bzO+bi=kTPIth%)rH#B_UrkPED zNzsWwbzcBVUv#J&9&ud)RNDTx_V4+n)G+_KlWKJ?14;-_5vrvCNmZ-t@_EN`0n`2c zrUgx%7O18>BT6%8&YClC&b$Q+=g(iVWNGk{3c<^kEvL*D5<(}rVhoKI`l)oBX~a~7 zDJ8^f;l-#O4cLOBrvp0M`osyTwaL%|x<>X$GQdck6q#(d9B*d;mC933D;DgxJV51; zB#;sl{bwz+BV||sP=h3wwCLl9Pafn^s?}K&nC2p}LGxJ$2vFTG-m~2)V;@5$Cli$; zDbQ#6WkyP`HsIl_?%fme%t8lx_kl}NdV|dBTviYHoH5TuNR!Q})`vd~ojNrjXxdbN zU;k+V)BLBRbU`y_s#)dcFPJ}X-op8d)gr@Eo3V~X$8nN$5h z%h%uE&v)uHKmQqlGiL?NP_rxH;O5PrzhJ@QrHhumyL{QQ;N_uVRC2N6(TRy<6Dg3; z=+g@c4=$_PT2Hd7J-cKo=HOxCXq1q;1g#sf;WFev;Z9wxhg{vfaZ4gctav7PJpDq^Zd3VM^%q@;7q)KII zT!F7m)Cf|Sho!$TI&%a=+kgNTB4(=zY*H+O0L-pP??!`@>G5` zfGXTCIjF9MZv@H^hYJ2C_pTwZsmj0R^nhv80%y($ znmvEMh}6P`ix({l4t_6q$x>?KgiMi9vC-7j9zI3a#b0)DE1`7t69k)$y$bvn23b~yp2&KZJ!HVc=`tf z36i)m8etc!3zuw{wd=;KH*D#ZF;|Q)+_`&K%#|vrQg}c|tDv9+Et@iF>J`Kohs9_a zmYoHuVisEoQWlEG)EEV&BmVXJf4-rV3uPxOFh`l-^m6B;<^NF$&8rmBE75LK**=2^+?BcJ9#Uh7atc z;l`p0@_mf^DSLsPM&m`C5eL8tHz|7D&QO_k?%o5f0;z}DWy@aru#*fl&F~6ZvL^a{ z;5u`hO;}l^{1Pn`0~I~iG=XYzrWJwGGU@@8#GF!BCdoaI7Lw#5P>@VSqIT~N*;q+W zbM38XvozjpZz(Q2h*?+oy}EASjh_)XJunc}^Y!uZp5lc-`T6j>&lE88RerUmP6MkM z0f95;%$_-C?!3A47cO41WYLn3f|oB}78(i{C7h}N(Pv^(Dy7xc8`iI-II@L>FwB^+ z?t$GN>R<(Z@e4*>#&qxBeJLB4 z9$q%<*%re^F#7vZrcPuM>k&0G5hpO(Lp@{hU z_mqiVKHgs5J{Wn;zXqX0AkhPoHD|WuddSw|WsV`iOP7V&!cAB$75Aao_LZyFGd{a^ zBg1f8wo_^)0fvH}mUcjV6r39!WKcR&Ft{S@@^NcG!-5%mCo0d+*(|-qkWc z)MwdVfKBX`NY_cpe@ST$unl$B%uT;k6eIuY+$>drYR*pefAR?mni+ru5hF~Q;^pOS z^=dG2@}voqy(f8rAQ(>d_n$g_nxr^EGX$@Ba{z47qQy&9aJ%Ky4#^LD7rAMhgO2c2zYb3-<*JG@^chp@iyN1M8 z0@DE`tR%g817KSLY`mm7X9z##rs!Rwt31G>EX`I{hnDBFz)EG7(HF#|tDALolO;quRuO@i;2a*!boHJ+k?0K{25}Phr6s!enA=IKGwFoUni~lwu zB{f-3l$o3SS<+;*ku;etDhy0A0cR)32w;C8U+2zKxYlFRo9JTtEn?ElJ9qA?d(QzY z0x&s9DuJm)@vo7vwE*UK7*izymJHtBqiS2I19s_u0c>zaHVb4-clZ*SAKF1HHD30F z(2kiNY=PPCozgTf|I5~O384!EykES}>87_rom^Xc&zu@CZTi#z;?OBRUXwv;@CJzb1aQi`_7p$=sZ)`%K*TX<_Ut+HF!>7?FJ8J-Ei;7L!nJ5ErV>R>Mubu& zEZv~CyuX>@LpHPQ+$U>raf?!PIex4(btd1TpRBG_xOSbIi(ay$F2dXH-Lqw3(F@-y zDlSUx8II{6$l z-8t@DES1gjdTWSv2(Hg?ieIb?ZonAFE(A>YO(Fiw!NO0D~1 zJ<9^N{32@tr5R5T_Tc_B@*jL8;RM0gnX~+X<%l)5Oot8JkTi9tJhnP3o4*Qd0$gz# zp(>%6Zp#SiUt{}s0o?FX;H-pQSr48dbadP9_kZZ-Sa*4Lc2@CSM@EY%jNhuSdr8tC zyKXYhO;=v;y`r1gv0wG5zTD4iUTMy^5VQZuK*EG+fkFP$0{y1=`b_ei=ft2!ac@VpbJ-beE&v@&$HN$`{a}~tyXJgBXP)aM95t7;M z|LZY324>%<;VHAgN$L^yes&-SpzE(z+0Wtnpad>NO!^1{*GU~Nyd`k$+&)XstIOJ3 z`6FH!($Ji~`(!X#AOS@{p#KzqUoWqT9+M_|d5#|sULIq|j~_Q-{Dg7iJtkt&uxj4k zeuN$V{?n%emtN(f#6`lrP%Uj9yetHUuXJFj87HT%WpabL4d(tf$$|;;YZ=#K^*XcP z(w2nlCv7-rXN31ui;=MFH*M(-65dmQx~H-nAbj6fg-*pHSN|EV3`w(Z$rhK(xJU}c z^sV5vK7|6X3dkDnRGeJGS5n;iSl3GlF{C0Y4SxGJ8#~`ukL`DDUt#Y`m3?I`a}DQ@ zhs~Z5G;R8fsZ)KtrV@lrM4;3}&9nTtu^tn~jU79FtQzMyX52)N3ARaYlO;F^5>(*_eDnWZ@=MGj?R+NP<9J)Ak z=r`_~R6ltdhk2G$>-tOpib+vIrOR+C9^mdBU@M<3xDjGdT(4+7DEB_}Fhh7#f7VO_ z{l6n+D`XDt9==l8;>CJ-g4q|&Vo_uNy&==|4fi|;&q%TQRs#_^y>?1DStkyj(!tZy zZk7#hFX01}i4R>6;MD<=|Ep_=lkLCqp4h)8Y59Pp!DLBnG45|`!SAz`p1$G_(0J}ndig2BboIHSlVabDQV$AyCk(=q`LpeZ)Lr`~2M7HYI zho#C&2~4_NdXoa%RvOxFEA4cZiCi+%cS4;P?|;2FVa5#7$!P=>zKEWerza-O({t>| zG2_OJ88rsz#;Vcv$Br8}e*DCVcvRf!6fa+YUy_Bu83EG+XW$nwZs_Y0wX{KScvu)2 zxfCvvRS#F{#jq65a3{)HBHK%s(AKN4PHs|R&4mNEeHH z<7d5FM)wT>sNMX4xJ_z^Qnj}?d34Ga?vxm0yPdlTxxn$yo1~2&O;-W+g931s{@xQO zdQO@!cKo=ph~4PXB6=gnjvF~@^r+FJ$Bmz0^Qh=KiGXC%WE{j)o4*Ru=2W5#OIc&V zJW8-3ZwcEmE|y`vB$;7mRF^#v&X6#+vsje_1gsJzDEVmLbq+eizLI(ByCTEx`x%X*L=5D6$H4e4_*lJ-&u5w$%8Y#sL!f!Nz&`41%U zwOJnJ8h!z__qKwXZ2wbt9LUo^t>F!^c6xJF7dtuPNmI&RQ3SPZ_Wk=X5gZug8}@>a za1ICzoI%)6dO6w4(__r|@uNqpF)pLVjvhN^%-Hdx+}%fx95rV2$noPbeiJ;DSUfL3 zA8&6$IDb8Yn>l;-OkxqL8^Mc1{x>`S;AiSGYZJ;aHQvk}qkP9ng1mlTjU` zT1`=dFys$9LzJqy)_2W#ov^(U`GLd zOR|ssN>!-vCHxfl8E^mVv71BP)j1`4TQ-x_ij-6YuYm|2wIC2I=}(K`9XxP=;CIKi z#Ah@HhuQv~b)U0lKkhP>q=|H$kYk*u$G9=0$Br5`ZnV3*$lVC=8#U5>vQ6R zDZs1sGtZ$_DepvI#GjIfhno1wIL|SoaI|Aaj2=DGebg}b(IZ9-AMQSC`oyJR?cC4zXLU##y%fh&|qqF$4H zRfCyIIae1HzrQ03vVJFjpd~zxe}6u$>(L)@yU3<0IL69lRUgU#v^C$qer=q88Kq?*bzgA3>`jV z$cRxR!4D{L!Q<3;&BGbnH<>btx4$3Y1qMx@Icv6>XIP*XIR?|}jUoq2YCu_p30GNb zLwiobV*7&aJ)B*U<-vq`=rCty{xAub^OD9@A?<3FYvMW5({T z8SeTo)R-b}f7k&7bL5KbO~~$#gc9(8ajslr&bMgq4;hFw>ug?@JMB9E?^o*4dD5G` zlI|mS)NjCU$F?n{O**`WanfIrJ10G2(W<=t7r%gMe%@Y_CQtGlJ8tx_ks}eck)zy) zx$DrW;qMM{cON!vr2A-0;#h)ZqQQw?6imEH*#M8+Ld~c|(6V63vL(S0p`p>A#Zs%J z^~(UbP#ZUG{dvpQpV`qY-MT$&8wNT_`0*RmDXCP#2ftM2DpkX4kQ%jgb-OB3M-HYd zFI&fQSWv;ZtE28Zl;XE8Mes_N(FcIdH>^gW#H9#c6(%Cr+4v?-(^!jdU6@bolV0?!$%*aUVK# z*pOi(h7DH3jKfFRMt*=7mV85Mbd$yWO`T2=V5&qdvlcAI_R%~Ki;Vk#sWR!}F?Pya z_@+%H{#$?DAxAF=l%ZSv2BZGi@l#gVD8_U2gtbfNYd5Z5Lt}4I(Z4N*53`5YB_BiZ z*!2T>(MFL(aHFS3a)#Hn=?nW_)#jx`{Nb#MtR2U=;S3HxV={)tEBHd*UQK05;Axq7L8<8;Yt983=lV zh7KAuc-WvJh~EfzHPS#Rqm6g)1Uxml_7sv^n#wb0&6p`+GL*3p<~B(${vOM4?<$&m zYc_(O?Bdz6YZtzDzifOKW#vzH^3)0G8S5R;OQeqCh7LHcw0>_^F zSCP|X?a)hsUZ%wm{?Ke{e?xCtN!SAEn2NxY5+`fCRPTwK@gFxu_;h2Z2S%zCy@o`| zmoA+@cb?OWQVG_D4}IN5yr=j3Ud!J2aQq~1PmhV?#*K0xF?iTW+{4hppf_yLpn(JQ zUxNk@K@f)xQ|=!Uwu~A>ygXqd7EiZ#0RdByKKv~C86|4Tw%7es8rgbjDJZv;QEbgH-!}_2?lE?MD%PrdhBBPZ_zX6 zc81IS)o=SL0$8LNEIz_8331yfkg?Ymfs+UsiPO@JB5@6=qFiDOkHkzTC+PZm0DaxQ zjg}JbwPc6*$1V?^yNHgTPntMktmoJ2Mr!PeAwW@Lx%JpFpytE2BN#j zA<>>V-BF{*j~hpVHffTNzmFeLa$wMmz*+MaEMBrSWEnLlX=cUON?Mte%GNAsWt-t^ z5+AcD5=86l_;rp^+R3ENM?0D zqV#EWDZ?$iMCovGdH9^vQQyPMbIh30qeh~fz%_8tu)zZc4C>!+(2#-s`}gZNu>ase z0|pLKLkz=_vk@anHpY*ULX_uZFJFpw{(&#ve%EweBR zCf?6%-`cTlJAhF^AxLLZKn6=r1DQw}iLA~vvp3%<2xY1}Y&yGV(jl_~86&~3le0hL zb=zXxUY8k@p*OK=x}M790DF~4E*@L*@ezb|eyC}~i={G0IZ!uppV>WxK4wA>0{BGa z>?s9W%J&X?k?Fye%ZGFX?Ocye-NuvVj~+2>_^_b^2M*{zaLC}k{rmO-IQ~PwK7IQ3 z=|2!W5xSx7BSw;7j2gqg;NdaZ+ncbCE=~~Li1`cWEewXz7{SzL3^R?%O<)Pz_vvog zx>+`Dk<0GeOHUq&lP&+u=;9#$I0sGdJPzW@RY)G`ldH^V|(Ha9SAcMW%}h}&PqCrIiqDVU`G z`aW(BKT7U^QQNV7>q~SGup3WfE60633xh0M2baY$GgE!4=k#1$#Y z@2^ZI$Q^jNT>M^y>6mi&@LLUTvgXA5r&Yzic65tmy-p0@}+Qz z0QSfZ*rEReFewGgeDl;?hE#3|N#>3vA4wH8H7rNWzRdiyn3)hWbzQsK0NjMfS1M*` zDX}lG{TlQV`9rxpe&OhRxshYY6b26+Y#mae@4$h*`u6MJw|76)=ij}7tY6>=grNLgH-KKSbc9cSFXd z>nFrM%8I)YxL>7BSmF|3fY<=8R6lEdF?`hMVHh@y|4;<2Pk+?2SD!w%-l~@?+KH+T zz%fWbH_}~VywSwI6DNDweCqg3n?5~=p4Wm!67fcavqB*$g^8b4Ym94HFj`X|_F)i8 zYIOU#1kLFlDs<}i3a8KTXT;sJ30*R+>s54Gk+5a%+@n@foNdrSvjLj+(D(>SLn&xd zX?l$>cu*p2zGoLU>YD(j=^g=DD-GL_H9KU&85Jjr ztVO-Z1+lt3dF~O)jdCA8WKjP>L(~B0zWw_3={ulLpMJf2_UqlPSC5`Oy4!kdeg59B z?*JmWp@T=bj~G32)YviOC!(G-TBlB@hc7u6N!IePh{%Y@_!QW+v{YABU%QFQCMlLs zkJ*{V@W%@<`?#%ns!x4fko6Gqz(rD*kT} z6EX9>FEhBek0D@+nW+L6%-C`b17DCa0qk!CIJ!}@XZ0keugciAV0%_y!U++?N?9T1 z?!!k8=|8w1TG+RDpFX{M_UP54cdwqkd-dqrt%vINWsjb{dg2lK;z}ifpg1Ch^~p4N zfXsjTG;;b`a~CgK8XCMj5|b8}L<=HiHH*foY}m4C9h30eeiq%_t@c+xcqr|tzL-|p zHs{IaFI~Q9Wm?F1(@sQ&WORr2Gv>@5q!lA$4yPkWH~)`_F}uCM4Yd9it=|v32x7&A zxda{Q_3|)hFG*M#JB3ChOei!uE7>k#B~=j8s%K6>-(67blK0#RxSHKZ^dB&IVBfyI zdiU?$vv;>1-MjYe*}YfqE?v4?yM5WcXYZap`wbW%UcipmsBz;wC*w1H&`D~UGw04; zuqb%x@^DxVc+A-3RjZQMki-8CRtUT-0NW`c8cVWi*w|^2CqU~0DZB(|64dGrlY$~W zuDkRFbo(z7Cec~Ba}k4C!t#Ct0j;ub&2+^i8U1tAku6$?U>Voe&oMhTU2n0#)RO5u zVQgs-0;z3c9Z}OLWNyg|cFl&G{-s3dC}zHB*z8p}F>KprytoH}1#?{AD{0`AA%7Y; zV4&)&^=#CmXZIdxXSc51x^(Z}rBmn5UAqBjuO7X6_37Vl@Zdp1hEo?AHFmtmc)~Vc ze;*%_x!JQ8FJ8ESJqaqZGP9vEiAl+;*9c{ch0^Rx{AJtLos>fMrtQ`%=tq@)wblvK zc7cUZm!M1P#M1OzDx)lBT7nL0J$O+oftJnUaChURM#XMMJBW6d@CNG!CrMoCp)az{ zrndWX@9MY}^1DXZCbeKgM+C6`^_NYYMj_ZPf4HX?yy+2p|6#|r)w)k8WsV4Tr9Q#+ z%Rz*-{d@NB+r4{_?mfEp=-H)5&u(1-u4@<7`QKf-fnCp@z4{CoIC$V-vip&Ab|%p3 zpX}@HH_dPA^dM2!#Y-eBjfja(h>IiI1-n&DJifbbGk)mTT|1y9(Pq`>`|zYEBtbYs zwfe#pT*CE=cv2|2umUsmaJM+i0B|h!72DRg1mU1`U#HX&MH6ti#79!OV@ zZpd2K&Rx59>D*Cus?-_mx_0l?qeriP1J$6v4#Ucgo5qWsm6SRq%Sk!aZu`ap)*5R>??hkWwW0kf9wa zNK){Kf3CY_Gwh`85WqPo4+H$D5SxX4N?HYzE%68u|_X_J~X)6C08p;ZzgN^Q)O|4S0WF(`ANcTx@@%^WGQiSYFTegYXducu@ zh*72lFhR^dLMRE9OsoiRgsR8Z^;`Gu-CDvM`J%w3;CTvfe7nD`kJhtYw~i=dmmb}^ zbneu#L&uIkb!gwdW2cTkb?MrW~%Or=6em z5ts$Nq?msdcgk9C2|q-}O1fI2n58lneK|Az#?^yD8JsfWsY~JWd$g{-P|6-Xx}%hx zK&xw)jz4wk(BY@H9ox5Wr#e&s$8O!acgGQ^!H!fbhmTg{8hX%oo#Zz?XnNq>Sq!x- zUA~OHV3HP~l`=#p%h8z=+q6a7{??A2oHwT0ckswj=#|GgeN>jXLjpC%l&_h{2^2oS zLGn&@&svg5nYNE?V0#5GID|T0>J>jK?H8t1_cpp~cBtEHT@Gg&5wSeU*>|Fp$JFaZ z?CLWS(~~K^E-BQco!eLCWM!mZzjEDaMD z`*!Wy|J1HcTV$+Vd$hAFh!OSnA2fJ4g(M1V6FimI2b1B8!>XSO{91M!aB=BY9dcro$nm2m{$S7mrpUPqmoHthUZZPp%K*EX(hX>7 zI7}1mC6!~kIOpF*{~vgrv6$}7dhZUacBcL=DU}7h@^T;EH_S)4zOZGNm%}Y|X76O$ zGOCnH7nLa#Qk{KUvVoJjbcsTe4cyDVd;8|K%NI@yOC;Z=sMP9fRlca&wR;bw3IpG% zW5-TEb!Z1(?c20%*QRybw(UE#|4HEL(Ys$i;<=$?M!Am~OXt92@%7kJ0#L%=g5hZ6dx(1F%`w1Rc4~#Hav5a zHUKja*lPDpzx1zLPzsl(-mea!(_Jz!ViD=-mZwP9BX&*pG@GBgm7R!D zd}hIH8M-=_lsI&gZ%@_j(nXtP=Z+mYcEHY`oewu8-=+A4M7?b5E=X6G&bE$a1*!J! zI;gfTty{Hj)4EODw(U9qWVfC@d-v@-c<``cBh;vm#=;+*JjL6Oz(Vk1Tw@ujli2&X zw1v6}Jn6}XM;W>L3HL=`Gi zsBU7u%F@5QpT4Yd-v1|Bl)MQ>B#Jez``-++L%kuJ?q`(1X3YMi*j(&p*zlrO+A_+C z>6FoNMR%`+6QbLFass>EKb4bp=hls@m(HJ=Kl$@KP zZJTy&TeY>d(%NWkE42FwA?rzE)kh6>p>#BE+-MKld$8_({1|nazi9TN;ALug?MQaM zuyR0H;w%K+z^MlbWOwe`rR}Z&kLF++o4$TKVLM&*%motki;$1(VQdL#q?#PE!GbyD z9G>vOV-PdF-vH4v@FNjyiG00ky-r$8S@Rp-Hk6&=_safL!HBAA%cBb7i zpo@Re6Q`2SyFLK)g5iR^i!NTv$r4k4@!TH^hS^-oeBQ@v#)N+>7wdD2U$GlFp?WA| zJ6jvWkE*q5RjK8Vt$u9X2K)6>r*5jJ*7sli22ctb;ZAoRD)dCJDL&JK0%k+mS{S@E zG>i@&yTX{lOHE?2IfuKLmi}cMhc|TamP2;>k}q;ujO+M~*Xoz>l91wccs;h5&JK0EB^tOHRs&SPQnUh~y?H9>cIvU0 zA(P@PNzuo8X;zQ#Ws4XZ$Yhd4UVA|6K#AeHaQ1ZClX;rrF#>0zY9Cj@W_qnpO1Gl(i_x}A=nE>LNF$;09^laBs7o6-30I~HleJ5oy zyCOPdpZEJH$DHR@_TxdZR~|opQYhjjlL+iodJS9>Xq{!xXqTI4;e)_U+fF|B&HB+((bJc^D=t z?>hbg0ked8x;U66eNk~Niy<3IT)AfTO56I%TYuifAS@(ZVPMrfly(pT0)%F&*FeSL zH#&0H)C~h>ULTUu5GyfRrX%j}W|>*A`<3q=3mx;vhG$qYVfH4h1Rx0>g!Y$1@YVaK z*}<*L+8icbfb0?11q_Yy%%%g1bo zFzWzj&kTKydhIanx^T5_`NMbLeJ_7~uYS;4e*I(HwyoQD>D;AT_nv(vK_BK$gO0+_ zL?&MRrpx?_2)x!*|WU`}VtUzi;tF3)S-T)@|CfN5HyuCG;9HaLBNs?ve|ki!|k?O_xFS zrHhw@EnkjLj7>_2V|G{$7gVFR1L&n z#k(#mRtO@HEpd#-)bBI!{iaX74zsmPPFILF9;PYsCd(yL=>nDT_wpow_uY5u z)_q$rYqcVWai9_#wjaDYDfshtwBWwoXkZ^>yeQ;(J(vJ0T@@)HZaeeL-lsiIFgA5D zxs*2I$~(3B@%t8t)c5M!vdx-*`(3l<&D6JTz6Yz;t=qQi*jY-ly~QmK89I90sIe3A ziVXQrr;jvuft0GFLc*h&=8sK+Kb0ipGj>C4+yY~HC$!sxBtO3$J!(95T%B@&bbHZu z>8&etkR+ss>MeW%Y7!22GvgZHHaomKU~{fwL8kRTBely?{;=0)_3OQ72j`z*S;aay zy0u!9XLfK${ho`H0Hj^5FP%z?ge7DG07CgBkl(&7Jtxa<-_NWAps!rY*Q!?St+!m> zs#;Z5sq$UR@4s)33^fCvX5Xl0T5~}NS<(^e(z82Z{J=p_g4{=s8w>fu+n4^wjJfmX z)171s3)=@H5~3Jc;qn9kK@i=_<_r+pv6o3e@@e9$a_r1tVE;U{yvlTP_1d%ylih;4 zgfN*h!v95F*<|qzEBJ6E;Xg?d#1wr*<)o$Doz!mi>%CLgEtB0}VOcgphbLr+#+(RH znyRB@!$hguc9pY>ksT&DK6<@I#ApvQ&J2PxD0{bvMOGSuUbq7dw8s_UTg$4B!EEK9mIQQok25qC%&GL}FKW z;C4<4aGTN&lFVDYAyBx0s)@>pv^!t(`Zy`X6X z3XHDLL|w}8>88V+uqrw6G(M{n0ERa&U#j^Bb# zQ}vCZskPZp&A)B&?GK0%0Cnutxo4lg(u5p549e{2@e@30*#-qsmSNJ4eQGQ%XQ6i@ zyeAo~;5G+)jkeQ5+eNSTVA`Q$&^wROs?|;#&Zr9}ilqu_dl`7<=$Y+y8En$+@P7f% zr-7G?fT!S1@My}>X5Wk@@Z6*`yL-Y%#OYtHdWH!Rrzyrhy(zv;lwAvs%WsZTvjFfN}`ojF1#oC2nItnx*(Z<+!}lSWNcQ^Pm5W(G9tGc=1F8ALjD z>q#H2-v9^z?xV*@dL~+homzsrUPXsQOixDUtxzCwXq(IwG!~jGTe@DoA~J%BRo@mSm385{ z-my5`N$&FYZL^`B;b<`pMH6Qs3JH}8?;E^;XXT%*&j)sq(XG#OUWpFSj%~L}&GWWL z38sjkAOixU1yq75sIh8f&=C^bQ|VjWTFW0Py0n)RuWIjp0|pI)#>Ak+ zB-l%8`rl^Hoke!HER?nwr73oXq^{=7pG=ms=awzEd-v{w)+(fk6ZW9;tai2>PD~I2 zAgQoxw+MKnWV=F4ly%=bZDHXGZfvlS(7G>8a3uulcr6-!7g{gi{a%- zP&yzxeWwfzW<~bw;hgS% zxYEZ&cF^xL=QK#G4l)x5Y2$`=v%KAHrR&IiH1)ETO$>Q2snbiLwq0}tnNmx0$YR<( zw!vMq2G3?QON??Y_YD(DU@3Ly4fSC-yOAOEDJc#x4vo74m2F2#4e{>B36Ak|&?hah zQowXUrC+1QjYVLz#$}r{{ifMB&6=s^^}hZ7hacOtZQqg7aF3q7=`su*G8`M@F$s;D z9yDX_;suLrA@8tfj>XxW30k#s6{o=9Y|#bUP8LFG4koWGhCe|!MqewVT_{H$B1Zv4 zUT#X!hemz$*O_L=e=fW$-i%y^e2$C>kVY3=?iAZ?yX8eI$vUmFQnqq7wB@MO|K-=^o_r6^!3*W zj%sWIBLzb5BR4-%1^9`^OgFk$NX{_iW{l?)Z&vhB?VZEc1{Lx@QQ@+zAt^bLOB?IC z1JB7bX!an)<^eQ{S_uSuF*;c)AA;y0OW zUEIpi3)%ZpYF!R7s3!e^=jp00EIJf&L*)cjw!~HzbFtZ2FHT6`HZ;hXBhs-6JjAky zPttCDAiX+q)Aoq3bg)Hi?yjA?ndgvV_odb34q?Dy447VC^vBQZ)Azr!eXW6pCU!;O z`2PEETeWT14*luYqff7Xg9Z$h@u2Y&r}z=)vDjnYq9w~B!o#A&qM>!Ca6p#c96`+H zU$^d1yDI6lprc0*Qb#*!)Ax5ch_N-%&s0|{kz^6Wpgya7IJm z&f|7td-dp(gmTfcP63nhmS8gR!}7Jh5~XR>_#0c3hM>}%*yX$L+u()hX7=par$2}6W4!arnQo2{NSN4T=f_FI{Y+R4sWVwU}k9v_$8Lafs}Lxyf+ zjb~L^q+pMJ!DRgp$~DXE=+-|i7gTIG>AXjr!+c^>28~+m;r{~}DQIlO5BWBDVlFTn zdu0^qQZg4rLQ4@*=^u)nEd>NSEBjx>`;-E5Sxg9}>Wz5`<>O}SfJ~cK6+kA`EnmL! zS6_eqZ>+7giS~`HdE*v8v}{LVtV_3^^l=9bp$#%-l!up>Kl>GC&0e@<5e18oaNKch zJe#!1D%sz;1^c>9rc7AEp^j9CHugJShf#k~7u_LW(XKd1x+Ff?xUr4Zm17!phaVMc zMX~O99s`F`8gP_<0SZUcKQ0x-kEm0`g>kNh4}e)Os4uf`1y`@v6XlHtP#dlHMTW*S7hY7 z7A@#_wC~utQ#U=|9yD|q>GmW))&WeLs}@#T1bHVmG9o^SLp^H6`>VK8y>-JT;@X|? zT=%6(a-`EH;7u?H!DbS=jhA3i4SVXw__5feS(~FUg>$?3y;628_z3APZKJ~&MeUhU!Nb#Y2}K_JG@8OZo_c4 zYDy)Oe2>0z_uf5V$>T-FP8vJO2ZkTpj210`-W?jsvdC1{*FvsZy>SC`TEFNy44eEU ziIH)5T_?`i&X&>bCQ_hVP1E&Xx;}Je>3QAZr`3JVX<{@>-(RS`$zT={b1@2^8)tFM z^sQ?)Gbgc>4E$URRRUDB?i&?nzTQ4ip0pJWZ$)KerTt zDNV}RA*lTC#*GyaXwBXQ0tr6h>-Fv1k5baeF=H7yn$75$w5BWGrE`zI{e})2!U*<6 zpDD`!qnQil&07@Ad~kF$XIhg|lErau*t|tpPmnwJ95|wH1ZRVZI#q_q8)G0#U)044 zXn~X(H+OY?00JhrfyZq*1`v=fK@v$={tE;&f(YZyUJT%TpY=d=Ae&UpFt_yadR_6_ zJI|h8Y5-_;%hUH>*RA2vrwNsoi}GU$eZ1O!p~~6H6_;;Y`71qfZPx5ts?zPdcJEGO zyYG;JBgc&K@bL?p9t6V#a$!hhw6I&_lU7pWU_KS?-}LKF0w}H($ym8Wm54v5+Rl^{ zGbq(D=2J<`uB~maTpzj?$WVovDQdy zQid;0o^EU2g1R8(9gl9qF@@bJUWi?KBlc!uYuo2y<*R9 zo+{ZD@sX@mloOQnz^+oOs6;Ly^*?Gk(#~Oo3@#0S#QWt*Bsp2dmVDb|#VTD}zV_5s zP|S;qi;Z%uC=c3TfynC`WK7kybj~Oh@FP@tK&1NbtI+lo^yvHaJdKs3UN2LP4ZL;5c^{IMT%sUMxtcyudCJQ${x> zfl%OKq+)||H2Fr-D%@dfAyjqi)ql{yp-eta@R`O0=fZhR?Qkd|DIt07>J{s@3RQLw z*Xu+ejvo7iBuW8@cF~bLMAuLa4b}ndXy7PcDiWYUa;Sr0)B8~hFw|2GFswyXeE}nf z^(N0H;w3PwMaOb@xcrm?lKu)1wtQROD=-Kh=;{@sgmY){4YEq%?zX~&%E5LRyxp6U zNJ+}A5fXg%O|x&m`~FALhISq40r%+Jf8fyJRKtCzv$2n;Ff@VzoaB`&ILJ-Ccl*|D zd(?gxstv-fmQc_h0EqcRCTutAh+0sZdZrk%+eIIkwm4X@4_4Zk+7l598#q|Wv;~KJ zaCr1)9FB_++~a2tKlx~gSWCqXw4x%+M1l5Hdl7}K>YQn3?Mr&Ppg>jyuairnImWE( zg$dQer2^wS#d~n0MxcO+Afwb8*B4=E+WZFy7j4^jhA~W!X}}N+rN?ByfEjb<&R?=T zGBQ@B)Jt8var62Ozi$6^-_G5KpwBV;tY?t)NwAW#EBl&Fx03d^uNYat-bu9q44#6)L#^Clsod%0))(}`T73KhPv89P>ADsa%EMKj!;{C4 zL=keuRoLI2ar>48N`#6G)ttU4Z{JsxP%)a#>g<^H?%SBmpsZL&c|2ip1yO__pqjv% z>#p_wxX)mi6C)=0`@%0-xPS&NoTcP7tJji2Q*+w6WB=i_-;Vw!tNC>$pfq_|C=z^s zrUTNUA~S=_=0s`t%hWeI%F}J!B0yA2p02SuRTUQF9#ChVZqbZ~aSa+5hiosJ1Z(k$ zWS*X>Km9Rwk^kD7S8OP?14xdo}_BZ+V*C6AZJM;T3UVl5OIH79% z4E^<9-J8f)|Jv5rL?v5mqP}S*-Pg7q5dR)BmNsz2@DUR{*gP|1?tEWUM^DE_GvlgFx4neN102?UJFM3)jKkvyC7ReGV z-)Db7wR?9(?4gP1Z$En$w`j~hQj}1BuDslPQirc?jik)xAdx}EZ<;{?Y1yh3M4HSiR;^Q;>ixP?lHvn=bedlJ46&qte0APk(iODb()7`5 z`)9Y$5wk;0Vf{=*1qL22dXM3Ls`86@Jdq9_UjO{RVyh5c~leI)+LcA@`7V}m#9lP*wc0DfH1nPHFixbK% z_V+{DJsPWzia*vq(f-Hy$^ZQQub+PUx4(b!<(HIAXq-0xt|jdYsj2npJ(QyB2#WL` z<2`+uOlNUvAe-7|&8Ki;T(np%wJozPFGE#{_0%dtMXD%Uv=(EGjkCoY;#5L$qDsL#> z1;Ky>K@bcGB8noqD`HyH-@L|kb<8e{ih#P}Dme`q1zC|SMifPYfgFYm6FR>1|5e@B zJw1W$*X*}@zVCeJEXO_2zgKnZR@JRrx9+X4zSDyeE*t6?ZzUEzLw940!C=6TC#qr1 zT8&ois|r+Z#&c@>@gSWYZ{wWyo;r3?gMO>Wf<@UWY42Ml+m@4V-M zM<06VX_S+_^6DEX$iSkdpK^Yp}c!8vJKl~;C3K&cD(k zK+i4h*p51l@4q{M)o`IYE#sfh!6Ke|xL+?wrdQ9Ny?W`r9qMzffy^iblZ@lx~q{)*%#D$RZc}o{lHu%rwpyIt~V+#r| zwr8u)taD3 zLii*hd=jQ1)JDgqh8IBeUhr#p3|{s4YL-;*L34YQbU<%Z6#L-zCSH9+Ju+qKQFNC> z`Dn-k&xs*o{Au$hJf8E*bZuI~x(m1VB3I)u{V&V@JDlu#t zJ2njp)`Y+@zlyM6EB>7HX}j&I!g z`&WP0r@N<({mU~?J$YB(zFKb?tK`LQ@)cL0>TP(z4I}YSSYD(R3-h#oa;3nyb?|$4+e~&tONrJy$5)dhYSabA=|K6RwMPgBv6Hc z3p>E}vl?#+V74W+wZ{V>*e)4|3o5WQqM~%iww*g)yyo@^(dgt)jypN8fQDl>PbVUmNrKOJm1Rd%eoCc; zDCxI2LO>nJE&~S$5)2+GD@?F_2=33|*A8Iqh=ni_$ct&1T`#tYDwTn#pcnCw zpSH_Fua97$DqNr+&8w#?Rm!oTg4e(iH^bOD>Op5;I86+)@C4pZJbLu-;X?=Z?b^C$ z*QOD}h75TG{_XLRzKc`(^dE59putyPKLQuH?(B5W{g2@K^K;Mt4Oc`a;8A282293f z2UMdkUcTzH)hJH<8hs*vL{}kLu8zZaBzD-bBfi&Ba3}4frCz3DbTU~uT)rhvSso3c zEy8=sfW!2H2=KiDKLUIt0RmOf*&5YGYaRaLFovcW0sR;FJwy3k)zm!*1Oo`>NqKb) zPpuZc18EMkFucQu_WrPE&$~BXH*`?%D;|A(c(6}KU$|>H>#K(kA8`wU^#`6do^AWg z3$MTN`q=Rk&}C`{UX;cAfVfb#1gE6f%c5I^NaiDG(|^%_r7m{PU7WmD@vs<8mOH$0 z(fD$x0hOeqV+5E8V6ZlOYSOcs2-b+yhuZJbRGD4-;Ud^A2zh_McpsErjOD$6WTZM%E3dg8!>X!optu|PvO*#v{YNGQXgD2LSDS|vuIKp>UIPll{xtP)PB4g8VdJ~aJz z&4)n*FbM2CIOsy;U7b~R`oxi42X{Yw%ZMv_TzJ_PefnI2y+Jmd-;nEWx@pwi_y76n zr=CY{^o?;JCgK==&fGcldWXI;bq#8IaiEC0OEkH|ekd9DKq(N~g=L?1L#C70A|BWw zCr?`fQY!;$1qFe*NSF*GNX<#mS5m1&)y`@Rp$tDcfVdEz4r`l%3ZqyLsbJ9iZJ!K|Iww?l7Gbbi>zk2=yJ3~{QT7W;Nm8``+2a8$Y? zA&jp<*NVj^mpk!%p&witSbz877CGDU=)NHkn`dt3Jc40jPQT3U&9Jf5e#|hPg&9vXc`&+GeL+8>s8 z`8C(!;iU)v`ueMHPJ9P%QR6Ype=S3kr?r{5g#X=UbZXf8<2D44bP+(`sUx7n+PxR^ z%3cr$G`I^ucksouhBVWFmBE;N3ii1O@UhP`Ag+D-LX)YZ;8%epwP1&Uzvk4wBkw+N z-6bO~M^Ps-)I)C=_2;rz$G$ZQF9cB24Q#(QZp4$OC^$@kXkhoh{=Sox3~G+2*7l3C z%y3#(!-7g_S=4K=_oB-Ql$O=mQZ(ckG)$h3VB(bi0rT@4ZeV$lqB9iiP88zk#Llfx z-+I-pmk%6p=@o;ox#g}${`v+AmS!S1x&k$#C|`%U(w%2JT_n+WC86>WVu8J)01`7r z%L84K5RA|h4;0`VSl10^1nvW&44fns+OHYVqe&(s2O*e%T7vbU=J>A3_g{Pa;6Vci z47ur!M_(WN{^atdi$2@%)mPvANV$g{E?ghRBj6~!q^vaPBAUcvn}o|b=5yBOf=dT+ zAs3Eun(l(yLsY`aO=QYHV>6~gS8B9U_&9&n(choF%6T_-ZnM)m2ljNUP8I_>WR0-5<`gz@^ zEn9Gt2fY7+5bt0Gag0(;-r!MifJbETHOb?LSY%w~hCZq=TKZ^El{L-;D)=UScR8Fl z7MfhdwVujDdk*fwdjz}o?#I)|^w6wGF_ZU{J@^;`eOl4bXU&G4G&ur1g#?=;Mr+Iu zF6KZvTM2m4P3<1hj7>qYoA(%bMrfuKDA)<_rpijyKe1rLU+ zv~NLW<4Mpsh43*%Lx*yxa1nkL1+fT{{D?rjBqll_KuTP#0+CuADq zcbR*f`y02OAh}N__+6?~ci>-bs(g%pZSb!phvw8=ew=qsPb^Cum$*DJ=vERmm(IU7 z%wOx0E~nV+kH7x@tR*X`%3=M44P_g~Z7AQ6zG~TuWsB!c|8T;X=N)#t&8G3^36Ny7 zKRu3TC^`Q0%Nhg}EYIQT&r0)CS0uYR~> z-48pUx(CJ|EIT;vVEMtcUBCSD^Y>q^T3$YRyb~mJn;wBkbo_n#if?x8J$wv~Wa8VgnJo7!LL$^b4@(Ghjvd!*zbM~suyAGeMf-X+zO#+J+kk&AHV$9{Eq-~M1V*FsdmTs zdF!_wIOPqhP|S%?y>Ujlk?E~IeRTitKYjJtg2|1bl7X7Pe%nEyG<|#o=GcLqz$_FP zdzd|<#F!0)Q5s^tUfC!n$?hCKADGiVMSI&Mli{nWJa*vspBUmI(Wy2e1Tmw8*iHy^ zN)dxeW5muagh)tmIyJi@;}8sDV}v6*!tHVa;g_qUB2pu0070tnl|3ee=B6Q$NJv5g zLuzDSR7grBl?2^s!|1DJe^g9zBt~=DoOT$cR}Mr4WrabDrnueU4k#6LoO3c*2JcNxwg6jQNb^^OWp55pHvO(nPnw{C#C0wJR_V+?pL zb-+_oQj!uupFW^q@LK&3I2+C`3;ZClzj$hTT1twZWF+ct10&EeVYI49vIg@a3x@a_ z`2w>*Us#1Nq^G7PV-Wla1ws%^SpLAY3|xLADJJQO5>_k8T&-v=spTP|fnu|#52zKK6 zN?uMj^8<#|6eBr-6qr7wJ0y+=d{IVK%gxg1dAZ_48F-K-)nFB3OJt5nef-fsR7J!3 zZuQa`dGsZWjCyTC-uO~7*%Vl!6`j}g*q_?XpUS6i5nFnumTrKnBq%nyT`(2Tr`9Z; zS&*O4-=ePb83ww68p&>QHJCUDtdzo_K$HRI`8*gPxLWh^%)){Ke5>uS!3Ei1ES>a| zP0nO2_Qx{C$F|YOniLim+_42=w){x}x=m-Ku(I$d??e?3A&Yu0g_|nDYT2IESjuRLLXZD$*gA0 zeE3d=`e|NP76e87K;}-TOyR8_RpgPMX+Fk_&!nr$NGKL0WRA+hSCW~TJ=->|T{gR2 z4L*{lDMwp@7iL8$C3G@6E6IKmGCs0y+3a0aMjFl!lxf+S_y#Y`>_O^$!rj2ZCW%jM z|9;)F6)05FQ}Bu4-cY@PF`V18ehE=fP7ZUM>6NmQT3(HSo2bzStjk}A0M?euD zeD_Jf6X|N?YZ`tNZcJFzVR5C$62!y@SO*+1FA)_bC~gH*uW8BT^P6T`|M;V z*k-{;gC-E!iapzZtpL(VEPEexCNolxO4B8PI}T!~cI^Cp&Xf5GPCF(k8aaX5dwL(J z$!gH)tL*A>#K1V2(*8YwNUAgF-IC>aQLU@q2`i4Q1~j3MY4B=j2v{ zo|j#An;aTNqrbQNJ=G_Er=M#<&qwusp4dP$>E}BC9AAGo=Fe?@B#rTL)&tdFa{;6p z$xlN9vfuL?CD5IU@3`ApD^4)q*pGwsW7Qd`X`K)u`$m5(`G3PwsbN#UDiMr_#cp>b zH2kuJ$2)Dh8gSO6yCXHBksrT|U`GpjouiXTN27iA6-=T{@t<_2xSbAiBc^&a^3hii zhO0quD`yfZV#H6K4VZn^aAH*cB`MCtx<+DmCM4CHz}LWmLk)PclAZ7)^*&tx$FGyl zA!mC-YNCZ-{|&!(!FZ0OC%c_&=ykZR|8f4g8uXQH;T%DDg;k80fW|2}*3qSx^?lbF66-Kq;7FsXFThhk(|15wqah(Skh-j45t0B zxtD5$@;?o^=9FgS>e__nO+#!81p&IwPOK61B%^S@27xKX%|dL4<6Yi+=|}+bhPjcY z`F*26-D7C|iy^LhT*t(|XXKq+hnH`_F|2`9+8nPHq~5AI2?|oi7M;R#%rCTq_8MpA z78D<7!IOi{G;ro71qMjl!ZYLkVdPhrka#delT$IR^?~(cIsh*$p%Jod;3tStno@f` ziuHkz>xvR$6(b~8O(gHEBY3qn3?qST-2&6WN(76AR71ShO`|jjK%oeuTbkktDX&H7 z2S$_Atx7PLG*C#0w>#mbs805#0a;+XwQ7Oc1eRw78#YQK@K|H8{7<83ek+q_mJ{vv zgoMe3qSVxY5FZ!&!v^D8x6O24Nt1kK4+;QXFPcxeCSy5gpbd)5A;7q8Q$SM%t zz)?(S((21@y3tJE@M(FQRxN}FL1rU7e2AW$mLZG8d*ck?1KT%zIu~k{>}6S_C=vD-~))pm;(+Mqp>i`FpJ*SLX|y=G zkb z7jumwFMWgZT(c=w;)sRJf^~|{8e@H;+-RX8fq|?~oo`SYnbt^BZ2pE3g^hBfn35pk z8?$JE#!%S0gmW-IArAW*A4$DW)CO)?`)Ds^=@ZSvTzoRi6h<8nN3zhOjssud4A>?l zQ^hbStPwUI;vxDMnl<=BD4T0Kc+^!yWuYm30EtJiFw>k+nomldb7-Yn#(#`7Jl1k- z7icMu6OR-w<3;!!)&(7g6`lVA!T+{RNVn(bQTPo9!+IdjNMhm5@rUr{zM)nLV&MyG z>CeD!iMI~yqv`NCHv0lj8Gf9t&=#Bg3K^s|xEQ?&&oOsQ@N0yoikH~36UPfUA)-tI zvK->fgjh$Bc~s5G$H+tI+-&#~k~_InA{`5y7Xq(dIz1fAfMJ>ab z991@P4CuFjB8Z?+m&!T_C`urvZkNEUH|liO0vAsUr5d0^aRH~YT*+K@d(bSi zITNGel#vd;sg{5>fP6go6y-UwaH4{s7N=m(aOQ}i+{p9QkVr-40rIM$-IZ!q5S^Lc z+2L^B9Hy6+h9Hlf7}mh5sBD_Y)Kp?IB`sCx8ixR91=CSK6HO?!)D}%irSmaL&{8Ze z>aflvlm8@g1Z1c)a0 zzXBq6f!wM^jo>;@bEStNT2mtdB3BXbm}r{L78IMk0=6enyJ}Kminm`)QQ^MhcN!V60UkM9yT=bb>ODzO9jCoCxWNoR!stxFlGuet=6| zl41k{rn51jA-5KDfFT%F9*2As%f$f2=wU=XsE|!;kw(DCD+0#b$yhT{3lq-NT*G3; zI`v?og*FyUlyGAr$aS+9)*Te(Mi0nB#0W+4CRtq^F!5>&9ticM>dB~}~31!4sMJj4(# zyu-0BRTE0NFYF%?$J=avE3*;CkuOAL7<_2_IKFHUCoVaO(w$c5iE3dH1kw21sP8v0 zJ{gl?LEpk76qaFyqB$96yeky$KZz^km{%wprOU{sh#o4Il^8=bq0DONl#QWsOlWQ` z)v1)aWvY}sh($9jo>hw?fk3J;xUGle;F%)z*vNdrERB+C z#BIDt&OY$2l8_D+vl2b2Lo{pi2tsi!BRM0$84sEk!L2o#;amcp(UP&mPm__fWTLUTg=qu18ydf4Dj(WIiP*SF z{i3H7Lr~=RYN6yiW>b4xc__$(rsd}#a=B+deADI12b8hd6R`Nj!kq<8NwmABGe6 zM43v-c-4Orj#*sP@q;VKk2W*|Rc^rka%M4=NGHK?Uz!ge{35^%slz8VW63i=hJQC;Yx-bwfX{F^}rspv% zn3o?5w&@V_Syiwp__xpynGJr$io}sV0&#QPyo!kR3mdoi;kX6NyKz3)w+=T2hoOhz z)~E}05zLe10Ov3(4sAPOj@=Y|5(IBJt?POOoOWa)$g|G;2~0cbI^Fu=h0yxM(bz+9 zQRan-ix<92P)C&*OFmw>$$YLkac2!jn0Xe=?-6bym+dY7vDd=?J?dvCIzc_(lzbcp zE9Six&NXspCwXy~iY1RnrmT3Xx;lq2;w+|3C=IV_w5Qhj;v?6E#EShQRi4c~jHg7f z1xFjfem<0z_w@15hocUe9PlC~7Z1KJeUV-xe`9c+a?Ig3PTn#+f_h#p_wm6^qsgPo z56BJ1G5Dqt+~)~AXObwnXl3${^oDRjIA|N!m2l=EpxyQ2|%zd69&v24GoiRmErDW6G9W>!| z0W(hOVK_0*a6A|U(c?i4Yl)az;$f>hD|7^HJhvBn+@y0nZf2YwCJ~HFoYjsYG!smP z)aIy#!a4XdWK5aYr5&<29l=R_j2D=;4vCYk#|O?QONEyYO;_w9?2xXQ!{)p#qpk3r zS5H&uUJQWp^(9V>pAC9C&sP|2ZNiEK@7R8r^KIh!s4=$OOgQg0DJ-B&yEq|- zjmCzvdxWaimhE#50Up7!WMm=1Gn~ryP3jimMJRa=8wvvP5S(9{iEZK`IH==cC^}`p z!%LI~qf7c0;3!BZ>a}pn)gx7G4Nlhv!f-0z$Ji;G#cx{0rcTA_fYZE(nCII^blZc@ zzM^}Hd?7hTi>we`x3Ek+5>7i$TnUT^P8Hx3(eOiB=Co5*DV}MMgkw$QJRlV>Sdd(Z zIxGee39pF+550~WU2;?{FL+-vL%c;mnF^N%v0ETblg69_OjjpN&aIKI6&s#a%Mq>_ zjN<`5OSA>!+xW%@p5+j%gz!G27DtuWW=N# z!hW}UF+BoCdmg4tT>E2i9mcK#mbfOYIbw_0t=|!rJ$fkLY?&@&!wwaJzL0eEgVRDW zO#+MSMbK}E3X34hoDs1_znf%EQy7d{a&a{rEGy=1stCe#U`tpgOfNr2CmZ4!l^RqK zVBQ@imN~YMUc+Ec67DDgj6AWJUk6TD)fm+ed|?P)Opq0(2=7V!Mkgt58qvuxRhEc~ zN=sL`m{&Sd=8_a#lEvImK~!v#b&%vXcvfkdh+Sf2-Zv9vO-WpO18-vqi9#X4EnRS; z%OoOI5bNGpEw@xk#nFg~)Trh=Mu8G*AqrZ)WsZ458BMpcsbnI0d6PLgj1K!(-45rh z#i)!1*IXFIbW`ctt}vX~HLJBaP-Oo?8$+u&DpjM(0MH{lwT|OjzO%&fy2pM28Wg=^ zJR}!-^OuseR5DI{{-*!K@{jy9*N9xhC0K)w^2E|25 zfF{Sso(ZCL$oiNfKx8eLWXofUyjSxd0C62|efDDBf8>d(kFq!@Txr6kKU$3Aki>rh zWJ)4Li9$HaamKjpRu@GZa6-kp)A@ft%A8?S8s@mH-Td2C2ZX9gD{9L(-EhB(DaCOG z5-YP=j2#aM3P}1F*TqD-s`_67;esJ3L6zbe)7gONDr4M~0HR1QKK2>|QKx}3J**$P z``CbzXirf8m8x<>D6F_Jc?M9!t_bx(bu=PsBLwqmCU5m3=jrT0jkM&5U=KY**tgVb z1XGFyA|)p;7BWH$pvkP{RzTs7A(-3{NHeYdOoaab!!&GD6WMcy4;RSz#um( z-qIA_SjXiLqrxb!-|R^kQ>a4}ahX!Dt<*F{ zLf1429oj0io$3#0F_j$IVv6n`(iMQn?n-rgSWU)-x_&+Zp? zzu^1}&O5Jb*K@n*osDy(PHM;W4jtRKZ`ZbMX`42!TeoV}vZda_QQV@qd2_Xy-qcpq zq>0o-EtCq>d?^pD0(10Syx5y<R&iEtY7!}zj~}({_(;E3l`3v zHg(G6f4)EQ&9}zB`qImPef+7%AA9(oKix5E)aXBqgh&QoeZ|1b2lnsXt7oqs7hQP4 z`6Lasi++yL$=4su@9Qev|G`Q&IydYH~11LYbRH-J)29Maw!}PJqIY7+mO?=k_J4NxtMIDSJ}1 zR6WfPjZrfVsEVE=Ll53_*PVC#apVmn zZX9~`)mL41*}zNsUQB}Me&Ge@ov(K_x)_~v&gs;tBgvwDyLN5cwJj}W&CpxAN?MR& zKrgf+qlr|gH!%ve0)sU}&mo>QXhkr?z$gOg!8C8G0x{sV^&~0TlPD*!G;~Bf*s)N8 zF(eS`K!{UwXAnIF759QfBS}fr61_BfPv1PUl<$ZFTc<>B&=V%e&wgjK3Qa6FmK*$eM=p=R2+w1L&wnnMZ zMsKaPlu9(11;*YKMvGySBItQqt^zf{tKFC~Ez>}UAU#bYep9_Eo@8H=9}dCm*38z3 zm~|Sp=%h$}7jePH5kTL?V+pF;NZg7)C*7KRKR`9fN~)BmrjuIfS&r* zve#bt%VUo{@yLVs-FxR9w~rp7-;i@#!e&v9^J^S?Re!+PcoZnUNB6Zd~**dmw zr?=HGaHF*jBhXtI#jfVfn&I)@BE5-LXhX*!XeL{Lk|kwInE{wTb$wFMi^`MaNvv^u z5`yTBB|Dq}BokG$)g=0zVaVL>&T-1Q#8riX3j;t)trRI$!#qnFTBZ&~A{Uua(4?qY zaY^g8zZ^V{9`!2uLx;8-wyteR`(nd}^=sFxT(NxV;(2psPU|r3qYoxR&7XVziN_v) z=8^mFz30x`Z@u-Fn{FI&{q@5JUpZjlfC2rX zO1KqltF%vdR5^}06;}X*G+^auu9c#vvYW&pv$L{uvU79u^9zfbl?>jp^YCe(3YRbI zzxXz!tY5YAU&}sTf`Lt)I;qpU6W(~~rLwZ;{`~O&+;iWZcZ~YuO*fCae%STHuN`{z z75xTW(yLdG3%i}$wR5M==fGt3wt8uz>B(WPda>SIYNjBdEZJ(7k)fn( zY1GonK;thJHWx&9Hm}Q%!(knKSK+9&26k$<2ayjxjFBl)K>o#c+-^9&w`=x2=ouPt zsjR!`h?a~dTj43p!szk~o4@`2j)Ny^f@scas9S>TGuFXP!$~ihKYz~D58waC+pice zwte~8#~*(Dk^Aqv?bgvZ-8}O8Yp)$P_^QD}E+2Sl-~N4jUUXsCF6VUa1dYV7^fnTv z%30Eq+>qMb;JI>BpD5~K1!I(GWr9X(Y>4PufvM7xY{=RxFhUP{pL!e~GzzS=<1Q#p zGY?@*0qKxD-Bx9*K$lbnqc#!@tWD@%hVkU5rln!|&dSa$D4PG}*8P>fAT7$@uUFP( zu3x+Qv*jNzoSlu7Ts@!IQUPa98^KK8&}cieUREq}Q2+7TnJ9z1x^RhL~c;L=Na z_wLcNSGR6mJD=0BUAs1=r5J>*WeMArj!_tx9KDI7kOrcgzEq4u$2bh0Ar<4`zIAY+ z0j#Ulj-&R|h(YLlQ5 zn{K}GhO38MJ^0Fjm-ffnd2z4aJ-eTeA$9K5p?%x7ZCWF4C~1K)72Dw=o2u}UPBASy zOi0Vt5hRN;S4X7Ks7{WoK!CXrF)by;pfr~e8KXB5I-rwJ>U%z(Un$_g|md>9!bIN-Y#=P?C z^N&9I;KL7$P8@y9A8xq*h9QHlxzEMXR)NM`U_I5$Hgvl_Rel%pnaj~P)e%Q7eLnxD~qyw_Y zrhsRx{aEHzcgMt}q?C-@$LDR_aj**Qn7>}XZr$2-t5&Z3WZ}FSA5MB_%q!3Sd#ETx5F zL&UKxtdM4sxTyW1f~&{qScMj!2E?^bUT89PR7R6gdjON#!IhAl(eM3FHtnyjSn>Ji zt5>aDzIg7GG0#2q=!19Oa_b+44H?>QXm@GeVbM-Ny%tEO=;eg z>;wX80y812*RYJfBo^Y&sHv-^}*OD9=-F{;e&?`=+(XZ1)V#?*4nyC+n|R)aSLZL4Igw7Mq;f^ z%A=Nsq7k83Z#As9As0@xg}Vq~p{<0Io5+-ZM(a=&x>92+MMRyDS}<>iVIV_Um!cd00N$!Rexd0BlAtvO(fHEL(%fjT|`_I~fYz*knLZg>mcXr%43G zV1rmOJn~6-0+aksI)cvI9nlvgdEmrPC(oQb@u_=n9|_&-)4f}l4tR7QWVq7?xxd`J zJalQ4-~bg&tbzp>F$9(+IAJWeWS6=N8t(Agy|!vukzZvJ8$rUbm4L#nmU4F*-e#vho@r{TA7Qta1ZNKlnyJbj z+i^Vl{}NMJB~^k#Q->@0hOQTP>(uGo^UeqFrHJt8C2c(Tk83itqod$J;vim1@?3HGZQ%Sk-WL!p&X>b^BSy$Yn~kKj!r~^ytyej30NlWFoIZ;WF2;`Euw|nkS~6BFa3)%MqAUrTui~Y3YBxszHcZF%7&l&s{8rvh* zM?w>JQe*Rym{fX80qb!Ui2Q2)d;w(T$KO{XKZD%>{-7Vc?Xdi#`R_APfDwdK+{0`7 ICjQ(11>0FCF8}}l literal 0 HcmV?d00001 diff --git a/images/mapper-icon/Mapper-small.psd b/images/mapper-icon/Mapper-small.psd new file mode 100755 index 0000000000000000000000000000000000000000..eaee18ba760d997624c6d0b4ccab45335a946796 GIT binary patch literal 1388453 zcmeEv2S8NE^Z&lP}K*`(|fnXJ=<;XWP5Z5pfAj zVA7?+Er5UQnNk9ciRc{BH&#V4jP=wCBbl;cic^$^ znVPLnm^yH9($on_U6TEJ_HgVrIc#!9R)#(|(RXr2x*;cQa(BPPqQa1!(-!oBQvx0z9AhtwNDBN?C2ZXr9)syr_e5;?R>tqCKPaGmK$ozP z;IM!oU-|Ou;fPST?BtZN_^4<(J3Mvw8<(4#73S|hY0{()lY%>BW~cfGcInc^KOo3I zD5yO!+UHC$)r)A})Wg2{mZsOR?yxi`7ew2~*lFOBmWzEQt z(}9<c6VY=}shchHE;bbdr<8QZlnM5_5YbW@V+PB_)#T`A;+?%L-+! z*>3(-s5a@X@nMmf>6zJJR^KDIoBzGQ_e|9{GBPeZGbJru-ydC5Lqh ziHZ*D6%Y`GPw$|>z+M5N5nZ~3^ae0A7`5HaUrtcP1ySM9>hx{7|1<7LrD9{l`WkX_ z6Ty7nD9%e7c+m$1=z|kOlG}#{Bqz7;n35FIKCx5hax>dmJ`!$aB40^iTpiC+?7qAG^)=j)oYGdTY~d>nC zy=##toSA$luWlbq3OS`+`{#uD7 z+BQk=Pk+)lz2VR7j6{lK-JeKKW=ig)#B6=9RJ4)5GJg`q>PhY-eeZVgKq??7apFG{ zKiX_$M6WIZp@9Lx)P^HE1qAg*`;F=y5!k6qKo?8Z{(bQW1q==f4hsql>)6R+=KpuZ zU!}E4;}Q+2`s5z|mQq;&|4lVkGfVd$G@AQ$tPi5rBDrtq+d4KeDa}CM5j=;QG@S=l zcTyXyZUrs}WIJM%kilQcgT5;7xN>-d&aal!ClR&MwY9b{oPOZ4CqNqurR$Miak`<>`Tvk!k zBv31^nn=ltQ!6g3C~6X@6<1B9WW}i!msJ!s3DkP%EyQNXd#*D=w=jY7(dwS52g3#i)A)RyK|U=<;&2@^S&RBb2zD+=Q_> zc*rsO8glh|LtX|&P?+wK>Dlz`#?vOGO(u+pv|LI<(GpF4_Q1pp{ovli2LmK!CMCcL zy3CXWeQsWsZtVCZL^`uLmd!Hpm%@Bm0@JfxmdCP)6m!h7V&jREiAc{i$Q(J3%}Yzq zO*3$khZ0kdOYEG>6 zAu`(#S@mGZt$G+RHa*80GcYxGqV=J7MtYR>0dlM&9g#F4RThLKC5DrQk>G|OXO2(y z^-a#q8ylW^ia6v~Ot#@JSVa2WcoEsjgNGP$qnpL2Q{7sxi1cLNd%@##(sMaHZgP5f zycOMXqCN>{+Hh_Y=X+(Xh)a#jv1kQ_2^n`{n+?#XNM!EFc5r6aJ+n5N=FblN8($LbR@6q&;-prFkb>hDb7&vtpOS)@bbPBh zM35oxFU+?>f)FFHk@&XYP-z54El`GVnq_!q)WRjM@BCI?qUbxJ((^xwyeBgI&~Qo2%gv0@8}!+9wiN(I=VE1KM+uoA6w@~&)fcyiU4A9rKTxeN<5g1~ z5}T7+y}wf@rsob$Os$6Hnxs!pAFQ98+c&4r;Mf5ci)wEHS3`Cfmzh1aS9)5irNeWR z3g5>9M#(kF`jo^xZs8p!>a%n2fj-m%u8!_BHkBWPw-K&~L_Z?N8i6EiV5Wg&?U0+9 z1y3PIZ)-vw(_w4xf;x}ow)rl&6RLgO9q7EZw5ELE37{GDEZ||u-nnsbA4K&TkY>nK zplxKJ_oT!3!$*wt)%<~$?0|L$Jx@%^$%;*gCU=A3V7@P%(3dFjZ?w#k1H65*z3uzN z`TE+#)gNoA%~%=50dA8@er^{0_Gcz+h8x|QN@58`zu)ZI}9 zt4dh~%lrVg6@8WRwJO-yg^aC#jxnDjRj}qu;J7}^*t(rI<*7^hV)Gi5G<{MB5~%h1 zO9U(C{R1}nb)fvLZ;tSdl3K7YX>}4_Op%xE3-2LG@7w-PCH{{S+OTEAN4t1^ik`d_ zz3)(H8JsLsw;?%=ztJVlQ0@H|HWdEDWHuBiDCsoK1diE{*f&Ij`vt4XIJIYS6^Xw`sU?r>)4uVE-5xj)@LKC5d&{hZ%Itx97-ogMO zUKk;a5mE(%kS9DPJR`g;%oFm3_l0G`7s3YN2jN#?w{TcEEnF6E2^9*Z!dc<1XrySV z@KGIw|Wan=0EY zJ1ZlVI^{@ZiZWX{UHOu7zVZX*O65l7&&s{ZQ_5?~a+O-;rSes^S9MkOQ4LWgsh&_x zSItr7t3Fk&Q*Bl4Rh?E9sLXaQc3Qi(c42mX?S|W>+D)>XZ8zWUW4o{Hw%P5sJ8xH_ zR;%l(Td70Uebgh=0u!QG*SLzsikA=zPy!(4}@4jUbI zI-GZ?bo6j+<=EYEu;X~gS&j=FS2}KUJmz@Y$;rvjsf*Jfr!=RTP79n?JN@Ex+Ns>x z)47dvr1PWBdCspof8xB^`LOeC7Z;b7F5xaCU2-nMQkDh0}lwQreBE6Elp7C1hwax3ix7xdvcVF-E z-Yr)$3JHU+=|w zE9>p4SL)NyC)`Kx^ODahpZz`+^?mF2u0Nsv>-E>yKi)vqpiP584JI{sufetk*BW{^ z?AkD?;Y$tIG(4hJXxnJxwNGk4)b7w0H)`A{rcqX-g^hk}bggmS#^H^}H-5A6_l++% z@oLheNm`RPn`~-w#kY=cFW+?E1-@H-3z}-1_Gy~e^!=uPG_CY&)b4**_+L_G%IM{qb*!~Z>z=KjX#GL!eUGRg>HNrqN8Wv8R~to}kT&DmENZi}t)gwGwrOqOZM(aj zUAr#r4DCK>cd)&4`(EuQwqM@w9=<)s^jOm4RL`b86MKHp^GtZN@Z|81!!P!Fq}TXfD|+3C z2#Cmz_$s0_GAwdh^-D+e(w{}e$lDX%cBcoLSmkb+1$sjPyarz z_c_?NQQxG#%la1d3+*?p-`4)l{S*4<_dheB&4BCy8)Fr*{bJvUJvOk#K*PZGI;QKZ zdqa0Tu2tLf2ln9s%-6LpCnBo>eDJNBKi1xb-fZzo+%4o`kF`Le#d zexCkfO81m`DVI{ar@oPTd0fwN^T%CFi%iQ;D;(cv{NnMY6Lb?knINVQOJ9{?pD`w5 zgTd1p1*SX$srgr4D&U-HJ^u!($ z7fmdiG<4G1$sUsplXpyMGv&o87pF!~{pd;6lSxl*d8+ACvz|IVt=F{orwh}^PTw-4 z*^FmroSzvp^V3<5vnI^?{polesST8=9iLR`t9X_m*>B1oRc)?w^ssRS@4SZYRapB%b$dW#J;iaP2V?Pdb8-Q(Qo}azvKMHZ#%x7_x7m;0~UO}u=&E*78>)% z)mLJUjQ2pV=4=*eo zy7cFdI)Ak6;|3qU{ITg1!zZUcjsJA(ve0GAKGS|Sce!GD-tx;U9$m5P^Qh0iUfF)- z`>X1%dU-WlowxeR7h}FSu%`c-Eo(ckUHN5;FW>pf>#LX63G1e;D_B2n{i&}rYkeC>zSAI@$Wz2)eS!++ej zb5p|g zLwA0?tIMv9ySwfFeoxOmKkSX#`_sO@`?l{NxPSM7AqNf}eDvU{L&=9O9Zo-7cx2*{ z%A+%n*&Tc7xcl+>C$uL%IQhuQRi`?g`tEep=^bYh&Ky0PboT1GoO8zW&t7o3F#lqc zi_0zrUHbNN^yOVwMqN37HS22Swb|D_uD^4m^^GrYhTq&#FudS=VRoT-YtHTZw?8TB zShS@$uJ}YrMoDGqOJ()TJ}vKD{&U5!ii?$#j2hzt(<7#h=KkhmVumR4v4%JI2FMwh z%b&@EX6l>xB7cH>X!^XySVP>@dzlXt#6#kB=FDHjHaapE&o`JWe`3iqfh34W*|W^q z-rnB9-r2#y*~7`v$)k>&v$Ir&ftE;=WtE;Ow zeYtu|Njz#oK-|nc?7@71Qt)F64?*c6h{rMfSBhJOp5W3>nz;bObSasjP}!+9_709t z!kv+r^HN$O-I<^glnRx?PNTNBQ#uDD(nG0g>KSO)Yfz$>-xITf)ZVYZ|3yTz=5^wK z7#rL%`{~`0nilgC3QiX$<%HDzV9hfvquv;t+1f3p7PgBMFZm_h+=Rh%b{ z+Af5%&@|9f1rDC@^Rf$?^}084@I(CWg5c(3PiH?pFEXJ{QclOh7HVQZ(=ues9x(A? zz2x3QI_2umSXsC~2d%1D5RWitCC}4?^<>w3d|^tO(SOZ5-TJ*caC+Y!Z)JxoCf<0e z&E%dVr+)NUzm@MEC>%A{pu6q1Z0qVbibfdEpNu`RGvl+KBP&wl8x8YXnR{T`2gl+j ztZlL(<>KNo#+w&2QrDkYbF1BzcZaPwbs^>S;b)?~O835c-LP)+sFdFhmF&8BBQNjf z^qu|kMfT2D0r^)ho%#9M({n#o`SdyWi@xHL-emuic66!hdoc%Ae(~13(?cV+rbGt1 z9{v2Qt)223@7%NJ0P?bI;Ot;37cUioC(T#@xP#jH3M zUA$`f!ozRve%k#0+&6oFo{=*8@#{xNWiCBhV)BnQ@0{^TMO*i|QCI%BbUewwDOLaF)_OEi?yBy4-Z}R@}`iN z=P$qVYTIR>w7mTB(SSumzIwf=eaN-7t=H-4kA z#J7ftOfzzE-0y2P4Sgr<`~B}GG#vADhoT=c=1xn`S(WqXPwR_@ip;-gMlZedz)zd? z{JdBfv9ZtUGpA=g)-3Jaw?~g#+qUf_Q_?<>MJ$TAX6{~3G5-0}`$H3cTHg5R+7;V3 z&OiRe$N7hsqG<#!`+U;;~aL`(x zcE#_N9%}pK%!Z?0EgiGgE6-nK(F^oG5mOvaztX63`uvyY4EV_2xNk<2-NiG8*saR& zIrCZL%HHQ1Jh!q#)0L$|9=~XQJGE@|jIxkr#p73ewtAUi%$Q%A{xLc6wa^|vfA!6G z-yA#8^@PZFPQCcpK9_~7iawjzdqvlgyRVOFb-7E);-sE=d5yf2rw*u$%)dHu`^&1( zVcll0uXv@T&9ONH=4^Oz?&G81oUv$2k3K=STQ2tri=1&)SF{wBwEO19j|b|GeEP$- zobl^Q4wXJy_|1g2zFqgGy!X+{;s(!;?)(0_b=MZP+p%Hx@TOWT-+4uA9!=sC+any{hG0GzRT~ge|U86u=NLyn%|i8k;r`3hJAB$ z%lrG^K0Iw@(8X_O_J6_Lr_O=j4i|M_9Bb&REjsZ|+YcL+hBkUdpVx8wyY4>}jVOAz z{O0fj(;mNaJfYpm_og*yIjT?A8>VS%-ru>sa`&9(eO`HS;5xsaOMac(`nd(Oj&v8< zx93lVE<68i$g8Uwf3Z0!_SqR}mGwn7Yvs<5k~TG7H?(x)LG_i=pU$@U`L=T8bGqn^ z&gNGdF6(*OXVf<$Q?2|-WV?rD6+ZUrPpdcFbn)2ubG>hW%{jlb=YfP-F_(>Bj&1q# z!SeAC?O|ztttsyc9>cdlou3=)#gefQHx?7iz8G+iLpT?o93IL7M!mnl@MV$s^TnV# z0F!bZiLjKDAA(Hi6dTLhAYjilf+wHXM%M#)1HSY~EQ}QzTEpXo)-sL)IDwe!!(10W z*+LKxQ%^qs`7}PmO=(x)bBH+_XEKJ>4EcW7>r?N36&l04GCPF@goFhL_*&j-3Q|nD zM5y9%iNcysz!GIum=JNP1q{7$7cd)XQy!sZRe6LCw&hU>78*h)MHOvM3}2Y@B*{p3 z@>ATF;ECC-%gCfTMmaA6VGbA{bWc^6W$gqIfx=ZWRUr#pWeMExPDzMD?Wa}-wL0*B zQwOTd1{27ryW=An9M{2#*{S+mZ0MjxdRiSMYs6x6b0e#*5;_jV+nOip2jwLuBbqEF zqfHC)Iw7}Ew6VeVnKnPzJ`c_|@a+|pu_L{OIhwx=+OjYhZG|A-E^c5<5OL~=-2?jU zbWEowTXqmQ8!}@Pb9424k;?*Xm}|3WAC;CnPM_T?Jr(Pex#RGvX1)Q&?G7-gTh_*W z?vXkJOS0CbGH$`&rxdJx2A0rpuacokUgxEPX|P(CgID`n7f^Z14=5qC&&cVBwIHVA zun!3&s4@}_X?^3PisRrB~vz7}pz zdEa?)c`W77sEm|stdVj5z^44Nwpz5-!XwwiJLQ4Y-d}_>cEVXK;52Q&&|Tt@JQ4aP z$td^}-z{N)Lc&P|PzWFdT&-a+km9dDuHF0xBu>$1`|{;-Y0p-Ao`K5Zi;pYo#{Ag; z>=>ECSN(liD%K}5`A(@GRDS$?0PO`(r9->;^OQ7dX;cfI{5dCyw2q#Y(sNdd{LDSH z^rUo3Z)in*nK-0VQ3j`F=xO&-ul;+q zjXx|J%kxQ@oSj8!HwO)v@yqaGKW_F9TGnxn{{eD8=t7)7J{_1Hg$3e?u`QhH7B1;6 zqM6{tk0kHTK+tT(m$yX0hK5v{8rOjxR;=~Nk!UAmMrML0=M{G<6)u~WO8ek^tJ$~* zvUQ8{PEVN}glc+9%F{;9mi(y}*?o3Sxn?BhpcK)=6DFnQCXKUfFI3FE4>OAyfE z+lBOo#IaV?SXf@!=)p=GVkx)S^jwlf4NFa!mdmnX&GJllvi+nQwlG~*lDq*8JHcp_ zAYXZ@>3sJWuY&Z%)O5Z}t(PG+{Ba8kV^KM|2FvcXDk#{UkYU}`W(yyio|kRG4<|*I zuYs94dI_GLWP#DW7hczI=_?p|qJ-+AOc<9H2`p|G24?vB%KJDpXt8P7-=#;pl{A?} zW>55$EuNw}jmYdcYF(U0pX{mlfrsky8JeA&WDEQ2)VA9OEvao_F1(du$u~)#DD7P2 zB94acRHsc4aK?M~YKoGOJ0;zsDAcO}3EM<-qUj!I$Q^8r%Z}!831~AEcP|ASh@OGs zs;NOwf=E}wR9|_c;N*16CJ~CMVKYoV#hheH7KIGAfmsyN3ghjNms^;FbQ*e2$}l8Q znCL)M1WL|HrY<=cA88Orag*uG_9D!TymVhdXj%m(V47`mP8ac%Tv>%DWkkuDFrx}g z$um>aDR*it3W+U>m&~A)3_sCgz>;<~Selfo?O&xr5p7A0)lZ}f8px9ok9N|B7@=$| zg{(@2c==Myn88h`g`Nd2Gl?J70K^X;Yd^}^EPjC+z?QItKQSX`0)U(Gk>rFi_kAv@ z2e@~n3M_Grzav_~?4m-L-C+B>?o!N^Rbf2F_O4(Bns*pM?z~GeM2%u>?^3@j_3>na z?6ChJ4-+nYS070;y{Zp)rst&eu~Y*pVw#@Q3@KC(yf>id35E&OXIY=qbN;9M__sJ; zIfkG(nGb>Zvh?{q%L=z-KKj9!p6AZE{5pG=BgLgpnMfvqczzlmDjHcp+ELj~_5tD% zX4rENK8BlFvm}tU zHw-s?@NQzbl(*yqI4qD+NI$4txMUyXKk)%#V27)lc7lK=H;vUy`@KxG#RsTnqJ211 z{3|9qFz+%wjC1N@;4fXN2V z>LEGVoZkUSx$=Nca3pwaa+YO)7xd40J;j>@(XHaO3RcBy6|82wDydU@u)G>(GGPo+ zwc*tUX64leRx@7hWVhnKL9UJqsr(OA2Qp6)Nq$*%zy@ZOs|~DXawW4nHbK5@%Mbf9n7{)J*}im-pO{^B zkS8j4T-9+M%n9)`LfjNr8-R`7Oey)LOUcQt-mKvZ`yz@uHb$=fy+#=1px+A~Iqpt( zoFE{?O-YSycFimwF=)9gnq+yVyG4_1Vbz94eGL<Ge<1p%t5_u1kgZ_w4-|)+3Eap(X!XCm{EWCsU-H!Tvh$@VI-V zj;wKNQWyEM<~<^uLu!!s7&$3!*3`+HIj{z)lTL9=`m&}T@5^B|NS%6$W73y3b;cMD zt3m1sr#L2kSyPjTjH?EzZLDQg>Qg+m?4$k7@gaA6kbW{g(!j-rm^8q%v6JN8){YNJ z3ix@qf7kfXtxqZQ9QrA{^RD2Kb5A(kbyu(p$96D^x$`c?5H*Uiz27}P>G&{QHTiVt%0U758y{l!pah>E&}yhBkCeuTo)y%J9AjvF5m_>Buxp`-#RkB$0DOs{js2a1o z{a4gU$w$0S;?f5sV~BY!D?Modm8tpIE}h0ECUy*J-zhXOq|_ftH0SZd&_&<&a@AYEuV1me0tr@0ev0E#npQ& ze9*|llk53E6F#KF_>p6Jwci|@JJtK6<-Nm)*Pr>@^OtUb23?#M52^4&kPs|imT;gj zpK+AOg;fB;pjgXMH3|<+Ovf>*>T{@W6cL9Nwk#YV#&YBx(cX7N>u4G94jf0qiOXbW=`gSD4-BpmDnwF3~qq3zR8%$l@9W^z}BEHtP{Qy z@z)XCeA?qF6kB~l5DLU!DB{~AK8fSC$M~NhA;1h|o$(Zea1viV0$W*pUG^^8oe{4G zjekNL=g|jRRvJk?9dl7->2Zmv`W#s>1a(Ns##;&!a`l#%5fBiUJjFu(x&sz}=_G2V zMW7V#l&m*oX5a^9tzZSYSoXniyVhbrCmy)P08LtdsU~S!;qgTTAJq;^&K<`!qu;eAfbpvj>=TFeuG`ZB zv%X~l0h_l>F#H%ie+L6C5b*xUGH1LwB7Dk_om-5Xn{2I^;dz<{z2rL6hpbZ@##iKp z?+|9NF4RP%-GM>Y-k&;m<`YKq))PL3yA1FxHrWlq(ls$dyO`05TbnDuf`L`B@evURBE-EN}61* z-k<$fZsNdVSeCq#1>0u&T0}z&EdOY&Pd71})T#P*44c`hf5}>(s(Kav2Wx$XB}@pH z#AccC4!40tSYEAV1G8N_xc^$8MDzZu8~*{XDVv#t?;X6_z^uI5z-q>;W+hR5puA4- zWvb0Y)n={F24>~e239j(?c<5&1LZYiCR2u3LPI~%ry^1TNSWILNpA^+iaw#-v7ZStcD(r4bpz2*s{AL-7 z^bgQwxSwlqKUbof%Yc1z36@E}zA*`yg@^Bwoy2{Z|G(V{J_i1qt6i2AE?VuXb`ddY zH>Ax5UIJEI?c&o4NcShnW2?wg@@l&wEy=AY)?7PSL40UmZ8xMf)s@Th=SH=Y2LCw& z(sG4Om|3s|{(E*q+N?yV@ctq_BzETsfETIo|N7OT(jkAk5)u4&RwBafrCPS$c_U@) zu4h>rtZ*Cc0EMb4@!EMsh~t6JE0SJ5kd=sF_D@y|{%j>81xK0x3GLdI2q+F;iLkUW zJ|`@+%ltTQ8R#d8v*YA;}T8rf--{Gs|@bX$$sggF62-xP~(w#rp ze(}?c(oz||;Y;G1oJ|Gasj`R4Y;$^)Hdc7sBWZ?|=GqN$H&GGQZ;lDe+b9`fog| zJ4ur4|MFSgQfW_+MZ3e9cyU-4hhH}Rn)J%4r?!VzlQKR0H%gkn8(-FM+Wqpsb1J&@ zr|LT0VsE3zS~ker>^-5ZPRg6#qF<=M#|Zw&26U1nt)xSLOeiBef^XOnq0C}Ib~Ey< zgl>d^t94EHwP~c&=X}*%9#N77q7$rDXU?&X!}Zg0);w`aHh-?#^EQQ^|NSG)Q`Mv% z9&Y#8!tn5<&7DdQeyxk0!6hesk<=sPJqGV_P_>@jBK1%C9s|djQlgqm-C2@)Fkbwe zH3f;eG;RA(Tj>YGRwto9&0<{2mOd1xc<-!*ss9zVU_Qyqd%vBB9skC7UlATO_v7Uq zUG|3u?P_4$KYvqrT<@3673#U$0f@VIjsujt;p86MsF%`b-@JrW+`JaP4{ zN#BRZ6rR7FbV07S+VOt%9f`K%{qX6lEmEqs^Dz9mPc87jv-42X`A>HqlE?99I}bbl zqcQpY#`|5ccPJG56l=%(*cSPBb{_J1(>r$_TG}KZv&-$0hvl}(!*ctS;8GjqVY!|1 zu-sO8xT?MW)p%dF2t3t;&%JiM|Ci(a2VyZ`25LCoud+i`<9%D3W%+%p>N^kZ@g^z$ zx|C`g30y2tzTX@?sL3ALu93M-OiWKp?v)#tnZtiD!SeDHcPV9TV$Ou#c&AFb{vHwd zEt3gV@1v@3lL9}cW=I}}mre~EmzHZy>TKa=Kw3_&yt}sgkMLMa0WSH>n$6*AkGl@P z`NE}1$8or9yk>{~=yC?DhGh9C(W=R!S`09GTd%g`%Vt^gUdP5QZ}zYipX0d19K6X8 zd!?=K^umj%Qmel-?18*chYE*ZS*rnM!n9nL2@lIN-6QVt(wgY>+-zA|u74tDP9`SD z5P<*<{#kr-j`eLO;Zd6jM3-ET(o3^s{OWsSB_Gi`GP}d=k*M%*ThFzOL+%AWN6W_Vkd#&|eYrWU#X4QXm78^D1e(_SMs{~P(5D=l~$>!K4@eYAgxJI6``W|xkr5CQNcA= zstF9{m{S2q`5P-iUPJjWe)}e5BxdK*kxx8w3G*|M3cl74n(p{WZOd|8ns4~5_EMy( ziGlmQmG&Q=W3m|Mhxc})Y7-r8WLe_&k2fWgb&{|9Z9cx=X60&|lK;Qnc+BRaQ^j{S^4KPJt2Q% zol*!7er-vk(Ob&GV_H02@<)7Bc<-I(wrwb%8SWnF`$VQXBD_z79cSIYX&zp`H0YV> zZkNJ4x6B&u65Tbt&*QzjxCc*_b*Ret*6vG&NNm?Bt6JH=;SDMDUX%y_h7{@TD}n!r zcJ01oXo|EiS#Fck`l8e>d01|nJS?|Q9+ulE56kV8hvl{^;aJ=2U#(L<+}vwtdj4{z z=Yd!Z5UJrh+_f#E+pOsHbm->5Ya5X``_3>$e+$sO&__V?s5?g>vE%U(p5?z_| zekLfCtKdi;WOhriQg$ll3;v)U7N_-l)6{1Tp7t=s{7dd@rI7RKw<)xnDRidLFA>_s z6rY|C`-+EDjw{74o2^Iq52oz8<#znn2>#BL%Gu)N4G3;$$}tu1+1+Ax4#Q^{x+-@8 zc8Dn&w^NreWv{~Zjz!FV)Rm%b`%DFk!)`FvphB?t7pzpaZmo6AFn@DAs(9Oe2ynj zOeza9F=5Pwn@m%0(jHMXuWVev?ER;FvUr5kjjk|Ni)Ys_kHM)Szlf8FJx{S~9Ff1~ z!0KyEWA}n_?_0}_jmnwgyg1|(FfMQm^;M?mB?jJr@V+g?ijI>Q3Y6`}a6-Arl)mS` zQr~83xA99?&x;YvO!(=;n@m~%kJGIow$5U`awaG}DZ@*siGABp*p=0BSh*T1k!b4Irm$@# zQ?x3ZPzHXkFvaAGUQi;py`uPqn~|xviia3`@^{jsxkVQzIh8ZVo-dE|Vrw-1j|6*7DEhQrI| z3hZPSC@9mFY|2@%AZ%V;A`$rR7u9X1d|5O%EoYk5;$FslMPCzBIj$6pTE_vnAPbpeUplPSSX5@-%IM%8QB~XkG&9F!4&p;-UB`5&Sgbh zG%AOF9~IX@HF|%Eb?gY%>$W)A%v?5#=1_WXd^lrK=Aorb?KI&J@!+ejX6E$y>8Wcj zh~i^WaI*qXxxth^XVxl#^0a7jD`m>JeZg4qMxlJnk!w=1|+0?z_ zp;@f}ael61t>;aohuKnY+wF`Km0yZGN|@rf_&v;m*GJ~pT`QToaoU>Ft9f>1Fj3=o zh^Jl%LS?mn=Gv7+;Ra8um|ElqgSAim6gh+UEc85amyUKv(mq?nYab?PkkoHM$;~%9 zQ(N_nowHXwmgjs8i3^ybd3h>wJS)D&*%q7N=2!8WBU-Y0vFW1-CG1MweB-{y?NRSO zgJ0WauGsoa@MS6OnqLK^y&}E_32QEiKjYSlv4&9Td4=6jf~1>A;{?=Z{$2Or^VebGq*dq<|4~FCPRgo-LYuQ65!d zp=j)iM7>Q0$g6>I0=~10{ZU1ao94M6ladT9)f6$8_M-S}XWzil3oqjkENru)+0D1n z&p%;)-3<>8*QcR&eTs8#F?;*yyhTgi&g%|egR&G1`)@Hezf2TP^n`|}xBqNc#8k28 zu07uZNd}nQ@tw6_C}yrg?`0*%y(^b|aX>8mbYvYgxq#RTiH80Zc(;3_(j+QNzIYV< zkmAi7jo}42rtJ}bh{wY0)P1mEZ+r-CQVic?UN;O>9qJ~1St4FOaaG(bQBkxOqtI^Z z_8T*xg9;@bZR&G_X}W!6T^)|WtiQOuh z(~27zzV;3w&t52<-{~b&|dle-qnZXKbHY zwC@^IX~$X?&qW`jQne57)&M2;xHf@Oqqp+Bp$G56Q|rmJL4D0OFwc#TXy;Wkb|7i<8pE(bA6>W8T(i!F2>KBSI+1G>#i7EMusg5C&C*W zRndBU+0LhmhTUKestGr~RWo6#`3W>p)yN&Qr8X!Ag{q+!QO^V?E=!dvSq!rnNmuNg;TLyyCNFU1cW%a~@q z5&nup(U2QVa9D2^*<(mM?~D>n&3#?vOZBLBk=Fj|(?;ebFuw)j)m=v_E7$fYW%k)7 z9lZa36>c|})70z1^={q3CW(5c?qEx`^-Kq9*VmcmG1Dlx#&h0<*>0BfWO^Uh5@sJ~ zPGM~d2aY>_ovAZn)`wKaqlKIuV`7TICCx9Q#O(%+8H4nw?`E;-Spnz}XP8p$KPl0H znWi`Af!On=dE!VVB)SYq&p!_TVg8qwnSEo!w?$&4;2D+j}%=qSpSaE325Ehv0 z1-WIIu3hPDVveft>8o~}I`P{Fqfw2Dum6BSa?+QaG?m_jqnP(y<3gr3;9V;2VpkAe zwDKy`bl+&cuxNN=az$AajaQkt@2$t%D$#)qyj=R4*2o$RD=9yp)v@E~Wd-IvA3c>i zZu*LXq8A*ojBE-jqQXd9&Uh_fk*; zSJ*}2r7~XXF-J;YtIyaZ@%cJ0ToT2?&l7!M_Xcn>J-@m3$gyNt@D{nR zq}F4e3rZTGaNB2sP`}D6;=&hyVeGrtKqyG`xW!Zrzcl9i5UB#DQH?n${>8ezSLprzd*cW_EqVh~ylYC!s|QQoC+HQ{hv@96FoDV~+5~ zj(0%|ZuHx=zSO5*V0L~pFBiSw4cGtsuRt;|cBDJ z(TqE@3xmqeVhCfce!cPx-0GR9Hq9}AKD93Trk_MH?CV3)Y23ap>`O-I1XF33iHls}V83#`eN>^iSyjp0pDpRa_P@Y- zUpV(A{n2$?_ay%9yh!L&=3(b91O$GYm@frooK8un&qp>yTKwE!_d= z-LGw}LoUEkrgUFcF&^l4FPnCb_C&Mxo^MWrviVOJFf1HX@qwCb(qX2|tXzuWq+|0% zg~mM}E!k9lrvGvH#o}bLJcpRj^(71{HPKJL|H-RyYSd5DhUb_tV-O96F>w&q{TfDh zv(Pbh+lCHYXw#Q0$9|@mQ!ySM+`(U(K>3=j7Jujl^DwGt6~*pj$}HnjXLMZZB`x7* z94ju1D`XCVU%;qo7MN!#3284=b~|@C1YMEarkmt*&i?+jqc@pd&vQTiaW$I8nR}SZ zd$sX-C$d;?n&56KXKsHCLu{4uMRB*5Mwh#oa@3Xc@i6tt6(8EOVIvuvxq1G#@O#_- zR&sO>c_KS8TzfH*xw^nt?|Nxp@6$N-Hf!^tsle&B(QnBT!r9K;XWW{PEwA;~h^J}v z_IXJx+#UCsi_sXrdP72Sa7y}J{AsKk^U*+mf1kB?wMQvRKDY2<7KE-T7)@xuFm>>| zh2^Ug>zT37phM_+&tC`}_4h~EcGhx!& zvn+V0Sny6S8mpyFKMtfHS$#VnrY#V~E5~*nxg-{{M$h$B+dsAe?O^F)Abij48}%49 zJ}cwVt*eov@c4z|C1b<;_oTS5nS%pfugJW=ta!;|9|vf9h%T(xxkCZ8w)izuM_20C zi0?b^=1(fkn%`$M+ler2sQo?uoH6(1gFh|q-q;I@p*a7eCxSM|UmMJTwZ@13S|Zd2 zANr#>U*J^#^99ToX}vkTG3Km%@oB{#;WJrHkh(3XNa-2^uK{a_k2}v>;?NcQ(7ZAG zR)<50CpUatIA6fn&%}Ib;;S-Wn)9m2mu9=n`830|he`L{xJ&a~=KQY!-^Gmi(hQd= zUz*-BMS2n8Tc9gVS(WD(1GgNX z3Z$oWgnt}&5jaJFnGi0;y%Ha39?Fz&M%*n{g7{nc(o~Zfcw+udR-Rvge5HvcbG{LH z*YT02l+2)E%$FvUD8DjZrZb#=dA>AnWXdnZvovF50{`axD@cPWiYC%Lk%`m0hJ4DI zxd}DL3&6h$yc@U^&ldPaJl{rH&73cZXZW(5&LzYZu~Lpt`4R8vL7Px|;um40}F>4`McVFZVSM>taMQk^JX&sGo*L{XZ;kmN$VQ=UZ8QhOFW zX@Y`ytwfv=R0&rqKjm70XCu-|6A(m?G=cDl9%O?L(JDlG!jq;L2+zn{g_Lh4$D!6& z$#Er_P~A#(O*AW!7im)k&qJCYkXl`-j7PkbAif-TY5XtgPYHOq0(cq!R6?pWaH9IB zj|mc#;z;YrDWE!~G!%CO`ARLfEMFR)OL1k0qk1E4HK7$yi!bEQq^nY^E8%hF`P3zn zP8A`Zcq6$*Add912x&<7h$itxvXX|*6n7h~lUln}%eT<#rD3s%dolQ;I*{Uu zVNIwWrNJu2m%vI@pw2|DZzLZh_?6<#yk1Tty#l&U`d9`YO7Nl9OXc|uA1O}K0jZou zSV?Fx@G6nNG!T_!e-1LFI;T89gUjHNJC@hWZIQ2{n zQAkQRwDw=TfkEg&OCi4;9qT{11Ed`h$%%12OY&g2B_21nH7Dgh_gX%{cG zDStD*!14#)%dj1h`5)GL)!v7XE zb6XC71A@Yv^I)Yp7wZ`7XGAoyyq*auv(2aldiWanBsj^ZB=c>`C#ii6WRlU>h$C72 zYuw03;7Ib~Ij3f&sq_b{_=t<4>7q)v7tb+Q|&BNyurX41)S zq;3PA;YRAvk?CYN67#Mm9o$GvpD-O+61b5Z1~*a%HxjsJo$N;H;701u>FMCCa2&!v zj%Tz|(5TcwQ*|(0Iv7ly>_+NfVL+n<@warc8wos72RBj&H&Q3NkveFG4wgqJyOE$_ z)X8on@+;$If*T3?Ugf3{);)5*9ZYL(k|;8IOXxUi(CDYK5v zHG@|!VNO?eBV}AkqHyRW2|_-Y1XK_WSWVF9E>ksJ!hlJM(($^Z(#mcmFEPoIs8LBs zhu}u4sg4Oxb|b-IC1g%GQtncnDEfs7REri&k|)W9c&9vxqNVmMc;p@u9%(Mskc2Ch zpK^uk2706=)VUEo(gebjw1v1OT5uyd-Yr><=&g9Gkn*kMIMn*!Mw0x=wdQpz)iu$q zL|&v#6+92wjpR6R$#|KF7r04)89|R+MM;0qo1q0%K!@m438`{764Er|V}b;wIMO=V zjRYMj4sHUMdL>$#>_+l9xQblMNn6qHa0`K+m+K3~k!;}CAg){|yOH3X%4Fnm63<56 z3GhOC2{#gDsN^(>FOsS3Mk4MuS|?c-sg~hJLYFC>hkqB@Y`;70O#l-xv$FM*Y+ zK;4O4-$*`i@jy$;m+A#>B-)RT$CG^EMuJc7M(WTL%5jnokYz)c2rCIK1|HlxGw5n)K$Pqku+C9M)1>XPB2 zRa;e!-^Mf174L{{i%?@s)J2*m7c&!pT*=wvA>!Pq2T*3>F0y;-YO_EHPl$V4E zFH-P|zR6)=+PjZjcnQlE6Ha7QIm#%h0;w|0BgZDyA@!1-NT?%qXhvSWa3VpwDjrTG z(v)#kqVlO4MB%xVr-c&uJ z7L$$=!HMKhYRs||2?C__)WVt3qC@65vI_}3GiX$5p{H6HEiH_tR(2t^ur8ocg7{ln*@Xn2sD%ru zg$t>bT}UjonYFMw^5!-(Xc)D!3yJ*7c$wfrf_}MHb|JNJ>3A98LV|y@7A~X~in{kC z$_Y!%>A{5r&*r_d3#o+z09+I2*#d_Psf7y(UWqRg=NZK-b*FjK$?;$iY11==!^FE~-E@8l= z_-J|EQE6otl9!ldNz|w$q(g8a6;#KBC%cf~uo5yS94U9HPJ{+^sDdqsBu)|wu})bM zK}+RPWfPw4L4wsv#2IB=srZyByf)yGl5h_a^hgm1Pf`|QmT19K;dr-XF`}#DjY7(| zlH*Y0g9l0Cr*4*4E%87&@F0;FDN_Z{L-rs!4!kkWEAav^1~4P&aSxKK54timfC_}^ zQwgDR4-&#O<70vVr8rVL*@FZfDGnY27dkvht?WVaICzO%$w^t!>2L#qZkMYI#gS~_ z(jcx}D|?XOoyuh7aT3o)?FsNgY6%Y#WvJvdi7%3=>_H;#HX0`x7paorK|+?+&!2e5qdGL8AF+c|6Gn9whkW9;6oCpd3f) zS}B*)2qOt4#xp!fZoJ^SAzcN+<$x0}r9^|v6CNbOr7(OHXF?h>0HnB5lOnqCAhoWJ zgdr(E)ruvSv{X>qlii}^gg~kwdyu^7log4a*dQEg`LYLz>;!E?YHnmM;6ZW(3d0u8c^mylxM zK!Rm6*LK-~gfvLX1QSu?QmdoZ2M3ayeafC%rxBw=3QbS>dUNAXlX zQiCMBC1oWcUS@)({!I=7$C~>$We_-3hFPm}@WFE_PYd@ExTNC>r54qrvKR1HkuqMMeMm=X5T|H}90WUT zvomRB_YnYCcORt&?xTWOIReFi`^d?WbyvACFD)2m>fMNe&x081K}4-!59!f9yapT-klpZbBNkkJ|5Xhx@2Kj62*% zPj|F8BWIl24cy1N4YV+4M(uGRzkN-@f-LJ_3j|~3ou!wyx)6skf znvN1*ua`3xdsb9@pR@N0U{`;}7!k&TsQ3H*l5>IbpyVcX8cvA`Vv-a4S-wwq362`@oz^D|( z-e9Q%a`O2Ygs=_o5cd3n8MYrK+;0|@sIwTLAf3wR6a3H@P!5V>Ch;3lB8nvuzySbr z@(#%3hGK>&UUI^Ixz=DQhOm6|uwBWQWNjAfPSz+NFT+6r_6VZaX;}vxJh-UgXsSQ{ zPytPFJ7p>S6>Nok8*CJ!?2p6RgBA-8Vn9-`Hm4xKOCcn{5)UPh)7622)CuY4NJYoGxz8lWcbd*tv4yZ7J!Zi@Rh+rN2ye7E>;|L!lP2UY^Z z?uYvKKTrbK-4FHef1m`dc0bfVR&`N3rS9(G{Hd7&-5c9Gla}^o?iYobwfBc42T95# z?K>`VX&iYiOW*Y5$PcM1;F8cWdj%XDM}8-WGh80HocPoMmN&lZW1X`e57otXIv+gy z;obRYl+Q`5C)snQ#%CN+n1cAo~$6;*bKIW@OmB$bE@7R7Lf8_Hl%jx*q5>W&uw9l3S25YoghPlBnD8qjEXt=6JA5Vl zpBa1(0ee!FDtLlOj#H(A2R3%%9ID{r3-=IPWwGBu=~%x_OEW9tiJ5Q?M<#umcj^KU zemA!5I@fOz5ZMQNy)mHT#lpT~7j3JKz52#_GkD#O&J7>w6xF{^JbVdS!#>EI1?NU> zLSqIF=sOtu!ZGX;PU3K?i?&Udn7FtB{f7oJRstYJTxBlKO*(YzKRABS!2UzSa3qSk zIyDaHp&KzYK0aHx7u1e{|Hap@RqM;$!Wx5!c<-FF0aY;+Rn*h7E}y z6hGJ#LiKQK*}2b%6n)}jj}9LOq9e8J2=jDp*=4|E>Erc@V;&zh9LQr@<1i)@t}rjx zR$X=aCo;#U>c@_LY{c;4Lq@c9KE}M=+V&ihF=f&d8EL60W5D%w=4Pdg8qg!SRU;f6d37C;{nn?8=g)g(_LSuK$dJ|z z+|)*#ZT0l-l{|CtSF4vUetm{vWK8HI^_?-8rHUsFt7INtFTDTMd;+TN5WX5f;=p%jAq*LYpkG=PRi|XpyM`wniqXMGX#e!WB6$`PW zSP@jzXf#SwQCa|NG>Oqy6E$koSYnAScEuih@4ffldsmb)bDy>MDSHYUzW@8(dw=(W zo;hdE*=Mi)?7G(4YpuZ!w4*Qhw)A!11(mRS26t9_&)Y`h?%}OGALEPdLF446`E(Ci zp&*Wx?rL`zul)}eWIdv{(tsqngC^y?7*g~})~W6|=fyB{!@vCa=7zg8S;9mE*O z**^$yT4;o|#~^iPX-`m9>S*v9@Vlc1T*3d_pfhMOpJ+cKw2)3~WAjOOmJ-+)bvhdz zo}EJ?eU?oYE-&Cgx=lJFZ14mIx@&U@t}!+?x=+Z-Xmc6P4fvw_fcLMm{4$WKE))0A z2_OvHWZ;WVZ-c!RSetA0HXo4%weStd%|zYC1`j`xaXsjxxOyACUZ?wrIYg(^qYzm- zqh4zRC$~EoY(&b&Mytgm8_YJaZ>L)Z8(SR;Z372|dm6nKucM&ISEqfAhSzFQ2BP^A zU%;8`h#>s-;50=~Xs|xe7?2+Hso{+}Bl6c`lESr?hW-N_x$U0Y?*Fn6 zBNg!|AjDc{pT_zVfgMlNJ+`^6Pt!fMxvBq%&l~zrx@R`m^y&D#iqGdZSM(XW7dDsl znfSbj&zCkA^jWyRpf}+2JU(CAoYNcec~+0T6q{RmqwNP=y|lTjPqR(O)hnC(`q#Eu z`j<8j_0MgM`d2tVwbkff+dR@gvVCpySpN{8Pxbe0-{A9}^!XOocWvL=Jk#HiKHuT` zwypg6Tz?DK^!Xn5)z9bpo46x>ick6VQ{?j=`P1hU91hqyhCp3b{{dG-eGLNv&+EMdyMuMb_UxI zxO!px#7<+IhNrJ=AKIni&MVsoIDbI7?%93B_cyk8asGhw9d>?ed)w|4`_9jAZEvyf z?`&_{rQ`lv>HN<22K!Fu40fNN-`QSg->sc9S-R_X*`MDd{~LI2dZzr}fUbI`a^D8B z45WMw1V(m6*s^TX8Old?ne6JN?NhsScJ~eKL0L%Ge7%jeBnn~+7#n{Y^jO=5MaQ@-o>q8 zQLnP)KbA9i8_F8Xc$6;XRkB2}qJ;|;^l*30zeTM?^(06W*TM`ojz^q zjv z@87CE9hTT&O!`0a4Mrjo^pynhEG{EbtGcW+KzIDPD}#Gau|s+W`BFe_KuhY#$PC--k%yKwsO zu1%{JOdm5Wu}4T_KktH8iSRdGJbQfa_ORvteijf*YD%Ihcv2I*5fnpym|Hf>EnC1u3k8CaOcJq^QQhX zB(7U<1K-m5Kl=`&L+|X^xP0y(qld&n!Q)#dzuv6M45)LIE$zemcW+R(N1V=%^~>i@ z89lgPm)7-sO1ppV9hB|S-CI}BpFFT*!?L+kMh))!HQp&@`p%luj|~}R(m#Fr$mp2GHj8(jJiK@F%K765cdT3b*QAjH`+VK%tE#2Mcj6|p zcMPArh|F75+mnZPZ(KQd9AuWx`hDbpKAl?yR4M5ykTH7&T@$a|#4G!^uUj(f_Ynhn zcWN0>xkNslS~9a&-pH>U+rNG7;@K02_wUuQMcqop^Je$TySMbpgF82_oISRG8+#=s zn!S=ot5S(qvCx>oD~})Exqj*Fv3*gR)r!6Kf=bQ z19op(_4l9Sf5f!Wv}XB2@;fvM#L-kkGY#NKlfZ*Jm^O~=!6Yza z98Cg&HOdu|i^gA(JB{+n{o7YD)9j|1W-JZqCU^y*Nm$9UedXH4lSg)MqN!#mjcC8J zp0G()zz7wMzA4QDn9wjiZAx7*ZS1i4?rj@ZD^uVz@4R`9S>Qfqfm3XLnmT4^{I_iz z`hMY^7tbEyor@~3h&&z4LXN*ZCrsFYDm9tf*bmJa!Wj>N{)}U^5{`G-jv`D=>2mjOiLoGY2BsWJib1 zo|pyB(L2lknmlS?-!5(HS1sw5%{#0~U%hzBXO08g*Dsqh=|7;;x?a_i+0l_^cFZcBiOd(wXf^r4y1D;qk-v=Xe|FcaRme2zx-k~x^w`hFAqRmDP12>B!9X&pB~9&p9d z-eVw8*)ZR*I#~ACl>ZFu-JwCnLM}G2PGIj;qE0QnBh8+)eq#E#cK*b^&8hRJj2_Ul zWpyui2OF*Yj-&ccR=V+1A#}L6FP_}Le&P7uO{w!HkLn-M#=mraM;mOWa3bi_DA?N5 zPYi}4S()i+?_NK9c=O_^L#$bT8Pf0TK;Po|?4i!($xGAV{C(ZoXV9|0fA!?ba ztqs9S#4$wPi!IslO>sqX6eh28WahL{O#~zof`K2zGHb*3%=JJ^lMyT@Swh56?e6RYCDfh^OOPY z8}$6HW7VQ=>_(}uKHt=D71yVE#iIH2y0@CVu64V1Yf`&wN>r1oWw3U=)8upVX%W)I zCm=i}s-=IG!giYX8dsfn%k~YuD>jWtKo6=_$^n7UT;1!1b*NdUR%oBN-d%(0mvTg_ zxaquuIyA3TqgD5Wz7d^+>K1dRoB8~Mf~r-*R#aS6x3&$d7jeQap1anoVbhu=>wev{ zcb{$@TL)AG=YYFT{vkmXE3^%dh>qykzEzFlm=tigWUJ0iD>v>D5fRy?U1&4k!szlJ zHTj+W+qeI!&exIQ;oo#<*SevPbh~s*tafc9Bf`6O2n`9U=TlU#!QP*TN8`>N8a4ek zGQ3MjNV`@|>QpIdufbAKz}~k*mo`npA|k#CZXMdTS%c~oOJFj_gJOYU!HrsUiwN(~ zDkQXd<62eA7juS1q^6*gUvOxn2BAG7ziAWPzIBuOepSmC#VUa(Ud=nTYS^kvL`1t* z?Sh*&s_j#yY(Y#NnHrD00bRSd2nzc)^6NGsZJRdw%CBmf0*(ee$?w&s=hv-6yF_#g z4i0J7sDWSA$_2@#M3dj4MyH;g+J<%O*)BLFsA1zeRjZWF3tL%De$QrIyLAZd(Dmz( zb}bqaG;UbGMpf@(u4G53$?saTW!uJ0+l7R5 z3~AD+VU6-$Mcfe&(x}OAQ>tN`W)0i6!~SH`#*J#0E8&?3TWUsZ6Z!lXFv zG;Ua}bg=@C*!*%wWXT#0>(y$~yvbL-zO||pE9is(J3HB6&(~J# z;_2yTkD#-NJLzBpFCR-8>AxiX-K+k;^Dno=s;v3HQ^WV@cPp*+0x#GHTKPpKwQ@wohaEFCC%tbb4ERN9R2G+}!g&_i)bd zmJcGcy&dfr!faUMth05@q0`1w>SSE`t&dp>7-Jx`!_%vZ2PIiFhf zo3;*p5$4h%q(zgu)ykJFls}K1j^$vNr=V9Q|Hf@P^^8e;KENfh_qXkv1yn6t)ZI~! z4Q`OIcP(1MKd@bo*g+$Idp^bGw~>STc5VHYPw7I=QUV9}l2z-q=@$F_uT$qfU*fW0 z`q=OLbq42MG_Nhs!7RbT854er`=$jXfb0^?n*5*ddbF-vDcb~drj70&(Y9XY;(6_? zlK6YxpJRvgYS#cZ1J)BP{`;>-`G1Uv?-E$8B-T1s5h9Ei zON`5%mo5JDm*j3i)k|4R@p4JtWh+;$ST^UkK|Na5EQR&aB9m83@-1DtdUfi`1(SwF zwy9Od&E7K2>m{yBm#3~>w|4d7X~TPk)G6a`CDEHDZcCT1TC;xR`VGrxj_TJTz&o2% z%U7;m_s^zH|D?_vo7ky-`TSP$db`AZ*^1P4|7_X1ZOgj9Ck*J?sG_xG@0R2T$+a6d zZ{N9d$3M%a4C>yrl82S-kZ#$E)$9M+x@*s#-J4fV|Dk8I${r4uINvYvSjx!m+`E7O zzHO;F{pM|Z4jego?!x)Ahc_?&ExxUvr{=>)5 zUb=MQ{E@AT$A1@EJ)6{Mq_rEj?A&|s_~{E*E?>NG6seO!Yj`?Z)@{a;BB)!W-oEGH z(bMNIUAuPqB1_(`Mj&Eh$Dl=O3!UW2etvyowhtUpTU5(YWv0*C;F%Uwpx^q&U5> zVe|Igs7Fw^di^H7@z26B@u4-dd1Dz`{-15T_8mD6z*nx`xOwf;`9mA$jgD(4)2JfS zSh55o5uJYL-b2SvoxO1R%C#Feu3kKIV8fh|{X%OL;-xZs1H*CytJ0Ha&e0p!uV1-v zYTw$KBl@YH6&J{7TbBMY zG`h8)R8@r?)fX0E)2nt@W?s&7@$B(^n-)zP64}a1`sshKVYPbb=yBwK z;bPXMycf?w-LQV)gn>PpRVlzHfGSQhfiW`{Q>CIj$4&tAg^L$2T)c4h_2qm_^H!p&YV7d;`oW3yE5S39JuyqGzpZ;8wea4I3{Smf!`~B_5e>a;0KDR z@wn$rSGHF(5E}nFc_C_SQ4P;N=4IC-k#sEiA8L-lTi^sddaze-G@j5uhlyz&qw!*a zXW;d>7%vn+tSQa2PvsB-z~-e79JzEQ9ufCX?ay1DTek6dk<_AeeC`{6E~g~l9kQZR z+uvxQ_BR@B-aIA8J6q$&C)ZD2MZjXC;q{&K2Vdjr(Yr@ij~_(S+n>MuiA?Fc>nln=evVu+4yu z{_$$+0W3-S>IZaf%rs_buWY66z;1s`&7cm!-#m1Xx&*ubc4Y>23Vwgf1?m>I>B#ZP zO6m<#j<@KV<#=r~bq#j=Q|cY+9Q@5A2dI0F2Ztrx{`VZzrFSjUE5}6 zWn|pgM4dy*@gDCe0C#LZ^%Qpd-O3E=EBwvvXQ;Q>rsK_LtEiJ`KVgGLyY@O=8#8S_ zrr-RBI*Hw^$`teP%}5*~6^IWTo9al14oyBlE@49n@ov%{jJ< zdd#Y>zoEzM8ZaI`X5QrgH}#kcSCt-fHZ674O4wDR#~iqM>(Bk+%UF*|y|-!8*58M+ z9&_M!YU+wPtjE};-dmTNI($0oF{jU`rY`uN^_a~MR;RA|em-5>UOSkYI(;PTG20)e zrmh^4N<9W|ZcR-cJD&BJ{dZDRmkmy(9>a6|c{=Mcr!FDK!K}w@e6R*N&ZcYItA|rl zXN+JyW-D@BHDosR7@p&Azp);(?=H)cdJOJwNKO42LG{sNP9n#JgQ=I;tw$MChyKNS z%#|alsek^=dd${`DEg4U=-T$qw$#+|<5-W8GNc~EGaUH`>oLc#q^ABon0krb_8V)E z;Y`+Jp4jaAXV%ZG$DDq9asQSDi&&3&@aEv|1uI#Pd3t;Q()p~%yga;i{(kB)_a5$8 zfPmS0&8{&EXH$<^95xs|W_9ck^q60M_0JHi4%V=9polJUt7*3k!2ht$&Jndb$>7J!Vcf zPfw3Rtj7dyE`-~jtjBn+4)FAJFUopM_hp`*1zkN^kMUehITmI;=KFET(Vg`eUzTGb z)?;QyP>!s}G+;RvU_Hij^;gKT2^b-%;4W}-<|as?~PPO#PP-) zGc6i9da@o!wp%w%q+tj>F#UX`DD}^(y?m2rF&r8ezG4v{kFlF zm64TYv`c^cD?@W_7ADP9rLy<_6*mwvf*XKAmPsQRTl`v+GG zP7I9h6VqZyQgnwQZNnmm^oy(;Rk~pVr+R||2E`@DMW=*&4T_76PYxJVzjSz1LPT@` zuIaXKX)l%}rC2c*TJD)feA^`Uj9Bcszz4x^Yf}z%g3*#f2~@6RlKTK^Q-P# z4gYIZ^{W}+Un`(`Z7=DsbOR?`)k})%70|wEkVFpO>X+`5l9CwU>pO7ZK%ap%d=ip+ z`})Lq?p8%n1pyQ$~QbBp?^yK(xr)z z@+aYnOH>HOC;KprK9LD=zJtOOef@l@`6@Z%#d@fdfWV~a@RWq4u!Mxz1{`^4pM;cz z~6Efi`&GHCNh>Gbo*dmX*0o7^-RI4uLA*3|V1ML|Vo)Z4AauL%?`PB2JN){@e zwJ6n>+5ImyMpUHI7K#0nVp)?%Mfyg^M#n|RrzE32{7l;;Dl(u~LQ-6KN`vsk#Mqd~ zaB6bj0r62%Lng{CQ_ojT`rj_KLPRONNk>v1TXrYqQ9xiqY(f%RD7rz7dcM|f3r(gz zZW$ODnv~EhCN{c3a{J~@yjnI3tmRj?R;{Ylef-31gmnLH@aW>f!PbGxBPcG;*V@QS zPH8p(gHjsgCRHZCnH)AaG1|9%baF!fq{!%I1JDW7kuHtL;NXCk@yRLS@sZIjn>Ijk zd}3mv0_yrV4XWO_TD7J)npOAnYh0~%le%^No8e~d8W?PBSed>m)Gz}T&@>^kKlM8) z+o&95E4*rvZAelK=Hc*IAq|x}C)r!c!h%eTm}JaSgXN*fr#&_xJ4AnHdRHE)u`z6b zCWa>^M^nw$FD+NSI0^Ec=pELA0pXD}=o&=Qv=|*#&)4LxS}gO|V*V$M#`6|l%|8Eu zeWK$(H?8ulSgPzZNlxgMGB7+Tx^ZvxgD=hYJV(=4%(LcGWo(RT1Ms`1?IQP3vv#yT z*YdkqKCyE9DoeL15v2kC%NOPl|I69)>~e|Zs9JNYE4R9GR0BCmoZGu{t1Cw}kfX%8 zy(_o6a#RC3N}Sufa;qyxHISpkxxFj5x^h$lIZB+{yK<{5M>UY6#JRmIx4Lpv135~Z z+q-hBD@Qeuqr|zrE4R9GR0BCmoZGu{t1Cw}kfX%8y(_o6a#RC3N}Sufa;qyxHISpk zxxFj5x^h$lIZB+{yK<{5M>UY6#JRmIx4Lpv135~Z+q-hBD@Qeuqr|zrE4R9GR0BCm zoZGu{t1Cw}kfX%8y(_o6a#RC3O8nX0<)k{pMaRPfZXjIUn6Dgu;t7BEagbvkLI&c` zIWZ|FJ|(1oN@9O<6GZeljV3fXr9%W9zgUWv@hQ>K@%`iI0bSEMFgA(4=VR}7h#5pF zn#82gJM@_68J*NNJT5w{+1FvX#fkA3;LMZIt3z~3|HP1pzL9w7q6vjVVFLd3f=gis zxEH2q`op1+{+tq()cD4-X%d?fFO|q8qJKcHSGt(m{0{wvBtR}~*B zVs`;?2=ksx#&b_f8JL(7FM|Nch$Q7p``*dH%9p^T_(1hbe2V&|O+;+6@}zC=lmW_@ zW^u6yX2|P@>QT+QNo2p?Qbq7u(!j|n5T#)blI^3syrL5NM>I;fMI~f^&6DCS?lpG{hvCIWje!od&v_X% zEpR69Yw66!Q91T3uS685hCgxtS{nph!5@M%;#X(!$M51-l7Pz3?O!p9oorKdp3U-i$w$JJs3!uK1MG z__I4S9;7=gPlRy6AI2LmwfUzFb3`}4%fJ7hJY#jiNRz8C`^No*3_Is)_*lklzvX`(;7{Cr5jVxGaHBO@XjtW&-U&|bZMqoQg~z6Z zh4(f|l{YdvHa0AJP)f_>7Gc3{C4xi9Noa6M{3O!PoppTl+8*#2=bxTg8kBQ81|gHca)RyHL% zvJbAS;o2oBtUXpqeLaoF!KJrw9U)w&pa&wYCNLp!Fl!U93Xv7P5Eh`0SBvO@vC%0h zRYMW&AUr7wu?XT4!{Y~QG#oR%rE#}#lV-R1$7df3={|Q!ZOfm(ZO7V7Tkx2APkmFp zmoOi5^Ly0IMDHdGJ;J+dsQ{>#Q)VS9DS|e7?Op zp_hRLLUYGQ#juDEG4Upj@Ins%!`Fly=#uwqT;yMfP|rPlG_EH-G&awVY4rJ>H8$Pm z;E7ggw>FNQz{4Y$N6Cl0-{Tt3*{8{eZUVmQl4IDipb2ar<`vmLX#h{6rxX1ga6ZJO zDXb}>DTDaARS_q*w&p8MBTX|+D@}+dOw&d4Ey9lU(ZoS!7^oSl8Lkdkg&ik6Dh?w?4lcCjW?X`Kd`L%_$CAHq# z%G&DMI@$)>X4*E|_S!Dmp4#5pcx`{}Q0)lqSnVY3pW1oarP?*x&D!1C!`jo@%i7!8 z$J#gAbo>;ji_Sw=Tvtx#tE;1HtZS_c({8t9$(znoe)JN*S)Bm6!r~gyGM88qLPk&Z_TmMRLu+3vz%(k*^fNe|LuWftV z4zwL*JJoiP?FQTZw&!i{+kUXKvnyy<-mbP?3%f3MeeJ%t8)rA$F4b_Ro)zRBAz%j%z%5kvc1jmJrTO3b1-gnG!a&s#0RNpDgsjt&;r|C|soc23ib9(FS z>|ENpu5&x*80Vjyr#r89KIDAc`IC#gOC^^;mu@coUByx)--l)7k=AD^$W8O1)U*vPl=bf){zHjpl$u~9Mx_l?| zJ#%$*E$15O8t(do>rB_pt`}Y3yXANDa|>~ca~tQj%QC3ci}Trz*j1|?%kPAR#wO`rG(&bBcEd6uo)Y8|>IF+eWrdOFMW%iVLRkmc=;Ic!@E-!n< z+tIs@cOUO*-Uq$Y%9StIx!mY-|CD=FzEJs=<%g7CUjABzJQW&N_^!hI3g;@?RjgC7 zU&YxKPgT-Y@~_mV(#%RHE9)xPsvJ{!cIDGmY^&6*5?^J0m5Wtfsy41Vpz89fw|xrw zwDI}b=O3RJzGZ#8`u^^F(AQASzgldyh1IV5x%;*9``K@c-<#@{sz+9zS^Zp%JT-!9 z{8(dCjn_3R*Nm|k@-rf48>xb8$SN~Q6uLeCE z%x!S1VX21U4d*w!)2K|Ns78w#J#1XDao@%(8oy{#tw~ao4NcMl0|JKy?rLh&w0YAp zO;0p)Z`QHdpUrLrl@971v^?llbN}YwH{aF5wngg}zqh#9vUtnLmP=c{YE`?{uvYt8 zyR`1m`p?#P+Ei+j)MiVtF1S_jT+IQ{Lby3&%-!%K?k8kdG^Y1pQ+lB7syASGq_}e1i#(ulKN4_56J=XQK z?b*5KvYwgYA>s4G-$%5Jm=*CVGB9#l z`!wt`wa>Gdz?eT{UiWR$_piQb{X+UJ?q`hc9Ge>F5Z5zqbG&G%hnL5_LLjV8tzdfMAfSCh64(v2=-5|F?@q>;H zt~B`9!4HQ74f*?f{r8dI?-^QZ=7S$t1T)Lnhsw96WjB z6t5{`ro8*3#~+8M)|~p+G^c6(r`?*~X8NWXWoJyBk@;tzKhMu>GBb5nv01;)N}C-u z`^=n1b5{LT{I9WprO%C-dud+tc^l`Kn?H4d-GTuN9xUv%@ZjHd{$9GM$fB`}vKGfL zzO|(Nl6^~SFI~E<*s=-Bb<6uNf4rjGic>3_uH3Y$>Z*CE1yjea)~xQo`pKG}Yc8y9 zy>`#KI_p-g_g+71L;ek8HflEx-uUXDKL6a>)MeA@&8;@?+49wv4O@M-F5XsZ+sy3+ zwolmMv}5EBI z-P(S;>Fr~8I^4N>H}dY&d&&1Q?~i8#H$KFD$VeOWvGB(wpXz)% znBF=4VaC8r`^@QC6|=S&S{tqz69uhLa4;hEgDuzCYw^Pq>@g^6i!h}q z(T>m-Rj&JLD@bW{NaM%Ee9}84X{)nqLNRf43={9^-Gw+#X`C{D(rBRBk(3)yfq|Z@Rm(P4C+Dx*FxN)D<|+cvv(tjzz*$Rr4;6>Dgo;C3PQ-zq zL#KKt)IK^TZ`|R3kn>m@urt+MzPa?kp-o%@$yE|9x&^@(2mQLb@TYvEizwj%U9yLy zQ=%_=`69bBC8CtMM{XVD_JRL@_5qp$q(7S@xNyb6roSsGc5N?+|`lA8@e=r4KeK@*&k5@?!7TSVg<5Z$hSIL&A@ z+iGYlMpCe{*^IVp#P77FBYqD{if5ZfRCp&+uC|Yk>K_>`({v-9LsA%(64HN4QbMdr z0o+s&(qm*pSX^Qg5D+tpJr*&!B=?EwmC_73#V9IcB-iAFVxdoTQsda(Q2(a%K}6fpm>1|5dv{!OGPI>qnq0tCi7cZ+FsT!OpSYHqeSt#p;gOodXt`BY7ybh(q#!hrn zEOaSRGFq4TgkVH01yq2RZ#tqV90#vA*39lnR86D{F)y+^t3Z<{Ag1Yn2#YusKn=zw zKp7hxo($@t;iOMfYP)DkQg}SnJEV3QYzF-^H6kft3t4$P4!VD_a`Fv~j_5#5U|Mff zLgyjS6CIb>Cpp;C~yZP-X|9MnN>IhI9GQp+O-fYD zOrnU4jiCC`Q)AKdnD}0Ur048=zxaOgFynNS6MKozVuR%$P^zN=9r*P6} zb`9CZ&g?x}(da0MqYw^~J<8&!jH3pQ+BoXsAPa~_I0A7r!$Bo%g@an8Esl0LI^ZDr zsuPZ{adg4)jdXmAb9Wq6-X6j^Qo8Ob-KTVN+HSa}gYux=XF4d)&eB0N=$h!sbiZ^C z6VlVWav6yZ(Ur>H%~NkXqVbGk?8e{)cOrS#uP zXZgGNSSzXW~I)g}%#Y8b9=1JkvED#1m3Fx|YxKeQRfVOww7trZL(N zhxwVF(|!3&*Wy8AoW6@^x~4;(1Nunc=}c+qKAmZvpnG(tnVzn#ooP;?`*fBMnrr0w zM!u%GQ9fusk`J0g<$09u(VQjEqw<-a_ryWZ=~{f3u6s)N>1^$wJn6oCmgi;pOmjKS znRIlOjxIRU(OEh=;Y{-_cYpSnE9P(N~?Mer=I~~-PbWj?(U8vqDpRaKcKB7nKWGfuaaWuuz z5J!C+b#eIPsDZ;5M@1Z^aTLK(5QjS*!?8hm0>^O8JWVEkCF>=-=64op-smz+?kv@$ z>(cd~bVifAD>NCpOns(ZhCWj#zP}1N8uSLcj}8VqoblVCuY}xJYqIdvXlHaVIHkK7 zoQw`emgvZpgS2nO$@=C9`dlW2OxM=a>StXr{7cE@4AYR0K zws;Y*y%%2Fh(Z}r@H~tX-YQzWR2lDbnH8MMd6y|wyeNLr#Wk-TQN)WMgcmn4daicz zdw8u}r7G3@GHW>bRjX2|T$z$ZJw4p*d1-aJG~w0FjAni+YF<~6EMCgHl8>K%oq7!# z8X7w{Y!Fb#zgm?F$e^HuyDLMh!>=l;wX;nX^pLaoBxJFTWl>Nny=T#q-pC=KVNmPPurA%Y8+tf*@7g&mq(xx8 z+SMwRaVlO|$s}FKWQSCaf+&Y8%2BdR1t0%_#w~)wy7q|b(=XnT=p5ItcSQHjp{*JR z)TrX@U6SR)sxm{!XD7?20xOKWhbQ1vsZpn4P)Nt_QGF9r1`qpb`0$^896B&LF1mZi z;AZvwU8FZCClNpAb3k`-emV-@aKr(uiGF5!KXhmIUKW!C(~%Tw1F z);h0Vxn%y#$zy&R(62{W-c}9$s|XnygyQXI8TyFD^RE}!x?}g=i9<$=n=*Uh@--W` z?%1_^_pTjVH?CPWfBJ-x1N()$g|%oV>pJ_SKB0c(i zH+0m58S|H|-Lm`Ou~X+RT)KSu(uK39j_ljEZs{Ck>(;M(XtRK79Rx9{A!ee?R|^Cu7P+^}rUq>*j|`g98ktc`{$ zh=yX7pf>tZmTyffrA;0-dg9E5t2XR7aO}*bYq#${c=Y(mlgAJ5-?@J2^x<6_md&2% zHasP|OY4UIyn0zhs2LxZ3S3hxaN`#3yGF$i96oO9+@))`?mcq); zHTTmj_lhP3$6)Q;qc19Z(yT?PDEO%h*KR*}LYb$1{FIsQ{ORL|ca;0(QwO%Vt(-e? zcyd%np=#!0-G@;6I}cGc?>>C|l#bs?%yQ1mNdJWV zpWeT5{;1oowF{??7OTfe&HjQ^{)%$>%kipL%fFqK|Ni6WRDR0dkd=jBdveT7|McPQ z%O`iQopC$3Ep_fBp^98xR00(hh4N?7uU2K*gU0*U&mP`%yKrpR zx`orEN~(g1OJH)Yd=9~6^q5PJ1MmjaaJ-2IT4~dmjs%SwK z!!-$m(uc4xnP6BC4CgN2WNjd0FnqudaoJ~OW}p>bJa)Tt`P6<^OYuGM%jTHQ3aj~F z{{n#3Rsb0Avt34mT^6;%o9Av1ZwfWk7_(YQHT#=iDE@L*{3~X~e}nq5&0^K#_Vm75 zJ)K)Mz+$B4e#^4(wHpM5p#P))OrE`%7yi0Q?HllGWk%$V%1L|c_Tn)rhj*d)a4aD; zs;If&$+ql?gGY>?KA*~d{M?mWyz1HLLhpHw-1V$#+}=D_s%CIstR(ensF~kmyqnVX z!M9X1FZ$tA7tM?Q6cuAb72`%7=h|5eu6dJ2r1b6@(yX4E_kFoqO#6LUx9G$nBYD9O zu!29qxTR?!6XVK2KjdaZ)#8?hPJ`JE)iP`RkKaXg32vt5{m{JJm85d_OZjmOtMWZm z?z>`#Hy9ouZyhSdjXDe}<>;<;i!kZOM|2KWvwox$8w|IHXHnw8!0m()JHd;+Ln$_! z%~-MTqe`?{8E$+mu_~EAWz^ufh%Rc*Pf%zdy&cUnj7bEiNtN9tq8~98=qFp$LSvF= z^Nhi82Zh#VWxBCZgt-QTX!)FpBL~Jss5w8iTISZg%oET@uvkk~&1PVl!Lkgukh3Ps zO`c&c9NWEq$;|O12F9rwKetk3@T0uQQ@~uH$fqw|zx(Lf%Qt-LMZSg`s0=qgaHxA6 z+P-Gt^l>8wtJ%Jg%S%EH<8z6lr1E|@1gy}4l_>8CTBx5q7s`7L<#m&$6IKyhRxOw| zZiJdGeiV|4Fqmo88g3XADi-(1=}R}vihCKq1n6d{zT;$oZdZ0xJOvd_U^QNoWzO^i_Uv1)qBfw>jrP?J<$fCIC zQ4RT6HN1IF)o^h8x@Gg!T;BU^r`ZL!(J;h9&v< zzO8GQshNH-Eh>oBd8{t}!T>jV{ps9XkOMPQ&Oasp!-MM!DgxXdkG)9=4 z>-7SU=;L3HS1LGkNwffmF7dchN%x_n8ng0nK9uSd4W)Fd{{XxOhUT;&@Rf$i6oMo~ zGKI>ROLe+WnpTK&U;@C^bI14ZPzJy#iz?;JY>z&sqJ()tiP_A@@&x$11b*kH)pDi& zBDKE4{>jphDnjhzB80+%qxJWpm9V~0JEgsQ{roZO{yR6OE|IhSm1U~~{)$|E1NOsA za@ZwG4oav1e=Tiy}Mv6(5wO?RP#=9`O&6s_h@R{yLAm}=a(N-`bBgNlPOG*AUMkdl32eIto|9VJu*&zv}F z=y$!lhqZ3jpthfkWd_6ICSy^hha#V4!#NpDHmpl=FlUbLbA!5T?$oiv!Px>#!#aLG z6=XtxGCZ=LSmMcn2XG2mFy*Q&!3nvFc%b~+waJa}j3!NG$JT-M{Cz5Tmy#*XVvsxt z5?AC(JbE|~d73lnvM5c1gk}zRb4X|&x4Bct0TQWL0jZLAsp5**JDZ_`>e<1Hq%om@ z3ETck6*V}m{4%O14=`X@Z(BIi4I?tSPY(%Inc`6Q%fS8uu)^61A5&&~EGAa|6j=F( zjFp$A4^FIr3hXY89x#X0RBj?L((=3JmC4Oz*a{2SWDXxv6eNE1%LB)+E6?4{~(<>ywdx^=7PYw(o(77q?hCK+tb8=Ah zUJDq)VyyGaGK?e?C^AewbE$wYipH$MRuN)|_37cpTMs)H*fn9BCU!E&zZprR9aQJC zd?N`ABtbH93<5*mY)}>zP~NzF=E&ZyYnRO7w+swImzS zizNUvO${Fx6SQEd6;1wf32$jOWeZ|Ugrvt8BrFF(*5-IiS>Ro>WR8lrHsCs&;#GH0 zRxrxm9PyV#8#_t=X)tiar6t7AWyBkRc!IQ65MpWPjqmtrX&m#-E9Z{w2j+Rx$3w!3?$+M4l{w~2f$}aZ zn3e4xw)<&i3(N4)c-#W>wTq_?0rR3+BzqBN$P?8n5T~kQP8TS1I|gf*t?bPyI`a?4 zlNOjSpC!zYDuFpZ8_b^s%6ly_n{Iho+qN^jH=Z)VjP2z+*Des|R0VTzvj+a=n9~Hx z+(IH7%(9$rwwp(rVP~vkhIaUKm__5L17E^yb(`;v@vMsZ!@D;M=1ssn>6c;D&ekx$ z6EJg&3lq%$LbiWpJa2{>axvO@8)1g_fVXoUvj1Ug6k8dYg_Sz04rhzmWPi;}ZSm50 zLB#x8#=J|yoSY5j*8=9FOx85RY`Sk~MHBGccuB?l9&;G!0?r7bFngO~ zekD*o#xR4jnSS8&>Vap*%jTGGqi;i?rgr9%C$+PW*?4{-P(ERbSydT)R%7tQc$HD6 zC6d%oTxUS~4^kOGzK#dz7FIw%7f7E}K%0u&-K=cvsXBzm#%oqUlNJFosREjm2&R+R zQ-ShnfEG9Y%6w+yPt_?rG}1nrG0PM*gl|&%h)RVj-dy&Ud-oH8^ckz5t?mp8DuxHf zo2H;eC4;16Ap0|O(2oSl=gdIUZl@rTGkt^Q*5N(lZ4=P4NX|75CYlse&<_RD=Vj0q z+l!=p$ZmV_uJMjl(6aV{6f4$1-xnxzQxc<{*$$-1hN+b;z1zlnR#8JlmsDbc9)i>? za#P04CoGog3QyJ!eCOfs~jjybt&Q^Ap$Mq$F_C0gdB-{m(-RS4` zbI%&zn4^aHenQe$sNy@g)md7H5Y1+8$Sx5t%&ixA;z2QEzrMD36!6G z3AEL%Xu~n%C+ncUAjh8+NI(AqXzRL*BgPC48VupTljM0z9~UUUlt9b63ljz8xM_3A zrq@z;aoCt?j`?3m@>#lL0%dNfBy#>H`WmwLwX*LGor!{3mE@0;BoBQ}_LBS&f$|$O z%%X-$vSYTgF@6AM914@bJNKP`Bgr2UD06Ek3(SIzvtlJIXfH?y$@Mid;>%2T@p+l{ zpg{M%Rm8B|R&5tl^I^dhKx3fl7Bo~6;?K+R`vuC}Xi7vZTMAg%MVsum8w|d%Mk7f_ zQr9rm#TUf*eFEW+=8#1dRdyD~Y!6eD1^p-KT`i<|rme9i#qSXabK5Ixhy@D+3v*Ix zV^}y=v4&U?;sY#%_}v2G3`@i&${NW?RubY(_q$>5QHkt5Ec6!H$?!V`!rX+*1TneU zh_=60WOzc%twAc1H2eJVzf)W65C|JSiDzdhKc1n@p z`}VM^so5qF=9XY)h)uK>suim$%o1Y7ilm$*zkgY2u~i_0Q0?a z#k!;{$4eTEg{sEFQjXoMkd@8Jtm8E?HMX*E?x^Z3%Ako!cK?FHVv|C7r1b_C%%^Ds z9d_R)HnO~dMO&0o%sSu;+KP<|S;Z>N)IbIHDUybUoBUeeNOw{30w_gxjE7D+R+b7N82 zsKvULVzok-8^>`|#`5GeDH;6>R?^m_bq-iIabX+l%#Ld9(jweyM-bLU6-BB-_%{i# zo;9$yYQ{sR^`afMsl6`;EZV!sh6FPUF-ro`-bGbVtW@Z7vpRcugS)IHVAalAFtg== zCDRvEOM`v znMvu(6uR6L&q)Rhyroo61KF^gIjb>`FL_gr1D1?o1i-Rh!b7slGFMD2RR~X(06WQm zfw!b&*v~BGt(!@6z_5wQPAlP#on~1;S6g_oLYEuv8M zF$!Q&I48RpOP$0bh454fFr22Brx4*=5S@>7}{pGW=KHBF_I+#m(}JebQSwQGq~_R@Z@l1ncUp|*qBA8F<-DF za1vBbS-QCj*;x`?xR;Bq%Nw_#1XtQ+Hj`P({wp&A@<~?=Mdm1U6~jOdwGgoNNfJ!0STB=Zj1~O@SrPCJ=1R1sF4@KCp@5jJko`-73omvHxMUZTAO5qlSZX4$ zWEsl;TO$HHGNEI8>N6F(imjjkmwD|lxO11TbK`KW84iLgUO(ip4fh0S z9rho~tJq1)esO&4a-$fgUHYQZ!3ea|6sm&lpwzgMTM(h0G!rTZauLnLUHw$=Ga9-~ zBvgdaFWL@}Nz5M#T@&j;6)iUblAK@Rz32=!mTf?B*EEh+u!#8=>IS%dOjgKp^TATW z4kw4Jx11VtyqWwZqXCWPcZDvuA1o^ZmK>BMzYpd?%guZ3O#z$fSIAD{3(5wgKv*yzGzA<# zkSmlV_f#gP*vfXre94zKoh&5p>?{Vd1iIXO5YGPGdK$4Z6ty__ncuvdxqwQ$FM7dw zLtuT-Dz}%LZZz}MLTWeODm$@2z-bmn` zKvuRLloT8yUhYM{MG->p@Gvq#z~0lH6@7l^I|>sHlW# z-}tHEhwvG}`?Ww=HXc+XJ1{l4sytI5%Yhv*lCq447-0*uh%ZUruLR26e9!_ixrZ?y z?+rVcuLbv{O!DL`2Cf-FVa-vYDSkHnR6~fikxq z%oZ~DVK#TQ{0QU0>>%xwA1#P3G}VcB-jI%MY9&BamC`|zFd zv;}1DyC4hTO#ZeMM;{85x$U48$g<@NcT)yrfrEbo(?N5{Ovhrym|u2EA+y-~0%6&7 z&?;o^%g!Jx>S&5U@Y;Azgsh0*d#xJt%MvLl-xDZv)4}W^OYY95kYB=b&=fNF*f2E; zuoS`X2!v(J!R#Qf-OAi|<-i==i{QEOqC!~S9H&+mwGJlc{LCaEOLto!%ws-fiL8INjKukJ#j`)f3ibB{7v6(?XGxkU~Ulj=RfK%BarchRpO?iM574ZWjHyH$CMYF_Q7_zl*=d$=^fiSli%nmW>9bY*k5CTgsd!S>ZBe8WYJGVwK~& zWxOZTHECkm2p~Jt_Ok+AZYlU#z>rbRINTe?2Qp!9)g!s7h}Qhp_J>akgn1||Gr$xH zivxxZ0xqbuwIuL+*Ci`KsfA@P6?0pDi|yf40$pw-m@Qz6z;*oGRTf@N4c>9p_}Cg? zZpsg{_T+3fOHT-ddEBh*0dKYf_=;p8XbM;o%4t758(Zh&0$pw&_&LDje9HkVUK5v$ z&&&anlIhTH6Z>vUX78v#_$HgEvja?l$RsaZ9(I9=#up^pKoh{S7Gm6pfdI^$r_Jr0 zj|g}Y^Aq@m)c{9L!1j3I#2bif{WWXxVb6m9sGzDyF&kfHfiRA%byIY{k ztpTk99zGVD#PuYjngc!tV?YtGq)O!O3X0#gyqBh$eeM(pKm7t=NaZB2p@r4S@`z*! zSeaQ~$|fz6ufo{jlDK9jyJYEh2xKYNSY;s^4(apAlBORbOffYRg#uzh^c05YFsuO0 z(88WY3Wgv@tl&<$COb>p1hOx(LrZ#bG%uGt(5g}742C_~ zU}gU0Y>!klYL%zRjRMhkW?0Km%rbBrh}wphg#*f7L|2mSXQYNiNi`Q(0)vh(H6jn05 zqJ$L+G=znKo`JboXXAalN}$S346S2@aT2r)w4X$*D~w3_vd6L7&=zF{#kB^pjmU$Z z`&XOo6>_XA1*%z}#fk`l`$#lldP;?lqjp7DRNBOPUyj<9g}`9e_mbDGH9md0K$RO8 z3RtVups6^bAKI0$LiW7K*>r`c;aKemE1-J6fNqpjNX&VD{7BUk)66SznLt$|V)cMs z618g!8n44fLr(~G$}wWn84D-dtPoa52`iwMk+G7X%{`2*S${1NsFGPBN9xY^fMujm zeSZcinf9>`{NzDtLV5sU!k86yd^B7US*RaV2erit%~J>_SA_+WqYi8e6gwMBAYn6U zLmuW1##dgkTSd8I}8weFYVnU)tN$?Vv zbl##aDo(&GiEs!qP7*^ToFWUeP@%~MdeF=sCRlP4hK*=aY)k68h?2%*jTI*?0yz>Z zFwMBN`3lK%45o%cY*2(8bDXOnU=lZO!n$yrWXUJBCvYA=u)UZRflQV@%pH^FDMYy# zZ?w}mloQ&MEi~Avf=3kE)09y&7PbtHsTfd|OZ!Wq$sG-CNOj?P_p3TK%wzOcnk80F9c_4Ax4#A4ns`K9EB#A-i>xP)ShMJe%q=&!k`k) zrE5vXGGRzL&H^SlSpQuWoj9=tWkRw z5^>6MBnvd=f!{*OA(jnzLlNlG2n*1C-$;A;Xj}w3QQ$FoSW!g zCBvMmkmTNjE-Fl*EJiB}gSimWGKm&U)21wE941-msW8dKpLxboxSx=qe3n5ZrYJPI zYJiLb$?%04e>QAU1g&WTlN|Sy#f)H&%SBZQh!4cMo(oM+uMxMnGZ98piXg=jYfBj=v&1t&$wUa^s|B6}fuhiVXt5rvkmQm-{U(iR zTT)ZbUFMb@N*Lh@C@C<59D!0o$qaY#(gl`!o zGr$v2GNBMbi&*>%(iO^a>?Aw5Ulfwh*_4M6fdWcQvVD?&9K)M3FJq`hmW0}p+ulj+ z!F7g`iAjvblo%3Or=nvOJ6a*hJ^qSHC}DcX+7fn^gGWx7xsZh4vo2Q{hiQhA+1&vp zZ6RGo+zFDAC^VCdQe3CW{_Q^sN$%>$!UQU_j|oa}($1!g7on7-Z5ApPQD{%G?PmxL zK&h}@GD`fyjzKq4p-8f}QD4G3NYZBL_WqogNORxciCf+imUBr(wf*Ci~{WcBv5 zLX$i9u^530$`-sWVbMq;FXV;C&zZ@aFfOY|Jr*0kEKCle_`(4bBPSqRc6uU%Zo zhJBx8GiuV30LerJVlC~cB?XX`r5P3=p>w4;u#f=3TJp$}7ArIb=#y!3e?Ar(&;leh zHUN3)23SjQB6?}^p_Vkm$i#X9qs976zq}+< zku+wRpoIE@Vy7u6)tGJaJU~JDWSZQEkA(xYhLQvYVxT#{r6ujfE)$d#@Q5idme1#0 zCFerS7B2erESaM0yywm3c^Z`hN{G0OdsfzG3QcDXC8T*06NsxcjZ7z_q)2=+P42hH zVgLeV(Ng%`3y7L*@|lXZWGFcs!sm1&Lut&a#AaCX?q(<<-XhQt`GRogZZnkhTW2yw zlH{2Rj93CT^{61BvmgarpbNv*bgF^ zp&Sd85c(KT2|G4<<6eLW6$yaE^0HMW_P3rU`d7P%~qL&R!$hJ&Gcp@#(v}>GY zl+CF{m=JB3%|k#`@RSsnj77H*&0StABqXyLcur+4XM@JQLXY0TS>+^3N5*I8_9ZvQQZUhB-zJV zVkGf^!U>vVB>D2WLQ(O=^Clt40wYAdGf>w`EC`#@)1;_+Dn<$(egNjngwcF;mb=bV zh2pQkh@k#}SO%OHnd1Agc8nh^VJvBvv zNGz5B@wF5)FMEjAgv$F0#R-7uBtrzk;%X~Sf;P<(;!DZiO@s(-8jEQ^0W20{J}kl# z-c<++{&lRMmh&klKwPzH&yllNNU!zwy)0_IkV52{Kt%k1{JYZsU%KfwmO*rvGMgiiw?FUaX{z{$5pGo#@mJk=rATAORNjG4&Ufog%3LbS5L?A4T z1(I5E!737ZI7CIxdMZW4GlvM-j+jV!P3E!6lxh5?Lh%mEhw2%7rPL9-@#J@n++fWDArk`_Lbec-5UEBz zyQmPH%?R>eZ)NIUWv&@FeL-rz&3kFbKt*UU+?FESnE*US^4#pnqla;xN(QLR-{%#2 za~M5OGk{U?Lq<=UvvmEAyb?fl6}~0er&$3=d?VvDR6VaeeVoq`YhOy{Tp z89w4MpaU@1d7f0rEoAtLQ++b&hAkCcr~w(iL#Igl=pf*`0<$*?1Y?R1`<38`B{mA{ zuWU3cHz57eG-uq%@6HR0lhS_vOwOS8BYfcciNUawxG00+k`$Rvzz2^zhL7=3eDyV( zw~s007BhTaR`D%czmqZiwD#J{?RODYZrR{tEEGA{D=U*SZ68$#lA~Q2T72dH>ED3l zsFdO3W-Nq&%JD&1{A5F%u)%O%ia{shV?S0yd@wOk_$gw(E(w$NVTIf>hOdk%zRtY; zmO*SgEwN9S(DaN&Jj;yQn9>Nk4f;(VbWhfNB0MqFUY72VLXN!Z%9)}I!iHq>@bQF> zWHz)OUv;xcE!Q^ji=)gOvtFGwydq5$#lA$XnpsUlkH3?6nCPRxtattwt@^o!P zyfZRj6Tv~bNxz*lcPi%%TQ?3cpRM;R)D#aoE~=G-hh$JFz|qtKa3{`Rhss7>jWqa; zwAb08V{8*GxRSVBct&Hy#;Tb{hoi%u1d|`zl@e{NqXEKxRQs}LPP-xUE z@Ssvq;i2)4oE5nVl0i=}C3h2*d z`v>$hQ6##|m;iPO8Vri)%Hi>Wo~7HaklVmV0-rZYUm}B>JZIU4-KLC$0Y5E!f*GP& zf&;?~R;hP)E()3o5nB~%+!;;+=P82&^NCp#;7&1c>LTY^5cwvN-I+PK?cnB#OTrpUYmnI-y-A_Q-QJjB zO>5Bq#on91*->2Q;&<-ILVzql)`}1k$O4QBW-*4xpFjvdh`=F|c@Q21LLl(qK_FlV z7zYPrFc4w08$;M-jD&=CjkZ}di)P>VP5Zt}GZI>7=KjBPs;axH?>=+yATf1e)ZKOJ zoT~HnsZ*z_tGeZ#qsW?^iW5SpvBNhgPJ9elDu=ChYiS~ADF?dvmbs&x3_Iy*!Xl@* zbfKK+fkvKn&PA8APoY*fBQ3YIZsmIA!pJ}GM`^UoETVF*gO<|BAAIup*Kp@DSUJ~&(P*=RzvdU{^eBfY{{7bxAVE3me_RJm$}Mn;%W{=Fb(mD{ zwtL|)Nx3}DKXYf$M#_0r!*l@!f6qO^u>-572b=Sqva(qfh$Yzz%fZn{-d zjOHcCH;X82H}4(O?-|d7AZ$m_r**2jVso6b#CEk{#h$0uMv57Jd8Zu9I!%rA!mw)= z2Yi^2K)^tQAa~-v%RkF0i#Di5)=M~Lq1O)HAck-D9zBD;k78QkH>(T^BVig&vEal^*5hZLIOnFK;Tnrd;oQzvwu;S@&u z(8hDDFi_mF%lM8x^U~WNf0dSB(XfifcETUi79rXOM&EyiUgI>yrhSAk2!R53rKU6f z$|*|hJoD=Hz!PX>f1Z=cvk!7&j`Z3rhppcehIqEH#Nr%BFU%tc^`q0A!f3ad(hGBB z=Uf22ZoB6Z=!GVhU)exoR_Thnx;FA=uK_|Ye4cVF-?01)gy*c6x04}1{H0S=DR-fy z7y4{+l_R}2$ieCW*}%4M8bgzD*^6dj7Wet5csLHd%=fZ?;S^R5 zy#oDYC@J;2^`1wB>&O@VXSZQ^={nib3u=v~p+D(`=@jlfxqs1I@KmR$3IU-~ z>pYm@&5l~^fpNRksDG^-qMq!i1+C72S~EXKRL5bD=v_n%DW=0ecghkQ#~iiB<91CT zwO)D?W@!3f8ZFYktwFk4(CYN`d0%3#4>5u0Sxg`GsZ$g!Dm%BIx?5*Kt?Njw-(ZGl zP8LRPDmG@{yHXBJ2kB}RWv7i4YH_C-`QBFE zoJHS?X0NexD}QY>eeRdTp%zC2PM_j5G5$@B_kal1VF|}F& ztv-C?#Ual;>hzSlr3#@GLQ(Gdq0>NCN^CPB zrQUK%65F)`{l}60Arw9T(yMR4=p9OZG<{KVZ7ZHgx&y!p9&L^`mC9R2A@<~_xp#!O zJsIwnUuq@%(wk0EwDc=Tsrw%P&8zQCos+`>y&Bkdb(Ld-6y2%O9;Fapb2u~c1|F%L zsJ>Q6O1Ja_{K+HEh=&1UD19^d5k$$SE{ZIN^y|(@IBNuQY1h8 z1U3KI>HmXM60P&nbUKG`)P0XX2c2f;&|IhHlS;4$igX%l=#;gPbo%wxAMjiS-sLr+1c>1l>f>gp=uapgFVN=r#4L@}78cDoPtt+*+UO0=j; zsWb!u^@W#TdlTkYSf#uwntu?}#nb^8VwV%CG|p6M!B-TVJtkFBfuOVgN2#<$Q%Q!? zH%x-VJ@AKI!IJV{4Q|_czU>g%@bdE#VZ~Y>r4g;$QgdPG zIL^QLs_W?P-)^_4+@pwiD)WV&DShPF#2rGUd1GvG$B+GVry{HKfygj>XUqiXMagkeZMcAKDaCvSLXV)((j& zOCA(Jrx$+z;j|@{W~0r3Mz97MMr>KviV2e?2rs8Rk5N9Jps_|;$VZEJlt#1;s|)cZ z-z4hVubF_6>8~-BH>=R&gwlD}5v|{o`mAoOWPK2=P;Wuux$vv3PpABfrcg($y>iq+ z$1cY>AAGq)^Fh>LgVn)!l+^jn6X^aNb)0^dR_Q2pXvI}mzp*qR6_E-Iv#5{Fw)qHn^S@@&FAkSLU{n=Cz|gecj{Efoc=G>AuZf&lqW3}-N| zhBhJ$lI@g=33R5?lf|=18**?t2>B6jVf@aWgYu+exhO5VQQ90av>7M0c?OdpGfQ~f z3BTiN!+V3E%vfqTO;v>%i1uH95j{A12Dnizs8ipV6xxV2Q=|gO1o`}HfBIq> zJq_kDjObN`HvLH(C^JTAlQsi$f6u9|PHH2U^U&HFrA?>Q2JMs9YhS=I3C5dmeln|6 zO#CUAL&fm!A8~6&lN(5%`vuLfXxg}ekNE1F(1zAVb@k)V3&V$E{nxcTcA4`2pVRf$ z+0`Xa=tt|~j7QpEZvR~0eAC&nuoczou}6Hy`56%16WP>abP zfe7Sntt)WdW|x1vQwXhi8qa%{q$U?5rb0-M;n&QH<~8c6gwllf@Hm=4k>P)SpL+p3 z-H|@iX_mA)M&KnDIR%`X;%GAEy{TEMRnm-?(&QAS$@-PhWZ_pI4<${=VIsOl(B}G> zElwe{t|>sbggp$VggB6*;BaOoH_3gf>!e%4dvu`4o*mRwpa>$4A?VKFEyZNH=3i(H zj8KGDGIjOy4LDPD(BoyufgtMhEHz&u+M~E5gISNZT^OgX%9_ev}(&AR2sk>T+(1@>!I+AwwNSU|P$i&%fu^Q`oT>imY?W zpcP7ie&3T$aumWQOwv)<^u~uXYdq#FH`z!%(66BPq7f;1NQ|h|tfO=)w46ogL5q^Q zXr_V#rV%uo`g0jE<+xJ^%6dm~M~sEgXzW4_nu%ujIM9g7=%ZY!2wH^{=r=&9fu3s^ z)ZnLSuYWkRR!xPl?Jh+t2 z*-*qt2r1A9=ekb@l@@0>T5Nz-(am^$@>37pbE{~9CypBKI+IqXh_|sV9eaYf!{YAu z`X7iX?hj^C19T_)NC~{cnj#}QX!(ai3UrB3>Qb9L8 z$n4JLkq&2zuuit_Omt5evmkmDI`B|pK!C?Aw9Z6;^gaq0EL0dG=4vMLeBt-spXGKN z-f>b$pcb@qibW1uRhgpW|dFh_y&FS}~OE%!Y9G!!UawPAqPSIMoAqoN~9;YcifN3iFfD7hSp!sR%try-;0h8Mzmo1 zhi@Q=x?6Y)qj%`kikONb&Mc1iuuT$m}%BBG02Me1j*if6*p6p`WlLzXiDEv47}PgwI<#vge2o&wj@XRQbJAEG&qUr{sO*%@ zv?GA21$1jPp+$EADpcpqMacL3lMm1Y9oEuNP^p5ky;=dY9TiU1#;wm!`Ji4bl9!8C zj|8$D6=*oQVO4$EqUnhDWPCw0wZq&E-+QzY=C~@L^H*cMm)=Oj7E7$?k^{WM0osm3 z8y6J0>Xh9v{dP_MLLuK#fI6KA;IltycGoe{0tdX$2KSizOx%HutLw`b zQfvfQamx)?QJ^UAglTHFC^?crS3Vo*gP1y6LTw~wyZ}=w&Bw(V*FH zG=R2UTh_JIWMhPjrxT3&q4Yd+L`zTt!Ry)s({y?8o5h zbB|A>sfO}-nN`dt|)o(1HOYY%`2kyA}dbBQd zWN;7ik<>~}eh#Y73w2@r)KSvhHnz}D$-~W+3sGpP0li6B)Sluk)IMt(e1nW6czoux ztrkssE~KN6ak_cxVjg+s#g8%X>-ztVyz&In(ZPs8N7)>TBG9sK#uO>)KFqCqvnHJu zxS;MNUUc%9Atl#VAU8C#^WVT*CS=_?N~z79#vugF^wJWf;jUk3)TGmr7VaN~U*Ls0 zc-r*wdi9zSy07badxTSW1d3&|sP{$P5vQ};=OB)wxCTvnKBQBKbrPmsF-;|$+HZG`Le>(!F|ERCkpP1pvPc=kiwv*!8Cw}uWqGq?&t-Lsd$6geSAR;W|X2ipI zOhF+Ylz4afD>cbQkR0eglF%M^0p5Uj6esA9`kMO4QiqK`=i;kxy5pgzKi@L>eaV{Y6*ZbjO2F z|9PvDSjsCTR4r+awFu!P#lv`-i*SsG3{=a2KSL8)BSg|Y8h_!H6K=czsdqN*-iNUc z?t2NZgwQJ4GzZI!@oZg8Oo=f9>z%Giq$Nz4vf#UZ;T6~4a^K^xtlrgrfG4zzdBp?2 zjYUpW6pD$c+YOti+p|;?NlTSz7KEDq`Ilce>F!6LuiJ?yvecm!@yZ6OB*rp(-~pRv z(*ghth((mk0~xO55<{YP!!NxC&g9tw$KH#04FlJb{55=EfgFAP{8}=12_@BXktVZV z-td!f!_hQS^FauQcFO%q8Ar*OkhLSsj> zya~;6>NaJT1-vXlVo9U*Rr$0?@6mgBjL4o}jo=n&B58pXiKIq(Avel*Hp7PahKuzF z5=)E=*EN3yL+1x`Z0I~9;Y!ZeBsMuWeB?N~JNG{J@|xXvyg()-RvIWMX&{&j6T)LF zGNB830$5dYt|pR}R>>O{y?L6gy9HNbLOMIq)* zC~TwQ5Z{~`nnYU9rEd2HXpQ$i_VyO#GH3`bYe+$C(^N7aejIZVg*#3Sr)%*?e-L%0>((EJ%TIk=IH2veyQ{>6~z+-T7XAAGtHt2e~+v|I;45m{&O>M_iDkcUJC z2c0Ak{6Z7B#=OlFZomJ@g_`Abb@_3VNAoCb+lI!EF>}vj7@E14BUGy5R81nS5fiPN zd@lOM$3NfUnj!@Sen2>qs z0YM9}vE*WS8+=*AN3@oR#?YdMGzuci{#27kOUvjc50s(S-H*JuP8p4uR`{OixJJN7 zED=j+$$^VrMvnPE)&$ZDGHTBhV&Ntu7J9ZpSxr7F$SbMQBkbNjbYsH@v~xzz2BMFl z3ij*pe`JWGdpv|fE8JsrgbzGbtnP6>-{JvOKZ-8b4M}68!NXFxZpW(W3yE$FFP5DR z?JxZ-dch~s;BL5o!SXM)BAQ!W{htPly#_SZMx~T{JrSq@i0qTCAqp+y&hIy}7kbhEepDD6z1aYghL! z9@n1&U#P|vT)R$usn8WiZx>T{mz?We4^Li4)OIf#QNlK5I1hBrTGci@<78@I`LgWU7T@m5sE%pto%VS z-`mr;di0oMma>$SdEAQX(TKhiD?eR%+eGt4Jc7_xw6-*5D~NCcYNatXw(2)|?G8Or zI`KS2wS&2D>h!;%$)nY8;%1=#ghRdO(a*7>So9;MT)!czejC4{?KbiW(g_?cu_?c< ziKDlWt9#3#%_DDaQ@(&+!XZ}-Q|+oS`6V9DVstRFzjS|{{@-ijXw95r& zCn$$FS{6(<7oK3_mtqHovg3Z-&^rB7G;y>p4&r3H#c1Qfr<;_iRN|eqflm6IF>NB* zxursGMz}@{J5L>$N{*n@|KEl@b#tl9q;T_zV&w;_Sze&ybm~61f>UX~9&L=m>HvE1 zFmON$34BEpxLd!w7hM5g`dAuU3yoXqAhD~HKLiYk`26DLvMKaJAwBl!-`@w3FKZ%c ziJrQ_=rPg5O?7-h153oWcIo{{^g6g5TQ_`6Z<)bTXLf(m9q^mVTpOgNc+O3xaOUAR zx2v1n9OytL@fJOZJapijeQmq(GVVftcf~|GAHy9xUVp}`BVDO&?!6v!KJ}arzOZ4h zw8U1HiV6NBEEV5C*tG-iGk+H^Jfzn*P+jSfthhsyHSOEAJ5J-v+aGwQS>5qf5Q;r| z9X}+g2T|Ady}NgA+fcFa-De-ii^VjbBOXA}!bi8d?R=}qX>+(V;kNspC{cc5Emt?t zfhVMg50T`yy}Ng8UspK&<>#Jy?18&)yICySbF18Ch~B4L0ophm?jq$guz!XC#==k` zd;h*RV6m-b`A082_w-{A-*@M&cuh2|2NUz{Jcy+ig}D{q@7z5MW3RyIamCJyX&?|H(hrXFWMC&I@1D?%uUyW%kFAGvKyzJuh&!OpRdL99MI(aFh zTf)I0lX==PnVpQ!PZU1i>?f1iTA%g?De%C(cin!=%{N?k^_7=hbip~}=ta`4i`NMc_HRd@j_< zix^!!4u!BH%!rG~u?ltRHI@4%&(c)7*U zYDw3>kU60Pk)`ZsT>>4Kh#P)@jdgp&qSv2!@=P&X%F+vRt&Ky5dmp0^gxX~1A$mUBaiMm9c5khmIT`9a0Cgt)9M*irWfxzd ztN>5TNgYuNy{^hFrPH}T%B`gFu!0_t9OY7^6>ziPwLbroXP$WUq5JNpM#)BSkyK^K zV6++q+7Vtm=azy;?gSbh=CKvre8+>Ih^ft^9Y>%@;OK?I@BwZ1_!a%<-aO|&c;T2Rzjvhq;fg=}48{4~kd-d!W$&!T< z;?AEpPHQl@^PMPw)|GeuqyvZ3*H60p={5Yg1dAddJ#hHP=rYh!_U_(L_{p=% ztxSO9f&n`i2?M6F(-A+tJIEF9gLq}Y=tqBk`$N_I4YY%Y4j(?!b@&46khmePOn;Nz z;+?nRK3{t^nXPl5Nj#Y?iKiF$nBuQP?eRo#uQx^4URbp@Y1O}>&FBW@BmM%RWJNva*aj>OeC z;J-wAmhl%}l~&i(x_aG)4Rs4%d*q=9?qy?TH+7+MQ`Ar4es}ThrdNOHk}g(~F8H{- zy1uC~`=clBz31+`?zn9dt9MzDQTAt>r&ny~VlE^x^dRr~!qOFA{^qtxx7>QmEfa5M zhv_J#d1g{7!y_ZmZy>GS8+rGPmu|n}s%x+7ykY2d*Io_l3a!b~AolU(Ifmiwb{AZ9 z>E%~kdF2&XTn4N6G%8VldKH6Pe}3mWJ?F%`@y4HX{smnZ1}-@5eD?BscS4n>R}bin zfqtXt5$2F#BS()JH*Wm+ad6mhK~R;QsQ{g}^}9^egz;`XTDvnCd?UL?1xB7s9OSjA z+amj+4f7^Tpx+6^gZCT^?i>;rJOC|C^m0_K=|*<)L4kgO{wV`a>KfQJsB2*0B%i$h zfAM7O#Ol>R|E>Xnle&W^eodbHzMna_KS#;53^%2sKVhj37N z!yQ%G;poxQRe1_?b*&%0GIUy`b1z@C>qxSQ7L8xgBgxVA)bS-8iC-P*JlvnkLr48j z@#}OYL(i9p^Rs=TZ0}L8gQ8J+be^a`uRL{4M=Jk^_@i_ENM+M?EOj7W%I7_jY(3p8 zQ_ru?_53Q0j(XknYdyb?CFMB_f0RbYvG~*T(zVJT?nrsmpO-h@iUsl0lc!bZ{QsQ&c)I*+2$QRk=fRi75> zI7-ro`HvW>(=k%rpAx_foX$N1sclH@FjBup>XDQq{ti$&4azJ~ zj;0*&cY@MoQ06dYA5#JbWghBtG^LFxK7&H<(>t278D**&~M|gE`iMg8#6F z-KEg-PL!Ow+V2aZ(b~+8lvXYW`*s~^w5F1HTDZhY5I+h`rDb7cF_~NT~sZxO7U1{SfWm&l}{ZhW&Wc~!Txeb9=*4y16Y)XpwaT4j+A2m5$3`)vBO1cYKW^S z1dSHQbfgsc4>K30t{pB~JwjahA!xJ=q$4E{l#UdghgM#6q~vfqkk_QqJ7_vmvO`d4 zjX_6BRtO5M7U)RH3@T*}r>7&G?uA}HJsa;xSq42(bAv(;r8`oV05_uO=+M*Uj+Di? z57bz73O$JJNJ+!pI|?5CKF~AOj+BM42BPTqj-F?Bq|AqPku=>F=oK3sDf3{FMA0pX zp5k?+%tf0Zif)ngK#nMZ6tZI7!gn)e4%sOvdK;sU<`PdwP`ji@J{>7@@aIGQ^tQZD zS|ia+e?gBg1W%XWbUg<&9*k}c??aRNipz9=hYc>%Y0{jt;xb(?wlC4WPNn*Fd7L%P zgetCsMlAvcm+ACr%&)liYh1te!JBYdxF4xh+CGg+JG|-siF&kYl;1&~4|mCzs`&P5 zRNhaW(k1zJYm`^P2j`vQuwnIy&9mjN|U`(lva+&O-7sGd?wEMG|Yo$i{AExZ}XG^YDjYcn9 z??~C>&ta}+jq)*5cKP!_>BN+=8%Is{eZsVz{uQz;o%R{iw)+d1)?)C{i@`fmw)qR0 z)@;!J%(N~3Vx~13G}>R+k+Rue!n6j1_9gRe^p`QM&Y;n*HZr*l{&J?)8dTbn){(N# zU&*v;gGQUlh;yyKim6ow^=sx_?XO{4g+Ze|T*SG`U(3{TgF1&fTmAJ+D>Z1ecZxV$ z{0&SkF{tyIv&rAYv?7B>o0f>P(cjF}LW4?g-K7q_m1+3~O$0T*)H?qvSO>L>6$YJN zYD?YvYUa%~Xv>)9ORe^=mAp9yo!%MSky7bj$Gpo8TBhW!@Nba3Sq43e>1Fc^cF8%%pwgOh>iqXH=PZL( z$+UF;H=uQ5W>#;XB1Br|Kfru53|bBIE%6^>+H`{^f~3V9Y;^gj8MJ!lOXJ{y)(h(8 zHNrM26rk{-%m0NzqqVIR4A8%{h)?Hhkv2V#LxnE?=LVIQaw^aNsX%83(Uw5{(4f*PKv5zE2Ok);4Gt&uIzG%N>vA_boD?GHBhtyf z%^D{K2_}`+k|`}aJv0$6x!q}phfXW06z>raO@vKAh+F(&50#cBDb9l)+8#+0oCiEq zT8X1L_j_pV4kz`<`#jWr8fTk_Mr$}os_4u2dZ@GlLzTJPLpx+}?($G+set0#;i1uE zcel*#9x6R6SDaftGaiK1zi*{i?C|FL3_!ONIMaA`%xj6 z;96tQUI7=i0YO`3&_u}PleCovZ3>s>^HccTg(tmwdB2A(i>Qle%?9lc%-88(z;^X>NMGOfsj4(VS363PoCi zGs~b(XX<)ChV#%P!=TMz8bw;7g{K?TnM_^duV&7r25lD8DAE#~OAP94rmpnYG3O$K zHkWA>X$j6WgG%#B9VyNJM&?{#&=xR_A}zr=-=K?qi z4BBFzr>aF1M)=G!Xkvn?+P{WrGYuNWSVvQ;5P=cj41*@7nJWD2nKs>^srYIG)211; zY;MP;{!L8#i$PP-)n=xBVbJnqJ1+8XW$ILes-mlHO#9rR6-dr}{|=^pYEV^lwUcR| z7_?%^nd{%p)Q=3RimvuB?L&iBCOMb;+nD-+K~>RJJJa4bXqA#P!@r-Y?-^7TUs3GU zg_YrYi>+bKCH{j8wRb3`Q~GShU)SM zgC?e;=E>OW_Xe$%`OxDs-xPx;rlV#v<-ZMzh^=O0GNucqy<*VBl+m}#GD)P=Xm(ZBM|=vC*Cvp4@v%Y z9P^QcERTdEoTCe~XccV1w17@W=%|#iT8sV69Gz0aDkuIk>0MGns}=*AImkDV7A;O3 zXws>DcBM99MvgQ{GitaQz5%Tt^hPHZbh)PmsK=ve?XJ{%%*zq~I$sSQ5WU6EoTmlW z0ztn&1@t;ZgcL7!`PUdZsErVR4WdF3Pp&rTqKyPnt9X8n_*WYAO}-;2AT`yOT8a2D zklKYhv>G`?D+#3fa1Usnj%wNBa@YG}UFiBXb-f8i$5!MxBxRHtIi|@Rn~~$Nlu>Hrn2sEsshdDYkHoboHs~{? zgbm06pTe~$GID(F^PwN9Ie3{Z4NM zi&#P<#+_uF8rA znNmhI{Em>(<;jsHrF6lwtddeXJbALElnOi_5QrT0e# zULz%B!0&W&E7<1AQOgpRvDYC9TRrr8DPbwRj*zg~lcRwpES5g!2wo6J%Oa@Pqu!zk z{GwM&!$?<1K-8?|Xpto>lpg1ZA73fg=&g=~`55yG35cAv9IK>+dD7!h9gc$T)?&3I zV-8v&$)L?UW}dZD3dW`KeysQ8SjSR2Q@^G$0Lws+ZIrV?%9ttXYdrLgQo;=Q9Q4Ad z1>$N$!e)zg@tIx_J20O(53Samda z84_@J5$&5f_Gl6iPx$CbtS(`%A>jy~b$D~M8xlH$==%%_s6%Q;P@ZpCMpx<#-0>qA zdwa_`5G3PpP@Y4EjDtb+!$C3*7B~M5-|8adu(300{#|KM|0uZ+ zVQej=w;J^KB=QwULM7X_2P{ zv96FZ)5t;bw8&A6SXW8;%E*)JK*cdC$&P*(`~+{i&8yHI3>6YBa>KQr=B&@S@iA>0*GKJnzB zisoYZj8f!dgI>gn@4{N2!xzIY!t@ z%AbrpG%gTQvJmkKDS!0jAVp{?uafYtK`&=TG7#+w?zatUg;E4d1|0!!8ChsxA*zth z9XA>98%B;QC&w}w>{6cBjXc#(9_l)X{(BFd)L4r20JT3qIhLu=GHDuoZHmJ2q z3C;jbeW|Y+8S0$Espp}YIbZ6_Mh+Ue2qiO62zH7h$M1|B4NeZ~ohU|0ebJLa+z6W1 z_ND&Tpfz&+vsm+#RLm6W(It&~*qE|VG$!Rz>5-G3q1h=q+qp;dU`fwI10yP*a$q70 z0j^1>ff3Pj90~MzNte(nEuMxNBmrx$4f;x^=Q{lKh)L(CffXEbYMzq=tE!D0G_ayN ztiT^lKKN4UD`L8gH8MxOBZD3{={acNMKTJ3B@KzN&%#3|DwcvW)t5>SoOFJkHpMFc z4%FbVpB^~r)b&yaEcyif5Wjyyr_+#) z$U&N887Cxs!*7K!=|siy0Z?h$4Z~Z5pC)B#p(n}z2J|%^It}^gE?}tu%lD$2WQo8WvJ1Sbix>p$9a24lyi5@GR?rrJO-INCF=A%Q9$7v@T%>>w(Ap zvV>9(oraYpVJAx1k0U+e(D}u%(wDj$683>kk1%xlZpptFcCrsR(IX0zPOX%tDcV7$ z#}7J{hNRR64#E!GkZ+lXPD4@*M3IN+fz-twdK>76{YO&wg0?UOZ6jpP^`z4vmL`y9 zdD8I`rPS?6pAnQ!==f5<^rY|i`LHZIfPU4~iPA~e&phc^BnL|lq<-W{Kj6d8Cp^6b z!0&rV2YobK(3SF*C;gBw6+^d_*FDL!6--C>SiknA`0S>A+8`avA8o{>eBq8~O1e6Z z#`TB@`NPSf^l*P9n`qIFV`VqxSQ7L8xgBgxVA)bS-8iC-P*JlvnkLr48j@#}OYL(i9p^Rs=TZ0}L8gQ8J+ zbe^a`uRL{4M=Jk^_@i_ENM+M?EOj7W%I7_jY(3p8Q_ru?_53Q0j(XknYdyb?CFMB_ zf0RbYvG~*T(zVJT?nrsmpO-h@iUsl0lc!bZ{QsQ&c)I*+2$QRk=f$@ho*8zpIW(>`89Kv5EZD;N($l6Pe{ z?PC?R-L!8v?TaR6H|^U^`;&ck(>^_arxx*ByJ?@kV(7jEz;4>7MJDRCN0WVa(>|66 zVS@o}C8Mn}cGJGyv~M@<+fDmod$irOk4Kx>HefgHQxu^#u!zvgZrZn-_PhLc)4tub zZ#V7hp3!dFx109urhV};0lR6xi`Q5w{~%DYoAzltuidooZm1@^u$%U=@(|16?WTP~ z!EV~OoAzl-sok`1H|^U^`?O7oHZ9ss`)aGX-L$Weu$%VXt^e!LPH3Z)-Lx-on4$LJ z*iHM`P_8!E*-iU))4ng&ZrZ0F#BSOr6zrybLcwm@cVAp%H|-l+{_LiG1CBD@OKmsp z)4L$-rhU6t84ujjz`ZE>fFm0?K+eUJuMo)qU$`~bFv+krtFBW$KsFp)sfD_{i!^3 z)c+K}PFFJYe2F+$o0`2xy$*^-<4SN*(3)2Jomn&#le7F>Bgy(kxs~jP&`7O`M+bEy>t@yE*U^ngC zP5anwXgBTKP5S}?yJ_EU+IRP;|JH8Wx109W_g`1=wsgB`Uw>)AZ23(>yJ?@k0^5;N ziT8@r%L|qpuXe~}nh$R_-+Z>RYhenRAgrD`3tQcx|}g#5?9SxoTV4F6LZd(2AKe2d@ej zobwH;`WEb7=A3KL%9t|?uL&2Na}28b7VJLeoMq4|nKK>l2B+_Jer?dymtYSt-wcCR z!+cBdHgH0HxN=t!}f_U)#9eN(#Kv~M@@3HE9 zD$>ehzCwfVQ|8;@e1}!%`8lWt9@-%mphZGYw#@?;#_KQ&SDxxTB<+S zIcGC~YP0ioRh_ngX^r^Gq3X|d>O!VA_*=Nlxkj0b zm{#YM`eY(S+T!Z|qXHoAwPj?56!dEkbSn zRK4A_Z#V7RP5Y@GSYTi`?b}WJ=J)OGrhOG$M%}cJM?bzR{`SV3C*AwhQ|kQM#~%B4 zoDcB%eJLXCXFt8+<|iMabNmMSe1U*C^tPNNjur^?Az}kSd0c!)+2CNNO8?ZKDZNS=q zwE=4b)&{H%SR1f5U~RzKfVBZ@1J(wt4OknnHehYQ+JLnIYXjB>tPNNjur^?Az}kSd z0c!)+2CNNO8?ZKDZNS=qwE=4b)&{H%^u7(?-8THoKf#9=h`T>~ZSk=7VB3R2!1@F0 z53D~>t-kq6yu>L?HVEuvh2i6~`)?niU z>kq6yu>QdM1BHO~2i6~0f1p}}jSs9pu>QdM1M3eI0@fc`e_;KAY7I6%u>QdM1M3g0 zKTrr*e_;KA^#`go*!aNu1M3g0Kd}BlAz=N1^#|4;sMcWP1M3g0Kd}D5`U8c4^#|4; zSbv~egN+ZYKd}D5`UC3^6av;CSbt#sfocslKCu44`UC3^tUpi)Sbt#sf%ONfHQ4yT z`UC3^tUs{+Kp|lLf%OO0AE?$~;{)pttUs{+!1@D)fb|E~A6S2&T7!)btUs{+!1@F0 z4-^8{A6S22{efx?Ha@Wa!1@F053D~>2po(4K-DcEf8nlo%Aca|kUw?(^#Gsmh7H@d ztLyFbLmuNAN$UDmpRaKne(IXew{6?H)x37;UU|EA?c7B_mA{4RrbX`BX z59&JBqflRI9pd;EgkHOztLgr;^@Viz!aakgxA=Tbtg`#hR`qj}v@_zSVH1L?o{ z-{4_RdINB{bz+^Q{;}#8Egy<=3kW_#<)*^|u2|(NiLY1s`w8&|4n zr&O0mhk|Q)PGY!Rs)qmp>aVx}H~1&T;#$=(8TtpS?50Q!XP)TuT~H%0SkEp0dE<@0 zD0WhCq7<7pVZwyJe{;RK^e9i4M3BkN8#xzC*|9@`AT)lNzKHnPW~sh9^rrs0atj9e zd}rixicoCZ#$Voe<84*jw&`h1`uIiwFya0dN6Qcxh@|xoKGsYuB!d4DM!>pm!`&af zK&h>cJ9@<<`|oZ9X!wcl6ivc23LB@%d8!Q#Dzt_3D4U@y7dBvX)8-K3?KG zrSOgk6BY&Q59R5n2-&yYc;k(~X%R9t{RIq}y7ruee|kZGM`Pn;+qTxL;g9}~4OXh# z^iLBeJiJb7<3OTN@rnOSyFpvnu_!@A&waq^+jCQa<_m z8!9^&Qnk(h7XXYbmsg}!^=%U-Jh{=)6BIfh>N|hy<_Qy?cG9AM5@b&_fPg+r2Fr!#lJx99eWlEZ|>*&|F$+ynDAdp ze^Ys(zf9Y@weFVy;I3lmFEDs-FhG7l>Tl{!S4p#fLucngSDwxbFHSezdd|%gCcMmw zuKH3MLW9pYY3sZR6aI&igLF3&D92YYfk~NM()QBpp#6z2Z3|XhrX)lEY1>%p@$*lD z<3Fqa@pJ2~{R07O{|Pl%+x`;@XYz$5-2T1Y{IPyL&G*eHbN<<0TRimncxw;V9;`iJ zFeQJsJ=peO+kkm|Gukq6yP_4nn2i6~0e_;KA^#=+8>kq6yu>L@`1{)t( ze_;KA^#|4;CQdM1JxRAd|>^7^#|4;Sbv}pu>QdM1M3e|Yq0Tw^#|4;Sbt#s zfkMFg1M3g0KTxf~#s}6PSbt#sf%OLp0qYN}Kd}BlwFVm>Sbt#sf%OO0A1DN@Kd}D5 z`UBM(Yt-;0z)*o1ZVEuvh2MPh}53E11 z{y?<`8y{GIVEuvh2i6}b1gt->{=oVJ)f#MkVEuvh2i6~0f1nVs{=oVJ>km|Gukq6yP_4nn2i6~0e_;KA^#=+8>kq6yu>L@`1{)t(e_;KA^#|4; zCQdM1JxRAd|>^7^#|4;Sbv}pu>QdM1M3e|Yq0Tw^#|4;Sbt#sfkGgZKOh>V z2kg%xU=gqgSOhEr76FSuA3)$xAJFj_Qt{~IW2iFU{i<}nI`O-&2<`?_R-s-^&7RYu~*BciDXkZfoDk z2fY1t2W@NLNr$^@>D?OK*1nSrc*7Gq+Sb043-@}bpH!$Px4gbBee+wB6wJ=CZzGaS z?bpBl3kPNQB$*}lW!Vic2irDK|D-Qtk~H0q=053Kw03q^Nz#h!!-9AG-g)!}k6JOZccVS76=DX(nyXHL1(|cDWr_4Sp z^l#)1q+e$qNJLpy#`Z7&Re>7z_~hJO7N|rZ&`}U!azw)a=vP}3QIwSt z*gf4{9u>y6AN^{B1u8KJtbOa2Fd(aB3wC0VCMmTqC{)87e>u}ycEaGU{&g6XW%7T_ zwmv&yU zl4z_UZ{wp1VB`2~VP_Hwvmz@)O}XCk`bvvTQV>`iVy)$EAK6elDPRWzUm8AyFK+qM z?j#k~=f!q?76kXCcWft_G`k3f`&qkOCuwL00$%IBJJUY+O(NW0ykFjv%)+c}6Ytxz zEh0%qK=&vAzA4FtS@F7k|NgMWBk2gZ_Wh;pD3gx17N0Ecg~DtRjz3AaSo8`4(x1%j zmBMTx(!T#>k?17^*uI}R+DoO`VhHcs?Ze<+GX%nWL9dl&3j*B#W*-Rmo*e-1!rm** zmNaqVBX(Tbi)Ju!;{0AL%@#HBmUat5FCsAUxn3;H78RIiNAkUD1rzOB%wEO&78`u` zD&GIztEJh(rdSAi6@kxswKQAUJPSduB9PgurP;y?ECjuZKxMC%W(%`#3hZ4Q=#|CC ze|LuSOL}=boTAPc8}_wBMQ3rYzvA_hN6vIu#)Socij(JzGd70J6^3x1%j`Smekrlk zC?&=3Pwn@;{sT@t`TK)T9Xw?C$TQA7D~z_^spMF|i7$_aw|450Ve$3=l8{TnJ2d8A zyhm93P6*-_Z;v-rsD?H}MU8rlm)&dM)_+HZxJMZ*-r-t)k6;bUZ1D~YO;QMc8xM91 z*I50wH=o73TX2Vo_M*$O{vkJZSn4&!N$oY?XP#yMx<}Ymtk6d za*{3H$-y<8RExKPOj6wo3AcC?-XUQQnP}XdI4>;T24G%%z1K$O?p|2?c92U7zK(Qx zqTxNLFRvf$6|jN1#akHOw}7`@AM0)WNx0VRyN~0oTevz`$HW0|@eYNhgL6s>JgzrK z^n@@GCInf$y=WTOLC_jhbrD-G&Ta8lphZ8%2y4V_7Vqdds~i!54DUjS9jt;o6y9gY zIkkCQW>+94?ikbU2!*%ZFO|5P;FV- z*tw+)`#ddP$a<9D+INrOi^3e-4lLeLAnOS^7;ZbBckm1Wjzde_iM8*=WiLXvr4uxaNg5t6toqmRlY=))wRmed2cISqd5d>2zTT8X;?1(H zeS0BJ__cA*EZz>j1};u&f=?{o1gL^q9TN<)CeGrmf#Kk;rzQ5>;+By9bLGLiFMhuv3^E~;W5+s7PK&pT zYZ!>!3<)^1csn3Qg*$2nM^yL|i+6Zzyp$f`?XX(By{IPg+JV^-YVmfU=#>+Tcdx+g z6e96HVZeze_sRM1;1=&dla!Obj(1W(jUd$elL+7#q}W?Rq?{IS162J&z!nKSJ-2oq ziN)fr0cD;>g3Zhs`O1)E@s12u0;m>mdT~b&fHVM%Q;xOoV8D8UXRtIg#o}$^k?bpr zcd`*SJd%B7@eYk}vV6FM_ep(o{v7H=04x7E98i9WMyt@Z< z+*}H2!PsZl_+u|J!t{{38a}Lgr_`9z^pO1)HL}KWt^pRMseFvFf zq=ZONI((5bdGNO1-vMfo@Nz)y=7|Sy8=pkOyN5T!;vJ`|U$=rPBi zH;%u?QE*8XS;zbP0`D^{-Z7!wtq@V+eZq++4P?BBpMJ)f@mtIB#}N~xiO;0reKPQ- z2Rx(tI^J;^Zd{q&^|2=ma6Tw7YV`4Et?}15g6YQcp0x<<5lJa2sr-D$!Fw1w%=l}6 z{BcA?C2>Tr`KQ34q+&rEeQ*>5dJa&|IR4O!zs7Mw>cuTK_FyiBH*GK$0U_QP7Ju!J zKaSV{OL#8ETRbr40EIiu;}5;~Yn*T-V-u+3Emmy8fyZC_Uh(+eywHe>8Z4DdLsupa6EDtIwF2+ zIsQ0$id%R21>U^Ffkr%-LK!mr_(L!L8b`(}Nyd#r8E*d z?-AhQed38cq=$hcL^UAgy7-byfBMtQF1ze!-N(dnVkG`T|M5O|XVrw9cG zj!6{6gm_MW#^^C)CE|em{2%}Lf(rn9FZ?bHQAVh=5ZPi+GLh-dRVXxtobVLtFKNI- zQ&!Yt0`NhnVAg8Lu;C+5A2s?+M*N54&pGGZbI&{PM?V7W-<4l?5KaUS@D_l7PXUe? z1%MA7&L)25m@#9|K6~7_AC4an)B$^M|ISNNfr-GbTQU^h!oZbUyJqBLjExSz7XHt|xCN2;sD+lHPoqlJ;`x_lEgC~C^{zx<_CR`R1 z$%rUMB${&QDoS=8Zv=%Dq;Obx%Bet{A%`a)4$ukq@6OM}DI!MUi6JQ=6Sd-k0p~az zCJq}X$YJI~h7KJ%Y}l~jJ^JCU9Xme_fOt=%0UAY~z#EV0DF_!94#4447;!)j%mF%~ z9`A3c!7y})d2JAT`yvV`iW~*oC}btYI1=7?NDsUnz}crT;)BS_;aTVh*p1&0jH^%= z-W=X*#S<=F(W4&Y7hpiplZ*Hv7l6|!QvnVqPG0=vfdk3P@jC@Sz;6CdGcV)3lH7a< zBPn!psMJttk?Ru?;&?EOC$A`!@kUH20GB=mh%@8@^Y07T3HopScS>9}2$wq|-X<|3 zq7jKojwh2ca0hVq;)ulwa+o=Ouyp6{@V(Gg*uI0mkpc>fbl z7=RxzC(sq{41U;u28N{^a|9EC@K+9=&Pn*ZF;fKKBi`QE5zB#Vd!k>WbLv3PUwF^h^(Q~LS$1(@nnxDuaqBZ+#M5A@!x=u91?K$ zDZ;C;Ux90JU?=E%?bpA5a*GNHLT@H2z-hLSf8@#O$B=*L{^?CgRO=Qt-&LJ;Wwppk|U6uJocoU2!sGPL{c|}_Z_xl1~XZaW2Qb) zOyGE_XLvPwra{lR!`I5BxTUyfW{&P$#ULwsmgqUUQ?=uXCuRg<&X^4CLlqt>Eg5A; zl^It>;>sMgXq6#3QkC?O$7B{6S#~IDsI-vP?nYuVWp|@GNnnXz!!R6SlH;p#LWIOu zWO8$V=M+jVTwdwn;0u@T<&WuF2@Hv8;XOub5?3|i%A9z}dL;1}se4@2h%0lC(CO36 z#|X^FNHzM5DOkL}OSihGceAJb#}<~!*Ct{K5sAkZQs0T%BoS|qXwT($0=>j5@vWhB z{1)8XHIGr3Yp7fHgstKMuvj1X@g)z76G%*^2cHj2P64AxqEd5R&`bDK%tO zzP-dC5P}lASGNR-k%cEvS^`DIk4z5>kH8**6n`}n$?>gLEJWc(lDsg|H09lN_e>*e zr_tzI)??=Mc&xJmuP1fy39l-@JIB6OuEOZs(p#0^+s6?4hDb_ut$Lw|L|56$JN6KW z)r?}z7r%zdlkC2Xi=QKECF01?-O{_um*^%LRF9wp1|xK895^K^>$g^sWEC7*Zs^oRftWBk zA*vZ7sawS*N=~;5KaQAU#wq5E$B6RBM}3cvTE=oqQ64RwOXuX=1fOrQRy1XUKj#wfuHUb&>kE%Xe!M`7BZOj3_4}F>?JPQIjM#QjtBT^-7(4 zT1edd5g8&9dkpBYC4hB3UKBoF>KW}8_dpMW)wqaOXgBD|mfwvkeTyt%U%+r|!Nj#W zwyN~4+_c_;pts8TRzQrS(C9cs&k;v)HfxU&NbvoLUtIhgJ=Xaoj??mk;S5eWPE`}t zj$NJDbM^p`9v9MM0ii1&LadYIoP=hLlp2Xu&?4oCB>6a}g-|<0QWBKxMk(E3NkYZD zBeKUek0KxnxjR*k!owx@ez8$%rN}+5SdRs$n)I<_@8g!J5)&mS@i+X~s+p)w&+F84 z35iEqNIVv67Gs@a%$6+VdP8o)RP2GE9pxNP54L-L zQ%AYKy|B>H*eaw zal?l7>(;GZvu5?GRe5KRkiRi=7cN=4bm@}C3+H_K=?Cxp;pOL_ee%BHk$yio_0&`3 zZ_uDY-^XuIRaJFOO>JFWeM3V-V`EdZ__egQw6wIYY-nv=)zY$hWlKXVSF^3`d~vsD zc|d&7h5HKJ758UcxXJF>zJ2XFC_TV>UofvOnTu>|S8x;F3^(xJq2UdxqUA%v1QewDrP;meY0i8n&dlJm# z2&NORh_D)1w+^>y-hg`}!42~U+^bf$w%j2Ae`tOh!@YRH*I#`6r#Gj(_?u@QzcLcs z!%ubahIO-f-&S2C;I4;#H#RkrbxX)wTbi3!Hm_V+i^P`tI@EOg0}^c#b-bxosEji2SiQ;0*4CEda|v*dH~D>cyvZqWCbjHM zfH%B}awh;C*4>JG+uGWI#Mb)ewzl0rb>a5;p3-4m?&5u^&`VKc!MnYCXhucc3pdq_ zejf9V>`lU@a7XqgB}*2iWfa~rVniam$v<&d3Ao|D z>)E=c1IGdGyQ!Hv&BoTomWD>cd*?+u;HTEOP)>32o`LMvo~iLiMU#Gpje zQC5|#o6I{VZ_@O|qt~5DmOWy`*pZ3wCa01&sjZ{l65gbtiMmT*Og>K8dpX@Gyc}r7M%PQcEZnLqup}CRpZhKwF+jqUwXFluVJzL#`Xn5;* z@Ofke#umie@F(J&GohQ^^Nt6ciY-C4Z!~(!TgPD-=BoS z*GSWm@SH~iFk1I$^6G+R>8wEn^Hu}P^$RWS*Jg@w;4n^)n3s<1`5G7|@J*OY{NezaU@Fg4#3Ww56og?BA*fik|zk=YU4(9FH zciWbW4Zw$0L7~mAKN+=HN14tMsOb8m`!X--%1nxir_2!U))?|db)vzoyvcLHa0hvl z=bm}|ib!yWm^T?!Ea9e+4h=3Fnh=LHz*{%KtJK%m)-^TMHDgMpstyDGn(FE*$G+Q| zMjC*hPJmzK0{+iJLG52deGjyfX5t--Qz6Ks;tjE~9t!W^*u+ zf&Xr*#2BX*fLB-4*EZH*xL;daR$i^ycU#*F9>A-1o~r@=0x4-|-MtGB;NdS#)7N_g z+0+!?QM}1-O}Izh`*r=?ppnk6ei^?vfw#XG!63lo-jc%a`esZ@QBc@a*VF`%8{oGa z>gr3YYpQB%N~PXVqxKkzp1+f>@;uHP+ePHTUA?KU0+#IUSC#KRb8s#O~c|n zzc2y+!3-VmQNK-uc{6Yka1Bk;;g;T8S$8T{LiLKO~3~XH2^>N&7HdDJvxsGZ+Rc|f;@0*=8Z{M_ldlE zLj0(1-fvRKkT`Dw%*R)eJAqv{Qh!O9bEpEuYst83~i;ZQ2dt18OMT%_gM zdikhc!R|lJ0DROXudUaW@6mZkOAeK2lp^rv>PDERQr@ItEaNSvsUE}(X+y)CiSs4^ z{7drQn2Tbt>j~|;#@fc}dW>)?iYqEAiU4|Ld1XmiS!Jmae@nnuc>wnfG65eo=8n%= z3;=p?5enR(w0)5tGDPtv>)+DgzPLKbo4o%HW?_Hz%X_4E2z>~iHyPw+e3s)uvT#gF z)gltFp|NHi2P8G+m8F#xRb>@rB}G*gB_*NoZd>&uW!v(2vIlTn{;vn$npeHG2S7Wl zLFfi2Ag|jig!d+)*PBex@cx9nNlhYRle=8JZ-)B@z9Rart*frDs>felHQah-O+{5{ zWl3d0WzmX~lG5VR(xM=Lf`o=2dH^4AdNAUYa`uHk``OQ~b)f6!i2-n^e$$W}t2fyw zp6Qfoa9;;+QeSU)llQRR1S6fxT=+tq1+O=`|3fF+h_kCHIH`l5!h~ymLseycHNZwp zURhlQ4_;DUR9;kBoL^j!Ux;5Y-h12IS9@K`_eO*UoKnXcaKv!o+NzV;fIR5J$%Ye; zc}Mdm>)s{2vao1PYVq{Z+ zproR-thBVGqNKQRMNv*+enC;bfw$BKhmDteTuRE|uz+tSv(rI|bRIsV3$S@+NJG^y z56qR0AvVEc!RZhEM49)~fIA`HY_w>lU#U;$}bsjx{V|LriO-?7~fP@U~;Ous-mg}K$ljQ z6&J#plopp1ugK5O$;&UuFDS@H-9vy)No@~%0sr3ckS1QPLlWY=?dzMFZ$=1Oh$Ito zBZH`EG`Q7BN5M^NVetsJ(C{WRzxcSDvB@I@cU2zY&2W$S3E-}-$Ag|KIFj0m>WZ4O z@-i5BSz&2eVL@?8K|wwYoQyj+FFOp_ocy8}@P0!=0bXce;=S1VN42PV%xo_$%(afV zo<-J`@RrUyrj_+r3k$druhoBT#3tSJCKxgT?`nEBA@Dx467wW=WhFI;w`*!D$||c$ z5RsIYmFJh2!K90F3JMBxfOmFwUUqIIyxTwR??K#u;Tge*XXu#Fxz;D{`7dn-ee_gs z&al@Cay^3vciSES4)3j2)^GRBn`|&v)?-;2jVtS`jgii~uXigp$sxR}s;bXdaE};X zS_h9^U0PRDRaJwCyQ~a7WN~rXigI}F!o1?V!h(Xl<$3v;Svk2mN(miz_AvaFm*?l@zUlWs8q|A$R3De76fc0)XHo94aU*%#%F zO|Xw4j5m3OMmnQ7%n0*Oi1jAyO)9J5zpp3f&5R?K*20~Xp%=|Bqp^EgQCU9bO7ikn z6clFVF3$(rD{`}Qv$9~|8B&e#hn@f81w7?B4Th+gRi9-7{@1NhSyTp5DS0IppDS;o zJ8yl`+v81EQfLyvn_LnGX^|W}Z}JFtmgp`ktFIpp>lSz~si?$6V0l$pSwS((yCkOw zK}k_THYQARbMp!^vvad^VBJ}nOR}_?L&6_v$>g=MAr zMFj=GyRfJrZ^ep&g3O$}tYzuh%d?kdrDwvvJpg+yNy)jVcmW?*qHHjR<9~Pn|C=o_ zFfqhbxcE`6@D~I(ca}lkWF3dzvMH6l$-wb{QDr%vycZW$7m;C?6y+4> z7GxJA-d?dHCvSQB^4zSYOO|D3q^GAZjqXoK-^z2nfS(+DXt!^#wu_UY|37Ue;Gx}< z09yV_K)Z0qi*U8~VULAR@n#N% zxLz5yYm9Q}xOb0)JK`Q<-qP9=Z!*}~WO<6*WI3z?!1CY{?z`%A>Ai;!A6HtA$CKqH zWhKQ$MX+ehaw81P&Pz|v&dkov%3hwHp1EY%lFZCy=?fO=T17Z-do~F0Yu0&D6W1ZI zYk%GZ{LC2KkzN@EH;pRsbDX#7O*U(>3GLWUv^U9R7fplt^2(}Qc#>gc+{1_c6weeu zDn!U#T9}`QiNJ!q{M_8!rCHh8IhmQumoLpsU%G5b`r?cwix%i=MLhp%kQedMxuF3M zmHNpT1M!b~@I48W)BG;wYUaLbi$8;(TUHU{y#cgZ&rYmN*S zLU8jN3FP}9B6^cwcVnbeUXFAY(veR>xXJ(}@Te^5Dy2(WgXD?nf zfAPY33un)pJ(sOV*DuQX&VTg+?t3`qNL&vKgJ#VY*fe(62E8Kc4(&~}m-n#qcHv&x zN?WRVq!Y@UTqL}2n179V6ZR&L3vYtB1S=fM@XY;fVcZlEUW|Afon-!s!hB4VEYDh= zk+}pDQ%lpAElx|%Sd_M8-h#yo7oe}4zhL3qnXy%@=RNZ`2CH(n z{?0V`u%4gkNZrI+FY@zgy!FP?8VJg!((EQ8P4&xP95>wL#Nn?#-%5Ftabq&*rd~kejp`Nv!YPfAH|p?K}4#-i4XKN3N2%u>{5Ct0k99N{UO0 ziY|h8LE&l4`TpVR-c9c>nYZH81BKuIi(b*PH|~VkN<89QGiHYj#S@kr4nCwz-sX2M z3pY2Gv^15tVw)lp61Ub^8hMkS>0>$5?H{dMxn!X}S;rA~Kf8{l=UsB|7c|B7 z33@h95N~G)-t_vF_a8sFbLY;3oA+-Z5qSU3P4wDt7METxy>bP}uU#oDDY;Nwcs{@Q zV!`HBvk(1XuCSx+-}JBr-lE71yzK282JiT?o@IGQ!CO-|ViPo#Xe$}F z$xR5i5I1)$%a2XosjQnUg@urzU=RT#lTXl*ywjR_qseULcMNH%2X}8F=7w`$Lk_U? zQgP{}qU+ZX0RnkRVNqfJ#lj2cE*9ipEGRGl7+alI-ZM3kmimhazguVv{LSsbv9!(*67^38M1CVBGo(UWI8{QdoiIOH9C z7k&3z_ix|2e}}p@SFYW-e(B1!E0?caExmH7sPJ;hrIP$}1x1Bt&*on!$UkG$-?G(t z`(K;uZMS_?TVYHGKi|FwV{q2Da*cO+pzpM7nBU4Cw+IDo-9)Z)XY?zzF&&eeoYLH6 zL1o?K9vaA}Q24VK-#q>1DMoOfJ((!v4dDK{cW>Q)c;nhF{C^9r<+bZKO0R)6V(v?q zE|**=DlEBt_FQ4n#S3RnpFMN-q)~sHt#%9l%v^Qb?Wf^7X}K}zA1vG)KbU2_OR>Xe z;C+nmmYtiJaGTu3y3QSWH&$pUD>kXLo2+>L45?P6|48O9zIldO#gE$?yccrH^~SB+ zS8m_Fj-ccwme#vea;f;rrOQPnC56T3OG?fc6kfP+F8{>Ib7#uZH(_bB_%E2UQGYY^ zzsa=)zQ!Ry>rT^(LC36rk&n!8QI@=|Zt}PKI(OEjAm*F6!cBq*H-N*upFVzp&HK-v z9Td39zWw)7UJBYq*9L*fmFw58T`jtN2?5~cqN2hJ7mEuo6kWJjaPIus{4?kB%jF#u z6uE=q700~)?RVC7#_A>glS6>ko!bq4e_{QLcvJ!I-|CGeMJ61Z5N>VNOj^cK&r<#J z7vCAN$*CjzL~K%NH@QnQ(g6FL7muGle}WV@rn=50-UQpfRogo^ZzDH#{Z8qP+c!(D zUo9=YR$OwW9Z$y?j=jy06qxKHtiwiCk6S!I{ z{P%yi=WTwzS>6@m{s$yY7`G!g`Nc1Obd8%lL00=Q^L};@Il#w{?mf7>1iby&y!~1i z-oA6=R>_SUmrAb|U%pyebm`)S%dnD?g7YN>7ta?U1UwJY`KM2uIdtSK1o9x_M)Dm)+7$ zE*I;w*WO8R)S=Y(+2>7`!>%7chlwED`t;5tT!i!B-h=yT#GB)9ziiCmfvf?#^wO2W zD_1TTmJ}D3Ttu{8eDUJB^9AP$^3UcUgThaoJaOvS@gozB`j@>GtgRnr0V3Mbe_cty zBmd6yCxfnKi=moUklbhaD?OlNXy;9nw=pqO#DaQk!YfQmHvw*IYyx>7cHkysbhM5( z?DHl;7$tlB=oyB}a3u&D%17whJiK?FP*c9j&(CieOq-H`SYElf_~PZ_%SFXS7mD+X z&R)nbIDh&aBJR^C@{gWAdGgTFlgEynI6OQkD0cFWtH1c?fBo0L`PsESEBc#Krlu1x zuZ-1{h|qzC^_o4S4WrnR)OVONgva%ra>IB%u%jRot99fZq^ZEWJU98)6|u>!#{mBH zDJB&^#IWS!$9Iw8e)8~bpr!)cey47s(|!Xz_sgYMiwZ9l6%=2}FDy7;c)s8aBJOji zPn|k*;`s3s$B!I6a`NbrV@La~yZ1XOC2{w^t}Ko-fz~#MngwhIgYBy=<#KN3{kAaW zW7B#Q7i^wlezoQ%#M?X?uepg_K;1Srp~+O}=b){$ag!Z%)nBFE6z9Fi+zN z4>0fY!M!9>H^KJv>vH`%V(qIXmo67xy;6J;{r956^QX?AKYRLAe*W2$u#s~|PoE&( zM~lXB^RXU`T%In zNzR=*ck0Zk(`P{&0zYx$=+PrzA2@vcSPuuhvFrW_gNxis`_T-RG2 zL^E#8JGOHZ=IzK$pl-%(<0jiE3tQ=)&Kx9MQ(6h>`{dPY zMVCu17Z-v0g)9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa z0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs* z5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*` z6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9 zKoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy z1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eK zML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL& zPy`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa z0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-ewlO@pETDp{5($dqr zYNcDZjP9A;Gc$Yi$jZv@g_4ugJ2$s)zkdDu4;VOb@Zcdsh727#e8i|xW5$ddH*WmI zNmHgwnfm{nh|M|~;_T9JNe*4YS=Z_vfc=X`Gy?eKB zUb}Yb>XoZki;GImpFeZr+==5yj~_m;fA8MiU+vz$e&ceC#rp=f=ZTggnV<(LpH*)OwF~diW7&>Cu&>@5J1`QlA zus=%QK7IS-_Q}o7&B;NHds4~D&dTc1qX#(j=+Rx23|R=3Ml2m$C}}B{l4>fQ>`Uh^ zUDC9I7t`^&Wn^^ko|%P`osDu&QTg_pZ@zi<{K=C?cOTxpbL-Ca(i^2Em#!8U7M{Ow z`pk)A$B%q{@X)~nU+&nm_kWru9-o1P0Fz8;OEHP(h zW)fYf5C4cSA*PbnHI20kX5t>g2GNd|l4L5$_9Z1XRX+)xJ2QJ68PI{8PcSLHcW*}C ze?Z=#L4$yNI3XWR%*TK09Pj`B%O43YvE>axTHOBd18MvF@4qA1FTQ#9EgNbXYx-SJ$7{8{zH4d`s(w~w(j_7`}R*i-nMSz`gI?ExPIja z?=M@veA(jVi{72LVBYLG^XJT(Ic@snDU&9RpE73j#PK6X4;eag@UW2}-ETl&XgU|9 zb9&`K&)Heo_&$R%3pt~EH!x)ycSxx0D2PNi;v;?Zl29 z^%IhujKidM!qM?IfX<%)-jda`Cn3-6+qeIK0bo8DG8r~(q8o8AIyRugXXrF{&A_QAYk{-$>P^1VzkgmHdHJvrBS(#1 z;uP+Gg@gh8#~*+Gb4E;?D7;O)zxxhG^6iTkFP=Sl^yo2c`_8>vcdn6@l#~=-xNz>= z*<(kJ96GXh@1EVeKi~2BN83Ky`qAd~AAh7B5=*?)mOgA|>380Cs z1_%OWDQoxi07Roi=^*bq7$w@S#KgqL#?k9Y?DDvD5=rTu=tQTb;|m6F>5+{B;<Tml!{q-<#WyYJ9Qe)Hn_)8~($K7RQ45pdtRck{;8t5>1y zqJpz0PnZQGdf|JsL&H`R7jwk3t>`6&v?cAKA35A=} z*AFoZS$Y5d1L5R@h7A43ih2L>H$VR#vIc5PA<~TdXWx7WD|!Cx$>o4eQpf`Ec#Z_f{?WVClQd zmd;zSVBzfPGv-a3GGpSnaTCXkA2E6;ERRg2f4_kUuyPQ5WcSF1OLypKkr+(@#IyuzB6O57vJ0{)**Gmn~kl;NAK2 z=e|8_=Jc79r%stXanjh)qel!MH4=gAK(OwMma$h(&n#*W5p+?o(FOjDCMq>GS?U=| zrY|=+1HS9{M~tIm2(AVeTuHRzR_YjP7C6H~&83a0w6!no!otGC>5IUJqc?!s0X{kg zHbV|W#HkJKf@lc7LV;p-Hd@c#aPk2IPF9HfKM;0E9do>g8-W}f-+c?*FUUx!k$iOj z{+&BF@7%m`1C8X>l9G!B=g*!!arn@|J$pXi^Vt_WKKpFP=1(?mUcYATx|J)Jty=od zl6RLcm^E+C?3swLCrzC;ZuG=)BPSui9x`x9ULIP;e!a4D&?<5e4NX%+NX`L0YfWAv z`Ee{61v94aIWs2RMuIDVN~|W;Vv_~kwk_QOX|TD3lv_eW+qB`=(eWgBdNbx8L)JoG zPH_v2B^?^4(7XpEPCX$soxiDw_dkCN+#oDiv!bbSMC%C8Mm+ND`SYjGo<4f~1hjA8 zxpm|EjT<*guU@`P0r#0x$BrI8{MD}AJ9qB<>~ zBgmdUd)m~g6UUC8IA+Y~(L;wIdK%QXUtVq>bQ^MD)|u2M;`_;{QISUIVw_OX2y~);h>^>1#}x4%HkzCn@P4Zm)53YJ`Z9$Ffb?x$OUxq7DDOJx-j;b zIPw)Do|Fucr=?K`6u`3)Aoki;KJGvK`sp_iGPyK`ASjHRx6neqc>4U=GsJ(7pzV9N zq3r87u3jrCxqRuu#q;OSo;iN#;Gz9reDV3Vty@3c_R+=-AFf%u`u&wlRxMt>aN)f9 zb7ukf+b3JMY~HwW!2lUQH7?RbiJKDx>)G%_4hX|UYIwBp5Rxnyb6(ngk4Z_Tb zsq!JgOjuK|j+RB+if&2E{<08SYJ_w+CB^6S&lj9Kb?g|dn%AFcmr^QzTrmcO@n#gc`K=FXirZ|Np#>Uw_2Ktm#O%e&x6>em(VHVB9uuXJ2gFvFX!~*Kgdka^>pxSG>Du#lmHaksO&ZdHR$IQ^vtc z5c-WCG&r|!|D0aAx#%@!pfykLlHNHjB`Gy2H94^(bqM3QIV4qcJ4k*hCEcb?NT{@8 z%_4Nhty-~m{fRIYVoTDM486oX-!(Ut7N+85IuBygi=NM?i4F;GL7@{&o=zto-?K0@ zo`_dugcy&YJUKaq^IGZM?v~B_r~mmJya_hxnKy)sAozQRP#Z?_;O=dN+ShNEUb=GS zN@+<^@x{W6XY2UZt|{WOTfhrPg60WZ*D>_H(R#EA-r)+I(xo;^p36A&Cxm# zak6r1IU#X0<}s}CM0kqmc4qubnY{n|SA-Z!CfuTsu2B&EJ%0T7!JS8UZ{N6a>&BJR z($Xs>MbwqNa5n$U=~G9K@7ssm_RbwUKHj)#%lh@}R;_;T{deA7`VLa8Z_k`PW9rl? z(kF)`0;CT-aIA zb|N$vHXeg$B`%(9JTX%9I~o4(*D9S zBx4Xpk&MlRiLa((i4PC4<>}ePRGONK=`_eqdZA!WC#ofTl5m#R&^bjcLBXO&L?TDS ziOzmp9Q8Vr5sep?CGUTI_JmL)+Ixon44Ov1efAVa^6(B~KV-IVUAbObiiA`_F>+D` z=gyx$dGh$-z5DijwQt|<9Xq#g+q(Iq_3PHIUb$?=(nSmAFL-C>+_&c-)Sf*i_0uFQ3}18SzHpqqpGGO5Z59a>OeHJS3DN|90ruwr}47X)OYt z025E`{GVm;{=3IdpK7=Xxh%+#z_bzkVI1Ll>D5wBNL{#az97Hg%yA^Q5AWT-_p{wQ zzx-^=$D2Odv}WD9HLI4dT(WQ>dh9dao{GVPi4(?;A2)LNuptA63`C@z(<{3>G7RbI zox2k3j)?mx2@(@Su{C5JiS7%-hJ%o-Mf5?msVzjInsWPR$eCy}3%=2G>>*KWfN^i) z+T<-$X=D`BSloRGs*h&Uw@DYMcsDJ9!6?mzze z@lz1C1^)EuV}v93?%umydh1&0^($Ad7MB#B&Ckz2bK=mE1BVXm+4cG7dypjAvUTf6 zYu3X_K3u(G(ZY8Z%$+-H-i+CZ|0a!@IBvwaQ9}m}%IlNcJ1?tOb|x&lb2@_Tq!fgG zNQA`3A?D+x5(%5bYBp;b>Iu};9*R(dG%Y2KZ@0uuQntZMY&Bwy8yi3y$(;>N#W8yA zHH{k!;)EQ~`7P-aP|TuhJdol;0B;8ukL(~M4&KQH4tf9nheuBYZQ&Zy?O-+lA^+MXHK0y0vGvm_nt3ze)joiJ9d1wY5nF+YuBuM|HJo|u6TFxf_D)8 zO`A0p1HR*?4IejZ@Zezs`%x~tSFf(!Gt<-45M!spvO6L@4HJ=mjqC?^Vl>vU>%f54 zTKw$m%eG0Vx&5O`12%unJv}9A+!19i25OD92`X(ia9m7hLEC)mtwuOWI4XZrn(=$G zlOs-q#;F@bJx>k~5sQ<;VdCfzlcW6B5%2$a{_yF2<_(1l*0Ak|j~`M#>h|p`H%m%O zFJCGwE;xVY-08C?zdpQg@2)SAA^CF0_8p&W{B-^L%@}51_rcQl7cYKy;erKoF)}t` z!lWr9#ts`bY|zjF{f73*$HI9*y;SrPpp)f?62nBr7 ztu+H7S#z`j&Wu{atUHAcnWRZjQ5uRn<5psP(^MRz*Y-BVA&593Z_-4loX60MXxr`*-i$y>|Wj)hmT11?Mm3pU*#g?C8Ng z2fo^~^Yh(Ek8Ioi(I;zHfB4~sH6N~eZ`tyNi9xy+m+}$%ojY~>*#3hUuG#b1j!$=fylKORwI6@H{(}!b zSoXp4_m?f2w|v2zd2i2|KV#aANz*4!nmimc83qm-I27?nPF5cbJ*Ia`?VO&Rl9Z5u zEEFjliEBc2dpp7On3;~HqTEnzON`zWS4zgvJ9Kt5rgxlmIk~Re%0=G_^a9fF) zQ1YgZyWSi1^rG*4!;D_Ol#%m0@S8Q_X0s_i4irvWplp^e!d3VnISNuLWZ~fvQPHu9 zwyE#`M8rqh)(e~j8Il_m`V|!y<)1xyVE_I@`!QCtWBVsNwr}3NZuN%MYu;b|KIZ8x zS+wBo#TfIQJ8kCNsgq}pojPsum?@(t3?DggXx_lyebHq{8`(9jYkH?n$*JhoU?_~E zKQYKi**^3kS=!V>Qc?sTnmaTcVUvf5UJS$LR*LPK4a9<)6-YNG)Do77q|K%d8yluS z*`A;JWbO?bRKVK@=~@a4spk_6-fhER;IZ*ZHq$hJdtV0)?vEbdNBDU;e%BxR<2pKbj9*T3*TNiZyqM<%tre-W#Z&jO~%KfxA7qHgefIZjMI+2)Bv0i2MX@a!OC`%!uwG zi(AJ{afpVyGPK_?sk^GYA#hFKR^H^`;qBYUBn-0J$#3pIdI)(xdi3PsBLpN5Fdlj9 z+NIK~7^*pU?%0t-yZ7$@a`)Dq7(4iQ{pvNVS1(<)Xvwm7F-->{_N*DRX3w93SvoUE zPnw7U_A#S}BGH;RIB!6|!F_YGagMQ=d>bJNgD-<^bZKH3^&F%FKfjiWc>C!1M9aLYpML(3XcO=IaBYMnw{G9KR)R?T{D~u9AKklW-_BjzKi$4*%ck`k zKKyXS%4JIyFPXPs;o>=OzddWl%<0poPn|Sr?AX!cMopMFd;$_A!-fs$J78e%etr69 zWoGo~+AS@uYf4H=Qer|98cAu}&^9uE?YP%YR-)&k1a5;i!apl+!%7_EuAI2-d25#9 zkoTK*e$oW?trYuB%o&;a|1 zgJ18b>6Kr6wq?Wmwd>y7upX1MmoHrezn=N_%z0C1ygg;oSj=LbIBx9N5u=8p!#*f) zP~Jc^lKuMh>WQ9B*RI`>)K2X}LkN&Id>h#)^lXH-wFnR~i7sw#CuP^hDsGV$k(nkI zcVkoNr;EGNxNY_Ah_~z$k%1fj{g!AjW&Vl`#o$dbI45V}-%;@inI=2=Hy|e7ch?CPh0* zxqHn@I2VPWM2g!(;%449&TaCN%G0*`jX=-^?4hYuK>*MIQfJWPt}m5VvqGtRqS4lI+c`IBuTo)&#?={pOK-8$H|MT6Amj#atMZ(O@_<$U4U(p>pWCN*?*aWO8i zT24e~THc0Dd#OFOJpUG{FPSSe37qnT9HGdN5c-a6AJ;MUk5b=%e0uFJrnHiy+_-n` zM)8%i`;Htv^wpO;Hg4Ly@q<;XS1+0W-hz1;c$_hL&WtHA>e1sSj2<$2)aW52h7QW3 zGGt(%etmOMva&GQ53xv(jLdFn6p#RRatiY7iI}ECMxv!j2sbs8OdIVax|5*IDL{@& z^t3ix3E~n9ZEMV32JK4WmiiWV8f@G<#Tx=|YUWMA#mGH};2~|=MMT9Ve=2eRKTJZq zbN3!nBzN!LMD%z1)S)jw!z9)X8$X0wuUa&B;k?;17fhWtX$Gz{7&BtTh|yz)jU19U znC6QN?ANzXA6(ag@yD#3%$~SpCX26`>6X!@6UGoa!L&QVND=@WSrVen0k1(|;r#{;>W>N8{b}}lAIyHofLPC-y>XFHMn-0OMmi0!ccvbDC%~q8l?gO` zH7*W=HpE)?ZminYX!8ge(-ye7^A3I6h`X$I(#pG%xU1sdGLMt&rUcYhk)}0$y@-Lh@x^L&6&$oQC?VVLCR=mA@*0dQjrc9bLcG#F< zgGZ2QAA08iP1A&N@k~}mcg&5;Nbk;*g)xHAxpPVix{(OBDMvyxNMb1e zC!}rGwv4zbwF5EW6Z)IQO~DnVrpmVxePqVWyRx*Micd6e!5div!p722s5d4B4g&{o zd}CW!nlX%$`4P(wLzmMvg-$ zoi`lUDD}x1fXh&Dl^d?+=z*&YdSue%cl<-NmzJJ}ndO-3hwFvF8Wa9HcBHA5JRnK5 zF-sQ#Ao@1pM4LO4oGRh|4W&v1ZIK7$7?6e&9N{iY+=#pl-i~Q&T}DT?Vs?{y<>yaC z2E`J9<3s5NPFY!W8B7B`80CSVB>Wt>e}Ca3X2m1;JAM4O;n1W`T1b69$f%KMDW!^5ySNAe(*R=>F~7Hhi>V?%cVvr{KEM5o5;<%R}do+&Q;* zW==08QAI&hh|;ZVx2_O1Chki~ccyDUi8GBuQeoJzkyuP3L9|U)0&P=zyPe1aQV^)o zMvEfEP2&mP7ToOI90OY0Nuxt)%9iN*t~PDJ*Ukg(<>AbCx}P{Qa2|GmzP+f$^!KM3 zK*1sHBHIsm`SMp=3ij^Yv0>G_Gv-pJdgRCv!^RICF=Q~VPU@YDR@*2;lxSs@5jF{mq|F%~-eLz3CIjjl!k$qsI;(j(!VYjGm1M z8lXwXNLP2Ii|qgxo`H!AouqA~q9i9L8=59v(~Lh3MyOxIup#XZn9oYK4chv=9hsM+ zh+8*qp0p!qYhwzsoh(b-un?n2^LB-}ozeH3?B7z~Z&?gQ3LG&Z^KR8TFc1M@#CJ;v z(^&MznsjR>|PmJnKT0@6|+J>7pM`}A(Ey5j|I)n!KeW|i4{!| zHVQ%!PHPKUQ>@(~QkOR80NF@rmK3@-_OuOgQ{ENvtbKpduoAO#yH?y4@}?mN!P{cs z}@A#(XIX zJ)*+5*h7Nmb}eGF|5W8P9b5N@QC*DDG8lBp#g+JXJpW1kS<*?sSLeuDj0&nEO83yXq&F$ z8G-BwdUr+DJLw(uTGSO2FEiXS2!@lyjvyB}7{~2^+Ug z+k<8~P_mQk5u*xk>BX>ZY`NCCiMBJm9T_-yi`i|I&t%|;QN#>RjPZnFS{t&22_54x zc1y-fbAZvx#YAIT8ExSPJ1MD0^OxZmqL7-+g`OxHM*ux85z$+D_{DE6Lk16urq5z#4QwZAQxIqqm5{h~bz7{YMx0wJykdP@ zBSOiW97T%>o43?~`y+2fBbk(%4aMbj_|B6Y2|4B#lFvZuAkN?!Ju_`Dl+4Bbv zG>Rf%3*2HTfv3qE($*3s+)7GqqnTtU;g%Ag*xXFAm1w-3X(vrH)J)LKo4RR~y)+KY!trOCJ3;lk#I;N2FnOF3VfSsQwPUWc5y^dQh}9dafy8p zh%{h%nZWYfN$nJvfIG^d*hhqBfpZ?5noI^xyb*^}SjZA5VT*$Y2CO@FtWb zcrMxrV7Djlmrr01A=>mk5z7Mz*y!2_+%%&E?<+=ce1yC!rmZO(2JVcw&6pf-Ye4H& zz}@s}L)FF>^eqkC?BF!4Mgj+L5;z0*Vhh)Z2ht2TOez$}Nt4oA$j=3G%dN&8r$FB? zn*hmL3`tV^$b*u`yh|r(bHv?5#@t3bX>o4UmW#c(O_&^R>bUDw0NnIygR*H0@83`q zDG71K15Nk`ZwZ_Pjv3)X;gq`~jf-ndq{bz2zL978yz-xZU9`^c6qcVZj+Ue zkaXTvrd@%9Sg||LTQ?MLGMh?)dw3X*!WWs4mqK>Phf*|7Gw}s*t^E|l0a65v(6Hdl z8zgBi9AeYyp)Ur-*=U0~`Nu7!1vb7_<J%lSu*iueq1X6QbEp@oX?iYqAmQ33 z*yJKwlEi?`eaW(%q^XIv_?C(bonDE!Exfgq6-7H`@YY&Pt&wR&!1-EOws4_v9k^lQ zq;LjK;fk3!1&V?@Bd0Emu4g(6^hdg`iTdhX^k9VN%3E-=v@yygd+v4tx22gZ%SuSx z_@}ANticMlt!807vNAUI*Q;lWDYf^r+|x&)ZyrjSz$r}8fYX#QiVQ)8J%x#r!Ub~b z3u%S9hKe zwJRWO`Lh{h+1nQQ=GR+5YY)KN&QPFmJtl;sa3HP$Cr^RMeSL{HNn8+zMo}0b%tb>^ zT;;YvES?}ZidmAUl}Il!p{Q`v z!qDzP`Mh-xw}93jlp%0Oz;WfV7N!W`bOD=?xUd$o7Xv2uiKNF!+JdsU#d9HUyq+Q+ z6et^B#eudJwqYcOZ=1ATrg2N$T16{lY&^jZFm~88w%Q-y zGDsm0=Sgk^oIS-%oV{EiCw~xOiw#>k7c!vH6WSVTORq+UEsVsH1EjvBD4LVl&^C;_ z>a-nlbe6Xk5z@)CaMq)7lfca}3WbFng_|u!Sg8aqhzlDh;wG^Zbc3(hq%&bEq-*9a zbZu2O8wt8vnzV(HaMzu2+t8-qieoH0E0I+;q+9WpBXK*u?+r6=8{p!z0Joik3xQJ{ z?m*#S4v}Lnz80wnJKz|Rk!ubExFBspEH~wogop59w6r$Ielq;yj;*tBn zI&z>Dn2E8zrCYU#_{3?_bj`;h-jw}gVN2K6X=|`4CZU8wBg3}^Z^KEns6p(BCIHj_F&M4ScAJs;6*l9BU3n+3Uvxrn2AkOJ6oukGM_ ziQCG%OxSuWNxd6x%MIGXxXYyN8Y{5^wV`eMuwve(aUP>v&%jYX2Hun}G?9TgPc7t@ z(;zPFT(+PVv<@CG8+ITN^5!`6n#)qkpwvCmz2DdHI3Lcw)JJvTc zS+Eq%2emwk2Aq06hQ>t;hR8t_3J+Q0bVD4)EfaM>+9YnXVH>RV5lM}<2AhqfB5m8! zu6PvKU{e9aO8nV4W?3yJ@z#O!umeM6c)HPvZgngNG00? z2L{n@8m@vXPsk3KwswPPLsPtT4{;udwtOaDg71iI9j&Z|muS-FoRpAud9>}t4e&Ik`pw`zz$(c&o`?YuN2KAPa~164caCjv6Z$nw4LK^0_CDjCwQ}kTY+;8rNDI) z*MaLAH>;eRQ60N=gJ_#1O`D=Apj#HTY$G*k^YjKO?W)mshPR6lTMlF5U8bRkHdBjI z^dLo7xb7(iae-Vw(@7d4rY(Wo4z!uIE^4w7-ABrVZIg1WF)E2>LsPaz z+LfbSp}r+x=UllGXTurZ!ci!$V&aCvc~+rpH?{A2*~kf|nYy(bBT-YUh{B4NH#jn} zrecN->ZVUZpNu}CURWfS2+$A^ZJ8@>w_OQ<=BGMycFgake(V!Z$7zJ378)l z3P(=7K?4(UL*homX{&N0E=Rm!E%uxRYhBi?>_&FDO|Y#-QcZ2^jPwnAj?V6JcnC9Z zUEpTaVp|7JIa;f&n22jVd;$(*F>4%`&^RtO`*pJx#BD5Fh+8||_R@BOcDdGVpwwV# z_r_nUxm8Kv08hXzZKocKgO15nBy_$rZfvP*ylp@eZqXL&24aRys8RT%7&oj6J2$kb z$5LIJ2h}9$+{-QRY58c@2U6j>&_#vm7IHHa7fUKjz(S?s)?jYkGCxP(7Od&k%v-k+ zI^hj0xCLu#R?1ofJhD{Q_JMP}s{Jg>+uD6XV}~Fuszm}964$WvHEcR09h*}d6yBJM zeiX5x^ESaYB+Xk58$i_j$&8QdV9Qcd+XvO>$VagEAG#5Z#MvYOwj9 zvWBK&1JT6OW)oE99vjg7P#)2+Yz?(Z+)~sPXuDi-SDUt@i74Wr&hfU8mM6gs+!67H zcF1*=9Z{D1tgvxbHQ45RN1xQH8h&I^mF)u?+;)g;{$9<8mUq|KI{3))JF8)* zN?R084@?T@O+n7g*ScLMZ7XZrAp{M0MVi)XVX30+gKFY4y_`=}g=b5DfBwp8AJO5x z#+vWB(ssmcL{8kwgepT2o4?C&Tk{b6+z7QERGgIds~f_p5VbfrI&j)>$l2IwPuyPQ zJOU&IYTVgSb^MT@Zkk3+r<2)QZ?Ml<*yZ|m_2==J)b0Fcm4OQzw-cAFTMsAh3rf~=Hilk>p)xi$!4B5-t zpj{c*I#`3Xd5ct5ErgYNvF%%#KS_mcCZO8@H;5y9sDm>$;jX zXuHeXvR26MQKeuRPj+4VYW+mz)kOZymN+H`8L$k}&Tkp7cCcjx+D=K_S{u7Z20#+I zt@g}+wcW1FbNRInULbZ^Ka>-|?KEx{IU#cvwFA7CT{yGOd>rFB)~eV&vSnEH#|~gR ze=zIk(o}5J!oilE#%+-^x3aifpqB9fSEM?4-*R_Z>tXlEc_3APrW5A@PqF!E~}r1>KxJb<2qke6CZukO3>*?`s?L}^B=hXQ&sGE!0YyL&Py`f# ziV3I&xMD`teTJE_dAqvDRj*eB6ahs*5l{pa0Y$(?1XPIPB9KlTMzMARk_(@rc)Jj$ zI+P-y2q*%IfFhs>C<2OrBH)OC3c4LpQTr4DML-cy1QY>9KoL*`6ahs*5l{pa0YyL& zPy`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1ZsnTn$uPr)YW?`09KoL*`6ahs*5l{pa0YyL&Py`eKml04I6qlh?2T}y;9sy+_ZgkFH zzVb38uU~G@tnTSuQai@SMMp)2hqVa_3JPr9%FoZ&$H&XNMT-_Kn>TOK+|vWUsE!}} z#?QvmZw-o{7R0Y(%Af7ge>204VB@2{a@;S64C>#zXI4gfmy{&p-5$JygMtED`x9;- zZ{ppedGqF;o*te8H~n(D;$7tvH8>Bvdt{_B@0h6eVc;Dc7|_b!*U!h>$JGe;che?~m7i1x_>h79*iSkW?`RWm z@)K`O-=2bZ6XGrWgnz6}`FEw!gLiH(_(|tv_V38BcA>%4T>ATRb4mIp_lCTo?_~3zk;osf5bWZ6Q7Z=?=f_Mi~c;f5p?c?RuvISxj=G}~VH)-0q3F}+R zTmMx-6`wc>e$arvIXyEoy1-AsJ2E1yt<8Z6(gbhvZ}3KUhokS7ynz37#9;25l*}V0z`ZQd3$kN3Eo3wPLzLF4zvk|0_$Y`p?gUpJa81pLAsZj-c>6pcT6FlpI9&lHG*-oAgb%weUo! z+qSt>yel{*#5+5qYbUgqG12YA!`rq&cb?L*lpSmd-bQpnybV8T06%$C>AM2Fc7M(r zkW2a|-m%e95n*lG1XFlI@gU(QeIr%nA+je>H+nmwy<~kW-gZDM*pb)24-M~hf`20& z8yVgvG#I!snuc5~aLdjP4W_YwGj5JgNZ)#YUio(gnDsx?@MRD1PNDQS^bP+;IOs2U zBRlAUwi4lqHkPK#yCL(oOP?tJ*2y~F7zll5W~3t>8&CdCyk+)V^ya}@MBmh03O~`t ziYXej1Fpu49dp+94(!(_yGOUKos$#eqoX5vbe`;6HkZwtgEkGNarVT_o6N-Et^7pe zSdw3=eG=X-6zRcM{{HA(dV9%fY-A{qH}NLg z2v4Mav!4)dows|@u8sL@7~aXs%t%W~PNdmvnC%mS-h3;}twQpI#u|jaX`)Y4;Ku(v z8A1dng11@Uig)?ye))1hpPVdAKVjaO4be6A9 z*}iozp$+#UUJ)JF|GhtWlb<9f#PMix2)a8wR}=0{V>_C8%iyFj*@@`M>*1imyPo1* z(HZLBw>Qs5PELrA;_f`@n|kv!^#sX5bac>IB6q^Ui3m?Dybaobt$0_+yHD?|3=B75 zI4!D!@RI;BmIQY=_7&j9vx>VD2EgI}{Cyfd-!Stv8nh9IWXd^(<}t+s%kqAjTT_ zbHCobGBG@#loW^AgBVTYaL|{MCoO@SdOAGs1Xq!Wt`3c*fwc&}ZS33KqQTnGci-He zncdSnCt-L8qnF&h-Uv@%Cn1599OUMb65kwo3)~1!NZr(# z2X7RPzRd!6v&OPk{G<WT-neXsXv0mc&86Gr2Fd*@=Hu#T)%-d!w;zASVrqw$c)vkh~RdCwS*%b;DvD9pm69VeRNz67=SIVk}+K zAiFvu70aQC5uMbx$qu?9?ppB^gx}1YCL?3APXNZ|<-{ru2E`l*%2f&8oIMdEPfL79q$wz;*m*f%kEvr;i1 zS3e^cvo$eVjP7LzWMhMZs5j43Avh1q*X!^=1CJ96183BPTc_=Yk)RNUt7%qzyquo6 z__9-Sd~AmfVPPS`;O*xFc{djm?3*I+rhy&PK!cu$RlF;vYyXU#UYT9R+EvJY)1_$$ zziFfxc7o1jW13Xe_$>`MHI~LjJCL`oa5wW2?c>_>sOdd9**((JQk&42skVc?lOiJE&E_&F*WOuKn?GadA2>o{x{gwU=lvgJ|p$OW+}Q zLSvRqp=|n3Bw{I5#lU4>$7JAc###Hg_B?8OPp_Umur?bm-l54pA)z!CnTCov_{NOE zCgdf|TbuMvU3n>P-A&vt5R|}cny&rvbF+G2_6e=Uf$TRmm+)`4Z=_DhPPnNQDnJ3qw{IdH;ty@QrEx$3Qu@u6@nAecS9t-dCXEbZyIL+a!fKXqZ7vIWf#c+KJFN2ZM~<9TO+D=HJ;pGBLW75EC02*`_U4 z_F;W{Hm6AtxE_sIi=I5}gvNEqz_mzF3s2nFzH1?0m4(?5Jer2=H|#sm&yOwxq*|JhuTW;=DeV8C+sHfYTvcO8*4pcGBQTfFj^cCNK+x2qq{>hz7Yz7H?!u} zQU)i4TwoT??Y6XQV|*uz)*eiXi^J#+E=|K|gO3;1gn+)$m8YI1j}cQpr-2+N7H(ny zCnt1U;$<{yHMkoW1JdL{TDmGUlrAOlMstbYPE#5qmgX%+b>QFVR|@wAbd$o}Haw{X zKf&6ASlcHa*;sThMRVD_Ij5@7)oI-Dtp@a;j0Fwc25fg6MU%k^?rKoTWgHr7`|xCB z8oeammewW9A#jLy?&8eO{+97q=yqq!vBA`L6nCJ?cA{RV=!Mc{7S zSk?}2tQ7{{n5@|j;Wy^8p}mB_IT(bSyv3mjshbL!2^CG@ZtNt&!^<$)8r+@PEuF4) zg?-b?Z15AxFS7Oc(k7N6REZ)*^28Hc8|A?Vs-bT8?5GW@-LI2xoe zVx+1hYm&D<#=!cPO{V*ju04IzQoVF7Nn8Bv4u{{K6rE6O$&m?na~hh=MDZ>|x=!t; z?j^1zp=+grG4-2z^GH@98bt6dBEdHS8~R3L$w?DSG^q6L6lBwrXz6dPWZFI=94lAh zS9j1{(yVU;-WrTvdz}=sAZS>~_Z}>?& z$`1PaVz8Jl0u+<$#dr>N;~BUiZgz2ZZ!GcoUy+HJ-Udr+Vzd~3(l(T`gYa*JC+O|) zsvB}Vp8UjY-K1>tZueKX3B)Vf?3RYn9qP_gHWrsQU{N0rk*lJK-VNX-44Z7*a1z;4 zD&CbpNnO)BVI}GgxRe`{eQ53=4b9W#tuh*vEhQ|4JVZmzSt|F(T?>AKrDM^(M0W?_ zH?k*~?Bj{(TSkKvnrJyeMqQuN-5i*_4t7!tyt~q5O-zo(N{?tSF?b2y7`a4m9$tcW z5^W?EUD@1H;^sAIENi8`%t(WuAp4EcG_1=;*Ue)lo9JC~)C~_|+$Q3$L*OmtQhEQa z?kh67(6xXVZ3qpPygfQMr=}jDZDHNq%`rZ$`)^gz z6sqwu%!Z()edyBpR(}58_&q=mBu_9bkI>sx$Vy(5n8d7-YJjWaOYy_=iE$mUQW#d6 z!Dt$q%VroVX5NNzi#Ag6t|DXe%W!c+LOgh5G8@7ZA1pf9l!l5aI-y8Vme;SP6e>59 zcs1KyQj+82qBwnm!Fl{<1JYHP7E95I8TV_EeKk8tZShXSwZ-5~zmoyp7@hZMs`V~4 zakHDehGqifuSV16cj~9KWK9Y;lXx}Tom0RYlLs+6 z&%8Y_+JKRH_7e^G>lm5TmY;M^p=;-Htt%ED#KLS?W`_KPu4Q0-i#YJL75J;swE10V zEe;yJ#I=`nDIl#9)~FFr@u9H=p@P>IcWw9y!f)`VmDwo#rtG(jzDeJ&U58Q#NWV2J z@6~KmcL#pLylHfYdDBoa`3XMP>#J>o{%RPT-vzv5V~`$1`UInwQs08Nu7cNh!%X7U zY~$jFc=}-y=8ct4#9SZN_iL{0+R-<7Q#uw)SGD$~bsm|wXfI!PGpQv%q2EELrNZDR zn9K&=oIQbmzrHr2*X-4Ji1}TRu0nbcKa>#^0DZH6E8b>6F5V_TA>K5b4e^Pg@7K5& zugkj@;uC##6<_PBc)M86c88-M2ISdn#M|uOO5b*@T(JYZ>0&ARog?;dick2u9mU%f zc>w*`?) zA1K}~#aZFOth{OXQt7)wXw`TE^Tx&8_#H+$T&#Fk180RVRlF-iOhLOQ2!%+od=wJecBLAtIMPLGgAe&I%8vcvpzXrBBd#2gvaT z)n2-kukFDUZ(AZY`&jU%)$o+QYnHS)y5jvRC>!sQf**sy@(@bj29g@xQoL&vuie2F zZ#yJ4x|vYj<$P+YU*M?ohmI6tCUE z6>mEvHM&Fbu2H;p2Uoo9kksf7#k)rF+8tc+wnI{*I~4C4#cOwPjW>QwS6nZp#xLy{ zxpD{dreEI?yzy%SYJQa~`Pv?fe|?9RGgZ88(bULegEue#t@K?Zl#PQ*-dN65@iwT` z+?H5=kd}}2_oZJaQT|3U5ES58+Uzbw6Ym#@OoPH9B zA1~$~8>OG$QSk8>;DHO4zGntohLG?yYACW*b-_O>@}BOx$kMq>m(D17#CKwwZaec1-YWB^ zQ>RX;sVT`x9TOAc<6@&bL`8j`Ky!oeHrG3{K-r$kSRQQe_=?`f;B?bSZ$XmJ- zyLoSgH_6G#Nd%i{$3#c97q}67`}weY3ph2t!Gb8`{GJlz1xQHeNo|9`T$@wv#%+8$?8jr9ybL5);E)F~&W1;1NCM zJ11_UqX~6X`}UC$(01E4p&>zm0j=1)5epJ-A@91w8$^JFZmG~l2fajb;fL75y?iY8 zJ9FC}q$9zaVS_e_8)kyG(!j0DyH4mEL&y?TNO-BM}8C6sPMspuPGoA*`jCWKK31qKGdNuX_CAHwbFAvA-146u0e?$+l zv(m-iAEbfW*T=`(+Y3$t+HBp_RMtV<^#FtlpiuGk75BUeN+@!NcuU8b=-AC-u0|#h>R=-S8Y$_?*W-hGb&9fCIM_=B?wF;ZZ_|_g1erE1mARPDf183 zr&at2oCRuP%}&A%rAAxet~0z18W5kkh(AD*q{`|RA}v`1w$wIxiKJa;aMzR4vj-{= zx#YGHmA9yXwS{(_!(ESAIJ#jzuDfB<%$j|hwO#krU9X`7+z9$%mmXm%Ot1)a@oBg%@LrkK| z!BYcM1NRzezON&cjcPjP?Rv}>%5_uP4BZh27w^-8KJ#IX6JE!*%n)jRyN9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1ekzI zd)Jx}6#7?;UIBl#z!mRT%UivhBA^H;0*Zhlpa>`eihv@Z2q*%tD*@HNd=+Qbploct ziuYCTp$I4fuMq)dAFuXgstYh*EpPR1ihv@Z2q*%IfFhs>C<2OrBA^Jo_5@VN{?(ph z#rxIrR`2!-2`F*DLfG}*z<;XL9sxza_Sma;Rs`IbfU*uZK6&b+J4HZg+$l^hc@nW! zyj_A)9pq1ufD-qsJu8a$tL3fUO%YH8UONIWRZ#>K0YyL&Py`eKML-cy1QY>9KoL*` z6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9 zKoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy z1QY>9KoL*`6ahs*5l{pa0YyL&Py`eKML-cy1QY>9KoL*`6ahs*5vUacuiXz}s0t|p zihv@Z2q*%IfFhs>C<2OrBA^H;0*Zhlpa>`eihv@Z2q*%IfFhs>)Hs1R+^x8h$Qy6e zum7fdmHPGHc*9i)-l+d(gNARt)u^$%ltyp8)v&>v^<4x2)o<9SNz-PYp3U8(czQN# z+N4oK7XjevH}!1k?d#|79>vesyQODSR{?mVeuGBMHgEZ4>$Yv%-J@*Vw)K-iEt)lI z;F52^8*jeVv_-AVYpBkfZuM=^^sP5t;Q;j;H1_nV&PS=0*X{`P@oe0nz6+Xw`VE`3 z@UN9RROd}UZ{y#hNkbRZ|Bd<$n|cLQ=cClhYj=eOcr|V40t3L;znOPXt<<49Z@N1& z$h(;<3;^}_@CmNYN2!(9e$^q^$HP_i_YJ9)I#lOPzlsj=bzS{KtMgH6<+XdGLsk7< zrhj$*YemG>cvGc+_5D}=?}82x#Xr8v|Egg{wenKsf6D)q|0(~gN8>it$pX2t|9dt< z3fxqxguz|wAJfLqlQO{8Qh5Y!UjO=9(Ku9=fE(ApdEz^1I?C;&8dGi)jnm6|Z zfhqxT)AN5IseNc`U$2&4K1l&yUS2I*G^a^1LIL~@a8>|rTmKGi1N?owyZz6Xznkad z6TI?g+@6*RlSGyCg=1h5b^i z{>zvDd#;aH3(sav8rO~bf1TbjI^uJq`q;V}LIQB`1$Z6L|JU7;V~79IQva9R1AHCpe>Ai6L7VEo+*F6^e=KX-Pd4@c^FVJhz`Ak% zkM;R0o9h46*H_lRE`0xwpSG#~%botdQvY?K{x58+|L39pK3;nJXAXbL&jq+?{AXMJ zpZd4<^WpZtZp1&f_5U)kwI8mE;P?+YAmx_pq1A6Q~IM4r@x;2*8k-nr3Chb=->WIpMH0)|I3%ZCkgyIA_$pK#D1Lo zsob}}o_p8-UaD4|8Zco z)bIB7-&sBNyL#uy=uXO?4_x%4Vwrx-=G3ZG-|0=29js5QrqoUdb z_;Jd=>gspn`tMJO3~5!xq=zPc+`Fs(f88}MJjh=k@vEZx-M0RJnAtftEYMeGyyb|G z>9jh%>89tuD7#a1+t%LtfKTN*{U$nYTL0r&DIG%ny{eY;F;(APzyHTFliCOSwb17K zRQdF~ZT-7O?+5 zByL*&uhU}NwrYt*@T$@DF#vb1f0r0q0j%nAzc~ADtN+8DqeJ{W8&~`EyX*J=aB7EO zUr$;Cvg%ErJL~^YN>q@KN25v(c50$G-Mi`c|4?#dAQpgYP~Fq-uKGWi6cOl!6)>x* zez*0%10BNyS~h9;W@U!@O{zCG-BkYv62n@f{?$JH?yCO-31O{UU;$L;C;Awt+_L4S z=YM~ESgYn#f2vG%Ah>P)+xg4-^ITWUIjzhCH?9A^IMg5Of6&Pnr81Q^J=bmP-_{@N zpJ*>ucwB$*(0Jv%W_gd;tVPF*<(0%Lgi}wG``bYq10;q)Qji%T7+>tD(GSANkSlRvm? z{o~rT#`<5h`WY*LTR>B_oZfWf`t$JDTUY~*T(GK{Aa|{Qd{}_DM-y88O|JY_#Q?}n z>%TuCoa#>t;MLXo;}?)H{QnlO`&RXF5H~&l2NEL!G5w(lezK2Nys3Nj?-(hjzwp9; zRR1cbg5CA}ccke*9!;7wZX(wGt7G*)m>d;?>0iyT{-bHluPU?wZhHO?V)|PvTJM1t zy+arz214sj{ST!^w`uL&!lN0l`p5OJS`x%f&;OxLF>M2UF#WX|rT~ibU#IGSxKm6! zO#f|8D}NdWPZkYu)V1?}q-#QCC|&(S%bvmj>RkQ1b>szbQGZ(WQkw`@XTJYOGdf1K4ZsgU zw`lHxs~_uJ{f~7|?$9<6OFrrK=L}GtIseBplcU=O(y|}C=qb*BU8_GWfQ4`Z1 zb-MmhSpT6epZ_{t|J<(V0OI^((PQC%au%S|1OM`gZkqo(E$ZLe(0`q*KY#y4{9niF z-$&{nDL`}mD=U9@z5eHPZa4A$r}~@nKXs=5ePsQ;(EmmKt2_YiuKvgR4@3r->o4-Z zl|K1+f&11!F^b}U<2Mf~pZg~e_92kj?I5Lm79k3&+rR~Et+Hes}9z`cUnBwfAhuoN1d$y$(%0n5y7QMW4vR4;c{|>)^N=tw>6zhQ0rTX{m9Lx3R;a{qM73RLUar=L=XD6)x=jTns z-^l)Q^IxSaK;1O{!T3i^y8vG=Pw|sKb+7(esnHn!NAOQ;AJ@J4&&PYDbZFDs2f;7o zkKRvR`~DyAksO8jKM4Lf`l)jD-L?O7JQMZz_d@VPO8{2=>+i1hKbF}sGQ^K2fb-|Ok4}HxtAAIlf9XT(pH|EL-8TPmBt5QOfHz+NMc@9aMt|;Gf2@Dm z+KVoL*X{OCoqu)yWiP(!cijIAdFgKDtLPBd`M*!FyH)??R~>?VT=o5L z<{jjA_1_&Cgr9$M#rMB{!=_#VZddHQ+i$;p@$}KXo28d7 zoI3E?C+k;I<;Ra6+`m^=x3tux#JHFaQIQd0ZQF!~gaii#1qKH25pm>J4nC3}R(%fpUVUf{siK%J#xc-0m^B3QI^Ww#` z$M95oLP3TTbW`uX~JV@;EmEpgBm zEgXLy%=x7`?^Un94K)C0(D1Fd8euKrsuV22+LVfiXLB%U6%gDeynSp!x8GC!|MUCj z&z?Pd`tZ(;D~0Ed9oV&F^V${j=S&$ld|+;7dTPh`==Krq+JvC${(e}=q9rY7M@xD( z#WAt4YgvVte0Ev&VS#VduTQ@tQMH2g=V=-GhK(9G@$hWn?bkXmv|VJ(Vygb{o;`i` z^zr@MSBlOZ*|&4srnUIi&zLZBaNnL?Q-J4A-H#hGtKmFGq5Xf6{La>b!=L>|{I?-MzwLoMwh^`N|ouTbNr zp3S}JE6^_PcQ1eX%SZUuUpjN>i!JNlU$S8Cw8>*f4;|dMS4LWL0%{-LHZ<5;d!EdL z+MB9fOI51W+v@dii28dpZ|URTIw*YF%isU({{7O^`#;^V;+;9uCygC3B(Gm?@2rgU zPDu$dqV`ZemM>u8(=<++#ZKq`RaW295LBwx#*?-B^A|ukKv2Z-f4f_H`iqT=W+S{A z(64u|o;|vE>)NGr3j97MDl)ts+HgAgxO@{!_;T%OCYSr2d|<%&V*OJa(zqFY0sI5Q z3N|mEIdi-k3wCUp+^%pGw8~^~q!owpX z+P9C2>JW_*6HS3_1O+~DM?^*< zqz^|hk5eyV9kFgBuU(Gj-i=Q_=|CD_eX2h~004jj{HZ@c2+%ufheEMEbdOW-=VPh8 zX!TyH>(hsJVbKN9kN`9#0A4=S83+gjgWwPpbZUYi`&Jy^^YGL>ldIA z13*uJ2yo9ZAVBoyPl#wDckb=+TMd>w}IR zE!T@UAK!bU_LfG^74e7*mF?uA>;TYrfCzY@%HBRyyuHx7!#s9c$HRr_dNM@G7JD( zCIKk{5b*FoQpJ`ZpUosaUn{oMPVGiS=q zZqU+&gC71`iL~$01&^>L_Rg2qk3GN*PXmw(1jL6TKml#=ctm)j0gFd>xp2a7K&vMm z06hv2o`?8gr2s+N0^-9Q;ZGG2Yge2fQB34bJ_5vNA;J?Oa0A2uHniK79gqFK;jCVW z0MZd4Jq;3{iNT>DZKBf)_juw_-;h=ho{NH*yfE}20}cb&(1StYa2ImKo|nVEeO`~A zKw8-%oI!M_DQzzVOo*1~f+-c%a}3m-!S;Qk{uT?i-%2QP{VYj`_@ zp5QG8aB%R%dyyt;-h_LSyiuCW6V2+HL>Cys9i|w-(Fe{=qL_Zdb*U@2X#0!2#Sb&gz5Dm-soeJOmxqK zUYi2oZQFI|h=?QPrDbJhUF&TtkFjN$M)khc}1M0Y&0~ykn?H8U4lZ7!h``ay3ijDN{UWN-rwFs~U(n@^WAf(9zZ` z(ye)<9aQbj`kU2B!x%N8Nt#kLt)$06xiHO1-tv19!kZy*VJIMnNG^VR1YQzqFdNOu zl#y|zZjEjsVNMHNxDJOqAMs@z6j&O;`$+Jm8uZf8#00nP90rf~Xcv+6vvK!HPqRRPX7$~?p4BIf(coa$zZ}`v{cndF? z_@wu2MB?~uE1)jCcZS|Q!y9_{&U{n1Q0H-PqjFpyRwEyPLQt1X-7hYV0clOY$ zdx*NQq1PB2#D-B2s-hy-7?leROe^If=!ERxq>ICQa_B8N^sXTKBB{h(fN%H@yiS@Z z8%@ACC-amO#%(Rvh`>=uK`sta9KDi;-pP|*O_X2JMD+0{e*YHdENWDpERG#fGcz_M zS~)Ez%z9d_BY+dcMds**P|O{9eE2O%dUKX;DZEW<{QYRUvF$mlWFl{8X~&SjF>&d| zONlyu2i1c5edy(U^`=**ZbRfFX&y;i$%Nj0wwlUmiv@Kb!J;8(W@Zc5#gJAEhr(;bv&4ByeUF+pt8G1a#o)6h%4svC}PXm`M|foB4!sfkc_7@iOYR zg1YrKo)ulASW!}Ri)eBd#tPpge2ZwNmPmap6R_OSj@9cK*DJHbcGxi^nn*;A!=_l0uB&#MG#^{9RUd~P#oEmV=_o&ChcwA;1*;rF7@a} z_>9Wc{VnCi!Rk0Q ztu4YrD+^7m&s29X-9qbQ)?-Uz=1NjY)n#UN9z_^HGm55wn4?)8(M*$!wx4F8`Yfo2 zMMP^d+J3Re8z(b~Xc`eUjyc;T*FS!+|-_FWsKaV4mRr0HAa(9 zz+o&wv(%_Q79Y10s~Zdp%d*gT|ZWJr&51~8VH(#=;juqVI<=*94&@JMNX!U z#!b4LR7F0`SoWybiRl=pilhuxMl4<_S5ibZkqlQ)G>w~NBF-7ZtIOQ$(ihv0MvpbS zm{EPL8{2nUb=Ip>6zf?zU?SF2X5+;l0jq9gwO!;Zv~Of(E-|zr4<#oQGZtRg(d$JF zkU&X?RwA2`69gl@GNL;Aq-_IRn392QgW|9QM&2*-9$_aDL4#d5Fvl1SVKTu8s^VrMCUdqjM=Nbi zOvI`xPNre99D^Ae6dMjCw2!ot;b`$w;nb3xvQsy*^)t)*v8s@&Z~Mwj%u}(Nr|CGR zmFDv^tF>uZGm~|$b7anSH4<@u(+;MUbp{$=0|;wPlm#csJj3ncI;?~0N@cLGb*oxDb_VQ0&G`A$1U1k@yt7(=N2vR`tH|&n3^obsLH6kzV}Lk z+SlULmL%xX#A#b!j`fyx08(cobsj)}@;bxHMRG#Ft68a8SFz5lh1tjnUZ=Bcogy0+ z8HJT7y4W!BXojrw%)<;2raGD_hv_3&blz;3S>ftBPpr-Z;F%m_>O8ktS&Y>nZ-F-Y-SfZC&rE066-Kf9lFLAkpmNPz=XoYQDcf(mQo1R z+O;VrSc)KE7w%fZ21_ag7;lAz6Dh{q3CfBM##>?GM2hitg0fuDaP9g%8CufTVdfuit%=WvSNerR#-TZV!WN8tk__@6&6mU z7;h&iD>fK!g@qF-#@h+XiVenFVc|rI@pgi;VuSHkSU8blyq%z|*kHUB7EYuXZzm`# zHW+V(g%c^p+X>2w4aQqx;Y5n@c7n2EgYi~aIFVw!ouI7PV7wI;PNW!bCnzg67;lAz z6Dh{q3CfBM##>?GM2hitg0fuDaP9g z%8CufTVdfuit%=WvSNerR#-TZV!WN8tk__@6&6mU7;h&iD>fK!g@qF-#@h+XiVenF zVc|rI@pgi;VuSHkSU8blyq%z|*kHUB7EYuXZzm`#HW+V(g%c^p+X>2w4aQqx;Y5n@ zc7n2EgYi~aIFVw!ouI7PV7wI;PNW!bCnzg67;lAz6Dh{q3Chd{BftnS0*pZ75^(W0 zZ7xfHR*Clpl0r6Y!GO^R%YKG0*rqg_+OX^0LPzw{+}CkyTr{f z4A^OZN^Z#GcDv#13X?FHfvxuQlD!_U*W-aH7)`)V`<2a7{b=)fVFm^hu+@Hx^dQ=j z{6518*lEA2AS)HNlw`ORK@-q(0XyxV)4Flym|NOsrl+6@ASXl{JqtVSPj8!7{%h^N zbFyFpelKzXXYJ4ESnx4Av%JziJy`+Z-1=vAxk@~-?dh75B<}#{>{pNaO**ljni}+D z2XM~*+%F~bE3)gwzOeIQ3P*ZmrUvVQzqI<=%Nl3E{&(L0CHpIzXM_LO!|`|YD4M~M zzD-E{4#uC_qkn__D{}GjY0npPcE&$2{?E}*n+tm%9ewp+zxLNYO|#NMeh*v->6zzj zUVrUnrt~?x?XPIwI8%zfy34oO{^2n6?Oy+td1k(kh}gRSKW)`!l%4v#=hmv8Iwe{O!N*POn+{x_{fiYLtSZC!s=^HA6az;^oo)Mgde0~4{=K6?IS{OkvDu-863e}V~^ z9fP*oZu+x5J^He`Peycb8Q(L43eCXku$quk{{Z;uHN$~Wq-t13nnHdZi zKYA0wfSv0%=! z87U#@$y+_xpWQJpGc7queDYQg_UCkNESg^Gl(o!;^B?-; zqh9RKE6s0|8iMN|!=umM{XegyMOI1(uK()EeodEVnaSY(^=1G3qTF=x^2>1d+j{>w zr(+X1`plmI+8O_5x6hG2cdnoH&n#>dZuz$IKd*IWz-t!#0gIjd&uy6w&TkTYY_>lQ zo`0I&{M&55s(G^6sZVf9JNZ}U)$jAqDNTb`-v6}k{WE62p83bY`;Wp=X8ezx$DhUb zUu}E;`RpbZ_WeSGTJ!ZL^ghec!-o(5dg#Eu zJv+8;{%Pa-@4i{PX7%bXzxZO+s#Vk=pM5$!BmDdC9PdxdZCTjXaBJ7Deftibx|H_l zJNTqW!Tg^azFM>T^N-(t{-N9dwQ%;7Q^yQD{^I(F#LzFj+cpw=z2LmuM` zZ|ssTw?8>O(|F6uYSbvZNnU=NPG!B<)YcyUYSrhTyubYUB{yF>XUfDA`}Zj6*tT_k zv)rbQbFv#X%F0p(f;%5?c>Hh$;P!ZZCYwJ1-w#R4YMS46z|q>>pMCPtThBjo*L4?7 zpETa}_@$GWwTyyFI>ODoo(g+D#w7`@(}a zT~>3>(YJFvzND0l#`*88e*W%@&Yd)F z#E|3rbT97Erl47qM(6>jU;K;#nYK7+1kGG90k_YeoYtt>)Nw=lbnjN)wXCG5V>{UC zO|q563+s+xSj$ZX`(9r#B_q38LCZFUZQHeLSJ=8`i@e5QJtY)S7H@gT$Qp_}z#oDM zG|6q&B0oRBMf2PyIgOBYc(Yjec*P#{;yjUkx7Qa4rKD#z%4rN+PIgu%xO^x{^gG+o zOZEi-Fo9rlN?LkGMrKBOn($7u_+B@9wq=LkDLV;y03zT|3Wh?-u!Z22?to<7?A>~0 zh`ObaeIB9|OaWq1QXrrh7gk@l(Zlx^0H830rr`4mZ}g*ZBLt`>RlByGhJgCpzSyB3Xumf7 z+V=&$q1C#!NVgSSUWjx|esLS5?=|1j>UhwB%^q(#2=vR`*#$#E&ucm`|0K}NCM{cx z1znkw-24>KMcI6_GeBoF^5;zj9hH@xI|H;L^Teh#pp!GsZE_*#l=RyhUj;fR?Sq^f zL9a>uBl|AU$5OgxF9!W2d48kEL4OOqpS29MIFyt18fbNJcILaFZzO$|u>v%h)Fb07 z&~pOMq^}2E;V(%41+?7%K-yl=H+@~wj(~pT>zX#g&v9S*j!XLhbhB@8+T;Mo9rBGx z+Xw3NpOW_PB#vw0pPF`NkmGv!=cb();<&N?E7RH~gI)ifX^m4j?q>gEX-TOZ_muzT zw7)<<^{+@vPv^K@{tamv861}!*q_!kljDj4?)3Iq95*r0B7Jrvj=L(*JN?OQ*bjkI z(huZt+=jrs^b?wJTvpOO=|42(xS>g}rccP@xC@ikrmtOQHYVq>EE#!+5QdzD@4g zhU3-*dL(i`*ROK+gd8MBv@b^BeyG z@IT^ymM;cf?ViRzoW^m_yW8^xgzj?>;MA86{Cgz|xrp5dT< zlCJgi0{tNAJx@o_DM6pN3FvRZVcr1f9if}Ne}NzMNdDTp5A=`ZPQIT&pG&#ew+3`Z z>X*Kipk311``<_PX;=B*0Ns+l+y5fyn;D}6&x1aY`AXnf&}*~ulb!~>yV2sLWuS|) zvw|;yuFrWfxEB2Oqnu^I9-z<@=G+^+4UUUuId=z}fCh5z3O)kI&9B*a1dGw}lYM*e130eMfSwNee)es_ z!w`2~0$l|9G?bqJ`Vh1~2=VH!?7M;wf!>h4C^!Q2itKxWwGi(XWZxHj0d!XOzk`*a zQ?nNbb3o6?ekk|_Tq~ZOy(D-O=*a9xgCjtPWj_(j1|5|BRPYzLwCbPzOz?HkHW^{~_qYv~Gd5 zpdY8b7}yG$lHNM$H_*!T$CH9!=k@gV!RDY%GTsT6fzHbq85#!qO~$6sWY8X&3zN~j z?tx2`>p=rq|4n%WbbeM<>YJdOvog}wfR1bQZrUEub&c*w55YW7&z_Xg9`xtzE}6qX zXXYejO#{tnoZ9F{&}SRFvzLL6YU0XS4Z5$1yYXSryPKvq$pwz5Ap`i~;>TBPZu% z(Dmsfvrh*-oOV;Avq1CG)?`fuJu$US)(p_AQZC6nA9Q{4-i!sHZIZ`kp!46{&|B$O zgMJz;O1}}bF!*rVouJnyHBP%9ba&v6)Fq&&269rL1pUnaV9N8LL;M|6UIYEm_eSzN zpu>G*lRpCe)_WlIIp{gw+d^N19`$q!Z2*1Jvpu*O^f}M2U@P$7*FF1#3qe2h91iXP z{lfESa6I_?4<1)&HRxVXD0DXX74OXsCBt#j$eSN}7PO#N=?LaRX6dhZA=hd6Q2`)KIj5U0|8uY|6H zxK`|25xNxO-9+EU&=QD$7yAx}UVyl`#1~3l0dewUU(4jp5I^_$AZ|lk&GnB-o&fQ7 zpnqEOwGe-2_%BU<590CN{yUR{5T{r8pGm$6;`U+xd&x&2j`s-slpJgcT>MRvAA|UQ zm;dI_Q&2w69|*k$?XvvW1Xl?0Jt?>u;`;->Ta!4@3SUlA0gT(u_i$h!*xBpt6qp8f zU-iE3zX|4XnfFBh>k!{h@NV?&g!$!o7x)T6w|E-)s$kwPdtUV}gLPc%Im3Gt)>Yxj z^j3rR^t|i&0@j=6nd2z~-R5rXxeqwJ@BZGM4Emt^4)^82=R)^M?t{Q<0(_~n3bf4q zF24ylX1cH7r-B~f2k_f~^J?DB&j5Xe|IFnAy^p`!bvNvX%lIj-E}+x+uCCRvf3V)g z4|9D2`Oo;XT>k}4bzkgy8T3T=BG=QPH@cs9JqY@R`*YXrpe;Q6Tvvjg<4NV`g1+qO z!dHQMz2o>(Ku_~tzz+vq?tO^w1)Amii0=e?zHcwz0(6Zp*PRJE)IY-Q0e!)Lk^49B zpH_kYxOaoz7ue+91X`F>;Q0ph$)wXgpMjPJ@9}&D`gZVJ&pV)FLQTD|g8mVz@;(oG zSMsyoWuVari zwFf)5fVKp?CxhmtP4qVdZIpJZe+$IRRG8ljph;$Q-Vd_ zj-abKfY?lF$O*8KCEd*7(i?eKvHQ|02-cp@;l8fEFkF1NVW} zBv%KX0)07oUEmE+K4o0eXP_feK2F*QdT&bK;C|38DX+o{y}&K?gisUE`%>42x_}-| zot8WpbVAxc$)|&Ul=foEe9+?bNvXGhE=~`nJqa4hcsuQV&`UGsr~d%TXP%hx7wE#w zU}j_Bdm!_R%rel~S@&m+26Z*MuF)LO>l#hTz7=$6_P=wMgZ`7XDEkf2%&c{dUI#7D zY}4p9(33OH&qC!_q<@r&+P#$4BJ(ZKJ*k&wyaU=f^~d!0Kx{{ziP zE=*eqIwSP&RCFEid9X?98qn^+`%=CIeIlt<$_CKpNzW($4El6nO!6BEh!uH0?+#a zbpCAVxg<~k`jxwPpb+#L_wW8rpncuT{Ux9-_ci`*pkDWN{wbiz?*H>Y4BE(jvwuHm zGxu%&KH%?d-HZHpgO<4;@c#{Zy!$afIt8vuG1|0CA}^d9~4%)a7pDY7E-P-OrT_y1;#^ z%LV$Dd%o*1_`_fB+g!Uq`+1hReg^%Q=X2M$pldz*U7vv#cr*C-LFah8@ykI!^iJYm z1a0rTj(-yLYTxtx!=PJy8+a7APw)rbcY;3W@9Dl7v|XUueLd)+z$5N!K>bPIxUU4g zEGgM@8E7Cl(sL>34Z#}(?GRcIkBH$qBlMl;CGg+m(6^pxphtsWd-6aJ1lM}jf}d{* zuJPOk`fczl54s-wG`QLm0(~d=rDr7^A1{I447xPZ1fAZW6+Bdk#b3SOF;4hxjKzj$bc?N>^3hwZ90mX6t9PU)E z2J%=J!1?_Qn0Lsvg!_aj$MtG0@vVT_!+d-P1)b(=3BBPu$ajjMkNR#9bcOGIL3jK9 z7Bt)659q=C{Fewi&5y2X;dy|c1L|-b`v-baV!QuVFC1oEPGFrEF5|etfmXgwpmPJ& zz8;|e34H1s3c4xK(tk2&Uecxhsh}q(ZSc zpzV{prkn-3DD+P9*`UGDnB)r3D}tLsm7sqmEeutGUXqj;1ppX0SNje|2m;d;rd7xkU_5>DyR{I_ZTm+io8x^<|^mTYxdL`%-@4No1L36#= z_^$(9=^5nzKhVoP9zQxScK3YZy90Ee`xYPaqo>^yeD{H#>u%;-4BFnk!}~DkZvHjz zqo6PF*Ls(NUdT`MJ`FmU@8n$ungvfs(e=nC*Y}=RKwoja>_PXbx4Q21p!>Z$T#G#4 zfx=a&hX)UMz;&OeJLnSEzdh%IKIwYU^C;-^u17puLEmsK_4EJ_c;EFO&sCtGxnA(B z1^wFfil-1fV54if2i-62aQ#;}?tXQB=;;WKaMbmQ=PJ+uzsj=#G>8A%a{?TF5$^FzH!5T8@rqrE6zcXm(qJ`C}@mOtu2&p&?T z-}GD$`YwNwrwQmId>_x#(C<2)^9%r;!GG-j4#pe9-{hVPTF#Gmr-0`2dG05{F311E z4*}ikdXC==^ZML%34aafE3T9Hj-U^^^7yY{{@1(qx-J8)aed$_0)Q6N&aN!b#;zn6dY5R9|t`f^h3}yKz{-~ z74#_Rc+eKER1Q5a8RTlg4F;X+D&qQrUgzoqz6#fmu8~|Z=vvpATsu$~U&G~tcI2<- z8iSt9-@~PYUd2DdC4s)if55pwxAWg|e}ezyx%YF2K*zb0Tzf$8aJO~+0=mjQ!1W_& zvgb_Kcc3FY7rT(Z-RimD^#$k#&)cp~KwEo%bbSmu%j@D%q+0GRLszeG9Qi(w^dmt_eCVzPuJ@%lkJ98;;4XB%4f7ieg(yyk+crw?>+a-HR7J;E ziJ$@Z2tjk)C|aWUglNFL-6%?->q~TX4?gDJB@9RX*+ke9*p2+#3?ZuKk_ zG~j(v&|>dlLC1MZ;b?*5!aLiG3hwtV^TOpG_mTH^FFa=8c6;5Xb0~`FWNtwJx_WEfIj4z=N$xkmS>=M2xtL3M;M0c-J3nbL7#NL;yDQv zu5vshL3_H-^o$1exVw4Ag0ALMJmW!^@Y~!ILFe%wx=#f?fnVZ29kexnwR;lipRTFy z$)IapL)>S9KIdxht^oZ%SI9jT^c>edej4at*Bbs@&^E5+{B%&SYY9ICbQ^aAKMV9z zu7gzpG@_$jU{Uj_O+*B!{fam@ANcYwaf z_2oOmj{KB6o}UA{2F`@9f^Oi3@&ABs<3{jfV2|(TM)Us${gWHdZwK|dPT_k&T+MKu z!QThk+*QFJ1nuHFho2AeINMdiHwAUOlKAhS{2;g4g|1(J=KkxN47!?I>}mu0K6kNe zCye(Jce?9E&_}smu8TkyaZO!=K(7Uh$)F3l_1w2GpYyo4xko`K!^&zv$3lyNpo6)Y zTqDq4+<3UBg6kKqKldSMA=iO>05q4&;^u?GtqO;(S7B-#x*tSaK+<1;7uv+gfP6~K zUn%JnNe4-a_@gaF(nGLc(6(Ns=z0Nd-%7ex(yt`_0u)Y>+)B`V(2qe&K~V%40{RXp zifwOzBHu&TCD(yI2f7&a8PHcip8#D2x&(A5=mVf$`2QaGza{*Su8;b||2M<`Q{aE} zJo_qFfJ2ezF;`RWGSC&S4%~&HyIs9GbX}IikA{0Eh^u@BHv_Z^y!;%{TfwUofi@ zA-|FTTT*m}N9DDWa7y(9r5nu!u0Y-okU<4QeMt~7u1Q-EEfDvE>7y(9r5nu!u0Y-okU<4QeMt~7u z1Q-EEfDvE>7=dGhfVFQM(5)0QoRVy~DcwcVM5Nr^wY76}E9zG01X|riI$_wN4LyR_ zqrT`XdNJNCPAN{C#b!_HAiY>v@IPa8s_Ir01<2SeG77_XbBboXGeW@*`n_X*& zYTSC-lNzAY82zO#~ntnyTvSl#PIKY!?}UVY{2E0WPEsuE|z6 zs6(C}x}I|NyeBpIZlpPUlBQ`9bpbtcgUeaklNxxg2WSxQp`S>K%|;I#o1CRZ#>P}k zM$wXVKSK}v2K)v|Y&Lq_c+c6hCv`~rmeVMnlYVSPr)gwG^su@)dlso{s(uRfhy_>B zb9qencS-bmnwQa|I-HA>J*i751OAi~xuKuiVVe=)C(PPGp85`MVz?&=FMFYRqs9_?MkB*lZ8dV>IdT>Wf_t>E*r)2D=ey4@gv_v+P4y>{W!E+Y$F z+kw}&Y+cw6{w{XsqAtZHrDa{q%e!^!*4>sZjDN$=UN-;_?;1Z_Sl*oK6Si9P+vN$i zmFRD172-(_9q{PVE|d&6G{SkVXBVb1OE=BF(PsHp)WQ!B1Q?sV=vsqn-8u*K` zdriXX6ryj}IQE-38OiBIuZK$TP4dw z+ukrGHC0$Y0w5p&3iY&EU+a~`2@@s1$g$qHo?E6nvq`H&PA|oJx5wl03VsicpA>|U z9{~yVb@+x>5p=HCrf{BvLtb3GxO#E*Lb7mBe}hJw!^+J`9GZbVzE*7EM$hNK18lv zlmNGy2w9wL77)`-jyr%)my*7kLN|!RoDiq2|D(48#r2hx^%0epdSjcHmF!RfUyc3> zcU*4@qIyq7!KsbvWzO1dH)@oXnVFH1o|cxHnv#;790~=4Nl9><>G#8}61swi>u=$zQo7n8 z&k7Nsi?ZpQqpDTxJ2A?U8ca785Z!F_i=FVBYQUqH$7>K*O;pIXOgIg`*itS-we_=XrrNdC~ZF5}tOS3ONRpI}iu?G1V6QsR%9wn0mzfz?=)eBD;z zS)`04rT!C#pKEF+Nfjj2g~~bV2eh?6^sL)HY%sm4or*JcUR{6H7YA!=|Gp&N9?{~( znrPx&ix;S$rqtGc5Zm8jsyYaBlV}f@$(B{sKYh&^O{xQK@d2=BgdE4uc3Eq|{VmCI| zy1t`wOz>9xyWTHUjtSnD{99;b?%cSx4zFPy6TIOCw0LpxV}iGJ|Bk@Pi8l>{*55Qd zCU}c4SjL5m9}~PI^zR6qTz;9>s*4{Jyx}e4cyaM#f_F6j9f6Ze?~7Y<@neEF`I>w^ zM{#^f*`PIQ|Bk@Pg|9?}cku?z87~ff5u5lXNIkoF-S)n^x41_9^%wa53;fuE`282v zs;OHL^(b+d$Ggs4VcB6^Jnj}|J|Xkv;#$>rozRz^R6D!jxHw)jsQ3-92IJzK#Xp&3 z>f+|^6rZ)kE;UI~U*~JMF0Lkk9xXH=7q{xoxxuDgM` zxK$_D`FgSiPm1Fvx;s}>D1RxtA-cF`wQ@m&isHx#<&~Kx=x^Z1vGZD;n(1ogO6hk8 z8k~#E3)D|EklRhg7xb5@E|q^|Lg|>;DfI&N^TMNrhUMb=3#CgN#1j?LKk4Ne<_m3) z@XKxp9<8$mL%*gf8dMZV6sXl1W;CA$B({s2E?dVp8cdPl4yGy^+($pu>I^fg54;lH z#f_Z2!8o(gLJW5>Rng!+z^7Jcn4w-SLBDSn?oDk6NS-ldpw;RsOv=KQu zx$HREXKWgg5zzB!ow5ez;?^TMbMeUhDIQzv__J;ixvvxa*RbG^2f>x8?kY8fDjp5?D z@v;+Qfozfl0rbcIqI7a%l!Qx0x@6Q|bn=8-Dw{AkcqC_R(KpcEPHc3rj))I!!O0VQ z*~jSYMX!zU-$Zxu$Pa4)(6PGggg-PQbZ@bF!mm2=D2^7+mYtvkn3G#4kf6Jmn6~w` z5-o}T37RApV?CLmYbB;_eXT@GqJM%WiN#n?Cg@s;X7y(9r z5nu!u0Y-okU<4QeMt~7u1Q-EEfDvE>7y(9r5nu!u0Y-okU<4QeMt~7u1Q-EEfDvE> z7y(9r5nu!u0Y?dpcGMa(#&|pH1EU#mM}afkj^bv<7y(9r5nu!u0Y-okU<4QeMt~7u z1Q-EEfDvE>7y(9r5nu!u0Y-okU<4QeMt~7u1Q-EEfDvE>7y(9r5nu!u0Y-okU<4Qe zMt~7u1Q-EEfDvE>7y(9r5nu!u0Y-okU<4QeMt~7u1Q-EEfDvE>7y(9r5nu!u0Y-ok zU<4QeMt~7u1Q-EEfDvE>7y(9r5nu!u0Y-okU<4QeMt~7u1Q-EEfDvE>7y(9r5nu!u z0Y-oku$@3H+ZX{xfDvE>7y(9r5nu!u0Y-okU<4QeMt~7u1Q-EEfDvE>7y(9r5nu!u z0Y-okU<4QeMt~7u1Q-EEfDvE>7y(9r5nu!u0Y-okU<4QeMj$K!+h6cM#to=6-V_eoGmN+Gh$#b%H-*FY4C8G(V#)yHP2sRT!+6_{m@>e4Q#fqTFy6K!rVKFN z6b{=njJNHGDFci*g~Rp?<83=)$^hd{;jlf!c-xMcGQfCKIBd@_-nJvA3^3jl4%;(~ zx9x~21B^F?!}bj0Z98Jh0OL*Jusy?g+m4trz<5(QY|k*>wj-ttFy0gn+cS)}?T9G@ zj5meD_6*}~J7UTJ<4xhPJ;Qk0j+ipQcvCoR&oJJ$Bc=>6-V_eoGmN+Gh$#b%H-*FY z4C8G(V#)yHP2sRT!+6_{m@>e4Q#fqTFy6K!rVKFN6b{=njJNHGDFci*g~Rp?<83=) z$^hd{;jlf!c-xMcGQfCKIBd@_-nJvA3^3jl4%;(~x9x~21B^F?!}bj0Z98Jh0OL*J zusy?g+m4trz<5(QY|k*>wj-ttFy0gn+cS)}?T9G@j5meD_6*}~J7UTJ<4xhPJ;Qk0 zj+ipQcvCoR&oJJ$Bc=>6-V_eoGmN+Gh$#b%H-*FY4C8G(V#)yHP2sRT!+6_{m@>e4 zQ#fqTFy6K!rVKFN6b{=njJNHGDFci*g~Rp?<83=)$^hd{;jlf!c-xMcGQfCKIBd@_ z-nJvA3^3jl4%;(~x9x~21M$Rrsh!&sb`EYbw#sf?@m5bJq4Gzgnl0u04ri7|HRDJ- zMaRX=ia26)<2sC(GMA{l&GtpOItqZpnMirl9!2NEt<%YRvza-Jm@*eBZzq}69zAM? zO`eKgBfT=*fdb%g#sY7%spBQsqep9PvF$Ko%3NIYwy+nY*&!4Fhcgy<8?HKVPYugTsg^bBs5I!}g3W@9-WIz6Ie=L^9fb#1J^C9!j0Z?X+q;R1*UC`YTl4fJl-V3NYcPt zyra~>h5{;ne_Kp@cc1SC+}OQ|dmM_kka<_+1r zx>>|6@FrkGQpoO?8{ywu`qH?EZ^ z6`ba6WY~zcK5x7W#CUQojeGbu?AwBBfve*8{|MZpMcAZoqjdbnZ*!DkYf08Pjp6F3 z!23ZO_h1^g_VPp>Pi7FjpHh<+^9_?X2CP;c&{7fN{bF;9cb`p^cgqi72{)(xdvi;L zcggOC&Rf1HR6{26ro2CBP2ujgL4`Z4f8VL#&b?kAcjCqq8801K2)H`Q-|*>C1OCX7 zwe;r;UA|G{{`KG?{FyLzaoWH8D!lVIlDvsCW`l|+I)qh48qRXT&)q5SZnqLYE+MdE z%(7Cv-&JtuPA74bS11jJw+bwsm0x|3@v0NM22;2T-jVNeWZs8xKvBG$;CLjH$!qx(?_t6dSWP@Zz{#+!=KX_$yTukQ zZ_Gb_c@voMEsYpShzco5@+Q;TmQbRE5fS(Q=-bL(_|r3W0LQ#V(9*w(f4{Bp7G4`9 z?r|_{(0vKPTbEl@g~};XI#V85OTYS1^oG2IQg8z{kh%W+if&VNnDjS?hpA$FVazDwbKvkq^>8f}Ten}9_nO$if~R3`%X zwgD9GyvHcqGH>m9QU)*kH}P=rPO(sNa1nL<^Ojs#MT^cV-x4u!b)~s{3y&Rq$B8uV zJ80aZgUhfXA!Ak&7`OrNeftU6@$TPB$W@@}qzFe=Cv{q*A`!S6_a$oF+BZ%V&Nzj6 z->1YAF@6izg!}f=xZ^sW7{C&XWoH$#YR*DlsG-|QLkhgrxMzsBl?rs?f*U(IrmQID z{j|b+wiHiPsD*5t#&7sSorK0n)Ew2saX)oCu^fPJQGFTe)C2GhDR~o5l^C9LE!5SVWENyazu0UZAbOhLoDO&KXnUy}wWajz4Ws zD)x`<$Z%Dia zWR{4(qx!l?*|(@KeS;$+r+~wQSS60gDe;Sy2QE^07ws|x-e7o(u!Voa`1WtVEi0g} zyNlM~(2YU}_H98a-Y6quRuU+p?BD;q!aMihGH`wZ{hrGP*-;lVqyfwgO2bW+Y5#a7FCxEwjT_1R&@(#mVtd@u?6bTr ziwd{a!F2+P!WpLo-rGt@;LUD@XM@rFMr2;`gbp1M@23 zHwZMF*%?!e-2`SOiO}vHK?Cpb@Ln}>d3jR8_8(0$F+l&G5=N}3A!%YC5J}uz8sOIj zD(B8yc<3;`&mpNB0hdpd_zoV!7Gk*oc1;@^cs>kf5csE^h@kq%deo1>jGpc<1Jx^nw(`A)q7*Z-pG9t&5awqDe!K z@B6E8=RYnsvjUugjWQHyoDqOuq5+=U`?jBO^iBl-*3dI6(AgIP(Dzl(1M}J~$HRmH zj?ReKBri_RiDJn-4d{IIG7r!r$ zeVSI};k(Y%0iK&%aN57WL&)R!e5nwLE+z9jc$q4e0(7*cMhBn03Xwsi*=JbVxN ztw4%>--XZo5Wl<$B2gp>cH1YZaZlVqji|K6Da~UO!okHP?bvb-dHB8?@+izyrs41= zLJ7tu=-R$&+;a|6xV0B48r+scxOm_5Vke3*nMpj}1XsOlL-iHj@(ya;x2R}Cme@g3 z>?VSVkhJRH034$9`cp~9WIDlltI!eLq~s#?J!Px^sQRER?kArpHBSYtU!wqSc**B+ z;@eY+&07bTScIVwy1!cO++9}a_D6jb*eDg&!F4XeQS`=-O14~9Kyt?E#PDw_h8O@- zza0exH;q)|9=sk6r8682Tjs5P3`>O-`7DVNB5MNpzV-8Uh?_jlaJ-6senMb9tYt~m zO5s4;W~J(fzw+|VL_dzC;f5D^?v83Qpp%VJZz~$DEAjXoC3TiQ>2V4)fP$u$*f4{qV01n=9@85>Li$DPt z`dtyS^^P;uxVwBn&_%?FtdP_ISMyd!6gq0F$pdQqGV+(VjcP72cif3h3~K$GBuL}Y zz`+G#vv2yTaSvHf51^C7L&&NIPCB?4OUTot8u)&hH=zId$@6<5T)OR$k>C=&&8Oc4|79<3SVI8+mZVR(zqu3@56aF_Zf zC-48{BSmZufkf3E79t7(I~;KNyeX46S-#^{VR&2dYyfs>kNSlzkf| zOqTC>l^JghETe)C)vuW3jfQV%>$kQ9T)O4K=Ssn&RbH+FtjWs6_wIEZs)<1(-ukep zQu)I0an-BFd3kg9Q!VL?mN;DxIm8>omOWhM<{HA90^!4A_`KL;`HolV^46^1tl)t9 z-PgSQ`@}!Bpx^6|mbd0m0NC_zPOF?-%xVDB1>oTTJMMYwQ!*^weug@9cY0em;~DnQ zyGkJ&qu!)*3+YIK<8Tt^ys34Q8Kd;NG-YU zW5^4)dr1AxL*Dh|(^O;PI>1TTC~YmI;CoHc!xhG4%4GSDSJj30fs53!r0@kbHKScd z14sK$-P0m(IQ2T=FJ9-os1Rs17vkI}hWxsF6u|&2Z?Ut_Ym0QumgD<7-3WB83Ry;Nh6I@;Fv0 ziO>5>(aFixQ)w;lcQo%jEZnaEvl7L%cc4mtF@=F+-l~^21?D)tg@eWvX9t+u7A zM21V0%Vju0)B$YBVa$q(z}2wLh?^|m@v6vpi>v-=gc|pxZ4@Yj41%hv!klGQ)$0P+ zWi1xcjxFUJhiYOl0^Zog*7jE8zHA?=)1b0afOdfl8znTIuvtmzsI5h>L)>Kfj#pXb zt*rVZ`fo1ex!%^|uT`2@ZI*lHRZWSa{Gn zRSOMqx?VORClugPWRYv61R8*Zjnc{kDuOqWB#E22o8wjH{!OETRbQnJ-Dhkk+sHR2 zGBUDOOv_brLI#c_xm+VAG_VEu1YG^3jNEakCI(G-YfVy ziY8=b;5b1v5xT0q+Zu3u6J)Y{$E%EZD})q=yUtSKZhpVgOr1KQ^ix+6G^HfwEUQ#5 z_tXVW-d^gvIFy2yI}X*vAkAA0+wv-j1kl?$MuofWt3(4TDdC~Ya0W&~G?g<1%E_Q{ z0<|DF%fRJxro0E@n>V>gD*dDCw=wg|S5ftH22N%&9l21IK#ZvkbCwj-Rt^$*NID@? zHAtW!ZsKl^SE>A4g@wp|-&5t?gEkPga+0P2Ik86JCbgtsqYMFDtV0dSZmN;-J~iMh z6ia7J$Dx`Sw90!;sS5YWTP1Reh(s_5nU5Nr*g+3Cf-aXUiHwFg0UMGg?&f%vHUIut{W1JCG6R7M zv3A|0jE+pmo-8FO_^6C*ET(k_M};JD8g|0*hSj`VNPBnl2T2r$5CjlXE!hF53E*N8 zQ6MDgor>BR+meWDU?&7`VOeq@exntQyXCX;vP}TUZNvnIOsd3JDN+MhYH&(5s+@(% zk{|I<*c}fVCI;zvB2OPBFmE;P!ndVn@Gs<9Mqyk?ut9kUIsukMmk1x!HqO4?0 zmhX6#32(G8V6Mj9r{_X&#z~XqJ6;ur_krcA*xi4F5o~eUI+TebnXXbM7vV%? zz-icG4s*upI8+mZVR(O_x-uC;{S9or#qP&pUqMQy5LGgdZ$L3^Pz%7tA@t3oQcPM@ zljS>JWx^Z4eb$k_GC7I(jct0h^kX8Ds5yUVNuU;!mVnddwK)R0<4{cu(!41)`wy&9 z{lePVZQ6B7B{Xj%Cr6FReIceJ2afs~PDQT3$W4~-coof?X7Ro1?}?nD{D}m4O>}~! zPLD8f@Y1zZ+p7f{&xp;nl zA}&WfmVQF-@- zu%}9LfkGNz9gycxqLBulA#k|@b>g=+A*jDZ8Md*D zTLLb#7805+5`FzS$fYLBcf3l)Tb$!Y)v0ob;cbq*CNe?5mR@})Yvr_t9t~G7r|u$q zyF4f)c7OZcYs;Q^_{o>wTeVpzcO0sTL7KNX?eBU?-YxweQQwU~Q2nI=a)m^iU`-{7 zCUTOZhtQFTQ6-rd(^MBKBaFKG@e3!EHzSVC4udOieBEUEj#ugM{-%e-S&|;>5IHq( zOjD)@30$V9WILRufT0Wqj4N8^TLl_ zpntSgbV(TCLN$R=NGhOliaG$G&D)d7h>cO_DZlw>vV6y@==i|3h&VRv9ovPNl>JWx`v$?$(miVXYHbwEd(Kg|1KlyFn?D9-zyf zEhQ+>Qs$HKYT6jDMjeN0V$hg(|MhyvbYL44>Gc-MM7-n;6aidJh6TQHrs|qR1DPl= zS-#^{hP;QMn|Re`K-Q>O)f;E`YHy9yz*W5Wt1eRZ-rAOQZUU`t(s8IJ290>1uo>gQ zYDCqn9QP#^+LKw7-b;oQfjtcz?t;jWvYb3k>Jp|4O_uL?l>zTDTWKcxP<6t#sv$j! za36|VsEX+rt^*wK%fNr0q4+k<)hOpUR1<^xyiX%>@7-&})u^0+PLee!7MiFdis6m@ zWAdx$MtC*lCd+rcN|*Pf9Wo}7kp-{>OgbqJMVDzjoVrLcaq#uhU)0nvH7jr&s)<28 z-c`G$10EoWr0c>P#!|yZd3lfG1Uz9B{==S5hj1~XJkk!u5;L}XbtcPqyh?}njNJkP zJxI}%?a|*^7_+R1GwtST;#;Oh0ULIJ<4{cuYI)ZfIHbrL;OX$nd44uVo>GZ{|Dh>kIYmB zO_haa3STOvYKXE_y_6wu=>_C!;8FmG;$`ivq1A0=vV6y@6yE4l%-eNHg)35%G-$7G zhVp6$7j84jdrbh2_Q9UZbvYUrcIY$k#6dmVN(~%`YGP2~jegeDfK|9+g{4VLrIC_S z16Mh?bh+}wSYxu9nv!Yvek8tjvVZ@N@7!Jq2ZqV=9j}TBZ=eheO;`(63T8Q_=1uxG zdaNSk-_`uVKMiDD(Ej|lp$j?6{ylGJNwgR$bh+d|NnF)Ilp;6gcyH~aTLs`}6g zv(;+PyY*kYGg-dlRWaecTil{>aNQ72?Sw0|T=h#vF54%CdsS50?>JNwgVFQG4lThJ zBn)xIujQ1;R>7{Mgqp1t`fxR+W8arvlTgEkyvgz%uc{+&61D~6GG-+qV8xW7m~!s# zEnan>^d@oduOaR@R1<^I^WLLj9SLyxf?Vc{cY$26Z{O!-8b8)682k!BTPLQNEZ^~} zXnE_y4oe(EB_6_v4lY*^4*s6%T`xV>xw%D)QQW3B%ERON`%+=zFRnI$jjEs-4(_;z ztO{40(mbw3I{;CCsg*ups9j6_`6mvQ|2J~9@QL>SmYwMlo3SyXsfBS#io zBk_K;1qD_=bMlvZw4n@ui?HEe$AgB6K{B2k4SRoSg>@L$RX?;9zw`kAU9by17YTnOMM?&f$E z>EC9&BZ930u8>73fvipuL44mn3*O!J1vPEK)5&wCaG=C*JTc@QF>G_-G;F*JNZt?Y zJ2Y83ZHpQ=^8Wiv9=3#X}Rv+C3GdNh>d98pWz4Bq(4*Xk>4PgByakmAb~z{ zc7m;9veJAxN%5`}E+>2tX%{04yj|fx-PVm(xGG{p<1OF_{UHk(1mlM#b z(3m(;%p(Sq5Oxg!mk$UQ1vy^y@AbOP~KeoP7O=Z$#jl>MrckSF6t_6V}GbUBVd%NLlu+uPZUDP6SnlPPn9IA;yI-U@O zOq0M`+)P`Ml_Y4KrUS}iB7RWG<9?BOYk3obKRzT;I<@W!koB90l8O7Y%p zbnTu0v4UI1i{pq=fZ<9?o-!SWYGTlkHx3q9C1<5!qa5kQsx(nYa+cF1@E0hbCNlpb za4UE*a79(XiLA-;9j~H!6M!%}u}Jz?OFIm4Jc|BJkHUNR?(qa-GTCQ4$=d`t0a%rE z9IA;ynzw>So6=`3lxiDmOJt`e5)yAzvVvqxreC3OoA4$h2o2n1`Hojv<88vauD~Vy z)g)sgJ(0$(++*sHrztBr$Dx`Sw7@$YY>UJRzt$#yQ2h`eCZ6{R!CQD{ivxCo^A<0| zwPC;#Y}82GRb8T@jH=}#qt^we5V*x8=B=lSOH(NmcXPbT)W0pmwgjB~C=xx|h_%odG1T8g=f`mIV*petl`W+)%em8~k9|n$ z3SDhVMocA4mhX6#KJUn3Tk&uKF1<%2-os_WbmFZDdFx-JI1bgsApQQJ03$ls(2GW? z&0)tTYDC^|(v;=gaQv|*>E96tlsNu;2ZCx{HUgwJslPu2;HSxi>BN%}^Hw{!@J%2S zcXPaojwf}2jrwXSOC{pKI@O5xr!-|b*8{zONV&F{(Z+3}goxZiDI5-tLp3p2SKd45 zU8F;lIA(Ne5$|(l!gS(J^msg4-o!32S-#^{b>&Ut*70z%fj;lA4BoD9y9wRzN6K4> zCendfnto zC9!*Db2^IPT;6Jd_wI;zqs9rs8@+R{$0*V=0j1DMd!%5@#EEtHL=0G^r;F|#K|sNK z1_Q_BO%89ztCV;WJKlH?X?YWzF?z#S*k0A=jre1rq+-}aHE!oPR1<@B;jP1S=Z+nE zrFai$d2cd!a+2F$!majiAy0yaG{IU(s;W(v?|4;Ac?0Z7d0)^}6-IFOLCjkX91$gf zs$r|Dp{e6gO$^qBx9qiIv=(Wb0~ekO;!pzmVy*%2qV1M=>%c}ur~zyh@4XGpzbV#2hDJ=~2;bhdYx9Xs4S2WP9FDg!Y*a%z zxXIz|cvYBqV(P01q!zZhgOj|so(8z}fnOox-mT-`x_6z_4rrLXbz!4I*`<|~R7Av+ zO{WUD^#V$}jWTaYP$Z?_@es>YRYXG)xZ_T2Vla$<%iy#LL)av5Z8zD$*9;VK>+!yt z#7#n#%a1YMX0TBm;;iv)0Y2vaYzGOq4)2ac^d|gUhE0?aE-fWt_nwOH5p4Lsf$+8hTk>yhEI}?W`1i}@3T`d%<95(= ziJx9lDoDhdH0|JyJF$tui2U0kZ0z7#;3&lG+O@8dq^;%sl0p?y@PUM!S5k&}&pEpE6m~N`ad*vBAv{k&v;&-kH;uL3`G3JbG;+8l1s6p6N zkZ6Kda&ko3uD$wRcYZTn+!XK*-@pY7fm%pn#FS#HSf%SgoJA@Dcif3h3>wF8D|j_+ zjCpHMlL~zN%p3GMbiJtNts78Cuqdr_#gZ_*H5{!J$gIW2)UmSTG3tt2K`o>d-V24x zb4$E+_^Nk-1l&?QA;QX@JzL+t|N4urzi$N*TJ$1_f?7-oz)hC#coogt zDoRW35N~wWe6h^fv&9;Tcj?z6ZcPARfBQ-JkHVDQyak^%)ok_hvEuDgcFfZ&XHFkF z81mjWQ+jouQc+RfwWxcK(r!hir9FBTO{whFy=ZEWUfs${drmFuR#^@W8*>x6Dz1{7 z$5nGRTmd(po5fW?Y96FVb5pqUq}nb}I-TRj3?5oAVBCamqRFS-Tp^Scm3HZtQn+dJ z7i&_2r3EDg)l<$(>C>lpWYxTisTK1o3d*2tT=D2ZV`{4BRMpIzUNskL2gCoPM@{O6 z>LwSDu9;d@Q_x*#IBwvun(Fy;=1!X2uOIXpHdkr{xiJ;zRLw2uDm0sL;hd`CK`{Qz z>T~+_OR1VY71>3$B>N+(W}h?f+=8xU<-Ll>&z~|6wLf*_h+?55o?O3vbLZ7mRm@5W z_UXTHPDSO6s(A%ds?M1{yKk#~Z~nJc!StzpTb%! zt@;Ha)u(3awBF-jH;CpS*SFQV^XARzU0l3i!GbOex^}6qIj6X^SFc{hC1u5BWkt}U zXzqox=T$5$nmxC*I0znRaMj$(n(1@qO|PC^fX1zuQayiO-&U=VA$gP5HEYhuk+P}T zbGrzu>{3}ht9W6>oZ`|hCB?I56&v(4@2A&k4zVuE7*v3{pu?zs#*9Ugp$dSFrf$vO53>eTL2r%o*|>Rwqjt*BRJd1X;WRY^%v*Rt+Ys-UcNO1COJ zHqloz?(o@j=T*$Etit1#Pb=?PUQtzA)T>7?GWxudGoiYA zWZTfTWcAeP(=N0!E25=( z3@PAP8ij02j03y9H^ix_^DC=rq@|&lH*@-wnu?kW3rfMyfezJ*=smTv_q6JoSrzm8 zRm_<)b9!Y3@+#E2Pq9)ZweM4mjyU276c2Bq6pn8QEyZ$d5vt?^(yoNE5W6hExXegI za2YO!D^d=Ly$h)2pVoAKV*VOO|x&T2k7xyr*y(*{-CdT?e=c zoI1V2bl5Q!H884lQ#WRM6mQahvX$?FJuyK0LN9$t)5>~DcnI0s-9V0Gal~Q zs-W?p@i1eUxX^0I!g<5S&x0#ms2GMS&|N9&PThfy98^*u+IX?FZsO9r_-~a zty$r}>0r+`w|D=mcHGm+ecyg#YoFX3mc0A>*hUlkU$*?Q|1CXf)V_^peRbr;U)CNO zS2+B~H`o4JvihOt*5C8%#TUPI$*LWH-e2?gSG%%L%75Y4f9792JoCmoKHWI|LJr5Gxu*j?YogPFI;qK^37Q{ zopPkvA8+>Bx+yoQW^&cUiWN(Dzf!aG(G%0h-FHs&Gul1#^0<`pM~?O@>9O`#Zpzsu z@6C5N+PCe&p?n|Dm+#N{^WuV(`Pc9z_pDfwdDEOFYg#|OV#B|hKG8ne+pYC&PhPfg z@wCD_r;MMSopRClJ+Is}?uo?@_SyLGy7H9#Uax)7|Bk1I<+cv+H>bVTVcWA;-MQkP znKyj4{HB@roK{o4gb-xyqU>EbWXp8eU|JBLo1)AOp|t_txT7av^tRbXw>gsRhDO+W9t>jRsP zF3uh~XT_nb?_B539aY>e@cl1=D^_3fVC9_c{7pm8y!XwIytke=e95mTw^;P(#j^%n zw`ozQ7lZtmr#5+y%y$)>_TE*ayuDoyefwux=|j(y+*37b^q#FdMkarJ_w}Fu@#O8< z`DrJWwQaV%^P;&cbH4s)d*;NVZ(h7+Q_+Jn`Yi4B&{uCQDcFDf_`myn(YW=>uHFfo zk}EfDJ@Ks@Thw&E`JbvMSA`yW_TdjkKiP6%qvNi=Dll$TcFXJ6FDV&3eb3sBPp#Z_ zWY)!(tr_UgdHaV++uy!sqx8?^FOPX|Qm3Du`1$iKmmL}N-Hd5zH`ko^`oWKPzPq!@ z3#;dBK058%>a(A`KWFW`{kb1P4hWvwe((o{Dj6&-1Z5-a`-!+UpDy9{0H}^ z9DVDb?6bMnXLWRIgA6)kq@!ZE5D*IB5d{heqbg=qwI5M&Aw0*@Eht8R*!S2PGa(4H!PYU|Pk@xmCpjM;4Dl_iQtZ2Ms7dk1A$B(R-I|G`|t`gTK2bto^3ZY;19I+o^5CtT0k;4ZK8dx#6YHC4gIfA7u z6lJA_8V8xOZavD2M-_A>Bh3SMUpQg>f}Hyt{Q+7Ek*JN=TQ|4T6MpH%Nn|lytX*2uOp30!o7*DV<7}fPi#& zcXvvcdUl@&UcYag>-_#Y>*egu-1WI*c6MhUBoT0>B;OXqa*W$KODjvIuoRgVVvc)n z55u3bZEx~b;7^375b(*QC|Tx=98esb^PjsO9Lz1$pQjwdq|s#;RF1Ks!Gw*c4akMI z;p5?&WTC*oqf){>fZ=d2WMLM3h_o2pE?=TEi*gh4*#Ifhx}Hj*`v=?MpZClYTNp5+ zM0`l}A1fV`1z!~v@NO!c5hc54$I=F>;OzXo_o|Ak^u_1;4X<_n=~mRR=YHjnEmOoZ z*?w9lYfccw@=@pWxsK(>p7I&CO=WlIsjS@0;(k%-egP}GMO!EKn6+aH%d%F4PTv0* z4yMz2Oi=F%ljI`?;z;(Rv0CYR9S!>*vuot>^jzwpG>><_d_)Xjlb|jBxP=#=rIQ(B zO#&O=c%00aGi~A)6(E(~)Fl3fVo31aRdG_|vz+<FKG+i|pAB^+DdNRLn-v zgYN?!&a+8JMz6X#5=y&$6SirKiwbzQOLbBdidx@3AzL;iyEt}tvwYn1(IXo_qO&De zp-^LKZ=7Ki@pUD>n#~_IoHyo^%3Mz-zI`(-hAo~%Dq%T-IjTm3SsR8?t-x6Dn?GTW zjYi@)Zu3)4tO0rd7xDwn>G5$dq;XEYfG%{yD@)B}exE3QpLioBSABX#)0TE3ohcXW2 zUMNL=x<=Rv0YOHHLI}l_$W&tJ!D!lIn30UD2$vEj^w=MQRMVOAU>N+=Bk?)JnFmqx zkhX&Y)5!+yH_r!EHt#uO*530*P!z*TJ4pM)1~+0%Zi`gc)KEeg?x(g$>W1>w-)52a z8;$`IR#WDo$OW4hoCua-0Nqm1Arstv;$|7r2M?*FAIS=+27i3$k%hMT-i(2)M7@d=j)LW;QM;KV(|rnp5ZWNKNI z5M%~%;q(wT*~zCD!482AVQh+Sa*wbtC1Kg$#!E6$KEI#zzP`1v6~1+)Rlc>ZRU7-g znbZ-LROIkej%dG8scG?PnX6Fe1TIAnrJN|T&flx|93N`S?^9w$pGQ8SF{E9fZpyl{M=EI{E zYZVWt53Ev0a;#W4No(o!#aS~*wstlth9gEAGoBc;5!#1ZNk44)q17?j$Jcg|_;WVY zX5EyQTP?maAuOJZr_S~){;6>-hiSZgf?}`O`ondpb!z-~Ywgh`W?!F=Bk$1fAlk(B znTJen*<#dvst@uG_fC~q{`P8_A61EN1HS)pzdlRz>vg$rj{B(lgy-n4i88AwZmI?d z#X;w~^Y>o1C!3O6eYTu85;6Jx{_+vUQr*w&8wMYWKa_t2{$#&PkqUh`Y!cSy6Vih} zz+}gi$s8Lu7l*`Dtld!2{B^mIp^!%_On0tSx=^@~vv8-dT06DW$E>9sr8;WHd4{y2 z#u{zLW7=YhXU50Kv&m(=f1YJX$+Eg_|Kl9BXM=Zz_q$)dzjDv8NWWo5VZI204H_W* z{UO1WT?>!rZA!ZZsU2AiX;`=*DIKXHKT5J=ia;_Z-`uMlfsmA*WTa$efq`#)-?C~d zzU{uQ*^+yOR~J`j?;^Z&)vLG|cTm-BH@nyHLzNPb3*`*ezK(M?bJk{7X0F)dzHlpf zEV-_5!^vCW0HGdX86j4WCW9t}7sZ7JpT48ar_bvgs2q@p*Rn}#(aka@A1z&HT{&Nb zUJISQ{>gkYx9B;3UTGL<7`qU^!2CewBh&VMG92%XgDuu`il2?dKagdStNS-7gGbiiCK$%@mE6pMa}m}i{kr}3$b8%*{7YJ4|!XKUIl*Bym6<0$i-8^e6lh~ zVBBOJSQ23T28$q2Gw=YVu~D={w8qG`X%0CTS-UknxJ#C)?9tJ$&Mu$^wMVT+d58Lk zK5V88otp@mFqoXR35B1AjCJ_nUMpbJYSZ$Q2es0)zwS_Mby{{>30i4Xa*%sQv#S{Y z6kX0%N$9hrtlZP-NN0%`bg}ZT@|MvO(Vu8imC|#~a?x|WVzv_sl!fCEb7^wz6zb{+ zU#Gh)xe3UA%6X~K81t|*uj@jBmYtK@>CrK(cE;DZujNy-yUV-6Q_4;zPFIfhYqcB1 zPRi@-#~+SdHiUa3dJYCs^;YYwrZSEZ#u2vtso!Nae{W8CS01uL@KQ%eA6I=;!`b`2 z(bJY_C;7rp&zXN^#cMM$il~dzcd<^X9R)Dy^Draewcw}*lu0}uWKQa7@B zR37PQgESn`EU7MGr=5rpoy$F}F!cGgDKZk)QG z1`$tUOb4QceGaoBKH#R|`jtau7ss1w`^wh(Wr7ZVxvdQ2sLr%USzk?Ixt)jm@{#`D z=vnvKlh+roPh7nksou1eD%JI`rMPO+7R}C>k7pm$mNIyaosDh2INg5F4Qo^N)qSpT z^LIu^v1Q3dkb(OFfsK?+(D?6fMYeO2h7Jo=r>&>crxU!Ac9UZR`!BZ}>)7p9mSAnu zJQuGO+miY*5Hal+6y|Jah4?>SDZ^yJ>i&-ZmhdPcGT|oCDse1HJgGRDH2Hn9=kh?y>#>simUIh2M&pi_(kni$h9aN?w<2ml~Chm&unkm-CeuRnSx< zSK?HDsDiC>uR5%@s$Tr2{cWg5uBN$Gu(q;}r7rjT!|zG;c=Ztts10u${xo_tp8jz7 zvD;+Xw9;(YJkz4pGTN%r+TSMM*4ZxI-qIn~(by^6`Mpb^tEQW;ySj&`r>d8`x3Z7B zud<(~ziNPY;M*YoVC|65P{Z((;ieJEk@nH2qdj9vV?*N_R+!GJ-}1j_|JeQc1BVWC{`UC#zn{{<7v|f? z%KzJo`ZGss7vSZVlKu9T_+Jmr|9IXPdkTIw1wO9;54_NCwPH_!uOqjj;5a)aJ7lwg z^*;$5lLZ6|K~($t2M zlT{Vi;xKatK41PN|Krgc#CI0}=!al_Hom{l&omF8sWC&|A(%OV%?=D2np;}}l>hZ+ zL9hB4aEpZvU=n-EXZY`+1OUfafc+AHDE-n=fRbL!9{7~)L@DEF?&M@^{g{&eF+ftc zcD6MKh~PCu9&ix+OeH|c$;QgY%?j?G0E&Qba3F)Ry$kRm(%J(cv-5EBaROuFptIKY zmcUku03|mkJ0&*{HzhY0A0;<0J3yAT26jgNlZOS^wqfJvY%z+31AHRT?Jun_(PuV#P#a~<7ngK~OF$ZG94tOW_ zl+zHR5%OLLXa#q1z+qm)r*?pHHFIZYz)!#)LBT!fI(P){Wf<2-bn>0 z!IwRd#BB(?O{zij!NmpI2U{pb^|h_JqlUe;Gk}i=9O8$%2W*X*+X62COCH!ef_Me> z;%^IOP&an8G&ZqbYwkuV?(S^vgh2yFoRbr5d3eB< zpC4=`B<@Bzfdjk-shpf3m6H=nm6isjp;A0NAQumCfGxE!1JdHDH3 z9xy|oG(SJc#m^6NfmdJ)a!E*lToMwXCJEpGTaZUu8l*}~^8h6boXB) z2Wk;Y1tmdQu;m0M64-)Vpif{6a&dBkT%hGU8t4&t4f22^$#yUQ>%Do|O_KX4rE zf!;y$54KR`;0V|Qxj_HG7Mew{3PAtvEJJJLt_DElAR2HK^zRNCcm-aAcA;?tqXTLO zDWEN|1!E4Lq3A$+pcSYj*n%8)2gn0@cUPI9cc3)XYbX`;4;%rxp!E&<2aO3-7HmNt z&^rv8yUK=E1DGRl1Zn})^|yvWYoIrG)dnI5dmtCIB0&E@EZ`XE-yJUS3cQBeg~km; z4x)g14%!3%Lvev;kn2u6R31tPM?vpEY3R%Z{e$Kc>NVIxc|Z@L^$YrS7Y$G|1`Q|$ z`Ud(0wjdoEC#W`X6s!o42lVK!I-nT?dr+Go7nm`y1-U?vz!n;7C?b&OZzMt6f1`1S z732huJG5*z?^z6Th>JtV|DMGm2L_F#B$V>^ED1TJq@d$} z&r*;BItsR6zQGoH4aNp~1&%>`&@6eC&CLPokp`)N3{($02dD>P2dEcbNemiLJ9HG_lLB=^r65)Tt&lTR3m>${ z4tNFXW(VdH$Pdu~j6v)J^>llN_JMdna7aLK@ZM_R15p6d5N{!9_@O#Mk0A&oA^d2B7KIFNi##2cjK{4597G-&q950mRV!09qjWfjjRlHedvT08;?Ht$ygdgw_dw71aFSdhg`!vT?hX{EvA43k7sW-eO|shGrUy2&h!3 zB*^@)3j2T0Gsq2Lg;WtB4XwvJ**g@#84_urrthll&TnXqgV6(`2GAjM4&aBx__i7$ zR&MXy|Ap_?1{66&`p)`4=Ej{?h*f|Ni5fr$tp96%|Fr_40;?H7{RaXFB0vU01y-#) zozT_bpZ5_6=RY_gR&QtgtregLkq74MZN~nsP9d~ATfp5Aq5e_01&ar>kTsaS{(;2HTA)7=UO`12R>f8d8i0?fhPssY%B z%!z-FfzSDWQU8Yq0N?FXF^~iB-2j3D&o^ zqqwo6f$2pnu1ao5ZD~6d=$2bEkoyW5_eARcW1@P|4FXY*j1N&eyDwX?lB>0vhJG}Z z)iX}GNKO)y;k)l2W);`Il~Oz%~V;Abs2jGAJQLo~hN{_t0&{4yJ*!=^zewYDW! zqQ##yojKIK7L@V#L0qd%-2cXNpiJ4NVI~8>2)L&DMnvBiVj@x^YE+=|mzbdYT0LxnvR>L2Q*tgSN1}4P&w1^ExCTxgDbFbWMZBQus-m=jw zmL;dy!XGh71ud-nnx=)p59by6F%5;yYfz!EamE6(<}b0!F}DUj;FmxNY9&kg!NMuEcYfb#%w5ca@3=` zB}fGjcCD-~{c%i~=p%bn)2jV1HB%iveae+tc$B$`C$&cyHARFxCCJb0;ztx}<(N|) z-F9Wm&Zv#rEu;P|SC!04h16$Kq@!O}d$TgWajkW$JUyHr`qu zL$1Lj0{psNPlkK*WHbq6qtcoY_sS81Lda1(mm{X0BRV59eNuNKEb#R6@mJG#wh5OC zPDXP^U^Incq*A~e#7=qB@}SG;x79M$v04`|M_*3DT+~Cc0Dk&6KJqlXKkAUQqS5?^ zN{W4y+Skm`MUzG1=s5G#wBpUu1#M~h%nMY{H*Tsc`oGy3iq$voFq^yxe7PU?MmRDu z=RtwX{2Pi|gy!8e4h0J=$x9!UF@%uXN2nUze4q6RVZ~&y?s*JS2kyi4q_{hc+4t}v zo{@K)tJCCfFFx)S$e9%mug;%mArKm3Udg52G9|EY8|)%BNdYh z#U8lQrQs$cVR2QA+xSjZlPsdhrIDaMvi``SS2I^l z6p01d3ffzin!XDxs(d5mui>2@^T}C7;zA>|L+udTs#n9TX@N~>*BwXUVX;n|;cQ7e z=g2f@lNmC%sJNU?EBMM6D~hPo^TEl|Y$^^Tvn)QUc;r&QTTA6^fD&dFeYB;@HdB#b zDBY8j7$+<3_w~bU?3>t5iNid^_7azegtl7yu0M11mFu$d7F-tItg+?0-Naha;lkHp zcu5d`d0jVtXfuoPbt!f%SEtzmo>5veeDg$5HVTJ1U*Ud75i`!~R5Z>U)&<`OwZDkV z3s95e*bqW#*~l{9M!6F5>&p7Cy9_PLejxg>5G;Grp3HgC0METgx}Vk&>fX*C%NqHi zvU`6Hx55{JP{u;*GgBLfy`GJk z;x3yLX4=ud9qzRkI9Rg+4ma~6LtO?3zgMCbSj%z_Zqf&dQQojs#fJJc6&}j8aFJtP zT^2`=IH!fN)Yn&!z4FQ6o#HhaUXLeuGmZ_%B92YIue-TTqIta<*RlEVsJPm?vD&&+ zcU~fsx$}6YalX**V7x`)np4=c?gOo5qdziAtrA|8X5(?CxdoNQ*ZYrw-%fF=SEiFK z{unw;o8t+T!J$PCHGf)>xO~!f6iaKf`MlgotQE+o$?f8L$RO8{P z(7c1X@XT(#jD^;tDVyE^%}MH|vF?zQ7k;X;I^QsxY}hcx&fyR5(eU{(M03dz6%Ef6 zuKB{%nfW8U4?R&!(g`WUrtzNHSX@=I703Ll*3J0S` zUC61G!-Tq&)`4H2Qq-6M*=O5KO|%Sg@rEfa8_p3YZr!B^C*D(UDSDQ@m+~JCrdnL7 zRnb{eipwLSptCtuCUKZYp@zQz_xZC0pEuPB$*EJga-1(r$ol%ErGd`m~bK zAfN1=-J%#n(+J7c1M<#$QW{Ohx=gFw(Y2lCJWo`Z@49 zhC+wpZ9&KThpPI*HcVy*ww5Kz=ZIetpXzI=)Z2Qht7J=KIpj-Y*(!#aYE-qT-`pX0K_z5bBX|c$k+1m!A_nQ=dK09@Ck91rIxLPFDQ;-st$q zip>tiZ;sWjM+phNLQc6Bvn>zn(rtr-5B^ZJP7q+$8@=Xy6FSR)rHTmG>8U1`tj=T4 zc_o}yph#hcM`9#&_zfeT>`$La18iufMbN~b=O6eRMLHKw?=NueJ*j)=X(^`Ae^K*! zGOz8`6Yu*fmc7}UHPT~PQmqEm2j`l@Z&I(O^PJp0o|U+ME*4thN_1~}_@1brVvUb> zv1TO2NB8U2w&q^l2JTz2Q9O;0GNC_)13b8X6<<1^pRa3*j_K)J*B{G$aZY)>3e>qj2{XP`jFwAetR=&;RW{+yGJ6~Ho30AupY%`Y-LnrZu@h`Xpgww zPHv8fi6Q>*wo*UALidQgJsQ&QQNsLM4jD}ky$xo{7ibl+1*t4czmY!X5O-sFue(pI z4d~-H>j^OZ@h*66|9f@sreH^AR?2X8X_cmNL8ZEPb+K4h=6Qm_h$1Cnu-<;Byh_m5 zOup#TgqD3~Qdp#AnPbMFV&xlKM-Ss~W^NKJ%zyeE77PaO3iKe@9`?g>G5ZM;}vk zSz8kC!31|$)zgPIQ6`v`&+9xS9_V`e`MypF>T`<~4VaV9SdmTQoMd0J`|_f{pesjB zFMjKex-4@MNP%(iHiLD`p@iwD@}iT6LDUo3#ZY~FL z`ExXi{9*#DRXHt_>-U{%}sA2CDcGP*gl zW#OE_ikmEzl)jNAPaYSQelrw#UrW(hx8eX>if%=`uJ}^Vf?Y28o2HWP`C}+TMD;B)?|FJnrcmwq{IH?W(HjA~$#DGiAvvi@B~f!3wTUmTfyl0PfWE0*5d%*qd9#h)fH_3BBP zxBR%)>qXUEXFME}LXux{QQL{KrS-c0XB@sW+jq}n*M^iF>`$TMipdwVG7Vfm@H=B{ zbc`~>s=so@m##{Llh&HC-7{NQLxE)<1l8PVz}iVh`#DW75ylz z4UWPQlnxr#NQa~$A~Wpv*LV+ESjYLx9x-jGQ#asDm{vJYJ1|p^m z*YHK!M4@cn+=_m$gWM*9cf97uS?LYWQkw0*p*jn{iHPf4cRi2L`%Djh;O#5e1J$Q6 z3q|uVKGZ@;Ocu+&!Y+LGsSH2(I+-3&?o#YfMi-S(CZgeseubYG^AeCEheIhNK_w6&)b`=WpK_BjS#)t|<(HxG zWEI1>ls3WUaVwLP=G4Qp&H2IIi}rdOPpeM(*yxdS(G{7jrK(6LVJp&!#wwrLuh=;p zScx622hs%}aoSCH zA;ei8qRyc$DON}Q@cgVrPT^7aS})xAiG z+B$8D_+^9~t?x*A@L7u9#}Iy;o!~88r=P!{glcZZWmsW*`+Fg|BB}|18qdsuM4A5bA!h#B;oXkR-YN zF4#;}^z*1XOb#Xaj}0fd&6E}nHlN{#KRy+bl%%L&JDf(N8)5GdBDhV55yxkz-q-3U z8#ekS`J`z+>xCZI@}GF^b6vS7eZ=2K*y^(`3b`862WAHVLUV;sO$(mh7VC=SLf zm6{IBYUA-;RgZs19wJTjL6z}_S(iy~*z5tB;*?45D%pn?2Qz$rZUq8YI&#JWO)?c?>)T7o9oJ1ROKzaDkd*YA5~p+^Nt)qGalbzC?! z{m4O$pH;?pVAjo1p73!m!KNjjXxvZ!ODr6({T8u8J*{QDjTGVch2DM#p%aH-A5NpSQixEce}|Zk4Jr?6+Y&m`sk?C@J%mB`c}? zX9jI4hg6B)iSW34>kmI32J&=2+aRk&=rQFwBjdYD&OIg+MPqqh45t}dpwCMf5yQDV z+Iv#kaWkKn^9a+u=lN2wf59B)=P&&(&kAZ0I8`)e%$r{Xn>Q1i`Kc5>Udo@2&nTxZV1V+e)`Vd9s=TIWX|YtNBk6}7ZmQ)9XL*|x zT;-weqMw_a=M!E0!4sDpZy% zE3)YI)o9D^VW7ZXXBhRM`}Va>_2`U>KHPU${pUQR`%=AZ`fr}M1TV*<1hW(-Bget! zwwgBvkI{LJ44lf*bZ+p2j$uj^N&DZK{L=$}!(LNO~`TSyEiaoOEW!7A|1O3%b``{?zF zLYytGYlrwc0#8C2KW)XBp}#Lw zQiemaE$rN@!co46To{tfP-X4c>^|b6Y+{M%-07D;5^cjCvcD3&7GW+TG7*6B?QnK_ zmcNzBk7@A(&D`>BT%bksG+!UO3Uxf3D{{jAZzhh?sTb|n*+hM4VMe?M2G5R|1qqJe z9qfaHI^gmiwILWskDAo8548o@(rF_%lI*652$iTZ_mwGa$U3dH<@F6&-dn@W*NpPS zEEI|$c1%n?3upa}oswyOl+_p)7WGNTu}}Prxo$LFpoHOvP}#OImKP1r;(WI;FDLA{ zBZ)D3G44_?>~Dbt0r8#o+6nFBLb&P%nc6Z~4fgtnSPC}f1Xn$ZCHA@|d$WbXXaQQm z9`+ZJFsgYC=Gjg93$oYJJTwRb?6c!^xgBuJI(dSdiA}C2)|tCXNEM~Z-8l^} ziH_%e^ljZeEs=Y%@PzMSp}Rbxu(y222dB^No>jK~+fChT`J+&*)5|HfB)iACeX8-P z-z&pXzc>neuSa|SvRz(WSDm*F{A8W(Z#Y^AtM9jc@;ZC=!)sv*Dvf@H*v#3Tkm&CR z$No2oNAlVA%kEO&-+#yU?Uu7EiMoi>cvs^1OxS{9VY}^mrAj?&aXlDG^hsPx?`6dgf5;v;3Ins+wBDttOw}j~}BRi+ubet6{Z;lW7JSnm|Cd=}C-JQ1&{ee_Qlp63NtN-uWqD>YK{^%Ifx&3dIL zxCUKz4`~=@?cn`%TN~N2O)>Cp~oR z-+V9l?v;5QFSG`wk|{7+QB{?*$~a}-qj0OWwn5PeU#0Llom)IaR+w(;Ol#L_)qa8G*Eb>Q&+k4({MLqW=sE$fB%lwsF7kzKp3rI!+92W^5`=^u>SMGhigL~y(2>R$UD5@mvG7}$~V`cvTO2t z?>-%|q-;okIY0Y@vwtA^C-~~TQHEl7!f!XQfz)b$AmN6=>SmsWe|WsTM@4U{qUDd$ z(kJu!Y9@n^#$W&ZAwDEYyJBtbv^S&uE#VQ%lGTSWM0ZH$ADf}EXhCg+XCBq&Pfv*- zD~C)V`yNHnzi?sXP0W4S;3=!?%u0)W;qqjD&QG5t4baZ_p5vvDjnWqP*Byi)|FU+> z&gy)r#ZBsTwq~zC_5&w`CrC!}$^ah$f#(SyopOEDj2nYLTRn+m$w(8&xM+K?PsIRY z+a}*)C@a;h91^8ephuA|K{h=Py|BK97x#xl-?pvZZZw=cJn5DhGSxS0x!>4S=lxrb zRf%MrN*k8PTc?>Mr!i9#-I!Pm$tV!hiJV;Uym*5m?KK~(R|grpag*c&8lM zitv!Wee20v)UWDhdxnIn6fIwI=%}9m$%&ZeF%}Qm?;M|tu8xh*@ItJWX`WRurNW>& z5RlT6LG>>Pb*pb`3|PET^r`Kpl8Y?LageD|Gk;>s8kJx9IX}&*+pFGA1yZ3d%qHT9;W~E7i z@gUKyk-qtCoG^I7n<@TXfe+uc*$e+L@#jvn7LhR=OmOL`z3hYYM0c8-dhe;avPbwO zc{r2@Zk0DlgI{)XzSH9@;}4?18YN|Nw90LMc=gT~MTIxiU3_^dNlBE*H}T6*I`g}P zB?J9H6-FiV>qNXnbX;wJ*Y4Ip0&bJX1Lm89>Z_%!dCSf00TNvV+VYxV@TfQj&$VKJjXUi?OTXYx^;BzezGyKYb)qoG-TL@f z@j~9MGB7Wm*eWiqPFrMvALH{z31E>hxaACxgV4MIPSRnD-NPXS(S?9$L=33 z@V_I`!?dDL^wyFGXL17y^Kn_X{IiC43R!m@4L-~94Ue6ijXfOCGVhZtlcx9zACivL zz>>@&_>ucUm4PPyAo7$G&LSN~|0$Q>epx@fP5tI}sOJ2A9+R+)YzsR@sW0?`cJT)7<#G##-*&I3B zN2XOwMrGiNTZ2zF9Q_qt!cW(XaLeTZnIj2fXvQjLhJ^hwi^*V}whT4G8tQPPx__CT z+N8~livf}MZ`&BAEKZk$o#5~Rt3Hz5d3pOF50)LTW_m!#O6e!$f$+pHgErTb0&6tDx@W$_ET%HKAPm2%H z$!iB&DA(-53s|r8VQz`-TeZ-J@BAK|GtFJ4)GRU9o8NO8e(hN@8(EhV?VO9v^UAa) zD0e*BDmYPzaHd+yFK0`q<_Qn->w}eX1iG=HZnQ?LIokU%~{7_N5!?|IAL{YTPJC z*-Xm%$1)nOU^8J<`9wCX@$SlP(L_q0l?IOSD$th@C+wuX_FWN8?rX=~ zvZjtFXAPM~&)eMG?aVOJb3kia%E;L1{&J1VQr&{5Z@$ELFyTpZ{5kJEwTgctQTN*b z2$#2n^i3L z>y1=7oB6|t%F2V_=Xx{q1I)q@m<`bj)_qZdD;^TJKr$^byznEOb3MLlLhT9KP8XqdXcTQI;m?*a;+9xwz z8D9|+Tn%US$mEiv;Em}Z;7sB1$%rqnQ}Z}pvOkWwTw?Sd8Vgx_S|m{H{UY^v4P|?S z*D!zO*u?^0FvDh})J$M#KBOc3__%y>sLzzeg#%}4#3A|v8LP5jY0Ub$Bh&jtT)EV9 zEJZh-=K{5+Tyi%4PHX&5L&WP_CUN~1;Hyjf}EA zw~3;AuRSmkRqu9BQ~i{xiU~8R$09xMflh9!`A_-Sv7}Fn62(pl<0@;;gA9x{#&p`g zMIs7W`JzytVfRBM!C2{MkoJkO(Wducn_x1@IDYbdaOdPzk zL+ax?QAJ#|xcbbE0*cLNWH-YH-lFoE3Q5YUpVM^C{ANS0+jLAFe>TsOoY?*;kK=oW zZ;-a_7%#v_N>-6-H(37-BZOgdxfhA+c#HD-b@VZ!`PD$)F!Q{-bgiLw@_yS2bLrIC z2Rw}@k1-4fTVDrQUuB;gc;n~Uz@K#9d#9C*GXExRG+XY~zF(TEg%mmKC340qKbc|HcZAO@#w-bDnR~A4a8Qmn1(J+-MaPj!+ z9x(6tB>teh{XIcmT<7^yif1lW1W)8}UyTVq&smU(C9VB=Z?0IOCg6;5>ep%!cEdjk%8SHiWKsciaicf!rA1zJ2*h?Q41Kk>?J(Md|epw?`f$eaC}V zy?{omJoSiuQ)WB%%&3sW19fhlXNfB4sA~r&2M;+CQO)EPzYP8G5GH2z#WUjZ>!@u= zWI6vev-s*pIKeU`amZFMFngjp8Ixl4l!pvMiQVOu>duFQ9wu7etY1|}r?N5qFn@4d z)E~!xI@Hk4>J-Chm3>OBHgb7_9`pG#V?g~v(twj*ZTzLjw0iWarR(V~!lo(Mz!rzTlPx(FOyVu|C>_Msd5GXlc$8hXV#O-*{sS}s3iKG{U4SeZxBwfx8GO@q4Ll0hV65+U){wB`xW`16zv+`JDS{C06 zRoGP1i{pN`YJ*5V4MWYL$hz$zT#PjNbdo2V6 zRNEM^xXK;6!#(?ik~To8Bdx>GY;`7`vP{l9(UA4AF670G=!0GossrVxn}O;PWDbc5 zIgjTB3}qKXclQQM1B$XD3_WFZe_u4t^4%-x)AZX=8igCT$c!@VYP9<>q}JGY{c9n_ z{UE6?=XyXr(+HstuVAS4wBx|DKt;)y@feGL-1&_OPhtaBeCX#tEb~dBN|Hk_3Fj^> zN5bv75Co^97jGC2vpaNt4VZ9^<`N@p_0i-@i=8g*8O67mysK^zY_s|kMxKA*5mUA6 z{YBeZ@L5>%jIlJ&RY-(Nm^hD*H?@kXROs6mxJOY)@`||$_3kIEh3Jd;jKazdgg!(b z3*O9nZ(O$x>oq;9ClC0Ve)q&AjJpyW;EIhFYi^RvEMf*2vI&yapTpi59*h%z3qJou zOiM!1{^zp1?B{A@0FEeG!S}ojY=sxU1y6Q#CkQ1OjuLc?!o4~z3s}uz^fFN^k~w^z zN)axKHc(kb%<8;Ayze(p!uB0DeXU_Eb$5^E^&n%r z;EFYaP_Irenny1vf9?rPM3@#XMvtZ|b#Z**Xv<(wYi_%&OyY>MXbdKHpWy3lO40Dv zuTjRFG+|0W8r{5%?;5hCc*Lmf)z5{g5Qpa>PI%$~ceqh+!fdxa_(I)a$Hocn95bL+ zOO?K0QKEm&Q%ae&jiiR^OGKQYxmZ%S&&Lkm89Ro9NAIz9D3avri4DZ%@yIE3?B5sh zNZ~OH;%at3h|9}yWmoyqP&mc-qkAP9M|SK8_r4Hi6*@~%)d9~f#;}}cI zIJ?+Q%?5$_LH~Q5sqyx8uSn3G=%2P4L_ZyK?|9{WSbBQ0aTzFwoJ^GYoM2_cx9__x z839gd!&|23#?mjIBZe^3TAXp^>%5w7h!b6L%xT+M9IYBm+wGnSUk{6spPtuD$xsuh zcUCyI`qxE&bm4=fe>RYH2TA z2LFs>CotGxHVrt9Fz@|f^_la`&c0%4YTD(S{jucz;i`B77rkRgR2O=_=evDwi2h;g z$wIL+{s$d7Ws|xHv-M)iGCS-JFGCqycX2?fj`Est5)v`O$`aPU+T z%L(LmeP;CP-XZ%sPQ-pN8mg}rp%7RtQg)s`nMiT)f^*ha?;EMjBu{A;nWFudzuU4t50Ke0}_tq_xFc zG+FgE+>h217TV#(QrIrXxeI)ag>9^3m7ilJN9VXT zyrazPaCmZR0s>r}#$|hPx&=WGA21typbkrU&Ag)W>iS!OVpR8iQHrn89rvXE=A;r;8VydjNx~zqBd}!?yP;$8IbNXQkg<%>HXg z_;{DC9uGSiMpmn&$K!+yxHm0HO)fv#`}I-WaCFOD-UhOr<$0b@HVAsnobIQ`8f9-y z8ypVtAcwiYG+JeDz{V^MW}!x4G4m=^xfW!zl-T#nN2ISr;qMS7lo^%L30I&zvK~sn z7K>O*6yjnSC5b%tob!@2Z`)94Nzs4y(fV1sH$3gLj{(mcP=Z)D(20DMo`Ew`M(W<7 zVxt0S%X_V9@^AKsb=VB@?OLJ_MC|jEm0Rdz_h0*Ap=H>KNbpF`cu9vcZqd`Kz`d{0 zvf_#v^~nb@2U%#bhsTdRHH^`Z{4AwF8+ z4l3ecgzZwt%Pj2(J@gkCA8p#+$Cxq`N;8SRCqc;Uu%8ha?=*{xjuXsL@gu;ZI>BdL ze2HVN-UJu)I^Eyl3GpL~pAtIkev$pX3cJlh{jn3emS!jA{!GtKJDr*%N<8wu9%L3Q z->b?jJx5M(*b!wYoIn`IV5;9&`KUK$LU$$|;XVjouyDK@7S0w=-Zt?>q#(lfaH>0| zX_rmSQ7^$8on3=euo3B})S(^nZ+!E3(r{~%aIciUAp*mLt&NLcIMxKw9--zWA}W`{ z`C{rhl6l)Z+oC=Z8j9tP7384{6cufe>7R0iF)n!Fl>8~_?qd>u+Du}v_d#u5+$wT zn50H8=OOaLqZQA05eC`wmsZqjOR%kRi;Y*`g_559phNuQ($}-5gd(C`I;u#U_DQXk zbo@8B)-@k2`cHkX=$+*LaS|!5r0V!5G=5klhNTzl-{a1H4S2-b(G*Dzv3AO^J?iDO zLt~f}h5JCCF-%!S&&)$0NwoUsrA7{Mo*YB_Cmj||zGHsR1FxF5E&?O`UJ+)TbOCb~ zPNeF-jT5FbhRFEbh^SU%6?t2_Fe~wrPLLVcAhfhE6aD9m_`$U?HlxDIt&+#XCHoE5bk|ptIl~lus#Zi7lav$8+)Z&x6fJS_g$~{b^jl_ z&MCT+uv_!7ZQHi(q|>o&+qRv5oDMte*tTukww=j0b1r7qoO4xG?^<e~q>EPE6f0VQnXWA7u}XmB=teZnMue$m76_BGB2_$DZC{5{gWs)WI zG_7R?53i`>*!<#4PeJGwK|J~j3n1J|jQgzVGV&&&zlC|XH1K3USs=5817XWCym(;T z5vZDfTwER&L;Yq`GkVALNbNIgmpzSHScFWKbLE+dxo8&3zZo^h?B2kIx-lam*{|=qyBk}=ZqbUe5c74UG zYD_X+%f%8yf%o226CR{Aq7~9dP{^w4wgPOEV;xLOeMf5EWJUn}_Q}=}PmiU%x);F3 zruA_l(m|NXnao4__;RdRx~?yRo0AV9IRWV!RY^DFZ>N5XIdadF z>ph2Kh@`A}nKPLJjidnQS_41q%eZ!2+X;En{9<;(Jl#X&+qBD zj#s%E(ObPDjsEM7e<%As*H;>%rs?^yoaeEMsY6B(hMaEKbncVLR4|&Dhclj&O|H1{ zEPd4nM}D2x8y$$>r;~*!M~p1}8(D_vN5k9vqf8wF%$Z_JSLYr>@JdR zR!{k}Idffchl7r4bH_zX)9=y#t-0EE)uqkfiV~Mk`+A7g?f?w3HD*n43<=$O)tZ77 zFR5jj+#DgUgyYP>n#8&{Risd^-J<6n&iYvXaqt zts`(`xqob45nJ+-u901Kcpg{6lTWic)>k@Dch65M6e%M8gocK#q{l|aWgI7byR@n- z1*9*eFlb*kZ_Uq-(!umDoS{C9_ou^&3K#>!Mg?5X*~LM~?PIBKV_+43(FDU4GipcgiyCDji2s9RyYJcp=7o zE!45T7G$AiRrgGDHEp{KUQF%30(10mpMq?(!cG%9`+9^|ZaVIjZ5;-41a^0>Edr|| zw=aRHO&>#=qjo{36|9?QVT`#Pv^g$zM4~EnP~`E6^%vlf<9DhhWR;2LHEgNgBLlM2 z)S4@nnyr$B!S7@f`!etVS+T;T8%I%-1TF3}UUo6}*v;>;5Vh-rtP`K0KNA@WEB9aF z*CBP)iH`vWn0iwco5eu?R5DBx!C;(;6tD=k2UJlircBQD@TZ&@|-LzLmU>GC6K%>#k6LkC zs1Y&it!+(^P|yQl-G(VgCefTfGWzvc!AUv6YaxA>@6a*nUUpgLC5Ij@Q7dS2O$+#O z#jj6d3m_3@yjE&7Tzk9)3^`CR8EAk`@>Gj2^IS~A&|e?p#nLr50(9Kx-WnleW=P}K zr_l9{UQsHg+W+LZ+iF^2%6YdFkTyWDO4{(y>wib4=-FJ+j+GctQciL&=X zO^#bH3a}BB6L2LykD}-Y8j;X0iBlIxIye)10b2 zYi?qjoAxKS&4zXV`cP*Z_va!{3xDiy-JrE&i zbN(NhtOo?M$bxQld>bQ&5sj{{ z>+~t=p$fpUc~S-XzgnSsddjAzc@WjDJp(sK6K>lsC?^ja*rG*#vreh%a)B z)0%%P-0UanG%F&&1jpY}VAWEtI zy#QhH%E3i9d$z1DDoWg|$T|6JGTtZ}Krh8sek6gPYWo;uk&31Vy!$qEZLc&w`+v$` zzmW+F!Ats=UzG>Vzw!$3AQb3GB0kT#Xz`vJ&}soXHV7OJr0+%K0H zrUvHSk1*Q653>9`to~DKGSFw-HkVhU*?$Rljqh`>%@@C%->9*armqD4Dj(S!wWZ1Y zL(kE!e=7wT*H)Kjkqlr}oRKlw9KN6_yO0l8tuVcg8m(jCQlnrnGH|&-B#dlSaF7nJ$)#n5<31_w6FxI=oN zBovfid7BwPjABtPVa&#cEKF)e1|#+A@7`Rrxq5}}vm1Lkk-=>~MI9;ACM7wXtsjHM zFD!Ar_@u(sJpm1q4WJM-r|cGsI~-RiwGb?>39f6)YZiBMS$HURSl$N7Ms`6O`Z!So}6OJlL{^$NOr>@)rBA761tasPz$U;%ms3N z5~DW$BZ%k9a^C$xHjiS?_2|a@jp`ud_-#g2d)24h4$GhQSAw0E`srhR*9Dt8R=n7_ zrga16f$*3&9-$ruy{MrpfExhFN$mj4sK+=e@dW5d>xAAqK|8LA&M0FUlANu4 zMM;a|6U($!MDbJ2M$bSumK(YmwG6`z!dwkCt-8^jkP1Vs!Ll2_Rv1RFvm1x|;CR^6vmtzJgd``pc% zRGq=ZirphvuVcG%?49c?7k73hQ!Zd1WAu2}1+d@zei=EAQU|T%$vnQ}u3TR(EBZ=Q z6O>!NM;t9D)H)n^JNM(mUAVhM)tvfN&nbbCVfRPy|0Cf6@ByiL7jHLk@3qH?9?NJv zJ2|Sf?TwGwd-facsZgOQz7SBOguaB2YDzrWp91!la4j8AQ9aw`e5)jTHvq3}YOroS z9+uFVhI@qbu}f&~xt0dq6-s0HjOW#2H+8EC*my~(?kA9vP?A8bnahmDok>314-sWW z+2+9v51|+(YN^8klZ5^qCXXTjT1@&nyt32S^Y6UWY1_AuKm6A7?@8bd>_kbY-hXmI|8VVq*)pA|2a+UZJ*XtIr$p>krv` zx7mc}&;(cfdf*$bFUf!H}wpn71%Yu8A={P2xV_P5-FS1W8W;@InBoND_k&a z#+4pRWMb?jX$T381Q$o7X3&jL)Y+?bH{Q?!N|BOvXeKKTj3S&TjF^h){m2D{*9A^8 zpLyd~Y(IV1JXV;(&K9Br0-hu;8Ts>+! zXnyup-ayw8nbRriO-x={n|NVmi)N^!JjTdI_Dq)Py@RB`tpg4 zF5F1_>}VhutMqJn%!KQt>;sVEWCy2=!M2QkQ%Ipv2~~Y0$%8Nem;-`fE|4||UUrWq z9Z1&3y%Mdr?m=~GYknP!n@*{2ZX@#)xfh)>XuaZ<*-jEFPQMaOJ$qP@ibo)_`yqlc z*HJ7n1LzIC#FZUg22q)Rx)C4&cxSf+WdNj)?&iE5+zg-m)Rnp@N)a2D0JEZmijFDq zA6x^1pt1d3$wrElwpORQA`^t;W~Hb1Q(e3+hK*%JGiIJhejHH<@sKgGWqQ$uc@lqU zzBM?bEq^)duU`tnH45pto_6Q;A#*|X^4e5f=um|yV69xd$7i+eh@oJ=YJ1D5>rtuo zcR<}3LzHTx?QvzVb;JTAEz(;KrSF^79&}B$^u+H-paJOsIVtSm2 z7+_D(&LU6ZcId;6%yv?C_h9Ret$*35=$xiau2NRsT@Q&=FFRcd#=aQ8mnkk*`K6^j zuTj5@pM8$lCVM&a*$SW~%T(;r|ZSj_d`X-4A0^w@QSq;)mX6unK7SrqC#pZ@_&rwkJUI`l9X9hX`>-mZ89uyy@6 z(p-vXeOPm^SYmR9Q1v0s=>0k&%mQaelPwqG*wrD{OVPUt=3-=6Sjw}e>%YBfwfr3m zw&PVcEN*E6OPbx4dSV2~k{sze$I=dPu)!B4D?^68?kbAQIQ>s0qR;5pEh@Oj=VF3l zxS_9n5X-DWQ0OrI*{`rADBbZ2)b^6oFA1JzAj_Z zp{M>F)kv$c1AlYwe;@iZe@Ue|H(VRUhjy`@c>)i7__jSL@9GlBI&MBA;lUYhuVlp8ALF^sJ#Yfl-qDv5 zvP7>i0Ph762Vg3RrU+fYjYI#nK%MjJk$s+g6jd85!(VfjQ=ie2l@Y0HZgY;Q=U;UE z=3Vj^H>ag1%u`$U;M3&1YRowxsVHxF#ZwFLtFxvEF@Q5Yy|4kOX7A~6asBuKH2{ZR zQ~`l9oD?lWp3~m;7n?wjF7mpBnqvFI- z^qd+d_-Fr=%+0|<>J&0xn3k=`-W6~K$iIo=wzVKpuSp*AA* zvo((GU1V{0qVR~KKMy%k(hgFcR7kVH6(HR>IxFjtpnYS&3glvLdGf&WzJqkL73<%` zbO=Ag(oc+c)HujfOwA-}WSC;Ia=jj_H1Rx-Saw2G%Xjl#GNWS$ zW|!kaH>VlPbNP7d^>n0Zk-A9mY81rOK$sWh!K{1uZYn_E18JVMG(;$=QD$Cygf)) z38(*JKEJ?3_Jyh;e38`33+~6=`ZKon@@gRug=o;*HO9y8r~e`uKF#l5qy3+1&h_YA zi0HF%{#%?1(4pJwuPw2seJpaYme&jfMATp!_?MoP`0waoIV%dMA6=SQy_in=EPC5` zxW>kOYVx9pKG>fqdLN}aH*zUSGUt%_+U%HiS1Rltm}4UPPFB#pic#BIs0DChKdBM| zPFdSMk9vIce>pN7O7ddys>-qO&hJWKfnyf^Y4-}J9@#M_dT+-Yg<@U^cdqe7r)$!Gbpof=s0!t!jJ-wv|>Y<4;8uZQcjVre(^ zyCZoXdnFZ6%pNFMHk7%eh$S#2Yo}$)@sTQyQ8+5y49}2)ulhp}P_k0g!1K3ru{Efi zcs+Xe!A^P-c+c3s4S{esc~#FUl9O{Xc`+n#2}X&w7bt3y8>jn4augq zq8lqv&#r$9AYJspytJdHUKoNJ z1#OlhG;-0K4Z|!UbeL(leq_}^_5Ac0xK&?A=d}v*$!{>-slENjyhvKqhnK!~CHJDF z;+*Y*f#_!8YTe^WcT{Q4yIO;ykehpQs%AIO3s zgU}{`n}n@rZ5HRrVD28A01^$si+o@tK>y%e`fGf`?6mm0wIu9SKfXze-KIaJu4U@z zHf0J7&hQvR=o~eR0W_vwnL|*(+7@6JYLK|jP&aJQB_19>Z+x?->YMgFC`{$m_B3~( zrjKOaopO9`LU=p_Ek;FrRlH%OXWSmb%c;VF<=Drf1EN3>pJ-6Hpdz$xqSSljOmR%C zd1P;Sp(Ki%;ae_T^JYdFDmm|t&@WUY;x@cFD0?_=%#$pmrn`emPm-sl4D7wR;7I!< z0?RG?u=11vF#G-CKzYhA8WTxK|Ez+-M&)q3>9@e6xEVoS(_#{Y{huyE)O=eH%XHL?diGE{ZTw0rlz_QBDW0sfxAWCH;CH5~OU^71(K}P} zg=w#;WofOh|8KRBUj|TX=RcI1n=2+{|4By7z8Y6_IK(J?60>A)_s3}isNyGK58(Lh z5JPuYl(4jNN3?6!`#ti|;wSVa;V)EP^Bw2Whp5XB$`>qMxK-q@NcFuObkGo~M_ua6 zTIOwLW6Iy$BfiG8-q3K^2>!`Y7#sd{2R-qgBA<#f+bh%s=S7vLk9j}S6aA5TFwM=q z&Ab|+vvW-muyDEjQ@P`Qg~VCf9K0POMLnit+ou!LcAL;7A--9Y@em?Z_*^^1W>0qb zE?QPxT(1K=oqo7i=Y_RlP8c0u4xHq4f`k_}SRPi{r67j4rT0Ex{96hFVp&^3omL~G zy{+T=^g;@8SC8QhwWz<=W_ln9G(7ggA_$l74{_v&~CDG~pzoFDK>VG6+!4d*G1R7Q8qV4Aqy0yT_q8@)C68V&8M@m9hT z8F(7qY0uBP7*T4vm&iH;_ErJHtdGu~vX2vF#lXiB#oOghNy8)TD184LZCD|pSTcpq zV|K=gBQfTdopY}{|M;7riTdydKRLYFXq$OpjrJVfX?^aoa;!4jE>j) z{hX2F*uuBuCwH%UYYPH4A)10ATEggY|9z}u;8)jTB6x7->n)%fY+-NqP6&U28ySP5 zB{{w1<+w2i5~;*qCehA8yl0!RHvN5WUrEsSgOotKDki4=yV$AO&@t<3c`qTPO^tkE zyhsm|?dkC>U5D&Nx{;!ih;G^~ixs z^2UfNxB+kKPS&N$pVID1+LGf=*e@)=6sOXF;6HvYR+EJYS0DQ->gA*l-MF1OZ{@K0 zx1{>YEex8<|3{a7$@i3a!ZmWp_M34j_$G7VP`p&8q1Z!5dLo<;UYAh9()Z_QJ!jSN%!sgE zP{D8}6&bI#E+W%!4r-6}j&3~HJ)(%RH^~{!PgA^3XZ&L%Wx`$70q;X$HrA?YB#A3^ zH9d&=tSMMg?d}Sk~ zX;QuE0+HOyY6xo&)dJ6u{3_^nh7+PkEG8+a+e%P9)U~f*pB90AdK$H+XxL!<&7Wk( zxn-?~NfLjQDV1~c#rT8lt7WYvJ_V>{M-+0JFEYs{hOCj+ipw3vO{U+hA-0KQ{tY~z zvczIBO#KZ@#hQ1Z<=BM0h{_4&SM_*|s60BAZ?)T)`T-3;S3hz zuiA({%Zs-MWH9<{i1ERUvb_nI#3RbQau`7ZI0=KZNJ%#!1pS;NVBjIjI=#k#{`i?e z0#}cT+avqTynjmMAZD;6LTKE0UvTTCxuP2FthJuYFV zcmFjKtzfFPX4qop+EVB02A{#J*TB_tAuez!Gg8t=ukNT%@=pprlGIOpE`3%#QLrO> z9==!UpIW}t9dGG_e&WKMdsY<`R1Z z+it+8o=n+qg1bR1yqH4~cyDd)8^oY)quzA440-*vEHN+Vu%zPe{&Rf#dI>wWQ5ko9 zUP?9b4fb2#D<8i)B8HblhVZ#?OdLC)am5KYDKyge&3W0w9l=+B2HPzgZPt6$gWc-1J?kJvh_^$0xE4H)%t8R8^{SGuJdyh8sPpPd7QEX0D`H(_st0Mn8)j zqJQF}uu&p(vH+{V;E~fpwWsKmNYU|WS6pM*23ic;WCx&gS$pUUv@X#X-Z<t1e zYB&ov3nnT)9;t!+ASD?(@fv5!zo1Nh?mnr^wt`Y8_)+-nD`^qj?*@?gGsS0OneqPR zJoQ5tZCA8nOpAKS2E!dO44Oen4QE%0(kjMa%U%p9dVy9*Hk+ry<FAH5wO=M)nJet1PWzL?p7-3G)RxYfQ zFN9ju0%0;eJ`Kuph3s1ZSZkXFVWU%`j>KPf{vRzK^Be6)vxePMT6!1{RUA7oRTGqzp+ByuC6f6qsAV8@}QvA2u-o1Jw%A1id2{pGL&_xFK!4m8oKVOWM{ykL#^ra7NlrX(p?fhWH1{MlO|Gr(l8V8C`>oxtB>lIr z-4D8-I98naS`zm^n*;ke!@RE8Te0wA-$oPOS$Xnz62A4V=^mL{6cLM+g6Z#@>CArR zqi8Bb(68FJo=;1BBqq?R*^Mx}zU5@kyXev{Z28{tkeVd#9luK`3;1Xj(lcX#j`6b9 z3}K3p4?@CZOzWGyX*A#;9Nx+epSAZ=G(IA!b0fex$DRvve~IP?Dzyzg>T~79mGB|V zIpAmhsGTZ0!;f2&CG4rxgLan6S^mzl^cEjA*X%4Jdv5Vc1sR*{to@;r^dQ|b5Ays5 za?)LRKe0#l&5;OqGKT4{0;4Mx-TsDOsiqS^P5IvM9D4>F2Hl`LASaj7k?eIbwwR`x zSzZ-cYMQlfeRCcI1$9{kJbvz9_w;J!dYVbhZeo?-EZ>%Y%)bRr@I%s|_7B=+82ZJp zH+W81l*YD42dIT+g&8!N>^Js=m~5#fFY0jC1uaccJ7PVeSkgiWvd!HKZymCCg zF~uLHb{Y3Ac64B#WJ>e{&K+HYPN}pBU7H?fROs}20!IW zx7(KP_2!rP7FUH&6LTQ9=p3#*KaqXTc<4wB?;fIcP*V1a*Q!yJZW!$BIA6{Vf=TM7 zfu6Nf=0gjIm@YXt?$;|$a^OxNM>7cL;|aYK`7N(-)wY3)>KPX&8#N7B5f0K#4-*8= zdgrJ#uvKO#l))iOmQYq4cKlF8AIPSeigBYxr{Ev|=H|){ty?tA!WsLTeN0N9eo)9L zbh8R(*lu8OyH9MLJokj6Ju(@gQCnJ0ix52pD}&Nz|nk1l&-59)bb)*jNTQ{H`wdc7dG_ zmF{c&i+yaW{;UIFQ65(39EZd&?_Ba~tNrT!Jj|Vf&<2Dm{Bgje;F&3v<=3lbOga z!1#mZb-MY)Fg6eVMVeL_SvY>#LcZF>Dqfe{#W%_yD|0d{?35HwFxqwF;O&k~u1l;Q znkV)4$}Z*<>xbZbO0HYv5M7u77k>3l_W6}Tq^s|)`yd%;f;vwI6B9e>Q*9SJ#l91L z+KLc=E5osO`3n>jKWZg|G#hv_fVsZE@L#P}U?@s**cU73;<##@uf6;nV(joC0f=D} zUz9LkZ!Wj^cpPf-Zsc7IxkScmyq#iqy=bqCp;c;gm`4x~m*V#w3h0DC(tUTHX1GU4 zt+RS$?qb1Ov&w~_y}fgbEnQw=mUz(GzaAx}d!ezLASi2@bB@W$P?fjCD~{aXbze%E z@0pDEh|>C~jAXAjgA}EgG~pcRL3It~Tv>CxtNF1U*alb|J2M%vosa~H)z}&<5Z17~ z<-^Q+zmg2>JJQTo^buUTjkkl;Atx>Hi$E^S-!lxj?}lha-pec31BhQL7ie2r2b4lD>b|FK6_>IBp@| z$L%TfurHRS(+v*&C|j|lvAB5v65wxe=u+(%wYrEW*${^r=ieOgtX(Pb<;clxGs&Ot z+97WzJ&95w(M^bz!?m~X8&dc&`WIfRElERFgpG1Ay0TtaZ)~Es>*L}E&IM5(#ZTfk zVT+-55RJ`)fGN7WpwEj~#; ztQ9IOREozDI3~KCAa}#23cr2YwTug&voRx+<183sNpp4}b7#<;IeuNlc)un4#0<=n zzm2G=xFrINzwg$wK@l09#!ZVCSJe&&xEmJRa(Y9nfu1~G@TqRMtp+&Alw`*8A1Zm= z8mmqy+FDewAyx~*w-r9rf)nn}f`kS~%-Nv?nlV63CRJd8|B}i<3%j|-iv}E-5N<9) z8qWW-37*-22PpyHwSemQ$6KNd-AkmodOnhvGDcCSq?8Nr53V5$DlBPGs>#i-ZYHLB zdWnwbxnhJtiQ5BGjL!?;(#W9ULgFOwh0>$p9Cq00`2@#OYtr^<=Y=a$CV9}{BLOn~ zPKH|eb>eIjpg^8}31?hHf|qarV%-DL`h*xYBRU7bo$=PLvXyK)fH1<}Dus9IyokP8 z>r;N_&RY{Dk4l$DmgA>cJQL7e_ntr`acN`-NLj)figfhs2_r-bd>X5S!e@w& znpv+Qlz~t2KHXS-0VJNL(FCm`7KQz~?~z!QB$?Q`OTfjUK^zpW{7m{S-(RFKv4yCL zTv@$&Z2|8i@r)?m5Sf!H`zC6wzET^6B z;W$6aJ(*(nn7M1G+^>L#IfxWp32>}&h&^yoKS8#UJ(wv|aIukR+CJvYGi5iOAtkNG ztImN%;0@t`{Sevn98@@rzq;(3d!)dtc(Hc1M~-MiqLl7(opBnP}oX$S2hS^6je33iKgT4;2&sD(OJh_ zzZ8lf_V!hh;&D?FnjCBedpo_uj9o<+-;=KT= zx&|K{9X6d8Fye0Wo5;KY*>!E%|CA*mYPV2%8LM!6KOM#!QD2xV%WTQyy}*i4ycZCY zlI%N6gQUGE_W8h;Ml-0rbLk+Z>MftwERSHB=F9CjmIsRSSCS6zuxO$66jwx}r;+uv zf}Hq6AHp?*7;8af2DWPg|q~>~zc8R(@-cOU+x>cbQ58C>r!%9KNUL#SYMfj~_i0?s72-)t}jQ$9l!lOcbm$ zEK@t|%2!e_lF4Q=#KL~J+eD@0S;T6eg|$FUAru3o#ZG7GR=*f-utLfnybq{x{q~$a z;EcX!7~HP=>q3_!oCALcttXFv5)jVyS+6deq3RezYcUxNFXr{FRzFHf)*8K&>OUxN zerr_ERW77Zyn%HE8z6~@gdqe&g%AS=O#p-aC1Qzy34+i#e}CkEza&Q0d-Qnt8kJF! zc6D8>+H9xWa$y)Ds_T^;xKqWsXEfC3Xf>bVg@BaBG6M@P>%lSwH%5Y8Q#HKYP8M^^ z(pW*NhKb2)SxO$I5%iZdEOkCBZG%q#1DkkIwE?T!L!rZgOgW50M`2dm5i>MHDoMaj zGhD8IS{bkktDG-Zl}pzJ*bZ@YOj>}2|LG=FF5JeE@^_u?at z=Kbfj$%Fx8bMX7=%+1Y1)8?rpt;DCRK!paA*_C;H3bW*;CgL{=LKGHwC0^H@fk)JvDm9$uJTrr(Y_X?lY0%$~t3h7S*Tx2%YkshgQV)<+99|7Zt-En9kC2OJd*c~|Cj03rYZ#&4MR^Ow2>h`^6jI2PpO94y^p{rc~kictGLN8h# zN0udZG!>96pe~_qp7|lsxo~l$rrxBGJnlPTNQ@Z3A8zPeTl;2S6BWQ~I|cLcsU3nR zU9d?i!P<*m$Sg>)nI|rDgTPq_#flsaXoad1v4bUKWCjK&MnAW2;T+w!Xzr2JR`@%2 z3M~Mla7~~6brUuU^9Pz^Cq}hjF9{``=etHvRNe(mE#)33H3L!}vn>ot)zd}Yaz-tB z@#FdF7=GyfhtD#JCpuv5(TO(0E9t<#V|@N0zOct0Kmh#YgMw7DAF$s(nGj!GZ3MJlo)?z#y#TjMiJUb->LA4J%AWom> z$JcIx9$3r(R&xawWdk5MR_bFv1V11HLtL|vZ05SvZ-8!RX-g{QxEuNAB*6C%I!82; zJ(za0EKXzDcVb){p_I5sI;C7Yd7=A2u-|a@-0Op#`2j6$d2EBC=UHKs*}rG0V&>tX zi?j=6v(u|qR(bcd@ovQ2W&lM!d}g~srba}(GXwT{NyGVYZdkyz9h$4454PKb<@owl zDIRuaC{9w0gAiG#;VO?(3WX<%$NdF1xn`i$^4Za z-J6%=v`pmDm<%vi#C8wx7?$#}t_7lxc9#^u03(dk4n;&+ru!2N(q$xRP-umdC>lUQ$#4&kXJZmhB6^1=?JMYVn!!oA^G(xxT z0uH4zQ(tv2;Nk5~$dO^JcPsS|M2%zop%+BrJ4~))K`^L+m1o|ZZ)zc(nA<-4=3DM_ zA53Gum)~$$#vKpGcJqHT+WL?%i}8};jDY+N@Xvi-X;1_H_fw(1QZ52*a3VRPyp-YW z@a`Rv?m6K>2?``0F3)tp4$tD@RT*I~Wm6i|J(MVMdRwCX<}y2K^bl3PN>iUk8s$}p z4h)(dr;&f|fbv)2V)+pRSwNy|6bRFhv6PD;!F27e5U`%(nuoMI7jHn5HZP5gV^>(V zdc~cyu!V|82jjRB?%kLW>kNV@APlwS`BH`%+}J?baRR6K(IF3!LT;EGqT53W_5~ge zkr`@rr+DyH?WnQ=%hxU8;revcgI3Pzi8}dTV_{Y24^8|h`D0OHtOTh4yVIL5uXFiA z<_~k)g?Dr!z3^Rz{e2maTd`adGldUo^sCiHBN`_K^<_9zCQ_>~B(s4SkRdmIc2ax! z$gj&nRd1W>!%~2fwzSf}bPJ_`WS$6T70Ll-dHw;;@=d2L?5wx$#B2RrqwT@{D>vVV z6-mepXc*{33T@VHISEV1+?l#TMrO?I*~@T}zW;UUBEMLTwhO_r|i@4bDA=RcqDB9%c-} ze%2yle{INas6b?2XN$IaJv1cUCzKqK!a58|fIxg;{8irdDHL?Sz(lQ%*ysr;+B3$c z^ptKg7ge#|9+z?0;jLX5to_Z?cG%8_=L`ge!H0>Yv6NnL+ux}AMQx=W^=W2rMhGo% z`67_tx=;q2&}ge<@VMG7#STH2TZo-`Tg#R3t(a)RcZj=-YpR2_V*b4!;jIku4ruFs zf<6gE@lqic1XwN5u<;HD09@f)to^i)CzVNAclBqy)N!!%Ret_O)pobbR{v+pPsPnI z$JSPThp`tYgKU` z(KTFdx2Hv7uI#4Y$BQfLDtE@$lRg^j@-=8~hC2^2zRKR{tkt&EdbfSWUa-3{hQFU6 zU50N9p{q=_S#}h*cTWIGgXW_@4cEyBHcs<&9 zr8{kZC_ecKT%12gJF)KWg?|(eVCSaRo1I=wuW#SD=93{&UvC7*N0ayVsZ!~oQ{4Wu z=Nf$5XnK$DLkh9i9LsOs_1cupJ5J5|AUwWc_lKvs%?$1eonqB`p4W7S=ZxxVJQ$z# zTYOd&xa}fYHu^cAEZ^R>uC8yVn;%ZIvJ?)-$FFC^V1K7`=Dz8>-#$l&)e{xEX~FcPH&-1_ zc>YYc>BXX~OmP^F+DU2La;C2sX79DM7KLJwI!r!~1FKGF_u(Sc@d=E+-i;UBSvpp0 ze!sLI?%?lN@Ux0hnt&*hRhViR!l5W?>S*^`&X*BU@B_mk*Srz2E(pOBg6e+*uE$dM zQ$U=t49;VjEA6?w$*Wa+#td*|Y?zYeUt+?pW(8OO?SIRD&ie0<;jvyLDU;*0Fb*{$~@sgW&*~D6(Y}86c9)@c@luiDQ5pPu-ic6*2t<0L{$%L@NRFxgyS{84eCnNBv zlUBCViFXGFkNiqVOSv|HpRXQr*H6nM zfISxk#anG8mk>?TvY?F)BX%X#KnekdXV4#u*^`$k&ey2nCtbrpS zvyGOXkXiaCzQaeOq#ocC$%3lH`P5F^mnm1MVg|F6qm%ju(V$rtDa1}tO-m0RHN;i= zu~o5NGO3;JsQr7?ccD{2x3kJS*ZEK0m2ISN0tXFW=4eM6w33A1Go(TouDz1X?nIbC zdG>gwz2+35LF;i1K!Dx3yEYbVXM^UFd{$H38h&Wg@+F6PJh@- z54?bL+)Ow~{#^sYQ-hglKq%XYXRUooIg`oI zB`otz6QoC&7`Z45pu-6n*WZIEB{M)7RPhtXPBbKdA18(v;;?ItykeD831|UWmPtXeb*NIHcm66lR6Jv9<~Q0O^TOEWMFukGmBV6 zba>FX4ZsTXIyJ++MVRhnqyu!q6^CJ=Jx!|e5uJyH9h*MZ8(q$FV^gVH55Mi{oNJAX zIl4$cT*n&SrRhFYKU}2sfNt0iKa{vIX#oP`Six_8WZ$Q*HJ z4L~rb34C2JU&(YCh%V4wcj*E1R;je=c0o;BH>bQ_RU4~BS7ZqpQ|e>7!TgVN#Vr7C z@qFdQV;ef7P8Y{ri2gYAHsT+)Xi7Sxrb2C(mQbuWzMARA_&nND_E8?oRXXo4+;kX- zBORq_eJppnG-m3KxfbJpro=Br8Ste{(mH7xh6WjAPgM`If#?c`J-?>nH+ai(x_URC z>-)+dJT&8^~p#0OsHV5Otcf;p%_QLF{(|b4S8(Wsw$11jMHo!*Qz2vw=;bLXeM~c z;_oNMYdymcU~`UUeu4lpB5e5xU*=C^Q`1%chYEcq?_IL&lStg~TjxZJ^-JNX8a97) zEFNgj~ z7FG*z6K6pbxGiiWxq$6xkfMWlS2u`yl(q=+6BMuPfa{B zdB3x@wtDQ!x1@=+Y`wK#8 zRE$Ms(y18Ez%fn`mDZU`?HoXcvfBN*5MS>nF`054PP^J^wfph;eh`P@*TY|`)*3BN zt0SXFKmbK*)x960%x=}?X!RbDGJ(j?WYpNz_Iy7K3!zd2A|gEKy6pP&+EhOpRvT?k zPab|OCX>$eORw$gd4mD5=nVPoI@`im%+y0g2!O7-hu~YK%SONm%*XIu9qd>v003_=w;({ zTIp~&n^QVnjE>Vvhof;=SZR2`7q-_skPD+pZ?6rqr7#xgA=@Z&tJUd;ArYiVEa_Gd z81i0_8b;^QxLgj4hx2Gu2JE}aV}tmyc{~~}q;gppFCq-134|P`h(o1|_zXH9=PFs{-3~~vH$$61>YmUv43RwXuM>_tggaDjOZO}XXh;%6l`8GX; z4@8=*hGO-A2zE0eMsG1VGyFI>i$PK3Xq+g#QW`@>^l+$nx7V<4E}L4LrFZ&i^ai6P zyxpBmTif{=b>Z!jnNq6M#@A;U#RYb~q98UwTacyY=-PFQ@C|c?y395+@Y~O2Hd!2@ zuJCqhq!&Zs9=)ct^CN;dv%=ev3R5YS`SGoGy&p4#9;~Hem>-K3f-#sZ7S;u+Pa1^^ z|Eyq)5z1tR(%2~D(a|o#7tUdJU3#b-6eO7(JcYN*a5`x1^n^vL_rS&J=^O?XX9Vjo4K0|#)Ukrq8Z{P7r8DUax|+?_&^R8rUUA)U!zhS< zUi>673vr>;`;XxB`?>s%%!!nk4eyU=pcu`Dw{j?ysPpxxevUxG7fj^@)2InFYAAyp z%A^KSu}~_NpxZlSNp6x~to8(})f7=it3bi>^G`NHb0g0Y;04MZp%g4=EEv=Ci}67y z(GxI00}rJ#RAhXwK&7L*Ro{mIcn2#5A|WHktg%4FMIlj|MU9J^NUT_{LMC=N(J%Cpcsqt2NTL1VHA%Di3`WA%2V4875i;f!FA zIYUHpzj|M+bgSL0c1A?WgfPF{lfL`q`{EhRpm!rEoJrv71ShWY*IAOp3k7j zgakk=E`h32CW}n!WXz>gnmEZST82$+b~-f4%-j^Z9Mf5J4879EP8VZA>GU{ZMv7W& zl$fK9jQrS4YY;z+sYo)}^eJW)0We!*iK6K(%;-#8bh6pTGTO4Ua~yUxE7vOLawR~% zM8Jy&afwe6@VkjRwDO}doiB%4Ml!t_c^Zc>?bR&}z?u8G8QG_((qPXG)On4*%C zO-!T8Y|GJFRIK!@?6@qOT5ZXWHfLwE@}2hVpj?U?1P#Q5%NHUM6X0ANyE)RVI@K4AgKV zsXUfLnQ6A-iWrkNN{1dY8CgjT1sfv(rsRsnE{Q$QM9+5V<77s;G9xB8$%t#yg~nXI zQg0VA7(r1Yy)cK99OH7~3b6N-fZ_#!0QlKZyZ{gY-;aw@&Po^POg43<+NcSlLE#n0 z+G**MNS-|x=TdojPK7K{rlqFGU=F2`0I12b#Bv#8g)=h7Vy8Ip0=n3y)kS6)b2P~e zK}<5;#LUztWhbg+I5$%xO2mSk`XF9Ru3edHw5#dy_FSqfNyw2V#fw}7K)WeJ_gJ29Cn->#&m zB;{u(C+gTbu_%TBm=o_XJCdy|nUcw6ChFuOXQU-7!xpQ~q^OcIah4#uAe&3eOc2Fz z6que7o0A|kSZ&#Pc3MG_E}NApkw!^*`OF|@zEeg3v@!5xRszLFV^FfhbW2n-fPDh#{f5*jWxJ&oqmJX^vC!qU98eg>B8rU?;hZk@4L0 zf^@1Wh6e3sIxEMLBT=NNl5)9tuE@wuvE&$d1@;6ASDY9}0Ho=73HHP!0kZ&$)+jlA zXEF_Bn(E4DQyfOjl1?!y1YA2+$->wSVGfe&X!7ik;~c#*w;(G^P@q?Gk{osx0Z;;_ zoLr$K${GssyQu>nt! zi^W0N3Td?1md;FM(d?wyBwEe6Y!@@irm`vH3q)p_*p$GQ+v&W5WVKzdamGeDc^07# zQ|D9Y+H4ta<`qQR^4UpRevT_i%}kWIH0)SGeo%6Jek6&ZF(Jm3%u1o_3&c{KsiP8F z^J7m=qth9iWzJGF)mcsv0gxL>r|DSfnMzrFx+;MpvZZ4M9IDcsLrZ5<(=o^mL9ESU zVieGX7JE#7lExu%rsT-8IaoeT#xCI4a?CVc94jNy;&c)KBQvpdd6a}HVC5@q(s&wO z!?L5&VvN|5x zs$?F|m6WMt2PIof#$-_nJ6gbr#CZy7GCj{_5Ljgiu}L;OTa_T=MJDJ3vgjC*E`btb zFA%6OJvCa1$FsD8yeu1q0GKURr)ymWVw24#Fy$~4V-tip$1HPdSs5w$=ExKUjhf9# zp))1vIk8e#L1d05OKwSz62-GP=`oZXc9x815M=3v(i}-P0Z{MIt6~+&t{9gsk`a$( zMKYq4E}BdetEAa&OqDc=%ho9+c`AKmf>?&DmFYo2DY-$B6uU)A$&zrGS~{MmOlMGY zK${Z))1^uZmrLh>RubxQdr(SDu6H!hBbjtldI3*I$yBf@^a2qj@121OfaQu&Dlj(| zcctU86r~u?5a&jdJT&v!6fV!EqvY{{7Mz=r3&smXi?W!MkEMdZ@MB0Rh1fgkCIAu) zE)}CRDIZG(0q|ooB*n%w;z9JpWQSDX%A{yr)Fd@@O@o4hbar+eL!L=xa*@I#02)|g zry^aH#iLj$wit0Pu2ahF>hu^ElcKYyN2dfOI%SzzxqQByg80mqS(GpIPZc2^3U%oMIH#vTWm;c^;HY!gS#!TH`=FE>$|q%+zbaLUfmCyO%mF1<;g zjO$#Gw$zwmhz=6z&0H6cX%mtyhqweGCy5eGQN$SStn}EZlzg$(!eNUz1>!7O5I^6T zY~eBE`nk^H>L>hu5iF6J@a-u6JE=8Z7kRVlX zV;pe_g2X%)TN+PcCdqKScxn<`6;Hc~^VSQw>5C^zl@7{WSj5x%w@yNR&Z>$DW8Gmx3v0XSnN zan@-=-9|dK&1N!ci6L5qV;H6Q(?5+RmF^O(y43~%@Z`ERCy+Y=2(o*2$>bA zad-j}uZqDI@!1P=FHjz4%SH0r*m5T%A3jBZ4^^dX3x7g_n zyrjb0<$+6CZC|(rhg!YbT-)uUiyYqEPHlW^js@H&DbYrqBbD&P>q$Dakv*OwH(B(_ z9ffOm@~o~@=-K+AA2{ABYgcRMEejuB9~z~B}VOV z5)L;;y^iz>reQQH9mA;L+X@asq*t(1uQRIOn~)53uAFZ7J(*DtzE|J?3eJd<;zk-u zl^_ee{XG6N$Y+snHCgQnaM07E2L+Hp<$!;ypB%yoUCu~_6S@}gDU$p|_%6Z&2L8jy zpBJczRM5}^;l&6Kbo4;FM@A|b=z(-Aj8rhu1L?~c37$*v;O2^gjUGszL@IFfK%ffH zIOxHHPYGvBDWAZV01mh+GSoVw#Q>NpGT;MH;U+>Er^(c7o$x_qMjtQ|Ympg|06v0< z1b84mk!Zsc@r}eAo)CTDeC-a22*gOt;Txh5IYpCD5Q!LxJA6YlVg&Li1wm@@X zD%~Es@C}hk`hh}7lpOL2CIUhjc=DhEVc^Mw3Iu{D4=NA{o;;{PAb9ei0)gPkg9-$K zCl4wR2zsm@u0SAo@~{O0!IK9UlmOj>3dDdX4=NA?o;;{P40!UO0#V?}g9=1}Cl4wR z1)e;pKonrK2Ne{MbfN{Opl}QiB1A9`A_#rtf=;jtVbAcOfskhqSVX`72E}!kZ*p@C3OLv}^#Z-ts?$^4uCHtcUhLm09=>#v)N+XS2+?dR*uRC^o^!gM$xJRWb1ur6^=5<(NIwsWq+@#yM9r^UH1$EktxpvpJ_S)aC zGYSv%9xZ6QA`|Dt1$3md?!P*5_gJ4+b@mvtVv~2A*wZ7f`<($>dJL`Ef7j5nH=B)a z8WoZ5DEMOklKbINbEXY!zPaP!5ov8F&uKm=bw=Edy#e3c9C3Ju|D@rqRs;!jGfwbW zy`u-SF4Y^JHMr33H%8B`9WX6GV4YLe=8S&siMyeFTF+Q8uy4R0 zm*q2dY+`5qy>&opRLHk~JxPeH6EQycOv5i1>7G@;JH_%kCr-pekSds}v@ z%Au?yK4T;sDpkH1T0tCtyMPf2Q&kZeY2~L!;-(|(BIecr*Dgx zJ)+Kw-3;rTwo%xD^CLN7TUCy)m~|=F7B_h?^Sn_#v;lT?^%swS!ZL%CRKAB|1DZ57 z1#Nc5Wj=BJePR1L`EN;nKrP8MH4~_c%9#Her=u@WckECbplW ztdzBW_8w}rgBG6@q0*`J4Z($Tnd zVV$t1JCRn1&wfO3Ss0#ab^sDjxSz9RM@Jr=|uk`~b%s5zK zcY_L>pZ~ZurO%@Y7xRM?TIYr(wayDmoRJ%rJi`!iF#3 zGRBQ7J|zk3e*HB0hVf~~pM4T;-lmsPheDY_Ma)x;Do_C$@FfzQ=`NpMWwj?oO!#v8 zVN6f4rL$?2WaJeJl^mvJDTvWJ@{eiyiW1GxIMH{n#gcN z5583@49O1HYjZ4?z^G^ymrqj#r1qu?domM!&9sipaBj9Jkr%>EZm;T;87P<8x&F!0 zfX?Ost-J#`3SsS-G3t&MXQG4>EYC3+^}PaOcw$C|DxniQP~aP-z#=>Qix@nC*x4mj zU`^>|3#O;=bLbr%DsgX{k&$8(vSspKk?D?35tu(;-8q#RAhHOvlPyVX2~}tGja7z- z1k^6E;bDwWWte|jD7%Y76c!#L@MmVI^buXe0oD+f($F?h92vw3Qu#}IXU8cz1f<(2 zvM!9a>ZESInQB3jFx1IpWpr&Tp{IA~z~psluQjv6#j3WQ;=3sVLsZ(d&P<)$maJ4o z1sejDB2`S9rY$Q@kOAa(Rg}pi} zgJ@ZCK|TAH)fjVUEfRxNn8Bih(Ow-zpwBgG_3}ubAB~Bs047m66wR@6zbK)w-zyVt z1)uh{y<1e*jzPFTM<1aKm&#d+ZpL0I1};6kXNXv! z6otq;m?SaX+u5Rmgfjn*R9{U`mO9fMtFi>P4euNmuhPf&N(t-|-m@D&gcsB!(PZH0 zG6aF`jQrHDf#LMtRvDJsR?#b<8x|l@F?2b$-o8T2-j>oOkeV9E2~mSHDXo(xnBU$J zrZh1{;_j(o;ceTqk^*uBJf%fq4-rLJDn?v_gQ{rjP)9~YMn)%^;sfKmF_@uzNhd~B=QLVonBEyB;b*e-Y>P~) z36Enb*okrCFke|5+ulPGBF^cgr|LQh!$LX-qzmPCiYX{Cxl2HHFF~N%$PCNLiBX2I z4U~uu%4oYb#YVLRDNSjX#K1UzUsGxaR;(pRl@^;#?df1=IC}P0Dlv-+@5EuKd%Qz# zh$fv!eV~>y#-h57M|rV^%JwBY7dY<6q=n#2+|OM621v!((BOheP$4Wd zR|pXak2n^BLj>|s>x;3NIPk>t-2^Wfj1V?z43QB?o-zD9@I24%6cGf1s}O<)T!B^= zg5i0^;J}avwAegAi&*bT*7E}3cu@1a0yrMzJ+F%BrFj4oy(^%{lepG5!#CN_;+ySD z^)vWFR{<^#-(bKD4%E;_pa4IV-zDygLttz=aDYZ-L!XP}0?fT)`)SR-Jf$Dek8ou8 z36*~FzVW_g)qK#NFRuA`Nbu-vRtU`FLP^tMz8B1$qlTc?HisI*V&Pt5M>byFVh7FF z|BBcVjpp}@9YFxdo0|eZn03dXhp-?YK)hHW19ttpbAbx&c`_IkgNC)&Etv=CJBNXX zFrdlKgkGLI5e9Iu9C?5WPl>|sW;)6Q8V3pnlZK{&K?Y-N8qTD7x(x$h*=EC*T3wS@iNS+{*?)Xmt#V z3nAa&0>DAV#v|VhQ0p|77sJWE$^Q_;Neccsh7<6Z95#zp9)^2$AWJeF>Sts<^6;C6 zLn8w#<~NHp6&3ND_OBOkl7fGZ-z2>9=@X=d%hV?{EQIaNZj2eipfkPLU81r4zo1V@ z3jR5E6YvOaULNsAYPd{>Q#m2XOj;!0RA}7O*8l7JHIX1}k2!ot~1vKfPJO4XJmFe;VK zDG#%WL5wn)4J*vhA>WFnn+d%}ujU|HZ{>fhc|s*9D8cT}YMv1AU`YvRrhmM26XQf> zF&yL2LKx7W_b?p8QNPEl*U9>y>F>DP-);*2F^0S0G3X%t&y7lz(=|nFdjBBu*5@=XAm&zsc@9J`b;V1-$A%P;9XCuR(R}EUK3-qt=a)M0? zSuVZ1oS&m&2yhGz_!E@Jrb3J`m#Jb{><}8{VG+Ao)DT#>$cx{ldO-jB7C9`$hA1e( z@b~ud-(Q6i&|q<`^0E6}9ZrnN2w{O8qKMsKETc0y#q4I6>Hz&$^b1$xZhG0uh3wyxf{Xh1ik{3P4F)Wp|X&{kThR`KSKzc z?tHX8llF^QGdRx!wEhpAuMF~^)MU;6X;ZL@%~@y z|A40sqTrunI0+A#56jbFc~^%Me3M`rRx!UZY6u(iYLT-`^?&~L{Y~)ZLKKwX_j@}) zY@7*hSxBLfccm2y0S_x@f0J;fDzmzzGGK6{*kH)PLr}}gOMjQ@0{!c&OOgUFe#0WB z?^Y;uDisYh0KYwVrT9&Pqk(<39O6y*s+GlV4vct0m02X;I5UI^O@1$Sm+Amf|Ly&I zf`XFjZ4j2M1Pg9U^1uTo%(D0_gQN(N*ZR608+pA~v0WHPq-M#i!T;AwB0)h3LB!u(r+Hcq z?`%Vm{9u48E>F3CR~L|h!59qkBzL!wp-%!1*BSE1{N>(u$zYFa(Js*7;gUGVmC>_KgMnn92JMYd^u{hclH3G z6wzV5YY)3QAslec^s*=6C3}Dv{vXh^Z_OnskAVYGP*S+xTdBRb!A5Wc)`u@wHw`V5!35)Lxr`gc^q>;$ z5*B#*KqnjVnsB-Viz&E?pm7E$igd5;AGzXw(P+%S*B^?c0K@~4@R|O81a}xXG3JFh z4(59fwOsyC#2hSdj(`047q}^Ui8w5|_y69(@`LSUB$L#4w3FXG{Pm7jGBG91n>SFx zizJZ_!cGOvM6jLiWoLt?p%+_V(&xW{FJ<%}vY|tPn1%U~q=0s%(V;<#wp0P_h(4h+ z#sMoA4h0<)G7L5%vpH;f5sZP^2kLKd3BzGPN$VR7;m~$6?iVKF1#E!h^}@rkVDM#! z$_YHo5foy41{%UN=vk1lU{!a&V6Yw|mvhiCgg-jc!Bj{l{axPLP%oEk^nF}jmFVix zIR6zb4bzH2VBR~#;$fXTgCJvy-Y{W^3ue^P!2pDI9igE?Jd{sh!eK#M*P}SKzMg{Z zd1E5q6g#w=P;Qe>!;ljQ0Gct0{I;QTBSV8HKWw%Eo(M3CS%d*p383(c9!VL3yn5S# z9}M0Y8=PhV&tMozhH9CEcJe53?-d5aIZ*kN8jS|tSu~hh%7WPtA76J9Q%6geU=NKR zj6p9jIN+vUTzR97e2TkW;6VLvD8gvK9mLh^T*4gSRh-d01q@l}2*QdBb1;wv(P@yG zFt83%=YGH#U|I!tfDksQbfO$X{SCz)=9Igi=rkD10ml2fpP8WhpymK40W<>~S!akU z8O+6mhXA1vNO=SE1ZoL@NdXFXarznWDaoaK%YbH)T)rpIO7z`m_Jwa|=Dgd?hGm;Ml_;GTQwNhHtP5K<;p$W9y+G@__|T_?}0|A2!5>Lhi1W zAcMf|AHhaL6p%p-P_O8b1-cya2wYnrbTMDu$^cDocjHm!1RUrfQdtZrkuWL)iUt%9 z_?3CzdIf;e3L1&SK+STPo(`4`42SXU@|51UI`S+uUx}QH00WiK@`hU+m3FPyl&Cvc1IYk9Q0PynFTs$sM%Fgu7Wn<#1RqVEOU-l3<%#U%r2aFS&jO zF&yAE#U8p_z;`zQuElBOnv80Pdx~)JY7|B5P{8b8s~y(lKt-G==H#^}MPN0njTYiP zOxEj(R;z$zJyM;p!k`~I^`wY$ELvg{oh@ zq5HWQaRN_z5gm?Hz<_8`Bix8KB)TCQDCmZ0B%qDj+{>G|L%Z?iQ;T>Ae1zpmFnFf1 zp|=YdL&u4Y){X!@0vnXQjk$-FB0ilz{;iMk7CpG?)^_ER6^pJ+4*d`0A4C6&mow zr>k%URT>EegTJ3hJ7so?;)K&fmTQ;QIrY`e^^jO1!6L;F& zdBS;;cjwOei<_TJf8aB#^Zf7Eout` z+g7{u&F<)xs&h47j-1h}+F0Y}b}{>Zrrv!sJowi?Yx{I>j#u-au+BcMYxeT***9Vy zH=mY2e`2S?_=dOcRnA_|Sd}?r^YuR0IKS=m9i22_O@LuKZ!Yzoaa!`P%_rjR7vp&+ z7f*bIr|mj3fB&NRD=*JAS`xoMvqJYjJ8j|yQ1+auYuFgkIeOrOiavXtC%g7|UgwJm zwX2?(-{Is&(PmYm<>zyMw4UP6NEp>I;MS4}RR>Y|omNcvv+*x$nzE7ZIvy!aQMXdgFnvTzN~3Ace|b(?%UthWW}N9Kde6< z>@#Xb<)80gOa1P^3m@veEv>dZJ^ajrwOOT2 z+xpt-jT-dzwiZtFpqcdOL2dVKq~@b;br=t@4bo@oz9< zsC;&pMLvN862>;6@q&-~E~<8OXyNQuM=dv=9F4zG*;!{}<*%wpxpimVnKhYO@r1gy zsoJJ(TUIt3GeB{vrK`r$fmxS2ZD`o5THS4P??ybO_Nf?AH*HYo%R}#WsaUVY_Q;m? z+qM&R5QoZ#O=w#%ukoLvs^j8y-R!kEBYSW=jf>n{bHXrte67ZFE@w3OcI5tJE!H;v zOE~mc&l~E8jDjlDd4{9ER<62j(j{T__Tm~Ns|WJJXOECt`4h^0O zDqw@B2)g(s)%$Mv=AFxSR@!-GXY|fZJ5w4A*9q^p6OKJDp?x#p7vXQb-y)xna!zGP z3t|_HYqj_F)ynp^DbauX)&1tl*lr!uI$dnPb0Tg^;`b8s%A|(q#t?uPgCtplQ$3j zYRp$N`M+<-{+;6++xdE>!vTkThHTHd7PZ0tx9{K0p47_wHu7o>m%LY%6+@q-UZ`x{ zJx$v>V}{{E?+DH7;mxxDG#{Fi}!+r0666Z&TUkmjqQnfBCQnsw!zV!EHfy-FR(W&QGiI#;gqc6L)?1z+@tNQA8tf}$CxU{(l#N+~r|(X0X>K*X<>)b8TXt@l#;GyQK0S0=9sGRug3yuE4@|2zEiUxP z27JT3AJ=ZUo%7?LQQ3_*P1dCsav1t=8PPrIdKXO8soAp{X{ObChKPgYz z+}qmX=j!6>88z_+cg?esXVGk_mu3#yz%_E042-S%sy*I8+2*HiPr1XQ#ge^Wwq@@O z%P!oI{4(FaErYc-_~DhKP1W1fg)0ZC`_*k)m|S?L#@4OfS9brgw`tq?>I>M*} zpSa8F4)|=+Ijkq!W*b_~dCQ$QuSJ;?GBTw$>+vr71gH9=(IIV~7 z?6^B;k94QwcgK~XSGLC5qGTO!OQ%R`MVVs5W(dSllHbNU`I((3M(0HvzTtl}x#P^( z*$Z_GYc1?E{^r#0dy{Dfz{ zpS52n2w(boeY?e)FL$lk|8RfZpngmDuLzyhHYRwJer-- zFlo)=bkWQ?7gK7U3|SnmxpcXkqUor=cTe2Cc;nuU<2GJ@l>caGi_^7#E37$s#^|gT zcbFX+uNE{nPh!tBWt)uFsj4PxOp!sqsD3M0edxz!Ys>}tzu)iq*DsIvKkkF^)Z^O8F)KX+DtYW|&#s{;F+e0=go=A)a#nH93uFU>!{?8fUkdsY}$ zULV>k|4!2^Vb;)7uQx0=ofo9pF0OyH^U-gQeq{^Hr%xXF+j?{BCd_>0as|_@FE722 z?)vUWV&wp z@fqD`ET7qG=E#}PXBlUmo*g-R{hUs7Ce5unH+Sx}c}es3%*W?1UeIE}$OW$!S{7bf zBww_1F?;di?^}NV^^%H9a+dtLw8zq;%LL0dEDv5jdqtBKBUk#Y%vpJJRqs`&R!6Vi zz6M{jd~L_I)7CXyH)4H-_4(`X{*dv*r41U>#V0ka=+3A?BfVhgpZ$A7LNaaFla&&c%R>D=x7vZN4nH zeDHV0?`N-QuiU!oxcc&s!PjbEn{>U+^~E=6H-7q4^ykr=sW<<)WxMs_cHv+3|C;u< z|KDrxbiZ@(uJZ05_w4uH+#mIz`GbWI84tHVl0CZc*!=j#lMzpwKV9?;f41j&^7HF2 z@?KVZIptNrtBtRtU!Qwpe)FbcEuSZYAG|1Q#R}-{6}M!$5{0|l#gkHE(H52_3ZIJ_ z?UXo~`%9cm3X38txI~YNqL8W`SrF8sC;N6q3j|WMY9gN1M#d}AnnhL@cjNh!I*f$H z#0}s-)nTN$7dL!Yhmrj5MvCX@38JwqH2dTO{XTa$_I>?4*fAEkCI_ns*iqodyH`hk zEAEcG-T6VCIWWJ1!2oQcXe)NO`Pgn92X^6r#j%R|kR$@dsR68aK__~Y@BCVtDP?pi zsb1-VQTa3F1}%Q`S%ODc-T&i)N1CPHEqLgAX~AQFBQzS`Ktm;?;8?Cb}Sq^;>Zp9gCfBQ;jq~>j+a=FSQMuO{@-Om{%1vz z7_W1u`AI5_7zh0<0A416Ps6|~gFX#_m)w0C0Hnx1iwB=ukr1wOpMwG6+4xxiK365d zxa#LIk?_j?ED}Cf64{?CiNq|G&m!S-C6Sm2_E{KwZrz3|_0K9ytk0E1SmEU}G4K-Q z|6H?2!$#xwKjD2nfer`b~mfQH8Vg!>lKT`%jw@M*aAo?5WHR1;vBjmm&HdnM8~tFUhCCl^AO-(f$0GOrhf5eN0#~ z7E*MB|Ie|ND2ngaC&UgECD}^SO=Ho)O%4=6vHt;VPyn+$LAQWI0$P6-@uf5$O6EY3 z9zncw>yr0f-=AZsMj*6`+gpJ~Zi<1_3!yd9jYkEMyI{Z@Vqv3F`RrA-5Ji)PvuJ4L zDzwruvA0GD*gIg=0=J97I~7xD;@{Evk4oFCQYO;ILE8~7U%1C*14lj^0G`lEpPo2!9VVpgiKTlX}1D0VgwloqVQCzA^lwu;eG7y=K{mxmyB{ zk4O!+zhRXSB<~fw$mC!(6SV0GTE~|ZXB_$Ad_Gg(^2&h)t&4?NLhMI`N*bxb(H55e zHrSWdXiBpgEm;AiRX>g&NlA3T#waKxp?keEiHM6bDV36B)aStp{SKqSQoJHR8O4)} zu-lT;YIko(#3!HpxjL&x?^j}TBJlqVC|W%RHde~B+Ov?mbCGb6yA8R)mKeE^!Umj1 zn<*cST_FD@SpJ*+Ndp)~k;@vzAnfsFC9lxh^?D1LI2d0)J#lyBG86Z%=DkO4O43ea zayT63C(`bYTxRlKGE{=~#e+t`JGAaP{Sy;+M=mpQ@3y@+?cR)MfOt?DpP0Bia+!&H z)juUD_fDL}0JeW>;_k?0Chk@Llt{c}uERhHIBb6+?e55BCht`Ry)StdP>+9V@?_*P zllQ8HN+gdqcs8ls#T|?qaC~Cw?#N}NPVSZFF76+vY zm21%(79}>v%rjZFS$;H%9F0)Iz7{qG;VMYjy|Ci9#gR&2?Irx4C1|N=FvjC=*s-ll z5lkraU^=uKYx{QW=*A+{?=2ngFAo4`><}sn#g*V`CGiatj%PhnQuoqln|&Tb)UBN2oE2+RN;LP(fXMW?_>DR2!7g@zMX zN}#8<j{|UqUI~;_Ivm?|GZN2U1-dfv_ z+&bHj*cuz+a3#z%aQxJxWz^J8poL%zSAx;fcsWLs4suqb2PUy_8WEbFMnEMhZGeon z^dsK0(fmUd)hNXd)R34)Z~9HdMJZ7EC;WHK;Lnq+(vezCilCP(S30Kzw7SL8}36?2gc6 z!00`3c7K8mm-O~T*lit%AUVb%IR?8g>P--%7zFGfMKFqB2{H@-By@I%NKo4idMDaI z(UgPO6zYdlJ>MW#V$}H{v>t$*v^Z#8C0J2ADHLX@!7w77fr>D}5)d!Y+(06c09sIk zXj+jeqBKDfptCz=1PTH2B*BLF9nto6adxA@XbJV;$WDv28+FB54s#!1ItMxtbdck( zsObqm)C47dL>inlw0#yh(R=U*apOyiAweEW6)07qMF-n+nPGD-N9{5f4GiOq6?i75IP;DfYSDSLqwr_6b2E6&IIiq zc94+~UFZ#ZLPVi60))Neob$9$6reM+l){h(-@`=nmbZ zlu70x4k7U93rY-~3C^P%5U`Rp zvev^q0(pc8Nj(aNek5w>N|rPdJPPCaj_weF1oC7-MKqH6N>Yx_WFSN(Dawd64@VGa zh>oI>Wf-APmL!r{B)DQpNAiK55K-t%(2j1%R8cC3E_6o05K-t%a-75og(+qyk$N#p zJm{jJ=B7ZOB_5N`M&tdBMyiz5RjjWuL9x_ z3d#4yS0QnbZ_$~^cXS~i5QE4&^p1QYuZTt(gU?`!1Q-wa%i)*n2hqzL#XI~bMCamM z4*W#$Bbs!$h`8nAA^{C(Lz2Pgx_|KGom%PpPe|Px#6K4ukOv}4`~)-_Nd(F!f}m2w z{Bx;$)AX_T?@FBuxxfH~-AE&%5c1@e4_EjiU?8mmDZvALBMuY*9i_P7{Vc!`M~F}m z-NO|3mBSYTz>oZ))ITz4S>Zk@I+yJsjzFHI6Ui|7+C#hhDxdZ1`axdqvU98O1w18U3SELs7YGwt$P1KZs7=GHJ!MdwAf!3aDN@A@keAi;}c+ zfD1^7sK9}b`l09_8MLf$pB$YdVhh;qoR04sq?8^SWAhd9&v(6NB16C zc>78echrUKa9L`WtLUg1efl|y( zF$Q!OQSDXQIb{hqO8s9Y^JE3!(fXb%(8`Cr7Ep->5>%6X;0v;lkW?2@PUJV2%@Lt| z<%l3dfvxDTtSdx+*Qbwpk68B}=pOz^xKE%z5z_mmii+nMI&9E~CV+xgK**EI0oJ8r^T0<0eu!-!cO_{5 zxS(Z$B%*(OY7FQmP&ppCAoM&bB1lN!69W|s4D}W@R#o5odH^W@-;Cugl4l9Cozp7HmYFAsCB^jNJA2x<$KDtzIZg-s8{ zE>7{MEa3z^cy;#S+FwJiZ#wiivueL-XO^5Sm~Q#vD@I<#%yFZq4bJ3zd8+2J{GOJ) z?+@(`d_AVm7jv#(pC+oY<=Z0%56wLivgEv~dBoBxcUy3Nxv&$d`TZM>;=x7pVk9hh(#J9)VI2;*GCtMpaL z6!yl2f2FTF_owTSYR&nSL$0mYznD7rWl*2-N6t;mT6b+L%@UmUvcF+k_^XafPwn>^ zUa+O6V_X01$GSgu{gs$C|4OgTvZ{W~Ck^bDoMu~++$y7vN_6Mm^=k)uwTjs2pSOu> zxzQ-@TAw|Q_f(wuJMD?BSKnp7pRDK~acIq?vxk~=e^F`1Bqp={ci+~gue&r^|0hQ> zv5V^7>K@jUK98G)tqzfJTFqE#ei-@k>%^hYE`MKG`;d5jm1X;LW5>tmoZOqTV6aa> z7Dswg|6Er2zhdc?2+-khFDBzcRx3&wS^L>|md3^bn zq%W43MqN7)yGb=-(zyg$lV?M&k6SZ%@}XYN4b*3rAI|l@QqB2bOy#TN?2%0u{W>fD z{$Li*pO3jk|M+#+f?lznSL;_z`cUO;@pMpA<@yu3>jt!V{raydeGHqYohrEd zN9?9ES${DD58b(Vt$O3O-?cuqUhqShtjeg3-+1)tvL0FM2Nyo;{7e+!7;pVyxAEoLN!F`po4>4L zeX;WS7k$Q88!KEEIdk;MF1@8S`wwojJ*Pp>T8sVqFq4v>{r$z!)%Ll+TwXT5U&BG2 z+l)DJb=RFoyfsOG{gOZE_s%b0O__UZ(%I4Dt2R&H^lScs9<1-yRj)U%;;TT_)!fNp z4}SP_aB}P|pDiz6u>SrdYu(MPt``fIG&%8G+_M>Zj7w)$tz0y0_KZ{7mq8a&-ZbpD zuy3;lRptl&KH~TfkB){meX~Drq_ul5-@p27Qng;y^w|BR?}uRrdOZC8ynSu2Lj^Un zBd3o1p-;`PMr9WA4lU~Ws!E;e_k5JSUcWwca6|2T{ZAXOMW3- z_IM|0M!+ive)-|3)aS4Ny8PmYoQzZSujo{M^lF{|i215-=cg9xRaf_{Z+T!{SkOw| z!_^&A3+J2;Y*c%M<%{(-cC9v_U3LD>#iQ1s7FGi!>Nkd)4!?H zXX|Bb&o;`h_wF9pLwWOp{j^p0d()5B=vku|tx=6$zI|uU`F(O>T4=xKcaPsoONeOD4tB-%jE)a!dIgr~|E+P#g2g+mHIMEYF#qn+ znpE2M$Qs&t1F;61oj*r4)OOkyRqgKap#%F4t=KoJ5&u|prBMw38LDQ+ql&i8mjR9H z9*Y)E+3UM>^axCTg%VsiexGLN2w}hPo(}29A9=gmcdyq6Y*~Hr_KF>ELMIFy+3#7` zQCIG@ssF?H4H0vwFUK+bLmO3UTcKCN+74qXR~vj)vZaxDc9(rOBF7~**BQ?EB&_Pa zuO%)DJ2SLF;5QZL%%2n3zuSoAKEns@a$f3vZ$kGPiIRzl!o{r*E6H>Dz_RR6dmkraPs-=JfBd+uD?RcF%+cm)8}pI#Y4wm$HgcO>3NL zkSSg?Yt+Ua&Li8E!w#!wU8M>Knmg?ZnyiW48>g(aV(g$Nx&04I`y_q;um|H_{`E_1 z_U$oLil`KJMiF-MMA+$J8NFLO#p4<>2Y)}lO`E7;BkKB9trsXu@NW^a(c0(DwH;L- zsaxoF)ca+@cQacVHZ-64s`A>w^C~x>_y%6wy3NXw<;Pc)`e)8s*hni=KOGcT|BG28 zA8#ITqXs&Q-3qLCwHl~2`LRF_e>;ARD>z1FO&-Dh{f zU)75FfibJM?@`*Bu@CFj-B<1CMpwh%Zgj7t(ciccc_MKXeRYVj?F64e$4~g2sIa7^ zK<2X}W|VPbVc!JlfQtUhB$?GacCYE+mEZCtx%&XCr^ZY#(2-r9W}-+yHXn#^%p*guSx ze&{YXd|10qD~3@IG^@PwiL-fK-ucb3BCZFnn(Up`?o@isJ4j~_L(ZlA`x zI}el|mi4&baDz_XaE+xR1|As zcpDhCzP^3$ZgqA2OLce8#a4>1D;r%N+h15~Xzz9PTi4z_8?Tx&7aKN%nYLrrv8P&f z9qyiK{1zctOw{l4!IJgj$^GBNQz<+B#}EB+e*BcH@i9@eCAPl17JoAo`!mbcXl`Wx zZll`O?;^*W3`yJBZUn!7H??4N)w3BJu-e$eA=kEc0Ya7_HIOcTqikSi`{Ts;Y%EhNva3&IuJO=k4#N zuH(C6Y=tHI%j??rXtXrImNa;>9H;2zmi^maPWYNywJ>1E{dO~=_86wuP+qIvr?D!u z%GheIpr2zpb#*1wX*~aE#Iy1HXAC<)YrlBB;~QhWMwk5+O3~d>y)*5vPw46mY0|2e zV*AkN&Bj+8+;c*kiP)+7GZGfMB)DSxccPcZ{(hqtbr`ZH=Ca9#cWrifaN46PGa7SM zb60k{D?0l4?v~1~9kI1IKh-!WJ$<6q1)uEToz&!J2Uq>nL}IG`_v?DqX-|b4rVO5a zSe?-!-{s4l{CD`MsTs+dr;=uUX%{vJUf5T^R`Y&wetT!#XjoliN~AvOE@R-0K)Kf0 zom@3?b%p9JYmZqsY?69umFjY(T)e+!Ui}Wi^}np*`o7t>wdU-uUj6l-GvC|`Uf|HT z>l7cRsaI=XT>qN&JB*#en~)?PFl=y#d0htb8H3x?emSgM@WjLPu8xHZ4R zu<+KC7PK6@w9Ap5t0&c|w#2vE%u#i}t(WyUY-xT6m89V#mbymjkv0^(GzOR@nd6lL{*I* zxvhcUBH@PN$2!dVEpkDd5jJ`a>6qz~C-KFjLH|-<3hCJEPyn;{f zmpMCoIu5HYEjjj#-+>-D8H@dPzh>H?LdDm%qoclT;xZhjHyktd?t#I{BEw>TgZlLp z+fHTZljkn=$Ol{7UH{_V#>FjzClBuM{f;MpMxDltp+C0U88LRkkS+($TwXUjXooJV zQ+?sBhLcAyw!{hoqsFbown{pfrXM)nVB)AgeRj{CIP%nJvEYXA)aV--J$|w)TUY#h z+nndk7fj#QfH`TM_X5{U-}|^->a7j6mLC18`LgwKS9>pA$GkVvm~gb_9r;hc z>z17<_+op6kiuDiL0NHkR;!CE0st6XXN<*uh1*YA<{XP@B-an#hys#*Q_->%rH=KXwAEcI6WH&2`P zd%fqu`83POrOb&1f~iCIeLe1>|Nb$Ov)SA2FJGmOxNYg(qD{)^_FrNTdi8!?^I+b# zJr`=8W{qO5&)j;k;v;46*SD@^|FaivU! zI;nB()Wn#~MD^oFZLr+a-?Y<4`(A3lW{&JdjaAd?jjXY1e7#%4MsW;{AN1RKO07w% z`b8aV#SL{pp>1s2q|ut3+KqM^GN;@Um}<6L-Au4|@Zg%P>TN64#dcpB*zf6w9VYA7dBxtvq%3=gmuxZ;na&E%y8&-TcJ$$4_?(Jsj0JZ|%P1-!2EWO1t%RMr%WI zT~${6;}%z&?|eC8;Ld)x`&|y8&^8ZVB!9wLo`i~U^Cj1@dX4`yP*m{sKGgMEK2ETyQxaE2zxS%6|TP%#bY znuKKy7Z%9`3J5YJY(OMSI6%hYy4^^B8R@^{dT&SYUBTWfG5?jwS^zW@%XX2Tn_CLF zH%rn=AXsC#jX>r$0v+4sh;}<>lL4Z_tTI%$-8ZMS8Hw{@`g=B;xaEUeJz!4pnTS>; z1KR*CVHQEmJBQghOwpzUa}vj|3A*_NmpWulqJL4+yQts4NRQibute@-EsNl16tj}` zuZlPyGMi{UkVQXkB^$_d$7uQk%q?f?FocYhGhkm@Dt?2kAt=H{k`0YU*C-h|V`OwG z1CkGV8pX7{{HcfC4(b&SbX`XERKt)Y^rWiVlA^;KN!D!}H3}RC3UPurAns`><_3;; z$+T#DstjLPcE*r$076q3)nCv!9<>l@B(q7`IYqaXjArFD!%(9^j8T_B!UUkmcpwJI zw0LBSMnyKacuGKG4h=o;kaZ?EtT8=??JlIf^6`9N+~dgAu72sAAL?6b)3>z{rH~J##g% zxdO(b#&R92P_e54drowl!pa-gf=7+MtS_-Y2|r5tNhTRul#BFY93+zz;Phl&MjR;h z#*ob`=TmBe+Jdo z!JMH#hw5(m8Hi>tpa3>2`Ph`?U;~m#O_;=JBvHeqN6kc5%v{NgYymY40Sg6-Z1)@3 zd|5DMrS~_iN0S<*lIBt96Z#X`Ho~}RzB;78K{FgaN7^Iw*Kd%;2Cz7)_8D%>$W4kW zhh5ChG1n+nG}#E|8tR@2jWcr%ae?xv1C%MaJ`qK~PnEt&g)gE+GDdx@+{Z3*BCRno zePBx7J`+t$`I%~-0i4nW75)hGOw7SBK6|1Bcj)|eYW9$+`rG3!V76fFfEB0a4yOF} z1UOhRddw*@HFHG9s)$Y+GBBl739NC~QtWKQ9l1Piv~np-M6(7{3Nx4}Trn&UH*0VR zC5=J>b^3^m1ROTlt`-Uoa?Cj{!VctSsc>VEPmMtYO9tZ!IaU1FX<{WHqzlfs*d!pB zfC#Bd!x@4%THF*MsLsbQ1q@WM|H+@fw^IBoEpj7hQXws>qeW$OQdJpu?YEig3b9dK zbtNjUM70%BT7^^v9jUA$#iXEo28tTQ)RJ+fWS{C8>z9Z+a)8SICPl}Q=q2d%ENh)yb2?xnYX%LCn zM99V>cFZvnJMqy_AB(HUJsj{6Vz*Ql&Zc5TIZ>7C*c@$QHxUxrX7iXV8nX(ViHnWc zr1*(4D7qyaTrjUQv_uk)pso-VNQ2ZACJ&ABn5u%im#Ua2(z#QwMd9gUr3G^~4LD>a z>gr1%tYC*p6&vC;Y>L1?0B=nBpRqi&=`Ac>S~-az<7YAH`p zts!@LfDq;wHHvLI=AdM~H9BBF4`TfbxZ%O)A*eJc6;2V?c6dqtSObbVxM1F9Xc5#E3RmNcF0xw8r8C{3 zler+u)S)w=q0o`Lpv_GnB9xMFN`n-G6|^WojKhoKs6IY{XMAGPulx(?TAc=5bk^=qn6bk|BXk{wPLy~MbKyk*nNYTDM{d;&D|Y!=IqE}N^}`alVMZ$RsvI* z>be2}>s-w1*px5SG1h6sNttSXB(D4MdC;FDxIT)3f636%1Y`*}CODyKbe#jPTVbw+ z5?w);wr@llltFHVuwp9HCQ&^J(IXMJXA=5nB%CFO2KWJ|@ROR4FY`x#vPD7rH_Wvw za`{OQx9fB3`u;89@FO~Q4R`nvo%XmMz((g7lXoL+K+<(DF7?K>0;24TrC+8yp$;p| z#Y;?&U~)&Vc!C6<%EWxHFdYw~5j@i&%H8Aurg0Z4mc(hKqg)z15j1I{ zn|^q@T!#>Mm1BAYmSBSGp{O9KyeOf-hEBuv>_~lh}*KVu!4_66a+D{Fg{bLCodL!>&HA z;LJs!A5UzX!0HtJ8zN1Q#2_R#xtEdoyk0Lkj!1qeCp=pQNGybym8#=Or>d!6d~C~NYc=3 zGG6;DMG=aW@(%3~nWQ{Q6msAOJmScYNf{o0`8XYp%Vi1&%MWCJ7T%X8o+U}lq~muQ zrG{*_GLZ(b^H)g2!^nQe954=H5kiv~EK<_EMWz2o6dQ@0&t!d40N(^@O{SPmaM`6; zYxCeYjY5(*6ylkqbW=3sO(m{un2-0ULz~GY8X9Ed;$&F{4ysXTAdLsjTbv^&u5BoQ z&8FP9LODdAa@!mhm|GiMw7#OpiouIX3Jqhks3y{;=6{D09FOYWeC8O2mBkWwawEa; zDwcb6!jS?e%Jg!Pi23BW>+InaqtXCgcl?s%5ei$~69A}qMucuaFTsK|XqNtwTh zJvvU=r6;gu5EQ%tP!Ms$ppKBDE97`^aMIv_pnx@}LhwuSY}J)76?H_>ux31|*EJGl zm{vT>6-P-C=B->RgZn~`En1og=oZx zJ;~)(B~P%IV2cS0*CjeN8hFk0M`YKif*nK?_K8IUoNOKdL-vD9HB&gfA4*=X2!-z0BstO>|*VWkMTc@VLrlrr13$at_<%@EVZB?gzpYM zyL$&V$_Wmllf z3|C25zf7fWg@%J`G&tPg!Qu|aR`lD)I2$DZMmS6KMJz2VrpJJhDox62QkqQuI$ZDJ z#q1D1rO6Uc`x_;&R{v{O&cD9JaqOmplJTS$G2xlGxwvC4w8;F+Odbfs0xeiL40G9K zNua`%G+I277Vz694US>%yYZkB#8qlMIEL|w%yu78u8@YsAU+Ui1@j9b;Uo&S*6Vn8 zleDh^P5VmrrpeQik(940O-bfp`VRX9C+gF1l41u@YmE6S<~(e~VZiiNESn;}!NXFo zf+c(#YYsxJRnc(*bJyi5xFT)HSS1)y{6xrHq(zBqH{ywW#h=LhNi`VIVx(a)g(Sp! z%)+Sk=?ml(yDUCo@M$CNUC|0F+N+txj|8JJyPwKK>SUVF56Q@p%rAteC6ZPcJZS|> zTftJ3FbhtAc}|e4akyBNxFSmasq@4YJOwDViHR1&{?rvJ# z;fQUBaMm#IRF=RoN+)ZlBr0K^sh*HFmV51TjfniEA`L4DXH#&z4{HZvYbu^Xi+xzi z@zYG;XF7Jq?BB>1k?LQOj+drji59U^5wQ|uWR$4rD>&JYkzN8Lttg$*5PbAvmPKNv zC0ZfsCl44Yrp`%XqapZ?N8aNB$3;iPMS_Wdkz#V=$ctR?BTrzWm^362&%-fN`%+Pd z7A!IK>Ar_z^#I(BCC5*}*d#@bW9l$0B|6nXel1DL8R2J~Y9_FgYJ=riFz+bb?5CCj zX5y*%gJ6iR8xvT5k7O(|z; zFbBiTA?y)@Rex!_KfgqZ)E~_Hs~RMuGgKt9p=s&5j2{H9TQ*G`kE+Azgn>cBb5+fj zaLB4|pb^Aptr$7glEj$TU*9$zvuzsq7D6CA3VEX*)%6NyQp3zIxvT$(b3718Quam$;*l7;ZJlUj7j3#WQ?ibp4R zbXG@aH1{)w(>Lyq%hR)R@5Dgh5gpDM_9gvbNi%>SOuK9#@*+!1jM#VCfEe?LwedpK zBTZIGI5;uxPmKDD$Nb4LslkpVH&j#3Lo?xh6(l125^3FVQg-Hf#bVP~)Sim{tEkQ+ zCD}`w+&Wltji+>pd;yaF!fKrRkMUH=lqe#vPgfQ5P>R4oiRmxntIK>(ML)}+&;OKI zK$4!8VG5sQ^2DUcivV1H5ddCwsuNce@t%Frp0Ty>k98He^zYvWb3_6jxP5rgpWu&T z15Olfb3eRfRI8N0i+(*tzx)@M0n_vp91r;AOZ`6faS1Sv4I*Ey#k3GIzThXxQ%8Qc z{oHiF->7TGF0J9$Vu=^mq~nDdm+JaBXNnm#79<`EJT7LW%5Q)d@knS9GX_xzkw!2d z&49)Eehn{#sC6+)p2d!{_;@jP1Nq*Q$OHu^7V@!A2TClYqNLK;SHr~~8pt<8!d@9R zO)Df}KMWiDV5r!a+{6C8I;@clYm<^t2w;v=3h{)RETtw(s44S=n#AW-?@Oo2l4%l| zIG`8z44}9%XgVC0@f4bg%PbQ5;s1sX`xmt+X!)ZQ7Dgy=0hwJ}no2{%3`HRZf-Z;p2oz{}&(l!)}!6 zH(IBft#R_4s2B+oc4WpDZ`q1&+Y$}i^nN2~QONm7$%GU$ax45kE{&VJ#aoX|dRLy! zvnfXTq*(Ez%u+m8q%1gBGN;nWnKHUx3R>i@lu0Mb6!%){LTOP)(thV!iMoX`=P%|; zX;POiCy^W=v`5j>zJq&M=(SWx8q7dwL+LfQ)E2#rK|pY38C zNGoGjBig|Ry$i#nZs8tDn1XL1Wp45^b(4u$4}*rE5|=fXI~hl=#Uyl9$TI%@8)r!z z?2_g)#)LB`(XGnt&cBF7iP#h$JKspy?M$LKF0&iw*^ z7QUw>euhT(1bXzw&Y*8UCeKoZCPKsMIc1%nr#e4RP4PUXJJJ!(Q(;E}mncQKD(SA2 z`}SorG$FYwqZ2jSmm0Y*lZPdkG_0D1Xs~@r99!AGBzF-i{9Gk>JDN010q;wCH*gfH zpwN46bgb|v_utr9>Bn51$sIW%y=_z6nb?*NyVAfaM3cIU+SIAp#e0(q4)nZLr~skU z9coZ@rG*3DU3NbwRdfe9*ON?@oeFl?*M)P}iiNxS)RCEye&iZT+$;XKyN3SL;(xluQF2O>riwI_(aq#ds@#Q^(!(h8Zu0)w zRAmIET`o4_Fja)8h#*{oM{n|F3J8lOPNQ^2!zaFjDGEuDrs9MsCZ-oaC1}6{I<=X5 ztb)&D6r@0$yqS9}>`v|+Yl+UWz`-WmW5e-(f}w?^mVf`u_Ug}NO&%ad2*=z z4<|!3d5N;J>AYyXu($<<78X&+GJ4{nqB}wY@Y<{aGN#SeKTyi>1 zt;_D1R8f9tdefv#FUSt*%?#wuNp?Sj%Tw>#{QgOkX0Fp&;$MEFr-B7jiQntVZlv`1 z{hsVL2X;55fn(7$e%EJoM`cLUIx;5!S4b3P2_d%3z^^c~K2L=d-7m>*m^88Zml=TL z_e}Pu7m5sgqIpji?wf?usn3X?o=zv|GwGS!$eS?d#s*)Y90?cUzj#+B_hfQcj@*-YSDHh2vNM;sAM1NJ|#oAS>=j=4=x= z*@(%>#58w0P0*kvTxvvz8YkvZW2Edymh#IZchHPTZLIbrHC<5A)(WVn&($EK9iWkvM)?EmIx4L!P@YUqKFp>b6(^bD|IMj003_Dk`4 zqbRq8O$CX-kZ=r&sPa#@Sq{mfC1eJ`YCu4|QCChX0~Gm@8lS8ENPbMN!jH#7Yl8Yd zsr(b-p<^-q0gZ`DRpBQU_$-shRq~u8$TDWQ_xkftm15H)UCKa>71z%~l z`|+*!)OL?AhVQ>px8HW32K5N;g82(}OAPvg;fr0c8_S1AAuq==2^eCYIoK@;lNVQ$ z%YxEP8>({iM1>pAC6G|L{YMmTgOo4gRX>r!CAu7rUoRwhdJq+Jny&{@t`J;_@~cOC zl4MoK1{a!--oy$Oj~G=jE!3$(#Y0pO$MQ$Yl(0{gqEb~#JkHod&`wvSBo!$m@(#zd zS4X+4SypDBG7UDpGLu|7DY6OyWfNGgUW5C=%ds52j^O_uW#^S}HpHR+?};0K`hB8B z1^^%b-@gsG|0aqR8`meA9{h0rjbO?tw7s$NJpUPJ|5;cIkVbF@XvZNv4Avtd&Hanh zZW{U%a8B_r9uglK^X~lx5T|hizYyYI#O*QQ4bUERv(ul1H$Z#**vJ2_!9tilI@he+nR6nLxC-j~e42ddT!=`x~(RtYalKS^G8TFle&|=)=pi>`5=I3bkUyh2; zKmrZom3KuGUnV=a${uDig;j`tsgz@Nhc5dt=c0E0mc3kJYI z8w7*z-%w!9&+9p)9Pqtx2u0s&a!!^sF~E6HjY09`R?K^LP=%k>nwaVCu_ktp)%x9I z(=S#fbSL3sKJ<#zxSzTsn2Dj4FFYC1ecbza*Om^rv~=VJq<__u0!sqSjD#;;v}w>3 z2|^sA!lj$+`b0~(UNZ6-?!O)&EnI4>(5b&V?LWTGLg7~+V{N>AfUp5jUpr*934SoBS>UKEG|O0vrnOs5$wjk=*GwB&PRtIu+ecN z9zuf-s!<2rFE}y<2|NIjsK7ymVnl2lAA+JoNNh-7WQcI9+BlJ_7aGeJbTAmVu;qPzp+WQXX#wM|Z#w9}2+dh8x6l$$AT z*iL4O5bv>)M&ydX8imvwvW-}jcv`jyQIinYBkvz zzWsR<^YODh9Mt!Y-83XgsDvr07E zNL8Z2$H}yD!`XQ8!mxBUK1vd0Bs@{o5zHuIo(B1*a%MDzjZaSs#xJDBjDg6?CzyHI zgmTh5HRb~s_Y)UQgt0WKk>Q*Aheub@_A^L?MdGX|xmcB6#ToF#L=H_(WJLbqDT$vL z^ARO~(l`qeUC|jaA(#!p(Sq^BMNB8d7?2cpVT9ljU5h+utwlE3r{sH6US?HGHHgVsbc|vWhXHQR1pNHX!AE?Pm6JJ1p zw3S+)ZZ?G_{i3Mk4HT8cByc1dpgN|oB#KPw;*uvgWspnVk})HZM{HbZ66rxCHc`h!8$N+q};<bppQ*f=l3P%?|55>3!`FNCB?9|7EWy!+qpzkOEZp^p zcS2sA0T)xJT=E^iLOFgRwT6>9N7s9cmnRbp5yd@Y91NlOk;IUICktaH5t1;B8Ah0C zgcwI+<`Ie?+Sl;oXR+fa6f9$21TWynxHP2yBKX)f+Y~?gFM^LL!bnbq&#<{tcVCEu%cpXM_w$N73W zvjlZ6s`XZ_%qvNmlO)7$^XQZF@w4vTqI3%xnf>=W`fnfJy~Ah!0-$D&G7nEurt}@Y zJV$X^&_@Y|a+-%JW!Aob%dEgi(HlH10EIBA(a<@~*UH^)nM~ufF#fpb9?s$UPPd)! zR-28dc?^NuKM>^NUwr>|T7;JyjfVI6wM9CFMq>-pKV?$oPvV3ukuNWPuNRs%)YSjS zK8MDVpAPDQ4%%-Pdv(BGKi|zGtY2XtAiP2UR*MKc`S$PhgWIFS<7TNm;On)_+`sF! z22@zicf0Lsq1P>U<`GUpWvAWF|3{noMZF01if`3gsa3(QU0bgyWlPg@mSvimmGg4GHe}xas+PKMV=X+b`L`h8 zAVH~84jINqPVlWjD>STZTykMwyIkzmrDp2uZ6}XIy$f2Y&q?6-hqwxTR8<6 z`}U+^sanRcBt2(yGI!dnVnyXkX70RKE4O1<&${xfomMUX`%%9A!Di^RyM<=HUCJQo z^{P=V!mQX#1+i0RQ#p0cIX$xMjBIMQFo5%N?Xr1J`X+-pZFZ_?@c3(mwWg^V#j^CA zq)OKKkIPAtvDab4ry9BAH0)q?Fi0bakS3fxJJZUy%I!?2TJP1c#xQ})s7juJso1PF z+X>2sDs@J^pRe^@ms!g5rIpN7Dtvdr|oj5-2Sbcxh(&?n+ZzQ zZoW{hRl95{=N^vArE0I9IWKpbwI0<4^X-b;;HpoMQF^UR2sAUR4MkzNw3@Lbw@oUaX9pk?=atZ?d4y~nbT&g*J30g zNd{Dd_1empN=d}7=Q|(C&pNGUH~Cq5Ks^;it8vO_svmH@7XD{RQx=lXE za{so1Sut{$`DiqYA5DPCYrDzkGoWr2;FRx{0UgU&tCmVjFPklql&*YT?;)#HWf+BZEeghMtz&omLrB?|FBippjn&sI-e9uYb+!C27&Fk>ZoX0E z59ovHqcb7Q7t2oLl{ar%l9@3LMa|ikYL6aSH_r*=$o}x$lO5^(3uZsFYMK7Y$spCL|~ZkErar4tih8E{@sl@har^_%Eao5 z`^d;tIBCl_y44Kq?tF*ic~nsnD#FIBRU73@r`v9RaBf%{QM(J&I6)&2@uQ>_f#j^0 zM`Sv4SpKbCOWletX42D`qU5ZBGl{U&6f1AvMPtEEbPqV;wQCN~Kd~>mU!LGG4h>^FXp;HPwNw zqME|NuM8gQZWE4~W@A^>GAIM4e4BQZk!iRT3AWUo;w|2wRy@T5JRjlf(PMVjF4K2^_7*U ze8kT$J#OgNtbNqfn2R4hkiLqRqhLAC*2Q(*HLIf@aQ zo40E5?X=yjRBPpk$jE6`gL1p+`N;4yf2mklfOJfj_RL~Zx-FhC8vOety*@9yI)@%JE zS3m@)S*hfRhMzIR1MAme*LV8rr>%6n`Cg|CvK%IN4BuiNnL{=V+5lw?jW|*1>#@_V zqIK2MWZ+c>)r?sw(~%5nRqH@May2;R#;e(FwuE6y1II~fw}l~~(OT)q z%j?GwcMebx#P3q3@O#E@!$H~}zAUI2VYO2yQcS{daIMksBaJmhNpVPX$$EX5fU9BZ zH{v`gl{?kfhDczQVMM<`EpJ2H#5FNLHcrKShY+*z_6vF}zA*iBueEb%yN;*=vJjv_ z7b$m#O$b%xtTCKsMRuB*N^13XDF-q)F!1>lt#z_y1z72~X8VswA)8vQMLC?Ih=DtP z%?(FJT8TM}337h2S_i3ad21sp{x)K!onot2O!D#lm5Pl%sWw@u3mkdBe!Fj$uw=D- zYYcGEio+&=kGOIJDKm%CXjH9(+}X#&Wn7;Sbz!rN^APNUZnYcjubiR}JjvleK$^z{ zp&D|GkE&E`H(R6XAWpIh+})^pZ6V@dWWR`eD&*UpVM`k4Mn;}RvD#g6UZX0D$I%#U zlrWMpYDXg(qp~=Xw4GIHbaSQJ2#QE5wc3?tV<>W_6*eBHwtO(~)V?=8;xrjx7u7yoUhEA^YmIooMJPcZ>R=JDxAPpE_ zQch0POjI+|PJ@59a&hem9$s3tcAs#7O|)rMU(@7>w2Jh|6l@r2_!dio+-luF)B`{J2J`#|4*E6ZsJ049|~XPTHk*XN;o+SF7|I#bG!@ zDp4gH$D0x72Yx8uXmFEux;g+?8!2!U>msK$*E!#DGkbw&$hKiDE4dWsn8eeqw#X-@ zv7S%~No@T(*}j&D9j55^v_fqSf`I94X$M#p`7{~deV$uM~r{N)-LWCfI31R0Z#k?688;m z(L+WNj~YeJV@8p_x+l~Z;9{WXK&;wjbli&e6>{AU)bY z<3kJ89tAGa=9uszl}7_CUT*-zNTo4BMq5n6?0=-I)<{dn!(Oe{!*UpaIE?{fRheNCPMNXC zaW2ShbC++hv*742XMiioP$g~#HXDji?_~bAlPN`V{ zdhdrE*>ahy4sH^D#~b^-*~_#5_cL&=02M&r;I2t?uX#8Rs5E&n^(1o>90RxOeZjAX+2ZFsu5fC05lL3QAJUH4LaD*(zIwv55E?~#ffe1uZ zQPMO4F*NdFtB{b}J!JcE3Qevv`5K@jIywx>Oipl$Z8(j(hxy;j9F4Uh&mA@4n0$lY z%EB5QRJl*m@R_2u9Wv43TS zI@Hqk;n+|UR_g1j5l(!$rz>G5)1{^)Q5i>q8i@cA7=)Q0)Z z#vLQ=rD*RQwlxND5wR_jZY~%$*S;~Q+9kf=?>P=*A&4CTkxjgOi|8V&oVi-d%)v9{ z6!Ew;%0ynb%EVpg!rR-kbia&f82&b!%?3UDbl-i+-feEG{>sU&y%y%D+Z*mh>RfK_~VakcK(N-%`VJNWV3Vo6VR-s z9|k9XL_g3?R@rW!?aA!;6ocr!bT z{|$Z=_x91hYxrMRwbr)KzbB;f<$ZA#{aeKUI@X!mf;V#$sPT2jI(@IeyZH_D?BqHv z{>L9Pt*Q35VeD65jmzb`dnL#&yDM+?%|*5*@HqN|7aA?$RfR6$!pA-NQ22JL4bYjK zK(JnX!5^Z>;q!f%>g>TYeEv~ol_yrw5}mxjH+$#NfK7}v?%Bnbr9K|#OCyM{9eX-IJgoE%ls( zyU7Zy=li0&Y@a>&d!@ZuXwI!|*HpKrPZvycrBhLzc5zQ${IH+UMwvL$H%7W!%#A$fKtJ2CULnw?zokF!(CVj(+i!Y-cKIMLBAm|c~2vU6LthwOZC(abI! ztmU$cXA^FA>H7IPyZjiuXLE(+`RvMTbt5a)PtUS)$1G>n$?jBE?OhpJeP(?>Yb;GZ zXHEH~owc@))T|w9yV=#N>~nUlSVYT|-I%#CvzwdyC)us*jo0jULtf0fs|$YCdzgI5 z1~cucY<(`v0%ul~$kGB@*v!^r0UiPfuRI?X*M_Tquy1mL? z_ueP6x6P#>d;hZWhPrvYy)v`UXSK)dk4t?iTev>I$d+zfQ`t&s^Dz6?%y+XNGtzFh zzIykWZJy+IvY*A9&un)|*`3Jl<$Dv8E%#_*#+{s*n0vdunppI1Iu^p6SYD8DwL z%13qRWDdfSH|ST zMSE&;M%#LuoUaycCYLXa-AP%!vnKT!b#ci>Z6iy!A zd~8gfKAvflm+u?PlXsKzvy)GDYhp5gnr}~*UoYyD@5;eDbTipF_ueKuGtTSO#K~4= zYIezcomza}d7F~f4&JBq>{Wfreth|y+B|4wr#xSto!V8M<*B2^8-410sk1S4lUv=H zdR)0aoyxCHK1@}P4&J6desp?Mt?o{4I=j}}fNrK|ZjTSA7kjgh)ADh?F>Q36#p(6q znayces$5L(yq&#GA02GWPhXh1?diLT?(Ot*?c;O0^j`b@Tz*Jfrv zN@p{s+@iAYV zEA6)q=W4EG%yr#^iTPQ#T%2F=7EkBR!^6$_t>;c|e&^Hg%%A2yO7pk--p%}vX7*sd zx_ODZng6U=`ogsTxww#OS=IXMu>>PjI zEFaV=&E;$TQC%HOb*TXz%hP*vhtQ2IAJ4DI8yE9$@?Ks#lCPSkA{S(@DK~ac zua)V~s-q|yvx~}RGpH#C-t(PuyO7&aKzKBj&%?=Cb>6>dsD?KGL-q6@SL&(h?yFDs z)VBJ5_-bhrHzyKwqpftkMQvlmhW|;PTDMTmS z&hx>odA+H8n5A}e!R(aKVRLkHW3BJ*wyXos)vWu?wPUM#u>EdNo@^_&Qa(7f-I?o_ zeHOf}+ds-vu=B0;rPYP%-P!7DIFSYNwY9T)SAJ`)RxdYDUgWi@gIaS<^LD~D-=FQQ zUHVUlYo(k0*|lz7x>=XzCiV4g@4dQy`c~Xq&s&f4>z|eT9~;YCcizV4LTP^EIR94M zc)6ZB*l6wR>gLkUL3?w!fvfx|I)? z{H=B?Z*Q-h?5u1%raHTQF*lvvF4aF;PWJif!%_1ORVOIjy*f9qHx=i7Hvi_%u7A9{ z_F1p)9<-#k`|R#ax~+C`5xVhm#|x_GD2JQgMYp@}RSG-T{?u)+x;pt?}M3ukZMJK@^On;jmPKd!>UNw>3;U7g?9(U+~~ zoxS;E)XmP*bn$5Cb8)`CE7=F$u7BG8*u8xVHh1d_wb#9+%Y)55XMX;D@9LrG?Y-MO z?fv$}wyMA~-nwPWJN1IjGJG#88Do5|`TK0I+e0@81jNsw;MxQ)BuCG7(C%L0=?ZkhU zl#_>v-qOiu@G*O;yyYiP_qHB;r!U>-?pgM!(mS)l=ET{FJvR;AoK+T$`SZDzQ|Ww5 zeK*gqt(C3w`tjBN#Y%bR>SAZ*ym0Y+GT*w)PCPDNT9;evm#3?9Czr3?O5tjucs+UL zJ{+uG-Ct~t(|LLl5_w=Ax_t|{4a=-d^xOIQIwA#F{2lIyya;3BIaG<_E zKa`#en~(Dw#q6UuUA=#NEVMSCvQN1g=;mqdc(w3!b#`(1)V!>j&syG?d_KwESDvfp z!qrRe>MD5Io0H9#;_chwk9lpc_rq(*A3vTSy2bp|Ug0snwS9e)zu!K+$oKY6&kAej zr>BMM()DSfy-+wWTF2dM=%#qlkspf9gS~voSbBXgofV~aseb)1U)J^|y?nZH@0M%c z+IdCY{VY~aZf<*(kIJ_Csw(Q<>&eB;%j-v{nSE2w8f$N-EA{KQ+DE%x)gI^9s%KZL z57ox;A!1D5jnm@Z`^Dp8{k`?NxA9@m)Jh-MzGHrL-dp+FdT?gd?s{^$K5<*$tZ%Ph z)a%bmxZjwavu7K@to+z0u1FirB{SD;?(eEM&9}nldP|W`CR?W$FF#t%#q2(G^J)Fq z)IV?b@)PZ>nk%=rb5Gapmj!dDGe2Fi|KBF}Y>*Yfvxms>86T&1qVn@Uc{l!N=G{oK zy#JGi8jgRGYW$N_1 zd*mms%35>)rg5GEfg8olq(>4|8y~e!HwS;z9P3 zA!1b;;5HUAs5#Lklq13&oJ!4qjl=}y3z(pM4HJ~FVS@5COi;dr2}&FjMz<{>f|Qd- zY9TSf7uP9%@NK}x&fC}>0P(l70D#%|$1^G*;AjeTbwsM-v zGDr>SA%+m<$YU&y5X09^9v>m(FCm1Sju77O`Nl|WkiLKo($}y-`WiM!U&99JOV}XA zvB8vcx}m3r!^V1uVS_QUejFP{@&e;xgY+eAkYd;{gl#Pf>IoLPy4U!~pi2(Lkkz>; zAcVL{p~MkQ94668M#b$n3z-x0l^EYO)Q?KrI+=M`T))|vUCZAt?W;2<%kFl2r~bD3 z+Q{yQ*`>KzubR(wri`oIz?fX!zS`M5(zaF)A7_uY+r6S02Fj5&4I;>-Bz63E|8+^4 z+FpCkEo`qZwL06K17!ue*|`Z_G#L5HRv zPOqBxJ6-?%$<^+cmz;X@u;c7qbl(;?jmciT9^9)~Z}n+={UdzZUhU-Si~iDdFPz&cRiGOTukgpqNzbo5-I&NN=61E>zIDAf z^IncYCpUAZ)>n>aGQ_jWmWP=RiHec#NH}?D7 zVlgP{XLH`0_OiC_Osg}C?(xzOh2= z<3=^+-?J+7CX4=l=Nceq&X&y^RVuAky_GyHvV62Lx4cw#YvyU4%;D~EdfVGupS%f{ zY#q7f_dE~F;*Jr});(Z%+W1ugdaVxkAbR!ie_B8y@;%2x`->u&S2c*8Y6#O8qDR;^q zVDPQ9xVafDDgM^$25snOV|}gke*BDAlaCnMJ8^Z^4b*b?`4FC>A;@zZ=cra$!=LF{ z*MGs_Ms8S|ac{gAolF0H(XXr=d+?s{npMlSciE!dHT}v(P{JUEcJuR#o0DrdWR+0M&`t0n zFJ8Oa?S8o3Zoge=t4B9mc$F-@+BRSX7nT;?rv03RW_;=*=ozTU;-aRYo3+J_-dyO{ zWLMhPSK-%=R3!~<8inTe+GTgbx!mve`k|RvO#$?5^>5o*^cixI)IM`nCkd+Fxm^zD zW?J*!n{j?06r}Y9zuaywN#)axH9iso5e|;S9p`0V#yGdW{3AqL_8{l)y_ueqS;7OI$n`Zxbll6-5ZmGM|nkd-z;+l4xfXUikr&;l;ovMLJ=kmj;GC#Su z8_jF2ygE~dJ$*ipknCu2WB-1i_4@hGM|0J69EF;D8ts$3yE(T3ZM!fv6Dx5d;yNtoo29fS=<0Xp54F}znxm#+%Q!9 zE%nx{o|Vw#vZ%_Xe{it79IglYJpLBX=5?V#>R%phPrJ2!DTmhUA=;>>wN+ivwyz5- z=IQRyxmI;~gy zF9RBC@*)mU{LmWu7Efd=f+neqMn?eIcwIZ=K)Apl<=w1{vx!!4bLjexy%>p=fNm0t zK^{WziWK^ep~1 z(JNbo%EKbB4gN^v97y_q| z6r;p?2%(YOK$sdhl@|r!O}0UxIdF|x;0fm+28vte+WleZs2@}NSW1XbX%UML(2dpH zm&{;!siOG@Tc4WPjr&q|&Knn3&P8{x9xNWN%*DU0h1>RYi^&}4^O1N)df0oo{B%w{ z_Y`wBlZiY7+{@muF4^xtn@yz0BLvR0{2zXI#KAOl!7f0*WZsfk9n9I;K5 z!@a0IMNJ`urmg+&5Lk)K91`58I4X=}kjyc&Nzgso2dBF=BzQ#PL0mQvhH8J5 z4?LJsX)*lqvT6G}cV-2o_CS%89qlOD?pb#JF(JcHObb}YKhFHh)#46N?a)`|O99!6 zSe!*if=!6n^ZLxkp#5cr&rb`JVbJLPP^Rs=#ZX{%cGK+`%?%C-!%;v6sOcepW zyJ_d__~JVB=S%F120Utwfu56=`Lj-4A6W8-2kQ2GVM&UX#$7a*P0eAL&JqP?*yDukM;MP{-mX zZspDvx32S_tcOc;d+p$E0z}NWY`pOk=VGVy4r{Z~R<^IQHeLt32pe-frHl}-g1)IVBSG8v6(Mvu ze_C4$ef?fp+`M~Q=x=fiZGh13p|FkJ!;ZbcO zpx|=rvsv|5Zr8RMNN39{Z{_D72&5f4x*dOmlGB?F9i9A&TR+)E$6AaXIo8~ho_Y`9 zKJNsJ3r0ma!XoTL2xUx7ep=hBH=otD%5LFxd-XOHnB^>7mgnzP@y4?Bd3S z^PEOgUh3dsDJaic6bRV-_}&{A~l};%B7DK z#KBVYsORY0&O=ZmldU#tPug;R3KNk5}Q#+S^L9S7r0F4lDhdMZu)y=nJ%7J;y4v zUUTy9^%DIImKpJBms6k1;at<6PkbjA4)>I(4SwXJK_4%>wYpRRr1`ivHdjyXw%1y= z+&?*553dbWHLbJe!oXU%%4(@p`0TH8zwAr){cPx!jSuJ2&&zJjnzSTk>9e}MdU967 zizX7AK3u}qjg8{%18g4u$T`2<{_IDP$&OduIO=S#hSPat%edX~j=k$G&NdqCyfN3S z#Rq?{cz)_?H%hl}^`9rRaF|GE;oSb#p}qSUb}55Q7cvLvc3`X>OgR<*b@E9wS6ev` zn561L)D&R{QEQcQ6XD*&`ux=N3qtbsps;e^inHZpY}Vz>)J6C*IdcIsTzm|k-@8Su zw(JM#PB$pH3n*@I^=MIja4s)rvN4p|+T-mWQl%EfjYC3`*}sniuw-{K^qeQ)9k=(8 zb};mpE_JSEB;&O|oZ!+i?ekY#`{CAMnyxgk3OX0^NS2ykZY*7LwhiR8%j>O=?e)zT zG84JAhBTAXM%n$C?e4qU-Ne=I#-lbn)?6w77i(oM_lUds*gFRCBxs5o(AaLG^Kl=C z#=}llCqaU^x1%;zWh;9e&flGOor62MB=ov*a9r|Bb$cmh@qn?-0ntDCc#WtrA<4}j zZ@RE$x$T3H-^JtRyI-;QYkpy&I&B~RXs?G(tucXhs0pM&D1Z!ttH<8TqknL)zCw67 zHc26Hr+>xYNzu6ou_j|Sabft-UL!~ zC3e#KQt$LG+?Kj8u<(sSm|a+sLC(EG57`RrAoU0A$1O`}S6!H!CjzK>X+AUa*YOFn{D#L(dOK%V6IbmLNrBA!b|Eb-Qz`o9lU%uxSNYZSDCL| zrnVu^?w62Yec&|TP8LMbYS@;=*w2N+p&Nkf?H{lO0j-zf=P!01ccyoqEI^u%o=~}a z7(M|CY)@hS1e-cB4{dB8`MHy~bpd%djiqL|Fu!B~cce{&q_JJYHp{iLUA(4tJY)Y# zoU5p-*iyo^rHM$+@5eOpC~uW2;oQNFEv__1*|Wz<^hM3ND6}d>pj3>BSGPW4y$cM~ z_HOHwZ_ntw!QI@b>v?nS?BvwB=v^uO2aMkK_v+OSoMUbshFCMgA4k*nV6Na~bsZ_X zyra_LWcr#jM#Hk3E5)GD++7|~?{enmDailWn{exkGs2n8F-_n}wvKOMxvPR7K%zBa z1NO}o9Wa#_SjgRlPhiLQj`<;tiRpWL_BNd9waTv6b3qgWfm{&Xah3X<1>`gr)ZF*? z(`|pRbk_Cv=jK9WPu(MyC#ROLV~#24&g^Ab*Zau4n!?u}rZKma!);HN5cVxOXA3WI zW)|zRNSyb3U1{%OXFahk*ld7pDPFD~KNou8k7uQhFiqvQ`FjzeHp;GW8lW9dATcd{ zJiu9TJLgwsT9Zcipt}*i)N(8FZmQ2qudpJW1#|Uz@d8U)5XZRC%*TQul<;(>;^xWa?X+S3mXM(sjmLH0%-Fs6cH$egjEz zS<=gQyK}ss!*4C%eYIN6?(nMET$}G5`FHem#4on6ar}lopsm-6&n7@vSYJOkk1iW` zN0rn1lQ#DfkYuh9bL5w+xUtBxm!*N017<(Kt<2i<#nMl+vbXkK-X8pZ`J;)t{v%lj z_50fGTYcfBbZ;yvjb7vYaiigeG|?-EfcFoZP*>eL=bc=vBMF8lZSUMTx@nb~_1_BG z-rNUf$b7Nh{H$yPje3-;zAY{lZL=i+@(BpL!{4I^FBt?&MjPmUrmE$dFwds(P_91_ zqSJoiXxg4>d-{$m2VdA6`fo%@@$dmoISAR(=*X z>vx^r0bsk8*7{lZ`vwV2myH;+?=FxXSzHF3G-stO@UHg{3iZwOqezYczx*xfCqIuBq#>HKZKE6j?!F8utvPr8 z^jUxWy2_(K6E*N{Oa7_}qsm9?zry!*W$Qd=dL#f-3g#R4znN=ux1}6eN?~)c*)5!u zgQIZ)9W}lJs@xCgruOvQoQHeprmxd3fNy=h^7S()!IY8R`qj(Xv~TO%69whX+mmf? z3VhD?KCq3qoAc|l&DMwOHvtdobVjIe=BG6h_4{z9aer5MKxql5@~YkZk(q<5--U+U zk}pPnWvne;7k?{9HKPkW(+A*)mx{NQuS&1!^$tT(vX#ZP#jE40@wss%)vuneWEdAX zd|xX$(17UJdjO31vINHLvr-%AKq)WPNl4FGCnJYQeLmdISWl;Y(AgF&0If>3m-GcJ zAaCRs?tkjyOz{(`d@WsDkbukZkpa%Y@!dKMx6Vq6zrveLwfKAQ!#;T{5VJHHfZz6dn&fNJ?J9z`zy*@FVDsL zdGqY3xm;Pkmli6w z2LD`t7a!)%>drvggMNQ;aMHhgH@C!+dWh%Mn=0_-Y)4t!&qAXbd?}&H*!32#e^pyp z6O^|3n_pQ^&v}c1u&Q4Nho3C0*Zr&R^I-t1I~sF+`9}L$_!g;Hul4gi7S`7E`(5dr zj&&)0F_=C%gL``P;qEWbuas*Sn>4KX#d~#$fmNTLJ~`jf0$33mgjhX07n@ammx?vh zd^Kl2mBl?8R3m+)w2h2eZ7B1A8YgHSgsx2!F5%?M%H!yqikc{ zo1aHmF&cYmmHtWT8DRyKdDrkS?M45pezFI#=0*uhy;6t5agmPoy9H0l2q5;b>O11% zGz;r}|Jsx~bgVm#h3OytciY1XH``pqgX!;TlZCalDt}7nbgXLbEWh}EyhFvBY2L!X zbO15Av|IaC23CD~Rx`C#2G+B#*!(&=^04AOY!~tR%<^2j_k2vny1s9oFvmJqObhn9 zHZI?qKIX~f0Gh%4T44YYFN_Sg)%|c-*7y!$ZEx&c*;vnt*}zy0O%TpKtjOmjhI?KW z+aLP|9qUHs*KD8Nn^dg3@4XfiD?qa){CpSa!&NWx;$i#emyUHyxo$U`t7$6Mqm7ph z4=Y?7KjOn}{pf~;wJx5B*A^XXyEuLH&|O%hVTHox zETzl+U!^8O#Dx*8yUW?1>qch{U|sG9HBb5LtYBk(vlaqaXCE&zj|eMFzpI`}8-7`F zAU1X`)*;s7s9fABI+~|849;+)@hV`ftJy}u!wMyIX`nsNKjdu*rOcFTTYkCN%P*Td z7Z7V{6sta)Sw3sE0M^ZP0Bc&V?mi=|Xl=^-LSVSBU(1a*jCJ+6-S(?@FwL*eUr67H zt((0X`BWZ$q~-GGUErywq~+hawDR!rP5)fY0p?lJdaS2?KoQ$7jHlS-Xe{kE@CzfF z`g~bzuPHKmIx6qaSH*s=5!(-2I(iB-$wptOiQDN@ll6H++`Zp8U_I@Mhqbq1 z;3;9*TolB^lj$_$^9w;dTHDUDo@(N8`bVHYT}s0Qg8pyXuwNLJS}pP9IK7OY`Z*3h z-4surimR-rZSi!*s<@Lj`ofNQsyAw^&-cXhY2%9Z^i{m-oG%2P5|egmh*z(V1;*!> zmUw-Q6ysDB!9?EtjM)sVugOigNGH4#)r7a#%k{D zci32=k;0r|7|jQEN|!a<{gI`eE*EV4=$B3IA{{|R9n z5V5ox0}&1cG|t<%_!1xEie6F&lf#M5k98v-Y3D_7|QY>=8J^*&73(!GYjB@!+!p7>vmiAR6z5hB-}I>NoQH$tpE zM+WKsIUs!`${GUudpl|kyT8bdvb&F6ra-(Dla{;E{6t!}5(4t72dS`8-=gTjJgs%}P zbXNHGVQ|L}z@wC6{uOD!fvgiS+)n$XP;L&1$k` z1lg06<8=1Ko)KkmtI}4RV{mwF$np6GSDX$X9RAbW6XsUFb4%a!XZ=3SfZxwA*DR%Ilv8wQo-F+PW9wiVkLccupfhtt9s887OAo`YP_Dy`O2^WlE z?(Wm9GjE0MN%Jp@`ZKaDZ)QJS>4i%npIQUs>I_(#ifT>zO-XNs*8?IJv!`EEaWC@2 zx^z1;-{LT;2$MsiZPCK{veHrWwS1Qi?-O#^LAT`UxwT2}mTTs*Y(0OUro*(Sw}KqX z1Wf7MOsKUn9pw>_%NSwn#aKfm({8)RlYu7oUVA4rPeYBXaU75E&P2(B$ey4ryiQk6JS|KCfO>)nI^aK+f9N`ExH1S) ze>CS2ZH+o^6bg?cGIF!WV7I(DIOx&q29O804So#1@Q2^Bxf*|Ld z7}+gfpN7-Cq9BmYGs1ftlMw}25xj2ji-PbG4)gQ05yH{Lp7<#1AxOFEm!}B)#JifU z52c0bYjXl496ZZ})yWqiWw`FZBU-=wVN!%0gyf&EP-dsi7lMM~(4+Geli7Xq<>z$4 zQENVC|Jd|1JHw5Oj72Fk$Kx$IH1S3AIm6f_xtI6T zenm(O$r}yNkY?2}MFB}^TO%593{e0kCZPFXpPWQxhVo;Wxp0E~nlC~xAvBJqOHqFF z0QK}dv8fAw)j(VJBYM;X_p!qdox?GAH^_0V?|qQFQ9K$jB)kQoxl!71zTt?{0%+k} z$S0v8^GHd<_;BTN*#H#APGK~lg;?F8@3134`H75+v}_UqDsptkPJjv5g$SCEK&`ry z=2*nJ+^E~xIZC`eK?oZFE#R4rt$tv~e7i=RJB7^Ie3+AYcCwr(N5GG#NjKoyXt{uN z18rpv^KKe#tVEa>o#VvP4Xq6@IAhuOVbTqNqDVJ8$s+C^6o3{>x-kqW7|>P{O98mA zzc2|YFq%-ornE>vJmMrA3eJaKs3>E^RPZv?cy|Gi+Ih};{f62~+@Jw}X{Rp6iTK_cV z$co%TXk%Lc0~ex4Qrq$7ehBL5RsbYrFJmoQ8~+YSt-MF(bHASYzL77I$jtfpfweuW|V8>!If6V5w zCPX+-yxCX~loqx&v9^C8v0+{SJ_0MHmXAzSPE^hp zUdxvvOBa)r^M%*)`3=shg26h-KeOt zJO2C1LuA zgIe@DBB7h%6_v39Z7^&phXsgz5fw(D%^z!oMsUF&QCWu*(+H%DB&d;Nt2(kgj;kKc z97fp~acT;$e`6KAgwz!J3^H2%4b12wVqEMRxgN6!Cun9zW!|rcW!~=|xDn}yHB8wx zg;AOJ{56Htr1TV7ksqU`Fc9^Oq6nO1!G;!5;ltZ8>`9-nx8P$BGx`{VIMCR8ALC3~ zMEy_9NmG(Q^T&(vVVS-pDqMmUQNF`v=C*>NVoK4H$M$@sdm(az0RXK{ls}00T#A&{ zPc521NXxn9@CS#h5ydB-UF`uN_oNYwx%e=A09FPf;H zjg=5bjal|b<=cQ1h@27bKm;?Y9#yb6qVE{?3>=ag7}98#s^0+%Dg(stSwVq}&t7__`G@ zdJ}Y`{Bh&uVK90F%c7D*ZwN~J*i0O~QD(~78Jef(6E>V)#b*5I4LdIIv6(n}0|uvN zLsCZg>)|+~H>t35pin(r(&!B=hEs?jG1uU7 zyi|(fX>>x};!K8!Dr1Am5dK)_m?eJE5*PcP5hZ>+6|JEhj0-VC;&eP|C&UD?!lnU& zW^r;L9S{i2`QU~uXk$|ZnK%}`buo5jUcg&(it{F2nHQdRJ+aEXNJ^VbWgdLmaraax@msh0qnJP9cP=KPRj(~D{v zkO`N^ldv%ml#x8-F*b`4Ais+p*&S-k1_4TZ1OS3!Gb5=ziCvypDb~ybj;WdVTq)MT zN?%zB@?+z&7Cwh|?jQ$0o+Uc$){S*O&PSc`!k-E~{}bdO4w=SGg`WT6E^rMo)oZkD ziP+?JGNGf65um8EaClr?1Slpy($(6@MELQ9sT|3e4X5Fcpxwnf7&0G-*#uH1zz9bY zmS9_Kx0^)cuY)>Zp3lrXho=)Ql0BVhk?iS&iexW;CQu|RWr~w2k_CV!QzQ!kB`=bN zP$p6&3(M%D54sNK4l>Kg0HcH)1co9A%M%2PcnxB%D2IKh!Ox*nwx*u_4v&8^^La z3m%~yy)%(wmde9&I*vV{u#<O57a8%LZuRZu`EnVE| zYGSGLX4uMU`Rk4Bv=g85U!jL9!bsoKS9Z5o63LnN6~GPVXTHcQX8mP%*}Iv8H)gS) zn!5}uySpwjv7(#E8CdmcRGXDz7yDg@mEEN^Hdc5Oxr&AydR?pRoFL$I!kWxv%K ztZjVSoMvK8Ury`CtQjtZ7W-wI8*_oBg?w8?_|Y$?DDd}z>%HCSULb>*^=f8!In4pG z|J!{dPaB7$_DobSp5`zy_`Hw}=-9BBb98?1sBybD_Yus`55{4~DlSUlJH5V{aVmO% zg?*>j8uct{c19PwxG3_9n$i6;YIHE!^?Lq3{RR{KCw09ZZTWb^c6;-gB*mFTfcT;w zecOsJcQE8`x8SvU9AUc{H|sG+n2Np&FY2Ndo)M;Kr@@U~70Y$U3h%^6Tt5+mZ^Xy8 zddW3aWSo?JE1%~SPOPwx=6%GC*>Dv0(YyqD*e+3FAFZm7X+w`+*hh;Z+5zH87WUDq z`Viktwy=*zKJMPym#tW9;Hiy09;#Vr7zreY|IMB`oYC^8MYaKKk}*qQX9!a>5a)i5B+J`b2JhPpGhu zR@E2C=$b@fA8id?zL{KM9}SeVSvYt7zR z8N0BLTqS2`CQRRqS=h($J()8yW?>&O9j=X;Vi%N+Ti8b%Qi?b^iDj?CZYA^)alSx# zFj790tw-7&VTEc=Ctf#@k4U;v<5yor%8h`@F^OCJqD+nn-Qu@Gxx$ZXa_~)62S zsp23c`?9>qQhh!;fF#h1y0dqIZ~30t>X)A2A9m$l)Lp&QK8>b~z;5v{HdmhP8lN_y0c|UPA(N=hVhnH{}S<^?$aEzPX z4HrJ~EkRq%OyiLRn8<7U*qI^R<#Fi7zK8@mz9cm`I|d&?Gw&Po?VsebqwX{;3iN3{ zpSKok!w5$8x$DcqQsO~Z)$mw)oK!315lkl43Y|@moYKIY^JH1^6k<+bCj~+AnX)tz z-~<#$2eK2#AfT~WaRdtRbNzf1yV9P7#y%vG=koesweM>$J5ufY+Kb+e;x9mZ?Qz|Y zOtJeYN#p?90+(PTlo@k24?odta;y#MY=&)40V{4aW%~pl=NrE7$rUWDjpU<@Sr~IX z(aRl=CwIByaiD}^Tq$t!70bA1xKZdXx+FG+2PHgxG!CE%9Akh1jd#Ph-@k!?Lb8$K zrNsh9p_EW0*(?S&*(g7PHcAP_IKlfd?){@?rjS?vjmeJ$s0rTn9d+*?r>UOw-ap4w zXI}jmv-F2??_XROcg%bLC~D1r^4jl^u?UDg+b7JctuAV3^i?K{@Jf858nuV4No zt}lZm0Ia@zNevQ+pQt(8K?SwBx{H37D1n76Rf2g&Dw!wWXItejT#1{<=KGI zhwx*}LaGkht75A)dM#C0wcH&$+GkT5w}TI$J#*wF2EBU*dje>09PG(aNYz0u{)Bwb zZa>`5AMrgIN~)X{LNX>JEyiH@0dy<0mMX5rc!xJ`gJ?kWB#xhhg>nzXN$~z%+%f8I zYXUo!&l3u%z?t_Um1#*%)f%0&iCuw}1=kJA0SYlX1 zb%uh{wr1Srn+r^u{mWM)*B?l;eGe-lX#dr88M$oHEAy5zbIi)T{_l;zt_Or@mgJ}4 zO?-LpYC6iTgKo9jc~}+Vs}%5W0)>c=JBLiHbJAw_o`v;+StAF~49uPCAu(Kkjoe`R zxyP!^Gg~V^Gav3)1C@C>q8>2i-X2Fx^0LgtDmE=~hk;d}o;moCuW48T8aUjL^2bbb zpt7;97j90NSm%3Lv9k#A{xmV+PtvKJ#) zJAe4#dGGr77_aKnKi7@UZX`(U*`_{Qal#Uv%Syl;C6X+%A0rW%=5L>^vs}%` zk)3Hf?35j)i~2;aU8qB6tQpw!ipoX`xs}gn4pXmn)OVx7bJY#O0esb{ehSozXm8zXMJ=+!J4fHVIFo5q{1t zZ{+r@Fia8)^G{mi@+3UPhIbRZF_~?mv*Ppw;%SXZ96|}}5gj&8u?{xlD@o)Hg4!Wf zW=Uei5u|+ei2QYHQC>|WGzW|?p5{+Qkw!O%?&P2FQF9BM+vWs4={qIEvltvz`nQ>F zLT-(w^Ta%;3(Qx@!T(M$Cxr|D`64iH=6CR3onTCoVWgz+u{n`-cEF@#=m4&QHey5a zVLjZWi&4;K{sz=ZDoLf+UBpogUU&_QPIjFg`zQtuQ{87kjYLV~)+G{a7}%Aho-e>% zTN-)m&&V1wvxeTxeq^Z-H%0VEY=K13m?g9+_rA3_&|(vQ*rY{P6UCaeNu7@ZpfPtq z$C|W|%D9m#X8&gKm?eq4W+PdDGe$|GpFP?2h$+hPA2CW2{heUfBc=lPNyjWn4Sdl=ka z^$#glb0ZDuEyPoI@OB2 z?a)nwOM2m!^}!ysAsZ#x`GfEo9~ z!|=hSLhtvCap7V3ZHFPT&UhCdh7Spw>Jzx|Ffti-G8Z0(UkYZ+wB#2ahO@D2ss~G; z;$CYJn&9>%<< zaNG+I{ZN9p9gYTw$Gq^6ovvo1naqWU;rlVROiO;@VQ6&>&Q+gOwHo`jL((Hibm3w6 zJ`b`5CUN0mc#1bOavS%;Lsl^wxfPMHOYP;j@Gv}wHszi#;PT^m7aoS6JmNVX z2`@Z!3%{rp+bObWiN6GD^uj|9nKp?F55sReOm3>%7amTa9GmCDLmn4*0v8@SQ0N}_ zh+2z#;bG(zg(1Rbj0+FL3Oz1=Y~M5ag@;rkb8kCLCeu!NjURH~Gtq^I;l>Y<{gCX! zLvO-qrNMEdgUkyLSfuiYto4D^_A$csPDW7v;jkaZB}6c7s7=GKKZE_^P@G!hcm#(D8xbQIiw!{KmW}v$TrLG8!k{fg-iPP=`->=XDG5PU-+_=9SfIU{Cjfl=wE0hR5>S1DTZeU< z`K;Mf3P1&tle)cHzrS+uvQ`={ZB``n^iZB($<)y+u&S$-Kia$Pr)Mwob7>egO3z=n zp3}neYbC$9@?+(f77xzyM=x(*Vx#d|5*x3@ve?|(uRul6{Ca7N>-$zwY;Wvz#rDVJ zleqiddlnD36-zwYcsUV|2hWe0Pp+4L#na+RRy;2p=ftbK%&vHS-FXzd2g*>q zQ9ASD&31Ob-|sg{{lVqeV*gkAJOOC>zg;ssm^bF2op3OO)u!{smxtDFYsY$AJGnZ4 zN-xaKcJ^k>#rNv$-r@S@-1FOPCS#R$veWPPvm2^4w|n>6sV^-!MTzO(!ne9}gO&xA zzyUPrO7rMwR-EZQfabj~syMON29>-H2C%@joi0|-RN{h6`-a7GH85Xjvf!kx@0V3D zBfmf5&C+$Q-`^?bz2T0GRn2wsOUnG3*sN|<#pdJvrntVl^D&sd*6(03ZQ4e-^3%vK zn>!a`so71ZaE0W)*f=q|=>9{owA(8Lj4(eN`K~g&$$ofIT4HdXHu7u%T>m?l&Mobz z>0)XA1eO8U(=&CmlrH4f)?nLaI=`RUfwjOp@WE=jSWzC*h3!r&z3?*)#(Mi>+N6!| z7nSo+h|Wk0HE9u`slD8mBsjG0+G0E1M+?+!$lE6Pb@(k6o}N~~uah6-{)?rzjDS8@ zW~`5)C}xEc`X`i-S^L*(ZvlMK%3*|$^FMjaH?6$gQqC6PKa2K1+o}x%$b(_y{_V#S zOWKB5`S@xNDqE+gS7PgKqtoo>F1DnC_<7^>b88`AFUQ zM08K1)Y98B{3l)(>F#>=)&a1eN3TGccK0p8soF2Pv@`=mOcvS#`vL}Zuh$@;eGw(3 zN%yT?^iLb^{rQDtuiRNPU)nx=Q^3=s0(vUv^zs8SX-5J^Bj#pIQGP$1Z4|8B_1dC% zGDC_zxBL`2S)-<}E0>Q-9z00UlK?3aU6e}GgQfiOVqaf5R(C4K)kD^{1*&rH{4j0P z(%FUV($bAGv$S-BXi3ifKD;?YKPBizuD6y2*e2jvs-JI2h(P7~{jP?7K_y*(KbGz0 z--~Oc$K*!twrl^ApEL+V!!Ww%YY^nQfG*pTyYImGN8fgT4{YE!xsFS7=Q8RJK!ba9 zv-t4pK2!6JlTHhc=8hQ6&1t#@`Fv4XJsKWV--V}z%HI8ByS}xNKJ?s3(Yc4T=7ve; zNIT7onN*e8-;JJw^{RvhqRh$1Di#29ix-;xtA6|9zNgsloqSXR-2rGSyS1aMX8k>L zUpY9soNwF}*G_?*&|X3(_+$T~H2l`F0E(8W&fTjgKR%Bj;5s8+yrR$6e(UGWUZ+0& z@Y|?etsivCSG!-?#%XnXvzmMF?*Toox2g?MI9fb!f3B>*pL8xOEj@jQM4ehnr6#0_(rU~)hDBTwYO~nve03!I{5EWwR*ENy@OiHb6@px zeY#iJ094gGSbIE@=D<_=uxU+eD@|+W;GuT4Io(?PeF6r*p!QFI<-D-&C#|e=@Ih$? z9&#Yy;Qic(V_e<<)vV9Vtd$!-mu00^?^cd#*O!gf-ix&MQz=#I8y_zNz+_U@HX+pd zi(}pmcI-nI2!q7Nn(}U7wh@nFogOsuFXzvdqm$GAV$bToRStjC&&E&dpxwB=ofjJC z51D6VHUTu|FTNms_Uoz!4UCfSF#7N@kD@-6>w>U!+UyRmq_xG(w}bEj<2bZ8W-F6l z`JGD#97RY=^HAvkm8)%j*ym9+yLpe}m8z+j`gFN{04BWmQhm#2_dV1AP2_X~)T@gt zb7P`j+?uJwT=n|;`tn=lsIL6Po_JZVo=F=Vvpz{m8_o-At2*!ZTj!hz6tGE_^I~cF z%%-`>k;0xb2b+*-35=6#{j zzb;>sid(x7p>bOpd_SW>`l12J*gin`*eM)k`$Q7wyH=~d&3|6j1!I1I76Fm~C)+2H zb8yn4$DCNsVK*vj%*+7;a{Hb^428|R^q>G>Xm+)H=zPySL-E+MOL}_-IZ62RcW}im3{Rw@# zA`!h12ldbD?%o>khc)e{a{fOR-ZH*WOZXPSA1l9K{9#`5{}xLD=;?!wyQ<#V?#9NhG4kISETyp|2b z8n7&48&xRQnZTsvl6>5|#?*2lu>zXIE)VBOjzha9zKMr1 zqcig8GJmEV`m9htkf9O*isd}JhV;$5+DsR^51X(>Oc#E>Pw_TQem}DaHOg)}ck^hb z3n$kN_@08#cl}fPW?WGKWG^<=C-2j8>YJpf8@D%&x3$lo(hb0W0FTg%p>4kk|E|7? ztLm^V5H;q|0Rv%kGE)x^+&I|}6Aauy5x*Bubq=K^Q1tut$GPumXk6ccNx1LQhVE?S z(~PS`mX)8$APL0bvoR^%&V(F4Y{rKgSvE2FY)mGz)8Hm4Si)pl5|ff+z$B&d@Y$H7 zhC79E_-xFfla$88XPiuixA43m0%HrWRux3KaaujwsT97&waWeePP4w6*ETDKukw8T zyg9t8wDg;uwa26H#YX??Zg+82Ix*3HUww6;{JJ08d=!3w>HU$25snMI?o?)avUcMpcylKnJ{uY z_Ljk@tvyCOZLCn9iyG!~{MpqfUEByygg}cIVszT0c4gcFUPGOEL>KOj$g02p>{TSOs zNmd^%eab2J`(vvgXQwd1>Z7H{!sbt|K3Z@TApWDP&u83*u0A&W{eia8965x8Cjr=+hDihEA`Y7)3mTc%dYxO-J6zFUeg zfSd;aYR|dCOCtayUz=HNcm}GRwayf845%%WSy^3ooY=1ga{O@NGtK6#2LR>SWVBKM zMyIn^abdhguoK@SRz5MKP`-fwi}(MEppes>#Y(q{q}AK`KhJ8K9r@xt2a!Yy$HlFQw>?CQl8 z=&HI79!P%r&VM><^PBc^`_t;+V$NaDTn9dwc6QkFi|@xfBte}OKJ7`X=C<e+=i581n!gG&Z6&Mu{I0@UPCS}2TM zIE-ij3r^v^ zy&#n^C@pMUr7;9hkkU$2LWx1{B`TF5hn<`EOXE@rL1`s1=|I@*-Dj8-#ohq=B})_# zL?taq9$=<(4+1i2C+9q%GCAiwCV~PADv}gXL{wB1MFdokWJD1K0Rt+60WhN|f&p&L zE<3~7`Fq~;;oSS&4`&~7cWb+=r$X1Ss=B+n^_=DJD^d$IQx%+%`ywA2?t39z?DEL= zRU@})cJ*kzYaioWIH=Ep+^Q4S_rH94&gjJac9SmKG#J0uU=(O}V5OdgR2i`}kewHYTnQesj&*LqF^v_VmJ8*Kay@u+`B}=y!{9_<_{2*MFR4 zz3jk?W4RwM7H!$8-7;y-Da)cQYiGCqb@JR7AN@9Ta>@ob{h~^1pYZO=Lo18N=kz<- zdBut8o!0EVRxjwiX0`Bq+imZ(I{D1Tjf2k}Ke%#s=&oa1*Npr5=h6=&C5smOKKu45 z&d-CEkGV#o`MTY+Qx^Ao^=-$_8|&YDc9Zpi4(jQJ#BVp=b*kM13r7C5bbaZU>oyL2 z`o4XWQbWG|ZR5*-{zBbR+G_uGz{>AdOq$&H?`pJlKPo3W~Roe6Pq1L5#Q%}^j{`i{iJH?0PAC0_e+L-=b zto8|iyteVxRIFha-~43Xz`LiO==bp(rA?gflRIzy?o;;<_V|yM&-Vlu%C^{p=T=Q< zU072$>+2VrU*CxY`NkrlaJoxiM49idEm+)>d>3NuOD>ZtJ`D5@Hw%g zuMHe~0+A(B04`Heviz z?$0Og?R0+Wss5ZXFWe`)v)}cLy1zFwX5ITly9er?U;5@A@q4qiok9zr@}He`*^c#F z9(m~24TrzG=lPkBpw7VEj*q{c!)y12qKz(j9RsRh4u{_w`w zEgyAhx8%zEZaeIw_l^4cL96DLsh_Q_dv&h8*RbhY<@G}!|6ys^@IklytM#A$!UV9( z*PYnBr{9{~;+yz?e%b5pXL{>j>-NFkhmKo24Zm6S!qy4yjsu_mxiDqh$_F)V<#oF@ zDYqOs^mm(w;`}_Kzp7{ORSO56%@1LYUrCIZeQRZ&pzI|6)e){az3BMlckO;=R+_!S> zCeOOpI~EOha^2nArJnjt8vc7Nb@RHLCg0lYJQ-~g&OKaY> z@-O{r=bJ+>-+pShf0^;_&m@Y+1D9>veGmP@rYRr9yw534bUnOx>E)eI-`m^J-mzru zjOCA=v|YAx>4!Z(<#zmWnEA||%Z7H_bc7sK-#6*#Ay-|pd~)&WIXy00HG0pfW!+b7 zxhyCdf(`0Ny4T!v{bj9>O_|qq!RNP4>UjN47IDwZS`QVSw3l~!?}NKG3|?@5ck#ES zMZNER?Y3#%T0hkLXvcT1DqeB<{pw#-^}nOhh`;rimxP(#1G|{coIU*5Lmj_bG0yYT zmLcCUQQnoA%Yv&_FZTcPwXFq>WakDlH9DwkxcA4u zQr|4@a@mH!mf6pLtGW5}k0*S8yv;T9|33Hrw-5Jusyj2b^NfKzp1A!XCi}H^Av2P6&kK+AyUn+j*z`*=~XWlAKy7A4^;~!YK zux_(R^9s7#nAfqJVBx96mJMI-nVI@|>{9Wzx~bYh=kDIP{E61bru#n`H1CQJZdrQG z#@zd(o(rmn&-!h`JuAoB+rHj@+9_wZYZvaAB;7fy4SJaKDDT~EeD>-t)6%@+g6Ww* z`_;Yj@nr)JEsfNln4_7Tx%-;=emeN`FwxW7A1^w2Z4cfdwSIW|(bvB|we|UDZ8KWm z?Q8qS*LmFe{D)l?oc#MQT0K8Kqy4FE$0OtGcF?iwuGlJAb>QyBTaORAYunMlnGV04 zzGm3M#~$EVEvL7=zi(lWH?F?%=HWe*6aG9|H)rh+&;PmO(Fd>F+$+=Yt2J`{#+!zJ zxgyJd;?c`iZkT#*?`K-PRpx`vhC8Iemk*Rq_frqO`oT*yhx#toxw?FH>OOk;ocCYs z@cuw+U2#$F^m6$de;(hzzn{V}U`mPi{;A>b&E7Wt^)2GomyG;o%VF&Sk2v$i(m~T6 z(0ugAun7L+;r4Is`r^m*iILx6w}156rT5gW;(QbTBz*g^FWvlbk9&82|N4wh$_>}- zeLVI^>kh;3+VXY(-w#dKtk68Q)GHo2{IXTzz$>d3E$paYwQ=cd4gSKMo)2I6{zHGQ z>k(qS_zpv2_;dTN9H7m7(fST%+^eERb$dK?`Fr81XzRWY9v!4uq+sv3>)n&xAG^9; z&v<6m>=F8v;hyL(TOQctP;T3I<+j~-oE~0|1pV;gtmpQ9<12mSfA5(C-TV6neX{*% z{nVp}PW0$7XG@18?Q>JUUwcWvwrwx-d~x+dkNx)L)87o379RNcmUEB3GV_sq+iQBV z&mR*Ho<2k*)GxK)w1(aBS^xW%|Mhs^AJ5JB^MR9z{EN#!j@~qJ{xwTi7B46JpPse- z^Ao+-Ha@1fDSm$ccaNXDclF8hl9RWd`EcFpAs;`mfa>~}Xy)1P%sJQdLiSvn^BX(f zvUSF$^D7p-_T&#w&gCS(9n@~XfJe{WD^d6G?4Q5dV_x4@x$w6qFTLS;*Bvt+`|j;U zqwl=4-qGobO{@COSQ9?B$g_0GsTKPzZMG8Gt~(67j?s}1Jc8Ee2M@ac&J!t=O6mVG-qQ@3@{i+iGMn;!$8`COJM zD)I79p1kVj>suX=zL;9Ia@T8rJYoFg$?dyOK08+QTeoLKr_Y=^b^4LtdA}Xlesf{^ zH(eGCJGOVTZPh)W9sT@U`uF@oNqqxOV%I4i6X)*8QTH zFBXyNGol5V>t>(2`u;V8LYII2<`d&vw_VwJ&8ibGKE8g9a{dQHMf8q2U6%%8e=c)= zZrZm9+xXI?EA|ZOPIe!`3qEx7x_j4cessphR!?<*Khp8ez@a_M23@gc*zM$HlTNgm z+T}j^n|EGy-`zvamv<56=LioxzT?EkBPZmFC972j7qr!z*Y16G?&cw!8O~MAKmep{)>7|>*i^9XOGW%4pq*6VzhaIYRJfyH!oQA zT>lg6e50nW=(qOk#HxS8`3|!f!`}C=bQC}L{bocbw z`@+b;e9a{fwjD5HrI~s4xo;QLf4AhJ7p9$fzHQevU#;V|@-OXVlFfOg)m+6g@2s02 zZu7*BS^MOp?iuj#*3>ZD;LlDjWH)@|dGNs0FMB-E>BY_}r*18_On$}a&FeB_H`mSl zux{q=!iia<-a7Z(pMR;Z&^tkk$P9YhO&+XS!0E{H(2S@R?Qz+m)2o-g0cj=>dx#UG`+_!Yoau&0XSl z+o_NE^#e02-&vjMxXbz1s%JXwjSO{+?tgO7nRXu>3Ll&{18=oa72FZO*W2UWms)q7 z`dQoCT0cEZdGKd^#NbKUd-f@obowkmb$)PE_u=DC;?u^o-%d>WBgdKdP4@Qava_{i-qX{sAF;lN z?RR~M_do;ny_r{MBp;pn)X2KQg1u2=2 zVpaR~eGJ1_j_-N}ZS1vR$9R>a*R-cewdm<*m#!Z+Ql_5E`@Z!Y5vDpL+N9DyH{#m* zF$P~(-HO(kwkxOZ9jM;;;h+6Ctl4|>qE^`_>e`&^^T#DuYcfxwWIO`}2J0!03Z(?Y3;6 zuD7q*(&~6!pM9<0n_jqV`W~~XPS*oB>K9Cp*9*_Kf9KXVXNKY3x9x~_`0JoRe|4L6 z4-ON3xVvtRq17$d{B~;a{FBS-W_DT9>50P&#zf9e8`PT9X}opFk%P9L!(&hE7<#32 z%$+-KZq;zV;%`$2PUJ=^h;zWZm)Z9BTlzP+s{cHFC&rhb@z z>7u7PzSj1yhnHV&GOlhX?n)1x_P%b!we!EdeP7qP^#}CR-~QmW^FLj3;85EQv-+=J zG;jO!JzRHiHqEvy5-*I+-Hms*t?3l(Yb)`_u9|e?8#CUW*TpCLFr)fN_sOm`gIx{6 z4_+KS{cP(GR(0I|L3ZbXArGG38fiWKNx|jQ&(UWd`*_<`NBYTU^t*K4sNFrsXudkg z?%KEP&T)=l*OSXvb@*&Vz&d!Yb!edD?VE;-417JqxAM*2N1koh#;GBC9lP_H5wV-P zv~iNBUNkO=3dsXeHj9GlSaOm>4rhPXea=81by)!5Mx%}RHO}qZ= zba;Nk-LLN1p5NcZo7ZVi=lUV9P8|E~wCU}X`YiX(w`YdPIibJYO&5-|Cng|ZLLgi4ddKeXKp1N|Gn{wYpI7fQdeEu^`|i#rjpA0wjK%Y+AVu} z*B^oJ7rB3!bI+@y#~*oRpRSi@0f-2Z@Q!;!Xy zzo#4OTn!8Ki-&&tO`e}JM+(rSFwhtvPqFW%kdWlfK} z&O}#E9`hph-HppBnTy($EIE1>p1J>leW|y4>OL4a-FM|jLzfNT-|H>U3y10tWp=%+ z?AEpKuLED#P4~BXA=m2lx(+*ZrseHl8yVE^x&vBPrm%|?_DTT2sl(ru&djuPCLNtHRk-lW-ItHQW;16* z$5BhNqi<;Q*PyvG2Db|Kn%d6teAh?SvnM3GVd~3!@cU;9;@hsWKDuziVriE*b`J>5 zkazJhLyok6qW#Vx)BIgN*u7&eF1}-G&pSTuuyTaNoGuQD6b}D&{_A@^;|I553b%=- z^@-G3*GH^_Ufggor{?oH|ao12=mUocyeIr9D;_=Drz>*ustHd{K??2+9W8ph6k*T2x)W6|VEkGl^D_PcwuJKpQ$?Dd&H%DFG`G<28mpRQl6 zc=U4p=!HFQ-k<6J_*31-55}))HRV$KufNJZI&;Z0=XMWy>(@CQhpeDiNvD>!t?F=H z%J+QNAx}tOK6b0}9=3nxe(}sJmv>n(_ep8Ly;DkU|1y|06Ix&K_6Bn7gjF4vJY6Ua ziDVA{dft1lXY{I_2cDWbS~;`b^=k%quunfY&$;2r`R66)5}S{ooW1IjRc#M1tGkt5 zSv(^$Iw!7wyDqM8-ukI(Gd9wS@Pp|vA*!kpr@&#DOcb@xT(bRYE7#5td=1If+ zzE|w&HlOZ)`n9F6xqhCr|Jvo!Bbr&G34G#-kl5Mdfi}yI^qBkDl~a$c_;L3&`rb?4 z7F_xA#4&@FqjL*JPyJSYH>cOc@fV7+hjZ-<&$P9R7qwf|w$G6ZXVc$YheOk${{YN; zQvB$`DJtXcR(sr`JJzkAP5jXI#nB%fJ;vK8evthAmD$JloWEg!{I#psnf7=8dg?pY zjnf|F96ERVn_b@V^)1(}&%f;Pso>ye{I=hXmCeHLcu>@b>~PPH^i`wfUwpzhjj;`T z_4${caje_C$E-d22zt!;?3VQXizKRR29Dh^c-Hjq9&7#bjzDhOhHusle|KIU)L#p%bE)jj*QZ_{n} zyf=B;cGZG;@-7`8|DwbF;tsQ(dGMfy*JkU%R;Wl<@aB$KcjqEOuiW{5-Sp<2tp`NA z3|;jc``WDUu}dZnp7+P7?w>FCarfTV=iOT)o$&F;rzaZ7V<%KQ*LPtgoJ^ry=jMC5 ztXld}&u+Xq?Vp|^@7VC{{d?$g*=+A~Aa%`&lXC`tdDYUd1Y`P;QeEd0eQq)g(CU;2 zr*7m}Rmy|YrmdTD`(>yw&Yk~7h7Zu|eN%X=A6s*BUf=XZrZTFtf|xw+qTqJ32nyR8wvs*FVs0+-TR^Gp-oE>~(s5zg^=SF2BEZ z-ID#gdQt-V?7Y$X*>7I6`Rs7#Bd2`lxwD6S(f#%J1I3ef2B+V#WajdLPd7DYpMTl? zg_+%EpEw!W%33a8D4DXi-{@7go)dp~XttzR=M}qG{`}RMQHejr>!(;Jpz>>K`CI#X z9aeRp+M~_tg;(udKN2;ukEn0|>!;V(fB)l4Zw=?2aVuO85L(-FqfsG_R!)D@_{ZEQ zKYDKT>%H3T`}Lt@(bUbp|L+bAy>C~ zf5v;O27UR+vT29bkBEOaBPI_Wi54b3=nXv*pFFJ_#Xq^J6X(gl{HljvXvNfBu4^5C z{tekP?fIH7`ir zliv5)T*(z3w(09<_dC=6o>w-IgIK!p6+bLOU z!mjB(U+Ast_DZjPk4-)8+_>c7kEE(wyGuWs@$pP=+i?@UL$)kj?x^R!ygs9vaDC?o zVoM7zbftJNfAh@GXQ%!666bQktKT2~!T8e?xBmG{H}19@uln?yHU91CQzX$!|5n~+{CKWBmhr|i^%#Mdx8Q1=QSXR^ zH|7pTZb$@U;gMWZG2!KEMN+Og?eu%mhD3S8i4&XS!bq$pHuN6F|136~DertyY&gTz z#76Sy|136=SKj%e*hsAWVgEIy6!{;flp zBmcA5Sgf%?tEIr=wWYwP{=-t>Q!Pq?Pt}$J#zQZI|BsT2wro)j7xrzcmX@G5&lP*q zL4T}LAusH5p`=8pN@wCJdCV6_HT1%7BLt|1d+X(qNEX%k^55$*dMs}w*O+x>%5_wb z%H-pz@I>_0TsNC?;GYlMl<@!dQJYHfpO4y9c>nFAHr2#`K5A11{BIw%>1_Jvqc)vH z|LvnTwFv)w)TV~uzkSrER_&jU+SHi+w~yM?GXL{Yn;PW*`cZlOa#PfJSd7PSYzeDp zD~>lS(|mpg<1w)_;~gO$hx0I;$H#d%k7OtwhldU4@rIB5H)pZPNY_spVGSa|^64of zM{?D1&qZ;e*a!@@4?NN_lB-L_Jz2Lmb)hlPi5u!8L08I|D%NAXu{>;K_1rI{yuPlT zc=Z^L^U?R_FS4GdD1xdl|LwvThT66ASADD!*2vJmgkd}!Ly7*Eu<|d>`!>hN^7&MA zST#N#uX*3*?NEF)5}_u(=CJ1N&;}T4#v8Z0I1IJeHDL(0nlMyyHDRb^Yr>ExHDS%^ zxcIoJWv|(;W!lgN7?2JeNJp8imhs_0I&dHzIFJq;NCyt20|(MkuGBT31xN=0q=Nv` zQSMT;jIZ2#stE(qfof>&c0f7^ARPpd4gyFA38bUknQQs^${nVfFd!WykPZ?^2MMHu z1kzFN%GMkgNJqJw({j6VkESLJNCyR^g96e)0qLlm=NFwH<&IO$ae;JDKssn39W;;* z8b}8Xq=N?1K?CWaf%2e%bkIOLDs9+Bc~I{Bx6FfbKffjnNCyL?g8|aP0O??Wbd-k^ zHOB?gQSP0$+>Qm(!2;=EfpoAyI#?hbA-lk>A-lk>A-4?FOyuG z4%9~hjtis%@EpT{bYMU_$^+UKd5^~YKp2pYN;%ajksxN@&7*TtE zV1VZs;5i0(jy_CnKG)dI=kY52l8ZiHd9I^ny#hSP0M9YNa}4mjacZjO7(jUdo@0RL z7zNZTz;g`n90NSZ0M9YNbBqGcbHH;9@Eij?#{kbUz;g`n90NSZ0M9YNa}4kt13bq7 z&oRJri~-66@Eij?#{kbUz;g`n94k-3wX9cw=NRBQ26&DEo@0RL7~nYuc#f6l&02mw zz;lcR(gApm0iI)k=NRBQ26&DEo;OYyxBSe2=NRBQ26&DEp5uV$X!4*Yuh3Ki5LVk> zp$P|IyIKcvz;hh%9Iv$PRR__`M(r^G&vC$W9Pk_mJjVgg@$!6ci}Jt$&vC%>>b%88 z=>W#(IN&)Bc#Z>}jsu?Kfai^qPA&5Q@Eiv`#{tjrih~#B3ot&%YuxAY zaKLjM@Eiv`#{tiAz;hh%90$hdc#ZMs^8udYfaf^iISzP^1D@l+_#7`^B57Ghfaf^i zISzP^1D@l6=Q!Xw4tS0Op5uV$IN&)Bc#Z>}jsu?Kfaf^iISzPUonySH zj@LNNN8RG)zGHKrkdNk_o7>&yFjTJq&vC$W9Pk_mJjZM7=JU{G?}a*a@o|BA1$d4F zp5uV$IN&)Bc#Z>}jsu=o^ZueT0zAh7&vC$W9Pk`3UjS}-UIL!ufaf^i zISzPE0G<&9)$%}BAZqfD06ZrE&k4YD z0`QywJSPCp31ECq0G<z;g=loB}*A-?7z;g=loC3z@ z6yP~k<1`=LHqw0Usku*x!Yb`ha~P^ufaet8IR$u50iIJecJtAFrOnqHFFG#ZIR$u* zZjG(Y=StcxI?n;mDZq0I@SFlXrvT3>z;g=loB}+j0M9AFa|-aBs^s5A=>z;g=loB}+j0M9AFbE;M1$a&Yo>PG56yP}pcuoPHQ-J3b;5h|&P63`%faf&eISqJD1D?}>=QQ9s4R}ri zp3{KmG~hW6cuoVJ(}3qR;5iL=P6M9Pfaf&eISqJD1D?}>=QQ9s4R}rip3{KmG~hW6 zcuoVJ(}3qR;5iL=P6M9Pfaf&eISqJD1D?}>=QQ9s4R}rip3{KmG~hW6cuoW3a~c?* z(>3m+r#}GCX~1(D@SFxbr-AV~U1K~tF5o#0cuoVJ(}3sbnV1^)X~1(D@SFxbrvcAt zz;hb#oCZ9n0ncf`a~klR20W($&uPGO8t|M3Jf{KAX~1(D@SFxbrvcAtz;hb#oUU=2 zj~?J^*(XF{mGoBoi8SCj4R}rip3{KmbdBA7UiG@w#mB9^519r$rvcAtz;hb#oCZ9n z0ncf`a~klR20W($&uPGO8t|L}JZAvU8NhP}@SFiWX8_L`z;g!hoB=#%0M8k~a|ZC7 z0X$~_&l$jT2JoB#JZAvU8NhP}@SFiWX8_L`z;g!hoB=#%0M8k~a|ZC70X$~_&l$jT z2JoB#JZAvU8NhP}@SLf%In{nU19;8=o-=^w4B$Bfc+LQxGl1s|;5h?$&H$b>faeV0 zIRki(o{FupmH|9x0M8k~a|ZC70X$~_&l$jT2JoB#JZAvU8NhP}@SFiWX8_L`z;g!h zoB=#%D!yG*{{YV!z;g!hoB=#%0M8k~a|ZC70X$~_&lzBR&H&?crpA39j{!Vq0M8k~ za|ZC70mkP{jq&B<)_TqWo-=^w4B$Bfc+LQxGl1s|;5h?$&H$b>faeV0IRkjk0-m#g z=Pckk3wVy6fUjvcSio}@@SFuaX93Syz;hPxoCQ2*0nb^$bGF85K98-K+}tNbVU>2W zISf5T4|rbPw|btlfafgWIa^~lA6+YNd9MLFF5o!}c+LWzvw-I;;5iF;&H|pZfafgW zISY8s0-m#g=Pckk3wX`~p0j}GEZ{i{c+LWzvw-I;;5iF;&H|pZfafgWISY8s0-jgv z=|%Mq@SFuaX93Syz;hPxoCQ2*0nb^$a~ANN1w3Z~&so587Vw+}JZAyVS-^7^@SFua zX93Syz;hPxoCQ2*0nb^$a~ANN1w3Z~&so587Vw+}JZAyVS-^7^@SFuaX93Syz;hPx zoCQ2*0nb^$a~ANN1w3Z~&so587Vw+}JZAyVS-^9&ic5{P=*d|itoFWebOjFBuJ*bb zk_OnWb{>uoc+LkrM`8l;0qFodM|arO?hAO1o(ciB1JVJE&-pd(^LTu~bAF{SP;F27 zfahq9q1w*^jL-QM&o7D(@SG2L&Idf_1D^8%&-sAoe86));5i@goDX=;2R!Ekp7R0E zt8;=E=X}6(KHxbY@SG2L&Idf_1D^A1oJMC`<=k)X zi=wbfc{GQi`V4r^2R!Eko>$k6s+I@3=b=R%=ku!f@l=mnyIs@W5-snU_}_N_%a_I+ zsHs=~%a{M<%Y_{HU)?BYM2mK}F>n4C_P>1jU)^Xt75;aA{QswoKz+i>TtRc65RDou zW3}e6|1a7w^fY2+?xy*;e6&{nd zoaQj}BzI+;QVBzsQd=GaJ@HtXE2wT)y%)P0R=sDZ8dkjqR1K@nDOAGHlz#IZKs5}j zj5VrZ)in_+Vd%EV=5wn#3@yb_84p&&s`G1=F!cCV%j2TexoSQO`XrTkn##WD7VDOI zg;u|+jB6{~pECaO=maHQB=dK z_lQ)&(8*8}9~yGj?8~oSXKLOTtv*zl7p;a>?~Sj9Rp+28VW>Vg&$U#;s`utq!}yhX zu1XkM?xtnFpjB}yH%D(84$Ch;itq4=QFM7r_U}e1D{F%`#TxEW!ISj4-SD9Z3WR>P{#fnO9>Q~%J44V8JC>UP!j*DeaHsRw9Ph?=$ztsYUCr>^XamQ86{ z@6pN=mFpzc?W)(4FAA$^|Iq3dmFpkX?W*^2Uldkb|IpG(HF=7bd8++<)paJS@!_>) zgqHAYd9I=rLn_yvD*K`(25ag#dS<0%JC0U4sm#4L?~CeqWuCPfR=tn05{52cwoDsZ z-KWMp99?>^*$!Q@ZkY~L>ucI3wBkx_{-LEXYtvSJuC4j=p%qdpW0y)8UfT{KWh&Rz zD%+tY3|f9Zw895){#LK2RQ5%WwY5wyS{b8qeZRV0b={e2SoL1wYFPCirD|BM=V;Z6 z%G_{eJ9OK1%eEM;T2Z-vRNbz6kAF3+wl9EIsHm;u==RB)dWCM`t2r*Z4X>u$&?VQF z&Y;y9YR@-*Z62T}f-Cc^&7T?dJ1_1FptptuBi>ZkPUxit#$XA(%Lcs~fNO}y(d%y- z-)vHT;Yc|UeOB~J7rA)ycnsZmg4UWL1gL_d_)&9T?qab#30i~<-Tx$+I3BGu)Ic?$ zwM2vhL4%lN1$<0MQ9?=}Aw>e7fS!0AdL==pbjF#=G`*1kk%sfSb{aZVqHXBfsjjZB z75Z2HYGr;b5^NBopV|#^@oAUSN#bJ=PShB*`~iQA%gv|9c+lB{*7Nh=l$%5w_NlYf zdF$MDnYwsgs;<7ySQo8xqMtJ8XKkIUE_C6qV^L_ZuC5+^2@EExd^B`8ZrxDy0);Wy zSgPyLFTQ+lf7edsY`E|y3AH!l^f)uldek_f%+QL^s}~a9R3_+6qwEu-e_G9C)X}Op z)R)^9^sU_eM&HUgj=q%}EA*}0&qUuE*HA8hFJE^le=qlO%iqgwLHT?68f*D``CLZd z8`oY$FD&9}Qyy;${n{7}*C3Rp;@L!cGRoBQJBiZ&?|%YxART(|QM$hIgEzemNrdtx z68Bf%HBnCMg?v+aWByE_9xdvHQI%KI*wm_~J)3iN;=~K@ZR#}s+CswV4tq28F0Vfr zn=tsNE!zjz2R#!8TPZb9oe+5gL0Ku~HI_6ccPZ@Vdj?-SvD5g%xI#1$MP*c9h(==R zafJzko#prAj6*+{gSmt28#l>>Ck$?SagI%`s~5#n-g;^*F~*HnS*xe%v1mPCbPN7y zl))Iz<)Q!RF&H_HqQ@ckE_@B1*a`hKKIQR^GoaUPG#w88HeqlelSz!@a`XB8*gP>d zp7L|iGN1f%G&nv6Z80WYjAfjKF|qX3O_4OmgI@mQP6ZPg^wN;}a=cDgJe!#?cyReZ zm9GowiYCyAgc1!sbbjpcEpQEnxAwD)+FF>3WV19~G?uzUnj#Fi`-|i5Y!tm2OD>+6%?3T=7>~>6^)TcZFGYIBpfx+(W6(uO z3Z0RR7plcbyDy=dPH@s8(~^Y5bkY%HvDF0=^$5Ayln=kh-kj#M7?|N9;N++fsSQ$ph! z-|FNozvpFQx$=$S@yB3{38TkRII4f>wK3y(yvB52yyd^s@skFEJsG-n{szU40wb$?T?qSkyZPd&s53KBp_53f`&|h9 zcl!mSsO6o=<%hVWK%h5<9SVbAAQbo|0;51E7YM{$MlVR4rGcDB$}c&k{JdPO&Z-3h zQXh_J1RS2A@e9dpZ2UnY7Si62*pByS}e$y|LMg7QYR6xTCq@V zY7pv8l0x1kEu`JD0_&^%%S&X!yjvp{@a*Uac4@$DXfVVZ|K1p3Wy6bi`VW8RvI2ob z?-!H=5`iEuF*GGvAU6o~Y6D7|MBr>py-ZL}ogQse`O-HC@^UH47MVy8M#)2e^UK98 zw`+<}FA$;BqI_}7Mf$i3Z7M&f+C8kQDLbK<(`q;ATQ`G5~&221l@Lt zS)GsugavCsAJhpOBQ$4Z0d>IqzQP;4w*^J z%gW`7xLP3!_#FWS%DSYHXUS+RN@1!A zLkXodnxKP;WHOshIq6*7AP~q=^5tS7dbug8CK3^<5Uz6G1|(vkNMcl*B@JeSCLs4a z3Vw&A*cf5lCCfLcWd_oy5j9v8L3d74Qiu&=f52%p7p*}%qY?=Tol)aeNc;tlIh^-5 zCRq`3G)RM{d&{Woy8^Hp~!lL_Kk* zHIZ^jv8)UKzmne=Ax9ihSgjE%=!hn=?wEu2g_GKF!s(19mCHj_!F!Dhese~l4V!ca-_I5KlalTC>nL@1bz$AVs?BC8Gt3Z`(##j|@1 znXsck`VB-nZX}IjLM@7M1tK+@R)~1fs4dbMA>(jHa|OMPj3)D8IuI3z&}$WVad$q1 z`^0gySJtiNISAS>(o$+)5p~_237^783saPgBiBg6d?o4Q`gjITq3K-KMtp9roX@6t zS#y*ua*`@rl2zL=t&CT6WTIBmm9}_-ssHGIji2!U+_;n~F{o$lR8f(1PG=2TOE!n! zL}yZ#NCQ7l+`Pz?jzw79XT?fFkCkUB zxv~nof@d{K4W4{at2L%|c0Uyr_=9;;Ar!`w(FSpeD^OFM#t4ln9Lt5M3>yofjOOQ9 zOUWtM5f(~@d8~RfN6rm#1xidDZHVzGA<6nMpO?a8m_W|z6nq__;8F^7|E-5g@=}cv z(uB*Ox49L3Ehin*74k)X-W###7zH;dQfKpyLO5inGSWDYDj4HzHsBCP82vwFY-5D~ za_;<7j)#dTQL-g$k&uD%7%^cXEz0BsVgr>>y5u?5gqD-GL`zKs6bk7KE*7g4N`Ia~ z)kKAP&*tat)D~RGRfTVnH5FnCt>?+!@1ZMS&Aj(744;%UD#i<91t^ z3>8D!Vo8shDPsnez1om%jL?Z&axx(*B}LApSJ`N%BTSNRwO&gV++mK=5FsKmhsKX` zEk?v-MO&&NmXfoi*OXMGl!!fMh_Aq9IjsYR#Gg9p{q{U(`&7L|ko*3hA9XcOsq*>1CFj8Y{8thEM}*EQI;esGK%S zxw1UXlXC3Q#!u+8h!muqDY;7~+FX(~sCapXm&$O9CAQ$?#jsq-MTK*|lsi@s6?q)J zKCj1Ur7~FzhqC%O$3at8jY{ifDJfUc7-3qQ3W!}YiG|7-ITmh7%VsPwHit$h-ZZbl z$}5@asNU}hhZS5Bw@Jt}uJfw1a!RoS9EcRUmfHRej9(p;LdNu|x*7-7I{ zmZS?Bfk2y36f8w;Dk;+k!gfJ2uhHwhs8e9c{Zn>0{oRm3#`TQKA05*DqU<1-ryAzzHp8WMbsN{+g{MB2kv!&fE zhhsKSah{5f$TWOQF6FhjqWP?c+Zdrg8cGVSQNBoI$gmvT>2(;xb#SbW5!%TTWp^1vzNk#Djpa*3 zK%0{%2+GM-qpFk2Fr1LzkChm zs;B~!FB0lV0T~dMMPo%3e9@gJNRLS; z<16g>80F-+Q%)V-l!tbX$CXtXO{_c=4vI_h7@I7zc>`lku;~J~nAN*P4$`LJYRFR7 zE)F^rlz|P=6B#9cYCf`&jDV#7Qe3+dZb5Vw{&!3}2SvMIX<3^t`SCX@=jOTHf zy;h$JVWnXl1+^@jh;SpGoPa~xxE8HJtp>4OXK)Td?5D=KnP&x%zA4PXSxZ zS7LMtjA)>|Y)$Vxh|%a9j)R3590PpNR)#nZXmjK^&vHG+;%*@v1wV`GH>XaBHqH(^f8jRiTuT!ck!OPrgE zMZGlY^)`rQPD9vLG)lN4Y11emEAYn5e7{?Va@fyGG!)lOWn(;2juzL{%kV^mlCdE? z7fE8_NF=ObQXbmtO*q4RBO3~1%A_eP5puXmcbdm(>TpWc9yU}${a}sW9bn?2u+&6_ zwQ8opEK73IX!4E|E7F0aC7;PS^}$q8#}Npml!6hsVscYd9xN3d`9jnYcN0NfW0IpP zmdt5lQJq=GvrxJW<#HpaC!L8H}&WJTInawOf^Of0Muhh#M2wr7N)LMT|2 zam%C!W_BP8`IHhwaph)`b3zHZ=+3EG3~Uv(da%p4F5xkzGs;}ZWo@9eUUNv9ldDBk(kLtlWvDw7 ziK9-kAetAaL)Jo2EAg8hVh=5jI!Td{fll~bz_7gWm;n} zE7?>^DwA`IUY|;-F@@D?M_9@=k^!Au9t%f&l*K7bNMmRWopX8V5GJ!|41ySjI=lsC zA?zb_CQ1`9JKSCV2bb519t z{CSR5qYbAVc$&bh5>ZZ!8Mvr&H%6%CiUKlDQj^U(O%{{ZrOJ!Ly1dOxTLpPCt7c0% zR*)3qwqk%%@^r;ig3Nd{0j@VK?0Fq^X3M8`2U!R)A;uGLDi#qbPz?pv6ri zDSM7dm2#9MYsSNzY)K!DVwNypW6JS4j%)%i>UB|48zBnll9*19I!gkxE6b)CCE=xV z3X*XWp$3N{VHL^a{H9N6_lt!}G_98iNy0p-h(KdKw-@!+PD3IV=5TW=UeRbIA|OS@KiyI z!eUM68vG56fFsAX9EVk9j+(O~J!vGPE@M!w)vB{frJRj6m9j_5mP!Oa<#zgLi6^J` zOW3@{9(K!w0*=b1DJ3%sQZ5u`i72O(<>g9-h@_Cr6|#OASB+CVu9ZZ+(O8l3IBgfE zs<0B@nDH1rexA!{iD&c;qJRlkraiexG@?z(Bj$Km#BuTBJiA&IkW2hFlTKZdiOedR zlTL|>v0RGwsGOxt*xA&#A+ripiGp%vx}^5_1!{{iRdk_i?^%vJBMM=qBG=8eX0+jO zIB3(au8^FF zitwN(<~PdhXhzBvbo$bKffscGv6xV*b&F-J&FOY0NRd0naq@j=sF^gUvS_Zth)1%i zCiN_Kmse;~sFYMZnT#imMmL(*ZaQyd!B`A+H5r@}_8GZKvpB9W(MEz%n$&zxj*!?n zMH8+_`s^}|DaPm(O-QMAQ6?nB-9kDYE224=T#;ZCdX7<}&gQhbXr60v`3tOFZcFmQ zMy-bqaee7PT%e81Y)PkymzCxH?l3Vsvt9=Ik)x8dgUVrEbaN=4PNXcP zJmfa$l*LRbWWfx{fG-_Qr7~tJ?DSd1D!E@2lVaLnBp4K#oNBH*R4O%11bNMTk=J8P z(J{BbK>Gw9I%o140$gRx%+clG% zb`@D&(p0nkaiB08xM3GBdiU~TZmAkbnvWR;8z6<@s z80~SXoOnZ~!HE}rj$lA9&z0hFDaXOH2jy{5Ob}5QHDNsH4i_4Gr$$-HgKk;T6*CTz z&+IVr*oeVpb^8jY#!@zBNM+9MRmogRH}2J{tyZouK{>>Fufr5A2M-JV~p>aA>uDp?-L!DovQy2H)eyKH!Tm6No7R|;h@;a+h z<0Lb@Xj4~2NSUo(v!qaH@XPEfemt2o^HRA;$ja(inq&2q#9WQ5RC0T*8dD|}DpDM4 zG*AlBjLNK1C3!)fM`x7dYKKrLYie!0DW6o3Os0cu6i-K!UROvQ4{_sYBt+?Sn99V6 zFpgB|v@6ZN2v@Bs8cn&ZmXT0slwvG8j4r;!m+=I`Az4GP=^~fBJj@egDm7Z7T97Nr z*b*I8MKLz+wOJ8BB~FSlnpE5ZW2I8XutJ6#_2L{ZP|&=R7_*vjwOf~r@Ix`DNi1zF z3}R= z@EQ2Ym^aR(oi3E8I@%O)qDiJ=j1xp%FEPg*%ouXHR5t8%YK^!pk#oB&WKM}LIB;ks z8ifYZ`7Do;=Cz@iC!%j?y2!;hV`#FY7*S9zUJyaA(PsR32(>6YURc%;XDm7*lqIz! z718T;0x!qvl!-~VNoytOXv#)$C1JUN;9Bz~ldI`mw&+S`7EN9>@T@`9ppyxwk4a=K z{||d_9_~C??IDrTVPRJ|51&NZRZQ7JcI;ClwshOG|XYJGp=W4x}V2NR~*0h)RwA8dTGpNnia9_{Q<(t?@ zu`A7KV?qgaSJb$~b6JaZ%}Q}dH3(x=EqadN0@zkcO`_qFt767MPTv-z=2T-KyX88( zqI5TJ8?uKmA)wNBvsnwMpH{%RFZ^^XEt;yNLN69p_LZIJzVSo zHcmFYI)YgdN>X`hbHHjy4pNDf>}8!%2BPB>6k%eOiz*~*L<{B6z)oj`fF3QShAb( zBjrBCE(y3RXu;CeK{2F3`ADZ@#1UDl89<=!CET(wCA;pV3~@P2u1{GTy7HWzr9^*8 zdGl329JE!6VBjtqqCqQ>hY@rylwe1(tcEU(hg~hJ)evW_;JqGK>tm{eq_)GC_2D{# zO#`aNrtFxal1%oT>S~myQ_HcmBwON|YFb!c;)W+}V$Q zk$_{-+?D8eTxK}8dP?1p<@!3dsl%7UsZD@{S+vS3HcY9|C93`i^V_AU$mi8?Aa{rs zDH?s&>abNsln4V0eWIDut!|P+j&7+aTeEa#a}H5;A(JahSL(oJ;1sC6kiOzR-*KY*iL3 zVp7-9(oD415P=Jdi%Mszu|uh!C9YRilGvE_2I9hTU~fnUD4+n$<6*A8i426GMqsjB z;CRfld%$bNdE8#FHhpy}h5fPI>Dqu@*R`ZnAtuEc->*$km2nBnUDgJ%h5@vmr8L1T z><;20uEaR-E;5T$Qul~~9NQCqy#-K`nIy%at=kpWu!U%e^HW~Z$_-kDpIvdP^*^A zN;SUU=LV>|3Om{65$2Y-pbp{o=I4|80FV|5A4&eQs+c1lIH=MPlEbmnpZi@}%mzB5 z(&NfR#%m^GqFILoXl_^?IvE`q>N+G&dyBsUeae(Jb8Qz{CISP?BCwr3I%~JX zXgSAmYuQ@*alfB6neo&IIYE}TOJzfqiG~F1ilek9mCmHosZ@p_AfndEW6K%}8HWko z95T&$W)DxhWQ$`L^~?@1kiv{(Iz{l(09FsJ*<9wmJg7-bW4{$w~2PDd2fr)bG1~15x$2e&-%Wn>7R$@(AIrL~%8^gKG$pjFH`K;H2 zvs6cmDaE8|tal>Ea9zTe+zP{tRuf65Il|UKnCY9a{v}}{KPKJH0d<+_<;DgjTfk_g zE!3m!Wizk%eoPf7YJilS@@yWe;Z%VvO3MiY_qGY}{B6SB+QF%$Ebz+>4XEYTqMS@zdA?*P% zCetEA*y~)6P8Mpcj3!EBI&PJvu2c3oe#Uv0%xvf-hHS6d>*5WeYO*7oApvM<=%u)y zlR+RelV`nXnVZmsmZg#88VG_&zuhj@Ij%x0gQ38b>q*Fq_Rn=}Z>O_-C z#EmNaGP4o^Law6I*N6Qqk4&|O84h}BQ!2#kkl3$@(@Dwm2hqmELs?xrQ!&^F^LSeC zG8PI5@+zzj!BiklCUMiPSN%n<-*iL_6d_05u1Abso?%$xmomAP zkECJ}C8cE*C8mWYJBffvp?tdUP+G*2n9^WfGMn(twnVy4bEK-2B-xdEMV7kjeK}_h z8Db8EtU(YW!8;Rd)-42};PFY#B|`;kPr9HnLMzR7Ijt}JY9S|g?7W3pWV|Hn)R-=1 zxKtX=Ad1^O!cJJ06Zpkz*<2iI#kt^&MnJ1kWZY&WbnGVS0JcJcau5@Uik4V!qYNZN zX+jJYbn369&R{H-ykaV^VP^=(7s|+5P9b9I1!D(s0bIcwWPno}cx?UuRlF za|WnzvA&{$K^+eWlW+Hdf6(T=na{Ym&u<=K6VKW0l zy(@xZO$BjSS{inW^fX+aE1cHk~%FUvN$hPZeJ~Gz~HW%ep-PaM1U2g$$X_{1< zXs*jk(qRhhC`VO623I2JbiEK`jR{a6CL`IBdc(HLct zY#ez>L=GL`jPZ1srg&Rr+I6laX^uOsS>-ir)*Mw!u=qK`k{vgasXJWcjHx9!Ri|u- zs-Q4EfCnSU_n4mAE?J5(C8oLhbV;gxwVL%eq)+GZyXCY;7U( zu}*c|W|+vutlE+$5<9J^SieGQQw~{`;da1#lR~xZ1DtDFq0u0}+8mHt zl=Z|Gn|VlArH&HHp@zry!siOjkm%U~f~P2rCHxo-?OZF6g|rFIFUOn^5Ec4vXKv@m zYCay0$A$H-r%xP$bjUf&XJN`^Lb1mp*i@vtbdjD5UXUi!yqCx-hDuXgnp6o%;*xd? zB}vE3P@l~K7zT95q!@7P^$_^ZRBo2yQZIv$BrbVU4PRh-&$7Cp!EMlOf8fk3l}^C) zLXzZ79fft9OcR=JbD*#2>YP#4`o=U|^wyfCR z0oZqTX-r>EUk;SyZx}~T6ktvn&ZeOY=ojEr_ms0<5kf?90$STRH$yG zz8(Z^EI}rSoODnxs;`6g3f=4}YMe3%8RwCXGO8PkPJX2zzCZAVY8|MwLQfNfmaX(S zeT)oid9K_J6Bm^L2;lNf3v??qNk!x0`nF-cx}ug!m9pbC7K6d8$+yrx!J1SzX;}@3 zHYOO#12*D#iOFG2;2UwVT_i^|k~UIU>FfOt-iW7@gbLT2RJko_FzXv-UCz{e8mee*sYZl|$ItxJMW-Eu% zHLmIubM_=GiK*G4<9eq|0p-*yH+sIOn^m2Y#-3Oi)PzdPsad(5wO56H7jczBEOWe# zH!(cjeDqe)lj#V6Em_VN-9?*`$|H$b48)Z?_CS0MU*!(O##kiSY0AodxVDTVhe-;} z9@a(+?0q4F|3q`WyD2Y5XdEVweI1Xj>u z)=40k>KD!OYJ_rwYBtgpU2XzWO3f2;Zs}YL;xA>rIE1>^(nfuc7FQtGG)%=bi=Zb6 zD9?dkH7e^5`h?nU3p`CMZ7(4}w`TXs=AsWR+|XjZRtVyWm}{_Fd%Yf#xeA?lGsWe= z@S_P?0cC1R*42Zs>1bXqZIj)R=Si5C>o<%7(aMM7I0gxRai&q))N*t#v?c)i6yf|WLJn}O(K7#j#dJ{HHgLfVX%2WSmq%cal)t!RXWWNN8iA7I~#5|>O^nG(fa zpmUJB5MD;HWK0(mwO-OXn*%o3o?Wy>Audvs?U$n{pSZoHCIhMv&K88eteFl%b$LgbUqJ>}AEoY~ znJyiY7{(>qLspod^VkX=gY*KTYP=!iOuZ;owH$BxQt6i#fT^uhbT2Zb<$%PnoO1 zJoTdPN@id$X?tFkgiWW}wk+e{w?2-+X^shZHB zeHm%?QDSqzB8cHY?gcWPAfBstw<7^6@G! z#zG=8bF{5aLW}Tm09({G_BCu9IfXQ{VLA-C+S06(zTysi(XBD{aXRVy&U{wrJps4@ zAzckm6dM~%7igr_anMFBIAg0K2qP@7sfA^MZb4whN>+g-4ZhQ*tzOmkGNR|<-fWoV z2`6qQuvLyQ&#C0g*-#`WRDnqn*{~=Z@bQ|4@_Y<3wFQ@$DqhN}Gcpb|6>pd25r;L9xmdwi zxWH{*Z0Fk5W+9`oInrvbk5FL@M+LQejUz?SbR`7bPb=8X(g_(W7N{k2WO-Tb!rqz2 zz#(Q#5j-Vus!?U&@tqZfam^y(f(>9ytYaIrlcJVK5W^%Nn;b~>K>RD!W*wnIPYAS- z)&Yk>rq&=~Vn1K&#B`HsPg*u&j5q-4dR!73l*chc-Q+>%+n6(Kz^j(9UL5FwK2)ME zXi`LfUJ?T^HW`6l_WOl~8yMjb(ei%1M+oVX%n6Z`YNj*n3Uoh}8?l7V>mURQI{u8pIf^XvGa&6LKLDkV6kN!p#~o?$=6U*F%SBg;A6xy{b5f5|}_g zd%DfC?yy*fN3K~Z5Gqz=1h`0qB&;_nCnd{ciYaSJe?Y~al>571%zJX z*yV=RDg&Rqp|^n;+bcFH9Roy4FVMYWEEdU7nAE`P0s|^_hP1rZlBiD8V$v4;o&v_S zQLa*~1N!RB13YHFO%O+#135n^OGotE2aExhJnGOUK9MIMpM!BAtg zz|+)W0-E2sK6e?eUCGZCrlZ?yox?-`P_#FxaLe|F0?Cj%eznEOL=Q~rimiq=hsWOB zbOc)8K<>H|4C(8!Fd`6*E>93HTqVmUYGs*RTI&xBi&-tdC{2vKLb7_BG3I>R9jFz? zt@z{N%pWwsTULsx*2r~-EvLhd%B8`aBr5?1AK0XG;@LD)A zh5`i^;w@Tf98eBB5ERdE6|i0)CzA+GtH=;_>8vn^6rn7srCc-gv7#y0%}zx(63I3J zGxefsKEK+E5`*Enn^zaiP>$$PAOcz;h^}GuXlzudF%;EmSLXs_GkyY*IFQCNka9sx zjWG%kACc?(Es5&qxSrz~>qoES)y!hKUTV;6z1k{-7R|VJ$beX1^qqoh^^qwfFagvF z(gGnX!`!%z2(U&v8?YPI9z4H29(PH3NhRwT8=~Hhze3vM-ehD`01oQTVu-eFd77}~ zh9kuQ2nIBm6OoujCSI00CQ${2;xH%MjlYv zeznL&_0rhl?5Qf33XUXg2!sm>Gj)q8TkUm(I#Jf93d^#MPIoz-E37nzYgYj{Q0#63 z;{$(9SSy7pSMqF_h6-8Kxrp|m1B}Vx0U6J-Vi56u54Ls)w>+~1|0|AF#VN^AvtLb` zW35CPOF{?!dkkN=hD`kxhK@%v{CzmkW`awnKwXn=aS?gBDcBRrO|^-GB@d@9xFK6c zrKTzq1!c`_;q`!BCxJFD19K?daXE?tSaPp5?E8Pd|n-i zqgFN75nH1i)wCs(^RZNuato}t^d~x1GO1d7X*Adh70R>RvI%sv<+SA18v9ylF$qFj zP9yr1;t_gA0JJ-D(ExaMbkDypbOzeIH>x74?V9QeY^nk=3VRn)izRG66#5BCJ8BupC;&fEf9M@&E@6aJ5xMQ0V)7Rn*4KX{5rU5tizU zF>C=!S7wSKYpR~pny^ARNlR?F;bHkZ{GY^8)+RwGCIo9Wi(O`Np`D_{d@{r#t^{}j zfON^3Vrit5H3F|AbpW^<3}Ofbcq;UnK1B4`k&0P3*F95!E;haCpgpOQYbA!cY!*7R zxe4J!9+C37x!OfNcy5_t2Doso2TnaQV-RUY&cTe*u$831nsIj!dV zI67GGddotsF`DNwEgb-d3dpJ^B^_@S;^__$D47CbXq$tx%l8lWpoo|t{ydp$4 zvEmKsLcd9OhS_RXm*}WZXpUB-8sl(HSR;yu3T{HAv`ka7o!T3wF$Z5zB>3Pr>_YRXO|7OB~Iw_qFKmMQ^YM)0 zO3Nfq>Z&l8J)c$$u`sBQT>x*}%T=L3PevmzEpf^-ewxDSIza|Dcyb~OfFOT9L{{^v5TQV-2L@w`BR7?G z)u4)=tl3z8CWW~*bdT#?IR<;3?2fwpP>_HF^V_srj!-5biA9Gj%=+CP668Bl9z3lx z2rWQhIqAmv65KOdiR0T^zciq!NLmv#HFoMs$SUx#X+!R?qduyGb;IVGW4&JP@iSPr zKnfj%UB1wddY}%kw3J$+Mg!ALS7WWAFTzz}ASYuATL+x_KREQ#RkRM%S%|DN+|ES1 z=-C#ewN`^`VV2)h12&nimI^{eRIl1bx^Ti7fD)S*WiY78t^nHb7H$~*%F4;RG1^WE zT#(yl55l{JqV*iGa}6slU0!jdw#3>wjHVSc!UO2 z$Yp&rF4(Nwz=_cyPW%>I{@uP&z{$Vewk^A1#%n- zT~it-h}tWbW`p(OkOWNDHncP_iuM4FhX!4WI1;G{iEF2Bi00iHIZ9C;l&t2e0#fS% zz^G(^^guK+&?KhT$pM8#i~w6+6EvU?Pcy(=jdX6+8bVl~5C#bZQWGnrc=%*cASCpu zl^?}`aK$a$A9>0Q+)vX%Q`tP_4drrFt50SWJWVJwlx#jX>g61bBS2V0_u=q!0LV&A zg;eGe*Yt#O-{{VQzEY0*5sAAPP{-|*MM0ObIbaf6Aw-O*mt!nho-Lb<0>nwnZJYUi zML}{Pd4oU9hU6jv6N86XKIaTmA+-HYGs^cyTA|!2sveSGR@WG8yRxWjPAud`tZI!m z>I?K3@O2PvNGNh{fI*g>!ER$R2_;aNrxiwFe+U^U;>>bjw%{v+7K)8iBan*g*rq_( zm<$&hk!*oOLmzY_goA_jX#y5u+?hk4K@iO(Ril@7oKBH;Rs>d8B0*o&ATe)F*+J77 z$yPo!gY{~|vF&c7<<=If(j*f%v=GoA8l3_!#<*C7F7$EKEjO#S3;eQ8(!f!5P>^4v z722>T8c1tuZp+TQ$%vxY7sUo!yj!df6AsLXQijW2ttx?|fR`aftT_hew(E>MQ+12! z1H-W@Wfj(LvqqC{FKS{V;UB9?D>3}%UE!2usw)R3D%f4Ln9A5`6<#XYnanBH?ML#X@e)mn{Tki-YgTt zq3tzGB5+4ShYcoD$gxf^X?I%;Q{|Px2=t_|3_zZlfnIW2%2yed^28cYxbk{W3CuCVi^nap_|Nb+N;?55^-e2$jS}| z7;m+?aw@=TEUQH|TxbBgCiSdDP6|_q9ji1xtu1g9!ie<(i+Ah=z$ul;0_Im36i_n{ z4iW^D5pp)IlvR16E#PT)k=r0 z@J`+6B+9I=Ftjr4)Yi+_oSE>&`OMq&crevMUK}gzj8PgirP`F{FQy`7_gt<3BEY6^ z4O=#RlPFpupw0lIB}l#5Z-A=6irw`bg1U<-6QFcmA_U|!4wvxnI(b~oQ7#Xmz3hz2%^3$(Ffynp z=qeScFIIPJvkH`=&ZMX9_PF_Uz1qa(8cKBm zCWTbXt2Jz?NjC;*Swt9Ofbvkxz#Gpn0629J>;TJB0`p4?1B-mSt4`wn!0)l#YM?~c z#C5htU+r9eHbKCkQg5sP`{vRFoRhEx)J)n%JfY=>klHwDxt{~jSm_TDs1;(hI+z@3 z9S|(T9*Hyf44U2BYr?wF1>FbYt;nS>uO!NlTkE1FWDe!B%m%cNFpFZF2tY8;@CCfs z_e^F1wH~R=$TxGSFzMC89sud%(i$SD;GkPAO?$aI$47H)89)fZK*JXTc+uA7T6<(y) zUSDOrVU{?q4Q}^Qv~`5J&Pg|*IsZWWmN#{QkA99|mMA!!DuKS-)slo0f`BrYga zf(BV&$(F>K2h~NqX|@8r&ay(1>R>grBsip5qct3MH&+{u8vz!<8fsXXIEy*eg&{?- zW|ZpzHYs2b*eE?;>$c)yuPWBu$wKyLHNjCJ;Y=Dsno`oo>|dJ${_WSP3@$?~1OH~s zvABdg$*@-JuJY6ZD#g)Cc>)0y0DIUh8z5aC*r3H!&x!=1Qg}Ovw#8=iv-n5 z%OBb!E}68ad9hp+CbRsk%b8fBm78<86~Q{g2O245Ic!;_+fA~oHyUQGj%}z31k@#L z7J{A4Bc!TKxrg^sLu>k)wE;?F)mFZ?kgWhTpz{?F3M3%PyzCZ-Lcv&|p}-O9Olbms z#i^rt!n;`D*_ozK*PyPY6J`aUnXOQ$E+tj1<9Wi63+3@FobZx55Jc8)#o5a5@mvAb zDkv*x8)X;*31$4z64o~G?vA2C4?KpOM<{nnWi5&~g2B>YPBuF{&s9OKn}Mug9_4)* z93X+0L5U4hiK$7qf&`S(?5SfUYX<7XE+=&_6Gx1z^w&BEc0P_k$5~IsC=bGy1s-XB zIb;}-@jH?rE?QmJP~C1cFO@+ZKvc@TvNx?y`co-Q8$_u-rP*xI>Q4i@zQ!B^zgM$e zl~8e7fqN@fAd&2bS(p#BbYW|7%T()jTpEdN8|p~ZNj{av6~LMY{u zY80*Wl&YH_moq%+3YmrJ(qc>m>Y|E+<^kfEnCgoFVd-MAITCt~-IeiWzdWQp0ZU~M zV`D>5DSJi}D@2{?>y3Uf%}07Ea(xWsVN;^cTAtPd7+h!}xuzA5xG6G4B-R>%w$kzi zpUgV|H(10R)PrG$3?~%E zR90N{zys5$(V+iIEVG6&5?8?{IA)6_m0{!|%*HTB_BRIvW1_HXw*e;tk~*KY43i!K za#T0Uu$XKWic3jP;Ak%|4kxPxlvZfw=d(Z|yv0Ho`vwHjGm!aF#b9$lV+7eoaRbar zgp4IYVV%dUq2_`Haj~MB7-o)Bs%){#kO9VSu>y^Wt8~!XY9^`y?LsLW;1C=zONZuq zfrTB!qz7G$o=GDk4E+j{=Q{SpKv|523Kgo$|1DB_e2DUB^pxw&!1>BdYEsQw4>;(z z)sD2{D=F{PoK70_M@}E|6%s1V1X)PLD1+p%pl3EQiB{}3n8t*xKI|1b5O-6Rxx575 z?cA`TMj5$z$}I#;tr!^rFIXjUozjcMRK*rVi06_HFIrJh4#9v4kh8CpLDIR<0Z4;7 z1v5ZI^lXx}Or!;7Mae3%oy`H+u#EHF6pF}D61DIJrc;gE;n=ZVP!Y!^Zkn4H!>~4& zY;jQmozOH66`SfVyI|p>yq(-6n#UwTaY8vO+~&IgUfS72sw)#+SfpovS}) zX;|ggICdVf<)BxGvR4ehf_gdGxFvzNSX=eD0C-QZg;45nVkN7p2(?V@@k&+5k?peH zA}S&V_yZHDIcAwK%cFJV;3J)E(QFoh0c~_bDEY$}U8p(}fppzU{sGVYJ9u;Juj(8K zllgG{ET@X8&+7;$t;bt(+O*U_vWj_M$ff2}b(LmXH3Zy(1Hwz2E61bV2GqWbByo~8|0J3qO z!HBvklSO(+DKT`E9Y6*fs0j(K)pp1%HV2#*a0eH}unB8dzf~@-7$jBmd_JQMx`sE} zE3e>k2&nKvV(HOK0t(p;7(xN$RVD%qs)v}o8-+;Isz&QjB&6E1Nz;gFq%03EI&Qoo zbIl&FQhdXts~xskX)A6sF8bqUeq=0kAX_yT&8QhaMs$>ivmdrxGw8#rvU!A2*ar9jssu(dc(*w` zuW(IweahuJPM4edq}h`?g$_BgsQ8co!OH0XPri}?M_}-} zY=ZAh$nb$ZjZs%<=JEFW#s-fgf?K@;Al6}lG*(l63RQJnicX5W>S808XMjC5^Qx;s z=&%$zf;*Gs$cEYLmpYXZGH!wy!SC0S+8P*d*$ptOLKU(NcLqBkLu){z;ER!Gx(;F) zu#aw>L!cK;6Y67xB_DGgikS#bJf4-M7f2AR$eG1hh;pFmhxRcg?f zLWKpjic~$mY-x1U(7hB~4U*E<8l{jyKu=tgHjfY)=^>Co1P&S}88jv=B~wFG%dsFV znhh6AC|4!hI`qyUhV_k#l0jNCD&0;UI0vUTsB&Hz%G$1(|MeyXq}o5($sgO(pYRj0 zBJ!Z5DWgya%H)U8uVX8Sp+zg91xGoofik~>6Kn;lvL& ziQ1p6^5rIF87- zOnum%FE}Grzb>%`#yv7HS6&x~WN}rU^Ilr%^}Qmv5cyDtk_k#_0f<0*76C2U zGg@OPGn5G=9dU9f#VgbgN4S!<=rQG%*!7CtuC>$pgsTjvU@{^sdXpYPT(vIsFuPP9 zW9-aLT4Kq_aRO9_WbJB2?CTCwaI-cKP|V}G$jwv!dusB@xAMpTSp=mJSX3F-FcPs; z4KL>=uy8EBc>Sa-#XyDfP;(Mo;m)e%K%v!Az?l&Nh&KsabcPu%k5Y}kR~{BR6T>so(l1@qN#Bh-WNV)K+0n;Q#SMs&d>+7{XyTZ5BY0o+(z zy#W?VQDho8R8>;lf{V)D3d@1is5#PS49YVtW0P@ybec8?{M#1rglT&8Bmc*{W2iJC zh~PgZEBTh?KnA*X8!hre-OC~Qh1N==MNSzegWd!-6&%<>aNR8nN&$o7g^O{m7B8Vx z{G-Xsll-)KgsP3MYsPO@R$mt~|NBzs>*D?14)oc0l5sXF{{P>@{9}6t zIO>DA&Aumx!IfN}#-lGh_7xk=1U5cN>vMGiLJIIH&h%goy$GzpNDrfQZ*KkVum2w( z`oBiw6KfXECc)zWIn-mH_{VWreYJ50o1K0(x?%kD772%JPWeI0!*J-7dEjQZOz1)J z=!=hi!$u#4^{eA2`{?-Z9_aN)<6yElOqi@JZ~o|*n=d^!?BAy9aYz2Rp&p%W!Goq5 zobFps)+a);qR0Kox`V@J0|{7WYN;MP`I@tFLK{;AA7Ww3pli!N{aoJyDq;CgKUVdi zxy_S*YW?aH5BP6W@T4dC=nP0pePN8(t$?0*wwq`1#37$B2mgAnjDtoS9fvli`>?uY z>#?`kvi|xlo8P~Az|F^f=&{SW{^E_P-FO1mAK7?b4_o&&dHgGX|408nChW=PWIfEk zp*zgP^I~XN1n~9j$ybucePFXE&*OWK9P+p~589l7NBiR(g8su>TW8|Qk9_?qbRmKU zNa)`>AU*L8_}i@in}7KGOZ0CZem&fOzT{t;*8fb`f2QkSnt^}m!~eOv{xe3T5$MsNzrhkX-y@U>lJ3bSB+}dLie%ffxxY59IYm>&>G!CHYC)$~q^CLDp zP#(GN&$v!`4%>0-pKQFf^-n6ZMRmG50@8vl8~+jc14-zfy`_*l5HID)BhlJ4SZrjT z&<%65wI!Gl<{K)2i9NB*{p$zipvfJM19p=uKx1^^g!aR*{a%i;mO0V?&w+)FY-+K! z{Uj8ogVur2rWE>bwjK-0;7d3AW3y(uVfp{vKnFA@eH+w95-A8V`@ zO=y6z*m$S8`Oap4%Z+ZAv&Z%MWNYw8*k)tT!{8ru6M}$YHoJ8`Hf4|Ngt>Y4Tjz|L zcnfQWLJO{iRkw^Ol<(Nu9y1SuhsSzg(#N0FyFRcFe(oJ_;-lAW8QtN3@%A6=vd=#I z96sLt6+54|?U(M|`$+AM?|uB-+kSY*KJVM{9VeZ2puFL&!BHw`QMwq z@lV;IZ+zp$?jQW}`QLw#xc8yim;d_6-M>Ed=RZ6F{mg4`f9Q`7Im?UR{KGqcb=?D( zU3ukSe*NovmaB(v{Oe(Fyz*DOy(;;_51$+KzV`JO-2KPjedieTjoIZt`qDKYcOQOe z`1^~~@89{S%U<)yON<|1|B;{n@pr4AeSXKE&R(#`TW4VAe{uIa@65m8r&phTY5uftU31grAFlk*vHniizixV)AtuX1?p%J)yY^Lo3SKu3 zPCKpE`^@$izxSP2B$uBP9e>LEK7RWnvn_9WvGaN3#y@$i_j+w-Hz+2Z%F(ciq~-qYT6>FyUD^FLQ!y4NX3Km6l|AKJ3x&DfUnlUu*=TBCUK zmA9Ofe&t!a-ha(c7GFQ~ymw!5-$}Qge)#I%k9_@;KS+MO(_Ypdcl}|vGcMXjOO;Q4 z_8q_e>ZiYU^bhVk_c_a}-t+03o_@k@U%%w5caPXZFZi!_-0;HTomai|Y1Grt-~E2( z^yB)aA04>kdynW{{GQ7$J?|OMzw1@|2;YCoe|_uO$A9I6XI`+!nTmfLDO?b9JN)GL z^`E?TuQT8E!fk%`>1%>7op@1xJCA-v?nN)UNxu2v({_L4=-0pV;BBwlW3Ts5j~vf- z|JHdoY}pyP<5w?x%h~=d&)#9zZQpgqme*BQ2k5Jv5C7V;CdVAJ-S?kMT>S~`?O#6f zMb~UsIr*#a&t7%wZ}(SsdYgORNjp%dy;`ztSzJLf#@!GoXmxt&g2 zZTsAv9zyTG_NWtI_^JE9)jD_hjMqxi1s9sn<4&-4A5jn9eBdeBQ=)C(_3VqDf8cp1 zjpx*}zyE^QzLWm;r~MsH-S&YOQP>aM{a>=*D{npM<@Gn5zU}*3PyNer>46tOE4f|e z;R~O0^nS?6mmYX@(b{vzyFPhpdhXS4zbzY@%PrP3&$;iVx9mXX_I~<%ULsw=-GT3L z_5EMcf7*WJMRy;3)x&2uUvkGz?|ACH!NJ7$&`A>vZ=I zl)vz z`@t`CIv>A@dE-^#?v_H`bjAD>=lnA+eQExionL+aqR)+5d!2Rhg-0Lb@E;j2uB%96kny22HIc!6q4d-WHH zOAg)hxP#Bek3DH}>%s5(v-0XUGrzk1(yyPh>$@ISUu|^p*M0it{oi>0)4ur0TbEzi z{koTbfV@$==-q$WW8WLz^9KD@A&Q(wUh|A?w)^D{cYJF5OY!I5bFO&N-rrRI^zNTt z__;6t^yAwcJNoja+f;7<*5wa!-51|^*(cOzedwUO$)|k#Lo?>4Gw%HE&$b--$Vbm- zUiiQ>$E(IYr{25maQ|mqb;~u4JHH9l@ZN!+aLhA*y3dK)g`d9g;_G_eD|fm5nZ)k5 zop#VA*Bumnf6Kx8?)?ML+wCQ%zyCt&XBzRH1NJOEV|(kK+rPHkn|HpJ_|fqXy!GaM zWBlPypK#-QPu}th^~$S%vd@)=9(~0%qx`;azxK72>D7mBcl&=AulnW9pFQys{Y>{M znX}7T4_xHOX9*|QcKPNHyv~8!zmNRy3rmgV!FSTTJ^$PjUd6oqH>EdHf4$^~UpXrH zap$(}ett$d+5gx(4rG5a|MEFMTYUZxi2BzfzWA5F+;YPC7v0UB@T=f!?Coa-PZ@vi z&)@po8_qfKo(JFa-s>OGcRK5>?eE=l?y);jufE`h_wDed{C00Wde7qy8t-%88}3sp zAASCIXZ-TwqxLyOCCte-;b&j^&~YbSf96%Y?)1S6c08o}6JclJg`eMHy4^k7{c!SP z^WxVYRo$}9<%d02xLAM3)w}%_Z{+rZ+#^Eg}is~oo9csbOFgY;O7^+*N%_h>2=3hp|`^aUV0mpvwb%Hl`DpCzYzQ5et-V*1@}efXTHBW!n*sO zx9xG^d|CNYdUN!={;Ab(pL%V67wzI3Z@u%bufFf|2Y>MQXLq+>o^)X0_}fl=`OCg_ z%#JDhp<{>oU8~RBy6-tZQg?r~*m_e1#diA2foFc^)LV}I-IqRCdWM$!Zz=iTEk~aJ z;af-Fx&2FzG_R8Wch_g^b@4fW+B1GnK05c7ukLWgai>&1aQTIgTyEcYPUAy~{NCGa z?`UI>TVHtTT_?R=_|kh$J^Kjf)DvF&(tA!|FMQg$m((vksuFp?`Qx0!E3k^-@f+^Z@J*&6Lxv!4?cg>j^AA{2XFgg^Y8;Y$K06y?Az7p z&rbgF{`>XrdBZlN9iFk{FV26{mzT%9W&82BFIYbK;2&<-=Ym&!?v6taIPoXnQtvpd z_t*FS>Zp64zPRjx6W+Sit{8ptx8x0P7W{o*`pV?ZpF5Q-Q6zy>hBM{@$1y-qb>J+|H5xmXCA-H7qryy8N53E2ZkmPig*fw=+Jr^9%P9 z#82+`se7I$9{1xNpG&`>Prv+^rjio&~IP<#b2F& z-Ej{*JLS>&^Y|OieC03hsr>fV3$L)QKj!S?_xniRdgQBz|04axkM4}V{`{NoIqK_u zb>DaHc*PCMTfVo;XTG@S+26T;9c=n!GenZCk>9d~t(bI0b_T2Yf z`|ju5QN8^dlfUfcM_BGXKU}TarHG6Wm9Jt3pZ@QRH{ye*)w7Bkl z_Y6{c)Y26v?vp+1s+T?dy7+bH|K?9W|I9CbbHZ(fufPBF z?Jqm_&u`wgcjQ~WpMGaI>4@sF9~^%0!OMSl?;*GD&3*UMAMAJUgRf{l?-zS%H-7Q8 z{By|Des}nlxwn7fkhAd5leZ{OJO4Gy7fGMHy^@&Vq}H=lLP8(#K>@GSSPo6i5ufrp$% z+9)ACYFZv~VbAH#IkK1+8`0Q~nDqVXy{=!>dvC|(~-+9~gz0dy2zQ?}m z=?@%oD+Z0{`6C(G#z;XTLk+_><#LwP*5$-Vc~Rf9&(WJ_h5(yOm$Q=lr7#5I#8V~=7Ke= zP5p2hY~(cL<1!RTDhSB7M0A}r9D!D(TB}78Tl0p;AFT8DDLC6 zn8Wu5uU$jfe_!mG!y}8j<8y=HfR`S+AKx8V-PiOFc*Xxm+k3}T^~aCnHlflYGBZLM z_qy&y_9}!*DrE1yC6O5osVI9Zsg#zoDTK1gh{~v}WF)e`uh;8b_5OT+pWoy0`{#Fh zIQN{_>$%r!oYTDurb=q!s{79vy4cIwIH-&6byPDsD5@Z3;eHfn!e?M6qJ$ICGA7vg zdRQ=O;Jo-G4v0#ch{}qCFV#7Y7qSvlI1qAl%>&fpoJ#MJ&swSx=4_@jJcL(29 zVSLcQ$k0TB#4j%-Me?$CIi}-jt8L4Fz)+KP+};YucvA0_rjDJWo4PSU|Ae=s0iU$s-zQ?HaokJ7WxwdD10tSg>Y|p8((=CG7c0d{_7aK$r**unjXWgX z_Mdh!cQX{#7iM%Z5g_V;m#NJ4JS9!NNeB*}WC@mM{`a6kOJ#BhXrT=O z5GH7YK>|KP5f< zPq0A*GAA`JTli>(ye~k$6IKDc9=5#y-6g;g#U(`~41BG8;A3O3qjA>V%Nefg|8f1# zCW(rc0k0i=ge#${E1?H}!reteO@$XH4!%6^D7i=fs4mz>F;L<4@$$8mFwg~Gjsifz z_v4`l(|JblaRn(IX)`$g0(v)jZNlInx1`b#}v=hg6smA6@B_r zzL55dB6Hu}2ZbLbnJBJxWwvNX*Rt-~X>|uI6c~A_wo%~EJrWl?$VWdA`&Q?PasQ^3 ztLbL+Hyq3N9(h+KIlX>atb1%Tg)+Mgz3{CE$DV-(^46FkU+u`dq|5O*Kby#xeC&H~VcF{`)d#Kit3lTgJS$_wKvAW5X-` zJ0Bi<=R~PICRC~-B2m}7)xJb=pHA2>DvEla4+qaIZP{~cGhN4Bfe$5`<;I3?RsG+% z6NL#Uq6I}J9^Rr0msArfxHYyb@s4p$oZ}wK#L8c(GPxhE{BMS9WW9c^8qCvn@ZwTI zO6Aeq&&?fijm7CID@^XLN+YKf7(@J73!0W*y=)hwh%TV1n*DB)@_v~@a;Ac-;!ktX zpWJgIq}@K26&1gK|5kLm+h2F0m9&(xrBZpStfkJUKV{a!^Bq3v>AR4maiN084~XMW zjnj@iu8C0M8Zzhl^Xr_yy~z7p0e4xi*H_;=^2l&-vP-m+&bN@&z?9wcN@##`zs1(n(1R>g!7Nm7R0x;o8+f_ChEP4(S+647N}-TP zFP6#W6UzOB+l8vy;^1!T($^O}SfazPzuq)`$b&m@)8kMRi|t3kqb%s;X$yE6_Np*n zVlqy5x0?c~|zJ z!}jE>@7g$C?3&bwYBL?RoFfF#rm5PucGJ+kjvvrq;6J?SDubx1Le^Eu!#!GmE_p0YTRho!pDYoYJ61v zcxWlwCyAgNpqG1-tA6b(jTfJh&QIR0u?shp1kHuM@K?##qrMEvA4o? z!fYZPw@!f{MOMC+`Tgkryva_!@eAYZ>ljC%VID7g`(Bk~iOdaC(I;gQs%iAS%DGM-+rseZoc)y+PiKF$}#j!b<4AMJXH zeHSdwzxL~D{w&_6XaB19=dDlt=PQC<1YKMWS-m&Em9uEe%`Hw*lu<35Ygdx2ai@0? zBhud5ak_HFabAl#$hnWxTy|5cSDIYv7MV|;xpG(2-lx*1>dUnh$rRlweo^$pw|Gq3 zb6076shgj|#8RW~K>XCpcdq@D6;;Q1cM&$tGrE;Z_2=|E_aFLH5Wu2P!+o3EM4@6X zLLprKy}}`RiGbH;ug#na9+@SSZTg(`*?8*M)E?CmNzK#y`uC^K4ldtW^7#|JEI;o% zBR2PG;C$CYq4^E-+g}pDh;2J`>%ce**N&i(sWFKKo|#IHDu%-h4oouJr(H8oW#TrV{#7>sISE;SrJ@JwKKwY~wwpi!qoC#kKu?aF9c z+k%z6m8jKxt$fVys~_qv>|8#wUC2mCmOH9O@U3s1ZjJYl_i)s3rJjfOQNbU&iCWCs zE_(9m>W8(pKHdO7K)3I<&RHG%Shd&$!3@2udp7r&?*+zeQ=(`J2*HX|a{LMPr1w0c*}JH((7$7_ zt(-M8Qa1|j4Kj>y${ANVcVf|Ek-tz~A%E@V-uqVPYoEWHd$)CeaQ?djxlBH1ZNi~K zPS?BhK7~QPYQF{35+3W2jCP!Se&0?rBm0XH(+BbU3RVMyN(OAvKi}Sdd%vW<CmUU}Wnm5yJar<)QlS{w6?5!nziaV4hYl%fk0!cTLR+1f(KcuLp6r^&d zUQS&|vq@`DS5AMN!Ig0}V=2=yvnT6N*2`?6?D!nI9KW3JcTV1^yDM||elADu)!fy4 zZubWAj_1|fC*8mQfb+rihg1)JACBdp$!~q6{;24&*yF4M)`F`~D4zH}8GmZ=wCkD9 zvp3IWodlEPB)(tBlmWhv#m%CA>2 zRzy^+R|Zu6uJWk*@!J0N@Eh|tebuL{J8O>BG}r3X*1y$!TV1D8S6Q!6U)CVkQ2b8j z-K+P+_b(fz8w;DHn+ltW%`aO>Ek&)ettD;pZ58cG?XNr3JKlC`b-w?g_o1!Ju&by0 zO!q*KP0whrOYh7_zmLm(A^o)dS3a?PivP^{Is1#qmx2M}K>48hVB?U%P~Wii@WfZ| zuPfgozHj~>Kf*I|e-uAjHl{h&I&L!lZNg(>`A6jBw#n3=!arY3DNi*{pPv3U<2AEB z8~uy@*S$Hy+?(G=e}A5LonKzK@`wFT-lEK6-O{P0k>#^1^ec(0BC929I&1ywuIuYm z%oGa|zn1^k^(lOAMxI&ze|utg)XUKiJkR3AWMMj0KIM9rq#r@A_JZMG_;?VO%OGh_buamZ3@ZC;c98Q8fv;W&d1i$?2s_Noy z?aYgpI1Zj^+W2@mdKmbC<$x=kfP<;|-`0Of|CRW^+GA^JoFoqSZvrjIcho=(EhNOG zz*Eo7jDmt>A>?@fAL|R@<08Cpi2}48Q!D1 zNIpw9KTB_K#{dXIl#-ExjjGT%j&AngNvj;Mw4@YxeoNq$mL~B^lSr`tup@YG`hNq7 zOG**&5K^2VEeSxSiQ+OO8Cifs6er>d(hzauRW|U{)Y{hD8$EILmg9x5TJn-#rR2q4 zmE=XQLINBWEopPG$pO9y04%A%Q)oQ0iHa5uZ?5X==wbtWYGn)T1HOtIK}(q>CN#=*V9KrwI-S9FA&nv@=PLQUgIM@gH z`v;cE;^+@y%9_JnGw@czh7KUY)WDZDZ%_iB)HMK*f*Ni%U{lT&zP_-T@qeq5NRf>y ziPZ^NlJtM;#Q$#@rlLhM|N9>wfFgJq7@)i|P;ONa(y$m<%JIrd%S!Vew)N*#J?CQ! zv=D@EZ%awRGm!|-An?GUrnb>41rB%(d!<0U!ZX&Zsfo}rG$Ij#5y1h^5R6EKU_|i! z<{Lz1Wg(2LEQA4pFN@G+Wg(cXEChpB@C?D!)F7Ce8YHC#4tRzznwqdzQbmct*N!m;*zG zdchWW2bF?nND7L+K^iIruaWNX0)j!kAXf;6i3qiW*N_yZnkG1)OwgbZ4hRn~FnePb z#SDg_!XBs!JY(Wx(qa6O@E9uA3D4L8!Jv-t3`t>*#5BkH$c}?u5DeFInJU3zi2@eH=U{J>m;oud#hMY0uK?_0h zum>`OXJmPJfe8pXLfRNAJVTI;1HwQBH)0ej2rR#$Io1tzge?#Zi*=|YW)}<@o*@iW z5TI{FITjYsRj>u)0g3$!XUGpKv=Mhmcz6rJu+V@yLgT?Us3SaM@V1l!b3qY z?I9Ta!$gChSf*j*A2yl2x^IiGE{BDB9JsdhiFhgs24m# z0L)kzIoJv#1HwS1Hll>=ICzV3g!aJ{+SbT|7Vg7aUm!?HaNp; z%vEY?I5^IO1AYQ@>=L}31`aT*MaRESFi!;s9Dw2R?~{NIfG!QD1n~IxDUA*i32XoN zNkWGV7-iuBKgrx>$cKsw*7ffb{3l*;sH$S^|2|dG0npXev95og>gdqWz}o+PYM=va zg=ge<_<`L(17TON4ZFiWp^kWh8bMl99S>T-Z)ZpoHAvDVa8FVN_Eu5Bfj<uX!A z+@}FxWr!L8U4;ZXz%2><@CJ6Jrb19rQ^BrPa72v_2o)Sb1whGcz?0xCa|004gWKy;`c{FGL~&;W0AVA4rK0@5H< z)Fsui)ByBk!Ei4t3kgYsa3srtkW^#<8c+`c6QP2T0U!*Ol2w7+p>c2|X@UwM005#0FVFyp&_A6h%A&<2F1OG8bk-T(m-1v1MCLq01{dn@c})U&^XWv34wMT=)oj| z6F1NVkTIIjMFbF92o*U88cz!8iy2Q6D5pY#Zo~m8q43}aa)789Cuklu;39AbEf4OH z`ye5pwVDcqhklTRGZdf)@H0eLL*7P~$3e%Cr2v9r;e^D-R0WW*4WMITu|apkjhHMb z0*N3ZL6J}cLPLqjZX-ZDAl)H~6bKF#NgxFj7ikT^kw2g}@X!M?DkQ)k`vYxKnBQfA zE>II3Tscup%-c5EO@ls6aOuAdn1> zM1a>g5Pci%1Z01tJ4A2qCp}YrK2|WUQ4s8X{Vcu>y2}U}M z0RnIyPzAaXXo)2=KmtQQN-0E$2vh{)C3@dBvDcY zkN}Z~%9s=^R!}sALe?4cHPlWG6B=A0ouSbHS*-G6z5wV5714xEKoLM02nIxwQU|3K zpkk?lj0dHXk;X%1@c;*)!$cu-1O~w(i>xQ8slcY_fSQ66=)lM*mQV+*>7eG3t%1Oi*C7x%$Wj8rFdDFwAtwbu zfz=xT;Yo-O3ClESF04f00TJL0s01=B3R)~ZK)`?lXarq^1cx3*Q2^RueZyK+08Nym zFbiSOf`}qYsR3=l@sBo81?dU`iLhaFs1~w03?*=_sSZsC1wlzl0#yKr1`e3IfD5{Y zGC>J3`H=o7-vAa|aG?%V5>T8;%1DD8L;eE1a3DpYtO(1D()B)F6!lF`yM*@+B zhh3o35nx3}gup#2C7>U1h8n>>z#US=x&a?}1&sp&6xINeI&4FEjyQvuhaezyFn5yC zN$SX1D5EiDpt#T?FzYbM09B|4BnKox5dec0Qd0w?D{O*O19-q|U|DoPBAD4=3L&q< zVM_+G1n77Y9FnnMfijTP$vi*`OXEQwa3CJkxedSnXQ)Kw0u&`A9^m&^JQ-*jXaN-Y zKyIWfG8??w2qM58m0=_%vMafG02xr=K-r-AfViMcfvqJN&_Eg}mkKt*{sYC6kgYL4 zqw0>C5D*5!!?=bv*{BfY5f&9?U?>@FXY;1SF{=a{`yc)Iq#~TY;sZ>HZ0Y42LL_>nWzh-?) zER@iUP`Q7OgGmTFHUx1L~95)6=H2XlKA4U{Ihto^IA0-1e@cvyK z06if$a&ZJ^#8wi3AYcW<4VezR-55Dx7c>k|2P+bQ3)B-D5=p|1gZlna0p==~3WEQsM08A1be zGAC5mVH2FD0BsREd8|Q>f^I{G1R(=fEpP~jQ3o|c35sz-K?%%>X7o^ke^TH<{KH8z zKqMCwfDQl%|M`Q(h`cm`+97^02mY-rV3mi~0kH?rkyX)T7+a1(KsZc86T$fl7P^QZ zFfwL;cm`19i5Udi-~-v@u0EbYKB~hk>`QH zz~6ujHj@Vt5BLFd!2^zaP$A?vY~~^bkf1=I85Kz&D$*RHk?juk2lXA1ggFmq)Tn*{ ziXd^}%nE=2U??c$0zja(VVJ;J2fhGkfcZ2`qa6FYpiZGB#Pm!xaaJPZV`X2CPt{86CzAngxualK)0m=mEqV zO;I5^;3?!nct)1TG={uTxWcFh;vqL;lSE`Pkc2>ZAQw^-#ws>ItK$GY7&u5Hs5N*N z1PB4AVVXm8p}~tt9xQ<*XioCC&ZF4_6b(a$;Qwv=qJ|NN5dw6Fo`$6Wi!L;#!UTq#VA%o(;0IF}oUmy)^b3py zY!QlC6I%+Bm%d;w3XsrPjv@f#y^$_RNhBEzS~#jA9w1uaz7AH~fbh`Dq`q26S6&8s9F$DpY9@~OKQ#4R8Hu4hM1#pLYV|5)4SIBuV<3NNVnPBNa zj)SXA*o%$$Kr&!0fR1(tKx9FmLT|z;5%4s5h(zRopEqodxeZGqG(cmO8ym5~B1#3D zg2BRs`2y7#n0|jlYhxw9F+fB2kmrzyuQX5y9N2OXnhFajuvmsruq+K0*n zMjO;1vSG0WILKX;MH`yKnnHs6DNq%}9Yr9lbf5*s$wmqT?pV;mp%2>k@6HVvsGw~( z##$r=N+Xns;0h7}=xDNy)pBTURL`(X1J_tl1SUkwB9vSxw806Q7;y)ppn67La$^G| zOdW^@#7Cl&LzrAd5F7G*9_~^?zVLv%c32dk`U+csOj!KGc^B%1$Y?$WxI=$S!8r}) z5QqR;P-tK?ZLpTuus@WOpt|8B6toycI23{_U|EPx&I90CtZ2clC}?qLW=I^S0Gz%+ z?!Y{#3DXDSZ#V~AhM`i0?cV@*f&*FukO1(|@@Rf8g~Ao%fC3hPK}RClz%`uhpnIS- z6du|gvpgh-9D~e)$Y7Nh5`g6je1gp%zz-!5CM()}h2;X81Mx!xG&UTQ`G9c*8`U5Y zcnwKGR!Ddt13*Wq0svv50+fMO&;kIC^FTYYVUbL*9!cZjAMy=g2hg#ULB@lzghd_< zSZHD7VI(f%3zovNa0mlEU@n5n1s3a2251tpU?8^v5=atM7=gP`{e!gzrVIFk#hru( zDYRlCFR>wi;1{!#`=Vdy$+W`|4$$Rak_<|N2}5K2IJ z1O{m#4j7M(3?v&AA#O-X-a*EOgSSu(Cm@4GuCy)fB3#k2&0dySNEdMt+z}$wR0lA>DoLp^? zbPyR31ZN;KdFulR4xIv>gpv}QKfzffv_13$e0K2H_HgV$Llic*0k_y<9tJH$0f68@ zxu*v59F%KRECCBh63aX+gn;lcb739<{zxxa>EY-ChfeSZM2CYV%yqPf1Stb4vGD+! z4#XO=Eug$1AOX!^F@hUwI;;{PH9<6>;6hJ@;3fD6V~UK1#TQ6PNEQ#n83igtfzJi7 zJD>`D(hYB6aH8D^7^cYP1ZW^Ey2)+@vOq#=F!pieXoTn}j|j*pPznGBd;mvsA+Kd%^#V`A$XN!j z|DuzV7S6XIS0Fa1g|K{L(>0_!#ud5_pn^`oADVMxkU&KYO%#C35FuC((Igh2qv8Z= z36|Z`z+`0eVIBq{`fthvYYxyH8X7FYrQys9{5B-`tqs5#@r2gih&wo@lOqw3B!F)w z#&}{daHxdn>TnGLxkI!Kp&B&nweaa)un+K06Ins2#g5}y^gX93tWH< zT#6zC^*|u9RO&>udjL8SS3rAX<_e!c!Kg-$Q4m?=9T*irLcoA*R>&P#5k5e}q<{v* zYd~P|a2d@pp&>E*AjLpPZHP}qa~tSK1dfd)*z5sSbs#p_27p^F;2&m0xC{X7fZp&S z2r}Kqz>5{@jT9w;FhYDVn}8WM2o<=7Q-|{|6hzQ;U_6C$7(kN*q8Jtil1~Txm zDl#hoh3}KWx3d0U-X;6be=*zmHW)lX>OWrx1Kv!8Z>oX+Ch7%VskBA3{8N9F+b7Hx zJrfR8$|zh?vT*Qv)==CadoMDU7#A#4*U3$_%!%_}A}f?cATYt#~cGe`+N7wUFZ3s?EVi++rqX0u^-K*0|dJ zJzu?@n0F{Mt9(1LP(8AEF76}K9$Al9b+jgO@@AZb-%Fg7!MqHgXJiA#ud`JoevXdW z?-Z2Rk+QAu%RS=z&E;Dij|ay(>t0L^+jr-Cnz1-(yYd#GtRHv8bjBIR6f)CDFy%>$RWo!iSiDQ}v_cC22es`)0-m#y1w zFis(K;ki6FOB7x6=_-L+4N4+12{c#lyu8~rsmtd3fyuvjE5pD(ihcglZdGAs1&4Of z1oVPA!#;?dC*>;-qK#66UQ^ z6hlR}QK1)hhunYI@k^R!WzO92yOG6FbMDvGoaM4^OcM>?$10CRH(JEYB!0n_R)sPv zm*qQld}XMT%&4Jopk`g~%BNt7j>{!98cd5XF|MUm1%D{Z$Q81{nNdhq+I0mVecu#M z{bz-#nA_P*E8roWe~{K=E#q+RRLg{s;E;U+yuGwb-$;fQl-(|rH(Pb`u2Eb(Lfy=7 zm{(uTd`ZGd;d4folsRph{e=_M&wh_M6&W=zJd{%YDKXL_dj4$YsIP02Pn_aU`{a4M z=VdHmj#Y{!NAN)wpBMXhWl5}R6x?e;q4(;)c`4p)m~;XEpIu^hCHnA?so@!d>It^$ z`d}YTzty*2Hnm*7j31VL5n7W_=#tV^SzqlySM&ac(3tGqC#-{Wdf!`_J6^>6qV`QH zXmVnD#HZ`5={DH%#5TTTma6+81!LGliXVO&AEr~zhBZn#uyd}RIj(FVt25D5*)^IX zMB`ZWnWFvR!>?D`d#Z+MwtI|>GP?WM!v%`)x^KV3w)*q3P|)$jT=*4`sWtTLd>IYF zpg*E-s;f+}DkWbx()sgRgz5e;eWig{!IV2r7_(W-5`HtExjFlWa?UhqQ&nO?h zu6E(*{iXnB*4z@x8I{#TMceRQ!w$@5N2u5z;C$y#eBbJ1y65_~ESB|It?Li@6fQLB z*dJbhFEbWA&hYwRQm_I4*IwM#Z491;l>M)Q33b7h-#uvi#qY?jxLt3V3jZ5^*@x7N@AH5bl_f{!jn5TRuqis(GxLIEgppJa8-p0A^ z5CfWV+IDM-(VIYR0_6vRjw{Izz^Xo@ny08eD$6F z%S^}himn>*gs#UXd`|WJ%&L2UI$0m`LN*28epBVQ z^ujY)Xln@n9QDW;eLZPuHYF(%K`p z+BMV2+OIr|JEMNzdtkY`NMdL8d{!V>C)LlnCB-l)nKLnZw_$bZg)yO<%RC9UA2r?N zJ|^68q`YV4`n=O@!Me!Mp8G$Q4hO6+bbng!)++p*FCu-y*iDnkDgVlmd%3|(GgchU z$Hez#+1&`SH}1^3qM2ir+5dgLgIP}UrR*loiFF+DlSG8-=+@s>lrLA$OUiSOj26*< zoO0(~Tgy4X@i9*F1l1?=CtKFJo#tM+DMzijtuCHtanRtZ$0a_c_Yv|xYwu%Cw90m( z^f1oJ@L*LslCIynv*E|mrH1oaJExb!#5XU6M=vhfRVZePryMC)jk;+E`` zqS7?4n~pSRk_;;kF+5s%v)1WItU0+WW!hkmdNmW*ZkEfcN1qG6+WDxr1dg5O11(qj|;k9>Len zxc2Q_uixysa<>8>gRnmO+L!%`!o-R>O2g*Vlys`-Bd)p=s^UXc-9i8df>zM@C z@h|kZGPSpiyk8t7+|F%bluqY4m8{dGF_j#?EzkS#jt?9TJ*sq)3k-EC)wbRe7p#0_ z@)!#<9FxDUDYyNqWQp@=eOgfD`FVfgu;qDq;qr)#2G38G_~LYKOG>q%=NHcv5!=2^ zDIT0L@sekqUAWRO5n=PmokVYW&`Vb7{+ad51$BMJ!#iuNf~mZ<&23I+XKHb7P5HI^ z{=^4bP2cT4$+aB2f9+9@6Nx)@pxbZDs5-ZB)$N`5V%f&GeisegD4dPxS{2;WjVAFy zR+CO9UOt1o4Y|GF?9{r2;uL&ko?krCx3lm}ZV3Ga8Sn5jI$K_q-YHFvDilreP2cm= zTZ_3esLk+9>=GePDj{y7`V6&Da{pCAjqK*voMI!++RqOyw>z2eR4wiF@wzG8sl|~m zTE)HG-5W@F*pbo@sC7F}>Ef9NXQlx|sPhlA z^zBx=u7{KT|+WQKGNNY?ELIN~Fp3$CgZ zXluVI4CLpN5ISb5kh}INTQlYNpSKCmyaNg%7;sk_qqWM2Kx<|+V5_>|-T%LEga6J;*DpMKez4O2V7sver z%REO+r*F_ya~|tY*nPP%j`z_pi$mBCy1Bef9~L!+5)(yjVg|RGHF77skJGe&fBSVu zT!6=7(1qgK^x?$Q&yMqnxV^gk(lCeTm|8{H++in$P8pq;yqbWLJ3CXv21s8V7gOzI6H%fs64E9GWd=xWA>k&2xETGYxgb5fi<$)lZlbzU~R#9C$m` z`{}NC7B@4>Dpi-n51G)sSGuq;dEwP|U40)fGpf+$jHXt~m6&gZKX<;&7RkiV?~FJb z`#je0rzD~=riL)2_L&M zn?zH?erC&Uxpc=DgI(_ozmg}W@|+ZSq4-$d$8ONG&;8!Ri`0K|JN>1SWD-{gjDc)`S9+)amE6mgC z1sXKz^vy8Jn?3%p;C%4p*7V*NMP+-5V|`*xFXK`eTQ5ykUEURL7Nz48t772xQ@ct{ zCXDiae3<~Zrx5j{@6$9)b=!s2eZ{t>h}}ymvQbLvBHUwo@`}>)yTYpx+~ZA8c&C@^ zk_R)A37dU{#gxQYnC)L`(BrqaA1%3!U)^o7$8ANEF7?T$UHdZa4ljx1W#I}l^t;bc zPQ>n5QX{&nu?jg|9^!Q3h&`{Fc>h$`3K$`mWBsbWJ!qx*(;l*Ui)58BiMMC&$EVGM zn>0sSa~!+_e)P0@`~SFk`0I%tFRHTFy^(7ZFPH_AU5%Iy#p76p?yIG<_iEl#Fo{_Z zc6k{UQ_G%gRjssBvxLVrAxFv9hONWlMFrzYhCN*0-|Ivh2HdiE7ytDnt!z)E7qzlz zjB)j(x)J;4l*tyO*(+tyAEi{&o}cb4qdEDSGM&|LJi7YZ?ar^td{sB@DIaaf^SrCc zAMo7JiCU~d_Q}s%AKOx0{4~m112|&m%-pmWyDnvm4;3nU)ZOh&d>C^g@mOklos zBZ&(`GEwM{rZ-;}9KGE68lc8_&di5_+->Xes+{_DGokU1k=Pll-K0H;? zl$;#PdHq!Ohfz@qiC&h`yM&2e9vP|}%Z=9K~s!wwba7wEAd=K%mDf_He{2c3lGzb%DMUQgt z?`Kp!s3QMVqRZ|BFCAttGgXPnNoZw_lDNurOX!lv$E>h>S3>55xVtu zN-3!Dk3MZG@Y8wBi_7}cHr{fvBtE4&ddQU}Lv6=pgY#}4LsK_yRekE3GkZBO@hjNi zy_|-Ap11ot{}@x5I`x~oT004{$((A=Y+MZWHr(~!IV9b04{{%n_u5=>@rG%6ZJmY# z{&&}8ePZ_si)HipL=_Ke*NG^L`xbV0Y zE>)GjWRHi(lTJ6{kL!!|7+*faeB}0=703P^(O>k!td8*C7O>`Hbez}kYF(er&k8>7 zS;V`hRlB%v*GO=XXv`O$n&Og|MeI&aCV6@b95DnRp^<$CqHWr{7%AP8!!(5g#?Ren zSA2A{I}dlHqW6lMT2p#-NAZUP1XJtU19npE2Hx#YgXeu`*)zmn5ty;2rSJO3TfYD7>7i|Xemq|i&gg{*af@5e-8Y=Mk@|hlH~$*7 z!sEXjTyFWvM0e~z^KIs!@~FQ3Z@%?IKX0s!2`cdTnOGHyOzpVXWodV9(WlPkY2 z);py#nV3Fjw%TADxX79;U?0`lxUMoSJnMS+`lVTM?sK>be2DVBh5KtSDk(FVOMNWQ zRK?C$wRmUSc0WqeE4w+&ue@UgXLKm(<||9>Ci!p=fvmH8@*5sZ_HpR5AMD-f`=X{q z9Nn6Zypy7n4n3%)q!7HerD?g=)9ZdP?xW*GH}04lyPpTecmE=p@Ke%% z-h48Xf6C2%s>?y}$6P^0ws+;1;*6plb9u_A+4zQ-y>5yBEDiCx!2d1ScX+r}{+qyS zfgQK=8r*VXb%KLxqX*0Luis)e}ao%W0R8RFmdU6;BJpIu@1+U%=9Lwea2 z&Q}}A%9R?g|K(x9)5tql1ljB|(rjCQ7Dew+Fcq8>;Ez0Dx0=bv!4uee=;+ZMA#^-W zOx$y|9Pc%I^lx?_x$Kzn%(jBv;qlp1lpfQ2+^*H)iKmuqIczD*HTef;^b4=s{pz=- zD2Sg+Zw)!wFjUM`uEWM)@M4q6G48n(Hl1AhOZ|>c>q9d^e=cZV_FJk;PM6UsW!B=z zh^w%?x2@qG{ZsYxL^*-+mK^QuNiQ|l!+*|C$xXNa;r}(i>LqV*+0u;r)jS)UL+IV` z<_w|9pt$rGcx(0J6bS?q>yqjHQaUVn zcxn`vVqtz*cdfPfj?&F5NoN=sGdG#!95Nu53&i@j?V$?x`jbkz^Veq5jK^}O?u)BY z!_v~U&l~tw9p}F|Ft2q*;$24)_eI>1BZi;ovgt`SkTU?7WR!f);V^mfuKJ^ld>ex}D~f&E)t*FYvvPhd zQT*28ukCC>KgvBiht1Zg3UsPOm9Fcc=ho=yFTPD%=*d=$ytyI}gD)j9hFP(3k<7K?Y9qeuADWE+i zmUy@J=IA}TaQ{B{yu0s8iLVX#Sk(ezCFjqHW+gRkY3yO%Ov5lBO&cG7R@gtlin7Pz zb%%Uvx0v#y??iT!^E+uHT*|@(*XCWWX#}~=5!#;BcHdtt!$pQyTg3U5oOsvw{AP4_ zbb5B%5i#f7pwjK#`w}$zDMu!LJv+J@nowN3V`@vv?&-8UfX^kj^Znwh-OAO$mmRNM2se*As+!xHGWxK<&`)}xLpWeHnvXVE z2cNlp+xf1k!h0!?U)K?-Q`9pfA}0qW#-tt#xEPjGShXrg1n!>kUa%e^y~*43LyznC zCSR%;Itp#cv`~KAx6Gxqn>r&aFK&Cm#6hEWSg^+tmp?jj*@4C8s{YZr=^-5W1P8UFmmuF>o2r6$Jq z!!(o<`1fPp)XJ;{4RudX$7~J~I4JW%Y(~h^ryZ8KlF@95*-yIKs5D1M%` zk@$?i<=B5NZ=`J}MZOzXH2xO*XZv%vI(CR;RI^z3tUlW^z9{G;FaDGA3u(Ydx+qgy zMDos4>1BmWv>nA~*J=DG+1Y-+J144Iw7GFbaN6>;-uqORU{ZTrj5PfnKjI|*x>VzF z^`8e$Y*Nv@^qW8OV&~GV{j$PU@f0JTO4FqQQ4g!oOOw5e?9_iWFHr+>Sq&IAC+_8yy(fe{))J zaNYfyZABXy9OPwG#<%&`rhW9XS))z5P-DANoe!TYj|%PqQ>pCr9Lsjb`%>>$Xx0 zcbM5N^Cyp4P+vM7lB-X(dosxGiD2Z6N8RWE&4)9e?-J!@yMnh*Zdu$S84>S4zyBxQ zr-r_kXU4n7LuOvs`bpg!+B4MuBFOmd83)mXrzva^x6B)g-X}=jiQ;~6w3{RVv*v6Z^`_I z`p^7)f+@U63+xYTNrQP$m=;tbXB8&=Lj6a5Nn7=2s~zR%;=hGieI*SK(ML&c*<9#f zGyn5F{{UxDGqF~;DorW6it|E5N9RlMOY)KWr{+fo+#mbgH*GVUUM_v_wa1|%VWg45 ze4#zgkAv=#)JxrOiPLX)WF#F6a%4Sy%!T0nz?@=?qjPDY*@@I9UN;!P{IQ^v2UqKO zemXIalvdta#*-Lg6kJ$UJa}3m`oa}C$}hLXs$3SwZRiJ+3@&}x)%U3Ci2l<*KW)`b zxvLiCgs7?~J5|1x66Z;hO|5#)N>e|KI)ofp<@qDOg-uWm=X+V+dpQ5q>!LnI#{tE_ zr$;9hxIfcfo_PIqvb8@XcTL&a`lidpT(pNw*v>; zy{%%?#{;;79VQM_@;kZ}hgdbLFu1C^@=gV5HnuEL+RPrd8(ry{ZSu->4}CdUe4A^O zSxi!O;#BUs_wKAG-=wv?qZ%b0ag>CtD<4*q9A0hSecjfK(Z0x*m{&WinFMB01?Jn& z#CF>nT;_hlHB7skt1;KrbxO9eevVXh*yiJ=;3u-111jS7OEwCK_tK2hZeKcXxBRh7 zF~zk?-}$UL>&#&nrdO9^-%Go(gsKR#y;Z}Fo$L0SZIifu?89lJkz+x=(fF7r=^`Q6!2`nd)BJ)HHVmC-Pnw%!|jn~uSXiV6OTSMP14x3uxROJri=vn+Lk^T{7h?Kn!~dQT!*w-po%u07_cJesioe#r(i8K0!G%+axvHih%BazbKP00sRD#yjg0{HwbwJ=I zkAV^~A*tbzrM|T_$L0Nx*<1#n`Y_n*UNqb*v;1KE*;v5c+aKH&l$*SW6ladouD+$*R{n+wchNI`*jc}Fk60M@c6=p6a8gHz%{qVV~Pv~tK z`R=e)yb8+HqS)WFCVnl~P_Lvh?xisEo70?XbtSiY8l6}lI}5(D*|w08rrJyE5z`l( z%|#<){+Q7FF6i4_F?g)iG*s^xC~SW|gm$QTAXdZE}8Rx*6Z< z++%vU$UXY>E2EF+*!v5o=2RRXX0l$XQa<$nFFsK-vSQ;v6SikWSgQZ)B;(}A$ZoU9 z6>*2S{SQNPp0}jFeX3>lz}=!`Bvqb*lF&?5#{5v-#SF^6U z8&!Mi2rASGcw7uoYxewfEbg`lMa_Nw=SS97I)}&J6nP$I)6G55uP-KfQ2(-Eu_~*< zcMD;QeVrOb<)T*I@wL$=U2pFCTGt1?RIkQU$a%lmTlA*bUw+_X%Ng}22lR3%?cV2c zJ(!QG`*HS5VOZ*S#i^!X4|Y*)LD`QKkNqRJ2L#S+@~KS`QNX$QR;Z1!G;N9BvX7^! z%snNHz1L(VtD=zh?5+Ga#hxkMZWaRm^$~Rr{WO=mW2Ls$Pi~dmO1V3Lc2=x{RO#4p z*UCL1cW=<7ZTs%)1AWw;^EONZ4GA4=99_&#=2260p$oTL?CGP@750#hyROBgZ+*me zvL|&VQm(bjbNK)T%SW%`>&nafrCj!zbr=*_ir=uY=ktp-x36uwbimIT?;vL}Pu>6S zNRuLesY^EZmq^j&;Yc3cxkW$qCrR9Q>oq>&&&I55I~(~f@CaiP*fW=Q3@!ZfPSsRH zh{`XDRa4jNa2QVgVep^6FX!xuK}Rwto9HCfqUU}5(()*-eI)K<8@!YzZ9>C)Pr0WM zj7fs*5jUd`v_=xgr0p%!b?rE{^<}f;TwA-I-m+`x)*_I;xtqJ#n|W|KAO3y^cf7eL zK#RF-!q`-`%QgTkqp~jy-Z7FfB_3*Qph>XaQrFOC>QW-YW&!r_dKhrFV;vVo_Vz!ugFGwsc z-;o>L*!!#ElQunuQm7DKI`k%8SXG5x^l6$xF!0%iKP%{(DN$Y z!xE9wbM}6z<%0zO-XO->?TW9ezs+1Z>D%0W_1oG2#X3vOCf&)H>CWv*?}Xi!sde3o z4>eq3^Y#1^BE376O6sYx2p?4&e`KwBm3#PgropSbQZ}V{*FC(F|7Vwx!ZU|{QrrhY z&X?^>Kl?_%vbHx|Ke$5kGlA#TvQxieM)k-qk+Sb40lN7;34LaV#TQ=}MTFx*e-pCj z&+Ep@+Ns?1OEuP6d`wv~bA{fKcK)hImAlamnzIk|KCE8+tx7Gy{w@CZe)b=^EpNQesoQtDI5dUp zKMOCXN^jsj6c_9jv(24T%k4w?pR3}3_D+QE?BZ`d;t*O7x@MB>XU|i zLa6dBUh=2Tl$^1mPfXQJ4YS{KhqP`ib*gvgz)jc7gWm%?uL_T(n%Oa)d_HG)J9F#$ z$JVR!b`Qhe-5cJ#oj*WIV5{w{{&-;GjYL*h+YLXQC z&Ug1Vtyl>+{$p_lmghTG-k3DEjakz^Ilc8)!-~u2hXWGD90%Km9cu3T`3{9n(LM0A z8=v1{s;AsLboO-grEG?wd-Bs?PMjSI-%EX_zKZ?wq4gI@J0G=}tgxEQUvXmZ*D^E1pa&ykw)4bEC0X~+FZ16ec$a^DTR$s6(0N?* zqF=o$X;^#E=EXOK(|5J~i+AxKRpKPZ85*-}JD2(K`LI)w(1=cP+!LCKye8SiQ)^=C z70v0Y;0KCY*1XT%eoK>V&|mGQ-ttba+(Q<}!&aQK==brED zX5uX?=88XcC)!7+K8n?Ub-?JzBs=eOI)(#HmYR+|YVpz`le~8ql7pM_3hngQD0gix z?(#6Dd!eTMbFd}h(WMR%ut)&Ez98`DJ^lR1_dM`ZQmrB1lyN(*9F-c#{TjHr#c7E5 znIpxsUk{xnDVi*)RicHPIJthPJH>y^nD*iDDy3y^K2W&oNeKV6KfhyCcz|g)T^~p5 zC_})jq%*B83(3O)PwAMajy}pcG#B{L9M-4%M~y4Dg6n}6BBskbL|)5_c8 z-tRw4MH^|EWc|eK$d)GyryJA!x@KQ{#CLYI**G6>yZvcUG_tKg#%}s+%FI!*3k=QA zZx>Wj6)p@jyDqQ(?l#|Tx29*ESesWXX>g3GimvS2k;#Jh>l7S~yYB6wr3&=V9qjgd z^Cd<6HGL5EN*kS5gUoH~0Gq_ubf5iSy^;#ws>#2UyvV)l%Cz(DAl(w`LAF1IZwYR} z(W=uE3_-dy96!bWShTAS+*Hq8lcLOMJoaao9rxGhn261RPpeLTw9~81D|x-oWLHJH zR?~v(zLhH_}&0CxsrW$+L``Bp?!t*!5u5gVM;3Q?b3VNNB2u5^Nyyp zl$H?cbA`%B%`3Kk@r!4F>FG+0RTSd&FAC9<$1S)7Q0whw#--e6WA4_6pxiy zmH7B9Z2Q2XV0l3%nbKJ`s_D#FSm}_qQgGz-lz7|9ghcgxQjEFlo5xmN9jPJRgGvtB zgFF3%e(W@6o0sGTP27~c)MJ1>7s$P29SwKfFWygt&%72A~Zf1a^gBuT9Yr7dM}mf zj*Z7og2WPjZ+qI9(rFy7xo?2VW5cjYQ*UPpR(y#DsNVWieYcKiP)c;)j8V}$TYPweq3IGtVS`x6Qu0a^hrMA&f`-CJza_B4Je-K9vcvh+O96n z6N_NTP6nxOdYA8Ny!kW@;vbUII^|BLjGFRr>wVX* z?exsaZ1lN(0-=ra`*FA3nXwkWcxS6f)gKHzyg#<{?-F>@d;c|A@$AzARx}Ys>v`r& zRo!)p_Ljw}AhYMYt(6(mFA zClUoYu-M`fgn$+{udMn)6tW)K`w!sL$gPH-S}8>LY-%|hkQ+<33gYqcetVy@$k1E> z46=M(aARC=%4cf)fcR%gHxq|P_XLCXWUnSNDV#(`c?)>>CwjFwJ11)m9QU#H5f0m4 zO?c{iz1{8_{d&3V?1TAfBP{Tp>E6P6!U%jtCs6Yq}ProKlPg!^lZ9bAiqk+n~iX`UQ@b~OneL{_5g`|GTm zz{5G-@&SX#`9Izrx0BZi5A^gN4{RnDxSWN1KB9LFeH2e7f5yFXQu<6UEjTg({N;2t z-!=L?&ZaZo2dNgy7&;m{)xhR-rRyh^g311Up3|If5YZFt(-byU)_InZOI$gP;{v=* zAaSp(vkrB)Rq>7UC>sv#_7&jCeXs4}P=%}CaCQw|Ai)jFrpukv$xw+ zPdw`Ri23GUZou_!f)^bN!F!v?ozFn9Hf_;^>pzZqH{+abaMr~3!yoz)96kTX$(K4w zv#~|n#;=`QvkkXrffrx9_QrN@U?+^6DSuVzdEU4gRR#OW*wMeg;nHSGopF2{IcN${ zqCJB5?(YS4w_|?5A6q)8Pm3iV_Y~%}A#<|U|Gv!h{7NVItu~nc=0Th~31leb-LWfj zF>j5Z(NtjJPx-2?Y1^Bv!iX;ih<>r>U8f5jsunYP6xyyvTPwT~-!9GZC=|fQA~oI| zBUqLd=q$KTeNbUH{hnE%>c|;^(mGXi*m&qNWZi;J7+DHkWMHT|k88IDLq<6^hJ&Mw@N*nK|fV>tSpX;YWvg-94r5JdY z4bEz&41PGV9{^#WTLN1UW_CTF6)BqPGmiL(=!>q8csho=CRl4`=bNzO6GE!TXT`@^v{=B=jJ(Q4^}^(3`OkZj$Y za=ufw0=PFXKVwBSTIV2hx$7NUtQGiqi;7&q#m)I;Il561G2<3O*2wTKJ(JRm(5|!D z?{&hq`)O8Nm3JT|k=0p~+stX{cyWA{v-qv95$Y`UcN-BaXl1U?c&Rq3wN>uA9`o3{ zVLKXS9fheC?3;9`W?C!dW;p!Xo{`9aDs+>PCdX4v4B2s?oZZiA*Xez6P-)5$yipuE zdbs}lcIqepz|;2t2GCsw#NUian5+3dnXigu#;Oi#zmrpXjhT(`pZUeS^ev@w^YnHq zJwFXCF52Ut81TdLRpjARg@km-6{w!utVsK9h(0^N;oY+wO}(W|cF!Cce}%!=B8~!^ zF4)xr>|f<7!w1|xZkS1P5c`;}DZoAyg4=i49`y70efIUb#I8f@&vA`NnFT_35am4* zmHb!SD@h2uOJ0e;|o(t?VYwKFT@j9PF5Z2()!nxo5pm~ z7BoY8oNG7q+1!i>vq4$q=hiK`M=b|q1le3Xc~eiVnjs-avDE^!uMM3QOW)oNyXShbAx??yM>5C)QpwO14q7R!PMM$C-~$43RouLj?n_RAi;1E`?x=`@>WQU z+P8GVND1@KoguKUIYa_)I1xb(e(0zge~iF(U3W#i&f9WU1GYZtglm_HV;B~H*A8H# zZ>VlbPJJzi6OJUAn}$y^6j}LuHjEAcj8+XtnFgkA0ujqu;u_Pze~cpGF2e-_(`-0l zr#h*+jrc<}(<-l122b4wf_V!rDF1Fm7L6lMfq~Wj;3Ayt)D^e!ETm?r&#?O#DoZC! zZo7qV;06j|L2I_#{X3=gswImq!nsj^_bw=Y-P^tO`|}Buyr1#L>asa04=FwTM~2vr zZ>NoYocZ{p`3e-_`4>~vC8#u)UGp9Xm`%IyS#-10^JA}3o{M0n!9=A_N`U|SJXk1Xz zcyYF4wt}z8mcSSm>+0Yg!^Rm{dGMJps@ZoL%exUJu!4SBJ88YR^e4aC;>;h^puYQq z*=LCy%ZCPEA&^z|qIic;L&^?Ud+-J|Md`z#Sg~>{zv*#?vN%w)y*xtA6b{1|v}qQ(her>Y}6};VdZB9&S`=O5d7wq0yorECBv~ zrQ0NH`f0qLw$-*MLHW?DEix0Y>Zeuo@CEi?`qxSsI1GhJT- z@8Ah7eZ>EH#o&$3SCB<;EW$Zc3|ZC~p}6X@=|$4pU71fnOn`Bl&VCq%$VT-jbU-GN#vMrn;&1oX2y$3}hOe-77J&M{d>=Z+` zRv5%p2XhfkqiqF07ZTd^CQbF26)Vnq+wFT04nks^Rj+_uMK#4&j9}CVBZgI%v#V}& z>^_8G*(T!k&$MEz{I6Ox6)P7ph<7ES#_KIU*``SUlTOA<|8d=gMop5W}_B=u^ck&L9TFTt9gpw`lRMm z+hPWIoHJn}hwvJV*O1hdyoPmZNE68dgAWUMgGKur=_-IF7K>Bu1CR^U4D)<>6K&B0 zt#F#q8h45EXV9N!Xpgw%cc)^>@+rTK3s>A*wP*ly*yWE&3hgOxcJymH(^wl3)u{8b zye`InHrAn!z7a?zKRU+Oi9>)kp( z9VY0mNSW~l#7lBBWgT=&nEg=&XqMLr#o9-x3=IU}P@M7LF+aEivHaGJzD82*7ZqmD z`jgQ*g#)5S;=|`zWrFBr-_Qoe9GVdMJ5nBqncQ2Rw&OiBy2o}BQ2B$=W>3D-v^{=U?t8gGl6?tQLX@JNM8+)$o9?f zwWTZhz-Ri3`!s$7Jpi$GUSQX6eukaush<9H^F`#c$8x5`1ka8bzr$DlQh=cQ#aSPz z`{<9ARN{*_8Ef=vKbR|Ds?!utydl_5VTJ7@|BwO)4JqC5uZ7`ozHPMV@}WGW7Kdg< z*^OaJqFPCmE9RmqI8`nECq`^n@5-!Uf_L|eXZE|7KP1@~J7d02h#6a@`vZ(dZ`2+H z0=TcQaBozoVRS+%u&*%v`g1(TFLz0;`Z;WVa~%F5+fq%uVK-@F{{#5;yJ=~G-Bv3x z(vwO=vi_|PBkp9Q`=hAFcT9g`ImlZ}-6Y ztgt)5>os>YbgOMOfZ~N?cvJgFS3vTfuU;+Tx0%$5(9VG^dbU=;e#=;20)jWV3PzUQ z<8iqJ<(hrO*&Rh9lE57Y4CYvn&aECSr6Kd0$^neN^S=zKEi*$K@&zW^cSb>88bnBl zG9D#Peoqe3GP%_2PG@o#?$kPvF#ZlYagEc#TcvCk_>CeDsTFX{UEZ5IAIbZv6;<9t*{>J@bwSVI+&j=>| zVJCpGW~A}vevi(ap3=8%Ro~y{?2F2jnU0>P+bFjzZ+7%o^a@%(q#r(T1o@*~@BKhcVz3bLF(g2h+o(wJ z>7$|<0`xOmA+wyEvi8x>JkD(|>&T9g3XRe6d(iYNTj^`tdC|yHSjt|@j*KB4K$E8Y z$@Z2l@=O@w4)o^SSH)E&NDTV6njd_NU?_DPfg*eDBZ9(vm_ql2MjzpJV!bc9?D;Bi zwA*lC@>P`@LuhuOz5c8RA&7458L_8Fo%|*N9~TL*4+l6RFNSioN)0$Ii5gio@Lp zw~PZiKW;5`ZL@=|b2(kzVuwmQMW`=G7I{AN*W-QsX|oLH4WECf1u&;=`=X?dx}p7E zW;1r=-i2qkFNxtAjuKZASz}g(Qmo#eO-$VRvGJY4BXd?akgESJ5+-ebj0dPYsZ z(MyA`mA@^KegP=dYPAaxI$MebkYpWLo?#J@A>fK#Dl0G{>`J)T;0=hwNjR}>C0D@y zIPm1e5$iSs<0wB1X(R|P!$#Q)IyqFxxj-txFA}={YN^g5m#7i`eRrFnb82`iqM$(V zoT1Cb=t|V_NVo2wWYs>qpbP8Rk{-8sD$a}s zQ{*4uNP6dVs|sHMlmY5IVXtsHCqUd4kiLZ*3Ic5?yqJFeoJ!f z$yITg`!1q8NSHLcGoaxLjLjGyc7_R4OZYuxAv~1)(ez%r#P?g;@_A|u5yte#KX@7| z3%#knMrY-yGg9lvio4k#yL}-dbbq9nQsQ*^EaY3lN)YHgw6;$M^r|vb<>tLn+t`pg z7~rw;N0~WO*F}+|s^PdrTcgA$;%>9`BEB#JlB?J^nSMc%L zCNHa?;rc{eED2jEQ7`zIoL!r*zr(=$r;}+SL`1(_9xKV1>^jUW&Qx)#JZ z9Wl2hXj~7Suw3qQg>tj}EPBKGr>)m!gu=cZ1Y$~YJaO|?$itsb=_3gpN8)K4O5K_NdYbCLp z&V?b(xg@#Zp%A_{th-4=Hdotu{KagRD~a1@7ZbAV59h&s6OZ;%-~N#(#?NpWkQCVpAqxBn!N6BY6qbSDcm7-A-hJ71)E`drjk=m(;4g9>aHERYB*|#_Z(UpPGmCNS%t+jCcA0=|Z=y*D z$l&=3Y0J<;K7{l_9kpMw4Sd0L%9eYnMUDCV%x`LK+3|DKk0hh9z`wUsNR3Iu8txKb<(G!{RXeWvd@Yxp*ir<3@SfMG- zJK()}OfC+TQTF)LpFiLe?AQ#TUy8*BW0z%=P+$%uZ8))^_mVR+GaLO#*kN=#f23_o zby^`eEfMzql3*0=D(L>FX^uYT+l(`IyO+D=7-INNah&MP{-=3Z&WS7x*CBNXiO4RI zFwBvI7q!HeNiB0PMaMC2Av!kQ`jQ(w)o65wRJI3YPNY0(mRz~#wT;QJt~xn1G2L4A z$q*UY%X8;=Gi=NiIbU@;3ZSUPYYN!rU+e67Ujx16*JcO2MxS=Ao*uu>6aIfy*^fn$ zT}%gw)q6&umAC&jhUF0}PEzTO4aMv?ItuuHX^?>gbNkwZdzD!q3s1UTX`c%zg^*f{ zlIV1ohqcbFHAOU=5id{rSEtxFm|fQ~NUHGdM)gvcX{ti zrycJuPP?ce6_-?bQ2?PTuZ_d7J9H%vE4;<_WCJ*H8=XgB!aUHh!iDe!7vQ4x@dfh} zg8q)cDT$w##3!n`tX=<9y)zF8N{DulmNocrSMG_Fk^M16f>GWT@LlI0ZmM%M0mYxc zWbF0HtQSs>=?}ntZqiETPaT$$eVU|;5>}_1UWMA< zGqWbk<#g+b@%ZQv(`(dELFQQ)5p|v!I5)5Zj<%|gL7_Oo+li4nK9Jq(iQ>#iO?{h? zVP_8UPTJ6n=N_#^!}%(8R4mfkJDh&NTGE{o$&|P7Dy;xag~G67p})o|I;Hv-QX7&> zZ1%s2dg7|;Ri04NwBZA2WD)6Hyk3rkqmJeONilT>)AURWrGqgynVlwtgEjeKAjx%7 zT$2CYTJfF%NLuchtlsHiFeQsT*CUF4I{Z8X*lW@ME)IPZ!7|j~ud3*1p|8^4?BswOz;VKxu63Lm|?n|B%P^+N2+1-A{?rY%| z9^Tlez*7BXx8zcwK}Sv4p{Ppe+-OY_U`zWgLAKo<0qF>!T$CzeQ9&!(N}ld?BA)tEw4<&#UbekdKT9F4&Kjfv+8noR!5o`H<@Ika#_S|QpV8O z>AbeG%5PR?McfeSq*LX%zSWHKiOG4%m=IwQw$o0w5yp9&fRRUP9#r-nY|P~xzy)+S zRaqjy@OUoO{gEQgtf*;Nm3@8(CTH|Nv8ZL^Mr;?s10Jze1Hm8VGZ+Qtc6iC7wM7BX4XC%eSO*zFj?QY%(El z9Iaa3d=`A@!*eo-ZcJo>=5ho+=#nJ z-$jF_1C4|J66e9lXBG1ux#mEXVfK;#D3^fpsM2iov6SZY^;T4#V_Zlj9z==g$ViCw zW}*R*=EIMZTDnq#78d;E5Wzss_9Ok@m7{3dJ1hW#!omstRriN-#0j z&l@IyDW~-d&zXcW8MyN#n05>8(J<&M9c$xN|6M^H3s{)nMYswrU1&QPk1h=Q@XzO) zyf!|$ekT+2=z8F{qa95|0Sh)z$I8|zP%pN(QD4AkA+4q^GSKojZ|N$DvkWpwFqV{e zZXak<=lFg&ck;^_Cj`S%eLVmEnPSd6!%&zBHC1B-tJrqIl`_$G31_mG6;3sx)+a1Zff$gitEyjgt3y{B} zdB^gU5yM@4*iBvsW&KQ@ZU55Taplq1VVI)lYKAAkg_AJgOLUW}y{xmr7>>36i!lHM z5C#5`pB?b^X{qBKr;Ek{|MDF?#%=*9v_dcj-WgXVTlHrWR0Pu9Zl0~le_jqwr+Bwq zmP*d=7)xH4b%0!;dgl-BXm_s7@!dK_DXK|e)o^kTvvWbP!Uu?Dk*l3bADsMFT%y!E z1x#Qg1(pV%EW}(E$OH1bcQjPnGGfq;ze#g|7V^^&;%*&kHnTZVBz#yDnXVe7SzKHeN%Oq!EgdBsr6X0G!t9T$UF!le^Gif^(xym6wjNCg-jgy5#^;Yq!1$ z&jd1WrA|coqkJq5GH-NV@qabMnq09Arx->#H{g{WgqOm; zb<|H*T7K*Ip#;`Fjj4!0agDrnj)Wo3Lw2vgdpoPD4=X-h7sF}d(twY%L5|G8C>i3* zeD;isnE^4?#)meDs-Zr$VvsuOM+Tb=LH-Kf!^wgQL}mFU|GNE9vobTwV01iI@BoG}mLj0w~;* zocoE+!pnAuFa066{oLG=qq@QCt#cB_*DxY4<<~!yHD}J_=u~)JwgLX$zpC)>UXJO+ zT7EEd6FEfYB#d0;YP}kgsy~Kj5FGz5a))IORZS-;yfyKND0)7Ags6ikayF|LTfdzs z1Xa5ScC96QNx9uc+#m6nK)Xs$^s0dXi8y6yOT%%aZb3Ei&gpAYJ;8|=tXRQ}b1sI7 z%%+rEUI^zOFD6%O6yx;iW(Bjl>K;!U;d8ry&GWI3H0sdVH$PgdXCjARk24rg(Z3-PF3PR9WBi zTlrAdmjH1>07gkXM|ef_rfVE@BC^b0kWV96^x3FA%dj7eX+# z%htB4;Xou&fLw86@2=N!gLu?g_3TBI=0d!bN$4d{Wj&VO@FAQ$9?BeBH88qhoV;{d zlgzR#4Zo_MrimM-qWL80jM3>|^=l`fOyA&`mUu5Um?bMz_k77?Z7+sU7=FO6NXX48 z(Sd18Jg@EWyq1MgVRuf#4)WID2nkoHt8FXuVnaxXb@_Kw(Li7@sgj7plqpNiNYx_Q zqio^dZLXddy|ovrjP%})=Zn;nc&Ot5Hy^t77r?Aa*8{jKv6Ru{ZX>!#5dGnO{doKR z8Z3wg9PE0&$ac^fu(oZhkcX3f0t*WJYc#Grs6V`y3Uu;60NdI+k?J67o-yUX3VLVK z=3+WyY@!=#pOjsjo-SY77Yn)4k{1Q~e!gU|9$(r5Q>B`Vzr2=A()!*^=+qs#q$*7k9UoN>XQ+~K;5w1#^^#~?@Pp5f{u9ZSuLDGt zRJC=`@1!-=VT%pRiFnA*RY6rm8zO#<>~YhuTnh2q{#=`)?nUpzEMs$S9_u-{x&03P z%SiCba`%uw55S~>CVEp{Jl85Bd^Or0wm|YHU4poaaQ4psn`}P^7P#N6LtB>0@no|_ z^GvkrbL#!}$Q3BkFA1aw2MJ-qlF?Kcjo@+nn-Z5$YW>@~w%WaK{Z65pjx#*uI#qt7 z0h8=uD#yP$_jLagCBA@BvcjD+Or4_U*?DRrxtu8kD>tAonZ|G4^nu8tf_v3X9lboB z^~vm?P-+eN^p8-WbyC071uu&X5}i;VCh4HpJ*S;z=_b73bH7{&_Z^FFsl}pT)dHUZ z*~~m(^^VPsLo5#beaxp~`!5BL9((}zsAL^;Wfdv&G8sc<{bYu`m@DN;P2?%HPcgCM znFt<_(Vw2;R3*-Y@k(z#CyiT8F5tRS=A}o6`k*qj{&n588C)qx;9MZdW#aGvd)ba$ zj>Q1J{!jSS>%vwF2kBD2Nzx<#bu}WYWcbfWAV>pCeLW3|GK&LRq9hHjx)I{r((dQC z4Z$Z37>W+6r*D^l@rg zzznON&J!j+Fmp3~$Qq+#)de*SU_#^oM^Pcc$FM_H8Z4bt ztgKo~`P!1Tj$8kD+u~IPbKBF&{BUI?RufqBqaO9PFx0j(V?;r*NZyi;pv5I?(rHIn z$6uEf_9G{dTV7_Xs}*jXv<3@YP8KC8g-atpUdtWf;@Vx9I1O2bu<{c>Vdr1Fq?@R) z)+V(AmHdN>Co)!yVe{Wb+!-1jGb$gZRNDRZEUUQ)%1)A7X4S21B&FMIYbOrCVeZ%rX#_~NfK|90Z0z!APQI%t6`8#ACV zmKk@0sd* zoNbx5Qi(*;eW4ZMmBZU{ok$htBdV_2uuDOeB|fp+pFE|KQAEc%2Dni6a%d zbDPiu_GT|8KEB$rN1MK^O^xmVW#G7iz`hATD3N&8u^=+h6D;%6vEJ{6e|pYh<(>(szn*0c9d+0?6L z6`T~IbMi>}VgxYT!Nm({!yznjcYft$LY7K;x$?j`w2yJ-J|bYI|l&~wp@`Q}2`l$djZf!!fV>hcl z@g6+l7%sy;3FUSQvZf|ocWTwB!S#sd`rR)=%HPQNXmS-Cg-3MU-GeZj4|@C9+QPxE z?pXCtpH5yYWRqXew0G?WI<0Q`6p<53qUzMFRxkq92R<9Q8{`jeU?xwl+&-<$;d6B0 zJ8!?d)PAJUtbOZe&{Wd<;^MQFhmd~%pK?3i_6-#?CO&T=?#2v6R8Vs>YV+kKwAk8C zr;^pDQ6=tTc~Lb_pSX^}M0&gn5A7elqKcHfpEo&2#!J=e)&7D1qV-Y%{J3Q-xaPLC z6})L!)YBvmOTom~-hus~jzbh7!X0V&6p@V2$oJ|ao;n;H+Qk6!Go&N`?8~?&qH0@S z<7vLl-acj3tj{4)A=CD$;fb7fu-T{k(;M(xxH&3aJ9^jIWrzkiz;<<^EgMNVAo+}5wOeF zmK}Yjp0+zRccwX1Sl1l`tn5~=sH#WS!{?Z*rc?N>TEbY;2R!@go49~gk zTT6`Q(SI{97Vf=kd)U&6sj{DHLocmM5<`*t~KWqX~GtQyj&wSaoH?Wk0ijSfa z!G1QqxA7Woka*8O;nMEKyC7HKC!i`gIaUsKyczV6<+j(sLzwlrw{~|2LhkAFi&`!D z2948JKw&dbp;pW-jQq3${(E~7S&FhEscVA`Kgf=qp#&Nww+DUMRV!9d+33>Ahm2Nk zsYRHg`h}m~p26sTw=L93n9{4ykeVhJhZLJ!vkL7RB7(r}(XG&~_^52%^ZPkSgMXjP zs^2DRXW5u0+>EM#G`n0?-AiYK>+e7VZX|Q#m_|XsG1d7vN)1)9NL_$g>pNuY$2YeH z>dbDq9VRlcYRE=0h(c=Y`OgY0S`;o!I!w zw3-2fJ(hrb&nf%^ZoMk_I?V(CWklfynF36I5z|ql{*?nS9RUZ$Udnw$zB&E%%Km%7 z473Sbl@x@xr49V5Gagh++U5?+ftMzyQL-emDxdjpY0m`?B9s8B8TrUCmE|iXz;Y!_xJ14H3O{ zOM)H6jUoQfAPhaaVUTOeY#{J(is-m;3$#? zko{j`Aw1A)OCt9TbJ37?>m0~HayWyEr@LN(c^zm}oS)&YMdo`N#~va~st5OUt<1fg z=cFiAiwtMQ@{eR||EhP6#}$XVH9_ybaDx_oaVNG{a@@!z?$b8Io^P7YOq0?lH=XlA~8(0s#@vgE&DCC^$knJ+*n33;R zrn6PH0$J8KezDG@`iCSIh1e~!_;fu0E+@t)ofkH?l@Fu*V7*Ar+ik5{-jd1)R{Hq% zV65i*#Cni4rTEm-N=`cu(F?!d&Vt&(`J1p{{6czZ$3`OA{*QDzttY4h#6F}P>drGw z#c~{RV{lBcc3crXU)IRwk_teVK)RsR% zB@wsBU4LxwKd2j*-TkuFw!7=uD%{r~2`(Wck9-q9O()t&Ux3GJ$$gBi365V}3j(&I zS(Bf&iJ(=~;S2N|trH>A$*Jkb(%GAg-`3UQ54?rkbr8Z#goJ}!{}C)h+|(^fR?>laKIRI$JS@-xzS&`JDfc##0F%!yWO=DOut8 zT(Ic3bU6lWJGTvbIp6htDo5zTo?=zMhB3I4<+J3XSW^ED8(}uxouE<*RR}tKS5F%G zdwev?7}L_U%&W$e8-Uy17$rs<{a>ffzmicD;RWF;ij3nO&CJ4!<(KQD?(DrR0&kO~ za3RSSk7IV$awB0P&<_`DZ{0?+3y2)ySi{Yxe}nFpUD!Rl2DQ*P2=w~FuBOl2;=Rp@ z#cHvT8?X&rUT3ldm2XI+E{g*l$CnrLYimcNn+S@O;@Pwq$vx$-hAw93mL0cV$}!zd zmIc^IJZwOH^Eb!$!y$d2>o+`zE)%Xs;i(}U5p#dtLGW-04l!n^y(5DhIF^t7PiJ1< z?612$dyy%1xzWs-~ExgN?|T&pnKJ1-sha&Ct2 zOO)Z9$*o@OlhGV+nc*L0USMJq*`JJ8ho<*Ce{NL5SZ>_miT2prCg_Mlg|vN9;LDZ% zc|l3m?r#sHmpv3Y`f8}YYTJYJx@A3wx#@A@Wn|eYxyh(PYfndul0YvI9U|hK+ulTk z?@`8;)Wq4y07~D1}z2%ziTmXO_Cv95;KG_s zWrNR-)$2l0J}scdDX%{h&v867x8@=ALEZj3;mq;uD!RBQ?nfa7ZEt$Ev$6s(1r8L; z$zp+vX)nwW$MluCQhqFD>(DM{y?@o19UatG1xX8ox`x&9hcJc`U&dnD5Fb|CLN7-H z{7t0>77y_8j4@mfWzV#rbAozfEjJPeJhF3g+VFk&Dm1`{bh2L}ghDMhBC|w`-#o_o zSGmPG{qSx8P+uLqh)9F;RmR2_M%;lqbN|{us?zt7MvQv8>}I$ft@t&cP@GXRk3get zn+t7m4NLWI8|<*b8>l8c4p#ia>5c82(i?uJZd_3B)V8N~Jb3O*1%nMb_*f^3vjVw4 z%_)K5dB0YdBkxpcuO%MdXOKv;Su`qk=8dn3-`}7mvcsHb;^JQiyI1l}R#qj) zSYi!vGuX+!XI9b^H4EyC*FhDPE@xm-B@XTo+HZ=Rs-;$?=OUR= zGv7eUZg<6m?fDau47f&~e#|!I$J@s3>>ln;O0^5}_%~eu;wfG+&LvC4xK*9M;r3*~1z*R*DaHHP^x@qGJZKo~Xl)E#bxU-#g z7nlA8v0$fPOkk7s`17rSiaM+Odod7^reh7KCD0YJ%@beJlAtn!orTCr+-szKY^4zA zCfOxe4w_)hxAE8eKDg@cK%UW$MC|rJFWb#Mo$|p-ADw^H@?+`9ZljP)o{?kY;<@Qn zR#O?GF*Wb`VW@%dIa4>YDmzQL0;hL=)pt;rNu7X(Q~QHjAX&24F3O!ryb78bhX zn-S7}uR62o>X-cclNa~gw#t=El5-O5iMy{v!9tG*_e*BzSagw@9@`xQ+%RETIOoCk2p-sl&h{k99I6tKlT@(C;=f~mnViC6ZPd<@BA#BU`1ENoKtd;;uoz(Cf{Lee4)RF2NnAr_>!vwibgi_aDpvgVj1i=Xr<#%Om5s0 zN4>G6qf7Hh;|X95N?=o20D?1C`Mpjc3~m;{UED8QfxCLyJ{ix{9v_RQImJX7Ph<5P z1FVWn_43NZXNS*L^&1mX2B@Fh)Zo6|q=4*Cim;F4Q@5|SUH^Q(zE6ZV#%9(hU}rSN z;1`=@4(jt$z(h=b{JlXm8eYhpMDqTrm)gu+uy>&9F6~LP)hzK3pqbB*k360Vn-45I zE7qggMi|^inD&aDMCp8S-_ljD9k-UY6Qh0waj!Log+Lr1 z-a)&q5_X2PLCJ$P$*A#uyk9EczKBcITJKUasrhZoJqM%^3q(A8eVD0!`pLCZlyz0# zFHa)2!k?1G|1+>^1@SW2I^|-`=qK03+ZFiT_vKEXTxF_O=hcu|+h9r-Y*$GRW6AVk zp2o@5n1txp0`N6Per*$b9uV{jemN*OUmL18AhtB2RMF-7ncnODQ|5Z>O`l`C&z6nY zab_MIMa+KdG5i-kUBCDBea~SSx_r8Ib<*lJ9(6~wANG9m^i^7^{Bcs*IM4$0>W#+U zH+O9&aG1AQHC$V$-u2)ad*?Tr&g2rb?&=3zesMJ97kT zbiwo@3^QlVI{GE;N|A|+=3PQ2xX_W!rOpgif;2ijKYsjblsM(5BmrPcU}`A$PhTMa zKH}T3VBo?`)_O~q$$|B6qN?r4kyQ+X?=df6=L=ZQ3dc%7FDft435o}78 zWh$No3efm;cOvW<`nrM5P!Wc-Be6Ia=}Yx&IW^DRQ%c_yaAG<9&2ndHg=uj;`T2GE z4wa88TCou$2Woq6ex_P8q%W!Sl{Mrtu7nH81(bDi2$FSb+si zzy*_7UrVhyQ?CW(Ekw)511IlZ0PDO$R1oVgCoO;JKx&4fG$YIbP}z)?s8)AgFjqm9 zmCee)P#Y?;T0^yo8Dykr%fN{R%;4;dFzts<6);_oFg5O)+y*DVZhJnv)qRhDtYU6( z$MCJ~4Yl0sz3%?=Ym6dRg-{D;1}AtIX9|P*3*A8HVVRMcn1Vmk$vJC8Hv+(5_vy-B z<*AyW%kiClrvIJxSI_Xbp?RSl_JDT#gW43mz zbEHDW;nIb+iDTK`tnoCt&doV}Pi1K7UEI$dd`lug>4Arsj_1|YUXWK`yDS+NRW=Wp z@jb1HD<6H&H-;(EuHe}}@|8{pJOmjDI4TnI^0LSu<4viY|J3do@g1p2kS6kj3--~if<))ukOxh8FXdj?(_Ua%DrpFh|;qN#jrA@9UfO?aPnJKLRo`bIY$EVPMmTcry^{W(_ zTirVcCn;SNOw}U$I5Kk}mQ zVwn%S4t6pa_K`TkvY?jO$D-`>l0%jHk>vsNyN)=-^V|5M0UTjl>Dp~t+kP0+bLWLPrjPO!>n^3={pz2jcbnqa$M zpo7qLz%Z*vNBbu#Y9}vy-8UN%G(1Vk*l4~`-EVOMjg&!Sz!v^OcWLT>*_M&~xPAE6 zjG5Car)?c??q?8KGp$i)q0qD+$$(xNv@%dpL>z6rZp7PELp%L%?{@kxL2#^A#AFxM zg^~u8ee|Mo8q}^;Wbn_mp3m(eCJQMHo&AQ+o;dpZE3HB%sh0=ng6EDMxm>Z{Z((@& zb%P?zwZH4rrNKkBO_sqE&ThmaCyGyxtbaT14BMXVr^JqFn|_J#Hx*Ouk7>*FBs*go=A#23oN_LQ;7=d>HCavA*&m1#b>2unZYiC{?U&RURwYDDej(Jts~kQ? z$4q$`$<)}~C~DStL4Ns{J9GmiMjnqL?cQ>@9ambY&`F3%bFl|JwWRuqKynZ#sg~dlOWUqCyf{5T#>5P_P3QKtWMdiVbX_*iaD_0Xt0; z6#pnOW=iPAC#bwGgqNFTSy+ z5BR4|zc8y$vOM8!8?_QAS9$T?A=#jd+~E`QWnpGl_@U?EEluk!pCtR(MvYBQCM3q+ zp737d`Tm~Gjp4?UD~~*{27GNUa=q|+Xw|;;UTCM?Dhl@!+wLLvERz-)-tEV0h&g;G zhhFL&dmy8Ib%gNBJSN$aO}C*?%-UDe&PYOQ9uqCBUp zQEU1%u8u02IEp37*1Mb>QaJzFF1>*BMY+?I^11RiwcP+Gyd)>&28uapKk*$`f75s zgbtZ>Lglpe8~6)Pe&6A&qi3?^+)%lBjcZNdJ3CVMDJdkJd5=AQVcUK=LB*PzYlUB3 z&^0)&p7cY@&8bZH)7kE$J+}|@POXuW^;T&18EtR;5&9(US;pC+dvW-SD(9%0k3Z=A zs(Ab)?G4>jw||wAY=tEBG1?^8PfN&8t0M*buvGUcE+JsyjV7+p^m}TeiIag7L|R@n_bBZE)h2+|u4X zWp*k%&lK4j^k{eTO&_J{xDG=FyD7=q3kWlGd~NapdhuP;(|s5TZppKSRxNuRD}{H2`wi}D z^oZnhzN0KsFESmZq|~o)s+R9^-RgnDjP!%v4lNe%AKUFsD*qm}raZK|Az7pFm8bWW zP{Z#Edf)jBVy*XoDbPWk>3z+mWS3u6IOeB2LEdowOp;+qTT_p-1pkREHwMOh@Gu*- zP%G0VdOE{#hTr509p$&2ELqg;jji9VP0*vfalV+~T&#KG*0C#PoAZCAo_pN-MQndy z$EQBGh~-FuMN5YKpX}H6a-YU=?elv*&{lOX0ufSip|W2pva$0} zIOTHTnXUV-lr>jBBJ{ias!5a=nYCvIT)2|3=lP?o_r#8}fzONs`jhkra`bKaSH_cl zpWl_~Wi0Pk@tOEWeYHC0xx=9+?p~wudryAbbyBDMJKo2N*cT(PCU6ODD$Br?OivD0#E6FWt4z_tb{)pJ*~dN8gm5GyXmm@RPUCf zr4FG3d>_9fKc1K(ct)sF@L~5=HE@HYBVKl^SorT+Mx*YHap-y~wfa zx%?jmZ}n~NlOD0|$E7~Mnk$=7c=W`(ZXcc|LCK!8>j;r=+|3@LvfAV7-{$MvrrBA- zPHtRVZt>jfmE+OlF}M1T7kGsRgjn%oWfK;~*Y&sXG>bgKysO@r8swXNiQzRmEphnM zp(kBR;pFtf60~05b+sLWveI^%ZuWXfVXpPzZl%^c1mvad)Q?s7?kv1vy(2_nv6Xy~ zf{DM?*B5I{P1bwF7rEW!Yw|@f@P1yhvD=ic3NMaQ&^uw^XcHmKDcw}hx#@Y;DbAZQ zr`m7wqu?ctTDl?wff|<@t(97ZrA8EAS?lGW&>s)=4Xe^9sdT>(lapdDqxzI^lRrYB z>>;OM7T@*!+hvveom1e=?L&{tz7451>m=pxNI0Y|7=I&XZBP~0DpzEDE~ob+r{J+m zq-FLyMaNe^|5)mHy+U=AYixPE=FROZ9CtsqJBAPPT{dRE`!N+vW1pt|>7(MVCtd6x z-qv_ai+E^|al}|7)hvj=;?njyZY^vO_$$4m2fk|5nhknw-yF8K#laR;QU6jRVrU;p z>C^oXZfhQmSZ&+Dsy<8Qd>ArDQ!;nuQ|y+AW81bjMpuYCn;E?^qM2C~23`Loq8D{4 z^68;|wdms7cp;f)Mh zL?TwIG>@ujecniUrMV&YYNmAxkINY@c~RWcjXw(09lZ9{T5PS{RK8Cytn|uN1I<_N z^)edH;z;iDYlj_TpK~wsw)b}x_9`n6X(~l>I%voUsccHz=pmE0{41Yr#)CbuCkJrh zfk*jm8&%`Q?ec0pR`iBi+HnpAF1bAv%cn1VP4$OQ;z2~1fkS-s%f2b(w8~A@*E{Xa z(oCH9IIEa~pBR-wt-nDN&!B0-hg=8qDtU%bFsUWxa`s+!3X}hqDxAWshH*UqVx5TX)MH(>U z2F-kDEk6@g?{G)Z*FTs2o zFlcvD)RJ}B7IVXF+hYrXrr??rghcpHn1|b9JH6bi{X(i~rG7jfFOKkN1|9cW|4B(m z=&X4rN!;rmy>vs4#eJcZmJiE9ormkBvWsnB*Hs@2792;|M%29(w6FXrRlBqItJnEd zEzP~_WH-3_Y2SVe9ZzXfF0vF1HL6w2`I0IgJ$H90|S@za>^&cAp?m?Z%^Ad#`t|?8Ik%2)K2u z+(zZ?P|5`Zq~pD(91(jSpne5jY>yp2JASLeQ&c?M_RfQpqc-8e);s!Ci(lZw#3O8P zKb9pWm~YvO5IWH3+qHaU>98hDqD(Z=UP9XL_A_Memg~=v4K_h9Qe4)K1gq`nQ$MLE zbQN(gp}>Bxo?1v>tf#jmP!gYcRBN^Sji-Ee@M}9znk%k(ib@fkw;>&rx(Zwuvvv{gY zwB24l-68|I&uJ%!OW$G z-*>_IeC<2x?nYNEe=_jX9n(K9dt2Pe&2BWYn?Hf`9qq{m3l4)4Y2<6OpyW%jM>Sh% zrRJ}zvfQHmk#e-QUGZ=gW6qHE+mR)LVa1PKHC;51JE=I_5PR|fZ(XIMhJAgh?~TjG zQ@)!U-YknTq}gddF6P2m9p_t8K5-9|a(l}nTh5eka=h--E)M4rp-6mL5k=de-Dn#Xpp(EjTiOL@;@ zZge#%T65bSvsz`w%^NlnvBX~8Vvp6px?&46pF$Vwn9%z00Njw$u`ns6H5#MNysI5I zh}({>NV&h`zW7+aRb-CD=??V^m?y7!z>jfNs~LA>E^q1GkY6S8ORl9c>R|Bd`sacL z9tVz|);R3qFK^MC8|3LO5~iqKl+`O>zLmzgTy@_K?$ul>-!;x=QD1Q@j6DC*ChB3e zLJYMjaFN)OEe7S^JdRpwU0Wemu4^XY#Ir8_=xw2$&6^C)pS2@YHv}E9x)8j-%|Ck7 zAWL2?)kK}=Lgwxmu5?aX^d`IY9fx+E6`!oB(?6}N(ZW@<{?p2gC>p-z7L)YlCOy!1Js9c%1WclR| zj|l?@t>vqn;k|kk^@xkES2c^_97`R4#nh(P3h$1Ivk<%)DBIkz(g`D_H`n5OY@EG!t&_-)g&k3?mJL$3iAi`Ds3eV0UF78%=%(-)yQ;=gf(NEl z6(z7I`tAytnr;X|OxDq?U-9YXFx}AgMv7L0=j!(-imI)Je-3awi5)#1y1$;wU*l5Y z-hF0|mfqNBb~?>tmx~gYVCL=<5$Sai0lg>U!%$Cc0$&Q=wn44aZ29mYAd_Bt^1J20 zXf;gZL~q-#8}Zi-za*hAujkqLR6IDrJb0dl{wV z{xd6$4fo_`p1-p9N`P@uonM<&+X#-=rWb5(HV zZLyFMne*xRIxZv(`Mq%2q z$Q8C%pXe5_*!3BaIL+q6k&BbcCqxLUfz~?{f(HQGGe>rE&K3mCPn)2dv7jE zpWn7D?CQCTf|IDn`L;Zm^)*`x4eGN59;7b6dN+!1+3nLl=Yo}_!ne!wyhj!a#22h~ z@-;fhyFnw^G1cOdo)7mX-hRtNU&JtSb(OkWYoso$ynnstE-J{8cTT-hzj^)(IXam6EF zL;Sd}{k}q5+DpR2U`qhc_8;T!X!fmxe^smzd@2Q)c**-H4&nV4` zk&jj{-geJdKC0OA?J3-LUd@}Qxb|qg<-ULY>PnvBu3QOOVdJxhF=~FTB1@X~g#S_z z9JKfTF0&};bpKwi;>g3x4@;G*9?1X37a408tX7orG*%)1#ml}n!LNblw@1PY(xBIY7G3nHRmP{Qb2P6e`jdZmK~$C3Y+j^nA~0Mm2pNL=Eh<^ zr?##XTPo1FW203Auc~l4Y^pcLe$hyrMr|l8ODUQs{EpA^ecIm5o1f%AAL757qkZ|O zH=1jQ=ku$d@10*!gN^1!t==F~I8dSf#_WK_`OKpwQoKv2#8H0oOQ-DMp^HSby;s_= zeK_11JYDE58J8P4C~Og+cz;QYxtC6-vd6Qg#rDgcmnm`Q)*staaG^lMe#On+*o=sk zN0++_JH*4@FV+zG=B5gtE{a$t8Z36=`^#u*GkhGgM6i9OYvEa^J9SaMFwy$fZQi%G z;wW5&UPsNiU+L!EC7Lzu;}R}cjBNann;N~SnLF}D_%@?OG-7T)TtpH(wV#ji;MDZe zYGLmnhp5ot+5+T0xbTWb^Pp?$M&7#Tt>rKIh`U6^hi;GgvgFuu|Ib198`qhX-9gF* zui;xKpy?l6nR)TR2fuxxTAItizw_d7zn&@Jrf|Rk^%}M>_Dq`NAy?%aiR;SHm|vnL zDUs)3C{Mp$4^^e5>k0x31~r`?X6~pJTCwNaWVnNv&0YJ;qQPZp`{Qz#Vpm&^OMKpp zx@>(yT=?NN_%g-IFKBuzm;O3iYU@#1dvxvY3sJPsb!%Ks-95nIzH>wSy!{=OTWn?>8r zZKI#u%Tiq1`|uNW&?ZOb#Lf&)CCNy&aQ_O@^?dLnv8$2!t7<%7)a~#6c(`_ThhTU8 zyFwS?V)NE-Hf0xt>y#~v%Xob%9<`iJ+};`UzUI^Q%}c(7-Y=W>FDIILYdzm;**#$7 z=M+16elx$k;Q6aZ&yz`$$3FXS1g`p1K{bgY)&&+%LO`_>ojc|t60CU)%T*BTBO z9#M{WiFun(^FEXIW2~0JrSZ+eUr5mXhyJ;Q&mMh_C3r$&EaPzHMcUX1}HHcyQql!(9P2FY~z?{kA>=5H=H zNTUU6jF=Apy&5A(Y@Ro8n85-)a5&TDzgGl@LxS@)A)wj&YhZBQ|7h0#n0B}xG|7Kx z(qlUQZ+y~cu8z4R!S$he{zH=i)A4_!$$+^)=4mp7ruq*}NT%ceMiY{`M&@ZUTDYna zu>W9LBVd25>lrD?Cug1Ap6=&#tla%PkDS~;yM34fzkRj@+w(NI2s-I;3T(>XuQbfT zZgp4L?B#U^>|f}86}W*mOjBpa8Mo8W-Zb|;eeRU+aT9Q3X@xVR{?}IF%z*!KtIS#Z z8>`F-`^T*^SKZ%OWiGgX+$wX|{Eb!Jc{}Z&n^)$p{~N2!4gXKa%B)%c+9x_Ug`|e!XMa*jz%ZFzHX?G2)wV;LrNw^{0Kp&xRunGoOhr!e}|E3&U z?l4f`on?rwa~;8zB&SNL{#-rjEVusl#Uux%>XE>h@L}RJ=Uh zd|iG0Rp2mf7+iC%h@ba$_Ye|~4p=mh zKOB$j2rQT{dy({^>Ikrn|sDpdEss&~5%+&|}3hF9&+8IOfyX z#G%I~4m~z;=&^|d0wI6#1&M&q%ChnYXof6V77YkN0Oc>T5RFY7`fTFRXA_4$n>h4Y zc3C+!F*rX%!fXk{h|M7yufDzHgU|(8_O=l$0iN~HgQ0361vUKFA_1go>@Kv z+N@m`4YI)|4g)rE7_f=MkWCzhZ05u8PoI%sDp|C7zCf2He~AOUFl4i?X4l5NT+PPL>Uj)7kvZ^98y<`_q@%dc~3j3`VTj=F`~3VZ$1;J)M*z|Kan|=UbGYup{Q+mw&*9M7-U46P#Ic}vgPgMSg-t()>n+^p@YxzRFMqS^ zem;#&9ME;ypFTquV}H^X^l2n8%kqVlKR}zU_beL7DVu%{XVcH&Z2CDII_O;B3!69= z^ll^!IoliO`@&{EX8Y>AT+ObR`7}2BoJ~K6L)X`T`V3uEFQEOIXCyeIWbv_l1~lm4 zkSWWeft<4G=WsUt92Aa)c4x0==Eb(4#{(O)bIIaE!i;9u?mS=E^m90yehxSMGhWCB zoB3eV&*9KH$)EXy4o4QySoxdv8C>N4nLi{PF^j$D#lfbZ!`bw6IGcVBhmOGi^kqSh zpW*l`9}=`imd|s(0GVBf*Ol3 zGMrtLEIwTWgIO#vOVb4h8^7mcjs_~spELw=cAwAj&0XWn(dO{b9L->Mz0A=J+1NFj zy(XFEGccINr*kyqY;BsQ85+&@UlvWz&~UbgvS>(z{w!tyG;kX4>E-S#BnS@ccX$T4 zLnk#l+fSVWAEbfLA-020)jUoC8mM~E3oGY!8^GZJ+|U|fXkn#isHX?2h6Q4SIoL}^ zNJA@qgn`LALkn|DeM@V7gs!C?(ooL`AaR7j21`AdxuKC2%+k_iKR6!}^gHe9dwS-0 z2#GL2>In&|sabE^ASB4a!NCb092`iC&30ZVk6W|G8>~GKb8rZHgV)>;je~9 zzzqbBTw`De*Ixq%HD`_6AtQq|hYgMN5pd*Tg#IBtz&WFlgGYlYH}ehr5Fm#Icq{@B zS@8JH!||Sjd(&-F{TAUsl_fm(z4RjqH|}fLRDDrB8T{h*Fan0&EpozjvGe$&-aLtK z_qSZ##wg)k#=Y30LgLA(2b?yZaiX09t-)ux-+Qle5hw7Fo!6}6OrljDzYhMrVx66f zSZSaI&ai25oQ%CdmRVkqjp$HReh!BgOrqB!i&LaJczfUlF0&}M{twZ*h`v`OLaeUx+9sEDH|Ch5368=AO`@Q%$Z$gXD%|(_s&PDPr zmlKCVvk+CYzIKm&bw*0q#c5mV(UVJ-m~O8yFoS6y6x)=e)**Ck4c*I(uzf0EXyo$- zgFr>vIiLO_1{!yyN#h=xqhXA&dwOLRXZ3;68@5Z9^rU||sItZMnz!9r^{{|$eZ~rZ zVeey~IO_RrY9C5*EphW~6IbE4ePZ*HTh_LNyjM<^u5_|(?EdO$MLGRKGeTKYH})|8 zh~9@qt~?FcRp%baaCwQPX_*oFLqhq%8=~a9e6RbQ?TC2~UcV~Iu;aE$ew(%BCAaF3 zuYX|EwNPHu3f=rqJ(5u&SEz4Ix4pcKX>ih38GY?3?SK20LtY!dmGWxUM$Xl@g|?d! zn!|IG(Q^5D`O4w9zDFpj=t$}M+eLELE4@%I_Y2%~#-yCHWoRty&8q_M7~#4=2zht$F5lykr?$iNT7#>Ls)^P?^N*MLepbE9)26<>bpIkZMlQM4 ze_*jcuYjz#R`-uATjK2e8kqCJU@+)lw&CDmH8bm+TcG**5Yh3lP(_qiZiu0L zeDm_+=JtH=gKpm6heXk?*-yDGCoZqrewmgrt-rqW&PB)ZqZ{hRqNg`T+B|eyr&h%w z8M{}=g-dOZjixsGYGspT*4_SnW>0P`k1)~@jGRyo8F1!zL%4e%!N3i=*V-o7i^@$k z2}UMrm>K#DY9Bghms#DPC{=)vdi{bJmz@x-q$ZHKWXBD}8rLl?R(heXdkua<+YT{|cwf`C=!?M)d%2>nMU}EGIcH*x50S*P zQxoF%tV-5#zuO#K)k(JCexd{Ce|-+u_#(P9{W{~YuX*h0vR&O{gDI$zYRwn_;W+EvWjwRgD6NX)CX>~F@Va_EkFxe%1*9yQBfmfDeX`fC#ghMyj`5gyROE@?fjNjT2 z05Ea^J#e;n*en&sq0O-dyn=!6I=`vVyYo5VfJb|l0t0;792y*Av-%;qzshL;)&$Z2 zwSJvhDTs$xcZGd4InC6&#Cy-xmR(4c!I6U2vAldiTG3 zUNe)QdIksM*B~dALML-lX;Xu|R0^3yrcBd#sALj}GEL>6&`G>x%J}3jh7MzpL7yC= zjnk*cX|(ZQ^y#T#%E;98uiwg6GdT@<-h55O8 zx%mY-ov87zg#~#fKhb5m1^JmHj3M$AV=045Ys)Xp?V6nI&dJZMMN_^O!j5eswk zaz@57>A6DLUA>t(IbT1Ij}yAEAZW^XQGRa2Ku-_4IVbnqWb?P2+;LQQFEJ}8v*%~} z*X*xZIeD2`8DCQ9gMH2YK!8lo{FaUBC6iF3%x@XPmDyP}7z_zT&B@Ma`k9)MmY0^E z{^eWJ`|c53M-7@O@H!y@g(8xA330FEhVtX%3$Yk-A0;_9rsn6X=vUn_(XXOgBc8Vr zni@+c8A=TD#K%{!3eY{>=&F}5<0dO#yh!-j+uehE{`^_nDE)~rwjwI3yyZ^)Skj00 zjg!Cvl~(^I@lE~2WZT;}Z%WXF*KZQOQAV(z;^W>9jz-74!VZ2+!qvp0dZUV5$dJI` z;CSZe&!1Ak{ZratM^|O?r<9K=$;m12o4ZF6KO|yXdwR-B=p)Tv!=h=E&^%0#sCnP$ zxdJ&~zu}u6e3+=noc=14^|_(1btEMt71xI9$nT|3O%3PZ0RKwH;53mq-dy&v=9dYW!J*-y(r7$(U;vB9ccHN8 zj_+Nlx^m#qWXeF(tN@KlqY?8XzK{ojz+gr?`A0!|6|rii9vb)R{=@u&31FZ-AJ+^C z2m+S|X*7J%vzOne2UUg!2h*NMJ${oKk(akry&I20zmBK?`p1()X80FF`~x{pUzU() zV5f<-<<(-yq>Lm&UL_un!!#9wXcv`d2+VMUtw7I?jIAV3)22b_iqO79N0kup_>vY} zcU2dZ$|Ok#+z%)_ApP{X?^G(CCQGFa4nkivDw#}p|IGb74*#?O(_9La3uITEugA4O z0$f0VPU8Rq+>pRDO$q47et43&bZHa*?d!~!L=Y$;skpKU&*c9dE65I&ItiG`gg4JS z2hIlL@lDYWTHP~DlCT~PA_--I2U zgf>0MBb7>{gFh8f7_gp5+bzrie(fhLIU^m1SLNLX)K4U*XBT~Mzyg6O1}{?puuJ#f zfpMrGM^Px`_ZKg2l;Pk|#o?pgfKZoIe*2PBUi}Kg6o3{Fl#$*u;vz@$fhh_#_i-DM zfJb-Yap>9>W-gO63UkA64>V$jrWnkrq7g2L$Y1LNHIR>z6H!Mb66kmaj;pl@=ok8$ z^)V|p=uQb(JAhxBX@FvJ149A2!TsCgIuelp1f+2NH3SeaG3jkY;Ki54wf#*vP`4HV z0c0Hwu+k}@{i%kb&OvvFWL)?vIzFl2g*Y71s?k6Mz7;BA}4z6j?I1C@KL|Bj67n2R6!&0%k(v z-_w%>zI-id#5ChaelbL51SpjL^5m$e#Y8gX4<8Wd%MY8TPl;B4d-Lv1!n;q|Uz4yc z1R(G`sZH<0pQY8}L9<{wgzrpybiW7~7)eTskBfd88FuZ;eS9kk2r!dMNssu_LL!r= zNrD6rK;M^#(Pi}@V6mKUF(DUD9zA*WdCm`98;R9DzCCLLvrC#$Pr#Q)y{yLdkU+qs zB)=nvy)HgX%q*&HBX*ESr?4taf;rY1TgKj^FNiS~b!4SuLXh1tO!GmQHIXED|AaWD1QMq)S zJhl%#JvLOG5p??0#nAZdie?m!OrAtR0yF7se)T-Jo0-bGM}0Uf7K0*BjWy=@y1Add zmylQ2g(kIrc{+unjZObvqclSK>+pBAbRu_tG#*FC%AqF4dNL0>xm7`8_UEMuB^j_m2N{ZbF$qr~raE5>c~{tqrISGJK)vA|9tFsO!O57WgNd1%WK4)nPW&7l8M&15 z13x@E`g442e1Z&%l+X0IrM}ftSk5 z9qF=sax-v633TpIc{#-wKgSc6%E|enTNUKwTn5JKOy%TP(Z&G3tlZmA)3SW>GiXH? zpcONDIe&C_cmFLpIgRmq@^ZQ(y9T7&X21%7LlbMIBz@4`UFZ-=NwtZal9F13JzYRr zQYvFqNrtms|0Ienn0l4 zH&anwe}j;=Go6B)NZqBTxZ1rE-P7Agw-oN^?r+XRe@;#gDu4u_olGSRV21%zL7h9; zJUB=x3dBKBrKcY=FaSDuUvGC!O>=T`T;MEJp;5>L90pUm->aK8I5ZgNhQpxI7#wb3 z0Mq~DTT5?k5hf-1r@D(30dt2i1EpxwMqD13J7BfjVLJ1pjijeTl>Au+#iRd zQm1H2WslRq0tvZ@Z|lb4umg<+=&#AicOvFNmC$X6J`$#>(_s56f_hEt-j#sI;S;K` zU6qA>$;t2hCFVgD_fI5H3)CnOoB&^-|BHHHqJp6W{am%D3gGhg!SI{9am6>!<8K0 ze*=>WviP9@;F?*e!eXAL3Q(vSFVpVtZNcNGapKsF6ZI9z$xrTN7gRUTK^2gAsB5cI z3j)>?s1Q_}N>6DdyAvzrD$9l|=(Bj4b8R#ZcNRPLE1&tU8 zNMZY`pqP_-6B+>ij@!0} zlfFaQ3{H4<@z9pFCQesUn&)r|?a{7MFtx-P^?2NeU9K6J?&|jBH3N6v@VLub@apU`UJ*Q*kF{mFyWd(6D5KOrjmjxgbmPD9= zDWGR#&gFiNXxZ_-2{Ah(^_*w>}eB|D;PAsn__Qb^!%runaN&b6BFY- z>r89rj#XgBx_R9=d{;BV7>4mSK>B0QgdWNkB;wi6HAe6PJp%*1Y?w_ak$J2FAr%_~ z{R{n_9sLh=4UHy3bq!4iyF0p@bquw$M^_oFX${xbepI!lVJtNz>G!e9Lw#f2C=fJq zt-(cf3*6Yy9W_$F)zHv{GOBH)-Ac2w!{%GGqF$690!W2}K_BGWu+D_ORt>p+y(zl! z`(>obTK)C<#_JG?T_cF~h}PDM%F@gf#L=ur5K;m2HbH3LvfkX>!fd@&ZRXMgKYiAM zq?nl)M53BUbWQbIe{{Fy+<7Y_gE;vcdraf-gHgNI89Ur3#;eDXa1-esCWcmiHT_-P zScg3hon5HLZ>efBGGZ@TV1h7!xxgjGX zrNjg)ve4Qi5pkFSbdH`y3uwvIJ4P6EUmu!|kr+Uu>QfuLYtl0mWn@HaXTb`y6`-yt zA4G%RNuwO~ztaj?0q^CA>+J)jufIMoWvz^i z_|AD?#Yfrb33eKEOFCH*YBsGJq2OBULH8|J_Yq!WCRCIK^{e>1!k+w=p2$+k2+#?0c={EEgou)<=V2F;S3_Tuw7ty+M{ z=os;Vbg##5Ac^qIe1L>z4^}|tN#J5km7MRt4oBVv&g!KdLzBLI9>A>N)nsPzLqW$^a(fb&4^R$WLEO%ADrV3gl6@pP2BGT<9X5 zbWu46ID~4)44|s-euR?wJ}v*t#+7qu1spU52+LpXB~FvLi5HaLLr_H;0ORJIl$9Wt z7e9PHt8VmV2CYaytBp)F5AFx872F8{${zrdkcFCBZ)M0mpkF%rl(xEWe$70zQUi5j zI+2$|#JH<`_%*}c_$3`$l-tx-dA|QzTs;F;ph{}pz--(EBC&C^Ru(w%0OtTee|O0f zcaTq6v-9sieg=n6b6^FEm`+?o=nB!W_zu7U9XpMYYJ8L>2h`tx_u>7!)U3j~Ij{oQ ziNuCTr{!897y=z|GcYnO_f0>T%E(A;Pk56MpO~8Y?#vum0sN_U3ai}T_JiFAyuo0x zRVPT!>FEs&1JBD=ms2>BL{)@Jkts zIl~OvD{V_7Anp~waopV%1%SQ3iIg7~>2>SIl`A*zzxrHM*VT`kLo1XA zis!n400YO3?a{nCJs{Upjei||BkYRbsS9^wGs_wPxWUYU6$<$yV%4Q8W+oGrS_kN8 zx$f$^xH~t!T@IbT6Q5Jl-jDxsXvG{@0qO6HG*z&Di^butn_!p%sKiIt_B$R7h3c_uITvY@4yPC?M3k6{{pP!U%uh_-++|_`^*0sSUL6XZ@>z94y=IB<9=yyYHHC} zF*K*3!St$WO%5&k>M8KxU8Tj(%MCuN(`aSz>=Y4X4flvIVGR$fE#djiFwJNd1Vhbc z24^66*82Ut3OxIR*U+;l^!$Ut^@A5;1Oqu}pwYtM{w%^A0XsC=+2LK8+2KAJQ5M7g zx#2vj^S_|ZYH*rUHgD)WN8KkY%rXIntf>An6k<`GWoC!_WM(tqEIpeSXQ{cN^kP=F z9HnN5dnM)#XEVD?T#yUO2$XLz0XD+vyHG_JVwz9DE$Sf77rf+4wB zLRw0a`s>lz-Rsq5=@N7?x+q8@2&Q0f;;$`rO@$v}>E*7GCYDzB=6cpfJ i#Lq|Po#Nr(ri0J)sT@39JUqNglf2wK++3W)9RCjo86NNe literal 0 HcmV?d00001 diff --git a/images/mapper-icon/Mapper.icns b/images/mapper-icon/Mapper.icns new file mode 100644 index 0000000000000000000000000000000000000000..0ae8f58b0e8d6ea5beb2be5f8e53533a4561529b GIT binary patch literal 278110 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 literal 0 HcmV?d00001 diff --git a/images/print-mode-separations.png b/images/print-mode-separations.png new file mode 100644 index 0000000000000000000000000000000000000000..7753a5304832ec6aa12133c1d7e9b42cca52ab4f GIT binary patch literal 1073 zcmV-11kU@3P)6IU33znQUv<2W}bv6C1GAZWQrfCLiif{GWY`UqY29oj|PMOm{e1;I8I zP*p5I1aXNF5nygfNFfv@ElG%zm>ApRyT>zK95J$z_%dTV_F4TNpPBjQzn@>4Fm6+{ zB&6ef)lLVn&gxsH+1@2@_-$Z(;6EXI@wsSkG7#{Ql{`72tzkx*52T}qSP-(0VJFTe z0|6^67SI40WsqIX2hz}1`5@#$cFQPD1_DAX7#M⪻nlL17*p4WJSosj7UgL1_G9O zF9a9vUIuD-hf=-K9cBJYqDEQop$`bKC&HQ~4hhLmV31vIJV1u@DoY_2baa~bBh6nchK617$h{lj!0e!xR_2b~r7|I4ht zRa6=nbG#8n$tIA2F~-$%{IJBl*^^YfD~b9y&$DETFXqY5G8rRcKkQQ^u5f;qDc}a9 z-}58T&(M3`l~1^UO6(;QJ@j|d1yTw%LbzfwlQ#6!8`S;>znLF2z|b{bh$=hFWQ6c` z_CvXwj9%buv3kRVUhq)J!k-$k99t^pPcE6gNAbGDUne*Xco~q9D_X5q4W&@8aq)M4 z-I4})$Sn~&3XU#%8>k_j!V~U~aWzvmr^SfO@a!n3gp#7H*B{8hc+1pHM%P%b=7vYy z5kR6(YCzffIdqTfWm4y+te^ar?*IqRFd+@C&OY>x8A~_J@!IsTUi!DSr!JMagNzHg z5mvL)emvo>S#jlhH!yY{_lyT31IZ|zRvPVEPaS27)C6OIpPdO}^#zhn8#ar8g!B{V zUAUWXGSj=~dD9Y3Z7!=RqftI&nkeexO55$ z;#ty1I4K5P3d-W=4ijjn!^=RNPimzgun#oFWAj+JmxIEq%pr*vUyMkGbdKx}T*o*i zPS7(vjT0?C{JU_kv791NYm@<(xx1eu<@f051vDpZ=Gi<;XOpW{`kHpN* zQHuix8LtQ=Bg>y@M77yma2z;K@^u}eEXe)V3j0?CtseUu3zR4Kl8F`qYJ}Bl2O4iJ rpe5#4SZpC61EaoHt3$t6U#tBOX`D;yd`}gQ00000NkvXXu0mjfm*CvB literal 0 HcmV?d00001 diff --git a/images/print-mode-vector.png b/images/print-mode-vector.png new file mode 100644 index 0000000000000000000000000000000000000000..443cdc72994832eb3e878580d5936c7c552dd3ae GIT binary patch literal 2932 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 literal 0 HcmV?d00001 diff --git a/images/redo.png b/images/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..3eb7b05c84809454c095bcb61b27d83585540bb2 GIT binary patch literal 1502 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 Y0bEjTJ%m&ch5!Hn07*qoM6N<$f;~xXI{*Lx literal 0 HcmV?d00001 diff --git a/images/save.png b/images/save.png new file mode 100644 index 0000000000000000000000000000000000000000..17b1274e6dbd233e0f56ab845ee3bb9831a1206a GIT binary patch literal 890 zcmV-=1BLvFP)dM|bP2XkDem`LSnYU0B5#{!+cb8q>On?Rwh{BCmUgYJQ zpHhGSovib*BMA8J)KyfK=N6}OuOk1!ig4xaPY@C2H>I59X~>5^fhuD2#xLqM&dbQ^7KY#b}`+}s>MHZfaBz@`K|0d9cfMn=He!2+yE(vb3(;7A0l zl^z0REkT#S5EHtFhX*)rh!bE22v9r%C%6lY3?iVO2lQlifvO^fChS9?>|J0AP1wf+ zWkuHmg#_%$dtlf$bPZp7?|T%olP~)Z*|Q_%w@Iq>P(RSLuFwPGdB6=W`@hlI{TY>f z$6+xt5)zzU#Ed_goXRAE3JDmGfSYY+_ZO}#y+pNIB@9ENC?X8QzGZ7IaU5feq0wmY z+3M#=^@$t-GuQ)~+XWdqG1O``qA234ufJh;XA6Lt*$cdSeVH%}F~-npwX)=K94Dp| zlTN5hV8~fu^8`{aRw@<3Fl2XUi`L{40NYz@SZisw+quMk8Qd9=_(UoA5&}I0oPe>h zF`_8f6o7++gMrD7F$q^kKowBpqHSo*U;>BH0W_P+xhX&U%z-A_{&|xYQW>b z8DJb3C81wtbkZ^Z1scFUu;AH^(GUqo@b?!El-*1o0HEJDptg2<0pmCbqKem#dezS?F+s%WcnB#ryQC1VV*1`CE4JUiWW_dZ+1=Y8{5Aq? z`ogk%9{!ElWPGl!uEyr>AKEsfWUkQH)%HMg(tg?M(UX8V+vifanie=~8_4Qm=x9jfwf?L$S?&i;?Q-)l zj4=BA)4P5}jV2R9!4UrX!mHBI@K~HrH%KfJr4<4F|`Hd!2iKJ1_s5fF<@@J&R(7z0Nq+b9r(>ap%xXC4UHds);(`th0$sU z979!k4n~IDXluJNu0**9Zs0D zSyF5e;xZ^9m8-F#(v*l-x?c0@Jx$rM>|AgcGxz@^!>uPmA ze-xSqlovaoX&ML!s%FjL&wlaMhB@;VZ!RjGJ9kFG+>HFvdE>6`mLRFEe`r`$RcA(e zs+67MB&Egq-1_x(q<_EFE#vuFvjU3 zQ4yjjV_$JmeGo$0m6-{^J8Bm2zkTxAH~?^}s!^-O4ELZ9LR^~cN zfy5Y|K79@xm5`TZht*<0n%x9S2zW-pF$zV|AP5pHR%_G|FjXZZ;0TzitSKo`MOIN( z;)Kzt1EmCRw+~enxtLX+0Yam=c=;j@9DGaE5);T##vXg)&7&J-RjCr=qTOUPYES?1 zaV{R0!E*_*0fEO8!t%PMmSe}>-&3R$! z1fg&kzy95B7>&j+L`iH`Wwn)nGy&6epnB~NlU>oyZr-?NZca`<1_u4t$B^R~Zd*`< zhL>KCb@udLRYmy;Ns~Hx!q237?y2(P;<877zSS5G2{0IV$dZDtt}$d~+t7aX3f_MA zyUtE)P46oVn)eF*$qFY2kknUc->~`Mc2~R2^qo9N!7>r@fJ$Ld(qsbWv2G;@P zCk{vYrur32X56=BHQ&?iL1wxYlv4P{BM{>Xii$FE=8ibetMb&ia0gLm0BCvGj_EH_sE&t)YtLb3B zF9FPf=UCE1N5^qc6%}zo0wt3Nmnv!CIR;Ts;7oU7^ZI(#XfPZ`lC$Rg{g+@>?t54A~CaQmmyb>+U2zZx~Kz3bL}zIDX=!CWsMK z%+5h>UMlQ1BXoSC5{$uOHXtk8hD?VQBOVWSKE6x-+rEFunYm_|EP5!ih7D_0=w(&i z^PLqaD4u5!C1w4cHS<|lrsKlb7jf{=JK~%FIxdb55AF8({QghQG?gq|SjkaF@XVflf+)p)hj1EiGT| zZ)rMyL=B_C*suvkP1NQ>%8FZ1G*Bxa^l`~OZH`?0*C>KPPs+^ zgcBZ&MZ+!bp;0X}+X_$>u7N?$?;B_W5C9OGvNtj_3;>^S4gj~HX5(73 z#l9Z^C}lFBMo%>!sK9?qe9fg~px>9kw;7!huTAmqW&Z&pdN>M(u2oY20000 + + + + + + + + + + + + + + + + + + + image/svg+xml + + + 2016-10-02 + + + Kai Pastor + + + https://github.com/OpenOrienteering/mapper + + + parts + puzzle + + + + + + + + + + + + + + + + + diff --git a/images/svg/map-parts.svg b/images/svg/map-parts.svg new file mode 100644 index 0000000..b819a2c --- /dev/null +++ b/images/svg/map-parts.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + 2016-10-02 + + + Kai Pastor + + + https://github.com/OpenOrienteering/mapper + + + parts + puzzle + + + + + + + + + + diff --git a/images/svg/move-to-gps.svg b/images/svg/move-to-gps.svg new file mode 100644 index 0000000..70683ac --- /dev/null +++ b/images/svg/move-to-gps.svg @@ -0,0 +1,451 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/symbol_point_explanation.png b/images/symbol_point_explanation.png new file mode 100644 index 0000000000000000000000000000000000000000..87a26fe8a61a7810b114a2c552adce40d310ff1e GIT binary patch literal 3441 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 TqwS3D00000NkvXXu0mjfse_L8 literal 0 HcmV?d00001 diff --git a/images/symbols.png b/images/symbols.png new file mode 100644 index 0000000000000000000000000000000000000000..579f75b093e9ff6c3134cd6038abd24f98ccb327 GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ!=5gVArXh)PTS3U$U(s6e$+PR zj*R2H7KPj)jDE-e+0uLW?v|DP)I5PHTi{hu zP4`ymVg~kt2AKxV0295m*rKTntCSul`rOIb5$ygai%V)MQy{CZy7k#>#iw{V=UzSV zY^VAH>BelA_Qc)g47#=7+h+PVGFu$b?r1oD$?^l|2LFzSY!(c+8}eBC4oDvI{Bd^v z2GLi_^@rkC{Jko@La6U*k@a`Y0tWU7*#oZ`(^M1I{!!c;1N07qr>mdKI;Vst0B{{( A$^ZZW literal 0 HcmV?d00001 diff --git a/images/tag-selector.png b/images/tag-selector.png new file mode 100644 index 0000000000000000000000000000000000000000..c7a9c05c57667cab25f152a1d853c7dc16e4caeb GIT binary patch literal 661 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 literal 0 HcmV?d00001 diff --git a/images/text-align-baseline.png b/images/text-align-baseline.png new file mode 100644 index 0000000000000000000000000000000000000000..c86af6c8f0ed2aaeadde1d89e17b0ec601c3cac8 GIT binary patch literal 1259 zcmVPx(rAb6VR9M61SKVtIRTTft-1*#(>F#DX+s4G$BsG?XR06RoQt(C5Vks62f<=PV zmr`4#zUZIeLlJ#Y1ocVqN%|!E;D6AU;>&_7OO_;?*<^M%&Ti)8-a9@x6J}?UxM@RQ zTsWNBJ9p0copXM3&K=JyrmOkT)(~ym+$FXkaiH>^Y5V zLI~#O<|><;o0k}4_eZfS9wGf7&(6+fY}*E7Y)?4nd)jqfluD(XuInS0U{nC*`T6oYW+0Pah7K+>3+d~d4%L1kJS4!z``*CApVq#3!^-mucfI2xj`Lg3UDZ(`xjm^Pe zaBp{a_ulI2s+r7yEX$B2=>-7lBLh$rGex{!uV(_N1E}|Uy>^m}oO4tv zm6L>!cODr)Hk-Xwtyb0D-Q6MMrfK?=((j{vl+qt9%VNna6^lh(mgSp|3SjE&*|Voj z(@aIyY&Ki2>wXt)^gQp|_4V~uJWGtRs69UoVCu*KG)=p?xVShTu|IO|I1U`gsRQW6 zwznO}S@V5AMaFWud`eN2kB$mJD3wZAB}oz}r9<^^wOW6Bo_8<#faiJNY;SKn$!w{r zDhPt`)h@Mf`5+(qA1E+TU%Mzb%#c^@B3I=Tf6Uh-jz|tac+8g zdb!)}?hz>h!5Bj(lgV<<0bp-Ex{h=t5ePvLAd2Gi0CE8ALjlOLeEt0S^W)8Ca}PiS zf>Mgr)z#EFAg&L(m&@fQJkNWNbN=6`OSWwbQTn1tD5bD0tLD1yE2GSKO6l*0 zVZ17eVsf@X5QH}Y+yTIbI6w$_{mhv&`7=vY514`*v;cF{~Lc#!~@H$!-D5YQY z`~95*=1YYT@>(hbilTg?X7;7f?RK{qV-Lcn6FC!NeIy8lhmrl!@As{IK5y*P zWAe6b-(`&b5(iK+4C6V^^B@QUqDB%m5yn^pz)1i~EMk%Xz(Sz_JjU2xl+tG;X_)7t zD1s33tnd2;09_$8bXrlAFM}YE6Dbw}h`#Ut0H75fF0@dq0OZiF55Nt@33Wx1qz}TZ zM45@MDW&u_fIp)MR3LDk@K>P*z!l;z(GvzBjQVjZ#%eAmNEp3EJV8Pp;{-p<_&-=2 V5w@i?!#)52002ovPDHLkV1kbNTbTd= literal 0 HcmV?d00001 diff --git a/images/text-align-bottom.png b/images/text-align-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..4dc59be1a7cebf1990fd3277d959f39cb2ef0b74 GIT binary patch literal 1263 zcmVPx(sYygZR9M61SIuu5RS^H}zWwya)^_YT4GE!1N~jv8C{hR@6p0H6Do{m5B_t{o zp>nB8BUCQ%Cvd0`96(6G35gTx3BiH?pqGe?C5)_CNt|`Ew!_-lkN0*tWY=2nCc$Z= zUa&Np_1m{IznS^%%v<39{^L%wIh)OXF+DwfQJ`_bkv)O#xG|hVe=$Qf_guJn`vNBey)zItp`c9*o z5Q5p++0xF=&Q-?PgJJCQr%3-NGcz+O(=@>t>x<`keQnz|3WY*ORn?(OFf4%L+}zw; ztJNBCcoYaqDO6RR0Z=+K0II64E-fvU8;wT7Ii)nQHZ?Uht|-c_GXs#z~@?F~3E3;hsbUJ;fTrSH82L}VjbzOHUr9XuGD5XCchQZ=l%IEW{D2lfq6~N@BOP4O_ zx}J!vUavQ7+x|Y>=s3=ITU%R=XqFgbVS9cWz~rd`D2j4>VPRo3WPj+~vMgAZRRz$A zY;RhYwduNUf{evt@w_BSADY%|aR6RiTwENDk}uSaMx(_Ts{oje3S;bHv)ODO)t)n%Oc}sSL!SpSnanS% ztE<=6*VhN8ogfHM6s7BVo*Uio`-jH^p6A8g-QBcp+XJK8bzN+3Za#1v=h`siI5Rah zwbX96`$P&sFvgHdrP7|~0YHB}ybg6JAqbA+!1Mg`05Sl~69I^#c=O7YE2H&#y$>J+ zK`F(?#zx{C5Y@-s%VaX+j^n)VdEPfCav+^ff23)eXj#^=Bljd7UkfvnN~J_X$gTe> z!R+PBmnUkq+CUnIjWWJxfNUHGNs^v&U3Vtvph*C-EZ?1+oE)iEs|ka;uJ7*e?^{vB zaVOz_S(fEoE|)lvXqr}b9B0jS-8)ACAR{9qS54F8!t{laP)cDKM#Z-6SB9DKl+r&m zO?#E+`S@%B$8m20SOdTYI6w$_{o=)oqw&_u7=vLLJxb}(JTk|ceqC`!83Y9(sgZnt+CV-JI-6FTD}eZ&vM3nKfo+wB_JY*ss> z$7D^@yvG>(H431hY1(s+VBz&}jmlLWvG`*F%gYR<<<7`{bbjD%i<6W&S2 Z{{>te5w?pI`s)Ay002ovPDHLkV1hi#U-JL} literal 0 HcmV?d00001 diff --git a/images/text-align-hcenter.png b/images/text-align-hcenter.png new file mode 100644 index 0000000000000000000000000000000000000000..1a767dac33051199e3203b6f2726ad1ad67f877e GIT binary patch literal 1336 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 bJMTW@o`YMT6+WLI0P>@!tDnm{r-UW|4yS)= literal 0 HcmV?d00001 diff --git a/images/title.png b/images/title.png new file mode 100644 index 0000000000000000000000000000000000000000..81449cc177fd7e7bbeb4ec948f18694498ae83ed GIT binary patch literal 46905 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 literal 0 HcmV?d00001 diff --git a/images/tool-boolean-merge-holes.png b/images/tool-boolean-merge-holes.png new file mode 100644 index 0000000000000000000000000000000000000000..6cd170e88f98b357727cf6d4ac121cb7a8f6b86f GIT binary patch literal 417 zcmV;S0bc%zP)kw!D2NXFt0Pc2tE6`5|c{L z6FKJ_|80M}4QQF9&DP?IT7Y$*wXUMox>0u4Ghq@yE^rX34KH(m6XAhB0<3Me72$ys z6Rmf+S_BEXj?-1jDJ2N2>)YyPs3%euGu=&7vi16btkB*ERtL`5=ZV%`Y_HBcMG(Au zCEG2c2k$nb9U?e5+X+{SklPR51{qEc@fyt-Gfdp%lRvpR$F5EEW`6q#_g*^uZ?3m!hCW5Vz}ITK#RC zGd|=tljQ96z+umx%*lN7W#-F7zzUaA3QO<%{l0)Lul6dA<-xxu3)~inSnT+(eSyxB z{)Vp+^zWUG84)Gt@?hqiIBx;~kUXcC8`Hzi+8{*c{PBc@2zkf?twaCIk$@g{*8(A! z^Zii{t3-d(^lnTKtCd0UBos9Ok_dw*Bvga7$=Cq^A;{Po3|Q)aapoJZ1eg$@hfiSB zGK~z>#zg=w`XoZkq3gRt005BtKl5d{I}Fvd2#8Qhp{#!Jg-uH>dDf#N>$tilNtu_a z9vy+KZi9UZh-0A6S@ts_FE>4U3bOj1=mXUTK1#bfiUEG?tbHKoqr`` zCb2UPjIp-^;B92}Et7Q%K$!Tke?~u{olK9Ofvj#KK=jV2L-O9U%Ipou-VFoMjz)z7 zN1NsGzagsVX~sx7EmIMfAgCzj;@b)ngJBVeyEdTzJO|mk<`Dy7t)@#rmUgJ_jPqC_tP*@~%t4P&Kz2VT z`H(!jGEF)n(;%1ycCbtaW3;r5Fro8R;8~aq@;c9Ix#kta#LIx53_w2mKp4zzqRRQ;>U?1%L@yY+M4rVYr@f?;T$nOn0W*?hn(~!0fRpLGukx ztlEyt&V{okB|1BEV{ggCUOh1@JNiyf&!FG$i-m2u-?@}hsAuQdxa_=%ed412g+*7swbYW@grCr?8gULb`c3<)JhJyZs2#1z* z@6fYL))}pRlCdC8*K2!y(%!$%+Omz#bm8c>J0SgFsjghc-+yfE3AZ}k1|T~x2<5R^ l(GQ=Nw&z>>(_=w%b|B7(3NaTw!%=x^I} zc}ORnq?4ZRw1F9VGU>cmm8w@2h7m&uL8Y5;QkC<`2u;Oh&wK1IZM zfbx$f*gg3ATTj*(&I1;dd=eU< zTYwtK?UMp>y z4UN1yb;d2H>pC)S_3jR2+x6Y{O%i(Jg;x=%t#WW3Q$HBU{ghW9g3f4ir4d39-#bnn zZ01-x67u@2*OnEPI(K#3dy#LPJr45bW40%pRckvU&-)h?0_4r-o$dMT>-F60`#-ej VqK!(wMxy`#002ovPDHLkV1l0_XaoQN literal 0 HcmV?d00001 diff --git a/images/tool-connect-paths.png b/images/tool-connect-paths.png new file mode 100644 index 0000000000000000000000000000000000000000..273e6f45ec8daeb10b666b4c9bed8b565db72871 GIT binary patch literal 1191 zcmV;Y1X%ltP)Px(VM#WU!(Qsz4_ofa8E0_ybr3?g8V#s407rbp=qg z!Cs&b2)%ji1Sfrm2(~)#9C{4^>(L6Cxg{p^kNEA^y8zR`fGK;f?g~KB22TO~KD3KbNygQ z<616qM75C;;kI*)KRc7hK=Z}7!88J4EM<0#gL zQpy`!0%)3c3b-8%1{;Sm`#G(gL%o=c7 zO8HDWUa6tc?{>Qz$esBBqQXH!!X?d|QP z)9E%LMA0w|vpTL}!+(8|NCdCfi>~VcLsH5sFMG(?{|@Q8j@RoY5{Y1!Gn)W3O;dr8 zq9~-(X)2Wpb}n}tmn)SD>2w-JQ2;`=*QOQd^!a?aTrRTNY<2ee-MZTvL)mN=m&=9E z=fgftHvtI0-w(k2{5*E4)hkA9O>7TQ3*flN;{jl4X$fGWjsO$3Cbq}%S^&+54TjtlLTGbUF!#!NSK;C=W`7Y##9}cD zg#xKm3Si*5x1nJerVv71SXgLNRh8A%RV>Tu6hf2@!x(=F^MPnIiV%XEH*d1MysS$p zwdVo=2qEq)FE4*wE|>Rqb#<|_vQmXOCWJ^ChVf{toC_ho1U`#KqjYq1Fg!fW%*@Oh zaMmykYg;WDa6K3d5|783nwmn__391zv?Y^L<~2>*)RH-!PGYeb?d|Q1jEs=UWB|6; zl5K#v=61UqRaM37^^#7f$!4?6&(Bw3{@zFp4LBSQT3T8N1OkM^VG4x;$z+mZvA71D zuVubvTWGL~hL%jK$dJ`D^=DR=iUsR6RBr`h^C|39{hzX02|(Sw@94qyNP002ovPDHLk FV1jVdI9>n% literal 0 HcmV?d00001 diff --git a/images/tool-convert-to-curves.png b/images/tool-convert-to-curves.png new file mode 100644 index 0000000000000000000000000000000000000000..bfda2b77222f5d45b6c7352f9754987a8ba0b663 GIT binary patch literal 1557 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 QtN;K207*qoM6N<$g47g$^8f$< literal 0 HcmV?d00001 diff --git a/images/tool-cutout-physical.png b/images/tool-cutout-physical.png new file mode 100644 index 0000000000000000000000000000000000000000..adc8029197cb89a37ae87ee0d014caa2a49d1bda GIT binary patch literal 1928 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 ilD}&Cy%mD{KG&03B&m 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 literal 0 HcmV?d00001 diff --git a/images/tool-measure.png b/images/tool-measure.png new file mode 100644 index 0000000000000000000000000000000000000000..1b500cbcf915bedce1effa95b71ecd30e5af9501 GIT binary patch literal 1574 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 Wy*OQewa(fA0000cV0Lz zyfG4uSL!k%FQyTZneL*TKZF4Xl`oc=x!_3ChfZC zJ~A2SqI(q2tzH@w_c@{QQCAep3n9xV{_~uPPvQ@uDYx^gvyL5=#az7|qQ^VQNMHgn z99nt{xdNrZM75=qR+|auPBVAzhWYgB5Q*R*Z)pGIapmNi5VEzzFCa)X=PU6c>Kh*BNQ;F|myd3@2Zs>id4nIbLR@j=f-HksA!1gD z>{U+je9F)HcopX^hZqVraiqn{w>UZ9cbghTf<4M83tA2!3y@tM@mh4{Hr_t6oijgr z@&9&(6Pg5vRb%;5jO8z3R*1Z=8>CK*^1VAihQpkCx}LYD5_ANk>{0%);-RAD`Q90` zFol6r&N^Iu)~jpn9Cc2pK~;l==V`8Y4@MhnX4h40!I_5kpTZWOzGK7Mvo7Sr6+$aQJk z;?D;FhkWr<*Q}8k8jkL^a`koyF&v^z>n})rJ%~1~A2A%_>g^CmcUy^}p=;KdeI^Ug zEtODFTTit~OOJOFyL=p5(QLuu`5}z@O67Tr*EL5}2S> znBKH_K8RLfnvuW+0L?W<{3(4dKm-*HRs#TYkx3eb=%%__9tameqY%v&t_G`t2&!BF z2_akCGwBqStMdKdtQtl3DzO+fNGNjwR`;bMG7k(OnbY$!xc~xl1ZEOTd7V-}Fo3%$ z6*9RZ+c^cKTmS_afcDKmtgebZQVG?TQUE-|GgK*}xd3~GY3}(37@3UIYBMpIF2kEtZyI8? zxHqY0FkMEg&BVxLoO`|j_AaIP62L+A3_22AbdTcPIp0z{;#JH4dqaq|+*C)r3RSTg z=T0*h-J|G8a8Nz70>HIcF!l>$cy9G_=Wdwe`|24?m+@K5S`frq;-AN?45rICzOSA; zcf)vY^|D_WThUP$4M{ujF2Kt(k1$u>$VV?V(dqILbbo_gm~SkFwBzOCJ}E`%u4g&% zq>Z<}yoEB@%jd>yJASX`{m%9rKhUQ2@j+0}hnF=x(_-NU$LMl<@ZHzZru8rOhXeU+ zx;Lq&BVNT|x{SwmHS$D58K*Dx5es^GP3z0quK;*+e+xh@P*b6pLzWR48S~;zT6q1C zoh>D$bO+^JiEQEKoSOS-IZ9cYt@0#dM#f($H7?P}*+d2BMLV%AwY=zT;nXs5KDx7wQT2`h0000Eaj?aro`DK)z-L0T=eQw%nZa z8krX!7u?e?cQEHsY{#YhQU{ZAyX4QzHz<^U;U{+Dl$TD~7E9k?%f82tZU=ki&g(V2 zSMH%1c)fJPvBw+>G`g4$C@sCShX1gDj@aXFwutMZ3>Uud{asS+@}wgpS-{EQSOn_< zHvXBvI96e{p^aSVw#{C1i*-vI>JzOdL1*n2KC^ z{ma;u4s#@}FVdQ&MBb@0QtL4;Q#;t literal 0 HcmV?d00001 diff --git a/images/tool-touch-cursor.png b/images/tool-touch-cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0c418e5cd4d44aa15c754bc884aad6c8435c6a GIT binary patch literal 1167 zcmV;A1aSL_P)ne~Dvk6q;#k?SM?ig%FSok%U0;+O@$ho*tiHL5;Cro z5VcTnkr>1pvJmRRE<~=YmUbEv$tE$-b|K`MZqh*~qLadO^3!zY-E&>co%oo{@6(Dt z7>1d5?>XQ3x!?T`Lk*8*S=RxasIY9EbAGB?o2&|_9{}iAKmF=ERbqMJ`%9ej3!VX3 zmUSM$5CE#iXc4d)fEmvDcufG5p-}*aa$eR*1@IaG2f%bO;ChjblWL=`Hh#uA{{%HY zwrwwjLLmddYbub|P$=|*ZQHSu8Bmdr0oVm_gL8fk|G;Be)>i|Fp3=lp=9o}Qjp_V)HZW{kZ(H#c|RV*+e}2+rlzJ=A;h)6QH^QkV@UbjFG0Vn+lwBt<>h5LI5?Q;=;&x| zZ*Tw3i+5#vM)~S5?15Cdqo(URlAD{Dn3zCYTiYA0k;pq9L5#bJ%uw{|FT-xRW?>8r z64^`^BO@b+(&_ZnV?N)_#l^*29?RjkEbF@F;`U3G07wZ4a?p7AFa`z&o^EJpxY^y^ z{kR9`d0!_qSNbnh+C@Yl0y0h(EiEnR>+6H2X?8FetoP6hsbV}(1pomeiS4Z|SiQYC z+u#3UAQ1SX>Bx~oUg}^}eQh!gQWAu4z*r7m!^3E7Y@Bay zZf<=<8DJm~grNNl2$B2j?(X8)(W8jIAN8Gl>Eui}91he9z@Mkak_$Brk|anH1d*!@ z1d@b@nGB8}JB~ANoq0772z=Dl)#a<8vix}f>n=JjUjdRJN2urpNaRk1AOR@>pU*%f z5JYVWQ$T4B=1TZPV7=zDeU}NJqOkKW=wL5pgwyiU0z!Hrb>l9 zW`_}E}>{UCSNF?Uh*4BR5+k3FGx@!Mi!>P+Fu8a8~ zSqa7RJ%A|ZJmr#P&*-}T^3KjqA|8(?T6Qk5Z97&(&)3jN5&vp3=Xo2yjp{$HjMoWQ8Y47Fs_P(5#-{T+cy=VU(0=Vr23Vn)*n=t&##vteH1lGz-Hp)1QD^r zHWv@PAaR6q8t{8Dyt;O)o|-Ozlxjp>d*mDC(WOrnj*Be=;v5?9`Hhx`BKt8w4(JRZ4cn-0$iU~jub!Zz&-7OsDwq4}#34f;`k2mr+g z^(FyaFdk~I68J-4#t^FtyX>QgQ_tw&zx_*nhcccw`e^GWeQ{aX4OS|6}Qz#}-6Ce5dV?@56Wl`02MnQvgJ2cYp?{ra?1kFp8f! zfpJd*LH=2G*~jqA8tpywT>rcX%U5jFYub0oPS~F;%YR#R5l1QaW^rj^@#nODKSnGzZxMj0m z&ibsmeD=-P&K~_;+Nt^?c4YPc7O02sWde6Rwk|Pz;lWF1kAJK8P{+mhj%KHHeH51Wrp|^FbzWS*x zx-%Z5F)B-KQGQMAdU3LcmCKw@9=7=S%ni0U{R(0)WecVmQzx-}zM=F~R5vtmN znsJJgux-0$TOQIK8f2dsIJqxng&IJ@s7d04K;a{^0GP0rE&77-yw9FF`r6g_KMff}Ak zUA^Logu$vHGd7F}@5~B7Cd?8M`_o$(=di&4W;JWPYzd#5mEBqe_XMLh-EI|W2*AN>@ z_?FKvuhS}QL?R-SP&t>Pd9f3XB8fyk8x_#In{a;i1P7Ni)zy|)g@VD;Bi6`B=IPHo z%#Q)1swSuD6o^|z;-y?Df=MLu!nx6WeH(3+)gd<&F{g$GV)Nti%z+)d*~P@igiLPK zsH%#p=5}rui4|WL{#FQ*GXt1szsuUDrPd>L_2E=1?nXz4&B^clf~Vd(PAa!%(gRgB zIe;4pPiHQGxqT7#laI2lv4PcqSg@y(s=yL1V+P&hYN)u1o}rFLB^3fDwn z^(rdv`8YLqHF5hbbu9FK#$qvs_8#QKuU_Fwv1q|{kUxUS9Jmq0HHlbNgD5-j;W zPyjO(Kz><~cy7!`pjdR;08>Df5}#0>;RF8*=LG0fU`oJY00000NkvXXu0mjfTzv$L literal 0 HcmV?d00001 diff --git a/images/view-show-all.png b/images/view-show-all.png new file mode 100644 index 0000000000000000000000000000000000000000..00e6b83cc005014c81fa510adbe3c9b7d8379adb GIT binary patch literal 1256 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 literal 0 HcmV?d00001 diff --git a/images/window-new.png b/images/window-new.png new file mode 100644 index 0000000000000000000000000000000000000000..e091702e33f9ff7debfba272eb848dbd246acf84 GIT binary patch literal 671 zcmV;Q0$}}#P)%-6o#L3=gzE*u)}6qBJ!a`AQ&5ovCzUFVPYdzR>Z=D zA46hCVPZ@yXzi@j*3yJXC`7WFk#(1KXLp%2D7{&Nk&>)AX#kpOaZc9{OZv$9aS$Uo$$znHwa?v*m`69XD65&7%5v9=xC|8Dv zck)`Q%)VG!diux$)a&)d<>h4&>5{*GhV&h{58x`GHP9wV1SX5hth{`+`4HF#P*o8T z?md2nQ|;LQ=FA4S=RdJ9x6ODV#Ix5BlOf$JlGNUF`I1jADvK(iJ^JGKXtrbXj9+JV zGG%(K!9<}+aN!2lzlF#UY4amSnxNZU3>9hvxi)~QvEhyZ!c2v+k$_xQur`b56|nw1 z#!iD)ux=PM#(F+9H24Fc(+LD#Ks1yk2tqs)BT`1)CazgRh%xdLBtz;9cuDm)fU5H3 z!JW?B50o0j?Lk@v*#M(J25gbQY=JCLt?t4g-`fDR3kPt&*;Rxuh?K#tBl;Ey03T%@ zWQMI`j0k&eBj_4{OaT>CKum;c45T1~AQNEjQu^@*fVKpjbKL;|;5smZFo{SEI8;Va znZuI;&~pHQe5(|Y{YAl;!=!t222Rvr@1s6FPT(&n=Kv=G9DmR+ot*^E1VFz=aCQ?o zb%65`{Ffgh(|5;XvH&8Y&N+#qs4u3@IYdN(yafi*G+kR=UA^D6o6$d{sw7GB8i)=Y z2Z0eF3xt4AZ^qMXtqHILd_BHp(<+bN+?-vjQd+-AzX0iyL}k`Ue{BE&002ovPDHLk FV1isSCa3@a literal 0 HcmV?d00001 diff --git a/iwyu-mapper.imp b/iwyu-mapper.imp new file mode 100644 index 0000000..0eda1f1 --- /dev/null +++ b/iwyu-mapper.imp @@ -0,0 +1,29 @@ +[ + { ref: "iwyu-qt.imp" }, + + # Mapper uses -DQT_USE_QSTRINGBUILDER + { include: [ "", "public", "", "public" ] }, + + { include: [ "", "public", "", "public" ] }, + { symbol: [ "M_PI", "private", "", "public" ] }, + + { include: [ "\"util/qasconst.h\"", "private", "\"util/backports.h\"", "public" ] }, + { include: [ "\"util/qoverload.h\"", "private", "\"util/backports.h\"", "public" ] }, + + { include: [ "\"qtsingleapplication.h\"", "private", "", "public" ] }, + + { include: [ "\"ogr_core.h\"", "private", "", "public" ] }, + + # ? + { include: [ "", "private", "", "public" ] }, + + # libstdc++ debugging headers, for use with -D_GLIBCXX_DEBUG + { include: [ "", "private", "", "public" ] }, + # Seems to confuse iwyu: { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + +] diff --git a/iwyu-qt.imp b/iwyu-qt.imp new file mode 100644 index 0000000..8e6712d --- /dev/null +++ b/iwyu-qt.imp @@ -0,0 +1,1174 @@ +[ +# --- Generated by make_iwyu_qt_imp.sh --- + + + # QtCLucene + { include: [ "", "private", "", "public" ] }, + + # QtConcurrent + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtCore + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QJsonParseError", "private", "", "public" ] }, + { symbol: [ "QSignalBlocker", "private", "", "public" ] }, + { symbol: [ "QTextStreamFunction", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSetIterator", "private", "", "public" ] }, + { symbol: [ "QObjectList", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QXmlStreamNotationDeclaration", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QHashDummyValue", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QModelIndexList", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAbstractListModel", "private", "", "public" ] }, + { symbol: [ "QChildEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QXmlStreamEntityDeclaration", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QJsonValueRef", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAssociativeIterable", "private", "", "public" ] }, + { symbol: [ "QRegularExpressionMatch", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMetaTypeId", "private", "", "public" ] }, + { symbol: [ "Q_PID", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMutableListIterator", "private", "", "public" ] }, + { symbol: [ "QRegularExpressionMatchIterator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QScopedArrayPointer", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAnimationDriver", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QFutureWatcherBase", "private", "", "public" ] }, + { symbol: [ "QMutableByteArrayListIterator", "private", "", "public" ] }, + { symbol: [ "QMetaClassInfo", "private", "", "public" ] }, + { symbol: [ "QStringDataPtr", "private", "", "public" ] }, + { symbol: [ "QCollatorSortKey", "private", "", "public" ] }, + { symbol: [ "QMutableVectorIterator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QByteArrayList", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QXmlStreamEntityDeclarations", "private", "", "public" ] }, + { symbol: [ "QXmlStreamStringRef", "private", "", "public" ] }, + { symbol: [ "QTimerEvent", "private", "", "public" ] }, + { symbol: [ "QXmlStreamNamespaceDeclaration", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QCharRef", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTypeInfoMerger", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMutableHashIterator", "private", "", "public" ] }, + { symbol: [ "QBBSystemLocaleData", "private", "", "public" ] }, + { symbol: [ "QLinkedListIterator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextEncoder", "private", "", "public" ] }, + { symbol: [ "QListIterator", "private", "", "public" ] }, + { symbol: [ "QMetaEnum", "private", "", "public" ] }, + { symbol: [ "QLatin1Char", "private", "", "public" ] }, + { symbol: [ "QVariantMap", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QUrlTwoFlags", "private", "", "public" ] }, + { symbol: [ "QScopedPointerDeleteLater", "private", "", "public" ] }, + { symbol: [ "QAbstractTableModel", "private", "", "public" ] }, + { symbol: [ "QJsonValueRefPtr", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QPersistentModelIndex", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QModelIndex", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMapNodeBase", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QListData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QFlag", "private", "", "public" ] }, + { symbol: [ "QFutureIterator", "private", "", "public" ] }, + { symbol: [ "QDeferredDeleteEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QByteRef", "private", "", "public" ] }, + { symbol: [ "QMetaProperty", "private", "", "public" ] }, + { symbol: [ "QScopedPointerArrayDeleter", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStaticArrayData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QLineF", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QInternal", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QXmlStreamNotationDeclarations", "private", "", "public" ] }, + { symbol: [ "QVectorIterator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QProcessEnvironment", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMessageLogger", "private", "", "public" ] }, + { symbol: [ "QXmlStreamAttribute", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMapIterator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QExplicitlySharedDataPointer", "private", "", "public" ] }, + { symbol: [ "QPointF", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QThreadStorageData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QBitRef", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGenericReturnArgument", "private", "", "public" ] }, + { symbol: [ "QEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMutableFutureIterator", "private", "", "public" ] }, + { symbol: [ "QXmlStreamWriter", "private", "", "public" ] }, + { symbol: [ "QJsonValuePtr", "private", "", "public" ] }, + { symbol: [ "QLatin1String", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStaticStringData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAtomicInteger", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMultiMap", "private", "", "public" ] }, + { symbol: [ "QSizeF", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSharedDataPointer", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QVariantList", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextStreamManipulator", "private", "", "public" ] }, + { symbol: [ "QReturnArgument", "private", "", "public" ] }, + { symbol: [ "QStaticPlugin", "private", "", "public" ] }, + { symbol: [ "QAtomicInt", "private", "", "public" ] }, + { symbol: [ "QStringRef", "private", "", "public" ] }, + { symbol: [ "QDate", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMetaTypeIdQObject", "private", "", "public" ] }, + { symbol: [ "QMutableSetIterator", "private", "", "public" ] }, + { symbol: [ "QMutableStringListIterator", "private", "", "public" ] }, + { symbol: [ "QTime", "private", "", "public" ] }, + { symbol: [ "QContiguousCacheData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QObjectData", "private", "", "public" ] }, + { symbol: [ "QMetaObject", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QUnhandledException", "private", "", "public" ] }, + { symbol: [ "QIntegerForSize", "private", "", "public" ] }, + { symbol: [ "QMutableMapIterator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QFunctionPointer", "private", "", "public" ] }, + { symbol: [ "QVariantComparisonHelper", "private", "", "public" ] }, + { symbol: [ "QMetaTypeId2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStaticByteArrayData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QEnableSharedFromThis", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMapNode", "private", "", "public" ] }, + { symbol: [ "QMultiHash", "private", "", "public" ] }, + { symbol: [ "QTextDecoder", "private", "", "public" ] }, + { symbol: [ "QLatin1Literal", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QListSpecialMethods", "private", "", "public" ] }, + { symbol: [ "QItemSelectionRange", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QContiguousCacheTypedData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QVariantHash", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QLinkedListNode", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDebugStateSaver", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAtomicPointer", "private", "", "public" ] }, + { symbol: [ "QEventLoopLocker", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMutexLocker", "private", "", "public" ] }, + { symbol: [ "QWeakPointer", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QHashData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGenericArgument", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QNoDebug", "private", "", "public" ] }, + { symbol: [ "QMapDataBase", "private", "", "public" ] }, + { symbol: [ "QRectF", "private", "", "public" ] }, + { symbol: [ "QHashIterator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QForeachContainer", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QArgument", "private", "", "public" ] }, + { symbol: [ "QMarginsF", "private", "", "public" ] }, + { symbol: [ "QXmlStreamReader", "private", "", "public" ] }, + { symbol: [ "QItemSelection", "private", "", "public" ] }, + { symbol: [ "QBasicMutex", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QScopedPointerPodDeleter", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QHashNode", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QXmlStreamEntityResolver", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QIncompatibleFlag", "private", "", "public" ] }, + { symbol: [ "QScopedPointerObjectDeleteLater", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMutableLinkedListIterator", "private", "", "public" ] }, + { symbol: [ "QMetaMethod", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QByteArrayData", "private", "", "public" ] }, + { symbol: [ "QWriteLocker", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QXmlStreamNamespaceDeclarations", "private", "", "public" ] }, + { symbol: [ "QStringData", "private", "", "public" ] }, + { symbol: [ "QByteArrayListIterator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QArrayDataPointerRef", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QFileInfoList", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QObjectUserData", "private", "", "public" ] }, + { symbol: [ "QScopedPointerDeleter", "private", "", "public" ] }, + { symbol: [ "QLinkedListData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QReadLocker", "private", "", "public" ] }, + { symbol: [ "QMessageLogContext", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStaticAssertFailure", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMapData", "private", "", "public" ] }, + { symbol: [ "QSequentialIterable", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDynamicPropertyChangeEvent", "private", "", "public" ] }, + { symbol: [ "QByteArrayDataPtr", "private", "", "public" ] }, + { symbol: [ "QFutureInterfaceBase", "private", "", "public" ] }, + { symbol: [ "QXmlStreamAttributes", "private", "", "public" ] }, + { symbol: [ "QStringListIterator", "private", "", "public" ] }, + + # QtDBus + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDBusSignature", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDBusVariant", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDBusAbstractInterfaceBase", "private", "", "public" ] }, + { symbol: [ "QDBusObjectPath", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDBusPendingReplyData", "private", "", "public" ] }, + { symbol: [ "QDBusPendingCallWatcher", "private", "", "public" ] }, + + # QtDesigner + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDesignerComponents", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtDesignerComponents + { include: [ "", "private", "", "public" ] }, + + # QtGui + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QExposeEvent", "private", "", "public" ] }, + { symbol: [ "QTextBlock", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QPictureIO", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleInterface", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QRegularExpressionValidator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextBlockFormat", "private", "", "public" ] }, + { symbol: [ "QDragLeaveEvent", "private", "", "public" ] }, + { symbol: [ "QBrushData", "private", "", "public" ] }, + { symbol: [ "QIconEngineV2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextImageFormat", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMatrix2x3", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextBlockUserData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QOpenGLContextGroup", "private", "", "public" ] }, + { symbol: [ "QOpenGLVersionProfile", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QPaintEvent", "private", "", "public" ] }, + { symbol: [ "QHelpEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QOpenGLFramebufferObjectFormat", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QEnterEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QHideEvent", "private", "", "public" ] }, + { symbol: [ "QGradientStop", "private", "", "public" ] }, + { symbol: [ "QFocusEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QWidgetSet", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextListFormat", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDropEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMatrix4x3", "private", "", "public" ] }, + { symbol: [ "QMoveEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextTableFormat", "private", "", "public" ] }, + { symbol: [ "QAccessibleTextRemoveEvent", "private", "", "public" ] }, + { symbol: [ "QImageTextKeyLang", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QWhatsThisClickedEvent", "private", "", "public" ] }, + { symbol: [ "QAccessibleTableModelChangeEvent", "private", "", "public" ] }, + { symbol: [ "QPainterPathStroker", "private", "", "public" ] }, + { symbol: [ "QAccessibleTextCursorEvent", "private", "", "public" ] }, + { symbol: [ "QTextFrameLayoutData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QOpenGLFunctionsPrivate", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleTextSelectionEvent", "private", "", "public" ] }, + { symbol: [ "QAccessibleImageInterface", "private", "", "public" ] }, + { symbol: [ "QTextItem", "private", "", "public" ] }, + { symbol: [ "QOpenGLShader", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGradient", "private", "", "public" ] }, + { symbol: [ "QCloseEvent", "private", "", "public" ] }, + { symbol: [ "QActionEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleEvent", "private", "", "public" ] }, + { symbol: [ "QConicalGradient", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleTextInterface", "private", "", "public" ] }, + { symbol: [ "QAccessibleTextInsertEvent", "private", "", "public" ] }, + { symbol: [ "QTextCharFormat", "private", "", "public" ] }, + { symbol: [ "QRegExpValidator", "private", "", "public" ] }, + { symbol: [ "QAbstractUndoItem", "private", "", "public" ] }, + { symbol: [ "QAccessibleTableCellInterface", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QIconDragEvent", "private", "", "public" ] }, + { symbol: [ "QTextFrame", "private", "", "public" ] }, + { symbol: [ "QScrollPrepareEvent", "private", "", "public" ] }, + { symbol: [ "QWheelEvent", "private", "", "public" ] }, + { symbol: [ "QDragEnterEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QNativeGestureEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextTableCell", "private", "", "public" ] }, + { symbol: [ "QInputMethodQueryEvent", "private", "", "public" ] }, + { symbol: [ "QMatrix2x4", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMatrix3x3", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QInputEvent", "private", "", "public" ] }, + { symbol: [ "QAccessibleEditableTextInterface", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QResizeEvent", "private", "", "public" ] }, + { symbol: [ "QImageCleanupFunction", "private", "", "public" ] }, + { symbol: [ "QLinearGradient", "private", "", "public" ] }, + { symbol: [ "QPlatformSurfaceEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QWidgetMapper", "private", "", "public" ] }, + { symbol: [ "QAccessibleStateChangeEvent", "private", "", "public" ] }, + { symbol: [ "QInputMethodEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextLength", "private", "", "public" ] }, + { symbol: [ "QHoverEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QRadialGradient", "private", "", "public" ] }, + { symbol: [ "QTabletEvent", "private", "", "public" ] }, + { symbol: [ "QIntValidator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDoubleValidator", "private", "", "public" ] }, + { symbol: [ "QDragMoveEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleTableInterface", "private", "", "public" ] }, + { symbol: [ "QFileOpenEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStatusTipEvent", "private", "", "public" ] }, + { symbol: [ "QTextInlineObject", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMatrix3x2", "private", "", "public" ] }, + { symbol: [ "QTextLine", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QOpenGLDebugMessage", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMatrix3x4", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QToolBarChangeEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleTextUpdateEvent", "private", "", "public" ] }, + { symbol: [ "QWidgetList", "private", "", "public" ] }, + { symbol: [ "QTextTableCellFormat", "private", "", "public" ] }, + { symbol: [ "QShowEvent", "private", "", "public" ] }, + { symbol: [ "QAccessibleBridgePlugin", "private", "", "public" ] }, + { symbol: [ "QScrollEvent", "private", "", "public" ] }, + { symbol: [ "QShortcutEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QOpenGLDebugLogger", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextFrameFormat", "private", "", "public" ] }, + { symbol: [ "QAccessibleValueInterface", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleActionInterface", "private", "", "public" ] }, + { symbol: [ "QFontMetricsF", "private", "", "public" ] }, + { symbol: [ "QTouchEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QApplicationStateChangeEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleValueChangeEvent", "private", "", "public" ] }, + { symbol: [ "QWindowList", "private", "", "public" ] }, + { symbol: [ "QPaintEngineState", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTextBlockGroup", "private", "", "public" ] }, + { symbol: [ "QPolygonF", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QOpenGLTimeMonitor", "private", "", "public" ] }, + { symbol: [ "QTextFragment", "private", "", "public" ] }, + { symbol: [ "QWindowStateChangeEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAccessibleApplication", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMatrix2x2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QScreenOrientationChangeEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStandardItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QContextMenuEvent", "private", "", "public" ] }, + { symbol: [ "QImageIOPlugin", "private", "", "public" ] }, + { symbol: [ "QMouseEvent", "private", "", "public" ] }, + { symbol: [ "QTextObjectInterface", "private", "", "public" ] }, + { symbol: [ "QKeyEvent", "private", "", "public" ] }, + { symbol: [ "QMatrix4x2", "private", "", "public" ] }, + { symbol: [ "QGradientStops", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtHelp + { symbol: [ "QHelpContentItem", "private", "", "public" ] }, + { symbol: [ "QHelpIndexModel", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QHelpContentModel", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QHelpSearchQuery", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QHelpGlobal", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtNetwork + { symbol: [ "QDnsServiceRecord", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDnsDomainNameRecord", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QNetworkProxyQuery", "private", "", "public" ] }, + { symbol: [ "QNetworkConfigurationManager", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QNetworkProxyFactory", "private", "", "public" ] }, + { symbol: [ "QHttpPart", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "Q_IPV6ADDR", "private", "", "public" ] }, + { symbol: [ "QNetworkAddressEntry", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QIPv6Address", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDnsMailExchangeRecord", "private", "", "public" ] }, + { symbol: [ "QNetworkCacheMetaData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDnsTextRecord", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDnsHostAddressRecord", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtOpenGL + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGLFunctionsPrivate", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGLFramebufferObjectFormat", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGLShader", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGLWidget", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGLContext", "private", "", "public" ] }, + { symbol: [ "QGLFormat", "private", "", "public" ] }, + + # QtOpenGLExtensions + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtPlatformHeaders + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtPlatformSupport + { include: [ "", "private", "", "public" ] }, + + # QtPositioning + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtPrintSupport + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtQml + { symbol: [ "QQmlProperties", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QQmlAttachedPropertiesFunc", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QQmlImageProviderBase", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QQmlTypesExtensionInterface", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QQmlListReference", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QJSValueList", "private", "", "public" ] }, + { symbol: [ "QQmlListProperty", "private", "", "public" ] }, + { symbol: [ "QQmlTypeInfo", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QQmlIncubationController", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QQmlDebuggingEnabler", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtQmlDevTools + { include: [ "", "private", "", "public" ] }, + + # QtQuick + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSGGeometryNode", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSGMaterialType", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QQuickTextureFactory", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSGBasicGeometryNode", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSGOpaqueTextureMaterial", "private", "", "public" ] }, + { symbol: [ "QSGTransformNode", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSGMaterialShader", "private", "", "public" ] }, + { symbol: [ "QQuickTransform", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSGNodeVisitor", "private", "", "public" ] }, + { symbol: [ "QSGOpacityNode", "private", "", "public" ] }, + { symbol: [ "QSGSimpleMaterialShader", "private", "", "public" ] }, + { symbol: [ "QSGSimpleMaterialComparableMaterial", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSGRootNode", "private", "", "public" ] }, + { symbol: [ "QSGClipNode", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSGDynamicTexture", "private", "", "public" ] }, + + # QtQuickParticles + { include: [ "", "private", "", "public" ] }, + + # QtQuickTest + { include: [ "", "private", "", "public" ] }, + + # QtQuickWidgets + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtSql + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSqlDriverCreatorBase", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSqlRelation", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSqlDriverCreator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtTest + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtUiPlugin + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtUiTools + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtWidgets + { symbol: [ "QTableWidgetItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionViewItemV4", "private", "", "public" ] }, + { symbol: [ "QTapAndHoldGesture", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMacNativeWidget", "private", "", "public" ] }, + { symbol: [ "QStyleOptionTitleBar", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsPolygonItem", "private", "", "public" ] }, + { symbol: [ "QStyleOptionTabBarBase", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionToolBar", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QItemEditorCreator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDoubleSpinBox", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionToolBox", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsEllipseItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QSplitterHandle", "private", "", "public" ] }, + { symbol: [ "QGraphicsDropShadowEffect", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsSceneHelpEvent", "private", "", "public" ] }, + { symbol: [ "QGraphicsObject", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsSceneDragDropEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QHBoxLayout", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsItemGroup", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsAnchor", "private", "", "public" ] }, + { symbol: [ "QTileRules", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionButton", "private", "", "public" ] }, + { symbol: [ "QStyleOptionFrameV2", "private", "", "public" ] }, + { symbol: [ "QGraphicsSceneMoveEvent", "private", "", "public" ] }, + { symbol: [ "QStyleOptionHeader", "private", "", "public" ] }, + { symbol: [ "QWidgetItemV2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QItemEditorCreatorBase", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionProgressBar", "private", "", "public" ] }, + { symbol: [ "QTreeWidgetItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleHintReturnMask", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionComboBox", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionTabWidgetFrameV2", "private", "", "public" ] }, + { symbol: [ "QWidgetData", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsBlurEffect", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDateEdit", "private", "", "public" ] }, + { symbol: [ "QPanGesture", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QWidgetItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionSlider", "private", "", "public" ] }, + { symbol: [ "QSwipeGesture", "private", "", "public" ] }, + { symbol: [ "QStyleOptionViewItem", "private", "", "public" ] }, + { symbol: [ "QGraphicsOpacityEffect", "private", "", "public" ] }, + { symbol: [ "QPinchGesture", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsTextItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionComplex", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionTabWidgetFrame", "private", "", "public" ] }, + { symbol: [ "QStyleOptionSizeGrip", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionFrame", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsRotation", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionTabV2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsRectItem", "private", "", "public" ] }, + { symbol: [ "QGestureEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QUndoCommand", "private", "", "public" ] }, + { symbol: [ "QStandardItemEditorCreator", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionFocusRect", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionMenuItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QVBoxLayout", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionViewItemV2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsSceneResizeEvent", "private", "", "public" ] }, + { symbol: [ "QStyleOptionDockWidgetV2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionTab", "private", "", "public" ] }, + { symbol: [ "QGraphicsPathItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QMacCocoaViewContainer", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsPixmapItem", "private", "", "public" ] }, + { symbol: [ "QGraphicsSceneHoverEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionDockWidget", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionToolButton", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleHintReturnVariant", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionGroupBox", "private", "", "public" ] }, + { symbol: [ "QGraphicsScale", "private", "", "public" ] }, + { symbol: [ "QWizardPage", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsSimpleTextItem", "private", "", "public" ] }, + { symbol: [ "QStyleOptionTabBarBaseV2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsLineItem", "private", "", "public" ] }, + { symbol: [ "QStyleOptionToolBoxV2", "private", "", "public" ] }, + { symbol: [ "QSpacerItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsSceneWheelEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionFrameV3", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionTabV3", "private", "", "public" ] }, + { symbol: [ "QStyleHintReturn", "private", "", "public" ] }, + { symbol: [ "QTapGesture", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionViewItemV3", "private", "", "public" ] }, + { symbol: [ "QListWidgetItem", "private", "", "public" ] }, + { symbol: [ "QPlainTextDocumentLayout", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTimeEdit", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionSpinBox", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsSceneContextMenuEvent", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QGraphicsColorizeEffect", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QStyleOptionGraphicsItem", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QTableWidgetSelectionRange", "private", "", "public" ] }, + { symbol: [ "QStyleOptionProgressBarV2", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QAbstractGraphicsShapeItem", "private", "", "public" ] }, + { symbol: [ "QGraphicsSceneMouseEvent", "private", "", "public" ] }, + { symbol: [ "QStyleOptionRubberBand", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + + # QtXml + { symbol: [ "QDomNotation", "private", "", "public" ] }, + { symbol: [ "QXmlDeclHandler", "private", "", "public" ] }, + { symbol: [ "QXmlParseException", "private", "", "public" ] }, + { symbol: [ "QXmlNamespaceSupport", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { symbol: [ "QDomNamedNodeMap", "private", "", "public" ] }, + { symbol: [ "QXmlEntityResolver", "private", "", "public" ] }, + { symbol: [ "QXmlAttributes", "private", "", "public" ] }, + { symbol: [ "QDomElement", "private", "", "public" ] }, + { symbol: [ "QDomDocumentType", "private", "", "public" ] }, + { symbol: [ "QDomEntityReference", "private", "", "public" ] }, + { symbol: [ "QDomDocument", "private", "", "public" ] }, + { symbol: [ "QDomImplementation", "private", "", "public" ] }, + { symbol: [ "QXmlSimpleReader", "private", "", "public" ] }, + { symbol: [ "QDomNode", "private", "", "public" ] }, + { symbol: [ "QXmlErrorHandler", "private", "", "public" ] }, + { symbol: [ "QDomDocumentFragment", "private", "", "public" ] }, + { symbol: [ "QXmlLexicalHandler", "private", "", "public" ] }, + { symbol: [ "QDomComment", "private", "", "public" ] }, + { symbol: [ "QDomCharacterData", "private", "", "public" ] }, + { symbol: [ "QXmlContentHandler", "private", "", "public" ] }, + { symbol: [ "QXmlDTDHandler", "private", "", "public" ] }, + { symbol: [ "QDomText", "private", "", "public" ] }, + { symbol: [ "QDomAttr", "private", "", "public" ] }, + { symbol: [ "QDomNodeList", "private", "", "public" ] }, + { symbol: [ "QDomCDATASection", "private", "", "public" ] }, + { symbol: [ "QDomProcessingInstruction", "private", "", "public" ] }, + { symbol: [ "QXmlDefaultHandler", "private", "", "public" ] }, + { symbol: [ "QXmlLocator", "private", "", "public" ] }, + { symbol: [ "QXmlReader", "private", "", "public" ] }, + { symbol: [ "QXmlInputSource", "private", "", "public" ] }, + { symbol: [ "QDomEntity", "private", "", "public" ] }, + +# --- Manual additions --- + + # convenience + { include: [ "", "public", "", "public" ] }, + { include: [ "", "public", "", "public" ] }, + # http://doc.qt.io/qt-5/qt.html + { include: [ "", "private", "", "public" ] }, + # http://doc.qt.io/qt-5/qtglobal.html + { include: [ "", "public", "", "public" ] }, + { include: [ "", "public", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + # http://doc.qt.io/qt-5/qevent.html + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + # http://doc.qt.io/qt-5/qmetaobject.html + { include: [ "", "private", "", "public" ] }, + # http://doc.qt.io/qt-5/qobject.html + { include: [ "", "private", "", "public" ] }, + +] diff --git a/make_iwyu_qt_imp.sh b/make_iwyu_qt_imp.sh new file mode 100755 index 0000000..1bf0cf2 --- /dev/null +++ b/make_iwyu_qt_imp.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +echo "[" +echo "# --- Generated by ${0##*/} ---\n" +LAST_MODULE="" +for I in $(find Q* -type f -name "Q*") +do + INCLUDE="${I##*/}" + INCLUDE_LC=$(echo "$INCLUDE.h" | awk '{print tolower($0)}') + INCLUDE_DIR="${I%/*}" + MODULE="${I%%/*}" + if [ "${MODULE}" != "${LAST_MODULE}" ] + then + echo "\n # ${MODULE}" + LAST_MODULE="${MODULE}" + fi + #echo "$INCLUDE_DIR $INCLUDE $INCLUDE_LC"; + if [ "${MODULE}" = "QtTest" ] + then + # Use only for module QtTest + if [ "${INCLUDE}" = "QtTest" ] + then + for J in $(find "${MODULE}" -type f -name "q*.h") + do + INCLUDE="${J##*/}" + echo " { include: [ \"<${INCLUDE}>\", \"private\", \"\", \"public\" ] }," + done + else + echo " { include: [ \"<${INCLUDE}>\", \"private\", \"\", \"public\" ] }," + fi + elif [ -f "$INCLUDE_DIR/$INCLUDE_LC" -a $(grep "#include \"$INCLUDE_LC" "$I" | wc -l) -eq 1 ] + then + # Use public include for include + grep "#include \"$INCLUDE_LC" "$I" | sed -e "s/.*\"\(.*\)\"/ { include: [ \"<\1>\", \"private\", \"<${INCLUDE}>\", \"public\" ] },/" + elif [ "${INCLUDE#Qt}" = "${INCLUDE}" ] + then + # Use public include for QFoo symbol + if [ $(grep "#include \"q" "$I" | wc -l) -eq 1 ] + then + echo " { symbol: [ \"${INCLUDE}\", \"private\", \"<${INCLUDE}>\", \"public\" ] }," + fi + elif [ -f "$INCLUDE_DIR/q${INCLUDE_LC#qt}" -a $(grep "#include \"q${INCLUDE_LC#qt}" "$I" | wc -l) -eq 1 ] + then + # Use public QtFoo include for private qfoo.h include + grep "#include \"q${INCLUDE_LC#qt}" "$I" | sed -e "s/.*\"\(.*\)\"/ { include: [ \"<\1>\", \"private\", \"<${INCLUDE}>\", \"public\" ] },/" + fi +done + +cat << END_EXTRA + +# --- Manual additions --- + + # convenience + { include: [ "", "public", "", "public" ] }, + { include: [ "", "public", "", "public" ] }, + # http://doc.qt.io/qt-5/qt.html + { include: [ "", "private", "", "public" ] }, + # http://doc.qt.io/qt-5/qtglobal.html + { include: [ "", "public", "", "public" ] }, + { include: [ "", "public", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + # http://doc.qt.io/qt-5/qevent.html + { include: [ "", "private", "", "public" ] }, + { include: [ "", "private", "", "public" ] }, + # http://doc.qt.io/qt-5/qmetaobject.html + { include: [ "", "private", "", "public" ] }, + # http://doc.qt.io/qt-5/qobject.html + { include: [ "", "private", "", "public" ] }, + +END_EXTRA + +echo "]" diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt new file mode 100644 index 0000000..abd9372 --- /dev/null +++ b/packaging/CMakeLists.txt @@ -0,0 +1,470 @@ +# +# Copyright 2012-2018 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +message(STATUS "Configuring ${PROJECT_NAME} packaging") + +option(Mapper_PACKAGE_QT_ALL_TRANSLATIONS "Add all Qt translations to the packages" OFF) + +macro(deploy_qt_translations basename) + find_package(Qt5LinguistTools REQUIRED QUIET) + get_target_property(LCONVERT_EXECUTABLE Qt5::lconvert IMPORTED_LOCATION) + if(NOT QT_TRANSLATIONS_DIR) + find_package(Qt5Core REQUIRED QUIET) + get_target_property(QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) + execute_process( + COMMAND "${QMAKE_EXECUTABLE}" -query QT_INSTALL_TRANSLATIONS + OUTPUT_VARIABLE QT_TRANSLATIONS_DIR + ) + string(STRIP "${QT_TRANSLATIONS_DIR}" QT_TRANSLATIONS_DIR) + endif() + set(install_translations_code ) + if(Mapper_PACKAGE_QT_ALL_TRANSLATIONS) + file(GLOB basename_files RELATIVE "${QT_TRANSLATIONS_DIR}" "${QT_TRANSLATIONS_DIR}/${basename}_??.qm") + else() + unset(basename_files) + foreach(_mapper_trans ${Mapper_translations}) + get_filename_component(basename_file ${_mapper_trans} NAME_WE) + string(REPLACE OpenOrienteering ${basename} basename_file ${basename_file}) + string(REGEX REPLACE "(_..)_..$" "\\1" basename_file ${basename_file}) + file(GLOB translation_files RELATIVE "${QT_TRANSLATIONS_DIR}" "${QT_TRANSLATIONS_DIR}/${basename_file}*.qm") + list(APPEND basename_files ${translation_files}) + endforeach() + endif() + foreach(basename_file ${basename_files}) + set(input_files "\"${QT_TRANSLATIONS_DIR}/${basename_file}\"") + foreach(arg ${ARGN}) + string(REPLACE ${basename} ${arg} extra_file ${basename_file}) + if(EXISTS "${QT_TRANSLATIONS_DIR}/${extra_file}") + set(input_files "\"${QT_TRANSLATIONS_DIR}/${extra_file}\" ${input_files}") + endif() + endforeach() + list(APPEND install_translations_code + "execute_process(COMMAND \"${LCONVERT_EXECUTABLE}\" -o \"${CMAKE_CURRENT_BINARY_DIR}/${basename_file}\" ${input_files})" + "file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${MAPPER_DATA_DESTINATION}/translations\" TYPE FILE FILES \"${CMAKE_CURRENT_BINARY_DIR}/${basename_file}\")" + ) + endforeach() + string(REPLACE ";" "\n " install_translations_code "${install_translations_code}") + install(CODE "${install_translations_code}") +endmacro(deploy_qt_translations) + + + +find_package(Qt5Core REQUIRED QUIET) +if(NOT QT_LIBRARY_DIR) + get_target_property(_qt5core_lib Qt5::Core IMPORTED_LOCATION_RELEASE) + get_filename_component(QT_LIBRARY_DIR ${_qt5core_lib} PATH) + string(REGEX REPLACE "/[^/]*\\.framework" "" QT_LIBRARY_DIR "${QT_LIBRARY_DIR}") +endif() + + + +# cf. http://www.cmake.org/cmake/help/cmake-2-8-docs.html#module:CPack +# cf. http://www.cmake.org/Wiki/CMake:CPackPackageGenerators +set(CPACK_PACKAGE_NAME "OpenOrienteering ${CMAKE_PROJECT_NAME}") +set(CPACK_PACKAGE_VENDOR "OpenOrienteering") +set(CPACK_PACKAGE_VERSION_MAJOR ${Mapper_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${Mapper_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${Mapper_VERSION_PATCH}) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Map drawing program from OpenOrienteering") +if(NOT CMAKE_SIZEOF_VOID_P AND MINGW) + set(_env_lang $ENV{LANG}) + set(ENV{LANG} C) + execute_process( + COMMAND ${CMAKE_C_COMPILER} -dumpmachine + OUTPUT_VARIABLE HOST_TRIPLET + ) + set(ENV{LANG} ${_env_lang}) + if(${HOST_TRIPLET} MATCHES ^i686) + set(CMAKE_SIZEOF_VOID_P 4) + elseif(${HOST_TRIPLET} MATCHES ^x86_64) + set(CMAKE_SIZEOF_VOID_P 8) + endif() +endif() +if(ANDROID) + set(_system_name "Android-${CMAKE_ANDROID_ARCH_ABI}") +elseif(APPLE) + set(_system_name "macOS") +elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(_system_name "${CMAKE_SYSTEM_NAME}-x86") +elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_system_name "${CMAKE_SYSTEM_NAME}-x64") +else() + set(_system_name "${CMAKE_SYSTEM_NAME}-unknown") +endif() +set(CPACK_PACKAGE_FILE_NAME + "OpenOrienteering-Mapper-${Mapper_VERSION}-${_system_name}") +set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING") +set(CPACK_STRIP_FILES "TRUE") + +set(CPACK_SOURCE_PACKAGE_FILE_NAME + "openorienteering-mapper_${Mapper_VERSION}-src") +set(CPACK_SOURCE_IGNORE_FILES + "${PROJECT_BINARY_DIR}" + "/[.]git/" + "/3rd-party/clipper/download/" + "/3rd-party/proj/download/" + "/3rd-party/qt5/download/" + ${CPACK_SOURCE_IGNORE_FILES} +) + +set(MAPPER_MACOS_SUBDIR "") + +if(WIN32) + # Packaging as ZIP archive + set(CPACK_GENERATOR_DEFAULT "ZIP") + #set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) + set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME} ${Mapper_VERSION}") + set(CPACK_PACKAGE_EXECUTABLES "Mapper" "${CPACK_PACKAGE_NAME} ${Mapper_VERSION}") + + find_program(MAKENSIS_EXECUTABLE "makensis") + if(MAKENSIS_EXECUTABLE) + list(APPEND CPACK_GENERATOR_DEFAULT "NSIS") + # The title displayed at the top of the installer + set(CPACK_NSIS_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") + # The display name string that appears in the Windows Add/Remove Program control panel + set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_NAME} ${Mapper_VERSION}") + # NSIS start menu links will point to executables in this directory + set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") + # A path to the executable that contains the uninstaller icon. + set(CPACK_NSIS_INSTALLED_ICON_NAME Mapper.exe) + # URL to a web site providing more information about your application. + set(CPACK_NSIS_URL_INFO_ABOUT "https://www.openorienteering.org/apps/mapper/") + # Extra NSIS include + configure_file(windows/custom.nsi.in windows/custom.nsi @ONLY) + set(CPACK_NSIS_DEFINES "!include \\\"${CMAKE_CURRENT_BINARY_DIR}\\\\windows\\\\custom.nsi\\\"") + # Extra NSIS commands that will be added to the install/uninstall sections. + set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "Call installAssociations") + set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "Call un.installAssociations") + # 64 bit build + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") + set(CPACK_NSIS_DISPLAY_NAME "${CPACK_NSIS_DISPLAY_NAME} x64") + endif() + endif(MAKENSIS_EXECUTABLE) + +elseif(APPLE) + set(MAPPER_MACOS_SUBDIR "/Mapper.app/Contents/MacOS") + set(CPACK_GENERATOR_DEFAULT "DragNDrop") + set(CPACK_PACKAGE_EXECUTABLES "Mapper" "OpenOrienteering Mapper ${Mapper_VERSION}") + set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper.icns") + set_target_properties(Mapper PROPERTIES + MACOSX_BUNDLE_INFO_STRING "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}" + MACOSX_BUNDLE_ICON_FILE "Mapper.icns" + MACOSX_BUNDLE_GUI_IDENTIFIER "org.openorienteering.${CMAKE_PROJECT_NAME}" + MACOSX_BUNDLE_LONG_VERSION_STRING "${CMAKE_PROJECT_NAME} ${Mapper_VERSION_DISPLAY} for OS X" + MACOSX_BUNDLE_BUNDLE_NAME "${CMAKE_PROJECT_NAME}" # less than 16 characters long + MACOSX_BUNDLE_SHORT_VERSION_STRING "${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}" + MACOSX_BUNDLE_BUNDLE_VERSION "${Mapper_VERSION_MAJOR}.${Mapper_VERSION_MINOR}.${Mapper_VERSION_PATCH}" + MACOSX_BUNDLE_COPYRIGHT "${Mapper_COPYRIGHT}" + ) + install(FILES "${CPACK_PACKAGE_ICON}" + DESTINATION "${MAPPER_DATA_DESTINATION}" + ) + +elseif(ANDROID) + # We don't use a CPack generator, + # but run androiddeployqt from CPackConfig.cmake + set(CPACK_GENERATOR_DEFAULT "") + + set(KEYSTORE_URL "KEYSTORE_URL-NOTFOUND" CACHE STRING + "URL of the keystore to be used when signing APK packages." + ) + set(KEYSTORE_ALIAS "KEYSTORE_ALIAS-NOTFOUND" CACHE STRING + "Alias in the keystore to be used when signing APK packages." + ) + if(KEYSTORE_URL AND KEYSTORE_ALIAS) + set(SIGN_APK "$,$>") + else() + set(SIGN_APK 0) + endif() + configure_file( + "${PROJECT_SOURCE_DIR}/android/CPackConfig.cmake.in" + "${PROJECT_BINARY_DIR}/CPackConfig.tmp.cmake" + @ONLY + ) + file(GENERATE + OUTPUT "${PROJECT_BINARY_DIR}/CPackConfig.cmake" + INPUT "${PROJECT_BINARY_DIR}/CPackConfig.tmp.cmake" + ) + if(NOT EXISTS "${PROJECT_BINARY_DIR}/CPackConfig.cmake") + file(WRITE "${PROJECT_BINARY_DIR}/CPackConfig.cmake" + [[# Placeholder to enforce 'package' target creation]] + ) + endif() + + # For Android, we create a dummy qmake project which provides + # - configuration for running androiddeployqt from CPackConfig.cmake, and + # - a project for debugging the Android app in Qt Creator + # The directory name must match the Mapper binary. + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Mapper") + configure_file( + "${PROJECT_SOURCE_DIR}/android/Mapper.pro.in" + "${CMAKE_CURRENT_BINARY_DIR}/Mapper/Mapper.tmp.pro" + @ONLY + ) + file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Mapper/Mapper.pro" + INPUT "${CMAKE_CURRENT_BINARY_DIR}/Mapper/Mapper.tmp.pro" + ) + +elseif(UNIX AND EXISTS /usr/bin/dpkg AND EXISTS /usr/bin/lsb_release) + # Packaging on Debian or similar + set(CPACK_GENERATOR_DEFAULT "DEB") + set(CPACK_DEBIAN_PACKAGE_NAME "openorienteering-mapper") + execute_process( + COMMAND /usr/bin/lsb_release -sc + OUTPUT_VARIABLE CPACK_LSB_RELEASE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REPLACE "Linux-x86" "${CPACK_LSB_RELEASE}_i386" + CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}" + ) + string(REPLACE "Linux-x64" "${CPACK_LSB_RELEASE}_amd64" + CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}" + ) + set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Kai Pastor ") + set(CPACK_DEBIAN_SECTION "graphics") + set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://www.openorienteering.org/apps/mapper/") + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS "ON") + +endif() + +if(CPACK_GENERATOR_DEFAULT) + set(CPACK_GENERATOR "${CPACK_GENERATOR_DEFAULT}" + CACHE STRING "The binary package generators (ZIP;DEB;DragNDrop;NSIS)") + set(CPACK_SOURCE_GENERATOR "OFF" + CACHE STRING "The source package generators (TGZ;ZIP)") + mark_as_advanced(CPACK_GENERATOR CPACK_SOURCE_GENERATOR) + + include(CPack) +endif() + + + +# Cleanup obsolete cache items +unset(MAPPER_LIBS CACHE) +unset(MAPPER_QT_PLUGINS CACHE) + +unset(MAPPER_LIB_HINTS) +unset(MAPPER_LIBS) + +if(Mapper_PACKAGE_PROJ) + if(PROJ4_DIR AND NOT PROJ4_ROOT) + # Cf. find_package documentation + string(REGEX REPLACE "/CMake$|/cmake$" "" PROJ4_ROOT "${PROJ4_DIR}") # U + string(REGEX REPLACE "/PROJ4[^/]*$" "" PROJ4_ROOT "${PROJ4_ROOT}") # U, W + string(REGEX REPLACE "/cmake$" "" PROJ4_ROOT "${PROJ4_ROOT}") # U + string(REGEX REPLACE "/lib/[^/]*$|/lib$|/share$" "" PROJ4_ROOT "${PROJ4_ROOT}") # U, W + elseif(NOT PROJ4_ROOT) + message(FATAL_ERROR "PROJ4_ROOT must be set when Mapper_PACKAGE_PROJ is enabled.") + endif() + install( + DIRECTORY "${PROJ4_ROOT}/share/proj" + DESTINATION "${MAPPER_DATA_DESTINATION}") + list(APPEND MAPPER_LIB_HINTS ${PROJ4_ROOT}/bin) +endif() + +if(Mapper_PACKAGE_GDAL) + if(NOT GDAL_DATA_DIR) + unset(GDAL_CONFIG CACHE) + find_program(GDAL_CONFIG gdal-config ONLY_CMAKE_FIND_ROOT_PATH) + if(GDAL_CONFIG) + exec_program(${GDAL_CONFIG} ARGS --datadir OUTPUT_VARIABLE gdal_data_dir) + endif() + if(gdal_data_dir) + # Search in CMAKE_FIND_ROOT_PATH + find_path(GDAL_DATA_DIR + NAMES ellipsoid.csv + HINTS ${gdal_data_dir} + ) + endif() + endif() + if(NOT GDAL_DATA_DIR) + message(FATAL_ERROR "The gdal-config script must be available, or GDAL_DATA_DIR must be set, " + "when Mapper_PACKAGE_GDAL is enabled.") + endif() + install( + DIRECTORY "${GDAL_DATA_DIR}/" + DESTINATION "${MAPPER_DATA_DESTINATION}/gdal") + get_filename_component(GDAL_LIBRARY_DIR "{GDAL_LIBRARY}" PATH) + list(APPEND MAPPER_LIB_HINTS "${GDAL_LIBRARY_DIR}") +endif() + +unset(MAPPER_QT_PLUGINS) +if(Mapper_PACKAGE_QT) + set(QT_LIB_SUFFIX "") + list(APPEND MAPPER_LIB_HINTS ${QT_LIBRARY_DIR}) + set(MAPPER_QT_PLUGINS + generic/qevdevkeyboardplugin + generic/qevdevmouseplugin + generic/qevdevtabletplugin + generic/qevdevtouchplugin + imageformats/qgif + imageformats/qicns + imageformats/qico + imageformats/qjp2 + imageformats/qjpeg + imageformats/qtiff + imageformats/qwebp + platforminputcontexts/composeplatforminputcontextplugin + platforminputcontexts/ibusplatforminputcontextplugin + platforms/qcocoa + platforms/qwindows + platforms/qxcb + position/qtposition_cl + position/qtposition_geoclue + position/qtposition_serialnmea + printsupport/cocoaprintersupport + printsupport/cupsprintersupport + printsupport/windowsprintersupport + ) + if(ANDROID) + set(MAPPER_QT_PLUGINS ) # Ignore for now, handled by androiddeployqt + endif() + set(QT_PLUGIN_TARGETS ) + foreach(module Gui Positioning PrintSupport Sensors Sql) + find_package(Qt5${module} QUIET) + if(module STREQUAL "Positioning" AND Qt5Positioning_DIR) + # Workaround for QTBUG-58812 CMake: Plugin config not loaded + # unless plugin class name ends in Plugin + file(GLOB pluginTargets "${Qt5Positioning_DIR}/Qt5Positioning_*.cmake") + foreach(file ${pluginTargets}) + if(NOT file MATCHES "Plugin.cmake") + include(${file}) + endif() + endforeach() + endif() + list(APPEND QT_PLUGIN_TARGETS ${Qt5${module}_PLUGINS}) + endforeach() + # Cf. Qt5's qt_de.ts for dependencies - qt_de.ts would not load without them. + deploy_qt_translations(qt qtbase) +endif() + +if(Mapper_PACKAGE_ASSISTANT) + set(assistant_find_options ) + if(CMAKE_FIND_ROOT_PATH) + set(assistant_find_options ONLY_CMAKE_FIND_ROOT_PATH) + endif() + find_program(Qt5Help_ASSISTANT_EXECUTABLE + NAMES assistant Assistant assistant.exe +# HINTS bin + ${assistant_find_options} + ) + if(NOT Qt5Help_ASSISTANT_EXECUTABLE) + message(FATAL_ERROR "Qt5Help_ASSISTANT_EXECUTABLE: not found, " + "but required by option Mapper_PACKAGE_ASSISTANT=" + ${Mapper_PACKAGE_ASSISTANT}) + endif() + message(STATUS "Qt Assistant - found") + if(WIN32 OR APPLE) + install( + PROGRAMS ${Qt5Help_ASSISTANT_EXECUTABLE} + DESTINATION "${MAPPER_RUNTIME_DESTINATION}${MAPPER_MACOS_SUBDIR}") + else() + install( + PROGRAMS ${Qt5Help_ASSISTANT_EXECUTABLE} + DESTINATION "${MAPPER_LIBRARY_DESTINATION}/bin") + endif() + list(APPEND MAPPER_QT_PLUGINS + sqldrivers/qsqlite + ) + + deploy_qt_translations(assistant qt_help) +endif(Mapper_PACKAGE_ASSISTANT) + +if(CMAKE_CROSSCOMPILING AND MINGW) + set(_env_lang $ENV{LC_ALL}) + set(ENV{LC_ALL} C) + execute_process( + COMMAND ${CMAKE_C_COMPILER} --print-search-dirs + OUTPUT_VARIABLE MINGW_SEARCH_DIRS + ) + set(ENV{LC_ALL} ${_env_lang}) + string(REGEX REPLACE ".*libraries: ?=?([^\n]*).*" \\1 MINGW_SEARCH_DIRS "${MINGW_SEARCH_DIRS}") + string(REPLACE \; \\\; MINGW_SEARCH_DIRS "${MINGW_SEARCH_DIRS}") + string(REPLACE : \; MINGW_SEARCH_DIRS "${MINGW_SEARCH_DIRS}") + list(APPEND MAPPER_LIB_HINTS ${MINGW_SEARCH_DIRS}) + # Grep is used (and desperately needed) to speed up objdump parsing. + find_program(gp_grep_cmd NAMES grep) +endif() + + +# Install all plugins +unset(MAPPER_QT_PLUGINS_FOUND) +foreach(_qt_plugin ${MAPPER_QT_PLUGINS}) + string(REPLACE "/" "/lib" alternative_pattern "${_qt_plugin}") + foreach(plugin_target ${QT_PLUGIN_TARGETS} NOT_FOUND) + if(plugin_target STREQUAL "NOT_FOUND") + message(" ${_qt_plugin} plugin library - not found") + break() + endif() + get_target_property(_qt_plugin_location ${plugin_target} IMPORTED_LOCATION_RELEASE) + if(_qt_plugin_location MATCHES "${_qt_plugin}" + OR _qt_plugin_location MATCHES "${alternative_pattern}") + message(" ${_qt_plugin} plugin library - found") + list(APPEND MAPPER_QT_PLUGINS_FOUND "${_qt_plugin}") + get_filename_component(_qt_plugin_dir "${_qt_plugin}" PATH) + if(APPLE) + install( + FILES "${_qt_plugin_location}" + DESTINATION "${MAPPER_RUNTIME_DESTINATION}${MAPPER_MACOS_SUBDIR}/../PlugIns/${_qt_plugin_dir}") + else() + install( + FILES "${_qt_plugin_location}" + DESTINATION "${MAPPER_LIBRARY_DESTINATION}/plugins/${_qt_plugin_dir}") + endif() + break() + endif() + endforeach() +endforeach(_qt_plugin) + + +if(UNIX AND NOT APPLE AND NOT ANDROID) + install( + FILES "${PROJECT_SOURCE_DIR}/doc/man/Mapper.1" + DESTINATION "share/man/man1") + install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/linux/Mapper.desktop" + DESTINATION "share/applications") + install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/linux/openorienteering-mapper.xml" + DESTINATION "share/mime/packages") + # Cf. http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout + foreach(_size 16 24 32 48 96 128 256 512) + install( + FILES "${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper-${_size}.png" + DESTINATION "share/icons/hicolor/${_size}x${_size}/apps" + RENAME Mapper.png + ) + install( + FILES "${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper-${_size}.png" + DESTINATION "share/icons/hicolor/${_size}x${_size}/mimetypes" + RENAME application-x-openorienteering-xmap.png + ) + install( + FILES "${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper-${_size}.png" + DESTINATION "share/icons/hicolor/${_size}x${_size}/mimetypes" + RENAME application-x-openorienteering-ocd.png + ) + endforeach() +endif() + +# By exanding all @VAR, custom_install.cmake makes the install more traceable. +configure_file("custom_install.cmake.in" "custom_install.cmake" @ONLY) +install(CODE "include(\"${CMAKE_CURRENT_BINARY_DIR}/custom_install.cmake\")") + diff --git a/packaging/custom_install.cmake.in b/packaging/custom_install.cmake.in new file mode 100644 index 0000000..611a70d --- /dev/null +++ b/packaging/custom_install.cmake.in @@ -0,0 +1,218 @@ +# +# Copyright 2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + + +# Sets CMAKE_CROSSCOMPILING and variables which describe the target system. +macro(handle_crosscompiling) + set(CMAKE_CROSSCOMPILING @CMAKE_CROSSCOMPILING@) + # These variables must describe the target system + set(ANDROID @ANDROID@) + set(APPLE @APPLE@) + set(MINGW @MINGW@) + set(UNIX @UNIX@) + set(WIN32 @WIN32@) +endmacro() + + +# This function is an Android variant of BundleUtilities' fixup_bundle(). +function(fixup_bundle_android runtime dirs) + # First, collect all dependencies + set(resolved ) + set(remaining "libMapper.so") + string(MAKE_C_IDENTIFIER "${remaining}" id) + set(resolved_${id} "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/${remaining}") + while(remaining) + list(GET remaining 0 current) + list(REMOVE_AT remaining 0) + list(APPEND resolved "${current}") + + string(MAKE_C_IDENTIFIER "${current}" current_id) + execute_process( + COMMAND "@ANDROID_NDK_ROOT@/ndk-depends" "${resolved_${current_id}}" + COMMAND grep ".so" + COMMAND grep -v ":" + OUTPUT_VARIABLE output + ERROR_VARIABLE error + RESULT_VARIABLE result + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(result) + message(FATAL_ERROR "ndk-depends failed: ${error}") + endif() + string(REGEX REPLACE "[\n\r ]+" ";" output "${output}") + foreach(item ${output}) + if(NOT item OR item STREQUAL current) + continue() + endif() + list(APPEND depends_${current_id} "${item}") + string(MAKE_C_IDENTIFIER "${item}" id) + if(item AND NOT DEFINED resolved_${id}) + set(resolved_${id} "NOTFOUND") + foreach(dir ${dirs}) + if(NOT resolved_${id} AND dir AND EXISTS "${dir}/${item}") + message(STATUS "Found '${item}' in '${dir}'") + list(APPEND remaining "${item}") + set(resolved_${id} "${dir}/${item}") + endif() + endforeach() + endif() + endforeach() + endwhile() + + # Second, record QT libs, and topologically sort ANDROID_EXTRA_LIBS + set(qt ) + set(android_extra_libs ) + set(remaining "${resolved}") + list(LENGTH remaining length_before) + list(APPEND remaining "MAYBE-STOP") + while(NOT remaining STREQUAL "MAYBE-STOP") + list(GET remaining 0 current) + list(REMOVE_AT remaining 0) + string(MAKE_C_IDENTIFIER "${current}" id) + if(current STREQUAL "MAYBE-STOP") + list(LENGTH remaining length_after) + if(length_before EQUAL length_after) + message(FATAL_ERROR "Cannot resolve circular dependencies of ${remaining}") + endif() + set(length_before ${length_after}) + list(APPEND remaining "MAYBE-STOP") + elseif(current MATCHES "libMapper.so|libQt5Core|libQt5Gui") + continue() + elseif(current MATCHES "libQt5") + string(REPLACE "libQt5" "" lib "${current}") + string(REPLACE ".so" "" lib "${lib}") + string(TOLOWER "${lib}" lib) + set(qt "${qt} ${lib}") + elseif(resolved_${id}) + set(depends ) + foreach(item ${depends_${id}}) + list(FIND remaining "${item}" index) + if(NOT index EQUAL -1) + list(APPEND depends "${item}") + endif() + endforeach() + if(depends) + list(APPEND remaining "${current}") # Redo after dependencies + else() + set(android_extra_libs "${android_extra_libs} \\\n ${resolved_${id}}") + endif() + endif() + endwhile() + + # Finally, write ANDROID_EXTRA_LIBS to mapper_libs.pri + file(WRITE "@CMAKE_CURRENT_BINARY_DIR@/Mapper/mapper_libs.pri" " +QT += ${qt} +ANDROID_EXTRA_LIBS = ${android_extra_libs} +") +endfunction() + + +# This function wraps BundleUtilities' fixup_bundle() +# to make it work for cross-builds. +function(fixup_bundle_portable runtime dirs) + handle_crosscompiling() + if(MINGW) + # gp_tool and gp_cmd are needed for module GetPrerequisites. + set(gp_tool "objdump") + set(gp_cmd "@CMAKE_OBJDUMP@") + # grep is used (and desperately needed) to speed up objdump parsing. + set(gp_grep_cmd "@gp_grep_cmd@") + # This function resolves all unknown items which do not match the + # MinGW DLL name pattern NAME-NUMBER.dll as 'system' libraries, + # thus catching the Windows system libraries in the MinGW context. + function(gp_resolve_item_override context item exepath dirs resolved_item_var resolved_var) + if(NOT ${resolved_var} + AND NOT "${item}" MATCHES "-[0-9]*.dll$") + set(${resolved_item_var} "/system/${item}" PARENT_SCOPE) + set(${resolved_var} 1 PARENT_SCOPE) + endif() + endfunction() + endif() + + if(WIN32) + include(BundleUtilities) + file(GLOB_RECURSE plugins "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/plugins/*.dll") + list(APPEND runtime ${plugins}) + fixup_bundle("$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/Mapper.exe" "${runtime}" "${dirs}") + # Strip bundled DLLs + if (CMAKE_INSTALL_DO_STRIP AND NOT "@CMAKE_STRIP@" STREQUAL "") + file(GLOB dlls "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/*.dll") + foreach(item ${dlls} ${runtime}) + execute_process(COMMAND "@CMAKE_STRIP@" --strip-unneeded "${item}") + endforeach() + endif() + elseif(APPLE) + include(BundleUtilities) + file(GLOB_RECURSE plugins "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}@MAPPER_MACOS_SUBDIR@/../PlugIns/*.dylib") + list(APPEND runtime "${plugins}") + fixup_bundle("$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/Mapper.app" "${runtime}" "${dirs}") + if (CMAKE_INSTALL_DO_STRIP AND NOT "@CMAKE_STRIP@" STREQUAL "") + file(GLOB dylibs "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}@MAPPER_MACOS_SUBDIR@/*.dylib") + foreach(item ${dylibs} ${runtime}) + execute_process(COMMAND "@CMAKE_STRIP@" -x "${item}") + endforeach() + endif() + elseif(ANDROID) + fixup_bundle_android("${runtime}" "${dirs}") + elseif(UNIX) + # Add required symlinks. + execute_process(COMMAND ldconfig -n "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}@MAPPER_LIBRARY_DESTINATION@") + endif() +endfunction() + + +# Write a minimal qt.conf if needed. +function(handle_qt_conf) + handle_crosscompiling() + set(qt_conf "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}/qt.conf") + if(EXISTS "${qt_conf}") + message(STATUS "Skipping ${qt_conf}") + return() + elseif(WIN32) + message(STATUS "Writing ${qt_conf}") + file(RELATIVE_PATH rel_path + "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@MAPPER_RUNTIME_DESTINATION@" + "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@MAPPER_LIBRARY_DESTINATION@") + if(rel_path STREQUAL "") + set(rel_path ".") + endif() + file(WRITE "${qt_conf}" "\ +[Paths] +Plugins=${rel_path}/plugins +Translations=${rel_path}/translations +") + elseif(APPLE) + set(qt_conf "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}${runtime_destination}@MAPPER_MACOS_SUBDIR@/../Resources/qt.conf") + message(STATUS "Writing ${qt_conf}") + file(WRITE "${qt_conf}" "\ +[Paths] +Plugins=PlugIns +") + endif() +endfunction() + + +# BundleUtilities stumples upon "/." +set(runtime_destination "/@MAPPER_RUNTIME_DESTINATION@") +if(runtime_destination STREQUAL "/.") + set(runtime_destination "") +endif() +set(runtime "") +set(dirs "@MAPPER_LIB_HINTS@") +handle_qt_conf() +fixup_bundle_portable("${runtime}" "${dirs}") diff --git a/packaging/linux/Mapper.desktop b/packaging/linux/Mapper.desktop new file mode 100644 index 0000000..9894694 --- /dev/null +++ b/packaging/linux/Mapper.desktop @@ -0,0 +1,29 @@ +[Desktop Entry] +Type=Application +Name=OpenOrienteering Mapper +Comment=A free software for drawing orienteering maps +Comment[cs]=Svobodný program na kreslení map pro orientační běh +Comment[da]=Et gratis program til tegning af orienteringskort +Comment[de]=Ein freies Programm zum Zeichnen von Orientierungslaufkarten +Comment[eo]=Libera programaro por desegni orientiĝajn mapojn +Comment[es]=Software libre para dibujar mapas de orientación +Comment[fi]=Ilmainen ohjelma suunnistuskarttojen tekemiseen +Comment[fr]=Un logiciel libre pour dessiner des cartes de course d'orientation +Comment[hu]=Tartalmilag ez az igazság +Comment[id]=Perangkat lunak gratis untuk menggambar peta orienteering +Comment[it]=Un software libero per il disegno delle mappe da orienteering +Comment[ja]=オリエンテーリング地図作成のための自由なソフトウェア +Comment[lv]=BrÄ«vpieejas programma orientēšanās karÅ¡u zÄ«mēšanai +Comment[nb]=Fri programvare for tegning av orienteringskart +Comment[nl]=Een gratis programma voor het tekenen van oriëntatieloop kaarten +Comment[pl]=Darmowy program do kreślenia map do BnO +Comment[pt_BR]=Um programa livre para desenho de mapas de orientação +Comment[ru]=Свободное программное обеспечение для создания спортивных карт +Comment[sv]=Fri mjukvara för att rita orienteringskartor +Comment[uk]=Вільна програма для креслення спортивних карт +Comment[zh_CN]=用于绘制定向越野地图的免费开源软件 +Exec=Mapper %F +Icon=Mapper +Terminal=false +Categories=Graphics;Maps;Qt; +MimeType=application/x-openorienteering-xmap;application/x-openorienteering-ocd; diff --git a/packaging/linux/openorienteering-mapper.xml b/packaging/linux/openorienteering-mapper.xml new file mode 100644 index 0000000..3a73e54 --- /dev/null +++ b/packaging/linux/openorienteering-mapper.xml @@ -0,0 +1,53 @@ + + + + + Orienteering map + Mapa pro orientační běh + Orienteringskort + Orientierungslaufkarte + Orientiĝa mapo + Mapa de orientación + Suunnistuskartta + Carte d'orientation + Tájfutó térkép + Peta orienteering + Mappa da Orienteering + オリエンテーリング地図 + Orientēšanās karte + Orienteringskart + Oriëntatiekaart + Mapa do biegu na orientację + Mapa de orientação + Спортивная карта + Orienteringskarta + Спортивна карта + 定向运动地图 + + + + + Orienteering map + Mapa pro orientační běh + Orienteringskort + Orientierungslaufkarte + Orientiĝa mapo + Mapa de orientación + Suunnistuskartta + Carte d'orientation + Tájfutó térkép + Peta orienteering + Mappa da Orienteering + オリエンテーリング地図 + Orientēšanās karte + Orienteringskart + Oriëntatiekaart + Mapa do biegu na orientację + Mapa de orientação + Спортивная карта + Orienteringskarta + Спортивна карта + 定向运动地图 + + + diff --git a/packaging/src/CMakeLists.txt b/packaging/src/CMakeLists.txt new file mode 100644 index 0000000..3dc590f --- /dev/null +++ b/packaging/src/CMakeLists.txt @@ -0,0 +1,36 @@ +# +# Copyright 2013, 2014, 2018 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +message(STATUS "Configuring ${PROJECT_NAME} source packaging") + +set(Mapper_Source_PREFIX + "openorienteering-mapper-${Mapper_VERSION}") + +set(Mapper_Source_FILE_NAME + "openorienteering-mapper_${Mapper_VERSION}-src") + +set(Mapper_Source_FORMAT tgz CACHE STRING + "The archive format for source packages (see `git archive -l` for valid options)") + +find_program(GIT_EXECUTABLE git) +if(GIT_EXECUTABLE) + configure_file(Mapper_Source.cmake.in Mapper_Source.cmake @ONLY) + add_custom_target(Mapper_Source + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/Mapper_Source.cmake" + ) +endif() diff --git a/packaging/src/Mapper_Source.cmake.in b/packaging/src/Mapper_Source.cmake.in new file mode 100644 index 0000000..b26b4de --- /dev/null +++ b/packaging/src/Mapper_Source.cmake.in @@ -0,0 +1,64 @@ +# +# Copyright 2012, 2013, 2014 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +# Synopsis: +# cmake [ -DPREFIX=my/prefix ] [ -DARCHIVE_NAME=prefix-src ] +# [ -DOUTPUT_DIR=/usr/src ] [ -DSKIP_CHECK=1 ] -P Mapper_Source.cmake + +if (NOT PREFIX) + set(PREFIX "@Mapper_Source_PREFIX@") +endif () + +if (NOT ARCHIVE_NAME) + set(ARCHIVE_NAME "@Mapper_Source_FILE_NAME@") +endif () + +if (NOT OUTPUT_DIR) + set(OUTPUT_DIR "@PROJECT_BINARY_DIR@") +endif () +if (NOT OUTPUT_DIR) + set(OUTPUT_DIR .) +endif () + +if (NOT SKIP_CHECK) + # Check that the working directory is clean. + set(ENV{LANG} C) + execute_process( + COMMAND "@GIT_EXECUTABLE@" status + WORKING_DIRECTORY "@PROJECT_SOURCE_DIR@" + OUTPUT_VARIABLE GIT_STATUS_OUTPUT + ) + if (NOT GIT_STATUS_OUTPUT MATCHES "working directory clean") + message(${GIT_STATUS_OUTPUT}) + message(FATAL_ERROR + "Source package must be created from a clean git working directory, " + "or you must specify -DSKIP_CHECK=1." + ) + endif () +endif () + +# Let git build the archive +message(STATUS "Creating ${PREFIX}.@Mapper_Source_FORMAT@") +execute_process( + COMMAND "@GIT_EXECUTABLE@" archive + --format "@Mapper_Source_FORMAT@" + --prefix "${PREFIX}/" + --output "${OUTPUT_DIR}/${ARCHIVE_NAME}.@Mapper_Source_FORMAT@" + HEAD + WORKING_DIRECTORY "@PROJECT_SOURCE_DIR@" +) diff --git a/packaging/translations.cpp b/packaging/translations.cpp new file mode 100644 index 0000000..64db0fc --- /dev/null +++ b/packaging/translations.cpp @@ -0,0 +1,14 @@ +/* + * This file is part of OpenOrienteering. + * + * This file is not compiled. + * These string are used for packaging and desktop integration. + */ + +#define QT_TRANSLATE_NOOP(a,b) + +QT_TRANSLATE_NOOP("OpenOrienteering", "Orienteering map"); +QT_TRANSLATE_NOOP("OpenOrienteering", "Software for drawing orienteering maps"); + +//: For the moment, we use this existing translation instead of the previous one. +QT_TRANSLATE_NOOP("OpenOrienteering::AboutDialog", "A free software for drawing orienteering maps"); diff --git a/packaging/windows/custom.nsi.in b/packaging/windows/custom.nsi.in new file mode 100644 index 0000000..5a4f376 --- /dev/null +++ b/packaging/windows/custom.nsi.in @@ -0,0 +1,79 @@ +; (C) 2014 Kai Pastor +; +; This file is part of OpenOrienteering. +; +; OpenOrienteering is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; OpenOrienteering is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with OpenOrienteering. If not, see . + + +!define MUI_WELCOMEPAGE_TITLE_3LINES +!define MUI_CUSTOMFUNCTION_GUIINIT check_x64 + +Function check_x64 + StrCmp "@CMAKE_SIZEOF_VOID_P@" "8" 0 +5 + StrCmp "$PROGRAMFILES32" "$PROGRAMFILES64" 0 +3 + MessageBox MB_ICONEXCLAMATION|MB_OK "The x64 package cannot be installed on a x32 Windows system. Installation aborted." /SD IDOK + Abort + ClearErrors +FunctionEnd + + +!define SHCNE_ASSOCCHANGED 0x8000000 +!define SHCNF_IDLIST 0 + +Function installAssociations + ; The application + WriteRegStr HKLM "Software\Classes\OpenOrienteering.Map" "" "OpenOrienteering Map" + WriteRegStr HKLM "Software\Classes\OpenOrienteering.Map\DefaultIcon" "" "$INSTDIR\Mapper.exe" + + ReadRegStr $R0 HKLM "Software\Classes\OpenOrienteering.Map\shell\open\command" "" + StrCmp $R0 "" 0 +3 + WriteRegStr HKLM "Software\Classes\OpenOrienteering.Map\shell" "" "open" + WriteRegStr HKLM "Software\Classes\OpenOrienteering.Map\shell\open\command" "" '"$INSTDIR\Mapper.exe" "%1"' + + ; The file type associations + WriteRegStr HKLM "Software\Classes\.omap" "" "OpenOrienteering.Map" + WriteRegStr HKLM "Software\Classes\.omap" "PerceivedType" "document" + WriteRegStr HKLM "Software\Classes\.xmap" "" "OpenOrienteering.Map" + WriteRegStr HKLM "Software\Classes\.xmap" "PerceivedType" "document" + + ReadRegStr $R0 HKLM "Software\Classes\.ocd" "" + StrCmp $R0 "" 0 +3 + WriteRegStr HKLM "Software\Classes\.ocd" "" "OpenOrienteering.Map" + WriteRegStr HKLM "Software\Classes\.ocd" "PerceivedType" "document" + WriteRegStr HKLM "Software\Classes\.ocd\OpenWithProgIDs" "OpenOrienteering.Map" "" + + System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, p0, p0)' +FunctionEnd + +Function un.installAssociations + ; The file type associations + ReadRegStr $R0 HKLM "Software\Classes\.omap" "" + StrCmp $R0 "OpenOrienteering.Map" 0 +2 + DeleteRegKey HKLM "Software\Classes\.omap" + + ReadRegStr $R0 HKLM "Software\Classes\.xmap" "" + StrCmp $R0 "OpenOrienteering.Map" 0 +2 + DeleteRegKey HKLM "Software\Classes\.xmap" + + DeleteRegValue HKLM "Software\Classes\.ocd\OpenWithProgIDs" "OpenOrienteering.Map" + ReadRegStr $R0 HKLM "Software\Classes\.ocd" "" + StrCmp $R0 "OpenOrienteering.Map" 0 +2 + DeleteRegKey HKLM "Software\Classes\.ocd" + + ; The application + DeleteRegKey HKLM "Software\Classes\OpenOrienteering.Map" + + System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, p0, p0)' +FunctionEnd + diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 0000000..29fe36a --- /dev/null +++ b/resources.qrc @@ -0,0 +1,132 @@ + + + images/about.png + images/arrow-down.png + images/arrow-left.png + images/arrow-right.png + images/arrow-thin-upleft.png + images/arrow-thin-downright.png + images/arrow-up.png + images/close.png + images/colors.png + images/compass.png + images/control.png + images/copy.png + images/copy-coords.png + images/cursor-crosshair.png + images/cursor-cut.png + images/cursor-delete.png + images/cursor-draw-circle.png + images/cursor-draw-path.png + images/cursor-draw-point.png + images/cursor-draw-rectangle.png + images/cursor-draw-text.png + images/cursor-fill.png + images/cursor-georeferencing-add.png + images/cursor-georeferencing-move.png + images/cursor-hollow.png + images/cursor-invisible.png + images/cursor-paint-on-template.png + images/cursor-rotate.png + images/cursor-scale.png + images/cut.png + images/delete.png + images/draw-circle.png + images/draw-freehand.png + images/draw-path.png + images/draw-point.png + images/draw-point-gps.png + images/draw-rectangle.png + images/draw-text.png + images/georeferencing.png + images/gps-distance-rings.png + images/gps-temporary-point.png + images/gps-temporary-path.png + images/gps-temporary-clear.png + images/grid.png + images/group.png + images/help.png + images/magnifying-glass.png + images/map-parts.png + images/mapper.png + images/minus.png + images/move.png + images/new.png + images/open-orienteering.png + images/open.png + images/paint-on-template-settings.png + images/move-to-gps.png + images/paste.png + images/pencil.png + images/plus.png + images/point-handles.png + images/point-handles-2x.png + images/point-handles-4x.png + images/print.png + images/print-mode-vector.png + images/print-mode-raster.png + images/print-mode-separations.png + images/redo.png + images/rotate-map.png + images/save.png + images/settings.png + images/symbols.png + images/symbol_point_explanation.png + images/tag-selector.png + images/templates.png + images/text-align-left.png + images/text-align-hcenter.png + images/text-align-right.png + images/text-align-top.png + images/text-align-vcenter.png + images/text-align-baseline.png + images/text-align-bottom.png + images/three-dots.png + images/title.png + images/tool-boolean-difference.png + images/tool-boolean-intersection.png + images/tool-boolean-union.png + images/tool-boolean-xor.png + images/tool-boolean-merge-holes.png + images/tool-connect-paths.png + images/tool-convert-to-curves.png + images/tool-cut.png + images/tool-cut-hole.png + images/tool-cutout-physical.png + images/tool-cutout-physical-inner.png + images/tool-distribute-points.png + images/tool-duplicate.png + images/tool-edit.png + images/tool-edit-line.png + images/tool-fill.png + images/tool-fill-border.png + images/tool-gps-display.png + images/tool-measure.png + images/tool-rotate.png + images/tool-rotate-pattern.png + images/tool-scale.png + images/tool-simplify-path.png + images/tool-switch-dashes.png + images/tool-switch-symbol.png + images/tool-touch-cursor.png + images/undo.png + images/view-show-all.png + images/view-zoom-in.png + images/view-zoom-out.png + images/window-new.png + images/mapper-icon/Mapper-128.png + doc/tip-of-the-day/tips_en.txt + + + doc/tip-of-the-day/tips_de.txt + + + doc/tip-of-the-day/tips_fr.txt + + + doc/tip-of-the-day/tips_ru.txt + + + doc/tip-of-the-day/tips_uk.txt + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..252448e --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,346 @@ +# +# Copyright 2012-2014 Thomas Schöps +# Copyright 2012-2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +find_package(Qt5Core 5.3 REQUIRED) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5Sensors) +find_package(Qt5Positioning) + +if(ANDROID) + find_package(Qt5AndroidExtras REQUIRED) +else() + find_package(Qt5Network REQUIRED) + find_package(Qt5PrintSupport REQUIRED) +endif() + +set(CMAKE_AUTOMOC ON) + +configure_file(mapper_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/mapper_config.h.tmp") +execute_process( + COMMAND "${CMAKE_COMMAND}" -E copy_if_different mapper_config.h.tmp mapper_config.h + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +) +include_directories("${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") +include_directories(AFTER "../3rd-party/qbezier/src") # Always, last + +set(Mapper_Common_SRCS + global.cpp + mapper_resource.cpp + settings.cpp + + core/autosave.cpp + core/crs_template.cpp + core/crs_template_implementation.cpp + core/georeferencing.cpp + core/latlon.cpp + core/map.cpp + core/map_color.cpp + core/map_coord.cpp + core/map_grid.cpp + core/map_part.cpp + core/map_printer.cpp + core/map_view.cpp + core/path_coord.cpp + core/storage_location.cpp + core/virtual_coord_vector.cpp + core/virtual_path.cpp + + core/objects/boolean_tool.cpp + core/objects/object.cpp + core/objects/object_mover.cpp + core/objects/object_query.cpp + core/objects/symbol_rule_set.cpp + core/objects/text_object.cpp + + core/renderables/renderable.cpp + core/renderables/renderable_implementation.cpp + + core/symbols/area_symbol.cpp + core/symbols/combined_symbol.cpp + core/symbols/line_symbol.cpp + core/symbols/point_symbol.cpp + core/symbols/symbol.cpp + core/symbols/symbol_icon_decorator.cpp + core/symbols/text_symbol.cpp + + fileformats/file_format.cpp + fileformats/file_format_registry.cpp + fileformats/file_import_export.cpp + fileformats/native_file_format.cpp + fileformats/ocad8_file_format.cpp + fileformats/ocd_file_export.cpp + fileformats/ocd_file_format.cpp + fileformats/ocd_file_import.cpp + fileformats/ocd_types.cpp + fileformats/xml_file_format.cpp + + gui/about_dialog.cpp + gui/autosave_dialog.cpp + gui/color_dialog.cpp + gui/configure_grid_dialog.cpp + gui/file_dialog.cpp + gui/georeferencing_dialog.cpp + gui/home_screen_controller.cpp + gui/main_window.cpp + gui/main_window_controller.cpp + gui/modifier_key.cpp + gui/print_progress_dialog.cpp + gui/print_tool.cpp + gui/print_widget.cpp + gui/select_crs_dialog.cpp + gui/settings_dialog.cpp + gui/task_dialog.cpp + gui/text_browser_dialog.cpp + gui/touch_cursor.cpp + gui/util_gui.cpp + + gui/map/new_map_dialog.cpp + gui/map/map_dialog_rotate.cpp + gui/map/map_dialog_scale.cpp + gui/map/map_editor.cpp + gui/map/map_editor_activity.cpp + gui/map/map_find_feature.cpp + gui/map/map_widget.cpp + + gui/symbols/area_symbol_settings.cpp + gui/symbols/combined_symbol_settings.cpp + gui/symbols/line_symbol_settings.cpp + gui/symbols/point_symbol_editor_widget.cpp + gui/symbols/point_symbol_settings.cpp + gui/symbols/replace_symbol_set_dialog.cpp + gui/symbols/symbol_properties_widget.cpp + gui/symbols/symbol_setting_dialog.cpp + gui/symbols/text_symbol_settings.cpp + + gui/widgets/action_grid_bar.cpp + gui/widgets/color_dropdown.cpp + gui/widgets/color_list_widget.cpp + gui/widgets/compass_display.cpp + gui/widgets/crs_param_widgets.cpp + gui/widgets/crs_selector.cpp + gui/widgets/editor_settings_page.cpp + gui/widgets/general_settings_page.cpp + gui/widgets/home_screen_widget.cpp + gui/widgets/key_button_bar.cpp + gui/widgets/mapper_proxystyle.cpp + gui/widgets/measure_widget.cpp + gui/widgets/pie_menu.cpp + gui/widgets/segmented_button_layout.cpp + gui/widgets/settings_page.cpp + gui/widgets/symbol_dropdown.cpp + gui/widgets/symbol_render_widget.cpp + gui/widgets/symbol_tooltip.cpp + gui/widgets/symbol_widget.cpp + gui/widgets/tag_select_widget.cpp + gui/widgets/tags_widget.cpp + gui/widgets/template_list_widget.cpp + gui/widgets/text_alignment_widget.cpp + gui/widgets/text_browser.cpp + + sensors/compass.cpp + sensors/gps_display.cpp + sensors/gps_temporary_markers.cpp + sensors/gps_track.cpp + sensors/gps_track_recorder.cpp + + templates/template.cpp + templates/template_adjust.cpp + templates/template_dialog_reopen.cpp + templates/template_image.cpp + templates/template_map.cpp + templates/template_position_dock_widget.cpp + templates/template_positioning_dialog.cpp + templates/template_tool_move.cpp + templates/template_tool_paint.cpp + templates/template_track.cpp + templates/world_file.cpp + + tools/cut_tool.cpp + tools/cut_hole_tool.cpp + tools/cutout_operation.cpp + tools/cutout_tool.cpp + tools/distribute_points_tool.cpp + tools/draw_line_and_area_tool.cpp + tools/draw_point_tool.cpp + tools/draw_point_gps_tool.cpp + tools/draw_path_tool.cpp + tools/draw_circle_tool.cpp + tools/draw_rectangle_tool.cpp + tools/draw_freehand_tool.cpp + tools/draw_text_tool.cpp + tools/edit_tool.cpp + tools/edit_point_tool.cpp + tools/edit_line_tool.cpp + tools/fill_tool.cpp + tools/object_selector.cpp + tools/pan_tool.cpp + tools/point_handles.cpp + tools/rotate_tool.cpp + tools/rotate_pattern_tool.cpp + tools/scale_tool.cpp + tools/text_object_editor_helper.cpp + tools/tool.cpp + tools/tool_base.cpp + tools/tool_helpers.cpp + + undo/map_part_undo.cpp + undo/object_undo.cpp + undo/undo.cpp + undo/undo_manager.cpp + + util/dxfparser.cpp + util/encoding.cpp + util/item_delegates.cpp + util/matrix.cpp + util/overriding_shortcut.cpp + util/recording_translator.cpp + util/scoped_signals_blocker.cpp + util/transformation.cpp + util/translation_util.cpp + util/util.cpp + util/xml_stream_util.cpp +) + +# Extra header to be shown in the IDE or to be translated +set(Mapper_Common_HEADERS + core/autosave_p.h + core/image_transparency_fixup.h + core/objects/object_operations.h + core/renderables/renderable.h + core/renderables/renderable_implementation.h + + fileformats/file_import_export.h # translations + fileformats/ocad8_file_format_p.h + fileformats/ocd_file_import.h # translations + fileformats/ocd_types.h + fileformats/ocd_types_v8.h + fileformats/ocd_types_v9.h + fileformats/ocd_types_v10.h + fileformats/ocd_types_v11.h + fileformats/ocd_types_v12.h + fileformats/xml_file_format_p.h + + gui/map/map_editor_p.h + + templates/world_file.h + + util/backports.h +) + + +# Resources (from project root) + +list(APPEND Mapper_RESOURCES "${PROJECT_SOURCE_DIR}/resources.qrc") +qt5_add_resources(Mapper_RESOURCES_RCC ${Mapper_RESOURCES} OPTIONS -no-compress) + + +# Mapper common: static library of full runtime +# (To be used by Mapper executable and by system tests.) + +add_library(Mapper_Common STATIC + ${Mapper_Common_SRCS} + ${Mapper_Common_HEADERS} # for IDE + ${Mapper_RESOURCES_RCC} +) +add_dependencies(Mapper_Common + Mapper_prerequisites +) +target_link_libraries(Mapper_Common + libocad + Polyclipping::Polyclipping + PROJ4::proj + Qt5::Widgets +) +foreach(lib + mapper-gdal + printsupport + Qt5::AndroidExtras + Qt5::Network + Qt5::Positioning + Qt5::Sensors +) + if(TARGET ${lib}) + target_link_libraries(Mapper_Common ${lib}) + endif() +endforeach() +target_compile_definitions(Mapper_Common PRIVATE + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + QT_USE_QSTRINGBUILDER + # MAPPER_ENABLE_COMPATIBILITY +) + +mapper_translations_sources(${Mapper_Common_SRCS} ${Mapper_Common_HEADERS}) + + +# Mapper executable + +set(Mapper_SRCS + main.cpp +) + +if(WIN32) + enable_language(RC) + configure_file(mingw/resources.rc.in ${CMAKE_CURRENT_BINARY_DIR}/resources.rc @ONLY) + configure_file(${PROJECT_SOURCE_DIR}/images/mapper-icon/Mapper.ico ${CMAKE_CURRENT_BINARY_DIR}/Mapper.ico COPYONLY) + list(APPEND Mapper_SRCS ${CMAKE_CURRENT_BINARY_DIR}/resources.rc) +endif() + +if(ANDROID) + add_library(Mapper SHARED ${Mapper_SRCS}) +else() + add_executable(Mapper WIN32 MACOSX_BUNDLE ${Mapper_SRCS}) + target_link_libraries(Mapper QtSingleApplication) +endif() + +target_link_libraries(Mapper + Mapper_Common +) +target_compile_definitions(Mapper PRIVATE + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + QT_USE_QSTRINGBUILDER +) + +install(TARGETS Mapper + RUNTIME DESTINATION "${MAPPER_RUNTIME_DESTINATION}" + BUNDLE DESTINATION "${MAPPER_RUNTIME_DESTINATION}" # macOS + LIBRARY DESTINATION "${MAPPER_RUNTIME_DESTINATION}" # Android +) + + +# Workaround Qt private include dir issue +# Cf. https://bugreports.qt.io/browse/QTBUG-37417 + +set(PRIVATE_MODULES Core Gui) +if(WIN32) + list(APPEND PRIVATE_MODULES PrintSupport) +endif() +foreach(module ${PRIVATE_MODULES}) + set(qt_module Qt${module}) + set(qt5_module Qt5${module}) + if("${${qt5_module}_PRIVATE_INCLUDE_DIRS}" STREQUAL "") + foreach(base_dir ${${qt5_module}_INCLUDE_DIRS}) + if("${base_dir}" MATCHES "/${qt_module}\$") + list(APPEND ${qt5_module}_PRIVATE_INCLUDE_DIRS "${base_dir}/${${qt5_module}_VERSION}/${qt_module}") + endif() + endforeach() + endif() + target_include_directories(Mapper_Common PRIVATE ${${qt5_module}_PRIVATE_INCLUDE_DIRS}) +endforeach() diff --git a/src/core/autosave.cpp b/src/core/autosave.cpp new file mode 100644 index 0000000..b018154 --- /dev/null +++ b/src/core/autosave.cpp @@ -0,0 +1,143 @@ +/* + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "autosave.h" +#include "autosave_p.h" + +#include +#include +#include +#include +#include + +#include "settings.h" + + +namespace OpenOrienteering { + +AutosavePrivate::AutosavePrivate(Autosave& document) +: document(document) +, autosave_needed(false) +{ + autosave_timer.setSingleShot(true); + connect(&autosave_timer, &QTimer::timeout, this, &AutosavePrivate::autosave); + connect(&Settings::getInstance(), &Settings::settingsChanged, this, &AutosavePrivate::settingsChanged); + settingsChanged(); +} + +AutosavePrivate::~AutosavePrivate() +{ + // nothing, not inlined +} + +void AutosavePrivate::settingsChanged() +{ + // Normally, the autosave interval can be stored as an integer. + // It is loaded as a double here to allow for faster unit testing. + autosave_interval = qRound(Settings::getInstance().getSetting(Settings::General_AutosaveInterval).toDouble() * 60000); + if (autosave_interval < 1000) + { + // stop autosave + autosave_interval = 0; + autosave_timer.stop(); + } + else if (autosave_needed && !autosave_timer.isActive()) + { + // start autosave + autosave_timer.setInterval(autosave_interval); + autosave_timer.start(); + } +} + +bool AutosavePrivate::autosaveNeeded() +{ + return autosave_needed; +} + +void AutosavePrivate::setAutosaveNeeded(bool needed) +{ + autosave_needed = needed; + if (autosave_interval) + { + // autosaving enabled + if (autosave_needed && !autosave_timer.isActive()) + { + autosave_timer.setInterval(autosave_interval); + autosave_timer.start(); + } + else if (!autosave_needed && autosave_timer.isActive()) + { + autosave_timer.stop(); + } + } +} + +void AutosavePrivate::autosave() +{ + Autosave::AutosaveResult result = document.autosave(); + if (autosave_interval) + { + switch (result) + { + case Autosave::TemporaryFailure: + autosave_timer.setInterval(5000); + autosave_timer.start(); + return; + case Autosave::Success: + case Autosave::PermanentFailure: + autosave_timer.setInterval(autosave_interval); + autosave_timer.start(); + return; + } + Q_UNREACHABLE(); + } +} + + + +// ### Autosave ### + +Autosave::Autosave() +: autosave_controller(new AutosavePrivate(*this)) +{ + // Nothing, not inlined +} + +Autosave::~Autosave() +{ + // Nothing, not inlined +} + +QString Autosave::autosavePath(const QString &path) const +{ + return path + QLatin1String(".autosave"); +} + +void Autosave::setAutosaveNeeded(bool needed) +{ + autosave_controller->setAutosaveNeeded(needed); +} + +bool Autosave::autosaveNeeded() const +{ + return autosave_controller->autosaveNeeded(); +} + + +} // namespace OpenOrienteering diff --git a/src/core/autosave.h b/src/core/autosave.h new file mode 100644 index 0000000..1585969 --- /dev/null +++ b/src/core/autosave.h @@ -0,0 +1,92 @@ +/* + * Copyright 2014, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_AUTOSAVE_H +#define OPENORIENTEERING_AUTOSAVE_H + +#include + +class QString; + +namespace OpenOrienteering { + +class AutosavePrivate; + + +/** + * @brief Class Autosave implements autosaving behaviour. + * + * Autosaving means that data which has been modified is automatically saved + * after some period of time if no regular saving (typically triggered by the + * user) happens before. + * + * Classes which wish to implement autosaving may inherit from this class. + * Inheriting classes must implement autosave(). Furthermore, they must call + * setAutosaveNeeded() when changes need to be taken care of by Autosave, or + * when normal saving has terminated the need to perform autosaving. + * + * Autosaving, as implemented by autosave(), may succeed, fail temporary (e.g. + * during editing), or fail permanent (e.g. for lack of disk space). + * On success or permanent failure, autosave() will be called again after the + * regular autosaving period. + * On temporary failure, autosave() will be called again after five seconds. + * + * The autosave period (in minutes) is taken from the setting + * Settings::General_AutosaveInterval. + */ +class Autosave +{ +public: + /** @brief Possible results of autosave attempts. */ + enum AutosaveResult + { + Success, ///< Autosaving succeeded. + PermanentFailure, ///< Autosaving failed for some persistent reason. + TemporaryFailure ///< Autosaving failed for some transient reason and shall be retried soon. + }; + + /** @brief Returns the autosave file path for the given path. */ + virtual QString autosavePath(const QString &path) const; + + /** @brief Performs an autosave, if possible. */ + virtual AutosaveResult autosave() = 0; + + /** @brief Informs Autosave whether autosaving is needed or not. */ + void setAutosaveNeeded(bool); + + /** @brief Returns true if autosave is known to be needed. */ + bool autosaveNeeded() const; + +protected: + /** @brief Initializes the autosave feature. */ + Autosave(); + + /** @brief Destructs the autosave feature. */ + virtual ~Autosave(); + +private: + friend class AutosavePrivate; + + QScopedPointer autosave_controller; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/autosave_p.h b/src/core/autosave_p.h new file mode 100644 index 0000000..0b96e79 --- /dev/null +++ b/src/core/autosave_p.h @@ -0,0 +1,69 @@ +/* + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_AUTOSAVE_P_H +#define OPENORIENTEERING_AUTOSAVE_P_H + +#include +#include + +#include "autosave.h" + +namespace OpenOrienteering { + + +/** + * @brief AutosavePrivate is a helper class of Autosave. + * + * AutosavePrivate implements most of Autosave's behaviour. + * Autosave is meant to be used through inheritance. + * Due to the implemenation of QObject and moc, only the first inherited class + * may be derived from QObject. That is why Autosave itself is not derived from + * QObject, but rather uses this helper class. + */ +class AutosavePrivate : public QObject +{ +Q_OBJECT +public: + AutosavePrivate(Autosave& document); + + ~AutosavePrivate() override; + + bool autosaveNeeded(); + + void setAutosaveNeeded(bool); + +public slots: + void autosave(); + + void settingsChanged(); + +private: + Q_DISABLE_COPY(AutosavePrivate) + + Autosave& document; + QTimer autosave_timer; + bool autosave_needed; + int autosave_interval; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/crs_template.cpp b/src/core/crs_template.cpp new file mode 100644 index 0000000..ea51ca4 --- /dev/null +++ b/src/core/crs_template.cpp @@ -0,0 +1,140 @@ +/* + * Copyright 2013, 2014 Thomas Schöps + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "crs_template.h" + +#include +#include + +#include +#include + + +namespace OpenOrienteering { + +// From crs_template_implementation.h/.cpp +namespace CRSTemplates +{ + CRSTemplateRegistry::TemplateList defaultList(); +} + + + +// ### CRSParameterWidgetObserver ### + +CRSParameterWidgetObserver::~CRSParameterWidgetObserver() = default; + + + +// ### CRSTemplateParameter ### + +CRSTemplateParameter::CRSTemplateParameter(const QString& key, const QString& description) + : param_id(key) + , param_name(description) +{ + // nothing +} + +CRSTemplateParameter::~CRSTemplateParameter() +{ + // nothing, not inlined +} + +std::vector CRSTemplateParameter::specValues(const QString& edit_value) const +{ + return { edit_value }; +} + + + +// ### CRSTemplate ### + +CRSTemplate::CRSTemplate( + const QString& id, + const QString& name, + const QString& coordinates_name, + const QString& spec_template, + ParameterList&& parameters) + : template_id(id) + , template_name(name) + , coordinates_name(coordinates_name) + , spec_template(spec_template) + , params(std::move(parameters)) +{ + // nothing +} + +CRSTemplate::~CRSTemplate() +{ + for (auto&& param : params) + delete param; +} + +QString CRSTemplate::coordinatesName(const std::vector& values) const +{ + Q_ASSERT(params.size() == values.size() + || values.empty()); + + auto name = coordinates_name; + + auto value = begin(values); + auto last_value = end(values); + for (auto key = begin(params), last = end(params); + key != last && value != last_value; + ++key, ++value) + { + name.replace(QLatin1Char('@') + (*key)->id() + QLatin1Char('@'), *value); + } + + return name; +} + + + +// ### CRSTemplateRegistry ### + +CRSTemplateRegistry::CRSTemplateRegistry() +{ + static auto shared_list = CRSTemplates::defaultList(); + this->templates = &shared_list; +} + +const CRSTemplate* CRSTemplateRegistry::find(const QString& id) const +{ + const CRSTemplate* ret = nullptr; + for (auto&& temp : *templates) + { + if (temp->id() == id) { + ret = temp.get(); + break; + } + + } + return ret; +} + +void CRSTemplateRegistry::add(std::unique_ptr temp) +{ + templates->push_back(std::move(temp)); +} + + +} // namespace OpenOrienteering diff --git a/src/core/crs_template.h b/src/core/crs_template.h new file mode 100644 index 0000000..b4bba02 --- /dev/null +++ b/src/core/crs_template.h @@ -0,0 +1,325 @@ +/* + * Copyright 2013, 2014 Thomas Schöps + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_CRS_TEMPLATE_H +#define OPENORIENTEERING_CRS_TEMPLATE_H + +#include +#include + +#include + +class QWidget; + +namespace OpenOrienteering { + +class Georeferencing; + + +/** + * Abstract base class for users of CRS parameter widgets. + */ +class CRSParameterWidgetObserver +{ +public: + CRSParameterWidgetObserver() noexcept = default; + + CRSParameterWidgetObserver(const CRSParameterWidgetObserver&) = delete; + CRSParameterWidgetObserver(CRSParameterWidgetObserver&&) = delete; + + virtual ~CRSParameterWidgetObserver(); + + CRSParameterWidgetObserver& operator=(const CRSParameterWidgetObserver&) = delete; + CRSParameterWidgetObserver& operator=(CRSParameterWidgetObserver&&) = delete; + + /** + * Informs the observer about a change in a CRS parameter widget. + */ + virtual void crsParameterEdited() = 0; + + /** + * Returns the current georeferencing. + * + * CRS parameter widgets may use this georeferencing for calculating values. + */ + virtual const Georeferencing& georeferencing() const = 0; +}; + + + +/** + * Abstract base class for parameters in CRSTemplates. + */ +class CRSTemplateParameter +{ +public: + using WidgetObserver = CRSParameterWidgetObserver; + + /** + * Constructs a new parameter with the given key and description. + */ + CRSTemplateParameter(const QString& id, const QString& name); + + CRSTemplateParameter(const CRSTemplateParameter&) = delete; + CRSTemplateParameter(CRSTemplateParameter&&) = delete; + + /** + * Destructor. + */ + virtual ~CRSTemplateParameter(); + + CRSTemplateParameter& operator=(const CRSTemplateParameter&) = delete; + CRSTemplateParameter& operator=(CRSTemplateParameter&&) = delete; + + /** + * Returns the parameter's permanent unique ID. + */ + QString id() const; + + /** + * Returns the parameter's display name. + */ + QString name() const; + + /** + * Creates a widget which can be used to edit the value. + * + * The widget should be simple in the sense that it can be used as a field + * in a QFormLayout, together with the parameter's label. + */ + virtual QWidget* createEditor(WidgetObserver& widget_observer) const = 0; + + /** + * Return a list of actual specification parameters values from a value in + * storage format. + * + * The default implementation returns a vector which contains just the + * single edit_value. + */ + virtual std::vector specValues(const QString& edit_value) const; + + /** + * Return the widget's value(s) in form of a single string. + * + * This string can be stored and used for restoring the widget. + * + * \see CRSTemplateParameter::setValue + */ + virtual QString value(const QWidget* edit_widget) const = 0; + + /** + * Sets the widget to a stored value. + * + * \see CRSTemplateParameter::value + */ + virtual void setValue(QWidget* edit_widget, const QString& value) = 0; + +private: + const QString param_id; + const QString param_name; +}; + + + +/** + * A template for a coordinate reference system specification (CRS) string. + * + * A CRSTemplate may contain one or more parameters described by the + * CRSTemplateParameter class. For each parameter, spec_template must contain a + * number of free parameters for QString::arg(), e.g. "%1" for the first parameter. + */ +class CRSTemplate +{ +public: + using Parameter = CRSTemplateParameter; + + using ParameterList = std::vector; + + /** + * Creates a new CRS template. + * + * The id must be unique and different from "Local". + * The template takes ownership of the parameters in the list. + * + * The coordinates_name may contain placeholders written @id@ which refer + * to the parameter with the given ID. They can be replaced with actual + * parameter values when calling coordinatesName(). + */ + CRSTemplate( + const QString& template_id, + const QString& template_name, + const QString& coordinates_name, + const QString& spec_template, + ParameterList&& parameters + ); + + // Copying of parameters is not implemented here. + CRSTemplate(const CRSTemplate&) = delete; + + /** + * Destructor. + * + * This deletes the parameters. + */ + ~CRSTemplate(); + + // Copying of parameters is not implemented here. + CRSTemplate& operator=(const CRSTemplate&) = delete; + + + /** + * Returns the unique ID of this template. + */ + QString id() const; + + /** + * Returns the display name of this template. + */ + QString name() const; + + /** + * Returns the display name for the coordinates of this template, + * e.g. "UTM coordinates". + * + * The values list must be either of the same size as the templates list + * parameters, or empty. The given parameter values are substituted for the + * respective @id@ placeholders in the coordinates_name which was passed to + * the constructor. + */ + QString coordinatesName(const std::vector& values = {}) const; + + /** + * Returns the specification string template in Proj.4 format. + */ + QString specificationTemplate() const; + + /** + * Returns the parameters. + */ + const ParameterList& parameters() const; + + +private: + const QString template_id; + const QString template_name; + const QString coordinates_name; + const QString spec_template; + const ParameterList params; +}; + + + +/** + * A directory of known CRS templates. + * + * All instances of this class provide access to the same list. + */ +class CRSTemplateRegistry +{ +public: + using TemplateList = std::vector< std::unique_ptr >; + + /** + * Creates an object for accessing the CRS template directory. + */ + CRSTemplateRegistry(); + + /** + * Returns the list of registered CRS templates. + */ + const TemplateList& list() const; + + /** + * Finds the registered CRS template with the given id, + * or returns nullptr if the given id does not exist. + */ + const CRSTemplate* find(const QString& id) const; + + /** + * Registers a CRS template. + * + * Note that the directory and thus the template outlives the + * CRSTemplateRegistry object. + */ + void add(std::unique_ptr temp); + +private: + TemplateList* templates; +}; + + + +//### CRSTemplateParameter inline code ### + +inline +QString CRSTemplateParameter::id() const +{ + return param_id; +} + +inline +QString CRSTemplateParameter::name() const +{ + return param_name; +} + + + +//### CRSTemplate inline code ### + +inline +QString CRSTemplate::id() const +{ + return template_id; +} + +inline +QString CRSTemplate::name() const +{ + return template_name; +} + +inline +QString CRSTemplate::specificationTemplate() const +{ + return spec_template; +} + +inline +const CRSTemplate::ParameterList& CRSTemplate::parameters() const +{ + return params; +} + + + +//### CRSTemplateRegistry inline code ### + +inline +const CRSTemplateRegistry::TemplateList& CRSTemplateRegistry::list() const +{ + return *templates; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/crs_template_implementation.cpp b/src/core/crs_template_implementation.cpp new file mode 100644 index 0000000..ab54df3 --- /dev/null +++ b/src/core/crs_template_implementation.cpp @@ -0,0 +1,294 @@ +/* + * Copyright 2013, 2014 Thomas Schöps + * Copyright 2014-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "crs_template_implementation.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/crs_template.h" +#include "core/georeferencing.h" +#include "core/latlon.h" +#include "gui/util_gui.h" +#include "gui/widgets/crs_param_widgets.h" + + +namespace OpenOrienteering { + +namespace CRSTemplates { + +CRSTemplateRegistry::TemplateList defaultList() +{ + CRSTemplateRegistry::TemplateList templates; + templates.reserve(5); + + // UTM + auto temp = std::make_unique( + QString::fromLatin1("UTM"), + ::OpenOrienteering::Georeferencing::tr("UTM", "UTM coordinate reference system"), + ::OpenOrienteering::Georeferencing::tr("UTM coordinates"), + QString::fromLatin1("+proj=utm +datum=WGS84 +zone=%1"), + CRSTemplate::ParameterList { + new UTMZoneParameter(QString::fromLatin1("zone"), ::OpenOrienteering::Georeferencing::tr("UTM Zone (number north/south)")) + } ); + templates.push_back(std::move(temp)); + + // Gauss-Krueger + temp = std::make_unique( + QString::fromLatin1("Gauss-Krueger, datum: Potsdam"), + ::OpenOrienteering::Georeferencing::tr("Gauss-Krueger, datum: Potsdam", "Gauss-Krueger coordinate reference system"), + ::OpenOrienteering::Georeferencing::tr("Gauss-Krueger coordinates"), + QString::fromLatin1("+proj=tmerc +lat_0=0 +lon_0=%1 +k=1.000000 +x_0=%2 +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs"), + CRSTemplate::ParameterList { + new IntRangeParameter(QString::fromLatin1("zone"), ::OpenOrienteering::Georeferencing::tr("Zone number (1 to 119)", "Zone number for Gauss-Krueger coordinates"), + 1, 119, { {3, 0}, {1000000, 500000} }) + } ); + templates.push_back(std::move(temp)); + + // EPSG + temp = std::make_unique( + QString::fromLatin1("EPSG"), + ::OpenOrienteering::Georeferencing::tr("by EPSG code", "as in: The CRS is specified by EPSG code"), + //: Don't translate @code@. It is placeholder. + ::OpenOrienteering::Georeferencing::tr("EPSG @code@ coordinates"), + QString::fromLatin1("+init=epsg:%1"), + CRSTemplate::ParameterList { + new IntRangeParameter(QString::fromLatin1("code"), ::OpenOrienteering::Georeferencing::tr("EPSG code"), 1000, 99999) + } ); + templates.push_back(std::move(temp)); + + // Custom + temp = std::make_unique( + QString::fromLatin1("PROJ.4"), // Don't change this ID. + ::OpenOrienteering::Georeferencing::tr("Custom PROJ.4", "PROJ.4 specification"), + ::OpenOrienteering::Georeferencing::tr("Local coordinates"), + QString::fromLatin1("%1"), + CRSTemplate::ParameterList { + new FullSpecParameter(QString::fromLatin1("spec"), ::OpenOrienteering::Georeferencing::tr("Specification", "PROJ.4 specification")) + } ); + templates.push_back(std::move(temp)); + + return templates; +} + + + +// ### TextParameter ### + +TextParameter::TextParameter(const QString& key, const QString& name) + : CRSTemplateParameter(key, name) +{ + // nothing +} + +QWidget* TextParameter::createEditor(WidgetObserver& observer) const +{ + auto widget = new Editor(); + QObject::connect(widget, &TextParameter::Editor::textChanged, [&observer](){ observer.crsParameterEdited(); }); + return widget; +} + +QString TextParameter::value(const QWidget* edit_widget) const +{ + QString value; + auto field = qobject_cast(edit_widget); + if (field) + value = field->text(); + return value; +} + +void TextParameter::setValue(QWidget* edit_widget, const QString& value) +{ + auto field = qobject_cast(edit_widget); + if (field) + field->setText(value); +} + + + +// ### FullSpecParameter ### + +FullSpecParameter::FullSpecParameter(const QString& key, const QString& name) + : TextParameter(key, name) +{ + // nothing +} + +QWidget* FullSpecParameter::createEditor(WidgetObserver& observer) const +{ + auto widget = qobject_cast(TextParameter::createEditor(observer)); + Q_ASSERT(widget); + if (widget) + { + const QSignalBlocker block(widget); + widget->setText(observer.georeferencing().getProjectedCRSSpec()); + } + return widget; +} + +void FullSpecParameter::setValue(QWidget* edit_widget, const QString& value) +{ + // Don't accidently clear this field. + if (!value.isEmpty()) + TextParameter::setValue(edit_widget, value); +} + + + +// ### UTMZoneParameter ### + +UTMZoneParameter::UTMZoneParameter(const QString& key, const QString& name) + : CRSTemplateParameter(key, name) +{ + // nothing +} + +UTMZoneParameter::~UTMZoneParameter() +{ + // nothing +} + +QWidget* UTMZoneParameter::createEditor(WidgetObserver& observer) const +{ + auto widget = new UTMZoneEdit(observer, nullptr); + QObject::connect(widget, &UTMZoneEdit::textEdited, [&observer](){ observer.crsParameterEdited(); }); + return widget; +} + +std::vector UTMZoneParameter::specValues(const QString& edit_value) const +{ + auto zone = QString { edit_value }; + zone.remove(QLatin1String(" N")); + zone.replace(QLatin1String(" S"), QLatin1String(" +south")); + return { zone }; +} + +QString UTMZoneParameter::value(const QWidget* edit_widget) const +{ + QString value; + if (auto text_edit = qobject_cast(edit_widget)) + value = text_edit->text(); + return value; +} + +void UTMZoneParameter::setValue(QWidget* edit_widget, const QString& value) +{ + // Don't accidently clear this field. + auto text_edit = qobject_cast(edit_widget); + if (text_edit && !value.isEmpty()) + text_edit->setText(value); +} + +QVariant UTMZoneParameter::calculateUTMZone(const LatLon lat_lon) +{ + QVariant ret; + + const double lat = lat_lon.latitude(); + if (fabs(lat) < 84.0) + { + const double lon = lat_lon.longitude(); + int zone_no = int(floor(lon) + 180) / 6 % 60 + 1; + if (zone_no == 31 && lon >= 3.0 && lat >= 56.0 && lat < 64.0) + zone_no = 32; // South Norway + else if (lat >= 72.0 && lon >= 3.0 && lon <= 39.0) + zone_no = 2 * (int(floor(lon) + 3.0) / 12) + 31; // Svalbard + QString zone = QString::number(zone_no); + if (zone_no < 10) + zone.prepend(QLatin1String("0")); + zone.append((lat >= 0.0) ? QLatin1String(" N") : QLatin1String(" S")); + ret = zone; + } + + return ret; +} + + + +// ### IntRangeParameter ### + +IntRangeParameter::IntRangeParameter(const QString& key, const QString& name, int min_value, int max_value) + : IntRangeParameter(key, name, min_value, max_value, { {1, 0} }) +{ + // nothing +} + +IntRangeParameter::IntRangeParameter(const QString& key, const QString& name, int min_value, int max_value, OutputList&& outputs) + : CRSTemplateParameter(key, name) + , min_value(min_value) + , max_value(max_value) + , outputs(std::move(outputs)) +{ + // nothing +} + +IntRangeParameter::~IntRangeParameter() +{ + // nothing +} + +QWidget* IntRangeParameter::createEditor(WidgetObserver& observer) const +{ + using TakingIntArgument = void (QSpinBox::*)(int); + auto widget = Util::SpinBox::create(min_value, max_value); + QObject::connect(widget, (TakingIntArgument) &QSpinBox::valueChanged, [&observer](){ observer.crsParameterEdited(); }); + return widget; +} + +std::vector IntRangeParameter::specValues(const QString& edit_value) const +{ + auto chosen_value = edit_value.toInt(); + + std::vector spec_values; + spec_values.reserve(outputs.size()); + for (auto&& factor_and_bias : outputs) + { + spec_values.push_back(QString::number(factor_and_bias.first * chosen_value + factor_and_bias.second)); + } + return spec_values; +} + +QString IntRangeParameter::value(const QWidget* edit_widget) const +{ + QString value; + if (auto spin_box = qobject_cast(edit_widget)) + value = spin_box->cleanText(); + return value; +} + +void IntRangeParameter::setValue(QWidget* edit_widget, const QString& value) +{ + if (auto spin_box = qobject_cast(edit_widget)) + spin_box->setValue(value.toInt()); +} + + +} // namespace CRSTemplates + +} // namespace OpenOrienteering diff --git a/src/core/crs_template_implementation.h b/src/core/crs_template_implementation.h new file mode 100644 index 0000000..b7656a0 --- /dev/null +++ b/src/core/crs_template_implementation.h @@ -0,0 +1,135 @@ +/* + * Copyright 2013, 2014 Thomas Schöps + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_CRS_TEMPLATE_IMPLEMENTATION_H +#define OPENORIENTEERING_CRS_TEMPLATE_IMPLEMENTATION_H + +#include "crs_template.h" + +#include +#include + +#include +#include +#include + +class QWidget; + +namespace OpenOrienteering { + +class LatLon; + + +/** + * This namespace collects CRS template implementations + */ +namespace CRSTemplates { + +/** + * Creates and returns a list of known CRS Templates. + */ +CRSTemplateRegistry::TemplateList defaultList(); + + + +/** + * CRSTemplate parameter specifying a text parameter. + */ +class TextParameter : public CRSTemplateParameter +{ +public: + TextParameter(const QString& id, const QString& name); + QWidget* createEditor(WidgetObserver& observer) const override; + QString value(const QWidget* edit_widget) const override; + void setValue(QWidget* edit_widget, const QString& value) override; + +protected: + /// The type of editor widget returned from createEditor. + using Editor = QLineEdit; + +}; + + + +/** + * CRSTemplate parameter giving a full specification. + */ +class FullSpecParameter : public TextParameter +{ +public: + FullSpecParameter(const QString& id, const QString& name); + QWidget* createEditor(WidgetObserver& observer) const override; + void setValue(QWidget* edit_widget, const QString& value) override; +}; + + + +/** + * CRSTemplate parameter specifying a UTM zone. + */ +class UTMZoneParameter : public CRSTemplateParameter +{ +public: + UTMZoneParameter(const QString& id, const QString& name); + ~UTMZoneParameter() override; + QWidget* createEditor(WidgetObserver& observer) const override; + std::vector specValues(const QString& edit_value) const override; + QString value(const QWidget* edit_widget) const override; + void setValue(QWidget* edit_widget, const QString& value) override; + + /** + * Determines the UTM zone from the given latitude and longitude. + * + * Returns a null value on error. + */ + static QVariant calculateUTMZone(const LatLon lat_lon); +}; + + + +/** + * CRSTemplate integer parameter, with values from an integer range. + */ +class IntRangeParameter : public CRSTemplateParameter +{ +public: + using OutputList = std::vector< std::pair >; + + IntRangeParameter(const QString& id, const QString& name, int min_value, int max_value); + IntRangeParameter(const QString& id, const QString& name, int min_value, int max_value, OutputList&& outputs); + ~IntRangeParameter() override; + QWidget* createEditor(WidgetObserver& observer) const override; + std::vector specValues(const QString& edit_value) const override; + QString value(const QWidget* edit_widget) const override; + void setValue(QWidget* edit_widget, const QString& value) override; + +private: + const int min_value; + const int max_value; + const OutputList outputs; +}; + + +} // namespace CRSTemplates + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/georeferencing.cpp b/src/core/georeferencing.cpp new file mode 100644 index 0000000..3a943eb --- /dev/null +++ b/src/core/georeferencing.cpp @@ -0,0 +1,918 @@ +/* + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "georeferencing.h" + +#include +#include +#include +#include + +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include + +#include + +#include "core/crs_template.h" +#include "fileformats/file_format.h" +#include "fileformats/xml_file_format.h" +#include "util/xml_stream_util.h" + + +// ### A namespace which collects various string constants of type QLatin1String. ### + +namespace literal +{ + static const QLatin1String georeferencing("georeferencing"); + + static const QLatin1String scale("scale"); + static const QLatin1String grid_scale_factor{"grid_scale_factor"}; + static const QLatin1String declination("declination"); + static const QLatin1String grivation("grivation"); + + static const QLatin1String ref_point("ref_point"); + static const QLatin1String ref_point_deg("ref_point_deg"); + static const QLatin1String projected_crs("projected_crs"); + static const QLatin1String geographic_crs("geographic_crs"); + static const QLatin1String spec("spec"); + static const QLatin1String parameter("parameter"); + + static const QLatin1String x("x"); + static const QLatin1String y("y"); + + static const QLatin1String id("id"); + static const QLatin1String language("language"); + + static const QLatin1String lat("lat"); + static const QLatin1String lon("lon"); + + static const QLatin1String proj_4("PROJ.4"); + static const QLatin1String geographic_coordinates("Geographic coordinates"); +} + + + +namespace OpenOrienteering { + +namespace +{ + /** Helper for PROJ.4 initialization. + * + * To be used as static object in the right place. + */ + class ProjSetup + { + public: + ProjSetup() + { + auto proj_data = QFileInfo(QLatin1String("data:/proj")); + if (proj_data.exists()) + { + static const auto location = proj_data.absoluteFilePath().toLocal8Bit(); + static auto data = location.constData(); + pj_set_searchpath(1, &data); + } + +#if defined(Q_OS_ANDROID) + // Register file finder function needed by Proj.4 + registerProjFileHelper(); +#endif + } + }; + + /** Helper for PROJ.4 initialization. + * + * This helper adds "+no_defs" if it is not already part of the + * specification. It also takes care of resetting the pj errno. + */ + projPJ pj_init_plus_no_defs(const QString& spec) + { + auto spec_latin1 = spec.toLatin1(); + if (!spec_latin1.contains("+no_defs")) + spec_latin1.append(" +no_defs"); + + *pj_get_errno_ref() = 0; + return pj_init_plus(spec_latin1); + } + + + /** + * List of substitutions for specifications which are known to be broken in Proj.4. + */ + const char* spec_substitutions[][2] = { + // #542, S-JTSK (Greenwich) / Krovak East North + { "+init=epsg:5514", "+proj=krovak" + " +lat_0=49.5 +lon_0=24.83333333333333" + " +alpha=30.28813972222222 +k=0.9999" + " +x_0=0 +y_0=0" + " +ellps=bessel" + " +pm=greenwich" + " +towgs84=542.5,89.2,456.9,5.517,2.275,5.516,6.96" + " +units=m" + " +no_defs" + }, + }; + + +} // namespace + + + +//### Georeferencing ### + +const QString Georeferencing::geographic_crs_spec(QString::fromLatin1("+proj=latlong +datum=WGS84")); + +Georeferencing::Georeferencing() +: state(Local), + scale_denominator{1000}, + grid_scale_factor{1.0}, + declination(0.0), + grivation(0.0), + grivation_error(0.0), + map_ref_point(0, 0), + projected_ref_point(0, 0) +{ + static ProjSetup run_once; + + updateTransformation(); + + projected_crs_id = QString::fromLatin1("Local"); + projected_crs = nullptr; + geographic_crs = pj_init_plus_no_defs(geographic_crs_spec); + Q_ASSERT(geographic_crs); +} + +Georeferencing::Georeferencing(const Georeferencing& other) +: QObject(), + state(other.state), + scale_denominator(other.scale_denominator), + grid_scale_factor{other.grid_scale_factor}, + declination(other.declination), + grivation(other.grivation), + grivation_error(other.grivation_error), + map_ref_point(other.map_ref_point), + projected_ref_point(other.projected_ref_point), + projected_crs_id(other.projected_crs_id), + projected_crs_spec(other.projected_crs_spec), + projected_crs_parameters(other.projected_crs_parameters), + geographic_ref_point(other.geographic_ref_point) +{ + updateTransformation(); + + geographic_crs = pj_init_plus_no_defs(geographic_crs_spec); + Q_ASSERT(geographic_crs); + projected_crs = pj_init_plus_no_defs(projected_crs_spec); +} + +Georeferencing::~Georeferencing() +{ + if (projected_crs) + pj_free(projected_crs); + if (geographic_crs) + pj_free(geographic_crs); +} + +Georeferencing& Georeferencing::operator=(const Georeferencing& other) +{ + if (&other == this) + return *this; + + state = other.state; + scale_denominator = other.scale_denominator; + grid_scale_factor = other.grid_scale_factor; + declination = other.declination; + grivation = other.grivation; + grivation_error = other.grivation_error; + map_ref_point = other.map_ref_point; + projected_ref_point = other.projected_ref_point; + from_projected = other.from_projected; + to_projected = other.to_projected; + projected_ref_point = other.projected_ref_point; + projected_crs_id = other.projected_crs_id; + projected_crs_spec = other.projected_crs_spec; + projected_crs_parameters = other.projected_crs_parameters; + geographic_ref_point = other.geographic_ref_point; + + if (projected_crs) + pj_free(projected_crs); + projected_crs = pj_init_plus_no_defs(projected_crs_spec); + + emit stateChanged(); + emit transformationChanged(); + emit declinationChanged(); + emit projectionChanged(); + + return *this; +} + + + +bool Georeferencing::isGeographic() const +{ + return projected_crs && pj_is_latlong(projected_crs); +} + + + +void Georeferencing::load(QXmlStreamReader& xml, bool load_scale_only) +{ + Q_ASSERT(xml.name() == literal::georeferencing); + + { + // Reset to default values + const QSignalBlocker block(this); + *this = Georeferencing(); + } + + XmlElementReader georef_element(xml); + scale_denominator = georef_element.attribute(literal::scale); + if (scale_denominator <= 0) + throw FileFormatException(tr("Map scale specification invalid or missing.")); + + if (georef_element.hasAttribute(literal::grid_scale_factor)) + { + grid_scale_factor = roundScaleFactor(georef_element.attribute(literal::grid_scale_factor)); + if (grid_scale_factor <= 0.0) + throw FileFormatException(tr("Invalid grid scale factor: %1").arg(QString::number(grid_scale_factor))); + } + + state = Local; + if (load_scale_only) + { + xml.skipCurrentElement(); + } + else + { + if (georef_element.hasAttribute(literal::declination)) + declination = roundDeclination(georef_element.attribute(literal::declination)); + if (georef_element.hasAttribute(literal::grivation)) + { + grivation = roundDeclination(georef_element.attribute(literal::grivation)); + grivation_error = georef_element.attribute(literal::grivation) - grivation; + } + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::ref_point) + { + XmlElementReader ref_point_element(xml); + map_ref_point.setX(ref_point_element.attribute(literal::x)); + map_ref_point.setY(ref_point_element.attribute(literal::y)); + } + else if (xml.name() == literal::projected_crs) + { + XmlElementReader crs_element(xml); + projected_crs_id = crs_element.attribute(literal::id); + while (xml.readNextStartElement()) + { + XmlElementReader current_element(xml); + if (xml.name() == literal::spec) + { + const QString language = current_element.attribute(literal::language); + if (language != literal::proj_4) + throw FileFormatException(tr("Unknown CRS specification language: %1").arg(language)); + projected_crs_spec = xml.readElementText(); + } + else if (xml.name() == literal::parameter) + { + projected_crs_parameters.push_back(xml.readElementText()); + } + else if (xml.name() == literal::ref_point) + { + projected_ref_point.setX(current_element.attribute(literal::x)); + projected_ref_point.setY(current_element.attribute(literal::y)); + } + else // unknown + { + ; // nothing + } + } + } + else if (xml.name() == literal::geographic_crs) + { + while (xml.readNextStartElement()) + { + XmlElementReader current_element(xml); + if (xml.name() == literal::spec) + { + const QString language = current_element.attribute(literal::language); + if (language != literal::proj_4) + throw FileFormatException(tr("Unknown CRS specification language: %1").arg(language)); + QString geographic_crs_spec = xml.readElementText(); + if (Georeferencing::geographic_crs_spec != geographic_crs_spec) + throw FileFormatException(tr("Unsupported geographic CRS specification: %1").arg(geographic_crs_spec)); + } + else if (xml.name() == literal::ref_point) + { + // Legacy, latitude/longitude in radiant + double latitude = current_element.attribute(literal::lat); + double longitude = current_element.attribute(literal::lon); + geographic_ref_point = LatLon::fromRadiant(latitude, longitude); + } + else if (xml.name() == literal::ref_point_deg) + { + // Legacy, latitude/longitude in degrees + double latitude = current_element.attribute(literal::lat); + double longitude = current_element.attribute(literal::lon); + geographic_ref_point = LatLon(latitude, longitude); + } + else // unknown + { + ; // nothing + } + } + } + else // unknown + { + ; // nothing + } + } + } + + emit stateChanged(); + updateTransformation(); + emit declinationChanged(); + if (!projected_crs_spec.isEmpty()) + { + if (projected_crs) + pj_free(projected_crs); + projected_crs = pj_init_plus_no_defs(projected_crs_spec); + if (0 == *pj_get_errno_ref()) + { + state = Normal; + emit stateChanged(); + } + } + emit projectionChanged(); +} + +void Georeferencing::save(QXmlStreamWriter& xml) const +{ + XmlElementWriter georef_element(xml, literal::georeferencing); + georef_element.writeAttribute(literal::scale, scale_denominator); + if (grid_scale_factor != 1.0) + georef_element.writeAttribute(literal::grid_scale_factor, grid_scale_factor, scaleFactorPrecision()); + if (!qIsNull(declination)) + georef_element.writeAttribute(literal::declination, declination, declinationPrecision()); + if (!qIsNull(grivation)) + georef_element.writeAttribute(literal::grivation, grivation, declinationPrecision()); + + if (!qIsNull(map_ref_point.lengthSquared())) + { + XmlElementWriter ref_point_element(xml, literal::ref_point); + ref_point_element.writeAttribute(literal::x, map_ref_point.x()); + ref_point_element.writeAttribute(literal::y, map_ref_point.y()); + } + + { + XmlElementWriter crs_element(xml, literal::projected_crs); + if (!projected_crs_id.isEmpty()) + crs_element.writeAttribute(literal::id, projected_crs_id); + + if (!projected_crs_spec.isEmpty()) + { + XmlElementWriter spec_element(xml, literal::spec); + spec_element.writeAttribute(literal::language, literal::proj_4); + xml.writeCharacters(projected_crs_spec); + } + + if (!projected_crs_parameters.empty()) + { + for (const auto& projected_crs_parameter : projected_crs_parameters) + { + XmlElementWriter parameter_element(xml, literal::parameter); + xml.writeCharacters(projected_crs_parameter); + Q_UNUSED(parameter_element); // Suppress compiler warnings + } + } + + if (!qIsNull(projected_ref_point.manhattanLength())) + { + XmlElementWriter ref_point_element(xml, literal::ref_point); + ref_point_element.writeAttribute(literal::x, projected_ref_point.x(), 6); + ref_point_element.writeAttribute(literal::y, projected_ref_point.y(), 6); + } + } + + if (state == Normal) + { + XmlElementWriter crs_element(xml, literal::geographic_crs); + crs_element.writeAttribute(literal::id, literal::geographic_coordinates); + { + XmlElementWriter spec_element(xml, literal::spec); + spec_element.writeAttribute(literal::language, literal::proj_4); + xml.writeCharacters(geographic_crs_spec); + } + if (XMLFileFormat::active_version < 6) + { + // Legacy compatibility + XmlElementWriter ref_point_element(xml, literal::ref_point); + ref_point_element.writeAttribute(literal::lat, degToRad(geographic_ref_point.latitude()), 10); + ref_point_element.writeAttribute(literal::lon, degToRad(geographic_ref_point.longitude()), 10); + } + { + XmlElementWriter ref_point_element(xml, literal::ref_point_deg); + ref_point_element.writeAttribute(literal::lat, geographic_ref_point.latitude(), 8); + ref_point_element.writeAttribute(literal::lon, geographic_ref_point.longitude(), 8); + } + } +} + + +void Georeferencing::setState(Georeferencing::State value) +{ + if (state != value) + { + state = value; + updateTransformation(); + + if (state != Normal) + setProjectedCRS(QStringLiteral("Local"), {}); + + emit stateChanged(); + } +} + +void Georeferencing::setScaleDenominator(int value) +{ + Q_ASSERT(value > 0); + if (scale_denominator != (unsigned int)value) + { + scale_denominator = value; + updateTransformation(); + } +} + +void Georeferencing::setGridScaleFactor(double value) +{ + Q_ASSERT(value > 0); + if (grid_scale_factor != value) + { + grid_scale_factor = value; + updateTransformation(); + } +} + +void Georeferencing::setDeclination(double value) +{ + double declination = roundDeclination(value); + double grivation = declination - getConvergence(); + setDeclinationAndGrivation(declination, grivation); +} + +void Georeferencing::setGrivation(double value) +{ + double grivation = roundDeclination(value); + double declination = grivation + getConvergence(); + setDeclinationAndGrivation(declination, grivation); +} + +void Georeferencing::setDeclinationAndGrivation(double declination, double grivation) +{ + bool declination_change = declination != this->declination; + if (declination_change || grivation != this->grivation) + { + this->declination = declination; + this->grivation = grivation; + this->grivation_error = 0.0; + updateTransformation(); + + if (declination_change) + emit declinationChanged(); + } +} + +void Georeferencing::setMapRefPoint(MapCoord point) +{ + if (map_ref_point != point) + { + map_ref_point = point; + updateTransformation(); + } +} + +void Georeferencing::setProjectedRefPoint(QPointF point, bool update_grivation) +{ + if (projected_ref_point != point || state == Normal) + { + projected_ref_point = point; + bool ok; + LatLon new_geo_ref_point; + + switch (state) + { + default: + qWarning("Undefined georeferencing state"); + // fall through + case Local: + break; + case Normal: + new_geo_ref_point = toGeographicCoords(point, &ok); + if (ok && new_geo_ref_point != geographic_ref_point) + { + geographic_ref_point = new_geo_ref_point; + if (update_grivation) + updateGrivation(); + emit projectionChanged(); + } + } + updateTransformation(); + } +} + +QString Georeferencing::getProjectedCRSName() const +{ + QString name = tr("Local"); + if (auto temp = CRSTemplateRegistry().find(projected_crs_id)) + name = temp->name(); + + return name; +} + +QString Georeferencing::getProjectedCoordinatesName() const +{ + QString name = tr("Local coordinates"); + if (auto temp = CRSTemplateRegistry().find(projected_crs_id)) + name = temp->coordinatesName(projected_crs_parameters); + + return name; +} + +double Georeferencing::getConvergence() const +{ + if (state != Normal || !isValid()) + return 0.0; + + // Second point on the same meridian + const double delta_phi = 360.0 / 40000.0; // roughly 1 km + double other_latitude = geographic_ref_point.latitude(); + other_latitude += (other_latitude < 0.0) ? delta_phi : -delta_phi; + const double same_longitude = geographic_ref_point.longitude(); + QPointF projected_other = toProjectedCoords(LatLon(other_latitude, same_longitude)); + + double denominator = projected_other.y() - projected_ref_point.y(); + if (fabs(denominator) < 0.00000000001) + return 0.0; + + return roundDeclination(RAD_TO_DEG * atan((projected_ref_point.x() - projected_other.x()) / denominator)); +} + +void Georeferencing::setGeographicRefPoint(LatLon lat_lon, bool update_grivation) +{ + bool geo_ref_point_changed = geographic_ref_point != lat_lon; + if (geo_ref_point_changed || state == Normal) + { + geographic_ref_point = lat_lon; + if (state != Normal) + setState(Normal); + + bool ok; + QPointF new_projected_ref = toProjectedCoords(lat_lon, &ok); + if (ok && new_projected_ref != projected_ref_point) + { + projected_ref_point = new_projected_ref; + if (update_grivation) + updateGrivation(); + updateTransformation(); + emit projectionChanged(); + } + else if (geo_ref_point_changed) + { + emit projectionChanged(); + } + } +} + +void Georeferencing::updateTransformation() +{ + QTransform transform; + transform.translate(projected_ref_point.x(), projected_ref_point.y()); + transform.rotate(-grivation); + + double scale = grid_scale_factor * scale_denominator / 1000.0; // to meters + transform.scale(scale, -scale); + transform.translate(-map_ref_point.x(), -map_ref_point.y()); + + if (to_projected != transform) + { + to_projected = transform; + from_projected = transform.inverted(); + emit transformationChanged(); + } +} + +void Georeferencing::updateGrivation() +{ + setDeclination(declination); +} + +void Georeferencing::initDeclination() +{ + setGrivation(grivation); +} + +void Georeferencing::setTransformationDirectly(const QTransform& transform) +{ + if (transform != to_projected) + { + to_projected = transform; + from_projected = to_projected.inverted(); + emit transformationChanged(); + } +} + +QTransform Georeferencing::mapToProjected() const +{ + return to_projected; +} + +QTransform Georeferencing::projectedToMap() const +{ + return from_projected; +} + + + +bool Georeferencing::setProjectedCRS(const QString& id, QString spec, std::vector params) +{ + // Default return value if no change is neccessary + bool ok = (state == Normal || projected_crs_spec.isEmpty()); + + for (const auto& substitution : spec_substitutions) + { + if (QLatin1String(substitution[0]) == spec) + { + spec = QString::fromLatin1(substitution[1]); + break; + } + } + + // Changes in params shall already be recorded in spec + if (projected_crs_id != id + || projected_crs_spec != spec + || projected_crs_parameters.size() != params.size() + || !std::equal(begin(params), end(params), begin(projected_crs_parameters)) + || (!ok && !spec.isEmpty()) ) + { + if (projected_crs) + pj_free(projected_crs); + + projected_crs_id = id; + projected_crs_spec = spec; + if (projected_crs_spec.isEmpty()) + { + projected_crs_parameters.clear(); + projected_crs = nullptr; + ok = (state != Normal); + } + else + { + projected_crs_parameters.swap(params); // params was passed by value! + projected_crs = pj_init_plus_no_defs(projected_crs_spec); + ok = (0 == *pj_get_errno_ref()); + if (ok && state != Normal) + setState(Normal); + } + + emit projectionChanged(); + } + + return ok; +} + +QPointF Georeferencing::toProjectedCoords(const MapCoord& map_coords) const +{ + return to_projected.map(QPointF(map_coords)); +} + +QPointF Georeferencing::toProjectedCoords(const MapCoordF& map_coords) const +{ + return to_projected.map(map_coords); +} + +MapCoord Georeferencing::toMapCoords(const QPointF& projected_coords) const +{ + return MapCoord(from_projected.map(projected_coords)); +} + +MapCoordF Georeferencing::toMapCoordF(const QPointF& projected_coords) const +{ + return MapCoordF(from_projected.map(projected_coords)); +} + +LatLon Georeferencing::toGeographicCoords(const MapCoordF& map_coords, bool* ok) const +{ + return toGeographicCoords(toProjectedCoords(map_coords), ok); +} + +LatLon Georeferencing::toGeographicCoords(const QPointF& projected_coords, bool* ok) const +{ + if (ok) + *ok = false; + + double easting = projected_coords.x(), northing = projected_coords.y(); + if (projected_crs && geographic_crs) { + int ret = pj_transform(projected_crs, geographic_crs, 1, 1, &easting, &northing, nullptr); + if (ok) + *ok = (ret == 0); + } + return LatLon::fromRadiant(northing, easting); +} + +QPointF Georeferencing::toProjectedCoords(const LatLon& lat_lon, bool* ok) const +{ + if (ok) + *ok = false; + + double easting = degToRad(lat_lon.longitude()), northing = degToRad(lat_lon.latitude()); + if (projected_crs && geographic_crs) { + int ret = pj_transform(geographic_crs, projected_crs, 1, 1, &easting, &northing, nullptr); + if (ok) + *ok = (ret == 0); + } + return QPointF(easting, northing); +} + +MapCoord Georeferencing::toMapCoords(const LatLon& lat_lon, bool* ok) const +{ + return toMapCoords(toProjectedCoords(lat_lon, ok)); +} + +MapCoordF Georeferencing::toMapCoordF(const LatLon& lat_lon, bool* ok) const +{ + return toMapCoordF(toProjectedCoords(lat_lon, ok)); +} + +MapCoordF Georeferencing::toMapCoordF(const Georeferencing* other, const MapCoordF& map_coords, bool* ok) const +{ + if (!other) + { + if (ok) + *ok = true; + return map_coords; + } + else if (isLocal() || other->isLocal()) + { + if (ok) + *ok = true; + return toMapCoordF(other->toProjectedCoords(map_coords)); + } + else + { + if (ok) + *ok = false; + + QPointF projected_coords = other->toProjectedCoords(map_coords); + double easting = projected_coords.x(), northing = projected_coords.y(); + if (projected_crs && other->projected_crs) { + // Direct transformation: + //int ret = pj_transform(other->projected_crs, projected_crs, 1, 1, &easting, &northing, nullptr); + // Use geographic coordinates as intermediate step to enforce + // that coordinates are assumed to have WGS84 datum if datum is specified in only one CRS spec: + int ret = pj_transform(other->projected_crs, geographic_crs, 1, 1, &easting, &northing, nullptr); + ret |= pj_transform(geographic_crs, projected_crs, 1, 1, &easting, &northing, nullptr); + + if (ret != 0) + { + if (ok) + *ok = false; + return MapCoordF(easting, northing); + } + if (ok) + *ok = true; + } + return toMapCoordF(QPointF(easting, northing)); + } +} + +QString Georeferencing::getErrorText() const +{ + int err_no = *pj_get_errno_ref(); + return (err_no == 0) ? QString() : QString::fromLatin1(pj_strerrno(err_no)); +} + +double Georeferencing::radToDeg(double val) +{ + return RAD_TO_DEG * val; +} + +double Georeferencing::degToRad(double val) +{ + return DEG_TO_RAD * val; +} + +QString Georeferencing::degToDMS(double val) +{ + qint64 tmp = val * 360000; + int csec = tmp % 6000; + tmp = tmp / 6000; + int min = tmp % 60; + int deg = tmp / 60; + QString ret = QString::fromUtf8("%1°%2'%3\"").arg(deg).arg(min).arg(QLocale().toString(csec/100.0,'f',2)); + return ret; +} + +QDebug operator<<(QDebug dbg, const Georeferencing &georef) +{ + dbg.nospace() + << "Georeferencing(1:" << georef.scale_denominator + << " " << georef.grid_scale_factor + << " " << georef.declination + << " " << georef.grivation + << "deg, " << georef.projected_crs_id + << " (" << georef.projected_crs_spec + << ") " << QString::number(georef.projected_ref_point.x(), 'f', 8) << "," << QString::number(georef.projected_ref_point.y(), 'f', 8); + if (georef.isLocal()) + dbg.nospace() << ", local)"; + else + dbg.nospace() << ", geographic)"; + + return dbg.space(); +} + + +} // namespace OpenOrienteering + + + +#if defined(Q_OS_ANDROID) + +QScopedPointer temp_dir; // removed upon destruction +QByteArray c_string; // buffer for const char* + +extern "C" +{ + /** + * @brief Provides required files for Proj.4 library. + * + * This C function implements the interface required by pj_set_finder(). + * + * This functions checks if the requested file name is available in a + * temporary directory. If not, it tries to copy the file from the proj + * subfolder of the assets folder to this temporary directory. + * + * If the file exists in the temporary folder (or copying was successful) + * this function returns the full path of this file as a C string. + * This string becomes invalid the next time this function is called. + * Otherwise it returns nullptr. + */ + const char* projFileHelperAndroid(const char *name) + { + if (temp_dir->isValid()) + { + QString path = QDir(temp_dir->path()).filePath(QString::fromUtf8(name)); + QFile file(path); + if (file.exists() || QFile::copy(QLatin1String("assets:/proj/") + QLatin1String(name), path)) + { + c_string = path.toLocal8Bit(); + return c_string.constData(); + } + } + qDebug() << "Could not projection data file" << name; + return nullptr; + } + + void registerProjFileHelper() + { + // QTemporaryDir must not be constructed before QApplication + temp_dir.reset(new QTemporaryDir()); + if (temp_dir->isValid()) + { + pj_set_finder(&projFileHelperAndroid); + } + else + { + qDebug() << "Could not create a temporary directory for projection data."; + } + } +} + +#endif // defined(Q_OS_ANDROID) diff --git a/src/core/georeferencing.h b/src/core/georeferencing.h new file mode 100644 index 0000000..e91449a --- /dev/null +++ b/src/core/georeferencing.h @@ -0,0 +1,695 @@ +/* + * Copyright 2012-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_GEOREFERENCING_H +#define OPENORIENTEERING_GEOREFERENCING_H + +#include +#include + +#include +#include +#include +#include + +#include "core/latlon.h" +#include "core/map_coord.h" + +class QDebug; +class QXmlStreamReader; +class QXmlStreamWriter; +// IWYU pragma: no_forward_declare QPointF + +typedef void* projPJ; + +namespace OpenOrienteering { + + +#if defined(Q_OS_ANDROID) + +/** + * Registers a file finder function needed by Proj.4 on Android. + */ +extern "C" void registerProjFileHelper(); + +#endif + + + +/** + * A Georeferencing defines a mapping between "map coordinates" (as measured on + * paper) and coordinates in the real world. It provides functions for + * converting coordinates from one coordinate system to another. + * + * Conversions between map coordinates and "projected coordinates" (flat metric + * coordinates in a projected coordinate reference system) are made as affine + * transformation based on the map scale (principal scale), grid scale factor, + * the grivation and a defined reference point. + * + * Conversions between projected coordinates and geographic coordinates (here: + * latitude/longitude for the WGS84 datum) are made based on a specification + * of the coordinate reference system of the projected coordinates. The actual + * geographic transformation is done by the PROJ.4 library for geographic + * projections. + * + * If no (valid) specification is given, the projected coordinates are regarded + * as local coordinates. Local coordinates cannot be converted to other + * geographic coordinate systems. The georeferencing is "local". + * + * Conversions between "map coordinates" and "geographic coordinates" use the + * projected coordinates as intermediate step. + */ +class Georeferencing : public QObject // clazy:exclude=copyable-polymorphic +{ +Q_OBJECT + +friend class NativeFileImport; +friend QDebug operator<<(QDebug dbg, const Georeferencing& georef); + +public: + /// Georeferencing state + enum State + { + /// Only conversions between map and local projected coordinates are possible. + Local = 1, + + /// All coordinate conversions are possible (if there is no error in the + /// crs specification). + Normal = 2 + }; + + + /** + * A shared PROJ.4 specification of a WGS84 geographic CRS. + */ + static const QString geographic_crs_spec; + + + /** + * @brief Returns the precision of the grid scale factor. + * + * The precision is given in number of decimal places, + * i.e. digits after the decimal point. + */ + static constexpr unsigned int scaleFactorPrecision(); + + /** + * @brief Rounds according to the defined precision of the grid scale factor. + * + * @see scaleFactorPrecision(); + */ + static double roundScaleFactor(double value); + + + /** + * @brief Returns the precision of declination/grivation/convergence. + * + * The precision is given in number of decimal places, + * i.e. digits after the decimal point. + * + * All values set as declination or grivation will be rounded to this precisison. + */ + static constexpr unsigned int declinationPrecision(); + + /** + * @brief Rounds according to the defined precision of declination/grivation/convergence. + * + * @see declinationPrecision(); + */ + static double roundDeclination(double); + + + /** + * Constructs a scale-only georeferencing. + */ + Georeferencing(); + + /** + * Constructs a georeferencing which is a copy of an existing georeferencing. + * + * Note: Since QObjects may not be copied, this is better understood as + * creating a new object with the same settings. + */ + Georeferencing(const Georeferencing& georeferencing); + + /** + * Cleans up memory allocated by the georeferencing + */ + ~Georeferencing() override; + + + /** + * Saves the georeferencing to an XML stream. + */ + void save(QXmlStreamWriter& xml) const; + + /** + * Creates a georeferencing from an XML stream. + */ + void load(QXmlStreamReader& xml, bool load_scale_only); + + + /** + * Assigns the properties of another Georeferencing to this one. + * + * Having this method in a QObject is in contradiction to Qt conventions. + * But we really need to assign properties from one object to another, + * where each object maintains its own identity. + */ + Georeferencing& operator=(const Georeferencing& other); + + + /** + * Returns if the georeferencing settings are valid. + * + * This means that coordinates can be converted between map and projected + * coordinates. isLocal() can be checked to determine if a conversion + * to geographic coordinates is also possible. + */ + bool isValid() const; + + /** + * Returns true if this georeferencing is local. + * + * A georeferencing is local if no (valid) coordinate system specification + * is given for the projected coordinates. A local georeferencing cannot + * convert coordinates from and to geographic coordinate systems. + */ + bool isLocal() const; + + /** + * Returns true if the "projected CRS" is actually geographic. + * + * \see pj_is_latlong(projPJ pj) in PROJ.4 + */ + bool isGeographic() const; + + + /** + * Returns the georeferencing state. + */ + State getState() const; + + /** + * Sets the georeferencing state. + * + * This is only neccessary to decrease the state to Local, as otherwise it + * will be automatically changed when setting the respective values. + */ + void setState(State value); + + + /** + * Returns the principal scale denominator. + * + * The principal scale - or representative fraction - is the ratio between + * units on the printed map and units on ground. + */ + unsigned int getScaleDenominator() const; + + /** + * Sets the principal scale denominator. + */ + void setScaleDenominator(int value); + + + /** + * Returns the grid scale factor. + * + * The grid scale factor is the ratio between a length in the grid and the + * length on the earth model. It is applied as a factor to ground distances + * to get grid plane distances. + * + * Mapper doesn't explicitly deal with any other factors (elevation factor, + * unit of measurement). Technically, this property can be used as a + * combined factor. + */ + double getGridScaleFactor() const; + + /** + * Sets the grid scale factor. + * + * \see getGridScaleFactor() + */ + void setGridScaleFactor(double value); + + + /** + * Returns the magnetic declination (in degrees). + * + * @see setDeclination() + */ + double getDeclination() const; + + /** + * Sets the magnetic declination (in degrees). + * + * Magnetic declination is the angle between magnetic north and true north. + * In the context of OpenOrienteering Mapper, it is the angle between the + * y axis of map coordinates and the latitude axis of geographic + * coordinates. + * + * This will also affect the grivation value and the transformations. + */ + void setDeclination(double declination); + + + /** + * Returns the grivation. + * + * @see setGrivation() + */ + double getGrivation() const; + + /** + * Returns the deviation of the grivation from the one given in pre-0.6 files. + * + * Only valid immediately after loading a georeferencing from a file. + * Returns 0.0 in any other context. + * + * Files from Mapper versions before 0.6 may have used any number of decimal + * places for grivation. Since version 0.6, grivation is rounded to the + * number of decimal places defined by declinationPrecision(). When this + * rounding takes place (i.e. only when opening a file which has not been + * saved by 0.6 or later), the difference between the original value and the + * rounded value is temporarily provided by this function. This value can be + * used to for correcting dependent data. Any changes to declination or + * grivation will invalidate this value. + * + * @see getGrivation() + * @see declinationPrecision() + */ + double getGrivationError() const; + + /** + * Sets the grivation (in degrees). + * + * Grivation is the angle between magnetic north and grid north. + * In the context of OpenOrienteering Mapper, it is the angle between the y + * axes of map coordinates and projected coordinates. + * + * This will also affect the declination value and the transformations. + */ + void setGrivation(double grivation); + + + /** + * Returns the map coordinates of the reference point. + */ + MapCoord getMapRefPoint() const; + + /** + * Defines the map coordinates of the reference point. + * + * This will not update the map and geographic coordinates of the reference point. + */ + void setMapRefPoint(MapCoord point); + + + /** + * Returns the projected coordinates of the reference point. + */ + QPointF getProjectedRefPoint() const; + + /** + * Defines the projected coordinates of the reference point. + * + * This may trigger changes of the geographic coordinates of the reference + * point, the convergence, the grivation and the transformations. + */ + void setProjectedRefPoint(QPointF point, bool update_grivation = true); + + + /** + * Returns the unique id of the projected CRS. + */ + QString getProjectedCRSId() const; + + /** + * Returns the name of the coordinate reference system (CRS) of the + * projected coordinates. + */ + QString getProjectedCRSName() const; + + /** + * Returns the name of the projected coordinates. + */ + QString getProjectedCoordinatesName() const; + + /** + * Returns the array of projected crs parameter values. + */ + const std::vector& getProjectedCRSParameters() const; + + /** + * Returns the specification of the coordinate reference system (CRS) of the + * projected coordinates + * @return a PROJ.4 specification of the CRS + */ + QString getProjectedCRSSpec() const { return projected_crs_spec; } + + /** + * Sets the coordinate reference system (CRS) of the projected coordinates. + * + * This will not touch any of the reference points, the declination, the + * grivation. It is up to the user to decide how to reestablish a valid + * configuration of geographic reference point, projected reference point, + * declination and grivation. + * + * @param id an identifier + * @param spec the PROJ.4 specification of the CRS + * @param params parameter values (ignore for empty spec) + * @return true if the specification is valid or empty, false otherwise + */ + bool setProjectedCRS(const QString& id, QString spec, std::vector< QString > params = std::vector()); + + /** + * Calculates the meridian convergence at the reference point. + * + * The meridian convergence is the angle between grid north and true north. + * + * @return zero for a local georeferencing, or a calculated approximation + */ + double getConvergence() const; + + + /** + * Returns the geographic coordinates of the reference point. + */ + LatLon getGeographicRefPoint() const; + + /** + * Defines the geographic coordinates of the reference point. + * + * This may trigger changes of the projected coordinates of the reference + * point, the convergence, the grivation and the transformations. + */ + void setGeographicRefPoint(LatLon lat_lon, bool update_grivation = true); + + + /** + * Transforms map (paper) coordinates to projected coordinates. + */ + QPointF toProjectedCoords(const MapCoord& map_coords) const; + + /** + * Transforms map (paper) coordinates to projected coordinates. + */ + QPointF toProjectedCoords(const MapCoordF& map_coords) const; + + /** + * Transforms projected coordinates to map (paper) coordinates. + */ + MapCoord toMapCoords(const QPointF& projected_coords) const; + + /** + * Transforms projected coordinates to map (paper) coordinates. + */ + MapCoordF toMapCoordF(const QPointF& projected_coords) const; + + + /** + * Transforms map (paper) coordinates to geographic coordinates (lat/lon). + */ + LatLon toGeographicCoords(const MapCoordF& map_coords, bool* ok = 0) const; + + /** + * Transforms CRS coordinates to geographic coordinates (lat/lon). + */ + LatLon toGeographicCoords(const QPointF& projected_coords, bool* ok = 0) const; + + /** + * Transforms geographic coordinates (lat/lon) to CRS coordinates. + */ + QPointF toProjectedCoords(const LatLon& lat_lon, bool* ok = 0) const; + + /** + * Transforms geographic coordinates (lat/lon) to map coordinates. + */ + MapCoord toMapCoords(const LatLon& lat_lon, bool* ok = nullptr) const; + + /** + * Transforms geographic coordinates (lat/lon) to map coordinates. + */ + MapCoordF toMapCoordF(const LatLon& lat_lon, bool* ok = nullptr) const; + + + /** + * Transforms map coordinates from the other georeferencing to + * map coordinates of this georeferencing, if possible. + */ + MapCoordF toMapCoordF(const Georeferencing* other, const MapCoordF& map_coords, bool* ok = nullptr) const; + + + /** + * Returns the current error text. + */ + QString getErrorText() const; + + /** + * Converts a value from radians to degrees. + */ + static double radToDeg(double val); + + /** + * Converts a value from degrees to radians. + */ + static double degToRad(double val); + + /** + * Converts a value from degrees to a D°M'S" string. + */ + static QString degToDMS(double val); + + + /** + * Updates the transformation parameters between map coordinates and + * projected coordinates from the current projected reference point + * coordinates, the grivation and the scale. + */ + void updateTransformation(); + + /** + * Updates the grivation. + * + * The new value is calculated from the declination and the convergence. + * For a local georeferencing, the convergence is zero, and grivation + * is set to the same value as declination. + */ + void updateGrivation(); + + /** + * Initializes the declination. + * + * The new value is calculated from the grivation and the convergence. + * For a local georeferencing, the convergence is zero, and declination + * is set to the same value as grivation. + */ + void initDeclination(); + + /** + * Sets the transformation matrix from map coordinates to projected + * coordinates directly. + */ + void setTransformationDirectly(const QTransform& transform); + + + QTransform mapToProjected() const; + + QTransform projectedToMap() const; + + +signals: + /** + * Indicates a change of the state property. + */ + void stateChanged(); + + /** + * Indicates a change to the transformation rules between map coordinates + * and projected coordinates. + */ + void transformationChanged(); + + /** + * Indicates a change to the projection rules between geographic coordinates + * and projected coordinates. + */ + void projectionChanged(); + + /** + * Indicates a change of the declination. + * + * The declination has no direct influence on projection or transformation. + * That's why there is an independent signal. + */ + void declinationChanged(); + + +private: + void setDeclinationAndGrivation(double declination, double grivation); + + State state; + + unsigned int scale_denominator; + double grid_scale_factor; + double declination; + double grivation; + double grivation_error; + MapCoord map_ref_point; + + QPointF projected_ref_point; + + QTransform from_projected; + QTransform to_projected; + + /// Projected CRS id, used for lookup in the CRS registry. + /// If the spec is directly entered as string, the id is empty. + QString projected_crs_id; + QString projected_crs_spec; + std::vector< QString > projected_crs_parameters; + projPJ projected_crs; + + LatLon geographic_ref_point; + + projPJ geographic_crs; + +}; + +/** + * Dumps a Georeferencing to the debug output. + * + * Note that this requires a *reference*, not a pointer. + */ +QDebug operator<<(QDebug dbg, const Georeferencing &georef); + + + +//### Georeferencing inline code ### + +inline +constexpr unsigned int Georeferencing::scaleFactorPrecision() +{ + return 6u; +} + +inline +double Georeferencing::roundScaleFactor(double value) +{ + // This must match the implementation in scaleFactorPrecision(). + return floor(value*1000000.0+0.5)/1000000.0; +} + +inline +constexpr unsigned int Georeferencing::declinationPrecision() +{ + // This must match the implementation in declinationRound(). + return 2u; +} + +inline +double Georeferencing::roundDeclination(double value) +{ + // This must match the implementation in declinationPrecision(). + return floor(value*100.0+0.5)/100.0; +} + +inline +bool Georeferencing::isValid() const +{ + return state == Local || projected_crs; +} + +inline +bool Georeferencing::isLocal() const +{ + return state == Local; +} + +inline +Georeferencing::State Georeferencing::getState() const +{ + return state; +} + +inline +unsigned int Georeferencing::getScaleDenominator() const +{ + return scale_denominator; +} + +inline +double Georeferencing::getGridScaleFactor() const +{ + return grid_scale_factor; +} + +inline +double Georeferencing::getDeclination() const +{ + return declination; +} + +inline +double Georeferencing::getGrivation() const +{ + return grivation; +} + +inline +double Georeferencing::getGrivationError() const +{ + return grivation_error; +} + +inline +MapCoord Georeferencing::getMapRefPoint() const +{ + return map_ref_point; +} + +inline +QPointF Georeferencing::getProjectedRefPoint() const +{ + return projected_ref_point; +} + +inline +QString Georeferencing::getProjectedCRSId() const +{ + return projected_crs_id; +} + +inline +const std::vector& Georeferencing::getProjectedCRSParameters() const +{ + return projected_crs_parameters; +} + +inline +LatLon Georeferencing::getGeographicRefPoint() const +{ + return geographic_ref_point; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/image_transparency_fixup.h b/src/core/image_transparency_fixup.h new file mode 100644 index 0000000..3700923 --- /dev/null +++ b/src/core/image_transparency_fixup.h @@ -0,0 +1,95 @@ +/* + * Copyright 2012, 2013 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_IMAGE_TRANSPARENCY_FIXUP_H +#define OPENORIENTEERING_IMAGE_TRANSPARENCY_FIXUP_H + +#include + +namespace OpenOrienteering { + + +/** + * ImageTransparencyFixup repairs a particular issue with composing + * transparent pixels. + * + * QPainter::CompositionMode_Multiply and QPainter::CompositionMode_Darken + * on a QImage of Format_ARGB32_Premultiplied calculate the resulting alpha + * channel in a very efficient but not accurate way. A particular case is the + * composition of two fully transparent pixels which should in theory give a + * fully transparent pixel. Qt yields an alpha value of 1 (in 0..255) instead. + * The error accumulates with further compositions. + * + * This class may be used as a functor on a particular image, providing a + * comfortable way to fix the described case after each composition. + * + * Synopsis: + * + * QImage image(100, 100, QImage::Format_ARGB32_Premultiplied); + * ImageTransparencyFixup fixup(&image); + * QPainter p(&image); + * p.setCompositionMode(QPainter::CompositionMode_Multiply); + * p.drawImage(another_image); + * p.end(); + * fixup(); + */ +class ImageTransparencyFixup +{ +public: + /** + * Create a fixup functor for the given image. + * + * The image must be of QImage::Format_ARGB32_Premultiplied. + * It may be null. + */ + inline ImageTransparencyFixup(QImage* image) + : dest(0), dest_end(0) + { + // NOTE: Here we may add a check for a setting which disables the + // fixup (for better application performance) + if (image) + { + dest = (QRgb*)image->bits(); + dest_end = dest + image->byteCount() / sizeof(QRgb); + } + } + + /** + * Checks all pixels of the image for the known wrong result of composing + * fully transparent pixels, and replaces them with a fully transparent + * pixel. + */ + inline void operator()() const + { + for (QRgb* px = dest; px < dest_end; px++) + { + if (*px == 0x01000000) /* qRgba(0, 0, 0, 1) */ + *px = 0x00000000; /* qRgba(0, 0, 0, 0) */ + } + } + +protected: + QRgb* dest; + QRgb* dest_end; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/latlon.cpp b/src/core/latlon.cpp new file mode 100644 index 0000000..f7af8d9 --- /dev/null +++ b/src/core/latlon.cpp @@ -0,0 +1,36 @@ +/* + * Copyright 2012, 2013, 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "latlon.h" + +#include + + +namespace OpenOrienteering { + +QDebug operator<<(QDebug dbg, const LatLon& lat_lon) +{ + dbg.space() + << "LatLon" + << lat_lon.latitude() << lat_lon.longitude(); + return dbg.space(); +} + + +} // namespace OpenOrienteering diff --git a/src/core/latlon.h b/src/core/latlon.h new file mode 100644 index 0000000..f00452d --- /dev/null +++ b/src/core/latlon.h @@ -0,0 +1,138 @@ +/* + * Copyright 2012, 2013, 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_LATLON_H +#define OPENORIENTEERING_LATLON_H + +#include + +class QDebug; + +namespace OpenOrienteering { + +/** + * @brief Specifies geographic location by latitude and longitude. + * + * Latitude is a geographic coordinate that specifies the north-south + * position (φ, phi). + * + * Longitude is a geographic coordinate that specifies the east-west + * position (λ, lambda). + * + * LatLon is meant to be similar to QGeoCoordinate (part of Qt since 5.2). + * This Qt class might eventually replace LatLon. QGeoCoordinate has altitude + * as an additional property which is rarely used in Mapper at the moment. + * + * @see QGeoCoordinate (https://doc.qt.io/qt-5/qgeocoordinate.html) + */ +class LatLon +{ +private: + double latitude_value; + double longitude_value; + friend QDebug operator<<(QDebug dbg, const LatLon& lat_lon); + +public: + /** + * Constructs a new LatLon for the latitude and longitude given in degrees. + */ + constexpr LatLon(double latitude = 0.0, double longitude = 0.0) noexcept; + + /** + * Returns a new LatLon for the latitude and longitude given in radiant. + */ + constexpr static LatLon fromRadiant(double latitude, double longitude) noexcept; + + /** + * Returns the latitude value in degrees. + */ + constexpr double latitude() const; + + /** + * Returns the longitude value in degrees. + */ + constexpr double longitude() const; + + /** + * Returns true if this object has exactly the same latitude and longitude + * like another. FP precision issues are not taken into account. + */ + constexpr bool operator==(const LatLon& rhs) const; + + /** + * Returns true if this object has not exactly the same latitude and longitude + * like another. FP precision issues are not taken into account. + */ + constexpr bool operator!=(const LatLon& rhs) const; +}; + +/** + * Dumps a LatLon to the debug output. + * + * Note that this requires a *reference*, not a pointer. + */ +QDebug operator<<(QDebug dbg, const LatLon &lat_lon); + + + +//### LatLon inline code ### + +inline +constexpr LatLon::LatLon(double latitude, double longitude) noexcept +: latitude_value(latitude) +, longitude_value(longitude) +{ + ; // nothing +} + +inline +constexpr LatLon LatLon::fromRadiant(double latitude, double longitude) noexcept +{ + return LatLon(qRadiansToDegrees(latitude), qRadiansToDegrees(longitude)); +} + +inline +constexpr double LatLon::latitude() const +{ + return latitude_value; +} + +inline +constexpr double LatLon::longitude() const +{ + return longitude_value; +} + +inline +constexpr bool LatLon::operator==(const LatLon& rhs) const +{ + return (this->latitude_value == rhs.latitude_value) && (this->longitude_value == rhs.longitude_value); +} + +inline +constexpr bool LatLon::operator!=(const LatLon& rhs) const +{ + return !(*this == rhs); +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/map.cpp b/src/core/map.cpp new file mode 100644 index 0000000..95d0978 --- /dev/null +++ b/src/core/map.cpp @@ -0,0 +1,2673 @@ +/* + * Copyright 2012-2014 Thomas Schöps + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/georeferencing.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/map_grid.h" +#include "core/map_part.h" +#include "core/map_printer.h" +#include "core/map_view.h" +#include "core/objects/object.h" +#include "core/objects/object_operations.h" +#include "core/renderables/renderable.h" +#include "core/symbols/combined_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "core/symbols/text_symbol.h" +#include "fileformats/file_format.h" +#include "fileformats/file_format_registry.h" +#include "fileformats/file_import_export.h" +#include "gui/map/map_widget.h" +#include "gui/text_browser_dialog.h" +#include "templates/template.h" +#include "undo/map_part_undo.h" +#include "undo/object_undo.h" +#include "undo/undo.h" +#include "undo/undo_manager.h" +#include "util/util.h" +#include "util/transformation.h" + +// IWYU pragma: no_forward_declare QRectF + +#ifdef Q_OS_ANDROID +#include "core/storage_location.h" +#endif + + +namespace OpenOrienteering { + +QPointer map_symbol_translator{}; + + + +namespace { + +// ### Misc ### + +/** A record of information about the mapping of a color in a source MapColorSet + * to a color in a destination MapColorSet. + */ +struct MapColorSetMergeItem +{ + MapColor* src_color; + MapColor* dest_color; + std::size_t dest_index; + std::size_t lower_bound; + std::size_t upper_bound; + int lower_errors; + int upper_errors; + bool filter; + + MapColorSetMergeItem() + : src_color(nullptr), + dest_color(nullptr), + dest_index(0), + lower_bound(0), + upper_bound(0), + lower_errors(0), + upper_errors(0), + filter(false) + { } +}; + +/** The mapping of all colors in a source MapColorSet + * to colors in a destination MapColorSet. */ +typedef std::vector MapColorSetMergeList; + + +/** + * Shows a message box for a list of unformatted messages. + */ +void showMessageBox(QWidget* parent, const QString& title, const QString& headline, const std::vector& messages) +{ + QString document; + if (!headline.isEmpty()) + document += QLatin1String("

    ") + headline + QLatin1String("

    "); + for (const auto& message : messages) + document += Qt::convertFromPlainText(message, Qt::WhiteSpaceNormal); + + TextBrowserDialog dialog(document, parent); + dialog.setWindowTitle(title); + dialog.setWindowModality(Qt::WindowModal); + dialog.exec(); + // Let Android update the screen. + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); +} + + +} // namespace + + + +// ### MapColorSet ### + +Map::MapColorSet::MapColorSet() +{ + // nothing +} + +Map::MapColorSet::~MapColorSet() +{ + for (MapColor* color : colors) + delete color; +} + +void Map::MapColorSet::insert(int pos, MapColor* color) +{ + colors.insert(colors.begin() + pos, color); + adjustColorPriorities(pos + 1, colors.size()); +} + +void Map::MapColorSet::erase(int pos) +{ + colors.erase(colors.begin() + pos); + adjustColorPriorities(pos, colors.size()); +} + +void Map::MapColorSet::adjustColorPriorities(int first, int last) +{ + // TODO: delete or update RenderStates with these colors + for (int i = first; i < last; ++i) + colors[i]->setPriority(i); +} + +// This algorithm tries to maintain the relative order of colors. +MapColorMap Map::MapColorSet::importSet(const Map::MapColorSet& other, std::vector< bool >* filter, Map* map) +{ + MapColorMap out_pointermap; + + // Determine number of colors to import + std::size_t import_count = other.colors.size(); + if (filter) + { + Q_ASSERT(filter->size() == other.colors.size()); + + for (std::size_t i = 0, end = other.colors.size(); i != end; ++i) + { + MapColor* color = other.colors[i]; + if (!(*filter)[i] || out_pointermap.contains(color)) + { + continue; + } + + out_pointermap[color] = nullptr; // temporary used as a flag + + // Determine referenced spot colors, and add them to the filter + if (color->getSpotColorMethod() == MapColor::CustomColor) + { + for (const SpotColorComponent& component : color->getComponents()) + { + if (!out_pointermap.contains(component.spot_color)) + { + // Add this spot color to the filter + int j = 0; + while (other.colors[j] != component.spot_color) + ++j; + (*filter)[j] = true; + out_pointermap[component.spot_color] = nullptr; + } + } + } + } + import_count = out_pointermap.size(); + out_pointermap.clear(); + } + + if (import_count > 0) + { + colors.reserve(colors.size() + import_count); + + // The conflict resolution algorithm below is simplified by setting + // iterator `selected_item` to a real list element which is not related + // to the actual color sets we are merging. This is the extra element + // identified as `end_of_merge_list`. + MapColorSetMergeList merge_list{other.colors.size() + 1}; + const auto end_of_merge_list = end(merge_list) - 1; + + bool priorities_changed = false; + + // Initialize merge_list + auto merge_list_item = merge_list.begin(); + for (std::size_t i = 0; i < other.colors.size(); ++i) + { + merge_list_item->filter = (!filter || (*filter)[i]); + + MapColor* src_color = other.colors[i]; + merge_list_item->src_color = src_color; + for (std::size_t k = 0, colors_size = colors.size(); k < colors_size; ++k) + { + if (colors[k]->equals(*src_color, false)) + { + merge_list_item->dest_color = colors[k]; + merge_list_item->dest_index = k; + out_pointermap[src_color] = colors[k]; + // Prefer a matching color at the same priority, + // so just abort early if priority matches + if (merge_list_item->dest_color->getPriority() == merge_list_item->src_color->getPriority()) + break; + } + } + ++merge_list_item; + } + Q_ASSERT(merge_list_item == end_of_merge_list); + + size_t iteration_number = 1; + while (true) + { + // Evaluate bounds and conflicting order of colors + int max_conflict_reduction = 0; + auto selected_item = end_of_merge_list; // Note: non-const copy of an iterator + for (merge_list_item = begin(merge_list); merge_list_item != end_of_merge_list; ++merge_list_item) + { + // Check all lower colors for a higher dest_index + std::size_t& lower_bound(merge_list_item->lower_bound); + lower_bound = merge_list_item->dest_color ? merge_list_item->dest_index : 0; + auto it = merge_list.begin(); + for (; it != merge_list_item; ++it) + { + if (it->dest_color) + { + if (it->dest_index > lower_bound) + { + lower_bound = it->dest_index; + } + if (merge_list_item->dest_color && merge_list_item->dest_index < it->dest_index) + { + ++merge_list_item->lower_errors; + } + } + } + + // Check all higher colors for a lower dest_index + std::size_t& upper_bound(merge_list_item->upper_bound); + upper_bound = merge_list_item->dest_color ? merge_list_item->dest_index : colors.size(); + for (++it; it != end_of_merge_list; ++it) + { + if (it->dest_color) + { + if (it->dest_index < upper_bound) + { + upper_bound = it->dest_index; + } + if (merge_list_item->dest_color && merge_list_item->dest_index > it->dest_index) + { + ++merge_list_item->upper_errors; + } + } + } + + if (merge_list_item->filter) + { + if (merge_list_item->lower_errors == 0 && merge_list_item->upper_errors > max_conflict_reduction) + { + int conflict_reduction = merge_list_item->upper_errors; + // Check new conflicts with insertion index: selected_item->upper_bound + for (it = merge_list.begin(); it != merge_list_item; ++it) + { + if (it->dest_color && selected_item->upper_bound <= it->dest_index) + --conflict_reduction; + } + // Also allow = here to make two-step resolves work + if (conflict_reduction >= max_conflict_reduction) + { + selected_item = merge_list_item; + max_conflict_reduction = conflict_reduction; + } + } + else if (merge_list_item->upper_errors == 0 && merge_list_item->lower_errors > max_conflict_reduction) + { + int conflict_reduction = merge_list_item->lower_errors; + // Check new conflicts with insertion index: (selected_item->lower_bound+1) + it = merge_list_item; + for (++it; it != end_of_merge_list; ++it) + { + if (it->dest_color && (selected_item->lower_bound+1) > it->dest_index) + --conflict_reduction; + } + // Also allow = here to make two-step resolves work + if (conflict_reduction >= max_conflict_reduction) + { + selected_item = merge_list_item; + max_conflict_reduction = conflict_reduction; + } + } + } + } + + // Abort if no conflicts or maximum iteration count reached. + // The latter condition is just to prevent endless loops in + // case of bugs and should not occur theoretically. + if (selected_item == end_of_merge_list + || iteration_number > merge_list.size()) + break; + + // Solve selected conflict item + auto new_color = new MapColor(*selected_item->dest_color); + selected_item->dest_color = new_color; + out_pointermap[selected_item->src_color] = new_color; + std::size_t insertion_index = (selected_item->lower_errors == 0) ? selected_item->upper_bound : (selected_item->lower_bound+1); + + if (map) + map->addColor(new_color, insertion_index); + else + colors.insert(colors.begin() + insertion_index, new_color); + priorities_changed = true; + + for (merge_list_item = merge_list.begin(); merge_list_item != merge_list.end(); ++merge_list_item) + { + merge_list_item->lower_errors = 0; + merge_list_item->upper_errors = 0; + if (merge_list_item->dest_color && merge_list_item->dest_index >= insertion_index) + ++merge_list_item->dest_index; + } + selected_item->dest_index = insertion_index; + + ++iteration_number; + } + + merge_list.erase(end_of_merge_list); // no longer needed + + // Some missing colors may be spot color compositions which can be + // resolved to new colors only after all colors have been created. + // That is why we create all missing colors first. + for (auto it = merge_list.rbegin(); it != merge_list.rend(); ++it) + { + if (it->filter && !it->dest_color) + { + it->dest_color = new MapColor(*it->src_color); + out_pointermap[it->src_color] = it->dest_color; + } + else + { + // Existing colors don't need to be touched again. + it->dest_color = nullptr; + } + } + + // Now process all new colors for spot color resolution and insertion + for (auto it = merge_list.rbegin(); it != merge_list.rend(); ++it) + { + MapColor* new_color = it->dest_color; + if (new_color) + { + if (new_color->getSpotColorMethod() == MapColor::CustomColor) + { + SpotColorComponents components = new_color->getComponents(); + for (SpotColorComponent& component : components) + { + Q_ASSERT(out_pointermap.contains(component.spot_color)); + component.spot_color = out_pointermap[component.spot_color]; + } + new_color->setSpotColorComposition(components); + } + + std::size_t insertion_index = it->upper_bound; + if (map) + map->addColor(new_color, insertion_index); + else + colors.insert(colors.begin() + insertion_index, new_color); + priorities_changed = true; + } + } + + if (map && priorities_changed) + { + map->updateAllObjects(); + } + } + + return out_pointermap; +} + + + +// ### Map ### + +bool Map::static_initialized = false; +MapColor Map::covering_white(MapColor::CoveringWhite); +MapColor Map::covering_red(MapColor::CoveringRed); +MapColor Map::undefined_symbol_color(MapColor::Undefined); +MapColor Map::registration_color(MapColor::Registration); +LineSymbol* Map::covering_white_line; +LineSymbol* Map::covering_red_line; +LineSymbol* Map::undefined_line; +PointSymbol* Map::undefined_point; +TextSymbol* Map::undefined_text; +CombinedSymbol* Map::covering_combined_line; + +Map::Map() + : color_set() + , has_spot_colors(false) + , undo_manager(new UndoManager(this)) + , renderables(new MapRenderables(this)) + , selection_renderables(new MapRenderables(this)) + , renderable_options(Symbol::RenderNormal) + , printer_config(nullptr) +{ + if (!static_initialized) + initStatic(); + + georeferencing.reset(new Georeferencing()); + init(); + + connect(this, &Map::colorAdded, this, &Map::checkSpotColorPresence); + connect(this, &Map::colorChanged, this, &Map::checkSpotColorPresence); + connect(this, &Map::colorDeleted, this, &Map::checkSpotColorPresence); + connect(undo_manager.data(), &UndoManager::cleanChanged, this, &Map::undoCleanChanged); +} + +Map::~Map() +{ + clear(); // properly destruct all children +} + +void Map::clear() +{ + undo_manager->clear(); + + for (auto temp : templates) + delete temp; + templates.clear(); + first_front_template = 0; + + for (auto temp : closed_templates) + delete temp; + closed_templates.clear(); + + object_selection.clear(); + first_selected_object = nullptr; + selection_renderables->clear(); + + renderables->clear(); + + for (MapPart* part : parts) + delete part; + parts.clear(); + current_part_index = 0; + + for (auto symbol : symbols) + delete symbol; + symbols.clear(); + + // Don't clear() color_set: It is shared. +} + +void Map::init() +{ + color_set = new MapColorSet(); + + parts.push_back(new MapPart(tr("default part"), this)); + + widgets.clear(); + + undo_manager->setClean(); + + symbol_set_id = QString(); + map_notes = QString(); + + printer_config.reset(); + + image_template_use_meters_per_pixel = true; + image_template_meters_per_pixel = 0; + image_template_dpi = 0; + image_template_scale = 0; + + colors_dirty = false; + symbols_dirty = false; + templates_dirty = false; + objects_dirty = false; + other_dirty = false; + unsaved_changes = false; +} + +void Map::reset() +{ + clear(); + init(); +} + +void Map::setScaleDenominator(unsigned int value) +{ + georeferencing->setScaleDenominator(value); +} + +unsigned int Map::getScaleDenominator() const +{ + return georeferencing->getScaleDenominator(); +} + +void Map::changeScale(unsigned int new_scale_denominator, const MapCoord& scaling_center, bool scale_symbols, bool scale_objects, bool scale_georeferencing, bool scale_templates) +{ + if (new_scale_denominator == getScaleDenominator()) + return; + + double factor = getScaleDenominator() / (double)new_scale_denominator; + + if (scale_symbols) + scaleAllSymbols(factor); + if (scale_objects) + { + undo_manager->clear(); + scaleAllObjects(factor, scaling_center); + if (hasPrinterConfig()) + { + auto print_area = printer_config->print_area; + auto center = QPointF(scaling_center); + print_area.setTopLeft(center + factor * (print_area.topLeft() - center)); + print_area.setBottomRight(center + factor * (print_area.bottomRight() - center)); + printer_config->print_area = print_area; + } + } + if (scale_georeferencing) + georeferencing->setMapRefPoint(scaling_center + factor * (georeferencing->getMapRefPoint() - scaling_center)); + if (scale_templates) + { + for (int i = 0; i < getNumTemplates(); ++i) + { + Template* temp = getTemplate(i); + if (temp->isTemplateGeoreferenced()) + continue; + setTemplateAreaDirty(i); + temp->scale(factor, scaling_center); + setTemplateAreaDirty(i); + } + for (int i = 0; i < getNumClosedTemplates(); ++i) + { + Template* temp = getClosedTemplate(i); + if (temp->isTemplateGeoreferenced()) + continue; + temp->scale(factor, scaling_center); + } + } + + setScaleDenominator(new_scale_denominator); + setOtherDirty(); + updateAllMapWidgets(); +} + +void Map::rotateMap(double rotation, const MapCoord& center, bool adjust_georeferencing, bool adjust_declination, bool adjust_templates) +{ + if (std::fmod(rotation, 2 * M_PI) == 0) + return; + + undo_manager->clear(); + rotateAllObjects(rotation, center); + + if (adjust_georeferencing) + { + MapCoordF reference_point = MapCoordF(georeferencing->getMapRefPoint() - center); + reference_point.rotate(-rotation); + georeferencing->setMapRefPoint(MapCoord(MapCoordF(center) + reference_point)); + } + if (adjust_declination) + { + auto rotation_degrees = qRadiansToDegrees(rotation); + georeferencing->setDeclination(georeferencing->getDeclination() + rotation_degrees); + } + if (adjust_templates) + { + for (int i = 0; i < getNumTemplates(); ++i) + { + Template* temp = getTemplate(i); + if (temp->isTemplateGeoreferenced()) + continue; + setTemplateAreaDirty(i); + temp->rotate(rotation, center); + setTemplateAreaDirty(i); + } + for (int i = 0; i < getNumClosedTemplates(); ++i) + { + Template* temp = getClosedTemplate(i); + if (temp->isTemplateGeoreferenced()) + continue; + temp->rotate(rotation, center); + } + } + + setOtherDirty(); + updateAllMapWidgets(); +} + +void Map::setMapNotes(const QString& text) +{ + map_notes = text; +} + +bool Map::saveTo(const QString& path, MapView* view) +{ + bool success = exportTo(path, view); + if (success) + { + colors_dirty = false; + symbols_dirty = false; + templates_dirty = false; + objects_dirty = false; + other_dirty = false; + unsaved_changes = false; + undoManager().setClean(); + } + return success; +} + +bool Map::exportTo(const QString& path, MapView* view, const FileFormat* format) +{ + Q_ASSERT(view && "Saving a file without view information is not supported!"); + + if (!format) + format = FileFormats.findFormatForFilename(path); + + if (!format) + format = FileFormats.findFormat(FileFormats.defaultFormat()); + + if (!format) + { + QMessageBox::warning(nullptr, tr("Error"), tr("Cannot export the map as\n\"%1\"\nbecause the format is unknown.").arg(path)); + return false; + } + else if (!format->supportsExport()) + { + QMessageBox::warning(nullptr, tr("Error"), tr("Cannot export the map as\n\"%1\"\nbecause saving as %2 (.%3) is not supported."). + arg(path, format->description(), format->fileExtensions().join(QLatin1String(", ")))); + return false; + } + + QSaveFile file(path); + QScopedPointer exporter(format->createExporter(&file, this, view)); + bool success = false; + if (file.open(QIODevice::WriteOnly)) + { + try + { + exporter->doExport(); + } + catch (std::exception &e) + { + file.cancelWriting(); + const QString error = QString::fromLocal8Bit(e.what()); + QMessageBox::warning(nullptr, tr("Error"), tr("Internal error while saving:\n%1").arg(error)); + return false; + } + + success = file.commit(); +#ifdef Q_OS_ANDROID + if (success) + { + // Make the MediaScanner aware of the *updated* file. This is an + // attempt to resolve issues with files being transferred + // incompletely to the PC (#1115). + Android::mediaScannerScanFile(QFileInfo(path).absolutePath()); + } +#endif + } + + if (!success) + { + QMessageBox::warning( + nullptr, + tr("Error"), + tr("Cannot save file\n%1:\n%2").arg(path, file.errorString()) + ); + } + else if (!exporter->warnings().empty()) + { + showMessageBox(nullptr, + tr("Warning"), + tr("The map export generated warnings."), + exporter->warnings() ); + } + + return success; +} + +bool Map::loadFrom(const QString& path, QWidget* dialog_parent, MapView* view, bool load_symbols_only, bool show_error_messages) +{ + // Ensure the file exists and is readable. + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) + { + if (show_error_messages) + QMessageBox::warning( + dialog_parent, + tr("Error"), + tr("Cannot open file:\n%1\nfor reading.").arg(path) + ); + return false; + } + + // Delete previous objects + reset(); + + // Read a block at the beginning of the file, that we can use for magic number checking. + unsigned char buffer[256]; + size_t total_read = file.read((char *)buffer, 256); + file.seek(0); + + bool import_complete = false; + QString error_msg = tr("Invalid file type."); + for (auto format : FileFormats.formats()) + { + // If the format supports import, and thinks it can understand the file header, then proceed. + if (format->supportsImport() && format->understands(buffer, total_read)) + { + Importer *importer = nullptr; + // Wrap everything in a try block, so we can gracefully recover if the importer balks. + try { + // Create an importer instance for this file and map. + importer = format->createImporter(&file, this, view); + + // Run the first pass. + importer->doImport(load_symbols_only, QFileInfo(path).absolutePath()); + + // Are there any actions the user must take to complete the import? + if (!importer->actions().empty()) + { + // TODO: prompt the user to resolve the action items. All-in-one dialog. + } + + // Finish the import. + importer->finishImport(); + + file.close(); + + // Display any warnings. + if (!importer->warnings().empty() && show_error_messages) + { + showMessageBox(nullptr, + tr("Warning"), + tr("The map import generated warnings."), + importer->warnings() ); + } + + import_complete = true; + } + catch (FileFormatException &e) + { + error_msg = e.message(); + } + catch (std::exception &e) + { + qDebug() << "Exception:" << e.what(); + error_msg = QString::fromLatin1(e.what()); + } + if (importer) delete importer; + } + // If the last importer finished successfully + if (import_complete) break; + } + + if (view) + { + view->setPanOffset(QPoint(0, 0)); + } + + if (!import_complete) + { + if (show_error_messages) + QMessageBox::warning( + dialog_parent, + tr("Error"), + tr("Cannot open file:\n%1\n\n%2").arg(path, error_msg) + ); + return false; + } + + // Update all objects without trying to remove their renderables first, this gives a significant speedup when loading large files + updateAllObjects(); // TODO: is the comment above still applicable? + + setHasUnsavedChanges(false); + + return true; +} + +void Map::importMap( + const Map* other, + ImportMode mode, + QWidget* dialog_parent, + std::vector* filter, + int symbol_insert_pos, + bool merge_duplicate_symbols, + QHash* out_symbol_map ) +{ + // Check if there is something to import + if (other->getNumColors() == 0 + && other->getNumSymbols() == 0 + && other->getNumObjects() == 0) + { + QMessageBox::critical(dialog_parent, tr("Error"), tr("Nothing to import.")); + return; + } + + // Check scale + if ((mode & 0x0f) != ColorImport + && other->getNumSymbols() > 0 + && other->getScaleDenominator() != getScaleDenominator()) + { + int answer = QMessageBox::question(dialog_parent, tr("Question"), + tr("The scale of the imported data is 1:%1 which is different from this map's scale of 1:%2.\n\nRescale the imported data?") + .arg(QLocale().toString(other->getScaleDenominator()), + QLocale().toString(getScaleDenominator())), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (answer == QMessageBox::Yes) + { + /// \todo No need to clone iff the other map is discarded after import. + Map clone; + clone.setGeoreferencing(other->getGeoreferencing()); + clone.importMap(other, mode, dialog_parent, filter, -1, false, out_symbol_map); + clone.changeScale(getScaleDenominator(), MapCoord(0, 0), true, true, true, true); + QHash symbol_map; // clone symbol -> this map's symbol + importMap(&clone, mode, dialog_parent, nullptr, symbol_insert_pos, merge_duplicate_symbols, &symbol_map); + if (out_symbol_map) // original imported symbol -> clone symbol + { + Q_ASSERT(symbol_map.size() == out_symbol_map->size()); + for (auto& symbol : *out_symbol_map) + { + Q_ASSERT(symbol_map.contains(symbol)); + symbol = symbol_map.value(symbol); // original imported symbol -> this map's symbol + } + } + return; + } + } + + QTransform q_transform; + if (mode.testFlag(GeorefImport)) + { + /// \todo Test and review import of georeferenced and non-georeferenced maps, in all combinations. + /// \todo Handle rotation of patterns and text, cf. Object::transform. + const auto& georef = getGeoreferencing(); + const auto& other_georef = other->getGeoreferencing(); + const auto src_origin = MapCoordF { other_georef.getMapRefPoint() }; + + bool ok0, ok1, ok2; + PassPointList passpoints; + passpoints.resize(3); + passpoints[0].src_coords = src_origin; + passpoints[0].dest_coords = georef.toMapCoordF(&other_georef, passpoints[0].src_coords, &ok0); + passpoints[1].src_coords = src_origin + MapCoordF { 128.0, 0.0 }; // 128 mm off horizontally + passpoints[1].dest_coords = georef.toMapCoordF(&other_georef, passpoints[1].src_coords, &ok1); + passpoints[2].src_coords = src_origin + MapCoordF { 0.0, 128.0 }; // 128 mm off vertically + passpoints[2].dest_coords = georef.toMapCoordF(&other_georef, passpoints[2].src_coords, &ok2); + if (ok0 && ok1 && ok2 + && !passpoints.estimateNonIsometricSimilarityTransform(&q_transform)) + { + /// \todo proper error message + qDebug("Failed to calculate transformation"); + q_transform.reset(); + } + } + + auto symbol_map = importMap(*other, mode, filter, symbol_insert_pos, merge_duplicate_symbols, q_transform); + if (out_symbol_map) + *out_symbol_map = symbol_map; +} + + +QHash Map::importMap( + const Map& imported_map, + ImportMode mode, + std::vector* filter, + int symbol_insert_pos, + bool merge_duplicate_symbols, + const QTransform& transform) +{ + // Determine which symbols and colors to import + std::vector color_filter(std::size_t(imported_map.getNumColors()), true); + std::vector symbol_filter(std::size_t(imported_map.getNumSymbols()), true); + if (mode.testFlag(MinimalImport)) + { + switch (mode & 0x0f) + { + case ObjectImport: + if (imported_map.getNumObjects() > 0) + imported_map.determineSymbolsInUse(symbol_filter); + imported_map.determineColorsInUse(symbol_filter, color_filter); + break; + case SymbolImport: + if (filter) + { + Q_ASSERT(filter->size() == symbol_filter.size()); + symbol_filter = *filter; + imported_map.determineSymbolUseClosure(symbol_filter); + } + imported_map.determineColorsInUse(symbol_filter, color_filter); + break; + case ColorImport: + if (filter) + { + Q_ASSERT(filter->size() == color_filter.size()); + color_filter = *filter; + } + break; + default: + Q_UNREACHABLE(); + } + } + + // Import colors + auto color_map = color_set->importSet(*imported_map.color_set, &color_filter, this); + + QHash symbol_map; + if ((mode & 0x0f) != ColorImport) + { + if (imported_map.getNumSymbols() > 0) + { + // Import symbols + symbol_map = importSymbols(imported_map, color_map, symbol_insert_pos, merge_duplicate_symbols, symbol_filter); + } + + if ((mode & 0x0f) != SymbolImport + && imported_map.getNumObjects() > 0) + { + // Import parts like this: + // - if the other map has only one part, import it into the current part + // - else check if there is already a part with an equal name for every part to import and import into this part if found, else create a new part + auto* undo_step = new CombinedUndoStep(this); + for (const auto* part_to_import : imported_map.parts) + { + MapPart* dest_part = nullptr; + if (imported_map.parts.size() == 1) + { + dest_part = getCurrentPart(); + } + else + { + for (auto* check_part : parts) + { + if (check_part->getName().compare(part_to_import->getName(), Qt::CaseInsensitive) == 0) + { + dest_part = check_part; + break; + } + } + if (!dest_part) + { + // Import as new part + dest_part = new MapPart(part_to_import->getName(), this); + addPart(dest_part, 0); + undo_step->push(new MapPartUndoStep(this, MapPartUndoStep::RemoveMapPart, 0)); + } + } + + // Temporarily switch the current part for importing so the undo step gets created for the right part + MapPart* temp_current_part = getCurrentPart(); + current_part_index = std::size_t(findPartIndex(dest_part)); + + bool select_and_center_objects = dest_part == temp_current_part; + if (auto import_undo = dest_part->importPart(part_to_import, symbol_map, transform, select_and_center_objects)) + { + undo_step->push(import_undo.release()); + if (select_and_center_objects) + ensureVisibilityOfSelectedObjects(Map::FullVisibility); + } + + current_part_index = std::size_t(findPartIndex(temp_current_part)); + } + push(undo_step); + } + } + + return symbol_map; +} + + + +bool Map::exportToIODevice(QIODevice* stream) +{ + stream->open(QIODevice::WriteOnly); + Exporter* exporter = nullptr; + try { + const FileFormat* native_format = FileFormats.findFormat("XML"); + exporter = native_format->createExporter(stream, this, nullptr); + exporter->doExport(); + stream->close(); + } + catch (std::exception &e) + { + if (exporter) + delete exporter; + return false; + } + if (exporter) + delete exporter; + return true; +} + +bool Map::importFromIODevice(QIODevice* stream) +{ + Importer* importer = nullptr; + try { + const FileFormat* native_format = FileFormats.findFormat("XML"); + importer = native_format->createImporter(stream, this, nullptr); + importer->doImport(false); + importer->finishImport(); + stream->close(); + } + catch (std::exception &e) + { + if (importer) + delete importer; + return false; + } + if (importer) + delete importer; + return true; +} + +void Map::draw(QPainter* painter, const RenderConfig& config) +{ + // Update the renderables of all objects marked as dirty + updateObjects(); + + // The actual drawing + renderables->draw(painter, config); +} + +void Map::drawOverprintingSimulation(QPainter* painter, const RenderConfig& config) +{ + // Update the renderables of all objects marked as dirty + updateObjects(); + + // The actual drawing + renderables->drawOverprintingSimulation(painter, config); +} + +void Map::drawColorSeparation(QPainter* painter, const RenderConfig& config, const MapColor* spot_color, bool use_color) +{ + // Update the renderables of all objects marked as dirty + updateObjects(); + + // The actual drawing + renderables->drawColorSeparation(painter, config, spot_color, use_color); +} + +void Map::drawGrid(QPainter* painter, const QRectF& bounding_box) +{ + grid.draw(painter, bounding_box, this); +} + +void Map::drawTemplates(QPainter* painter, const QRectF& bounding_box, int first_template, int last_template, const MapView* view, bool on_screen) const +{ + for (int i = first_template; i <= last_template; ++i) + { + const Template* temp = getTemplate(i); + if (temp->getTemplateState() != Template::Loaded) + continue; + + double scale = std::max(temp->getTemplateScaleX(), temp->getTemplateScaleY()); + auto visibility = TemplateVisibility{ 1, true }; + if (view) + { + visibility = view->getTemplateVisibility(temp); + visibility.visible &= visibility.opacity > 0; + scale *= view->getZoom(); + } + if (visibility.visible) + { + Q_ASSERT(visibility.opacity == 1 || painter->paintEngine()->hasFeature(QPaintEngine::ConstantOpacity)); + painter->save(); + temp->drawTemplate(painter, bounding_box, scale, on_screen, visibility.opacity); + painter->restore(); + } + } +} + +void Map::updateObjects() +{ + // TODO: It maybe would be better if the objects entered themselves into a separate list when they get dirty so not all objects have to be traversed here + applyOnAllObjects(&Object::update); +} + +void Map::removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty) +{ + renderables->removeRenderablesOfObject(object, mark_area_as_dirty); + if (isObjectSelected(object)) + removeSelectionRenderables(object); +} +void Map::insertRenderablesOfObject(const Object* object) +{ + renderables->insertRenderablesOfObject(object); + if (isObjectSelected(object)) + addSelectionRenderables(object); +} + + +void Map::markAsIrregular(Object* object) +{ + irregular_objects.insert(object); +} + +const std::set Map::irregularObjects() const +{ + return irregular_objects; +} + +std::size_t Map::deleteIrregularObjects() +{ + std::size_t result = 0; + std::set unhandled; + for (auto object : irregular_objects) + { + for (auto part : parts) + { + if (part->deleteObject(object, false)) + { + ++result; + goto next_object; + } + } + unhandled.insert(object); +next_object: + ; // nothing else + } + + irregular_objects.swap(unhandled); + return result; +} + + +void Map::getSelectionToSymbolCompatibility(const Symbol* symbol, bool& out_compatible, bool& out_different) const +{ + out_compatible = symbol && !object_selection.empty(); + out_different = false; + + if (symbol) + { + for (const Object* object : object_selection) + { + if (!symbol->isTypeCompatibleTo(object)) + { + out_compatible = false; + out_different = true; + return; + } + else if (symbol != object->getSymbol()) + out_different = true; + } + } +} + +void Map::deleteSelectedObjects() +{ + auto obj = selectedObjectsBegin(); + auto end = selectedObjectsEnd(); + if (obj != end) + { + // FIXME: this is not ready for multiple map parts. + auto undo_step = new AddObjectsUndoStep(this); + MapPart* part = getCurrentPart(); + + for (; obj != end; ++obj) + { + int index = part->findObjectIndex(*obj); + if (index >= 0) + { + undo_step->addObject(index, *obj); + part->deleteObject(index, true); + } + else + { + qDebug() << this << "::deleteSelectedObjects(): Object" << *obj << "not found in current map part."; + } + } + + setObjectsDirty(); + clearObjectSelection(true); + push(undo_step); + } +} + +void Map::includeSelectionRect(QRectF& rect) const +{ + for (const Object* object : object_selection) + rectIncludeSafe(rect, object->getExtent()); +} + +void Map::drawSelection(QPainter* painter, bool force_min_size, MapWidget* widget, MapRenderables* replacement_renderables, bool draw_normal) +{ + MapView* view = widget->getMapView(); + + painter->save(); + painter->translate(widget->width() / 2.0 + view->panOffset().x(), widget->height() / 2.0 + view->panOffset().y()); + painter->setWorldTransform(view->worldTransform(), true); + + if (!replacement_renderables) + replacement_renderables = selection_renderables.data(); + + RenderConfig::Options options = RenderConfig::Screen | RenderConfig::HelperSymbols; + qreal selection_opacity = 1.0; + if (force_min_size) + options |= RenderConfig::ForceMinSize; + if (!draw_normal) + { + options |= RenderConfig::Highlighted; + selection_opacity = 0.4; + } + RenderConfig config = { *this, view->calculateViewedRect(widget->viewportToView(widget->rect())), view->calculateFinalZoomFactor(), options, selection_opacity }; + replacement_renderables->draw(painter, config); + + painter->restore(); +} + +void Map::addObjectToSelection(Object* object, bool emit_selection_changed) +{ + Q_ASSERT(!isObjectSelected(object)); + object_selection.insert(object); + addSelectionRenderables(object); + if (!first_selected_object) + first_selected_object = object; + if (emit_selection_changed) + emit objectSelectionChanged(); +} + +void Map::removeObjectFromSelection(Object* object, bool emit_selection_changed) +{ + bool removed = object_selection.erase(object); + Q_ASSERT(removed && "Map::removeObjectFromSelection: object was not selected!"); + Q_UNUSED(removed); + removeSelectionRenderables(object); + if (first_selected_object == object) + first_selected_object = object_selection.empty() ? nullptr : *object_selection.begin(); + if (emit_selection_changed) + emit objectSelectionChanged(); +} + +bool Map::removeSymbolFromSelection(const Symbol* symbol, bool emit_selection_changed) +{ + bool removed_at_least_one_object = false; + auto it_end = object_selection.end(); + for (auto it = object_selection.begin(); it != it_end; ) + { + if ((*it)->getSymbol() != symbol) + { + ++it; + continue; + } + + removed_at_least_one_object = true; + removeSelectionRenderables(*it); + Object* removed_object = *it; + it = object_selection.erase(it); + if (first_selected_object == removed_object) + first_selected_object = object_selection.empty() ? nullptr : *object_selection.begin(); + } + if (emit_selection_changed && removed_at_least_one_object) + emit objectSelectionChanged(); + return removed_at_least_one_object; +} + +bool Map::isObjectSelected(const Object* object) const +{ + return object_selection.find(const_cast(object)) != object_selection.end(); +} + +bool Map::toggleObjectSelection(Object* object, bool emit_selection_changed) +{ + if (isObjectSelected(object)) + { + removeObjectFromSelection(object, emit_selection_changed); + return false; + } + else + { + addObjectToSelection(object, emit_selection_changed); + return true; + } +} + +void Map::clearObjectSelection(bool emit_selection_changed) +{ + selection_renderables->clear(); + object_selection.clear(); + first_selected_object = nullptr; + + if (emit_selection_changed) + emit objectSelectionChanged(); +} + +void Map::emitSelectionChanged() +{ + emit objectSelectionChanged(); +} + +void Map::emitSelectionEdited() +{ + emit selectedObjectEdited(); +} + +void Map::addMapWidget(MapWidget* widget) +{ + widgets.push_back(widget); +} + +void Map::removeMapWidget(MapWidget* widget) +{ + widgets.erase(std::remove(begin(widgets), end(widgets), widget), end(widgets)); +} + + +void Map::updateAllMapWidgets() +{ + for (MapWidget* widget : widgets) + widget->updateEverything(); +} + + +void Map::ensureVisibilityOfSelectedObjects(SelectionVisibility visibility) +{ + if (!object_selection.empty()) + { + QRectF rect; + includeSelectionRect(rect); + + for (MapWidget* widget : widgets) + { + switch (visibility) + { + case FullVisibility: + widget->ensureVisibilityOfRect(rect, MapWidget::DiscreteZoom); + break; + + case PartialVisibility: + if (!widget->getMapView()->calculateViewedRect(widget->viewportToView(widget->geometry())).intersects(rect)) + widget->ensureVisibilityOfRect(rect, MapWidget::DiscreteZoom); + break; + + case IgnoreVisibilty: + break; // Do nothing + + default: + Q_UNREACHABLE(); + } + } + } +} + + +void Map::setDrawingBoundingBox(const QRectF& map_coords_rect, int pixel_border, bool do_update) +{ + for (MapWidget* widget : widgets) + widget->setDrawingBoundingBox(map_coords_rect, pixel_border, do_update); +} + +void Map::clearDrawingBoundingBox() +{ + for (MapWidget* widget : widgets) + widget->clearDrawingBoundingBox(); +} + + +void Map::setActivityBoundingBox(const QRectF& map_coords_rect, int pixel_border, bool do_update) +{ + for (MapWidget* widget : widgets) + widget->setActivityBoundingBox(map_coords_rect, pixel_border, do_update); +} + +void Map::clearActivityBoundingBox() +{ + for (MapWidget* widget : widgets) + widget->clearActivityBoundingBox(); +} + + +void Map::updateDrawing(const QRectF& map_coords_rect, int pixel_border) +{ + for (MapWidget* widget : widgets) + widget->updateDrawing(map_coords_rect, pixel_border); +} + + + +QString Map::translate(const QString& symbol_text) const +{ + auto result = raw_translation(symbol_text); + if (result.isEmpty()) + result = symbol_text; + return result; +} + +QString Map::raw_translation(const QString& symbol_text) const +{ + auto result = QString{}; + if (map_symbol_translator) + result = map_symbol_translator->translate(symbol_set_id.toUtf8(), symbol_text.toUtf8()); + return result; +} + + + +void Map::setColor(MapColor* color, int pos) +{ + // MapColor* old_color = color_set->colors[pos]; + + color_set->colors[pos] = color; + color->setPriority(pos); + + if (color->getSpotColorMethod() == MapColor::SpotColor) + { + // Update dependent colors + for (MapColor* map_color : color_set->colors) + { + if (map_color->getSpotColorMethod() == MapColor::CustomColor) + { + for (const SpotColorComponent& component : map_color->getComponents()) + { + if (component.spot_color == color) + { + // Assuming each spot color is rarely used more than once per composition + if (map_color->getCmykColorMethod() == MapColor::SpotColor) + map_color->setCmykFromSpotColors(); + if (map_color->getRgbColorMethod() == MapColor::SpotColor) + map_color->setRgbFromSpotColors(); + updateSymbolIcons(map_color); + emit colorChanged(map_color->getPriority(), map_color); + } + } + } + } + } + else + { + // Remove from dependent colors + for (MapColor* map_color : color_set->colors) + { + if (map_color->getSpotColorMethod() == MapColor::CustomColor + && map_color->removeSpotColorComponent(color)) + { + updateSymbolIcons(map_color); + emit colorChanged(map_color->getPriority(), map_color); + } + } + } + + updateSymbolIcons(color); + emit colorChanged(pos, color); +} + +void Map::addColor(MapColor* color, int pos) +{ + color_set->insert(pos, color); + if (getNumColors() == 1) + { + // This is the first color - the help text in the map widget(s) should be updated + updateAllMapWidgets(); + } + setColorsDirty(); + emit colorAdded(pos, color); + color->setPriority(pos); +} + +void Map::deleteColor(int pos) +{ + MapColor* color = color_set->colors[pos]; + if (color->getSpotColorMethod() == MapColor::SpotColor) + { + // Update dependent colors + for (MapColor* map_color : color_set->colors) + { + if (map_color->removeSpotColorComponent(color)) + { + updateSymbolIcons(map_color); + emit colorChanged(map_color->getPriority(), map_color); + } + } + } + + color_set->erase(pos); + + if (getNumColors() == 0) + { + // That was the last color - the help text in the map widget(s) should be updated + updateAllMapWidgets(); + } + + // Treat combined symbols first before their parts + for (Symbol* symbol : symbols) + { + if (symbol->getType() == Symbol::Combined) + symbol->colorDeleted(color); + } + for (Symbol* symbol : symbols) + { + if (symbol->getType() != Symbol::Combined) + symbol->colorDeleted(color); + } + emit colorDeleted(pos, color); + + delete color; +} + +int Map::findColorIndex(const MapColor* color) const +{ + std::size_t size = color_set->colors.size(); + for (std::size_t i = 0; i < size; ++i) + { + if (color_set->colors[i] == color) + return (int)i; + } + if (color && color->getPriority() == MapColor::Registration) + { + return MapColor::Registration; + } + return -1; +} + +void Map::setColorsDirty() +{ + colors_dirty = true; + setHasUnsavedChanges(true); +} + +void Map::useColorsFrom(Map* map) +{ + color_set = map->color_set; +} + +bool Map::isColorUsedByASymbol(const MapColor* color) const +{ + for (const Symbol* symbol : symbols) + { + if (symbol->containsColor(color)) + return true; + } + return false; +} + +void Map::determineColorsInUse(const std::vector< bool >& by_which_symbols, std::vector< bool >& out) const +{ + if (getNumSymbols() == 0) + { + out.clear(); + return; + } + + Q_ASSERT(int(by_which_symbols.size()) == getNumSymbols()); + out.assign(std::size_t(getNumColors()), false); + for (std::size_t c = 0, last = std::size_t(getNumColors()); c != last; ++c) + { + for (std::size_t s = 0, last_s = std::size_t(getNumSymbols()); s != last_s; ++s) + { + if (by_which_symbols[s] && getSymbol(int(s))->containsColor(getColor(int(c)))) + { + out[c] = true; + break; + } + } + } + + // Include required spot colors, too + for (std::size_t c = 0, last_c = std::size_t(getNumColors()); c != last_c; ++c) + { + if (out[c]) + continue; + + const auto color = getColor(int(c)); + if (color->getSpotColorMethod() != MapColor::SpotColor) + continue; + + for (std::size_t o = 0, last_o = std::size_t(getNumColors()); o != last_o; ++o) + { + if (!out[o]) + continue; + + const auto other = getColor(int(o)); + if (other->getSpotColorMethod() != MapColor::CustomColor) + continue; + + const auto& components = other->getComponents(); + if (std::any_of(begin(components), end(components), [color](auto& component) { + return component.spot_color == color; + })) + { + out[c] = true; + break; + } + } + } +} + +void Map::checkSpotColorPresence() +{ + const bool has_spot_colors = hasSpotColors(); + if (this->has_spot_colors != has_spot_colors) + { + this->has_spot_colors = has_spot_colors; + emit spotColorPresenceChanged(has_spot_colors); + } +} + +bool Map::hasSpotColors() const +{ + for (const MapColor* color : color_set->colors) + { + if (color->getSpotColorMethod() == MapColor::SpotColor) + return true; + } + return false; +} + +bool Map::hasAlpha() const +{ + return std::any_of(begin(color_set->colors), end(color_set->colors), [this](const MapColor* color) { + const auto opacity = color->getOpacity(); + return opacity > 0 && opacity < 1 + && std::any_of(begin(symbols), end(symbols), [this, color](const Symbol* symbol) { + return !symbol->isHidden() + && symbol->containsColor(color) + && this->existsObjectWithSymbol(symbol); + }); + }); +} + + +void Map::setSymbolSetId(const QString& id) +{ + symbol_set_id = id; + symbols_dirty = true; +} + + +QHash Map::importSymbols( + const Map& other, + const MapColorMap& color_map, + int insert_pos, + bool merge_duplicates, + const std::vector& filter ) +{ + QHash out_pointermap; + + std::vector created_symbols; + created_symbols.reserve(other.symbols.size()); + for (std::size_t i = 0, last = other.symbols.size(); i < last; ++i) + { + if (filter.empty() || filter[i]) + { + const Symbol* symbol = other.symbols[i]; + if (merge_duplicates) + { + // Check if symbol is already present + auto match = std::find_if(begin(symbols), end(symbols), [symbol](auto s) { + return s->equals(symbol, Qt::CaseInsensitive, false); + }); + if (match != end(symbols)) + { + // Symbol is already present + out_pointermap.insert(symbol, *match); + continue; + } + } + + auto new_symbol = symbol->duplicate(&color_map); + out_pointermap.insert(symbol, new_symbol); + created_symbols.push_back(new_symbol); + } + } + + + // Add the created symbols + if (insert_pos < 0) + insert_pos = getNumSymbols(); + for (const auto symbol : created_symbols) + { + addSymbol(symbol, insert_pos); + ++insert_pos; + } + + // Notify the created symbols of the new context (mind combined symbols) + for (const auto symbol : created_symbols) + { + for (auto it = out_pointermap.constBegin(); it != out_pointermap.constEnd(); ++it) + { + // symbol is what was created by duplicate() above. + symbol->symbolChanged(it.key(), it.value()); + } + } + + return out_pointermap; +} + + + +void Map::addSelectionRenderables(const Object* object) +{ + object->update(); + selection_renderables->insertRenderablesOfObject(object); +} + +void Map::updateSelectionRenderables(const Object* object) +{ + removeSelectionRenderables(object); + addSelectionRenderables(object); +} + +void Map::removeSelectionRenderables(const Object* object) +{ + selection_renderables->removeRenderablesOfObject(object, false); +} + +void Map::initStatic() +{ + static_initialized = true; + + covering_white_line = new LineSymbol(); + covering_white_line->setColor(&covering_white); + covering_white_line->setLineWidth(3.0); + + covering_red_line = new LineSymbol(); + covering_red_line->setColor(&covering_red); + covering_red_line->setLineWidth(1.0); + + covering_combined_line = new CombinedSymbol(); + covering_combined_line->setNumParts(2); + covering_combined_line->setPart(0, covering_white_line, false); + covering_combined_line->setPart(1, covering_red_line, false); + + // Undefined symbols + undefined_line = new LineSymbol(); + undefined_line->setColor(&undefined_symbol_color); + undefined_line->setLineWidth(1); + undefined_line->setIsHelperSymbol(true); + + undefined_point = new PointSymbol(); + undefined_point->setInnerRadius(100); + undefined_point->setInnerColor(&undefined_symbol_color); + undefined_point->setIsHelperSymbol(true); + + undefined_text = new TextSymbol(); + undefined_text->setColor(&undefined_symbol_color); + undefined_text->setIsHelperSymbol(true); +} + +void Map::addSymbol(Symbol* symbol, int pos) +{ + symbols.insert(symbols.begin() + pos, symbol); + if (symbols.size() == 1) + { + // This is the first symbol - the help text in the map widget(s) should be updated + updateAllMapWidgets(); + } + + emit symbolAdded(pos, symbol); + setSymbolsDirty(); +} + +void Map::moveSymbol(int from, int to) +{ + symbols.insert(symbols.begin() + to, symbols[from]); + if (from > to) + ++from; + symbols.erase(symbols.begin() + from); + // TODO: emit symbolChanged(pos, symbol); ? + setSymbolsDirty(); +} + +const Symbol* Map::getSymbol(int i) const +{ + if (i >= 0) + return symbols[i]; + else if (i == -1) + return nullptr; + else if (i == -2) + return getUndefinedPoint(); + else if (i == -3) + return getUndefinedLine(); + else + { + Q_ASSERT(!"Invalid symbol index given"); + return getUndefinedLine(); + } +} + +Symbol* Map::getSymbol(int i) +{ + return const_cast(static_cast(this)->getSymbol(i)); +} + +void Map::setSymbol(Symbol* symbol, int pos) +{ + Symbol* old_symbol = symbols[pos]; + + // Check if an object with this symbol is selected + bool object_with_symbol_selected = false; + for (const Object* object : object_selection) + { + if (object->getSymbol() == old_symbol || object->getSymbol()->containsSymbol(old_symbol)) + { + object_with_symbol_selected = true; + break; + } + } + + changeSymbolForAllObjects(old_symbol, symbol); + + int size = (int)symbols.size(); + for (int i = 0; i < size; ++i) + { + if (i == pos) + continue; + + if (symbols[i]->symbolChanged(symbols[pos], symbol)) + updateAllObjectsWithSymbol(symbols[i]); + } + + // Change the symbol + symbols[pos] = symbol; + emit symbolChanged(pos, symbol, old_symbol); + setSymbolsDirty(); + delete old_symbol; + + if (object_with_symbol_selected) + emit selectedObjectEdited(); +} + +void Map::deleteSymbol(int pos) +{ + if (deleteAllObjectsWithSymbol(symbols[pos])) + undo_manager->clear(); + + int size = (int)symbols.size(); + for (int i = 0; i < size; ++i) + { + if (i == pos) + continue; + + if (symbols[i]->symbolChanged(symbols[pos], nullptr)) + updateAllObjectsWithSymbol(symbols[i]); + } + + // Delete the symbol + Symbol* temp = symbols[pos]; + delete symbols[pos]; + symbols.erase(symbols.begin() + pos); + + if (symbols.empty()) + { + // That was the last symbol - the help text in the map widget(s) should be updated + updateAllMapWidgets(); + } + + emit symbolDeleted(pos, temp); + setSymbolsDirty(); +} + +int Map::findSymbolIndex(const Symbol* symbol) const +{ + if (!symbol) + return -1; + int size = (int)symbols.size(); + for (int i = 0; i < size; ++i) + { + if (symbols[i] == symbol) + return i; + } + + if (symbol == undefined_point) + return -2; + else if (symbol == undefined_line) + return -3; + else if (symbol == undefined_text) + return -4; + + // maybe element of point symbol + return -1; +} + +void Map::setSymbolsDirty() +{ + if (symbol_icon_scale > 0) + { + symbol_icon_scale = 0; + QTimer::singleShot(0, this, SLOT(updateSymbolIconZoom())); + } + symbols_dirty = true; + setHasUnsavedChanges(true); +} + +void Map::updateSymbolIcons(const MapColor* color) +{ + for (std::size_t i = 0, size = symbols.size(); i < size; ++i) + { + if (symbols[i]->containsColor(color)) + { + symbols[i]->resetIcon(); + emit symbolIconChanged(i); + } + } +} + +void Map::scaleAllSymbols(double factor) +{ + int size = getNumSymbols(); + for (int i = 0; i < size; ++i) + { + Symbol* symbol = getSymbol(i); + symbol->scale(factor); + emit symbolChanged(i, symbol, symbol); + } + updateAllObjects(); + + setSymbolsDirty(); +} + +void Map::determineSymbolsInUse(std::vector< bool >& out) const +{ + out.assign(symbols.size(), false); + for (auto part : parts) + { + for (int o = 0; o < part->getNumObjects(); ++o) + { + const Symbol* symbol = part->getObject(o)->getSymbol(); + int index = findSymbolIndex(symbol); + if (index >= 0) + out[index] = true; + } + } + + determineSymbolUseClosure(out); +} + +void Map::determineSymbolUseClosure(std::vector< bool >& symbol_bitfield) const +{ + bool change; + do + { + change = false; + + for (size_t i = 0, end = symbol_bitfield.size(); i < end; ++i) + { + if (symbol_bitfield[i]) + continue; + + // Check if this symbol is needed by any included symbol + for (size_t k = 0; k < end; ++k) + { + if (i == k) + continue; + if (!symbol_bitfield[k]) + continue; + if (symbols[k]->containsSymbol(symbols[i])) + { + symbol_bitfield[i] = true; + change = true; + break; + } + } + } + + } while (change); +} + + +qreal Map::symbolIconZoom() const +{ + if (symbol_icon_scale <= 0) + const_cast(this)->updateSymbolIconZoom(); + + return symbol_icon_scale; +} + + +void Map::updateSymbolIconZoom() +{ + // A simple heuristics which determines the symbol icon scale from + // the mean of the line symbol widths. + auto values = std::vector(); + values.reserve(symbols.size()); + for (const auto symbol : symbols) + { + if (symbol->isHelperSymbol()) + continue; + auto size = symbol->dimensionForIcon(); + if (size > 0) + values.push_back(size); + } + std::sort(begin(values), end(values)); + auto percentile = [](const auto& v, quint8 p) { return v[v.size() * p / 100]; }; + + auto new_scale = qreal(0); + if (!values.empty()) + { + // Scale the symbol size at the 80th percentile to 90%. + new_scale = std::max(qreal(1), 90 / percentile(values, 80)); + + // The symbol size at the 20th percentile shall not get much below 10%. + auto small_scale = std::max(qreal(1), 10 / percentile(values, 20)); + if (small_scale > new_scale) + new_scale = (new_scale + small_scale) / 2; + } + // Convert from % to factor, and discretize to filter out minor changes. + new_scale = qreal(0.05) * std::max(1, qRound(new_scale / 5)); + + if (!qFuzzyCompare(new_scale, symbol_icon_scale)) + { + symbol_icon_scale = new_scale; + for (const auto symbol : symbols) + symbol->resetIcon(); + emit symbolIconZoomChanged(); + } +} + + + +void Map::setTemplate(Template* temp, int pos) +{ + templates[pos] = temp; + emit templateChanged(pos, templates[pos]); +} + +void Map::addTemplate(Template* temp, int pos) +{ + templates.insert(templates.begin() + pos, temp); + if (templates.size() == 1) + { + // This is the first template - the help text in the map widget(s) should be updated + updateAllMapWidgets(); + } + + emit templateAdded(pos, temp); +} + +void Map::removeTemplate(int pos) +{ + auto it = templates.begin() + pos; + Template* temp = *it; + templates.erase(it); + if (templates.empty()) + { + // That was the last template - the help text in the map widget(s) should maybe be updated (if there are no objects) + updateAllMapWidgets(); + } + + emit templateDeleted(pos, temp); +} + +void Map::deleteTemplate(int pos) +{ + Template* temp = getTemplate(pos); + removeTemplate(pos); + delete temp; +} + +void Map::setTemplateAreaDirty(Template* temp, const QRectF& area, int pixel_border) +{ + bool front_cache = findTemplateIndex(temp) >= getFirstFrontTemplate(); // TODO: is there a better way to find out if that is a front or back template? + + for (MapWidget* widget : widgets) + { + const MapView* map_view = widget->getMapView(); + if (map_view->isTemplateVisible(temp)) + widget->markTemplateCacheDirty(map_view->calculateViewBoundingBox(area), pixel_border, front_cache); + } +} + +void Map::setTemplateAreaDirty(int i) +{ + if (i == -1) + return; // no assert here as convenience, so setTemplateAreaDirty(-1) can be called without effect for the map part + Q_ASSERT(i >= 0 && i < (int)templates.size()); + + templates[i]->setTemplateAreaDirty(); +} + +int Map::findTemplateIndex(const Template* temp) const +{ + int size = (int)templates.size(); + for (int i = 0; i < size; ++i) + { + if (templates[i] == temp) + return i; + } + Q_ASSERT(false); + return -1; +} + +void Map::setTemplatesDirty() +{ + templates_dirty = true; + setHasUnsavedChanges(true); +} + +void Map::emitTemplateChanged(Template* temp) +{ + emit templateChanged(findTemplateIndex(temp), temp); +} + +void Map::clearClosedTemplates() +{ + if (closed_templates.empty()) + return; + + for (Template* temp : closed_templates) + delete temp; + + closed_templates.clear(); + setTemplatesDirty(); + emit closedTemplateAvailabilityChanged(); +} + +void Map::closeTemplate(int i) +{ + Template* temp = getTemplate(i); + removeTemplate(i); + + if (temp->getTemplateState() == Template::Loaded) + temp->unloadTemplateFile(); + + closed_templates.push_back(temp); + setTemplatesDirty(); + if (closed_templates.size() == 1) + emit closedTemplateAvailabilityChanged(); +} + +bool Map::reloadClosedTemplate(int i, int target_pos, QWidget* dialog_parent, const QString& map_path) +{ + Template* temp = closed_templates[i]; + + // Try to find and load the template again + if (temp->getTemplateState() != Template::Loaded) + { + if (!temp->tryToFindAndReloadTemplateFile(map_path)) + { + if (!temp->execSwitchTemplateFileDialog(dialog_parent)) + return false; + } + } + + // If successfully loaded, add to template list again + if (temp->getTemplateState() == Template::Loaded) + { + closed_templates.erase(closed_templates.begin() + i); + addTemplate(temp, target_pos); + temp->setTemplateAreaDirty(); + setTemplatesDirty(); + if (closed_templates.empty()) + emit closedTemplateAvailabilityChanged(); + return true; + } + return false; +} + +void Map::push(UndoStep *step) +{ + undo_manager->push(std::unique_ptr(step)); +} + + +void Map::addPart(MapPart* part, std::size_t index) +{ + Q_ASSERT(index <= parts.size()); + + parts.insert(parts.begin() + index, part); + if (current_part_index >= index) + setCurrentPartIndex(current_part_index + 1); + + emit mapPartAdded(index, part); + + setOtherDirty(); + updateAllMapWidgets(); +} + +void Map::removePart(std::size_t index) +{ + Q_ASSERT(index < parts.size()); + Q_ASSERT(parts.size() > 1); + + if (current_part_index == index) + // First switch to another part when removing the current part + setCurrentPartIndex((index == parts.size() - 1) ? (parts.size() - 2) : (index + 1)); + + MapPart* part = parts[index]; + + // FIXME: This loop should move to MapPart. + while(part->getNumObjects()) + part->deleteObject(0, false); + + parts.erase(parts.begin() + index); + if (current_part_index >= index) + setCurrentPartIndex((index == parts.size()) ? (parts.size() - 1) : index); + + emit mapPartDeleted(index, part); + + delete part; + + setOtherDirty(); + updateAllMapWidgets(); +} + +int Map::findPartIndex(const MapPart* part) const +{ + std::size_t const size = parts.size(); + for (std::size_t i = 0; i < size; ++i) + { + if (parts[i] == part) + return i; + } + Q_ASSERT(false); + return -1; +} + +void Map::setCurrentPartIndex(std::size_t index) +{ + Q_ASSERT(index < parts.size()); + + MapPart* const old_part = parts[current_part_index]; + if (index != current_part_index) + { + current_part_index = index; + emit currentMapPartIndexChanged(index); + } + + MapPart* const new_part = parts[current_part_index]; + if (new_part != old_part) + { + clearObjectSelection(true); + emit currentMapPartChanged(new_part); + } +} + +int Map::reassignObjectsToMapPart(std::vector::const_iterator first, std::vector::const_iterator last, std::size_t source, std::size_t destination) +{ + Q_ASSERT(source < parts.size()); + Q_ASSERT(destination < parts.size()); + + MapPart* const source_part = parts[source]; + MapPart* const target_part = parts[destination]; + auto first_object = target_part->getNumObjects(); + auto selection_size = getNumSelectedObjects(); + for (auto it = first; it != last; ++it) + { + Q_ASSERT(*it < source_part->getNumObjects()); + Object* const object = source_part->getObject(*it); + + if (current_part_index == source && isObjectSelected(object)) + removeObjectFromSelection(object, false); + + source_part->deleteObject(object, true); + target_part->addObject(object); + } + + setOtherDirty(); + + if (getNumSelectedObjects() != selection_size) + emit objectSelectionChanged(); + + return first_object; +} + +int Map::mergeParts(std::size_t source, std::size_t destination) +{ + Q_ASSERT(source < parts.size()); + Q_ASSERT(destination < parts.size()); + + int count = 0; + MapPart* const source_part = parts[source]; + MapPart* const target_part = parts[destination]; + // Preserve order (but not efficient) + for (auto i = source_part->getNumObjects(); i > 0 ; --i) + { + Object* object = source_part->getObject(0); + source_part->deleteObject(0, true); + + int index = target_part->getNumObjects(); + target_part->addObject(object, index); + + ++count; + } + + if (current_part_index == source) + setCurrentPartIndex(destination); + + if (destination != source) + removePart(source); + + return target_part->getNumObjects() - count; +} + + +int Map::getNumObjects() const +{ + int num_objects = 0; + for (const MapPart* part : parts) + num_objects += part->getNumObjects(); + return num_objects; +} + +int Map::addObject(Object* object, int part_index) +{ + MapPart* part = parts[(part_index < 0) ? current_part_index : part_index]; + int object_index = part->getNumObjects(); + part->addObject(object, object_index); + + return object_index; +} + +void Map::deleteObject(Object* object, bool remove_only) +{ + for (MapPart* part : parts) + { + if (part->deleteObject(object, remove_only)) + return; + } + + qCritical().nospace() << this << "::deleteObject(" << object << "," << remove_only << "): Object not found. This is a bug."; + if (!remove_only) + delete object; +} + +void Map::setObjectsDirty() +{ + objects_dirty = true; + setHasUnsavedChanges(true); +} + +QRectF Map::calculateExtent(bool include_helper_symbols, bool include_templates, const MapView* view) const +{ + QRectF rect; + + // Objects + for (const MapPart* part : parts) + rectIncludeSafe(rect, part->calculateExtent(include_helper_symbols)); + + // Templates + if (include_templates) + { + for (const Template* temp : templates) + { + if (view && !view->isTemplateVisible(temp)) + continue; + if (temp->getTemplateState() != Template::Loaded) + continue; + + rectIncludeSafe(rect, temp->calculateTemplateBoundingBox()); + } + } + + return rect; +} + +void Map::setObjectAreaDirty(const QRectF& map_coords_rect) +{ + for (MapWidget* widget : widgets) + widget->markObjectAreaDirty(map_coords_rect); +} + +void Map::findObjectsAt( + MapCoordF coord, + float tolerance, + bool treat_areas_as_paths, + bool extended_selection, + bool include_hidden_objects, + bool include_protected_objects, + SelectionInfoVector& out ) const +{ + getCurrentPart()->findObjectsAt(coord, tolerance, treat_areas_as_paths, extended_selection, include_hidden_objects, include_protected_objects, out); +} + +void Map::findAllObjectsAt( + MapCoordF coord, + float tolerance, + bool treat_areas_as_paths, + bool extended_selection, + bool include_hidden_objects, + bool include_protected_objects, + SelectionInfoVector& out ) const +{ + for (const MapPart* part : parts) + part->findObjectsAt(coord, tolerance, treat_areas_as_paths, extended_selection, include_hidden_objects, include_protected_objects, out); +} + +void Map::findObjectsAtBox( + MapCoordF corner1, + MapCoordF corner2, + bool include_hidden_objects, + bool include_protected_objects, + std::vector< Object* >& out ) const +{ + getCurrentPart()->findObjectsAtBox(corner1, corner2, include_hidden_objects, include_protected_objects, out); +} + +int Map::countObjectsInRect(const QRectF& map_coord_rect, bool include_hidden_objects) +{ + int count = 0; + for (const MapPart* part : parts) + count += part->countObjectsInRect(map_coord_rect, include_hidden_objects); + return count; +} + + + +bool Map::existsObject(const std::function& condition) const +{ + return std::any_of(begin(parts), end(parts), [&condition](auto part) { return part->existsObject(condition); }); +} + + +void Map::applyOnMatchingObjects(const std::function& operation, const std::function& condition) +{ + for (auto part : parts) + part->applyOnMatchingObjects(operation, condition); +} + + +void Map::applyOnMatchingObjects(const std::function& operation, const std::function& condition) +{ + for (auto part : parts) + part->applyOnMatchingObjects(operation, condition); +} + + +void Map::applyOnAllObjects(const std::function& operation) +{ + for (auto part : parts) + part->applyOnAllObjects(operation); +} + + +void Map::applyOnAllObjects(const std::function& operation) +{ + for (auto part : parts) + part->applyOnAllObjects(operation); +} + + + +void Map::scaleAllObjects(double factor, const MapCoord& scaling_center) +{ + applyOnAllObjects(ObjectOp::Scale{factor, MapCoordF{scaling_center}}); +} + +void Map::rotateAllObjects(double rotation, const MapCoord& center) +{ + applyOnAllObjects(ObjectOp::Rotate{rotation, MapCoordF{center}}); +} + +void Map::updateAllObjects() +{ + applyOnAllObjects(&Object::forceUpdate); +} + +void Map::updateAllObjectsWithSymbol(const Symbol* symbol) +{ + applyOnMatchingObjects(&Object::forceUpdate, ObjectOp::HasSymbol{symbol}); +} + +void Map::changeSymbolForAllObjects(const Symbol* old_symbol, const Symbol* new_symbol) +{ + applyOnMatchingObjects(ObjectOp::ChangeSymbol{new_symbol}, ObjectOp::HasSymbol{old_symbol}); +} + +bool Map::deleteAllObjectsWithSymbol(const Symbol* symbol) +{ + bool exists = existsObject(ObjectOp::HasSymbol{symbol}); + if (exists) + { + // Remove objects from selection + removeSymbolFromSelection(symbol, true); + + // Delete objects from map + applyOnMatchingObjects(ObjectOp::Delete(), ObjectOp::HasSymbol{symbol}); + } + return exists; +} + +bool Map::existsObjectWithSymbol(const Symbol* symbol) const +{ + return existsObject(ObjectOp::HasSymbol{symbol}); +} + +void Map::setGeoreferencing(const Georeferencing& georeferencing) +{ + *this->georeferencing = georeferencing; + setOtherDirty(); +} + +void Map::setGrid(const MapGrid &grid) +{ + if (grid != this->grid) + { + this->grid = grid; + for (MapWidget* widget : widgets) + { + MapView* view = widget->getMapView(); + if (view && view->isGridVisible()) + view->updateAllMapWidgets(); + } + setOtherDirty(); + } +} + + +bool Map::isAreaHatchingEnabled() const +{ + return renderable_options & Symbol::RenderAreasHatched; +} + +void Map::setAreaHatchingEnabled(bool enabled) +{ + if (enabled) + renderable_options |= Symbol::RenderAreasHatched; + else + renderable_options &= ~Symbol::RenderAreasHatched; +} + + +bool Map::isBaselineViewEnabled() const +{ + return renderable_options & Symbol::RenderBaselines; +} + +void Map::setBaselineViewEnabled(bool enabled) +{ + if (enabled) + renderable_options |= Symbol::RenderBaselines; + else + renderable_options &= ~Symbol::RenderBaselines; +} + + +const MapPrinterConfig& Map::printerConfig() +{ + if (printer_config.isNull()) + printer_config.reset(new MapPrinterConfig(*this)); + + return *printer_config; +} + +MapPrinterConfig Map::printerConfig() const +{ + MapPrinterConfig ret = printer_config.isNull() ? MapPrinterConfig{ *this } : *printer_config; + return ret; +} + +void Map::setPrinterConfig(const MapPrinterConfig& config) +{ + if (printer_config.isNull()) + { + printer_config.reset(new MapPrinterConfig(config)); + setOtherDirty(); + } + else if (*printer_config != config) + { + *printer_config = config; + setOtherDirty(); + } +} + +void Map::resetPrinterConfig() +{ + if (printer_config) + { + printer_config.reset(); + setOtherDirty(); + } +} + +void Map::setImageTemplateDefaults(bool use_meters_per_pixel, double meters_per_pixel, double dpi, double scale) +{ + image_template_use_meters_per_pixel = use_meters_per_pixel; + image_template_meters_per_pixel = meters_per_pixel; + image_template_dpi = dpi; + image_template_scale = scale; +} + +void Map::getImageTemplateDefaults(bool& use_meters_per_pixel, double& meters_per_pixel, double& dpi, double& scale) +{ + use_meters_per_pixel = image_template_use_meters_per_pixel; + meters_per_pixel = image_template_meters_per_pixel; + dpi = image_template_dpi; + scale = image_template_scale; +} + +void Map::setHasUnsavedChanges(bool has_unsaved_changes) +{ + if (!has_unsaved_changes) + { + colors_dirty = false; + symbols_dirty = false; + templates_dirty = false; + objects_dirty = false; + other_dirty = false; + if (unsaved_changes) + { + unsaved_changes = false; + emit hasUnsavedChanged(unsaved_changes); + } + } + else if (!unsaved_changes) + { + unsaved_changes = true; + emit hasUnsavedChanged(unsaved_changes); + } +} + +void Map::setOtherDirty() +{ + other_dirty = true; + setHasUnsavedChanges(true); +} + +// slot +void Map::undoCleanChanged(bool is_clean) +{ + if (is_clean && unsaved_changes && !(colors_dirty || symbols_dirty || templates_dirty || other_dirty)) + { + setHasUnsavedChanges(false); + } + else if (!is_clean && !unsaved_changes) + { + setHasUnsavedChanges(true); + } +} + + +} // namespace OpenOrienteering diff --git a/src/core/map.h b/src/core/map.h new file mode 100644 index 0000000..d46a348 --- /dev/null +++ b/src/core/map.h @@ -0,0 +1,1895 @@ +/* + * Copyright 2012-2014 Thomas Schöps + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_H +#define OPENORIENTEERING_MAP_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map_coord.h" +#include "core/map_grid.h" +#include "core/map_part.h" + +class QIODevice; +class QPainter; +class QTranslator; +class QWidget; +// IWYU pragma: no_forward_declare QRectF + +namespace OpenOrienteering { + +class CombinedSymbol; +class FileFormat; +class Georeferencing; +class LineSymbol; +class MapColor; +class MapColorMap; +class MapPrinterConfig; +class MapRenderables; +class MapView; +class MapWidget; +class Object; +class PointSymbol; +class RenderConfig; +class Symbol; +class Template; +class TextSymbol; +class UndoManager; +class UndoStep; + + +/** + * The translator for color and symbol texts. + * + * This translator is used by class Map but kept outside of the + * class' namespace in order to allow for forward declaration + * instead of including "map.h". + */ +extern QPointer map_symbol_translator; + + + +/** Central class for an OpenOrienteering map */ +class Map : public QObject +{ +Q_OBJECT +friend class MapTest; +friend class MapRenderables; +friend class OCAD8FileImport; +friend class NativeFileImport; +friend class NativeFileExport; +friend class XMLFileImporter; +friend class XMLFileExporter; +public: + /** A set of selected objects represented by a std::set of object pointers. */ + typedef std::set ObjectSelection; + + /** + * Different strategies for importing elements from another map. + */ + enum ImportModeFlag + { + ObjectImport = 0x00, ///< Import objects, symbols and colors. + SymbolImport = 0x01, ///< Import symbols and colors. + ColorImport = 0x02, ///< Import colors. + GeorefImport = 0x10, ///< Use the georeferencing for object import. + MinimalImport = 0x20, ///< Imports with minimal symbol and color dependencies. + + MinimalSymbolImport = SymbolImport | MinimalImport, + MinimalObjectImport = ObjectImport | MinimalImport, + CompleteImport = ObjectImport | GeorefImport + }; + + Q_DECLARE_FLAGS(ImportMode, ImportModeFlag) + + + /** Options for zooming to visibility of selection. */ + enum SelectionVisibility + { + FullVisibility, + PartialVisibility, + IgnoreVisibilty + }; + + + /** Creates a new, empty map. */ + Map(); + + /** Destroys the map. */ + ~Map() override; + + + /** + * Deletes all map data. + * + * The resulting map must not be modified before another init(). + */ + void clear(); + + /** + * Initializes an empty map. + * + * A map is empty when it is newly constructed or after clear(). + */ + void init(); + + /** + * Deletes all map data, and reinitializes the empty map. + * + * This method combines a call to clear() followed by init(). + */ + void reset(); + + + /** + * Saves the map to the given file. + * + * If a MapView is given, is state will be saved. + */ + bool saveTo(const QString& path, + MapView *view); + + /** + * Exports the map to the given file and format. + * + * If a MapView is given, is state will be saved. + * If a FileFormat is given, it will be used, otherwise the file format + * is determined from the filename. + * + * If the map was modified, it will still be considered modified after + * successful export. + */ + bool exportTo(const QString& path, + MapView* view = nullptr, + const FileFormat* format = nullptr); + + /** + * Attempts to load the map from the specified path. Returns true on success. + * + * @param path The file path to load the map from. + * @param dialog_parent The parent widget for all dialogs. + * This should never be nullptr in a QWidgets application. + * @param view If not nullptr, restores this map view. + * @param load_symbols_only Loads only symbols from the chosen file. + * Useful to load symbol sets. + * @param show_error_messages Whether to show import errors and warnings. + */ + bool loadFrom(const QString& path, + QWidget* dialog_parent, + MapView* view = nullptr, + bool load_symbols_only = false, bool show_error_messages = true); + + /** + * Imports the other map into this map with the following strategy: + * - if the other map contains objects, import all objects with the minimum + * amount of colors and symbols needed to display them + * - if the other map does not contain objects, import all symbols with + * the minimum amount of colors needed to display them + * - if the other map does neither contain objects nor symbols, import all colors + * + * WARNING: this method potentially changes the 'other' map if the + * scales differ (by rescaling to fit this map's scale)! + */ + void importMap( + const Map* other, + ImportMode mode, + QWidget* dialog_parent = nullptr, + std::vector* filter = nullptr, + int symbol_insert_pos = -1, + bool merge_duplicate_symbols = true, + QHash* out_symbol_map = nullptr + ); + + /** + * Imports another map into this map with the following strategy: + * - If the other map contains objects, import all objects with the + * minimum amount of colors and symbols needed to display them. + * - If the other map does not contain objects, import all symbols + * with the minimum amount of colors needed to display them. + * - If the other map does neither contain objects nor symbols, + * import all colors. + * The transform is applied to all imported objects. + */ + QHash importMap( + const Map& imported_map, + ImportMode mode, + std::vector* filter = nullptr, + int symbol_insert_pos = -1, + bool merge_duplicate_symbols = true, + const QTransform& transform = {} + ); + + + /** + * Serializes the map directly into the given IO device in a known format. + * This can be imported again using importFromIODevice(). + * Returns true if successful. + */ + bool exportToIODevice(QIODevice* stream); + + /** + * Loads the map directly from the given IO device, + * where the data must have been written by exportToIODevice(). + * Returns true if successful. + */ + bool importFromIODevice(QIODevice* stream); + + + /** + * Draws the part of the map which is visible in the bounding box. + * + * @param painter The QPainter used for drawing. + * @param config The rendering configuration + */ + void draw(QPainter* painter, const RenderConfig& config); + + /** + * Draws a spot color overprinting simulation for the part of the map + * which is visible in the given bounding box. + * + * @param painter Must be a QPainter on a QImage of Format_ARGB32_Premultiplied. + * @param config The rendering configuration + */ + void drawOverprintingSimulation(QPainter* painter, const RenderConfig& config); + + /** + * Draws the separation for a particular spot color for the part of the + * map which is visible in the given bounding box. + * + * Separations are normally drawn in levels of gray where black means + * full tone of the spot color. The parameter use_color can be used to + * draw in the actual spot color instead. + * + * @param painter The QPainter used for drawing. + * @param config The rendering configuration + * @param spot_color The spot color to draw the separation for. + * @param use_color If true, forces the separation to be drawn in its actual color. + */ + void drawColorSeparation(QPainter* painter, const RenderConfig& config, + const MapColor* spot_color, bool use_color = false); + + /** + * Draws the map grid. + * + * @param painter The QPainter used for drawing. + * @param bounding_box Bounding box of area to draw, given in map coordinates. + */ + void drawGrid(QPainter* painter, const QRectF& bounding_box); + + /** + * Draws the templates with indices first_template until last_template which + * are visible in the given bouding box. + * + * view determines template visibility and can be nullptr to show all templates. + * The initial transform of the given QPainter must be the map-to-paintdevice transformation. + * If on_screen is set to true, some optimizations will be applied, leading to a possibly lower display quality. + * + * @param painter The QPainter used for drawing. + * @param bounding_box Bounding box of area to draw, given in map coordinates. + * @param first_template Lowest index of the template range to draw. + * @param last_template Highest index of the template range to draw. + * @param view Optional pointer to MapView object which is used to query + * template visibilities. + * @param on_screen Potentially enables some drawing optimizations which + * decrease drawing quality. Should be enabled when drawing on-screen. + */ + void drawTemplates(QPainter* painter, const QRectF& bounding_box, int first_template, + int last_template, const MapView* view, bool on_screen) const; + + + /** + * Updates the renderables and extent of all objects which have changed. + * This is automatically called by draw(), you normally do not need to call it directly. + */ + void updateObjects(); + + /** + * Calculates the extent of all map elements. + * + * If templates shall be included, view may either be nullptr to include all + * templates, or specify a MapView to take the template visibilities from. + */ + QRectF calculateExtent(bool include_helper_symbols = false, bool include_templates = false, const MapView* view = nullptr) const; + + + /** + * Must be called to notify the map of new widgets displaying it. + * Useful to notify the widgets about which parts of the map have changed + * and need to be redrawn. + */ + void addMapWidget(MapWidget* widget); + + /** + * Removes the map widget, see addMapWidget(). + */ + void removeMapWidget(MapWidget* widget); + + /** + * Redraws all map widgets completely - this can be slow! + * Try to avoid this and do partial redraws instead, if possible. + */ + void updateAllMapWidgets(); + + /** + * Makes sure that the selected object(s) are visible in all map widgets + * by moving the views in the widgets to the selected objects. + */ + void ensureVisibilityOfSelectedObjects(SelectionVisibility visibility); + + + // Current drawing + + /** + * Sets the rect (given in map coordinates) as "dirty rect" for every + * map widget showing this map, enlarged by the given pixel border. + * This means that the area covered by the rect will be redrawn by + * the active tool. Use this if the current tool's display has changed. + * + * @param map_coords_rect Area covered by the current tool's drawing in map coords. + * @param pixel_border Border around the map coords rect which is also covered, + * given in pixels. Allows to enlarge the area given by the map coords + * by some pixels which are independent from the zoom level. + * For example if a tool displays markers with a radius of 3 pixels, + * it would set the bounding box of all markers as map_coords_rect and + * 3 for pixel_border. + * @param do_update Whether a repaint of the covered area should be triggered. + */ + void setDrawingBoundingBox(const QRectF& map_coords_rect, int pixel_border, bool do_update = true); + + /** + * Removes the drawing bounding box and triggers a repaint. Use this if + * the current drawing is hidden or erased. + */ + void clearDrawingBoundingBox(); + + + /** + * This is the analogon to setDrawingBoundingBox() for activities. + * See setDrawingBoundingBox(). + */ + void setActivityBoundingBox(const QRectF& map_coords_rect, int pixel_border, bool do_update = true); + + /** + * This is the analogon to clearDrawingBoundingBox() for activities. + * See clearDrawingBoundingBox(). + */ + void clearActivityBoundingBox(); + + + /** + * Updates all dynamic drawings at the given positions, + * i.e. tool & activity drawings. + * + * See setDrawingBoundingBox() and setActivityBoundingBox(). + */ + void updateDrawing(const QRectF& map_coords_rect, int pixel_border); + + + + // Element translations + + /** + * Returns a translated symbol text (name or description), or the original text. + */ + QString translate(const QString& symbol_text) const; + + /** + * Returns a translated symbol text (name or description), or an empty string. + */ + QString raw_translation(const QString& symbol_text) const; + + + + // Colors + + /** Returns the number of map colors defined in this map.*/ + int getNumColors() const; + + /** Returns a pointer to the MapColor identified by the non-negative priority i. + * + * Returns nullptr if the color is not defined, or if it is a special color (i.e i<0), + * i.e. only actual map colors are returned. + */ + const MapColor* getMapColor(int i) const; + + /** Returns a pointer to the MapColor identified by the non-negative priority i. + * + * Returns nullptr if the color is not defined, or if it is a special color (i.e i<0), + * i.e. only actual map colors are returned. + */ + MapColor* getMapColor(int i); + + /** Returns a pointer to the const MapColor identified by the priority i. + * + * Parameter i may also be negative for specifying special reserved colors. + * This is different from getMapColor(); + * + * Returns nullptr if the color is not defined. + */ + const MapColor* getColor(int i) const; + + /** + * Replaces the color at index pos with the given color, updates dependent + * colors and symbol icons. + * + * Emits colorChanged(). Does not delete the replaced color. + */ + void setColor(MapColor* color, int pos); + + /** + * Adds the given color as a new color at the given index. + * Emits colorAdded(). + */ + void addColor(MapColor* color, int pos); + + /** + * Deletes the color at the given index. + * Emits colorDeleted(). + */ + void deleteColor(int pos); + + /** + * Loops through the color list, looking for the given color pointer. + * Returns the index of the color, or -1 if it is not found. + */ + int findColorIndex(const MapColor* color) const; + + /** + * Marks the colors as "dirty", i.e. as having unsaved changes. + * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. + */ + void setColorsDirty(); + + /** + * Makes this map use the color set from the given map. + * Used to create the maps containing preview objects in the symbol editor. + */ + void useColorsFrom(Map* map); + + /** + * Checks and returns if the given color is used by at least one symbol. + */ + bool isColorUsedByASymbol(const MapColor* color) const; + + /** + * Checks which colors are in use by the symbols in this map. + * + * WARNING (FIXME): returns an empty list if the map does not contain symbols! + * + * @param by_which_symbols Must be of the same size as the symbol set. + * If set to false for a symbol, it will be disregarded. + * @param out Output parameter: a vector of the same size as the color list, + * where each element is set to true if the color is used by at least one symbol. + */ + void determineColorsInUse(const std::vector< bool >& by_which_symbols, std::vector< bool >& out) const; + + /** Returns true if the map contains spot colors. */ + bool hasSpotColors() const; + + /** + * Returns true if any visible object uses a non-opaque color. + */ + bool hasAlpha() const; + + + // Symbols + + /** Returns the symbol set ID. */ + QString symbolSetId() const; + + /** Sets the symbol set ID. */ + void setSymbolSetId(const QString& id); + + + /** Returns the number of symbols in this map. */ + int getNumSymbols() const; + + /** Returns a pointer to the i-th symbol. */ + const Symbol* getSymbol(int i) const; + + /** Returns a pointer to the i-th symbol. */ + Symbol* getSymbol(int i); + + /** + * Replaces the symbol at the given index with another symbol. + * Emits symbolChanged() and possibly selectedObjectEdited(). + */ + void setSymbol(Symbol* symbol, int pos); + + /** + * Adds the given symbol at the specified index. + * Emits symbolAdded(). + */ + void addSymbol(Symbol* symbol, int pos); + + /** + * Moves a symbol from one index to another in the symbol list. + */ + void moveSymbol(int from, int to); + + /** + * Sorts the symbol list using the given comparator. + */ + template void sortSymbols(T compare); + + /** + * Deletes the symbol at the given index. + * Emits symbolDeleted(). + */ + void deleteSymbol(int pos); + + /** + * Loops over all symbols, looking for the given symbol pointer. + * Returns the index of the symbol, or -1 if the symbol is not found. + * For the "undefined" symbols, returns special indices smaller than -1. + */ + int findSymbolIndex(const Symbol* symbol) const; + + /** + * Marks the symbols as "dirty", i.e. as having unsaved changes. + * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. + */ + void setSymbolsDirty(); + + /** + * Updates the icons of all symbols with the given color. + */ + void updateSymbolIcons(const MapColor* color); + + /** + * Scales all symbols by the given factor. + */ + void scaleAllSymbols(double factor); + + /** + * Checks which symbols are in use in this map. + * + * Returns a vector of the same size as the symbol list, where each element + * is set to true if there is at least one object which uses this symbol or + * a derived (combined) symbol. + */ + void determineSymbolsInUse(std::vector& out) const; + + /** + * Adds to the given symbol bitfield all other symbols which are needed to + * display the symbols indicated by the bitfield because of symbol dependencies. + */ + void determineSymbolUseClosure(std::vector< bool >& symbol_bitfield) const; + + /** + * Returns the scale factor to be used for default symbol icons. + * + * The full icon size (width, height) is represented by 1.0. + */ + qreal symbolIconZoom() const; + +public slots: + /** + * Updates the symbol icon zoom from the current set of symbols. + * + * The symbol icon zoom is chosen so that most symbols fit into the full + * icon space, and the number of symbol below 10% size is kept low. + * For a map without symbols, this returns 1.0. + */ + void updateSymbolIconZoom(); + + +public: + // Templates + + /** Returns the number of templates in this map. */ + int getNumTemplates() const; + + /** Returns the i-th template. */ + const Template* getTemplate(int i) const; + + /** Returns the i-th template. */ + Template* getTemplate(int i); + + /** Sets the template index which is the first (lowest) to be drawn in front of the map. */ + void setFirstFrontTemplate(int pos); + + /** Returns the template index which is the first (lowest) to be drawn in front of the map. */ + int getFirstFrontTemplate() const; + + /** + * Replaces the template at the given index with another. + * Emits templateChanged(). + */ + void setTemplate(Template* temp, int pos); + + /** + * Adds a new template at the given index. + * + * To place a template immediately below the map, adjust first_front_template + * manually with setFirstFrontTemplate()! + */ + void addTemplate(Template* temp, int pos); + + /** + * Removes the template with the given index from the template list, + * but does not delete it. + * NOTE: if required, adjust first_front_template manually with setFirstFrontTemplate()! + */ + void removeTemplate(int pos); + + /** + * Removes the template with the given position from the template list and deletes it. + * NOTE: if required, adjust first_front_template manually with setFirstFrontTemplate()! + */ + void deleteTemplate(int pos); + + /** + * Marks the area defined by the given QRectF (in map coordinates) and + * pixel border as "dirty", i.e. as needing a repaint, for the given template + * in all map widgets. + * + * For an explanation of the area and pixel border, see setDrawingBoundingBox(). + * + * Warning: does nothing if the template is not visible in a widget! + * So make sure to call this and showing/hiding a template in the correct order! + */ + void setTemplateAreaDirty(Template* temp, const QRectF& area, int pixel_border); + + /** + * Marks the whole area of the i-th template as "to be repainted". + * See setTemplateAreaDirty(). + * Does nothing for i == -1. + */ + void setTemplateAreaDirty(int i); + + /** + * Loops over all templates in the map and looks for the given template pointer. + * Returns the index of the template. The template must be contained in the map, + * otherwise an assert will be triggered! + */ + int findTemplateIndex(const Template* temp) const; + + /** + * Marks the template settings as "dirty", i.e. as having unsaved changes. + * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. + */ + void setTemplatesDirty(); + + /** Emits templateChanged() for the given template. */ + void emitTemplateChanged(Template* temp); + + + /** + * Returns the number of manually closed templates + * for which the settings are still stored. + */ + int getNumClosedTemplates() const; + + /** Returns the i-th closed template. */ + const Template* getClosedTemplate(int i) const; + + /** Returns the i-th closed template. */ + Template* getClosedTemplate(int i); + + /** Empties the list of closed templates. */ + void clearClosedTemplates(); + + /** + * Removes the template with the given index from the normal template list, + * unloads the template file and adds the template to the closed template list + * + * NOTE: if required, adjust first_front_template manually with setFirstFrontTemplate()! + */ + void closeTemplate(int i); + + /** + * Removes the template with the given index from the closed template list, + * load the template file and adds the template to the normal template list again. + * The template is made visible in the given view. + * + * NOTE: if required, adjust first_front_template manually with + * setFirstFrontTemplate() before calling this method! + * + * @param i The index of the closed template to reload. + * @param target_pos The desired index in the normal template list after loading. + * @param dialog_parent Widget as parent for possible dialogs. + * @param map_path Path where the map is saved currently. Used as possible + * search location to locate missing templates. + */ + bool reloadClosedTemplate(int i, int target_pos, QWidget* dialog_parent, const QString& map_path = QString()); + + + // Undo & Redo + + /** + * Returns the UndoManager instance for this map. + */ + UndoManager& undoManager(); + + /** + * Returns the UndoManager instance for this map. + */ + const UndoManager& undoManager() const; + + /** + * Pushes a new undo step to the map's undoManager. + */ + void push(UndoStep* step); + + + // Map parts + + /** + * Returns the number of map parts in this map. + */ + int getNumParts() const; + + /** + * Returns the i-th map part. + */ + MapPart* getPart(std::size_t i) const; + + /** + * Adds the new part at the given index. + */ + void addPart(MapPart* part, std::size_t index); + + /** + * Removes the map part at position. + */ + void removePart(std::size_t index); + + /** + * Loops over all map parts, looking for the given part pointer. + * Returns the part's index in the list. The part must be contained in the + * map, otherwise an assert will be triggered! + */ + int findPartIndex(const MapPart* part) const; + + /** + * Returns the current map part, i.e. the part where edit operations happen. + */ + MapPart* getCurrentPart() const; + + /** + * Changes the current map part. + * + * This is a convenience method which looks up the part's index and then + * calls setCurrentPartIndex. + */ + void setCurrentPart(MapPart* part); + + /** + * Returns the index of the current map part. + * + * @see getCurrentPart(). + */ + std::size_t getCurrentPartIndex() const; + + /** + * Changes the current map part. + */ + void setCurrentPartIndex(std::size_t index); + + /** + * Moves all specified objects from the source to the target map part. + * + * Objects are processed one by one. This means that processing one object + * changes the index of following objects. Thus the given indices must + * normally be in descending order. + * + * The objects will be continuously located at the end to the objects in the target part. + * Source object which were selected will be removed from the object selection. + * + * @return The index of the first object which has been reassigned. + */ + int reassignObjectsToMapPart(std::vector::const_iterator first, std::vector::const_iterator last, std::size_t source, std::size_t destination); + + /** + * Merges the source part with the destination part. + * + * Removes the source part unless it is identical with the destination part. + * + * The objects will be continuously located at the end to the objects in the target part. + * Does not change the object selection. + * + * Makes the destination part the current part when the source part is the current part. + * + * @return The index of the first object which has been reassigned. + */ + int mergeParts(std::size_t source, std::size_t destination); + + + // Objects + + /** Returns the total number of objects in this map (sum of all parts) */ + int getNumObjects() const; + + /** + * Adds the object as new object in the part with the given index, + * or in the current part if the default -1 is passed. + * Returns the index of the added object in the part. + */ + int addObject(Object* object, int part_index = -1); + + /** + * Deletes the given object from the map. + * remove_only will remove the object from the map, but not call "delete object"; + * be sure to call removeObjectFromSelection() if necessary. + * + * TODO: make a separate method "removeObject()", remove_only is misleading! + */ + void deleteObject(Object* object, bool remove_only); + + /** + * Marks the objects as "dirty", i.e. as having unsaved changes. + * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. + */ + void setObjectsDirty(); + + /** + * Marks the area given by map_coords_rect as "dirty" in all map widgets, + * i.e. as needing to be redrawn because some object(s) changed there. + */ + void setObjectAreaDirty(const QRectF& map_coords_rect); + + /** + * Finds and returns all objects at the given position in the current part. + * + * @param coord Coordinate where to query objects. + * @param tolerance Allowed distance from the query coordinate to the objects. + * @param treat_areas_as_paths If set to true, areas will be treated as paths, + * i.e. they will be returned only if the query coordinate is close to + * their border, not in all cases where the query coordinate is inside the area. + * @param extended_selection If set to true, more object than defined by the + * default behavior will be selected. For example, by default point objects + * will only be returned if the query coord is close to the point object + * coord. With extended_selection, they are also returned if the query + * coord is just inside the graphical extent of the point object. + * This can be used for a two-pass algorithm to find the most relevant + * objects first, and then query for all objects if no objects are found + * in the first pass. + * @param include_hidden_objects Set to true if you want to find hidden objects. + * @param include_protected_objects Set to true if you want to find protected objects. + * @param out Output parameter. Will be filled with pairs of symbol types + * and corresponding objects. The symbol type describes the way in which + * an object has been found and is taken from Symbol::Type. This is e.g. + * important for combined symbols, which can be found from a line or + * an area. + */ + void findObjectsAt(MapCoordF coord, float tolerance, bool treat_areas_as_paths, + bool extended_selection, bool include_hidden_objects, + bool include_protected_objects, SelectionInfoVector& out) const; + + /** + * Finds and returns all objects at the given position in all parts. + * + * @see Map::findObjectsAt + */ + void findAllObjectsAt(MapCoordF coord, float tolerance, bool treat_areas_as_paths, + bool extended_selection, bool include_hidden_objects, + bool include_protected_objects, SelectionInfoVector& out) const; + + /** + * Finds and returns all objects intersecting the given box in the current part. + * + * @param corner1 First corner of the query box. + * @param corner2 Second corner of the query box. + * @param include_hidden_objects Set to true if you want to find hidden objects. + * @param include_protected_objects Set to true if you want to find protected objects. + * @param out Output parameter. Will be filled with an object list. + */ + void findObjectsAtBox(MapCoordF corner1, MapCoordF corner2, + bool include_hidden_objects, bool include_protected_objects, + std::vector& out) const; + + /** + * Counts the objects whose bounding boxes intersect the given rect. + * + * This may be inaccurate because the true object shapes usually differ + * from the bounding boxes. + * + * @param map_coord_rect The query rect. + * @param include_hidden_objects Set to true if you want to find hidden objects. + */ + int countObjectsInRect(const QRectF& map_coord_rect, bool include_hidden_objects); + + + /** + * Applies a condition on all objects until the first match is found. + * + * @return True if there is an object matching the condition, false otherwise. + */ + bool existsObject(const std::function& condition) const; + + /** + * Applies an operation on all objects which match a particular condition. + */ + void applyOnMatchingObjects(const std::function& operation, const std::function& condition); + + /** + * Applies an operation on all objects which match a particular condition. + */ + void applyOnMatchingObjects(const std::function& operation, const std::function& condition); + + /** + * Applies an operation on all objects. + */ + void applyOnAllObjects(const std::function& operation); + + /** + * Applies an operation on all objects. + */ + void applyOnAllObjects(const std::function& operation); + + + /** Scales all objects by the given factor. */ + void scaleAllObjects(double factor, const MapCoord& scaling_center); + + /** Rotates all objects by the given rotation angle (in radians). */ + void rotateAllObjects(double rotation, const MapCoord& center); + + /** Forces an update of all objects, i.e. calls update(true) on each map object. */ + void updateAllObjects(); + + /** Forces an update of all objects with the given symbol. */ + void updateAllObjectsWithSymbol(const Symbol* symbol); + + /** For all symbols with old_symbol, replaces the symbol by new_symbol. */ + void changeSymbolForAllObjects(const Symbol* old_symbol, const Symbol* new_symbol); + + /** + * Deletes all objects with the given symbol. + * + * @return True if at least one object was deleted, false otherwise + */ + bool deleteAllObjectsWithSymbol(const Symbol* symbol); + + /** + * Returns if at least one object with the given symbol exists in the map. + * WARNING: Even if no objects exist directly, the symbol could still be + * required by another (combined) symbol used by an object! + */ + bool existsObjectWithSymbol(const Symbol* symbol) const; + + + /** + * Removes the renderables of the given object from display (does not + * delete them!). + */ + void removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty); + + /** + * Inserts the renderables of the given object, so they will be displayed. + */ + void insertRenderablesOfObject(const Object* object); + + + /** + * Marks an object as irregular. + */ + void markAsIrregular(Object* object); + + /** + * Returns the list of objects marked as irregular. + */ + const std::set irregularObjects() const; + + /** + * Deletes the irregular objects. + * + * This function deletes the objects which were previously marked as irregular. + * Only objects which are actually member of map parts are deleted. Objects in + * undo steps or similar are ignored. + * + * \return The number of deleted objects. + */ + std::size_t deleteIrregularObjects(); + + + // Object selection + + const ObjectSelection& selectedObjects() const; + + /** Returns the number of selected objects. */ + int getNumSelectedObjects() const; + + /** Returns an iterator allowing to iterate over the selected objects. */ + ObjectSelection::const_iterator selectedObjectsBegin() const; + + /** Returns an end iterator allowing to iterate over the selected objects. */ + ObjectSelection::const_iterator selectedObjectsEnd() const; + + /** + * Returns the object in the selection which was selected first by the user. + * + * If she later deselects it while other objects are still selected or if + * the selection is done as box selection, this "first" selected object is + * just a more or less random object from the selection. + */ + const Object* getFirstSelectedObject() const; + + /** + * Returns the object in the selection which was selected first by the user. + */ + Object* getFirstSelectedObject(); + + /** + * Checks the selected objects for compatibility with the given symbol. + * @param symbol the symbol to check compatibility for + * @param out_compatible returns if all selected objects are compatible + * to the given symbol + * @param out_different returns if at least one of the selected objects' + * symbols is different to the given symbol + */ + void getSelectionToSymbolCompatibility(const Symbol* symbol, bool& out_compatible, bool& out_different) const; + + /** + * Deletes the selected objects and creates an undo step for this action. + */ + void deleteSelectedObjects(); + + /** + * Enlarges the given rect to cover all selected objects. + */ + void includeSelectionRect(QRectF& rect) const; + + /** + * Draws the selected objects. + * + * @param painter The QPainter used for drawing. + * @param force_min_size See draw(). + * @param widget The widget in which the drawing happens. + * Used to get view and viewport information. + * @param replacement_renderables If given, draws these renderables instead + * Of the selection renderables. TODO: HACK + * @param draw_normal If set to true, draws the objects like normal objects, + * otherwise draws transparent highlights. + */ + void drawSelection(QPainter* painter, bool force_min_size, MapWidget* widget, + MapRenderables* replacement_renderables = nullptr, bool draw_normal = false); + + /** + * Adds the given object to the selection. + * @param object The object to add. + * @param emit_selection_changed Set to true if objectSelectionChanged() + * should be emitted. Do this only for the last in a + * sequence of selection change oparations to prevent bad performance! + */ + void addObjectToSelection(Object* object, bool emit_selection_changed); + + /** + * Removes the given object from the selection. + * @param object The object to remove. + * @param emit_selection_changed See addObjectToSelection(). + */ + void removeObjectFromSelection(Object* object, bool emit_selection_changed); + + /** + * Removes from the selection all objects with the given symbol. + * Returns true if at least one object has been removed. + * @param symbol The symbol of the objects to remove. + * @param emit_selection_changed See addObjectToSelection(). + */ + bool removeSymbolFromSelection(const Symbol* symbol, bool emit_selection_changed); + + /** Returns true if the given object is selected. */ + bool isObjectSelected(const Object* object) const; + + /** + * Toggles the selection of the given object. + * Returns true if the object was selected, false if deselected. + * @param object The object to select or deselect. + * @param emit_selection_changed See addObjectToSelection(). + */ + bool toggleObjectSelection(Object* object, bool emit_selection_changed); + + /** + * Empties the object selection. + * @param emit_selection_changed See addObjectToSelection(). + */ + void clearObjectSelection(bool emit_selection_changed); + + /** + * Emits objectSelectionChanged(). Use this if setting emit_selection_changed + * in a selection change method is unfeasible. + */ + void emitSelectionChanged(); + + /** + * Emits selectedObjectEdited(). Use this after making changes + * to a selected object. + */ + void emitSelectionEdited(); + + + // Other settings + + /** Sets the map's scale denominator. */ + void setScaleDenominator(unsigned int value); + + /** Returns the map's scale denominator. */ + unsigned int getScaleDenominator() const; + + /** + * Changes the map's scale. + * + * @param new_scale_denominator The new scale denominator. + * @param scaling_center The coordinate to use as scaling center. + * @param scale_symbols Whether to scale the map symbols. + * @param scale_objects Whether to scale the map object coordinates. + * @param scale_georeferencing Whether to adjust the map's georeferencing reference point. + * @param scale_templates Whether to scale non-georeferenced templates. + */ + void changeScale(unsigned int new_scale_denominator, + const MapCoord& scaling_center, bool scale_symbols, bool scale_objects, + bool scale_georeferencing, bool scale_templates); + + + /** + * Rotate the map around a point. + * + * @param rotation The rotation angle (in radians). + * @param center The rotation center point. + * @param adjust_georeferencing Whether to adjust the georeferencing reference point. + * @param adjust_declination Whether to adjust the georeferencing declination. + * @param adjust_templates Whether to adjust non-georeferenced templates. + */ + void rotateMap(double rotation, const MapCoord& center, + bool adjust_georeferencing, bool adjust_declination, + bool adjust_templates); + + + /** Returns the map notes string. */ + const QString& getMapNotes() const; + + /** + * Sets the map notes string. + * NOTE: Set the map to dirty manually! + */ + void setMapNotes(const QString& text); + + + /** Returns the map's georeferencing object. */ + const Georeferencing& getGeoreferencing() const; + + /** + * Assigns georeferencing settings for the map from the given object and + * sets the map to have unsaved changes. + */ + void setGeoreferencing(const Georeferencing& georeferencing); + + + /** + * Returns the map's grid settings. + */ + const MapGrid& getGrid() const; + + /** + * Sets the map's grid settings from the given object and + * may set the map to have unsaved changes. + */ + void setGrid(const MapGrid& grid); + + + /** + * TODO: These two options should really be view options, but are not due + * to a limitation: + * the current architecture makes it impossible to have different + * renderables of the same objects in different views! + */ + + /** Returns if area hatching is enabled. */ + bool isAreaHatchingEnabled() const; + + /** Sets if area hatching is enabled. */ + void setAreaHatchingEnabled(bool enabled); + + + /** Returns if the baseline view is enabled. */ + bool isBaselineViewEnabled() const; + + /** Sets if the baseline view is enabled. */ + void setBaselineViewEnabled(bool enabled); + + + /** Returns the rendering options as an int representing Symbol::RenderableOptions. */ + int renderableOptions() const; + + + /** Returns true if the map has a print configuration. */ + bool hasPrinterConfig(); + + /** Returns a const reference to the current print configuration. + * + * If the map does not have a print configuration, a default configuration + * is created first. + */ + const MapPrinterConfig& printerConfig(); + + /** Returns a copy of the current print configuration. + * + * If the map does not have a print configuration, the function will return + * a default configuration for this map. + */ + MapPrinterConfig printerConfig() const; + + /** Sets the print configuration. */ + void setPrinterConfig(const MapPrinterConfig& config); + + /** Clears the print configuration. + * + * After calling this method, hasPrinterConfig() returns false. + */ + void resetPrinterConfig(); + + + /** Returns the default parameters for loading of image tempaltes. */ + void getImageTemplateDefaults(bool& use_meters_per_pixel, double& meters_per_pixel, + double& dpi, double& scale); + + /** + * Sets default parameters for loading of image templates. + * TODO: put these into a struct. + */ + void setImageTemplateDefaults(bool use_meters_per_pixel, double meters_per_pixel, + double dpi, double scale); + + + /** + * Returns whether there are unsaved changes in the map. + * + * To toggle this state, never use setHasUnsavedChanges() directly unless + * you know what you are doing, instead use setOtherDirty() or set one of + * the more specific 'dirty' flags. This is because a call to + * setHasUnsavedChanges() alone followed by a map change and an undo would + * result in no changed flag. + */ + bool hasUnsavedChanges() const; + + /** Do not use this in usual cases, see hasUnsavedChanged(). */ + void setHasUnsavedChanges(bool has_unsaved_changes); + + /** Returns if there are unsaved changes to the colors. */ + bool areColorsDirty() const; + + /** Returns if there are unsaved changes to the symbols. */ + bool areSymbolsDirty() const; + + /** Returns if there are unsaved changes to the templates. */ + bool areTemplatesDirty() const; + + /** Returns if there are unsaved changes to the objects. */ + bool areObjectsDirty() const; + + /** Returns if there are unsaved changes to anything else than the above. */ + bool isOtherDirty() const; + + /** + * Marks somthing unspecific in the map as "dirty", i.e. as having unsaved changes. + * Emits hasUnsavedChanged(true) if the map did not have unsaved changed before. + * + * Use setColorsDirty(), setSymbolsDirty(), setTemplatesDirty() or + * setObjectsDirty() if you know more specificly what has changed. + */ + void setOtherDirty(); + + + // Static + + /** Returns the special covering red color. */ + static const MapColor* getCoveringRed(); + + /** Returns the special covering white color. */ + static const MapColor* getCoveringWhite(); + + /** Returns the special covering gray color for "undefined" objects. */ + static const MapColor* getUndefinedColor(); + + /** Returns the special registration color. */ + static const MapColor* getRegistrationColor(); + + /** Returns the special covering white line symbol. */ + static LineSymbol* getCoveringWhiteLine(); + + /** Returns the special covering red line symbol. */ + static LineSymbol* getCoveringRedLine(); + + /** Returns the special covering combined symbol (white + red). */ + static CombinedSymbol* getCoveringCombinedLine(); + + /** Returns the special gray "undefined" line symbol. */ + static LineSymbol* getUndefinedLine(); + + /** Returns the special gray "undefined" point symbol. */ + static PointSymbol* getUndefinedPoint(); + + /** Returns the special gray "undefined" text symbol. */ + static TextSymbol* getUndefinedText(); + + +signals: + /** + * Emitted when a the map enters or leaves the state which is saved on map. + */ + void hasUnsavedChanged(bool is_clean); + + + /** Emitted when a color is added to the map, gives the color's index and pointer. */ + void colorAdded(int pos, const MapColor* color); + + /** Emitted when a map color is changed, gives the color's index and pointer. */ + void colorChanged(int pos, const MapColor* color); + + /** Emitted when a map color is deleted, gives the color's index and pointer. */ + void colorDeleted(int pos, const MapColor* old_color); + + /** Emitted when the presence of spot colors in the map changes. */ + void spotColorPresenceChanged(bool has_spot_colors) const; + + + /** Emitted when a symbol is added to the map, gives the symbol's index and pointer. */ + void symbolAdded(int pos, const Symbol* symbol); + + /** Emitted when a symbol in the map is changed. */ + void symbolChanged(int pos, const Symbol* new_symbol, const Symbol* old_symbol); + + /** Emitted when the icon of the symbol with the given index changes. */ + void symbolIconChanged(int pos); + + /** Emitted when a symbol in the map is deleted. */ + void symbolDeleted(int pos, const Symbol* old_symbol); + + /** Emitted when the symbol icon zoom changes. */ + void symbolIconZoomChanged(); + + + /** Emitted when a template is added to the map, gives the template's index and pointer. */ + void templateAdded(int pos, Template* temp); + + /** Emitted when a template in the map is changed, gives the template's index and pointer. */ + void templateChanged(int pos, Template* temp); + + /** Emitted when a template in the map is deleted, gives the template's index and pointer. */ + void templateDeleted(int pos, const Template* old_temp); + + /** Emitted when the number of closed templates changes between zero and one. */ + void closedTemplateAvailabilityChanged(); + + + /** Emitted when the set of selected objects changes. Also emitted when the + * symbol of a selected object changes (which is similar to selecting another + * object). */ + void objectSelectionChanged(); + + /** + * Emitted when at least one of the selected objects is edited in any way. + * For example, this includes the case where a symbol of one of the + * selected objects is edited, too. + */ + void selectedObjectEdited(); + + + /** + * Emitted when the map part currently used for drawing changes. + * + * @see currentMapPartIndexChanged() + */ + void currentMapPartChanged(const MapPart* part); + + /** + * Emitted when the index of map part currently used for drawing changes. + * + * This signal may be emitted even when the current MapPart object does not + * change. This happens when the index changes due to addition or removal + * of map parts. + */ + void currentMapPartIndexChanged(std::size_t index); + + /** + * Emitted when a part is added to the map. + */ + void mapPartAdded(std::size_t index, const MapPart* part); + + /** + * Emitted when a part's properties are changed. + */ + void mapPartChanged(std::size_t index, const MapPart* part); + + /** + * Emitted when a part is removed from the map. + */ + void mapPartDeleted(std::size_t index, const MapPart* part); + +protected slots: + void checkSpotColorPresence(); + + void undoCleanChanged(bool is_clean); + +private: + typedef std::vector ColorVector; + typedef std::vector SymbolVector; + typedef std::vector TemplateVector; + typedef std::vector PartVector; + typedef std::vector WidgetVector; + + class MapColorSet : public QSharedData + { + public: + ColorVector colors; + + MapColorSet(); + + MapColorSet(const MapColorSet& ) = delete; + + ~MapColorSet(); + + void insert(int pos, MapColor* color); + + void erase(int pos); + + /** Merges another MapColorSet into this set, trying to maintain + * the relative order of colors. + * If a filter is given, imports only the colors for which + * filter[color_index] is true, or which are spot colors referenced + * by the selected colors. + * If a map is given, this color set is modified through the map's + * color accessor methods so that other object become aware of the + * changes. + * @return a mapping from the imported color pointer in the other set + * to color pointers in this set. + */ + MapColorMap importSet(const MapColorSet& other, + std::vector* filter = nullptr, + Map* map = nullptr); + + private: + /** + * Adjust the priorities of the colors in the range [first,last). + */ + void adjustColorPriorities(int first, int last); + }; + + + /** + * Imports the other symbol set into this map's symbols. + * + * If a filter is given, imports only the symbols for which filter[color_index] == true. + * Imported symbols are placed at insert_pos (if positive), or after the existing symbols. + * Returns a mapping from original symbols (in other) to imported symbols. + */ + QHash importSymbols( + const Map& other, + const MapColorMap& color_map, + int insert_pos = -1, + bool merge_duplicates = true, + const std::vector& filter = {} + ); + + + void addSelectionRenderables(const Object* object); + void updateSelectionRenderables(const Object* object); + void removeSelectionRenderables(const Object* object); + + static void initStatic(); + + QExplicitlySharedDataPointer color_set; + bool has_spot_colors; + QString symbol_set_id; + SymbolVector symbols; + mutable qreal symbol_icon_scale = 0; + TemplateVector templates; + TemplateVector closed_templates; + int first_front_template = 0; // index of the first template in templates which should be drawn in front of the map + PartVector parts; + ObjectSelection object_selection; + Object* first_selected_object = nullptr; + QScopedPointer undo_manager; + std::size_t current_part_index = 0; + WidgetVector widgets; + QScopedPointer renderables; + QScopedPointer selection_renderables; + + QString map_notes; + + QScopedPointer georeferencing; + + MapGrid grid; + + int renderable_options; + + QScopedPointer printer_config; + + bool image_template_use_meters_per_pixel; + double image_template_meters_per_pixel; + double image_template_dpi; + double image_template_scale; + + bool colors_dirty; // are there unsaved changes for the colors? + bool symbols_dirty; // ... for the symbols? + bool templates_dirty; // ... for the templates? + bool objects_dirty; // ... for the objects? + bool other_dirty; // ... for any other settings? + bool unsaved_changes; // are there unsaved changes for any component? + + std::set irregular_objects; + + // Static + + static bool static_initialized; + static MapColor covering_white; + static MapColor covering_red; + static MapColor undefined_symbol_color; + static MapColor registration_color; + static LineSymbol* covering_white_line; + static LineSymbol* covering_red_line; + static LineSymbol* undefined_line; + static PointSymbol* undefined_point; + static TextSymbol* undefined_text; + static CombinedSymbol* covering_combined_line; +}; + + + +// ### Map inline code ### + +inline +int Map::getNumColors() const +{ + return (int)color_set->colors.size(); +} + +inline +MapColor* Map::getMapColor(int i) +{ + return const_cast(static_cast(this)->getMapColor(i)); +} + +inline +const MapColor* Map::getMapColor(int i) const +{ + if (0 <= i && i < (int)color_set->colors.size()) + { + return color_set->colors[i]; + } + return nullptr; +} + +inline +const MapColor* Map::getColor(int i) const +{ + if (0 <= i && i < (int)color_set->colors.size()) + { + return color_set->colors[i]; + } + else switch (i) + { + case -1005: + return getCoveringRed(); + case -1000: + return getCoveringWhite(); + case -900: + return getRegistrationColor(); + case -500: + return getUndefinedColor(); + default: + return nullptr; + } +} + + + +inline +QString Map::symbolSetId() const +{ + return symbol_set_id; +} + + +inline +int Map::getNumSymbols() const +{ + return (int)symbols.size(); +} + +template +void Map::sortSymbols(T compare) +{ + std::stable_sort(symbols.begin(), symbols.end(), compare); + // TODO: emit symbolChanged(pos, symbol); ? s/b same choice as for moveSymbol() + setSymbolsDirty(); +} + +inline +int Map::getNumTemplates() const +{ + return templates.size(); +} + +inline +const Template*Map::getTemplate(int i) const +{ + return templates[i]; +} + +inline +Template*Map::getTemplate(int i) +{ + return templates[i]; +} + +inline +void Map::setFirstFrontTemplate(int pos) +{ + first_front_template = pos; +} + +inline +int Map::getFirstFrontTemplate() const +{ + return first_front_template; +} + +inline +int Map::getNumClosedTemplates() const +{ + return (int)closed_templates.size(); +} + +inline +const Template* Map::getClosedTemplate(int i) const +{ + return closed_templates[i]; +} + +inline +Template* Map::getClosedTemplate(int i) +{ + return closed_templates[i]; +} + +inline +UndoManager& Map::undoManager() +{ + return const_cast(static_cast(this)->undoManager()); +} + +inline +const UndoManager& Map::undoManager() const +{ + return *(undo_manager.data()); +} + +inline +int Map::getNumParts() const +{ + return parts.size(); +} + +inline +MapPart* Map::getPart(std::size_t i) const +{ + return parts[i]; +} + +inline +MapPart* Map::getCurrentPart() const +{ + return parts[current_part_index]; +} + +inline +void Map::setCurrentPart(MapPart* part) +{ + setCurrentPartIndex(findPartIndex(part)); +} + +inline +std::size_t Map::getCurrentPartIndex() const +{ + return current_part_index; +} + +inline +const Map::ObjectSelection& Map::selectedObjects() const +{ + return object_selection; +} + +inline +int Map::getNumSelectedObjects() const +{ + return (int)object_selection.size(); +} + +inline +Map::ObjectSelection::const_iterator Map::selectedObjectsBegin() const +{ + return object_selection.cbegin(); +} + +inline +Map::ObjectSelection::const_iterator Map::selectedObjectsEnd() const +{ + return object_selection.cend(); +} + +inline +const Object* Map::getFirstSelectedObject() const +{ + return first_selected_object; +} + +inline +Object* Map::getFirstSelectedObject() +{ + return first_selected_object; +} + +inline +const QString& Map::getMapNotes() const +{ + return map_notes; +} + +inline +const Georeferencing& Map::getGeoreferencing() const +{ + return *georeferencing; +} + +inline +const MapGrid& Map::getGrid() const +{ + return grid; +} + +inline +int Map::renderableOptions() const +{ + return renderable_options; +} + +inline +bool Map::hasPrinterConfig() +{ + return !printer_config.isNull(); +} + +inline +bool Map::hasUnsavedChanges() const +{ + return unsaved_changes; +} + +inline +bool Map::areColorsDirty() const +{ + return colors_dirty; +} + +inline +bool Map::areSymbolsDirty() const +{ + return symbols_dirty; +} + +inline +bool Map::areTemplatesDirty() const +{ + return templates_dirty; +} + +inline +bool Map::areObjectsDirty() const +{ + return objects_dirty; +} + +inline +bool Map::isOtherDirty() const +{ + return other_dirty; +} + +inline +const MapColor* Map::getCoveringRed() +{ + return &covering_red; +} + +inline +const MapColor* Map::getCoveringWhite() +{ + return &covering_white; +} + +inline +const MapColor* Map::getUndefinedColor() +{ + return &undefined_symbol_color; +} + +inline +const MapColor* Map::getRegistrationColor() +{ + return ®istration_color; +} + +inline +LineSymbol* Map::getCoveringWhiteLine() +{ + return covering_white_line; +} + +inline +LineSymbol* Map::getCoveringRedLine() +{ + return covering_red_line; +} + +inline +CombinedSymbol* Map::getCoveringCombinedLine() +{ + return covering_combined_line; +} + +inline +LineSymbol* Map::getUndefinedLine() +{ + return undefined_line; +} + +inline +PointSymbol* Map::getUndefinedPoint() +{ + return undefined_point; +} + +inline +TextSymbol* Map::getUndefinedText() +{ + return undefined_text; +} + + +} // namespace OpenOrienteering + + +Q_DECLARE_METATYPE(const OpenOrienteering::Map*) + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::Map::ImportMode) + + +#endif diff --git a/src/core/map_color.cpp b/src/core/map_color.cpp new file mode 100644 index 0000000..b0b00f4 --- /dev/null +++ b/src/core/map_color.cpp @@ -0,0 +1,364 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_color.h" + +#include +#include + +#include +#include +#include +#include + + +namespace OpenOrienteering { + +MapColor::MapColor() +: name(QCoreApplication::translate("OpenOrienteering::Map", "New color")), + priority(Undefined), + opacity(1.0f), + q_color(Qt::black), + spot_color_method(MapColor::UndefinedMethod), + cmyk_color_method(MapColor::CustomColor), + rgb_color_method(MapColor::CmykColor), + flags(0), + spot_color_name() +{ + Q_ASSERT(isBlack()); +} + +MapColor::MapColor(int priority) +: name(QCoreApplication::translate("OpenOrienteering::Map", "New color")), + priority(priority), + opacity(1.0f), + q_color(Qt::black), + spot_color_method(MapColor::UndefinedMethod), + cmyk_color_method(MapColor::CustomColor), + rgb_color_method(MapColor::CmykColor), + flags(0), + spot_color_name() +{ + Q_ASSERT(isBlack()); + + switch (priority) + { + case CoveringWhite: + setCmyk(QColor(Qt::white)); + Q_ASSERT(isWhite()); + opacity = 1000.0f; // HACK: (almost) always opaque, even if multiplied by opacity factors + break; + case CoveringRed: + setRgb(QColor(Qt::red)); + setCmykFromRgb(); + opacity = 1000.0f; // HACK: (almost) always opaque, even if multiplied by opacity factors + break; + case Undefined: + setCmyk(QColor(Qt::darkGray)); + break; + case Registration: + Q_ASSERT(isBlack()); + name = QCoreApplication::translate("OpenOrienteering::MapColor", "Registration black (all printed colors)"); + break; + default: + ; // no change + } +} + +MapColor::MapColor(const QString& name, int priority) +: name(name), + priority(priority), + opacity(1.0), + q_color(Qt::black), + spot_color_method(MapColor::UndefinedMethod), + cmyk_color_method(MapColor::CustomColor), + rgb_color_method(MapColor::CmykColor), + flags(0), + spot_color_name() +{ + Q_ASSERT(isBlack()); +} + + +MapColor* MapColor::duplicate() const +{ + MapColor* copy = new MapColor(name, priority); + copy->cmyk = cmyk; + copy->rgb = rgb; + copy->opacity = opacity; + copy->q_color = q_color; + copy->spot_color_method = spot_color_method; + copy->cmyk_color_method = cmyk_color_method; + copy->rgb_color_method = rgb_color_method; + copy->flags = flags; + copy->spot_color_name = spot_color_name; + copy->components = components; + return copy; +} + +bool MapColor::isBlack() const +{ + return rgb.isBlack() && cmyk.isBlack(); +} + +bool MapColor::isWhite() const +{ + return rgb.isWhite() && cmyk.isWhite(); +} + + +bool operator==(const SpotColorComponents& lhs, const SpotColorComponents& rhs) +{ + return lhs.size() == rhs.size() + && std::is_permutation(begin(lhs), end(lhs), begin(rhs), [](const auto& left, const auto& right) { + return *left.spot_color == *right.spot_color + && qAbs(left.factor - right.factor) < 1e-03; + }); +} + +bool MapColor::componentsEqual(const MapColor& other, bool compare_priority) const +{ + const SpotColorComponents& lhs(components); + const SpotColorComponents& rhs(other.components); + return lhs.size() == rhs.size() + && std::is_permutation(begin(lhs), end(lhs), begin(rhs), [compare_priority](const auto& left, const auto& right) { + return left.spot_color->equals(*right.spot_color, compare_priority) + && qAbs(left.factor - right.factor) < 1e-03; + }); +} + +bool MapColor::equals(const MapColor& other, bool compare_priority) const +{ + return (!compare_priority || (priority == other.priority)) && + (name.compare(other.name, Qt::CaseInsensitive) == 0) && + (spot_color_method == other.spot_color_method) && + (cmyk_color_method == other.cmyk_color_method) && + (rgb_color_method == other.rgb_color_method) && + (flags == other.flags) && + (cmyk_color_method != CustomColor || cmyk == other.cmyk) && + (rgb_color_method != CustomColor || rgb == other.rgb) && + ( spot_color_method == UndefinedMethod || + (spot_color_method == SpotColor && spot_color_name.compare(other.spot_color_name, Qt::CaseInsensitive) == 0) || + (spot_color_method == CustomColor && componentsEqual(other, compare_priority)) ) && + (qAbs(opacity - other.opacity) < 1e-03); +} + + +void MapColor::setSpotColorName(const QString& spot_color_name) +{ + spot_color_method = MapColor::SpotColor; + this->spot_color_name = spot_color_name; + components.clear(); + updateCalculatedColors(); +} + +void MapColor::setSpotColorComposition(const SpotColorComponents& components) +{ + this->components = components; + if (components.empty()) + spot_color_method = UndefinedMethod; + else + spot_color_method = CustomColor; + + removeSpotColorComponent(this); + updateCompositionName(); + updateCalculatedColors(); +} + +bool MapColor::removeSpotColorComponent(const MapColor* color) +{ + auto size_before = components.size(); + auto match = [this, color](const SpotColorComponent& scc) { return scc.spot_color == color; }; + components.erase(std::remove_if(begin(components), end(components), match), end(components)); + bool changed = components.size() != size_before; + if (changed) + { + if (components.empty()) + spot_color_method = UndefinedMethod; + + updateCompositionName(); + updateCalculatedColors(); + } + return changed; +} + +void MapColor::setKnockout(bool flag) +{ + if (spot_color_method != MapColor::UndefinedMethod) + { + if (flag) + { + if (!getKnockout()) + flags += MapColor::Knockout; + } + else if (getKnockout()) + flags -= MapColor::Knockout; + + Q_ASSERT(getKnockout() == flag); + } +} + +bool MapColor::getKnockout() const +{ + return (flags & MapColor::Knockout) > 0; +} + + +void MapColor::setCmyk(const MapColorCmyk& new_cmyk) +{ + cmyk_color_method = MapColor::CustomColor; + cmyk = new_cmyk; + updateCalculatedColors(); +} + +void MapColor::setCmykFromSpotColors() +{ + if (spot_color_method == MapColor::CustomColor) + { + cmyk_color_method = MapColor::SpotColor; + updateCalculatedColors(); + } +} + +void MapColor::setCmykFromRgb() +{ + if (rgb_color_method == MapColor::CmykColor) + rgb_color_method = MapColor::CustomColor; + + cmyk_color_method = MapColor::RgbColor; + updateCalculatedColors(); +} + + +void MapColor::setRgb(const MapColorRgb& new_rgb) +{ + rgb_color_method = MapColor::CustomColor; + rgb = new_rgb; + updateCalculatedColors(); +} + +void MapColor::setRgbFromSpotColors() +{ + if (spot_color_method == MapColor::CustomColor) + { + rgb_color_method = MapColor::SpotColor; + updateCalculatedColors(); + } +} + +void MapColor::setRgbFromCmyk() +{ + if (cmyk_color_method == MapColor::RgbColor) + cmyk_color_method = MapColor::CustomColor; + + rgb_color_method = MapColor::CmykColor; + updateCalculatedColors(); +} + +void MapColor::updateCompositionName() +{ + if (spot_color_method != MapColor::SpotColor) + { + spot_color_name.clear(); + for (auto& component : components) + { + if (!spot_color_name.isEmpty()) + spot_color_name += QLatin1String(", "); + spot_color_name += component.spot_color->getSpotColorName() + QLatin1Char(' ') + + QString::number(component.factor * 100) /* % */; + } + } +} + +void MapColor::updateCalculatedColors() +{ + Q_ASSERT(components.size() == 0 || spot_color_method == CustomColor); + Q_ASSERT(components.size() > 0 || spot_color_method != CustomColor); + Q_ASSERT(!((cmyk_color_method == RgbColor) && (rgb_color_method == CmykColor))); + + if (spot_color_method != CustomColor) + { + // No composition, thus cannot determine CMYK or RGB from spot colors. + if (cmyk_color_method == MapColor::SpotColor) + cmyk_color_method = MapColor::CustomColor; + + if (rgb_color_method == MapColor::SpotColor) + rgb_color_method = MapColor::CustomColor; + } + else + { + if (cmyk_color_method == MapColor::SpotColor) + cmyk = cmykFromSpotColors(); + + if (rgb_color_method == MapColor::SpotColor) + rgb = rgbFromSpotColors(); + } + + if (cmyk_color_method == MapColor::RgbColor) + cmyk = MapColorCmyk(rgb); + + if (rgb_color_method == MapColor::CmykColor) + rgb = MapColorRgb(cmyk); + + if (cmyk_color_method != RgbColor) + q_color = cmyk; + else + q_color = rgb; + + Q_ASSERT(components.size() > 0 || cmyk_color_method != MapColor::SpotColor); + Q_ASSERT(components.size() > 0 || rgb_color_method != MapColor::SpotColor); +} + +MapColorCmyk MapColor::cmykFromSpotColors() const +{ + Q_ASSERT(components.size() > 0); + + MapColorCmyk cmyk(Qt::white); + Q_ASSERT(cmyk.isWhite()); + for (auto&& component : components) + { + const MapColorCmyk& other = component.spot_color->cmyk; + cmyk.c = cmyk.c + component.factor * other.c * (1.0f - cmyk.c); + cmyk.m = cmyk.m + component.factor * other.m * (1.0f - cmyk.m); + cmyk.y = cmyk.y + component.factor * other.y * (1.0f - cmyk.y); + cmyk.k = cmyk.k + component.factor * other.k * (1.0f - cmyk.k); + } + return cmyk; +} + +MapColorRgb MapColor::rgbFromSpotColors() const +{ + Q_ASSERT(components.size() > 0); + + MapColorRgb rgb = QColor(Qt::white); + Q_ASSERT(rgb.isWhite()); + for (auto&& component : components) + { + const MapColorRgb& other = component.spot_color->rgb; + rgb.r = rgb.r - component.factor * (1.0f - other.r) * rgb.r; + rgb.g = rgb.g - component.factor * (1.0f - other.g) * rgb.g; + rgb.b = rgb.b - component.factor * (1.0f - other.b) * rgb.b; + } + return rgb; +} + + +} // namespace OpenOrienteering diff --git a/src/core/map_color.h b/src/core/map_color.h new file mode 100644 index 0000000..1ad3d14 --- /dev/null +++ b/src/core/map_color.h @@ -0,0 +1,846 @@ +/* + * Copyright 2012, 2013 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_COLOR_H +#define OPENORIENTEERING_MAP_COLOR_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace OpenOrienteering { + +class MapColor; + +/** + * The MapColorCmyk class provides a datatype for storing and transfering + * opaque CMYK colors. + * + * Components (c, m, y, k) are floats in the range [0.0; 1.0]. + */ +struct MapColorCmyk +{ + /** The cyan component. */ + float c = 0; + /** The magenta component. */ + float m = 0; + /** The yellow component. */ + float y = 0; + /** The black component (aka key). */ + float k = 1; + + /** Constructs a black color. */ + MapColorCmyk() noexcept = default; + + /** Constructs a color with the given components. */ + MapColorCmyk(float c, float m, float y, float k) noexcept; + + /** Constructs a copy of the given CMYK color. */ + MapColorCmyk(const MapColorCmyk& other) noexcept = default; + + /** Constructs a CMYK color of the given QColor. Used for type conversions. */ + MapColorCmyk(const QColor& other) noexcept; + + /** Assigns another color's value to this color. */ + MapColorCmyk& operator=(const MapColorCmyk& other) = default; + + /** Converts this color to a QColor. */ + operator QColor() const; + + /** Returns true if this color is black. */ + bool isBlack() const; + + /** Returns true if this color is white. */ + bool isWhite() const; +}; + +/** Returns true iff the MapColorCmyk are equal in all components. */ +bool operator==(const MapColorCmyk& lhs, const MapColorCmyk& rhs); + +/** Returns true iff the MapColorCmyk differ in at least one components. */ +bool operator!=(const MapColorCmyk& lhs, const MapColorCmyk& rhs); + + +/** + * The MapColorRgb class provides a datatype for storing and transfering + * opaque RGB colors. + * + * Components (r, g, b) are floats in the range [0.0; 1.0]. + */ +struct MapColorRgb +{ + /** The red component. */ + float r = 0; + /** The green component. */ + float g = 0; + /** The blue component. */ + float b = 0; + + /** Constructs a black color. */ + MapColorRgb() noexcept = default; + + /** Constructs a color with the given components. */ + MapColorRgb(float r, float g, float b) noexcept; + + /** Constructs a copy of the given RGB color. */ + MapColorRgb(const MapColorRgb& other) noexcept = default; + + /** Constructs a RGB color of the given QColor. Used for type conversions. */ + MapColorRgb(const QColor& other) noexcept; + + /** Assigns another color's value to this color. */ + MapColorRgb& operator=(const MapColorRgb& other) noexcept = default; + + /** Converts this color to a QColor. */ + operator QColor() const; + + /** Returns true if this color is black. */ + bool isBlack() const; + + /** Returns true if this color is white. */ + bool isWhite() const; +}; + +/** Returns true if both MapColorRgb are equal in all components. */ +bool operator==(const MapColorRgb& lhs, const MapColorRgb& rhs); + +/** Returns true iff the MapColorRgb differ in at least one components. */ +bool operator!=(const MapColorRgb& lhs, const MapColorRgb& rhs); + + +/** + * The SpotColorComponent datatype describes the use of a spot color in a + * screen or overprint to create another color. + */ +struct SpotColorComponent +{ + /** A map color which is a spot color. */ + const MapColor* spot_color; + + /** The factor describes the halftoning (screen). + * It is a value in the range [0.0; 1.0]. */ + float factor; + + /** Constructs a component with an undefined spot color and halftoning. */ + SpotColorComponent(); + + /** Constructs a component for the given spot color and halftoning. */ + SpotColorComponent(const MapColor* spot_color, float factor); + + /** Returns true iff the spot color is defined. */ + bool isValid() const; +}; + + +/** + * The SpotColorComponents type is a STL container that stores all + * SpotColorComponent elements which make up a particular map color. + */ +typedef std::vector SpotColorComponents; + +/** + * Returns true if for each element in lhs, there is an element of equal color + * and factor in rhs. */ +bool operator==(const SpotColorComponents &lhs, const SpotColorComponents& rhs); + + +/** + * The MapColor class provides colors which may be used by symbols (and + * objects). Apart from the mere color, it specifies how to output the color + * to different type of devices and optionally how the color was composed + * from other colors. + */ +class MapColor +{ +public: + /** + * SpecialProperties provides identifiers for (pseudo-)colors serving + * particular purposes in the program. + */ + enum SpecialPriorities + { + CoveringRed = -1005, ///< Foreground color for tool helper lines + CoveringWhite = -1000, ///< Background color for tool helper lines + Registration = -900, ///< Registration Black: all printed colors + Undefined = -500, ///< Color for objects with undefined symbol + Reserved = -1 ///< Never drawn + }; + + /** + * ColorMethod provides identifiers for methods on how to determine a + * particular realization of a color. + */ + enum ColorMethod + { + UndefinedMethod = 0, + CustomColor = 1, + SpotColor = 2, + CmykColor = 4, + RgbColor = 8, + Knockout = 16 + }; + + /** Constructs a black CMYK map color of undefined priority.*/ + MapColor(); + + /** Constructs a black CMYK map color with the given priority. */ + MapColor(int priority); + + /** Constructs a black CMYK map color with the given name and priority. */ + MapColor(const QString& name, int priority); + + /** Returns a copy of the color. */ + MapColor* duplicate() const; + + + /** Returns a QColor representation (reference) of the map color. + * + * This color is based on the CMYK color unless the CMYK color method is + * RGB: In that case, the returned value is based on the RGB color. + */ + operator const QColor&() const; + + /** Converts the current RGB values to a QRgb. */ + operator QRgb() const; + + + /** Returns the color's name for the mapping context. */ + const QString& getName() const; + + /** Sets the color's name for the mapping context. */ + void setName(const QString& name); + + /** Returns the color's priority. */ + int getPriority() const; + + /** + * Sets the color's priority. + * Normally you don't want to call this directly. + */ + void setPriority(int priority); + + /** @deprecated Returns the color's opacity. */ + float getOpacity() const; + + /** @deprecated Sets the color's opacity. */ + void setOpacity(float opacity); + + + /** + * Returns how the spot color is to be created. + * + * Returns UndefinedMethod, SpotColor (for a full tone single color + * referenced by name), or CustomColor (for a color created from named + * colors using halftoning (screens) and overprint. + */ + ColorMethod getSpotColorMethod() const; + + /** + * Returns the name for the single spot color or a label for the spot color + * composition which realizes this map color. + * Returns an empty string for an UndefinedMethod. + */ + const QString& getSpotColorName() const; + + /** + * Sets the name of a single spot color which realizes this map color, + * and sets the spot color method to SpotColor. + */ + void setSpotColorName(const QString& spot_color_id); + + /** + * Sets the given components (i.e. screens and/or overprint) for the color, + * and sets the spot color method to CustomColor. + */ + void setSpotColorComposition(const SpotColorComponents& components); + + /** + * Returns the components of the spot color realization of this color. + * Returns an empty list if the spot color method is not CustomColor. + */ + const SpotColorComponents& getComponents() const; + + /** + * Removes a component color. + * + * Returns true if components were removed. + * Returns false if the color was not part of the composition before. + */ + bool removeSpotColorComponent(const MapColor* color); + + /** + * Sets the value of knockout flag for spot color printing. + * + * The color must have a spot color definition, or no change will be done. + */ + void setKnockout(bool flag); + + /** + * Returns the value of the knockout flag. + */ + bool getKnockout() const; + + + /** + * Returns how the CMYK color value is determined. + * + * Returns CustomColor (for custom CMYK values, e.g. for named spot colors), + * SpotColor (for values determined from evaluation the spot color composition), + * or RgbColor (for values directly derived from the current RGB values). + */ + ColorMethod getCmykColorMethod() const; + + /** Returns the map color's CMYK values. */ + const MapColorCmyk& getCmyk() const; + + /** Sets the CMYK values, and sets the CMYK color method to CustomColor. */ + void setCmyk(const MapColorCmyk& cmyk); + + /** + * Determines the CMYK values from the spot color composition, + * and sets the CMYK color method to SpotColor. + * + * The spot color method must be CustomColor. + * If the spot color composition is empty, the spot color method is + * changed to CustomColor, and the CMYK color method is changed to + * CustomColor. + */ + void setCmykFromSpotColors(); + + /** + * Determines the CMYK from the current RGB value, + * and sets the CMYK color method to RgbColor. + * + * If the RGB color method is CmykColor, it is changed to CustomColor. + */ + void setCmykFromRgb(); + + + /** + * Returns how the RGB color value is determined. + * + * Returns CustomColor (for custom RGB values), + * SpotColor (for values determined from evaluation the spot color composition), + * or CmykColor (for values directly derived from the current CMYK value). + */ + ColorMethod getRgbColorMethod() const; + + /** Returns the map color's RGB values. */ + const MapColorRgb& getRgb() const; + + /** Sets the RGB values, and sets the RGB color method to CustomColor. */ + void setRgb(const MapColorRgb& rgb); + + /** + * Determines the RGB values from the spot color composition, + * and sets the RGB color method to SpotColor. + * + * The spot color method must be CustomColor. + * If the spot color composition is empty, the spot color method is + * changed to CustomColor, and the RGB color method is changed to + * CustomColor. + */ + void setRgbFromSpotColors(); + + /** + * Determines the RGB from the current CMYK value, + * and sets the RGB color method to CmykColor. + * + * If the CMYK color method is RgbColor, it is changed to CustomColor. + */ + void setRgbFromCmyk(); + + + /** Returns true if this color is black. */ + bool isBlack() const; + + /** Returns true if this color is white. */ + bool isWhite() const; + + + /** Compares this color and another. */ + bool equals(const MapColor& other, bool compare_priority) const; + + /** Compares two colors given by pointers. + * Returns true if the colors are equal or if both pointers are nullptr. */ + static bool equal(const MapColor* color, const MapColor* other); + + /** Returns true if this color's priority is less than the other's. */ + bool comparePriority(const MapColor& other) const; + + /** Compares this color's components and another. */ + bool componentsEqual(const MapColor& other, bool compare_priority) const; + +protected: + /** + * Determines the composition name from the components. + * + * Does nothing it the spot color method is not CustomColor. + */ + void updateCompositionName(); + + /** + * Updates all calculated color values. + * + * If the spot color method is different from CustomColor, resets CMYK and + * RGB methods from SpotColor tp CustomColor. + */ + void updateCalculatedColors(); + + /** + * Returns a CMYK color determined from the cmyk color of the spot color + * components. + */ + MapColorCmyk cmykFromSpotColors() const; + + /** + * Returns a RGB color determined from the cmyk color of the spot color + * components. + */ + MapColorRgb rgbFromSpotColors() const; + + + QString name; + int priority; + + MapColorCmyk cmyk; + MapColorRgb rgb; + float opacity; + + QColor q_color; + + char spot_color_method; + char cmyk_color_method; + char rgb_color_method; + char flags; + + QString spot_color_name; + SpotColorComponents components; +}; + +/** Returns true if both MapColor are equal in all components. */ +bool operator==(const MapColor& lhs, const MapColor& rhs); + +/** Returns true iff the MapColor differ in at least one components. */ +bool operator!=(const MapColor& lhs, const MapColor& rhs); + + +/** + * MapColorMap provides a mapping from one map color to another. + * + * In addition to explicitly user-defined key-value pairs of colors, it will + * (in const contexts) map colors with reserved priorities to themselves, + * assuming that there is only a single static instance of these colors. + */ +class MapColorMap +{ +public: + /** Constructs a new MapColorMap. */ + MapColorMap(); + + /** Returns the size, i.e. the number of user-defined key-value pairs. */ + int size() const; + + /** Clears the user-defined key-value pairs. */ + void clear(); + + /** Returns true if there is a user-defined value for the key color */ + bool contains(const MapColor* key) const; + + /** Returns the mapped value for the key color. + * + * Returns the user-defined value for the key color if it is defined. + * Otherwise, if the key color's priority is from the RESERVED domain, + * returns key. Otherwise returns nullptr. + */ + const MapColor* value(const MapColor* key) const; + + /** Returns the mapped value for the key color. Same as value(). + * + * Returns the user-defined value for the key color if it is defined. + * Otherwise, if the key color's priority is from the RESERVED domain, + * returns key. Otherwise returns nullptr. + */ + const MapColor* operator[](const MapColor* key) const; + + /** Returns a reference to pointer to the mapped value for the key color. + * If a user-defined mapped value does not exist yet, a default- + * constructed mapped value is created first. + */ + const MapColor* & operator[](const MapColor* key); + +private: + /** The low-level mapping. */ + QHash mapping; +}; + + +/** + * Constructs a QColor with the alpha given by opacity from the prototype c. + * + * A QColor object must be constructible from c. + */ +template +QColor colorWithOpacity(T c, float opacity); + +/** + * Constructs a QColor with opacity from the given MapColor. + */ +QColor colorWithOpacity(const MapColor& c); + + + +// ### MapColorCmyk inline code ### + +inline +MapColorCmyk::MapColorCmyk(float c, float m, float y, float k) noexcept + : c(c), m(m), y(y), k(k) +{ + // Nothing +} + +inline +MapColorCmyk::MapColorCmyk(const QColor& other) noexcept + : c(other.cyanF()), m(other.magentaF()), y(other.yellowF()), k(other.blackF()) +{ + // Nothing +} + +inline +MapColorCmyk::operator QColor() const +{ + return QColor::fromCmykF(c, m, y, k); +} + +inline +bool MapColorCmyk::isBlack() const +{ + return (1.0 == k) || (1.0 == c && 1.0 == m && 1.0 == y); +} + +inline +bool MapColorCmyk::isWhite() const +{ + return (0.0 == c && 0.0 == m && 0.0 == y && 0.0 == k); +} + +inline +bool operator==(const MapColorCmyk& lhs, const MapColorCmyk& rhs) +{ + // The maximum difference of two floating point member values + // which are regarded as *equal*. + static const float epsilon = 0.0005f; + return ( qAbs(lhs.c - rhs.c) <= epsilon && + qAbs(lhs.m - rhs.m) <= epsilon && + qAbs(lhs.y - rhs.y) <= epsilon && + qAbs(lhs.k - rhs.k) <= epsilon ); +} + +inline +bool operator!=(const MapColorCmyk& lhs, const MapColorCmyk& rhs) +{ + return !(lhs == rhs); +} + + +// ### MapColorRgb inline code ### + +inline +MapColorRgb::MapColorRgb(float r, float g, float b) noexcept + : r(r), g(g), b(b) +{ + // Nothing +} + +inline +MapColorRgb::MapColorRgb(const QColor& other) noexcept + : r(other.redF()), g(other.greenF()), b(other.blueF()) +{ + // Nothing +} + +inline +MapColorRgb::operator QColor() const +{ + return QColor::fromRgbF(r, g, b); +} + +inline +bool MapColorRgb::isBlack() const +{ + return (0.0 == r && 0.0 == g && 0.0 == b); +} + +inline +bool MapColorRgb::isWhite() const +{ + return (1.0 == r && 1.0 == g && 1.0 == b); +} + +inline +bool operator==(const MapColorRgb& lhs, const MapColorRgb& rhs) +{ + // The maximum difference of two floating point member values + // which are regarded as *equal*. + static const float epsilon = 0.0005f; + return ( qAbs(lhs.r - rhs.r) <= epsilon && + qAbs(lhs.g - rhs.g) <= epsilon && + qAbs(lhs.b - rhs.b) <= epsilon ); +} + +inline +bool operator!=(const MapColorRgb& lhs, const MapColorRgb& rhs) +{ + return !(lhs == rhs); +} + + +// ### SpotColorComponent inline code ### + +inline +SpotColorComponent::SpotColorComponent() + : spot_color(nullptr), + factor(0.0f) +{ + // Nothing +} + +inline +SpotColorComponent::SpotColorComponent(const MapColor* spot_color, float factor) + : spot_color(spot_color), + factor(factor) +{ + // Nothing +} + +inline +bool SpotColorComponent::isValid() const +{ + return spot_color; +} + + +// ### MapColor inline code ### + +inline +MapColor::operator const QColor&() const +{ + return q_color; +} + +inline +MapColor::operator QRgb() const +{ + return qRgba(qFloor(255.9 * rgb.r), qFloor(255.9 * rgb.g), qFloor(255.9 * rgb.b), qFloor(255.9 * opacity)); +} + +inline +const QString& MapColor::getName() const +{ + return name; +} + +inline +void MapColor::setName(const QString& name) +{ + this->name = name; +} + +inline +int MapColor::getPriority() const +{ + return priority; +} + +inline +void MapColor::setPriority(int priority) +{ + this->priority = priority; +} + +inline +float MapColor::getOpacity() const +{ + return opacity; +} + +inline +void MapColor::setOpacity(float opacity) +{ + this->opacity = opacity; +} + +inline +MapColor::ColorMethod MapColor::getSpotColorMethod() const +{ + return (ColorMethod)spot_color_method; +} + +inline +const QString& MapColor::getSpotColorName() const +{ + return spot_color_name; +} + +inline +const SpotColorComponents& MapColor::getComponents() const +{ + return components; +} + +inline +MapColor::ColorMethod MapColor::getCmykColorMethod() const +{ + return (ColorMethod)cmyk_color_method; +} + +inline +const MapColorCmyk& MapColor::getCmyk() const +{ + return cmyk; +} + +inline +MapColor::ColorMethod MapColor::getRgbColorMethod() const +{ + return (ColorMethod)rgb_color_method; +} + +inline +const MapColorRgb& MapColor::getRgb() const +{ + return rgb; +} + +inline +bool MapColor::comparePriority(const MapColor& other) const +{ + return priority < other.priority; +} + +inline +bool MapColor::equal(const MapColor* color, const MapColor* other) +{ + if (color == other) + return true; + else if (color && other) + return color->equals(*other, false); + else + return false; +} + +inline +bool operator==(const MapColor& lhs, const MapColor& rhs) +{ + return lhs.equals(rhs, true); +} + +inline +bool operator!=(const MapColor& lhs, const MapColor& rhs) +{ + return !lhs.equals(rhs, true); +} + + +// ### MapColorMap inline code ### + +inline +MapColorMap::MapColorMap() + : mapping() +{ + // Nothing. +} + +inline +int MapColorMap::size() const +{ + return mapping.size(); +} + +inline +void MapColorMap::clear() +{ + mapping.clear(); +} + +inline +bool MapColorMap::contains(const MapColor* key) const +{ + return mapping.contains(key); +} + +inline +const MapColor* MapColorMap::value(const MapColor* key) const +{ + if (mapping.contains(key)) + { + return mapping.value(key); + } + else if (key && key->getPriority() < 0) + { + return key; + } + else + { + return nullptr; + } +} + +inline +const MapColor* MapColorMap::operator[](const MapColor* key) const +{ + return value(key); +} + +inline +const MapColor* & MapColorMap::operator[](const MapColor* key) +{ + return mapping[key]; +} + +template +QColor colorWithOpacity(T c, float opacity) +{ + auto color = static_cast(c); + color.setAlphaF(opacity); + return color; +} + +inline +QColor colorWithOpacity(const MapColor& c) +{ + return colorWithOpacity(static_cast(c), c.getOpacity()); +} + + +} // namespace OpenOrienteering + + +// Allow explicit use of MapColor pointers in QVariant +Q_DECLARE_METATYPE(const OpenOrienteering::MapColor*) + + +#endif diff --git a/src/core/map_coord.cpp b/src/core/map_coord.cpp new file mode 100644 index 0000000..46216a1 --- /dev/null +++ b/src/core/map_coord.cpp @@ -0,0 +1,463 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "map_coord.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "util/xml_stream_util.h" + + +namespace OpenOrienteering { + +static_assert(sizeof(qint32) <= sizeof(int), + "MapCoord::setX/Y uses qRound() returning int, xp/yp is of type qint32"); + +static_assert(!MapCoord::BoundsOffset().check_for_offset, + "Default-constructed BoundsOffset must have check_for_offset == false."); +static_assert(MapCoord::BoundsOffset().x == 0, + "Default-constructed BoundsOffset must have x == 0."); +static_assert(MapCoord::BoundsOffset().y == 0, + "Default-constructed BoundsOffset must have y == 0."); +static_assert(MapCoord::BoundsOffset().isZero(), + "Default-constructed BoundsOffset must be null."); + +static_assert(std::is_nothrow_move_constructible::value, "MapCoord must be nothrow move constructible."); +static_assert(std::is_nothrow_move_assignable::value, "MapCoord must be nothrow move assignable."); + +static_assert(MapCoordF(1.0, 1.0) == QPointF(1.0, 1.0), + "MapCoordF and QPointF constructors must use the same unit of measurement"); +static_assert(MapCoord(1, 1) == MapCoord(MapCoordF(1.0, 1.0)), + "MapCoord and MapCoordF constructors must use the same unit of measurement"); +static_assert(MapCoord(1.0, 1.0) == MapCoord(QPointF(1.0, 1.0)), + "MapCoord and QPointF constructors must use the same unit of measurement"); +static_assert(MapCoord(1, 1) == MapCoord::fromNative(1000, 1000), + "MapCoord::fromRaw must use the native unit of measurement"); + +static_assert(MapCoord(1, 1) + MapCoord (2, -1) == MapCoord(3, 0), + "MapCoord has component-wise operator+"); +static_assert(MapCoord(1, 1) - MapCoord (2, -1) == MapCoord(-1, 2), + "MapCoord has component-wise operator-"); +static_assert(MapCoord(1, 1) * 2.1 == MapCoord(2.1, 2.1), + "MapCoord has component-wise operator*(coord, factor)"); +static_assert(0.5 * MapCoord(1, 1) == MapCoord(0.5, 0.5), + "MapCoord has component-wise operator*(factor, coord)"); +static_assert(MapCoord(1, 1) / 4 == MapCoord(0.25, 0.25), + "MapCoord has component-wise operator/(coord, divisor"); +static_assert(-MapCoord(-2, 1) == MapCoord(2, -1), + "MapCoord has unary operator-()"); + +static_assert(!MapCoord(0.0, 0.0).isCurveStart(), + "MapCoord::isCurveStart() must return false by default."); +static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::CurveStart)).isCurveStart(), + "MapCoord::CurveStart must result in MapCoord::isCurveStart() returning true."); + +static_assert(!MapCoord(0.0, 0.0).isClosePoint(), + "MapCoord::isClosePoint() must return false by default."); +static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::ClosePoint)).isClosePoint(), + "MapCoord::ClosePoint must result in MapCoord::isClosePoint() returning true."); + +static_assert(!MapCoord(0.0, 0.0).isHolePoint(), + "MapCoord::isHolePoint() must return false by default."); +static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::HolePoint)).isHolePoint(), + "MapCoord::HolePoint must result in MapCoord::isHolePoint() returning true."); + +static_assert(!MapCoord(0.0, 0.0).isDashPoint(), + "MapCoord::isDashPoint() must return false by default."); +static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::DashPoint)).isDashPoint(), + "MapCoord::DashPoint must result in MapCoord::isDashPoint() returning true."); + +static_assert(!MapCoord(0.0, 0.0).isGapPoint(), + "MapCoord::isGapPoint() must return false by default."); +static_assert(MapCoord(0.0, 0.0, MapCoord::Flags(MapCoord::GapPoint)).isGapPoint(), + "MapCoord::GapPoint must result in MapCoord::isGapPoint() returning true."); + +static_assert(MapCoord(-1.0, 2.0).x() == -1, + "MapCoord::x() must return original value (w/o flags)"); +static_assert(MapCoord(-1.0, 2.0, MapCoord::Flags{255}).x() == -1.0, + "MapCoord::x() must return original value (with flags)"); + +static_assert(MapCoord::fromNative(-1, 2).nativeX() == -1, + "MapCoord::nativeX() must return original value (w/o flags)"); +static_assert(MapCoord::fromNative(-1, 2, MapCoord::Flags{255}).nativeX() == -1, + "MapCoord::nativeX() must return original value (with flags)"); + +#ifndef MAPPER_NO_QREAL_CHECK +// Check that QPointF/MapCoorF will actually use double. +static_assert(std::is_same::value, "qreal is not double. This could work but was not tested."); +#endif + +static_assert(std::is_nothrow_move_constructible::value, "MapCoord must be nothrow move constructible."); +static_assert(std::is_nothrow_move_assignable::value, "MapCoord must be nothrow move assignable."); + +static_assert(QLineF(QPointF(0,0), -MapCoordF(1,0).perpRight()) == QLineF(QPointF(0,0), QPointF(1,0)).normalVector(), + "MapCoordF::perpRight() must return a vector in opposite direction of QLineF::normalVector()."); +static_assert(QLineF(QPointF(0,0), MapCoordF(1,0).normalVector()) == QLineF(QPointF(0,0), QPointF(1,0)).normalVector(), + "MapCoordF::normalVector() must behave like QLineF::normalVector()."); + + + +namespace literal +{ + static const QLatin1String x("x"); + static const QLatin1String y("y"); + static const QLatin1String flags("flags"); +} + + + +namespace +{ + +// Acceptable coord bounds on import, derived from printing UI bounds +constexpr qint64 min_coord = -50000000; +constexpr qint64 max_coord = +50000000; + +MapCoord::BoundsOffset bounds_offset; + +inline +void applyBoundsOffset(qint64& x64, qint64& y64) +{ + x64 -= bounds_offset.x; + y64 -= bounds_offset.y; +} + +inline +void handleBoundsOffset(qint64& x64, qint64& y64) +{ + if (bounds_offset.check_for_offset) + { + bounds_offset.check_for_offset = false; + if (x64 < min_coord || x64 > max_coord) + { + bounds_offset.x = x64; + x64 = 0; + } + if (y64 < min_coord || y64 > max_coord) + { + bounds_offset.y = y64; + y64 = 0; + } + } + else + { + applyBoundsOffset(x64, y64); + } +} + +inline +void ensureBoundsForQint32(qint64 x64, qint64 y64) +{ + if ( x64 < std::numeric_limits::min() + || x64 > std::numeric_limits::max() + || y64 < std::numeric_limits::min() + || y64 > std::numeric_limits::max() ) + { + throw std::range_error(QT_TRANSLATE_NOOP("OpenOrienteering::MapCoord", "Coordinates are out-of-bounds.")); + } +} + +} // namespace + + + +MapCoord::BoundsOffset& MapCoord::boundsOffset() +{ + return bounds_offset; +} + +bool MapCoord::isRegular() const +{ + return (xp > 2 * min_coord + && xp < 2 * max_coord + && yp > 2 * min_coord + && yp < 2 * max_coord ); +} + +MapCoord MapCoord::fromNative64(qint64 x64, qint64 y64) +{ + // We only need to check the storage bounds here because + // this is the technical invariant. + // Printing bounds will be checked during map loading ATM. + ensureBoundsForQint32(x64, y64); + return MapCoord{ static_cast(x64), static_cast(y64), Flags() }; +} + +MapCoord MapCoord::fromNative64withOffset(qint64 x64, qint64 y64) +{ + applyBoundsOffset(x64, y64); + ensureBoundsForQint32(x64, y64); + return MapCoord { static_cast(x64), static_cast(y64), Flags() }; +} + +void MapCoord::save(QXmlStreamWriter& xml) const +{ + XmlElementWriter element(xml, XmlStreamLiteral::coord); + element.writeAttribute(literal::x, xp); + element.writeAttribute(literal::y, yp); + if (fp) + { + element.writeAttribute(literal::flags, Flags::Int(fp)); + } +} + +MapCoord MapCoord::load(QXmlStreamReader& xml) +{ + XmlElementReader element(xml); + auto x64 = element.attribute(literal::x); + auto y64 = element.attribute(literal::y); + auto flags = Flags{element.attribute(literal::flags)}; + + handleBoundsOffset(x64, y64); + ensureBoundsForQint32(x64, y64); + return MapCoord { static_cast(x64), static_cast(y64), flags }; +} + +MapCoord MapCoord::load(qreal x, qreal y, Flags flags) +{ + auto x64 = qRound64(x * 1000); + auto y64 = qRound64(y * 1000); + + handleBoundsOffset(x64, y64); + ensureBoundsForQint32(x64, y64); + return MapCoord { static_cast(x64), static_cast(y64), flags }; +} + +MapCoord MapCoord::load(QPointF p, MapCoord::Flags flags) +{ + return MapCoord::load(p.x(), p.y(), flags); +} + +#ifndef NO_NATIVE_FILE_FORMAT + +MapCoord::MapCoord(const LegacyMapCoord& coord) noexcept + : xp{ decltype(xp)(coord.x >> 4) } + , yp{ decltype(yp)(coord.y >> 4) } + , fp{ Flags::Int((coord.x & 0xf) | ((coord.y & 0xf) << 4)) } +{ + //nothing +} + +#endif + +QString MapCoord::toString() const +{ + /* The buffer size must allow for + * 1x ';': 1 + * 2x '-': 2 + * 2x ' ': 2 + * 2x the decimal digits for values up to 0..2^31: + * 20 + * 1x the decimal digits for 0..2^8-1: + * 3 + * Total: 28 */ + constexpr std::size_t buf_size = 1+2+2+20+3; + static const QChar encoded[10] = { + QLatin1Char{'0'}, QLatin1Char{'1'}, + QLatin1Char{'2'}, QLatin1Char{'3'}, + QLatin1Char{'4'}, QLatin1Char{'5'}, + QLatin1Char{'6'}, QLatin1Char{'7'}, + QLatin1Char{'8'}, QLatin1Char{'9'} + }; + QChar buffer[buf_size]; + + // For efficiency, we construct the string from the back. + std::size_t j = buf_size - 1; + buffer[j] = QLatin1Char{';'}; + --j; + + auto flags = Flags::Int(fp); + if (flags > 0) + { + do + { + buffer[j] = encoded[flags % 10]; + flags = flags / 10; + --j; + } + while (flags != 0); + + buffer[j] = QChar::Space; + --j; + } + + qint64 tmp = yp; + static_assert(sizeof(decltype(tmp)) > sizeof(decltype(MapCoord::yp)), + "decltype(tmp) must be large enough to hold" + "-std::numeric_limits::min()" ); + QChar sign { QChar::Null }; + if (tmp < 0) + { + sign = QLatin1Char{'-'}; + tmp = -tmp; + } + do + { + buffer[j] = encoded[tmp % 10]; + tmp = tmp / 10; + --j; + } + while (tmp != 0); + if (!sign.isNull()) + { + buffer[j] = sign; + --j; + sign = QChar::Null; + } + + buffer[j] = QChar::Space; + --j; + + static_assert(sizeof(decltype(tmp)) > sizeof(decltype(MapCoord::xp)), + "decltype(tmp) must be large enough to hold" + "-std::numeric_limits::min()" ); + tmp = xp; + if (tmp < 0) + { + sign = QLatin1Char{'-'}; + tmp = -tmp; + } + do + { + buffer[j] = encoded[tmp % 10]; + tmp = tmp / 10; + --j; + } + while (tmp != 0); + if (!sign.isNull()) + { + buffer[j] = sign; + --j; + } + + ++j; + Q_ASSERT(j < buf_size); + j = qMin(j, buf_size); + return QString(buffer+j, buf_size-j); +} + +MapCoord::MapCoord(QStringRef& text) +: MapCoord{} +{ + const int len = text.length(); + if (Q_UNLIKELY(len < 2)) + throw std::invalid_argument("Premature end of data"); + + auto data = text.constData(); + int i = 0; + + qint64 x64 = data[0].unicode(); + if (x64 == '-') + { + x64 = '0' - data[1].unicode(); + for (i = 2; i != len; ++i) + { + auto c = data[i].unicode(); + if (c < '0' || c > '9') + break; + else + x64 = 10*x64 + '0' - c; + } + } + else if (x64 >= '0' && x64 <= '9') + { + x64 -= '0'; + for (i = 1; i != len; ++i) + { + auto c = data[i].unicode(); + if (c < '0' || c > '9') + break; + else + x64 = 10*x64 + c - '0'; + } + } + + ++i; + if (Q_UNLIKELY(i+1 >= len)) + throw std::invalid_argument("Premature end of data"); + + qint64 y64 = data[i].unicode(); + if (y64 == '-') + { + ++i; + y64 = '0' - data[i].unicode(); + for (++i; i != len; ++i) + { + auto c = data[i].unicode(); + if (c < '0' || c > '9') + break; + else + y64 = 10*y64 + '0' - c; + } + } + else if (y64 >= '0' && y64 <= '9') + { + y64 -= '0'; + for (++i; i != len; ++i) + { + auto c = data[i].unicode(); + if (c < '0' || c > '9') + break; + else + y64 = 10*y64 + c - '0'; + } + } + + handleBoundsOffset(x64, y64); + ensureBoundsForQint32(x64, y64); + xp = static_cast(x64); + yp = static_cast(y64); + + if (i < len && data[i] == QChar::Space) + { + ++i; + if (Q_UNLIKELY(i == len)) + throw std::invalid_argument("Premature end of data"); + + // there are no negative flags + fp = Flags(data[i].unicode() - '0'); + for (++i; i < len; ++i) + { + auto c = data[i].unicode(); + if (c < '0' || c > '9') + break; + else + fp = Flags(10*int(fp) + c - '0'); + } + } + + if (Q_UNLIKELY(i >= len || data[i] != QLatin1Char{';'})) + throw std::invalid_argument("Invalid data"); + + ++i; + text = text.mid(i, len-i); +} + + +} // namespace OpenOrienteering diff --git a/src/core/map_coord.h b/src/core/map_coord.h new file mode 100644 index 0000000..30c5d32 --- /dev/null +++ b/src/core/map_coord.h @@ -0,0 +1,1192 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_COORD_H +#define OPENORIENTEERING_MAP_COORD_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +#ifndef NO_NATIVE_FILE_FORMAT + +/** + * The legacy MapCoord structure, only used for legacy native file format. + * + * @deprecated + */ +struct LegacyMapCoord +{ + qint64 x; + qint64 y; +}; + +#endif + + + +/** + * Coordinates of a point in a map, with optional flags. + * + * This coordinate uses what we call native map coordinates. + * One unit in native map coordinates is 1/1000th of a millimeter on the map paper. + * + * The possible flags are: + * + *
      + *
    • isCurveStart(): If set, this point is the first one in a sequence of + * four points which define a cubic bezier curve.
    • + *
    • isHolePoint(): If set, this point marks the end of a distinct path. + * Only area objects may have more than one path. The first path can be + * understood as outline, while the remaining paths can be seen as holes in + * the area, subject to the filling rule. + *
    • isDashPoint(): This flag marks special points in the path. + * The effect depends on the type of symbol. + *
    • isClosePoint(): If this flag is set for the last point of a path, + * this path is treated as closed. The point's coordinates must be equal to + * the first point's coordinates in this path.
    • + *
    • isGapPoint(): This flag is used to indicate the begin and the end + * of gaps in a path. This only used internally at the moment (for dealing + * with dashed lines.) + *
    + * + * These coordinates are implemented as 32 bit integers, + * the flags are store separately. + */ +class MapCoord +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::MapCoord) + +public: + /** + * These flags provide extra information on each coordinate in a path. + * + * Don't change the values, they are used in import/export. + */ + enum Flag + { + CurveStart = 1 << 0, + ClosePoint = 1 << 1, + GapPoint = 1 << 2, + //unused 1 << 3, + HolePoint = 1 << 4, + DashPoint = 1 << 5, + //... + }; + Q_DECLARE_FLAGS(Flags, Flag) + + /** + * Offset and flag for importing and moving out-of-bounds MapCoords. + * + * Since reducing MapCoord bits from 60 (64 minus flags) to 32, we need to + * deal with old files possibly having out-of-bounds coordinates. + * + * In addition, we need to deal with files suffering from out-of-print range + * coordinates (issue #513). In the print dialog, the printable area is up + * to 1000 m of paper [!] wide and high. The top left point is allowed to be + * up 1000 m away from the origin in each direction. Every point to be + * printed (from the UI) must be within this area. + * + * The idea for correcting maps during loading (where we may hit the + * out-of-bounds coordinates for the first time) is to look at the first + * point in the map (the georeferencing reference point or the coordinates + * of the first object). If this point is more than 500 m of paper [!] from + * the origin, it is moved to the origin, and the same offset is applied to + * all other coordinates. After that adjustment, anything up to 1000 m of + * paper away from the first point will be in the printable area. + * + * The current implementation uses a global variable and thus can handle + * only one file at the same time. However, the global variables is + * initially configured to a neutral value. Any code activating the tracking + * of out-of-bounds coordinates it responsible to rollback this neutral + * configuration when finished. + * + * Since symbol definitions are stored in MapCoords, too, extra care needs + * to be taken not to adjust coordinates of the symbols' elements. + * + * \see XMLFileImporter::import + */ + struct BoundsOffset + { + qint64 x = 0; + qint64 y = 0; + bool check_for_offset = false; + + /** Returns true if both x and y are equal to zero. */ + constexpr bool isZero() const; + + /** Resets x and y to zero, and sets check_for_offset. */ + void reset(bool check_for_offset); + }; + +private: + /** Benchmark */ + friend class CoordXmlTest; + + qint32 xp; + qint32 yp; + Flags fp; + +public: + /** Returns the global bounds offset. + * + * It is returned as a non-const reference, so that it can be used in + * QScopedValueRollack. + */ + static BoundsOffset& boundsOffset(); + + + /** Creates a MapCoord with position at the origin and without any flags set. */ + constexpr MapCoord() noexcept; + + /** Copy constructor. */ + constexpr MapCoord(const MapCoord&) noexcept = default; + +#ifndef NO_NATIVE_FILE_FORMAT + + /** Creates a MapCoord from the legacy structure. + * + * @deprecated + */ + MapCoord(const LegacyMapCoord& coord) noexcept; + +#endif + + /** Creates a MapCoord from a position given in millimeters on the map. + * + * This is a convenience constructor for efficient construction of a point + * at the origin i.e. MapCoord(0, 0), or for simple vectors i.e. + * MapCoord(1, 0). Intentionally, there is no public version with flags - + * you need to use other argument types than int if the compiler complains + * about ambiguity. + */ + constexpr MapCoord(int x, int y) noexcept; + +private: + /** Creates a MapCoord from native map coordinates. + * + * This is exposed via fromNative() - you must be explicit when you want to + * use native units. + */ + constexpr MapCoord(qint32 x, qint32 y, Flags flags) noexcept; + + /** Creates a MapCoord from native map coordinates. + * + * This is exposed via fromNative() - you must be explicit when you want to + * use native units. + */ + constexpr MapCoord(qint32 x, qint32 y, Flag flag) noexcept; + + // Prevent selection of MapCoord(qreal, qreal, int) + MapCoord(qint32, qint32, int) = delete; + +public: + /** Creates a MapCoord from a position given in millimeters on the map. */ + constexpr MapCoord(qreal x, qreal y) noexcept; + + /** Creates a MapCoord with the given flags from a position given in millimeters on the map. */ + constexpr MapCoord(qreal x, qreal y, Flags flags) noexcept; + + /** Creates a MapCoord with the given flags from a position given in millimeters on the map. */ + constexpr MapCoord(qreal x, qreal y, Flag flag) noexcept; + + MapCoord(qreal x, qreal y, int flags) = delete; + + /** Creates a MapCoord with the position taken from a QPointF. */ + explicit constexpr MapCoord(QPointF point) noexcept; + + /** Creates a MapCoord with the given flags and with the position taken from a QPointF. */ + constexpr MapCoord(QPointF point, Flags flags) noexcept; + + /** Creates a MapCoord with the given flags and with the position taken from a QPointF. */ + constexpr MapCoord(QPointF point, Flag flag) noexcept; + + MapCoord(QPointF point, int flags) = delete; + + /** Creates a MapCoord from native map coordinates. */ + static constexpr MapCoord fromNative(qint32 x, qint32 y) noexcept; + + /** Creates a MapCoord from native map coordinates. */ + static constexpr MapCoord fromNative(qint32 x, qint32 y, Flags flags) noexcept; + + /** Creates a MapCoord from native map coordinates. */ + static constexpr MapCoord fromNative(qint32 x, qint32 y, Flag flag) noexcept; + + static MapCoord fromNative(qint32 x, qint32 y, int flags) = delete; + + /** It is illegal to call MapCoord::fromNative with qint64 arguments. + * + * Use fromNative64 instead. That method tests the values against the bounds of qint32. + */ + static MapCoord fromNative(qint64 x, qint64 y) = delete; + + /** Creates a MapCoord from native map coordinates. + * + * The coordinates are expected to be in the bounds of qint32. + * + * \todo Raise (and handle exceptions) when out of bounds. + */ + static MapCoord fromNative64(qint64 x, qint64 y); + + /** Creates a MapCoord from native map coordinates. + * + * This will apply the BoundsOffset() and throw a std::range_error if the + * adjusted coordinates are out of bounds for qint32. + * It does not modify BoundsOffset()! + */ + static MapCoord fromNative64withOffset(qint64 x, qint64 y); + + + /** Assignment operator */ + MapCoord& operator= (const MapCoord& other) = default; + + + /** Returns the coord's x position in millimeters on the map. */ + constexpr qreal x() const; + + /** Returns the coord's y position in millimeters on the map. */ + constexpr qreal y() const; + + /** Sets the coord's x position to a value in millimeters on the map. */ + inline void setX(qreal x); + + /** Sets the coord's y position to a value in millimeters on the map. */ + inline void setY(qreal y); + + + /** Returns the coord's x position in native map coords. */ + constexpr qint32 nativeX() const; + + /** Returns the coord's y position in native map coords. */ + constexpr qint32 nativeY() const; + + /** Sets the coord's x position to a value in native map coords. */ + inline void setNativeX(qint32 new_x); + + /** Sets the coord's y position to a value in native map coords. */ + inline void setNativeY(qint32 new_y); + + + /** Returns the coord's flags separately, merged into the lowest 8 bits of an int. */ + constexpr Flags::Int flags() const noexcept; + + /** Sets the flags as retrieved by flags(). */ + void setFlags(Flags::Int flags) noexcept; + + + /** + * Returns true iff the coordinates are within "regular" bounds. + */ + bool isRegular() const; + + + /** + * Returns the length of this coord, seen as a vector from the origin + * to the given coordinate, in millimeters on the map. + */ + qreal length() const; + + /** + * Returns the squared length of this coord, seen as a vector from the origin + * to the given coordinate, in millimeters on the map. Faster than length(). + */ + constexpr qreal lengthSquared() const; + + /** + * Returns the distance from this coord to the other + * in millimeters on the map. + */ + qreal distanceTo(const MapCoord& other) const; + + /** + * Returns the squared distance from this coord to the other + * in millimeters on the map. Faster than lengthTo(). + */ + constexpr qreal distanceSquaredTo(const MapCoord& other) const; + + + /** + * Returns if this coord's position is equal to that of the other one. + */ + constexpr bool isPositionEqualTo(const MapCoord& other) const; + + + /** Is this point the first point of a cubic bezier curve segment? */ + constexpr bool isCurveStart() const; + + /** Sets the curve start flag. */ + void setCurveStart(bool value); + + /** + * Is this the last point of a closed path, which is at the same position + * as the first point? This is set in addition to isHolePoint(). + */ + constexpr bool isClosePoint() const; + + /** Sets the close point flag. */ + void setClosePoint(bool value); + + + /** Is this the start of a hole for a line? */ + constexpr bool isHolePoint() const; + + /** Sets the hole point flag. */ + void setHolePoint(bool value); + + + /** Is this coordinate a special dash point? */ + constexpr bool isDashPoint() const; + + /** Sets the dash point flag. */ + void setDashPoint(bool value); + + + /** Is this coordinate a gap point? */ + constexpr bool isGapPoint() const; + + /** Sets the gap point flag. */ + void setGapPoint(bool value); + + + /** Additive inverse. */ + constexpr MapCoord operator-() const; + + /** Component-wise addition. */ + MapCoord& operator+= (const MapCoord& rhs_vector); + + /** Component-wise addition of this and a MapCoordF/QPointF. */ + MapCoord& operator+= (const QPointF& rhs_vector); + + /** Component-wise subtraction. */ + MapCoord& operator-= (const MapCoord& rhs_vector); + + /** Component-wise subtraction of this and a MapCoordF/QPointF. */ + MapCoord& operator-= (const QPointF& rhs_vector); + + /** Multiply with scalar factor. */ + MapCoord& operator*= (qreal factor); + + /** Divide by scalar factor. */ + MapCoord& operator/= (qreal scalar); + + + /** Converts the coord's position to a QPointF. */ + constexpr explicit operator QPointF() const; + + + /** + * Writes raw coordinates and flags to a string. + */ + QString toString() const; + + /** + * Constructs the MapCoord from the beginning of text, and moves the + * reference to behind the this coordinates data. + * + * This is the counterpiece to toString(). It will throw a + * std::invalid_argument if the (beginning of) text does not + * contain valid data. + * + * This constructor will initialize the boundsOffset() if neccessary. + * Otherwise it will apply the BoundsOffset() and throw a std::range_error + * if the adjusted coordinates are out of bounds for qint32. + */ + MapCoord(QStringRef& text); + + + /** Saves the MapCoord in xml format to the stream. */ + void save(QXmlStreamWriter& xml) const; + + /** Loads the MapCoord in xml format from the stream. + * + * This will initialize the boundsOffset() if neccessary. Otherwise it will + * apply the BoundsOffset() and throw a std::range_error if the adjusted + * coordinates are out of bounds for qint32. + */ + static MapCoord load(QXmlStreamReader& xml); + + /** Creates a MapCoord from map coordinates in millimeters, with offset handling. + * + * This will initialize the boundsOffset() if neccessary. Otherwise it will + * apply the BoundsOffset() and throw a std::range_error if the adjusted + * coordinates are out of bounds for qint32. + */ + static MapCoord load(qreal x, qreal y, MapCoord::Flags flags); + + static MapCoord load(qreal x, qreal y, int flags) = delete; + + /** Creates a MapCoord from map coordinates in millimeters, with offset handling. + * + * This will initialize the boundsOffset() if neccessary. Otherwise it will + * apply the BoundsOffset() and throw a std::range_error if the adjusted + * coordinates are out of bounds for qint32. + */ + static MapCoord load(QPointF p, MapCoord::Flags flags); + + static MapCoord load(QPointF p, int flags) = delete; + + + friend constexpr bool operator==(const MapCoord& lhs, const MapCoord& rhs); + friend constexpr MapCoord operator+(const MapCoord& lhs, const MapCoord& rhs); + friend constexpr MapCoord operator+(const MapCoord& lhs, const QPointF& rhs); + friend constexpr MapCoord operator-(const MapCoord& lhs, const MapCoord& rhs); + friend constexpr MapCoord operator-(const MapCoord& lhs, const QPointF& rhs); + friend constexpr MapCoord operator*(const MapCoord& lhs, qreal factor); + friend constexpr MapCoord operator*(qreal factor, const MapCoord& rhs); + friend constexpr MapCoord operator/(const MapCoord& lhs, qreal divisor); +}; + +/** Compare MapCoord for equality. */ +constexpr bool operator==(const MapCoord& lhs, const MapCoord& rhs); + +/** Compare MapCoord for inequality. */ +constexpr bool operator!=(const MapCoord& lhs, const MapCoord& rhs); + + +/** Component-wise addition of MapCoord. */ +constexpr MapCoord operator+(const MapCoord& lhs, const MapCoord& rhs); + +/** Component-wise addition of MapCoord and MapCoordF/QPointF. */ +constexpr MapCoord operator+(const MapCoord& lhs, const QPointF& rhs); + +/** Component-wise subtraction of MapCoord. */ +constexpr MapCoord operator-(const MapCoord& lhs, const MapCoord& rhs); + +/** Component-wise subtraction of MapCoord and MapCoordF/QPointF. */ +constexpr MapCoord operator-(const MapCoord& lhs, const QPointF& rhs); + +/** Multiply MapCoord with scalar factor. */ +constexpr MapCoord operator*(const MapCoord& lhs, qreal factor); + +/** Multiply MapCoord with scalar factor. */ +constexpr MapCoord operator*(qreal factor, const MapCoord& rhs); + +/** Divide MapCoord by scalar factor. */ +constexpr MapCoord operator/(const MapCoord& lhs, qreal divisor); + + + +/** + * Map coordinates stored as floating point numbers. + * + * The unit is millimeters on the map paper. + * + * This type was initially meant as intermediate format for rendering but + * is currently used in a wide range of functions related to editing. + * In contrast to MapCoord, MapCoordF does not store flags. + * + * The type is based on QPointF and provides additional methods for using it to + * represent 2D vectors. (Some of the methods do have counterparts in QLineF + * rather than QPointF.) Similar to QPointF, many operators return const values, + * although one might argue that it is no longer good practice in C++11. + */ +class MapCoordF : public QPointF +{ +public: + + /** Creates a MapCoordF with both values set to zero. */ + constexpr MapCoordF() noexcept; + + /** Creates a MapCoordF with the given position in map coordinates. */ + constexpr MapCoordF(qreal x, qreal y) noexcept; + + /** Creates a MapCoordF from a MapCoord, dropping its flags. */ + explicit constexpr MapCoordF(MapCoord coord) noexcept; + + /** Copy constructor. */ + constexpr MapCoordF(const MapCoordF&) noexcept = default; + + /** Copy constructor for QPointF prototypes. */ + explicit constexpr MapCoordF(const QPointF& point) noexcept; + + + /** Returns a vector with the given length and angle. */ + static const MapCoordF fromPolar(qreal length, qreal angle); + + + /** Assignment operator. */ + MapCoordF& operator= (const QPointF& point) noexcept; + + + /** + * Returns the length of the vector. + * + * The value returned the from this function is the MapCoords distance to + * the origin of the coordinate system. + */ + qreal length() const; + + /** + * Returns the square of the length of the vector. + * + * This is a slightly faster alternative to MapCoordF::length() which + * preserves comparability. + */ + constexpr qreal lengthSquared() const; + + /** + * Returns the distance of the coordinate to another coordinate. + */ + qreal distanceTo(const MapCoordF& to) const; + + /** + * Returns the square of the distance of this coordinate to another coordinate. + * + * This is a silghtly faster alternative to MapCoordF::distanceTo() which + * preserves comparability. + */ + constexpr qreal distanceSquaredTo(const MapCoordF& to) const; + + /** + * Changes the length of the vector. + * + * The MapCoordF is interpreted as a vector and adjusted to a vector of the + * same direction but having the given lenght. + * It does nothing if the vector is very close to (0, 0). + */ + void setLength(qreal new_length); + + /** + * Normalizes the length of the vector. + * + * The MapCoordF is interpreted as a vector and adjusted to a vector of the + * same direction but length 1 (i.e. unit vector). + */ + void normalize(); + + + /** + * Returns the angle of the vector relative to the vector (1, 0). + * + * The returned value is in radians, in the range range [-PI; +PI]. + * MapCoordF { 0, 1 }.getAngle() returns +PI/2, + * MapCoordF { 0, -1 }.getAngle() returns -PI/2. + */ + qreal angle() const; + + /** + * Rotates the vector. + * + * The argument is to be given in radians. + * Positive arguments result in a counter-clockwise rotation. + */ + void rotate(qreal angle); + + /** + * Returns a vector which is the result of rotating this vector. + * + * The argument is to be given in radians. + * Positive arguments result in a counter-clockwise rotation. + */ + const MapCoordF rotated(qreal angle) const; + + /** + * Returns a vector with the same length that is perpendicular to this vector. + * + * Note that in contrast to normalVector(), this function returns a + * perpendicular vector pointing to the right. + * + * \todo Replace with normalVector(), similar to QLineF API. + */ + constexpr const MapCoordF perpRight() const; + + /** + * Returns a vector with the same length that is perpendicular to this vector. + * + * \see QLineF::normalVector() + */ + constexpr const MapCoordF normalVector() const; + + + /** Additive inverse */ + constexpr const MapCoordF operator-() const; + + /** Component-wise addition */ + MapCoordF& operator+= (const MapCoordF& rhs); + + /** Component-wise subtraction */ + MapCoordF& operator-= (const MapCoordF& rhs); + + /** Multiply with a scalar */ + MapCoordF& operator*= (qreal factor); + + /** Divide by a scalar */ + MapCoordF& operator/= (qreal divisor); + + using QPointF::dotProduct; +}; + +constexpr const MapCoordF operator+(const MapCoordF& lhs, const MapCoordF& rhs); + +constexpr const MapCoordF operator-(const MapCoordF& lhs, const MapCoordF& rhs); + +constexpr const MapCoordF operator*(const MapCoordF& lhs, qreal factor); +constexpr const MapCoordF operator*(qreal factor, const MapCoordF& rhs); + +constexpr const MapCoordF operator/(const MapCoordF& lhs, qreal divisor); + + + +typedef std::vector MapCoordVector; +typedef std::vector MapCoordVectorF; + + + +// ### MapCoord inline code ### + +constexpr bool MapCoord::BoundsOffset::isZero() const +{ + return x==0 && y==0; +} + +inline +void MapCoord::BoundsOffset::reset(bool check_for_offset) +{ + this->check_for_offset = check_for_offset; + x = 0; + y = 0; +} + + +constexpr MapCoord::MapCoord() noexcept + : xp{ 0 } + , yp{ 0 } + , fp{ 0 } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(int x, int y) noexcept + : xp{ x*1000 } + , yp{ y*1000 } + , fp{ 0 } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(qint32 x, qint32 y, Flags flags) noexcept + : xp{ x } + , yp{ y } + , fp{ flags } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(qint32 x, qint32 y, Flag flag) noexcept + : xp{ x } + , yp{ y } + , fp{ flag } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(qreal x, qreal y) noexcept + : xp{ qRound(x*1000) } + , yp{ qRound(y*1000) } + , fp{ 0 } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(qreal x, qreal y, Flags flags) noexcept + : xp{ qRound(x*1000) } + , yp{ qRound(y*1000) } + , fp{ flags } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(qreal x, qreal y, Flag flag) noexcept + : xp{ qRound(x*1000) } + , yp{ qRound(y*1000) } + , fp{ flag } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(QPointF point) noexcept + : MapCoord { point.x(), point.y() } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(QPointF point, Flags flags) noexcept + : MapCoord { point.x(), point.y(), flags } +{ + // nothing else +} + +constexpr MapCoord::MapCoord(QPointF point, Flag flag) noexcept + : MapCoord { point.x(), point.y(), flag } +{ + // nothing else +} + +constexpr MapCoord MapCoord::fromNative(qint32 x, qint32 y) noexcept +{ + return MapCoord{ x, y, Flags() }; +} + +constexpr MapCoord MapCoord::fromNative(qint32 x, qint32 y, Flags flags) noexcept +{ + return MapCoord{ x, y, flags }; +} + +constexpr MapCoord MapCoord::fromNative(qint32 x, qint32 y, Flag flag) noexcept +{ + return MapCoord{ x, y, flag }; +} + +constexpr qreal MapCoord::x() const +{ + return nativeX() / 1000.0; +} + +constexpr qreal MapCoord::y() const +{ + return nativeY() / 1000.0; +} + +inline +void MapCoord::setX(qreal x) +{ + this->xp = qRound(x * 1000); +} + +inline +void MapCoord::setY(qreal y) +{ + this->yp = qRound(y * 1000); +} + +constexpr qint32 MapCoord::nativeX() const +{ + return xp; +} + +constexpr qint32 MapCoord::nativeY() const +{ + return yp; +} + +inline +void MapCoord::setNativeX(qint32 new_x) +{ + xp = new_x; +} + +inline +void MapCoord::setNativeY(qint32 new_y) +{ + yp = new_y; +} + +constexpr MapCoord::Flags::Int MapCoord::flags() const noexcept +{ + return fp; +} + +inline +void MapCoord::setFlags(Flags::Int flags) noexcept +{ + fp = Flags(flags); +} + + +inline +qreal MapCoord::length() const +{ + return sqrt(lengthSquared()); +} + +constexpr qreal MapCoord::lengthSquared() const +{ + return x()*x() + y()*y(); +} + +inline +qreal MapCoord::distanceTo(const MapCoord& other) const +{ + return sqrt(distanceSquaredTo(other)); +} + +constexpr qreal MapCoord::distanceSquaredTo(const MapCoord& other) const +{ + return (*this - other).lengthSquared(); +} + +constexpr bool MapCoord::isPositionEqualTo(const MapCoord& other) const +{ + return (xp == other.xp) && (yp == other.yp); +} + +constexpr bool MapCoord::isCurveStart() const { + return fp.testFlag(CurveStart); +} + +inline +void MapCoord::setCurveStart(bool value) +{ + if (fp.testFlag(CurveStart) != value) + fp ^= CurveStart; +} + +constexpr bool MapCoord::isClosePoint() const +{ + return fp.testFlag(ClosePoint); +} + +inline +void MapCoord::setClosePoint(bool value) +{ + if (fp.testFlag(ClosePoint) != value) + fp ^= ClosePoint; +} + +constexpr bool MapCoord::isHolePoint() const +{ + return fp.testFlag(HolePoint); +} + +inline +void MapCoord::setHolePoint(bool value) +{ + if (fp.testFlag(HolePoint) != value) + fp ^= HolePoint; +} + +constexpr bool MapCoord::isDashPoint() const +{ + return fp.testFlag(DashPoint); +} + +inline +void MapCoord::setDashPoint(bool value) +{ + if (fp.testFlag(DashPoint) != value) + fp ^= DashPoint; +} + +constexpr bool MapCoord::isGapPoint() const +{ + return fp.testFlag(GapPoint); +} + +inline +void MapCoord::setGapPoint(bool value) +{ + if (fp.testFlag(GapPoint) != value) + fp ^= GapPoint; +} + + +constexpr MapCoord MapCoord::operator-() const +{ + return MapCoord { -xp, -yp, fp }; +} + +inline +MapCoord& MapCoord::operator+=(const MapCoord& rhs_vector) +{ + xp += rhs_vector.xp; + yp += rhs_vector.yp; + return *this; +} + +inline +MapCoord& MapCoord::operator+=(const QPointF& rhs_vector) +{ + *this += MapCoord{ rhs_vector }; + return *this; +} + +inline +MapCoord& MapCoord::operator-=(const MapCoord& rhs_vector) +{ + xp -= rhs_vector.xp; + yp -= rhs_vector.yp; + return *this; +} + +inline +MapCoord& MapCoord::operator-=(const QPointF& rhs_vector) +{ + *this -= MapCoord{ rhs_vector }; + return *this; +} + +inline +MapCoord& MapCoord::operator*=(qreal factor) +{ + xp *= factor; + yp *= factor; + return *this; +} + +inline +MapCoord& MapCoord::operator/=(qreal divisor) +{ + xp /= divisor; + yp /= divisor; + return *this; +} + +constexpr MapCoord::operator QPointF() const +{ + return QPointF(x(), y()); +} + + + +constexpr bool operator==(const MapCoord& lhs, const MapCoord& rhs) +{ + return (lhs.xp == rhs.xp) && (lhs.yp == rhs.yp) && (lhs.fp == rhs.fp); +} + + +constexpr bool operator!=(const MapCoord& lhs, const MapCoord& rhs) +{ + return !(lhs == rhs); +} + + +constexpr MapCoord operator+(const MapCoord& lhs, const MapCoord& rhs) +{ + return MapCoord::fromNative(lhs.xp + rhs.xp, lhs.yp + rhs.yp); +} + +constexpr MapCoord operator+(const MapCoord& lhs, const QPointF& rhs) +{ + return lhs + MapCoord{ rhs }; +} + +constexpr MapCoord operator-(const MapCoord& lhs, const MapCoord& rhs) +{ + return MapCoord::fromNative(lhs.xp - rhs.xp, lhs.yp - rhs.yp); +} + +constexpr MapCoord operator-(const MapCoord& lhs, const QPointF& rhs) +{ + return lhs - MapCoord{ rhs }; +} + +constexpr MapCoord operator*(const MapCoord& lhs, double factor) +{ + return MapCoord::fromNative(qRound(lhs.xp * factor), qRound(lhs.yp * factor)); +} + +constexpr MapCoord operator*(double factor, const MapCoord& rhs) +{ + return MapCoord::fromNative(qRound(factor * rhs.xp), qRound(factor * rhs.yp)); +} + +constexpr MapCoord operator/(const MapCoord& lhs, double divisor) +{ + return MapCoord::fromNative(qRound(lhs.xp / divisor), qRound(lhs.yp / divisor)); +} + + + +// ### MapCoordF inline code ### + +constexpr MapCoordF::MapCoordF() noexcept + : QPointF {} +{ + // Nothing else +} + +constexpr MapCoordF::MapCoordF(qreal x, qreal y) noexcept + : QPointF { x, y } +{ + // Nothing else +} + +constexpr MapCoordF::MapCoordF(MapCoord coord) noexcept + : QPointF { coord.x(), coord.y() } +{ + // Nothing else +} + +constexpr MapCoordF::MapCoordF(const QPointF& point) noexcept + : QPointF { point } +{ + // Nothing else +} + +// static +inline +const MapCoordF MapCoordF::fromPolar(qreal length, qreal angle) +{ + return MapCoordF(cos(angle) * length, sin(angle) * length); +} + +inline +MapCoordF& MapCoordF::operator=(const QPointF& point) noexcept +{ + return static_cast(*static_cast(this) = point); +} + +inline +qreal MapCoordF::length() const +{ + return sqrt(lengthSquared()); +} + +constexpr qreal MapCoordF::lengthSquared() const +{ + return x()*x() + y()*y(); +} + +inline +qreal MapCoordF::distanceTo(const MapCoordF& to) const +{ + return sqrt(distanceSquaredTo(to)); +} + +constexpr qreal MapCoordF::distanceSquaredTo(const MapCoordF& to) const +{ + return (to - *this).lengthSquared(); +} + +inline +void MapCoordF::setLength(qreal new_length) +{ + auto length_squared = lengthSquared(); + if (length_squared > 1e-16) + { + auto factor = new_length / sqrt(length_squared); + rx() *= factor; + ry() *= factor; + } +} + +inline +void MapCoordF::normalize() +{ + setLength(1.0); +} + +inline +qreal MapCoordF::angle() const +{ + return atan2(y(), x()); +} + +inline +void MapCoordF::rotate(qreal angle) +{ + angle += this->angle(); + auto len = length(); + rx() = cos(angle) * len; + ry() = sin(angle) * len; +} + +inline +const MapCoordF MapCoordF::rotated(qreal angle) const +{ + return MapCoordF::fromPolar(length(), angle + this->angle()); +} + +constexpr const MapCoordF MapCoordF::perpRight() const +{ + return MapCoordF { -y(), x() }; +} + +constexpr const MapCoordF MapCoordF::normalVector() const +{ + return MapCoordF { y(), -x() }; +} + +constexpr const MapCoordF MapCoordF::operator-() const +{ + return static_cast(-static_cast(*this)); +} + +inline +MapCoordF& MapCoordF::operator+=(const MapCoordF& rhs) +{ + return static_cast(static_cast(*this) += rhs); +} + +inline +MapCoordF& MapCoordF::operator-=(const MapCoordF& rhs) +{ + return static_cast(static_cast(*this) -= rhs); +} + +inline +MapCoordF& MapCoordF::operator*=(qreal factor) +{ + return static_cast(static_cast(*this) *= factor); +} + +inline +MapCoordF& MapCoordF::operator/=(qreal divisor) +{ + return static_cast(static_cast(*this) /= divisor); +} + + + +constexpr const MapCoordF operator+(const MapCoordF& lhs, const MapCoordF& rhs) +{ + return static_cast(static_cast(lhs) + static_cast(rhs)); +} + +constexpr const MapCoordF operator-(const MapCoordF& lhs, const MapCoordF& rhs) +{ + return static_cast(static_cast(lhs) - static_cast(rhs)); +} + +constexpr const MapCoordF operator*(const MapCoordF& lhs, qreal factor) +{ + return static_cast(static_cast(lhs) * factor); +} + +constexpr const MapCoordF operator*(qreal factor, const MapCoordF& rhs) +{ + return static_cast(factor * static_cast(rhs)); +} + +constexpr const MapCoordF operator/(const MapCoordF& lhs, qreal divisor) +{ + return static_cast(static_cast(lhs) / divisor); +} + + +} // namespace OpenOrienteering + + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::MapCoord::Flags) + + +#endif diff --git a/src/core/map_grid.cpp b/src/core/map_grid.cpp new file mode 100644 index 0000000..2b21ed6 --- /dev/null +++ b/src/core/map_grid.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_grid.h" + +#include +#include +#include + +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_coord.h" +#include "util/util.h" +#include "util/xml_stream_util.h" + + +namespace OpenOrienteering { + +namespace { + +struct ProcessLine +{ + QPainter* painter; + void processLine(QPointF a, QPointF b); +}; + +void ProcessLine::processLine(QPointF a, QPointF b) +{ + painter->drawLine(a, b); +} + + +} // namespace + + +// ### MapGrid ### + +MapGrid::MapGrid() +{ + snapping_enabled = true; + color = qRgba(100, 100, 100, 128); + display = AllLines; + + alignment = MagneticNorth; + additional_rotation = 0; + + unit = MetersInTerrain; + horz_spacing = 500; + vert_spacing = 500; + horz_offset = 0; + vert_offset = 0; +} + +#ifndef NO_NATIVE_FILE_FORMAT + +const MapGrid& MapGrid::load(QIODevice* file, int version) +{ + Q_UNUSED(version); + file->read((char*)&snapping_enabled, sizeof(bool)); + file->read((char*)&color, sizeof(QRgb)); + int temp; + file->read((char*)&temp, sizeof(int)); + display = (DisplayMode)temp; + file->read((char*)&temp, sizeof(int)); + alignment = (Alignment)temp; + file->read((char*)&additional_rotation, sizeof(double)); + file->read((char*)&temp, sizeof(int)); + unit = (Unit)temp; + file->read((char*)&horz_spacing, sizeof(double)); + file->read((char*)&vert_spacing, sizeof(double)); + file->read((char*)&horz_offset, sizeof(double)); + file->read((char*)&vert_offset, sizeof(double)); + + return *this; +} + +#endif + +void MapGrid::save(QXmlStreamWriter& xml) const +{ + XmlElementWriter element{xml, QLatin1String("grid")}; + const auto name_format = qAlpha(color) == 0xff ? QColor::HexRgb : QColor::HexArgb; + element.writeAttribute(QLatin1String("color"), QColor::fromRgba(color).name(name_format)); + element.writeAttribute(QLatin1String("display"), display); + element.writeAttribute(QLatin1String("alignment"), alignment); + element.writeAttribute(QLatin1String("additional_rotation"), additional_rotation); + element.writeAttribute(QLatin1String("unit"), unit); + element.writeAttribute(QLatin1String("h_spacing"), horz_spacing); + element.writeAttribute(QLatin1String("v_spacing"), vert_spacing); + element.writeAttribute(QLatin1String("h_offset"), horz_offset); + element.writeAttribute(QLatin1String("v_offset"), vert_offset); + element.writeAttribute(QLatin1String("snapping_enabled"), snapping_enabled); +} + +const MapGrid& MapGrid::load(QXmlStreamReader& xml) +{ + Q_ASSERT(xml.name() == QLatin1String("grid")); + + XmlElementReader element(xml); + QXmlStreamAttributes attributes = xml.attributes(); + color = QColor(element.attribute(QLatin1String("color"))).rgba(); + display = MapGrid::DisplayMode(element.attribute(QLatin1String("display"))); + alignment = MapGrid::Alignment(element.attribute(QLatin1String("alignment"))); + additional_rotation = element.attribute(QLatin1String("additional_rotation")); + unit = MapGrid::Unit(element.attribute(QLatin1String("unit"))); + horz_spacing = element.attribute(QLatin1String("h_spacing")); + vert_spacing = element.attribute(QLatin1String("v_spacing")); + horz_offset = element.attribute(QLatin1String("h_offset")); + vert_offset = element.attribute(QLatin1String("v_offset")); + snapping_enabled = element.attribute(QLatin1String("snapping_enabled")); + + return *this; +} + +void MapGrid::draw(QPainter* painter, const QRectF& bounding_box, Map* map, qreal scale_adjustment) const +{ + double final_horz_spacing, final_vert_spacing; + double final_horz_offset, final_vert_offset; + double final_rotation; + calculateFinalParameters(final_horz_spacing, final_vert_spacing, final_horz_offset, final_vert_offset, final_rotation, map); + + QPen pen(color); + if (qIsNull(scale_adjustment)) + { + // zero-width cosmetic pen (effectively one pixel) + pen.setWidth(0); + pen.setCosmetic(true); + } + else + { + // 0.1 mm wide non-cosmetic pen + pen.setWidthF(qreal(0.1) / scale_adjustment); + } + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + painter->setOpacity(qAlpha(color) / 255.0); + + ProcessLine process_line; + process_line.painter = painter; + + if (display == AllLines) + Util::gridOperation(bounding_box, final_horz_spacing, final_vert_spacing, final_horz_offset, final_vert_offset, final_rotation, process_line); + else if (display == HorizontalLines) + Util::hatchingOperation(bounding_box, final_vert_spacing, final_vert_offset, final_rotation + M_PI / 2, process_line); + else // if (display == VeritcalLines) + Util::hatchingOperation(bounding_box, final_horz_spacing, final_horz_offset, final_rotation, process_line); +} + +void MapGrid::calculateFinalParameters(double& final_horz_spacing, double& final_vert_spacing, double& final_horz_offset, double& final_vert_offset, double& final_rotation, Map* map) const +{ + double factor = ((unit == MetersInTerrain) ? (1000.0 / map->getScaleDenominator()) : 1); + final_horz_spacing = factor * horz_spacing; + final_vert_spacing = factor * vert_spacing; + final_horz_offset = factor * horz_offset; + final_vert_offset = factor * vert_offset; + final_rotation = additional_rotation + M_PI / 2; + + const Georeferencing& georeferencing = map->getGeoreferencing(); + if (alignment == GridNorth) + { + final_rotation += georeferencing.getGrivation() * M_PI / 180; + + // Shift origin to projected coordinates origin + double prev_horz_offset = MapCoordF::dotProduct(MapCoordF(0, -1).rotated(final_rotation), MapCoordF(georeferencing.getMapRefPoint().x(), -1 * georeferencing.getMapRefPoint().y())); + double target_horz_offset = MapCoordF::dotProduct(MapCoordF(0, -1).rotated(additional_rotation + M_PI / 2), MapCoordF(georeferencing.getProjectedRefPoint().x(), georeferencing.getProjectedRefPoint().y())); + final_horz_offset -= factor * target_horz_offset - prev_horz_offset; + + double prev_vert_offset = MapCoordF::dotProduct(MapCoordF(1, 0).rotated(final_rotation), MapCoordF(georeferencing.getMapRefPoint().x(), -1 * georeferencing.getMapRefPoint().y())); + double target_vert_offset = MapCoordF::dotProduct(MapCoordF(1, 0).rotated(additional_rotation + M_PI / 2), MapCoordF(georeferencing.getProjectedRefPoint().x(), georeferencing.getProjectedRefPoint().y())); + final_vert_offset += factor * target_vert_offset - prev_vert_offset; + } + else if (alignment == TrueNorth) + final_rotation += georeferencing.getDeclination() * M_PI / 180; +} + +MapCoordF MapGrid::getClosestPointOnGrid(MapCoordF position, Map* map) const +{ + double final_horz_spacing, final_vert_spacing; + double final_horz_offset, final_vert_offset; + double final_rotation; + calculateFinalParameters(final_horz_spacing, final_vert_spacing, final_horz_offset, final_vert_offset, final_rotation, map); + + position.rotate(final_rotation - M_PI / 2); + return MapCoordF(qRound((position.x() - final_horz_offset) / final_horz_spacing) * final_horz_spacing + final_horz_offset, + qRound((position.y() - final_vert_offset) / final_vert_spacing) * final_vert_spacing + final_vert_offset).rotated(-1 * (final_rotation - M_PI / 2)); +} + + +bool MapGrid::hasAlpha() const +{ + return [](auto alpha) { + return alpha > 0 && alpha < 255; + }(qAlpha(color)); +} + + + +bool operator==(const MapGrid& lhs, const MapGrid& rhs) +{ + return + lhs.snapping_enabled == rhs.snapping_enabled && + lhs.color == rhs.color && + lhs.display == rhs.display && + lhs.alignment == rhs.alignment && + lhs.additional_rotation == rhs.additional_rotation && + lhs.unit == rhs.unit && + lhs.horz_spacing == rhs.horz_spacing && + lhs.vert_spacing == rhs.vert_spacing && + lhs.horz_offset == rhs.horz_offset && + lhs.vert_offset == rhs.vert_offset; +} + +bool operator!=(const MapGrid& lhs, const MapGrid& rhs) +{ + return !(lhs == rhs); +} + + +} // namespace diff --git a/src/core/map_grid.h b/src/core/map_grid.h new file mode 100644 index 0000000..02b17c8 --- /dev/null +++ b/src/core/map_grid.h @@ -0,0 +1,178 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014, 2015, 2017, 2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_GRID_H +#define OPENORIENTEERING_MAP_GRID_H + +#include + +class QIODevice; +class QPainter; +class QRectF; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class Map; +class MapCoordF; + + +/** + * Class for displaying a grid on a map. + * + * Each map has an instance of this class which can be retrieved with map->getGrid(). + * The grid's visibility is defined per MapView. + * + * Grid lines are thin. They are either drawn using a cosmetic pen (usually on + * screen) or with 0.1 mm (usually on paper). + */ +class MapGrid +{ +public: + /** Options for aligning the grid with different north concepts. */ + enum Alignment + { + MagneticNorth = 0, + GridNorth = 1, + TrueNorth = 2 + }; + + /** Different units for specifying the grid interval. */ + enum Unit + { + MillimetersOnMap = 0, + MetersInTerrain = 1 + }; + + /** Different display modes for map grids. */ + enum DisplayMode + { + AllLines = 0, + HorizontalLines = 1, + VerticalLines = 2 + }; + + /** Creates a new map grid with default settings. */ + MapGrid(); + + /** Loads the grid in the old "native" format from the given file. */ + const MapGrid& load(QIODevice* file, int version); + + /** Saves the grid in xml format to the given stream. */ + void save(QXmlStreamWriter& xml) const; + + /** Loads the grid in xml format from the given stream. */ + const MapGrid& load(QXmlStreamReader& xml); + + /** + * Draws the map grid. + * + * @param painter The QPainter used for drawing. + * @param bounding_box Bounding box of the area to draw the grid for, in + * map coordinates. + * @param map Map to draw the grid for. + * @param scale_adjustment If zero, uses a cosmetic pen (one pixel wide), + * otherwise this is the divisor used to create a 0.1 mm wide pen. + */ + void draw(QPainter* painter, const QRectF& bounding_box, Map* map, qreal scale_adjustment = 0) const; + void draw(QPainter* painter, const QRectF& bounding_box, Map* map, bool) const = delete; + + /** + * Calculates the "final" parameters with the following properties: + * - spacings and offsets are in millimeters on the map + * - rotation is relative to the vector (1, 0) and counterclockwise + */ + void calculateFinalParameters( + double& final_horz_spacing, double& final_vert_spacing, + double& final_horz_offset, double& final_vert_offset, + double& final_rotation, Map* map + ) const; + + /** Returns the grid point which is closest to the given position. */ + MapCoordF getClosestPointOnGrid(MapCoordF position, Map* map) const; + + // Getters / Setters + + inline bool isSnappingEnabled() const {return snapping_enabled;} + inline void setSnappingEnabled(bool enable) {snapping_enabled = enable;} + inline QRgb getColor() const {return color;} + inline void setColor(QRgb color) {this->color = color;} + + /** + * Returns true if the grid is not opaque. + */ + bool hasAlpha() const; + + inline DisplayMode getDisplayMode() const {return display;} + inline void setDisplayMode(DisplayMode mode) {display = mode;} + + inline Alignment getAlignment() const {return alignment;} + inline void setAlignment(Alignment alignment) {this->alignment = alignment;} + inline double getAdditionalRotation() const {return additional_rotation;} + inline void setAdditionalRotation(double rotation) {additional_rotation = rotation;} + + inline Unit getUnit() const {return unit;} + inline void setUnit(Unit unit) {this->unit = unit;} + inline double getHorizontalSpacing() const {return horz_spacing;} + inline void setHorizontalSpacing(double spacing) {horz_spacing = spacing;} + inline double getVerticalSpacing() const {return vert_spacing;} + inline void setVerticalSpacing(double spacing) {vert_spacing = spacing;} + inline double getHorizontalOffset() const {return horz_offset;} + inline void setHorizontalOffset(double offset) {horz_offset = offset;} + inline double getVerticalOffset() const {return vert_offset;} + inline void setVerticalOffset(double offset) {vert_offset = offset;} + +private: + bool snapping_enabled; + QRgb color; + DisplayMode display; + + Alignment alignment; + double additional_rotation; + + Unit unit; + double horz_spacing; + double vert_spacing; + double horz_offset; + double vert_offset; + + friend bool operator==(const MapGrid& lhs, const MapGrid& rhs); +}; + +/** + * Compares two map grid objects. + * + * @return true if the objects are equal, false otherwise + */ +bool operator==(const MapGrid& lhs, const MapGrid& rhs); + +/** + * Compares two map grid objects for inequality. + * + * @return true if the objects are not equal, false otherwise + */ +bool operator!=(const MapGrid& lhs, const MapGrid& rhs); + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/map_part.cpp b/src/core/map_part.cpp new file mode 100644 index 0000000..13e05cf --- /dev/null +++ b/src/core/map_part.cpp @@ -0,0 +1,376 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_part.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_coord.h" +#include "core/objects/object.h" +#include "core/symbols/symbol.h" +#include "undo/object_undo.h" +#include "util/util.h" +#include "util/xml_stream_util.h" + + +namespace literal +{ + const QLatin1String part("part"); + const QLatin1String name("name"); + const QLatin1String objects("objects"); + const QLatin1String object("object"); + const QLatin1String count("count"); +} + + + +namespace OpenOrienteering { + +MapPart::MapPart(const QString& name, Map* map) +: name(name) +, map(map) +{ + Q_ASSERT(map); + ; // nothing else +} + +MapPart::~MapPart() +{ + for (Object* object : objects) + delete object; +} + + +void MapPart::setName(const QString& new_name) +{ + name = new_name; + if (map) + emit map->mapPartChanged(map->findPartIndex(this), this); +} + + +#ifndef NO_NATIVE_FILE_FORMAT + +bool MapPart::load(QIODevice* file, int version, Map* map) +{ + loadString(file, name); + + int size; + file->read((char*)&size, sizeof(int)); + objects.resize(size, nullptr); + + for (Object*& object : objects) + { + int save_type; + file->read((char*)&save_type, sizeof(int)); + object = Object::getObjectForType(static_cast(save_type), nullptr); + if (!object) + return false; + object->load(file, version, map); + } + return true; +} + +#endif + +void MapPart::save(QXmlStreamWriter& xml) const +{ + XmlElementWriter part_element(xml, literal::part); + part_element.writeAttribute(literal::name, name); + { + XmlElementWriter objects_element(xml, literal::objects); + objects_element.writeAttribute(literal::count, objects.size()); + for (const Object* object : objects) + { + writeLineBreak(xml); + object->save(xml); + } + writeLineBreak(xml); + } +} + +MapPart* MapPart::load(QXmlStreamReader& xml, Map& map, SymbolDictionary& symbol_dict) +{ + Q_ASSERT(xml.name() == literal::part); + + XmlElementReader part_element(xml); + auto part = new MapPart(part_element.attribute(literal::name), &map); + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::objects) + { + XmlElementReader objects_element(xml); + + std::size_t num_objects = objects_element.attribute(literal::count); + if (num_objects > 0) + part->objects.reserve(qMin(num_objects, std::size_t(20000))); // 20000 is not a limit + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::object) + part->objects.push_back(Object::load(xml, &map, symbol_dict)); + else + xml.skipCurrentElement(); // unknown + } + } + else + xml.skipCurrentElement(); // unknown + } + + return part; +} + +int MapPart::findObjectIndex(const Object* object) const +{ + int size = objects.size(); + for (int i = size - 1; i >= 0; --i) + { + if (objects[i] == object) + return i; + } + Q_ASSERT(false); + return -1; +} + +void MapPart::setObject(Object* object, int pos, bool delete_old) +{ + map->removeRenderablesOfObject(objects[pos], true); + if (delete_old) + delete objects[pos]; + + objects[pos] = object; + object->setMap(map); + object->update(); + map->setObjectsDirty(); // TODO: remove from here, dirty state handling should be separate +} + +void MapPart::addObject(Object* object) +{ + addObject(object, objects.size()); +} + +void MapPart::addObject(Object* object, int pos) +{ + objects.insert(objects.begin() + pos, object); + object->setMap(map); + object->update(); + + if (objects.size() == 1 && map->getNumObjects() == 1) + map->updateAllMapWidgets(); +} + +void MapPart::deleteObject(int pos, bool remove_only) +{ + map->removeRenderablesOfObject(objects[pos], true); + if (remove_only) + objects[pos]->setMap(nullptr); + else + delete objects[pos]; + objects.erase(objects.begin() + pos); + + if (objects.empty() && map->getNumObjects() == 0) + map->updateAllMapWidgets(); +} + +bool MapPart::deleteObject(Object* object, bool remove_only) +{ + int size = objects.size(); + for (int i = size - 1; i >= 0; --i) + { + if (objects[i] == object) + { + deleteObject(i, remove_only); + return true; + } + } + return false; +} + +std::unique_ptr MapPart::importPart(const MapPart* other, const QHash& symbol_map, const QTransform& transform, bool select_new_objects) +{ + if (other->getNumObjects() == 0) + return {}; + + bool first_objects = map->getNumObjects() == 0; + auto undo_step = new DeleteObjectsUndoStep(map); + if (select_new_objects) + map->clearObjectSelection(false); + + objects.reserve(objects.size() + other->objects.size()); + for (const Object* object: other->objects) + { + Object* new_object = object->duplicate(); + if (symbol_map.contains(new_object->getSymbol())) + new_object->setSymbol(symbol_map.value(new_object->getSymbol()), true); + new_object->transform(transform); + + objects.push_back(new_object); + new_object->setMap(map); + new_object->update(); + + undo_step->addObject((int)objects.size() - 1); + if (select_new_objects) + map->addObjectToSelection(new_object, false); + } + + map->setObjectsDirty(); + if (select_new_objects) + { + map->emitSelectionChanged(); + map->emitSelectionEdited(); // TODO: is this necessary here? + // Not as long as observers listen to both... + } + if (first_objects) + map->updateAllMapWidgets(); + + return std::unique_ptr{undo_step}; +} + +void MapPart::findObjectsAt( + MapCoordF coord, + float tolerance, + bool treat_areas_as_paths, + bool extended_selection, + bool include_hidden_objects, + bool include_protected_objects, + SelectionInfoVector& out ) const +{ + for (Object* object : objects) + { + if (!include_hidden_objects && object->getSymbol()->isHidden()) + continue; + if (!include_protected_objects && object->getSymbol()->isProtected()) + continue; + + object->update(); + int selected_type = object->isPointOnObject(coord, tolerance, treat_areas_as_paths, extended_selection); + if (selected_type != (int)Symbol::NoSymbol) + out.emplace_back(selected_type, object); + } +} + +void MapPart::findObjectsAtBox( + MapCoordF corner1, + MapCoordF corner2, + bool include_hidden_objects, + bool include_protected_objects, + std::vector< Object* >& out ) const +{ + auto rect = QRectF(corner1, corner2).normalized(); + for (Object* object : objects) + { + if (!include_hidden_objects && object->getSymbol()->isHidden()) + continue; + if (!include_protected_objects && object->getSymbol()->isProtected()) + continue; + + object->update(); + if (rect.intersects(object->getExtent()) && object->intersectsBox(rect)) + out.push_back(object); + } +} + +int MapPart::countObjectsInRect(const QRectF& map_coord_rect, bool include_hidden_objects) const +{ + int count = 0; + for (const Object* object : objects) + { + if (object->getSymbol()->isHidden() && !include_hidden_objects) + continue; + object->update(); + if (object->getExtent().intersects(map_coord_rect)) + ++count; + } + return count; +} + +QRectF MapPart::calculateExtent(bool include_helper_symbols) const +{ + QRectF rect; + for (const auto object : objects) + { + if (!object->getSymbol()->isHidden() + && (include_helper_symbols || !object->getSymbol()->isHelperSymbol()) ) + { + object->update(); + rectIncludeSafe(rect, object->getExtent()); + } + } + return rect; +} + + + +bool MapPart::existsObject(const std::function& condition) const +{ + return std::any_of(begin(objects), end(objects), condition); +} + + +void MapPart::applyOnMatchingObjects(const std::function& operation, const std::function& condition) +{ + std::for_each(objects.rbegin(), objects.rend(), [&operation, &condition](auto object) { + if (condition(object)) + operation(object); + }); +} + + +void MapPart::applyOnMatchingObjects(const std::function& operation, const std::function& condition) +{ + for (auto i = objects.size(); i > 0; ) + { + --i; + Object* const object = objects[i]; + if (condition(object)) + operation(object, this, int(i)); + } +} + + +void MapPart::applyOnAllObjects(const std::function& operation) +{ + std::for_each(objects.rbegin(), objects.rend(), operation); +} + + +void MapPart::applyOnAllObjects(const std::function& operation) +{ + for (auto i = objects.size(); i > 0; ) + { + --i; + operation(objects[i], this, int(i)); + } +} + + +} // namespace OpenOrienteering diff --git a/src/core/map_part.h b/src/core/map_part.h new file mode 100644 index 0000000..ce31632 --- /dev/null +++ b/src/core/map_part.h @@ -0,0 +1,285 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_PART_H +#define OPENORIENTEERING_MAP_PART_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +class QIODevice; +class QTransform; +class QXmlStreamReader; +class QXmlStreamWriter; +// IWYU pragma: no_forward_declare QRectF + +namespace OpenOrienteering { + +class Map; +class MapCoordF; +class Object; +class Symbol; +using SymbolDictionary = QHash; // from symbol.h +class UndoStep; + + +using SelectionInfoVector = std::vector> ; + + +/** + * Represents a part of a map by owning a list of map objects. + * + * Dividing maps in parts is e.g. useful to have multiple mappers work on a map: + * every mapper can do the work in his/her part without getting into conflict + * with other parts. For display, the objects from all parts are merged. + * + * Another application is in course setting, where it is useful to have + * a map part for event-specific map objects and parts for course-specific + * map objects. Then a course can be printed by merging the event-specific part + * with the part for the course. + * + * Currently, only one map part can be used per map. + */ +class MapPart +{ +friend class OCAD8FileImport; +public: + /** + * Creates a new map part with the given name for a map. + */ + MapPart(const QString& name, Map* map); + + MapPart(const MapPart&) = delete; + + /** + * Destroys the map part. + */ + ~MapPart(); + + + MapPart& operator=(const MapPart&) = delete; + + + /** + * Loads the map part in the old "native" format from the given file. + */ + bool load(QIODevice* file, int version, Map* map); + + /** + * Saves the map part in xml format to the given stream. + */ + void save(QXmlStreamWriter& xml) const; + + /** + * Loads the map part in xml format from the given stream. + * + * Needs a dictionary to map symbol ids to symbol pointers. + */ + static MapPart* load(QXmlStreamReader& xml, Map& map, SymbolDictionary& symbol_dict); + + /** + * Returns the part's name. + */ + const QString& getName() const; + + /** + * Sets the part's name. + */ + void setName(const QString& new_name); + + + /** + * Returns the number of objects in the part. + */ + int getNumObjects() const; + + /** + * Returns the i-th object from the part. + */ + const Object* getObject(int i) const; + + /** + * Returns the i-th object from the part. + */ + Object* getObject(int i); + + /** + * Returns the index of the object. + * + * Loops over all objects in the part and looks for the given pointer. + * The object must be contained in this part, + * otherwise an assert is triggered (in debug builds), + * or -1 is returned (release builds). + */ + int findObjectIndex(const Object* object) const; + + /** + * Replaces the object at the given index with another. + * + * If delete_old is set, calls "delete old_object". + */ + void setObject(Object* object, int pos, bool delete_old); + + /** + * Adds the object as new object at the end. + */ + void addObject(Object* object); + + /** + * Adds the object as new object at the given index. + */ + void addObject(Object* object, int pos); + + /** + * Deleted the object from the given index. + * + * If remove_only is set, does not call "delete object". + * + * @todo Make a separate method "removeObject()", this is misleading! + */ + void deleteObject(int pos, bool remove_only); + + /** + * Deleted the object from the given index. + * + * If remove_only is set, does not call "delete object". + * Returns if the object was found in this part. + * + * @todo Make a separate method "removeObject()", this is misleading! + */ + bool deleteObject(Object* object, bool remove_only); + + + /** + * Imports the contents another part into this part. + * + * The other part can be from another map. + * Uses symbol_map to replace all symbols contained there. + * No replacement is done for symbols which are not in the symbol_map. + */ + std::unique_ptr importPart(const MapPart* other, const QHash& symbol_map, + const QTransform& transform, bool select_new_objects); + + + /** + * @see Map::findObjectsAt(). + */ + void findObjectsAt(MapCoordF coord, float tolerance, bool treat_areas_as_paths, + bool extended_selection, bool include_hidden_objects, + bool include_protected_objects, SelectionInfoVector& out) const; + + /** + * @see Map::findObjectsAtBox(). + */ + void findObjectsAtBox(MapCoordF corner1, MapCoordF corner2, + bool include_hidden_objects, bool include_protected_objects, + std::vector& out) const; + + /** + * @see Map::countObjectsInRect(). + */ + int countObjectsInRect(const QRectF& map_coord_rect, bool include_hidden_objects) const; + + /** + * Calculates and returns the bounding box of all objects in this map part. + */ + QRectF calculateExtent(bool include_helper_symbols) const; + + + /** + * Applies a condition on all objects (until the first match is found). + * + * @return True if there is an object matching the condition, false otherwise. + */ + bool existsObject(const std::function& condition) const; + + /** + * @copybrief Map::applyOnMatchingObjects() + * @copydetails Map::applyOnMatchingObjects() + */ + void applyOnMatchingObjects(const std::function& operation, const std::function& condition); + + /** + * @copybrief Map::applyOnMatchingObjects() + * @copydetails Map::applyOnMatchingObjects() + */ + void applyOnMatchingObjects(const std::function& operation, const std::function& condition); + + /** + * @copybrief Map::applyOnAllObjects() + * @copydetails Map::applyOnAllObjects() + */ + void applyOnAllObjects(const std::function& operation); + + /** + * @copybrief Map::applyOnAllObjects() + * @copydetails Map::applyOnAllObjects() + */ + void applyOnAllObjects(const std::function& operation); + + +private: + typedef std::vector ObjectList; + + QString name; + ObjectList objects; ///< @todo This could be a spatial representation optimized for quick access + Map* const map; +}; + + + +// ## MapPart inline and template code ### + +inline +const QString& MapPart::getName() const +{ + return name; +} + +inline +int MapPart::getNumObjects() const +{ + return int(objects.size()); +} + +inline +Object* MapPart::getObject(int i) +{ + return objects[std::size_t(i)]; +} + +inline +const Object* MapPart::getObject(int i) const +{ + return objects[std::size_t(i)]; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/map_printer.cpp b/src/core/map_printer.cpp new file mode 100644 index 0000000..d6c9528 --- /dev/null +++ b/src/core/map_printer.cpp @@ -0,0 +1,1373 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_printer.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include + + +#if defined(QT_PRINTSUPPORT_LIB) +# include +# include +# include +# if defined(Q_OS_WIN) +# include +# endif +#endif + +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_view.h" +#include "core/renderables/renderable.h" +#include "templates/template.h" +#include "util/xml_stream_util.h" + + +// ### A namespace which collects various string constants of type QLatin1String. ### + +namespace OpenOrienteering { + +namespace { + +namespace literal { + + static const QLatin1String scale("scale"); + static const QLatin1String resolution("resolution"); + static const QLatin1String templates_visible("templates_visible"); + static const QLatin1String grid_visible("grid_visible"); + static const QLatin1String simulate_overprinting("simulate_overprinting"); + static const QLatin1String mode("mode"); + static const QLatin1String vector("vector"); + static const QLatin1String raster("raster"); + static const QLatin1String separations("separations"); + static const QLatin1String color_mode("color_mode"); + static const QLatin1String default_color_mode("default"); + static const QLatin1String device_cmyk("DeviceCMYK"); + static const QLatin1String page_format("page_format"); + static const QLatin1String paper_size("paper_size"); + static const QLatin1String orientation("orientation"); + static const QLatin1String portrait("portrait"); + static const QLatin1String landscape("landscape"); + static const QLatin1String h_overlap("h_overlap"); + static const QLatin1String v_overlap("v_overlap"); + static const QLatin1String dimensions("dimensions"); + static const QLatin1String page_rect("page_rect"); + static const QLatin1String print_area("print_area"); + static const QLatin1String center_area("center_area"); + static const QLatin1String single_page("single_page"); + +} // namespace literal + + +} // namespace + + +// #### MapPrinterPageFormat ### + +MapPrinterPageFormat::MapPrinterPageFormat(QSizeF page_rect_size, qreal margin, qreal overlap) + : +#ifdef QT_PRINTSUPPORT_LIB + paper_size(QPrinter::Custom) +#else + paper_size(-1) +#endif + , orientation(Portrait) + , page_rect(QRectF(QPointF(margin, margin), page_rect_size)) + , paper_dimensions(page_rect.size() + 2.0* QSizeF(margin, margin)) + , h_overlap(overlap) + , v_overlap(overlap) +{ + // nothing +} + +#ifdef QT_PRINTSUPPORT_LIB + +MapPrinterPageFormat::MapPrinterPageFormat(const QPrinter& printer, qreal overlap) + : paper_size(printer.paperSize()) + , orientation((printer.orientation() == QPrinter::Portrait) ? MapPrinterPageFormat::Portrait : MapPrinterPageFormat::Landscape) + , page_rect(printer.pageRect(QPrinter::Millimeter)) + , paper_dimensions(printer.paperSize(QPrinter::Millimeter)) + , h_overlap(overlap) + , v_overlap(overlap) +{ + // nothing +} + +#endif + +MapPrinterPageFormat MapPrinterPageFormat::fromDefaultPrinter() +{ +#ifdef QT_PRINTSUPPORT_LIB + QPrinter default_printer; + return MapPrinterPageFormat(default_printer); +#else + return MapPrinterPageFormat(); +#endif +} + +bool operator==(const MapPrinterPageFormat& lhs, const MapPrinterPageFormat& rhs) +{ + return lhs.paper_size == rhs.paper_size && + lhs.orientation == rhs.orientation && + fabs(lhs.h_overlap - rhs.h_overlap) < 0.05 && + fabs(lhs.v_overlap - rhs.v_overlap) < 0.05 && + fabs(lhs.page_rect.top() - rhs.page_rect.top()) < 0.05 && + fabs(lhs.page_rect.left() - rhs.page_rect.left()) < 0.05 && + fabs(lhs.page_rect.right() - rhs.page_rect.right()) < 0.05 && + fabs(lhs.page_rect.bottom() - rhs.page_rect.bottom()) < 0.05 && + fabs(lhs.paper_dimensions.width() - rhs.paper_dimensions.width()) < 0.05 && + fabs(lhs.paper_dimensions.height() - rhs.paper_dimensions.height()) < 0.05; +} + +// ### MapPrinterOptions ### + +MapPrinterOptions::MapPrinterOptions(unsigned int scale, int resolution, MapPrinterMode mode) + : scale(scale), + resolution(resolution), + mode(mode), + color_mode(DefaultColorMode), + show_templates(false), + show_grid(false), + simulate_overprinting(false) +{ + // nothing +} + + + +// ### MapPrinterConfig ### + +MapPrinterConfig::MapPrinterConfig(const Map& map) + : printer_name(QString::fromLatin1("DEFAULT")), + print_area(map.calculateExtent()), + page_format(MapPrinterPageFormat::fromDefaultPrinter()), + options(map.getScaleDenominator()), + center_print_area(false), + single_page_print_area(false) +{ + if (print_area.isEmpty()) + print_area = page_format.page_rect; +} + +MapPrinterConfig::MapPrinterConfig(const Map& map, QXmlStreamReader& xml) + : printer_name(QString::fromLatin1("DEFAULT")), + print_area(0.0, 0.0, 100.0, 100.0), // Avoid expensive calculation before loading. + page_format(), + options(map.getScaleDenominator()), + center_print_area(false), + single_page_print_area(false) +{ + XmlElementReader printer_config_element(xml); + + options.scale = printer_config_element.attribute(literal::scale); + options.resolution = printer_config_element.attribute(literal::resolution); + options.show_templates = printer_config_element.attribute(literal::templates_visible); + options.show_grid = printer_config_element.attribute(literal::grid_visible); + options.simulate_overprinting = printer_config_element.attribute(literal::simulate_overprinting); + QStringRef mode = printer_config_element.attribute(literal::mode); + if (!mode.isEmpty()) + { + if (mode == literal::vector) + options.mode = MapPrinterOptions::Vector; + else if (mode == literal::raster) + options.mode = MapPrinterOptions::Raster; + else if (mode == literal::separations) + options.mode = MapPrinterOptions::Separations; + else + qDebug() << "Unsupported map printing mode:" << mode; + } + QStringRef color_mode = printer_config_element.attribute(literal::color_mode); + if (!color_mode.isEmpty()) + { + if (color_mode == literal::default_color_mode) + options.color_mode = MapPrinterOptions::DefaultColorMode; + else if (color_mode == literal::device_cmyk) + options.color_mode = MapPrinterOptions::DeviceCmyk; + else + qDebug() << "Unsupported map color mode:" << color_mode; + } + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::page_format) + { + XmlElementReader page_format_element(xml); + QString value; + +#ifdef QT_PRINTSUPPORT_LIB + value = page_format_element.attribute(literal::paper_size); + const QHash< int, const char* >& paper_size_names = MapPrinter::paperSizeNames(); + for (int i = 0; i < paper_size_names.count(); ++i) + { + if (value == QLatin1String(paper_size_names[i])) + page_format.paper_size = i; + } +#endif + + value = page_format_element.attribute(literal::orientation); + page_format.orientation = + (value == literal::portrait) ? MapPrinterPageFormat::Portrait : MapPrinterPageFormat::Landscape; + if (page_format_element.hasAttribute(literal::h_overlap)) + page_format.h_overlap = page_format_element.attribute(literal::h_overlap); + if (page_format_element.hasAttribute(literal::v_overlap)) + page_format.v_overlap = page_format_element.attribute(literal::v_overlap); + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::dimensions) + { + XmlElementReader(xml).read(page_format.paper_dimensions); + } + else if (xml.name() == literal::page_rect) + { + XmlElementReader(xml).read(page_format.page_rect); + } + else + xml.skipCurrentElement(); + } + } + else if (xml.name() == literal::print_area) + { + XmlElementReader print_area_element(xml); + print_area_element.read(print_area); + center_print_area = print_area_element.attribute(literal::center_area); + single_page_print_area= print_area_element.attribute(literal::single_page); + } + else + xml.skipCurrentElement(); + } + + // Sanity checks + if (options.scale <= 0) + options.scale = map.getScaleDenominator(); + if (options.resolution <= 0) + options.resolution = 600; +} + +void MapPrinterConfig::save(QXmlStreamWriter& xml, const QLatin1String& element_name) const +{ + XmlElementWriter printer_config_element(xml, element_name); + + printer_config_element.writeAttribute(literal::scale, options.scale); + printer_config_element.writeAttribute(literal::resolution, options.resolution); + printer_config_element.writeAttribute(literal::templates_visible, options.show_templates); + printer_config_element.writeAttribute(literal::grid_visible, options.show_grid); + printer_config_element.writeAttribute(literal::simulate_overprinting, options.simulate_overprinting); + switch (options.mode) + { + case MapPrinterOptions::Vector: + printer_config_element.writeAttribute(literal::mode, literal::vector); + break; + case MapPrinterOptions::Raster: + printer_config_element.writeAttribute(literal::mode, literal::raster); + break; + case MapPrinterOptions::Separations: + printer_config_element.writeAttribute(literal::mode, literal::separations); + break; + default: + // Do not fail on saving + qDebug() << "Unsupported map printing mode:" << options.mode; + } + + switch (options.color_mode) + { + case MapPrinterOptions::DefaultColorMode: + // No need to write an attribute for default mode. + // printer_config_element.writeAttribute(literal::color_mode, literal::default_color_mode); + break; + case MapPrinterOptions::DeviceCmyk: + printer_config_element.writeAttribute(literal::color_mode, literal::device_cmyk); + break; + default: + // Do not fail on saving + qDebug() << "Unsupported map color mode:" << options.color_mode; + } + + { + XmlElementWriter page_format_element(xml, literal::page_format); +#ifdef QT_PRINTSUPPORT_LIB + page_format_element.writeAttribute(literal::paper_size, + MapPrinter::paperSizeNames()[page_format.paper_size]); +#endif + page_format_element.writeAttribute(literal::orientation, + (page_format.orientation == MapPrinterPageFormat::Portrait) ? literal::portrait : literal::landscape ); + page_format_element.writeAttribute(literal::h_overlap, page_format.h_overlap, 2); + page_format_element.writeAttribute(literal::v_overlap, page_format.v_overlap, 2); + { + XmlElementWriter(xml, literal::dimensions).write(page_format.paper_dimensions, 3); + } + { + XmlElementWriter(xml, literal::page_rect).write(page_format.page_rect, 3); + } + } + { + XmlElementWriter print_area_element(xml, literal::print_area); + print_area_element.write(print_area, 3); + print_area_element.writeAttribute(literal::center_area, center_print_area); + print_area_element.writeAttribute(literal::single_page, single_page_print_area); + } +} + + + +#ifdef QT_PRINTSUPPORT_LIB + +// ### MapPrinter ### + +const QPrinterInfo* MapPrinter::pdfTarget() +{ + static QPrinterInfo pdf_target; // TODO: set name and features? + return &pdf_target; +} + +const QPrinterInfo* MapPrinter::imageTarget() +{ + static QPrinterInfo image_target; // TODO: set name and features? + return &image_target; +} + +const QHash< int, const char* >& MapPrinter::paperSizeNames() +{ + static QHash< int, const char* > names; + if (names.empty()) + { + names[QPrinter::A0] = "A0"; + names[QPrinter::A1] = "A1"; + names[QPrinter::A2] = "A2"; + names[QPrinter::A3] = "A3"; + names[QPrinter::A4] = "A4"; + names[QPrinter::A5] = "A5"; + names[QPrinter::A6] = "A6"; + names[QPrinter::A7] = "A7"; + names[QPrinter::A8] = "A8"; + names[QPrinter::A9] = "A9"; + names[QPrinter::B0] = "B0"; + names[QPrinter::B1] = "B1"; + names[QPrinter::B2] = "B2"; + names[QPrinter::B3] = "B3"; + names[QPrinter::B4] = "B4"; + names[QPrinter::B5] = "B5"; + names[QPrinter::B6] = "B6"; + names[QPrinter::B7] = "B7"; + names[QPrinter::B8] = "B8"; + names[QPrinter::B9] = "B9"; + names[QPrinter::B10] = "B10"; + names[QPrinter::C5E] = "C5E"; + names[QPrinter::DLE] = "DLE"; + names[QPrinter::Executive] = "Executive"; + names[QPrinter::Folio] = "Folio"; + names[QPrinter::Ledger] = "Ledger"; + names[QPrinter::Legal] = "Legal"; + names[QPrinter::Letter] = "Letter"; + names[QPrinter::Tabloid] = "Tabloid"; + names[QPrinter::Comm10E] = "US Common #10 Envelope"; + names[QPrinter::Custom] = "Custom"; + } + return names; +} + + +MapPrinter::MapPrinter(Map& map, const MapView* view, QObject* parent) +: QObject(parent), + MapPrinterConfig(map.printerConfig()), + map(map), + view(view), + target(nullptr) +{ + scale_adjustment = map.getScaleDenominator() / qreal(options.scale); + updatePaperDimensions(); + connect(&map.getGeoreferencing(), &Georeferencing::transformationChanged, this, &MapPrinter::mapScaleChanged); +} + +MapPrinter::~MapPrinter() +{ + // Do not remove. +} + +void MapPrinter::saveConfig() const +{ + map.setPrinterConfig(*this); +} + +// slot +void MapPrinter::setTarget(const QPrinterInfo* new_target) +{ + if (new_target != target) + { + const QPrinterInfo* old_target = target; + if (!new_target) + target = new_target; + else if (new_target == pdfTarget()) + target = new_target; + else if (new_target == imageTarget()) + target = new_target; + else + { + // We don't own this target, so we need to make a copy. + target_copy = *new_target; + target = &target_copy; + } + + if (old_target == imageTarget() || new_target == imageTarget()) + { + // No page margins. Will emit pageFormatChanged( ). + setCustomPaperSize(page_format.page_rect.size()); + } + else if (page_format.paper_size != QPrinter::Custom) + { + updatePaperDimensions(); + emit pageFormatChanged(page_format); + } + + emit targetChanged(target); + } +} + +std::unique_ptr MapPrinter::makePrinter() const +{ + std::unique_ptr printer; + if (!target) + { + printer.reset(new QPrinter(QPrinter::HighResolution)); + } + else if (isPrinter()) + { + printer.reset(new QPrinter(*target, QPrinter::HighResolution)); + } + else if (options.color_mode == MapPrinterOptions::DeviceCmyk) + { + printer.reset(new AdvancedPdfPrinter(*target, QPrinter::HighResolution)); + } + else + { + printer.reset(new QPrinter(*target, QPrinter::HighResolution)); + printer->setOutputFormat(QPrinter::PdfFormat); + } + + if (!printer->isValid()) + { + printer.reset(); + return printer; + } + + // Color can only be changed in (native) properties dialogs. This is the default. + printer->setColorMode(separationsModeSelected() ? QPrinter::GrayScale : QPrinter::Color); + if (printer->outputFormat() == QPrinter::NativeFormat) + { + PlatformPrinterProperties::restore(printer.get(), native_data); + } + + printer->setDocName(::OpenOrienteering::MapPrinter::tr("- Map -")); + printer->setFullPage(true); + if (page_format.paper_size == QPrinter::Custom) + { + printer->setPaperSize(page_format.paper_dimensions, QPrinter::Millimeter); + printer->setOrientation(QPrinter::Portrait); + } + else + { + printer->setPaperSize(QPrinter::PaperSize(page_format.paper_size)); + printer->setOrientation((page_format.orientation == MapPrinterPageFormat::Portrait) ? QPrinter::Portrait : QPrinter::Landscape); + } + printer->setResolution(options.resolution); + + if (page_format.paper_size == QPrinter::Custom || !isPrinter()) + { + printer->setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Millimeter); + } + + return printer; +} + +bool MapPrinter::isPrinter() const +{ + bool is_printer = target + && target != imageTarget() + && target != pdfTarget(); + return is_printer; +} + +// slot +void MapPrinter::setPrintArea(const QRectF& area) +{ + if (print_area != area && area.left() < area.right() && area.top() < area.bottom()) + { + print_area = area; + + if (target == imageTarget() && print_area.size() != page_format.paper_dimensions) + setCustomPaperSize(print_area.size() * scale_adjustment); + + updatePageBreaks(); + + emit printAreaChanged(print_area); + } +} + +// slot +void MapPrinter::setPaperSize(const int size) +{ + if (page_format.paper_size != size) + { + if (size == QPrinter::Custom) + { + setCustomPaperSize(page_format.paper_dimensions); + } + else + { + if ( page_format.paper_size == QPrinter::Custom && + page_format.paper_dimensions.width() > page_format.paper_dimensions.height() ) + { + // After QPrinter::Custom, determine orientation from actual dimensions. + page_format.orientation = MapPrinterPageFormat::Landscape; + } + page_format.paper_size = size; + updatePaperDimensions(); + } + emit pageFormatChanged(page_format); + } +} + +// slot +void MapPrinter::setCustomPaperSize(const QSizeF dimensions) +{ + if ((page_format.paper_size != QPrinter::Custom || + page_format.paper_dimensions != dimensions) && + ! dimensions.isEmpty()) + { + page_format.paper_size = QPrinter::Custom; + page_format.orientation = MapPrinterPageFormat::Portrait; + page_format.paper_dimensions = dimensions; + updatePaperDimensions(); + emit pageFormatChanged(page_format); + } +} + +// slot +void MapPrinter::setPageOrientation(const MapPrinterPageFormat::Orientation orientation) +{ + if (page_format.paper_size == QPrinter::Custom) + { + // do nothing + emit pageFormatChanged(page_format); + } + else if (page_format.orientation != orientation) + { + page_format.orientation = orientation; + page_format.paper_dimensions.transpose(); + updatePaperDimensions(); + updatePageBreaks(); + emit pageFormatChanged(page_format); + } +} + +// slot +void MapPrinter::setOverlap(qreal h_overlap, qreal v_overlap) +{ + if (page_format.h_overlap != h_overlap || page_format.v_overlap != v_overlap) + { + page_format.h_overlap = qMax(qreal(0), qMin(h_overlap, page_format.page_rect.width())); + page_format.v_overlap = qMax(qreal(0), qMin(v_overlap, page_format.page_rect.height())); + updatePageBreaks(); + emit pageFormatChanged(page_format); + } +} + +void MapPrinter::updatePaperDimensions() +{ + if (target == imageTarget() && page_format.paper_size == QPrinter::Custom) + { + // No margins, no need to query QPrinter. + page_format.page_rect = QRectF(QPointF(0.0, 0.0), page_format.paper_dimensions); + page_format.h_overlap = qMax(qreal(0), qMin(page_format.h_overlap, page_format.page_rect.width())); + page_format.v_overlap = qMax(qreal(0), qMin(page_format.v_overlap, page_format.page_rect.height())); + updatePageBreaks(); + return; + } + + QPrinter* printer = target ? new QPrinter(*target, QPrinter::HighResolution) + : new QPrinter(QPrinter::HighResolution); + if (!printer->isValid() || target == imageTarget() || target == pdfTarget()) + printer->setOutputFormat(QPrinter::PdfFormat); + + if (page_format.paper_size == QPrinter::Custom) + { + printer->setPaperSize(page_format.paper_dimensions, QPrinter::Millimeter); + page_format.orientation = (printer->orientation() == QPrinter::Portrait) ? MapPrinterPageFormat::Portrait : MapPrinterPageFormat::Landscape; + } + else + { + printer->setPaperSize(QPrinter::PaperSize(page_format.paper_size)); + printer->setOrientation((page_format.orientation == MapPrinterPageFormat::Portrait) ? QPrinter::Portrait : QPrinter::Landscape); + } + + page_format.page_rect = printer->paperRect(QPrinter::Millimeter); + page_format.paper_dimensions = page_format.page_rect.size(); + + if ( target != imageTarget() && target != pdfTarget() && + page_format.paper_size != QPrinter::Custom ) + { + qreal left, top, right, bottom; + printer->getPageMargins(&left, &top, &right, &bottom, QPrinter::Millimeter); + page_format.page_rect.adjust(left, top, -right, -bottom); + } + page_format.h_overlap = qMax(qreal(0), qMin(page_format.h_overlap, page_format.page_rect.width())); + page_format.v_overlap = qMax(qreal(0), qMin(page_format.v_overlap, page_format.page_rect.height())); + + delete printer; + updatePageBreaks(); +} + +// slot +void MapPrinter::setResolution(int dpi) +{ + if (dpi > 0 && options.resolution != dpi) + { + options.resolution = dpi; + emit optionsChanged(options); + } +} + +// slot +void MapPrinter::setScale(const unsigned int value) +{ + if (options.scale != value) + { + Q_ASSERT(value > 0); + options.scale = value; + scale_adjustment = map.getScaleDenominator() / qreal(value); + updatePageBreaks(); + emit optionsChanged(options); + } +} + +// slot +void MapPrinter::setMode(const MapPrinterOptions::MapPrinterMode mode) +{ + if (options.mode != mode) + { + options.mode = mode; + emit optionsChanged(options); + } +} + +// slot +void MapPrinter::setPrintTemplates(const bool visible) +{ + if (options.show_templates != visible) + { + options.show_templates = visible; + emit optionsChanged(options); + } +} + +// slot +void MapPrinter::setPrintGrid(const bool visible) +{ + if (options.show_grid != visible) + { + options.show_grid = visible; + emit optionsChanged(options); + } +} + +// slot +void MapPrinter::setSimulateOverprinting(bool enabled) +{ + if (enabled && !map.hasSpotColors()) + { + options.simulate_overprinting = false; + emit optionsChanged(options); + } + else if (options.simulate_overprinting != enabled) + { + options.simulate_overprinting = enabled; + emit optionsChanged(options); + } +} + +void MapPrinter::setColorMode(MapPrinterOptions::ColorMode color_mode) +{ + if (options.color_mode != color_mode) + { + options.color_mode = color_mode; + emit optionsChanged(options); + } +} + +bool MapPrinter::isOutputEmpty() const +{ + return ( + (map.getNumObjects() == 0 || (view && !view->effectiveMapVisibility().visible)) && + (!options.show_templates || map.getNumTemplates() == 0) && + !options.show_grid + ); +} + + +bool MapPrinter::engineMayRasterize() const +{ +#ifdef Q_OS_WIN + /* + * For Windows, non-opaque drawing to QWin32PrintEngine will trigger internal + * rasterization in Qt (implemented in QAlphaPaintEngine), in order to do care + * for the printing system's incapability to handle alpha. However, this is not + * desired in our map printing where raster output is an explicit option. + * + * QAlphaPaintEngines rasterizes + * - with the device's logical resolution but at least 300 dpi, + * cf. QAlphaPaintEnginePrivate::drawAlphaImage(const QRectF &rect) + * - in tiles of 2048x2048 pixels, + * cf. QAlphaPaintEnginePrivate::drawAlphaImage(const QRectF &rect) + * - when the painter is not opaque, + * cf. QAlphaPaintEngine::updateState(const QPaintEngineState &state) + * - when the pen is not opaque, + * cf. QAlphaPaintEngine::updateState(const QPaintEngineState &state) + * - when the brush is not opaque, + * cf. QAlphaPaintEngine::updateState(const QPaintEngineState &state) + */ + return !rasterModeSelected() + && target != pdfTarget() + && target != imageTarget(); +#else + /* + * For macOS (Cocoa), QMacPrintEngine does not rasterize. + * + * For Linux (CUPS), QCupsPrintEngine is based on QPdfEngine + * (via QPdfPrintEngine) which does not rasterize. (However, the print + * queue may rasterize, even when *spooling* to a PDF file. The vector + * output is preserved when redirecting the output to a file in + * Print... > Preview... > Print > Output file [...].) + */ + return false; +#endif +} + +bool MapPrinter::engineWillRasterize() const +{ +#ifdef Q_OS_WIN + if (!engineMayRasterize()) + return false; + + if (!view) + return map.hasAlpha(); + + const auto visibility = view->effectiveMapVisibility(); + return visibility.hasAlpha() + || (visibility.visible && map.hasAlpha()); +#else + Q_ASSERT(!engineMayRasterize()); + return false; +#endif +} + +bool MapPrinter::hasAlpha(const Template* temp) const +{ + if (temp->getTemplateState() != Template::Loaded) + return false; + + if (!view) + return temp->hasAlpha(); + + const auto visibility = view->getTemplateVisibility(temp); + return visibility.hasAlpha() + || (visibility.visible && temp->hasAlpha()); +} + + +void MapPrinter::updatePageBreaks() +{ + Q_ASSERT(print_area.left() <= print_area.right()); + Q_ASSERT(print_area.top() <= print_area.bottom()); + + // This whole implementation needs to deal with FP precision issues + + h_page_pos.clear(); + qreal h_pos = print_area.left(); + h_page_pos.push_back(h_pos); + const qreal h_overlap = page_format.h_overlap / scale_adjustment; + const qreal page_width = page_format.page_rect.width() / scale_adjustment - h_overlap; + + const qreal right_bound = print_area.right() - h_overlap - 0.05; + if (page_width >= 0.01) + { + for (h_pos += page_width; h_pos < right_bound; h_pos += page_width) + h_page_pos.push_back(h_pos); + + // Center the print area on the pages total area. + // Don't pre-calculate this offset to avoid FP precision problems + const qreal h_offset = 0.5 * (h_pos + h_overlap - print_area.right()); + for (auto& pos : h_page_pos) + pos -= h_offset; + } + + v_page_pos.clear(); + qreal v_pos = print_area.top(); + v_page_pos.push_back(v_pos); + const qreal v_overlap = page_format.v_overlap / scale_adjustment; + const qreal page_height = page_format.page_rect.height() / scale_adjustment - v_overlap; + const qreal bottom_bound = print_area.bottom() - v_overlap - 0.05; + if (page_height >= 0.01) + { + for (v_pos += page_height; v_pos < bottom_bound; v_pos += page_height) + v_page_pos.push_back(v_pos); + + // Don't pre-calculate offset to avoid FP precision problems + const qreal v_offset = 0.5 * (v_pos + v_overlap - print_area.bottom()); + for (auto& pos : v_page_pos) + pos -= v_offset; + } +} + +void MapPrinter::mapScaleChanged() +{ + auto value = qreal(map.getScaleDenominator()) / options.scale; + if (!qFuzzyCompare(scale_adjustment, value)) + { + scale_adjustment = value; + updatePageBreaks(); + emit optionsChanged(options); + } +} + +void MapPrinter::takePrinterSettings(const QPrinter* printer) +{ + if (!printer) return; + + MapPrinterPageFormat f(*printer); + if (target == pdfTarget() || target == imageTarget()) + { + f.page_rect = QRectF(QPointF(0.0, 0.0), f.paper_dimensions); + } + + if (f != page_format) + { + page_format.paper_size = f.paper_size; + page_format.orientation = f.orientation; + page_format.paper_dimensions = f.paper_dimensions; + page_format.page_rect = f.page_rect; + updatePageBreaks(); + emit pageFormatChanged(page_format); + } + + setResolution(printer->resolution()); + + if (printer->outputFormat() == QPrinter::NativeFormat) + { + PlatformPrinterProperties::save(printer, native_data); + } +} + + +void MapPrinter::drawPage(QPainter* device_painter, const QRectF& page_extent, QImage* page_buffer) const +{ + // Logical units per mm + const qreal units_per_mm = options.resolution / 25.4; + + const auto saved_hints = device_painter->renderHints(); + const auto render_hints = saved_hints + | QPainter::Antialiasing + | QPainter::SmoothPixmapTransform; + + // Determine transformation and clipping for page extent and region + const auto page_extent_transform = [this, units_per_mm, page_extent]() { + // Translate for top left page margin + auto transform = QTransform::fromScale(units_per_mm, units_per_mm); + transform.translate(page_format.page_rect.left(), page_format.page_rect.top()); + // Convert native map scale to print scale + transform.scale(scale_adjustment, scale_adjustment); + // Translate and clip for margins and print area + transform.translate(-page_extent.left(), -page_extent.top()); + return transform; + }(); + + const auto page_region_used = page_extent.intersected(print_area); + + + /* + * Analyse need for page buffer + * + * Painting with opacity is not supported on some print devices. + * This can only be solved by merging raster images before sending them to + * the printer. For the map itself, this may result in loss of sharpness + * and increase in data volume. + * + * In vector mode, a page buffer (raster image) is used to collect + * background templates and even lower foreground templates until only + * opaque templates are left in the foreground. Map, grid, and the remaining + * foreground templates are drawn on top of it. + * + * In raster mode, all map features are drawn to in regular order to + * temporary images first. + * + * When the target is an image, use the temporary image to enforce the given + * resolution. + */ + const bool use_buffer_for_map = rasterModeSelected() || target == imageTarget() || engineWillRasterize(); + bool use_page_buffer = use_buffer_for_map; + + auto first_front_template = map.getFirstFrontTemplate(); + if (options.show_templates && engineMayRasterize()) + { + // When we don't use a buffer for the map, i.e. when we draw in vector mode, + // we may need to put non-opaque foreground templates to the background + // in order to avoid rasterization + if (!use_buffer_for_map) + { + first_front_template = map.getNumTemplates(); + while (first_front_template > map.getFirstFrontTemplate() + && !hasAlpha(map.getTemplate(first_front_template-1))) + { + --first_front_template; + } + } + + for (int i = 0; i < first_front_template && !use_page_buffer; ++i) + { + use_page_buffer = hasAlpha(map.getTemplate(i)); + } + } + + /* + * Use a local "page painter", redirected to a local buffer if needed. + */ + auto* page_painter = device_painter; + QImage local_page_buffer; + QPainter local_page_painter; + if (use_page_buffer && !page_buffer) + { + int w = qCeil(page_format.paper_dimensions.width() * units_per_mm); + int h = qCeil(page_format.paper_dimensions.height() * units_per_mm); +#if defined (Q_OS_MACOS) + if (device_painter->device()->physicalDpiX() == 0) + { + // Possible Qt bug, since according to QPaintDevice documentation, + // "if the physicalDpiX() doesn't equal the logicalDpiX(), + // the corresponding QPaintEngine must handle the resolution mapping" + // which doesn't seem to happen here. + qreal corr = device_painter->device()->logicalDpiX() / 72.0; + w = qCeil(page_format.paper_dimensions.width() * units_per_mm * corr); + h = qCeil(page_format.paper_dimensions.height() * units_per_mm * corr); + } +#endif + + local_page_buffer = QImage(w, h, QImage::Format_RGB32); + if (local_page_buffer.isNull()) + { + // Allocation failed + device_painter->end(); // Signal error + return; + } + local_page_buffer.fill(QColor(Qt::white)); + local_page_painter.begin(&local_page_buffer); + + page_buffer = &local_page_buffer; + page_painter = &local_page_painter; + } + + /* + * Draw templates in the background + */ + if (options.show_templates) + { + // This is the first output, so it can be drawn directly to page_painter. + page_painter->save(); + + page_painter->setRenderHints(render_hints); + page_painter->setTransform(page_extent_transform, /*combine*/ true); + page_painter->setClipRect(page_region_used, Qt::ReplaceClip); + + map.drawTemplates(page_painter, page_region_used, 0, first_front_template - 1, view, false); + + page_painter->restore(); + } + + if (local_page_painter.isActive() && !use_buffer_for_map) + { + // Flush the buffer, reset painter + local_page_painter.end(); + + device_painter->setRenderHint(QPainter::SmoothPixmapTransform, false); + device_painter->drawImage(0, 0, local_page_buffer); + + page_painter = device_painter; + } + + QImage local_buffer; + + /* + * Draw the map + */ + if (!view || view->effectiveMapVisibility().visible) + { + QPainter* map_painter = page_painter; + + QPainter map_buffer_painter; + if (use_buffer_for_map) + { + // Draw map into a temporary buffer first which is printed with the map's opacity later. + // This prevents artifacts with overlapping objects. + local_buffer = QImage(page_buffer->size(), QImage::Format_ARGB32_Premultiplied); + if (local_buffer.isNull()) + { + // Allocation failed + device_painter->end(); // Signal error + return; + } + local_buffer.fill(QColor(Qt::transparent)); + map_buffer_painter.begin(&local_buffer); + map_painter = &map_buffer_painter; + } + + page_painter->save(); // Modified via map_painter, or when drawing the map_buffer + + map_painter->setRenderHints(render_hints); + map_painter->setTransform(page_extent_transform, /*combine*/ true); + map_painter->setClipRect(page_region_used, Qt::ReplaceClip); + + RenderConfig config = { map, page_region_used, units_per_mm * scale_adjustment, RenderConfig::NoOptions, 1.0 }; + + if (rasterModeSelected() && options.simulate_overprinting) + { + map.drawOverprintingSimulation(map_painter, config); + } + else + { + if (view && !map_buffer_painter.isActive()) + config.opacity = view->effectiveMapVisibility().opacity; + + map.draw(map_painter, config); + } + + if (map_buffer_painter.isActive()) + { + // Flush the buffer + map_buffer_painter.end(); + + // Draw buffer with map opacity + if (view) + page_painter->setOpacity(view->effectiveMapVisibility().opacity); + page_painter->setRenderHint(QPainter::SmoothPixmapTransform, false); + page_painter->drawImage(0, 0, local_buffer); + } + + page_painter->restore(); + } + + /* + * Draw the grid + */ + if (options.show_grid) + { + page_painter->save(); + + page_painter->setRenderHints(render_hints); + page_painter->setTransform(page_extent_transform, /*combine*/ true); + page_painter->setClipRect(page_region_used, Qt::ReplaceClip); + + const auto& map_grid = map.getGrid(); + if (map_grid.hasAlpha() && !use_buffer_for_map && engineMayRasterize()) + { + const auto rgba = map_grid.getColor(); + const auto alpha = qAlpha(rgba); + const auto opaque = [alpha](auto component) { + return 255 - alpha * (255 - component) / 255; + }; + auto grid_copy = map_grid; + grid_copy.setColor(qRgb(opaque(qRed(rgba)), opaque(qGreen(rgba)), opaque(qBlue(rgba)))); + grid_copy.draw(page_painter, print_area, &map, scale_adjustment); // Maybe replace by page_region_used? + } + else + { + map_grid.draw(page_painter, print_area, &map, scale_adjustment); // Maybe replace by page_region_used? + } + + page_painter->restore(); + } + + /* + * Draw the foreground templates + */ + if (options.show_templates) + { + QPainter* painter = page_painter; + + QPainter local_buffer_painter; + if (!vectorModeSelected() && use_buffer_for_map) + { + if (local_buffer.isNull()) + { + local_buffer = QImage(page_buffer->size(), QImage::Format_ARGB32_Premultiplied); + } + if (local_buffer.isNull()) + { + // Allocation failed + device_painter->end(); // Signal error + return; + } + local_buffer.fill(QColor(Qt::transparent)); + local_buffer_painter.begin(&local_buffer); + painter = &local_buffer_painter; + } + + page_painter->save(); // Modified via painter, or when drawing the local_buffer + + painter->setRenderHints(render_hints); + painter->setTransform(page_extent_transform, /*combine*/ true); + painter->setClipRect(page_region_used, Qt::ReplaceClip); + + map.drawTemplates(painter, page_region_used, first_front_template, map.getNumTemplates() - 1, view, false); + + if (local_buffer_painter.isActive()) + { + // Flush the buffer + local_buffer_painter.end(); + + // Draw buffer with map opacity + page_painter->setRenderHint(QPainter::SmoothPixmapTransform, false); + page_painter->drawImage(0, 0, local_buffer); + } + + page_painter->restore(); + } + + // Release the second buffer's memory first: The following drawImage() + // needs memory to allocate a large QPixmap with QWin32PrintEngine. + local_buffer = {}; + + /* + * Cleanup: If a temporary buffer has been used, paint it on the device painter + */ + if (local_page_painter.isActive()) + { + local_page_painter.end(); + + device_painter->setRenderHint(QPainter::SmoothPixmapTransform, false); + device_painter->drawImage(0, 0, local_page_buffer); + } + + device_painter->setRenderHints(saved_hints); +} + +void MapPrinter::drawSeparationPages(QPrinter* printer, QPainter* device_painter, const QRectF& page_extent) const +{ + Q_ASSERT(printer->colorMode() == QPrinter::GrayScale); + + device_painter->save(); + + device_painter->setRenderHint(QPainter::Antialiasing); + + // Translate for top left page margin + qreal scale = options.resolution / 25.4; // Dots per mm + device_painter->scale(scale, scale); + device_painter->translate(page_format.page_rect.left(), page_format.page_rect.top()); + + // Convert native map scale to print scale + if (scale_adjustment != 1.0) + { + scale *= scale_adjustment; + device_painter->scale(scale_adjustment, scale_adjustment); + } + + // Translate and clip for margins and print area + device_painter->translate(-page_extent.left(), -page_extent.top()); + device_painter->setClipRect(page_extent.intersected(print_area), Qt::ReplaceClip); + + bool need_new_page = false; + for (int i = map.getNumColors() - 1; i >= 0; --i) + { + const MapColor* color = map.getColor(i); + if (color->getSpotColorMethod() == MapColor::SpotColor) + { + if (need_new_page) + { + printer->newPage(); + } + + RenderConfig config = { map, page_extent, scale, RenderConfig::NoOptions, 1.0 }; + map.drawColorSeparation(device_painter, config, color); + need_new_page = true; + } + } + + device_painter->restore(); +} + +bool MapPrinter::printMap(QPrinter* printer) +{ + // Printer settings may have been changed by preview or application. + // We need to use them for printing. + printer->setFullPage(true); + takePrinterSettings(printer); + + QSizeF extent_size = page_format.page_rect.size() / scale_adjustment; + QPainter painter(printer); + +#if defined(Q_OS_WIN) + // Workaround for Wine + if (printer->resolution() == 0) + { + return false; + } + + if (printer->paintEngine()->type() == QPaintEngine::Windows) + { + /* QWin32PrintEngine will (have to) do rounding when passing coordinates + * to GDI, using the device's reported logical resolution. + * We establish an MM_ISOTROPIC transformation from a higher resolution + * to avoid the loss of precision due to this rounding. + * This transformation must only be used when we reliably avoid to + * trigger rasterization in Qt (cf. engineMayRasterize etc.) + */ + + QWin32PrintEngine* engine = static_cast(printer->printEngine()); + HDC dc = engine->getDC(); + + // The high resolution in units per millimeter + const int hires_ppmm = 1000; + + // The paper dimensions in high resolution units + const int hires_width = qRound(page_format.page_rect.width() * hires_ppmm); + const int hires_height = qRound(page_format.page_rect.height() * hires_ppmm); + + // The physical paper dimensions in device units + const int phys_width = GetDeviceCaps(dc, PHYSICALWIDTH); + const int phys_height = GetDeviceCaps(dc, PHYSICALHEIGHT); + + // The physical printing offset in device units + const int phys_off_x = GetDeviceCaps(dc, PHYSICALOFFSETX); + const int phys_off_y = GetDeviceCaps(dc, PHYSICALOFFSETY); + // (Needed to work around an unexpected offset, maybe related to QTBUG-5363) + + if (phys_width > 0) + { + // Establish the transformation + SetMapMode (dc, MM_ISOTROPIC); + SetWindowExtEx(dc, hires_width, hires_height, nullptr); + SetViewportExtEx(dc, phys_width, phys_height, nullptr); + SetViewportOrgEx(dc, -phys_off_x, -phys_off_y, nullptr); + const auto hires_scale = static_cast(hires_width) / phys_width; + painter.scale(hires_scale, hires_scale); + } + } + else if (printer->paintEngine()->type() == QPaintEngine::Picture) + { + // Preview: work around for offset, maybe related to QTBUG-5363 + painter.translate( + -page_format.page_rect.left() * options.resolution / 25.4, + -page_format.page_rect.top() * options.resolution / 25.4 ); + } +#endif + + cancel_print_map = false; + int step = 0; + auto num_steps = v_page_pos.size() * h_page_pos.size(); + const QString message_template( (options.mode == MapPrinterOptions::Separations) ? + ::OpenOrienteering::MapPrinter::tr("Processing separations of page %1...") : + ::OpenOrienteering::MapPrinter::tr("Processing page %1...") ); + auto message = message_template.arg(1); + emit printProgress(0, message); + + bool need_new_page = false; + for (auto vpos : v_page_pos) + { + if (!painter.isActive()) + { + break; + } + + for (auto hpos : h_page_pos) + { + if (!painter.isActive()) + { + break; + } + + ++step; + auto progress = qMin(99, qMax(1, int((100 * static_cast(step) - 50) / num_steps))); + emit printProgress(progress, message_template.arg(step)); + + if (cancel_print_map) /* during printProgress handling */ + { + painter.end(); + break; + } + + if (need_new_page) + { + printer->newPage(); + } + + QRectF page_extent = QRectF(QPointF(hpos, vpos), extent_size); + if (separationsModeSelected()) + { + drawSeparationPages(printer, &painter, page_extent); + } + else + { + drawPage(&painter, page_extent); + } + + need_new_page = true; + } + } + + if (cancel_print_map) + { + emit printProgress(100, ::OpenOrienteering::MapPrinter::tr("Canceled")); + } + else if (!painter.isActive()) + { + emit printProgress(100, ::OpenOrienteering::MapPrinter::tr("Error")); + return false; + } + else + { + emit printProgress(100, ::OpenOrienteering::MapPrinter::tr("Finished")); + } + return true; +} + +void MapPrinter::cancelPrintMap() +{ + cancel_print_map = true; +} + +#endif // QT_PRINTSUPPORT_LIB + + +} // namespace OpenOrienteering diff --git a/src/core/map_printer.h b/src/core/map_printer.h new file mode 100644 index 0000000..0f02022 --- /dev/null +++ b/src/core/map_printer.h @@ -0,0 +1,589 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_PRINTER_H +#define OPENORIENTEERING_MAP_PRINTER_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef QT_PRINTSUPPORT_LIB +# include +#else + class QPrinterInfo; +#endif + +template +class QHash; +class QImage; +class QPainter; +class QPrinter; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class Map; +class MapView; +class Template; + + +/** The MapPrinterPageFormat is a complete description of page properties. */ +class MapPrinterPageFormat +{ +public: + /** Copy of QPrinter::Orientation because QPrinter is not available for Android */ + enum Orientation { Portrait, Landscape }; + + /** Constructs a new page format. + * + * The format is initialized as a custom page format with a page rectangle + * of the given size, surrounded by the given margin (in mm). */ + MapPrinterPageFormat(QSizeF page_rect_size = QSizeF(100.0, 100.0), qreal margin = 5.0, qreal overlap = 5.0); + +#ifdef QT_PRINTSUPPORT_LIB + /** Constructs a new page format, initialized from the given printer's settings. */ + MapPrinterPageFormat(const QPrinter& printer, qreal overlap = 5.0); +#endif + + /** Returns a page format initialized from the default printer's settings. + * + * When building without Qt Print Support, returns a default constructed + * page format. */ + static MapPrinterPageFormat fromDefaultPrinter(); + + /** The nominal paper size (of type QPrinter::PaperSize) */ + int paper_size; + + /** The orientation of the paper. */ + Orientation orientation; + + /** The printable page rectangle in mm. */ + QRectF page_rect; + + /** The total dimensions of the page in mm. */ + QSizeF paper_dimensions; + + /** The horizontal overlap in mm of pages when the output covers muliple pages. */ + qreal h_overlap; + + /** The vertival overlap in mm of pages when the output covers muliple pages. */ + qreal v_overlap; +}; + +/** Returns true iff the MapPrinterPageFormat values are equal. */ +bool operator==(const MapPrinterPageFormat& lhs, const MapPrinterPageFormat& rhs); + +/** Returns true iff the MapPrinterPageFormat values are not equal. */ +bool operator!=(const MapPrinterPageFormat& lhs, const MapPrinterPageFormat& rhs); + + + +/** MapPrinterOptions control how the map is rendered. */ +class MapPrinterOptions +{ +public: + /** Basic modes of printing. */ + enum MapPrinterMode + { + Vector, ///< Print in vector graphics mode + Raster, ///< Print in raster graphics mode + Separations ///< Print spot color separations (b/w vector) + }; + + /** Color modes. + * + * At the moment, only PDF supports a different mode than the default. + */ + enum ColorMode + { + DefaultColorMode, ///< Use the target engine's default color mode. + DeviceCmyk ///< Use device-dependent CMYK for vector data. + }; + + /** Constructs new printer options. + * + * The scale, the mode and the resolution are initialized to the given + * values, all boolean properties are initialized to false. + */ + MapPrinterOptions(unsigned int scale, int resolution = 600, MapPrinterMode mode = Vector); + + /** The scale to be used for printing. */ + unsigned int scale; + + /** The nominal resolution to be used. */ + int resolution; + + /** The mode of printing. + * + * Note that other printing options may be available only in particular modes. + */ + MapPrinterMode mode; + + /** The color mode. */ + ColorMode color_mode; + + /** Controls if templates get printed. + * + * Not available in MapPrinterOptions::Separations mode. + */ + bool show_templates; + + /** Controls if the configured grid is printed. + * + * Not available in MapPrinterOptions::Separations mode. + */ + bool show_grid; + + /** Controls if the desktop printing is to simulate spot color printing. + * + * Only available in MapPrinterOptions::Raster mode. + */ + bool simulate_overprinting; +}; + + + +/** MapPrintParameters contains all options that control printing + * and provides methods for saving and loading. */ +class MapPrinterConfig +{ +public: + /** Constructs a default config for the given map. */ + MapPrinterConfig(const Map& map); + + /** Constructs a config and loads the map print parameters from an XML stream. */ + MapPrinterConfig(const Map& map, QXmlStreamReader& xml); + + /** Saves the map print parameters to an XML stream. */ + void save(QXmlStreamWriter& xml, const QLatin1String& element_name) const; + + /** The name of the printer. */ + QString printer_name; + + /** The print area. */ + QRectF print_area; + + /** The page format. */ + MapPrinterPageFormat page_format; + + /** Printing options. */ + MapPrinterOptions options; + + /** A flag which indicates to the UI whether to maintain a centered + * print area when the size of the map or the page changes. */ + bool center_print_area; + + /** A flag which indicates to the UI whether to adjust the print area size + * to the current page size. */ + bool single_page_print_area; + + /** Platform-dependent data. */ + std::shared_ptr native_data; +}; + + + +#ifdef QT_PRINTSUPPORT_LIB + +/** MapPrinter provides an interface to print a map (to a printer or file). + * It may render a page on any QPaintDevice, such as an QImage. */ +class MapPrinter : public QObject, protected MapPrinterConfig +{ +Q_OBJECT +public: + /** Returns a QPrinterInfo pointer which signals printing to a PDF file. */ + static const QPrinterInfo* pdfTarget(); + + /** Returns a QPrinterInfo pointer which signals printing to a raster file. */ + static const QPrinterInfo* imageTarget(); + + /** Returns a reference to a hash which maps paper sizes to names as C strings. + * These strings are not translated. */ + static const QHash< int, const char* >& paperSizeNames(); + + /** Constructs a new MapPrinter for the given map and (optional) view. */ + MapPrinter(Map& map, const MapView* view, QObject* parent = nullptr); + + /** Destructor. */ + ~MapPrinter() override; + + /** Returns the configured target printer in terms of QPrinterInfo. */ + const QPrinterInfo* getTarget() const; + + /** Returns true if a real printer is configured. */ + bool isPrinter() const; + + /** Returns the page format specification. */ + const MapPrinterPageFormat& getPageFormat() const; + + /** Returns the map area to be printed. */ + const QRectF& getPrintArea() const; + + /** Returns the rendering options. */ + const MapPrinterOptions& getOptions() const; + + /** Returns true if the current print area and rendering options will + * result in empty pages. (The grid is not considered.) */ + bool isOutputEmpty() const; + + /** Returns the quotient of map scale denominator and print scale denominator. */ + qreal getScaleAdjustment() const; + + /** Returns the paper size which is required by the current print area and scale. */ + QSizeF getPrintAreaPaperSize() const; + + /** Returns the print area size which is possible with the current page rect size. */ + QSizeF getPageRectPrintAreaSize() const; + + /** Returns a list of horizontal page positions on the map. */ + const std::vector< qreal >& horizontalPagePositions() const; + + /** Returns a list of vertical page positions on the map. */ + const std::vector< qreal >& verticalPagePositions() const; + + + /** + * Returns true when the Qt print engine may rasterize non-opaque data. + * + * Drawing non-opaque data might cause rasterization with some Qt print + * engines. This function is used to detect configurations where such + * rasterization may happen. The result can be comined with tests for the + * data actually having alpha. + */ + bool engineMayRasterize() const; + + /** + * Returns true when the Qt print engine will have to rasterize the map. + * + * Drawing a map with non-opaque colors will cause rasterization with some + * Qt print engines. This functions signals when this rasterization must + * take place (i.e. even when we try to avoid rasterization for templates + * and grid). + */ + bool engineWillRasterize() const; + + /** + * Returns true when the template will be printed non-opaquely. + * + * Drawing non-opaque templates might cause rasterization with some Qt + * print engines. This function is used to detect this case. It considers + * the template's state and hasAlpha() property as well the view's + * visibility configuration for the given template. + */ + bool hasAlpha(const Template* temp) const; + + + /** Creates a printer configured according to the current settings. */ + std::unique_ptr makePrinter() const; + + /** Takes the settings from the given printer, + * and generates signals for changing properties. */ + void takePrinterSettings(const QPrinter* printer); + + /** Prints the map to the given printer. + * + * This will first update this object's properties from the printer's properties. + * + * @return true on success, false on error. */ + bool printMap(QPrinter* printer); + + /** Draws a single page to the painter. + * + * In case of an error, the painter will be inactive when returning from + * this function. + * + * When the actual paint device is a QImage, pass it as page_buffer. + * This helps to determine the exact dimensions and to avoid the allocation + * of another buffer. + * Otherwise, drawPage() may allocate a buffer with this map printer's + * resolution and size. Parameter units_per_inch has no influence on this + * buffer but refers to the logical coordinates of device_painter. */ + void drawPage(QPainter* device_painter, const QRectF& page_extent, QImage* page_buffer = nullptr) const; + + /** Draws the separations as distinct pages to the printer. */ + void drawSeparationPages(QPrinter* printer, QPainter* device_painter, const QRectF& page_extent) const; + + /** Returns the current configuration. */ + const MapPrinterConfig& config() const; + +public slots: + /** Sets the target QPrinterInfo. + * Ownership is not taken over! Target may even be nullptr. */ + void setTarget(const QPrinterInfo* new_target); + + /** Sets the map area which is to be printed. */ + void setPrintArea(const QRectF& area); + + /** Sets the QPrinter::PaperSize to be used. */ + void setPaperSize(const int size); + + /** Sets a custom paper size with the given dimensions. */ + void setCustomPaperSize(const QSizeF dimensions); + + /** Sets the page orientation. */ + void setPageOrientation(const MapPrinterPageFormat::Orientation orientation); + + /** Sets the overlapping of the pages at the margins. */ + void setOverlap(qreal h_overlap, qreal v_overlap); + + /** Sets the desired printing resolution in dpi. + * The actual resolution will be set by the printer. + * + * Does nothing if dpi is 0 or less. + */ + void setResolution(int dpi); + + /** Sets the denominator of the map scale for printing. */ + void setScale(const unsigned int value); + + /** Sets the printing mode. */ + void setMode(const MapPrinterOptions::MapPrinterMode mode); + + /** Controls whether to print templates. + * If a MapView is given when enabling template printing, + * it will determine the visibility of map and templates. */ + void setPrintTemplates(const bool visible); + + /** Controls whether to print the map grid. */ + void setPrintGrid(const bool visible); + + /** Controls whether to print in overprinting simulation mode. */ + void setSimulateOverprinting(bool enabled); + + /** Controls the color mode. */ + void setColorMode(MapPrinterOptions::ColorMode color_mode); + + /** Saves the print parameter (to the map). */ + void saveConfig() const; + + /** Cancels a running printMap(), if possible. + * This can only be used during handlers of the printMapProgress() signal. */ + void cancelPrintMap(); + +signals: + /** Indicates a new target printer. */ + void targetChanged(const QPrinterInfo* target) const; + + /** Indicates a change in the map area. */ + void printAreaChanged(const QRectF& area) const; + + /** Indicates a change in the page format. */ + void pageFormatChanged(const MapPrinterPageFormat& format) const; + + /** Indicates a change in the rendering options. */ + void optionsChanged(const MapPrinterOptions& options) const; + + /** Emitted during printMap() to indicate progress. You can expect this + * signal to be emitted at the start of the print process (value = 1), + * after each page printed, and at the end of the process (value = 100). + * + * @param value Reflects the progress in the range from 1 (just started) + * to 100 (finished). + * @param status A verbal representation of what printMap() is doing. */ + void printProgress(int value, const QString& status) const; + +protected: + /** Returns true if vector mode is set. */ + bool vectorModeSelected() const; + + /** Returns true if raster mode is set. */ + bool rasterModeSelected() const; + + /** Returns true if separations mode is set. */ + bool separationsModeSelected() const; + + /** Updates the paper dimensions from paper format and orientation. */ + void updatePaperDimensions(); + + /** Updates the page breaks from map area and page format. */ + void updatePageBreaks(); + + /** Updates the scale adjustment and page breaks. */ + void mapScaleChanged(); + + Map& map; + const MapView* view; + const QPrinterInfo* target; + QPrinterInfo target_copy; + qreal scale_adjustment; + std::vector h_page_pos; + std::vector v_page_pos; + bool cancel_print_map; +}; + +#endif + + + +//### MapPrinterPageFormat inline code ### + +inline +bool operator!=(const MapPrinterPageFormat& lhs, const MapPrinterPageFormat& rhs) +{ + return !(lhs == rhs); +} + + + +//### MapPrinterOptions inline code ### + +/** Returns true iff the MapPrinterOptions values are equal. */ +inline +bool operator==(const MapPrinterOptions& lhs, const MapPrinterOptions& rhs) +{ + return lhs.mode == rhs.mode + && lhs.color_mode == rhs.color_mode + && lhs.resolution == rhs.resolution + && lhs.scale == rhs.scale + && lhs.show_templates == rhs.show_templates + && lhs.show_grid == rhs.show_grid + && lhs.simulate_overprinting == rhs.simulate_overprinting; +} + +/** Returns true iff the MapPrinterOptions values are not equal. */ +inline +bool operator!=(const MapPrinterOptions& lhs, const MapPrinterOptions& rhs) +{ + return !(lhs == rhs); +} + + + +// ### MapPrinterConfig ### + +/** Returns true iff the MapPrinterConfig values are equal. */ +inline +bool operator==(const MapPrinterConfig& lhs, const MapPrinterConfig& rhs) +{ + return lhs.printer_name == rhs.printer_name + && lhs.print_area == rhs.print_area + && lhs.page_format == rhs.page_format + && lhs.options == rhs.options + && lhs.center_print_area == rhs.center_print_area + && lhs.single_page_print_area == rhs.single_page_print_area; +} + +/** Returns true iff the MapPrinterConfig values are not equal. */ +inline +bool operator!=(const MapPrinterConfig& lhs, const MapPrinterConfig& rhs) +{ + return !(lhs == rhs); +} + + + +// ### MapPrinter inline code ### + +#ifdef QT_PRINTSUPPORT_LIB + +inline +const MapPrinterConfig& MapPrinter::config() const +{ + return *this; +} + +inline +const QPrinterInfo* MapPrinter::getTarget() const +{ + return target; +} + +inline +const MapPrinterPageFormat& MapPrinter::getPageFormat() const +{ + return page_format; +} + +inline +const QRectF& MapPrinter::getPrintArea() const +{ + return print_area; +} + +inline +const MapPrinterOptions& MapPrinter::getOptions() const +{ + return options; +} + +inline +qreal MapPrinter::getScaleAdjustment() const +{ + return scale_adjustment; +} + +inline +QSizeF MapPrinter::getPrintAreaPaperSize() const +{ + return getPrintArea().size() * scale_adjustment; +} + +inline +QSizeF MapPrinter::getPageRectPrintAreaSize() const +{ + return page_format.page_rect.size() / scale_adjustment; +} + +inline +const std::vector< qreal >& MapPrinter::horizontalPagePositions() const +{ + return h_page_pos; +} + +inline +const std::vector< qreal >& MapPrinter::verticalPagePositions() const +{ + return v_page_pos; +} + +inline +bool MapPrinter::vectorModeSelected() const +{ + return options.mode == MapPrinterOptions::Vector; +} + +inline +bool MapPrinter::rasterModeSelected() const +{ + return options.mode == MapPrinterOptions::Raster; +} + +inline +bool MapPrinter::separationsModeSelected() const +{ + return options.mode == MapPrinterOptions::Separations; +} + +#endif + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/map_view.cpp b/src/core/map_view.cpp new file mode 100644 index 0000000..7fee4d5 --- /dev/null +++ b/src/core/map_view.cpp @@ -0,0 +1,579 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_view.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_coord.h" +#include "gui/util_gui.h" +#include "templates/template.h" // IWYU pragma: keep +#include "util/util.h" +#include "util/xml_stream_util.h" + + +namespace literal +{ + static const QLatin1String zoom("zoom"); + static const QLatin1String rotation("rotation"); + static const QLatin1String position_x("position_x"); + static const QLatin1String position_y("position_y"); + static const QLatin1String grid("grid"); + static const QLatin1String overprinting_simulation_enabled("overprinting_simulation_enabled"); + static const QLatin1String map("map"); + static const QLatin1String opacity("opacity"); + static const QLatin1String visible("visible"); + static const QLatin1String templates("templates"); + static const QLatin1String hidden("hidden"); + static const QLatin1String ref("ref"); + static const QLatin1String template_string("template"); +} + + + +namespace OpenOrienteering { + +// ### TemplateVisibility ### + +bool TemplateVisibility::hasAlpha() const +{ + return visible && opacity > 0 && opacity < 1; +} + + +// ### MapView ### + +const double MapView::zoom_in_limit = 512; +const double MapView::zoom_out_limit = 1 / 16.0; + + +MapView::MapView(QObject* parent, Map* map) +: QObject { parent } +, map { map } +, zoom { 1.0 } +, rotation { 0.0 } +, map_visibility{ 1.0f, true } +, all_templates_hidden{ false } +, grid_visible{ false } +, overprinting_simulation_enabled{ false } +{ + Q_ASSERT(map); + updateTransform(); + connect(map, &Map::templateAdded, this, &MapView::onTemplateAdded); + connect(map, &Map::templateDeleted, this, &MapView::onTemplateDeleted, Qt::QueuedConnection); +} + +MapView::MapView(Map* map) +: MapView { map, map } +{ + // nothing else +} + +MapView::~MapView() +{ + // nothing, not inlined +} + +#ifndef NO_NATIVE_FILE_FORMAT + +void MapView::load(QIODevice* file, int version) +{ + qint64 center_x, center_y; + int unused; + double unused_double; + file->read((char*)&zoom, sizeof(double)); + file->read((char*)&unused_double /*rotation*/, sizeof(double)); + file->read((char*)¢er_x, sizeof(qint64)); + file->read((char*)¢er_y, sizeof(qint64)); + file->read((char*)&unused /*view_x*/, sizeof(int)); + file->read((char*)&unused /*view_y*/, sizeof(int)); + file->read((char*)&pan_offset, sizeof(QPoint)); + + try + { + center_pos = MapCoord::fromNative64withOffset(center_x, center_y); + } + catch (std::range_error&) + { + // leave center_pos unchanged + } + + updateTransform(); + + if (version >= 26) + { + file->read((char*)&map_visibility.visible, sizeof(bool)); + file->read((char*)&map_visibility.opacity, sizeof(float)); + } + + int num_template_visibilities; + file->read((char*)&num_template_visibilities, sizeof(int)); + + for (int i = 0; i < num_template_visibilities; ++i) + { + int pos; + file->read((char*)&pos, sizeof(int)); + + TemplateVisibility vis; + file->read((char*)&vis.visible, sizeof(bool)); + file->read((char*)&vis.opacity, sizeof(float)); + setTemplateVisibilityHelper(map->getTemplate(pos), vis); + } + + if (version >= 29) + file->read((char*)&all_templates_hidden, sizeof(bool)); + + if (version >= 24) + file->read((char*)&grid_visible, sizeof(bool)); + + emit viewChanged(CenterChange | ZoomChange | RotationChange); + emit visibilityChanged(MultipleFeatures, true); +} + +#endif + +void MapView::save(QXmlStreamWriter& xml, const QLatin1String& element_name, bool template_details) const +{ + // We do not save transient attributes such as rotation (for compass) or pan offset. + XmlElementWriter mapview_element(xml, element_name); + mapview_element.writeAttribute(literal::zoom, zoom); + mapview_element.writeAttribute(literal::position_x, center_pos.nativeX()); + mapview_element.writeAttribute(literal::position_y, center_pos.nativeY()); + mapview_element.writeAttribute(literal::grid, grid_visible); + mapview_element.writeAttribute(literal::overprinting_simulation_enabled, overprinting_simulation_enabled); + + { + XmlElementWriter map_element(xml, literal::map); + map_element.writeAttribute(literal::opacity, map_visibility.opacity); + map_element.writeAttribute(literal::visible, map_visibility.visible); + } + + { + XmlElementWriter templates_element(xml, literal::templates); + templates_element.writeAttribute(literal::hidden, all_templates_hidden); + if (template_details) + { + templates_element.writeAttribute(XmlStreamLiteral::count, template_visibilities.size()); + for (auto entry : template_visibilities) + { + XmlElementWriter ref_element(xml, literal::ref); + ref_element.writeAttribute(literal::template_string, map->findTemplateIndex(entry.temp)); + ref_element.writeAttribute(literal::visible, entry.visible); + ref_element.writeAttribute(literal::opacity, entry.opacity); + } + } + } +} + +void MapView::load(QXmlStreamReader& xml) +{ + // We do not load transient attributes such as rotation (for compass) or pan offset. + XmlElementReader mapview_element(xml); + zoom = qMin(mapview_element.attribute(literal::zoom), zoom_in_limit); + if (zoom < zoom_out_limit) + zoom = 1.0; + + auto center_x = mapview_element.attribute(literal::position_x); + auto center_y = mapview_element.attribute(literal::position_y); + try + { + center_pos = MapCoord::fromNative64withOffset(center_x, center_y); + } + catch (std::range_error&) + { + // leave center_pos unchanged + } + updateTransform(); + + grid_visible = mapview_element.attribute(literal::grid); + overprinting_simulation_enabled = mapview_element.attribute(literal::overprinting_simulation_enabled); + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::map) + { + XmlElementReader map_element(xml); + map_visibility.opacity = map_element.attribute(literal::opacity); + if (map_element.hasAttribute(literal::visible)) + map_visibility.visible = map_element.attribute(literal::visible); + else + map_visibility.visible = true; + } + else if (xml.name() == literal::templates) + { + XmlElementReader templates_element(xml); + auto num_template_visibilities = templates_element.attribute(XmlStreamLiteral::count); + template_visibilities.reserve(qBound(20u, num_template_visibilities, 1000u)); + all_templates_hidden = templates_element.attribute(literal::hidden); + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::ref) + { + XmlElementReader ref_element(xml); + int pos = ref_element.attribute(literal::template_string); + if (pos >= 0 && pos < map->getNumTemplates()) + { + TemplateVisibility vis { + qBound(0.0f, ref_element.attribute(literal::opacity), 1.0f), + ref_element.attribute(literal::visible) + }; + setTemplateVisibilityHelper(map->getTemplate(pos), vis); + } + } + else + xml.skipCurrentElement(); + } + } + else + xml.skipCurrentElement(); // unsupported + } + + emit viewChanged(CenterChange | ZoomChange | RotationChange); + emit visibilityChanged(MultipleFeatures, true); +} + +void MapView::updateAllMapWidgets() +{ + emit visibilityChanged(MultipleFeatures, true); +} + +MapCoord MapView::viewToMap(double x, double y) const +{ + return MapCoord(view_to_map.m11() * x + view_to_map.m12() * y + view_to_map.m13(), + view_to_map.m21() * x + view_to_map.m22() * y + view_to_map.m23()); +} + +MapCoordF MapView::viewToMapF(double x, double y) const +{ + return MapCoordF(view_to_map.m11() * x + view_to_map.m12() * y + view_to_map.m13(), + view_to_map.m21() * x + view_to_map.m22() * y + view_to_map.m23()); +} + +QPointF MapView::mapToView(MapCoord coords) const +{ + return QPointF(map_to_view.m11() * coords.x() + map_to_view.m12() * coords.y() + map_to_view.m13(), + map_to_view.m21() * coords.x() + map_to_view.m22() * coords.y() + map_to_view.m23()); +} + +QPointF MapView::mapToView(MapCoordF coords) const +{ + return QPointF(map_to_view.m11() * coords.x() + map_to_view.m12() * coords.y() + map_to_view.m13(), + map_to_view.m21() * coords.x() + map_to_view.m22() * coords.y() + map_to_view.m23()); +} + +qreal MapView::lengthToPixel(qreal length) const +{ + return Util::mmToPixelPhysical(zoom * length / 1000.0); +} + +qreal MapView::pixelToLength(qreal pixel) const +{ + return Util::pixelToMMPhysical(pixel / zoom) * 1000.0; +} + +QRectF MapView::calculateViewedRect(QRectF rect) const +{ + auto top_left = viewToMapF(rect.topLeft()); + auto top_right = viewToMapF(rect.topRight()); + auto bottom_right = viewToMapF(rect.bottomRight()); + auto bottom_left = viewToMapF(rect.bottomLeft()); + + rect = QRectF{ top_left, bottom_right }.normalized(); + rectInclude(rect, top_right); + rectInclude(rect, bottom_left); + rect.adjust(-0.001, -0.001, +0.001, +0.001); + return rect; +} + +QRectF MapView::calculateViewBoundingBox(QRectF rect) const +{ + auto top_left = mapToView(static_cast(rect.topLeft())); + auto top_right = mapToView(static_cast(rect.topRight())); + auto bottom_right = mapToView(static_cast(rect.bottomRight())); + auto bottom_left = mapToView(static_cast(rect.bottomLeft())); + + rect = QRectF{ top_left, bottom_right }.normalized(); + rectInclude(rect, top_right); + rectInclude(rect, bottom_left); + rect.adjust(-1.0, -1.0, +1.0, +1.0); + return rect; +} + +void MapView::setPanOffset(QPoint offset) +{ + if (offset != pan_offset) + { + pan_offset = offset; + emit panOffsetChanged(offset); + } +} + +void MapView::finishPanning(QPoint offset) +{ + setPanOffset({0,0}); + try + { + auto rotated_offset = MapCoord::fromNative64(qRound64(-pixelToLength(offset.x())), + qRound64(-pixelToLength(offset.y())) ); + auto rotated_offset_f = MapCoordF{ rotated_offset }; + rotated_offset_f.rotate(-rotation); + auto move = MapCoord{ rotated_offset_f }; + setCenter(center() + move); + } + catch (std::range_error&) + { + // Do nothing + } +} + +void MapView::zoomSteps(double num_steps, QPointF cursor_pos_view) +{ + auto zoom_to = getZoom() * pow(sqrt(2.0), num_steps); + setZoom(zoom_to, cursor_pos_view); +} + +void MapView::zoomSteps(double num_steps) +{ + auto zoom_to = getZoom() * pow(sqrt(2.0), num_steps); + setZoom(zoom_to); +} + +void MapView::setZoom(double value, QPointF center) +{ + auto pos = this->center(); + auto zoom_pos = viewToMap(center); + auto old_zoom = getZoom(); + + setZoom(value); + + if (!qFuzzyCompare(old_zoom, getZoom())) + { + auto zoom_factor = getZoom() / old_zoom ; + setCenter(zoom_pos + (pos - zoom_pos) / zoom_factor); + } +} + +void MapView::setZoom(double value) +{ + zoom = qBound(zoom_out_limit, value, zoom_in_limit); + updateTransform(); + emit viewChanged(ZoomChange); +} + +void MapView::setRotation(double value) +{ + rotation = value; + updateTransform(); + emit viewChanged(RotationChange); +} + +void MapView::setCenter(MapCoord pos) +{ + center_pos = pos; + updateTransform(); + emit viewChanged(CenterChange); +} + +void MapView::updateTransform() +{ + double final_zoom = calculateFinalZoomFactor(); + double final_zoom_cosr = final_zoom * cos(rotation); + double final_zoom_sinr = final_zoom * sin(rotation); + auto center_x = center_pos.x(); + auto center_y = center_pos.y(); + + // Create map_to_view + map_to_view.setMatrix(final_zoom_cosr, -final_zoom_sinr, -final_zoom_cosr * center_x + final_zoom_sinr * center_y, + final_zoom_sinr, final_zoom_cosr, -final_zoom_sinr * center_x - final_zoom_cosr * center_y, + 0, 0, 1); + view_to_map = map_to_view.inverted(); + world_transform = map_to_view.transposed(); +} + + +TemplateVisibility MapView::effectiveMapVisibility() const +{ + if (all_templates_hidden) + return { 1.0f, true }; + else if (map_visibility.opacity < 0.005f) + return { 0.0f, false }; + else + return map_visibility; +} + +void MapView::setMapVisibility(TemplateVisibility vis) +{ + if (map_visibility != vis) + { + map_visibility = vis; + emit visibilityChanged(VisibilityFeature::MapVisible, vis.visible && vis.opacity > 0, nullptr); + } +} + +MapView::TemplateVisibilityVector::const_iterator MapView::findVisibility(const Template* temp) const +{ + return std::find_if(begin(template_visibilities), end(template_visibilities), [temp](const TemplateVisibilityEntry& entry) + { + return entry.temp == temp; + } ); +} + +MapView::TemplateVisibilityVector::iterator MapView::findVisibility(const Template* temp) +{ + return std::find_if(begin(template_visibilities), end(template_visibilities), [temp](const TemplateVisibilityEntry& entry) + { + return entry.temp == temp; + } ); +} + +bool MapView::isTemplateVisible(const Template* temp) const +{ + auto entry = findVisibility(temp); + return entry != end(template_visibilities) + && entry->visible + && entry->opacity > 0; +} + +TemplateVisibility MapView::getTemplateVisibility(const Template* temp) const +{ + auto entry = findVisibility(temp); + if (entry != end(template_visibilities)) + return *entry; + else + return { 1.0f, false }; +} + +void MapView::setTemplateVisibility(Template* temp, TemplateVisibility vis) +{ + auto visible = vis.visible && vis.opacity > 0; + if (visible + && temp->getTemplateState() != Template::Loaded + && !templateLoadingBlocked()) + { + vis.visible = visible = temp->loadTemplateFile(false); + } + + if (setTemplateVisibilityHelper(temp, vis)) + { + emit visibilityChanged(VisibilityFeature::TemplateVisible, visible, temp); + } +} + +bool MapView::setTemplateVisibilityHelper(const Template *temp, TemplateVisibility vis) +{ + auto stored = findVisibility(temp); + if (stored == end(template_visibilities)) + { + template_visibilities.emplace_back(temp, vis); + return true; + } + if (*stored != vis) + { + stored->opacity = vis.opacity; + stored->visible = vis.visible; + return true; + } + return false; +} + +void MapView::onTemplateAdded(int, Template* temp) +{ + setTemplateVisibility(temp, { 1.0f, true }); +} + +void MapView::onTemplateDeleted(int, const Template* temp) +{ + template_visibilities.erase(findVisibility(temp)); +} + + +void MapView::setAllTemplatesHidden(bool value) +{ + if (all_templates_hidden != value) + { + all_templates_hidden = value; + emit visibilityChanged(VisibilityFeature::AllTemplatesHidden, value); + } +} + +void MapView::setGridVisible(bool visible) +{ + if (grid_visible != visible) + { + grid_visible = visible; + emit visibilityChanged(VisibilityFeature::GridVisible, visible); + } +} + +void MapView::setOverprintingSimulationEnabled(bool enabled) +{ + if (overprinting_simulation_enabled != enabled) + { + overprinting_simulation_enabled = enabled; + emit visibilityChanged(VisibilityFeature::OverprintingEnabled, enabled); + } +} + + + +bool MapView::hasAlpha() const +{ + auto map_visibility = effectiveMapVisibility(); + if (map_visibility.hasAlpha() || map->hasAlpha()) + return true; + + if (grid_visible && map->getGrid().hasAlpha()) + return true; + + for (int i = 0; i < map->getNumTemplates(); ++i) + { + auto temp = map->getTemplate(i); + auto visibility = getTemplateVisibility(temp); + if (visibility.hasAlpha() || temp->hasAlpha()) + return true; + } + + return false; +} + + + +void MapView::setTemplateLoadingBlocked(bool blocked) +{ + template_loading_blocked = blocked; +} + + +} // namespace OpenOrienteering diff --git a/src/core/map_view.h b/src/core/map_view.h new file mode 100644 index 0000000..5bfe179 --- /dev/null +++ b/src/core/map_view.h @@ -0,0 +1,548 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_VIEW_H +#define OPENORIENTEERING_MAP_VIEW_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "map_coord.h" + +class QIODevice; +class QLatin1String; +class QRectF; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class Map; +class Template; + + +/** + * Contains the visibility information for a template (or the map). + */ +class TemplateVisibility +{ +public: + /** Opacity from 0.0f (invisible) to 1.0f (opaque) */ + float opacity; + + /** Visibility flag */ + bool visible; + + /** Returns true when the template is visible but not opaque. */ + bool hasAlpha() const; +}; + +bool operator==(TemplateVisibility lhs, TemplateVisibility rhs); + +bool operator!=(TemplateVisibility lhs, TemplateVisibility rhs); + + +/** + * Stores view position, zoom, rotation and grid / template visibilities + * to define a view onto a map. + * + * These parameters define the view coordinates with origin at the view's center, + * measured in pixels. The class provides methods to convert between view + * coordinates and map coordinates (defined as millimeter on map paper). + */ +class MapView : public QObject +{ + Q_OBJECT + Q_FLAGS(ChangeFlags VisibilityFeature) + +public: + enum ChangeFlag + { + NoChange = 0, + CenterChange = 1, + ZoomChange = 2, + RotationChange = 4, + }; + + Q_DECLARE_FLAGS(ChangeFlags, ChangeFlag) + + enum VisibilityFeature + { + MultipleFeatures = 0, + GridVisible = 1, + OverprintingEnabled = 2, + AllTemplatesHidden = 4, + TemplateVisible = 8, + MapVisible = 16, + }; + + + /** + * Creates a default view looking at the origin. + * + * The parameter map must not be null. + */ + MapView(QObject* parent, Map* map); + + /** Creates a default view looking at the origin. + * + * The map takes ownership of the map view. It must not be null. + */ + MapView(Map* map); + + /** Destroys the map view. */ + ~MapView() override; + + /** Loads the map view in the old "native" format from the file. */ + void load(QIODevice* file, int version); + + /** + * Saves the map view state to an XML stream. + * @param xml The XML output stream. + * @param element_name The name of the element which will be written. + * @param template_details Save template visibilities (default: true) + */ + void save(QXmlStreamWriter& xml, const QLatin1String& element_name, bool template_details = true) const; + + /** Loads the map view state from the current element of an xml stream. */ + void load(QXmlStreamReader& xml); + + /** + * Redraws all map widgets completely. + * + * Note that this calls QWidget::update() which does not cause an immediate + * repaint; instead it schedules a paint event. + * + * Completely repainting widgets can be slow. + * Try to do partial updates instead, if possible. + */ + void updateAllMapWidgets(); + + + /** Converts x, y (with origin at the center of the view) to map coordinates */ + MapCoord viewToMap(double x, double y) const; + + /** Converts the point (with origin at the center of the view) to map coordinates */ + MapCoord viewToMap(QPointF point) const; + + /** Converts x, y (with origin at the center of the view) to map coordinates */ + MapCoordF viewToMapF(double x, double y) const; + + /** Converts the point (with origin at the center of the view) to map coordinates */ + MapCoordF viewToMapF(QPointF point) const; + + + /// Converts map coordinates to view coordinates (with origin at the center of the view) + QPointF mapToView(MapCoord coords) const; + + /// Converts map coordinates to view coordinates (with origin at the center of the view) + QPointF mapToView(MapCoordF coords) const; + + + /** + * Converts a length from native map coordinates to the current length in view pixels. + */ + qreal lengthToPixel(qreal length) const; + + /** + * Converts a length from current view pixels to native map coordinates. + */ + qreal pixelToLength(qreal pixel) const; + + + /** + * Calculates the bounding box of the map coordinates which can be viewed + * using the given view coordinates rect + */ + QRectF calculateViewedRect(QRectF view_rect) const; + + /** + * Calculates the bounding box in view coordinates + * of the given map coordinates rect + */ + QRectF calculateViewBoundingBox(QRectF map_rect) const; + + /** + * Returns a QTransform suitable for QPainter, so objects defined in + * map coordinates will be drawn at their view coordinates. Append a + * viewport transformation to this to get a complete map-to-viewport transformation + * which makes the view center appear at the viewport center. + * + * Note: The transform is to be combined with the painter's existing transform. + */ + const QTransform& worldTransform() const; + + + // Panning + + /** Returns the current pan offset (when dragging the map). */ + QPoint panOffset() const; + + /** Sets the current pan offset while the map is being dragged. */ + void setPanOffset(QPoint offset); + + /** + * Finishes panning the map. + * + * @param offset The final offset, relative to the start of the operation. + */ + void finishPanning(QPoint offset); + + + /** Returns the map this view is defined on. */ + const Map* getMap() const; + + /** Returns the map this view is defined on. */ + Map* getMap(); + + + /** + * Zooms the maps (in steps), preserving the given cursor position. + * + * @param num_steps Number of zoom steps to zoom in. Negative numbers zoom out. + * @param cursor_pos_view The cursor position in view coordinates, must be + * set if preserve_cursor_pos is used. + */ + void zoomSteps(double num_steps, QPointF cursor_pos_view); + + /** + * Zooms the maps (in steps), preserving the center of the view. + * + * @param num_steps Number of zoom steps to zoom in. Negative numbers zoom out. + */ + void zoomSteps(double num_steps); + + /** + * Returns the final zoom factor for use in transformations. + * Depends on the pixel per millimeter of the display. + */ + double calculateFinalZoomFactor() const; + + /** Returns the raw zoom facor, see also calculateFinalZoomFactor(). */ + double getZoom() const; + + /** Sets the zoom factor relative to the given point.*/ + void setZoom(double value, QPointF center); + + /** Sets the zoom factor. */ + void setZoom(double value); + + + /** Returns the view rotation (in radians). */ + double getRotation() const; + + /** Sets the view roation (in radians). */ + void setRotation(double value); + + + /** Returns the position of the view center. */ + MapCoord center() const; + + /** Sets the position of the view center. */ + void setCenter(MapCoord pos); + + + // Map and template visibilities + + /** Returns the effectiv visibility settings of the map drawing. + * + * Other than getMapVisibility, this will always return an (100 %) opaque, + * visible configuration when areAllTemplatesHidden() is true, + * and it return an invisible configuration when the map's opacity is + * below 0.005. + */ + TemplateVisibility effectiveMapVisibility() const; + + /** Returns the visibility settings of the map drawing. */ + TemplateVisibility getMapVisibility() const; + + void setMapVisibility(TemplateVisibility vis); + + /** + * Checks if the template is visible without creating + * a template visibility object if none exists + */ + bool isTemplateVisible(const Template* temp) const; + + /** + * Returns the template visibility. + * + * If the template is unknown, returns default settings. + */ + TemplateVisibility getTemplateVisibility(const Template* temp) const; + + /** + * Sets the template visibility, and emits a change signal. + */ + void setTemplateVisibility(Template* temp, TemplateVisibility vis); + + + /** Enables or disables hiding all templates in this view */ + void setAllTemplatesHidden(bool value); + + /** + * Returns if the "hide all templates" toggle is active. + * See also setHideAllTemplates(). + */ + bool areAllTemplatesHidden() const; + + + /** Returns if the map grid is visible. */ + bool isGridVisible() const; + + /** Sets the map grid visibility. */ + void setGridVisible(bool visible); + + + /** Returns if overprinting simulation is enabled. */ + bool isOverprintingSimulationEnabled() const; + + /** Enables or disables overprinting simulation. */ + void setOverprintingSimulationEnabled(bool enabled); + + + /** + * Returns true if any of the visible elements is not opaque. + */ + bool hasAlpha() const; + + + /** Temporarily blocks automatic template loading on visibility changes. */ + void setTemplateLoadingBlocked(bool blocked); + + /** Returns true when template loading on visibility changes is disabled. */ + bool templateLoadingBlocked() const { return template_loading_blocked; } + + +signals: + /** + * Indicates a change of the viewed area of the map. + * + * @param change The aspects that have chaneged. + */ + void viewChanged(ChangeFlags change); + + /** + * Indicates a change of the pan offset. + */ + void panOffsetChanged(QPoint offset); + + /** + * Indicates a particular change of visibility. + * + * @param feature The map view feature that has changed. + * @param active The features current state of activation. + * @param temp If a the feature is a template, a pointer to this template. + */ + void visibilityChanged(VisibilityFeature feature, bool active, const Template* temp = nullptr); + + +public: + // Static + + /** The global zoom in limit for the zoom factor. */ + static const double zoom_in_limit; + /** The global zoom out limit for the zoom factor. */ + static const double zoom_out_limit; + +protected: + /** + * Sets the template visibility without emitting signals. + */ + bool setTemplateVisibilityHelper(const Template *temp, TemplateVisibility vis); + + /** + * Creates the visibility data when a template is added to the map. + */ + void onTemplateAdded(int pos, Template* temp); + + /** + * Removes the visibility data when a template is deleted. + */ + void onTemplateDeleted(int pos, const Template* temp); + +private: + Q_DISABLE_COPY(MapView) + + struct TemplateVisibilityEntry : public TemplateVisibility + { + const Template* temp; + TemplateVisibilityEntry() = default; + TemplateVisibilityEntry(const TemplateVisibilityEntry&) = default; + TemplateVisibilityEntry(TemplateVisibilityEntry&&) = default; + TemplateVisibilityEntry& operator=(const TemplateVisibilityEntry&) = default; + TemplateVisibilityEntry& operator=(TemplateVisibilityEntry&&) = default; + TemplateVisibilityEntry(const Template* temp, TemplateVisibility vis) + : TemplateVisibility(vis) + , temp(temp) + {} + }; + typedef std::vector TemplateVisibilityVector; + + + void updateTransform(); // recalculates the x_to_y matrices + + TemplateVisibilityVector::const_iterator findVisibility(const Template* temp) const; + + TemplateVisibilityVector::iterator findVisibility(const Template* temp); + + + Map* map; + + double zoom; // factor + double rotation; // counterclockwise 0 to 2*PI. This is the viewer rotation, so the map is rotated clockwise + MapCoord center_pos;// position of the viewer, positive values move the map to the left; the position is in 1/1000 mm + QPoint pan_offset; // the distance the content of the view was dragged with the mouse, in pixels + + QTransform view_to_map; + QTransform map_to_view; + QTransform world_transform; + + TemplateVisibility map_visibility; + TemplateVisibilityVector template_visibilities; + + bool all_templates_hidden; + bool grid_visible; + bool overprinting_simulation_enabled; + + bool template_loading_blocked; +}; + + + +// ### TemplateVisibility inline code ### + +inline +bool operator==(TemplateVisibility lhs, TemplateVisibility rhs) +{ + return lhs.visible == rhs.visible + && qFuzzyCompare(1.0f+rhs.opacity, 1.0f+lhs.opacity); +} + +inline +bool operator!=(TemplateVisibility lhs, TemplateVisibility rhs) +{ + return !(lhs == rhs); +} + + + +// ### MapView inline code ### + +inline +MapCoord MapView::viewToMap(QPointF point) const +{ + return viewToMap(point.x(), point.y()); +} + +inline +MapCoordF MapView::viewToMapF(QPointF point) const +{ + return viewToMapF(point.x(), point.y()); +} + +inline +const QTransform& MapView::worldTransform() const +{ + return world_transform; +} + +inline +Map* MapView::getMap() +{ + return map; +} + +inline +const Map* MapView::getMap() const +{ + return map; +} + +inline +double MapView::calculateFinalZoomFactor() const +{ + return lengthToPixel(1000.0); +} + +inline +double MapView::getZoom() const +{ + return zoom; +} + +inline +double MapView::getRotation() const +{ + return rotation; +} + +inline +MapCoord MapView::center() const +{ + return center_pos; +} + +inline +QPoint MapView::panOffset() const +{ + return pan_offset; +} + +inline +TemplateVisibility MapView::getMapVisibility() const +{ + return map_visibility; +} + +inline +bool MapView::areAllTemplatesHidden() const +{ + return all_templates_hidden; +} + +inline +bool MapView::isGridVisible() const +{ + return grid_visible; +} + +inline +bool MapView::isOverprintingSimulationEnabled() const +{ + return overprinting_simulation_enabled; +} + + +} // namespace OpenOrienteering + + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::MapView::ChangeFlags) + + +#endif diff --git a/src/core/objects/boolean_tool.cpp b/src/core/objects/boolean_tool.cpp new file mode 100644 index 0000000..ef65f82 --- /dev/null +++ b/src/core/objects/boolean_tool.cpp @@ -0,0 +1,1085 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "boolean_tool.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "core/map.h" +#include "core/map_coord.h" +#include "core/map_part.h" +#include "core/path_coord.h" +#include "core/virtual_path.h" +#include "core/objects/object.h" +#include "core/symbols/symbol.h" +#include "undo/object_undo.h" +#include "undo/undo.h" +#include "util/util.h" + + +//### Local helper functions + +/* + * NB: qHash must be in the same namespace as the QHash key. + * This is a C++ language issue, not a Qt issue. + * Cf. https://bugreports.qt-project.org/browse/QTBUG-34912 + */ +namespace ClipperLib { + +/** + * Implements qHash for ClipperLib::IntPoint. + * + * This is needed to use ClipperLib::IntPoint as QHash key. + */ +uint qHash(const IntPoint& point, uint seed) +{ + quint64 tmp = (quint64(point.X) & 0xffffffff) | (quint64(point.Y) << 32); + return ::qHash(tmp, seed); // must use :: namespace to prevent endless recursion +} + + +} // namespace ClipperLib + + + +namespace OpenOrienteering { + +/** + * Removes flags from the coordinate to be able to use it in the reconstruction. + */ +static MapCoord resetCoordinate(MapCoord in) +{ + in.setFlags(0); + return in; +} + +/** + * Compares a MapCoord and ClipperLib::IntPoint. + */ +bool operator==(const MapCoord& lhs, const ClipperLib::IntPoint& rhs) +{ + return lhs.nativeX() == rhs.X && lhs.nativeY() == rhs.Y; +} + +/** + * Compares a MapCoord and ClipperLib::IntPoint. + */ +bool operator==(const ClipperLib::IntPoint& lhs, const MapCoord& rhs) +{ + return rhs == lhs; +} + + + +//### BooleanTool ### + +BooleanTool::BooleanTool(Operation op, Map* map) +: op(op) +, map(map) +{ + ; // nothing +} + +bool BooleanTool::execute() +{ + // Check basic prerequisite + Object* const primary_object = map->getFirstSelectedObject(); + if (primary_object->getType() != Object::Path) + { + qWarning("The first selected object must be a path."); + return false; // in release build + } + + // Filter selected objects into in_objects + PathObjects in_objects; + in_objects.reserve(map->getNumSelectedObjects()); + for (Object* object : map->selectedObjects()) + { + if (object->getType() == Object::Path) + { + PathObject* const path = object->asPath(); + if (op != MergeHoles || (object->getSymbol()->getContainedTypes() & Symbol::Area && path->parts().size() > 1 )) + { + in_objects.push_back(path); + } + } + } + + // Perform the core operation + QScopedPointer undo_step(new CombinedUndoStep(map)); + PathObjects out_objects; + if (!executeForObjects(primary_object->asPath(), in_objects, out_objects, *undo_step)) + { + Q_ASSERT(out_objects.size() == 0); + return false; // in release build + } + + map->push(undo_step.take()); + map->setObjectsDirty(); + map->emitSelectionChanged(); + map->emitSelectionEdited(); + return true; +} + +bool BooleanTool::executePerSymbol() +{ + PathObjects backlog; + backlog.reserve(map->getNumSelectedObjects()); + + // Filter area objects into initial backlog + for (Object* object : map->selectedObjects()) + { + if (object->getSymbol()->getContainedTypes() & Symbol::Area) + backlog.push_back(object->asPath()); + } + + QScopedPointer undo_step(new CombinedUndoStep(map)); + PathObjects new_backlog; + new_backlog.reserve(backlog.size()/2); + PathObjects in_objects; + in_objects.reserve(backlog.size()/2); + PathObjects out_objects; + while (!backlog.empty()) + { + PathObject* const primary_object = backlog.front(); + const Symbol* const symbol = primary_object->getSymbol(); + + // Filter objects by symbol into in_objects or new_backlog, respectively + new_backlog.clear(); + in_objects.clear(); + for (PathObject* object : backlog) + { + if (object->getSymbol() == symbol) + { + if (op != MergeHoles || (object->getSymbol()->getContainedTypes() & Symbol::Area && object->parts().size() > 1 )) + { + in_objects.push_back(object); + } + } + else + { + new_backlog.push_back(object); + } + } + backlog.swap(new_backlog); + + // Short cut for single object of given symbol + if (in_objects.size() == 1) + continue; + + // Perform the core operation + out_objects.clear(); + executeForObjects(primary_object, in_objects, out_objects, *undo_step); + } + + bool const have_changes = undo_step->getNumSubSteps() > 0; + if (have_changes) + { + map->push(undo_step.take()); + map->setObjectsDirty(); + map->emitSelectionChanged(); + map->emitSelectionEdited(); + } + return have_changes; +} + +bool BooleanTool::executeForObjects(PathObject* subject, PathObjects& in_objects, PathObjects& out_objects, CombinedUndoStep& undo_step) +{ + if (!executeForObjects(subject, in_objects, out_objects)) + { + Q_ASSERT(out_objects.size() == 0); + return false; // in release build + } + + // Add original objects to undo step, and remove them from map. + QScopedPointer add_step(new AddObjectsUndoStep(map)); + for (PathObject* object : in_objects) + { + if (op != Difference || object == subject) + { + add_step->addObject(object, object); + } + } + // Keep as separate loop to get the correct index in the previous loop + for (PathObject* object : in_objects) + { + if (op != Difference || object == subject) + { + map->removeObjectFromSelection(object, false); + map->getCurrentPart()->deleteObject(object, true); + object->setMap(map); // necessary so objects are saved correctly + } + } + + // Add resulting objects to map, and create delete step for them + QScopedPointer delete_step(new DeleteObjectsUndoStep(map)); + MapPart* part = map->getCurrentPart(); + for (PathObject* object : out_objects) + { + map->addObject(object); + map->addObjectToSelection(object, false); + } + // Keep as separate loop to get the correct index in the previous loop + for (PathObject* object : out_objects) + { + delete_step->addObject(part->findObjectIndex(object)); + } + + undo_step.push(add_step.take()); + undo_step.push(delete_step.take()); + return true; +} + +bool BooleanTool::executeForObjects(PathObject* subject, PathObjects& in_objects, PathObjects& out_objects) +{ + // Convert the objects to Clipper polygons and + // create a hash map, mapping point positions to the PathCoords. + // These paths are to be regarded as closed. + PolyMap polymap; + + ClipperLib::Paths subject_polygons; + pathObjectToPolygons(subject, subject_polygons, polymap); + + ClipperLib::Paths clip_polygons; + for (PathObject* object : in_objects) + { + if (object != subject) + { + pathObjectToPolygons(object, clip_polygons, polymap); + } + } + + // Do the operation. + ClipperLib::Clipper clipper; + clipper.AddPaths(subject_polygons, ClipperLib::ptSubject, true); + clipper.AddPaths(clip_polygons, ClipperLib::ptClip, true); + + ClipperLib::ClipType clip_type; + ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero; + switch (op) + { + case Union: clip_type = ClipperLib::ctUnion; + break; + case Intersection: clip_type = ClipperLib::ctIntersection; + break; + case Difference: clip_type = ClipperLib::ctDifference; + break; + case XOr: clip_type = ClipperLib::ctXor; + break; + case MergeHoles: clip_type = ClipperLib::ctUnion; + fill_type = ClipperLib::pftPositive; + break; + default: qWarning("Undefined operation"); + return false; + } + + ClipperLib::PolyTree solution; + bool success = clipper.Execute(clip_type, solution, fill_type, fill_type); + if (success) + { + // Try to convert the solution polygons to objects again + polyTreeToPathObjects(solution, out_objects, subject, polymap); + } + + return success; +} + +void BooleanTool::polyTreeToPathObjects(const ClipperLib::PolyTree& tree, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap) +{ + for (int i = 0, count = tree.ChildCount(); i < count; ++i) + outerPolyNodeToPathObjects(*tree.Childs[i], out_objects, proto, polymap); +} + +void BooleanTool::outerPolyNodeToPathObjects(const ClipperLib::PolyNode& node, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap) +{ + auto object = std::unique_ptr{ proto->duplicate() }; + object->clearCoordinates(); + + try + { + polygonToPathPart(node.Contour, polymap, object.get()); + for (int i = 0, i_count = node.ChildCount(); i < i_count; ++i) + { + polygonToPathPart(node.Childs[i]->Contour, polymap, object.get()); + + // Add outer polygons contained by (nested within) holes ... + for (int j = 0, j_count = node.Childs[i]->ChildCount(); j < j_count; ++j) + outerPolyNodeToPathObjects(*node.Childs[i]->Childs[j], out_objects, proto, polymap); + } + + out_objects.push_back(object.release()); + } + catch (std::range_error&) + { + // Do nothing + } +} + + + +void BooleanTool::executeForLine(const PathObject* area, const PathObject* line, BooleanTool::PathObjects& out_objects) +{ + if (op != BooleanTool::Intersection && op != BooleanTool::Difference) + { + Q_ASSERT_X(false, "BooleanTool::executeForLine", "Only intersection and difference are supported."); + return; // no-op in release build + } + if (line->parts().size() != 1) + { + Q_ASSERT_X(false, "BooleanTool::executeForLine", "Only single-part lines are supported."); + return; // no-op in release build + } + + // Calculate all intersection points of line with the area path + PathObject::Intersections intersections; + line->calcAllIntersectionsWith(area, intersections); + intersections.normalize(); + + PathObject* first_segment = nullptr; + PathObject* last_segment = nullptr; + + const auto& part = line->parts().front(); + const auto& path_coords = part.path_coords; + + // Only one segment? + if (intersections.empty()) + { + auto middle_length = part.length(); + auto middle = SplitPathCoord::at(path_coords, middle_length); + if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) + out_objects.push_back(line->duplicate()); + return; + } + + // First segment + auto middle_length = intersections[0].length / 2; + auto middle = SplitPathCoord::at(path_coords, middle_length); + if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) + { + PathObject* segment = line->duplicate(); + segment->changePathBounds(0, 0.0, intersections[0].length); + first_segment = segment; + } + + // Middle segments + for (std::size_t i = 0; i < intersections.size() - 1; ++i) + { + middle_length = (intersections[i].length + intersections[i+1].length) / 2; + auto middle = SplitPathCoord::at(path_coords, middle_length); + if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) + { + PathObject* segment = line->duplicate(); + segment->changePathBounds(0, intersections[i].length, intersections[i+1].length); + out_objects.push_back(segment); + } + } + + // Last segment + middle_length = (part.length() + intersections.back().length) / 2; + middle = SplitPathCoord::at(path_coords, middle_length); + if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) + { + PathObject* segment = line->duplicate(); + segment->changePathBounds(0, intersections.back().length, part.length()); + last_segment = segment; + } + + // If the line was closed at the beginning, merge the first and last segments if they were kept both. + if (part.isClosed() && first_segment && last_segment) + { + last_segment->connectPathParts(0, first_segment, 0, false); + delete first_segment; + out_objects.push_back(last_segment); + } + else + { + if (first_segment) + out_objects.push_back(first_segment); + if (last_segment) + out_objects.push_back(last_segment); + } +} + +void BooleanTool::pathObjectToPolygons( + const PathObject* object, + ClipperLib::Paths& polygons, + PolyMap& polymap) +{ + object->update(); + auto coords = object->getRawCoordinateVector(); + + polygons.reserve(polygons.size() + object->parts().size()); + + for (const auto& part : object->parts()) + { + const PathCoordVector& path_coords = part.path_coords; + auto path_coords_end = path_coords.size(); + if (part.isClosed()) + --path_coords_end; + + ClipperLib::Path polygon; + polygon.reserve(path_coords_end); + for (auto i = 0u; i < path_coords_end; ++i) + { + auto& path_coord = path_coords[i]; + if (path_coord.param == 0.0) + { + auto point = coords[path_coord.index]; + polygon.push_back(ClipperLib::IntPoint(point.nativeX(), point.nativeY())); + } + else + { + auto point = MapCoord { path_coord.pos }; + polygon.push_back(ClipperLib::IntPoint(point.nativeX(), point.nativeY())); + } + polymap.insertMulti(polygon.back(), std::make_pair(&part, &path_coord)); + } + + bool orientation = Orientation(polygon); + if ( (&part == &object->parts().front()) != orientation ) + { + std::reverse(polygon.begin(), polygon.end()); + } + + // Push_back shall move the polygon. + static_assert(std::is_nothrow_move_constructible::value, "ClipperLib::Path must be nothrow move constructible"); + polygons.push_back(polygon); + } +} + +void BooleanTool::polygonToPathPart(const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object) +{ + auto num_points = polygon.size(); + if (num_points < 3) + return; + + // Index of first used point in polygon + auto part_start_index = 0u; + auto cur_info = PathCoordInfo{ nullptr, nullptr }; + + // Check if we can find either an unknown intersection point + // or a path coord with parameter 0. + // This gives a starting point to search for curves to rebuild + // (because we cannot start in the middle of a curve) + for (; part_start_index < num_points; ++part_start_index) + { + auto current_point = polygon.at(part_start_index); + if (!polymap.contains(current_point)) + break; + + if (polymap.value(current_point).second->param == 0.0) + { + cur_info = polymap.value(current_point); + break; + } + } + + if (part_start_index == num_points) + { + // Did not find a valid starting point. Return the part as a polygon. + for (auto i = 0u; i < num_points; ++i) + object->addCoordinate(MapCoord::fromNative64(polygon.at(i).X, polygon.at(i).Y), i == 0); + object->parts().back().setClosed(true, true); + return; + } + + // Add the first point to the object + rebuildCoordinate(part_start_index, polygon, polymap, object, true); + + + // Index of first segment point in polygon + auto segment_start_index = part_start_index; + bool have_sequence = false; + bool sequence_increasing = false; + bool stop_before = false; + + // Advance along the boundary and rebuild the curve for every sequence + // of path coord pointers with the same path and index. + auto i = part_start_index; + do + { + ++i; + if (i >= num_points) + i = 0; + + PathCoordInfo new_info{ nullptr, nullptr }; + auto new_point = polygon.at(i); + if (polymap.contains(new_point)) + new_info = polymap.value(new_point); + + if (cur_info.first && cur_info.first == new_info.first) + { + // Same original part + auto cur_coord_index = cur_info.second->index; + MapCoord& cur_coord = cur_info.first->path->getCoordinate(cur_coord_index); + + auto new_coord_index = new_info.second->index; + MapCoord& new_coord = new_info.first->path->getCoordinate(new_coord_index); + + auto cur_coord_index_adjusted = cur_coord_index; + if (cur_coord_index_adjusted == new_info.first->first_index) + cur_coord_index_adjusted = new_info.first->last_index; + + auto new_coord_index_adjusted = new_coord_index; + if (new_coord_index_adjusted == new_info.first->first_index) + new_coord_index_adjusted = new_info.first->last_index; + + if (cur_coord_index == new_coord_index) + { + // Somewhere on a curve + bool param_increasing = new_info.second->param > cur_info.second->param; + if (!have_sequence) + { + have_sequence = true; + sequence_increasing = param_increasing; + } + else if (have_sequence && sequence_increasing != param_increasing) + { + stop_before = true; + } + } + else if (new_info.second->param == 0.0 && + ( (cur_coord.isCurveStart() && new_coord_index_adjusted == cur_coord_index + 3) || + (!cur_coord.isCurveStart() && new_coord_index_adjusted == cur_coord_index + 1) ) ) + { + // Original curve is from cur_coord_index to new_coord_index_adjusted. + if (!have_sequence) + { + have_sequence = true; + sequence_increasing = true; + } + else + { + stop_before = !sequence_increasing; + } + } + else if (cur_info.second->param == 0.0 && + ( (new_coord.isCurveStart() && new_coord_index + 3 == cur_coord_index_adjusted) || + (!new_coord.isCurveStart() && new_coord_index + 1 == cur_coord_index_adjusted) ) ) + { + // Original curve is from new_coord_index to cur_coord_index_adjusted. + if (!have_sequence) + { + have_sequence = true; + sequence_increasing = false; + } + else + { + stop_before = sequence_increasing; + } + } + else if ((segment_start_index + 1) % num_points != i) + { + // Not immediately after segment_start_index + stop_before = true; + } + } + + if (i == part_start_index || + stop_before || + (new_info.second && new_info.second->param == 0.0) || + (cur_info.first && (cur_info.first != new_info.first || cur_info.second->index != new_info.second->index) && i != (segment_start_index + 1) % num_points) || + !new_info.first) + { + if (stop_before) + { + if (i == 0) + i = num_points - 1; + else + --i; + } + + if (have_sequence) + // A sequence of at least two points belonging to the same curve + rebuildSegment(segment_start_index, i, sequence_increasing, polygon, polymap, object); + else + // A single straight edge + rebuildCoordinate(i, polygon, polymap, object); + + if (stop_before) + { + ++i; + if (i >= num_points) + i = 0; + rebuildCoordinate(i, polygon, polymap, object); + stop_before = false; + } + + segment_start_index = i; + have_sequence = false; + } + + cur_info = new_info; + } + while (i != part_start_index); + + object->parts().back().connectEnds(); +} + +void BooleanTool::rebuildSegment( + ClipperLib::Path::size_type start_index, + ClipperLib::Path::size_type end_index, + bool sequence_increasing, + const ClipperLib::Path& polygon, + const PolyMap& polymap, + PathObject* object) +{ + auto num_points = polygon.size(); + + object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(true); + + if ((start_index + 1) % num_points == end_index) + { + // This could happen for a straight line or a very flat curve - take coords directly from original + rebuildTwoIndexSegment(start_index, end_index, sequence_increasing, polygon, polymap, object); + return; + } + + // Get polygon point coordinates + const auto& start_point = polygon.at(start_index); + const auto& second_point = polygon.at((start_index + 1) % num_points); + const auto& second_last_point = polygon.at((end_index ? end_index : num_points) - 1); + const auto& end_point = polygon.at(end_index); + + // Try to find the middle coordinates in the same part + bool found = false; + PathCoordInfo second_info{ nullptr, nullptr }; + PathCoordInfo second_last_info{ nullptr, nullptr }; + for (auto second_it = polymap.find(second_point); second_it != polymap.end(); ++second_it) + { + for (auto second_last_it = polymap.find(second_last_point); + second_last_it != polymap.end() && second_last_it.key() == second_last_point; + ++second_last_it) + { + if (second_it->first == second_last_it->first && + second_it->second->index == second_last_it->second->index) + { + // Same part + found = true; + second_info = *second_it; + second_last_info = *second_last_it; + break; + } + } + if (found) + break; + } + + if (!found) + { + // Need unambiguous path part information to find the original object with high probability + qDebug() << "BooleanTool::rebuildSegment: cannot identify original object!"; + rebuildSegmentFromPathOnly(start_point, second_point, second_last_point, end_point, object); + return; + } + + const PathPart* original_path = second_info.first; + + // Try to find the outer coordinates in the same part + PathCoordInfo start_info{ nullptr, nullptr }; + for (auto start_it = polymap.find(start_point); + start_it != polymap.end() && start_it.key() == start_point; + ++start_it) + { + if (start_it->first == original_path) + { + start_info = *start_it; + break; + } + } + Q_ASSERT(!start_info.first || start_info.first == second_info.first); + + PathCoordInfo end_info{ nullptr, nullptr }; + for (auto end_it = polymap.find(end_point); + end_it != polymap.end() && end_it.key() == end_point; + ++end_it) + { + if (end_it->first == original_path) + { + end_info = *end_it; + break; + } + } + Q_ASSERT(!end_info.first || end_info.first == second_info.first); + + const PathObject* original = original_path->path; + auto edge_start = second_info.second->index; + if (edge_start == second_info.first->last_index) + edge_start = second_info.first->first_index; + + // Find out start tangent + auto start_param = 0.0; + MapCoord start_coord = MapCoord::fromNative64(start_point.X, start_point.Y); + MapCoord start_tangent; + MapCoord end_tangent; + MapCoord end_coord; + + double start_error_sq, end_error_sq; + // Maximum difference in mm from reconstructed start and end coords to the + // intersection points returned by Clipper + const double error_bound = 0.4; + + if (sequence_increasing) + { + if ( second_info.second->param == 0.0 || + ( start_info.first && + start_info.second->param == 0.0 && + ( start_info.second->index == edge_start || + (start_info.second->index == start_info.first->last_index && start_info.first->first_index == edge_start) ) ) ) + { + // Take coordinates directly + start_tangent = original->getCoordinate(edge_start + 1); + end_tangent = original->getCoordinate(edge_start + 2); + + start_error_sq = start_coord.distanceSquaredTo(original->getCoordinate(edge_start + 0)); + if (start_error_sq > error_bound) + qDebug() << "BooleanTool::rebuildSegment: start error too high in increasing direct case: " << sqrt(start_error_sq); + } + else + { + // Approximate coords + const PathCoord* prev_coord = second_info.second - 1; + + auto dx = second_point.X - start_point.X; + auto dy = second_point.Y - start_point.Y; + auto point_dist = 0.001 * sqrt(dx*dx + dy*dy); + + auto delta_start_param = (second_info.second->param - prev_coord->param) * point_dist / qMax(1e-7f, (second_info.second->clen - prev_coord->clen)); + start_param = qBound(0.0, second_info.second->param - delta_start_param, 1.0); + + MapCoordF unused, o2, o3, o4; + PathCoord::splitBezierCurve(MapCoordF(original->getCoordinate(edge_start + 0)), MapCoordF(original->getCoordinate(edge_start + 1)), + MapCoordF(original->getCoordinate(edge_start + 2)), MapCoordF(original->getCoordinate(edge_start + 3)), + start_param, + unused, unused, o2, o3, o4); + start_tangent = MapCoord(o3); + end_tangent = MapCoord(o4); + + start_error_sq = start_coord.distanceSquaredTo(MapCoord(o2)); + if (start_error_sq > error_bound) + qDebug() << "BooleanTool::rebuildSegment: start error too high in increasing general case: " << sqrt(start_error_sq); + } + + // Find better end point approximation and its tangent + if ( second_last_info.second->param == 0.0 || + (end_info.first && + end_info.second->param == 0.0 && + ( end_info.second->index == edge_start+3 || + (end_info.second->index == end_info.first->first_index && end_info.first->last_index == edge_start+3) ) ) ) + { + // Take coordinates directly + end_coord = original->getCoordinate(edge_start + 3); + + auto test_x = end_point.X - end_coord.nativeX(); + auto test_y = end_point.Y - end_coord.nativeY(); + end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y); + if (end_error_sq > error_bound) + qDebug() << "BooleanTool::rebuildSegment: end error too high in increasing direct case: " << sqrt(end_error_sq); + } + else + { + // Approximate coords + const PathCoord* next_coord = second_last_info.second + 1; + auto next_coord_param = next_coord->param; + if (next_coord_param == 0.0) + next_coord_param = 1.0; + + auto dx = end_point.X - second_last_point.X; + auto dy = end_point.Y - second_last_point.Y; + auto point_dist = 0.001 * sqrt(dx*dx + dy*dy); + + auto delta_end_param = (next_coord_param - second_last_info.second->param) * point_dist / qMax(1e-7f, (next_coord->clen - second_last_info.second->clen)); + auto end_param = (second_last_info.second->param + delta_end_param - start_param) / (1.0 - start_param); + + MapCoordF o0, o1, o2, unused; + PathCoord::splitBezierCurve(MapCoordF(start_coord), MapCoordF(start_tangent), + MapCoordF(end_tangent), MapCoordF(original->getCoordinate(edge_start + 3)), + end_param, + o0, o1, o2, unused, unused); + start_tangent = MapCoord(o0); + end_tangent = MapCoord(o1); + end_coord = MapCoord(o2); + + auto test_x = end_point.X - end_coord.nativeX(); + auto test_y = end_point.Y - end_coord.nativeY(); + end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y); + if (end_error_sq > error_bound) + qDebug() << "BooleanTool::rebuildSegment: end error too high in increasing general case: " << sqrt(end_error_sq); + } + } + else // if (!sequence_increasing) + { + if ( second_info.second->param == 0.0 || + ( start_info.first && + start_info.second->param == 0.0 && + ( start_info.second->index == edge_start+3 || + (start_info.second->index == start_info.first->first_index && start_info.first->last_index == edge_start+3) ) ) ) + { + // Take coordinates directly + start_tangent = original->getCoordinate(edge_start + 2); + end_tangent = original->getCoordinate(edge_start + 1); + + start_error_sq = start_coord.distanceSquaredTo(original->getCoordinate(edge_start + 3)); + if (start_error_sq > error_bound) + qDebug() << "BooleanTool::rebuildSegment: start error too high in decreasing direct case: " << sqrt(start_error_sq); + } + else + { + // Approximate coords + const PathCoord* next_coord = second_info.second + 1; + auto next_coord_param = next_coord->param; + if (next_coord_param == 0.0) + next_coord_param = 1.0; + + auto dx = second_point.X - start_point.X; + auto dy = second_point.Y - start_point.Y; + auto point_dist = 0.001 * sqrt(dx*dx + dy*dy); + + auto delta_start_param = (next_coord_param - second_info.second->param) * point_dist / qMax(1e-7f, (next_coord->clen - second_info.second->clen)); + start_param = qBound(0.0, 1.0 - second_info.second->param + delta_start_param, 1.0); + + MapCoordF unused, o2, o3, o4; + PathCoord::splitBezierCurve(MapCoordF(original->getCoordinate(edge_start + 3)), MapCoordF(original->getCoordinate(edge_start + 2)), + MapCoordF(original->getCoordinate(edge_start + 1)), MapCoordF(original->getCoordinate(edge_start + 0)), + start_param, + unused, unused, o2, o3, o4); + start_tangent = MapCoord(o3); + end_tangent = MapCoord(o4); + + start_error_sq = start_coord.distanceSquaredTo(MapCoord(o2)); + if (start_error_sq > error_bound) + qDebug() << "BooleanTool::rebuildSegment: start error too high in decreasing general case: " << sqrt(start_error_sq); + } + + // Find better end point approximation and its tangent + if ( second_last_info.second->param == 0.0 || + ( end_info.first && + end_info.second->param == 0.0 && + ( end_info.second->index == edge_start || + (end_info.second->index == end_info.first->last_index && end_info.first->first_index == edge_start) ) ) ) + { + // Take coordinates directly + end_coord = original->getCoordinate(edge_start + 0); + + auto test_x = end_point.X - end_coord.nativeX(); + auto test_y = end_point.Y - end_coord.nativeY(); + end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y); + if (end_error_sq > error_bound) + qDebug() << "BooleanTool::rebuildSegment: end error too high in decreasing direct case: " << sqrt(end_error_sq); + } + else + { + // Approximate coords + const PathCoord* prev_coord = second_last_info.second - 1; + + auto dx = end_point.X - second_last_point.X; + auto dy = end_point.Y - second_last_point.Y; + auto point_dist = 0.001 * sqrt(dx*dx + dy*dy); + + auto delta_end_param = (second_last_info.second->param - prev_coord->param) * point_dist / qMax(1e-7f, (second_last_info.second->clen - prev_coord->clen)); + auto end_param = (1.0 - second_last_info.second->param + delta_end_param) / (1 - start_param); + + MapCoordF o0, o1, o2, unused; + PathCoord::splitBezierCurve(MapCoordF(start_coord), MapCoordF(start_tangent), + MapCoordF(end_tangent), MapCoordF(original->getCoordinate(edge_start + 0)), + end_param, + o0, o1, o2, unused, unused); + start_tangent = MapCoord(o0); + end_tangent = MapCoord(o1); + end_coord = MapCoord(o2); + + auto test_x = end_point.X - end_coord.nativeX(); + auto test_y = end_point.Y - end_coord.nativeY(); + end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y); + if (end_error_sq > error_bound) + qDebug() << "BooleanTool::rebuildSegment: end error too high in decreasing general case: " << sqrt(end_error_sq); + } + } + + if (start_error_sq <= error_bound && end_error_sq <= error_bound) + { + // Rebuild bezier curve using information from original curve + object->addCoordinate(start_tangent); + object->addCoordinate(end_tangent); + object->addCoordinate(resetCoordinate(end_coord)); + } + else + { + // Rebuild bezier curve approximately using tangents derived from result polygon + rebuildSegmentFromPathOnly(start_point, second_point, second_last_point, end_point, object); + } +} + +void BooleanTool::rebuildSegmentFromPathOnly( + const ClipperLib::IntPoint& start_point, + const ClipperLib::IntPoint& second_point, + const ClipperLib::IntPoint& second_last_point, + const ClipperLib::IntPoint& end_point, + PathObject* object) +{ + MapCoord start_point_c = MapCoord::fromNative64(start_point.X, start_point.Y); + MapCoord second_point_c = MapCoord::fromNative64(second_point.X, second_point.Y); + MapCoord second_last_point_c = MapCoord::fromNative64(second_last_point.X, second_last_point.Y); + MapCoord end_point_c = MapCoord::fromNative64(end_point.X, end_point.Y); + + MapCoordF polygon_start_tangent = MapCoordF(second_point_c - start_point_c); + polygon_start_tangent.normalize(); + MapCoordF polygon_end_tangent = MapCoordF(second_last_point_c - end_point_c); + polygon_end_tangent.normalize(); + + double tangent_length = BEZIER_HANDLE_DISTANCE * start_point_c.distanceTo(end_point_c); + object->addCoordinate(MapCoord(MapCoordF(start_point_c) + tangent_length * polygon_start_tangent)); + object->addCoordinate(MapCoord((MapCoordF(end_point_c) + tangent_length * polygon_end_tangent))); + object->addCoordinate(end_point_c); +} + +void BooleanTool::rebuildTwoIndexSegment( + ClipperLib::Path::size_type start_index, + ClipperLib::Path::size_type end_index, + bool sequence_increasing, + const ClipperLib::Path& polygon, + const PolyMap& polymap, + PathObject* object) +{ + Q_UNUSED(sequence_increasing); // only used in Q_ASSERT. + + PathCoordInfo start_info = polymap.value(polygon.at(start_index)); + PathCoordInfo end_info = polymap.value(polygon.at(end_index)); + PathObject* original = end_info.first->path; + + bool coords_increasing; + bool is_curve; + int coord_index; + if (start_info.second->index == end_info.second->index) + { + coord_index = end_info.second->index; + bool found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); + if (!found) + { + object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); + rebuildCoordinate(end_index, polygon, polymap, object); + return; + } + Q_ASSERT(coords_increasing == sequence_increasing); + } + else + { + coord_index = end_info.second->index; + bool found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); + if (!found) + { + coord_index = start_info.second->index; + found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); + if (!found) + { + object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); + rebuildCoordinate(end_index, polygon, polymap, object); + return; + } + } + } + + if (!is_curve) + object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); + + if (coords_increasing) + { + object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 1))); + if (is_curve) + { + object->addCoordinate(original->getCoordinate(coord_index + 2)); + object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 3))); + } + } + else + { + if (is_curve) + { + object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 2))); + object->addCoordinate(original->getCoordinate(coord_index + 1)); + } + object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 0))); + } +} + +void BooleanTool::rebuildCoordinate( + ClipperLib::Path::size_type index, + const ClipperLib::Path& polygon, + const PolyMap& polymap, + PathObject* object, + bool start_new_part) +{ + auto coord = MapCoord::fromNative64(polygon.at(index).X, polygon.at(index).Y); + if (polymap.contains(polygon.at(index))) + { + PathCoordInfo info = polymap.value(polygon.at(index)); + MapCoord& original = info.first->path->getCoordinate(info.second->index); + + if (original.isDashPoint()) + coord.setDashPoint(true); + } + object->addCoordinate(coord, start_new_part); +} + +bool BooleanTool::checkSegmentMatch( + const PathObject* original, + int coord_index, + const ClipperLib::Path& polygon, + ClipperLib::Path::size_type start_index, + ClipperLib::Path::size_type end_index, + bool& out_coords_increasing, + bool& out_is_curve ) +{ + + const MapCoord& first = original->getCoordinate(coord_index); + out_is_curve = first.isCurveStart(); + + auto other_index = (coord_index + (out_is_curve ? 3 : 1)) % original->getCoordinateCount(); + const MapCoord& other = original->getCoordinate(other_index); + + bool found = true; + if (first == polygon.at(start_index) && other == polygon.at(end_index)) + { + out_coords_increasing = true; + } + else if (first == polygon.at(end_index) && other == polygon.at(start_index)) + { + out_coords_increasing = false; + } + else + { + found = false; + } + + return found; +} + + +} // namespace OpenOrienteering diff --git a/src/core/objects/boolean_tool.h b/src/core/objects/boolean_tool.h new file mode 100644 index 0000000..fee7888 --- /dev/null +++ b/src/core/objects/boolean_tool.h @@ -0,0 +1,273 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_BOOLEAN_TOOL_H +#define OPENORIENTEERING_BOOLEAN_TOOL_H + +#include +#include + +#include + +#include + +namespace OpenOrienteering { + +class CombinedUndoStep; +class Map; +class PathCoord; +class PathObject; +class PathPart; + + +/** + * Wraps some helper functions for boolean operations. + * + * The implementation of the boolean operation tools is based on the Clipper + * library (aka libpolyclipping) (http://www.angusj.com/delphi/clipper.php). + * + * Because Clipper does not support bezier curves, areas are clipped as + * polygonal approximations, and after clipping we try to rebuild the curves. + * + * @todo This class should get unit tests. + */ +class BooleanTool +{ +public: + /** + * A list of PathObject elements. + */ + typedef std::vector< PathObject* > PathObjects; + + /** + * Types of boolean operation. + */ + enum Operation + { + Union, + Intersection, + Difference, + XOr, + MergeHoles + }; + + /** + * Constructs a tool for the given operation and map. + * + * map must not be nullptr (for some member functions). + */ + BooleanTool(Operation op, Map* map); + + /** + * Executes the operation on the selected objects in the map. + * + * The first selected object is treated special and must be a path. + * + * @return True on success, false on error + */ + bool execute(); + + /** + * Executes the operation per symbol on the selected objects in the map. + * + * Executes the operation independently for every group of path objects + * which have got the same symbol. Objects which are not of type + * Object::Path are ignored. + * + * Errors during the operation are ignored, too. The original objects the + * operation failed for remain unchanged. The operation continues for other + * groups of objects. + * + * @return True if the map was changed, false otherwise. + */ + bool executePerSymbol(); + + /** + * Executes the operation on particular objects. + * + * This function does not (actively) change the collection of objects in the map + * or the selection. + * + * @param subject The primary affected object. + * @param in_objects All objects to operate on. Must contain subject. + * @param out_objects The resulting collection of objects. + */ + bool executeForObjects( + PathObject* subject, + PathObjects& in_objects, + PathObjects& out_objects ); + + /** + * Executes the Intersection and Difference operation on the given line object. + * + * @param area The primary object, here defining a boundary. + * @param line The line object to operate on. + * @param out_objects The resulting collection of objects. + */ + void executeForLine( + const PathObject* area, + const PathObject* line, + PathObjects& out_objects ); + +private: + typedef std::pair< const PathPart*, const PathCoord* > PathCoordInfo; + + typedef QHash< ClipperLib::IntPoint, PathCoordInfo > PolyMap; + + /** + * Executes the operation on particular objects, and provides undo steps. + * + * This function changes the collection of objects in the map and the selection. + * + * @param subject The primary affected object. + * @param in_objects All objects to operate on. Must contain subject. + * @param out_objects The resulting collection of objects. + * @param undo_step A combined undo step which will be filled with sub steps. + */ + bool executeForObjects( + PathObject* subject, + PathObjects& in_objects, + PathObjects& out_objects, + CombinedUndoStep& undo_step ); + + /** + * Converts a ClipperLib::PolyTree to PathObjects. + * + * @see BooleanTool::outerPolyNodeToPathObjects() + */ + void polyTreeToPathObjects( + const ClipperLib::PolyTree& tree, + PathObjects& out_objects, + const PathObject* proto, + const PolyMap& polymap ); + + /** + * Converts a ClipperLib::PolyNode to PathObjects. + * + * The given ClipperLib::PolyNode must represent an outer polygon, not a hole. + * + * This method operates recursively on all outer children. + */ + void outerPolyNodeToPathObjects( + const ClipperLib::PolyNode& node, + PathObjects& out_objects, + const PathObject* proto, + const PolyMap& polymap ); + + /** + * Constructs ClipperLib::Paths from a PathObject. + */ + static void pathObjectToPolygons( + const PathObject* object, + ClipperLib::Paths& polygons, + PolyMap& polymap ); + + /** + * Reconstructs a PathObject from a polygon given as ClipperLib::Path. + * + * Curves are reconstructed with the help of the polymap, mapping locations + * to path coords of the original objects. + */ + static void polygonToPathPart( + const ClipperLib::Path& polygon, + const PolyMap& polymap, + PathObject* object ); + + /** + * Tries to reconstruct a straight or curved segment with given start and + * end indices from the polygon. + * The first coordinate of the segment is assumed to be already added. + */ + static void rebuildSegment( + ClipperLib::Path::size_type start_index, + ClipperLib::Path::size_type end_index, + bool sequence_increasing, + const ClipperLib::Path& polygon, + const PolyMap& polymap, + PathObject* object ); + + /** + * Approximates a curved segment from the result polygon alone. + */ + static void rebuildSegmentFromPathOnly( + const ClipperLib::IntPoint& start_point, + const ClipperLib::IntPoint& second_point, + const ClipperLib::IntPoint& second_last_point, + const ClipperLib::IntPoint& end_point, + PathObject* object ); + + /** + * Special case of rebuildSegment() for straight or very short lines. + */ + static void rebuildTwoIndexSegment( + ClipperLib::Path::size_type start_index, + ClipperLib::Path::size_type end_index, + bool sequence_increasing, + const ClipperLib::Path& polygon, + const PolyMap& polymap, + PathObject* object ); + + /** + * Reconstructs one polygon coordinate and adds it to the object. + * + * Uses the polymap to check whether the coorinate should be a dash point. + */ + static void rebuildCoordinate( + ClipperLib::Path::size_type index, + const ClipperLib::Path& polygon, + const PolyMap& polymap, + PathObject* object, + bool start_new_part = false ); + + /** + * Compares a PathObject segment to a ClipperLib::Path polygon segment. + * + * Returns true if the segments match. In this case, the out_... parameters are set. + * + * @param original The original PathObject. + * @param coord_index The index of the segment start at the original. + * @param polygon The ClipperLib::Path polygon. + * @param start_index The start of the segement at the polygon. + * @param end_index The end of the segment at the polygon. + * @param out_coords_increasing If the segments match, will be set to + * either true if a matching segment's point at coord_index corresponds to the point at start_index, + * or false otherwise. + * @param out_is_curve If the segments match, will be set to + * either true if the original segment is a curve, + * or false otherwise. + */ + static bool checkSegmentMatch( + const PathObject* original, + int coord_index, + const ClipperLib::Path& polygon, + ClipperLib::Path::size_type start_index, + ClipperLib::Path::size_type end_index, + bool& out_coords_increasing, + bool& out_is_curve ); + + const Operation op; + Map* const map; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/objects/object.cpp b/src/core/objects/object.cpp new file mode 100644 index 0000000..87c1301 --- /dev/null +++ b/src/core/objects/object.cpp @@ -0,0 +1,3305 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "object.h" + +#include + +#include +#include +#include +#include +#include + +#include + +#include "settings.h" +#include "core/map.h" +#include "core/objects/text_object.h" +#include "core/renderables/renderable.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "core/symbols/text_symbol.h" +#include "fileformats/file_format.h" +#include "fileformats/file_import_export.h" +#include "util/util.h" +#include "util/xml_stream_util.h" + + +// ### A namespace which collects various string constants of type QLatin1String. ### + +namespace literal +{ + static const QLatin1String object("object"); + static const QLatin1String symbol("symbol"); + static const QLatin1String type("type"); + static const QLatin1String text("text"); + static const QLatin1String count("count"); + static const QLatin1String coords("coords"); + static const QLatin1String h_align("h_align"); + static const QLatin1String v_align("v_align"); + static const QLatin1String pattern("pattern"); + static const QLatin1String rotation("rotation"); + static const QLatin1String tags("tags"); +} + + + +namespace OpenOrienteering { + +// ### Object implementation ### + +Object::Object(Object::Type type, const Symbol* symbol) +: type(type), + symbol(symbol), + map(nullptr), + output_dirty(true), + extent(), + output(*this) +{ + // nothing +} + +Object::Object(Object::Type type, const Symbol* symbol, const MapCoordVector& coords, Map* map) + : type(type), + symbol(symbol), + coords(coords), + map(map), + output_dirty(true), + extent(), + output(*this) +{ + // nothing +} + +Object::Object(const Object& proto) + : type(proto.type) + , symbol(proto.symbol) + , coords(proto.coords) + , map(nullptr) + , object_tags(proto.object_tags) + , output_dirty(true) + , extent(proto.extent) + , output(*this) +{ + // nothing +} + +Object::~Object() +{ + // nothing +} + +void Object::copyFrom(const Object& other) +{ + if (&other == this) + return; + + if (type != other.type) + throw std::invalid_argument(Q_FUNC_INFO); + + symbol = other.symbol; + coords = other.coords; + // map unchanged! + object_tags = other.object_tags; + output_dirty = true; + extent = other.extent; +} + +bool Object::equals(const Object* other, bool compare_symbol) const +{ + if (type != other->type) + return false; + if (compare_symbol) + { + if (bool(symbol) != bool(other->symbol)) + return false; + if (symbol && !symbol->equals(other->symbol)) + return false; + } + + if (type == Text) + { + if (coords.front() != other->coords.front()) + return false; + } + else + { + if (coords.size() != other->coords.size()) + return false; + for (size_t i = 0, end = coords.size(); i < end; ++i) + { + if (coords[i] != other->coords[i]) + return false; + } + } + + if (object_tags != other->object_tags) + return false; + + if (type == Point) + { + const PointObject* point_this = static_cast(this); + const PointObject* point_other = static_cast(other); + + // Make sure that compared values are greater than 1, see qFuzzyCompare + float rotation_a = point_this->getRotation(); + float rotation_b = point_other->getRotation(); + while (rotation_a < 1) + { + rotation_a += 100; + rotation_b += 100; + } + if (!qFuzzyCompare(rotation_a, rotation_b)) + return false; + } + else if (type == Path) + { + const PathObject* path_this = static_cast(this); + const PathObject* path_other = static_cast(other); + + // Make sure that compared values are greater than 1, see qFuzzyCompare + float rotation_a = path_this->getPatternRotation(); + float rotation_b = path_other->getPatternRotation(); + while (rotation_a < 1) + { + rotation_a += 100; + rotation_b += 100; + } + if (!qFuzzyCompare(rotation_a, rotation_b)) + return false; + + if (path_this->getPatternOrigin() != path_other->getPatternOrigin()) + return false; + } + else if (type == Text) + { + const TextObject* text_this = static_cast(this); + const TextObject* text_other = static_cast(other); + + if (text_this->getBoxSize() != text_other->getBoxSize()) + return false; + if (text_this->getText().compare(text_other->getText(), Qt::CaseSensitive) != 0) + return false; + if (text_this->getHorizontalAlignment() != text_other->getHorizontalAlignment()) + return false; + if (text_this->getVerticalAlignment() != text_other->getVerticalAlignment()) + return false; + + // Make sure that compared values are greater than 1, see qFuzzyCompare + float rotation_a = text_this->getRotation(); + float rotation_b = text_other->getRotation(); + while (rotation_a < 1) + { + rotation_a += 100; + rotation_b += 100; + } + if (!qFuzzyCompare(rotation_a, rotation_b)) + return false; + } + + return true; +} + + + +bool Object::validate() const +{ + return true; +} + + + +PointObject* Object::asPoint() +{ + Q_ASSERT(type == Point); + return static_cast(this); +} + +const PointObject* Object::asPoint() const +{ + Q_ASSERT(type == Point); + return static_cast(this); +} + +PathObject* Object::asPath() +{ + Q_ASSERT(type == Path); + return static_cast(this); +} + +const PathObject* Object::asPath() const +{ + Q_ASSERT(type == Path); + return static_cast(this); +} + +TextObject* Object::asText() +{ + Q_ASSERT(type == Text); + return static_cast(this); +} + +const TextObject* Object::asText() const +{ + Q_ASSERT(type == Text); + return static_cast(this); +} + +#ifndef NO_NATIVE_FILE_FORMAT + +void Object::load(QIODevice* file, int version, Map* map) +{ + this->map = map; + + int symbol_index; + file->read((char*)&symbol_index, sizeof(int)); + if (map) + { + Symbol* read_symbol = map->getSymbol(symbol_index); + if (read_symbol) + symbol = read_symbol; + } + + int num_coords; + file->read((char*)&num_coords, sizeof(int)); + coords.clear(); + coords.reserve(num_coords); + + LegacyMapCoord coord; + for (int i = 0; i < num_coords; ++i) + { + file->read((char*)&coord, sizeof(LegacyMapCoord)); + coords.emplace_back(coord); + } + + if (version <= 8) + { + bool path_closed; + file->read((char*)&path_closed, sizeof(bool)); + if (path_closed) + coords[coords.size() - 1].setClosePoint(true); + } + + if (version >= 8) + { + if (type == Point) + { + PointObject* point = reinterpret_cast(this); + const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); + if (point_symbol->isRotatable()) + { + float rotation; + file->read((char*)&rotation, sizeof(float)); + point->setRotation(rotation); + } + } + else if (type == Path) + { + PathObject* path = reinterpret_cast(this); + if (version >= 21) + { + float rotation; + file->read((char*)&rotation, sizeof(float)); + path->setPatternRotation(rotation); + LegacyMapCoord origin; + file->read((char*)&origin, sizeof(LegacyMapCoord)); + path->setPatternOrigin(origin); + } + } + else if (type == Text) + { + TextObject* text = reinterpret_cast(this); + float rotation; + file->read((char*)&rotation, sizeof(float)); + text->setRotation(rotation); + + int temp; + file->read((char*)&temp, sizeof(int)); + text->setHorizontalAlignment((TextObject::HorizontalAlignment)temp); + file->read((char*)&temp, sizeof(int)); + text->setVerticalAlignment((TextObject::VerticalAlignment)temp); + + QString str; + loadString(file, str); + text->setText(str); + + if (coords.size() > 1) + { + auto raw_size = coords[1]; + auto w = MapCoord::boundsOffset().x + raw_size.nativeX(); + auto h = MapCoord::boundsOffset().y + raw_size.nativeY(); + text->setBoxSize(MapCoord::fromNative64(w, h)); + } + } + } + + if (type == Path) + { + PathObject* path = reinterpret_cast(this); + path->recalculateParts(); + } + + output_dirty = true; +} + +#endif + +void Object::save(QXmlStreamWriter& xml) const +{ + XmlElementWriter object_element(xml, literal::object); + object_element.writeAttribute(literal::type, type); + int symbol_index = -1; + if (map) + symbol_index = map->findSymbolIndex(symbol); + if (symbol_index != -1) + object_element.writeAttribute(literal::symbol, symbol_index); + + if (type == Point) + { + const PointObject* point = reinterpret_cast(this); + const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); + if (point_symbol->isRotatable()) + object_element.writeAttribute(literal::rotation, point->getRotation()); + } + else if (type == Text) + { + const TextObject* text = reinterpret_cast(this); + object_element.writeAttribute(literal::rotation, text->getRotation()); + object_element.writeAttribute(literal::h_align, text->getHorizontalAlignment()); + object_element.writeAttribute(literal::v_align, text->getVerticalAlignment()); + // For compatibility, we must keep the box size in the second coord ATM. + /// \todo Save box size separately + auto object = const_cast(this); + if (text->hasSingleAnchor()) + { + object->coords.resize(1); + } + else + { + object->coords.resize(2); + object->coords.back() = text->getBoxSize(); + } + } + + if (!object_tags.empty()) + { + XmlElementWriter tags_element(xml, literal::tags); + tags_element.write(object_tags); + } + + { + // Scope of coords XML element + XmlElementWriter coords_element(xml, literal::coords); + coords_element.write(coords); + } + + if (type == Path) + { + const PathObject* path = reinterpret_cast(this); + XmlElementWriter pattern_element(xml, literal::pattern); + pattern_element.writeAttribute(literal::rotation, path->getPatternRotation()); + path->getPatternOrigin().save(xml); + } + else if (type == Text) + { + const TextObject* text = reinterpret_cast(this); + xml.writeTextElement(literal::text, text->getText()); + } +} + +Object* Object::load(QXmlStreamReader& xml, Map* map, const SymbolDictionary& symbol_dict, const Symbol* symbol) +{ + Q_ASSERT(xml.name() == literal::object); + + XmlElementReader object_element(xml); + + Object::Type object_type = object_element.attribute(literal::type); + Object* object = Object::getObjectForType(object_type); + if (!object) + throw FileFormatException(::OpenOrienteering::ImportExport::tr("Error while loading an object of type %1.").arg(object_type)); + + object->map = map; + + if (symbol) + object->symbol = symbol; + else + { + QString symbol_id = object_element.attribute(literal::symbol); + object->symbol = symbol_dict[symbol_id]; // FIXME: cannot work for forward references + // NOTE: object->symbol may be nullptr. + } + + if (!object->symbol || !object->symbol->isTypeCompatibleTo(object)) + { + // Throwing an exception will cause loading to fail. + // Rather than not loading the broken file at all, + // use the same symbols which are used for importing GPS tracks etc. + // FIXME: Implement a way to send a warning to the user. + switch (object_type) + { + case Point: + object->symbol = Map::getUndefinedPoint(); + break; + case Path: + object->symbol = Map::getUndefinedLine(); + break; + case Text: + object->symbol = Map::getUndefinedText(); + break; + default: + throw FileFormatException( + ::OpenOrienteering::ImportExport::tr("Unable to find symbol for object at %1:%2."). + arg(xml.lineNumber()).arg(xml.columnNumber()) ); + } + } + + if (object_type == Point) + { + PointObject* point = reinterpret_cast(object); + const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); + if (point_symbol && point_symbol->isRotatable()) + point->setRotation(object_element.attribute(literal::rotation)); + else if (!point_symbol) + throw FileFormatException(::OpenOrienteering::ImportExport::tr("Point object with undefined or wrong symbol at %1:%2.").arg(xml.lineNumber()).arg(xml.columnNumber())); + } + else if (object_type == Text) + { + TextObject* text = reinterpret_cast(object); + text->setRotation(object_element.attribute(literal::rotation)); + text->setHorizontalAlignment(object_element.attribute(literal::h_align)); + text->setVerticalAlignment(object_element.attribute(literal::v_align)); + } + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::coords) + { + XmlElementReader coords_element(xml); + try { + if (object_type == Text) + { + coords_element.readForText(object->coords); + if (object->coords.size() > 1) + static_cast(object)->setBoxSize(object->coords[1]); + } + else + { + coords_element.read(object->coords); + } + } + catch (FileFormatException& e) + { + throw FileFormatException(::OpenOrienteering::ImportExport::tr("Error while loading an object of type %1 at %2:%3: %4"). + arg(object_type).arg(xml.lineNumber()).arg(xml.columnNumber()).arg(e.message())); + } + } + else if (xml.name() == literal::pattern && object_type == Path) + { + XmlElementReader element(xml); + + PathObject* path = reinterpret_cast(object); + path->setPatternRotation(element.attribute(literal::rotation)); + while (xml.readNextStartElement()) + { + if (xml.name() == XmlStreamLiteral::coord) + { + try + { + path->setPatternOrigin(MapCoord::load(xml)); + } + catch (const std::range_error& e) + { + /// \todo Add a warning, but don't throw - throwing lets loading fail. + // throw FileFormatException(::OpenOrienteering::MapCoord::tr(e.what())); + qDebug("%s", e.what()); + } + } + else + { + xml.skipCurrentElement(); // unknown + } + } + } + else if (xml.name() == literal::text && object_type == Text) + { + TextObject* text = reinterpret_cast(object); + text->setText(xml.readElementText()); + } + else if (xml.name() == literal::tags) + { + XmlElementReader(xml).read(object->object_tags); + } + else + xml.skipCurrentElement(); // unknown + } + + if (object_type == Path) + { + PathObject* path = reinterpret_cast(object); + path->recalculateParts(); + } + object->output_dirty = true; + + if (map && + ( object->coords.empty() + || !object->coords.front().isRegular() + || !object->coords.back().isRegular() ) ) + { + map->markAsIrregular(object); + } + + return object; +} + +void Object::forceUpdate() const +{ + output_dirty = true; + update(); +} + +bool Object::update() const +{ + if (!output_dirty) + return false; + + Symbol::RenderableOptions options = Symbol::RenderNormal; + if (map) + { + options = QFlag(map->renderableOptions()); + if (extent.isValid()) + map->setObjectAreaDirty(extent); + } + + output.deleteRenderables(); + + extent = QRectF(); + + updateEvent(); + + createRenderables(output, options); + + Q_ASSERT(extent.right() < 60000000); // assert if bogus values are returned + output_dirty = false; + + if (map) + { + map->insertRenderablesOfObject(this); + if (extent.isValid()) + map->setObjectAreaDirty(extent); + } + + return true; +} + +void Object::updateEvent() const +{ + // nothing here +} + +void Object::createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const +{ + symbol->createRenderables(this, VirtualCoordVector(coords), output, options); +} + +void Object::move(qint32 dx, qint32 dy) +{ + for (MapCoord& coord : coords) + { + coord.setNativeX(dx + coord.nativeX()); + coord.setNativeY(dy + coord.nativeY()); + } + + setOutputDirty(); +} + +void Object::move(MapCoord offset) +{ + for (MapCoord& coord : coords) + { + coord += offset; + } + + setOutputDirty(); +} + +void Object::scale(MapCoordF center, double factor) +{ + for (MapCoord& coord : coords) + { + coord.setX(center.x() + (coord.x() - center.x()) * factor); + coord.setY(center.y() + (coord.y() - center.y()) * factor); + } + + setOutputDirty(); +} + +void Object::scale(double factor_x, double factor_y) +{ + for (MapCoord& coord : coords) + { + coord.setX(coord.x() * factor_x); + coord.setY(coord.y() * factor_y); + } + + setOutputDirty(); +} + +void Object::rotateAround(MapCoordF center, double angle) +{ + double sin_angle = sin(angle); + double cos_angle = cos(angle); + + int coords_size = coords.size(); + /// \todo range-for loop + for (int c = 0; c < coords_size; ++c) + { + MapCoordF center_to_coord = MapCoordF(coords[c].x() - center.x(), coords[c].y() - center.y()); + coords[c].setX(center.x() + cos_angle * center_to_coord.x() + sin_angle * center_to_coord.y()); + coords[c].setY(center.y() - sin_angle * center_to_coord.x() + cos_angle * center_to_coord.y()); + } + + if (type == Point) + { + PointObject* point = reinterpret_cast(this); + const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); + if (point_symbol->isRotatable()) + point->setRotation(point->getRotation() + angle); + } + else if (type == Text) + { + TextObject* text = reinterpret_cast(this); + text->setRotation(text->getRotation() + angle); + } + setOutputDirty(); +} + +void Object::rotate(double angle) +{ + double sin_angle = sin(angle); + double cos_angle = cos(angle); + + int coords_size = coords.size(); + /// \todo range-for loop + for (int c = 0; c < coords_size; ++c) + { + MapCoord coord = coords[c]; + coords[c].setX(cos_angle * coord.x() + sin_angle * coord.y()); + coords[c].setY(cos_angle * coord.y() - sin_angle * coord.x()); + } + + if (type == Point) + { + PointObject* point = reinterpret_cast(this); + const PointSymbol* point_symbol = reinterpret_cast(point->getSymbol()); + if (point_symbol->isRotatable()) + point->setRotation(point->getRotation() + angle); + } + else if (type == Text) + { + TextObject* text = reinterpret_cast(this); + text->setRotation(text->getRotation() + angle); + } + setOutputDirty(); +} + + +int Object::isPointOnObject(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection) const +{ + Symbol::Type type = symbol->getType(); + Symbol::Type contained_types = symbol->getContainedTypes(); + + // Points + if (type == Symbol::Point) + { + if (!extended_selection) + return (coord.distanceSquaredTo(MapCoordF(coords[0])) <= tolerance) ? Symbol::Point : Symbol::NoSymbol; + else + return extent.contains(coord) ? Symbol::Point : Symbol::NoSymbol; + } + + // First check using extent + float extent_extension = ((contained_types & Symbol::Line) || treat_areas_as_paths) ? tolerance : 0; + if (coord.x() < extent.left() - extent_extension) return Symbol::NoSymbol; + if (coord.y() < extent.top() - extent_extension) return Symbol::NoSymbol; + if (coord.x() > extent.right() + extent_extension) return Symbol::NoSymbol; + if (coord.y() > extent.bottom() + extent_extension) return Symbol::NoSymbol; + + if (type == Symbol::Text) + { + // Texts + const TextObject* text_object = reinterpret_cast(this); + return (text_object->calcTextPositionAt(coord, true) != -1) ? Symbol::Text : Symbol::NoSymbol; + } + else + { + // Path objects + const PathObject* path = reinterpret_cast(this); + return path->isPointOnPath(coord, tolerance, treat_areas_as_paths, true); + } +} + +void Object::takeRenderables() +{ + output.takeRenderables(); +} + +void Object::clearRenderables() +{ + output.deleteRenderables(); + extent = QRectF(); +} + +bool Object::setSymbol(const Symbol* new_symbol, bool no_checks) +{ + if (!no_checks && new_symbol) + { + if (!new_symbol->isTypeCompatibleTo(this)) + return false; + } + + symbol = new_symbol; + setOutputDirty(); + return true; +} + +Object* Object::getObjectForType(Object::Type type, const Symbol* symbol) +{ + if (type == Point) + return new PointObject(symbol); + else if (type == Path) + return new PathObject(symbol); + else if (type == Text) + return new TextObject(symbol); + else + { + Q_ASSERT(false); + return nullptr; + } +} + +void Object::setTags(const Object::Tags& tags) +{ + if (object_tags != tags) + { + object_tags = tags; + if (map) + { + map->setObjectsDirty(); + if (map->isObjectSelected(this)) + map->emitSelectionEdited(); + } + } +} + +void Object::setTag(const QString& key, const QString& value) +{ + if (!object_tags.contains(key) || object_tags.value(key) != value) + { + object_tags.insert(key, value); + if (map) + { + map->setObjectsDirty(); + if (map->isObjectSelected(this)) + map->emitSelectionEdited(); + } + } +} + +void Object::removeTag(const QString& key) +{ + if (object_tags.contains(key)) + { + object_tags.remove(key); + if (map) + map->setObjectsDirty(); + } +} + + +void Object::includeControlPointsRect(QRectF& rect) const +{ + if (type == Object::Path) + { + const PathObject* path = asPath(); + int size = path->getCoordinateCount(); + for (int i = 0; i < size; ++i) + rectInclude(rect, QPointF(path->coords[i])); + } + else if (type == Object::Text) + { + const TextObject* text = asText(); + std::vector text_handles(text->controlPoints()); + for (auto& text_handle : text_handles) + rectInclude(rect, text_handle); + } +} + + + +// ### PathPart ### + +/* + * Some headers may not want to include object.h but rather rely on + * PathPartVector::size_type being a std::size_t. + */ +static_assert(std::is_same::value, + "PathCoordVector::size_type is not std::size_t"); + +PathPart::PathPart(PathObject& path, const VirtualPath& proto) + : VirtualPath(path.getRawCoordinateVector(), proto.first_index, proto.last_index) + , path(&path) +{ + if (!proto.path_coords.empty()) + { + path_coords.reserve(proto.path_coords.size()); + path_coords.insert(end(path_coords), begin(proto.path_coords), end(proto.path_coords)); + } +} + +void PathPart::setClosed(bool closed, bool may_use_existing_close_point) +{ + Q_ASSERT(!empty()); + + if (!isClosed() && closed) + { + if (size() == 1 || !may_use_existing_close_point || + !path->coords[first_index].isPositionEqualTo(path->coords[last_index])) + { + path->coords[last_index].setHolePoint(false); + path->coords[last_index].setClosePoint(false); + path->coords.insert(path->coords.begin() + last_index + 1, path->coords[first_index]); + path->partSizeChanged(path->findPartForIndex(first_index), 1); + } + path->setClosingPoint(last_index, path->coords[first_index]); + path->setOutputDirty(); + } + else if (isClosed() && !closed) + { + path->coords[last_index].setClosePoint(false); + path->setOutputDirty(); + } +} + +void PathPart::connectEnds() +{ + if (!isClosed()) + { + path->coords[first_index] += path->coords[last_index]; + path->coords[first_index] /= 2; + path->setClosingPoint(last_index, path->coords[first_index]); + path->setOutputDirty(); + } +} + +void PathPart::reverse() +{ + MapCoordVector& coords = path->coords; + + bool set_last_hole_point = false; + auto half = (first_index + last_index + 1) / 2; + for (auto c = first_index; c <= last_index; ++c) + { + MapCoord coord = coords[c]; + if (c < half) + { + auto mirror_index = last_index - (c - first_index); + coords[c] = coords[mirror_index]; + coords[mirror_index] = coord; + } + + if (!(c == first_index && isClosed()) && coords[c].isCurveStart()) + { + Q_ASSERT((c - first_index) >= 3); + coords[c - 3].setCurveStart(true); + if (!(c == last_index && isClosed())) + coords[c].setCurveStart(false); + } + else if (coords[c].isHolePoint()) + { + coords[c].setHolePoint(false); + if (c >= first_index + 1) + coords[c - 1].setHolePoint(true); + else + set_last_hole_point = true; + } + } + + if (coords[first_index].isClosePoint()) + { + coords[first_index].setClosePoint(false); + coords[last_index].setClosePoint(true); + } + + if (set_last_hole_point) + coords[last_index].setHolePoint(true); + + path->setOutputDirty(); +} + +PathPartVector PathPart::calculatePathParts(const VirtualCoordVector& coords) +{ + PathPartVector parts; + PathCoordVector path_coords(coords); + auto last = coords.size(); + auto part_start = MapCoordVector::size_type { 0 }; + while (part_start != last) + { + auto part_end = path_coords.update(part_start); + parts.emplace_back(coords, part_start, part_end); + parts.back().path_coords.swap(path_coords); + part_start = part_end + 1; + } + return parts; +} + + + +//### PathPartVector ### + +// Not inline because it is to be used by reference. +bool PathPartVector::compareEndIndex(const PathPart& part, VirtualPath::size_type index) +{ + return part.last_index+1 <= index; +} + + + +// ### PathObject ### + +PathObject::PathObject(const Symbol* symbol) + : Object(Object::Path, symbol) + , pattern_rotation(0.0) + , pattern_origin(0, 0) +{ + Q_ASSERT(!symbol || (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined)); +} + +PathObject::PathObject(const Symbol* symbol, const MapCoordVector& coords, Map* map) + : Object(Object::Path, symbol, coords, map) + , pattern_rotation(0.0) + , pattern_origin(0, 0) +{ + Q_ASSERT(!symbol || (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined)); + recalculateParts(); +} + +PathObject::PathObject(const Symbol* symbol, const PathObject& proto, MapCoordVector::size_type piece) + : Object { Object::Path, symbol } + , pattern_rotation { 0.0f } + , pattern_origin { 0, 0 } +{ + auto begin = proto.coords.begin() + piece; + auto part = proto.findPartForIndex(piece); + if (piece == part->last_index) + { + if (part->isClosed()) + begin = proto.coords.begin() + part->first_index; + else + begin = proto.coords.begin() + part->prevCoordIndex(piece); + } + + auto end = begin + (begin->isCurveStart() ? 4 : 2); + + std::for_each(begin, end, [this](MapCoord c) + { + c.setHolePoint(false); + c.setClosePoint(false); + addCoordinate(c); + }); + + coords.back().setHolePoint(true); +} + +PathObject::PathObject(const PathObject& proto) + : Object(proto) + , pattern_rotation(proto.pattern_rotation) + , pattern_origin(proto.pattern_origin) +{ + path_parts.reserve(proto.path_parts.size()); + for (const PathPart& part : proto.path_parts) + { + path_parts.emplace_back(*this, part); + } +} + +PathObject::PathObject(const PathPart &proto_part) + : Object(*proto_part.path) + , pattern_rotation(proto_part.path->pattern_rotation) + , pattern_origin(proto_part.path->pattern_origin) +{ + auto begin = proto_part.path->coords.begin(); + coords.reserve(proto_part.size()); + coords.assign(begin + proto_part.first_index, begin + (proto_part.last_index+1)); + path_parts.emplace_back(*this, proto_part); +} + +PathObject* PathObject::duplicate() const +{ + return new PathObject(*this); +} + +void PathObject::copyFrom(const Object& other) +{ + if (&other == this) + return; + + Object::copyFrom(other); + + const PathObject& other_path = *other.asPath(); + pattern_rotation = other_path.getPatternRotation(); + pattern_origin = other_path.getPatternOrigin(); + + path_parts.clear(); + path_parts.reserve(other_path.path_parts.size()); + for (const PathPart& part : other_path.path_parts) + { + path_parts.emplace_back(*this, part); + } +} + + + +bool PathObject::validate() const +{ + return std::all_of(begin(path_parts), end(path_parts), [](auto &part) { + return !part.isClosed() + || part.coords[part.first_index] == part.coords[part.last_index]; + }); +} + + + +void PathObject::normalize() +{ + for (MapCoordVector::size_type i = 0; i < coords.size(); ++i) + { + if (coords[i].isCurveStart()) + { + if (i+3 >= getCoordinateCount()) + { + coords[i].setCurveStart(false); + continue; + } + + if (coords[i + 1].isClosePoint() || coords[i + 1].isHolePoint() || + coords[i + 2].isClosePoint() || coords[i + 2].isHolePoint()) + { + coords[i].setCurveStart(false); + continue; + } + + coords[i + 1].setCurveStart(false); + coords[i + 1].setDashPoint(false); + coords[i + 2].setCurveStart(false); + coords[i + 2].setDashPoint(false); + i += 2; + } + + if (i > 0 && coords[i].isHolePoint()) + { + if (coords[i-1].isHolePoint()) + deleteCoordinate(i, false); + } + } +} + +bool PathObject::intersectsBox(const QRectF& box) const +{ + // Check path parts for an intersection with box + if (std::any_of(begin(path_parts), end(path_parts), [&box](const PathPart& part) { return part.intersectsBox(box); })) + { + return true; + }; + + // If this is an area, additionally check if the area contains the box + if (getSymbol()->getContainedTypes() & Symbol::Area) + { + return isPointOnObject(MapCoordF(box.center()), 0, false, false); + } + + return false; +} + +PathPartVector::const_iterator PathObject::findPartForIndex(MapCoordVector::size_type coords_index) const +{ + return std::lower_bound(begin(path_parts), end(path_parts), coords_index, PathPartVector::compareEndIndex); +} + +PathPartVector::iterator PathObject::findPartForIndex(MapCoordVector::size_type coords_index) +{ + setOutputDirty(); + return std::lower_bound(begin(path_parts), end(path_parts), coords_index, PathPartVector::compareEndIndex); +} + +PathPartVector::size_type PathObject::findPartIndexForIndex(MapCoordVector::size_type coords_index) const +{ + for (PathPartVector::size_type i = 0; i < path_parts.size(); ++i) + { + if (path_parts[i].first_index <= coords_index && path_parts[i].last_index >= coords_index) + return i; + } + Q_ASSERT(false); + return 0; +} + +PathCoord PathObject::findPathCoordForIndex(MapCoordVector::size_type index) const +{ + auto part = findPartForIndex(index); + if (part != end(path_parts)) + { + return *(std::lower_bound(begin(part->path_coords), end(part->path_coords), index, PathCoord::indexLessThanValue)); + } + return path_parts.front().path_coords.front(); +} + +bool PathObject::isCurveHandle(MapCoordVector::size_type index) const +{ + return ( index < coords.size() && + !coords[index].isCurveStart() && + ( + ( index > 0 && coords[index-1].isCurveStart() ) || + ( index > 1 && coords[index-2].isCurveStart() ) + ) + ); +} + +void PathObject::deletePart(PathPartVector::size_type part_index) +{ + setOutputDirty(); + + auto part = begin(path_parts) + part_index; + auto first_index = part->first_index; + auto end_index = part->last_index + 1; + auto first_coord = begin(coords); + coords.erase(first_coord + first_index, first_coord + end_index); + partSizeChanged(part, first_index - end_index); + path_parts.erase(part); +} + +void PathObject::partSizeChanged(PathPartVector::iterator part, MapCoordVector::difference_type change) +{ + Q_ASSERT(isOutputDirty()); + + part->last_index += change; + auto last = end(path_parts); + for (++part; part != last; ++part) + { + part->first_index += change; + part->last_index += change; + } +} + + +void PathObject::transform(const QTransform& t) +{ + if (t.isIdentity()) + return; + + for (auto& coord : coords) + { + const auto p = t.map(MapCoordF{coord}); + coord.setX(p.x()); + coord.setY(p.y()); + } + pattern_origin = MapCoord{t.map(MapCoordF{getPatternOrigin()})}; + setOutputDirty(); +} + + + +void PathObject::setPatternRotation(float rotation) +{ + pattern_rotation = rotation; + setOutputDirty(); +} + +void PathObject::setPatternOrigin(const MapCoord& origin) +{ + pattern_origin = origin; + setOutputDirty(); +} + +void PathObject::calcClosestPointOnPath( + MapCoordF coord, + float& out_distance_sq, + PathCoord& out_path_coord, + MapCoordVector::size_type start_index, + MapCoordVector::size_type end_index) const +{ + update(); + + auto bound = std::numeric_limits::max(); + for (const auto& part : path_parts) + { + if (part.first_index <= end_index && part.last_index >= start_index) /// \todo Legacy compatibility, review/remove + { + auto path_coord = part.findClosestPointTo(coord, out_distance_sq, bound, start_index, end_index); + if (out_distance_sq < bound) + { + bound = out_distance_sq; + out_path_coord = path_coord; + } + } + } +} + +void PathObject::calcClosestCoordinate(MapCoordF coord, float& out_distance_sq, MapCoordVector::size_type& out_index) const +{ + update(); + + auto coords_size = coords.size(); + if (coords_size == 0) + { + out_distance_sq = -1; + out_index = -1; + return; + } + + // NOTE: do not try to optimize this by starting with index 1, it will overlook curve starts this way + out_distance_sq = 999999; + out_index = 0; + for (MapCoordVector::size_type i = 0; i < coords_size; ++i) + { + double length_sq = (coord - MapCoordF(coords[i])).lengthSquared(); + if (length_sq < out_distance_sq) + { + out_distance_sq = length_sq; + out_index = i; + } + + if (coords[i].isCurveStart()) + i += 2; + } +} + +MapCoordVector::size_type PathObject::subdivide(const PathCoord& path_coord) +{ + return subdivide(path_coord.index, path_coord.param); +} + +MapCoordVector::size_type PathObject::subdivide(MapCoordVector::size_type index, float param) +{ + Q_ASSERT(index < coords.size()); + + if (coords[index].isCurveStart()) + { + MapCoordF o0, o1, o2, o3, o4; + PathCoord::splitBezierCurve(MapCoordF(coords[index]), MapCoordF(coords[index+1]), + MapCoordF(coords[index+2]), MapCoordF(coords[index+3]), + param, o0, o1, o2, o3, o4); + coords[index + 1] = MapCoord(o0); + coords[index + 2] = MapCoord(o4); + addCoordinate(index + 2, MapCoord(o3)); + MapCoord middle_coord = MapCoord(o2); + middle_coord.setCurveStart(true); + addCoordinate(index + 2, middle_coord); + addCoordinate(index + 2, MapCoord(o1)); + Q_ASSERT(isOutputDirty()); + return index + 3; + } + else + { + addCoordinate(index + 1, MapCoord(MapCoordF(coords[index]) + (MapCoordF(coords[index+1]) - MapCoordF(coords[index])) * param)); + Q_ASSERT(isOutputDirty()); + return index + 1; + } +} + +bool PathObject::canBeConnected(const PathObject* other, double connect_threshold_sq) const +{ + for (const auto& part : path_parts) + { + if (part.isClosed()) + continue; + + for (const auto& other_part : other->path_parts) + { + if (other_part.isClosed()) + continue; + + if (coords[part.first_index].distanceSquaredTo(other->coords[other_part.first_index]) <= connect_threshold_sq) + return true; + else if (coords[part.first_index].distanceSquaredTo(other->coords[other_part.last_index]) <= connect_threshold_sq) + return true; + else if (coords[part.last_index].distanceSquaredTo(other->coords[other_part.first_index]) <= connect_threshold_sq) + return true; + else if (coords[part.last_index].distanceSquaredTo(other->coords[other_part.last_index]) <= connect_threshold_sq) + return true; + } + } + + return false; +} + +bool PathObject::connectIfClose(PathObject* other, double connect_threshold_sq) +{ + bool did_connect_path = false; + + auto num_parts = parts().size(); + auto num_other_parts = other->parts().size(); + std::vector other_parts; // Which parts have not been connected to this part yet? + other_parts.assign(num_other_parts, true); + for (std::size_t i = 0; i < num_parts; ++i) + { + if (path_parts[i].isClosed()) + continue; + + for (std::size_t k = 0; k < num_other_parts; ++k) + { + if (!other_parts[k] || other->path_parts[k].isClosed()) + continue; + + if (coords[path_parts[i].first_index].distanceSquaredTo(other->coords[other->path_parts[k].first_index]) <= connect_threshold_sq) + { + other->path_parts[k].reverse(); + connectPathParts(i, other, k, true); + } + else if (coords[path_parts[i].first_index].distanceSquaredTo(other->coords[other->path_parts[k].last_index]) <= connect_threshold_sq) + connectPathParts(i, other, k, true); + else if (coords[path_parts[i].last_index].distanceSquaredTo(other->coords[other->path_parts[k].first_index]) <= connect_threshold_sq) + connectPathParts(i, other, k, false); + else if (coords[path_parts[i].last_index].distanceSquaredTo(other->coords[other->path_parts[k].last_index]) <= connect_threshold_sq) + { + other->path_parts[k].reverse(); + connectPathParts(i, other, k, false); + } + else + continue; + + if (coords[path_parts[i].first_index].distanceSquaredTo(coords[path_parts[i].last_index]) <= connect_threshold_sq) + path_parts[i].connectEnds(); + + did_connect_path = true; + + other_parts[k] = false; + } + } + + if (did_connect_path) + { + // Copy over all remaining parts of the other object + getCoordinate(getCoordinateCount() - 1).setHolePoint(true); + for (std::size_t i = 0; i < num_other_parts; ++i) + { + if (other_parts[i]) + appendPathPart(other->parts()[i]); + } + + Q_ASSERT(isOutputDirty()); + } + + return did_connect_path; +} + +void PathObject::connectPathParts(PathPartVector::size_type part_index, const PathObject* other, PathPartVector::size_type other_part_index, bool prepend, bool merge_ends) +{ + Q_ASSERT(part_index < path_parts.size()); + PathPart& part = path_parts[part_index]; + PathPart& other_part = other->path_parts[other_part_index]; + Q_ASSERT(!part.isClosed() && !other_part.isClosed()); + + int other_part_size = other_part.size(); + int appended_part_size = other_part_size - (merge_ends ? 1 : 0); + coords.resize(coords.size() + appended_part_size); + + if (prepend) + { + for (auto i = coords.size() - 1; i >= part.first_index + appended_part_size; --i) + coords[i] = coords[i - appended_part_size]; + + MapCoord& join_coord = coords[part.first_index + appended_part_size]; + if (merge_ends) + { + join_coord.setNativeX((join_coord.nativeX() + other->coords[other_part.last_index].nativeX()) / 2); + join_coord.setNativeY((join_coord.nativeY() + other->coords[other_part.last_index].nativeY()) / 2); + } + join_coord.setHolePoint(false); + join_coord.setClosePoint(false); + + for (auto i = part.first_index; i < part.first_index + appended_part_size; ++i) + coords[i] = other->coords[i - part.first_index + other_part.first_index]; + + if (!merge_ends) + { + MapCoord& second_join_coord = coords[part.first_index + appended_part_size - 1]; + second_join_coord.setHolePoint(false); + second_join_coord.setClosePoint(false); + } + } + else + { + auto end_index = part.last_index; + + if (merge_ends) + { + MapCoord coord = other->coords[other_part.first_index]; // take flags from first coord of path to append + coord.setNativeX((coords[end_index].nativeX() + coord.nativeX()) / 2); + coord.setNativeY((coords[end_index].nativeY() + coord.nativeY()) / 2); + coords[end_index] = coord; + } + else + { + coords[end_index].setHolePoint(false); + coords[end_index].setClosePoint(false); + } + + for (auto i = coords.size() - 1; i > part.last_index + appended_part_size; --i) + coords[i] = coords[i - appended_part_size]; + for (auto i = part.last_index+1; i < part.last_index + 1 + appended_part_size; ++i) + coords[i] = other->coords[i - end_index + other_part.first_index - (merge_ends ? 0 : 1)]; + } + + setOutputDirty(); + partSizeChanged(begin(path_parts)+part_index, appended_part_size); + Q_ASSERT(!part.isClosed()); +} + +std::vector PathObject::removeFromLine(PathPartVector::size_type part_index, qreal begin, qreal end_index) const +{ + Q_ASSERT(path_parts.size() == 1); // TODO + Q_ASSERT(symbol->getContainedTypes() == Symbol::Line); + + const PathPart& part = path_parts[part_index]; + Q_ASSERT(part.path_coords.front().clen <= begin); + Q_ASSERT(part.path_coords.front().clen <= end_index); + Q_ASSERT(part.path_coords.back().clen >= begin); + Q_ASSERT(part.path_coords.back().clen >= end_index); + + std::vector objects; + + if (end_index < begin || part.isClosed()) + { + PathObject* obj = duplicate()->asPath(); + obj->changePathBounds(part_index, end_index, begin); + objects.push_back(obj); + } + else + { + if (begin > part.path_coords.front().clen) + { + PathObject* obj = duplicate()->asPath(); + obj->changePathBounds(part_index, part.path_coords.front().clen, begin); + objects.push_back(obj); + } + if (end_index < part.path_coords.back().clen) + { + PathObject* obj = duplicate()->asPath(); + obj->changePathBounds(part_index, end_index, part.path_coords.front().clen); + objects.push_back(obj); + } + } + + return objects; +} + +std::vector PathObject::splitLineAt(const PathCoord& split_pos) const +{ + Q_ASSERT(path_parts.size() == 1); + Q_ASSERT((symbol->getContainedTypes() & ~Symbol::Combined) == Symbol::Line); + + std::vector objects; + if (path_parts.size() == 1) + { + const auto part_index = PathPartVector::size_type { 0 }; + const auto& part = path_parts[part_index]; + if (part.isClosed()) + { + PathObject* obj = duplicate()->asPath(); + if ( split_pos.clen == part.path_coords.front().clen || + split_pos.clen == part.path_coords.back().clen ) + { + (obj->path_parts[part_index]).setClosed(false); + } + else + { + obj->changePathBounds(part_index, split_pos.clen, split_pos.clen); + } + objects.push_back(obj); + } + else if ( split_pos.clen != part.path_coords.front().clen && + split_pos.clen != part.path_coords.back().clen) + { + PathObject* obj1 = duplicate()->asPath(); + obj1->changePathBounds(part_index, part.path_coords.front().clen, split_pos.clen); + objects.push_back(obj1); + + PathObject* obj2 = duplicate()->asPath(); + obj2->changePathBounds(part_index, split_pos.clen, part.path_coords.back().clen); + objects.push_back(obj2); + } + } + + return objects; +} + +void PathObject::changePathBounds( + PathPartVector::size_type part_index, + PathCoord::length_type start_len, + PathCoord::length_type end_len) +{ + update(); + + PathPart& part = path_parts[part_index]; + auto part_size = part.size(); + + MapCoordVector out_coords; + out_coords.reserve(part_size + 2); + + if (end_len == 0.0) + end_len = part.path_coords.back().clen; + + auto part_begin = SplitPathCoord::begin(part.path_coords); + auto part_end = SplitPathCoord::end(part.path_coords); + auto start = SplitPathCoord::at(start_len, part_begin); + + if (end_len <= start_len) + { + // Make sure part_end has the right curve end points for start. + part_end = SplitPathCoord::at(part_end.clen, start); + part.copy(start, part_end, out_coords); + out_coords.back().setHolePoint(false); + out_coords.back().setClosePoint(false); + + auto end = SplitPathCoord::at(end_len, part_begin); + part.copy(part_begin, end, out_coords); + } + else + { + auto end = SplitPathCoord::at(end_len, start); + part.copy(start, end, out_coords); + } + + out_coords.back().setHolePoint(true); + out_coords.back().setClosePoint(false); + + const auto copy_size = qMin(out_coords.size(), (MapCoordVector::size_type)part_size); + const auto part_start = begin(coords) + part.first_index; + std::copy(begin(out_coords), begin(out_coords) + copy_size, part_start); + if (copy_size < part_size) + coords.erase(part_start + copy_size, part_start + part_size); + else + coords.insert(part_start + part_size, begin(out_coords) + copy_size, end(out_coords)); + + recalculateParts(); + setOutputDirty(); +} + +void PathObject::calcBezierPointDeletionRetainingShapeFactors(MapCoord p0, MapCoord p1, MapCoord p2, MapCoord q0, MapCoord q1, MapCoord q2, MapCoord q3, double& out_pfactor, double& out_qfactor) +{ + // Heuristic for the split parameter sp (zero to one) + QBezier p_curve = QBezier::fromPoints(QPointF(p0), QPointF(p1), QPointF(p2), QPointF(q0)); + double p_length = p_curve.length(PathCoord::bezierError()); + QBezier q_curve = QBezier::fromPoints(QPointF(q0), QPointF(q1), QPointF(q2), QPointF(q3)); + double q_length = q_curve.length(PathCoord::bezierError()); + double sp = p_length / qMax(1e-08, p_length + q_length); + + // Least squares curve fitting with the constraint that handles are on the same line as before. + // To reproduce the formulas, run the following Matlab script: + /* + + clear all; close all; clc; + + syms P0x P1x P2x P3x; + syms Q1x Q2x Q3x; + syms P0y P1y P2y P3y; + syms Q1y Q2y Q3y; + + syms u v w; + syms pfactor qfactor; + + Xx = (1-u)^3*P0x+3*(1-u)^2*u*P1x+3*(1-u)*u^2*P2x+u^3*P3x; + Yx = (1-v)^3*P3x+3*(1-v)^2*v*Q1x+3*(1-v)*v^2*Q2x+v^3*Q3x; + Zx = (1-w)^3*P0x+3*(1-w)^2*w*(P0x+pfactor*(P1x-P0x))+3*(1-w)*w^2*(Q3x+qfactor*(Q2x-Q3x))+w^3*Q3x; + + Xy = (1-u)^3*P0y+3*(1-u)^2*u*P1y+3*(1-u)*u^2*P2y+u^3*P3y; + Yy = (1-v)^3*P3y+3*(1-v)^2*v*Q1y+3*(1-v)*v^2*Q2y+v^3*Q3y; + Zy = (1-w)^3*P0y+3*(1-w)^2*w*(P0y+pfactor*(P1y-P0y))+3*(1-w)*w^2*(Q3y+qfactor*(Q2y-Q3y))+w^3*Q3y; + + syms sp; %split_param + E = int((Zx - subs(Xx, u, w/sp))^2 + (Zy - subs(Xy, u, w/sp))^2, w, 0, sp) + int((Zx - subs(Yx, v, (w - sp) / (1 - sp)))^2 + (Zy - subs(Yy, v, (w - sp) / (1 - sp)))^2, w, sp, 1); + + Epfactor = diff(E, pfactor); + Eqfactor = diff(E, qfactor); + S = solve(Epfactor, Eqfactor, pfactor, qfactor); + S = [S.pfactor, S.qfactor] + + */ + + double P0x = p0.x(), P1x = p1.x(), P2x = p2.x(), P3x = q0.x(); + double Q1x = q1.x(), Q2x = q2.x(), Q3x = q3.x(); + double P0y = p0.y(), P1y = p1.y(), P2y = p2.y(), P3y = q0.y(); + double Q1y = q1.y(), Q2y = q2.y(), Q3y = q3.y(); + + out_pfactor = -(126*P0x*pow(Q2x,3.0)*pow(sp,3.0) - 49*pow(P0x,2.0)*pow(Q3x,2.0) - 88*pow(P0x,2.0)*pow(Q2y,2.0) - 88*pow(P0y,2.0)*pow(Q2x,2.0) - 88*pow(P0x,2.0)*pow(Q3y,2.0) - 88*pow(P0y,2.0)*pow(Q3x,2.0) - 49*pow(P0y,2.0)*pow(Q2y,2.0) - + 49*pow(P0y,2.0)*pow(Q3y,2.0) - 49*pow(P0x,2.0)*pow(Q2x,2.0) + 21*P0x*pow(Q3x,3.0)*pow(sp,2.0) - 84*P0x*pow(Q2x,3.0)*pow(sp,4.0) + 14*P0x*pow(Q3x,3.0)*pow(sp,3.0) - 126*P1x*pow(Q2x,3.0)*pow(sp,3.0) - + 21*P1x*pow(Q3x,3.0)*pow(sp,2.0) - 21*P0x*pow(Q3x,3.0)*pow(sp,4.0) + 84*P1x*pow(Q2x,3.0)*pow(sp,4.0) - 14*P1x*pow(Q3x,3.0)*pow(sp,3.0) + 21*P1x*pow(Q3x,3.0)*pow(sp,4.0) + 126*P0y*pow(Q2y,3.0)*pow(sp,3.0) + + 21*P0y*pow(Q3y,3.0)*pow(sp,2.0) - 84*P0y*pow(Q2y,3.0)*pow(sp,4.0) + 14*P0y*pow(Q3y,3.0)*pow(sp,3.0) - 126*P1y*pow(Q2y,3.0)*pow(sp,3.0) - 21*P1y*pow(Q3y,3.0)*pow(sp,2.0) - 21*P0y*pow(Q3y,3.0)*pow(sp,4.0) + + 84*P1y*pow(Q2y,3.0)*pow(sp,4.0) - 14*P1y*pow(Q3y,3.0)*pow(sp,3.0) + 21*P1y*pow(Q3y,3.0)*pow(sp,4.0) + 84*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,2.0) - 77*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + + 84*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 168*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,2.0) + 21*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,4.0) - 77*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,3.0) + 231*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,3.0) - + 168*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,2.0) + 21*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,4.0) - 84*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,4.0) + 231*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 84*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,4.0) + + 84*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,2.0) + 84*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,2.0) - 56*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 84*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 168*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - + 56*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 84*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 168*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,2.0) + 12*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,4.0) - 56*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,3.0) + + 168*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,3.0) - 168*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,2.0) + 12*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,4.0) - 56*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,3.0) + 168*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,3.0) - + 168*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,2.0) + 12*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 48*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,4.0) + 168*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,3.0) + 12*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,4.0) - + 48*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,4.0) + 168*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 48*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 48*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,4.0) + 84*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - + 77*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 84*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 168*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,2.0) + 21*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,4.0) - 77*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,3.0) + + 231*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,3.0) - 168*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,2.0) + 21*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 84*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,4.0) + 231*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - + 84*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,4.0) + 49*P0x*P1x*pow(Q2x,2.0) + 49*P0x*P1x*pow(Q3x,2.0) + 28*P0x*P3x*pow(Q2x,2.0) + 28*P0x*P3x*pow(Q3x,2.0) - 28*P1x*P3x*pow(Q2x,2.0) - 28*P1x*P3x*pow(Q3x,2.0) + 88*P0x*P1x*pow(Q2y,2.0) + + 88*P0x*P1x*pow(Q3y,2.0) + 40*P0x*P3x*pow(Q2y,2.0) + 40*P0x*P3x*pow(Q3y,2.0) - 40*P1x*P3x*pow(Q2y,2.0) - 40*P1x*P3x*pow(Q3y,2.0) + 88*P0y*P1y*pow(Q2x,2.0) + 88*P0y*P1y*pow(Q3x,2.0) + 40*P0y*P3y*pow(Q2x,2.0) + + 40*P0y*P3y*pow(Q3x,2.0) - 40*P1y*P3y*pow(Q2x,2.0) - 40*P1y*P3y*pow(Q3x,2.0) + 49*P0y*P1y*pow(Q2y,2.0) + 49*P0y*P1y*pow(Q3y,2.0) + 28*P0y*P3y*pow(Q2y,2.0) + 28*P0y*P3y*pow(Q3y,2.0) - 28*P1y*P3y*pow(Q2y,2.0) - + 28*P1y*P3y*pow(Q3y,2.0) + 21*P0x*Q1x*pow(Q2x,2.0) + 21*P0x*Q1x*pow(Q3x,2.0) - 21*P1x*Q1x*pow(Q2x,2.0) - 21*P1x*Q1x*pow(Q3x,2.0) + 98*pow(P0x,2.0)*Q2x*Q3x + 48*P0x*Q1x*pow(Q2y,2.0) + 48*P0x*Q1x*pow(Q3y,2.0) - + 48*P1x*Q1x*pow(Q2y,2.0) - 48*P1x*Q1x*pow(Q3y,2.0) + 176*pow(P0y,2.0)*Q2x*Q3x + 48*P0y*pow(Q2x,2.0)*Q1y + 48*P0y*pow(Q3x,2.0)*Q1y - 48*P1y*pow(Q2x,2.0)*Q1y - 48*P1y*pow(Q3x,2.0)*Q1y + 176*pow(P0x,2.0)*Q2y*Q3y + + 21*P0y*Q1y*pow(Q2y,2.0) + 21*P0y*Q1y*pow(Q3y,2.0) - 21*P1y*Q1y*pow(Q2y,2.0) - 21*P1y*Q1y*pow(Q3y,2.0) + 98*pow(P0y,2.0)*Q2y*Q3y - 42*P0x*pow(Q2x,3.0)*sp + 42*P1x*pow(Q2x,3.0)*sp - 42*P0y*pow(Q2y,3.0)*sp + + 42*P1y*pow(Q2y,3.0)*sp + 84*P0x*P3x*pow(Q2x,2.0)*sp + 84*P0x*P3x*pow(Q3x,2.0)*sp - 84*P1x*P3x*pow(Q2x,2.0)*sp - 84*P1x*P3x*pow(Q3x,2.0)*sp + 120*P0x*P3x*pow(Q2y,2.0)*sp + 120*P0x*P3x*pow(Q3y,2.0)*sp - + 120*P1x*P3x*pow(Q2y,2.0)*sp - 120*P1x*P3x*pow(Q3y,2.0)*sp + 120*P0y*P3y*pow(Q2x,2.0)*sp + 120*P0y*P3y*pow(Q3x,2.0)*sp - 120*P1y*P3y*pow(Q2x,2.0)*sp - 120*P1y*P3y*pow(Q3x,2.0)*sp + 84*P0y*P3y*pow(Q2y,2.0)*sp + + 84*P0y*P3y*pow(Q3y,2.0)*sp - 84*P1y*P3y*pow(Q2y,2.0)*sp - 84*P1y*P3y*pow(Q3y,2.0)*sp - 42*P0x*Q1x*pow(Q2x,2.0)*sp - 42*P0x*Q1x*pow(Q3x,2.0)*sp + 42*P1x*Q1x*pow(Q2x,2.0)*sp - 42*P0x*Q2x*pow(Q3x,2.0)*sp + + 84*P0x*pow(Q2x,2.0)*Q3x*sp + 42*P1x*Q1x*pow(Q3x,2.0)*sp + 42*P1x*Q2x*pow(Q3x,2.0)*sp - 84*P1x*pow(Q2x,2.0)*Q3x*sp - 24*P0x*Q1x*pow(Q2y,2.0)*sp - 24*P0x*Q1x*pow(Q3y,2.0)*sp - 42*P0x*Q2x*pow(Q2y,2.0)*sp + + 24*P1x*Q1x*pow(Q2y,2.0)*sp - 96*P0x*Q2x*pow(Q3y,2.0)*sp - 54*P0x*Q3x*pow(Q2y,2.0)*sp + 24*P1x*Q1x*pow(Q3y,2.0)*sp + 42*P1x*Q2x*pow(Q2y,2.0)*sp + 96*P1x*Q2x*pow(Q3y,2.0)*sp + 54*P1x*Q3x*pow(Q2y,2.0)*sp - + 24*P0y*pow(Q2x,2.0)*Q1y*sp - 42*P0y*pow(Q2x,2.0)*Q2y*sp - 24*P0y*pow(Q3x,2.0)*Q1y*sp + 24*P1y*pow(Q2x,2.0)*Q1y*sp - 54*P0y*pow(Q2x,2.0)*Q3y*sp - 96*P0y*pow(Q3x,2.0)*Q2y*sp + 42*P1y*pow(Q2x,2.0)*Q2y*sp + + 24*P1y*pow(Q3x,2.0)*Q1y*sp + 54*P1y*pow(Q2x,2.0)*Q3y*sp + 96*P1y*pow(Q3x,2.0)*Q2y*sp - 42*P0y*Q1y*pow(Q2y,2.0)*sp - 42*P0y*Q1y*pow(Q3y,2.0)*sp + 42*P1y*Q1y*pow(Q2y,2.0)*sp - 42*P0y*Q2y*pow(Q3y,2.0)*sp + + 84*P0y*pow(Q2y,2.0)*Q3y*sp + 42*P1y*Q1y*pow(Q3y,2.0)*sp + 42*P1y*Q2y*pow(Q3y,2.0)*sp - 84*P1y*pow(Q2y,2.0)*Q3y*sp + 84*P0x*P1x*pow(Q2x,2.0)*pow(sp,2.0) - 154*P0x*P1x*pow(Q2x,2.0)*pow(sp,3.0) + + 84*P0x*P1x*pow(Q3x,2.0)*pow(sp,2.0) + 252*P0x*P2x*pow(Q2x,2.0)*pow(sp,2.0) + 63*P0x*P1x*pow(Q2x,2.0)*pow(sp,4.0) - 154*P0x*P1x*pow(Q3x,2.0)*pow(sp,3.0) - 462*P0x*P2x*pow(Q2x,2.0)*pow(sp,3.0) + + 252*P0x*P2x*pow(Q3x,2.0)*pow(sp,2.0) - 336*P0x*P3x*pow(Q2x,2.0)*pow(sp,2.0) - 252*P1x*P2x*pow(Q2x,2.0)*pow(sp,2.0) + 63*P0x*P1x*pow(Q3x,2.0)*pow(sp,4.0) + 210*P0x*P2x*pow(Q2x,2.0)*pow(sp,4.0) - + 462*P0x*P2x*pow(Q3x,2.0)*pow(sp,3.0) + 210*P0x*P3x*pow(Q2x,2.0)*pow(sp,3.0) - 336*P0x*P3x*pow(Q3x,2.0)*pow(sp,2.0) + 462*P1x*P2x*pow(Q2x,2.0)*pow(sp,3.0) - 252*P1x*P2x*pow(Q3x,2.0)*pow(sp,2.0) + + 336*P1x*P3x*pow(Q2x,2.0)*pow(sp,2.0) + 210*P0x*P2x*pow(Q3x,2.0)*pow(sp,4.0) + 210*P0x*P3x*pow(Q3x,2.0)*pow(sp,3.0) - 210*P1x*P2x*pow(Q2x,2.0)*pow(sp,4.0) + 462*P1x*P2x*pow(Q3x,2.0)*pow(sp,3.0) - + 210*P1x*P3x*pow(Q2x,2.0)*pow(sp,3.0) + 336*P1x*P3x*pow(Q3x,2.0)*pow(sp,2.0) - 210*P1x*P2x*pow(Q3x,2.0)*pow(sp,4.0) - 210*P1x*P3x*pow(Q3x,2.0)*pow(sp,3.0) + 84*P0x*P1x*pow(Q2y,2.0)*pow(sp,2.0) - + 112*P0x*P1x*pow(Q2y,2.0)*pow(sp,3.0) + 84*P0x*P1x*pow(Q3y,2.0)*pow(sp,2.0) + 252*P0x*P2x*pow(Q2y,2.0)*pow(sp,2.0) + 36*P0x*P1x*pow(Q2y,2.0)*pow(sp,4.0) - 112*P0x*P1x*pow(Q3y,2.0)*pow(sp,3.0) - + 336*P0x*P2x*pow(Q2y,2.0)*pow(sp,3.0) + 252*P0x*P2x*pow(Q3y,2.0)*pow(sp,2.0) - 264*P0x*P3x*pow(Q2y,2.0)*pow(sp,2.0) - 252*P1x*P2x*pow(Q2y,2.0)*pow(sp,2.0) + 36*P0x*P1x*pow(Q3y,2.0)*pow(sp,4.0) + + 120*P0x*P2x*pow(Q2y,2.0)*pow(sp,4.0) - 336*P0x*P2x*pow(Q3y,2.0)*pow(sp,3.0) + 120*P0x*P3x*pow(Q2y,2.0)*pow(sp,3.0) - 264*P0x*P3x*pow(Q3y,2.0)*pow(sp,2.0) + 336*P1x*P2x*pow(Q2y,2.0)*pow(sp,3.0) - + 252*P1x*P2x*pow(Q3y,2.0)*pow(sp,2.0) + 264*P1x*P3x*pow(Q2y,2.0)*pow(sp,2.0) + 120*P0x*P2x*pow(Q3y,2.0)*pow(sp,4.0) + 120*P0x*P3x*pow(Q3y,2.0)*pow(sp,3.0) - 120*P1x*P2x*pow(Q2y,2.0)*pow(sp,4.0) + + 336*P1x*P2x*pow(Q3y,2.0)*pow(sp,3.0) - 120*P1x*P3x*pow(Q2y,2.0)*pow(sp,3.0) + 264*P1x*P3x*pow(Q3y,2.0)*pow(sp,2.0) - 120*P1x*P2x*pow(Q3y,2.0)*pow(sp,4.0) - 120*P1x*P3x*pow(Q3y,2.0)*pow(sp,3.0) + + 84*P0y*P1y*pow(Q2x,2.0)*pow(sp,2.0) - 112*P0y*P1y*pow(Q2x,2.0)*pow(sp,3.0) + 84*P0y*P1y*pow(Q3x,2.0)*pow(sp,2.0) + 252*P0y*P2y*pow(Q2x,2.0)*pow(sp,2.0) + 36*P0y*P1y*pow(Q2x,2.0)*pow(sp,4.0) - + 112*P0y*P1y*pow(Q3x,2.0)*pow(sp,3.0) - 336*P0y*P2y*pow(Q2x,2.0)*pow(sp,3.0) + 252*P0y*P2y*pow(Q3x,2.0)*pow(sp,2.0) - 264*P0y*P3y*pow(Q2x,2.0)*pow(sp,2.0) - 252*P1y*P2y*pow(Q2x,2.0)*pow(sp,2.0) + + 36*P0y*P1y*pow(Q3x,2.0)*pow(sp,4.0) + 120*P0y*P2y*pow(Q2x,2.0)*pow(sp,4.0) - 336*P0y*P2y*pow(Q3x,2.0)*pow(sp,3.0) + 120*P0y*P3y*pow(Q2x,2.0)*pow(sp,3.0) - 264*P0y*P3y*pow(Q3x,2.0)*pow(sp,2.0) + + 336*P1y*P2y*pow(Q2x,2.0)*pow(sp,3.0) - 252*P1y*P2y*pow(Q3x,2.0)*pow(sp,2.0) + 264*P1y*P3y*pow(Q2x,2.0)*pow(sp,2.0) + 120*P0y*P2y*pow(Q3x,2.0)*pow(sp,4.0) + 120*P0y*P3y*pow(Q3x,2.0)*pow(sp,3.0) - + 120*P1y*P2y*pow(Q2x,2.0)*pow(sp,4.0) + 336*P1y*P2y*pow(Q3x,2.0)*pow(sp,3.0) - 120*P1y*P3y*pow(Q2x,2.0)*pow(sp,3.0) + 264*P1y*P3y*pow(Q3x,2.0)*pow(sp,2.0) - 120*P1y*P2y*pow(Q3x,2.0)*pow(sp,4.0) - + 120*P1y*P3y*pow(Q3x,2.0)*pow(sp,3.0) + 84*P0y*P1y*pow(Q2y,2.0)*pow(sp,2.0) - 154*P0y*P1y*pow(Q2y,2.0)*pow(sp,3.0) + 84*P0y*P1y*pow(Q3y,2.0)*pow(sp,2.0) + 252*P0y*P2y*pow(Q2y,2.0)*pow(sp,2.0) + + 63*P0y*P1y*pow(Q2y,2.0)*pow(sp,4.0) - 154*P0y*P1y*pow(Q3y,2.0)*pow(sp,3.0) - 462*P0y*P2y*pow(Q2y,2.0)*pow(sp,3.0) + 252*P0y*P2y*pow(Q3y,2.0)*pow(sp,2.0) - 336*P0y*P3y*pow(Q2y,2.0)*pow(sp,2.0) - + 252*P1y*P2y*pow(Q2y,2.0)*pow(sp,2.0) + 63*P0y*P1y*pow(Q3y,2.0)*pow(sp,4.0) + 210*P0y*P2y*pow(Q2y,2.0)*pow(sp,4.0) - 462*P0y*P2y*pow(Q3y,2.0)*pow(sp,3.0) + 210*P0y*P3y*pow(Q2y,2.0)*pow(sp,3.0) - + 336*P0y*P3y*pow(Q3y,2.0)*pow(sp,2.0) + 462*P1y*P2y*pow(Q2y,2.0)*pow(sp,3.0) - 252*P1y*P2y*pow(Q3y,2.0)*pow(sp,2.0) + 336*P1y*P3y*pow(Q2y,2.0)*pow(sp,2.0) + 210*P0y*P2y*pow(Q3y,2.0)*pow(sp,4.0) + + 210*P0y*P3y*pow(Q3y,2.0)*pow(sp,3.0) - 210*P1y*P2y*pow(Q2y,2.0)*pow(sp,4.0) + 462*P1y*P2y*pow(Q3y,2.0)*pow(sp,3.0) - 210*P1y*P3y*pow(Q2y,2.0)*pow(sp,3.0) + 336*P1y*P3y*pow(Q3y,2.0)*pow(sp,2.0) - + 210*P1y*P2y*pow(Q3y,2.0)*pow(sp,4.0) - 210*P1y*P3y*pow(Q3y,2.0)*pow(sp,3.0) - 189*P0x*Q1x*pow(Q2x,2.0)*pow(sp,2.0) + 420*P0x*Q1x*pow(Q2x,2.0)*pow(sp,3.0) - 189*P0x*Q1x*pow(Q3x,2.0)*pow(sp,2.0) + + 189*P1x*Q1x*pow(Q2x,2.0)*pow(sp,2.0) - 210*P0x*Q1x*pow(Q2x,2.0)*pow(sp,4.0) + 420*P0x*Q1x*pow(Q3x,2.0)*pow(sp,3.0) - 42*P0x*Q2x*pow(Q3x,2.0)*pow(sp,2.0) + 21*P0x*pow(Q2x,2.0)*Q3x*pow(sp,2.0) - + 420*P1x*Q1x*pow(Q2x,2.0)*pow(sp,3.0) + 189*P1x*Q1x*pow(Q3x,2.0)*pow(sp,2.0) - 168*pow(P0x,2.0)*Q2x*Q3x*pow(sp,2.0) - 210*P0x*Q1x*pow(Q3x,2.0)*pow(sp,4.0) + 98*P0x*Q2x*pow(Q3x,2.0)*pow(sp,3.0) - + 238*P0x*pow(Q2x,2.0)*Q3x*pow(sp,3.0) + 210*P1x*Q1x*pow(Q2x,2.0)*pow(sp,4.0) - 420*P1x*Q1x*pow(Q3x,2.0)*pow(sp,3.0) + 42*P1x*Q2x*pow(Q3x,2.0)*pow(sp,2.0) - 21*P1x*pow(Q2x,2.0)*Q3x*pow(sp,2.0) + + 154*pow(P0x,2.0)*Q2x*Q3x*pow(sp,3.0) + 336*pow(P1x,2.0)*Q2x*Q3x*pow(sp,2.0) - 42*P0x*Q2x*pow(Q3x,2.0)*pow(sp,4.0) + 147*P0x*pow(Q2x,2.0)*Q3x*pow(sp,4.0) + 210*P1x*Q1x*pow(Q3x,2.0)*pow(sp,4.0) - + 98*P1x*Q2x*pow(Q3x,2.0)*pow(sp,3.0) + 238*P1x*pow(Q2x,2.0)*Q3x*pow(sp,3.0) - 42*pow(P0x,2.0)*Q2x*Q3x*pow(sp,4.0) - 462*pow(P1x,2.0)*Q2x*Q3x*pow(sp,3.0) + 42*P1x*Q2x*pow(Q3x,2.0)*pow(sp,4.0) - + 147*P1x*pow(Q2x,2.0)*Q3x*pow(sp,4.0) + 168*pow(P1x,2.0)*Q2x*Q3x*pow(sp,4.0) - 216*P0x*Q1x*pow(Q2y,2.0)*pow(sp,2.0) + 312*P0x*Q1x*pow(Q2y,2.0)*pow(sp,3.0) - 216*P0x*Q1x*pow(Q3y,2.0)*pow(sp,2.0) + + 216*P1x*Q1x*pow(Q2y,2.0)*pow(sp,2.0) - 120*P0x*Q1x*pow(Q2y,2.0)*pow(sp,4.0) + 312*P0x*Q1x*pow(Q3y,2.0)*pow(sp,3.0) + 126*P0x*Q2x*pow(Q2y,2.0)*pow(sp,3.0) - 45*P0x*Q2x*pow(Q3y,2.0)*pow(sp,2.0) - + 24*P0x*Q3x*pow(Q2y,2.0)*pow(sp,2.0) - 312*P1x*Q1x*pow(Q2y,2.0)*pow(sp,3.0) + 216*P1x*Q1x*pow(Q3y,2.0)*pow(sp,2.0) - 168*pow(P0y,2.0)*Q2x*Q3x*pow(sp,2.0) - 120*P0x*Q1x*pow(Q3y,2.0)*pow(sp,4.0) - + 84*P0x*Q2x*pow(Q2y,2.0)*pow(sp,4.0) + 114*P0x*Q2x*pow(Q3y,2.0)*pow(sp,3.0) + 2*P0x*Q3x*pow(Q2y,2.0)*pow(sp,3.0) + 21*P0x*Q3x*pow(Q3y,2.0)*pow(sp,2.0) + 120*P1x*Q1x*pow(Q2y,2.0)*pow(sp,4.0) - + 312*P1x*Q1x*pow(Q3y,2.0)*pow(sp,3.0) - 126*P1x*Q2x*pow(Q2y,2.0)*pow(sp,3.0) + 45*P1x*Q2x*pow(Q3y,2.0)*pow(sp,2.0) + 24*P1x*Q3x*pow(Q2y,2.0)*pow(sp,2.0) + 112*pow(P0y,2.0)*Q2x*Q3x*pow(sp,3.0) + + 336*pow(P1y,2.0)*Q2x*Q3x*pow(sp,2.0) - 39*P0x*Q2x*pow(Q3y,2.0)*pow(sp,4.0) + 24*P0x*Q3x*pow(Q2y,2.0)*pow(sp,4.0) + 14*P0x*Q3x*pow(Q3y,2.0)*pow(sp,3.0) + 120*P1x*Q1x*pow(Q3y,2.0)*pow(sp,4.0) + + 84*P1x*Q2x*pow(Q2y,2.0)*pow(sp,4.0) - 114*P1x*Q2x*pow(Q3y,2.0)*pow(sp,3.0) - 2*P1x*Q3x*pow(Q2y,2.0)*pow(sp,3.0) - 21*P1x*Q3x*pow(Q3y,2.0)*pow(sp,2.0) - 24*pow(P0y,2.0)*Q2x*Q3x*pow(sp,4.0) - + 336*pow(P1y,2.0)*Q2x*Q3x*pow(sp,3.0) - 21*P0x*Q3x*pow(Q3y,2.0)*pow(sp,4.0) + 39*P1x*Q2x*pow(Q3y,2.0)*pow(sp,4.0) - 24*P1x*Q3x*pow(Q2y,2.0)*pow(sp,4.0) - 14*P1x*Q3x*pow(Q3y,2.0)*pow(sp,3.0) + + 96*pow(P1y,2.0)*Q2x*Q3x*pow(sp,4.0) + 21*P1x*Q3x*pow(Q3y,2.0)*pow(sp,4.0) - 216*P0y*pow(Q2x,2.0)*Q1y*pow(sp,2.0) + 312*P0y*pow(Q2x,2.0)*Q1y*pow(sp,3.0) - 216*P0y*pow(Q3x,2.0)*Q1y*pow(sp,2.0) + + 216*P1y*pow(Q2x,2.0)*Q1y*pow(sp,2.0) - 120*P0y*pow(Q2x,2.0)*Q1y*pow(sp,4.0) + 126*P0y*pow(Q2x,2.0)*Q2y*pow(sp,3.0) - 24*P0y*pow(Q2x,2.0)*Q3y*pow(sp,2.0) + 312*P0y*pow(Q3x,2.0)*Q1y*pow(sp,3.0) - + 45*P0y*pow(Q3x,2.0)*Q2y*pow(sp,2.0) - 312*P1y*pow(Q2x,2.0)*Q1y*pow(sp,3.0) + 216*P1y*pow(Q3x,2.0)*Q1y*pow(sp,2.0) - 168*pow(P0x,2.0)*Q2y*Q3y*pow(sp,2.0) - 84*P0y*pow(Q2x,2.0)*Q2y*pow(sp,4.0) + + 2*P0y*pow(Q2x,2.0)*Q3y*pow(sp,3.0) - 120*P0y*pow(Q3x,2.0)*Q1y*pow(sp,4.0) + 114*P0y*pow(Q3x,2.0)*Q2y*pow(sp,3.0) + 21*P0y*pow(Q3x,2.0)*Q3y*pow(sp,2.0) + 120*P1y*pow(Q2x,2.0)*Q1y*pow(sp,4.0) - + 126*P1y*pow(Q2x,2.0)*Q2y*pow(sp,3.0) + 24*P1y*pow(Q2x,2.0)*Q3y*pow(sp,2.0) - 312*P1y*pow(Q3x,2.0)*Q1y*pow(sp,3.0) + 45*P1y*pow(Q3x,2.0)*Q2y*pow(sp,2.0) + 112*pow(P0x,2.0)*Q2y*Q3y*pow(sp,3.0) + + 336*pow(P1x,2.0)*Q2y*Q3y*pow(sp,2.0) + 24*P0y*pow(Q2x,2.0)*Q3y*pow(sp,4.0) - 39*P0y*pow(Q3x,2.0)*Q2y*pow(sp,4.0) + 14*P0y*pow(Q3x,2.0)*Q3y*pow(sp,3.0) + 84*P1y*pow(Q2x,2.0)*Q2y*pow(sp,4.0) - + 2*P1y*pow(Q2x,2.0)*Q3y*pow(sp,3.0) + 120*P1y*pow(Q3x,2.0)*Q1y*pow(sp,4.0) - 114*P1y*pow(Q3x,2.0)*Q2y*pow(sp,3.0) - 21*P1y*pow(Q3x,2.0)*Q3y*pow(sp,2.0) - 24*pow(P0x,2.0)*Q2y*Q3y*pow(sp,4.0) - + 336*pow(P1x,2.0)*Q2y*Q3y*pow(sp,3.0) - 21*P0y*pow(Q3x,2.0)*Q3y*pow(sp,4.0) - 24*P1y*pow(Q2x,2.0)*Q3y*pow(sp,4.0) + 39*P1y*pow(Q3x,2.0)*Q2y*pow(sp,4.0) - 14*P1y*pow(Q3x,2.0)*Q3y*pow(sp,3.0) + + 96*pow(P1x,2.0)*Q2y*Q3y*pow(sp,4.0) + 21*P1y*pow(Q3x,2.0)*Q3y*pow(sp,4.0) - 189*P0y*Q1y*pow(Q2y,2.0)*pow(sp,2.0) + 420*P0y*Q1y*pow(Q2y,2.0)*pow(sp,3.0) - 189*P0y*Q1y*pow(Q3y,2.0)*pow(sp,2.0) + + 189*P1y*Q1y*pow(Q2y,2.0)*pow(sp,2.0) - 210*P0y*Q1y*pow(Q2y,2.0)*pow(sp,4.0) + 420*P0y*Q1y*pow(Q3y,2.0)*pow(sp,3.0) - 42*P0y*Q2y*pow(Q3y,2.0)*pow(sp,2.0) + 21*P0y*pow(Q2y,2.0)*Q3y*pow(sp,2.0) - + 420*P1y*Q1y*pow(Q2y,2.0)*pow(sp,3.0) + 189*P1y*Q1y*pow(Q3y,2.0)*pow(sp,2.0) - 168*pow(P0y,2.0)*Q2y*Q3y*pow(sp,2.0) - 210*P0y*Q1y*pow(Q3y,2.0)*pow(sp,4.0) + 98*P0y*Q2y*pow(Q3y,2.0)*pow(sp,3.0) - + 238*P0y*pow(Q2y,2.0)*Q3y*pow(sp,3.0) + 210*P1y*Q1y*pow(Q2y,2.0)*pow(sp,4.0) - 420*P1y*Q1y*pow(Q3y,2.0)*pow(sp,3.0) + 42*P1y*Q2y*pow(Q3y,2.0)*pow(sp,2.0) - 21*P1y*pow(Q2y,2.0)*Q3y*pow(sp,2.0) + + 154*pow(P0y,2.0)*Q2y*Q3y*pow(sp,3.0) + 336*pow(P1y,2.0)*Q2y*Q3y*pow(sp,2.0) - 42*P0y*Q2y*pow(Q3y,2.0)*pow(sp,4.0) + 147*P0y*pow(Q2y,2.0)*Q3y*pow(sp,4.0) + 210*P1y*Q1y*pow(Q3y,2.0)*pow(sp,4.0) - + 98*P1y*Q2y*pow(Q3y,2.0)*pow(sp,3.0) + 238*P1y*pow(Q2y,2.0)*Q3y*pow(sp,3.0) - 42*pow(P0y,2.0)*Q2y*Q3y*pow(sp,4.0) - 462*pow(P1y,2.0)*Q2y*Q3y*pow(sp,3.0) + 42*P1y*Q2y*pow(Q3y,2.0)*pow(sp,4.0) - + 147*P1y*pow(Q2y,2.0)*Q3y*pow(sp,4.0) + 168*pow(P1y,2.0)*Q2y*Q3y*pow(sp,4.0) - 98*P0x*P1x*Q2x*Q3x - 56*P0x*P3x*Q2x*Q3x + 56*P1x*P3x*Q2x*Q3x + 78*P0x*P0y*Q2x*Q2y - 78*P0x*P0y*Q2x*Q3y - 78*P0x*P0y*Q3x*Q2y - + 39*P0x*P1y*Q2x*Q2y - 39*P1x*P0y*Q2x*Q2y - 176*P0x*P1x*Q2y*Q3y + 78*P0x*P0y*Q3x*Q3y + 39*P0x*P1y*Q2x*Q3y + 39*P0x*P1y*Q3x*Q2y + 39*P1x*P0y*Q2x*Q3y + 39*P1x*P0y*Q3x*Q2y - 176*P0y*P1y*Q2x*Q3x - + 39*P0x*P1y*Q3x*Q3y - 12*P0x*P3y*Q2x*Q2y - 39*P1x*P0y*Q3x*Q3y - 12*P3x*P0y*Q2x*Q2y - 80*P0x*P3x*Q2y*Q3y + 12*P0x*P3y*Q2x*Q3y + 12*P0x*P3y*Q3x*Q2y + 12*P1x*P3y*Q2x*Q2y + 12*P3x*P0y*Q2x*Q3y + + 12*P3x*P0y*Q3x*Q2y + 12*P3x*P1y*Q2x*Q2y - 80*P0y*P3y*Q2x*Q3x - 12*P0x*P3y*Q3x*Q3y + 80*P1x*P3x*Q2y*Q3y - 12*P1x*P3y*Q2x*Q3y - 12*P1x*P3y*Q3x*Q2y - 12*P3x*P0y*Q3x*Q3y - 12*P3x*P1y*Q2x*Q3y - + 12*P3x*P1y*Q3x*Q2y + 80*P1y*P3y*Q2x*Q3x + 12*P1x*P3y*Q3x*Q3y + 12*P3x*P1y*Q3x*Q3y - 98*P0y*P1y*Q2y*Q3y - 56*P0y*P3y*Q2y*Q3y + 56*P1y*P3y*Q2y*Q3y - 42*P0x*Q1x*Q2x*Q3x + 42*P1x*Q1x*Q2x*Q3x - 27*P0x*Q2x*Q1y*Q2y - + 27*P0y*Q1x*Q2x*Q2y - 96*P0x*Q1x*Q2y*Q3y + 27*P0x*Q2x*Q1y*Q3y + 27*P0x*Q3x*Q1y*Q2y + 27*P1x*Q2x*Q1y*Q2y + 27*P0y*Q1x*Q2x*Q3y + 27*P0y*Q1x*Q3x*Q2y - 96*P0y*Q2x*Q3x*Q1y + 27*P1y*Q1x*Q2x*Q2y - 27*P0x*Q3x*Q1y*Q3y + + 96*P1x*Q1x*Q2y*Q3y - 27*P1x*Q2x*Q1y*Q3y - 27*P1x*Q3x*Q1y*Q2y - 27*P0y*Q1x*Q3x*Q3y - 27*P1y*Q1x*Q2x*Q3y - 27*P1y*Q1x*Q3x*Q2y + 96*P1y*Q2x*Q3x*Q1y + 27*P1x*Q3x*Q1y*Q3y + 27*P1y*Q1x*Q3x*Q3y - 42*P0y*Q1y*Q2y*Q3y + + 42*P1y*Q1y*Q2y*Q3y - 168*P0x*P3x*Q2x*Q3x*sp + 168*P1x*P3x*Q2x*Q3x*sp - 36*P0x*P3y*Q2x*Q2y*sp - 36*P3x*P0y*Q2x*Q2y*sp - 240*P0x*P3x*Q2y*Q3y*sp + 36*P0x*P3y*Q2x*Q3y*sp + 36*P0x*P3y*Q3x*Q2y*sp + 36*P1x*P3y*Q2x*Q2y*sp + + 36*P3x*P0y*Q2x*Q3y*sp + 36*P3x*P0y*Q3x*Q2y*sp + 36*P3x*P1y*Q2x*Q2y*sp - 240*P0y*P3y*Q2x*Q3x*sp - 36*P0x*P3y*Q3x*Q3y*sp + 240*P1x*P3x*Q2y*Q3y*sp - 36*P1x*P3y*Q2x*Q3y*sp - 36*P1x*P3y*Q3x*Q2y*sp - 36*P3x*P0y*Q3x*Q3y*sp - + 36*P3x*P1y*Q2x*Q3y*sp - 36*P3x*P1y*Q3x*Q2y*sp + 240*P1y*P3y*Q2x*Q3x*sp + 36*P1x*P3y*Q3x*Q3y*sp + 36*P3x*P1y*Q3x*Q3y*sp - 168*P0y*P3y*Q2y*Q3y*sp + 168*P1y*P3y*Q2y*Q3y*sp + 84*P0x*Q1x*Q2x*Q3x*sp - 84*P1x*Q1x*Q2x*Q3x*sp - + 18*P0x*Q2x*Q1y*Q2y*sp - 18*P0y*Q1x*Q2x*Q2y*sp + 48*P0x*Q1x*Q2y*Q3y*sp + 18*P0x*Q2x*Q1y*Q3y*sp + 18*P0x*Q3x*Q1y*Q2y*sp + 18*P1x*Q2x*Q1y*Q2y*sp + 18*P0y*Q1x*Q2x*Q3y*sp + 18*P0y*Q1x*Q3x*Q2y*sp + 48*P0y*Q2x*Q3x*Q1y*sp + + 18*P1y*Q1x*Q2x*Q2y*sp + 138*P0x*Q2x*Q2y*Q3y*sp - 18*P0x*Q3x*Q1y*Q3y*sp - 48*P1x*Q1x*Q2y*Q3y*sp - 18*P1x*Q2x*Q1y*Q3y*sp - 18*P1x*Q3x*Q1y*Q2y*sp - 18*P0y*Q1x*Q3x*Q3y*sp + 138*P0y*Q2x*Q3x*Q2y*sp - 18*P1y*Q1x*Q2x*Q3y*sp - + 18*P1y*Q1x*Q3x*Q2y*sp - 48*P1y*Q2x*Q3x*Q1y*sp + 54*P0x*Q3x*Q2y*Q3y*sp - 138*P1x*Q2x*Q2y*Q3y*sp + 18*P1x*Q3x*Q1y*Q3y*sp + 54*P0y*Q2x*Q3x*Q3y*sp + 18*P1y*Q1x*Q3x*Q3y*sp - 138*P1y*Q2x*Q3x*Q2y*sp - 54*P1x*Q3x*Q2y*Q3y*sp - + 54*P1y*Q2x*Q3x*Q3y*sp + 84*P0y*Q1y*Q2y*Q3y*sp - 84*P1y*Q1y*Q2y*Q3y*sp - 168*P0x*P1x*Q2x*Q3x*pow(sp,2.0) + 308*P0x*P1x*Q2x*Q3x*pow(sp,3.0) - 504*P0x*P2x*Q2x*Q3x*pow(sp,2.0) - 126*P0x*P1x*Q2x*Q3x*pow(sp,4.0) + + 924*P0x*P2x*Q2x*Q3x*pow(sp,3.0) + 672*P0x*P3x*Q2x*Q3x*pow(sp,2.0) + 504*P1x*P2x*Q2x*Q3x*pow(sp,2.0) - 420*P0x*P2x*Q2x*Q3x*pow(sp,4.0) - 420*P0x*P3x*Q2x*Q3x*pow(sp,3.0) - 924*P1x*P2x*Q2x*Q3x*pow(sp,3.0) - + 672*P1x*P3x*Q2x*Q3x*pow(sp,2.0) + 420*P1x*P2x*Q2x*Q3x*pow(sp,4.0) + 420*P1x*P3x*Q2x*Q3x*pow(sp,3.0) - 42*P0x*P0y*Q2x*Q2y*pow(sp,3.0) - 168*P0x*P1x*Q2y*Q3y*pow(sp,2.0) + 18*P0x*P0y*Q2x*Q2y*pow(sp,4.0) + + 42*P0x*P0y*Q2x*Q3y*pow(sp,3.0) + 42*P0x*P0y*Q3x*Q2y*pow(sp,3.0) - 42*P0x*P1y*Q2x*Q2y*pow(sp,3.0) - 42*P1x*P0y*Q2x*Q2y*pow(sp,3.0) - 168*P0y*P1y*Q2x*Q3x*pow(sp,2.0) + 224*P0x*P1x*Q2y*Q3y*pow(sp,3.0) - + 504*P0x*P2x*Q2y*Q3y*pow(sp,2.0) - 18*P0x*P0y*Q2x*Q3y*pow(sp,4.0) - 18*P0x*P0y*Q3x*Q2y*pow(sp,4.0) - 42*P0x*P0y*Q3x*Q3y*pow(sp,3.0) + 27*P0x*P1y*Q2x*Q2y*pow(sp,4.0) + 42*P0x*P1y*Q2x*Q3y*pow(sp,3.0) + + 42*P0x*P1y*Q3x*Q2y*pow(sp,3.0) - 126*P0x*P2y*Q2x*Q2y*pow(sp,3.0) - 72*P0x*P3y*Q2x*Q2y*pow(sp,2.0) + 27*P1x*P0y*Q2x*Q2y*pow(sp,4.0) + 42*P1x*P0y*Q2x*Q3y*pow(sp,3.0) + 42*P1x*P0y*Q3x*Q2y*pow(sp,3.0) + + 126*P1x*P1y*Q2x*Q2y*pow(sp,3.0) - 126*P2x*P0y*Q2x*Q2y*pow(sp,3.0) - 72*P3x*P0y*Q2x*Q2y*pow(sp,2.0) + 224*P0y*P1y*Q2x*Q3x*pow(sp,3.0) - 504*P0y*P2y*Q2x*Q3x*pow(sp,2.0) - 72*P0x*P1x*Q2y*Q3y*pow(sp,4.0) + + 672*P0x*P2x*Q2y*Q3y*pow(sp,3.0) + 528*P0x*P3x*Q2y*Q3y*pow(sp,2.0) + 18*P0x*P0y*Q3x*Q3y*pow(sp,4.0) - 27*P0x*P1y*Q2x*Q3y*pow(sp,4.0) - 27*P0x*P1y*Q3x*Q2y*pow(sp,4.0) - 42*P0x*P1y*Q3x*Q3y*pow(sp,3.0) + + 90*P0x*P2y*Q2x*Q2y*pow(sp,4.0) + 126*P0x*P2y*Q2x*Q3y*pow(sp,3.0) + 126*P0x*P2y*Q3x*Q2y*pow(sp,3.0) + 90*P0x*P3y*Q2x*Q2y*pow(sp,3.0) + 72*P0x*P3y*Q2x*Q3y*pow(sp,2.0) + 72*P0x*P3y*Q3x*Q2y*pow(sp,2.0) + + 504*P1x*P2x*Q2y*Q3y*pow(sp,2.0) - 27*P1x*P0y*Q2x*Q3y*pow(sp,4.0) - 27*P1x*P0y*Q3x*Q2y*pow(sp,4.0) - 42*P1x*P0y*Q3x*Q3y*pow(sp,3.0) - 72*P1x*P1y*Q2x*Q2y*pow(sp,4.0) - 126*P1x*P1y*Q2x*Q3y*pow(sp,3.0) - + 126*P1x*P1y*Q3x*Q2y*pow(sp,3.0) + 126*P1x*P2y*Q2x*Q2y*pow(sp,3.0) + 72*P1x*P3y*Q2x*Q2y*pow(sp,2.0) + 90*P2x*P0y*Q2x*Q2y*pow(sp,4.0) + 126*P2x*P0y*Q2x*Q3y*pow(sp,3.0) + 126*P2x*P0y*Q3x*Q2y*pow(sp,3.0) + + 126*P2x*P1y*Q2x*Q2y*pow(sp,3.0) + 90*P3x*P0y*Q2x*Q2y*pow(sp,3.0) + 72*P3x*P0y*Q2x*Q3y*pow(sp,2.0) + 72*P3x*P0y*Q3x*Q2y*pow(sp,2.0) + 72*P3x*P1y*Q2x*Q2y*pow(sp,2.0) - 72*P0y*P1y*Q2x*Q3x*pow(sp,4.0) + + 672*P0y*P2y*Q2x*Q3x*pow(sp,3.0) + 528*P0y*P3y*Q2x*Q3x*pow(sp,2.0) + 504*P1y*P2y*Q2x*Q3x*pow(sp,2.0) - 240*P0x*P2x*Q2y*Q3y*pow(sp,4.0) - 240*P0x*P3x*Q2y*Q3y*pow(sp,3.0) + 27*P0x*P1y*Q3x*Q3y*pow(sp,4.0) - + 90*P0x*P2y*Q2x*Q3y*pow(sp,4.0) - 90*P0x*P2y*Q3x*Q2y*pow(sp,4.0) - 126*P0x*P2y*Q3x*Q3y*pow(sp,3.0) - 90*P0x*P3y*Q2x*Q3y*pow(sp,3.0) - 90*P0x*P3y*Q3x*Q2y*pow(sp,3.0) - 72*P0x*P3y*Q3x*Q3y*pow(sp,2.0) - + 672*P1x*P2x*Q2y*Q3y*pow(sp,3.0) - 528*P1x*P3x*Q2y*Q3y*pow(sp,2.0) + 27*P1x*P0y*Q3x*Q3y*pow(sp,4.0) + 72*P1x*P1y*Q2x*Q3y*pow(sp,4.0) + 72*P1x*P1y*Q3x*Q2y*pow(sp,4.0) + 126*P1x*P1y*Q3x*Q3y*pow(sp,3.0) - + 90*P1x*P2y*Q2x*Q2y*pow(sp,4.0) - 126*P1x*P2y*Q2x*Q3y*pow(sp,3.0) - 126*P1x*P2y*Q3x*Q2y*pow(sp,3.0) - 90*P1x*P3y*Q2x*Q2y*pow(sp,3.0) - 72*P1x*P3y*Q2x*Q3y*pow(sp,2.0) - 72*P1x*P3y*Q3x*Q2y*pow(sp,2.0) - + 90*P2x*P0y*Q2x*Q3y*pow(sp,4.0) - 90*P2x*P0y*Q3x*Q2y*pow(sp,4.0) - 126*P2x*P0y*Q3x*Q3y*pow(sp,3.0) - 90*P2x*P1y*Q2x*Q2y*pow(sp,4.0) - 126*P2x*P1y*Q2x*Q3y*pow(sp,3.0) - 126*P2x*P1y*Q3x*Q2y*pow(sp,3.0) - + 90*P3x*P0y*Q2x*Q3y*pow(sp,3.0) - 90*P3x*P0y*Q3x*Q2y*pow(sp,3.0) - 72*P3x*P0y*Q3x*Q3y*pow(sp,2.0) - 90*P3x*P1y*Q2x*Q2y*pow(sp,3.0) - 72*P3x*P1y*Q2x*Q3y*pow(sp,2.0) - 72*P3x*P1y*Q3x*Q2y*pow(sp,2.0) - + 240*P0y*P2y*Q2x*Q3x*pow(sp,4.0) - 240*P0y*P3y*Q2x*Q3x*pow(sp,3.0) - 672*P1y*P2y*Q2x*Q3x*pow(sp,3.0) - 528*P1y*P3y*Q2x*Q3x*pow(sp,2.0) + 90*P0x*P2y*Q3x*Q3y*pow(sp,4.0) + 90*P0x*P3y*Q3x*Q3y*pow(sp,3.0) + + 240*P1x*P2x*Q2y*Q3y*pow(sp,4.0) + 240*P1x*P3x*Q2y*Q3y*pow(sp,3.0) - 72*P1x*P1y*Q3x*Q3y*pow(sp,4.0) + 90*P1x*P2y*Q2x*Q3y*pow(sp,4.0) + 90*P1x*P2y*Q3x*Q2y*pow(sp,4.0) + 126*P1x*P2y*Q3x*Q3y*pow(sp,3.0) + + 90*P1x*P3y*Q2x*Q3y*pow(sp,3.0) + 90*P1x*P3y*Q3x*Q2y*pow(sp,3.0) + 72*P1x*P3y*Q3x*Q3y*pow(sp,2.0) + 90*P2x*P0y*Q3x*Q3y*pow(sp,4.0) + 90*P2x*P1y*Q2x*Q3y*pow(sp,4.0) + 90*P2x*P1y*Q3x*Q2y*pow(sp,4.0) + + 126*P2x*P1y*Q3x*Q3y*pow(sp,3.0) + 90*P3x*P0y*Q3x*Q3y*pow(sp,3.0) + 90*P3x*P1y*Q2x*Q3y*pow(sp,3.0) + 90*P3x*P1y*Q3x*Q2y*pow(sp,3.0) + 72*P3x*P1y*Q3x*Q3y*pow(sp,2.0) + 240*P1y*P2y*Q2x*Q3x*pow(sp,4.0) + + 240*P1y*P3y*Q2x*Q3x*pow(sp,3.0) - 90*P1x*P2y*Q3x*Q3y*pow(sp,4.0) - 90*P1x*P3y*Q3x*Q3y*pow(sp,3.0) - 90*P2x*P1y*Q3x*Q3y*pow(sp,4.0) - 90*P3x*P1y*Q3x*Q3y*pow(sp,3.0) - 168*P0y*P1y*Q2y*Q3y*pow(sp,2.0) + + 308*P0y*P1y*Q2y*Q3y*pow(sp,3.0) - 504*P0y*P2y*Q2y*Q3y*pow(sp,2.0) - 126*P0y*P1y*Q2y*Q3y*pow(sp,4.0) + 924*P0y*P2y*Q2y*Q3y*pow(sp,3.0) + 672*P0y*P3y*Q2y*Q3y*pow(sp,2.0) + 504*P1y*P2y*Q2y*Q3y*pow(sp,2.0) - + 420*P0y*P2y*Q2y*Q3y*pow(sp,4.0) - 420*P0y*P3y*Q2y*Q3y*pow(sp,3.0) - 924*P1y*P2y*Q2y*Q3y*pow(sp,3.0) - 672*P1y*P3y*Q2y*Q3y*pow(sp,2.0) + 420*P1y*P2y*Q2y*Q3y*pow(sp,4.0) + 420*P1y*P3y*Q2y*Q3y*pow(sp,3.0) + + 378*P0x*Q1x*Q2x*Q3x*pow(sp,2.0) - 840*P0x*Q1x*Q2x*Q3x*pow(sp,3.0) - 378*P1x*Q1x*Q2x*Q3x*pow(sp,2.0) + 420*P0x*Q1x*Q2x*Q3x*pow(sp,4.0) + 840*P1x*Q1x*Q2x*Q3x*pow(sp,3.0) - 420*P1x*Q1x*Q2x*Q3x*pow(sp,4.0) + + 27*P0x*Q2x*Q1y*Q2y*pow(sp,2.0) + 27*P0y*Q1x*Q2x*Q2y*pow(sp,2.0) + 432*P0x*Q1x*Q2y*Q3y*pow(sp,2.0) + 108*P0x*Q2x*Q1y*Q2y*pow(sp,3.0) - 27*P0x*Q2x*Q1y*Q3y*pow(sp,2.0) - 27*P0x*Q3x*Q1y*Q2y*pow(sp,2.0) - + 27*P1x*Q2x*Q1y*Q2y*pow(sp,2.0) + 108*P0y*Q1x*Q2x*Q2y*pow(sp,3.0) - 27*P0y*Q1x*Q2x*Q3y*pow(sp,2.0) - 27*P0y*Q1x*Q3x*Q2y*pow(sp,2.0) + 432*P0y*Q2x*Q3x*Q1y*pow(sp,2.0) - 27*P1y*Q1x*Q2x*Q2y*pow(sp,2.0) - + 624*P0x*Q1x*Q2y*Q3y*pow(sp,3.0) - 90*P0x*Q2x*Q1y*Q2y*pow(sp,4.0) - 108*P0x*Q2x*Q1y*Q3y*pow(sp,3.0) + 45*P0x*Q2x*Q2y*Q3y*pow(sp,2.0) - 108*P0x*Q3x*Q1y*Q2y*pow(sp,3.0) + 27*P0x*Q3x*Q1y*Q3y*pow(sp,2.0) - + 432*P1x*Q1x*Q2y*Q3y*pow(sp,2.0) - 108*P1x*Q2x*Q1y*Q2y*pow(sp,3.0) + 27*P1x*Q2x*Q1y*Q3y*pow(sp,2.0) + 27*P1x*Q3x*Q1y*Q2y*pow(sp,2.0) - 90*P0y*Q1x*Q2x*Q2y*pow(sp,4.0) - 108*P0y*Q1x*Q2x*Q3y*pow(sp,3.0) - + 108*P0y*Q1x*Q3x*Q2y*pow(sp,3.0) + 27*P0y*Q1x*Q3x*Q3y*pow(sp,2.0) - 624*P0y*Q2x*Q3x*Q1y*pow(sp,3.0) + 45*P0y*Q2x*Q3x*Q2y*pow(sp,2.0) - 108*P1y*Q1x*Q2x*Q2y*pow(sp,3.0) + 27*P1y*Q1x*Q2x*Q3y*pow(sp,2.0) + + 27*P1y*Q1x*Q3x*Q2y*pow(sp,2.0) - 432*P1y*Q2x*Q3x*Q1y*pow(sp,2.0) + 240*P0x*Q1x*Q2y*Q3y*pow(sp,4.0) + 90*P0x*Q2x*Q1y*Q3y*pow(sp,4.0) - 240*P0x*Q2x*Q2y*Q3y*pow(sp,3.0) + 90*P0x*Q3x*Q1y*Q2y*pow(sp,4.0) + + 108*P0x*Q3x*Q1y*Q3y*pow(sp,3.0) + 3*P0x*Q3x*Q2y*Q3y*pow(sp,2.0) + 624*P1x*Q1x*Q2y*Q3y*pow(sp,3.0) + 90*P1x*Q2x*Q1y*Q2y*pow(sp,4.0) + 108*P1x*Q2x*Q1y*Q3y*pow(sp,3.0) - 45*P1x*Q2x*Q2y*Q3y*pow(sp,2.0) + + 108*P1x*Q3x*Q1y*Q2y*pow(sp,3.0) - 27*P1x*Q3x*Q1y*Q3y*pow(sp,2.0) + 90*P0y*Q1x*Q2x*Q3y*pow(sp,4.0) + 90*P0y*Q1x*Q3x*Q2y*pow(sp,4.0) + 108*P0y*Q1x*Q3x*Q3y*pow(sp,3.0) + 240*P0y*Q2x*Q3x*Q1y*pow(sp,4.0) - + 240*P0y*Q2x*Q3x*Q2y*pow(sp,3.0) + 3*P0y*Q2x*Q3x*Q3y*pow(sp,2.0) + 90*P1y*Q1x*Q2x*Q2y*pow(sp,4.0) + 108*P1y*Q1x*Q2x*Q3y*pow(sp,3.0) + 108*P1y*Q1x*Q3x*Q2y*pow(sp,3.0) - 27*P1y*Q1x*Q3x*Q3y*pow(sp,2.0) + + 624*P1y*Q2x*Q3x*Q1y*pow(sp,3.0) - 45*P1y*Q2x*Q3x*Q2y*pow(sp,2.0) + 123*P0x*Q2x*Q2y*Q3y*pow(sp,4.0) - 90*P0x*Q3x*Q1y*Q3y*pow(sp,4.0) - 16*P0x*Q3x*Q2y*Q3y*pow(sp,3.0) - 240*P1x*Q1x*Q2y*Q3y*pow(sp,4.0) - + 90*P1x*Q2x*Q1y*Q3y*pow(sp,4.0) + 240*P1x*Q2x*Q2y*Q3y*pow(sp,3.0) - 90*P1x*Q3x*Q1y*Q2y*pow(sp,4.0) - 108*P1x*Q3x*Q1y*Q3y*pow(sp,3.0) - 3*P1x*Q3x*Q2y*Q3y*pow(sp,2.0) - 90*P0y*Q1x*Q3x*Q3y*pow(sp,4.0) + + 123*P0y*Q2x*Q3x*Q2y*pow(sp,4.0) - 16*P0y*Q2x*Q3x*Q3y*pow(sp,3.0) - 90*P1y*Q1x*Q2x*Q3y*pow(sp,4.0) - 90*P1y*Q1x*Q3x*Q2y*pow(sp,4.0) - 108*P1y*Q1x*Q3x*Q3y*pow(sp,3.0) - 240*P1y*Q2x*Q3x*Q1y*pow(sp,4.0) + + 240*P1y*Q2x*Q3x*Q2y*pow(sp,3.0) - 3*P1y*Q2x*Q3x*Q3y*pow(sp,2.0) - 3*P0x*Q3x*Q2y*Q3y*pow(sp,4.0) - 123*P1x*Q2x*Q2y*Q3y*pow(sp,4.0) + 90*P1x*Q3x*Q1y*Q3y*pow(sp,4.0) + 16*P1x*Q3x*Q2y*Q3y*pow(sp,3.0) - + 3*P0y*Q2x*Q3x*Q3y*pow(sp,4.0) + 90*P1y*Q1x*Q3x*Q3y*pow(sp,4.0) - 123*P1y*Q2x*Q3x*Q2y*pow(sp,4.0) + 16*P1y*Q2x*Q3x*Q3y*pow(sp,3.0) + 3*P1x*Q3x*Q2y*Q3y*pow(sp,4.0) + 3*P1y*Q2x*Q3x*Q3y*pow(sp,4.0) + + 378*P0y*Q1y*Q2y*Q3y*pow(sp,2.0) - 840*P0y*Q1y*Q2y*Q3y*pow(sp,3.0) - 378*P1y*Q1y*Q2y*Q3y*pow(sp,2.0) + 420*P0y*Q1y*Q2y*Q3y*pow(sp,4.0) + 840*P1y*Q1y*Q2y*Q3y*pow(sp,3.0) - + 420*P1y*Q1y*Q2y*Q3y*pow(sp,4.0))/(3*(7*pow(P0x,2.0)*pow(Q2x,2.0) + 7*pow(P0x,2.0)*pow(Q3x,2.0) + 7*pow(P1x,2.0)*pow(Q2x,2.0) + 7*pow(P1x,2.0)*pow(Q3x,2.0) + 16*pow(P0x,2.0)*pow(Q2y,2.0) + + 16*pow(P0y,2.0)*pow(Q2x,2.0) + 16*pow(P0x,2.0)*pow(Q3y,2.0) + 16*pow(P1x,2.0)*pow(Q2y,2.0) + 16*pow(P0y,2.0)*pow(Q3x,2.0) + 16*pow(P1y,2.0)*pow(Q2x,2.0) + 16*pow(P1x,2.0)*pow(Q3y,2.0) + + 16*pow(P1y,2.0)*pow(Q3x,2.0) + 7*pow(P0y,2.0)*pow(Q2y,2.0) + 7*pow(P0y,2.0)*pow(Q3y,2.0) + 7*pow(P1y,2.0)*pow(Q2y,2.0) + 7*pow(P1y,2.0)*pow(Q3y,2.0) - 14*P0x*P1x*pow(Q2x,2.0) - 14*P0x*P1x*pow(Q3x,2.0) - + 32*P0x*P1x*pow(Q2y,2.0) - 32*P0x*P1x*pow(Q3y,2.0) - 32*P0y*P1y*pow(Q2x,2.0) - 32*P0y*P1y*pow(Q3x,2.0) - 14*P0y*P1y*pow(Q2y,2.0) - 14*P0y*P1y*pow(Q3y,2.0) - 14*pow(P0x,2.0)*Q2x*Q3x - 14*pow(P1x,2.0)*Q2x*Q3x - + 32*pow(P0y,2.0)*Q2x*Q3x - 32*pow(P1y,2.0)*Q2x*Q3x - 32*pow(P0x,2.0)*Q2y*Q3y - 32*pow(P1x,2.0)*Q2y*Q3y - 14*pow(P0y,2.0)*Q2y*Q3y - 14*pow(P1y,2.0)*Q2y*Q3y + 28*P0x*P1x*Q2x*Q3x - 18*P0x*P0y*Q2x*Q2y + 18*P0x*P0y*Q2x*Q3y + + 18*P0x*P0y*Q3x*Q2y + 18*P0x*P1y*Q2x*Q2y + 18*P1x*P0y*Q2x*Q2y + 64*P0x*P1x*Q2y*Q3y - 18*P0x*P0y*Q3x*Q3y - 18*P0x*P1y*Q2x*Q3y - 18*P0x*P1y*Q3x*Q2y - 18*P1x*P0y*Q2x*Q3y - 18*P1x*P0y*Q3x*Q2y - 18*P1x*P1y*Q2x*Q2y + + 64*P0y*P1y*Q2x*Q3x + 18*P0x*P1y*Q3x*Q3y + 18*P1x*P0y*Q3x*Q3y + 18*P1x*P1y*Q2x*Q3y + 18*P1x*P1y*Q3x*Q2y - 18*P1x*P1y*Q3x*Q3y + 28*P0y*P1y*Q2y*Q3y)); + + out_qfactor = (14*pow(P0x,3.0)*Q2x - 14*pow(P0x,3.0)*Q3x + 14*pow(P0y,3.0)*Q2y - 14*pow(P0y,3.0)*Q3y + 21*pow(P0x,2.0)*pow(Q2x,2.0) + 21*pow(P0x,2.0)*pow(Q3x,2.0) + 21*pow(P1x,2.0)*pow(Q2x,2.0) + 21*pow(P1x,2.0)*pow(Q3x,2.0) + + 48*pow(P0x,2.0)*pow(Q2y,2.0) + 48*pow(P0y,2.0)*pow(Q2x,2.0) + 48*pow(P0x,2.0)*pow(Q3y,2.0) + 48*pow(P1x,2.0)*pow(Q2y,2.0) + 48*pow(P0y,2.0)*pow(Q3x,2.0) + 48*pow(P1y,2.0)*pow(Q2x,2.0) + 48*pow(P1x,2.0)*pow(Q3y,2.0) + + 48*pow(P1y,2.0)*pow(Q3x,2.0) + 21*pow(P0y,2.0)*pow(Q2y,2.0) + 21*pow(P0y,2.0)*pow(Q3y,2.0) + 21*pow(P1y,2.0)*pow(Q2y,2.0) + 21*pow(P1y,2.0)*pow(Q3y,2.0) + 21*pow(P0x,2.0)*pow(Q2x,2.0)*sp + 21*pow(P0x,2.0)*pow(Q3x,2.0)*sp - + 63*pow(P0x,3.0)*Q2x*pow(sp,2.0) + 21*pow(P1x,2.0)*pow(Q2x,2.0)*sp + 70*pow(P0x,3.0)*Q2x*pow(sp,3.0) + 63*pow(P0x,3.0)*Q3x*pow(sp,2.0) + 21*pow(P1x,2.0)*pow(Q3x,2.0)*sp - 126*pow(P1x,3.0)*Q2x*pow(sp,2.0) - + 21*pow(P0x,3.0)*Q2x*pow(sp,4.0) - 70*pow(P0x,3.0)*Q3x*pow(sp,3.0) + 210*pow(P1x,3.0)*Q2x*pow(sp,3.0) + 126*pow(P1x,3.0)*Q3x*pow(sp,2.0) + 21*pow(P0x,3.0)*Q3x*pow(sp,4.0) - 84*pow(P1x,3.0)*Q2x*pow(sp,4.0) - + 210*pow(P1x,3.0)*Q3x*pow(sp,3.0) + 84*pow(P1x,3.0)*Q3x*pow(sp,4.0) - 24*pow(P0x,2.0)*pow(Q2y,2.0)*sp - 24*pow(P0y,2.0)*pow(Q2x,2.0)*sp + 48*pow(P0x,2.0)*pow(Q3y,2.0)*sp - 24*pow(P1x,2.0)*pow(Q2y,2.0)*sp + + 48*pow(P0y,2.0)*pow(Q3x,2.0)*sp - 24*pow(P1y,2.0)*pow(Q2x,2.0)*sp + 48*pow(P1x,2.0)*pow(Q3y,2.0)*sp + 48*pow(P1y,2.0)*pow(Q3x,2.0)*sp + 21*pow(P0y,2.0)*pow(Q2y,2.0)*sp + 21*pow(P0y,2.0)*pow(Q3y,2.0)*sp - + 63*pow(P0y,3.0)*Q2y*pow(sp,2.0) + 21*pow(P1y,2.0)*pow(Q2y,2.0)*sp + 70*pow(P0y,3.0)*Q2y*pow(sp,3.0) + 63*pow(P0y,3.0)*Q3y*pow(sp,2.0) + 21*pow(P1y,2.0)*pow(Q3y,2.0)*sp - 126*pow(P1y,3.0)*Q2y*pow(sp,2.0) - + 21*pow(P0y,3.0)*Q2y*pow(sp,4.0) - 70*pow(P0y,3.0)*Q3y*pow(sp,3.0) + 210*pow(P1y,3.0)*Q2y*pow(sp,3.0) + 126*pow(P1y,3.0)*Q3y*pow(sp,2.0) + 21*pow(P0y,3.0)*Q3y*pow(sp,4.0) - 84*pow(P1y,3.0)*Q2y*pow(sp,4.0) - + 210*pow(P1y,3.0)*Q3y*pow(sp,3.0) + 84*pow(P1y,3.0)*Q3y*pow(sp,4.0) - 21*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,2.0) - 105*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 21*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - + 21*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,2.0) + 84*pow(P0x,2.0)*pow(Q2x,2.0)*pow(sp,4.0) + 7*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 105*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 21*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - + 21*pow(P0x,2.0)*pow(Q3x,2.0)*pow(sp,4.0) + 84*pow(P1x,2.0)*pow(Q2x,2.0)*pow(sp,4.0) + 7*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 21*pow(P1x,2.0)*pow(Q3x,2.0)*pow(sp,4.0) - 48*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - + 48*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,2.0) - 24*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + 12*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 48*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - 24*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + + 12*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - 48*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,2.0) + 48*pow(P0x,2.0)*pow(Q2y,2.0)*pow(sp,4.0) - 8*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 24*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + + 12*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,2.0) + 48*pow(P0y,2.0)*pow(Q2x,2.0)*pow(sp,4.0) - 8*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 24*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,3.0) + 12*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,2.0) - + 12*pow(P0x,2.0)*pow(Q3y,2.0)*pow(sp,4.0) + 48*pow(P1x,2.0)*pow(Q2y,2.0)*pow(sp,4.0) - 8*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 12*pow(P0y,2.0)*pow(Q3x,2.0)*pow(sp,4.0) + 48*pow(P1y,2.0)*pow(Q2x,2.0)*pow(sp,4.0) - + 8*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,3.0) - 12*pow(P1x,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - 12*pow(P1y,2.0)*pow(Q3x,2.0)*pow(sp,4.0) - 21*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,2.0) - 105*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + + 21*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 21*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,2.0) + 84*pow(P0y,2.0)*pow(Q2y,2.0)*pow(sp,4.0) + 7*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 105*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,3.0) + + 21*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,2.0) - 21*pow(P0y,2.0)*pow(Q3y,2.0)*pow(sp,4.0) + 84*pow(P1y,2.0)*pow(Q2y,2.0)*pow(sp,4.0) + 7*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,3.0) - 21*pow(P1y,2.0)*pow(Q3y,2.0)*pow(sp,4.0) - + 42*P0x*P1x*pow(Q2x,2.0) + 14*P0x*pow(P1x,2.0)*Q2x - 28*pow(P0x,2.0)*P1x*Q2x - 42*P0x*P1x*pow(Q3x,2.0) - 14*P0x*pow(P1x,2.0)*Q3x + 28*pow(P0x,2.0)*P1x*Q3x - 14*pow(P0x,2.0)*P3x*Q2x + 14*pow(P0x,2.0)*P3x*Q3x - + 14*pow(P1x,2.0)*P3x*Q2x + 14*pow(P1x,2.0)*P3x*Q3x + 14*P0x*pow(P0y,2.0)*Q2x - 96*P0x*P1x*pow(Q2y,2.0) - 14*P0x*pow(P0y,2.0)*Q3x - 52*P0x*pow(P1y,2.0)*Q2x - 66*P1x*pow(P0y,2.0)*Q2x - 96*P0x*P1x*pow(Q3y,2.0) + + 52*P0x*pow(P1y,2.0)*Q3x + 66*P1x*pow(P0y,2.0)*Q3x + 16*P3x*pow(P0y,2.0)*Q2x - 16*P3x*pow(P0y,2.0)*Q3x + 16*P3x*pow(P1y,2.0)*Q2x - 16*P3x*pow(P1y,2.0)*Q3x + 14*pow(P0x,2.0)*P0y*Q2y - 96*P0y*P1y*pow(Q2x,2.0) - + 14*pow(P0x,2.0)*P0y*Q3y - 66*pow(P0x,2.0)*P1y*Q2y - 52*pow(P1x,2.0)*P0y*Q2y - 96*P0y*P1y*pow(Q3x,2.0) + 66*pow(P0x,2.0)*P1y*Q3y + 52*pow(P1x,2.0)*P0y*Q3y + 16*pow(P0x,2.0)*P3y*Q2y - 16*pow(P0x,2.0)*P3y*Q3y + + 16*pow(P1x,2.0)*P3y*Q2y - 16*pow(P1x,2.0)*P3y*Q3y - 42*P0y*P1y*pow(Q2y,2.0) + 14*P0y*pow(P1y,2.0)*Q2y - 28*pow(P0y,2.0)*P1y*Q2y - 42*P0y*P1y*pow(Q3y,2.0) - 14*P0y*pow(P1y,2.0)*Q3y + 28*pow(P0y,2.0)*P1y*Q3y - + 14*pow(P0y,2.0)*P3y*Q2y + 14*pow(P0y,2.0)*P3y*Q3y - 14*pow(P1y,2.0)*P3y*Q2y + 14*pow(P1y,2.0)*P3y*Q3y - 42*pow(P0x,2.0)*Q2x*Q3x - 42*pow(P1x,2.0)*Q2x*Q3x + 36*pow(P0y,2.0)*Q1x*Q2x - 36*pow(P0y,2.0)*Q1x*Q3x + + 36*pow(P1y,2.0)*Q1x*Q2x - 96*pow(P0y,2.0)*Q2x*Q3x - 36*pow(P1y,2.0)*Q1x*Q3x - 96*pow(P1y,2.0)*Q2x*Q3x + 36*pow(P0x,2.0)*Q1y*Q2y - 36*pow(P0x,2.0)*Q1y*Q3y + 36*pow(P1x,2.0)*Q1y*Q2y - 96*pow(P0x,2.0)*Q2y*Q3y - + 36*pow(P1x,2.0)*Q1y*Q3y - 96*pow(P1x,2.0)*Q2y*Q3y - 42*pow(P0y,2.0)*Q2y*Q3y - 42*pow(P1y,2.0)*Q2y*Q3y - 42*P0x*P1x*pow(Q2x,2.0)*sp - 42*P0x*P1x*pow(Q3x,2.0)*sp - 42*pow(P0x,2.0)*P3x*Q2x*sp + 42*pow(P0x,2.0)*P3x*Q3x*sp - + 42*pow(P1x,2.0)*P3x*Q2x*sp + 42*pow(P1x,2.0)*P3x*Q3x*sp + 48*P0x*P1x*pow(Q2y,2.0)*sp - 96*P0x*P1x*pow(Q3y,2.0)*sp + 48*P3x*pow(P0y,2.0)*Q2x*sp - 48*P3x*pow(P0y,2.0)*Q3x*sp + 48*P3x*pow(P1y,2.0)*Q2x*sp - + 48*P3x*pow(P1y,2.0)*Q3x*sp + 48*P0y*P1y*pow(Q2x,2.0)*sp - 96*P0y*P1y*pow(Q3x,2.0)*sp + 48*pow(P0x,2.0)*P3y*Q2y*sp - 48*pow(P0x,2.0)*P3y*Q3y*sp + 48*pow(P1x,2.0)*P3y*Q2y*sp - 48*pow(P1x,2.0)*P3y*Q3y*sp - + 42*P0y*P1y*pow(Q2y,2.0)*sp - 42*P0y*P1y*pow(Q3y,2.0)*sp - 42*pow(P0y,2.0)*P3y*Q2y*sp + 42*pow(P0y,2.0)*P3y*Q3y*sp - 42*pow(P1y,2.0)*P3y*Q2y*sp + 42*pow(P1y,2.0)*P3y*Q3y*sp + 42*pow(P0x,2.0)*Q1x*Q2x*sp - + 42*pow(P0x,2.0)*Q1x*Q3x*sp + 42*pow(P1x,2.0)*Q1x*Q2x*sp - 42*pow(P0x,2.0)*Q2x*Q3x*sp - 42*pow(P1x,2.0)*Q1x*Q3x*sp - 42*pow(P1x,2.0)*Q2x*Q3x*sp + 24*pow(P0y,2.0)*Q1x*Q2x*sp - 24*pow(P0y,2.0)*Q1x*Q3x*sp + + 24*pow(P1y,2.0)*Q1x*Q2x*sp - 24*pow(P0y,2.0)*Q2x*Q3x*sp - 24*pow(P1y,2.0)*Q1x*Q3x*sp - 24*pow(P1y,2.0)*Q2x*Q3x*sp + 24*pow(P0x,2.0)*Q1y*Q2y*sp - 24*pow(P0x,2.0)*Q1y*Q3y*sp + 24*pow(P1x,2.0)*Q1y*Q2y*sp - + 24*pow(P0x,2.0)*Q2y*Q3y*sp - 24*pow(P1x,2.0)*Q1y*Q3y*sp - 24*pow(P1x,2.0)*Q2y*Q3y*sp + 42*pow(P0y,2.0)*Q1y*Q2y*sp - 42*pow(P0y,2.0)*Q1y*Q3y*sp + 42*pow(P1y,2.0)*Q1y*Q2y*sp - 42*pow(P0y,2.0)*Q2y*Q3y*sp - + 42*pow(P1y,2.0)*Q1y*Q3y*sp - 42*pow(P1y,2.0)*Q2y*Q3y*sp + 42*P0x*P1x*pow(Q2x,2.0)*pow(sp,2.0) + 189*P0x*pow(P1x,2.0)*Q2x*pow(sp,2.0) + 210*P0x*P1x*pow(Q2x,2.0)*pow(sp,3.0) - 42*P0x*P1x*pow(Q3x,2.0)*pow(sp,2.0) - + 350*P0x*pow(P1x,2.0)*Q2x*pow(sp,3.0) - 189*P0x*pow(P1x,2.0)*Q3x*pow(sp,2.0) + 70*pow(P0x,2.0)*P1x*Q2x*pow(sp,3.0) - 189*pow(P0x,2.0)*P2x*Q2x*pow(sp,2.0) - 168*P0x*P1x*pow(Q2x,2.0)*pow(sp,4.0) - + 14*P0x*P1x*pow(Q3x,2.0)*pow(sp,3.0) + 147*P0x*pow(P1x,2.0)*Q2x*pow(sp,4.0) + 350*P0x*pow(P1x,2.0)*Q3x*pow(sp,3.0) - 42*pow(P0x,2.0)*P1x*Q2x*pow(sp,4.0) - 70*pow(P0x,2.0)*P1x*Q3x*pow(sp,3.0) + + 420*pow(P0x,2.0)*P2x*Q2x*pow(sp,3.0) + 189*pow(P0x,2.0)*P2x*Q3x*pow(sp,2.0) + 294*pow(P0x,2.0)*P3x*Q2x*pow(sp,2.0) - 189*pow(P1x,2.0)*P2x*Q2x*pow(sp,2.0) + 42*P0x*P1x*pow(Q3x,2.0)*pow(sp,4.0) - + 147*P0x*pow(P1x,2.0)*Q3x*pow(sp,4.0) + 42*pow(P0x,2.0)*P1x*Q3x*pow(sp,4.0) - 210*pow(P0x,2.0)*P2x*Q2x*pow(sp,4.0) - 420*pow(P0x,2.0)*P2x*Q3x*pow(sp,3.0) - 210*pow(P0x,2.0)*P3x*Q2x*pow(sp,3.0) - + 294*pow(P0x,2.0)*P3x*Q3x*pow(sp,2.0) + 420*pow(P1x,2.0)*P2x*Q2x*pow(sp,3.0) + 189*pow(P1x,2.0)*P2x*Q3x*pow(sp,2.0) + 294*pow(P1x,2.0)*P3x*Q2x*pow(sp,2.0) + 210*pow(P0x,2.0)*P2x*Q3x*pow(sp,4.0) + + 210*pow(P0x,2.0)*P3x*Q3x*pow(sp,3.0) - 210*pow(P1x,2.0)*P2x*Q2x*pow(sp,4.0) - 420*pow(P1x,2.0)*P2x*Q3x*pow(sp,3.0) - 210*pow(P1x,2.0)*P3x*Q2x*pow(sp,3.0) - 294*pow(P1x,2.0)*P3x*Q3x*pow(sp,2.0) + + 210*pow(P1x,2.0)*P2x*Q3x*pow(sp,4.0) + 210*pow(P1x,2.0)*P3x*Q3x*pow(sp,3.0) - 63*P0x*pow(P0y,2.0)*Q2x*pow(sp,2.0) + 96*P0x*P1x*pow(Q2y,2.0)*pow(sp,2.0) + 70*P0x*pow(P0y,2.0)*Q2x*pow(sp,3.0) + + 63*P0x*pow(P0y,2.0)*Q3x*pow(sp,2.0) + 126*P0x*pow(P1y,2.0)*Q2x*pow(sp,2.0) + 63*P1x*pow(P0y,2.0)*Q2x*pow(sp,2.0) + 48*P0x*P1x*pow(Q2y,2.0)*pow(sp,3.0) - 24*P0x*P1x*pow(Q3y,2.0)*pow(sp,2.0) - + 21*P0x*pow(P0y,2.0)*Q2x*pow(sp,4.0) - 70*P0x*pow(P0y,2.0)*Q3x*pow(sp,3.0) - 98*P0x*pow(P1y,2.0)*Q2x*pow(sp,3.0) - 126*P0x*pow(P1y,2.0)*Q3x*pow(sp,2.0) + 42*P1x*pow(P0y,2.0)*Q2x*pow(sp,3.0) - + 63*P1x*pow(P0y,2.0)*Q3x*pow(sp,2.0) - 126*P1x*pow(P1y,2.0)*Q2x*pow(sp,2.0) - 96*P0x*P1x*pow(Q2y,2.0)*pow(sp,4.0) + 16*P0x*P1x*pow(Q3y,2.0)*pow(sp,3.0) + 21*P0x*pow(P0y,2.0)*Q3x*pow(sp,4.0) + + 24*P0x*pow(P1y,2.0)*Q2x*pow(sp,4.0) + 98*P0x*pow(P1y,2.0)*Q3x*pow(sp,3.0) - 39*P1x*pow(P0y,2.0)*Q2x*pow(sp,4.0) - 42*P1x*pow(P0y,2.0)*Q3x*pow(sp,3.0) + 210*P1x*pow(P1y,2.0)*Q2x*pow(sp,3.0) + + 126*P1x*pow(P1y,2.0)*Q3x*pow(sp,2.0) + 168*P2x*pow(P0y,2.0)*Q2x*pow(sp,3.0) + 96*P3x*pow(P0y,2.0)*Q2x*pow(sp,2.0) + 24*P0x*P1x*pow(Q3y,2.0)*pow(sp,4.0) - 24*P0x*pow(P1y,2.0)*Q3x*pow(sp,4.0) + + 39*P1x*pow(P0y,2.0)*Q3x*pow(sp,4.0) - 84*P1x*pow(P1y,2.0)*Q2x*pow(sp,4.0) - 210*P1x*pow(P1y,2.0)*Q3x*pow(sp,3.0) - 120*P2x*pow(P0y,2.0)*Q2x*pow(sp,4.0) - 168*P2x*pow(P0y,2.0)*Q3x*pow(sp,3.0) + + 168*P2x*pow(P1y,2.0)*Q2x*pow(sp,3.0) - 120*P3x*pow(P0y,2.0)*Q2x*pow(sp,3.0) - 96*P3x*pow(P0y,2.0)*Q3x*pow(sp,2.0) + 96*P3x*pow(P1y,2.0)*Q2x*pow(sp,2.0) + 84*P1x*pow(P1y,2.0)*Q3x*pow(sp,4.0) + + 120*P2x*pow(P0y,2.0)*Q3x*pow(sp,4.0) - 120*P2x*pow(P1y,2.0)*Q2x*pow(sp,4.0) - 168*P2x*pow(P1y,2.0)*Q3x*pow(sp,3.0) + 120*P3x*pow(P0y,2.0)*Q3x*pow(sp,3.0) - 120*P3x*pow(P1y,2.0)*Q2x*pow(sp,3.0) - + 96*P3x*pow(P1y,2.0)*Q3x*pow(sp,2.0) + 120*P2x*pow(P1y,2.0)*Q3x*pow(sp,4.0) + 120*P3x*pow(P1y,2.0)*Q3x*pow(sp,3.0) - 63*pow(P0x,2.0)*P0y*Q2y*pow(sp,2.0) + 96*P0y*P1y*pow(Q2x,2.0)*pow(sp,2.0) + + 70*pow(P0x,2.0)*P0y*Q2y*pow(sp,3.0) + 63*pow(P0x,2.0)*P0y*Q3y*pow(sp,2.0) + 63*pow(P0x,2.0)*P1y*Q2y*pow(sp,2.0) + 126*pow(P1x,2.0)*P0y*Q2y*pow(sp,2.0) + 48*P0y*P1y*pow(Q2x,2.0)*pow(sp,3.0) - + 24*P0y*P1y*pow(Q3x,2.0)*pow(sp,2.0) - 21*pow(P0x,2.0)*P0y*Q2y*pow(sp,4.0) - 70*pow(P0x,2.0)*P0y*Q3y*pow(sp,3.0) + 42*pow(P0x,2.0)*P1y*Q2y*pow(sp,3.0) - 63*pow(P0x,2.0)*P1y*Q3y*pow(sp,2.0) - + 98*pow(P1x,2.0)*P0y*Q2y*pow(sp,3.0) - 126*pow(P1x,2.0)*P0y*Q3y*pow(sp,2.0) - 126*pow(P1x,2.0)*P1y*Q2y*pow(sp,2.0) - 96*P0y*P1y*pow(Q2x,2.0)*pow(sp,4.0) + 16*P0y*P1y*pow(Q3x,2.0)*pow(sp,3.0) + + 21*pow(P0x,2.0)*P0y*Q3y*pow(sp,4.0) - 39*pow(P0x,2.0)*P1y*Q2y*pow(sp,4.0) - 42*pow(P0x,2.0)*P1y*Q3y*pow(sp,3.0) + 168*pow(P0x,2.0)*P2y*Q2y*pow(sp,3.0) + 96*pow(P0x,2.0)*P3y*Q2y*pow(sp,2.0) + + 24*pow(P1x,2.0)*P0y*Q2y*pow(sp,4.0) + 98*pow(P1x,2.0)*P0y*Q3y*pow(sp,3.0) + 210*pow(P1x,2.0)*P1y*Q2y*pow(sp,3.0) + 126*pow(P1x,2.0)*P1y*Q3y*pow(sp,2.0) + 24*P0y*P1y*pow(Q3x,2.0)*pow(sp,4.0) + + 39*pow(P0x,2.0)*P1y*Q3y*pow(sp,4.0) - 120*pow(P0x,2.0)*P2y*Q2y*pow(sp,4.0) - 168*pow(P0x,2.0)*P2y*Q3y*pow(sp,3.0) - 120*pow(P0x,2.0)*P3y*Q2y*pow(sp,3.0) - 96*pow(P0x,2.0)*P3y*Q3y*pow(sp,2.0) - + 24*pow(P1x,2.0)*P0y*Q3y*pow(sp,4.0) - 84*pow(P1x,2.0)*P1y*Q2y*pow(sp,4.0) - 210*pow(P1x,2.0)*P1y*Q3y*pow(sp,3.0) + 168*pow(P1x,2.0)*P2y*Q2y*pow(sp,3.0) + 96*pow(P1x,2.0)*P3y*Q2y*pow(sp,2.0) + + 120*pow(P0x,2.0)*P2y*Q3y*pow(sp,4.0) + 120*pow(P0x,2.0)*P3y*Q3y*pow(sp,3.0) + 84*pow(P1x,2.0)*P1y*Q3y*pow(sp,4.0) - 120*pow(P1x,2.0)*P2y*Q2y*pow(sp,4.0) - 168*pow(P1x,2.0)*P2y*Q3y*pow(sp,3.0) - + 120*pow(P1x,2.0)*P3y*Q2y*pow(sp,3.0) - 96*pow(P1x,2.0)*P3y*Q3y*pow(sp,2.0) + 120*pow(P1x,2.0)*P2y*Q3y*pow(sp,4.0) + 120*pow(P1x,2.0)*P3y*Q3y*pow(sp,3.0) + 42*P0y*P1y*pow(Q2y,2.0)*pow(sp,2.0) + + 189*P0y*pow(P1y,2.0)*Q2y*pow(sp,2.0) + 210*P0y*P1y*pow(Q2y,2.0)*pow(sp,3.0) - 42*P0y*P1y*pow(Q3y,2.0)*pow(sp,2.0) - 350*P0y*pow(P1y,2.0)*Q2y*pow(sp,3.0) - 189*P0y*pow(P1y,2.0)*Q3y*pow(sp,2.0) + + 70*pow(P0y,2.0)*P1y*Q2y*pow(sp,3.0) - 189*pow(P0y,2.0)*P2y*Q2y*pow(sp,2.0) - 168*P0y*P1y*pow(Q2y,2.0)*pow(sp,4.0) - 14*P0y*P1y*pow(Q3y,2.0)*pow(sp,3.0) + 147*P0y*pow(P1y,2.0)*Q2y*pow(sp,4.0) + + 350*P0y*pow(P1y,2.0)*Q3y*pow(sp,3.0) - 42*pow(P0y,2.0)*P1y*Q2y*pow(sp,4.0) - 70*pow(P0y,2.0)*P1y*Q3y*pow(sp,3.0) + 420*pow(P0y,2.0)*P2y*Q2y*pow(sp,3.0) + 189*pow(P0y,2.0)*P2y*Q3y*pow(sp,2.0) + + 294*pow(P0y,2.0)*P3y*Q2y*pow(sp,2.0) - 189*pow(P1y,2.0)*P2y*Q2y*pow(sp,2.0) + 42*P0y*P1y*pow(Q3y,2.0)*pow(sp,4.0) - 147*P0y*pow(P1y,2.0)*Q3y*pow(sp,4.0) + 42*pow(P0y,2.0)*P1y*Q3y*pow(sp,4.0) - + 210*pow(P0y,2.0)*P2y*Q2y*pow(sp,4.0) - 420*pow(P0y,2.0)*P2y*Q3y*pow(sp,3.0) - 210*pow(P0y,2.0)*P3y*Q2y*pow(sp,3.0) - 294*pow(P0y,2.0)*P3y*Q3y*pow(sp,2.0) + 420*pow(P1y,2.0)*P2y*Q2y*pow(sp,3.0) + + 189*pow(P1y,2.0)*P2y*Q3y*pow(sp,2.0) + 294*pow(P1y,2.0)*P3y*Q2y*pow(sp,2.0) + 210*pow(P0y,2.0)*P2y*Q3y*pow(sp,4.0) + 210*pow(P0y,2.0)*P3y*Q3y*pow(sp,3.0) - 210*pow(P1y,2.0)*P2y*Q2y*pow(sp,4.0) - + 420*pow(P1y,2.0)*P2y*Q3y*pow(sp,3.0) - 210*pow(P1y,2.0)*P3y*Q2y*pow(sp,3.0) - 294*pow(P1y,2.0)*P3y*Q3y*pow(sp,2.0) + 210*pow(P1y,2.0)*P2y*Q3y*pow(sp,4.0) + 210*pow(P1y,2.0)*P3y*Q3y*pow(sp,3.0) + + 126*pow(P0x,2.0)*Q1x*Q2x*pow(sp,2.0) - 378*pow(P0x,2.0)*Q1x*Q2x*pow(sp,3.0) - 126*pow(P0x,2.0)*Q1x*Q3x*pow(sp,2.0) + 126*pow(P1x,2.0)*Q1x*Q2x*pow(sp,2.0) + 210*pow(P0x,2.0)*Q1x*Q2x*pow(sp,4.0) + + 378*pow(P0x,2.0)*Q1x*Q3x*pow(sp,3.0) - 378*pow(P1x,2.0)*Q1x*Q2x*pow(sp,3.0) - 126*pow(P1x,2.0)*Q1x*Q3x*pow(sp,2.0) - 210*pow(P0x,2.0)*Q1x*Q3x*pow(sp,4.0) + 98*pow(P0x,2.0)*Q2x*Q3x*pow(sp,3.0) + + 210*pow(P1x,2.0)*Q1x*Q2x*pow(sp,4.0) + 378*pow(P1x,2.0)*Q1x*Q3x*pow(sp,3.0) - 63*pow(P0x,2.0)*Q2x*Q3x*pow(sp,4.0) - 210*pow(P1x,2.0)*Q1x*Q3x*pow(sp,4.0) + 98*pow(P1x,2.0)*Q2x*Q3x*pow(sp,3.0) - + 63*pow(P1x,2.0)*Q2x*Q3x*pow(sp,4.0) - 36*pow(P0y,2.0)*Q1x*Q2x*pow(sp,2.0) - 144*pow(P0y,2.0)*Q1x*Q2x*pow(sp,3.0) + 36*pow(P0y,2.0)*Q1x*Q3x*pow(sp,2.0) - 36*pow(P1y,2.0)*Q1x*Q2x*pow(sp,2.0) + + 120*pow(P0y,2.0)*Q1x*Q2x*pow(sp,4.0) + 144*pow(P0y,2.0)*Q1x*Q3x*pow(sp,3.0) + 36*pow(P0y,2.0)*Q2x*Q3x*pow(sp,2.0) - 144*pow(P1y,2.0)*Q1x*Q2x*pow(sp,3.0) + 36*pow(P1y,2.0)*Q1x*Q3x*pow(sp,2.0) - + 120*pow(P0y,2.0)*Q1x*Q3x*pow(sp,4.0) + 32*pow(P0y,2.0)*Q2x*Q3x*pow(sp,3.0) + 120*pow(P1y,2.0)*Q1x*Q2x*pow(sp,4.0) + 144*pow(P1y,2.0)*Q1x*Q3x*pow(sp,3.0) + 36*pow(P1y,2.0)*Q2x*Q3x*pow(sp,2.0) - + 36*pow(P0y,2.0)*Q2x*Q3x*pow(sp,4.0) - 120*pow(P1y,2.0)*Q1x*Q3x*pow(sp,4.0) + 32*pow(P1y,2.0)*Q2x*Q3x*pow(sp,3.0) - 36*pow(P1y,2.0)*Q2x*Q3x*pow(sp,4.0) - 36*pow(P0x,2.0)*Q1y*Q2y*pow(sp,2.0) - + 144*pow(P0x,2.0)*Q1y*Q2y*pow(sp,3.0) + 36*pow(P0x,2.0)*Q1y*Q3y*pow(sp,2.0) - 36*pow(P1x,2.0)*Q1y*Q2y*pow(sp,2.0) + 120*pow(P0x,2.0)*Q1y*Q2y*pow(sp,4.0) + 144*pow(P0x,2.0)*Q1y*Q3y*pow(sp,3.0) + + 36*pow(P0x,2.0)*Q2y*Q3y*pow(sp,2.0) - 144*pow(P1x,2.0)*Q1y*Q2y*pow(sp,3.0) + 36*pow(P1x,2.0)*Q1y*Q3y*pow(sp,2.0) - 120*pow(P0x,2.0)*Q1y*Q3y*pow(sp,4.0) + 32*pow(P0x,2.0)*Q2y*Q3y*pow(sp,3.0) + + 120*pow(P1x,2.0)*Q1y*Q2y*pow(sp,4.0) + 144*pow(P1x,2.0)*Q1y*Q3y*pow(sp,3.0) + 36*pow(P1x,2.0)*Q2y*Q3y*pow(sp,2.0) - 36*pow(P0x,2.0)*Q2y*Q3y*pow(sp,4.0) - 120*pow(P1x,2.0)*Q1y*Q3y*pow(sp,4.0) + + 32*pow(P1x,2.0)*Q2y*Q3y*pow(sp,3.0) - 36*pow(P1x,2.0)*Q2y*Q3y*pow(sp,4.0) + 126*pow(P0y,2.0)*Q1y*Q2y*pow(sp,2.0) - 378*pow(P0y,2.0)*Q1y*Q2y*pow(sp,3.0) - 126*pow(P0y,2.0)*Q1y*Q3y*pow(sp,2.0) + + 126*pow(P1y,2.0)*Q1y*Q2y*pow(sp,2.0) + 210*pow(P0y,2.0)*Q1y*Q2y*pow(sp,4.0) + 378*pow(P0y,2.0)*Q1y*Q3y*pow(sp,3.0) - 378*pow(P1y,2.0)*Q1y*Q2y*pow(sp,3.0) - 126*pow(P1y,2.0)*Q1y*Q3y*pow(sp,2.0) - + 210*pow(P0y,2.0)*Q1y*Q3y*pow(sp,4.0) + 98*pow(P0y,2.0)*Q2y*Q3y*pow(sp,3.0) + 210*pow(P1y,2.0)*Q1y*Q2y*pow(sp,4.0) + 378*pow(P1y,2.0)*Q1y*Q3y*pow(sp,3.0) - 63*pow(P0y,2.0)*Q2y*Q3y*pow(sp,4.0) - + 210*pow(P1y,2.0)*Q1y*Q3y*pow(sp,4.0) + 98*pow(P1y,2.0)*Q2y*Q3y*pow(sp,3.0) - 63*pow(P1y,2.0)*Q2y*Q3y*pow(sp,4.0) + 28*P0x*P1x*P3x*Q2x - 28*P0x*P1x*P3x*Q3x + 38*P0x*P1x*P0y*Q2y + 38*P0x*P0y*P1y*Q2x - + 38*P0x*P1x*P0y*Q3y + 66*P0x*P1x*P1y*Q2y - 38*P0x*P0y*P1y*Q3x + 66*P1x*P0y*P1y*Q2x - 66*P0x*P1x*P1y*Q3y - 30*P0x*P3x*P0y*Q2y - 30*P0x*P0y*P3y*Q2x - 66*P1x*P0y*P1y*Q3x - 32*P0x*P1x*P3y*Q2y + 30*P0x*P3x*P0y*Q3y + + 30*P0x*P3x*P1y*Q2y + 30*P0x*P0y*P3y*Q3x + 30*P0x*P1y*P3y*Q2x + 30*P1x*P3x*P0y*Q2y + 30*P1x*P0y*P3y*Q2x - 32*P3x*P0y*P1y*Q2x + 32*P0x*P1x*P3y*Q3y - 30*P0x*P3x*P1y*Q3y - 30*P0x*P1y*P3y*Q3x - 30*P1x*P3x*P0y*Q3y - + 30*P1x*P3x*P1y*Q2y - 30*P1x*P0y*P3y*Q3x - 30*P1x*P1y*P3y*Q2x + 32*P3x*P0y*P1y*Q3x + 30*P1x*P3x*P1y*Q3y + 30*P1x*P1y*P3y*Q3x + 28*P0y*P1y*P3y*Q2y - 28*P0y*P1y*P3y*Q3y + 84*P0x*P1x*Q2x*Q3x - 36*P0x*P0y*Q1x*Q2y - + 36*P0x*P0y*Q2x*Q1y - 72*P0x*P1x*Q1y*Q2y + 36*P0x*P0y*Q1x*Q3y - 54*P0x*P0y*Q2x*Q2y + 36*P0x*P0y*Q3x*Q1y + 36*P0x*P1y*Q1x*Q2y + 36*P0x*P1y*Q2x*Q1y + 36*P1x*P0y*Q1x*Q2y + 36*P1x*P0y*Q2x*Q1y - 72*P0y*P1y*Q1x*Q2x + + 72*P0x*P1x*Q1y*Q3y + 54*P0x*P0y*Q2x*Q3y + 54*P0x*P0y*Q3x*Q2y - 36*P0x*P1y*Q1x*Q3y + 54*P0x*P1y*Q2x*Q2y - 36*P0x*P1y*Q3x*Q1y - 36*P1x*P0y*Q1x*Q3y + 54*P1x*P0y*Q2x*Q2y - 36*P1x*P0y*Q3x*Q1y - 36*P1x*P1y*Q1x*Q2y - + 36*P1x*P1y*Q2x*Q1y + 72*P0y*P1y*Q1x*Q3x + 192*P0x*P1x*Q2y*Q3y - 54*P0x*P0y*Q3x*Q3y - 54*P0x*P1y*Q2x*Q3y - 54*P0x*P1y*Q3x*Q2y - 54*P1x*P0y*Q2x*Q3y - 54*P1x*P0y*Q3x*Q2y + 36*P1x*P1y*Q1x*Q3y - 54*P1x*P1y*Q2x*Q2y + + 36*P1x*P1y*Q3x*Q1y + 192*P0y*P1y*Q2x*Q3x + 54*P0x*P1y*Q3x*Q3y + 54*P1x*P0y*Q3x*Q3y + 54*P1x*P1y*Q2x*Q3y + 54*P1x*P1y*Q3x*Q2y - 54*P1x*P1y*Q3x*Q3y + 84*P0y*P1y*Q2y*Q3y + 84*P0x*P1x*P3x*Q2x*sp - 84*P0x*P1x*P3x*Q3x*sp - + 90*P0x*P3x*P0y*Q2y*sp - 90*P0x*P0y*P3y*Q2x*sp - 96*P0x*P1x*P3y*Q2y*sp + 90*P0x*P3x*P0y*Q3y*sp + 90*P0x*P3x*P1y*Q2y*sp + 90*P0x*P0y*P3y*Q3x*sp + 90*P0x*P1y*P3y*Q2x*sp + 90*P1x*P3x*P0y*Q2y*sp + 90*P1x*P0y*P3y*Q2x*sp - + 96*P3x*P0y*P1y*Q2x*sp + 96*P0x*P1x*P3y*Q3y*sp - 90*P0x*P3x*P1y*Q3y*sp - 90*P0x*P1y*P3y*Q3x*sp - 90*P1x*P3x*P0y*Q3y*sp - 90*P1x*P3x*P1y*Q2y*sp - 90*P1x*P0y*P3y*Q3x*sp - 90*P1x*P1y*P3y*Q2x*sp + 96*P3x*P0y*P1y*Q3x*sp + + 90*P1x*P3x*P1y*Q3y*sp + 90*P1x*P1y*P3y*Q3x*sp + 84*P0y*P1y*P3y*Q2y*sp - 84*P0y*P1y*P3y*Q3y*sp - 84*P0x*P1x*Q1x*Q2x*sp + 84*P0x*P1x*Q1x*Q3x*sp + 84*P0x*P1x*Q2x*Q3x*sp + 18*P0x*P0y*Q1x*Q2y*sp + 18*P0x*P0y*Q2x*Q1y*sp - + 48*P0x*P1x*Q1y*Q2y*sp - 18*P0x*P0y*Q1x*Q3y*sp + 90*P0x*P0y*Q2x*Q2y*sp - 18*P0x*P0y*Q3x*Q1y*sp - 18*P0x*P1y*Q1x*Q2y*sp - 18*P0x*P1y*Q2x*Q1y*sp - 18*P1x*P0y*Q1x*Q2y*sp - 18*P1x*P0y*Q2x*Q1y*sp - 48*P0y*P1y*Q1x*Q2x*sp + + 48*P0x*P1x*Q1y*Q3y*sp - 18*P0x*P0y*Q2x*Q3y*sp - 18*P0x*P0y*Q3x*Q2y*sp + 18*P0x*P1y*Q1x*Q3y*sp - 90*P0x*P1y*Q2x*Q2y*sp + 18*P0x*P1y*Q3x*Q1y*sp + 18*P1x*P0y*Q1x*Q3y*sp - 90*P1x*P0y*Q2x*Q2y*sp + 18*P1x*P0y*Q3x*Q1y*sp + + 18*P1x*P1y*Q1x*Q2y*sp + 18*P1x*P1y*Q2x*Q1y*sp + 48*P0y*P1y*Q1x*Q3x*sp + 48*P0x*P1x*Q2y*Q3y*sp - 54*P0x*P0y*Q3x*Q3y*sp + 18*P0x*P1y*Q2x*Q3y*sp + 18*P0x*P1y*Q3x*Q2y*sp + 18*P1x*P0y*Q2x*Q3y*sp + 18*P1x*P0y*Q3x*Q2y*sp - + 18*P1x*P1y*Q1x*Q3y*sp + 90*P1x*P1y*Q2x*Q2y*sp - 18*P1x*P1y*Q3x*Q1y*sp + 48*P0y*P1y*Q2x*Q3x*sp + 54*P0x*P1y*Q3x*Q3y*sp + 54*P1x*P0y*Q3x*Q3y*sp - 18*P1x*P1y*Q2x*Q3y*sp - 18*P1x*P1y*Q3x*Q2y*sp - 54*P1x*P1y*Q3x*Q3y*sp - + 84*P0y*P1y*Q1y*Q2y*sp + 84*P0y*P1y*Q1y*Q3y*sp + 84*P0y*P1y*Q2y*Q3y*sp + 378*P0x*P1x*P2x*Q2x*pow(sp,2.0) - 840*P0x*P1x*P2x*Q2x*pow(sp,3.0) - 378*P0x*P1x*P2x*Q3x*pow(sp,2.0) - 588*P0x*P1x*P3x*Q2x*pow(sp,2.0) + + 420*P0x*P1x*P2x*Q2x*pow(sp,4.0) + 840*P0x*P1x*P2x*Q3x*pow(sp,3.0) + 420*P0x*P1x*P3x*Q2x*pow(sp,3.0) + 588*P0x*P1x*P3x*Q3x*pow(sp,2.0) - 420*P0x*P1x*P2x*Q3x*pow(sp,4.0) - 420*P0x*P1x*P3x*Q3x*pow(sp,3.0) - + 63*P0x*P1x*P0y*Q2y*pow(sp,2.0) - 63*P0x*P0y*P1y*Q2x*pow(sp,2.0) + 28*P0x*P1x*P0y*Q2y*pow(sp,3.0) + 63*P0x*P1x*P0y*Q3y*pow(sp,2.0) + 63*P0x*P1x*P1y*Q2y*pow(sp,2.0) - 189*P0x*P2x*P0y*Q2y*pow(sp,2.0) + + 28*P0x*P0y*P1y*Q2x*pow(sp,3.0) + 63*P0x*P0y*P1y*Q3x*pow(sp,2.0) - 189*P0x*P0y*P2y*Q2x*pow(sp,2.0) + 63*P1x*P0y*P1y*Q2x*pow(sp,2.0) - 3*P0x*P1x*P0y*Q2y*pow(sp,4.0) - 28*P0x*P1x*P0y*Q3y*pow(sp,3.0) - + 252*P0x*P1x*P1y*Q2y*pow(sp,3.0) - 63*P0x*P1x*P1y*Q3y*pow(sp,2.0) + 252*P0x*P2x*P0y*Q2y*pow(sp,3.0) + 189*P0x*P2x*P0y*Q3y*pow(sp,2.0) + 189*P0x*P2x*P1y*Q2y*pow(sp,2.0) + 198*P0x*P3x*P0y*Q2y*pow(sp,2.0) - + 3*P0x*P0y*P1y*Q2x*pow(sp,4.0) - 28*P0x*P0y*P1y*Q3x*pow(sp,3.0) + 252*P0x*P0y*P2y*Q2x*pow(sp,3.0) + 189*P0x*P0y*P2y*Q3x*pow(sp,2.0) + 198*P0x*P0y*P3y*Q2x*pow(sp,2.0) + 189*P0x*P1y*P2y*Q2x*pow(sp,2.0) + + 189*P1x*P2x*P0y*Q2y*pow(sp,2.0) - 252*P1x*P0y*P1y*Q2x*pow(sp,3.0) - 63*P1x*P0y*P1y*Q3x*pow(sp,2.0) + 189*P1x*P0y*P2y*Q2x*pow(sp,2.0) + 3*P0x*P1x*P0y*Q3y*pow(sp,4.0) + 123*P0x*P1x*P1y*Q2y*pow(sp,4.0) + + 252*P0x*P1x*P1y*Q3y*pow(sp,3.0) - 336*P0x*P1x*P2y*Q2y*pow(sp,3.0) - 192*P0x*P1x*P3y*Q2y*pow(sp,2.0) - 90*P0x*P2x*P0y*Q2y*pow(sp,4.0) - 252*P0x*P2x*P0y*Q3y*pow(sp,3.0) - 252*P0x*P2x*P1y*Q2y*pow(sp,3.0) - + 189*P0x*P2x*P1y*Q3y*pow(sp,2.0) - 90*P0x*P3x*P0y*Q2y*pow(sp,3.0) - 198*P0x*P3x*P0y*Q3y*pow(sp,2.0) - 198*P0x*P3x*P1y*Q2y*pow(sp,2.0) + 3*P0x*P0y*P1y*Q3x*pow(sp,4.0) - 90*P0x*P0y*P2y*Q2x*pow(sp,4.0) - + 252*P0x*P0y*P2y*Q3x*pow(sp,3.0) - 90*P0x*P0y*P3y*Q2x*pow(sp,3.0) - 198*P0x*P0y*P3y*Q3x*pow(sp,2.0) - 252*P0x*P1y*P2y*Q2x*pow(sp,3.0) - 189*P0x*P1y*P2y*Q3x*pow(sp,2.0) - 198*P0x*P1y*P3y*Q2x*pow(sp,2.0) - + 252*P1x*P2x*P0y*Q2y*pow(sp,3.0) - 189*P1x*P2x*P0y*Q3y*pow(sp,2.0) - 189*P1x*P2x*P1y*Q2y*pow(sp,2.0) - 198*P1x*P3x*P0y*Q2y*pow(sp,2.0) + 123*P1x*P0y*P1y*Q2x*pow(sp,4.0) + 252*P1x*P0y*P1y*Q3x*pow(sp,3.0) - + 252*P1x*P0y*P2y*Q2x*pow(sp,3.0) - 189*P1x*P0y*P2y*Q3x*pow(sp,2.0) - 198*P1x*P0y*P3y*Q2x*pow(sp,2.0) - 189*P1x*P1y*P2y*Q2x*pow(sp,2.0) - 336*P2x*P0y*P1y*Q2x*pow(sp,3.0) - 192*P3x*P0y*P1y*Q2x*pow(sp,2.0) - + 123*P0x*P1x*P1y*Q3y*pow(sp,4.0) + 240*P0x*P1x*P2y*Q2y*pow(sp,4.0) + 336*P0x*P1x*P2y*Q3y*pow(sp,3.0) + 240*P0x*P1x*P3y*Q2y*pow(sp,3.0) + 192*P0x*P1x*P3y*Q3y*pow(sp,2.0) + 90*P0x*P2x*P0y*Q3y*pow(sp,4.0) + + 90*P0x*P2x*P1y*Q2y*pow(sp,4.0) + 252*P0x*P2x*P1y*Q3y*pow(sp,3.0) + 90*P0x*P3x*P0y*Q3y*pow(sp,3.0) + 90*P0x*P3x*P1y*Q2y*pow(sp,3.0) + 198*P0x*P3x*P1y*Q3y*pow(sp,2.0) + 90*P0x*P0y*P2y*Q3x*pow(sp,4.0) + + 90*P0x*P0y*P3y*Q3x*pow(sp,3.0) + 90*P0x*P1y*P2y*Q2x*pow(sp,4.0) + 252*P0x*P1y*P2y*Q3x*pow(sp,3.0) + 90*P0x*P1y*P3y*Q2x*pow(sp,3.0) + 198*P0x*P1y*P3y*Q3x*pow(sp,2.0) + 90*P1x*P2x*P0y*Q2y*pow(sp,4.0) + + 252*P1x*P2x*P0y*Q3y*pow(sp,3.0) + 252*P1x*P2x*P1y*Q2y*pow(sp,3.0) + 189*P1x*P2x*P1y*Q3y*pow(sp,2.0) + 90*P1x*P3x*P0y*Q2y*pow(sp,3.0) + 198*P1x*P3x*P0y*Q3y*pow(sp,2.0) + 198*P1x*P3x*P1y*Q2y*pow(sp,2.0) - + 123*P1x*P0y*P1y*Q3x*pow(sp,4.0) + 90*P1x*P0y*P2y*Q2x*pow(sp,4.0) + 252*P1x*P0y*P2y*Q3x*pow(sp,3.0) + 90*P1x*P0y*P3y*Q2x*pow(sp,3.0) + 198*P1x*P0y*P3y*Q3x*pow(sp,2.0) + 252*P1x*P1y*P2y*Q2x*pow(sp,3.0) + + 189*P1x*P1y*P2y*Q3x*pow(sp,2.0) + 198*P1x*P1y*P3y*Q2x*pow(sp,2.0) + 240*P2x*P0y*P1y*Q2x*pow(sp,4.0) + 336*P2x*P0y*P1y*Q3x*pow(sp,3.0) + 240*P3x*P0y*P1y*Q2x*pow(sp,3.0) + 192*P3x*P0y*P1y*Q3x*pow(sp,2.0) - + 240*P0x*P1x*P2y*Q3y*pow(sp,4.0) - 240*P0x*P1x*P3y*Q3y*pow(sp,3.0) - 90*P0x*P2x*P1y*Q3y*pow(sp,4.0) - 90*P0x*P3x*P1y*Q3y*pow(sp,3.0) - 90*P0x*P1y*P2y*Q3x*pow(sp,4.0) - 90*P0x*P1y*P3y*Q3x*pow(sp,3.0) - + 90*P1x*P2x*P0y*Q3y*pow(sp,4.0) - 90*P1x*P2x*P1y*Q2y*pow(sp,4.0) - 252*P1x*P2x*P1y*Q3y*pow(sp,3.0) - 90*P1x*P3x*P0y*Q3y*pow(sp,3.0) - 90*P1x*P3x*P1y*Q2y*pow(sp,3.0) - 198*P1x*P3x*P1y*Q3y*pow(sp,2.0) - + 90*P1x*P0y*P2y*Q3x*pow(sp,4.0) - 90*P1x*P0y*P3y*Q3x*pow(sp,3.0) - 90*P1x*P1y*P2y*Q2x*pow(sp,4.0) - 252*P1x*P1y*P2y*Q3x*pow(sp,3.0) - 90*P1x*P1y*P3y*Q2x*pow(sp,3.0) - 198*P1x*P1y*P3y*Q3x*pow(sp,2.0) - + 240*P2x*P0y*P1y*Q3x*pow(sp,4.0) - 240*P3x*P0y*P1y*Q3x*pow(sp,3.0) + 90*P1x*P2x*P1y*Q3y*pow(sp,4.0) + 90*P1x*P3x*P1y*Q3y*pow(sp,3.0) + 90*P1x*P1y*P2y*Q3x*pow(sp,4.0) + 90*P1x*P1y*P3y*Q3x*pow(sp,3.0) + + 378*P0y*P1y*P2y*Q2y*pow(sp,2.0) - 840*P0y*P1y*P2y*Q2y*pow(sp,3.0) - 378*P0y*P1y*P2y*Q3y*pow(sp,2.0) - 588*P0y*P1y*P3y*Q2y*pow(sp,2.0) + 420*P0y*P1y*P2y*Q2y*pow(sp,4.0) + 840*P0y*P1y*P2y*Q3y*pow(sp,3.0) + + 420*P0y*P1y*P3y*Q2y*pow(sp,3.0) + 588*P0y*P1y*P3y*Q3y*pow(sp,2.0) - 420*P0y*P1y*P2y*Q3y*pow(sp,4.0) - 420*P0y*P1y*P3y*Q3y*pow(sp,3.0) - 252*P0x*P1x*Q1x*Q2x*pow(sp,2.0) + 756*P0x*P1x*Q1x*Q2x*pow(sp,3.0) + + 252*P0x*P1x*Q1x*Q3x*pow(sp,2.0) - 420*P0x*P1x*Q1x*Q2x*pow(sp,4.0) - 756*P0x*P1x*Q1x*Q3x*pow(sp,3.0) + 420*P0x*P1x*Q1x*Q3x*pow(sp,4.0) - 196*P0x*P1x*Q2x*Q3x*pow(sp,3.0) + 126*P0x*P1x*Q2x*Q3x*pow(sp,4.0) + + 162*P0x*P0y*Q1x*Q2y*pow(sp,2.0) + 162*P0x*P0y*Q2x*Q1y*pow(sp,2.0) + 72*P0x*P1x*Q1y*Q2y*pow(sp,2.0) - 234*P0x*P0y*Q1x*Q2y*pow(sp,3.0) - 162*P0x*P0y*Q1x*Q3y*pow(sp,2.0) - 234*P0x*P0y*Q2x*Q1y*pow(sp,3.0) + + 54*P0x*P0y*Q2x*Q2y*pow(sp,2.0) - 162*P0x*P0y*Q3x*Q1y*pow(sp,2.0) - 162*P0x*P1y*Q1x*Q2y*pow(sp,2.0) - 162*P0x*P1y*Q2x*Q1y*pow(sp,2.0) - 162*P1x*P0y*Q1x*Q2y*pow(sp,2.0) - 162*P1x*P0y*Q2x*Q1y*pow(sp,2.0) + + 72*P0y*P1y*Q1x*Q2x*pow(sp,2.0) + 288*P0x*P1x*Q1y*Q2y*pow(sp,3.0) - 72*P0x*P1x*Q1y*Q3y*pow(sp,2.0) + 90*P0x*P0y*Q1x*Q2y*pow(sp,4.0) + 234*P0x*P0y*Q1x*Q3y*pow(sp,3.0) + 90*P0x*P0y*Q2x*Q1y*pow(sp,4.0) - + 162*P0x*P0y*Q2x*Q2y*pow(sp,3.0) - 36*P0x*P0y*Q2x*Q3y*pow(sp,2.0) + 234*P0x*P0y*Q3x*Q1y*pow(sp,3.0) - 36*P0x*P0y*Q3x*Q2y*pow(sp,2.0) + 234*P0x*P1y*Q1x*Q2y*pow(sp,3.0) + 162*P0x*P1y*Q1x*Q3y*pow(sp,2.0) + + 234*P0x*P1y*Q2x*Q1y*pow(sp,3.0) - 54*P0x*P1y*Q2x*Q2y*pow(sp,2.0) + 162*P0x*P1y*Q3x*Q1y*pow(sp,2.0) + 234*P1x*P0y*Q1x*Q2y*pow(sp,3.0) + 162*P1x*P0y*Q1x*Q3y*pow(sp,2.0) + 234*P1x*P0y*Q2x*Q1y*pow(sp,3.0) - + 54*P1x*P0y*Q2x*Q2y*pow(sp,2.0) + 162*P1x*P0y*Q3x*Q1y*pow(sp,2.0) + 162*P1x*P1y*Q1x*Q2y*pow(sp,2.0) + 162*P1x*P1y*Q2x*Q1y*pow(sp,2.0) + 288*P0y*P1y*Q1x*Q2x*pow(sp,3.0) - 72*P0y*P1y*Q1x*Q3x*pow(sp,2.0) - + 240*P0x*P1x*Q1y*Q2y*pow(sp,4.0) - 288*P0x*P1x*Q1y*Q3y*pow(sp,3.0) - 72*P0x*P1x*Q2y*Q3y*pow(sp,2.0) - 90*P0x*P0y*Q1x*Q3y*pow(sp,4.0) + 72*P0x*P0y*Q2x*Q2y*pow(sp,4.0) + 66*P0x*P0y*Q2x*Q3y*pow(sp,3.0) - + 90*P0x*P0y*Q3x*Q1y*pow(sp,4.0) + 66*P0x*P0y*Q3x*Q2y*pow(sp,3.0) + 18*P0x*P0y*Q3x*Q3y*pow(sp,2.0) - 90*P0x*P1y*Q1x*Q2y*pow(sp,4.0) - 234*P0x*P1y*Q1x*Q3y*pow(sp,3.0) - 90*P0x*P1y*Q2x*Q1y*pow(sp,4.0) + + 162*P0x*P1y*Q2x*Q2y*pow(sp,3.0) + 36*P0x*P1y*Q2x*Q3y*pow(sp,2.0) - 234*P0x*P1y*Q3x*Q1y*pow(sp,3.0) + 36*P0x*P1y*Q3x*Q2y*pow(sp,2.0) - 90*P1x*P0y*Q1x*Q2y*pow(sp,4.0) - 234*P1x*P0y*Q1x*Q3y*pow(sp,3.0) - + 90*P1x*P0y*Q2x*Q1y*pow(sp,4.0) + 162*P1x*P0y*Q2x*Q2y*pow(sp,3.0) + 36*P1x*P0y*Q2x*Q3y*pow(sp,2.0) - 234*P1x*P0y*Q3x*Q1y*pow(sp,3.0) + 36*P1x*P0y*Q3x*Q2y*pow(sp,2.0) - 234*P1x*P1y*Q1x*Q2y*pow(sp,3.0) - + 162*P1x*P1y*Q1x*Q3y*pow(sp,2.0) - 234*P1x*P1y*Q2x*Q1y*pow(sp,3.0) + 54*P1x*P1y*Q2x*Q2y*pow(sp,2.0) - 162*P1x*P1y*Q3x*Q1y*pow(sp,2.0) - 240*P0y*P1y*Q1x*Q2x*pow(sp,4.0) - 288*P0y*P1y*Q1x*Q3x*pow(sp,3.0) - + 72*P0y*P1y*Q2x*Q3x*pow(sp,2.0) + 240*P0x*P1x*Q1y*Q3y*pow(sp,4.0) - 64*P0x*P1x*Q2y*Q3y*pow(sp,3.0) - 27*P0x*P0y*Q2x*Q3y*pow(sp,4.0) - 27*P0x*P0y*Q3x*Q2y*pow(sp,4.0) + 30*P0x*P0y*Q3x*Q3y*pow(sp,3.0) + + 90*P0x*P1y*Q1x*Q3y*pow(sp,4.0) - 72*P0x*P1y*Q2x*Q2y*pow(sp,4.0) - 66*P0x*P1y*Q2x*Q3y*pow(sp,3.0) + 90*P0x*P1y*Q3x*Q1y*pow(sp,4.0) - 66*P0x*P1y*Q3x*Q2y*pow(sp,3.0) - 18*P0x*P1y*Q3x*Q3y*pow(sp,2.0) + + 90*P1x*P0y*Q1x*Q3y*pow(sp,4.0) - 72*P1x*P0y*Q2x*Q2y*pow(sp,4.0) - 66*P1x*P0y*Q2x*Q3y*pow(sp,3.0) + 90*P1x*P0y*Q3x*Q1y*pow(sp,4.0) - 66*P1x*P0y*Q3x*Q2y*pow(sp,3.0) - 18*P1x*P0y*Q3x*Q3y*pow(sp,2.0) + + 90*P1x*P1y*Q1x*Q2y*pow(sp,4.0) + 234*P1x*P1y*Q1x*Q3y*pow(sp,3.0) + 90*P1x*P1y*Q2x*Q1y*pow(sp,4.0) - 162*P1x*P1y*Q2x*Q2y*pow(sp,3.0) - 36*P1x*P1y*Q2x*Q3y*pow(sp,2.0) + 234*P1x*P1y*Q3x*Q1y*pow(sp,3.0) - + 36*P1x*P1y*Q3x*Q2y*pow(sp,2.0) + 240*P0y*P1y*Q1x*Q3x*pow(sp,4.0) - 64*P0y*P1y*Q2x*Q3x*pow(sp,3.0) + 72*P0x*P1x*Q2y*Q3y*pow(sp,4.0) - 18*P0x*P0y*Q3x*Q3y*pow(sp,4.0) + 27*P0x*P1y*Q2x*Q3y*pow(sp,4.0) + + 27*P0x*P1y*Q3x*Q2y*pow(sp,4.0) - 30*P0x*P1y*Q3x*Q3y*pow(sp,3.0) + 27*P1x*P0y*Q2x*Q3y*pow(sp,4.0) + 27*P1x*P0y*Q3x*Q2y*pow(sp,4.0) - 30*P1x*P0y*Q3x*Q3y*pow(sp,3.0) - 90*P1x*P1y*Q1x*Q3y*pow(sp,4.0) + + 72*P1x*P1y*Q2x*Q2y*pow(sp,4.0) + 66*P1x*P1y*Q2x*Q3y*pow(sp,3.0) - 90*P1x*P1y*Q3x*Q1y*pow(sp,4.0) + 66*P1x*P1y*Q3x*Q2y*pow(sp,3.0) + 18*P1x*P1y*Q3x*Q3y*pow(sp,2.0) + 72*P0y*P1y*Q2x*Q3x*pow(sp,4.0) + + 18*P0x*P1y*Q3x*Q3y*pow(sp,4.0) + 18*P1x*P0y*Q3x*Q3y*pow(sp,4.0) - 27*P1x*P1y*Q2x*Q3y*pow(sp,4.0) - 27*P1x*P1y*Q3x*Q2y*pow(sp,4.0) + 30*P1x*P1y*Q3x*Q3y*pow(sp,3.0) - 18*P1x*P1y*Q3x*Q3y*pow(sp,4.0) - + 252*P0y*P1y*Q1y*Q2y*pow(sp,2.0) + 756*P0y*P1y*Q1y*Q2y*pow(sp,3.0) + 252*P0y*P1y*Q1y*Q3y*pow(sp,2.0) - 420*P0y*P1y*Q1y*Q2y*pow(sp,4.0) - 756*P0y*P1y*Q1y*Q3y*pow(sp,3.0) + 420*P0y*P1y*Q1y*Q3y*pow(sp,4.0) - + 196*P0y*P1y*Q2y*Q3y*pow(sp,3.0) + 126*P0y*P1y*Q2y*Q3y*pow(sp,4.0))/(3*(7*pow(P0x,2.0)*pow(Q2x,2.0) + 7*pow(P0x,2.0)*pow(Q3x,2.0) + 7*pow(P1x,2.0)*pow(Q2x,2.0) + 7*pow(P1x,2.0)*pow(Q3x,2.0) + + 16*pow(P0x,2.0)*pow(Q2y,2.0) + 16*pow(P0y,2.0)*pow(Q2x,2.0) + 16*pow(P0x,2.0)*pow(Q3y,2.0) + 16*pow(P1x,2.0)*pow(Q2y,2.0) + 16*pow(P0y,2.0)*pow(Q3x,2.0) + 16*pow(P1y,2.0)*pow(Q2x,2.0) + 16*pow(P1x,2.0)*pow(Q3y,2.0) + + 16*pow(P1y,2.0)*pow(Q3x,2.0) + 7*pow(P0y,2.0)*pow(Q2y,2.0) + 7*pow(P0y,2.0)*pow(Q3y,2.0) + 7*pow(P1y,2.0)*pow(Q2y,2.0) + 7*pow(P1y,2.0)*pow(Q3y,2.0) - 14*P0x*P1x*pow(Q2x,2.0) - 14*P0x*P1x*pow(Q3x,2.0) - + 32*P0x*P1x*pow(Q2y,2.0) - 32*P0x*P1x*pow(Q3y,2.0) - 32*P0y*P1y*pow(Q2x,2.0) - 32*P0y*P1y*pow(Q3x,2.0) - 14*P0y*P1y*pow(Q2y,2.0) - 14*P0y*P1y*pow(Q3y,2.0) - 14*pow(P0x,2.0)*Q2x*Q3x - 14*pow(P1x,2.0)*Q2x*Q3x - + 32*pow(P0y,2.0)*Q2x*Q3x - 32*pow(P1y,2.0)*Q2x*Q3x - 32*pow(P0x,2.0)*Q2y*Q3y - 32*pow(P1x,2.0)*Q2y*Q3y - 14*pow(P0y,2.0)*Q2y*Q3y - 14*pow(P1y,2.0)*Q2y*Q3y + 28*P0x*P1x*Q2x*Q3x - 18*P0x*P0y*Q2x*Q2y + 18*P0x*P0y*Q2x*Q3y + + 18*P0x*P0y*Q3x*Q2y + 18*P0x*P1y*Q2x*Q2y + 18*P1x*P0y*Q2x*Q2y + 64*P0x*P1x*Q2y*Q3y - 18*P0x*P0y*Q3x*Q3y - 18*P0x*P1y*Q2x*Q3y - 18*P0x*P1y*Q3x*Q2y - 18*P1x*P0y*Q2x*Q3y - 18*P1x*P0y*Q3x*Q2y - 18*P1x*P1y*Q2x*Q2y + + 64*P0y*P1y*Q2x*Q3x + 18*P0x*P1y*Q3x*Q3y + 18*P1x*P0y*Q3x*Q3y + 18*P1x*P1y*Q2x*Q3y + 18*P1x*P1y*Q3x*Q2y - 18*P1x*P1y*Q3x*Q3y + 28*P0y*P1y*Q2y*Q3y)); + + // Just an arbitrary correction heuristic for negative factors ... + if (out_pfactor < 0) + out_pfactor = -0.1 * out_pfactor; + if (out_qfactor < 0) + out_qfactor = -0.1 * out_qfactor; +} + +float PathObject::calcBezierPointDeletionRetainingShapeCost(MapCoord p0, MapCoordF p1, MapCoordF p2, MapCoord p3, PathObject* reference) +{ + const int num_test_points = 20; + QBezier curve = QBezier::fromPoints(QPointF(p0), QPointF(p1), QPointF(p2), QPointF(p3)); + + float cost = 0; + for (int i = 0; i < num_test_points; ++i) + { + auto point = MapCoordF { curve.pointAt((i + 1) / (float)(num_test_points + 1)) }; + float distance_sq; + PathCoord path_coord; + reference->calcClosestPointOnPath(MapCoordF(point), distance_sq, path_coord); + cost += distance_sq; + } + // Just some random scaling to pretend that we have 50 sample points + return cost * (50 / 20.0f); +} + +void PathObject::calcBezierPointDeletionRetainingShapeOptimization(MapCoord p0, MapCoord p1, MapCoord p2, MapCoord q0, MapCoord q1, MapCoord q2, MapCoord q3, double& out_pfactor, double& out_qfactor) +{ + const float gradient_abort_threshold = 0.05f; // if the gradient magnitude is lower than this over num_abort_steps step, the optimization is aborted + const float decrease_abort_threshold = 0.004f; // if the cost descrease if lower than this over num_abort_steps step, the optimization is aborted + const int num_abort_steps = 2; + const double derivative_delta = 0.05; + const int num_tested_step_sizes = 5; + const int max_num_line_search_iterations = 5; + float step_size_stepping_base = 0.001f; + + static LineSymbol line_symbol; + PathObject old_curve(&line_symbol); + p0.setCurveStart(true); + old_curve.addCoordinate(p0); + old_curve.addCoordinate(p1); + old_curve.addCoordinate(p2); + q0.setCurveStart(true); + old_curve.addCoordinate(q0); + old_curve.addCoordinate(q1); + old_curve.addCoordinate(q2); + q3.setCurveStart(false); + q3.setClosePoint(false); + q3.setHolePoint(false); + old_curve.addCoordinate(q3); + old_curve.update(); + + float cur_cost = 0; + int num_no_improvement_iterations = 0; + float old_gradient[2]; + float old_step_dir[2]; + for (int i = 0; i < 30; ++i) + { + MapCoordF p_direction = MapCoordF(p1) - MapCoordF(p0); + MapCoordF r1 = MapCoordF(p0) + out_pfactor * p_direction; + MapCoordF q_direction = MapCoordF(q2) - MapCoordF(q3); + MapCoordF r2 = MapCoordF(q3) + out_qfactor * q_direction; + + // Calculate gradient and cost (if first iteration) + if (i == 0) + cur_cost = calcBezierPointDeletionRetainingShapeCost(p0, r1, r2, q3, &old_curve); + //if (i == 0) + // qDebug() << "\nStart cost: " << cur_cost; + + float gradient[2]; + gradient[0] = (calcBezierPointDeletionRetainingShapeCost(p0, r1 + derivative_delta * p_direction, r2, q3, &old_curve) - + calcBezierPointDeletionRetainingShapeCost(p0, r1 - derivative_delta * p_direction, r2, q3, &old_curve)) / (2 * derivative_delta); + gradient[1] = (calcBezierPointDeletionRetainingShapeCost(p0, r1, r2 + derivative_delta * q_direction, q3, &old_curve) - + calcBezierPointDeletionRetainingShapeCost(p0, r1, r2 - derivative_delta * q_direction, q3, &old_curve)) / (2 * derivative_delta); + + // Calculate step direction + float step_dir_p; + float step_dir_q; + float conjugate_gradient_factor = 0; + if (i == 0) + { + // Steepest descent + step_dir_p = -gradient[0]; + step_dir_q = -gradient[1]; + } + else + { + // Conjugate gradient + // Fletcher - Reeves: + //conjugate_gradient_factor = (pow(gradient[0], 2.0) + pow(gradient[1], 2.0)) / (pow(old_gradient[0], 2.0) + pow(old_gradient[1], 2.0)); + // Polak – Ribiere: + conjugate_gradient_factor = qMax(0.0, ( gradient[0] * (gradient[0] - old_gradient[0]) + gradient[1] * (gradient[1] - old_gradient[1]) ) / + ( pow(old_gradient[0], 2.0) + pow(old_gradient[1], 2.0) )); + //qDebug() << "Factor: " << conjugate_gradient_factor; + step_dir_p = -gradient[0] + conjugate_gradient_factor * old_step_dir[0]; + step_dir_q = -gradient[1] + conjugate_gradient_factor * old_step_dir[1]; + } + + // Line search in step direction for lowest cost + float best_step_size = 0; + float best_step_size_cost = cur_cost; + int best_step_factor = 0; + float adjusted_step_size_stepping_base = step_size_stepping_base; + + for (int iteration = 0; iteration < max_num_line_search_iterations; ++iteration) + { + const float step_size_stepping = adjusted_step_size_stepping_base * (out_pfactor + out_qfactor) / 2; // * qSqrt(step_dir_p*step_dir_p + step_dir_q*step_dir_q); // qSqrt(cur_cost); + for (int step_test = 1; step_test <= num_tested_step_sizes; ++step_test) + { + float step_size = step_test * step_size_stepping; + float test_p_step = step_size * step_dir_p; + float test_q_step = step_size * step_dir_q; + + MapCoordF test_r1 = r1 + test_p_step * p_direction; + MapCoordF test_r2 = r2 + test_q_step * q_direction; + float test_cost = calcBezierPointDeletionRetainingShapeCost(p0, test_r1, test_r2, q3, &old_curve); + if (test_cost < best_step_size_cost) + { + best_step_size_cost = test_cost; + best_step_size = step_size; + best_step_factor = step_test; + } + } + //qDebug() << best_step_factor; + if (best_step_factor == num_tested_step_sizes) + adjusted_step_size_stepping_base *= num_tested_step_sizes; + else if (best_step_factor == 0) + adjusted_step_size_stepping_base *= (1 / (float)num_tested_step_sizes); + else + break; + if (iteration < 3) + step_size_stepping_base = adjusted_step_size_stepping_base; + } + if (best_step_factor == 0 && conjugate_gradient_factor == 0) + return; + + // Update optimized parameters and constrain them to non-negative values + out_pfactor += best_step_size * step_dir_p; + if (out_pfactor < 0) + out_pfactor = 0; + out_qfactor += best_step_size * step_dir_q; + if (out_qfactor < 0) + out_qfactor = 0; + + // Abort if gradient is really low for a number of steps + float gradient_magnitude = qSqrt(gradient[0]*gradient[0] + gradient[1]*gradient[1]); + //qDebug() << "Gradient magnitude: " << gradient_magnitude; + if (gradient_magnitude < gradient_abort_threshold || cur_cost - best_step_size_cost < decrease_abort_threshold) + { + ++num_no_improvement_iterations; + if (num_no_improvement_iterations == num_abort_steps) + break; + } + else + num_no_improvement_iterations = 0; + + cur_cost = best_step_size_cost; + old_gradient[0] = gradient[0]; + old_gradient[1] = gradient[1]; + old_step_dir[0] = step_dir_p; + old_step_dir[1] = step_dir_q; + //qDebug() << "Cost: " << cur_cost; + } +} + +void PathObject::appendPath(const PathObject* other) +{ + coords.reserve(coords.size() + other->coords.size()); + coords.insert(coords.end(), other->coords.begin(), other->coords.end()); + + recalculateParts(); + setOutputDirty(); +} + +void PathObject::appendPathPart(const PathPart &part) +{ + coords.reserve(coords.size() + part.size()); + for (std::size_t i = 0; i < part.size(); ++i) + coords.emplace_back(part.path->coords[part.first_index + i]); + + recalculateParts(); + setOutputDirty(); +} + +void PathObject::reverse() +{ + for (auto& part : path_parts) + part.reverse(); + + Q_ASSERT(isOutputDirty()); +} + +void PathObject::closeAllParts() +{ + for (auto& part : path_parts) + part.setClosed(true, true); +} + +bool PathObject::convertToCurves(PathObject** undo_duplicate) +{ + bool converted_a_range = false; + for (const auto& part : path_parts) + { + for (auto index = part.first_index; index < part.last_index; index += 3) + { + if (!coords[index].isCurveStart()) + { + auto end_index = index + 1; + while (end_index < part.last_index && !coords[end_index].isCurveStart()) + ++end_index; + + if (!converted_a_range) + { + if (undo_duplicate) + *undo_duplicate = duplicate()->asPath(); + converted_a_range = true; + } + + index = convertRangeToCurves(part, index, end_index); + } + } + } + + Q_ASSERT(!converted_a_range || isOutputDirty()); + return converted_a_range; +} + +int PathObject::convertRangeToCurves(const PathPart& part, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index) +{ + Q_ASSERT(end_index > start_index); + + Q_ASSERT(!coords[start_index].isCurveStart()); + coords[start_index].setCurveStart(true); + + // Special case: last coordinate + MapCoord direction; + if (end_index != part.last_index) + { + // Use direction of next segment + direction = coords[end_index + 1] - coords[end_index]; + } + else if (part.isClosed()) + { + // Use average direction at close point + direction = coords[part.first_index + 1] - coords[end_index - 1]; + } + else + { + // Use direction of last segment + direction = coords[end_index] - coords[end_index - 1]; + } + + MapCoordF tangent = MapCoordF(direction); + tangent.normalize(); + + MapCoord end_handle = coords[end_index]; + end_handle.setFlags(0); + auto baseline = (coords[end_index] - coords[end_index - 1]).length() * BEZIER_HANDLE_DISTANCE; + end_handle = end_handle - MapCoord(tangent * baseline); + + // Special case: first coordinate + if (start_index != part.first_index) + { + // Use direction of previous segment + direction = coords[start_index] - coords[start_index - 1]; + } + else if (part.isClosed()) + { + // Use average direction at close point + direction = coords[start_index + 1] - coords[part.last_index - 1]; + } + else + { + // Use direction of first segment + direction = coords[start_index + 1] - coords[start_index]; + } + + tangent = MapCoordF(direction); + tangent.normalize(); + + MapCoord handle = coords[start_index]; + handle.setFlags(0); + baseline = (coords[start_index + 1] - coords[start_index]).length() * BEZIER_HANDLE_DISTANCE; + handle = handle + MapCoord(tangent * baseline); + addCoordinate(start_index + 1, handle); + ++end_index; + + // In-between coordinates + for (MapCoordVector::size_type c = start_index + 2; c < end_index; ++c) + { + direction = coords[c + 1] - coords[c - 2]; + tangent = MapCoordF(direction); + tangent.normalize(); + + // Add previous handle + handle = coords[c]; + handle.setFlags(0); + baseline = (coords[c] - coords[c - 2]).length() * BEZIER_HANDLE_DISTANCE; + handle = handle - MapCoord(tangent * baseline); + + addCoordinate(c, handle); + ++c; + ++end_index; + + Q_ASSERT(!coords[c].isCurveStart()); + // Set curve start flag on point + coords[c].setCurveStart(true); + + // Add next handle + handle = coords[c]; + handle.setFlags(0); + baseline = (coords[c + 1] - coords[c]).length() * BEZIER_HANDLE_DISTANCE; + handle = handle + MapCoord(tangent * baseline); + + addCoordinate(c + 1, handle); + ++c; + ++end_index; + } + + // Add last handle + addCoordinate(end_index, end_handle); + ++end_index; + + return end_index; +} + +bool PathObject::simplify(PathObject** undo_duplicate, double threshold) +{ + // A copy for reference and undo while this is modified. + QScopedPointer original(new PathObject(*this)); + + // original_indices provides a mapping from this object's indices to the + // equivalent original object indices in order to extract the relevant + // parts of the original object later for cost calculation. + // Note: curve handle indices may become incorrect, we don't need them. + std::vector original_indices; + original_indices.resize(coords.size()); + for (std::size_t i = 0, end = coords.size(); i < end; ++i) + original_indices[i] = i; + + // A high value indicating an cost of deletion which is unknown. + auto const undetermined_cost = std::numeric_limits::max(); + + // This vector provides the costs of deleting individual nodes. + std::vector costs; + costs.resize(coords.size(), undetermined_cost); + + // The empty LineSymbol will not generate any renderables, + // thus reducing the cost of update(). + // The temp object will be reused (but not reallocated) many times. + LineSymbol empty_symbol; + PathObject temp { &empty_symbol }; + temp.coords.reserve(10); // enough for two bezier edges. + + for (auto part = path_parts.rbegin(); part != path_parts.rend(); ++part) + { + // Don't simplify parts which would get deleted. + MapCoordVector::size_type minimum_part_size = part->isClosed() ? 4 : 3; + auto minimumPartSizeReached = [&part, minimum_part_size]() -> bool + { + return (part->size() <= 7 && part->countRegularNodes() < minimum_part_size); + }; + + auto minimum_cost_step = threshold / qMax(std::log2(part->size() / 64.0), 4.0); + auto minimum_cost = 0.0; + + // Delete nodes in max n runs (where n = max. part.end_index - part.start_index) + for (auto run = part->last_index; run > part->first_index; --run) + { + if (minimumPartSizeReached()) + break; + + // Determine the costs of node deletion + { + auto extract_start = part->first_index; + auto index = part->nextCoordIndex(extract_start); + while (index < part->last_index) + { + auto extract_end = part->nextCoordIndex(index); + if (costs[index] == undetermined_cost) + { + temp.assignCoordinates(*this, extract_start, extract_end); + temp.deleteCoordinate(index - extract_start, true, Settings::DeleteBezierPoint_RetainExistingShape); + // Debug check: start and end coords of the extracts should be at the same position + Q_ASSERT(coords[extract_start].isPositionEqualTo(temp.coords.front())); + Q_ASSERT(coords[extract_end].isPositionEqualTo(temp.coords.back())); + costs[index] = original->calcMaximumDistanceTo(original_indices[extract_start], + original_indices[extract_end], + &temp, + 0, + temp.coords.size()-1); + } + extract_start = index; + index = extract_end; + } + + if (costs[part->first_index] == undetermined_cost && extract_start < part->last_index) + { + if (part->isClosed()) + { + auto extract_end = part->nextCoordIndex(part->first_index); + temp.assignCoordinates(*this, extract_start, extract_end); + temp.deleteCoordinate(part->last_index - extract_start, true, Settings::DeleteBezierPoint_RetainExistingShape); + // Debug check: start and end coords of the extracts should be at the same position + Q_ASSERT(coords[extract_start].isPositionEqualTo(temp.coords.front())); + Q_ASSERT(coords[extract_end].isPositionEqualTo(temp.coords.back())); + costs[part->first_index] = original->calcMaximumDistanceTo(original_indices[extract_start], + original_indices[extract_end], + &temp, + 0, + temp.coords.size()-1); + } + else + { + costs[part->first_index] = threshold * 2; + } + } + + costs[part->last_index] = undetermined_cost; + } + + // Find an upper bound for the acceptable cost in the current run + auto cost_bound = threshold * 2; + for (auto index = part->first_index; index < part->last_index; ++index) + { + if (costs[index] <= cost_bound) + { + cost_bound = costs[index]; + if (cost_bound <= minimum_cost) + { + // short-cut on minimal costs + cost_bound = minimum_cost; + break; + } + } + } + + if (cost_bound > threshold) + break; + + while (minimum_cost <= cost_bound) + minimum_cost += minimum_cost_step; + if (minimum_cost > threshold) + minimum_cost = threshold; + + // Start at the end, in order to shorten the vector quickly + auto index = part->last_index; + do + { + if (costs[index] <= cost_bound) + { + auto extract_start = part->prevCoordIndex(index); + auto extract_end = part->nextCoordIndex(index); + Q_ASSERT(original->coords[original_indices[extract_start]].isPositionEqualTo(coords[extract_start])); + Q_ASSERT(original->coords[original_indices[extract_end]].isPositionEqualTo(coords[extract_end])); + deleteCoordinate(index, true, Settings::DeleteBezierPoint_RetainExistingShape); + + if (index > part->first_index) + { + // Deleted inner point + auto new_extract_end = part->nextCoordIndex(extract_start); + auto original_start = begin(original_indices) + extract_start; + original_indices.erase(original_start + 1, + original_start + 1 + (extract_end - new_extract_end)); + Q_ASSERT(original_indices.size() == coords.size()); + Q_ASSERT(original->coords[original_indices[extract_start]].isPositionEqualTo(coords[extract_start])); + Q_ASSERT(original->coords[original_indices[new_extract_end]].isPositionEqualTo(coords[new_extract_end])); + + costs.erase(begin(costs) + extract_start + 1, + begin(costs) + extract_start + 1 + (extract_end - new_extract_end)); + Q_ASSERT(costs.size() == coords.size()); + + costs[extract_start] = undetermined_cost; + + if (new_extract_end == part->last_index) + { + costs[part->first_index] = undetermined_cost; + } + else + { + costs[new_extract_end] = undetermined_cost; + } + Q_ASSERT(costs[part->last_index] > threshold); + + index = part->prevCoordIndex(extract_start); + + if (minimumPartSizeReached()) + break; + } + else if (part->isClosed()) + { + Q_ASSERT(index == part->first_index); + + // Deleted start point of closed path + // This must match PathObject::deleteCoordinate + extract_start = part->prevCoordIndex(part->last_index); + auto original_begin = begin(original_indices); + original_indices.erase(original_begin + part->first_index, + original_begin + extract_end); + original_indices.resize(coords.size()); + original_indices[part->last_index] = original_indices[part->first_index]; + Q_ASSERT(original->coords[original_indices[extract_start]].isPositionEqualTo(coords[extract_start])); + Q_ASSERT(original->coords[original_indices[part->last_index]].isPositionEqualTo(coords[part->last_index])); + Q_ASSERT(original->coords[original_indices[part->first_index]].isPositionEqualTo(coords[part->first_index])); + + costs.erase(begin(costs) + part->first_index, + begin(costs) + extract_end); + costs.resize(coords.size(), undetermined_cost); + costs[extract_start] = undetermined_cost; + costs[part->first_index] = undetermined_cost; + Q_ASSERT(costs[part->last_index] > threshold); + } + else + { + Q_ASSERT(index == part->first_index); + } + } + else if (index != part->first_index) + { + --index; + } + } + while (index != part->first_index); + } + } + + bool removed_a_point = (coords.size() != original->coords.size()); + if (removed_a_point && undo_duplicate) + { + *undo_duplicate = original.take(); + } + + return removed_a_point; +} + +int PathObject::isPointOnPath(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection) const +{ + float side_tolerance = tolerance; + if (extended_selection && map && (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Combined)) + { + // TODO: precalculate largest line extent for all symbols to move it out of this time critical method? + side_tolerance = qMax(side_tolerance, float(symbol->calculateLargestLineExtent())); + } + + Symbol::Type contained_types = symbol->getContainedTypes(); + if ((contained_types & Symbol::Line || treat_areas_as_paths) && tolerance > 0) + { + update(); + for (const auto& part : path_parts) + { + const auto& path_coords = part.path_coords; + auto size = path_coords.size(); + for (PathCoordVector::size_type i = 0; i < size - 1; ++i) + { + Q_ASSERT(path_coords[i].index < coords.size()); + if (coords[path_coords[i].index].isHolePoint()) + continue; + + MapCoordF to_coord = coord - path_coords[i].pos; + MapCoordF to_next = path_coords[i+1].pos - path_coords[i].pos; + MapCoordF tangent = to_next; + tangent.normalize(); + + float dist_along_line = MapCoordF::dotProduct(to_coord, tangent); + if (dist_along_line < -tolerance) + continue; + else if (dist_along_line < 0 && to_coord.lengthSquared() <= tolerance*tolerance) + return Symbol::Line; + + float line_length = path_coords[i+1].clen - path_coords[i].clen; + if (line_length < 1e-7) + continue; + if (dist_along_line > line_length + tolerance) + continue; + else if (dist_along_line > line_length && coord.distanceSquaredTo(path_coords[i+1].pos) <= tolerance*tolerance) + return Symbol::Line; + + auto right = tangent.perpRight(); + + float dist_from_line = qAbs(MapCoordF::dotProduct(right, to_coord)); + if (dist_from_line <= side_tolerance) + return Symbol::Line; + } + } + } + + // Check for area selection + if ((contained_types & Symbol::Area) && !treat_areas_as_paths) + { + if (isPointInsideArea(coord)) + return Symbol::Area; + } + + return Symbol::NoSymbol; +} + +bool PathObject::isPointInsideArea(MapCoordF coord) const +{ + update(); + bool inside = false; + for (const auto& part : path_parts) + { + if (part.isPointInside(coord)) + inside = !inside; + } + return inside; +} + +double PathObject::calcMaximumDistanceTo( + MapCoordVector::size_type start_index, + MapCoordVector::size_type end_index, + const PathObject* other, + MapCoordVector::size_type other_start_index, + MapCoordVector::size_type other_end_index) const +{ + update(); + + Q_ASSERT(other_start_index == 0); + Q_ASSERT(other_end_index == other->coords.size()-1); + + if (end_index < start_index) + { + const auto part = findPartForIndex(start_index); + Q_ASSERT(part->isClosed()); + Q_ASSERT(end_index >= part->first_index); + Q_ASSERT(end_index <= part->last_index); + auto d1 = calcMaximumDistanceTo(start_index, part->last_index, other, other_start_index, other_end_index); + auto d2 = calcMaximumDistanceTo(part->first_index, end_index, other, other_start_index, other_end_index); + return qMax(d1, d2); + } + + const float test_points_per_mm = 2; + + float max_distance_sq = 0.0; + for (const auto& part : path_parts) + { + if (part.first_index <= end_index && part.last_index >= start_index ) + { + const auto& path_coords = part.path_coords; + + auto pc_start = std::lower_bound(begin(path_coords), end(path_coords), start_index, PathCoord::indexLessThanValue); + auto pc_end = std::lower_bound(pc_start, end(path_coords), end_index, PathCoord::indexLessThanValue); + if (pc_end == end(path_coords)) + { + if (pc_end != pc_start) + --pc_end; + } + + PathCoord path_coord; + float distance_sq = 0.0; + other->calcClosestPointOnPath(pc_start->pos, distance_sq, path_coord, other_start_index, other_end_index); + max_distance_sq = qMax(max_distance_sq, distance_sq); + + for (auto pc = pc_start; pc != pc_end; ++pc) + { + auto next_pc = pc + 1; + double len = next_pc->clen - pc->clen; + MapCoordF direction = next_pc->pos - pc->pos; + + int num_test_points = qMax(1, qRound(len * test_points_per_mm)); + for (int p = 1; p <= num_test_points; ++p) + { + MapCoordF point = pc->pos + direction * ((float)p / num_test_points); + other->calcClosestPointOnPath(point, distance_sq, path_coord, other_start_index, other_end_index); + max_distance_sq = qMax(max_distance_sq, distance_sq); + } + } + } + } + + return sqrt(max_distance_sq); +} + +PathObject::Intersection PathObject::Intersection::makeIntersectionAt(double a, double b, const PathCoord& a0, const PathCoord& a1, const PathCoord& b0, const PathCoord& b1, PathPartVector::size_type part_index, PathPartVector::size_type other_part_index) +{ + PathObject::Intersection new_intersection; + new_intersection.coord = MapCoordF(a0.pos.x() + a * (a1.pos.x() - a0.pos.x()), a0.pos.y() + a * (a1.pos.y() - a0.pos.y())); + new_intersection.part_index = part_index; + new_intersection.length = a0.clen + a * (a1.clen - a0.clen); + new_intersection.other_part_index = other_part_index; + new_intersection.other_length = b0.clen + b * (b1.clen - b0.clen); + return new_intersection; +} + +void PathObject::Intersections::normalize() +{ + std::sort(begin(), end()); + erase(std::unique(begin(), end()), end()); +} + +void PathObject::calcAllIntersectionsWith(const PathObject* other, PathObject::Intersections& out) const +{ + update(); + other->update(); + + const double epsilon = 1e-10; + const double zero_minus_epsilon = 0 - epsilon; + const double one_plus_epsilon = 1 + epsilon; + + for (size_t part_index = 0; part_index < path_parts.size(); ++part_index) + { + const PathPart& part = path_parts[part_index]; + auto path_coord_end_index = part.path_coords.size() - 1; + for (auto i = PathCoordVector::size_type { 1 }; i <= path_coord_end_index; ++i) + { + // Get information about this path coord + bool has_segment_before = (i > 1) || part.isClosed(); + MapCoordF ingoing_direction; + if (has_segment_before && i == 1) + { + Q_ASSERT(path_coord_end_index >= 1); + ingoing_direction = part.path_coords[path_coord_end_index].pos - part.path_coords[path_coord_end_index - 1].pos; + ingoing_direction.normalize(); + } + else if (has_segment_before) + { + Q_ASSERT(i >= 1 && i < part.path_coords.size()); + ingoing_direction = part.path_coords[i-1].pos - part.path_coords[i-2].pos; + ingoing_direction.normalize(); + } + + bool has_segment_after = (i < path_coord_end_index) || part.isClosed(); + MapCoordF outgoing_direction; + if (has_segment_after && i == path_coord_end_index) + { + Q_ASSERT(part.path_coords.size() > 1); + outgoing_direction = part.path_coords[1].pos - part.path_coords[0].pos; + outgoing_direction.normalize(); + } + else if (has_segment_after) + { + Q_ASSERT(i < path_coord_end_index); + outgoing_direction = part.path_coords[i+1].pos - part.path_coords[i].pos; + outgoing_direction.normalize(); + } + + // Collision state with other object at current other path coord + bool colliding = false; + // Last known intersecting point. + // This is valid as long as colliding == true and entered as intersection + // when the next segment suddenly is not colliding anymore. + Intersection last_intersection; + + for (size_t other_part_index = 0; other_part_index < other->path_parts.size(); ++other_part_index) + { + const PathPart& other_part = other->path_parts[part_index]; /// \todo FIXME: part_index or other_part_index ??? + auto other_path_coord_end_index = other_part.path_coords.size() - 1; + for (auto k = PathCoordVector::size_type { 1 }; k <= other_path_coord_end_index; ++k) + { + // Test the two line segments against each other. + // Naming: segment in this path is a, segment in other path is b + const PathCoord& a0 = part.path_coords[i-1]; + const PathCoord& a1 = part.path_coords[i]; + const PathCoord& b0 = other_part.path_coords[k-1]; + const PathCoord& b1 = other_part.path_coords[k]; + MapCoordF b_direction = b1.pos - b0.pos; + b_direction.normalize(); + + bool first_other_segment = (k == 1); + if (first_other_segment) + { + colliding = isPointOnSegment(a0.pos, a1.pos, b0.pos); + if (colliding && !other_part.isClosed()) + { + // Enter intersection at start of other segment + bool ok; + double a = parameterOfPointOnLine(a0.pos.x(), a0.pos.y(), a1.pos.x() - a0.pos.x(), a1.pos.y() - a0.pos.y(), b0.pos.x(), b0.pos.y(), ok); + Q_ASSERT(ok); + out.push_back(Intersection::makeIntersectionAt(a, 0, a0, a1, b0, b1, part_index, other_part_index)); + } + } + + bool last_other_segment = (k == other_path_coord_end_index); + if (last_other_segment) + { + bool collision_at_end = isPointOnSegment(a0.pos, a1.pos, b1.pos); + if (collision_at_end && !other_part.isClosed()) + { + // Enter intersection at end of other segment + bool ok; + double a = parameterOfPointOnLine(a0.pos.x(), a0.pos.y(), a1.pos.x() - a0.pos.x(), a1.pos.y() - a0.pos.y(), b1.pos.x(), b1.pos.y(), ok); + Q_ASSERT(ok); + out.push_back(Intersection::makeIntersectionAt(a, 1, a0, a1, b0, b1, part_index, other_part_index)); + } + } + + double denominator = a0.pos.x()*b0.pos.y() - a0.pos.y()*b0.pos.x() - a0.pos.x()*b1.pos.y() - a1.pos.x()*b0.pos.y() + a0.pos.y()*b1.pos.x() + a1.pos.y()*b0.pos.x() + a1.pos.x()*b1.pos.y() - a1.pos.y()*b1.pos.x(); + if (denominator == 0) + { + // Parallel lines, calculate parameters for b's start and end points in a and b. + // This also checks whether the lines are actually on the same level. + bool ok; + double b_start = 0; + double a_start = parameterOfPointOnLine(a0.pos.x(), a0.pos.y(), a1.pos.x() - a0.pos.x(), a1.pos.y() - a0.pos.y(), b0.pos.x(), b0.pos.y(), ok); + if (!ok) + { + if (colliding) out.push_back(last_intersection); + colliding = false; + continue; + } + double b_end = 1; + double a_end = parameterOfPointOnLine(a0.pos.x(), a0.pos.y(), a1.pos.x() - a0.pos.x(), a1.pos.y() - a0.pos.y(), b1.pos.x(), b1.pos.y(), ok); + if (!ok) + { + if (colliding) out.push_back(last_intersection); + colliding = false; + continue; + } + + // Cull ranges + if (a_start < zero_minus_epsilon && a_end < zero_minus_epsilon) + { + if (colliding) out.push_back(last_intersection); + colliding = false; + continue; + } + if (a_start > one_plus_epsilon && a_end > one_plus_epsilon) + { + if (colliding) out.push_back(last_intersection); + colliding = false; + continue; + } + + // b overlaps somehow with a, check if we have to enter one or two collisions + // (provided the incoming / outgoing tangents are not parallel!) + if (!colliding) + { + if (a_start <= 0) + { + // b comes in over the start of a + Q_ASSERT(a_end >= 0); + + // Check for parallel tangent case + if (!has_segment_before || MapCoordF::dotProduct(b_direction, ingoing_direction) < 1 - epsilon) + { + // Enter intersection at a=0 + double b = b_start + (0 - a_start) / (a_end - a_start) * (b_end - b_start); + out.push_back(Intersection::makeIntersectionAt(0, b, a0, a1, b0, b1, part_index, other_part_index)); + } + + colliding = true; + } + else if (a_start >= 1) + { + // b comes in over the end of a + Q_ASSERT(a_end <= 1); + + // Check for parallel tangent case + if (!has_segment_after || -1 * MapCoordF::dotProduct(b_direction, outgoing_direction) < 1 - epsilon) + { + // Enter intersection at a=1 + double b = b_start + (1 - a_start) / (a_end - a_start) * (b_end - b_start); + out.push_back(Intersection::makeIntersectionAt(1, b, a0, a1, b0, b1, part_index, other_part_index)); + } + + colliding = true; + } + else + Q_ASSERT(false); + } + if (colliding) + { + if (a_end > 1) + { + // b goes out over the end of a + Q_ASSERT(a_start <= 1); + + // Check for parallel tangent case + if (!has_segment_after || MapCoordF::dotProduct(b_direction, outgoing_direction) < 1 - epsilon) + { + // Enter intersection at a=1 + double b = b_start + (1 - a_start) / (a_end - a_start) * (b_end - b_start); + out.push_back(Intersection::makeIntersectionAt(1, b, a0, a1, b0, b1, part_index, other_part_index)); + } + + colliding = false; + } + else if (a_end < 0) + { + // b goes out over the start of a + Q_ASSERT(a_start >= 1); + + // Check for parallel tangent case + if (!has_segment_before || -1 * MapCoordF::dotProduct(b_direction, ingoing_direction) < 1 - epsilon) + { + // Enter intersection at a=0 + double b = b_start + (0 - a_start) / (a_end - a_start) * (b_end - b_start); + out.push_back(Intersection::makeIntersectionAt(1, b, a0, a1, b0, b1, part_index, other_part_index)); + } + + colliding = false; + } + else + { + // b stops in the middle of a. + // Remember last known colliding point, the endpoint of b. + last_intersection = Intersection::makeIntersectionAt(a_end, 1, a0, a1, b0, b1, part_index, other_part_index); + } + } + + // Check if there is a collision at the endpoint + Q_ASSERT(colliding == (a_end >= 0 && a_end <= 1)); + } + else + { + // Non-parallel lines, calculate intersection parameters and check if in range + double a = +(a0.pos.x()*b0.pos.y() - a0.pos.y()*b0.pos.x() - a0.pos.x()*b1.pos.y() + a0.pos.y()*b1.pos.x() + b0.pos.x()*b1.pos.y() - b1.pos.x()*b0.pos.y()) / denominator; + if (a < zero_minus_epsilon || a > one_plus_epsilon) + { + if (colliding) out.push_back(last_intersection); + colliding = false; + continue; + } + + double b = -(a0.pos.x()*a1.pos.y() - a1.pos.x()*a0.pos.y() - a0.pos.x()*b0.pos.y() + a0.pos.y()*b0.pos.x() + a1.pos.x()*b0.pos.y() - a1.pos.y()*b0.pos.x()) / denominator; + if (b < zero_minus_epsilon || b > one_plus_epsilon) + { + if (colliding) out.push_back(last_intersection); + colliding = false; + continue; + } + + // Special case for overlapping (cloned / traced) polylines: check if b is parallel to adjacent direction. + // If so, set colliding to true / false without entering an intersection because the other line + // simply continues along / comes from the path of both polylines instead of intersecting. + double dot = -1; + if (has_segment_before && a <= 0 + epsilon) + { + // Ingoing direction + dot = MapCoordF::dotProduct(b_direction, ingoing_direction); + if (b <= 0 + epsilon) + dot = -1 * dot; + } + else if (has_segment_after && a >= 1 - epsilon) + { + // Outgoing direction + dot = MapCoordF::dotProduct(b_direction, outgoing_direction); + if (b >= 1 - epsilon) + dot = -1 * dot; + } + if (dot >= 1 - epsilon) + { + colliding = (b > 0.5); + continue; + } + + // Enter the intersection + last_intersection = Intersection::makeIntersectionAt(a, b, a0, a1, b0, b1, part_index, other_part_index); + out.push_back(last_intersection); + colliding = (b == 1); + } + } + } + } + } +} + +void PathObject::setCoordinate(MapCoordVector::size_type pos, MapCoord c) +{ + Q_ASSERT(pos < getCoordinateCount()); + + const PathPart& part = *findPartForIndex(pos); + if (part.isClosed() && pos == part.last_index) + pos = part.first_index; + coords[pos] = c; + if (part.isClosed() && pos == part.first_index) + setClosingPoint(part.last_index, c); + + setOutputDirty(); +} + +void PathObject::addCoordinate(MapCoordVector::size_type pos, MapCoord c) +{ + Q_ASSERT(pos <= coords.size()); + + if (coords.empty()) + { + coords.emplace_back(c); + + path_parts.clear(); + path_parts.emplace_back(*this, 0, 0); + } + else + { + auto part = findPartForIndex(qMin(pos, coords.size() - 1)); + coords.insert(coords.begin() + pos, c); + partSizeChanged(part, +1); + + if (pos == part->first_index) + { + if (part->isClosed()) + setClosingPoint(part->last_index, c); + } + else if (pos == part->last_index) + { + Q_ASSERT(pos > part->first_index); + coords[pos-1].setClosePoint(false); + coords[pos-1].setHolePoint(false); + } + } + setOutputDirty(); +} + +void PathObject::addCoordinate(MapCoord c, bool start_new_part) +{ + if (coords.empty()) + { + addCoordinate(0, c); + } + else if (!start_new_part) + { + addCoordinate(coords.size(), c); + } + else + { + auto index = coords.size(); + coords.push_back(c); + path_parts.emplace_back(*this, index, index); + setOutputDirty(); + } +} + +void PathObject::deleteCoordinate(MapCoordVector::size_type pos, bool adjust_other_coords, int delete_bezier_point_action) +{ + const auto part = findPartForIndex(pos); + const auto coords_begin = begin(coords); + + auto num_coords = part->size(); + if (num_coords <= 7) + { + auto num_regular_points = part->countRegularNodes(); + + if (num_regular_points <= 2 && (pos == part->first_index || pos == part->last_index)) + { + // Too small, must delete + deletePart(findPartIndexForIndex(pos)); + return; + } + + if (num_regular_points == 3 && num_coords == 4) + { + // A closed path of three straight segments, to be opened + Q_ASSERT(part->isClosed()); + if (pos == part->last_index) + pos = part->first_index; + + // Move pos to end_index-1, than erase last two coords + std::rotate(coords_begin + part->first_index, coords_begin + pos + 1, coords_begin + part->last_index); + coords.erase(coords_begin + part->last_index - 1, coords_begin + part->last_index+1); + partSizeChanged(part, -2); + coords[part->last_index].setHolePoint(true); + return; + } + } + + auto prev_index = part->prevCoordIndex(pos); + if (prev_index < pos && coords[prev_index].isCurveStart() && prev_index + 3 > pos) + { + // Delete curve handles + coords[prev_index].setCurveStart(false); + coords.erase(coords_begin + prev_index + 1, coords_begin + prev_index + 3); + partSizeChanged(part, -2); + return; + } + + if (pos == part->first_index || pos == part->last_index) + { + if (part->isClosed()) + { + // Make start/end point an inner point before removing + auto middle_offset = coords[part->first_index].isCurveStart() ? 3 : 1; + std::rotate(coords_begin + part->first_index, coords_begin + part->first_index + middle_offset, coords_begin + part->last_index); + setClosingPoint(part->last_index, coords[part->first_index]); + pos = part->last_index - middle_offset; + prev_index = part->prevCoordIndex(pos); + } + else + { + auto start = std::min(pos, prev_index + 1); + auto end = std::max(pos + 1, part->nextCoordIndex(pos)); + coords.erase(coords_begin + start, coords_begin + end); + partSizeChanged(part, start - end); + coords[part->last_index].setHolePoint(true); + coords[part->last_index].setCurveStart(false); + return; + } + } + + // Delete inner point + Q_ASSERT(pos > part->first_index); + Q_ASSERT(pos < part->last_index); + if (!(coords[prev_index].isCurveStart() && coords[pos].isCurveStart())) + { + // Bezier curve on single side + if (coords[pos].isCurveStart()) + coords[prev_index].setCurveStart(true); + + coords.erase(coords_begin + pos); + partSizeChanged(part, -1); + } + else + { + // Bezier curves on both sides + if (adjust_other_coords) + { + // Adjust handle positions to preserve the original shape somewhat + // (of course, in general it is impossible to do this) + prepareDeleteBezierPoint(pos, delete_bezier_point_action); + } + + coords.erase(begin(coords) + pos - 1, begin(coords) + pos + 2); + partSizeChanged(part, -3); + } +} + +void PathObject::prepareDeleteBezierPoint(MapCoordVector::size_type pos, int delete_bezier_point_action) +{ + const MapCoord& p0 = coords[pos - 3]; + MapCoord& p1 = coords[pos - 2]; + const MapCoord& p2 = coords[pos - 1]; + const MapCoord& q0 = coords[pos]; + const MapCoord& q1 = coords[pos + 1]; + MapCoord& q2 = coords[pos + 2]; + const MapCoord& q3 = coords[pos + 3]; + + double pfactor, qfactor; + if (delete_bezier_point_action == Settings::DeleteBezierPoint_ResetHandles) + { + double target_length = BEZIER_HANDLE_DISTANCE * p0.distanceTo(q3); + pfactor = target_length / qMax(p0.distanceTo(p1), 0.01); + qfactor = target_length / qMax(q3.distanceTo(q2), 0.01); + } + else if (delete_bezier_point_action == Settings::DeleteBezierPoint_RetainExistingShape) + { + calcBezierPointDeletionRetainingShapeFactors(p0, p1, p2, q0, q1, q2, q3, pfactor, qfactor); + + if (qIsInf(pfactor)) + pfactor = 1; + if (qIsInf(qfactor)) + qfactor = 1; + + calcBezierPointDeletionRetainingShapeOptimization(p0, p1, p2, q0, q1, q2, q3, pfactor, qfactor); + + double minimum_length = 0.01 * p0.distanceTo(q3); + pfactor = qMax(minimum_length / qMax(p0.distanceTo(p1), 0.01), pfactor); + qfactor = qMax(minimum_length / qMax(q3.distanceTo(q2), 0.01), qfactor); + } + else + { + Q_ASSERT(delete_bezier_point_action == Settings::DeleteBezierPoint_KeepHandles); + pfactor = 1; + qfactor = 1; + } + + MapCoordF p0p1 = MapCoordF(p1) - MapCoordF(p0); + p1 = MapCoord(p0.x() + pfactor * p0p1.x(), p0.y() + pfactor * p0p1.y()); + + MapCoordF q3q2 = MapCoordF(q2) - MapCoordF(q3); + q2 = MapCoord(q3.x() + qfactor * q3q2.x(), q3.y() + qfactor * q3q2.y()); +} + +void PathObject::clearCoordinates() +{ + coords.clear(); + path_parts.clear(); + setOutputDirty(); +} + +void PathObject::assignCoordinates(const PathObject& proto, MapCoordVector::size_type first, MapCoordVector::size_type last) +{ + Q_ASSERT(last < proto.coords.size()); + + auto part = proto.findPartForIndex(first); + Q_ASSERT(part == proto.findPartForIndex(last)); + + coords.clear(); + if (last >= first) + coords.reserve(last - first + 1); + else + coords.reserve(last - part->first_index + part->last_index - first + 2); + + for (auto i = first; i != last; ) + { + MapCoord new_coord = proto.coords[i]; + new_coord.setClosePoint(false); + new_coord.setHolePoint(false); + coords.push_back(new_coord); + + ++i; + if (i > part->last_index) + { + i = part->first_index; + if (part->isClosed() && i != last) + coords.erase(coords.end()-1); + } + + } + // Add last point + MapCoord new_coord = proto.coords[last]; + new_coord.setCurveStart(false); + new_coord.setClosePoint(false); + new_coord.setHolePoint(true); + coords.push_back(new_coord); + + path_parts.clear(); + path_parts.emplace_back(*this, 0, coords.size()-1); + + setOutputDirty(); +} + +void PathObject::updatePathCoords() const +{ + auto part_start = MapCoordVector::size_type { 0 }; + for (auto& part : path_parts) + { + part.first_index = part_start; + part.last_index = part.path_coords.update(part_start); + part_start = part.last_index+1; + } +} + +void PathObject::recalculateParts() +{ + setOutputDirty(); + + path_parts.clear(); + if (!coords.empty()) + { + MapCoordVector::size_type start_index = 0; + auto last_index = coords.size()-1; + + for (MapCoordVector::size_type i = 0; i <= last_index; ++i) + { + if (coords[i].isHolePoint()) + { + path_parts.emplace_back(*this, start_index, i); + start_index = i+1; + } + else if (coords[i].isCurveStart()) + { + i += 2; + } + } + + if (start_index <= last_index) + path_parts.emplace_back(*this, start_index, last_index); + } +} + +void PathObject::setClosingPoint(MapCoordVector::size_type index, MapCoord coord) +{ + coord.setCurveStart(false); + coord.setHolePoint(true); + coord.setClosePoint(true); + coords[index] = coord; +} + +void PathObject::updateEvent() const +{ + updatePathCoords(); +} + +void PathObject::createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const +{ + symbol->createRenderables(this, path_parts, output, options); +} + + +// ### PointObject ### + +PointObject::PointObject(const Symbol* symbol) + : Object(Object::Point, symbol) + , rotation(0.0) +{ + Q_ASSERT(!symbol || (symbol->getType() == Symbol::Point)); + rotation = 0; + coords.push_back(MapCoord(0, 0)); +} + +PointObject::PointObject(const PointObject& proto) + : Object(proto) + , rotation(proto.rotation) +{ + // nothing +} + +PointObject* PointObject::duplicate() const +{ + return new PointObject(*this); +} + +void PointObject::copyFrom(const Object& other) +{ + if (&other == this) + return; + + Object::copyFrom(other); + const PointObject* point_other = other.asPoint(); + const PointSymbol* point_symbol = getSymbol()->asPoint(); + if (point_symbol && point_symbol->isRotatable()) + setRotation(point_other->getRotation()); +} + + +void PointObject::setPosition(qint32 x, qint32 y) +{ + coords[0].setNativeX(x); + coords[0].setNativeY(y); + setOutputDirty(); +} + +void PointObject::setPosition(MapCoord coord) +{ + coords[0] = coord; +} + +void PointObject::setPosition(MapCoordF coord) +{ + coords[0].setX(coord.x()); + coords[0].setY(coord.y()); + setOutputDirty(); +} + +MapCoordF PointObject::getCoordF() const +{ + return MapCoordF(coords.front()); +} + +MapCoord PointObject::getCoord() const +{ + return coords.front(); +} + + +void PointObject::transform(const QTransform& t) +{ + if (t.isIdentity()) + return; + + auto& coord = coords.front(); + const auto p = t.map(MapCoordF{coord}); + coord.setX(p.x()); + coord.setY(p.y()); + setOutputDirty(); +} + + +void PointObject::setRotation(float new_rotation) +{ + Q_ASSERT(symbol->asPoint()->isRotatable() || qIsNull(new_rotation)); + if (!qIsNaN(new_rotation) && symbol->asPoint()->isRotatable()) + { + rotation = new_rotation; + setOutputDirty(); + } +} + +void PointObject::setRotation(MapCoordF vector) +{ + setRotation(atan2(vector.x(), vector.y())); +} + +bool PointObject::intersectsBox(const QRectF& box) const +{ + return box.contains(QPointF(coords.front())); +} + + +} // namespace OpenOrienteering diff --git a/src/core/objects/object.h b/src/core/objects/object.h new file mode 100644 index 0000000..d499034 --- /dev/null +++ b/src/core/objects/object.h @@ -0,0 +1,1302 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2013-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_OBJECT_H +#define OPENORIENTEERING_OBJECT_H + +#include +#include +#include + +#include +#include +#include +#include +// IWYU pragma: no_include + +#include "core/map_coord.h" +#include "core/path_coord.h" +#include "core/virtual_path.h" +#include "core/renderables/renderable.h" +#include "core/symbols/symbol.h" + +class QIODevice; +class QTransform; +class QXmlStreamReader; +class QXmlStreamWriter; +// IWYU pragma: no_forward_declare QRectF + +namespace OpenOrienteering { + +class Map; +class PointObject; +class PathObject; +class TextObject; +class VirtualCoordVector; + + +/** + * Abstract base class which combines coordinates and a symbol to form an object + * (in a map, or inside a point symbol as one of its elements). + * + * Every object must have a symbol. If the symbol is not known, one of the + * "undefined" symbols from the Map class can be used. + * + * From the object's data, a call to update() will generate the object's "output", + * that is a set of renderables and the calculation of the object's extent (bounding box). + * The renderables can then be inserted into a map where they are used to display the object. + */ +class Object // clazy:exclude=copyable-polymorphic +{ +friend class ObjectRenderables; +friend class OCAD8FileImport; +friend class XMLImportExport; +public: + /** Enumeration of possible object types. */ + enum Type + { + /** + * A single coordinate, no further coordinates can be added. + * For point symbols only. + */ + Point = 0, + + /** + * A dynamic list of coordinates. + * For line, area and combined symbols. + */ + Path = 1, + + /** + * Either one or two coordinates, for single-anchor or box text. + * For text symbols only. + */ + Text = 4 + }; + + /** Creates an empty object with the given type and (optional) symbol. */ + explicit Object(Type type, const Symbol* symbol = nullptr); + + /** Creates an empty object with the given type, symbol, coords and (optional) map. */ + explicit Object(Type type, const Symbol* symbol, const MapCoordVector& coords, Map* map = nullptr); + +protected: + /** + * Constructs a Object, initialized from the given prototype. + * + * Note that the object is NOT added to a map, and consequently, + * the map pointer is initialized to nullptr. + */ + explicit Object(const Object& proto); + +public: + /** Destructs the object. */ + virtual ~Object(); + + Object& operator=(const Object& other) = delete; + + virtual void copyFrom(const Object& other); + + /** Creates an identical copy of the object. + * + * This needs to be implemented in non-abstract subclasses. + * Implementation should use the copy constructor to ensure proper initialization. + */ + virtual Object* duplicate() const = 0; + + /** + * Checks for equality with another object. If compare_symbol is set, + * also the symbols are compared for having the same properties. + */ + bool equals(const Object* other, bool compare_symbol) const; + + virtual bool validate() const; + + /** Returns the object type determined by the subclass */ + inline Type getType() const; + + /** Convenience cast to PointObject with type checking */ + PointObject* asPoint(); + /** Convenience cast to PointObject with type checking */ + const PointObject* asPoint() const; + /** Convenience cast to PathObject with type checking */ + PathObject* asPath(); + /** Convenience cast to PathObject with type checking */ + const PathObject* asPath() const; + /** Convenience cast to TextObject with type checking */ + TextObject* asText(); + /** Convenience cast to TextObject with type checking */ + const TextObject* asText() const; + + /** Loads the object in the old "native" file format from the given file. */ + void load(QIODevice* file, int version, Map* map); + + /** Saves the object in xml format to the given stream. */ + void save(QXmlStreamWriter& xml) const; + /** + * Loads the object in xml format from the given stream. + * @param xml The stream to load the object from, must be at the correct tag. + * @param map The map in which the object will be inserted. + * This value will be assigned to the object's map pointer + * It may be nullptr. + * @param symbol_dict A dictionary mapping symbol IDs to symbol pointers. + * @param symbol If set, this symbol will be assigned to the object, rather + * than reading the symbol from the stream. + */ + static Object* load(QXmlStreamReader& xml, Map* map, const SymbolDictionary& symbol_dict, const Symbol* symbol = nullptr); + + + /** + * If the output_dirty flag is set, regenerates output and extent, and updates the object's map (if set). + * + * Returns true if output was dirty. + */ + bool update() const; + + /** + * Always regenerates output and extent, and updates the object's map (if set). + */ + void forceUpdate() const; + + + /** Moves the whole object + * @param dx X offset in native map coordinates. + * @param dy Y offset in native map coordinates. + */ + void move(qint32 dx, qint32 dy); + + /** Moves the whole object by the given offset. */ + void move(MapCoord offset); + + /** Scales all coordinates, with the given scaling center */ + virtual void scale(MapCoordF center, double factor); + + /** Scales all coordinates, with the center (0, 0). + * @param factor_x horizontal scaling factor + * @param factor_y vertical scaling factor + */ + virtual void scale(double factor_x, double factor_y); + + /** Rotates the whole object around the center point. + * The angle must be given in radians. */ + void rotateAround(MapCoordF center, double angle); + + /** Rotates the whole object around the center (0, 0). + * The angle must be given in radians. */ + void rotate(double angle); + + /** + * Apply a transformation to all coordinates. + * + * \todo Handle rotation of patterns or text (?) + */ + virtual void transform(const QTransform& t) = 0; + + /** + * Checks if the given coord, with the given tolerance, is on this object. + * + * With extended_selection, the coord is on point objects always + * if it is whithin their extent, otherwise it has to be close to + * their midpoint. Returns a Symbol::Type which specifies on which + * symbol type the coord is + * (important for combined symbols which can have areas and lines). + */ + int isPointOnObject(MapCoordF coord, float tolerance, bool treat_areas_as_paths, bool extended_selection) const; + + /** + * Checks if a path point (excluding curve control points) is included in the given box. + */ + virtual bool intersectsBox(const QRectF& box) const = 0; + + /** Takes ownership of the renderables */ + void takeRenderables(); + + /** Deletes the renderables (and extent), undoing update() */ + void clearRenderables(); + + /** Returns the renderables, read-only */ + const ObjectRenderables& renderables() const; + + // Getters / Setters + + /** + * Returns the raw MapCoordVector of the object. + * It's layout and interpretation depends on the object type. + */ + const MapCoordVector& getRawCoordinateVector() const; + + /** Sets the object output's dirty state. */ + void setOutputDirty(bool dirty = true); + /** Returns if the object's output must be regenerated. */ + bool isOutputDirty() const; + + /** + * Changes the object's symbol, returns if successful. + * + * Some conversions are impossible, for example point to line. Normally, + * this method checks if the types of the old and the new symbol are + * compatible. If the old symbol pointer is no longer valid, you can + * use no_checks to disable this. + */ + bool setSymbol(const Symbol* new_symbol, bool no_checks); + /** Returns the object's symbol. */ + const Symbol* getSymbol() const; + + /** NOTE: The extent is only valid after update() has been called! */ + const QRectF& getExtent() const; + + /** + * Sets the object's map pointer. + * + * May be nullptr if the object is not in a map. + */ + void setMap(Map* map); + /** Returns the object's map pointer. */ + Map* getMap() const; + + /** Constructs an object of the given type with the given symbol. */ + static Object* getObjectForType(Type type, const Symbol* symbol = nullptr); + + + /** Defines a type which maps keys to values, to be used for tagging objects. */ + typedef QHash Tags; + + /** Returns a const reference to the object's tags. */ + const Tags& tags() const; + + /** Replaces the object's tags. */ + void setTags(const Tags& tags); + + /** Returns the value of the given tag key. */ + QString getTag(const QString& key) const; + + /** Sets the given tag key to the value. */ + void setTag(const QString& key, const QString& value); + + /** Removes the given tag key and its value. */ + void removeTag(const QString& key); + + + /** + * @brief Extends a rectangle to enclose all of the object's control points. + */ + void includeControlPointsRect(QRectF& rect) const; + +protected: + virtual void updateEvent() const; + + virtual void createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const; + + Type type; + const Symbol* symbol; + MapCoordVector coords; + Map* map; + Tags object_tags; + +private: + mutable bool output_dirty; // does the output have to be re-generated because of changes? + mutable QRectF extent; // only valid after calling update() + mutable ObjectRenderables output; // only valid after calling update() +}; + + + +class PathPartVector; + + +/** + * Helper class with information about parts of paths. + * A part is a path segment which is separated from other parts by + * a hole point at its end. + */ +class PathPart : public VirtualPath +{ +public: + /** Pointer to path part containing this part */ + PathObject* path; + + PathPart( + PathObject& path, + MapCoordVector::size_type start_index, + MapCoordVector::size_type end_index + ); + + PathPart( + const VirtualCoordVector& coords, + MapCoordVector::size_type start_index, + MapCoordVector::size_type end_index + ); + + PathPart( + PathObject& object, + const VirtualPath& path + ); + + ~PathPart() = default; + + PathPart& operator=(const PathPart& rhs); + + /** + * Closes or opens the sub-path. + * + * If closed == true and may_use_existing_close_point == false, + * a new point is added as closing point even if its coordinates + * are identical to the existing last point. Else, the last point + * may be reused. + */ + void setClosed(bool closed, bool may_use_existing_close_point = false); + + /** + * Closes the subpath, merging the start and end point at their center. + * + * \see PathPart::setClosed() + */ + void connectEnds(); + + /** + * Reverses the part's coordinates. + * + * Reversing the coordinates results in switching the start/end/mid/dash + * symbol direction for line symbols. + * + * \see PathObject::reverse() + */ + void reverse(); + + static PathPartVector calculatePathParts(const VirtualCoordVector& coords); +}; + + + +class PathPartVector : public std::vector +{ +public: + /** + * This is dangerous when copying objects (which own a PathPartVector). + * + * Objects need to deal with the PathParts explicitly, at least as long as + * the PathPart contains distinct references to the object and to the + * coordinates. + * + * Other use cases may consider using std::vector. + */ + PathPartVector& operator=(const PathPartVector&) = delete; + + /** + * Returns true if the part's end_index is lower than index. + * + * This function can be used for doing a binary search on a sorted PathPartVector. + * + * @see std::lower_bound() + */ + static bool compareEndIndex(const PathPart& part, VirtualPath::size_type index); +}; + + + +/** + * Object type which can be used for line, area and combined symbols. + * Has a dynamic number of coordinates. + * + * The coordinates are divided into one or multiple PathParts. A PathPart + * is ended by a coordinate with the "hole point" flag. For all types of + * flags which can be set, see the MapCoord documentation. + */ +class PathObject : public Object // clazy:exclude=copyable-polymorphic +{ + friend class PathPart; + +public: + + /** Returned by calcAllIntersectionsWith(). */ + struct Intersection + { + /** Coordinate of the intersection */ + MapCoordF coord; + /** Part index of intersection */ + PathPartVector::size_type part_index; + /** Length of path until this intersection point */ + PathCoord::length_type length; + /** Part index of intersection in other path */ + PathPartVector::size_type other_part_index; + /** Length of other path until this intersection point */ + PathCoord::length_type other_length; + + /** + * Creates an Intersection at the position specified by factors a and b + * between the a0/a1 and b0/b1 PathCoords in the given parts. + */ + static Intersection makeIntersectionAt( + double a, + double b, + const PathCoord& a0, + const PathCoord& a1, + const PathCoord& b0, + const PathCoord& b1, + PathPartVector::size_type part_index, + PathPartVector::size_type other_part_index ); + }; + + /** std::vector of Intersection with the ability to sort them and remove duplicates. */ + class Intersections : public std::vector + { + public: + /** Sorts the intersections and removes duplicates. */ + void normalize(); + }; + + + /** Constructs a PathObject, optionally assigning a symbol. */ + explicit PathObject(const Symbol* symbol = nullptr); + + /** Constructs a PathObject, assigning initial coords and optionally the map pointer. */ + PathObject(const Symbol* symbol, const MapCoordVector& coords, Map* map = nullptr); + + /** Constructs a PathObject, assigning initial coords from a single piece of a line. */ + PathObject(const Symbol* symbol, const PathObject& proto, MapCoordVector::size_type piece); + +protected: + /** Constructs a PathObject, initalized from the given prototype. */ + explicit PathObject(const PathObject& proto); + +public: + /** Constructs a PathObject, initalized from the given part of another object. */ + explicit PathObject(const PathPart& proto_part); + + /** + * Creates a duplicate of the path object. + * + * Use asPath() on the result to obtain an object of type PathObject. + */ + PathObject* duplicate() const override; + + PathObject& operator=(const PathObject& other) = delete; + + /** Replaces this object's contents by those of the other. */ + void copyFrom(const Object& other) override; + + + bool validate() const override; + + + /** Checks the path for valid flags, and makes corrections as neccessary. */ + void normalize(); + + + bool intersectsBox(const QRectF& box) const override; + + + // Coordinate access methods + + /** Returns the number of coordinates, including curve handles and close points. */ + MapCoordVector::size_type getCoordinateCount() const; + /** Returns the i-th coordinate. */ + const MapCoord& getCoordinate(MapCoordVector::size_type pos) const; + /** Returns the i-th coordinate. */ + MapCoord& getCoordinate(MapCoordVector::size_type pos); + + /** Replaces the i-th coordinate with c. */ + void setCoordinate(MapCoordVector::size_type pos, MapCoord c); + + /** Adds the coordinate at the given index. */ + void addCoordinate(MapCoordVector::size_type pos, MapCoord c); + + /** Adds the coordinate at the end, optionally starting a new part. + * If starting a new part, make sure that the last coord of the old part + * has the hole point flag! */ + void addCoordinate(MapCoord c, bool start_new_part = false); + + /** + * Deletes a coordinate from the path. + * + * When requesting a control point of a bezier arc to be deleted, the other + * control point is deleted, too. + * + * If the number of regular points in the coordinate's part is not more + * than two, the whole part is delete from the object. + * + * @param pos Index of the coordinate to delete. + * @param adjust_other_coords If set and the deleted coordinate was joining + * two bezier curves, adapts the adjacent curves with a strategy defined + * by delete_bezier_point_action. adjust_other_coords does not work + * when deleting bezier curve handles! + * @param delete_bezier_point_action Must be an enum value from + * Settings::DeleteBezierPointAction if adjust_other_coords is set. + */ + void deleteCoordinate(MapCoordVector::size_type pos, bool adjust_other_coords, int delete_bezier_point_action = -1); + + /** Deletes all coordinates of the object. */ + void clearCoordinates(); + + /** + * Assigns the given prototype's coordinates subset to this object's coordinates. + * + * The range must be within one part. Last may be smaller than first iff + * the path is closed. + */ + void assignCoordinates(const PathObject& proto, MapCoordVector::size_type first, MapCoordVector::size_type last); + + + /** Finds the path part containing the given coord index. */ + PathPartVector::const_iterator findPartForIndex(MapCoordVector::size_type coords_index) const; + + /** Finds the path part containing the given coord index. */ + PathPartVector::iterator findPartForIndex(MapCoordVector::size_type coords_index); + + /** + * Finds the path part containing the given coord index. + * + * \todo Review where this signature can be replace by the one returning an iterator. + */ + PathPartVector::size_type findPartIndexForIndex(MapCoordVector::size_type coords_index) const; + + + /** + * Returns the path coordinate for the map coordinate with given index. + * + * @param index Index of normal MapCoord for which to create the PathCoord. + */ + PathCoord findPathCoordForIndex(MapCoordVector::size_type index) const; + + + /** + * Returns true if the given index is a curve handle. + */ + bool isCurveHandle(MapCoordVector::size_type index) const; + + + /** + * Returns the vector of path parts. + */ + const PathPartVector& parts() const; + + /** + * Returns the vector of path parts. + * + * Marks the output as dirty. + */ + PathPartVector& parts(); + + /** + * Deletes the i-th path part. + */ + void deletePart(PathPartVector::size_type part_index); + + /** + * Transforms the coordinates and the pattern origin. + */ + void transform(const QTransform& t) override; + + // Pattern methods + + /** + * Returns the rotation of the object pattern. Only has an effect in + * combination with a symbol interpreting this value. + */ + float getPatternRotation() const; + + /** + * Sets the rotation of the object pattern. Only has an effect in + * combination with a symbol interpreting this value. + */ + void setPatternRotation(float rotation); + + /** + * Returns the origin of the object pattern. Only has an effect in + * combination with a symbol interpreting this value. + */ + MapCoord getPatternOrigin() const; + + /** + * Sets the origin of the object pattern. Only has an effect in + * combination with a symbol interpreting this value. + */ + void setPatternOrigin(const MapCoord& origin); + + + // Operations + + /** + * Calculates the closest point on the path to the given coordinate, + * returns the squared distance of these points and PathCoord information + * for the point on the path. + * + * This does not need to be an existing path coordinate. This method is + * usually called to find the position on the path the user clicked on. + * part_index can be set to a valid part index to constrain searching + * to this specific path part. + * + * \todo Convert out_distance_sq to double (so avoiding conversions). + * \todo Return PathCoord rather than writing to the provided reference. + */ + void calcClosestPointOnPath( + MapCoordF coord, + float& out_distance_sq, + PathCoord& out_path_coord, + MapCoordVector::size_type start_index = 0, + MapCoordVector::size_type end_index = std::numeric_limits::max() + ) const; + + /** + * Calculates the closest control point coordinate to the given coordiante, + * returns the squared distance of these points and the index of the control point. + * + * \todo Convert out_distance_sq to double (so avoiding conversions). + * \todo Return index rather than writing to the provided reference. + */ + void calcClosestCoordinate( + MapCoordF coord, + float& out_distance_sq, + MapCoordVector::size_type& out_index) const; + + /** + * Splits the path at the position given by path_coord. + * + * Must not be called while isOutputDirty() returns true. + * + * Returns the index of the added point. + */ + MapCoordVector::size_type subdivide(const PathCoord& path_coord); + + /** + * Splits the path in the curve which starts at the given index. + * + * The second parameter determines the split position between begin and end + * of the curve (0.0 ... 1.0). + * + * Must not be called while isOutputDirty() returns true. + * + * @return The index of the added point. + */ + MapCoordVector::size_type subdivide(MapCoordVector::size_type index, float param); + + /** + * Returns if connectIfClose() would change something with the given parameters + */ + bool canBeConnected(const PathObject* other, double connect_threshold_sq) const; + + /** + * Returns if the objects were connected (if so, you can delete the other object). + * If one of the paths has to be reversed, it is done for the "other" path. + * Otherwise, the "other" path is not changed. + * + * \todo Review documentation, container usage, + */ + bool connectIfClose(PathObject* other, double connect_threshold_sq); + + /** + * Connects the given parts, optionally merging the end coordinates at the + * center position, and copying over the coordindates from other. + */ + void connectPathParts( + PathPartVector::size_type part_index, + const PathObject* other, + PathPartVector::size_type other_part_index, + bool prepend, + bool merge_ends = true + ); + + /** + * Returns the result of removing the section between begin and end from the path. + * + * begin and end must belong to the path part with the given part_index. + * However, any part_index value other than 1 is not supported at the moment. + * + * Returns an empty vector when nothing remains after removal. + */ + std::vector removeFromLine( + PathPartVector::size_type part_index, + qreal begin, + qreal end + ) const; + + /** + * Returns the result of splitting the path at the given inner position. + * + * Returns an empty vector when the object is not changed by the split. + * This happens when the path is not closed and split_pos is the begin or + * end of the path, or when the object has got more than a single PathPart. + */ + std::vector splitLineAt(const PathCoord& split_pos) const; + + /** + * Replaces the path with a range of it starting and ending at the given lengths. + * + * \todo Partially duplicated in LineSymbol::calculatePathCoordinates() + */ + void changePathBounds( + PathPartVector::size_type part_index, + PathCoord::length_type start_len, + PathCoord::length_type end_len + ); + + /** + * Appends (copies) the coordinates of other to this path. + */ + void appendPath(const PathObject* other); + + /** + * Appends (copies) the coordinates of a specific part to this path. + * + * The other object is determined from the part's path property. + */ + void appendPathPart(const PathPart& part); + + /** + * Reverses the object's coordinates, resulting in switching + * the start / end / mid / dash symbol direction for line symbols. + */ + void reverse(); + + /** Ensures that all parts are closed. Useful for objects with area-only symbols. */ + void closeAllParts(); + + /** + * Converts all polygonal sections in this path to splines. + * If at least one section is converted, returns true and + * returns an undo duplicate if the corresponding pointer is set. + */ + bool convertToCurves(PathObject** undo_duplicate = nullptr); + + /** + * Converts the given range of coordinates to a spline by inserting handles. + * The range must consist of only polygonal segments before. + * + * @return The new index of the end of the range. + */ + int convertRangeToCurves(const PathPart& part, MapCoordVector::size_type start_index, MapCoordVector::size_type end_index); + + /** + * Tries to remove points while retaining the path shape as much as possible. + * If at least one point is changed, returns true and + * returns an undo duplicate if the corresponding pointer is set. + */ + bool simplify(PathObject** undo_duplicate, double threshold); + + /** See Object::isPointOnObject() */ + int isPointOnPath( + MapCoordF coord, + float tolerance, + bool treat_areas_as_paths, + bool extended_selection + ) const; + + /** + * Returns true if the given coordinate is inside the area + * defined by this object, which must be closed. + */ + bool isPointInsideArea(MapCoordF coord) const; + + /** + * Calculates the maximum distance of the given coord ranges of two objects. + */ + double calcMaximumDistanceTo( + MapCoordVector::size_type start_index, + MapCoordVector::size_type end_index, + const PathObject* other, + MapCoordVector::size_type other_start_index, + MapCoordVector::size_type other_end_index + ) const; + + /** + * Calculates and adds all intersections with the other path to out. + * Note: intersections are not sorted and may contain duplicates! + * To clean them up, call clean() on the Intersections object after adding + * all intersections with objects you are interested in. + */ + void calcAllIntersectionsWith(const PathObject* other, Intersections& out) const; + + /** Called by Object::update() */ + void updatePathCoords() const; + + /** Called by Object::load() */ + void recalculateParts(); + +protected: + /** + * Adjusts the end index of the given part and the start/end indexes of the following parts. + * + * output_dirty must be set before calling this function. + */ + void partSizeChanged(PathPartVector::iterator part, MapCoordVector::difference_type change); + + + void prepareDeleteBezierPoint(MapCoordVector::size_type pos, int delete_bezier_point_action); + + /** + * Calculates the factors which should be applied to the length of the + * remaining bezier curve handle vectors when deleting a point joining + * two bezier curves to try to retain the original curves' shape. + * + * This is a simple version, the result should be optimized with + * calcBezierPointDeletionRetainingShapeOptimization(). + * + * p0, p1, p2, q0 make up the first original curve, + * q0, q1, q2, q3 make up the second original curve. + * out_pfactor is set to the factor to apply to the vector (p1 - p0), + * out_qfactor is set to the factor to apply to the vector (q2 - q3), + */ + static void calcBezierPointDeletionRetainingShapeFactors( + MapCoord p0, + MapCoord p1, + MapCoord p2, + MapCoord q0, + MapCoord q1, + MapCoord q2, + MapCoord q3, + double& out_pfactor, + double& out_qfactor + ); + + /** + * Uses nonlinear optimization to improve the first result obtained by + * calcBezierPointDeletionRetainingShapeFactors(). + */ + static void calcBezierPointDeletionRetainingShapeOptimization( + MapCoord p0, + MapCoord p1, + MapCoord p2, + MapCoord q0, + MapCoord q1, + MapCoord q2, + MapCoord q3, + double& out_pfactor, + double& out_qfactor + ); + + /** + * Is used internally by calcBezierPointDeletionRetainingShapeOptimization() + * to calculate the current cost. Evaluates the distance between p0 ... p3 + * and the reference path. + */ + static float calcBezierPointDeletionRetainingShapeCost( + MapCoord p0, + MapCoordF p1, + MapCoordF p2, + MapCoord p3, + PathObject* reference + ); + + /** + * Sets coord as the point which closes a part: sets the correct flags + * on it and replaces the coord at the given index by it. + * TODO: make separate methods? Setting coords exists already. + */ + void setClosingPoint(MapCoordVector::size_type index, MapCoord coord); + + void updateEvent() const override; + + void createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const override; + +private: + /** + * Rotation angle of the object pattern. Only used if the object + * has a symbol which interprets this value. + */ + float pattern_rotation; + + /** + * Origin shift of the object pattern. Only used if the object + * has a symbol which interprets this value. + */ + MapCoord pattern_origin; + + /** Path parts list */ + mutable PathPartVector path_parts; +}; + + + +/** Compares the length of the intersections. */ +inline +bool operator< (const PathObject::Intersection& lhs, const PathObject::Intersection& rhs) +{ + return lhs.length < rhs.length; +} + +/** Fuzzy equality check. */ +inline +bool operator== (const PathObject::Intersection& lhs, const PathObject::Intersection& rhs) +{ + // NOTE: coord is not compared, as the intersection is defined by the other params already. + const double epsilon = 1e-10; + return lhs.part_index == rhs.part_index && + lhs.other_part_index == rhs.other_part_index && + qAbs(lhs.length - rhs.length) <= epsilon && + qAbs(lhs.other_length - rhs.other_length) <= epsilon; +} + + + +/** + * Object type which can only be used for point symbols, + * and is also the only object which can be used with them. + * + * Has exactly one coordinate, and additionally a rotation parameter. + */ +class PointObject : public Object // clazy:exclude=copyable-polymorphic +{ +public: + /** Constructs a PointObject, optionally assigning the symbol. */ + explicit PointObject(const Symbol* symbol = nullptr); + +protected: + /** Constructs a PointObject, initalized from the given prototype. */ + explicit PointObject(const PointObject& proto); + +public: + /** + * Creates a duplicate of the point. + * + * Use asPoint() on the result to obtain an object of type PointObject. + */ + PointObject* duplicate() const override; + + PointObject& operator=(const PointObject& other) = delete; + + /** Replaces the content of this object by that of anothe. */ + void copyFrom(const Object& other) override; + + + /** Sets the point's position to a new position given in native map coordinates. */ + void setPosition(qint32 x, qint32 y); + + /** Changes the point's position. */ + void setPosition(MapCoord coord); + + /** Changes the point's position. */ + void setPosition(MapCoordF coord); + + /** Returns the point's position as MapCoordF. */ + MapCoordF getCoordF() const; + + /** Returns the point's coordinate. */ + MapCoord getCoord() const; + + + /** + * Transforms the position. + */ + void transform(const QTransform& t) override; + + + /** + * Sets the point object's rotation (in radians). + * + * This does nothing if the object's symbol isn't rotable. However, it is an + * error to call setRotation on such an object with an argument other than + * binary 0. + */ + void setRotation(float new_rotation); + + /** + * Sets the point object's rotation according to the given vector. + */ + void setRotation(MapCoordF vector); + + /** + * Returns the point object's rotation (in radians). This is only used + * if the object has a symbol which interprets this value. + */ + float getRotation() const; + + + bool intersectsBox(const QRectF& box) const override; + + +private: + /** The object's rotation (in radians). */ + float rotation; +}; + + + +/** + * A single PathCoord together with the object it belongs to. + * + * This is a convenient structure for passing around as parameter and return value. + */ +struct ObjectPathCoord : public PathCoord +{ + PathObject* object; + + constexpr ObjectPathCoord() noexcept; + constexpr ObjectPathCoord(PathObject* object) noexcept; + constexpr ObjectPathCoord(PathObject* object, const PathCoord& coord) noexcept; + constexpr ObjectPathCoord(PathObject* object, const PathCoord&& coord) noexcept; + constexpr ObjectPathCoord(const ObjectPathCoord&) noexcept = default; + constexpr ObjectPathCoord(ObjectPathCoord&&) noexcept = default; + ObjectPathCoord& operator=(const ObjectPathCoord&) noexcept = default; + ObjectPathCoord& operator=(ObjectPathCoord&&) noexcept = default; + + /** + * This constructor sets the PathCoord members according to the given coordinate index. + */ + ObjectPathCoord(PathObject* object, MapCoordVector::size_type index); + + /** + * Returns true iff the object is not null. + */ + constexpr operator bool() const; + + /** + * Sets this PathCoord to the point on this path which is the closest to the + * given coordinate. + * + * \return The squared distance of these points. + * + * \see PathObject::calcClosestPointOnPath + */ + float findClosestPointTo(MapCoordF map_coord); +}; + + + +//### Object inline code ### + +inline +Object::Type Object::getType() const +{ + return type; +} + +inline +const ObjectRenderables& Object::renderables() const +{ + return output; +} + +inline +const MapCoordVector& Object::getRawCoordinateVector() const +{ + return coords; +} + +inline +void Object::setOutputDirty(bool dirty) +{ + output_dirty = dirty; +} + +inline +bool Object::isOutputDirty() const +{ + return output_dirty; +} + +inline +const Symbol* Object::getSymbol() const +{ + return symbol; +} + +inline +const QRectF& Object::getExtent() const +{ + return extent; +} + +inline +void Object::setMap(Map* map) +{ + this->map = map; + setOutputDirty(); +} + +inline +Map* Object::getMap() const +{ + return map; +} + +inline +const Object::Tags& Object::tags() const +{ + return object_tags; +} + +inline +QString Object::getTag(const QString& key) const +{ + return object_tags.value(key); +} + + + +//### PathPart inline code ### + +inline +PathPart::PathPart( + const VirtualCoordVector& coords, + MapCoordVector::size_type start_index, + MapCoordVector::size_type end_index) + : VirtualPath(coords, start_index, end_index) + , path(nullptr) +{ + // nothing else +} + +inline +PathPart::PathPart( + PathObject& path, + MapCoordVector::size_type start_index, + MapCoordVector::size_type end_index ) + : VirtualPath(path.getRawCoordinateVector(), start_index, end_index) + , path(&path) +{ + // nothing else +} + +inline +PathPart& PathPart::operator=(const PathPart& rhs) +{ + Q_ASSERT(path = rhs.path); + VirtualPath::operator=(rhs); + return *this; +} + + + +//### PathObject inline code ### + +inline +MapCoordVector::size_type PathObject::getCoordinateCount() const +{ + return coords.size(); +} + +inline +const MapCoord& PathObject::getCoordinate(MapCoordVector::size_type pos) const +{ + Q_ASSERT(pos < coords.size()); + return coords[pos]; +} + +inline +MapCoord& PathObject::getCoordinate(MapCoordVector::size_type pos) +{ + Q_ASSERT(pos < coords.size()); + setOutputDirty(); + return coords[pos]; +} + +inline +const PathPartVector& PathObject::parts() const +{ + return path_parts; +} + +inline +PathPartVector& PathObject::parts() +{ + setOutputDirty(); + return path_parts; +} + +inline +float PathObject::getPatternRotation() const +{ + return pattern_rotation; +} + +inline +MapCoord PathObject::getPatternOrigin() const +{ + return pattern_origin; +} + + + +//### PointObject inline code ### + +inline +float PointObject::getRotation() const +{ + return rotation; +} + + + +//### ObjectPathCoord inline code ### + +inline +constexpr ObjectPathCoord::ObjectPathCoord() noexcept +: ObjectPathCoord { nullptr } +{ + // nothing else +} + + +inline +constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object) noexcept +: object { object } +{ + // nothing else +} + + +inline +constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object, const PathCoord& coord) noexcept +: PathCoord { coord } +, object { object } +{ + // nothing else +} + + +inline +constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object, const PathCoord&& coord) noexcept +: PathCoord { std::move(coord) } +, object { object } +{ + // nothing else +} + + +inline +ObjectPathCoord::ObjectPathCoord(PathObject* object, MapCoordVector::size_type index) +: PathCoord { object->findPathCoordForIndex(index) } +, object { object } +{ + // nothing else +} + + +inline +float ObjectPathCoord::findClosestPointTo(MapCoordF map_coord) +{ + float distance_sq; + object->calcClosestPointOnPath(map_coord, distance_sq, *this); + return distance_sq; + +} + + +inline +constexpr ObjectPathCoord::operator bool() const +{ + return bool { object }; +} + +} // namespace OpenOrienteering + + +#endif diff --git a/src/core/objects/object_mover.cpp b/src/core/objects/object_mover.cpp new file mode 100644 index 0000000..aca2618 --- /dev/null +++ b/src/core/objects/object_mover.cpp @@ -0,0 +1,304 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "object_mover.h" + +#include +#include +#include + +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/symbols/symbol.h" +#include "core/symbols/text_symbol.h" + + +namespace OpenOrienteering { + +ObjectMover::ObjectMover(Map* map, const MapCoordF& start_pos) + : start_position(start_pos), prev_drag_x(0), prev_drag_y(0), constraints_calculated(true) +{ + Q_UNUSED(map); +} + + + +void ObjectMover::setStartPos(const MapCoordF& start_pos) +{ + this->start_position = start_pos; +} + + +void ObjectMover::addObject(Object* object) +{ + objects.insert(object); +} + + +void ObjectMover::addPoint(PathObject* object, MapCoordVector::size_type point_index) +{ + Q_ASSERT(point_index < object->getCoordinateCount()); + + auto index_set = insertPointObject(object); + index_set->insert(point_index); + + constraints_calculated = false; +} + + +void ObjectMover::addLine(PathObject* object, MapCoordVector::size_type start_point_index) +{ + Q_ASSERT(start_point_index < object->getCoordinateCount()); + + auto index_set = insertPointObject(object); + index_set->insert(start_point_index); + index_set->insert(start_point_index + 1); + if (object->getCoordinate(start_point_index).isCurveStart()) + { + index_set->insert(start_point_index + 2); + index_set->insert(start_point_index + 3); + } + + constraints_calculated = false; +} + + +void ObjectMover::addTextHandle(TextObject* text, MapCoordVector::size_type handle) +{ + text_handles.insert({text, handle}); +} + + +void ObjectMover::move(const MapCoordF& cursor_pos, bool move_opposite_handles, qint32* out_dx, qint32* out_dy) +{ + auto delta_x = qRound(1000 * (cursor_pos.x() - start_position.x())) - prev_drag_x; + auto delta_y = qRound(1000 * (cursor_pos.y() - start_position.y())) - prev_drag_y; + if (out_dx) + *out_dx = delta_x; + if (out_dy) + *out_dy = delta_y; + + move(delta_x, delta_y, move_opposite_handles); + + prev_drag_x += delta_x; + prev_drag_y += delta_y; +} + + +void ObjectMover::move(qint32 dx, qint32 dy, bool move_opposite_handles) +{ + calculateConstraints(); + + // Move objects + for (auto object : objects) + object->move(dx, dy); + + // Move points + for (auto& item : points) + { + PathObject* path = item.first; + for (auto index : item.second) + { + auto coord = path->getCoordinate(index); + coord.setNativeX(coord.nativeX() + dx); + coord.setNativeY(coord.nativeY() + dy); + path->setCoordinate(index, coord); + } + } + + // Apply handle constraints + if (move_opposite_handles) + { + for (auto& constraint : handle_constraints) + { + MapCoord anchor_point = constraint.object->getCoordinate(constraint.curve_anchor_index); + MapCoordF to_hover_point = MapCoordF(constraint.object->getCoordinate(constraint.moved_handle_index) - anchor_point); + to_hover_point.normalize(); + + MapCoord control = constraint.object->getCoordinate(constraint.opposite_handle_index); + control.setX(anchor_point.x() - constraint.opposite_handle_dist * to_hover_point.x()); + control.setY(anchor_point.y() - constraint.opposite_handle_dist * to_hover_point.y()); + constraint.object->setCoordinate(constraint.opposite_handle_index, control); + } + } + + // Move box text object handles + for (auto& handle : text_handles) + { + TextObject* text_object = handle.first; + const TextSymbol* text_symbol = text_object->getSymbol()->asText(); + + QTransform transform; + transform.rotate(qRadiansToDegrees(text_object->getRotation())); + QPointF delta_point = transform.map(QPointF(dx, dy)); + + const auto move_point = handle.second; + int x_sign = (move_point <= 1) ? 1 : -1; + int y_sign = (move_point >= 1 && move_point <= 2) ? 1 : -1; + + double new_box_width = qMax(text_symbol->getFontSize() / 2, text_object->getBoxWidth() + 0.001 * x_sign * delta_point.x()); + double new_box_height = qMax(text_symbol->getFontSize() / 2, text_object->getBoxHeight() + 0.001 * y_sign * delta_point.y()); + + auto anchor = MapCoord { text_object->getAnchorCoordF() }; + text_object->move(dx / 2, dy / 2); + text_object->setBox(anchor.nativeX(), anchor.nativeY(), new_box_width, new_box_height); + } +} + + +ObjectMover::CoordIndexSet* ObjectMover::insertPointObject(PathObject* object) +{ + return &points.insert({object, CoordIndexSet()}).first->second; +} + + +void ObjectMover::calculateConstraints() +{ + if (constraints_calculated) + return; + + handle_constraints.clear(); + + // Remove all objects in the object list from the point list + for (auto object : objects) + { + switch (object->getType()) + { + case Object::Path: + points.erase(object->asPath()); + break; + + case Object::Text: + text_handles.erase(object->asText()); + break; + + default: + ; // nothing + } + } + + // Points + for (auto& item : points) + { + PathObject* path = item.first; + auto& point_set = item.second; + + // If end points of closed paths are contained in the move set, + // change them to the corresponding start points + // (as these trigger moving the end points automatically and are better to handle: + // they are set as curve start points if a curve starts there, in contrast to the end points) + for (const auto& part : path->parts()) + { + if (part.isClosed() && point_set.find(part.last_index) != point_set.end()) + { + point_set.erase(part.last_index); + point_set.insert(part.first_index); + } + } + + // Expand set of moved points: + // If curve points are moved, their handles must be moved, too. + std::vector handles; + for (auto index : point_set) + { + if (path->isCurveHandle(index)) + { + handles.push_back(index); + } + else + { + // If a curve starts here, add first handle + if (path->getCoordinate(index).isCurveStart()) + { + Q_ASSERT(index + 1 < path->getCoordinateCount()); + handles.push_back(index + 1); + } + + // If a curve ends here, add last handle + auto& part = *path->findPartForIndex(index); + if (index == part.first_index && part.isClosed()) + { + index = part.last_index; + } + if (index > part.first_index && path->getCoordinate(part.prevCoordIndex(index)).isCurveStart()) + { + handles.push_back(index - 1); + } + } + } + + // Add the handles to the list of points. + // Determine opposite handle constraints. + for (auto index : handles) + { + Q_ASSERT(path->isCurveHandle(index)); + auto& part = *path->findPartForIndex(index); + auto end_index = part.last_index; + + point_set.insert(index); + + if (index == part.prevCoordIndex(index) + 1) + { + // First handle of a curve + auto curve_anchor_index = index - 1; + if (part.isClosed() && curve_anchor_index == part.first_index) + curve_anchor_index = end_index; + + if (curve_anchor_index != part.first_index && + path->getCoordinate(part.prevCoordIndex(curve_anchor_index)).isCurveStart()) + { + OppositeHandleConstraint constraint; + constraint.object = path; + constraint.moved_handle_index = index; + constraint.curve_anchor_index = index - 1; + constraint.opposite_handle_index = curve_anchor_index - 1; + constraint.opposite_handle_original_position = path->getCoordinate(constraint.opposite_handle_index); + constraint.opposite_handle_dist = constraint.opposite_handle_original_position.distanceTo(path->getCoordinate(constraint.curve_anchor_index)); + handle_constraints.push_back(constraint); + } + } + else + { + // Second handle of a curve + auto curve_anchor_index = index + 1; + if (part.isClosed() && curve_anchor_index == end_index) + curve_anchor_index = part.first_index; + + if (curve_anchor_index != end_index && + path->getCoordinate(curve_anchor_index).isCurveStart()) + { + OppositeHandleConstraint constraint; + constraint.object = path; + constraint.moved_handle_index = index; + constraint.curve_anchor_index = curve_anchor_index; + constraint.opposite_handle_index = curve_anchor_index + 1; + constraint.opposite_handle_original_position = path->getCoordinate(constraint.opposite_handle_index); + constraint.opposite_handle_dist = constraint.opposite_handle_original_position.distanceTo(path->getCoordinate(constraint.curve_anchor_index)); + handle_constraints.push_back(constraint); + } + } + } + } + + constraints_calculated = true; +} + + +} // namespace OpenOrienteering diff --git a/src/core/objects/object_mover.h b/src/core/objects/object_mover.h new file mode 100644 index 0000000..765d767 --- /dev/null +++ b/src/core/objects/object_mover.h @@ -0,0 +1,116 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2015-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OBJECT_MOVER_H +#define OPENORIENTEERING_OBJECT_MOVER_H + +#include +#include +#include +#include + +#include + +#include "core/map_coord.h" + +namespace OpenOrienteering { + +class Map; +class Object; +class PathObject; +class TextObject; + +using SelectionInfoVector = std::vector>; + + +/** + * Implements the logic to move sets of objects and / or object points for edit tools. + */ +class ObjectMover +{ +public: + /** Creates a mover for the map with the given cursor start position. */ + ObjectMover(Map* map, const MapCoordF& start_pos); + + /** Sets the start position. */ + void setStartPos(const MapCoordF& start_pos); + + /** Adds an object to the set of elements to move. */ + void addObject(Object* object); + + /** Adds a point to the set of elements to move. */ + void addPoint(PathObject* object, MapCoordVector::size_type point_index); + + /** Adds a line to the set of elements to move. */ + void addLine(PathObject* object, MapCoordVector::size_type start_point_index); + + /** Adds a text handle to the set of elements to move. */ + void addTextHandle(TextObject* text, MapCoordVector::size_type handle); + + /** + * Moves the elements. + * @param move_opposite_handles If false, opposite handles are reset to their original position. + * @param out_dx returns the move along the x coordinate in map units + * @param out_dy returns the move along the y coordinate in map units + */ + void move(const MapCoordF& cursor_pos, bool move_opposite_handles, qint32* out_dx = nullptr, qint32* out_dy = nullptr); + + /** Overload of move() taking delta values. */ + void move(qint32 dx, qint32 dy, bool move_opposite_handles); + +private: + using ObjectSet = std::unordered_set; + using CoordIndexSet = std::unordered_set; + + CoordIndexSet* insertPointObject(PathObject* object); + void calculateConstraints(); + + // Basic information + MapCoordF start_position; + qint32 prev_drag_x; + qint32 prev_drag_y; + ObjectSet objects; + std::unordered_map points; + std::unordered_map text_handles; + + /** Constraints calculated from the basic information */ + struct OppositeHandleConstraint + { + /** Object to which the constraint applies */ + PathObject* object; + /** Index of moved handle */ + MapCoordVector::size_type moved_handle_index; + /** Index of opposite handle */ + MapCoordVector::size_type opposite_handle_index; + /** Index of center point in the middle of the handles */ + MapCoordVector::size_type curve_anchor_index; + /** Distance of opposite handle to center point */ + qreal opposite_handle_dist; + /** Original position of the opposite handle */ + MapCoord opposite_handle_original_position; + }; + std::vector handle_constraints; + bool constraints_calculated; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/objects/object_operations.h b/src/core/objects/object_operations.h new file mode 100644 index 0000000..683ac84 --- /dev/null +++ b/src/core/objects/object_operations.h @@ -0,0 +1,131 @@ +/* + * Copyright 2012, 2013 Thomas Schoeps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_OBJECT_OPERATIONS_H +#define OPENORIENTEERING_OBJECT_OPERATIONS_H + +#include "core/map.h" +#include "core/objects/object.h" +#include "core/symbols/symbol.h" + +namespace OpenOrienteering { + +/** + * Object conditions and processors, + * see methods Map::applyOnAllObjects() and MapPart::applyOnAllObjects() + */ +namespace ObjectOp +{ + // Conditions + + /** Returns true for objects with the given symbol. */ + struct HasSymbol + { + const Symbol* symbol; + + bool operator()(const Object* object) const noexcept + { + return object->getSymbol() == symbol; + } + }; + + /** Returns true for objects with the given symbol type. */ + struct HasSymbolType + { + Symbol::Type type; + + bool operator()(const Object* object) const noexcept + { + return object->getSymbol()->getType() == type; + } + }; + + /** Returns true for objects where the symbol type contains the given type. */ + struct ContainsSymbolType + { + Symbol::Type type; + + bool operator()(const Object* object) const noexcept + { + return object->getSymbol()->getContainedTypes() & type; + } + }; + + + // Operations + + /** Scales objects by the given factor. */ + struct Scale + { + double factor; + MapCoordF center; + + void operator()(Object* object) const + { + object->scale(center, factor); + object->update(); + } + }; + + /** Rotates objects by the given angle (in radians). */ + struct Rotate + { + double angle; + MapCoordF center; + + void operator()(Object* object) const + { + object->rotateAround(center, angle); + object->update(); + } + }; + + /** + * Changes the objects' symbols. + * NOTE: Make sure to apply this to correctly fitting objects only! + */ + struct ChangeSymbol + { + const Symbol* new_symbol; + + void operator()(Object* object, MapPart* part, int object_index) const + { + if (!object->setSymbol(new_symbol, false)) + part->deleteObject(object_index, false); + else + object->update(); + } + }; + + /** Delete objects. */ + struct Delete + { + void operator()(const Object*, MapPart* part, int object_index) const + { + part->deleteObject(object_index, false); + } + }; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/objects/object_query.cpp b/src/core/objects/object_query.cpp new file mode 100644 index 0000000..431f3d5 --- /dev/null +++ b/src/core/objects/object_query.cpp @@ -0,0 +1,780 @@ +/* + * Copyright 2016 Mitchell Krome + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "object_query.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/symbols/symbol.h" + + +// ### Local utilites ### + +namespace { + +static QChar special_chars[9] = { + QLatin1Char('"'), + QLatin1Char(' '), + QLatin1Char('\t'), + QLatin1Char('('), + QLatin1Char(')'), + QLatin1Char('='), + QLatin1Char('!'), + QLatin1Char('~'), + QLatin1Char('\\') +}; + +QString toEscaped(QString string) +{ + for (int i = 0; i < string.length(); ++i) + { + const auto c = string.at(i); + if (c == QLatin1Char('\\') || c == QLatin1Char('"')) + { + string.insert(i, QLatin1Char('\\')); + ++i; + } + } + return string; +} + +QString fromEscaped(QString string) +{ + for (int i = 0; i < string.length(); ++i) + { + const auto c = string.at(i); + if (c == QLatin1Char('\\')) + { + string.remove(i, 1); + ++i; + } + } + return string; +} + +QString keyToString(const QString &key) +{ + using std::begin; + using std::end; + if (std::find_first_of(begin(key), end(key), begin(special_chars), end(special_chars)) == end(key) + && key != QLatin1String("AND") + && key != QLatin1String("OR") + && key != QLatin1String("SYMBOL")) + return key; + else + return QLatin1Char('"') + toEscaped(key) + QLatin1Char('"'); +} + +} + + + +namespace OpenOrienteering { + +// ### ObjectQuery::LogicalOperands ### + +ObjectQuery::LogicalOperands::LogicalOperands(const ObjectQuery::LogicalOperands& proto) +: first(proto.first ? std::make_unique(*proto.first) : std::make_unique()) +, second(proto.second ? std::make_unique(*proto.second) : std::make_unique()) +{ + // nothing else +} + + +ObjectQuery::LogicalOperands::~LogicalOperands() = default; + + +ObjectQuery::LogicalOperands& ObjectQuery::LogicalOperands::operator=(const ObjectQuery::LogicalOperands& proto) +{ + if (&proto == this) + return *this; + + if (proto.first) + first = std::make_unique(*proto.first); + if (proto.second) + second = std::make_unique(*proto.second); + return *this; +} + + + +// ### ObjectQuery::StringOperands ### + +ObjectQuery::StringOperands::~StringOperands() = default; + + + +// ### ObjectQuery ### + +ObjectQuery::ObjectQuery() noexcept +: op { ObjectQuery::OperatorInvalid } +{ + // nothing else +} + + +ObjectQuery::ObjectQuery(const ObjectQuery& query) +: op { query.op } +{ + if (op == ObjectQuery::OperatorInvalid) + { + ; // nothing + } + else if (op < 16) + { + new (&subqueries) LogicalOperands(query.subqueries); + } + else if (op < 32) + { + new (&tags) StringOperands(query.tags); + } + else if (op == ObjectQuery::OperatorSymbol) + { + symbol = query.symbol; + } +} + + +ObjectQuery::ObjectQuery(ObjectQuery&& proto) noexcept +: op { ObjectQuery::OperatorInvalid } +{ + consume(std::move(proto)); +} + + +ObjectQuery& ObjectQuery::operator=(const ObjectQuery& proto) noexcept +{ + if (&proto == this) + return *this; + + reset(); + consume(ObjectQuery{proto}); + return *this; +} + + +ObjectQuery& ObjectQuery::operator=(ObjectQuery&& proto) noexcept +{ + if (&proto == this) + return *this; + + reset(); + consume(std::move(proto)); + return *this; +} + + +ObjectQuery::~ObjectQuery() +{ + reset(); +} + + +ObjectQuery::ObjectQuery(const QString& key, ObjectQuery::Operator op, const QString& value) +: op { op } +, tags { key, value } +{ + // Can't have an empty key (but can have empty value) + // Must be a key/value operator + Q_ASSERT(op >= 16); + Q_ASSERT(op <= 18); + if (op < 16 || op > 18 + || key.length() == 0) + { + reset(); + } +} + + +ObjectQuery::ObjectQuery(ObjectQuery::Operator op, const QString& value) +: op { op } +, tags { {}, value } +{ + // Can't have an empty key (but can have empty value) + // Must be a key/value operator + Q_ASSERT(op >= 19); + Q_ASSERT(op <= 20); + if (op < 19 || op > 20 + || value.length() == 0) + { + reset(); + } +} + + +ObjectQuery::ObjectQuery(const ObjectQuery& first, ObjectQuery::Operator op, const ObjectQuery& second) +: op { op } +, subqueries {} +{ + // Both sub-queries must be valid. + // Must be a logical operator + Q_ASSERT(op >= 1); + Q_ASSERT(op <= 2); + if (op < 1 || op > 2 + || !first || !second) + { + reset(); + } + else + { + subqueries.first = std::make_unique(first); + subqueries.second = std::make_unique(second); + } +} + + +ObjectQuery::ObjectQuery(ObjectQuery&& first, ObjectQuery::Operator op, ObjectQuery&& second) noexcept +: op { op } +, subqueries {} +{ + // Both sub-queries must be valid. + // Must be a logical operator + Q_ASSERT(op >= 1); + Q_ASSERT(op <= 2); + if (op < 1 || op > 2 + || !first || !second) + { + reset(); + } + else + { + subqueries.first = std::make_unique(std::move(first)); + subqueries.second = std::make_unique(std::move(second)); + } +} + + +ObjectQuery::ObjectQuery(const Symbol* symbol) noexcept +: op { ObjectQuery::OperatorSymbol } +, symbol { symbol } +{ + // nothing else +} + + + +// static +QString ObjectQuery::labelFor(ObjectQuery::Operator op) +{ + switch (op) + { + case OperatorIs: + //: Very short label + return tr("is"); + case OperatorIsNot: + //: Very short label + return tr("is not"); + case OperatorContains: + //: Very short label + return tr("contains"); + case OperatorSearch: + //: Very short label + return tr("Search"); + case OperatorObjectText: + //: Very short label + return tr("Text"); + + case OperatorAnd: + //: Very short label + return tr("and"); + case OperatorOr: + //: Very short label + return tr("or"); + + case OperatorSymbol: + //: Very short label + return tr("Symbol"); + + case OperatorInvalid: + //: Very short label + return tr("invalid"); + } + + Q_UNREACHABLE(); +} + + + +bool ObjectQuery::operator()(const Object* object) const +{ + const auto& object_tags = object->tags(); + + switch(op) + { + case OperatorIs: + return object_tags.contains(tags.key) && object_tags.value(tags.key) == tags.value; + case OperatorIsNot: + // If the object does have the tag, not is true + return !object_tags.contains(tags.key) || object_tags.value(tags.key) != tags.value; + case OperatorContains: + return object_tags.contains(tags.key) && object_tags.value(tags.key).contains(tags.value); + case OperatorSearch: + if (object->getSymbol() && object->getSymbol()->getName().contains(tags.value, Qt::CaseInsensitive)) + return true; + for (auto it = object_tags.begin(), last = object_tags.end(); it != last; ++it) + { + if (it.key().contains(tags.value, Qt::CaseInsensitive) + || it.value().contains(tags.value, Qt::CaseInsensitive)) + return true; + } + return false; + case OperatorObjectText: + if (object->getType() == Object::Text) + return static_cast(object)->getText().contains(tags.value, Qt::CaseInsensitive); + return false; + + case OperatorAnd: + return (*subqueries.first)(object) && (*subqueries.second)(object); + case OperatorOr: + return (*subqueries.first)(object) || (*subqueries.second)(object); + + case OperatorSymbol: + return object->getSymbol() == symbol; + + case OperatorInvalid: + return false; + } + + Q_UNREACHABLE(); +} + + + +const ObjectQuery::LogicalOperands* ObjectQuery::logicalOperands() const +{ + const LogicalOperands* result = nullptr; + if (op >= 1 && op <= 2) + { + result = &subqueries; + } + else + { + Q_ASSERT(op >= 1); + Q_ASSERT(op <= 2); + } + return result; +} + + +const ObjectQuery::StringOperands* ObjectQuery::tagOperands() const +{ + const StringOperands* result = nullptr; + if (op >= 16 && op <= 20) + { + result = &tags; + } + else + { + Q_ASSERT(op >= 16); + Q_ASSERT(op <= 20); + } + return result; +} + + +const Symbol* ObjectQuery::symbolOperand() const +{ + const Symbol* result = nullptr; + if (op == ObjectQuery::OperatorSymbol) + { + result = symbol; + } + else + { + Q_ASSERT(op == ObjectQuery::OperatorSymbol); + } + return result; +} + + + +QString ObjectQuery::toString() const +{ + auto ret = QString{}; + switch (op) + { + case OperatorIs: + ret = keyToString(tags.key) + QLatin1String(" = \"") + toEscaped(tags.value) + QLatin1Char('"'); + break; + case OperatorIsNot: + ret = keyToString(tags.key) + QLatin1String(" != \"") + toEscaped(tags.value) + QLatin1Char('"'); + break; + case OperatorContains: + ret = keyToString(tags.key) + QLatin1String(" ~= \"") + toEscaped(tags.value) + QLatin1Char('"'); + break; + case OperatorSearch: + case OperatorObjectText: + ret = QLatin1Char('"') + toEscaped(tags.value) + QLatin1Char('"'); + break; + + case OperatorAnd: + if (subqueries.first->getOperator() == OperatorOr) + ret = QLatin1Char('(') + subqueries.first->toString() + QLatin1Char(')'); + else + ret = subqueries.first->toString(); + ret += QLatin1String(" AND "); + if (subqueries.second->getOperator() == OperatorOr) + ret += QLatin1Char('(') + subqueries.second->toString() + QLatin1Char(')'); + else + ret += subqueries.second->toString(); + break; + case OperatorOr: + ret = subqueries.first->toString() + QLatin1String(" OR ") + subqueries.second->toString(); + break; + + case OperatorSymbol: + ret = QLatin1String("SYMBOL \"") + symbol->getNumberAsString() + QLatin1Char('\"'); + break; + + case OperatorInvalid: + // Default empty string is sufficient + break; + } + + return ret; +} + + + +void ObjectQuery::reset() +{ + if (op == ObjectQuery::OperatorInvalid) + { + ; // nothing + } + else if (op < 16) + { + subqueries.~LogicalOperands(); + op = ObjectQuery::OperatorInvalid; + } + else if (op < 32) + { + tags.~StringOperands(); + op = ObjectQuery::OperatorInvalid; + } + else if (op == ObjectQuery::OperatorSymbol) + { + op = ObjectQuery::OperatorInvalid; + } +} + + +void ObjectQuery::consume(ObjectQuery&& other) +{ + Q_ASSERT(op == ObjectQuery::OperatorInvalid); + op = other.op; + if (op == ObjectQuery::OperatorInvalid) + { + ; // nothing else + } + else if (op < 16) + { + new (&subqueries) ObjectQuery::LogicalOperands(std::move(other.subqueries)); + other.subqueries.~LogicalOperands(); + } + else if (op < 32) + { + new (&tags) ObjectQuery::StringOperands(std::move(other.tags)); + other.tags.~StringOperands(); + } + else if (op == ObjectQuery::OperatorSymbol) + { + symbol = other.symbol; + } + other.op = ObjectQuery::OperatorInvalid; +} + + + +bool operator==(const ObjectQuery::StringOperands& lhs, const ObjectQuery::StringOperands& rhs) +{ + return lhs.key == rhs.key && lhs.value == rhs.value; +} + +bool operator==(const ObjectQuery& lhs, const ObjectQuery& rhs) +{ + if (lhs.op != rhs.op) + return false; + + switch(lhs.op) + { + case ObjectQuery::OperatorIs: + case ObjectQuery::OperatorIsNot: + case ObjectQuery::OperatorContains: + case ObjectQuery::OperatorSearch: + case ObjectQuery::OperatorObjectText: + return lhs.tags == rhs.tags; + + case ObjectQuery::OperatorAnd: + case ObjectQuery::OperatorOr: + return *lhs.subqueries.first == *rhs.subqueries.first + && *lhs.subqueries.second == *rhs.subqueries.second; + + case ObjectQuery::OperatorSymbol: + return lhs.symbol == rhs.symbol; + + case ObjectQuery::OperatorInvalid: + return false; + } + + Q_UNREACHABLE(); +} + + +ObjectQuery ObjectQueryParser::parse(const QString& text) +{ + auto result = ObjectQuery{}; + + input = {&text, 0, text.size()}; + pos = 0; + auto paren_depth = 0; + QVarLengthArray nested_expressions; + auto* current = &result; + + getToken(); + while (token != TokenNothing) + { + if ((token == TokenWord || token == TokenString) && !*current) + { + auto key = tokenAsString(); + getToken(); + if (token == TokenTextOperator) + { + auto op = token_text; + getToken(); + if (token == TokenWord || token == TokenString) + { + auto value = tokenAsString(); + switch (op.at(0).toLatin1()) + { + case '=': + *current = { key, ObjectQuery::OperatorIs, value }; + break; + case '!': + *current = { key, ObjectQuery::OperatorIsNot, value }; + break; + case '~': + *current = { key, ObjectQuery::OperatorContains, value }; + break; + default: + Q_UNREACHABLE(); + } + getToken(); + } + else + { + op = {}; + break; + } + } + else + { + *current = { ObjectQuery::OperatorSearch, key }; + } + } + else if (token == TokenAnd && *current) + { + if (!nested_expressions.isEmpty() && nested_expressions.back()->getOperator() == ObjectQuery::OperatorAnd) + { + nested_expressions.pop_back(); // replaced by current query + } + // Cannot construct logical ObjectQuery with invalid operand, but can assign later... + auto tmp = ObjectQuery{std::move(*current), ObjectQuery::OperatorAnd, {ObjectQuery::OperatorSearch, text}}; + *current = std::move(tmp); + nested_expressions.push_back(current); + + current = current->logicalOperands()->second.get(); + *current = {}; + + getToken(); + } + else if (token == TokenOr && *current) + { + if (!nested_expressions.isEmpty()) + { + current = nested_expressions.back(); + nested_expressions.pop_back(); + } + // Cannot construct logical ObjectQuery with invalid operand, but can assign later... + auto query = ObjectQuery{std::move(*current), ObjectQuery::OperatorOr, {ObjectQuery::OperatorSearch, text}}; + *current = std::move(query); + nested_expressions.push_back(current); + + current = current->logicalOperands()->second.get(); + *current = {}; + + getToken(); + } + else if (token == TokenLeftParen && !*current) + { + ++paren_depth; + nested_expressions.push_back(current); + getToken(); + } + else if (token == TokenRightParen && *current + && !nested_expressions.isEmpty() + && paren_depth > 0) + { + --paren_depth; + current = nested_expressions.back(); + nested_expressions.pop_back(); + getToken(); + } + else + { + // Invalid input + result = {}; + break; + } + } + + if (result && (!*current || paren_depth > 0)) + { + result = {}; + } + + return result; +} + + +int ObjectQueryParser::errorPos() const +{ + return token_start; +} + + +void ObjectQueryParser::getToken() +{ + token = TokenNothing; + QChar current; + while (true) + { + if (pos >= input.length()) + return; + current = input.at(pos); + if (current != QLatin1Char(' ') && current != QLatin1Char('\t')) + break; + ++pos; + } + + token = TokenUnknown; + token_start = pos; + if (current == QLatin1Char('"')) + { + for (++pos; pos < input.length(); ++pos) + { + current = input.at(pos); + if (current == QLatin1Char('"')) + { + token = TokenString; + token_text = input.mid(token_start + 1, pos - token_start - 1); + ++pos; + break; + } + else if (current == QLatin1Char('\\')) + { + if (pos < input.length()) + ++pos; + } + } + } + else if (current == QLatin1Char('(')) + { + token = TokenLeftParen; + token_text = input.mid(token_start, 1); + ++pos; + } + else if (current == QLatin1Char(')')) + { + token = TokenRightParen; + token_text = input.mid(token_start, 1); + ++pos; + } + else if (current == QLatin1Char('=')) + { + token = TokenTextOperator; + token_text = input.mid(token_start, 1); + ++pos; + } + else if ((current == QLatin1Char('!') || current == QLatin1Char('~')) + && pos < input.length() + && input.at(pos+1) == QLatin1Char('=')) + { + token = TokenTextOperator; + token_text = input.mid(token_start, 2); + pos += 2; + } + else + { + for (++pos; pos < input.length(); ++pos) + { + current = input.at(pos); + if (current == QLatin1Char('"') + || current == QLatin1Char(' ') + || current == QLatin1Char('\t') + || current == QLatin1Char('(') + || current == QLatin1Char(')') + || current == QLatin1Char('=')) + { + break; + } + else if ((current == QLatin1Char('!') || current == QLatin1Char('~')) + && pos < input.length() + && input.at(pos+1) == QLatin1Char('=')) + { + break; + } + } + token_text = input.mid(token_start, pos - token_start); + if (token_text == QLatin1String("OR")) + token = TokenOr; + else if (token_text == QLatin1String("AND")) + token = TokenAnd; + else + token = TokenWord; + } +} + + +QString ObjectQueryParser::tokenAsString() const +{ + QString string = token_text.toString(); + if (token == TokenString) + string = fromEscaped(string); + return string; +} + + +} // namespace OpenOrienteering diff --git a/src/core/objects/object_query.h b/src/core/objects/object_query.h new file mode 100644 index 0000000..04e529e --- /dev/null +++ b/src/core/objects/object_query.h @@ -0,0 +1,285 @@ +/* + * Copyright 2016 Mitchell Krome + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OBJECT_QUERY_H +#define OPENORIENTEERING_OBJECT_QUERY_H + +#include + +#include +#include +#include +#include + +namespace OpenOrienteering { + +class Object; +class Symbol; + + +/** + * Utility to match objects based on tag values. + * + * This class can be used with value semantics. It can be move-constructed and + * move-assigned in O(1). Normal copy-construction and assignment may involve + * expensive copying of the expression tree. + * + * OperatorAnd and OperatorOr evaluate the left argument first. When constructing + * complex queries, left sub-query chains should be kept short in order to + * benefit from short circuiting the evaluation of these logical operators after + * evaluation of the left argument. + */ +class ObjectQuery +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::ObjectQuery) + +public: + enum Operator { + // Operators 1 .. 15 operate on other queries + OperatorAnd = 1, ///< And-chains two object queries + OperatorOr = 2, ///< Or-chains two object queries + + // Operators 16 .. 18 operate on object tags and other strings + OperatorIs = 16, ///< Tests an existing tag for equality with the given value (case-sensitive) + OperatorIsNot = 17, ///< Tests an existing tag for inequality with the given value (case-sensitive) + OperatorContains = 18, ///< Tests an existing tag for containing the given value (case-sensitive) + OperatorSearch = 19, ///< Tests if the symbol name, a tag key or a tag value contains the given value (case-insensitive) + OperatorObjectText = 20, ///< Text object content (case-insensitive) + + // More operators, 32 .. + OperatorSymbol = 32, ///< Test the symbol for equality. + + OperatorInvalid = 0 ///< Marks an invalid query + }; + + // Parameters for logical operations + struct LogicalOperands + { + std::unique_ptr first; + std::unique_ptr second; + + LogicalOperands() = default; + explicit LogicalOperands(const LogicalOperands& proto); // maybe expensive copying + LogicalOperands(LogicalOperands&&) = default; + ~LogicalOperands(); + LogicalOperands& operator=(const LogicalOperands& proto); + LogicalOperands& operator=(LogicalOperands&&) = default; + }; + + // Parameters for operations on tags. + struct StringOperands + { + QString key; + QString value; + + ~StringOperands(); + }; + + ObjectQuery() noexcept; + explicit ObjectQuery(const ObjectQuery& query); // maybe expensive copying + ObjectQuery(ObjectQuery&& proto) noexcept; + ObjectQuery& operator=(const ObjectQuery& proto) noexcept; + ObjectQuery& operator=(ObjectQuery&& proto) noexcept; + + ~ObjectQuery(); + + /** + * Returns true if the query is valid. + */ + operator bool() const noexcept { return op != OperatorInvalid; } + + /** + * Constructs a query for a key and value. + */ + ObjectQuery(const QString& key, Operator op, const QString& value); + + /** + * Constructs a query for a value. + * + * Valid for OperatorSearch. + */ + ObjectQuery(Operator op, const QString& value); + + /** + * Constructs a query which connects two sub-queries. + * + * The sub-queries are copied. + */ + ObjectQuery(const ObjectQuery& first, Operator op, const ObjectQuery& second); + + /** + * Constructs a query which connects two sub-queries. + */ + ObjectQuery(ObjectQuery&& first, Operator op, ObjectQuery&& second) noexcept; + + /** + * Constructs a query for a particular symbol. + */ + ObjectQuery(const Symbol* symbol) noexcept; + + + /** + * Returns the underlying operator. + */ + Operator getOperator() const noexcept { return op; } + + + /** + * Returns a short label for the operator which can be used in the user interface. + */ + static QString labelFor(Operator op); + + + /** + * Evaluates this query on the given object and returns whether it matches. + */ + bool operator()(const Object* object) const; + + + /** + * Returns the operands of logical query operations. + */ + const LogicalOperands* logicalOperands() const; + + /** + * Returns the operands of logical query operations. + */ + const StringOperands* tagOperands() const; + + /** + * Returns the operand of symbol operations. + */ + const Symbol* symbolOperand() const; + + + /** + * Pretty print the query. + * + * The output is meant to be formal language, for possible parsing. + */ + QString toString() const; + + + friend bool operator==(const ObjectQuery& lhs, const ObjectQuery& rhs); + +private: + /** + * Resets the query to the OperatorInvalid state. + */ + void reset(); + + /** + * Moves the other query's data to this one which must be in OperatorInvalid state. + * + * Leaves the other object in OperatorInvalid state. + * In effect, this is a move assignment with a strong precondition. + */ + void consume(ObjectQuery&& other); + + using SymbolOperand = const Symbol*; + + Operator op; + + union + { + LogicalOperands subqueries; + StringOperands tags; + SymbolOperand symbol; + }; + +}; + +bool operator==(const ObjectQuery& lhs, const ObjectQuery& rhs); + +bool operator!=(const ObjectQuery& lhs, const ObjectQuery& rhs); + +bool operator==(const ObjectQuery::StringOperands& lhs, const ObjectQuery::StringOperands& rhs); + +bool operator!=(const ObjectQuery::StringOperands& lhs, const ObjectQuery::StringOperands& rhs); + + + +/** + * Utility to contruct object queries from text. + * + * The text shall be in the formal language generate by ObjectQuery::toString(). + */ +class ObjectQueryParser +{ +public: + /** + * Returns an ObjectQuery for the given text. + * + * The return query has ObjectQuery::OperatorInvalid in case of error. + */ + ObjectQuery parse(const QString& text); + + /** + * In case of error, returns the approximate position where parsing the + * text failed. + */ + int errorPos() const; + + enum TokenType + { + TokenUnknown = 0, + TokenNothing, + TokenString, + TokenWord, + TokenTextOperator, + TokenOr, + TokenAnd, + TokenLeftParen, + TokenRightParen, + }; + +private: + void getToken(); + + QString tokenAsString() const; + + QStringRef input; + QStringRef token_text; + TokenType token; + int token_start; + int pos; +}; + + + +inline +bool operator!=(const ObjectQuery& lhs, const ObjectQuery& rhs) +{ + return !(lhs==rhs); +} + +inline +bool operator!=(const ObjectQuery::StringOperands& lhs, const ObjectQuery::StringOperands& rhs) +{ + return !(lhs==rhs); +} + + +} // namespace OpenOrienteering + +Q_DECLARE_METATYPE(OpenOrienteering::ObjectQuery::Operator) + + +#endif diff --git a/src/core/objects/symbol_rule_set.cpp b/src/core/objects/symbol_rule_set.cpp new file mode 100644 index 0000000..4485fcd --- /dev/null +++ b/src/core/objects/symbol_rule_set.cpp @@ -0,0 +1,418 @@ +/* + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "symbol_rule_set.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/objects/object.h" +#include "core/symbols/symbol.h" +#include "undo/undo_manager.h" + + +namespace OpenOrienteering { + +// The SymbolRule member "query" may throw on copying. +Q_STATIC_ASSERT(std::is_nothrow_constructible::value); +Q_STATIC_ASSERT(std::is_nothrow_default_constructible::value); +Q_STATIC_ASSERT(std::is_copy_constructible::value); +Q_STATIC_ASSERT(std::is_nothrow_move_constructible::value); +Q_STATIC_ASSERT(std::is_nothrow_destructible::value); +Q_STATIC_ASSERT(std::is_copy_assignable::value); +Q_STATIC_ASSERT(std::is_nothrow_move_assignable::value); + +// Analogously for SymbolRuleSet +Q_STATIC_ASSERT(std::is_nothrow_constructible::value); +Q_STATIC_ASSERT(std::is_nothrow_default_constructible::value); +Q_STATIC_ASSERT(std::is_copy_constructible::value); +Q_STATIC_ASSERT(std::is_nothrow_move_constructible::value); +Q_STATIC_ASSERT(std::is_nothrow_destructible::value); +Q_STATIC_ASSERT(std::is_copy_assignable::value); +Q_STATIC_ASSERT(std::is_nothrow_move_assignable::value); + + +// static +SymbolRuleSet SymbolRuleSet::forOriginalSymbols(const Map& map) +{ + SymbolRuleSet list; + list.reserve(std::size_t(map.getNumSymbols())); + for (int i = 0; i < map.getNumSymbols(); ++i) + { + auto original = map.getSymbol(i); + list.push_back({{original}, nullptr, SymbolRule::NoAssignment}); + } + list.sortByQueryKeyAndValue(); + return list; +} + + +SymbolRuleSet SymbolRuleSet::squeezed() const +{ + SymbolRuleSet list; + list.reserve(this->size()); + std::copy_if(begin(), end(), std::back_inserter(list), [](auto item) { + return item.type != SymbolRule::NoAssignment; + }); + return list; +} + + + +void SymbolRuleSet::sortByQuerySymbol() +{ + auto last = std::remove_if(begin(), end(), [](auto item) { + return item.query.getOperator() != ObjectQuery::OperatorSymbol + || !item.query.symbolOperand(); + }); + std::sort(begin(), last, [](auto lhs, auto rhs) { + auto lhs_op = lhs.query.symbolOperand(); + auto rhs_op = rhs.query.symbolOperand(); + return (lhs_op->getNumberAsString() < rhs_op->getNumberAsString() + && lhs_op->getName() < rhs_op->getName()); + }); +} + + +void SymbolRuleSet::sortByQueryKeyAndValue() +{ + auto last = std::remove_if(begin(), end(), [](auto item) { + const auto op = item.query.getOperator(); + return (op != ObjectQuery::OperatorIs + && op != ObjectQuery::OperatorIsNot + && op != ObjectQuery::OperatorContains) + || !item.query.tagOperands(); + }); + std::sort(begin(), last, [](auto lhs, auto rhs) { + auto lhs_op = lhs.query.tagOperands(); + auto rhs_op = rhs.query.tagOperands(); + return (lhs_op->key < rhs_op->key) + && (lhs_op->value < rhs_op->value); + }); +} + + + +void SymbolRuleSet::matchQuerySymbolName(const Map& other_map) +{ + auto findMatchingSymbol = [&other_map](const Symbol* original)->const Symbol* + { + for (int k = 0; k < other_map.getNumSymbols(); ++k) + { + auto other_symbol = other_map.getSymbol(k); + if (original->getName() == other_symbol->getName() + && Symbol::areTypesCompatible(original->getType(), other_symbol->getType())) + { + return other_symbol; + } + } + return nullptr; + }; + + for (auto& item : *this) + { + if (item.query.getOperator() != ObjectQuery::OperatorSymbol) + continue; + + auto original = item.query.symbolOperand(); + if (!original) + continue; + + auto candidate = findMatchingSymbol(original); + if (!candidate) + { + candidate = findMatchingSymbol(original); + } + if (candidate != item.symbol) + { + if (candidate) + { + item.symbol = candidate; + item.type = SymbolRule::AutomaticAssignment; + } + else + { + item.type = SymbolRule::NoAssignment; + } + } + } +} + + +void SymbolRuleSet::matchQuerySymbolNumber(const Map& other_map) +{ + auto findMatchingSymbol = [&other_map](const Symbol* original, bool ignore_trailing_zeros)->const Symbol* + { + for (int k = 0; k < other_map.getNumSymbols(); ++k) + { + auto other_symbol = other_map.getSymbol(k); + if (original->numberEquals(other_symbol, ignore_trailing_zeros) + && Symbol::areTypesCompatible(original->getType(), other_symbol->getType())) + { + return other_symbol; + } + } + return nullptr; + }; + + for (auto& item : *this) + { + if (item.query.getOperator() != ObjectQuery::OperatorSymbol) + continue; + + auto original = item.query.symbolOperand(); + if (!original) + continue; + + auto candidate = findMatchingSymbol(original, false); + if (!candidate) + { + candidate = findMatchingSymbol(original, true); + } + if (candidate != item.symbol) + { + if (candidate) + { + item.symbol = candidate; + item.type = SymbolRule::AutomaticAssignment; + } + else + { + item.type = SymbolRule::NoAssignment; + } + } + } +} + + + +// static +SymbolRuleSet SymbolRuleSet::loadCrt(QTextStream& stream, const Map& replacement_map) +{ + auto list = SymbolRuleSet{}; + stream.setIntegerBase(10); // No autodectection; 001 is 1. + while (!stream.atEnd()) + { + QString replacement_key; + stream >> replacement_key; + if (stream.status() == QTextStream::ReadPastEnd) + { + stream.resetStatus(); + break; + } + auto pattern = stream.readLine().trimmed(); + if (stream.status() == QTextStream::Ok + && !replacement_key.startsWith(QLatin1Char{'#'})) + { + auto parsed_query = ObjectQueryParser().parse(pattern); + if (!parsed_query) + parsed_query = {ObjectQuery::OperatorSearch, pattern}; + list.push_back({std::move(parsed_query), nullptr, SymbolRule::NoAssignment}); + for (int k = 0; k < replacement_map.getNumSymbols(); ++k) + { + auto symbol = replacement_map.getSymbol(k); + if (symbol->getNumberAsString() == replacement_key) + { + list.back().symbol = symbol; + list.back().type = SymbolRule::DefinedAssignment; + break; + } + } + } + } + return list; +} + + +void SymbolRuleSet::writeCrt(QTextStream& stream) const +{ + for (const auto& item : *this) + { + if (item.type != SymbolRule::NoAssignment + && item.symbol) + { + const auto& query = item.query; + auto second_field = QString{}; + switch (query.getOperator()) + { + case ObjectQuery::OperatorSearch: + if (query.tagOperands()) + second_field = query.tagOperands()->value; + break; + + case ObjectQuery::OperatorSymbol: + if (query.symbolOperand()) + second_field = query.symbolOperand()->getNumberAsString(); + break; + + case ObjectQuery::OperatorIs: + if (query.tagOperands() + && query.tagOperands()->key == QLatin1String("Layer")) + { + second_field = query.tagOperands()->value; + break; + } + // fall through + case ObjectQuery::OperatorIsNot: + case ObjectQuery::OperatorContains: + if (query.tagOperands()) + second_field = query.tagOperands()->key + QLatin1Char(' ') + + query.labelFor(query.getOperator()) + QLatin1String(" \"") + + query.tagOperands()->value + QLatin1Char('"'); + break; + + default: + qDebug("Unsupported query in SymbolRuleSet::writeCrt"); + } + + if (!second_field.isEmpty()) + { + auto first_field = item.symbol->getNumberAsString(); + auto whitespace = QString(qMax(1, 10-first_field.length()), QLatin1Char{' '}); + stream << first_field << whitespace << second_field << endl; + } + } + } +} + + + +void SymbolRuleSet::operator()(Object* object) const +{ + for (const auto& item : *this) + { + if (item.symbol && item.query(object)) + { + object->setSymbol(item.symbol, false); + break; + } + } +} + + +void SymbolRuleSet::apply(Map& object_map, const Map& symbol_set, Options options) +{ + std::unordered_set old_symbols; + + // Import new symbols if needed + if (&object_map != &symbol_set) + { + if (!options.testFlag(KeepUnusedSymbols)) + { + for (int i = 0; i < object_map.getNumSymbols(); ++i) + old_symbols.insert(object_map.getSymbol(i)); + } + + auto symbol_filter = std::vector(std::size_t(symbol_set.getNumSymbols()), true); + if (!options.testFlag(ImportAllSymbols)) + { + // Import only symbols which are chosen as replacement symbols + auto item = symbol_filter.begin(); + for (int i = 0; i < symbol_set.getNumSymbols(); ++i) + { + auto symbol = symbol_set.getSymbol(i); + *item = std::any_of(begin(), end(), [symbol](auto item) { + return item.symbol == symbol; + }); + ++item; + } + } + + auto symbol_mapping = object_map.importMap(symbol_set, Map::MinimalSymbolImport, &symbol_filter, -1, false); + + auto preserve_state = options.testFlag(PreserveSymbolState); + for (auto& item : *this) + { + if (!symbol_mapping.contains(item.symbol)) + continue; // unused symbol + + auto replacement = symbol_mapping[item.symbol]; + if (item.query.getOperator() == ObjectQuery::OperatorSymbol + && preserve_state) + { + auto original = item.query.symbolOperand(); + Q_ASSERT(original); + replacement->setHidden(original->isHidden()); + replacement->setProtected(original->isProtected()); + } + item.symbol = replacement; + } + } + + // Change symbols for all objects + object_map.applyOnAllObjects(std::cref(*this)); + + // Delete unused old symbols + if (!old_symbols.empty()) + { + std::vector symbols_in_use; + object_map.determineSymbolsInUse(symbols_in_use); + + for (int i = object_map.getNumSymbols() - 1; i >= 0; --i) + { + auto symbol = object_map.getSymbol(i); + if (!symbols_in_use[std::size_t(i)] + && old_symbols.find(symbol) != old_symbols.end()) + { + object_map.deleteSymbol(i); + } + } + } + + // Delete unused colors + if (!options.testFlag(KeepUnusedColors)) + { + std::vector all_symbols; + all_symbols.assign(std::size_t(object_map.getNumSymbols()), true); + std::vector colors_in_use; + object_map.determineColorsInUse(all_symbols, colors_in_use); + if (colors_in_use.empty()) + colors_in_use.assign(std::size_t(object_map.getNumColors()), false); + + for (int i = object_map.getNumColors() - 1; i >= 0; --i) + { + if (!colors_in_use[std::size_t(i)]) + object_map.deleteColor(i); + } + } + + // Finish + object_map.updateAllObjects(); + object_map.setObjectsDirty(); + object_map.setSymbolsDirty(); + object_map.undoManager().clear(); +} + + +} // namespace OpenOrienteering diff --git a/src/core/objects/symbol_rule_set.h b/src/core/objects/symbol_rule_set.h new file mode 100644 index 0000000..0dfd075 --- /dev/null +++ b/src/core/objects/symbol_rule_set.h @@ -0,0 +1,202 @@ +/* + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_RULE_SET_H +#define OPENORIENTEERING_SYMBOL_RULE_SET_H + +#include + +#include + +#include "core/objects/object_query.h" + +class QTextStream; + +namespace OpenOrienteering { + +class Map; +class Object; +class Symbol; + + +/** + * This struct defines a single symbol assignment rule. + * + * Unless the type is NoAssignment, a rule specifies a new symbol for objects + * which match a particular query. + */ +struct SymbolRule +{ + // Intentionally no user-defined constructors, destructor, assignement. + // Default ones are fine. + + enum RuleType + { + NoAssignment = 0, + ManualAssignment, + AutomaticAssignment, + DefinedAssignment, + }; + + ObjectQuery query; + const Symbol* symbol; + RuleType type; + +}; + + +/** + * An utility for assigning symbols to objects based on queries. + */ +class SymbolRuleSet : public std::vector +{ +public: + // Intentionally no user-defined constructors, destructor, assignement. + // Default ones are fine. + + /** + * Creates a list of NoAssignment items for the map's symbols as originals. + * + * This function can be used to easily initialize variables of type + * SymbolRuleSet despite the absence of specialized constructors. + * Each item is initialized with an object query for a symbol from the map. + * + * The created list is sorted by formatted symbol number. + */ + static SymbolRuleSet forOriginalSymbols(const Map& map); + + /** + * Returns a copy which has all NoAssignment items removed. + */ + SymbolRuleSet squeezed() const; + + + /** + * Sorts the items by original symbol number and name if possible. + * + * The symbol number and name are only available for queries with operator + * ObjectQuery::OperatorSymbol. Othe rules are moved to the end. + */ + void sortByQuerySymbol(); + + /** + * Sorts the items by tag query key and value if possible. + * + * The tag key and value are only available for queries with operator + * ObjectQuery::OperatorIs, ObjectQuery::OperatorIsNot, and + * ObjectQuery::OperatorIs. Othe rules are moved to the end. + */ + void sortByQueryKeyAndValue(); + + + /** + * Sets the assigned symbols to match the original symbol name if possible. + * + * The symbol name is only available for queries with operator + * ObjectQuery::OperatorSymbol. + * + * The symbols' types must be compatible. + */ + void matchQuerySymbolName(const Map& other_map); + + /** + * Sets the assigned symbols to match the original symbol number if possible. + * + * The symbol number is only available for queries with operator + * ObjectQuery::OperatorSymbol. + * + * The symbols' types must be compatible. + */ + void matchQuerySymbolNumber(const Map& other_map); + + + /** + * Loads rules from a cross reference table (CRT) stream. + * + * Each line in a CRT file takes the form: + * + * REPLACEMENT pattern + * + * where REPLACEMENT is the formatted number of the replacement symbol. + * + * If the replacement symbol number exists, the import creates a new entry + * of type DefinedReplacement with the given replacement symbol. Otherwise, + * the replacement symbool is set to nullptr, and the type is set to + * NoReplacement. No other validation is performed. + * + * The query is set to operand ObjectQuery::OperatorSearch and the tag value + * is set to the given pattern. + */ + static SymbolRuleSet loadCrt(QTextStream& stream, const Map& replacement_map); + + /** + * Writes rules to a cross reference table (CRT) stream. + * + * Entries of type NoAssignment are skipped. + * + * \see loadCrt + */ + void writeCrt(QTextStream& stream) const; + + + /** + * Applies the matching rules to the object. + * + * This operator can be used with Map::applyOnAllObjects etc. + * + * Normally, you don't want to call this unless the new symbols are already + * part of the object's map. Note that for efficiency, this should be + * called on a squeezed() map. + * + * \see apply + */ + void operator()(Object* object) const; + + + /** + * Options for importing of new colors and symbols in to a map. + */ + enum Option + { + ImportAllSymbols = 0x01, + PreserveSymbolState = 0x02, + KeepUnusedSymbols = 0x04, + KeepUnusedColors = 0x08, + }; + Q_DECLARE_FLAGS(Options, Option) + + /** + * Adds colors and symbols from the symbol map to the object map, + * and applies the rules. + * + * Note that for efficiency, this should be called on a squeezed() map. + */ + void apply(Map& object_map, const Map& symbol_set, Options options = 0); + +}; + + +} // namespace OpenOrienteering + + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::SymbolRuleSet::Options) + + +#endif // OPENORIENTEERING_SYMBOL_RULE_SET_H diff --git a/src/core/objects/text_object.cpp b/src/core/objects/text_object.cpp new file mode 100644 index 0000000..49c9933 --- /dev/null +++ b/src/core/objects/text_object.cpp @@ -0,0 +1,569 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012, 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "text_object.h" + +#include +#include +#include +#include + +#include "settings.h" +#include "core/objects/object.h" +#include "core/symbols/text_symbol.h" +#include "core/symbols/symbol.h" + +// IWYU pragma: no_forward_declare QPointF + + +namespace OpenOrienteering { + +// ### TextObjectPartInfo ### + +int TextObjectPartInfo::getIndex(double pos_x) const +{ + int left = 0; + int right = part_text.length(); + while (right != left) + { + int middle = (left + right) / 2; + double x = part_x + metrics.width(part_text.left(middle)); + if (pos_x >= x) + { + if (middle >= right) + return right; + double next = part_x + metrics.width(part_text.left(middle + 1)); + if (pos_x < next) + if (pos_x < (x + next) / 2) + return middle; + else + return middle + 1; + else + left = middle + 1; + } + else // if (point.x() < x) + { + if (middle <= 0) + return 0; + double prev = part_x + metrics.width(part_text.left(middle - 1)); + if (pos_x > prev) + if (pos_x > (x + prev) / 2) + return middle; + else + return middle - 1; + else + right = middle - 1; + } + } + return right; +} + + + +// ### TextObjectLineInfo ### + +double TextObjectLineInfo::getX(int index) const +{ + int num_parts = part_infos.size(); + int i = 0; + for ( ; i < num_parts; i++) + { + const TextObjectPartInfo& part(part_infos.at(i)); + if (index <= part.end_index) + return part.getX(index); + } + + return line_x + width; +} + +int TextObjectLineInfo::getIndex(double pos_x) const +{ +// TODO: evaluate std::vector::iterator it; + int num_parts = part_infos.size(); + for (int i=0; i < num_parts; i++) + { + if (part_infos.at(i).part_x > pos_x) + { + if (i==0) + // before first part + return start_index; + else if (part_infos.at(i-1).part_x + part_infos.at(i-1).width < pos_x) + { + // between parts + return (pos_x - (part_infos.at(i-1).part_x + part_infos.at(i-1).width) < part_infos.at(i).part_x - pos_x) + ? part_infos.at(i-1).end_index + : part_infos.at(i).start_index; + } + else + // inside part + return part_infos.at(i-1).start_index + part_infos.at(i-1).getIndex(pos_x); + } + } + return part_infos.back().start_index + part_infos.at(num_parts-1).getIndex(pos_x); +} + +// ### TextObject ### + +TextObject::TextObject(const Symbol* symbol) + : Object(Object::Text, symbol) + , h_align(AlignHCenter) + , v_align(AlignVCenter) + , rotation(0.0f) +{ + Q_ASSERT(!symbol || (symbol->getType() == Symbol::Text)); + coords.reserve(2); // Extra element used during saving + coords.push_back(MapCoord(0, 0)); +} + +TextObject::TextObject(const TextObject& proto) + : Object(proto) + , text(proto.text) + , h_align(proto.h_align) + , v_align(proto.v_align) + , rotation(proto.rotation) + , has_single_anchor(proto.has_single_anchor) + , size(proto.size) + , line_infos(proto.line_infos) +{ + // nothing +} + +TextObject* TextObject::duplicate() const +{ + return new TextObject(*this); +} + +void TextObject::copyFrom(const Object& other) +{ + if (&other == this) + return; + + Object::copyFrom(other); + const TextObject& other_text = *other.asText(); + text = other_text.text; + h_align = other_text.h_align; + v_align = other_text.v_align; + rotation = other_text.rotation; + has_single_anchor = other_text.has_single_anchor; + size = other_text.size; + line_infos = other_text.line_infos; +} + +void TextObject::setAnchorPosition(qint32 x, qint32 y) +{ + has_single_anchor = true; + coords[0].setNativeX(x); + coords[0].setNativeY(y); + setOutputDirty(); +} + +void TextObject::setAnchorPosition(MapCoord coord) +{ + has_single_anchor = true; + coords[0] = coord; + setOutputDirty(); +} + +void TextObject::setAnchorPosition(MapCoordF coord) +{ + has_single_anchor = true; + coords[0].setX(coord.x()); + coords[0].setY(coord.y()); + setOutputDirty(); +} + +MapCoordF TextObject::getAnchorCoordF() const +{ + return MapCoordF(coords[0]); +} + + +void TextObject::transform(const QTransform& t) +{ + if (t.isIdentity()) + return; + + auto& coord = coords.front(); + const auto p = t.map(MapCoordF{coord}); + coord.setX(p.x()); + coord.setY(p.y()); + setOutputDirty(); +} + + + +void TextObject::setBox(qint32 mid_x, qint32 mid_y, qreal width, qreal height) +{ + has_single_anchor = false; + coords[0].setNativeX(mid_x); + coords[0].setNativeY(mid_y); + size = {width, height}; + setOutputDirty(); +} + +void TextObject::setBoxSize(const MapCoord& size) +{ + has_single_anchor = false; + this->size = size; + setOutputDirty(); +} + +std::vector TextObject::controlPoints() const +{ + auto anchor = getAnchorCoordF(); + std::vector handles(4, anchor); + + if (hasSingleAnchor()) + { + handles.resize(1); + } + else + { + QTransform transform; + transform.rotate(-qRadiansToDegrees(qreal(getRotation()))); + + handles[0] += transform.map(QPointF(+getBoxWidth() / 2, -getBoxHeight() / 2)); + handles[1] += transform.map(QPointF(+getBoxWidth() / 2, +getBoxHeight() / 2)); + handles[2] += transform.map(QPointF(-getBoxWidth() / 2, +getBoxHeight() / 2)); + handles[3] += transform.map(QPointF(-getBoxWidth() / 2, -getBoxHeight() / 2)); + } + + return handles; +} + + + +void TextObject::scale(MapCoordF center, double factor) +{ + coords.front() = MapCoord{center + (MapCoordF{coords.front()} - center) * factor}; + if (!has_single_anchor) + size *= factor; + setOutputDirty(); +} + + +void TextObject::scale(double factor_x, double factor_y) +{ + auto& coord = coords.front(); + coord.setX(coord.x() * factor_x); + coord.setY(coord.y() * factor_y); + if (!has_single_anchor) + { + size.setX(size.x() * factor_x); + size.setY(size.y() * factor_y); + } + setOutputDirty(); +} + + + +QTransform TextObject::calcTextToMapTransform() const +{ + const TextSymbol* text_symbol = reinterpret_cast(symbol); + + QTransform transform; + double scaling = 1.0f / text_symbol->calculateInternalScaling(); + transform.translate(coords[0].x(), coords[0].y()); + if (rotation != 0) + transform.rotate(-rotation * 180 / M_PI); + transform.scale(scaling, scaling); + + return transform; +} + +QTransform TextObject::calcMapToTextTransform() const +{ + const TextSymbol* text_symbol = reinterpret_cast(symbol); + + QTransform transform; + double scaling = 1.0f / text_symbol->calculateInternalScaling(); + transform.scale(1.0f / scaling, 1.0f / scaling); + if (rotation != 0) + transform.rotate(rotation * 180 / M_PI); + transform.translate(-coords[0].x(), -coords[0].y()); + + return transform; +} + +void TextObject::setText(const QString& text) +{ + this->text = text; + this->text.remove(QLatin1Char('\r')); + setOutputDirty(); +} + +void TextObject::setHorizontalAlignment(TextObject::HorizontalAlignment h_align) +{ + this->h_align = h_align; + setOutputDirty(); +} + +void TextObject::setVerticalAlignment(TextObject::VerticalAlignment v_align) +{ + this->v_align = v_align; + setOutputDirty(); +} + +void TextObject::setRotation(float new_rotation) +{ + rotation = new_rotation; + setOutputDirty(); +} + +bool TextObject::intersectsBox(const QRectF& box) const +{ + return getExtent().intersects(box); +} + +int TextObject::calcTextPositionAt(MapCoordF coord, bool find_line_only) const +{ + return calcTextPositionAt(calcMapToTextTransform().map(coord), find_line_only); +} + +// FIXME actually this is two functions, selected by parameter find_line_only; make two functions or return TextObjectLineInfo reference +int TextObject::calcTextPositionAt(QPointF point, bool find_line_only) const +{ + auto click_tolerance = Settings::getInstance().getMapEditorClickTolerancePx(); + + for (int line = 0; line < getNumLines(); ++line) + { + const TextObjectLineInfo* line_info = getLineInfo(line); + if (line_info->line_y - line_info->ascent > point.y()) + return -1; // NOTE: Only true as long as every line has a bigger or equal y value than the line before + + if (point.x() < line_info->line_x - click_tolerance) continue; + if (point.y() > line_info->line_y + line_info->descent) continue; + if (point.x() > line_info->line_x + line_info->width + click_tolerance) continue; + + // Position in the line rect. + if (find_line_only) + return line; + else + return line_info->getIndex(point.x()); + } + return -1; +} + +int TextObject::findLineForIndex(int index) const +{ + int line_num = 0; + for (int line = 1; line < getNumLines(); ++line) + { + const TextObjectLineInfo* line_info = getLineInfo(line); + if (index < line_info->start_index) + break; + line_num = line; + } + return line_num; +} + +const TextObjectLineInfo& TextObject::findLineInfoForIndex(int index) const +{ + const TextObjectLineInfo* line_info = getLineInfo(0); + for (int line = 1; line < getNumLines(); ++line) + { + const TextObjectLineInfo* next_line_info = getLineInfo(line); + if (index < next_line_info->start_index) + break; + line_info = next_line_info; + } + return *line_info; +} + +void TextObject::prepareLineInfos() const +{ + const TextSymbol* text_symbol = reinterpret_cast(symbol); + + double scaling = text_symbol->calculateInternalScaling(); + QFontMetricsF metrics = text_symbol->getFontMetrics(); + double line_spacing = text_symbol->getLineSpacing() * metrics.lineSpacing(); + double paragraph_spacing = scaling * text_symbol->getParagraphSpacing() + (text_symbol->hasLineBelow() ? (scaling * (text_symbol->getLineBelowDistance() + text_symbol->getLineBelowWidth())) : 0); + double ascent = metrics.ascent(); + + bool word_wrap = ! hasSingleAnchor(); + double box_width = word_wrap ? (scaling * getBoxWidth()) : 0.0; + double box_height = word_wrap ? (scaling * getBoxHeight()) : 0.0; + + int text_end = text.length(); + const QLatin1Char line_break('\n'); + const QLatin1Char part_break('\t'); + const QLatin1Char word_break(' '); + + line_infos.clear(); + + // Initialize offsets + + double line_x = 0.0; + if (h_align == TextObject::AlignLeft) + line_x -= 0.5 * box_width; + else if (h_align == TextObject::AlignRight) + line_x += 0.5 * box_width; + + double line_y = 0.0; + if (v_align == TextObject::AlignTop || v_align == TextObject::AlignBaseline) + line_y += -0.5 * box_height; + if (v_align != TextObject::AlignBaseline) + line_y += ascent; + + // Determine lines and parts + + //double next_line_x_offset = 0; // to keep indentation after word wrap in a line with tabs + int num_paragraphs = 0; + int line_num = 0; + int line_start = 0; + while (line_start <= text_end) + { + // Initialize input line + double line_width = 0.0; + int line_end = text.indexOf(line_break, line_start); + if (line_end == -1) + line_end = text_end; + bool paragraph_end = true; + + std::vector part_infos; + + int part_start = line_start; + double part_x = line_x; + + while (part_start <= line_end) + { + // Initialize part (sequence of letters terminated by tab or line break) + int part_end = text.indexOf(part_break, part_start); + if (part_end == -1) + part_end = line_end; + else if (part_end > line_end) + part_end = line_end; + + if (part_start > 0 && text[part_start - 1] == part_break) + part_x = line_x + text_symbol->getNextTab(part_x - line_x); + + QString part = text.mid(part_start, part_end - part_start); + double part_width = metrics.boundingRect(part).width(); + + if (word_wrap) + { + // shrink overflowing part to maximum possible size + while (part_x + part_width - line_x > box_width) + { + // find latest possible break + int new_part_end = text.lastIndexOf(word_break, part_end - 1); + if (new_part_end <= part_start) + { + // part won't fit + if (part_start > line_start) + { + // don't put another part on this line + part_end = part_start - 1; + paragraph_end = false; + } + break; + } + + paragraph_end = false; + + // Shrink the part and the line + part_end = new_part_end; + part = text.mid(part_start, part_end - part_start); + part_width = metrics.width(part); + line_end = part_end; + } + } + if (part_end < part_start) + break; + + // Add the current part + part_infos.push_back( { part, part_start, part_end, part_x, metrics.width(part), metrics } ); + + // Advance to next part position + part_start = part_end + 1; + part_x += part_width; + } + + TextObjectPartInfo& last_part_info = part_infos.back(); + line_end = last_part_info.end_index; + line_width = last_part_info.part_x + last_part_info.width - line_x; + + // Jump over whitespace after the end of the line and check if it contains a newline character to determine if it is a paragraph end + int next_line_start = line_end + 1; + /*while (next_line_start < text.size() && (text[next_line_start] == line_break || text[next_line_start] == part_break || text[next_line_start] == word_break)) + { + if (text[next_line_start - 1] == line_break) + { + paragraph_end = true; + break; + } + ++next_line_start; + }*/ + + line_infos.push_back( { line_start, line_end, paragraph_end, line_x, line_y, line_width, metrics.ascent(), metrics.descent(), part_infos } ); + + // Advance to next line + line_y += line_spacing; + if (paragraph_end) + { + line_y += paragraph_spacing; + num_paragraphs++; + } + line_num++; + line_start = next_line_start; + } + + // Update the line and part offset for every other alignment than top-left or baseline-left + + double delta_y = 0.0; + if (v_align == TextObject::AlignBottom || v_align == TextObject::AlignVCenter) + { + int num_lines = getNumLines(); + double height = ascent + (num_lines - 1) * line_spacing + (num_paragraphs - 1) * paragraph_spacing; + + if (v_align == TextObject::AlignVCenter) + delta_y = -0.5 * height; + else if (v_align == TextObject::AlignBottom) + delta_y = -height + 0.5 * box_height; + } + + if (delta_y != 0.0 || h_align != TextObject::AlignLeft) + { + int num_lines = getNumLines(); + for (int i = 0; i < num_lines; i++) + { + TextObjectLineInfo* line_info = &line_infos[i]; + + double delta_x = 0.0; + if (h_align == TextObject::AlignHCenter) + delta_x = -0.5 * line_info->width; + else if (h_align == TextObject::AlignRight) + delta_x -= line_info->width; + + line_info->line_x += delta_x; + line_info->line_y += delta_y; + + int num_parts = line_info->part_infos.size(); + for (int j = 0; j < num_parts; j++) + { + line_info->part_infos.at(j).part_x += delta_x; + } + } + } +} + + +} // namespace OpenOrienteering diff --git a/src/core/objects/text_object.h b/src/core/objects/text_object.h new file mode 100644 index 0000000..35b2523 --- /dev/null +++ b/src/core/objects/text_object.h @@ -0,0 +1,414 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012, 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_OBJECT_TEXT_H +#define OPENORIENTEERING_OBJECT_TEXT_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "core/map_coord.h" +#include "core/objects/object.h" + +// IWYU pragma: no_forward_declare QPointF +// IWYU pragma: no_forward_declare QRectF +// IWYU pragma: no_forward_declare QTransform + +namespace OpenOrienteering { + +class Symbol; + + +/** + * TextObjectPartInfo contains layout information for a continuous sequence of printable characters + * in a longer text. + * + * Use the implicit initializer list constructor to create a new object of this class. + */ +class TextObjectPartInfo +{ +public: + QString part_text; /// The sequence of printable characters which makes up this part + int start_index; /// The index of the part's first character in the original string + int end_index; /// The index of the part's last character in the original string + double part_x; /// The left endpoint of the baseline of this part in text coordinates + double width; /// The width of the rendered part in text coordinates + QFontMetricsF metrics; /// The metrics of the font that is used to render the part + + /** Get the horizontal position of a particular character in a part. + * @param index the index of the character in the original string + * @return the character's horizontal position in text coordinates + */ + double getX(int index) const; + + /** Find the index of the character corresponding to a particular position. + * @param pos_x the position for which the index is requested + * @return the character's index in the original string + */ + int getIndex(double pos_x) const; +}; + + + +/** TextObjectLineInfo contains layout information for a single line + * in a longer text. A line is a sequence of different parts. + */ +struct TextObjectLineInfo +{ + /** A sequence container of TextObjectPartInfo objects + */ + typedef std::vector PartInfoContainer; + + int start_index; /// The index of the part's first character in the original string + int end_index; /// The index of the part's last character in the original string + bool paragraph_end; /// Is this line the end of a paragraph? + double line_x; /// The left endpoint of the baseline of this line in text coordinates + double line_y; /// The vertical position of the baseline of this line in text coordinates + double width; /// The total width of the text in this line + double ascent; /// The height of the rendered text above the baseline + double descent; /// The height of the rendered text below the baseline + PartInfoContainer part_infos; /// The sequence of parts which make up this line + + /** Get the horizontal position of a particular character in a line. + * @param pos the index of the character in the original string + * @return the character's horizontal position in text coordinates + */ + double getX(int pos) const; + + /** Find the index of the character corresponding to a particular position. + * @param pos_x the position for which the index is requested + * @return the character's index in the original string + */ + int getIndex(double pos_x) const; +}; + +/** A text object. + * + * A text object is an instance of a text symbol. + * Its position may be specified by a single coordinate (the anchor point) + * or by two coordinates (word wrap box: + * first coordinate specifies the coordinate of the midpoint, + * second coordinates specifies the width and height). + * + * TODO: the way of defining word wrap boxes is inconvenient, as the second + * coordinate does not specify a real coordinate in this case, but is misused + * as extent. Change this? + */ +class TextObject : public Object // clazy:exclude=copyable-polymorphic +{ +public: + enum HorizontalAlignment + { + AlignLeft = 0, + AlignHCenter = 1, + AlignRight = 2 + }; + + enum VerticalAlignment + { + AlignBaseline = 0, + AlignTop = 1, + AlignVCenter = 2, + AlignBottom = 3 + }; + + /** A sequence container of TextObjectLineInfo objects + */ + typedef std::vector LineInfoContainer; + + /** Construct a new text object. + * If a symbol is specified, it must be a text symbol. + * @param symbol the text symbol (optional) + */ + explicit TextObject(const Symbol* symbol = nullptr); + +protected: + /** Constructs a TextObject, initalized from the given prototype. */ + explicit TextObject(const TextObject& proto); + +public: + /** Creates a duplicate of the text object. + * @return a new object with same text, symbol and formatting. + */ + TextObject* duplicate() const override; + + TextObject& operator=(const TextObject&) = delete; + + void copyFrom(const Object& other) override; + + + /** Returns true if the text object has a single anchor, false if it has as word wrap box + */ + bool hasSingleAnchor() const; + + /** Sets the position of the anchor point to (x,y). + * This will drop an existing word wrap box. + */ + void setAnchorPosition(qint32 x, qint32 y); + + /** Sets the position of the anchor point to coord. + * This will drop an existing word wrap box. + */ + void setAnchorPosition(MapCoord coord); + + /** Sets the position of the anchor point to coord. + * This will drop an existing word wrap box. + */ + void setAnchorPosition(MapCoordF coord); + + /** Returns the coordinates of the anchor point or midpoint */ + MapCoordF getAnchorCoordF() const; + + + void transform(const QTransform& t) override; + + + /** Set position and size. + * The midpoint is set to (mid_x, mid_y), the size is specifed by the parameters + * width and heigt. + */ + void setBox(qint32 mid_x, qint32 mid_y, qreal width, qreal height); + + /** Set size. + */ + void setBoxSize(const MapCoord& size); + + /** Returns the size as a MapCoord. + */ + MapCoord getBoxSize() const { return size; } + + /** Returns the width of the word wrap box. + * The text object must have a specified size. + */ + qreal getBoxWidth() const; + + /** Returns the height of the word wrap box. + * The text object must have a specified size. + */ + qreal getBoxHeight() const; + + + /** + * @brief Returns the positions of the control points. + * + * The returned vector may have one or four members, depending on the type + * of object. + */ + std::vector controlPoints() const; + + + /** + * Scales position and box, with the given scaling center. + */ + void scale(MapCoordF center, double factor) override; + + /** + * Scales position and box, with the center (0, 0). + */ + void scale(double factor_x, double factor_y) override; + + + /** Sets the text of the object. + */ + void setText(const QString& text); + + /** Returns the text of the object. + */ + const QString& getText() const; + + /** Sets the horizontal alignment of the text. + */ + void setHorizontalAlignment(HorizontalAlignment h_align); + + /** Returns the horizontal alignment of the text. + */ + HorizontalAlignment getHorizontalAlignment() const; + + /** Sets the vertical alignment of the text. + */ + void setVerticalAlignment(VerticalAlignment v_align); + + /** Returns the vertical alignment of the text. + */ + VerticalAlignment getVerticalAlignment() const; + + /** Sets the rotation of the text. + * The rotation is measured in radians. The center of rotation is the anchor point. + */ + void setRotation(float new_rotation); + + /** Returns the rotation of the text. + * The rotation is measured in radians. The center of rotation is the anchor point. + */ + float getRotation() const; + + + bool intersectsBox(const QRectF& box) const override; + + + /** Returns a QTransform from text coordinates to map coordinates. + */ + QTransform calcTextToMapTransform() const; + + /** Returns a QTransform from map coordinates to text coordinates. + */ + QTransform calcMapToTextTransform() const; + + + /** Return the number of rendered lines. + * For a text object with a word wrap box, the number of rendered lines + * may be higher than the number of explicit line breaks in the original text. + */ + int getNumLines() const; + + /** Returns the layout information about a particular line. + */ + TextObjectLineInfo* getLineInfo(int i); + + /** Returns the layout information about a particular line. + */ + const TextObjectLineInfo* getLineInfo(int i) const; + + /** Return the index of the character or the line number corresponding to a particular map coordinate. + * Returns -1 if the coordinate is not at a text position. + * If find_line_only is true, the line number is returned, otherwise the index of the character. + */ + int calcTextPositionAt(MapCoordF coord, bool find_line_only) const; + + /** Return the index of the character or the line number corresponding to a particular text coordinate. + * Returns -1 if the coordinate is not at a text position. + * If find_line_only is true, the line number is returned, otherwise the index of the character. + */ + int calcTextPositionAt(QPointF coord, bool find_line_only) const; + + /** Returns the line number for a particular index in the text. + */ + int findLineForIndex(int index) const; + + /** Returns the line layout information for particular index. + */ + const TextObjectLineInfo& findLineInfoForIndex(int index) const; + + /** Prepare the text layout information. + */ + void prepareLineInfos() const; + +private: + QString text; + HorizontalAlignment h_align; + VerticalAlignment v_align; + float rotation; // 0 to 2*M_PI + + bool has_single_anchor = true; + MapCoord size; + + /** Information about the text layout. + */ + mutable LineInfoContainer line_infos; +}; + + + +//### TextObjectPartInfo inline code ### + +inline +double TextObjectPartInfo::getX(int index) const +{ + return part_x + metrics.width(part_text.left(index - start_index)); +} + + + +//### TextObject inline code ### + +inline +bool TextObject::hasSingleAnchor() const +{ + return has_single_anchor; +} + +inline +qreal TextObject::getBoxWidth() const +{ + Q_ASSERT(!hasSingleAnchor()); + return size.x(); +} + +inline +qreal TextObject::getBoxHeight() const +{ + Q_ASSERT(!hasSingleAnchor()); + return size.y(); +} + +inline +const QString&TextObject::getText() const +{ + return text; +} + +inline +TextObject::HorizontalAlignment TextObject::getHorizontalAlignment() const +{ + return h_align; +} + +inline +TextObject::VerticalAlignment TextObject::getVerticalAlignment() const +{ + return v_align; +} + +inline +float TextObject::getRotation() const +{ + return rotation; +} + +inline +int TextObject::getNumLines() const +{ + return (int)line_infos.size(); +} + +inline +TextObjectLineInfo*TextObject::getLineInfo(int i) +{ + return &line_infos[i]; +} + +inline +const TextObjectLineInfo*TextObject::getLineInfo(int i) const +{ + return &line_infos[i]; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/path_coord.cpp b/src/core/path_coord.cpp new file mode 100644 index 0000000..12ca41b --- /dev/null +++ b/src/core/path_coord.cpp @@ -0,0 +1,529 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "path_coord.h" + +#include +#include +#include +#include +#include + +#include "core/map_coord.h" +#include "core/virtual_coord_vector.h" +#include "core/virtual_path.h" + + +namespace OpenOrienteering { + +static_assert(std::is_nothrow_default_constructible::value, + "PathCoord must be nothrow default constructible."); +static_assert(std::is_nothrow_copy_constructible::value, + "PathCoord must be nothrow copy constructible."); +static_assert(std::is_nothrow_move_constructible::value, + "PathCoord must be nothrow move constructible."); +static_assert(std::is_nothrow_copy_assignable::value, + "PathCoord must be nothrow copy assignable."); +static_assert(std::is_nothrow_move_assignable::value, + "PathCoord must be nothrow move assignable."); +static_assert(std::is_nothrow_destructible::value, + "PathCoord must be nothrow destructible."); + + +// ### PathCoord ### + +void PathCoord::splitBezierCurve(MapCoordF c0, MapCoordF c1, MapCoordF c2, MapCoordF c3, float p, MapCoordF& o0, MapCoordF& o1, MapCoordF& o2, MapCoordF& o3, MapCoordF& o4) +{ + if (p >= 1.0) + { + o0 = c1; + o1 = c2; + o2 = c3; + o3 = c3; + o4 = c3; + } + else if (p <= 0.0) + { + o0 = c0; + o1 = c0; + o2 = c0; + o3 = c1; + o4 = c2; + } + else + { + // The output variables may point to the same storage (if unused), so we + // must not access any output identifier after another one was assigned to. + auto c12 = c1 + (c2 - c1) * p; + o0 = c0 + (c1 - c0) * p; + auto tmp_o1 = o0 + (c12 - o0) * p; + o4 = c2 + (c3 - c2) * p; + o3 = c12 + (o4 - c12) * p; + o2 = tmp_o1 + (o3 - tmp_o1) * p; + o1 = tmp_o1; + } +} + + + +// ### SplitPathCoord ### + +MapCoordF SplitPathCoord::tangentVector() const +{ + auto& path_coords = *this->path_coords; + auto& flags = path_coords.flags(); + auto& coords = path_coords.coords(); + auto path_closed = path_coords.isClosed(); + + std::size_t index = path_coord_index; + auto last_index = path_coords.size() - 1; + + /// Check distances, advance to a significant length, handle closed paths. + MapCoordF next = curve_start[0]; + if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) + goto next_found; + + if (is_curve_start) + { + next = curve_start[1]; + if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) + goto next_found; + } + + // Search along current edge + if (index < last_index && param != 0.0f) + { + do + { + ++index; + next = path_coords[index].pos; + if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) + goto next_found; + } + while (path_coords[index].param != 0.0f); + } + + // Attention, switching from PathCoordVector index to MapCoordVectorF index. + index = path_coords[index].index; + last_index = coords.size() - 1; + + // Search along control points + while (index < last_index) + { + ++index; + next = coords[index]; + if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) + goto next_found; + } + + if (path_closed && path_coord_index > 0) + { + index = 0; + last_index = path_coords[path_coord_index].index; + while (index < last_index) + { + ++index; + next = coords[index]; + if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) + goto next_found; + } + + if (flags[index].isCurveStart()) + { + next = coords[index+1]; + if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) + goto next_found; + } + + // Search along curve + Q_ASSERT(index == path_coords[path_coord_index].index); + + // Attention, switching from MapCoordVectorF index to PathCoordVector index. + auto pc = std::lower_bound(std::begin(path_coords), std::begin(path_coords)+path_coord_index, index, PathCoord::indexLessThanValue); + index = std::distance(std::begin(path_coords), pc); + last_index = path_coord_index; + while (index < last_index) + { + if (pos.distanceSquaredTo(next) >= PathCoord::tangentEpsilonSquared()) + goto next_found; + + ++index; + next = path_coords[index].pos; + } + } + + // No coordinate is distant enough, so reset next. + next = pos; + +next_found: + next -= pos; + next.normalize(); + + + MapCoordF prev = curve_end[1]; + if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) + goto prev_found; + + if (is_curve_end) + { + prev = curve_end[0]; + if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) + goto prev_found; + } + + index = path_coord_index; + + // Search along curve + if (param != 0.0f) + { + while (path_coords[index].param != 0.0f) + { + --index; + prev = path_coords[index].pos; + if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) + goto prev_found; + } + } + + // Attention, switching from PathCoordVector index to MapCoordVectorF index. + index = path_coords[index].index; + + // Search along control points + while (index > 0) + { + --index; + prev = coords[index]; + if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) + goto prev_found; + } + + last_index = path_coords.size() - 1; + if (path_closed && path_coord_index != last_index) + { + index = path_coords[last_index].index; + last_index = path_coords[path_coord_index].index + (flags[path_coord_index].isCurveStart() ? 3 : 1); + while (index > last_index) + { + --index; + prev = coords[index]; + if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) + goto prev_found; + } + + // Search along curve + Q_ASSERT(index > path_coords[path_coord_index].index); + // Attention, switching from MapCoordVectorF index to PathCoordVector index. + auto pc = std::upper_bound(std::begin(path_coords)+path_coord_index, std::end(path_coords)-1, index, PathCoord::valueLessThanIndex); + index = std::distance(std::begin(path_coords), pc); + last_index = path_coord_index + 1; + while (index > last_index) + { + --index; + prev = path_coords[index].pos; + if (pos.distanceSquaredTo(prev) >= PathCoord::tangentEpsilonSquared()) + goto prev_found; + } + } + prev = pos; + +prev_found: + prev -= pos; + prev.normalize(); + + next -= prev; + return next; +} + +// static +SplitPathCoord SplitPathCoord::begin(const PathCoordVector& path_coords) +{ + Q_ASSERT(!path_coords.empty()); + + auto& flags = path_coords.flags(); + auto& coords = path_coords.coords(); + Q_ASSERT(coords.size() == flags.size()); + + auto first_index = path_coords.front().index; + auto last_index = path_coords.back().index; + Q_ASSERT(last_index > first_index); + + SplitPathCoord split = { coords[first_index], first_index, 0.0f, path_coords.front().clen, &path_coords, 0, false, flags[first_index].isCurveStart(), {}, {} }; + if (flags[last_index].isClosePoint()) + { + split.curve_end[1] = coords[last_index-1]; + if (last_index - first_index > 2 && flags[last_index - 3].isCurveStart()) + { + split.is_curve_end = true; + split.curve_end[0] = coords[last_index-2]; + } + } + else + { + split.curve_end[1] = split.pos; + } + + split.curve_start[0] = coords[first_index+1]; + if (split.is_curve_start) + { + Q_ASSERT(first_index+2 <= last_index); + split.curve_start[1] = coords[first_index+2]; + } + + return split; +} + +// static +SplitPathCoord SplitPathCoord::end(const PathCoordVector& path_coords) +{ + Q_ASSERT(!path_coords.empty()); + + auto& flags = path_coords.flags(); + auto& coords = path_coords.coords(); + Q_ASSERT(coords.size() == flags.size()); + + auto first_index = path_coords.front().index; + auto last_index = path_coords.back().index; + Q_ASSERT(last_index > first_index); + + SplitPathCoord split = { coords[last_index], last_index, 0.0f, path_coords.back().clen, &path_coords, path_coords.size() - 1, false, false, {}, {} }; + if (flags[last_index].isClosePoint()) + { + split.curve_start[0] = coords[first_index+1]; + if (flags[first_index].isCurveStart()) + { + split.is_curve_start = true; + split.curve_start[1] = coords[first_index+2]; + } + } + else + { + split.curve_start[0] = split.pos; + } + + split.curve_end[1] = coords[last_index-1]; + if (last_index - first_index > 2 && flags[last_index-3].isCurveStart()) + { + split.is_curve_end = true; + split.curve_end[0] = coords[last_index-2]; + } + + return split; +} + +// static +SplitPathCoord SplitPathCoord::at( + const PathCoordVector& path_coords, + std::vector::size_type path_coord_index ) +{ + Q_ASSERT(path_coord_index < path_coords.size()); + + auto& flags = path_coords.flags(); + auto& coords = path_coords.coords(); + Q_ASSERT(coords.size() > 1); + Q_ASSERT(coords.size() == flags.size()); + + auto index = path_coords[path_coord_index].index; + SplitPathCoord split = { coords[index], index, path_coords[path_coord_index].param, path_coords[path_coord_index].clen, &path_coords, path_coord_index, false, flags[index].isCurveStart(), {}, {} }; + + if (index+1 < coords.size()) + { + split.curve_start[0] = coords[index+1]; + if (split.is_curve_start && index+2 < coords.size()) + { + split.curve_start[1] = coords[index+2]; + } + else + { + Q_ASSERT(!split.is_curve_start); + } + } + else + { + Q_ASSERT(!split.is_curve_start); + split.curve_start[0] = split.pos; + } + + if (index >= 1) + { + split.curve_end[1] = coords[index-1]; + if (index >= 3 && flags[index-3].isCurveStart()) + { + split.curve_end[0] = coords[index-2]; + split.is_curve_end = true; + } + } + else + { + split.curve_end[1] = split.pos; + } + + return split; +} + +// static +SplitPathCoord SplitPathCoord::at( + length_type length, + const SplitPathCoord& first ) +{ + auto& path_coords = *first.path_coords; + auto& coords = path_coords.coords(); + auto& flags = path_coords.flags(); + + SplitPathCoord split = first; + split.path_coord_index = path_coords.upperBound(length, first.path_coord_index, first.path_coords->size()-1); + if (split.path_coord_index > first.path_coord_index) + { + // New path coordinate, really + split.clen = length; + + const auto& current_coord = path_coords[split.path_coord_index]; + const auto& prev_coord = path_coords[split.path_coord_index-1]; + const auto curve_length = current_coord.clen - prev_coord.clen; + + split.is_curve_end = flags[prev_coord.index].isCurveStart(); + split.is_curve_start = split.is_curve_end; + + auto factor = 1.0f; + if (qFuzzyCompare(1.0f + length, 1.0f + current_coord.clen) || + qFuzzyCompare(1.0f + curve_length, 1.0f)) + { + // Close match at current path coordinate, + // or near-zero curve length. + split.pos = current_coord.pos; + split.clen = current_coord.clen; + split.param = current_coord.param; + } + else + { + // Split between path coordinates. + factor = qBound(0.0f, (length - prev_coord.clen) / curve_length, 1.0f); + auto prev_param = prev_coord.param; + auto current_param = current_coord.param; + if (current_param == 0.0f) + current_param = 1.0f; + split.param = prev_param + (current_param - prev_param) * factor; + Q_ASSERT(split.param >= 0.0f && split.param <= 1.0f); + if (split.param == 1.0f) + split.param = 0.0f; + } + + if (!split.is_curve_end) + { + // Straight + split.pos = prev_coord.pos + qreal(factor) * (current_coord.pos - prev_coord.pos); + + if (current_coord.index > path_coords.front().index) + split.curve_end[1] = coords[current_coord.index-1]; + // else + // leave split.curve_end[1] as copied from first. + + split.curve_start[0] = current_coord.pos; + } + else if (split.param == 0.0f) + { + // At a node, after a curve + split.pos = current_coord.pos; + + if (prev_coord.index == first.index) + { + // Split in same curve as first + split.curve_end[0] = first.curve_start[0]; + split.curve_end[1] = first.curve_start[1]; + } + else + { + split.curve_end[0] = coords[prev_coord.index+1]; + split.curve_end[1] = coords[prev_coord.index+2]; + } + + // curve_start: handled later + } + else + { + // In curve + Q_ASSERT(split.is_curve_start); + Q_ASSERT(split.is_curve_end); + + auto edge_start = prev_coord.index; + Q_ASSERT(edge_start+3 <= path_coords.back().index); + + if (prev_coord.index == first.index) + { + auto p = (split.param - first.param) / (1.0f - first.param); + Q_ASSERT(p >= 0.0f); + Q_ASSERT(p <= 1.0f); + + PathCoord::splitBezierCurve(first.pos, first.curve_start[0], first.curve_start[1], coords[edge_start+3], + p, + split.curve_end[0], split.curve_end[1], split.pos, split.curve_start[0], split.curve_start[1]); + } + else + { + PathCoord::splitBezierCurve(coords[edge_start], coords[edge_start+1], coords[edge_start+2], coords[edge_start+3], + split.param, + split.curve_end[0], split.curve_end[1], split.pos, split.curve_start[0], split.curve_start[1]); + } + } + + if (split.param == 0.0f) + { + // Handle curve_start for non-in-bezier splits. + split.is_curve_start = flags[current_coord.index].isCurveStart(); + if (split.is_curve_start) + { + split.curve_start[0] = coords[current_coord.index+1]; + split.curve_start[1] = coords[current_coord.index+2]; + } + else if (current_coord.index < path_coords.back().index) + { + split.curve_start[0] = coords[current_coord.index+1]; + } + else + { + /// \todo Handle closed paths. + split.curve_start[0] = current_coord.pos; + } + } + else + { + --split.path_coord_index; + } + } + + split.index = path_coords[split.path_coord_index].index; + return split; +} + + +// Not inline or constexpr, because it is meant to be used by function pointer. +bool PathCoord::indexLessThanValue(const PathCoord& coord, size_type value) +{ + return coord.index < value; +} + +// Not inline or constexpr, because it is meant to be used by function pointer. +bool PathCoord::valueLessThanIndex(size_type value, const PathCoord& coord) +{ + return value < coord.index; +} + + +} // namespace OpenOrienteering diff --git a/src/core/path_coord.h b/src/core/path_coord.h new file mode 100644 index 0000000..0ae9e88 --- /dev/null +++ b/src/core/path_coord.h @@ -0,0 +1,294 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_PATH_COORD_H +#define OPENORIENTEERING_PATH_COORD_H + +#include + +#include + +#include "map_coord.h" + +namespace OpenOrienteering { + +class PathCoordVector; + + +/** + * A PathCoord represents a node in a polygonal approximation of a path. + * + * Complex paths which may consist of straight edges and curves are processed + * into PathCoordVectors, approximating the path with straight edges only. + * + * Apart from a point on this polygonal path, a PathCoord contains additional + * information about that point: + * - the index of the original point where the current edge started, + * - the relative position on this edge, and + * - the length since the start of the path. + */ +class PathCoord +{ + friend class PathCoordVector; + +public: + /** A reaonably sized unsigned integer type for map coord vector sizes and indexes. */ + using size_type = quint32; + + /** A reaonably precise float type for lengths and distances. */ + using length_type = float; + + /** A reaonably precise float type for relative position in the range [0, 1). */ + using param_type = float; + + + /** Position. */ + MapCoordF pos; + + /** MapCoordVector(F) index of the start of the edge which this position belongs to. */ + size_type index; + + /** Relative location of this position on the MapCoordVector edge ([0.0, 1.0)). */ + param_type param; + + /** Cumulative length of the path since the start of the current part. */ + length_type clen; + + + /** Default contructor. */ + constexpr PathCoord() noexcept; + + /** Copy constructor. */ + constexpr PathCoord(const PathCoord&) noexcept = default; + + /** Move constructor. */ + PathCoord(PathCoord&&) noexcept = default; + + /** Explicit construction with all member values. */ + constexpr PathCoord(MapCoordF pos, size_type index, param_type param, length_type clen) noexcept; + + + /** Assignment operator. */ + PathCoord& operator=(const PathCoord&) noexcept = default; + + /** Move assignment operator. */ + PathCoord& operator=(PathCoord&&) noexcept = default; + + + /** + * Global position error threshold for approximating bezier curves with straight segments. + * + * @todo Make bezier error configurable + */ + static length_type bezierError(); + + + /** + * Returns true if the PathCoord's index is lower than value. + * + * This function can be used for doing a binary search on a sorted container + * of PathCoords. + * + * @see std::lower_bound + */ + static bool indexLessThanValue(const PathCoord& coord, size_type value); + + /** + * Returns true if the value is lower than the PathCoord's index. + * + * This function can be used for doing a binary search on a sorted container + * of PathCoords. + * + * @see std::upper_bound + */ + static bool valueLessThanIndex(size_type value, const PathCoord& coord); + + + /** + * Splits a cubic bezier curve. + * + * The curve made up by the points c0 ... c3 is split up at the relative + * position p (0..1). The new intermediate points (between c0 and c3) are + * returned in o0 ... o4. + * + * If not all returned values are needed, it is possible to have a subset of + * o0..o4 point to the same memory. + */ + static void splitBezierCurve( + MapCoordF c0, + MapCoordF c1, + MapCoordF c2, + MapCoordF c3, + float p, + MapCoordF& o0, + MapCoordF& o1, + MapCoordF& o2, + MapCoordF& o3, + MapCoordF& o4 + ); + + + /** + * The minimum required (squared) distance of neighboring nodes which are to + * be considered for determining path tangents. + */ + static constexpr qreal tangentEpsilonSquared(); +}; + + + +/** + * An arbitrary position on a path. + * + * A SplitPathCoord supports processing paths in connection with PathCoordVector. + * It can represent an arbitrary position even between the elements of the + * PathCoordVector. Other than PathCoord, it keeps a reference to the + * PathCoordVector. It captures additional state such as adjusted curve parameters. + * + * \see PathCoord + */ +class SplitPathCoord +{ +public: + /** A reaonably precise float type for lengths and distances. */ + using length_type = PathCoord::length_type; + + /** Position. */ + MapCoordF pos; + + /** Index of the cooresponding or preceding map coordinate and flags. */ + PathCoord::size_type index; + + /** Relative location of this position on the MapCoordVector edge ([0.0, 1.0)). */ + float param; + + /** Cumulative length of the path since the start of the current part. */ + length_type clen; + + /** The underlying vector path coordinates. */ + const PathCoordVector* path_coords; + + /** Index of the corresponding or preceding path_coord in a vector. */ + std::vector::size_type path_coord_index; + + /** If true, a bezier edge ends at this split position. */ + bool is_curve_end; + + /** If true, a bezier edge starts at this split position. */ + bool is_curve_start; + + /** If a bezier edge ends here, this will hold the last control points. + * + * Otherwise, curve_end[1] will hold the preceding coordinate, + * or the current coordinate for the start of open paths. + */ + MapCoordF curve_end[2]; + + /** If a bezier edge starts here, this will hold the next control points. + * + * Otherwise, curve_start[0] will hold the next coordinate, + * or the current coordinate for the end of open paths + */ + MapCoordF curve_start[2]; + + + /** + * Returns a vector which is a tangent to the path at this position. + */ + MapCoordF tangentVector() const; + + + /** + * Returns a SplitPathCoord at the begin of the given path. + */ + static SplitPathCoord begin(const PathCoordVector& path_coords); + + /** + * Returns a SplitPathCoord at the end of the given path. + */ + static SplitPathCoord end(const PathCoordVector& path_coords); + + /** + * Returns a SplitPathCoord at the given PathCoordVector index. + */ + static SplitPathCoord at( + const PathCoordVector& path_coords, + std::vector::size_type path_coord_index + ); + + /** + * Returns a SplitPathCoord at the given length. + * + * The search for the position will start at first. + */ + static SplitPathCoord at( + length_type length, + const SplitPathCoord& first + ); + + /** + * Returns a SplitPathCoord at the given length. + */ + static SplitPathCoord at( + const PathCoordVector& path_coords, + length_type length + ); +}; + + + +// ### PathCoord inline code ### + +constexpr PathCoord::PathCoord() noexcept + : PathCoord( {}, 0, 0.0, 0.0) +{ + // Nothing else +} + +constexpr PathCoord::PathCoord(MapCoordF pos, size_type index, param_type param, PathCoord::length_type clen) noexcept + : pos { pos } + , index { index } + , param { param } + , clen { clen } +{ + // Nothing else +} + +constexpr qreal PathCoord::tangentEpsilonSquared() +{ + return 0.000625; // App. 0.025 mm distance +} + + + +// ### SplitPathCoord inline code ### + +// static +inline +SplitPathCoord SplitPathCoord::at(const PathCoordVector& path_coords, SplitPathCoord::length_type length) +{ + return at(length, begin(path_coords)); +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/renderables/renderable.cpp b/src/core/renderables/renderable.cpp new file mode 100644 index 0000000..e871c06 --- /dev/null +++ b/src/core/renderables/renderable.cpp @@ -0,0 +1,738 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "renderable.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/image_transparency_fixup.h" +#include "core/map_color.h" +#include "core/map.h" +#include "core/objects/object.h" +#include "core/symbols/symbol.h" +#include "util/util.h" + +#if defined(Q_OS_ANDROID) && defined(QT_PRINTSUPPORT_LIB) +static_assert(false, "This file needs to be modified for correct printing on Android"); +#endif + + +namespace OpenOrienteering { + +/* + * The macro MAPPER_OVERPRINTING_CORRECTION allows to select different + * implementations of spot color overprinting simulation correction towards + * the appearance of colors in normal (non-spot color) output. + * + * -1: Mapper 0.5.0 correction. + * Results in undesired brightening when overprinting halftones. + * + * 0: No correction. Plain multiply spot color composition. + * Results in undesired green from 100% blue on 100% yellow. + * + * 1: Weak correction. Blends the normal output over the + * overprinting simulation with an alpha of 0.125. + * + * 2: Middle correction. Blends the normal output over the + * overprinting simulation with an alpha of 0.25. + * + * 3: Strong correction. Blends the normal output over the + * overprinting simulation with an alpha of 0.5. + * + * Options 3 and 2 seem to give the best results. Output from option 3 is quite + * similar to option -1 (Mapper 0.5.0), but without the undesired brightening. + * + * Options 1..3 work only as long as the color set and the symbol set are + * defined in a way that the raw overprinting simulation output and the normal + * output do not differ significantly. + */ +#ifndef MAPPER_OVERPRINTING_CORRECTION + + // Default: [new] middle correction + #define MAPPER_OVERPRINTING_CORRECTION 2 + +#endif + + + +// ### Renderable ### + +Renderable::~Renderable() = default; + + + +// ### SharedRenderables ### + +SharedRenderables::~SharedRenderables() +{ + deleteRenderables(); +} + +void SharedRenderables::deleteRenderables() +{ + for (auto renderables = begin(); renderables != end(); ) + { + for (auto renderable : renderables->second) + { + delete renderable; + } + + renderables->second.clear(); + if (renderables->first.clip_path) + renderables = erase(renderables); + else + ++renderables; + } +} + +void SharedRenderables::compact() +{ + for (auto renderables = begin(); renderables != end(); ) + { + if (renderables->second.empty()) + renderables = erase(renderables); + else + ++renderables; + } +} + + +// ### ObjectRenderables ### + +ObjectRenderables::ObjectRenderables(Object& object) +: extent(object.extent) +{ + // nothing else +} + +ObjectRenderables::~ObjectRenderables() = default; + +void ObjectRenderables::draw(int map_color, const QColor& color, QPainter* painter, const RenderConfig& config) const +{ + if (!extent.intersects(config.bounding_box)) + return; + + auto color_renderables = std::find_if(begin(), end(), [map_color](auto item) { + return item.first == map_color; + }); + if (color_renderables == end()) + return; + + const QPainterPath initial_clip = clip_path ? *clip_path : painter->clipPath(); + const QPainterPath* current_clip = nullptr; + + painter->save(); + for (const auto& config_renderables : *(color_renderables->second)) + { + const PainterConfig& state = config_renderables.first; + if (!state.activate(painter, current_clip, config, color, initial_clip)) + continue; + + for (const auto renderable : config_renderables.second) + { + if (renderable->intersects(config.bounding_box)) + { + renderable->render(*painter, config); + } + } + } + painter->restore(); +} + +void ObjectRenderables::setClipPath(const QPainterPath* path) +{ + clip_path = path; +} + +void ObjectRenderables::insertRenderable(Renderable* r, const PainterConfig& state) +{ + SharedRenderables::Pointer& container(operator[](state.color_priority)); + if (!container) + container = new SharedRenderables(); + container->operator[](state).push_back(r); + if (!clip_path) + { + if (extent.isValid()) + rectInclude(extent, r->getExtent()); + else + extent = r->getExtent(); + } +} + +void ObjectRenderables::clear() +{ + for (auto& renderables : *this) + { + renderables.second->clear(); + } +} + +void ObjectRenderables::takeRenderables() +{ + for (auto& color : *this) + { + auto new_container = new SharedRenderables(); + + // Pre-allocate as much space as in the original container + for (const auto& renderables : *color.second) + { + (*new_container)[renderables.first].reserve(renderables.second.size()); + } + color.second = new_container; + } +} + +void ObjectRenderables::deleteRenderables() +{ + for (auto& color : *this) + { + color.second->deleteRenderables(); + } +} + + + +// ### MapRenderables ### + +void MapRenderables::ObjectDeleter::operator()(Object* object) const +{ + renderables.removeRenderablesOfObject(object, false); + delete object; +} + +MapRenderables::MapRenderables(Map* map) + : map(map) +{ + ; // nothing +} + +void MapRenderables::draw(QPainter *painter, const RenderConfig &config) const +{ + // TODO: improve performance by using some spatial acceleration structure? + +#ifdef Q_OS_ANDROID + const qreal min_dimension = 1.0/config.scaling; +#endif + + QPainterPath initial_clip = painter->clipPath(); + const QPainterPath* current_clip = nullptr; + + painter->save(); + auto end_of_colors = rend(); + auto color = rbegin(); + while (color != end_of_colors && color->first >= map->getNumColors()) + { + ++color; + } + for (; color != end_of_colors; ++color) + { + if ( config.testFlag(RenderConfig::RequireSpotColor) && + (color->first < 0 || map->getColor(color->first)->getSpotColorMethod() == MapColor::UndefinedMethod) ) + { + continue; + } + + for (const auto& object : color->second) + { + // Settings check + const Symbol* symbol = object.first->getSymbol(); + if (!config.testFlag(RenderConfig::HelperSymbols) && symbol->isHelperSymbol()) + continue; + if (symbol->isHidden()) + continue; + + if (!object.first->getExtent().intersects(config.bounding_box)) + continue; + + for (const auto& renderables : *object.second) + { + // Render the renderables + const PainterConfig& state = renderables.first; + const MapColor* map_color = map->getColor(state.color_priority); + if (!map_color) + { + Q_ASSERT(state.color_priority == MapColor::Reserved); + continue; // in release build + } + QColor color = *map_color; + if (state.color_priority >= 0 && map_color->getOpacity() < 1) + color.setAlphaF(map_color->getOpacity()); + if (!state.activate(painter, current_clip, config, color, initial_clip)) + continue; + + for (const auto renderable : renderables.second) + { +#ifdef Q_OS_ANDROID + const QRectF& extent = renderable->getExtent(); + if (extent.width() < min_dimension && extent.height() < min_dimension) + continue; +#endif + if (renderable->intersects(config.bounding_box)) + { + renderable->render(*painter, config); + } + } + + } // each common render attributes + + } // each object + + } // each map color + + painter->restore(); +} + +void MapRenderables::drawOverprintingSimulation(QPainter* painter, const RenderConfig& config) const +{ + // NOTE: painter must be a QPainter on a QImage of Format_ARGB32_Premultiplied. + QImage* image = static_cast(painter->device()); + ImageTransparencyFixup image_fixup(image); + + QPainter::RenderHints hints = painter->renderHints(); + QTransform t = painter->worldTransform(); + painter->save(); + + painter->resetTransform(); + painter->setCompositionMode(QPainter::CompositionMode_Multiply); // Alternative: CompositionMode_Darken + + QImage separation(image->size(), QImage::Format_ARGB32_Premultiplied); + + for (auto map_color = map->color_set->colors.rbegin(); + map_color != map->color_set->colors.rend(); + map_color++) + { + if ((*map_color)->getSpotColorMethod() == MapColor::SpotColor) + { + separation.fill(Qt::GlobalColor(Qt::transparent)); + + // Collect all halftones and knockouts of a single color + QPainter p(&separation); + p.setRenderHints(hints); + p.setWorldTransform(t, false); + drawColorSeparation(&p, config, *map_color, true); + p.end(); + + // Add this separation to the composition with multiplication. + painter->setCompositionMode(QPainter::CompositionMode_Multiply); + painter->drawImage(0, 0, separation); + image_fixup(); + +#if MAPPER_OVERPRINTING_CORRECTION == -1 + // Add some opacity to the multiplication, but not for black, + // since halftones (i.e. grey) might unduly lighten the composition. + if (static_cast(**map_color) != 0xff000000) + { + // FIXME: Implement this for Format_ARGB32_Premultiplied, + // if efficiently possible. + QImage copy = separation.convertToFormat(QImage::Format_ARGB32); + QRgb* dest = (QRgb*)copy.bits(); + const QRgb* dest_end = dest + copy.byteCount() / sizeof(QRgb); + for (QRgb* px = dest; px < dest_end; ++px) + { + const unsigned int alpha = qAlpha(*px) * ((255-qGray(*px)) << 16) & 0xff000000; + *px = alpha | (*px & 0xffffff); + } + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + painter->drawImage(0, 0, copy); + } +#endif + } + } + + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + +#if MAPPER_OVERPRINTING_CORRECTION > 0 + separation.fill(Qt::GlobalColor(Qt::transparent)); + QPainter p(&separation); + p.setRenderHints(hints); + p.setWorldTransform(t, false); + RenderConfig config_copy = config; + config_copy.options |= RenderConfig::RequireSpotColor; + draw(&p, config_copy); + p.end(); + QRgb* dest = reinterpret_cast(separation.bits()); + const QRgb* dest_end = dest + separation.byteCount() / sizeof(QRgb); + for (QRgb* px = dest; px < dest_end; ++px) + { + /* Each pixel is a premultipled RGBA, so the alpha value is adjusted + * by applying the same factor to all 4 channels (bytes). + * Implemented by bitwise operators for efficiency. + */ +#if MAPPER_OVERPRINTING_CORRECTION == 1 + *px = (*px >> 3) & 0x1f1f1f1f; +#elif MAPPER_OVERPRINTING_CORRECTION == 2 + *px = (*px >> 2) & 0x3f3f3f3f; +#else /* MAPPER_OVERPRINTING_CORRECTION == 3 or stronger */ + *px = (*px >> 1) & 0x7f7f7f7f; +#endif + } + painter->drawImage(0, 0, separation); +#endif + + painter->restore(); + + if (config.testFlag(RenderConfig::Screen)) + { + static MapColor reserved_color(MapColor::Reserved); + drawColorSeparation(painter, config, &reserved_color, true); + } +} + +void MapRenderables::drawColorSeparation(QPainter* painter, const RenderConfig& config, const MapColor* separation, bool use_color) const +{ + painter->save(); + + const QPainterPath initial_clip(painter->clipPath()); + const QPainterPath* current_clip = nullptr; + + // As soon as the spot color is actually used for drawing (i.e. drawing_started = true), + // we need to take care of knockouts. + bool drawing_started = false; + + // For each pair of color priority and its renderables collection... + auto end_of_colors = rend(); + auto color = rbegin(); + while (color != end_of_colors && color->first >= map->getNumColors()) + { + ++color; + } + for (; color != end_of_colors; ++color) + { + SpotColorComponent drawing_color(map->getColor(color->first), 1.0f); + + // Check whether the current color [priority] applies to the current separation. + if (color->first > MapColor::Reserved) + { + if (separation->getPriority() == MapColor::Reserved) + { + // Don't process regular colors for the "Reserved" separation. + continue; + } + + switch (drawing_color.spot_color->getSpotColorMethod()) + { + case MapColor::UndefinedMethod: + continue; + + case MapColor::SpotColor: + if (drawing_color.spot_color == separation) + { + ; // okay + } + else if (drawing_started && drawing_color.spot_color->getKnockout()) + { + drawing_color.factor = 0.0f; + } + else + { + continue; + } + break; + + case MapColor::CustomColor: + { + // First, check if the renderables draw color to this separation + // TODO: Use an efficient data structure to avoid reiterating each time a separation is drawn + const SpotColorComponents& components = drawing_color.spot_color->getComponents(); + for (const auto& component : components) + { + if (component.spot_color == separation) + { + // The renderables do draw the current spot color + drawing_color = component; + break; + } + } + if (drawing_color.spot_color != separation) + { + // If the renderables do not explicitly draw color to this separation, + // check if they need a knockout. + if (drawing_started && drawing_color.spot_color->getKnockout()) + { + drawing_color = SpotColorComponent(separation, 0.0f); + } + else + { + continue; + } + } + break; + } + + default: + Q_ASSERT(false); // in development builds + continue; // in release build + } + } + else if (separation->getPriority() == MapColor::Reserved) + { + if (color->first == MapColor::Registration) + continue; // treated per spot color + else if (color->first == MapColor::Reserved) + continue; // never drawn + else if (!drawing_color.spot_color) + { + Q_ASSERT(!"Invalid reserved color!"); // in development build + drawing_color.spot_color = Map::getUndefinedColor(); // in release build + } + painter->setRenderHint(QPainter::Antialiasing, true); + } + else if (color->first == MapColor::Registration) + { + // Draw Registration Black as fulltone of regular spot color + drawing_color.spot_color = separation; + } + else + { + // Don't draw reserved color in regular separation. + continue; + } + + // For each pair of object and its renderables [states] for a particular map color... + for (const auto& object : color->second) + { + // Check whether the symbol and object is to be drawn at all. + const Symbol* symbol = object.first->getSymbol(); + if (!config.testFlag(RenderConfig::HelperSymbols) && symbol->isHelperSymbol()) + continue; + if (symbol->isHidden()) + continue; + + if (!object.first->getExtent().intersects(config.bounding_box)) + continue; + + // For each pair of common rendering attributes and collection of renderables... + for (const auto& renderables : *object.second) + { + const PainterConfig& state = renderables.first; + + QColor color = *drawing_color.spot_color; + bool drawing = (drawing_color.factor >= 0.0005f); + if (!drawing) + { + if (!drawing_started) + continue; + color = Qt::white; + } + else if (use_color) + { + qreal c, m, y, k; + color.getCmykF(&c, &m, &y, &k); + color.setCmykF(c*drawing_color.factor, m*drawing_color.factor, y*drawing_color.factor, k*drawing_color.factor, 1.0); + } + else + { + color.setCmykF(0.0, 0.0, 0.0, drawing_color.factor, 1.0); + } + + if (!state.activate(painter, current_clip, config, color, initial_clip)) + continue; + + // For each renderable that uses the current painter configuration... + // Render the renderable + for (const auto renderable : renderables.second) + { + if (renderable->intersects(config.bounding_box)) + { + renderable->render(*painter, config); + drawing_started |= drawing; + } + } + + } // each common render attributes + + } // each object + + } // each map color + + painter->restore(); +} + +void MapRenderables::insertRenderablesOfObject(const Object* object) +{ + auto end_of_colors = object->renderables().end(); + auto color = object->renderables().begin(); + for (; color != end_of_colors; ++color) + { + operator[](color->first)[object] = color->second; + } +} + +void MapRenderables::removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty) +{ + for (auto& color : *this) + { + auto obj = color.second.find(object); + if (obj != color.second.end()) + { + if (mark_area_as_dirty) + { + // We don't want to loop over every dot in an area ... + QRectF extent = object->getExtent(); + if (!extent.isValid()) + { + // ... because here it gets expensive + for (const auto& renderables : *obj->second) + { + for (const auto renderable : renderables.second) + { + extent = extent.isValid() ? extent.united(renderable->getExtent()) : renderable->getExtent(); + } + } + } + map->setObjectAreaDirty(extent); + } + + color.second.erase(obj); + } + } +} + +void MapRenderables::clear(bool mark_area_as_dirty) +{ + if (mark_area_as_dirty) + { + for (const auto& color : *this) + { + for (const auto& object : color.second) + { + for (const auto& renderables : *object.second) + { + for (const auto renderable : renderables.second) + { + map->setObjectAreaDirty(renderable->getExtent()); + } + } + } + } + } + std::map::clear(); +} + +// ### PainterConfig ### + +namespace { + inline + QColor highlightedColor(const QColor& original) + { + const int highlight_alpha = 255; + + if (original.value() > 127) + { + const qreal factor = 0.35; + return QColor(factor * original.red(), factor * original.green(), factor * original.blue(), highlight_alpha); + } + else + { + const qreal factor = 0.15; + return QColor(255 - factor * (255 - original.red()), 255 - factor * (255 - original.green()), 255 - factor * (255 - original.blue()), highlight_alpha); + } + } +} + +bool PainterConfig::activate(QPainter* painter, const QPainterPath*& current_clip, const RenderConfig& config, const QColor& color, const QPainterPath& initial_clip) const +{ + if (current_clip != clip_path) + { + if (initial_clip.isEmpty()) + { + if (clip_path) + painter->setClipPath(*clip_path, Qt::ReplaceClip); + else + painter->setClipPath(initial_clip, Qt::NoClip); + } + else if (clip_path) + { + /* This used to be a workaround for a Qt::IntersectClip problem + * with Windows and Mac printers (cf. [tickets:#196]), and + * with Linux PDF export (cf. [tickets:#225]). + * But it seems to be faster in general. + */ + QPainterPath merged = initial_clip.intersected(*clip_path); + if (merged.isEmpty()) + return false; // outside of initial clip + painter->setClipPath(merged, Qt::ReplaceClip); + } + else + { + painter->setClipPath(initial_clip, Qt::ReplaceClip); + } + current_clip = clip_path; + } + + qreal actual_pen_width = pen_width; + + if (color_priority < 0 && color_priority != MapColor::Registration) + { + if (color_priority == MapColor::Reserved) + return false; + + if (!config.testFlag(RenderConfig::DisableAntialiasing)) + { + // this is not undone here anywhere as it should apply to + // all special symbols and these are always painted last + painter->setRenderHint(QPainter::Antialiasing, true); + } + + actual_pen_width /= config.scaling; + } + else if (config.testFlag(RenderConfig::DisableAntialiasing)) + { + painter->setRenderHint(QPainter::Antialiasing, false); + painter->setRenderHint(QPainter::TextAntialiasing, false); + } + + QBrush brush(config.testFlag(RenderConfig::Highlighted) ? highlightedColor(color) : color); + if (mode == PainterConfig::PenOnly) + { +#ifdef Q_OS_ANDROID + if (pen_width * config.scaling < 0.1) + return false; +#endif + if (config.testFlag(RenderConfig::ForceMinSize) && pen_width * config.scaling <= 1.0) + actual_pen_width = 0.0; // Forces cosmetic pen + painter->setPen(QPen(brush, actual_pen_width)); + painter->setBrush(QBrush(Qt::NoBrush)); + } + else if (mode == PainterConfig::BrushOnly) + { + painter->setPen(QPen(Qt::NoPen)); + painter->setBrush(brush); + } + + painter->setOpacity(config.opacity); + + return true; +} + + +} // namespace OpenOrienteering diff --git a/src/core/renderables/renderable.h b/src/core/renderables/renderable.h new file mode 100644 index 0000000..413bcb5 --- /dev/null +++ b/src/core/renderables/renderable.h @@ -0,0 +1,486 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_RENDERABLE_H +#define OPENORIENTEERING_RENDERABLE_H + +#include +#include + +#include +#include +#include +#include +#include + +#include "core/map_color.h" + +class QColor; +class QPainter; +class QPainterPath; +// IWYU pragma: no_forward_declare QRectF + +namespace OpenOrienteering { + +class Map; +class Object; +class PainterConfig; + + +/** + * This class contains rendering configuration values. + * + * A reference to an object of this class replaces what used to be part of the + * parameter list of various draw()/render() methods. With that old approach, + * each new rendering configuration option required changes in many signatures + * and function calls. In addition, the boolean options were not verbose at all. + * + * Objects are meant to be initialized by initializer lists (C++11). + */ +class RenderConfig +{ +public: + /** + * Flags indicating particular rendering configuration options. + */ + enum Option + { + Screen = 1<<0, ///< Indicates that the drawing is for the screen. + /// Can turn on optimizations which result in slightly + /// lower display quality (e.g. disable antialiasing + /// for texts) for the benefit of speed. + DisableAntialiasing = 1<<1, ///< Forces disabling of Antialiasing. + ForceMinSize = 1<<2, ///< Forces a minimum size of app. 1 pixel for objects. + /// Makes maps look better at small zoom levels without antialiasing. + HelperSymbols = 1<<3, ///< Activates display of symbols with the "helper symbol" flag. + Highlighted = 1<<4, ///< Makes the color appear highlighted. + RequireSpotColor = 1<<5, ///< Skips colors which do not have a spot color definition. + Tool = Screen | ForceMinSize | HelperSymbols, ///< The recommended flags for tools. + NoOptions = 0 ///< No option activated. + }; + + /** + * \class RenderConfig::Options + * + * A combination of flags for rendering configuration options. + * + * \see QFlags::testFlag(), RenderConfig::Option + */ + Q_DECLARE_FLAGS(Options, Option) + + const Map& map; ///< The map. + + QRectF bounding_box; ///< The bounding box of the area to be drawn. + /// Given in map coordinates. + + qreal scaling; ///< The scaling expressed as pixels per mm. + /// Used to calculate the final object sizes when + /// ForceMinSize is set. + + Options options; ///< The rendering options. + + qreal opacity; ///< The opacity. + + /** + * A convenience method for testing flags in the options value. + * + * \see QFlags::testFlag() + */ + bool testFlag(const Option flag) const; +}; + + + +/** + * A Renderable is a graphical item with a simple shape and a single color. + * + * This is the abstract base class. Inheriting classes must implement the + * abstract methods, and they must set the extent during construction. + */ +class Renderable // clazy:exclude=copyable-polymorphic +{ +protected: + /** The constructor for new renderables. */ + explicit Renderable(const MapColor* color); + +public: + Renderable(const Renderable&) = delete; + Renderable(Renderable&&) = delete; + + /** + * The destructor. + */ + virtual ~Renderable(); + + Renderable& operator=(const Renderable&) = delete; + Renderable& operator=(Renderable&&) = delete; + + /** + * Returns the extent (bounding box). + */ + const QRectF& getExtent() const; + + /** + * Tests whether the renderable's extent intersects the given rect. + */ + bool intersects(const QRectF& rect) const; + + /** + * Returns the painter configuration information. + * + * This configuration must be set when rendering this renderable. + */ + virtual PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const = 0; + + /** + * Renders the renderable with the given painter and rendering configuration. + */ + virtual void render(QPainter& painter, const RenderConfig& config) const = 0; + +protected: + /** The color priority is a major attribute and cannot be modified. */ + const int color_priority; + + /** The extent must be set by inheriting classes. */ + QRectF extent; +}; + + + +/** + * PainterConfig contains painter configuration information. + * + * When painting a renderable item, the QPainter shall be configured according + * to this information. + * + * A PainterConfig is an immutable values, constructed with initializer lists. + */ +class PainterConfig +{ +public: + enum PainterMode + { + BrushOnly = 0, ///< Render using the brush only. + PenOnly = 1, ///< Render using the pen only. + Reserved = -1 ///< Not used. + }; + + const int color_priority; ///< The color priority which determines rendering order + const PainterMode mode; ///< The mode of painting + const qreal pen_width; ///< The width of the pen + const QPainterPath* clip_path; ///< A clip_path which may be shared by several Renderables + + /** + * Activates the configuration on the given painter. + * + * If this method returns false, the corresponding renderables shall not be drawn. + * + * @param painter The painter to be configured. + * @param current_clip A pointer which will be set to the address of the current clip, + * in order to avoid switching the clip area unneccessarily. + * @param config The rendering configurations. + * @param color The QColor to be used for the pen or brush. + * @param initial_clip The clip which was set initially for this painter. + * @return True if the configuration was activated, false if the corresponding renderables shall not be drawn. + */ + bool activate(QPainter* painter, const QPainterPath*& current_clip, const RenderConfig& config, const QColor& color, const QPainterPath& initial_clip) const; + + friend bool operator==(const PainterConfig& lhs, const PainterConfig& rhs); + friend bool operator<(const PainterConfig& lhs, const PainterConfig& rhs); +}; + +/** + * Returns true if the configurations are equal. + */ +bool operator==(const PainterConfig& lhs, const PainterConfig& rhs); + +/** + * Returns true if the configurations are not equal. + */ +bool operator!=(const PainterConfig& lhs, const PainterConfig& rhs); + +/** + * Defines an order over values which are not equal. + */ +bool operator<(const PainterConfig& lhs, const PainterConfig& rhs); + + + +/** + * A low-level container for renderables. + */ +typedef std::vector RenderableVector; + + + +/** + * A shared high-level container for renderables + * grouped by common render attributes. + * + * This shared container can be used in different collections. When the last + * reference to this container is dropped, it will delete the renderables. + */ +class SharedRenderables : public QSharedData, public std::map< PainterConfig, RenderableVector > +{ +public: + typedef QExplicitlySharedDataPointer Pointer; + SharedRenderables() = default; + SharedRenderables(const SharedRenderables&) = delete; + SharedRenderables& operator=(const SharedRenderables&) = delete; + ~SharedRenderables(); + void deleteRenderables(); + void compact(); // release memory which is occupied by unused PainterConfig, FIXME: maybe call this regularly... +}; + + + +/** + * A high-level container for all renderables of a single object, + * grouped by color priority and common render attributes. + */ +class ObjectRenderables : protected std::map +{ +friend class MapRenderables; +public: + ObjectRenderables(Object& object); + ObjectRenderables(const ObjectRenderables&) = delete; + ObjectRenderables& operator=(const ObjectRenderables&) = delete; + ~ObjectRenderables(); + + inline void insertRenderable(Renderable* r); + void insertRenderable(Renderable* r, const PainterConfig& state); + + void clear(); + void deleteRenderables(); + void takeRenderables(); + + /** + * Draws all renderables matching the given map color with the given color. + * + * If map_color is -1, this functions draws all renderables of color which + * are not in the list of map colors (i.e. objects with undefined symbol). + * Used by FillTool to encode object IDs as colors. + */ + void draw(int map_color, const QColor& color, QPainter* painter, const RenderConfig& config) const; + + void setClipPath(const QPainterPath* path); + const QPainterPath* getClipPath() const; + + const QRectF& getExtent() const; + +private: + QRectF& extent; + const QPainterPath* clip_path = nullptr; // no memory management here! +}; + + + +/** + * A low-level container for renderables of multiple objects + * grouped by object and common render attributes. + * + * This container uses a smart pointer to the renderable collection + * of each single object. + */ +typedef std::map ObjectRenderablesMap; + + + +/** + * A high-level container for renderables of multiple objects + * grouped by color priority, object and common render attributes. + * + * This container is able to draw the renderables. + */ +class MapRenderables : protected std::map +{ +public: + /** + * An Object deleter which takes care of removing the renderables of the object. + * + * Synopsis: + * std::unique_ptr object { nullptr, { renderables } }; + */ + class ObjectDeleter + { + public: + MapRenderables& renderables; + void operator()(Object* object) const; + }; + + + MapRenderables(Map* map); + + /** + * Draws the renderables normally (one opaque over the other). + * + * @param painter The QPainter used for drawing. + * @param config The rendering configuration + */ + void draw(QPainter* painter, const RenderConfig& config) const; + + /** + * Draws the renderables in a spot color overprinting simulation. + * + * @param painter Must be a QPainter on a QImage of Format_ARGB32_Premultiplied. + * @param config The rendering configuration + */ + void drawOverprintingSimulation(QPainter* painter, const RenderConfig& config) const; + + /** + * Draws only the renderables which belong to a particular spot color. + * + * Separations are normally drawn in levels of gray where black means + * full tone of the spot color. The parameter use_color can be used to + * draw in the actual spot color instead. + * + * @param painter The QPainter used for drawing. + * @param config The rendering configuration + * @param separation The spot color to draw the separation for. + * @param use_color If true, forces the separation to be drawn in its actual color. + */ + void drawColorSeparation(QPainter* painter, const RenderConfig& config, + const MapColor* separation, bool use_color = false) const; + + void insertRenderablesOfObject(const Object* object); + + /* NOTE: does not delete the renderables, just removes them from display */ + void removeRenderablesOfObject(const Object* object, bool mark_area_as_dirty); + + void clear(bool mark_area_as_dirty = false); + + inline bool empty() const; + +private: + Map* const map; +}; + + + +// ### RenderConfig ### + +inline +bool RenderConfig::testFlag(const RenderConfig::Option flag) const +{ + return options.testFlag(flag); +} + + + +// ### Renderable ### + +inline +Renderable::Renderable(const MapColor* color) + : color_priority(color ? color->getPriority() : MapColor::Reserved) +{ + ; // nothing +} + +inline +const QRectF&Renderable::getExtent() const +{ + return extent; +} + +inline +bool Renderable::intersects(const QRectF& rect) const +{ + return extent.intersects(rect); +} + + + +// ### PainterConfig ### + +inline +bool operator==(const PainterConfig& lhs, const PainterConfig& rhs) +{ + return (lhs.color_priority == rhs.color_priority) && + (lhs.mode == rhs.mode) && + (lhs.pen_width == rhs.pen_width || lhs.mode == PainterConfig::BrushOnly) && + (lhs.clip_path != rhs.clip_path); +} + +inline +bool operator!=(const PainterConfig& lhs, const PainterConfig& rhs) +{ + return !(lhs == rhs); +} + +inline +bool operator<(const PainterConfig& lhs, const PainterConfig& rhs) +{ + // First, decide by priority + if (lhs.color_priority != rhs.color_priority) + return lhs.color_priority > rhs.color_priority; + + // Same priority, decide by clip path + else if (lhs.clip_path != rhs.clip_path) + return lhs.clip_path > rhs.clip_path; + + // Same clip path, decide by mode + else if ((int)lhs.mode != (int)rhs.mode) + return (int)lhs.mode > (int)rhs.mode; + + // Same mode, decide by pen width + else + return lhs.pen_width < rhs.pen_width; +} + + + +// ### ObjectRenderables ### + +inline +void ObjectRenderables::insertRenderable(Renderable* r) +{ + insertRenderable(r, r->getPainterConfig(clip_path)); +} + +inline +const QPainterPath* ObjectRenderables::getClipPath() const +{ + return clip_path; +} + +inline +const QRectF &ObjectRenderables::getExtent() const +{ + return extent; +} + + + +// ### MapRenderables ### + +inline +bool MapRenderables::empty() const +{ + return std::map::empty(); +} + + +} // namespace OpenOrienteering + + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::RenderConfig::Options) + + +#endif diff --git a/src/core/renderables/renderable_implementation.cpp b/src/core/renderables/renderable_implementation.cpp new file mode 100644 index 0000000..b7a21fc --- /dev/null +++ b/src/core/renderables/renderable_implementation.cpp @@ -0,0 +1,796 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "renderable_implementation.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// IWYU pragma: no_include + +#include "settings.h" +#include "core/map_coord.h" +#include "core/virtual_coord_vector.h" +#include "core/virtual_path.h" +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/renderables/renderable.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/text_symbol.h" +#include "util/util.h" + +#ifdef QT_PRINTSUPPORT_LIB +# include "advanced_pdf_printer.h" +#endif + +// IWYU pragma: no_forward_declare QFontMetricsF + + +namespace { + +/** + * When painting to a PDF engine, the miter limit must be adjusted from Qt's + * concept to PDF's concept. This should be done in the PDF engine, but this + * isn't the case. This may even result in PDF files which are considered + * invalid by Reader & Co. + * + * The PDF miter limit could be precalculated for the Mapper use cases, but + * this optimization would be lost when Qt gets fixed. + * + * Upstream issue: QTBUG-52641 + */ +inline void fixPenForPdf(QPen& pen, const QPainter& painter) +{ +#ifdef QT_PRINTSUPPORT_LIB + auto engine = painter.paintEngine()->type(); + if (Q_UNLIKELY(engine == QPaintEngine::Pdf + || engine == AdvancedPdfPrinter::paintEngineType())) + { + const auto miter_limit = pen.miterLimit(); + pen.setMiterLimit(qSqrt(1.0 + miter_limit * miter_limit * 4)); + } +#else + Q_UNUSED(pen) + Q_UNUSED(painter) +#endif +} + +} // namespace + + + +namespace OpenOrienteering { + +// ### DotRenderable ### + +DotRenderable::DotRenderable(const PointSymbol* symbol, MapCoordF coord) + : Renderable(symbol->getInnerColor()) +{ + double x = coord.x(); + double y = coord.y(); + double radius = (0.001 * symbol->getInnerRadius()); + extent = QRectF(x - radius, y - radius, 2 * radius, 2 * radius); +} + +PainterConfig DotRenderable::getPainterConfig(const QPainterPath* clip_path) const +{ + return { color_priority, PainterConfig::BrushOnly, 0, clip_path }; +} + +void DotRenderable::render(QPainter &painter, const RenderConfig &config) const +{ + if (config.options.testFlag(RenderConfig::ForceMinSize) && extent.width() * config.scaling < 1.5) + painter.drawEllipse(extent.center(), 0.5 / config.scaling, 0.5 / config.scaling); + else + painter.drawEllipse(extent); +} + + + +// ### CircleRenderable ### + +CircleRenderable::CircleRenderable(const PointSymbol* symbol, MapCoordF coord) + : Renderable(symbol->getOuterColor()) + , line_width(0.001 * symbol->getOuterWidth()) +{ + double x = coord.x(); + double y = coord.y(); + double radius = (0.001 * symbol->getInnerRadius()) + line_width/2; + rect = QRectF(x - radius, y - radius, 2 * radius, 2 * radius); + extent = QRectF(rect.x() - 0.5*line_width, rect.y() - 0.5*line_width, rect.width() + line_width, rect.height() + line_width); +} + +PainterConfig CircleRenderable::getPainterConfig(const QPainterPath* clip_path) const +{ + return { color_priority, PainterConfig::PenOnly, line_width, clip_path }; +} + +void CircleRenderable::render(QPainter &painter, const RenderConfig &config) const +{ + if (config.options.testFlag(RenderConfig::ForceMinSize) && rect.width() * config.scaling < 1.5) + painter.drawEllipse(rect.center(), 0.5 / config.scaling, 0.5 / config.scaling); + else + painter.drawEllipse(rect); +} + + + +// ### LineRenderable ### + +LineRenderable::LineRenderable(const LineSymbol* symbol, const VirtualPath& virtual_path, bool closed) + : Renderable(symbol->getColor()) + , line_width(0.001 * symbol->getLineWidth()) +{ + Q_ASSERT(virtual_path.size() >= 2); + + qreal half_line_width = (color_priority < 0) ? 0 : line_width/2; + + switch (symbol->getCapStyle()) + { + case LineSymbol::FlatCap: cap_style = Qt::FlatCap; break; + case LineSymbol::RoundCap: cap_style = Qt::RoundCap; break; + case LineSymbol::SquareCap: cap_style = Qt::SquareCap; break; + case LineSymbol::PointedCap: cap_style = Qt::FlatCap; break; + } + switch (symbol->getJoinStyle()) + { + case LineSymbol::BevelJoin: join_style = Qt::BevelJoin; break; + case LineSymbol::MiterJoin: join_style = Qt::MiterJoin; break; + case LineSymbol::RoundJoin: join_style = Qt::RoundJoin; break; + } + + auto& flags = virtual_path.coords.flags; + auto& coords = virtual_path.coords; + + bool has_curve = false; + bool hole = false; + bool gap = false; + QPainterPath first_subpath; + + auto i = virtual_path.first_index; + path.moveTo(coords[i]); + extent = QRectF(coords[i].x(), coords[i].y(), 0.0001, 0.0001); + extentIncludeCap(i, half_line_width, false, symbol, virtual_path); + + for (++i; i <= virtual_path.last_index; ++i) + { + if (gap) + { + if (flags[i].isHolePoint()) + { + gap = false; + hole = true; + } + else if (flags[i].isGapPoint()) + { + gap = false; + if (first_subpath.isEmpty() && closed) + { + first_subpath = path; + path = QPainterPath(); + } + path.moveTo(coords[i]); + extentIncludeCap(i, half_line_width, false, symbol, virtual_path); + } + continue; + } + + if (hole) + { + Q_ASSERT(!flags[i].isHolePoint() && "Two hole points in a row!"); + if (first_subpath.isEmpty() && closed) + { + first_subpath = path; + path = QPainterPath(); + } + path.moveTo(coords[i]); + extentIncludeCap(i, half_line_width, false, symbol, virtual_path); + hole = false; + continue; + } + + if (flags[i-1].isCurveStart()) + { + Q_ASSERT(i < virtual_path.last_index-1); + has_curve = true; + path.cubicTo(coords[i], coords[i+1], coords[i+2]); + i += 2; + } + else + path.lineTo(coords[i]); + + if (flags[i].isHolePoint()) + hole = true; + else if (flags[i].isGapPoint()) + gap = true; + + if ((i < virtual_path.last_index && !hole && !gap) || (i == virtual_path.last_index && closed)) + extentIncludeJoin(i, half_line_width, symbol, virtual_path); + else + extentIncludeCap(i, half_line_width, true, symbol, virtual_path); + } + + if (closed) + { + if (first_subpath.isEmpty()) + path.closeSubpath(); + else + path.connectPath(first_subpath); + } + + // If we do not have the path coords, but there was a curve, calculate path coords. + if (has_curve) + { + // This happens for point symbols with curved lines in them. + const auto& path_coords = virtual_path.path_coords; + Q_ASSERT(path_coords.front().param == 0.0f); + Q_ASSERT(path_coords.back().param == 0.0f); + for (auto i = path_coords.size()-1; i > 0; --i) + { + if (path_coords[i].param != 0.0f) + { + const auto& pos = path_coords[i].pos; + auto to_coord = pos - path_coords[i-1].pos; + auto to_next = path_coords[i+1].pos - pos; + to_coord.normalize(); + to_next.normalize(); + auto right = (to_coord + to_next).perpRight(); + right.setLength(half_line_width); + + rectInclude(extent, pos + right); + rectInclude(extent, pos - right); + } + } + } + Q_ASSERT(extent.right() < 60000000); // assert if bogus values are returned +} + +LineRenderable::LineRenderable(const LineSymbol* symbol, QPointF first, QPointF second) + : Renderable(symbol->getColor()) + , line_width(0.001 * symbol->getLineWidth()) + , cap_style(Qt::FlatCap) + , join_style(Qt::MiterJoin) +{ + qreal half_line_width = (color_priority < 0) ? 0 : line_width/2; + + auto right = MapCoordF(second - first).perpRight(); + right.normalize(); + right *= half_line_width; + + extent.setTopLeft(first + right); + rectInclude(extent, first - right); + rectInclude(extent, second - right); + rectInclude(extent, second + right); + + path.moveTo(first); + path.lineTo(second); +} + +void LineRenderable::extentIncludeCap(quint32 i, qreal half_line_width, bool end_cap, const LineSymbol* symbol, const VirtualPath& path) +{ + const auto& coord = path.coords[i]; + if (half_line_width < 0.0005) + { + rectInclude(extent, coord); + return; + } + + if (symbol->getCapStyle() == LineSymbol::RoundCap) + { + rectInclude(extent, QPointF(coord.x() - half_line_width, coord.y() - half_line_width)); + rectInclude(extent, QPointF(coord.x() + half_line_width, coord.y() + half_line_width)); + return; + } + + auto right = path.calculateTangent(i).perpRight(); + right.normalize(); + rectInclude(extent, coord + half_line_width * right); + rectInclude(extent, coord - half_line_width * right); + + if (symbol->getCapStyle() == LineSymbol::SquareCap) + { + auto back = right.perpRight(); + if (end_cap) + back = -back; + rectInclude(extent, coord + half_line_width * (back - right)); + rectInclude(extent, coord + half_line_width * (back + right)); + } +} + +void LineRenderable::extentIncludeJoin(quint32 i, qreal half_line_width, const LineSymbol* symbol, const VirtualPath& path) +{ + const auto& coord = path.coords[i]; + if (half_line_width < 0.0005) + { + rectInclude(extent, coord); + return; + } + + if (symbol->getJoinStyle() == LineSymbol::RoundJoin) + { + rectInclude(extent, QPointF(coord.x() - half_line_width, coord.y() - half_line_width)); + rectInclude(extent, QPointF(coord.x() + half_line_width, coord.y() + half_line_width)); + return; + } + + bool ok_to_coord, ok_to_next; + MapCoordF to_coord = path.calculateIncomingTangent(i, ok_to_coord); + MapCoordF to_next = path.calculateOutgoingTangent(i, ok_to_next); + if (!ok_to_next) + { + if (!ok_to_coord) + return; + to_next = to_coord; + } + else if (!ok_to_coord) + { + to_coord = to_next; + } + + auto r0 = to_coord.perpRight(); + r0.setLength(half_line_width); + auto r1 = to_next.perpRight(); + r1.setLength(half_line_width); + + auto to_coord_rhs = coord + r0; + auto to_coord_lhs = coord - r0; + auto to_next_rhs = coord + r1; + auto to_next_lhs = coord - r1; + if (symbol->getJoinStyle() == LineSymbol::BevelJoin) + { + rectInclude(extent, to_coord_rhs); + rectInclude(extent, to_coord_lhs); + rectInclude(extent, to_next_rhs); + rectInclude(extent, to_next_lhs); + return; + } + + auto limit = line_width * LineSymbol::miterLimit(); + to_coord.setLength(limit); + to_next.setLength(limit); + + const auto scaling = to_coord.y() * to_next.x() - to_coord.x() * to_next.y(); + if (qIsNull(scaling) || !qIsFinite(scaling)) + return; // straight line, no impact on extent + + // rhs boundary + auto p = to_coord_rhs - to_next_rhs; + auto factor = (to_next.y() * p.x() - to_next.x() * p.y()) / scaling; + if (factor > 1) + { + // outer boundary, intersection exceeds miter limit + rectInclude(extent, to_coord_rhs + to_coord); + rectInclude(extent, to_next_rhs - to_next); + } + else if (factor > 0) + { + // outer boundary, intersection within miter limit + rectInclude(extent, to_coord_rhs + to_coord * factor); + } + else + { + // inner boundary + rectInclude(extent, to_coord_rhs); + rectInclude(extent, to_next_rhs); + } + + // lhs boundary + p = to_coord_lhs - to_next_lhs; + factor = (to_next.y() * p.x() - to_next.x() * p.y()) / scaling; + if (factor > 1) + { + // outer boundary, intersection exceeds miter limit + rectInclude(extent, to_coord_lhs + to_coord); + rectInclude(extent, to_next_lhs - to_next); + } + else if (factor > 0) + { + // outer boundary, intersection within miter limit + rectInclude(extent, to_coord_lhs + to_coord * factor); + } + else + { + // inner boundary, catch rare cases + rectInclude(extent, to_coord_lhs); + rectInclude(extent, to_next_lhs); + } +} + +PainterConfig LineRenderable::getPainterConfig(const QPainterPath* clip_path) const +{ + return { color_priority, PainterConfig::PenOnly, line_width, clip_path }; +} + +void LineRenderable::render(QPainter &painter, const RenderConfig &config) const +{ + QPen pen(painter.pen()); + pen.setCapStyle(cap_style); + pen.setJoinStyle(join_style); + if (join_style == Qt::MiterJoin) + { + pen.setMiterLimit(LineSymbol::miterLimit()); + fixPenForPdf(pen, painter); + } + painter.setPen(pen); + + // One-time adjustment for line width + QRectF bounding_box = config.bounding_box.adjusted(-line_width, -line_width, line_width, line_width); + const int count = path.elementCount(); + if (count <= 2 || bounding_box.contains(path.controlPointRect())) + { + // path fully contained + painter.drawPath(path); + } + else + { + // Manually clip the path with bounding_box, this seems to be faster. + // The code splits up the painter path into new paths which intersect + // the view rect and renders these only. + // NOTE: this does not work correctly with miter joins, but this + // should be a minor issue. + QPainterPath::Element element = path.elementAt(0); + QPainterPath::Element last_element = path.elementAt(count-1); + bool path_closed = (element.x == last_element.x) && (element.y == last_element.y); + + QPainterPath part_path; + QPainterPath first_path; + bool path_started = false; + bool part_finished = false; + bool current_part_is_first = bounding_box.contains(element); + + QPainterPath::Element prev_element = element; + for (int i = 1; i < count; ++i) + { + element = path.elementAt(i); + if (element.isLineTo()) + { + qreal min_x, min_y, max_x, max_y; + if (prev_element.x < element.x) + { + min_x = prev_element.x; + max_x = element.x; + } + else + { + min_x = element.x; + max_x = prev_element.x; + } + if (prev_element.y < element.y) + { + min_y = prev_element.y; + max_y = element.y; + } + else + { + min_y = element.y; + max_y = prev_element.y; + } + if ( min_x <= bounding_box.right() && + max_x >= bounding_box.left() && + min_y <= bounding_box.bottom() && + max_y >= bounding_box.top() ) + { + if (!path_started) + { + part_path = QPainterPath(); + part_path.moveTo(prev_element.x, prev_element.y); + path_started = true; + } + part_path.lineTo(element.x, element.y); + } + else if (path_started) + { + part_finished = true; + } + else + { + current_part_is_first = false; + } + } + else if (element.isCurveTo()) + { + Q_ASSERT(i < count - 2); + QPainterPath::Element next_element = path.elementAt(i + 1); + QPainterPath::Element end_element = path.elementAt(i + 2); + + qreal min_x = qMin(prev_element.x, qMin(element.x, qMin(next_element.x, end_element.x))); + qreal min_y = qMin(prev_element.y, qMin(element.y, qMin(next_element.y, end_element.y))); + qreal max_x = qMax(prev_element.x, qMax(element.x, qMax(next_element.x, end_element.x))); + qreal max_y = qMax(prev_element.y, qMax(element.y, qMax(next_element.y, end_element.y))); + + if ( min_x <= bounding_box.right() && + max_x >= bounding_box.left() && + min_y <= bounding_box.bottom() && + max_y >= bounding_box.top() ) + { + if (!path_started) + { + part_path = QPainterPath(); + part_path.moveTo(prev_element.x, prev_element.y); + path_started = true; + } + part_path.cubicTo(element.x, element.y, next_element.x, next_element.y, end_element.x, end_element.y); + } + else if (path_started) + { + part_finished = true; + } + else + { + current_part_is_first = false; + } + } + else if (element.isMoveTo() && path_started) + { + part_path.moveTo(element.x, element.y); + } + + if (part_finished) + { + if (current_part_is_first && path_closed) + { + current_part_is_first = false; + first_path = part_path; + } + else + { + painter.drawPath(part_path); + } + + path_started = false; + part_finished = false; + } + + prev_element = element; + } + + if (path_started) + { + if (path_closed && !first_path.isEmpty()) + part_path.connectPath(first_path); + + painter.drawPath(part_path); + } + } + + // DEBUG: show all control points + /*QPen debugPen(QColor(Qt::red)); + painter.setPen(debugPen); + for (int i = 0; i < path.elementCount(); ++i) + { + const QPainterPath::Element& e = path.elementAt(i); + painter.drawEllipse(QPointF(e.x, e.y), 0.2f, 0.2f); + } + painter.setPen(pen);*/ +} + +// ### AreaRenderable ### + +AreaRenderable::AreaRenderable(const AreaSymbol* symbol, const PathPartVector& path_parts) + : Renderable(symbol->getColor()) +{ + if (!path_parts.empty()) + { + auto part = begin(path_parts); + if (part->size() > 2) + { + extent = part->path_coords.calculateExtent(); + addSubpath(*part); + + auto last = end(path_parts); + for (++part; part != last; ++part) + { + rectInclude(extent, part->path_coords.calculateExtent()); + addSubpath(*part); + } + } + } + Q_ASSERT(extent.right() < 60000000); // assert if bogus values are returned +} + +AreaRenderable::AreaRenderable(const AreaSymbol* symbol, const VirtualPath& path) + : Renderable(symbol->getColor()) +{ + extent = path.path_coords.calculateExtent(); + addSubpath(path); +} + +void AreaRenderable::addSubpath(const VirtualPath& virtual_path) +{ + auto& flags = virtual_path.coords.flags; + auto& coords = virtual_path.coords; + Q_ASSERT(!flags.data().empty()); + + auto i = virtual_path.first_index; + path.moveTo(coords[i]); + for (++i; i <= virtual_path.last_index; ++i) + { + if (flags[i-1].isCurveStart()) + { + Q_ASSERT(i+2 < coords.size()); + path.cubicTo(coords[i], coords[i+1], coords[i+2]); + i += 2; + } + else + { + path.lineTo(coords[i]); + } + } + path.closeSubpath(); +} + +PainterConfig AreaRenderable::getPainterConfig(const QPainterPath* clip_path) const +{ + return { color_priority, PainterConfig::BrushOnly, 0, clip_path }; +} + +void AreaRenderable::render(QPainter &painter, const RenderConfig &/*config*/) const +{ + painter.drawPath(path); + + // DEBUG: show all control points + /*QPen pen(painter.pen()); + QBrush brush(painter.brush()); + QPen debugPen(QColor(Qt::red)); + painter.setPen(debugPen); + painter.setBrush(Qt::NoBrush); + for (int i = 0; i < path.elementCount(); ++i) + { + const QPainterPath::Element& e = path.elementAt(i); + painter.drawEllipse(QPointF(e.x, e.y), 0.1f, 0.1f); + } + painter.setPen(pen); + painter.setBrush(brush);*/ +} + + + +// ### TextRenderable ### + +TextRenderable::TextRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y) +: Renderable { color } +, anchor_x { anchor_x } +, anchor_y { anchor_y } +, rotation { 0.0 } +, scale_factor { symbol->getFontSize() / TextSymbol::internal_point_size } +{ + path.setFillRule(Qt::WindingFill); // Otherwise, when text and an underline intersect, holes appear + + const QFont& font(symbol->getQFont()); + const QFontMetricsF& metrics(symbol->getFontMetrics()); + + int num_lines = text_object->getNumLines(); + for (int i=0; i < num_lines; i++) + { + const TextObjectLineInfo* line_info = text_object->getLineInfo(i); + + double line_y = line_info->line_y; + + double underline_x0 = 0.0; + double underline_y0 = line_info->line_y + metrics.underlinePos(); + double underline_y1 = underline_y0 + metrics.lineWidth(); + + auto num_parts = line_info->part_infos.size(); + for (std::size_t j=0; j < num_parts; j++) + { + const TextObjectPartInfo& part(line_info->part_infos.at(j)); + if (font.underline()) + { + if (j > 0) + { + // draw underline for gap between parts as rectangle + // TODO: watch out for inconsistency between text and gap underline + path.moveTo(underline_x0, underline_y0); + path.lineTo(part.part_x, underline_y0); + path.lineTo(part.part_x, underline_y1); + path.lineTo(underline_x0, underline_y1); + path.closeSubpath(); + } + underline_x0 = part.part_x; + } + path.addText(part.part_x, line_y, font, part.part_text); + } + } + + QTransform t { 1.0, 0.0, 0.0, 1.0, anchor_x, anchor_y }; + t.scale(scale_factor, scale_factor); + + auto rotation_rad = qreal(text_object->getRotation()); + if (!qIsNull(rotation_rad)) + { + rotation = -qRadiansToDegrees(rotation_rad); + t.rotate(rotation); + } + + extent = t.mapRect(path.controlPointRect()); +} + +PainterConfig TextRenderable::getPainterConfig(const QPainterPath* clip_path) const +{ + return { color_priority, PainterConfig::BrushOnly, 0.0, clip_path }; +} + +void TextRenderable::render(QPainter &painter, const RenderConfig &config) const +{ + painter.save(); + renderCommon(painter, config); + painter.restore(); +} + +void TextRenderable::renderCommon(QPainter& painter, const RenderConfig& config) const +{ + bool disable_antialiasing = config.options.testFlag(RenderConfig::Screen) && !(Settings::getInstance().getSettingCached(Settings::MapDisplay_TextAntialiasing).toBool()); + if (disable_antialiasing) + { + painter.setRenderHint(QPainter::Antialiasing, false); + painter.setRenderHint(QPainter::TextAntialiasing, false); + } + + painter.translate(anchor_x, anchor_y); + if (rotation != 0.0) + painter.rotate(rotation); + painter.scale(scale_factor, scale_factor); + painter.drawPath(path); +} + + + +// ### TextRenderable ### + +TextFramingRenderable::TextFramingRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y) +: TextRenderable { symbol, text_object, color, anchor_x, anchor_y } +, framing_line_width { 2 * 0.001 * symbol->getFramingLineHalfWidth() / scale_factor } +{ + auto adjustment = 0.001 * symbol->getFramingLineHalfWidth() ; + extent.adjust(-adjustment, -adjustment, +adjustment, +adjustment); +} + +PainterConfig TextFramingRenderable::getPainterConfig(const QPainterPath* clip_path) const +{ + return { color_priority, PainterConfig::PenOnly, framing_line_width, clip_path }; +} + +void TextFramingRenderable::render(QPainter& painter, const RenderConfig& config) const +{ + painter.save(); + QPen pen(painter.pen()); + pen.setJoinStyle(Qt::MiterJoin); + pen.setMiterLimit(0.5); + fixPenForPdf(pen, painter); + painter.setPen(pen); + TextRenderable::renderCommon(painter, config); + painter.restore(); +} + + +} // namespace OpenOrienteering diff --git a/src/core/renderables/renderable_implementation.h b/src/core/renderables/renderable_implementation.h new file mode 100644 index 0000000..5af692e --- /dev/null +++ b/src/core/renderables/renderable_implementation.h @@ -0,0 +1,150 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_RENDERABLE_IMPLENTATION_H +#define OPENORIENTEERING_RENDERABLE_IMPLENTATION_H + +#include +#include +#include +#include +#include + +#include "renderable.h" + +class QPainter; +class QPointF; + +namespace OpenOrienteering { + +class AreaSymbol; +class LineSymbol; +class MapColor; +class MapCoordF; +class PathPartVector; +class PointSymbol; +class TextObject; +class TextSymbol; +class VirtualPath; + + +/** Renderable for displaying a filled dot. */ +class DotRenderable : public Renderable +{ +public: + DotRenderable(const PointSymbol* symbol, MapCoordF coord); + void render(QPainter& painter, const RenderConfig& config) const override; + PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; +}; + +/** Renderable for displaying a circle. */ +class CircleRenderable : public Renderable +{ +public: + CircleRenderable(const PointSymbol* symbol, MapCoordF coord); + void render(QPainter& painter, const RenderConfig& config) const override; + PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; + +protected: + const qreal line_width; + QRectF rect; +}; + +/** Renderable for displaying a line. */ +class LineRenderable : public Renderable +{ +public: + LineRenderable(const LineSymbol* symbol, const VirtualPath& virtual_path, bool closed); + LineRenderable(const LineSymbol* symbol, QPointF first, QPointF second); + void render(QPainter& painter, const RenderConfig& config) const override; + PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; + +protected: + void extentIncludeCap(quint32 i, qreal half_line_width, bool end_cap, const LineSymbol* symbol, const VirtualPath& path); + + void extentIncludeJoin(quint32 i, qreal half_line_width, const LineSymbol* symbol, const VirtualPath& path); + + const qreal line_width; + QPainterPath path; + Qt::PenCapStyle cap_style; + Qt::PenJoinStyle join_style; +}; + +/** Renderable for displaying an area. */ +class AreaRenderable : public Renderable +{ +public: + AreaRenderable(const AreaSymbol* symbol, const PathPartVector& path_parts); + AreaRenderable(const AreaSymbol* symbol, const VirtualPath& path); + void render(QPainter& painter, const RenderConfig& config) const override; + PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; + + inline const QPainterPath* painterPath() const; + +protected: + void addSubpath(const VirtualPath& virtual_path); + + QPainterPath path; +}; + +/** Renderable for displaying text. */ +class TextRenderable : public Renderable +{ +public: + TextRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y); + PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; + void render(QPainter& painter, const RenderConfig& config) const override; + +protected: + void renderCommon(QPainter& painter, const RenderConfig& config) const; + + QPainterPath path; + qreal anchor_x; + qreal anchor_y; + qreal rotation; + qreal scale_factor; +}; + +/** Renderable for displaying framing line for text. */ +class TextFramingRenderable : public TextRenderable +{ +public: + TextFramingRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y); + PainterConfig getPainterConfig(const QPainterPath* clip_path = nullptr) const override; + void render(QPainter& painter, const RenderConfig& config) const override; + +protected: + qreal framing_line_width; +}; + + + +// ### AreaRenderable inline code ### + +const QPainterPath* AreaRenderable::painterPath() const +{ + return &path; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/storage_location.cpp b/src/core/storage_location.cpp new file mode 100644 index 0000000..aafa7b0 --- /dev/null +++ b/src/core/storage_location.cpp @@ -0,0 +1,306 @@ +/* + * Copyright 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "storage_location.h" + +#include + +#include +#include +#include +#include + + +#ifdef Q_OS_ANDROID +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace OpenOrienteering { + +namespace Android { + +/** + * The cache of known locations. + */ +static std::shared_ptr> locations_cache; + + +void mediaScannerScanFile(const QString& path) +{ + static const auto ACTION_MEDIA_SCANNER_SCAN_FILE = + QAndroidJniObject::getStaticObjectField("android/content/Intent", + "ACTION_MEDIA_SCANNER_SCAN_FILE"); + auto intent = QAndroidJniObject { "android/content/Intent", + "(Ljava/lang/String;)V", + ACTION_MEDIA_SCANNER_SCAN_FILE.object() }; + auto file = QAndroidJniObject { "java/io/File", + "(Ljava/lang/String;)V", + QAndroidJniObject::fromString(path).object() }; + auto uri = QAndroidJniObject::callStaticObjectMethod( + "android/net/Uri", + "fromFile", + "(Ljava/io/File;)Landroid/net/Uri;", + file.object()); + + intent.callObjectMethod("setData", + "(Landroid/net/Uri;)Landroid/content/Intent;", + uri.object()); + auto activity = QtAndroid::androidActivity(); + auto context = activity.callObjectMethod( + "getApplicationContext", + "()Landroid/content/Context;"); + context.callMethod("sendBroadcast", "(Landroid/content/Intent;)V", intent.object()); +} + +/** + * Returns writable application-specific directories on all external storage volumes. + * + * These directories are named "Android/data/PACKAGENAME" and use "synthesized + * permissions", i.e. apps from the package may write to these folders without needing + * explicit permissions. Other apps may read these files. + * + * Files in this location will be deleted when the app is uninstalled. + * + * Requires API level 19. + * + * \see https://developer.android.com/reference/android/content/Context.html#getExternalFilesDirs(java.lang.String) + */ +std::vector getExternalFilesDirs(jstring* type) +{ + auto activity = QtAndroid::androidActivity(); + auto context = activity.callObjectMethod( + "getApplicationContext", + "()Landroid/content/Context;"); + + // Get directories with synthesized permissions on all external storage volumes + auto external_files_dirs = context.callObjectMethod( + "getExternalFilesDirs", + "(Ljava/lang/String;)[Ljava/io/File;", + type); + + QAndroidJniEnvironment jni; + const auto length = jni->GetArrayLength(external_files_dirs.object()); + + std::vector locations; + locations.reserve(std::size_t(length)); + for (auto i = 0; i < length; ++i) + { + auto location_jni = jni->GetObjectArrayElement(external_files_dirs.object(), i); + auto location = QAndroidJniObject{ location_jni }.toString(); + + const auto warning_path = QString(location + QLatin1String("/README.html")); + QFile warning(warning_path); + if (warning.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + auto android_start = location.indexOf(QLatin1String("/Android/data/")); + warning.write("

    \n"); + warning.write(StorageLocation::fileHintTextTemplate(StorageLocation::HintApplication).arg(location.mid(android_start+1)).toUtf8()); + warning.write("\n

    "); + warning.close(); + locations.push_back(location); + mediaScannerScanFile(warning_path); + } + } + return locations; +} + +/** + * Constructs a list of OOMapper folders on secondary storage volumes + */ +std::vector getLegacySecondaryStorage(const QString& primary_storage, const std::vector& external_files_dirs) +{ + std::vector result; + result.reserve(external_files_dirs.size()); + for (const auto& dir : external_files_dirs) + { + if (dir.startsWith(primary_storage)) + continue; + + auto index = dir.indexOf(QLatin1String("Android/data/")); + if (index < 0) + continue; + + result.push_back(dir.leftRef(index) + QLatin1String("OOMapper")); + } + return result; +} + +/** + * Constructs the cache of known storage locations. + */ +std::shared_ptr> buildLocationCache() +{ + std::vector locations_normal; + std::vector locations_application; + std::vector locations_readonly; + + // API level 1, single primary external storage + auto primary_storage = QAndroidJniObject::callStaticObjectMethod( + "android/os/Environment", + "getExternalStorageDirectory", + "()Ljava/io/File;", + nullptr).toString(); + + // Easy: "OOMapper" folder on primary external storage. + // Write access depends on the WRITE_EXTERNAL_STORAGE permission, which + // may be revoked by the user since Android 6.0. (Read access depends on the + // READ_EXTERNAL_STORAGE permission only when write access is not given.) + const QFileInfo primary_storage_oomapper { primary_storage + QLatin1String("/OOMapper") }; + if (primary_storage_oomapper.exists()) + { + const auto path = primary_storage_oomapper.filePath(); + if (primary_storage_oomapper.isWritable()) + locations_normal.push_back(path); + else + locations_readonly.push_back(path); + mediaScannerScanFile(path); + } + + // Volatile: Application-specific directories on external storage. + if (QtAndroid::androidSdkVersion() >= 19) + { + // API level 19 + locations_application = Android::getExternalFilesDirs(nullptr); + } + + // Difficult: "OOMapper" folder on secondary external storage. + // Read access, but (normally) no write access. + std::vector secondary_storage_paths; + const auto env = QProcessEnvironment::systemEnvironment(); + auto env_secondary_storage = env.value(QLatin1String("SECONDARY_STORAGE")); + if (!env_secondary_storage.isEmpty()) + { + // Mapper legacy approach, API level < 19 + const auto paths = env_secondary_storage.splitRef(QLatin1Char{';'}); + secondary_storage_paths.reserve(std::size_t(paths.size())); + for (const auto& path : paths) + secondary_storage_paths.emplace_back(path + QLatin1String("/OOMapper")); + } + else if (!locations_application.empty()) + { + // API level 19 + secondary_storage_paths = Android::getLegacySecondaryStorage(primary_storage, locations_application); + } + for (const auto& path : secondary_storage_paths) + { + const QFileInfo secondary_storage { path }; + if (secondary_storage.exists()) + { + if (secondary_storage.isWritable()) + locations_normal.push_back(path); + else + locations_readonly.push_back(path); + mediaScannerScanFile(path); + } + } + + auto locations = std::make_shared>(); + locations->reserve(locations_normal.size() + + locations_application.size() + + locations_readonly.size()); + for (const auto& path : locations_normal) + locations->emplace_back(path, StorageLocation::HintNormal); + for (const auto& path : locations_application) + locations->emplace_back(path, StorageLocation::HintApplication); + for (const auto& path : locations_readonly) + locations->emplace_back(path, StorageLocation::HintReadOnly); + + return locations; +} + + +} // namespace Android + +} // namespace OpenOrienteering + +#endif + + +namespace OpenOrienteering { + +// static +std::shared_ptr> StorageLocation::knownLocations() +{ +#ifdef Q_OS_ANDROID + auto locations = Android::locations_cache; + if (!locations) + { + locations = Android::buildLocationCache(); + Android::locations_cache = locations; + } + Q_ASSERT(locations); + return locations; +#else + auto locations = std::make_shared>(); + auto paths = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); + locations->reserve(std::size_t(paths.size())); + for (const auto& path : paths) + { + locations->emplace_back(path, HintNormal); + } + return locations; +#endif +} + + +void StorageLocation::refresh() +{ +#ifdef Q_OS_ANDROID + Android::locations_cache.reset(); +#endif +} + + +// static +QString StorageLocation::fileHintTextTemplate(Hint hint) +{ + switch (hint) + { + case HintNormal: + return {}; // No text for a regular location. + + case HintApplication: + return tr("'%1' is located in app storage. The files will be removed when uninstalling the app."); + + case HintReadOnly: + return tr("'%1' is not writable. Changes cannot be saved."); + + case HintInvalid: + return tr("'%1' is not a valid storage location."); + } + + Q_UNREACHABLE(); +} + + +QString OpenOrienteering::StorageLocation::hintText() const +{ + return hint() == HintNormal ? QString{} : fileHintTextTemplate(hint()).arg(path()); +} + + +} // namespace OpenOrienteering diff --git a/src/core/storage_location.h b/src/core/storage_location.h new file mode 100644 index 0000000..9537ab7 --- /dev/null +++ b/src/core/storage_location.h @@ -0,0 +1,140 @@ +/* + * Copyright 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_STORAGE_LOCATION_H +#define OPENORIENTEERING_STORAGE_LOCATION_H + + +#include +#include +#include + +#include +#include + +namespace OpenOrienteering { + +namespace Android { + +/** + * Tells the media scanner to register the given file or folder. + * + * This is required to make files quickly available for transfer via MTP. + */ +void mediaScannerScanFile(const QString& path); + +} + + +// noexcept since Qt 5.5 +constexpr bool qstring_is_nothrow_copy_constructible = std::is_nothrow_copy_constructible::value; +constexpr bool qstring_is_nothrow_move_constructible = std::is_nothrow_move_constructible::value; + + +/** + * Provides information about document storage locations. + */ +class StorageLocation +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::StorageLocation) + +public: + /** Various hints about locations. */ + enum Hint { + HintNormal, ///< Normal location + HintApplication, ///< Location which might get cleaned unexpectedly + HintReadOnly, ///< Read-only location + HintInvalid ///< Not a valid location at all + }; + + /** Constructs a new location. */ + StorageLocation(const QString& path, Hint hint) noexcept; + + /** Default copy constructor. */ + StorageLocation(const StorageLocation&) noexcept(qstring_is_nothrow_copy_constructible) = default; + + /** Default move constructor. */ + StorageLocation(StorageLocation&&) noexcept(qstring_is_nothrow_move_constructible) = default; + + + /** Returns the path of this location. */ + QString path() const; + + /** + * Returns the hint for this location. + * + * This function returns an empty string for HintNormal. + */ + Hint hint() const; + + /** Returns the text representing the hint for this location. */ + QString hintText() const; + + /** + * Returns a text template for giving the hint for the given path. + * + * This function returns an empty string for HintNormal. + */ + static QString fileHintTextTemplate(Hint hint); + + + /** + * Returns the known locations for documents. + * + * The returned shared-ptr will always have an object, even if it is an empty list. + */ + static std::shared_ptr> knownLocations(); + + + /** Forces a new scan of locations on the next call to knownLocations(). */ + static void refresh(); + + +private: + const QString m_path; + const Hint m_hint; +}; + + + +inline +StorageLocation::StorageLocation(const QString& path, StorageLocation::Hint hint) noexcept +: m_path { path } +, m_hint { hint } +{ + // nothing else +} + +inline +QString StorageLocation::path() const +{ + return m_path; +} + +inline +StorageLocation::Hint StorageLocation::hint() const +{ + return m_hint; +} + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_STORAGE_LOCATION_H diff --git a/src/core/symbols/area_symbol.cpp b/src/core/symbols/area_symbol.cpp new file mode 100644 index 0000000..ebcf6dd --- /dev/null +++ b/src/core/symbols/area_symbol.cpp @@ -0,0 +1,887 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "area_symbol.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include // IWYU pragma: keep + +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/objects/object.h" +#include "core/renderables/renderable.h" +#include "core/renderables/renderable_implementation.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "core/virtual_coord_vector.h" +#include "util/xml_stream_util.h" + +class QXmlStreamWriter; +// IWYU pragma: no_forward_declare QXmlStreamReader + + +namespace OpenOrienteering { + +// ### FillPattern ### + +AreaSymbol::FillPattern::FillPattern() noexcept +: type { LinePattern } +, flags { Default } +, angle { 0 } +, line_spacing { 5000 } // 5 mm +, line_offset { 0 } +, line_color { nullptr } +, line_width { 200 } // 0.2 mm +, offset_along_line { 0 } +, point_distance { 5000 } // 5 mm +, point { nullptr } +, name {} +{ + // nothing else +} + +#ifndef NO_NATIVE_FILE_FORMAT + +bool AreaSymbol::FillPattern::load(QIODevice* file, int version, Map* map) +{ + flags = Option::Default; + qint32 itype; + file->read((char*)&itype, sizeof(qint32)); + type = (Type)itype; + file->read((char*)&angle, sizeof(float)); + if (version >= 4) + { + bool is_rotatable; + file->read((char*)&is_rotatable, sizeof(bool)); + setRotatable(is_rotatable); + } + file->read((char*)&line_spacing, sizeof(int)); + if (version >= 3) + { + file->read((char*)&line_offset, sizeof(int)); + file->read((char*)&offset_along_line, sizeof(int)); + } + + if (type == LinePattern) + { + int color_index; + file->read((char*)&color_index, sizeof(int)); + line_color = (color_index >= 0) ? map->getColor(color_index) : nullptr; + file->read((char*)&line_width, sizeof(int)); + } + else + { + file->read((char*)&point_distance, sizeof(int)); + bool have_point; + file->read((char*)&have_point, sizeof(bool)); + if (have_point) + { + point = new PointSymbol(); + if (!point->load(file, version, map)) + return false; + if (version < 21) + point->setRotatable(true); + } + else + point = nullptr; + } + return true; +} + +#endif + +void AreaSymbol::FillPattern::save(QXmlStreamWriter& xml, const Map& map) const +{ + XmlElementWriter element { xml, QLatin1String("pattern") }; + element.writeAttribute(QLatin1String("type"), type); + element.writeAttribute(QLatin1String("angle"), angle); + if (auto no_clipping = int(flags & Option::AlternativeToClipping)) + element.writeAttribute(QLatin1String("no_clipping"), no_clipping); + if (rotatable()) + element.writeAttribute(QLatin1String("rotatable"), true); + element.writeAttribute(QLatin1String("line_spacing"), line_spacing); + element.writeAttribute(QLatin1String("line_offset"), line_offset); + element.writeAttribute(QLatin1String("offset_along_line"), offset_along_line); + + switch (type) + { + case LinePattern: + element.writeAttribute(QLatin1String("color"), map.findColorIndex(line_color)); + element.writeAttribute(QLatin1String("line_width"), line_width); + break; + + case PointPattern: + element.writeAttribute(QLatin1String("point_distance"), point_distance); + if (point) + point->save(xml, map); + break; + + } +} + +void AreaSymbol::FillPattern::load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) +{ + Q_ASSERT (xml.name() == QLatin1String("pattern")); + + XmlElementReader element { xml }; + type = element.attribute(QLatin1String("type")); + angle = element.attribute(QLatin1String("angle")); + flags = Options{element.attribute(QLatin1String("no_clipping")) & Option::AlternativeToClipping}; + if (element.attribute(QLatin1String("rotatable"))) + flags |= Option::Rotatable; + line_spacing = element.attribute(QLatin1String("line_spacing")); + line_offset = element.attribute(QLatin1String("line_offset")); + offset_along_line = element.attribute(QLatin1String("offset_along_line")); + + switch (type) + { + case LinePattern: + line_color = map.getColor(element.attribute(QLatin1String("color"))); + line_width = element.attribute(QLatin1String("line_width")); + break; + + case PointPattern: + point_distance = element.attribute(QLatin1String("point_distance")); + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("symbol")) + point = static_cast(Symbol::load(xml, map, symbol_dict)); + else + xml.skipCurrentElement(); + } + break; + + } +} + +bool AreaSymbol::FillPattern::equals(const AreaSymbol::FillPattern& other, Qt::CaseSensitivity case_sensitivity) const +{ + if (type != other.type) + return false; + if (qAbs(angle - other.angle) > 1e-05f) + return false; + if (flags != other.flags) + return false; + if (line_spacing != other.line_spacing) + return false; + if (line_offset != other.line_offset) + return false; + + if (type == PointPattern) + { + if (offset_along_line != other.offset_along_line) + return false; + if (point_distance != other.point_distance) + return false; + if (bool(point) != bool(other.point)) + return false; + if (point && !point->equals(other.point, case_sensitivity)) + return false; + } + else if (type == LinePattern) + { + if (!MapColor::equal(line_color, other.line_color)) + return false; + if (line_width != other.line_width) + return false; + } + + if (name.compare(other.name, case_sensitivity) != 0) + return false; + + return true; +} + + + +void AreaSymbol::FillPattern::setRotatable(bool value) +{ + flags = value ? (flags | Option::Rotatable) : (flags & ~Option::Rotatable); +} + + + +void AreaSymbol::FillPattern::setClipping(Options clipping) +{ + flags = (flags & ~Option::AlternativeToClipping) | (clipping & Option::AlternativeToClipping); +} + + + +void AreaSymbol::FillPattern::colorDeleted(const MapColor* color) +{ + switch (type) + { + case FillPattern::PointPattern: + point->colorDeleted(color); + break; + case FillPattern::LinePattern: + if (line_color == color) + line_color = nullptr; + break; + } +} + + +bool AreaSymbol::FillPattern::containsColor(const MapColor* color) const +{ + switch (type) + { + case FillPattern::PointPattern: + return point && point->containsColor(color); + case FillPattern::LinePattern: + return line_color == color; + } + Q_UNREACHABLE(); +} + + +const MapColor* AreaSymbol::FillPattern::guessDominantColor() const +{ + const MapColor* color = nullptr; + switch (type) + { + case FillPattern::PointPattern: + if (point) + color = point->guessDominantColor(); + break; + case FillPattern::LinePattern: + color = line_color; + break; + } + return color; +} + + + +template <> +inline +void AreaSymbol::FillPattern::createLine( + MapCoordF first, MapCoordF second, + qreal, + LineSymbol* line, + float, + const AreaRenderable&, + ObjectRenderables& output ) const +{ + // out of inlining + output.insertRenderable(new LineRenderable(line, first, second)); +} + + +template <> +inline +void AreaSymbol::FillPattern::createLine( + MapCoordF first, MapCoordF second, + qreal delta_offset, + LineSymbol*, + float rotation, + const AreaRenderable& outline, + ObjectRenderables& output ) const +{ + // out of inlining + createPointPatternLine(first, second, delta_offset, rotation, outline, output); +} + + +// This template will be instantiated in non-template createRenderables() +// once for each type of pattern, thus duplicating the complex body. +// This is by intention, in order to let the compiler optimize each +// instantiation independently, with regard to unused parameters in +// createLine(), and to eliminate any runtime checks for pattern type +// outside of non-template createRenderables(). +template +void AreaSymbol::FillPattern::createRenderables( + const AreaRenderable& outline, + float delta_rotation, + const MapCoord& pattern_origin, + const QRectF& point_extent, + LineSymbol* line, + qreal rotation, + ObjectRenderables& output ) const +{ + auto extent = outline.getExtent(); + extent.adjust(-point_extent.right(), -point_extent.bottom(), -point_extent.left(), -point_extent.top()); + + MapCoordF first, second; + + // Fill + qreal delta_line_offset = 0; + qreal delta_along_line_offset = 0; + if (rotatable()) + { + MapCoordF line_normal(0, -1); + line_normal.rotate(rotation); + line_normal.setY(-line_normal.y()); + delta_line_offset = MapCoordF::dotProduct(line_normal, MapCoordF(pattern_origin)); + + MapCoordF line_tangent(1, 0); + line_tangent.rotate(rotation); + line_tangent.setY(-line_tangent.y()); + delta_along_line_offset = MapCoordF::dotProduct(line_tangent, MapCoordF(pattern_origin)); + } + + auto line_spacing_f = 0.001*line_spacing; + + const auto offset = 0.001 * line_offset + delta_line_offset; + if (qAbs(rotation - M_PI/2) < 0.0001) + { + // Special case: vertical lines + delta_along_line_offset = -delta_along_line_offset; + + double first_offset = offset + ceil((extent.left() - offset) / line_spacing_f) * line_spacing_f; + for (double cur = first_offset; cur < extent.right(); cur += line_spacing_f) + { + first = MapCoordF(cur, extent.top()); + second = MapCoordF(cur, extent.bottom()); + createLine(first, second, delta_along_line_offset, line, delta_rotation, outline, output); + } + } + else if (qAbs(rotation - 0) < 0.0001) + { + // Special case: horizontal lines + double first_offset = offset + ceil((extent.top() - offset) / line_spacing_f) * line_spacing_f; + for (double cur = first_offset; cur < extent.bottom(); cur += line_spacing_f) + { + first = MapCoordF(extent.left(), cur); + second = MapCoordF(extent.right(), cur); + createLine(first, second, delta_along_line_offset, line, delta_rotation, outline, output); + } + } + else + { + // General case + if (rotation < M_PI / 2) + delta_along_line_offset = -delta_along_line_offset; + + auto xfactor = 1.0 / sin(rotation); + auto yfactor = 1.0 / cos(rotation); + + auto dist_x = xfactor * line_spacing_f; + auto dist_y = yfactor * line_spacing_f; + auto offset_x = xfactor * offset; + auto offset_y = yfactor * offset; + + if (rotation < M_PI/2) + { + // Start with the upper left corner + offset_x += (-extent.top()) / tan(rotation); + offset_y -= extent.left() * tan(rotation); + auto start_x = offset_x + ceil((extent.x() - offset_x) / dist_x) * dist_x; + auto start_y = extent.top(); + auto end_x = extent.left(); + auto end_y = offset_y + ceil((extent.y() - offset_y) / dist_y) * dist_y; + + do + { + // Correct coordinates + if (start_x > extent.right()) + { + start_y += ((start_x - extent.right()) / dist_x) * dist_y; + start_x = extent.right(); + } + if (end_y > extent.bottom()) + { + end_x += ((end_y - extent.bottom()) / dist_y) * dist_x; + end_y = extent.bottom(); + } + + if (start_y > extent.bottom()) + break; + + // Create the renderable(s) + first = MapCoordF(start_x, start_y); + second = MapCoordF(end_x, end_y); + createLine(first, second, delta_along_line_offset, line, delta_rotation, outline, output); + + // Move to next position + start_x += dist_x; + end_y += dist_y; + } while (true); + } + else + { + // Start with left lower corner + offset_x += (-extent.bottom()) / tan(rotation); + offset_y -= extent.x() * tan(rotation); + auto start_x = offset_x + ceil((extent.x() - offset_x) / dist_x) * dist_x; + auto start_y = extent.bottom(); + auto end_x = extent.x(); + auto end_y = offset_y + ceil((extent.bottom() - offset_y) / dist_y) * dist_y; + + do + { + // Correct coordinates + if (start_x > extent.right()) + { + start_y += ((start_x - extent.right()) / dist_x) * dist_y; + start_x = extent.right(); + } + if (end_y < extent.y()) + { + end_x += ((end_y - extent.y()) / dist_y) * dist_x; + end_y = extent.y(); + } + + if (start_y < extent.y()) + break; + + // Create the renderable(s) + first = MapCoordF(start_x, start_y); + second = MapCoordF(end_x, end_y); + createLine(first, second, delta_along_line_offset, line, delta_rotation, outline, output); + + // Move to next position + start_x += dist_x; + end_y += dist_y; + } while (true); + } + } +} + + +void AreaSymbol::FillPattern::createRenderables(const AreaRenderable& outline, float delta_rotation, const MapCoord& pattern_origin, ObjectRenderables& output) const +{ + if (line_spacing <= 0) + return; + + if (!rotatable()) + delta_rotation = 0; + + // Make rotation unique + auto rotation = double(angle + delta_rotation); + rotation = fmod(1.0 * rotation, M_PI); + if (rotation < 0) + rotation = M_PI + rotation; + Q_ASSERT(rotation >= 0 && rotation <= M_PI); + + // Handle clipping + const auto old_clip_path = output.getClipPath(); + if (!(flags & Option::AlternativeToClipping)) + { + output.setClipPath(outline.painterPath()); + } + + switch (type) + { + case LinePattern: + { + LineSymbol line; + line.setColor(line_color); + + auto line_width_f = 0.001*line_width; + line.setLineWidth(line_width_f); + + auto margin = line_width_f / 2; + auto point_extent = QRectF{-margin, -margin, margin, margin}; + createRenderables(outline, delta_rotation, pattern_origin, point_extent, &line, rotation, output); + } + break; + case PointPattern: + if (point && point_distance > 0) + { + PointObject point_object(point); + point_object.setRotation(delta_rotation); + point_object.update(); + auto point_extent = point_object.getExtent(); + createRenderables(outline, delta_rotation, pattern_origin, point_extent, nullptr, rotation, output); + } + break; + } + + output.setClipPath(old_clip_path); +} + + +void AreaSymbol::FillPattern::createPointPatternLine( + MapCoordF first, MapCoordF second, + qreal delta_offset, + float rotation, + const AreaRenderable& outline, + ObjectRenderables& output ) const +{ + auto direction = second - first; + auto length = direction.length(); + direction /= length; // normalize + + auto offset = MapCoordF::dotProduct(direction, first) - 0.001 * offset_along_line - delta_offset; + auto step_length = 0.001 * point_distance; + auto start_length = ceil((offset) / step_length) * step_length - offset; + + auto to_next = direction * step_length; + auto coord = first + direction * start_length; + + // Duplicated loops for optimum locality of code + switch (flags & Option::AlternativeToClipping) + { + case Option::NoClippingIfCenterInside: + for (auto cur = start_length; cur < length; cur += step_length, coord += to_next) + point->createRenderablesIfCenterInside(coord, -rotation, outline.painterPath(), output); + break; + case Option::NoClippingIfCompletelyInside: + for (auto cur = start_length; cur < length; cur += step_length, coord += to_next) + point->createRenderablesIfCompletelyInside(coord, -rotation, outline.painterPath(), output); + break; + case Option::Default: +#if 1 + // Avoids expensive check, but may create objects which won't be rendered. + for (auto cur = start_length; cur < length; cur += step_length, coord += to_next) + point->createRenderablesScaled(coord, -rotation, output); + break; +#endif + case Option::NoClippingIfPartiallyInside: + for (auto cur = start_length; cur < length; cur += step_length, coord += to_next) + point->createRenderablesIfPartiallyInside(coord, -rotation, outline.painterPath(), output); + break; + default: + Q_UNREACHABLE(); + } +} + + + +void AreaSymbol::FillPattern::scale(double factor) +{ + line_spacing = qRound(factor * line_spacing); + line_width = qRound(factor * line_width); + line_offset = qRound(factor * line_offset); + offset_along_line = qRound(factor * offset_along_line); + point_distance = qRound(factor * point_distance); + + if (point) + point->scale(factor); +} + + +qreal AreaSymbol::FillPattern::dimensionForIcon() const +{ + // Ignore large spacing for icon scaling + auto size = qreal(0); + switch (type) + { + case LinePattern: + size = qreal(0.002 * line_width); + break; + + case PointPattern: + size = qreal(0.002 * point->dimensionForIcon()); + if (point_distance < 5000) + size = std::max(size, qreal(0.002 * point_distance)); + break; + } + + if (line_spacing < 5000) + size = std::max(size, qreal(0.0015 * line_spacing)); + + return size; +} + + + +// ### AreaSymbol ### + +AreaSymbol::AreaSymbol() noexcept +: Symbol { Symbol::Area } +, color { nullptr } +, minimum_area { 0 } +{ + // nothing else +} + +AreaSymbol::~AreaSymbol() +{ + for (auto& pattern : patterns) + { + if (pattern.type == FillPattern::PointPattern) + delete pattern.point; + } +} + +Symbol* AreaSymbol::duplicate(const MapColorMap* color_map) const +{ + auto new_area = new AreaSymbol(); + new_area->duplicateImplCommon(this); + new_area->color = color_map ? color_map->value(color) : color; + new_area->minimum_area = minimum_area; + new_area->patterns = patterns; + for (auto& new_pattern : new_area->patterns) + { + if (new_pattern.type == FillPattern::PointPattern) + new_pattern.point = static_cast(new_pattern.point->duplicate(color_map)); + else if (new_pattern.type == FillPattern::LinePattern && color_map) + new_pattern.line_color = color_map->value(new_pattern.line_color); + } + return new_area; +} + +void AreaSymbol::createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + Symbol::RenderableOptions options) const +{ + if (coords.size() < 3) + return; + + auto path = static_cast(object); + PathPartVector path_parts = PathPart::calculatePathParts(coords); + createRenderables(path, path_parts, output, options); +} + +void AreaSymbol::createRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables &output, + Symbol::RenderableOptions options) const +{ + if (options == Symbol::RenderNormal) + { + createRenderablesNormal(object, path_parts, output); + } + else + { + const MapColor* dominant_color = guessDominantColor(); + createBaselineRenderables(object, path_parts, output, dominant_color); + + if (options.testFlag(Symbol::RenderAreasHatched)) + { + createHatchingRenderables(object, path_parts, output, dominant_color); + } + } +} + +void AreaSymbol::createRenderablesNormal( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables& output) const +{ + // The shape output is even created if the area is not filled with a color + // because the QPainterPath created by it is needed as clip path for the fill objects + auto color_fill = new AreaRenderable(this, path_parts); + output.insertRenderable(color_fill); + + auto rotation = object->getPatternRotation(); + auto origin = object->getPatternOrigin(); + for (const auto& pattern : patterns) + { + pattern.createRenderables(*color_fill, rotation, origin, output); + } +} + +void AreaSymbol::createHatchingRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables& output, + const MapColor* color) const +{ + Q_ASSERT(getContainedTypes() & Symbol::Area); + + if (color) + { + // Insert hatched area renderable + AreaSymbol area_symbol; + area_symbol.setNumFillPatterns(1); + AreaSymbol::FillPattern& pattern = area_symbol.getFillPattern(0); + pattern.type = AreaSymbol::FillPattern::LinePattern; + pattern.angle = qDegreesToRadians(45.0f); + pattern.line_spacing = 1000; + pattern.line_offset = 0; + pattern.line_color = color; + pattern.line_width = 70; + + auto symbol = object->getSymbol(); + if (symbol && symbol->getType() == Symbol::Area) + { + const AreaSymbol* orig_symbol = symbol->asArea(); + if (!orig_symbol->getColor() + && orig_symbol->getNumFillPatterns() >= 1) + { + const AreaSymbol::FillPattern& orig_pattern = orig_symbol->getFillPattern(0); + pattern.angle = orig_pattern.angle; + pattern.flags = orig_pattern.flags & ~FillPattern::AlternativeToClipping; + if (orig_pattern.type == AreaSymbol::FillPattern::LinePattern) + { + pattern.line_spacing = std::max(1000, orig_pattern.line_spacing); + pattern.line_offset = orig_pattern.line_offset; + } + } + } + + area_symbol.createRenderablesNormal(object, path_parts, output); + } +} + + + +void AreaSymbol::colorDeleted(const MapColor* color) +{ + if (containsColor(color)) + { + if (color == this->color) + this->color = nullptr; + for (auto& pattern : patterns) + pattern.colorDeleted(color); + resetIcon(); + } +} + + +bool AreaSymbol::containsColor(const MapColor* color) const +{ + return color == this->color + || std::any_of(begin(patterns), end(patterns), [color](const auto& pattern){ return pattern.containsColor(color); }); +} + + +const MapColor* AreaSymbol::guessDominantColor() const +{ + auto color = this->color; + auto pattern = begin(patterns); + while (!color && pattern != end(patterns)) + { + color = pattern->guessDominantColor(); + ++pattern; + } + return color; +} + + + +void AreaSymbol::scale(double factor) +{ + minimum_area = qRound(factor*factor * minimum_area); + + for (auto& pattern : patterns) + pattern.scale(factor); + + resetIcon(); +} + + + +qreal AreaSymbol::dimensionForIcon() const +{ + qreal size = 0; + for (auto& pattern : patterns) + size = qMax(size, pattern.dimensionForIcon()); + return size; +} + + + +bool AreaSymbol::hasRotatableFillPattern() const +{ + return std::any_of(begin(patterns), end(patterns), [](auto& pattern){ + return pattern.rotatable(); + }); +} + +#ifndef NO_NATIVE_FILE_FORMAT + +bool AreaSymbol::loadImpl(QIODevice* file, int version, Map* map) +{ + int temp; + file->read((char*)&temp, sizeof(int)); + color = (temp >= 0) ? map->getColor(temp) : nullptr; + if (version >= 2) + file->read((char*)&minimum_area, sizeof(int)); + + int size; + file->read((char*)&size, sizeof(int)); + patterns.resize(size); + for (int i = 0; i < size; ++i) + if (!patterns[i].load(file, version, map)) + return false; + + return true; +} + +#endif + +void AreaSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const +{ + XmlElementWriter element { xml, QLatin1String("area_symbol") }; + element.writeAttribute(QLatin1String{"inner_color"}, map.findColorIndex(color)); + element.writeAttribute(QLatin1String{"min_area"}, minimum_area); + element.writeAttribute(QLatin1String{"patterns"}, patterns.size()); + for (const auto& pattern : patterns) + pattern.save(xml, map); +} + +bool AreaSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) +{ + if (xml.name() != QLatin1String("area_symbol")) + return false; + + XmlElementReader element { xml }; + color = map.getColor(element.attribute(QLatin1String("inner_color"))); + minimum_area = element.attribute(QLatin1String("min_area")); + + auto num_patterns = element.attribute(QLatin1String("patterns")); + patterns.reserve(num_patterns % 100); // 100 is not the limit + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("pattern")) + { + patterns.push_back(FillPattern()); + patterns.back().load(xml, map, symbol_dict); + } + else + { + xml.skipCurrentElement(); + } + } + + return true; +} + +bool AreaSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const +{ + const AreaSymbol* area = static_cast(other); + if (!MapColor::equal(color, area->color)) + return false; + if (minimum_area != area->minimum_area) + return false; + + if (patterns.size() != area->patterns.size()) + return false; + + // std::is_permutation would identify equal sets of patterns. + // However, guessDominantColor() depends on pattern order if there is no + // AreaSymbol::color (or after the AreaSymbol::color is set to nullptr). + // So equalsImpl cannot be changed unless guessDominantColor is changed. + return std::equal(begin(patterns), end(patterns), begin(area->patterns), [case_sensitivity](auto& lhs, auto& rhs){ + return lhs.equals(rhs, case_sensitivity); + }); +} + + +} // namespace OpenOrienteering diff --git a/src/core/symbols/area_symbol.h b/src/core/symbols/area_symbol.h new file mode 100644 index 0000000..9528bcb --- /dev/null +++ b/src/core/symbols/area_symbol.h @@ -0,0 +1,326 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_AREA_SYMBOL_H +#define OPENORIENTEERING_AREA_SYMBOL_H + +#include +#include + +#include +#include +#include +#include +#include + +#include "symbol.h" + +class QIODevice; +class QRectF; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class AreaRenderable; +class LineSymbol; +class Map; +class MapColor; +class MapColorMap; +class MapCoord; +class MapCoordF; +class Object; +class ObjectRenderables; +class PathObject; +class PathPartVector; +class PointSymbol; +class SymbolPropertiesWidget; +class SymbolSettingDialog; +class VirtualCoordVector; + + +/** + * Symbol for PathObjects where the enclosed area is filled with a solid color + * and / or with one or more patterns. + */ +class AreaSymbol : public Symbol +{ +friend class AreaSymbolSettings; +friend class PointSymbolEditorWidget; +friend class OCAD8FileImport; +public: + /** Describes a fill pattern. */ + struct FillPattern + { + /** Types of fill patterns. */ + enum Type + { + /** Parallel lines pattern */ + LinePattern = 1, + /** Point grid pattern */ + PointPattern = 2 + }; + + /** + * Flags for pattern properties. + */ + enum Option + { + Default = 0x00, + NoClippingIfCompletelyInside = 0x01, + NoClippingIfCenterInside = 0x02, + NoClippingIfPartiallyInside = 0x03, + AlternativeToClipping = 0x03, ///< Bitmask for NoClipping* options + Rotatable = 0x10, ///< Pattern is rotatable per-object + }; + Q_DECLARE_FLAGS(Options, Option) + + /** Type of the pattern */ + Type type; + /** Basic properties of the pattern. */ + Options flags; + /** Rotation angle in radians + * + * \todo Switch to qreal when legacy native file format is dropped. + */ + float angle; + /** Distance between parallel lines, as usual in 0.001mm */ + int line_spacing; + /** Offset of the first line from the origin */ + int line_offset; + + // For type == LinePattern only: + + /** Line color */ + const MapColor* line_color; + /** Line width */ + int line_width; + + // For type == PointPattern only: + + /** Offset of first point along parallel lines */ + int offset_along_line; + /** Point distance along parallel lines */ + int point_distance; + /** Contained point symbol */ + PointSymbol* point; + + /** Display name (transient) */ + QString name; + + + /** Creates a default fill pattern */ + FillPattern() noexcept; + /** Loads the pattern in the old "native" format */ + bool load(QIODevice* file, int version, Map* map); + /** Saves the pattern in xml format */ + void save(QXmlStreamWriter& file, const Map& map) const; + /** Loads the pattern in xml format */ + void load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict); + /** + * Checks if the pattern settings are equal to the other. + * TODO: should the transient name really be compared?! + */ + bool equals(const FillPattern& other, Qt::CaseSensitivity case_sensitivity) const; + + + /** + * Returns true if the pattern is rotatable per object. + */ + bool rotatable() const; + + /** + * Controls whether the pattern is rotatable per object. + */ + void setRotatable(bool value); + + + /** + * Returns the flags which control drawing at boundary. + */ + Options clipping() const; + + /** + * Sets the flags which control drawing at boundary. + */ + void setClipping(Options clipping); + + + /** + * Removes the pattern's references to the deleted color. + */ + void colorDeleted(const MapColor* color); + + /** + * Tests if the pattern contains the given color. + */ + bool containsColor(const MapColor* color) const; + + /** + * Returns the patterns primary color. + */ + const MapColor* guessDominantColor() const; + + + /** + * Creates renderables for this pattern to fill the area surrounded by the outline. + * @param outline A renderable giving the extent and outline. + * @param delta_rotation Rotation offest which is added to the pattern angle. + * @param pattern_origin Origin point for line / point placement. + * @param output Created renderables will be inserted here. + */ + void createRenderables( + const AreaRenderable& outline, + float delta_rotation, + const MapCoord& pattern_origin, + ObjectRenderables& output + ) const; + + /** Does the heavy-lifting in loops over lines. */ + template + void createRenderables( + const AreaRenderable& outline, + float delta_rotation, + const MapCoord& pattern_origin, + const QRectF& point_extent, + LineSymbol* line, + qreal rotation, + ObjectRenderables& output + ) const; + + /** Creates one line of renderables, called by createRenderables(). */ + template + void createLine( + MapCoordF first, MapCoordF second, + qreal delta_offset, + LineSymbol* line, + float rotation, + const AreaRenderable& outline, + ObjectRenderables& output + ) const; + + /** Creates a single line of renderables for a PointPattern. */ + void createPointPatternLine( + MapCoordF first, MapCoordF second, + qreal delta_offset, + float rotation, + const AreaRenderable& outline, + ObjectRenderables& output + ) const; + + + /** Spatially scales the pattern settings by the given factor. */ + void scale(double factor); + + qreal dimensionForIcon() const; + + }; + + AreaSymbol() noexcept; + ~AreaSymbol() override; + Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; + + void createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + Symbol::RenderableOptions options) const override; + + void createRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables &output, + Symbol::RenderableOptions options) const override; + + void createRenderablesNormal( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables& output) const; + + /** + * Creates area hatching renderables for a path object. + */ + void createHatchingRenderables( + const PathObject *object, + const PathPartVector& path_parts, + ObjectRenderables &output, + const MapColor* color) const; + + + void colorDeleted(const MapColor* color) override; + bool containsColor(const MapColor* color) const override; + const MapColor* guessDominantColor() const override; + void scale(double factor) override; + + qreal dimensionForIcon() const override; + + // Getters / Setters + inline const MapColor* getColor() const {return color;} + inline void setColor(const MapColor* color) {this->color = color;} + inline int getMinimumArea() const {return minimum_area; } + inline int getNumFillPatterns() const {return int(patterns.size());} + inline void setNumFillPatterns(int count) {patterns.resize(std::size_t(count));} + inline FillPattern& getFillPattern(int i) {return patterns[std::size_t(i)];} + inline const FillPattern& getFillPattern(int i) const {return patterns[std::size_t(i)];} + bool hasRotatableFillPattern() const; + SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; + +protected: +#ifndef NO_NATIVE_FILE_FORMAT + bool loadImpl(QIODevice* file, int version, Map* map) override; +#endif + void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; + bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; + + /** + * Compares AreaSymbol objects for equality. + * + * Fill patterns are only compared in order. + */ + bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; + + const MapColor* color; + int minimum_area; // in mm^2 // FIXME: unit (factor) wrong + std::vector patterns; +}; + + + +inline +bool AreaSymbol::FillPattern::rotatable() const +{ + return flags.testFlag(Option::Rotatable); +} + +inline +AreaSymbol::FillPattern::Options AreaSymbol::FillPattern::clipping() const +{ + return flags & Option::AlternativeToClipping; +} + + +} // namespace OpenOrienteering + + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::AreaSymbol::FillPattern::Options) + + +#endif diff --git a/src/core/symbols/combined_symbol.cpp b/src/core/symbols/combined_symbol.cpp new file mode 100644 index 0000000..7eff276 --- /dev/null +++ b/src/core/symbols/combined_symbol.cpp @@ -0,0 +1,393 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "combined_symbol.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_color.h" +#include "core/objects/object.h" +#include "core/symbols/symbol.h" + + +namespace OpenOrienteering { + +CombinedSymbol::CombinedSymbol() +: Symbol{Symbol::Combined} +, private_parts{ false, false } +, parts{ private_parts.size(), nullptr } +{ + Q_ASSERT(private_parts.size() == parts.size()); + // nothing else +} + +CombinedSymbol::~CombinedSymbol() +{ + auto is_private = begin(private_parts); + for (auto subsymbol : parts) + { + if (*is_private) + delete subsymbol; + ++is_private; + }; +} + +Symbol* CombinedSymbol::duplicate(const MapColorMap* color_map) const +{ + auto new_symbol = new CombinedSymbol(); + new_symbol->duplicateImplCommon(this); + new_symbol->parts = parts; + new_symbol->private_parts = private_parts; + auto is_private = begin(new_symbol->private_parts); + for (auto& subsymbol : new_symbol->parts) + { + if (*is_private) + subsymbol = subsymbol->duplicate(color_map); + ++is_private; + } + return new_symbol; +} + + + +bool CombinedSymbol::validate() const +{ + return std::all_of(begin(parts), end(parts), [](auto& symbol) { return symbol->validate(); }); +} + + + +void CombinedSymbol::createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + Symbol::RenderableOptions options) const +{ + auto path = static_cast(object); + PathPartVector path_parts = PathPart::calculatePathParts(coords); + createRenderables(path, path_parts, output, options); +} + +void CombinedSymbol::createRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables &output, + Symbol::RenderableOptions options) const +{ + for (auto subsymbol : parts) + { + if (subsymbol) + subsymbol->createRenderables(object, path_parts, output, options); + } +} + +void CombinedSymbol::colorDeleted(const MapColor* color) +{ + if (containsColor(color)) + resetIcon(); + + auto is_private = begin(private_parts); + for (auto subsymbol : parts) + { + if (*is_private) + const_cast(subsymbol)->colorDeleted(color); + ++is_private; + }; +} + +bool CombinedSymbol::containsColor(const MapColor* color) const +{ + return std::any_of(begin(parts), end(parts), [color](const auto& part) { + return part && part->containsColor(color); + }); +} + +const MapColor* CombinedSymbol::guessDominantColor() const +{ + // Speculative heuristic. Prefers areas and non-white colors. + const MapColor* dominant_color = nullptr; + for (auto subsymbol : parts) + { + if (subsymbol && subsymbol->getContainedTypes() & Symbol::Area) + { + dominant_color = subsymbol->guessDominantColor(); + if (dominant_color && !dominant_color->isWhite()) + return dominant_color; + } + } + + if (dominant_color) + return dominant_color; + + for (auto subsymbol : parts) + { + if (subsymbol && !(subsymbol->getContainedTypes() & Symbol::Area)) + { + dominant_color = subsymbol->guessDominantColor(); + if (dominant_color && !dominant_color->isWhite()) + return dominant_color; + } + } + + return dominant_color; +} + +bool CombinedSymbol::symbolChanged(const Symbol* old_symbol, const Symbol* new_symbol) +{ + bool have_symbol = false; + for (auto& subsymbol : parts) + { + if (subsymbol == old_symbol) + { + have_symbol = true; + subsymbol = new_symbol; + } + } + + // always invalidate the icon, since the parts might have changed. + resetIcon(); + + return have_symbol; +} + +bool CombinedSymbol::containsSymbol(const Symbol* symbol) const +{ + for (auto subsymbol : parts) + { + if (subsymbol == symbol) + return true; + + if (subsymbol == nullptr) + continue; + + if (subsymbol->getType() == Symbol::Combined) // TODO: see TODO in SymbolDropDown constructor. + { + const CombinedSymbol* combined_symbol = reinterpret_cast(subsymbol); + if (combined_symbol->containsSymbol(symbol)) + return true; + } + } + return false; +} + +void CombinedSymbol::scale(double factor) +{ + auto is_private = begin(private_parts); + for (auto subsymbol : parts) + { + if (*is_private) + const_cast(subsymbol)->scale(factor); + ++is_private; + }; + + resetIcon(); +} + +Symbol::Type CombinedSymbol::getContainedTypes() const +{ + auto type = int(getType()); + + for (auto subsymbol : parts) + { + if (subsymbol) + type |= subsymbol->getContainedTypes(); + } + + return Type(type); +} + +#ifndef NO_NATIVE_FILE_FORMAT + +bool CombinedSymbol::loadImpl(QIODevice* file, int version, Map* map) +{ + int size; + file->read((char*)&size, sizeof(int)); + temp_part_indices.resize(size); + parts.resize(size); + + for (int i = 0; i < size; ++i) + { + bool is_private = false; + if (version >= 22) + file->read((char*)&is_private, sizeof(bool)); + private_parts[i] = is_private; + + if (is_private) + { + // Note on const_cast: private part is owned by this symbol. + if (!Symbol::loadSymbol(const_cast(parts[i]), file, version, map)) + return false; + temp_part_indices[i] = -1; + } + else + { + int temp; + file->read((char*)&temp, sizeof(int)); + temp_part_indices[i] = temp; + } + } + return true; +} + +#endif + +void CombinedSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const +{ + xml.writeStartElement(QString::fromLatin1("combined_symbol")); + int num_parts = int(parts.size()); + xml.writeAttribute(QString::fromLatin1("parts"), QString::number(num_parts)); + + auto is_private = begin(private_parts); + for (const auto subsymbol : parts) + { + xml.writeStartElement(QString::fromLatin1("part")); + if (*is_private) + { + xml.writeAttribute(QString::fromLatin1("private"), QString::fromLatin1("true")); + subsymbol->save(xml, map); + } + else + { + auto index = map.findSymbolIndex(subsymbol); + xml.writeAttribute(QString::fromLatin1("symbol"), QString::number(index)); + } + xml.writeEndElement(/*part*/); + ++is_private; + } + xml.writeEndElement(/*combined_symbol*/); +} + +bool CombinedSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) +{ + if (xml.name() != QLatin1String("combined_symbol")) + return false; + + int num_parts = xml.attributes().value(QLatin1String("parts")).toInt(); + temp_part_indices.reserve(num_parts % 10); // 10 is not the limit + private_parts.clear(); + private_parts.reserve(num_parts % 10); + parts.clear(); + parts.reserve(num_parts % 10); + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("part")) + { + bool is_private = (xml.attributes().value(QLatin1String("private")) == QLatin1String("true")); + private_parts.push_back(is_private); + if (is_private) + { + xml.readNextStartElement(); + parts.push_back(Symbol::load(xml, map, symbol_dict)); + temp_part_indices.push_back(-1); + } + else + { + int temp = xml.attributes().value(QLatin1String("symbol")).toInt(); + temp_part_indices.push_back(temp); + parts.push_back(nullptr); + } + xml.skipCurrentElement(); + } + else + xml.skipCurrentElement(); // unknown + } + + return true; +} + +bool CombinedSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const +{ + const CombinedSymbol* combination = static_cast(other); + return parts.size() == combination->parts.size() + && std::equal(begin(private_parts), end(private_parts), begin(combination->private_parts)) + && std::equal(begin(parts), end(parts), begin(combination->parts), + [case_sensitivity](const auto lhs, const auto rhs) + { + return (!lhs && !rhs) + || (lhs && rhs && lhs->equals(rhs, case_sensitivity)); + }); +} + +bool CombinedSymbol::loadFinished(Map* map) +{ + const auto num_symbols = map->getNumSymbols(); + const auto last = std::find_if(begin(temp_part_indices), end(temp_part_indices), + [num_symbols](const auto index) + { + return index >= num_symbols; + }); + + std::transform(begin(temp_part_indices), last, begin(parts), begin(parts), + [map](const auto index, const auto& subsymbol) -> const Symbol* + { + return (index < 0) ? subsymbol : map->getSymbol(index); + }); + + return last == end(temp_part_indices); +} + + + +qreal CombinedSymbol::dimensionForIcon() const +{ + return std::accumulate(begin(parts), end(parts), qreal(0), [](qreal value, auto subsymbol) + { + return subsymbol ? qMax(value, subsymbol->dimensionForIcon()) : value; + }); +} + + +qreal CombinedSymbol::calculateLargestLineExtent() const +{ + return std::accumulate(begin(parts), end(parts), qreal(0), [](qreal value, auto subsymbol) + { + return subsymbol ? qMax(value, subsymbol->calculateLargestLineExtent()) : value; + }); +} + + + +void CombinedSymbol::setPart(int i, const Symbol* symbol, bool is_private) +{ + const auto index = std::size_t(i); + + if (private_parts[index]) + delete parts[index]; + + parts[index] = symbol; + private_parts[index] = symbol ? is_private : false; +} + + +} // namespace OpenOrienteering diff --git a/src/core/symbols/combined_symbol.h b/src/core/symbols/combined_symbol.h new file mode 100644 index 0000000..0059d79 --- /dev/null +++ b/src/core/symbols/combined_symbol.h @@ -0,0 +1,124 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_COMBINED_SYMBOL_H +#define OPENORIENTEERING_COMBINED_SYMBOL_H + +#include "symbol.h" + +#include + +#include +#include + +class QIODevice; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class Map; +class MapColor; +class MapColorMap; +class Object; +class ObjectRenderables; +class PathObject; +class PathPartVector; +class SymbolPropertiesWidget; +class SymbolSettingDialog; +class VirtualCoordVector; + + +/** + * Symbol which can combine other line and area symbols, + * creating renderables for each of them. + * + * To use, set the number of parts with setNumParts() and set the indivdual part + * pointers with setPart(). Parts can be private, i.e. the CombinedSymbol owns + * the part symbol and it is not entered in the map as an individual symbol. + */ +class CombinedSymbol : public Symbol +{ +friend class CombinedSymbolSettings; +friend class PointSymbolEditorWidget; +friend class OCAD8FileImport; +public: + CombinedSymbol(); + ~CombinedSymbol() override; + Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; + + bool validate() const override; + + void createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + Symbol::RenderableOptions options) const override; + + void createRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables &output, + Symbol::RenderableOptions options) const override; + + void colorDeleted(const MapColor* color) override; + bool containsColor(const MapColor* color) const override; + const MapColor* guessDominantColor() const override; + bool symbolChanged(const Symbol* old_symbol, const Symbol* new_symbol) override; + bool containsSymbol(const Symbol* symbol) const override; + void scale(double factor) override; + Type getContainedTypes() const override; + + bool loadFinished(Map* map) override; + + qreal dimensionForIcon() const override; + + qreal calculateLargestLineExtent() const override; + + // Getters / Setter + inline int getNumParts() const {return (int)parts.size();} + inline void setNumParts(int num) {parts.resize(num, nullptr); private_parts.resize(num, false);} + + inline const Symbol* getPart(int i) const {return parts[i];} + void setPart(int i, const Symbol* symbol, bool is_private); + + inline bool isPartPrivate(int i) const {return private_parts[i];} + inline void setPartPrivate(int i, bool set_private) {private_parts[i] = set_private;} + + SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; + +protected: +#ifndef NO_NATIVE_FILE_FORMAT + bool loadImpl(QIODevice* file, int version, Map* map) override; +#endif + void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; + bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; + bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; + + std::vector private_parts; + std::vector parts; + std::vector temp_part_indices; // temporary vector of the indices of the 'parts' symbols, used just for loading +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/symbols/line_symbol.cpp b/src/core/symbols/line_symbol.cpp new file mode 100644 index 0000000..85e6a22 --- /dev/null +++ b/src/core/symbols/line_symbol.cpp @@ -0,0 +1,2076 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "line_symbol.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/path_coord.h" +#include "core/objects/object.h" +#include "core/renderables/renderable.h" +#include "core/renderables/renderable_implementation.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "core/virtual_coord_vector.h" +#include "core/virtual_path.h" + + +namespace OpenOrienteering { + +// ### LineSymbolBorder ### + +void LineSymbolBorder::reset() noexcept +{ + color = nullptr; + width = 0; + shift = 0; + dashed = false; + dash_length = 2 * 1000; + break_length = 1 * 1000; +} + +#ifndef NO_NATIVE_FILE_FORMAT + +bool LineSymbolBorder::load(QIODevice* file, int version, Map* map) +{ + Q_UNUSED(version); + int temp; + file->read((char*)&temp, sizeof(int)); + color = (temp >= 0) ? map->getColor(temp) : nullptr; + file->read((char*)&width, sizeof(int)); + file->read((char*)&shift, sizeof(int)); + file->read((char*)&dashed, sizeof(bool)); + file->read((char*)&dash_length, sizeof(int)); + file->read((char*)&break_length, sizeof(int)); + return true; +} + +#endif + +void LineSymbolBorder::save(QXmlStreamWriter& xml, const Map& map) const +{ + xml.writeStartElement(QString::fromLatin1("border")); + xml.writeAttribute(QString::fromLatin1("color"), QString::number(map.findColorIndex(color))); + xml.writeAttribute(QString::fromLatin1("width"), QString::number(width)); + xml.writeAttribute(QString::fromLatin1("shift"), QString::number(shift)); + if (dashed) + xml.writeAttribute(QString::fromLatin1("dashed"), QString::fromLatin1("true")); + xml.writeAttribute(QString::fromLatin1("dash_length"), QString::number(dash_length)); + xml.writeAttribute(QString::fromLatin1("break_length"), QString::number(break_length)); + xml.writeEndElement(/*border*/); +} + +bool LineSymbolBorder::load(QXmlStreamReader& xml, const Map& map) +{ + Q_ASSERT(xml.name() == QLatin1String("border")); + + QXmlStreamAttributes attributes = xml.attributes(); + int temp = attributes.value(QLatin1String("color")).toInt(); + color = map.getColor(temp); + width = attributes.value(QLatin1String("width")).toInt(); + shift = attributes.value(QLatin1String("shift")).toInt(); + dashed = (attributes.value(QLatin1String("dashed")) == QLatin1String("true")); + dash_length = attributes.value(QLatin1String("dash_length")).toInt(); + break_length = attributes.value(QLatin1String("break_length")).toInt(); + xml.skipCurrentElement(); + return !xml.error(); +} + +bool LineSymbolBorder::equals(const LineSymbolBorder* other) const +{ + if (!MapColor::equal(color, other->color)) + return false; + + if (width != other->width) + return false; + if (shift != other->shift) + return false; + if (dashed != other->dashed) + return false; + if (dashed) + { + if (dash_length != other->dash_length) + return false; + if (break_length != other->break_length) + return false; + } + return true; +} + +void LineSymbolBorder::assign(const LineSymbolBorder& other, const MapColorMap* color_map) +{ + color = color_map ? color_map->value(other.color) : other.color; + width = other.width; + shift = other.shift; + dashed = other.dashed; + dash_length = other.dash_length; + break_length = other.break_length; +} + +bool LineSymbolBorder::isVisible() const +{ + return width > 0 && color && !(dash_length == 0 && dashed); +} + +void LineSymbolBorder::createSymbol(LineSymbol& out) const +{ + out.setLineWidth(0.001 * width); + out.setColor(color); + + if (dashed) + { + out.setDashed(true); + out.setDashLength(dash_length); + out.setBreakLength(break_length); + } +} + +void LineSymbolBorder::scale(double factor) +{ + width = qRound(factor * width); + shift = qRound(factor * shift); + dash_length = qRound(factor * dash_length); + break_length = qRound(factor * break_length); +} + + +// ### LineSymbol ### + +LineSymbol::LineSymbol() noexcept +: Symbol(Symbol::Line) +{ + line_width = 0; + color = nullptr; + minimum_length = 0; + cap_style = FlatCap; + join_style = MiterJoin; + pointed_cap_length = 1000; + + start_symbol = nullptr; + mid_symbol = nullptr; + end_symbol = nullptr; + dash_symbol = nullptr; + + dashed = false; + + segment_length = 4000; + end_length = 0; + show_at_least_one_symbol = true; + minimum_mid_symbol_count = 0; + minimum_mid_symbol_count_when_closed = 0; + + dash_length = 4000; + break_length = 1000; + dashes_in_group = 1; + in_group_break_length = 500; + half_outer_dashes = false; + mid_symbols_per_spot = 1; + mid_symbol_distance = 0; + suppress_dash_symbol_at_ends = false; + scale_dash_symbol = true; + + // Border lines + have_border_lines = false; + border.reset(); + right_border.reset(); +} + + +LineSymbol::~LineSymbol() +{ + delete start_symbol; + delete mid_symbol; + delete end_symbol; + delete dash_symbol; +} + + +Symbol* LineSymbol::duplicate(const MapColorMap* color_map) const +{ + auto new_line = new LineSymbol(); + new_line->duplicateImplCommon(this); + new_line->line_width = line_width; + new_line->color = color_map ? color_map->value(color) : color; + new_line->minimum_length = minimum_length; + new_line->cap_style = cap_style; + new_line->join_style = join_style; + new_line->pointed_cap_length = pointed_cap_length; + + using MemberSymbol = PointSymbol* LineSymbol::*; + MemberSymbol members[4] = { &LineSymbol::start_symbol, &LineSymbol::mid_symbol, &LineSymbol::end_symbol, &LineSymbol::dash_symbol }; + for (auto member : members) + { + auto sub_symbol = this->*member; + if (sub_symbol && !sub_symbol->isEmpty()) + new_line->*member = static_cast(sub_symbol->duplicate(color_map)); + } + new_line->dashed = dashed; + new_line->segment_length = segment_length; + new_line->end_length = end_length; + new_line->show_at_least_one_symbol = show_at_least_one_symbol; + new_line->minimum_mid_symbol_count = minimum_mid_symbol_count; + new_line->minimum_mid_symbol_count_when_closed = minimum_mid_symbol_count_when_closed; + new_line->dash_length = dash_length; + new_line->break_length = break_length; + new_line->dashes_in_group = dashes_in_group; + new_line->in_group_break_length = in_group_break_length; + new_line->half_outer_dashes = half_outer_dashes; + new_line->mid_symbols_per_spot = mid_symbols_per_spot; + new_line->mid_symbol_distance = mid_symbol_distance; + new_line->suppress_dash_symbol_at_ends = suppress_dash_symbol_at_ends; + new_line->scale_dash_symbol = scale_dash_symbol; + new_line->have_border_lines = have_border_lines; + new_line->border.assign(border, color_map); + new_line->right_border.assign(right_border, color_map); + return new_line; +} + + + +bool LineSymbol::validate() const +{ + using std::begin; + using std::end; + using MemberSymbol = PointSymbol* LineSymbol::*; + MemberSymbol members[4] = { &LineSymbol::start_symbol, &LineSymbol::mid_symbol, &LineSymbol::end_symbol, &LineSymbol::dash_symbol }; + return std::all_of(begin(members), end(members), [this](auto& member) { + auto sub_symbol = this->*member; + return !sub_symbol || !sub_symbol->isEmpty() || sub_symbol->validate(); + }); +} + + + +void LineSymbol::createRenderables( + const Object* object, + const VirtualCoordVector& coords, + ObjectRenderables& output, + RenderableOptions options ) const +{ + Q_UNUSED(options); + PathPartVector path_parts = PathPart::calculatePathParts(coords); + for (const auto& part : path_parts) + { + createPathCoordRenderables(object, part, part.isClosed(), output); + } +} + +void LineSymbol::createRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables& output, + RenderableOptions options ) const +{ + if (options.testFlag(Symbol::RenderBaselines)) + { + createBaselineRenderables(object, path_parts, output, guessDominantColor()); + } + else + { + for (const auto& part : path_parts) + { + createPathCoordRenderables(object, part, part.isClosed(), output); + } + } +} + +void LineSymbol::createPathRenderables(const Object* object, bool path_closed, const MapCoordVector& flags, const MapCoordVectorF& coords, ObjectRenderables& output) const +{ + auto path = VirtualPath { flags, coords }; + auto last = path.path_coords.update(0); + createPathCoordRenderables(object, path, path_closed, output); + + Q_ASSERT(last+1 == coords.size()); Q_UNUSED(last); +} + +void LineSymbol::createPathCoordRenderables(const Object* object, const VirtualPath& path, bool path_closed, ObjectRenderables& output) const +{ + if (path.size() < 2) + return; + + auto& coords = path.coords; + + // Start or end symbol? + if (start_symbol && !start_symbol->isEmpty()) + { + auto orientation = 0.0f; + if (start_symbol->isRotatable()) + { + bool ok; + MapCoordF tangent = path.calculateOutgoingTangent(0, ok); + if (ok) + orientation = tangent.angle(); + } + start_symbol->createRenderablesScaled(coords[0], orientation, output); + } + + if (end_symbol && !end_symbol->isEmpty()) + { + std::size_t last = coords.size() - 1; + auto orientation = 0.0f; + if (end_symbol->isRotatable()) + { + bool ok; + MapCoordF tangent = path.calculateIncomingTangent(last, ok); + if (ok) + orientation = tangent.angle(); + } + end_symbol->createRenderablesScaled(coords[last], orientation, output); + } + + // Dash symbols? + if (dash_symbol && !dash_symbol->isEmpty()) + { + createDashSymbolRenderables(path, path_closed, output); + } + + // The line itself + MapCoordVector processed_flags; + MapCoordVectorF processed_coords; + bool create_border = have_border_lines && (border.isVisible() || right_border.isVisible()); + bool pointed_cap = cap_style == PointedCap && pointed_cap_length > 0; + if (!dashed) + { + // Base line? + if (line_width > 0) + { + if (color && !pointed_cap && !create_border) + { + output.insertRenderable(new LineRenderable(this, path, path_closed)); + } + else if (create_border || pointed_cap) + { + auto last = coords.size(); + auto part_start = MapCoordVector::size_type { 0 }; + auto next_part_start = last; //path_coords.update(part_start); + + bool has_start = !(part_start == 0 && path_closed); + bool has_end = !(next_part_start == last && path_closed); + + auto start = SplitPathCoord::begin(path.path_coords); + auto end = SplitPathCoord::end(path.path_coords); + processContinuousLine(path, start, end, + has_start, has_end, processed_flags, processed_coords, false, output); + + } + } + + // Symbols? + if (mid_symbol && !mid_symbol->isEmpty() && segment_length > 0) + createMidSymbolRenderables(path, path_closed, output); + } + else if (dash_length > 0) + { + // Dashed lines + processDashedLine(path, path_closed, processed_flags, processed_coords, output); + } + else + { + // Invalid configuration + return; + } + + if (!processed_coords.empty() && (color || create_border)) + { + Q_ASSERT(processed_coords.size() != 1); + + VirtualPath path = { processed_flags, processed_coords }; + path.path_coords.update(path.first_index); + + if (color) + { + output.insertRenderable(new LineRenderable(this, path, path_closed)); + } + + if (create_border) + { + createBorderLines(object, path, output); + } + } +} + +void LineSymbol::createBorderLines( + const Object* object, + const VirtualPath& path, + ObjectRenderables& output) const +{ + double main_shift = 0.0005 * line_width; + + if (!areBordersDifferent()) + { + MapCoordVector border_flags; + MapCoordVectorF border_coords; + LineSymbol border_symbol; + border.createSymbol(border_symbol); + border_symbol.setJoinStyle(join_style == RoundJoin ? RoundJoin : MiterJoin); + + if (border.dashed && border.dash_length > 0 && border.break_length > 0) + { + MapCoordVector dashed_flags; + MapCoordVectorF dashed_coords; + border_symbol.processDashedLine(path, path.isClosed(), dashed_flags, dashed_coords, output); + border_symbol.dashed = false; // important, otherwise more dashes might be added by createRenderables()! + + auto dashed_path = VirtualPath { dashed_flags, dashed_coords }; + shiftCoordinates(dashed_path, main_shift, border_flags, border_coords); + border_symbol.createPathRenderables(object, path.isClosed(), border_flags, border_coords, output); + shiftCoordinates(dashed_path, -main_shift, border_flags, border_coords); + border_symbol.createPathRenderables(object, path.isClosed(), border_flags, border_coords, output); + } + else + { + shiftCoordinates(path, main_shift, border_flags, border_coords); + border_symbol.createPathRenderables(object, path.isClosed(), border_flags, border_coords, output); + shiftCoordinates(path, -main_shift, border_flags, border_coords); + border_symbol.createPathRenderables(object, path.isClosed(), border_flags, border_coords, output); + } + } + else + { + createBorderLine(object, path, path.isClosed(), output, border, -main_shift); + createBorderLine(object, path, path.isClosed(), output, right_border, main_shift); + } +} + +void LineSymbol::createBorderLine( + const Object* object, + const VirtualPath& path, + bool path_closed, + ObjectRenderables& output, + const LineSymbolBorder& border, + double main_shift ) const +{ + MapCoordVector border_flags; + MapCoordVectorF border_coords; + LineSymbol border_symbol; + border.createSymbol(border_symbol); + border_symbol.setJoinStyle(join_style == RoundJoin ? RoundJoin : MiterJoin); + + if (border.dashed && border.dash_length > 0 && border.break_length > 0) + { + MapCoordVector dashed_flags; + MapCoordVectorF dashed_coords; + + border_symbol.processDashedLine(path, path_closed, dashed_flags, dashed_coords, output); + border_symbol.dashed = false; // important, otherwise more dashes might be added by createRenderables()! + + auto dashed_path = VirtualPath { dashed_flags, dashed_coords }; + shiftCoordinates(dashed_path, main_shift, border_flags, border_coords); + } + else + { + shiftCoordinates(path, main_shift, border_flags, border_coords); + } + + border_symbol.createPathRenderables(object, path_closed, border_flags, border_coords, output); +} + +void LineSymbol::shiftCoordinates(const VirtualPath& path, double main_shift, MapCoordVector& out_flags, MapCoordVectorF& out_coords) const +{ + const float curve_threshold = 0.03f; // TODO: decrease for export/print? + const int MAX_OFFSET = 16; + QBezier offsetCurves[MAX_OFFSET]; + + double miter_limit = 2.0 * miterLimit(); // needed more than once + if (miter_limit <= 0.0) + miter_limit = 1.0e6; // Q_ASSERT(miter_limit != 0) + double miter_reference = 0.0; // reference value: + if (join_style == MiterJoin) // when to bevel MiterJoins + miter_reference = cos(atan(4.0 / miter_limit)); + + // sign of shift and main shift indicates left or right border + // but u_border_shift is unsigned + double u_border_shift = 0.001 * ((main_shift > 0.0 && areBordersDifferent()) ? right_border.shift : border.shift); + double shift = main_shift + ((main_shift > 0.0) ? u_border_shift : -u_border_shift); + + auto size = path.size(); + out_flags.clear(); + out_coords.clear(); + out_flags.reserve(size); + out_coords.reserve(size); + + const MapCoord no_flags; + + bool ok_in, ok_out; + MapCoordF segment_start, + right_vector, end_right_vector, + vector_in, vector_out, + tangent_in, tangent_out, + middle0, middle1; + auto last_i = path.last_index; + for (auto i = path.first_index; i < size; ++i) + { + auto coords_i = path.coords[i]; + const auto& flags_i = path.coords.flags[i]; + + vector_in = path.calculateIncomingTangent(i, ok_in); + vector_out = path.calculateOutgoingTangent(i, ok_out); + + if (!ok_in) + { + vector_in = vector_out; + ok_in = ok_out; + } + if (ok_in) + { + tangent_in = vector_in; + tangent_in.normalize(); + } + + if (!ok_out) + { + vector_out = vector_in; + ok_out = ok_in; + } + if (ok_out) + { + tangent_out = vector_out; + tangent_out.normalize(); + } + + if (!ok_in && !ok_out) + { + // Rare but existing case. No valid solution, but + // at least we need to output a point to handle the flags correctly. + //qDebug("No valid tangent"); + segment_start = coords_i; + } + else if (i == 0 && !path.isClosed()) + { + // Simple start point + right_vector = tangent_out.perpRight(); + segment_start = coords_i + shift * right_vector; + } + else if (i == last_i && !path.isClosed()) + { + // Simple end point + right_vector = tangent_in.perpRight(); + segment_start = coords_i + shift * right_vector; + } + else + { + // Corner point + right_vector = tangent_out.perpRight(); + + middle0 = tangent_in + tangent_out; + middle0.normalize(); + double offset; + + // Determine type of corner (inner vs. outer side of corner) + double a = (tangent_out.x() * tangent_in.y() - tangent_in.x() * tangent_out.y()) * main_shift; + if (a > 0.0) + { + // Outer side of corner + + if (join_style == BevelJoin || join_style == RoundJoin) + { + middle1 = tangent_in + middle0; + middle1.normalize(); + double phi1 = acos(MapCoordF::dotProduct(middle1, tangent_in)); + offset = tan(phi1) * u_border_shift; + + if (i > 0 && !qIsNaN(offset)) + { + // First border corner point + end_right_vector = tangent_in.perpRight(); + out_flags.push_back(no_flags); + out_coords.push_back(coords_i + shift * end_right_vector + offset * tangent_in); + + if (join_style == RoundJoin) + { + // Extra border corner point + // TODO: better approximation of round corner / use bezier curve + out_flags.push_back(no_flags); + out_coords.push_back(coords_i + shift * middle0.perpRight()); + } + } + } + else /* join_style == MiterJoin */ + { + // miter_check has no concrete interpretation, + // but was derived by mathematical simplifications. + double miter_check = MapCoordF::dotProduct(middle0, tangent_in); + if (miter_check <= miter_reference) + { + // Two border corner points + middle1 = tangent_in + middle0; + middle1.normalize(); + double phi1 = acos(MapCoordF::dotProduct(middle1, tangent_in)); + offset = miter_limit * fabs(main_shift) + tan(phi1) * u_border_shift; + + if (i > 0 && !qIsNaN(offset)) + { + // First border corner point + end_right_vector = tangent_in.perpRight(); + out_flags.push_back(no_flags); + out_coords.push_back(coords_i + shift * end_right_vector + offset * tangent_in); + } + } + else + { + double phi = acos(MapCoordF::dotProduct(middle0.perpRight(), tangent_in)); + offset = fabs(1.0/tan(phi) * shift); + } + } + + if (qIsNaN(offset)) + { + offset = 0.0; + } + + // single or second border corner point + segment_start = coords_i + shift * right_vector - offset * tangent_out; + } + else if (i > 2 && path.coords.flags[i-3].isCurveStart() && flags_i.isCurveStart()) + { + // Inner side of corner (or no corner), and both sides are beziers + // old behaviour + right_vector = middle0.perpRight(); + double phi = acos(MapCoordF::dotProduct(right_vector, tangent_in)); + double sin_phi = sin(phi); + double inset = (sin_phi > (1.0/miter_limit)) ? (1.0 / sin_phi) : miter_limit; + segment_start = coords_i + (shift * inset) * right_vector; + } + else + { + // Inner side of corner (or no corner), and no more than on bezier involved + + // Default solution + double phi = acos(MapCoordF::dotProduct(middle0.perpRight(), tangent_in)); + double tan_phi = tan(phi); + offset = -fabs(shift/tan_phi); + + if (tan_phi >= 1.0) + { + segment_start = coords_i + shift * right_vector - offset * tangent_out; + } + else + { + // Critical case + double len_in = vector_in.length(); + double len_out = vector_out.length(); + a = qIsNaN(offset) ? 0.0 : (fabs(offset) - qMin(len_in, len_out)); + + if (a < -0.0) + { + // Offset acceptable + segment_start = coords_i + shift * right_vector - offset * tangent_out; + +#ifdef MAPPER_FILL_MITER_LIMIT_GAP + // Avoid miter limit effects by extra points + // This is be nice for ISOM roads, but not for powerlines... + // TODO: add another flag to the signature? + out_flags.push_back(no_flags); + out_coords.push_back(coords_i + shift * right_vector - offset * tangent_out); + + out_flags.push_back(no_flags); + out_coords.push_back(out_coords.back() - (shift - main_shift) * qMax(0.0, 1.0 / sin(phi) - miter_limit) * middle0); + + // single or second border corner point + segment_start = coords_i + shift * right_vector - offset * tangent_out; +#endif + } + else + { + // Default solution is too far off from main path + if (len_in < len_out) + { + segment_start = coords_i + shift * right_vector + len_in * tangent_out; + } + else + { + right_vector = tangent_in.perpRight(); + segment_start = coords_i + shift * right_vector - len_out * tangent_in; + } + } + } + } + } + + out_flags.emplace_back(flags_i); + out_flags.back().setCurveStart(false); // Must not be set if bezier.shifted() fails! + out_coords.emplace_back(segment_start); + + if (flags_i.isCurveStart()) + { + Q_ASSERT(i+2 < path.last_index); + + // Use QBezierCopy code to shift the curve, but set start and end point manually to get the correct end points (because of line joins) + // TODO: it may be necessary to remove some of the generated curves in the case an outer point is moved inwards + if (main_shift > 0.0) + { + QBezier bezier = QBezier::fromPoints(path.coords[i+3], path.coords[i+2], path.coords[i+1], coords_i); + auto count = bezier.shifted(offsetCurves, MAX_OFFSET, qAbs(shift), curve_threshold); + for (auto j = count - 1; j >= 0; --j) + { + out_flags.back().setCurveStart(true); + + out_flags.emplace_back(); + out_coords.emplace_back(offsetCurves[j].pt3()); + + out_flags.emplace_back(); + out_coords.emplace_back(offsetCurves[j].pt2()); + + if (j > 0) + { + out_flags.emplace_back(); + out_coords.emplace_back(offsetCurves[j].pt1()); + } + } + } + else + { + QBezier bezier = QBezier::fromPoints(path.coords[i], path.coords[i+1], path.coords[i+2], path.coords[i+3]); + int count = bezier.shifted(offsetCurves, MAX_OFFSET, qAbs(shift), curve_threshold); + for (int j = 0; j < count; ++j) + { + out_flags.back().setCurveStart(true); + + out_flags.emplace_back(); + out_coords.emplace_back(offsetCurves[j].pt2()); + + out_flags.emplace_back(); + out_coords.emplace_back(offsetCurves[j].pt3()); + + if (j < count - 1) + { + out_flags.emplace_back(); + out_coords.emplace_back(offsetCurves[j].pt4()); + } + } + } + + i += 2; + } + } +} + +void LineSymbol::processContinuousLine( + const VirtualPath& path, + const SplitPathCoord& start, + const SplitPathCoord& end, + bool has_start, + bool has_end, + MapCoordVector& processed_flags, + MapCoordVectorF& processed_coords, + bool set_mid_symbols, + ObjectRenderables& output ) const +{ + bool create_line = true; + float effective_cap_length = 0.0f; + if (cap_style == PointedCap && (has_start || has_end)) + { + effective_cap_length = 0.001f * pointed_cap_length; + int num_caps = 2 - (has_end ? 0 : 1) - (has_start ? 0 : 1); + auto max_effective_cap_length = (end.clen - start.clen) / num_caps; + if (effective_cap_length >= max_effective_cap_length) + { + create_line = false; + effective_cap_length = max_effective_cap_length; + } + } + + auto split = start; + auto next_split = SplitPathCoord(); + + if (has_start && effective_cap_length > 0.0f) + { + // Create pointed line cap start + next_split = SplitPathCoord::at(start.clen + effective_cap_length, start); + createPointedLineCap(path, split, next_split, false, output); + split = next_split; + } + + if (create_line) + { + // Add line to processed_coords/flags + + PathCoord::length_type mid_symbol_distance_f = 0.001 * mid_symbol_distance; + auto mid_symbols_length = (qMax(1, mid_symbols_per_spot) - 1) * mid_symbol_distance_f; + auto effective_end_length = end.clen; + if (has_end) + { + effective_end_length -= effective_cap_length; + } + + set_mid_symbols = set_mid_symbols && mid_symbol && !mid_symbol->isEmpty() && mid_symbols_per_spot; + if (set_mid_symbols && mid_symbols_length <= effective_end_length - split.clen) + { + auto mid_position = (split.clen + effective_end_length - mid_symbols_length) / 2; + next_split = SplitPathCoord::at(mid_position, split); + path.copy(split, next_split, processed_flags, processed_coords); + split = next_split; + + auto orientation = 0.0f; + bool mid_symbol_rotatable = bool(mid_symbol) && mid_symbol->isRotatable(); + for (auto i = mid_symbols_per_spot; i > 0; --i) + { + if (mid_symbol_rotatable) + orientation = split.tangentVector().angle(); + mid_symbol->createRenderablesScaled(split.pos, orientation, output); + + if (i > 1) + { + mid_position += mid_symbol_distance_f; + next_split = SplitPathCoord::at(mid_position, split); + path.copy(split, next_split, processed_flags, processed_coords); + split = next_split; + } + } + } + + // End position + next_split = SplitPathCoord::at(effective_end_length, split); + path.copy(split, next_split, processed_flags, processed_coords); + split = next_split; + + processed_flags.back().setGapPoint(true); + Q_ASSERT(!processed_flags[processed_flags.size()-2].isCurveStart()); + } + + if (has_end && effective_cap_length > 0.0f) + { + // Create pointed line cap end + next_split = end; + if (end.is_curve_end) + { + // Get updated curve end + next_split = SplitPathCoord::at(end.clen, split); + } + createPointedLineCap(path, split, next_split, true, output); + } +} + +void LineSymbol::createPointedLineCap( + const VirtualPath& path, + const SplitPathCoord& start, + const SplitPathCoord& end, + bool is_end, + ObjectRenderables& output ) const +{ + AreaSymbol area_symbol; + area_symbol.setColor(color); + + float line_half_width = 0.001f * 0.5f * line_width; + float cap_length = 0.001f * pointed_cap_length; + float tan_angle = line_half_width / cap_length; + + MapCoordVector cap_flags; + MapCoordVectorF cap_middle_coords; + path.copy(start, end, cap_flags, cap_middle_coords); + + auto cap_size = VirtualPath::size_type(cap_middle_coords.size()); + auto cap_middle_path = VirtualPath { cap_flags, cap_middle_coords }; + + std::vector cap_lengths; + cap_lengths.reserve(cap_size); + path.copyLengths(start, end, cap_lengths); + Q_ASSERT(cap_middle_coords.size() == cap_lengths.size()); + + // Calculate coordinates on the left and right side of the line + MapCoordVectorF cap_coords; + MapCoordVectorF cap_coords_left; + float sign = is_end ? (-1) : 1; + for (MapCoordVectorF::size_type i = 0; i < cap_size; ++i) + { + float dist_from_start = is_end ? (end.clen - cap_lengths[i]) : (cap_lengths[i] - start.clen); + float factor = dist_from_start / cap_length; + //Q_ASSERT(factor >= -0.01f && factor <= 1.01f); happens when using large break lengths as these are not adjusted + factor = qBound(0.0f, factor, 1.0f); + + auto tangent_info = cap_middle_path.calculateTangentScaling(i); + auto right_vector = tangent_info.first.perpRight(); + right_vector.normalize(); + + auto radius = qBound(0.0, line_half_width*factor*tangent_info.second, line_half_width*2.0); + + cap_flags[i].setHolePoint(false); + cap_coords.emplace_back(cap_middle_coords[i] + radius * right_vector); + cap_coords_left.emplace_back(cap_middle_coords[i] - radius * right_vector); + + // Control points for bezier curves + if (i >= 3 && cap_flags[i-3].isCurveStart()) + { + cap_coords.emplace_back(cap_coords.back()); + cap_coords_left.emplace_back(cap_coords_left.back()); + + MapCoordF tangent = cap_middle_coords[i] - cap_middle_coords[i-1]; + Q_ASSERT(tangent.lengthSquared() < 999*999); + auto right_scale = tangent.length() * tan_angle * sign; + cap_coords[i-1] = cap_coords[i] - tangent - right_vector * right_scale; + cap_coords_left[i-1] = cap_coords_left[i] - tangent + right_vector * right_scale; + } + if (cap_flags[i].isCurveStart()) + { + // TODO: Tangent scaling depending on curvature? Adaptive subdivision of the curves? + MapCoordF tangent = cap_middle_coords[i+1] - cap_middle_coords[i]; + Q_ASSERT(tangent.lengthSquared() < 999*999); + float right_scale = tangent.length() * tan_angle * sign; + cap_coords.emplace_back(cap_coords[i] + tangent + right_vector * right_scale); + cap_coords_left.emplace_back(cap_coords_left[i] + tangent - right_vector * right_scale); + i += 2; + } + } + + // Create small overlap to avoid visual glitches with the on-screen display (should not matter for printing, could probably turn it off for this) + const float overlap_length = 0.05f; + if (end.clen - start.clen > 4 * overlap_length) + { + bool ok; + int end_pos = is_end ? 0 : (cap_size - 1); + int end_cap_pos = is_end ? 0 : (cap_coords.size() - 1); + MapCoordF tangent = cap_middle_path.calculateTangent(end_pos, !is_end, ok); + if (ok) + { + tangent.setLength(overlap_length * sign); + auto right = MapCoordF{ is_end ? tangent : -tangent }.perpRight(); + + MapCoordF shifted_coord = cap_coords[end_cap_pos]; + shifted_coord += tangent + right; + + MapCoordF shifted_coord_left = cap_coords_left[end_cap_pos]; + shifted_coord_left += tangent - right; + + if (is_end) + { + cap_flags.insert(cap_flags.begin(), MapCoord()); + cap_coords.insert(cap_coords.begin(), shifted_coord); + cap_coords_left.insert(cap_coords_left.begin(), shifted_coord_left); + } + else + { + cap_flags.emplace_back(); + cap_coords.emplace_back(shifted_coord); + cap_coords_left.emplace_back(shifted_coord_left); + } + } + } + + // Concatenate left and right side coordinates + cap_flags.reserve(2 * cap_flags.size()); + cap_coords.reserve(2 * cap_coords.size()); + + MapCoord curve_start; + curve_start.setCurveStart(true); + if (!is_end) + { + for (auto i = cap_coords_left.size(); i > 0; ) + { + --i; + if (i >= 3 && cap_flags[i-3].isCurveStart()) + { + cap_flags.emplace_back(curve_start); + cap_flags.emplace_back(); + cap_flags.emplace_back(); + cap_coords.emplace_back(cap_coords_left[i]); + cap_coords.emplace_back(cap_coords_left[i-1]); + cap_coords.emplace_back(cap_coords_left[i-2]); + i -= 2; + } + else + { + cap_flags.emplace_back(); + cap_coords.emplace_back(cap_coords_left[i]); + } + } + } + else + { + for (auto i = cap_coords_left.size() - 1; i > 0; ) + { + --i; + if (i >= 3 && cap_flags[i - 3].isCurveStart()) + { + cap_flags.emplace_back(curve_start); + cap_flags.emplace_back(); + cap_flags.emplace_back(); + cap_coords.emplace_back(cap_coords_left[i]); + cap_coords.emplace_back(cap_coords_left[i-1]); + cap_coords.emplace_back(cap_coords_left[i-2]); + i -= 2; + } + else if (i >= 2 && i == cap_coords_left.size() - 2 && cap_flags[i - 2].isCurveStart()) + { + cap_flags[cap_flags.size() - 1].setCurveStart(true); + cap_flags.emplace_back(); + cap_flags.emplace_back(); + cap_coords.emplace_back(cap_coords_left[i]); + cap_coords.emplace_back(cap_coords_left[i-1]); + i -= 1; + } + else + { + cap_flags.emplace_back(); + cap_coords.emplace_back(cap_coords_left[i]); + } + } + } + + // Add renderable + Q_ASSERT(cap_coords.size() >= 3); + Q_ASSERT(cap_coords.size() == cap_flags.size()); + + VirtualPath cap_path { cap_flags, cap_coords }; + cap_path.path_coords.update(0); + output.insertRenderable(new AreaRenderable(&area_symbol, cap_path)); +} + +void LineSymbol::processDashedLine( + const VirtualPath& path, + bool path_closed, + MapCoordVector& out_flags, + MapCoordVectorF& out_coords, + ObjectRenderables& output ) const +{ + auto& path_coords = path.path_coords; + Q_ASSERT(!path_coords.empty()); + + auto out_coords_size = path.size() * 4; + out_flags.reserve(out_coords_size); + out_coords.reserve(out_coords_size); + + auto last = path.last_index; + + auto groups_start = SplitPathCoord::begin(path_coords); + auto line_start = groups_start; + for (bool is_part_end = false; !is_part_end; ) + { + auto groups_end_path_coord_index = path_coords.findNextDashPoint(groups_start.path_coord_index); + auto groups_end_index = path_coords[groups_end_path_coord_index].index; + auto groups_end = SplitPathCoord::at(path_coords, groups_end_path_coord_index); + + // Intentionally look at groups_start (current node), + // not line_start (where to continue drawing)! + bool is_part_start = (groups_start.index == path.first_index); + is_part_end = (groups_end_index == last); + + line_start = createDashGroups(path, path_closed, + line_start, groups_start, groups_end, + is_part_start, is_part_end, + out_flags, out_coords, output); + + groups_start = groups_end; // Search then next split (node) after groups_end (current node). + } + Q_ASSERT(line_start.clen == groups_start.clen); +} + +SplitPathCoord LineSymbol::createDashGroups( + const VirtualPath& path, + bool path_closed, + const SplitPathCoord& line_start, + const SplitPathCoord& start, + const SplitPathCoord& end, + bool is_part_start, + bool is_part_end, + MapCoordVector& out_flags, + MapCoordVectorF& out_coords, + ObjectRenderables& output ) const +{ + auto& flags = path.coords.flags; + auto& path_coords = path.path_coords; + + auto orientation = 0.0f; + bool mid_symbol_rotatable = bool(mid_symbol) && mid_symbol->isRotatable(); + + double mid_symbol_distance_f = 0.001 * mid_symbol_distance; + + bool half_first_group = is_part_start ? (half_outer_dashes || path_closed) + : (flags[start.index].isDashPoint() && dashes_in_group == 1); + + bool ends_with_dashpoint = is_part_end ? path_closed : true; + + double dash_length_f = 0.001 * dash_length; + double break_length_f = 0.001 * break_length; + double in_group_break_length_f = 0.001 * in_group_break_length; + + double total_in_group_dash_length = dashes_in_group * dash_length_f; + double total_in_group_break_length = (dashes_in_group - 1) * in_group_break_length_f; + double total_group_length = total_in_group_dash_length + total_in_group_break_length; + double total_group_and_break_length = total_group_length + break_length_f; + + double length = end.clen - start.clen; + double length_plus_break = length + break_length_f; + + bool half_last_group = is_part_end ? (half_outer_dashes || path_closed) + : (ends_with_dashpoint && dashes_in_group == 1); + int num_half_groups = (half_first_group ? 1 : 0) + (half_last_group ? 1 : 0); + + double num_dashgroups_f = num_half_groups + + (length_plus_break - num_half_groups * 0.5 * dash_length_f) / total_group_and_break_length; + + int lower_dashgroup_count = qRound(floor(num_dashgroups_f)); + + double minimum_optimum_num_dashes = dashes_in_group * 2.0 - num_half_groups * 0.5; + double minimum_optimum_length = 2.0 * total_group_and_break_length; + double switch_deviation = 0.2 * total_group_and_break_length / dashes_in_group; + + bool set_mid_symbols = length >= dash_length_f - switch_deviation; + if (mid_symbols_per_spot > 0 && mid_symbol && !mid_symbol->isEmpty()) + { + if (line_start.clen < start.clen || (!is_part_start && flags[start.index].isDashPoint())) + { + set_mid_symbols = false; + + // Handle mid symbols at begin for explicit dash points when we draw the whole dash here + auto split = SplitPathCoord::begin(path_coords); + + PathCoord::length_type position = start.clen - (mid_symbols_per_spot - 1) * 0.5 * mid_symbol_distance_f; + for (int s = 0; s < mid_symbols_per_spot; s+=2) + { + if (position >= split.clen) + { + auto next_split = SplitPathCoord::at(position, split); + if (mid_symbol_rotatable) + orientation = next_split.tangentVector().angle(); + mid_symbol->createRenderablesScaled(next_split.pos, orientation, output); + split = next_split; + } + position += mid_symbol_distance_f; + } + } + if (half_first_group) + { + // Handle mid symbols at start for closing point or explicit dash points. + auto split = start; + + PathCoord::length_type position = split.clen + (mid_symbols_per_spot%2 + 1) * 0.5 * mid_symbol_distance_f; + for (int s = 1; s < mid_symbols_per_spot && position <= path_coords.back().clen; s+=2) + { + auto next_split = SplitPathCoord::at(position, split); + if (mid_symbol_rotatable) + orientation = next_split.tangentVector().angle(); + mid_symbol->createRenderablesScaled(next_split.pos, orientation, output); + + position += mid_symbol_distance_f; + split = next_split; + } + } + } + + if (length <= 0.0 || (lower_dashgroup_count <= 1 && length < minimum_optimum_length - minimum_optimum_num_dashes * switch_deviation)) + { + // Line part too short for dashes + if (is_part_end) + { + // Can't be handled correctly by the next round of dash groups drawing: + // Just draw a continuous line. + processContinuousLine(path, + line_start, end, + !half_first_group, !half_last_group, + out_flags, out_coords, set_mid_symbols, output); + } + else + { + // Give this length to the next round of dash groups drawing. + return line_start; + } + } + else + { + int higher_dashgroup_count = qRound(ceil(num_dashgroups_f)); + double lower_dashgroup_deviation = (length_plus_break - lower_dashgroup_count * total_group_and_break_length) / lower_dashgroup_count; + double higher_dashgroup_deviation = (higher_dashgroup_count * total_group_and_break_length - length_plus_break) / higher_dashgroup_count; + Q_ASSERT(half_first_group || half_last_group || (lower_dashgroup_deviation >= -0.001 && higher_dashgroup_deviation >= -0.001)); // TODO; seems to fail as long as halving first/last dashes affects the outermost dash only + int num_dashgroups = (lower_dashgroup_deviation > higher_dashgroup_deviation) ? higher_dashgroup_count : lower_dashgroup_count; + Q_ASSERT(num_dashgroups >= 2); + + int num_half_dashes = 2*num_dashgroups*dashes_in_group - num_half_groups; + double adjusted_dash_length = (length - (num_dashgroups-1) * break_length_f - num_dashgroups * total_in_group_break_length) / (0.5 * num_half_dashes); + adjusted_dash_length = qMax(adjusted_dash_length, 0.0); // could be negative for large break lengths + + double cur_length = start.clen; + SplitPathCoord dash_start = line_start; + + for (int dashgroup = 1; dashgroup <= num_dashgroups; ++dashgroup) + { + bool is_first_dashgroup = dashgroup == 1; + bool is_last_dashgroup = dashgroup == num_dashgroups; + + for (int dash = 1; dash <= dashes_in_group; ++dash) + { + // Draw a single dash + bool is_first_dash = is_first_dashgroup && dash == 1; + bool is_last_dash = is_last_dashgroup && dash == dashes_in_group; + + // The dash has an start if it is not the first dash in a half first group. + bool has_start = !(is_first_dash && half_first_group); + // The dash has an end if it is not the last dash in a half last group. + bool has_end = !(is_last_dash && half_last_group); + + Q_ASSERT(has_start || has_end); // At least one half must be left... + + bool is_half_dash = has_start != has_end; + double cur_dash_length = is_half_dash ? adjusted_dash_length / 2 : adjusted_dash_length; + set_mid_symbols = !is_half_dash; + + if (!is_first_dash) + { + auto next_dash_start = SplitPathCoord::at(cur_length, dash_start); + path.copy(dash_start, next_dash_start, out_flags, out_coords); + out_flags.back().setGapPoint(true); + dash_start = next_dash_start; + } + if (is_last_dash && !is_part_end) + { + // Last dash before a dash point (which is not the closing point): + // Give the remaining length to the next round of dash groups drawing. + return dash_start; + } + + SplitPathCoord dash_end = SplitPathCoord::at(cur_length + cur_dash_length, dash_start); + processContinuousLine(path, + dash_start, dash_end, + has_start, has_end, + out_flags, out_coords, set_mid_symbols, output); + cur_length += cur_dash_length; + dash_start = dash_end; + + if (dash < dashes_in_group) + cur_length += in_group_break_length_f; + } + + if (dashgroup < num_dashgroups) + cur_length += break_length_f; + } + + if (half_last_group && mid_symbols_per_spot > 0 && mid_symbol && !mid_symbol->isEmpty()) + { + // Handle mid symbols at end for closing point or for (some) explicit dash points. + auto split = start; + + PathCoord::length_type position = end.clen - (mid_symbols_per_spot-1) * 0.5 * mid_symbol_distance_f; + for (int s = 0; s < mid_symbols_per_spot; s+=2) + { + auto next_split = SplitPathCoord::at(position, split); + if (mid_symbol_rotatable) + orientation = next_split.tangentVector().angle(); + mid_symbol->createRenderablesScaled(next_split.pos, orientation, output); + + position += mid_symbol_distance_f; + split = next_split; + } + } + } + + return end; +} + +void LineSymbol::createDashSymbolRenderables( + const VirtualPath& path, + bool path_closed , + ObjectRenderables& output) const +{ + Q_ASSERT(dash_symbol); + + auto& flags = path.coords.flags; + auto& coords = path.coords; + + auto last = path.last_index; + auto i = path.first_index; + if (suppress_dash_symbol_at_ends && path.size() > 0) + { + // Suppress dash symbol at line ends + ++i; + --last; + } + else if (path_closed) + { + ++i; + } + + for (; i <= last; ++i) + { + if (flags[i].isDashPoint()) + { + const auto params = path.calculateTangentScaling(i); + //params.first.perpRight(); + auto rotation = dash_symbol->isRotatable() ? params.first.angle() : 0.0; + auto scale = scale_dash_symbol ? qMin(params.second, 2.0 * LineSymbol::miterLimit()) : 1.0; + dash_symbol->createRenderablesScaled(coords[i], rotation, output, scale); + } + } +} + +void LineSymbol::createMidSymbolRenderables( + const VirtualPath& path, + bool path_closed, + ObjectRenderables& output) const +{ + Q_ASSERT(mid_symbol); + auto orientation = 0.0f; + bool mid_symbol_rotatable = bool(mid_symbol) && mid_symbol->isRotatable(); + + int mid_symbol_num_gaps = mid_symbols_per_spot - 1; + + double segment_length_f = 0.001 * segment_length; + double end_length_f = 0.001 * end_length; + double end_length_twice_f = 0.002 * end_length; + double mid_symbol_distance_f = 0.001 * mid_symbol_distance; + double mid_symbols_length = mid_symbol_num_gaps * mid_symbol_distance_f; + + auto& path_coords = path.path_coords; + Q_ASSERT(!path_coords.empty()); + + auto groups_start = SplitPathCoord::begin(path_coords); + if (end_length == 0 && !path_closed) + { + // Insert point at start coordinate + if (mid_symbol_rotatable) + orientation = groups_start.tangentVector().angle(); + mid_symbol->createRenderablesScaled(groups_start.pos, orientation, output); + } + + auto part_end = path.last_index; + while (groups_start.index != part_end) + { + auto groups_end_path_coord_index = path_coords.findNextDashPoint(groups_start.path_coord_index); + auto groups_end = SplitPathCoord::at(path_coords, groups_end_path_coord_index); + + // The total length of the current continuous part + double length = groups_end.clen - groups_start.clen; + // The length which is available for placing mid symbols + double segmented_length = qMax(0.0, length - end_length_twice_f) - mid_symbols_length; + // The number of segments to be created by mid symbols + double segment_count_raw = qMax((end_length == 0) ? 1.0 : 0.0, (segmented_length / (segment_length_f + mid_symbols_length))); + int lower_segment_count = qFloor(segment_count_raw); + int higher_segment_count = qCeil(segment_count_raw); + + if (end_length > 0) + { + if (length <= mid_symbols_length) + { + if (show_at_least_one_symbol) + { + // Insert point at start coordinate + if (mid_symbol_rotatable) + orientation = groups_start.tangentVector().angle(); + mid_symbol->createRenderablesScaled(groups_start.pos, orientation, output); + + // Insert point at end coordinate + if (mid_symbol_rotatable) + orientation = groups_end.tangentVector().angle(); + mid_symbol->createRenderablesScaled(groups_end.pos, orientation, output); + } + } + else + { + double lower_abs_deviation = qAbs(length - lower_segment_count * segment_length_f - (lower_segment_count+1)*mid_symbols_length - end_length_twice_f); + double higher_abs_deviation = qAbs(length - higher_segment_count * segment_length_f - (higher_segment_count+1)*mid_symbols_length - end_length_twice_f); + int segment_count = (lower_abs_deviation >= higher_abs_deviation) ? higher_segment_count : lower_segment_count; + + double deviation = (lower_abs_deviation >= higher_abs_deviation) ? -higher_abs_deviation : lower_abs_deviation; + double ideal_length = segment_count * segment_length_f + end_length_twice_f; + double adjusted_end_length = end_length_f + deviation * (end_length_f / ideal_length); + double adjusted_segment_length = segment_length_f + deviation * (segment_length_f / ideal_length); + Q_ASSERT(qAbs(2*adjusted_end_length + segment_count*adjusted_segment_length + (segment_count + 1)*mid_symbols_length - length) < 0.001); + + if (adjusted_segment_length >= 0 && (show_at_least_one_symbol || higher_segment_count > 0 || length > end_length_twice_f - 0.5 * (segment_length_f + mid_symbols_length))) + { + adjusted_segment_length += mid_symbols_length; + auto split = groups_start; + for (int i = 0; i < segment_count + 1; ++i) + { + double position = groups_start.clen + adjusted_end_length + i * adjusted_segment_length - mid_symbol_distance_f; + for (int s = 0; s < mid_symbols_per_spot; ++s) + { + position += mid_symbol_distance_f; + split = SplitPathCoord::at(position, split); + if (mid_symbol_rotatable) + orientation = split.tangentVector().angle(); + mid_symbol->createRenderablesScaled(split.pos, orientation, output); + } + } + } + } + } + else + { + // end_length == 0 + if (length > mid_symbols_length) + { + double lower_segment_deviation = qAbs(length - lower_segment_count * segment_length_f - (lower_segment_count+1)*mid_symbols_length) / lower_segment_count; + double higher_segment_deviation = qAbs(length - higher_segment_count * segment_length_f - (higher_segment_count+1)*mid_symbols_length) / higher_segment_count; + int segment_count = (lower_segment_deviation > higher_segment_deviation) ? higher_segment_count : lower_segment_count; + double adapted_segment_length = (length - (segment_count+1)*mid_symbols_length) / segment_count + mid_symbols_length; + Q_ASSERT(qAbs(segment_count * adapted_segment_length + mid_symbols_length) - length < 0.001f); + + if (adapted_segment_length >= mid_symbols_length) + { + auto split = groups_start; + for (int i = 0; i <= segment_count; ++i) + { + double position = groups_start.clen + i * adapted_segment_length - mid_symbol_distance_f; + for (int s = 0; s < mid_symbols_per_spot; ++s) + { + position += mid_symbol_distance_f; + + // The outermost symbols are handled outside this loop + if (i == 0 && s == 0) + continue; + if (i == segment_count && s == mid_symbol_num_gaps) + break; + + split = SplitPathCoord::at(position, split); + if (mid_symbol_rotatable) + orientation = split.tangentVector().angle(); + mid_symbol->createRenderablesScaled(split.pos, orientation, output); + } + } + } + } + + // Insert point at end coordinate + if (mid_symbol_rotatable) + orientation = groups_end.tangentVector().angle(); + mid_symbol->createRenderablesScaled(groups_end.pos, orientation, output); + } + + groups_start = groups_end; // Search then next split (node) after groups_end (current node). + } +} + +void LineSymbol::colorDeleted(const MapColor* color) +{ + bool have_changes = false; + if (mid_symbol && mid_symbol->containsColor(color)) + { + mid_symbol->colorDeleted(color); + have_changes = true; + } + if (start_symbol && start_symbol->containsColor(color)) + { + start_symbol->colorDeleted(color); + have_changes = true; + } + if (end_symbol && end_symbol->containsColor(color)) + { + end_symbol->colorDeleted(color); + have_changes = true; + } + if (dash_symbol && dash_symbol->containsColor(color)) + { + dash_symbol->colorDeleted(color); + have_changes = true; + } + if (color == this->color) + { + this->color = nullptr; + have_changes = true; + } + if (color == border.color) + { + this->border.color = nullptr; + have_changes = true; + } + if (color == right_border.color) + { + this->right_border.color = nullptr; + have_changes = true; + } + if (have_changes) + resetIcon(); +} + +bool LineSymbol::containsColor(const MapColor* color) const +{ + if (color == this->color || color == border.color || color == right_border.color) + return true; + if (mid_symbol && mid_symbol->containsColor(color)) + return true; + if (start_symbol && start_symbol->containsColor(color)) + return true; + if (end_symbol && end_symbol->containsColor(color)) + return true; + if (dash_symbol && dash_symbol->containsColor(color)) + return true; + return false; +} + +const MapColor* LineSymbol::guessDominantColor() const +{ + bool has_main_line = line_width > 0 && color; + bool has_border = hasBorder() && border.width > 0 && border.color; + bool has_right_border = hasBorder() && right_border.width > 0 && right_border.color; + + // Use the color of the thickest line + if (has_main_line) + { + if (has_border) + { + if (has_right_border) + { + if (line_width > 2 * border.width) + return (line_width > 2 * right_border.width) ? color : right_border.color; + else + return (border.width > right_border.width) ? border.color : right_border.color; + } + else + return (line_width > 2 * border.width) ? color : border.color; + } + else + { + if (has_right_border) + return (line_width > 2 * right_border.width) ? color : right_border.color; + else + return color; + } + } + else + { + if (has_border) + { + if (has_right_border) + return (border.width > right_border.width) ? border.color : right_border.color; + else + return border.color; + } + else + { + if (has_right_border) + return right_border.color; + } + } + + const MapColor* dominant_color = mid_symbol ? mid_symbol->guessDominantColor() : nullptr; + if (dominant_color) return dominant_color; + + dominant_color = start_symbol ? start_symbol->guessDominantColor() : nullptr; + if (dominant_color) return dominant_color; + + dominant_color = end_symbol ? end_symbol->guessDominantColor() : nullptr; + if (dominant_color) return dominant_color; + + dominant_color = dash_symbol ? dash_symbol->guessDominantColor() : nullptr; + if (dominant_color) return dominant_color; + + return nullptr; +} + +void LineSymbol::scale(double factor) +{ + line_width = qRound(factor * line_width); + + minimum_length = qRound(factor * minimum_length); + pointed_cap_length = qRound(factor * pointed_cap_length); + + if (start_symbol) + start_symbol->scale(factor); + if (mid_symbol) + mid_symbol->scale(factor); + if (end_symbol) + end_symbol->scale(factor); + if (dash_symbol) + dash_symbol->scale(factor); + + segment_length = qRound(factor * segment_length); + end_length = qRound(factor * end_length); + minimum_mid_symbol_count = qRound(factor * minimum_mid_symbol_count); + minimum_mid_symbol_count_when_closed = qRound(factor * minimum_mid_symbol_count_when_closed); + + dash_length = qRound(factor * dash_length); + break_length = qRound(factor * break_length); + in_group_break_length = qRound(factor * in_group_break_length); + mid_symbol_distance = qRound(factor * mid_symbol_distance); + + border.scale(factor); + right_border.scale(factor); + + resetIcon(); +} + +void LineSymbol::ensurePointSymbols(const QString& start_name, const QString& mid_name, const QString& end_name, const QString& dash_name) +{ + if (!start_symbol) + { + start_symbol = new PointSymbol(); + start_symbol->setRotatable(true); + } + start_symbol->setName(start_name); + if (!mid_symbol) + { + mid_symbol = new PointSymbol(); + mid_symbol->setRotatable(true); + } + mid_symbol->setName(mid_name); + if (!end_symbol) + { + end_symbol = new PointSymbol(); + end_symbol->setRotatable(true); + } + end_symbol->setName(end_name); + if (!dash_symbol) + { + dash_symbol = new PointSymbol(); + dash_symbol->setRotatable(true); + } + dash_symbol->setName(dash_name); +} + +void LineSymbol::cleanupPointSymbols() +{ + if (start_symbol && start_symbol->isEmpty()) + { + delete start_symbol; + start_symbol = nullptr; + } + if (mid_symbol && mid_symbol->isEmpty()) + { + delete mid_symbol; + mid_symbol = nullptr; + } + if (end_symbol && end_symbol->isEmpty()) + { + delete end_symbol; + end_symbol = nullptr; + } + if (dash_symbol && dash_symbol->isEmpty()) + { + delete dash_symbol; + dash_symbol = nullptr; + } +} + + + +qreal LineSymbol::dimensionForIcon() const +{ + // calculateLargestLineExtent() gives half line width, and for the icon, + // we suggest a half line width of white space on each side. + auto size = 4 * calculateLargestLineExtent(); + if (start_symbol && !start_symbol->isEmpty()) + size = qMax(size, start_symbol->dimensionForIcon()); + if (mid_symbol && !mid_symbol->isEmpty()) + size = qMax(size, 2 * mid_symbol->dimensionForIcon() + getMidSymbolDistance() * (getMidSymbolsPerSpot() - 1) / 1000); + if (dash_symbol && !dash_symbol->isEmpty()) + size = qMax(size, 2 * dash_symbol->dimensionForIcon()); + if (end_symbol && !end_symbol->isEmpty()) + size = qMax(size, end_symbol->dimensionForIcon()); + return size; +} + + +qreal LineSymbol::calculateLargestLineExtent() const +{ + auto line_extent_f = 0.001 * 0.5 * getLineWidth(); + auto result = line_extent_f; + if (hasBorder()) + { + result = qMax(result, line_extent_f + 0.001 * (getBorder().shift + getBorder().width)/2); + result = qMax(result, line_extent_f + 0.001 * (getRightBorder().shift + getRightBorder().width)/2); + } + return result; +} + + + +void LineSymbol::setStartSymbol(PointSymbol* symbol) +{ + replaceSymbol(start_symbol, symbol, QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Start symbol")); +} +void LineSymbol::setMidSymbol(PointSymbol* symbol) +{ + replaceSymbol(mid_symbol, symbol, QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Mid symbol")); +} +void LineSymbol::setEndSymbol(PointSymbol* symbol) +{ + replaceSymbol(end_symbol, symbol, QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "End symbol")); +} +void LineSymbol::setDashSymbol(PointSymbol* symbol) +{ + replaceSymbol(dash_symbol, symbol, QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Dash symbol")); +} +void LineSymbol::replaceSymbol(PointSymbol*& old_symbol, PointSymbol* replace_with, const QString& name) +{ + delete old_symbol; + old_symbol = replace_with; + replace_with->setName(name); +} + +#ifndef NO_NATIVE_FILE_FORMAT + +bool LineSymbol::loadImpl(QIODevice* file, int version, Map* map) +{ + file->read((char*)&line_width, sizeof(int)); + int temp; + file->read((char*)&temp, sizeof(int)); + color = (temp >= 0) ? map->getColor(temp) : nullptr; + if (version >= 2) + file->read((char*)&minimum_length, sizeof(int)); + file->read((char*)&temp, sizeof(int)); + cap_style = (CapStyle)temp; + file->read((char*)&temp, sizeof(int)); + join_style = (JoinStyle)temp; + file->read((char*)&pointed_cap_length, sizeof(int)); + bool have_symbol; + file->read((char*)&have_symbol, sizeof(bool)); + if (have_symbol) + { + start_symbol = new PointSymbol(); + if (!start_symbol->load(file, version, map)) + return false; + } + file->read((char*)&have_symbol, sizeof(bool)); + if (have_symbol) + { + mid_symbol = new PointSymbol(); + if (!mid_symbol->load(file, version, map)) + return false; + } + file->read((char*)&have_symbol, sizeof(bool)); + if (have_symbol) + { + end_symbol = new PointSymbol(); + if (!end_symbol->load(file, version, map)) + return false; + } + file->read((char*)&have_symbol, sizeof(bool)); + if (have_symbol) + { + dash_symbol = new PointSymbol(); + if (!dash_symbol->load(file, version, map)) + return false; + } + file->read((char*)&dashed, sizeof(bool)); + file->read((char*)&segment_length, sizeof(int)); + if (version >= 1) + file->read((char*)&end_length, sizeof(int)); + if (version >= 5) + file->read((char*)&show_at_least_one_symbol, sizeof(bool)); + if (version >= 2) + { + file->read((char*)&minimum_mid_symbol_count, sizeof(int)); + file->read((char*)&minimum_mid_symbol_count_when_closed, sizeof(int)); + } + file->read((char*)&dash_length, sizeof(int)); + file->read((char*)&break_length, sizeof(int)); + file->read((char*)&dashes_in_group, sizeof(int)); + file->read((char*)&in_group_break_length, sizeof(int)); + file->read((char*)&half_outer_dashes, sizeof(bool)); + file->read((char*)&mid_symbols_per_spot, sizeof(int)); + file->read((char*)&mid_symbol_distance, sizeof(int)); + file->read((char*)&have_border_lines, sizeof(bool)); + + if (version <= 22) + { + if (!border.load(file, version, map)) + return false; + right_border.assign(border, nullptr); + } + else + { + bool are_borders_different; + file->read((char*)&are_borders_different, sizeof(bool)); + + if (!border.load(file, version, map)) + return false; + if (are_borders_different) + { + if (!right_border.load(file, version, map)) + return false; + } + else + right_border.assign(border, nullptr); + } + + cleanupPointSymbols(); + + return true; +} + +#endif + +void LineSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const +{ + xml.writeStartElement(QString::fromLatin1("line_symbol")); + xml.writeAttribute(QString::fromLatin1("color"), QString::number(map.findColorIndex(color))); + xml.writeAttribute(QString::fromLatin1("line_width"), QString::number(line_width)); + xml.writeAttribute(QString::fromLatin1("minimum_length"), QString::number(minimum_length)); + xml.writeAttribute(QString::fromLatin1("join_style"), QString::number(join_style)); + xml.writeAttribute(QString::fromLatin1("cap_style"), QString::number(cap_style)); + xml.writeAttribute(QString::fromLatin1("pointed_cap_length"), QString::number(pointed_cap_length)); + + if (dashed) + xml.writeAttribute(QString::fromLatin1("dashed"), QString::fromLatin1("true")); + xml.writeAttribute(QString::fromLatin1("segment_length"), QString::number(segment_length)); + xml.writeAttribute(QString::fromLatin1("end_length"), QString::number(end_length)); + if (show_at_least_one_symbol) + xml.writeAttribute(QString::fromLatin1("show_at_least_one_symbol"), QString::fromLatin1("true")); + xml.writeAttribute(QString::fromLatin1("minimum_mid_symbol_count"), QString::number(minimum_mid_symbol_count)); + xml.writeAttribute(QString::fromLatin1("minimum_mid_symbol_count_when_closed"), QString::number(minimum_mid_symbol_count_when_closed)); + xml.writeAttribute(QString::fromLatin1("dash_length"), QString::number(dash_length)); + xml.writeAttribute(QString::fromLatin1("break_length"), QString::number(break_length)); + xml.writeAttribute(QString::fromLatin1("dashes_in_group"), QString::number(dashes_in_group)); + xml.writeAttribute(QString::fromLatin1("in_group_break_length"), QString::number(in_group_break_length)); + if (half_outer_dashes) + xml.writeAttribute(QString::fromLatin1("half_outer_dashes"), QString::fromLatin1("true")); + xml.writeAttribute(QString::fromLatin1("mid_symbols_per_spot"), QString::number(mid_symbols_per_spot)); + xml.writeAttribute(QString::fromLatin1("mid_symbol_distance"), QString::number(mid_symbol_distance)); + if (suppress_dash_symbol_at_ends) + xml.writeAttribute(QString::fromLatin1("suppress_dash_symbol_at_ends"), QString::fromLatin1("true")); + if (!scale_dash_symbol) + xml.writeAttribute(QString::fromLatin1("scale_dash_symbol"), QString::fromLatin1("false")); + + if (start_symbol) + { + xml.writeStartElement(QString::fromLatin1("start_symbol")); + start_symbol->save(xml, map); + xml.writeEndElement(); + } + + if (mid_symbol) + { + xml.writeStartElement(QString::fromLatin1("mid_symbol")); + mid_symbol->save(xml, map); + xml.writeEndElement(); + } + + if (end_symbol) + { + xml.writeStartElement(QString::fromLatin1("end_symbol")); + end_symbol->save(xml, map); + xml.writeEndElement(); + } + + if (dash_symbol) + { + xml.writeStartElement(QString::fromLatin1("dash_symbol")); + dash_symbol->save(xml, map); + xml.writeEndElement(); + } + + if (have_border_lines) + { + xml.writeStartElement(QString::fromLatin1("borders")); + bool are_borders_different = areBordersDifferent(); + if (are_borders_different) + xml.writeAttribute(QString::fromLatin1("borders_different"), QString::fromLatin1("true")); + border.save(xml, map); + if (are_borders_different) + right_border.save(xml, map); + xml.writeEndElement(/*borders*/); + } + + xml.writeEndElement(/*line_symbol*/); +} + +bool LineSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) +{ + if (xml.name() != QLatin1String("line_symbol")) + return false; + + QXmlStreamAttributes attributes = xml.attributes(); + int temp = attributes.value(QLatin1String("color")).toInt(); + color = map.getColor(temp); + line_width = attributes.value(QLatin1String("line_width")).toInt(); + minimum_length = attributes.value(QLatin1String("minimum_length")).toInt(); + join_style = static_cast(attributes.value(QLatin1String("join_style")).toInt()); + cap_style = static_cast(attributes.value(QLatin1String("cap_style")).toInt()); + pointed_cap_length = attributes.value(QLatin1String("pointed_cap_length")).toInt(); + + dashed = (attributes.value(QLatin1String("dashed")) == QLatin1String("true")); + segment_length = attributes.value(QLatin1String("segment_length")).toInt(); + end_length = attributes.value(QLatin1String("end_length")).toInt(); + show_at_least_one_symbol = (attributes.value(QLatin1String("show_at_least_one_symbol")) == QLatin1String("true")); + minimum_mid_symbol_count = attributes.value(QLatin1String("minimum_mid_symbol_count")).toInt(); + minimum_mid_symbol_count_when_closed = attributes.value(QLatin1String("minimum_mid_symbol_count_when_closed")).toInt(); + dash_length = attributes.value(QLatin1String("dash_length")).toInt(); + break_length = attributes.value(QLatin1String("break_length")).toInt(); + dashes_in_group = attributes.value(QLatin1String("dashes_in_group")).toInt(); + in_group_break_length = attributes.value(QLatin1String("in_group_break_length")).toInt(); + half_outer_dashes = (attributes.value(QLatin1String("half_outer_dashes")) == QLatin1String("true")); + mid_symbols_per_spot = attributes.value(QLatin1String("mid_symbols_per_spot")).toInt(); + mid_symbol_distance = attributes.value(QLatin1String("mid_symbol_distance")).toInt(); + suppress_dash_symbol_at_ends = (attributes.value(QLatin1String("suppress_dash_symbol_at_ends")) == QLatin1String("true")); + scale_dash_symbol = (attributes.value(QLatin1String("scale_dash_symbol")) != QLatin1String("false")); + + have_border_lines = false; + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("start_symbol")) + start_symbol = loadPointSymbol(xml, map, symbol_dict); + else if (xml.name() == QLatin1String("mid_symbol")) + mid_symbol = loadPointSymbol(xml, map, symbol_dict); + else if (xml.name() == QLatin1String("end_symbol")) + end_symbol = loadPointSymbol(xml, map, symbol_dict); + else if (xml.name() == QLatin1String("dash_symbol")) + dash_symbol = loadPointSymbol(xml, map, symbol_dict); + else if (xml.name() == QLatin1String("borders")) + { +// bool are_borders_different = (xml.attributes().value("borders_different") == "true"); + + bool right_border_loaded = false; + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("border")) + { + if (!have_border_lines) + { + border.load(xml, map); + have_border_lines = true; + } + else + { + right_border.load(xml, map); + right_border_loaded = true; + xml.skipCurrentElement(); + break; + } + } + else + xml.skipCurrentElement(); + } + + if (have_border_lines && !right_border_loaded) + right_border.assign(border, nullptr); + } + else + xml.skipCurrentElement(); // unknown + } + + cleanupPointSymbols(); + + return true; +} + +PointSymbol* LineSymbol::loadPointSymbol(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) +{ + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("symbol")) + { + PointSymbol* symbol = static_cast(Symbol::load(xml, map, symbol_dict)); + xml.skipCurrentElement(); + return symbol; + } + else + xml.skipCurrentElement(); + } + return nullptr; +} + +bool LineSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const +{ + Q_UNUSED(case_sensitivity); + + const LineSymbol* line = static_cast(other); + if (line_width != line->line_width) + return false; + if (minimum_length != line->minimum_length) + return false; + if (line_width > 0) + { + if (!MapColor::equal(color, line->color)) + return false; + + if ((cap_style != line->cap_style) || + (join_style != line->join_style)) + return false; + if (cap_style == PointedCap && (pointed_cap_length != line->pointed_cap_length)) + return false; + + if (dashed != line->dashed) + return false; + if (dashed) + { + if (dash_length != line->dash_length || + break_length != line->break_length || + dashes_in_group != line->dashes_in_group || + half_outer_dashes != line->half_outer_dashes || + (dashes_in_group > 1 && (in_group_break_length != line->in_group_break_length))) + return false; + } + else + { + if (segment_length != line->segment_length || + end_length != line->end_length || + (mid_symbol && (show_at_least_one_symbol != line->show_at_least_one_symbol || + minimum_mid_symbol_count != line->minimum_mid_symbol_count || + minimum_mid_symbol_count_when_closed != line->minimum_mid_symbol_count_when_closed))) + return false; + } + } + + if (bool(start_symbol) != bool(line->start_symbol)) + return false; + if (start_symbol && !start_symbol->equals(line->start_symbol)) + return false; + + if (bool(mid_symbol) != bool(line->mid_symbol)) + return false; + if (mid_symbol && !mid_symbol->equals(line->mid_symbol)) + return false; + + if (bool(end_symbol) != bool(line->end_symbol)) + return false; + if (end_symbol && !end_symbol->equals(line->end_symbol)) + return false; + + if (bool(dash_symbol) != bool(line->dash_symbol)) + return false; + if (dash_symbol && !dash_symbol->equals(line->dash_symbol)) + return false; + if (suppress_dash_symbol_at_ends != line->suppress_dash_symbol_at_ends) + return false; + if (scale_dash_symbol != line->scale_dash_symbol) + return false; + + if (mid_symbol) + { + if (mid_symbols_per_spot != line->mid_symbols_per_spot) + return false; + if (mid_symbol_distance != line->mid_symbol_distance) + return false; + } + + if (have_border_lines != line->have_border_lines) + return false; + if (have_border_lines) + { + if (!border.equals(&line->border) || !right_border.equals(&line->right_border)) + return false; + } + + return true; +} + + +} // namespace OpenOrienteering diff --git a/src/core/symbols/line_symbol.h b/src/core/symbols/line_symbol.h new file mode 100644 index 0000000..c89aa52 --- /dev/null +++ b/src/core/symbols/line_symbol.h @@ -0,0 +1,382 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_LINE_SYMBOL_H +#define OPENORIENTEERING_LINE_SYMBOL_H + +#include // IWYU pragma: keep + +#include +#include +#include + +#include "core/map_coord.h" // IWYU pragma: keep +#include "core/symbols/symbol.h" + +class QIODevice; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class LineSymbol; +class Map; +class MapColor; +class MapColorMap; +class Object; +class ObjectRenderables; +class PathObject; +class PathPartVector; +class PointSymbol; +class SplitPathCoord; +class SymbolPropertiesWidget; +class SymbolSettingDialog; +class VirtualCoordVector; +class VirtualPath; + +using MapCoordVector = std::vector; +using MapCoordVectorF = std::vector; + + +/** Settings for a line symbol's border. */ +struct LineSymbolBorder +{ + const MapColor* color; + int width; + int shift; + bool dashed; + int dash_length; + int break_length; + + void reset() noexcept; + bool load(QIODevice* file, int version, Map* map); + void save(QXmlStreamWriter& xml, const Map& map) const; + bool load(QXmlStreamReader& xml, const Map& map); + bool equals(const LineSymbolBorder* other) const; + void assign(const LineSymbolBorder& other, const MapColorMap* color_map); + + bool isVisible() const; + void createSymbol(LineSymbol& out) const; + void scale(double factor); +}; + + +/** Symbol for PathObjects which displays a line along the path. */ +class LineSymbol : public Symbol +{ +friend class LineSymbolSettings; +friend class PointSymbolEditorWidget; +friend class OCAD8FileImport; +public: + enum CapStyle + { + FlatCap = 0, + RoundCap = 1, + SquareCap = 2, + PointedCap = 3 + }; + + enum JoinStyle + { + BevelJoin = 0, + MiterJoin = 1, + RoundJoin = 2 + }; + + /** Constructs an empty line symbol. */ + LineSymbol() noexcept; + ~LineSymbol() override; + Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; + + bool validate() const override; + + void createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + Symbol::RenderableOptions options ) const override; + + void createRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables &output, + Symbol::RenderableOptions options ) const override; + + /** + * Creates the renderables for a single path. + * + * @deprecated + * + * Calls to this function need to be replaced by calls to createPathCoordRenderables() + * as soon as it is no longer neccesary to update the PathCoordVector in advance. + */ + void createPathRenderables(const Object* object, bool path_closed, const MapCoordVector& flags, const MapCoordVectorF& coords, ObjectRenderables& output) const; + + /** + * Creates the renderables for a single VirtualPath. + */ + void createPathCoordRenderables(const Object* object, const VirtualPath& path, bool path_closed, ObjectRenderables& output) const; + + void colorDeleted(const MapColor* color) override; + bool containsColor(const MapColor* color) const override; + const MapColor* guessDominantColor() const override; + void scale(double factor) override; + + /** + * Creates empty point symbols with the given names for undefined subsymbols. + * + * After calling this method, all subsymbols are defined, i.e. not nullptr. + * Call cleanupPointSymbols() later to remove the empty symbols. + */ + void ensurePointSymbols( + const QString& start_name, + const QString& mid_name, + const QString& end_name, + const QString& dash_name + ); + + /** + * Deletes unused point symbols and sets them to nullptr again. + * + * See ensurePointSymbols(). + */ + void cleanupPointSymbols(); + + + /** + * Returns the dimension which shall considered when scaling the icon. + */ + qreal dimensionForIcon() const override; + + /** + * Returns the largest extent (half width) of the components of this line. + */ + qreal calculateLargestLineExtent() const override; + + + + /** + * Returns the limit for miter joins in units of the line width. + * See the Qt docs for QPainter::setMiterJoin(). + * TODO: Should that better be a line property? + * FIXME: shall be 0 for border lines. + */ + static constexpr qreal miterLimit() { return 1; } + + // Getters / Setters + inline int getLineWidth() const {return line_width;} + inline void setLineWidth(double width) {line_width = qRound(1000 * width);} + inline const MapColor* getColor() const {return color;} + inline void setColor(const MapColor* color) {this->color = color;} + inline int getMinimumLength() const {return minimum_length;} + inline void setMinimumLength(int length) {this->minimum_length = length;} + inline CapStyle getCapStyle() const {return cap_style;} + inline void setCapStyle(CapStyle style) {cap_style = style;} + inline JoinStyle getJoinStyle() const {return join_style;} + inline void setJoinStyle(JoinStyle style) {join_style = style;} + inline int getPointedCapLength() const {return pointed_cap_length;} + inline void setPointedCapLength(int value) {pointed_cap_length = value;} + inline bool isDashed() const {return dashed;} + inline void setDashed(bool value) {dashed = value;} + + inline PointSymbol* getStartSymbol() const {return start_symbol;} + void setStartSymbol(PointSymbol* symbol); + inline PointSymbol* getMidSymbol() const {return mid_symbol;} + void setMidSymbol(PointSymbol* symbol); + inline PointSymbol* getEndSymbol() const {return end_symbol;} + void setEndSymbol(PointSymbol* symbol); + inline PointSymbol* getDashSymbol() const {return dash_symbol;} + void setDashSymbol(PointSymbol* symbol); + + inline int getMidSymbolsPerSpot() const {return mid_symbols_per_spot;} + inline void setMidSymbolsPerSpot(int value) {mid_symbols_per_spot = value;} + inline int getMidSymbolDistance() const {return mid_symbol_distance;} + inline void setMidSymbolDistance(int value) {mid_symbol_distance = value;} + + inline bool getSuppressDashSymbolAtLineEnds() const {return suppress_dash_symbol_at_ends;} + inline void setSuppressDashSymbolAtLineEnds(bool value) {suppress_dash_symbol_at_ends = value;} + bool getScaleDashSymbol() const { return scale_dash_symbol; } + void setScaleDashSymbol(bool value) { scale_dash_symbol = value; } + + inline int getSegmentLength() const {return segment_length;} + inline void setSegmentLength(int value) {segment_length = value;} + inline int getEndLength() const {return end_length;} + inline void setEndLength(int value) {end_length = value;} + inline bool getShowAtLeastOneSymbol() const {return show_at_least_one_symbol;} + inline void setShowAtLeastOneSymbol(bool value) {show_at_least_one_symbol = value;} + inline int getMinimumMidSymbolCount() const {return minimum_mid_symbol_count;} + inline void setMinimumMidSymbolCount(int value) {minimum_mid_symbol_count = value;} + inline int getMinimumMidSymbolCountWhenClosed() const {return minimum_mid_symbol_count_when_closed;} + inline void setMinimumMidSymbolCountWhenClosed(int value) {minimum_mid_symbol_count_when_closed = value;} + + inline int getDashLength() const {return dash_length;} + inline void setDashLength(int value) {dash_length = value;} + inline int getBreakLength() const {return break_length;} + inline void setBreakLength(int value) {break_length = value;} + inline int getDashesInGroup() const {return dashes_in_group;} + inline void setDashesInGroup(int value) {dashes_in_group = value;} + inline int getInGroupBreakLength() const {return in_group_break_length;} + inline void setInGroupBreakLength(int value) {in_group_break_length = value;} + inline bool getHalfOuterDashes() const {return half_outer_dashes;} + inline void setHalfOuterDashes(bool value) {half_outer_dashes = value;} + + inline bool hasBorder() const {return have_border_lines;} + inline void setHasBorder(bool value) {have_border_lines = value;} + inline bool areBordersDifferent() const {return !border.equals(&right_border);} + + inline LineSymbolBorder& getBorder() {return border;} + inline const LineSymbolBorder& getBorder() const {return border;} + inline LineSymbolBorder& getRightBorder() {return right_border;} + inline const LineSymbolBorder& getRightBorder() const {return right_border;} + + SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; + +protected: +#ifndef NO_NATIVE_FILE_FORMAT + bool loadImpl(QIODevice* file, int version, Map* map) override; +#endif + void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; + bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; + PointSymbol* loadPointSymbol(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict); + bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; + + void createBorderLines( + const Object* object, + const VirtualPath& path, + ObjectRenderables& output + ) const; + + void createBorderLine( + const Object* object, + const VirtualPath& path, + bool path_closed, + ObjectRenderables& output, + const LineSymbolBorder& border, + double main_shift + ) const; + + void shiftCoordinates( + const VirtualPath& path, + double main_shift, + MapCoordVector& out_flags, + MapCoordVectorF& out_coords + ) const; + + void processContinuousLine( + const VirtualPath& path, + const SplitPathCoord& start, + const SplitPathCoord& end, + bool has_start, + bool has_end, + MapCoordVector& processed_flags, + MapCoordVectorF& processed_coords, + bool set_mid_symbols, + ObjectRenderables& output + ) const; + + void createPointedLineCap( + const VirtualPath& path, + const SplitPathCoord& start, + const SplitPathCoord& end, + bool is_end, + ObjectRenderables& output + ) const; + + void processDashedLine( + const VirtualPath& path, + bool path_closed, + MapCoordVector& out_flags, + MapCoordVectorF& out_coords, + ObjectRenderables& output + ) const; + + SplitPathCoord createDashGroups( + const VirtualPath& path, + bool path_closed, + const SplitPathCoord& line_start, + const SplitPathCoord& start, + const SplitPathCoord& end, + bool is_part_start, + bool is_part_end, + MapCoordVector& out_flags, + MapCoordVectorF& out_coords, + ObjectRenderables& output + ) const; + + void createDashSymbolRenderables( + const VirtualPath& path, + bool path_closed, + ObjectRenderables& output + ) const; + + void createMidSymbolRenderables( + const VirtualPath& path, + bool path_closed, + ObjectRenderables& output + ) const; + + void replaceSymbol(PointSymbol*& old_symbol, PointSymbol* replace_with, const QString& name); + + // Base line + int line_width; // in 1/1000 mm + const MapColor* color; + int minimum_length; + CapStyle cap_style; + JoinStyle join_style; + int pointed_cap_length; + + bool dashed; + + // Point symbols + PointSymbol* start_symbol; + PointSymbol* mid_symbol; + PointSymbol* end_symbol; + PointSymbol* dash_symbol; + + int mid_symbols_per_spot; + int mid_symbol_distance; + bool suppress_dash_symbol_at_ends; + bool scale_dash_symbol; + + // Not dashed + int segment_length; + int end_length; + bool show_at_least_one_symbol; + int minimum_mid_symbol_count; + int minimum_mid_symbol_count_when_closed; + + // Dashed + int dash_length; + int break_length; + int dashes_in_group; + int in_group_break_length; + bool half_outer_dashes; + + // Border lines + bool have_border_lines; + LineSymbolBorder border; + LineSymbolBorder right_border; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/symbols/point_symbol.cpp b/src/core/symbols/point_symbol.cpp new file mode 100644 index 0000000..de5e831 --- /dev/null +++ b/src/core/symbols/point_symbol.cpp @@ -0,0 +1,725 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "point_symbol.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/objects/object.h" +#include "core/renderables/renderable.h" +#include "core/renderables/renderable_implementation.h" +#include "core/symbols/symbol.h" +#include "core/virtual_coord_vector.h" +#include "util/util.h" + +// IWYU pragma: no_forward_declare QPainterPath +// IWYU pragma: no_forward_declare QXmlStreamReader +// IWYU pragma: no_forward_declare QXmlStreamWriter + + +namespace OpenOrienteering { + +PointSymbol::PointSymbol() noexcept +: Symbol{Symbol::Point} +, rotatable{false} +, inner_radius{1000} +, inner_color{nullptr} +, outer_width{0} +, outer_color{nullptr} +{ + // nothing else +} + + +PointSymbol::~PointSymbol() +{ + for (auto object : objects) + delete object; + for (auto symbol : symbols) + delete symbol; +} + + +Symbol* PointSymbol::duplicate(const MapColorMap* color_map) const +{ + auto new_point = new PointSymbol(); + new_point->duplicateImplCommon(this); + + new_point->rotatable = rotatable; + new_point->inner_radius = inner_radius; + new_point->inner_color = color_map ? color_map->value(inner_color) : inner_color; + new_point->outer_width = outer_width; + new_point->outer_color = color_map ? color_map->value(outer_color) : outer_color; + + new_point->symbols.resize(symbols.size()); + for (int i = 0; i < (int)symbols.size(); ++i) + new_point->symbols[i] = symbols[i]->duplicate(color_map); + + new_point->objects.resize(objects.size()); + for (int i = 0; i < (int)objects.size(); ++i) + { + new_point->objects[i] = objects[i]->duplicate(); + new_point->objects[i]->setSymbol(new_point->symbols[i], true); + } + + return new_point; +} + + + +bool PointSymbol::validate() const +{ + return std::all_of(begin(symbols), end(symbols), [](auto& symbol) { return symbol->validate(); }) + && std::all_of(begin(objects), end(objects), [](auto& object) { return object->validate(); }); +} + + + +void PointSymbol::createRenderables( + const Object* object, + const VirtualCoordVector& coords, + ObjectRenderables& output, + RenderableOptions options ) const +{ + auto point = object->asPoint(); + auto rotation = isRotatable() ? -point->getRotation() : 0.0f; + + if (options.testFlag(Symbol::RenderBaselines)) + { + const MapColor* dominant_color = guessDominantColor(); + if (dominant_color) + { + PointSymbol* point = Map::getUndefinedPoint(); + const MapColor* temp_color = point->getInnerColor(); + point->setInnerColor(dominant_color); + + point->createRenderablesScaled(coords[0], rotation, output, 1.0f); + + point->setInnerColor(temp_color); + } + } + else + { + createRenderablesScaled(coords[0], rotation, output, 1.0f); + } +} + +void PointSymbol::createRenderablesScaled(MapCoordF coord, float rotation, ObjectRenderables& output, float coord_scale) const +{ + if (inner_color && inner_radius > 0) + output.insertRenderable(new DotRenderable(this, coord)); + if (outer_color && outer_width > 0) + output.insertRenderable(new CircleRenderable(this, coord)); + + if (!objects.empty()) + { + auto offset_x = coord.x(); + auto offset_y = coord.y(); + auto cosr = 1.0; + auto sinr = 0.0; + if (rotation != 0.0) + { + cosr = cos(rotation); + sinr = sin(rotation); + } + + // Add elements which possibly need to be moved and rotated + auto size = objects.size(); + for (auto i = 0u; i < size; ++i) + { + // Point symbol elements should not be entered into the map, + // otherwise map settings like area hatching affect them + Q_ASSERT(!objects[i]->getMap()); + + const MapCoordVector& object_coords = objects[i]->getRawCoordinateVector(); + + MapCoordVectorF transformed_coords; + transformed_coords.reserve(object_coords.size()); + for (auto& coord : object_coords) + { + auto ox = coord_scale * coord.x(); + auto oy = coord_scale * coord.y(); + transformed_coords.emplace_back(ox * cosr - oy * sinr + offset_x, + oy * cosr + ox * sinr + offset_y); + } + + // TODO: if this point is rotated, it has to pass it on to its children to make it work that rotatable point objects can be children. + // But currently only basic, rotationally symmetric points can be children, so it does not matter for now. + symbols[i]->createRenderables(objects[i], VirtualCoordVector(object_coords, transformed_coords), output, Symbol::RenderNormal); + } + } +} + + +void PointSymbol::createRenderablesIfCenterInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const +{ + if (outline->contains(point_coord)) + { + if (inner_color && inner_radius > 0) + { + output.insertRenderable(new DotRenderable(this, point_coord)); + } + + if (outer_color && outer_width > 0) + { + output.insertRenderable(new CircleRenderable(this, point_coord)); + } + } + + if (!objects.empty()) + { + auto offset_x = point_coord.x(); + auto offset_y = point_coord.y(); + auto cosr = 1.0; + auto sinr = 0.0; + if (rotation != 0.0) + { + cosr = qCos(rotation); + sinr = qSin(rotation); + } + + // Add elements which possibly need to be moved and rotated + auto size = objects.size(); + for (auto i = 0u; i < size; ++i) + { + const auto symbol = symbols[i]; + const auto object = objects[i]; + + // Point symbol elements should not be entered into the map, + // otherwise map settings like area hatching affect them + Q_ASSERT(!object->getMap()); + + MapCoordF center{}; + const MapCoordVector& element_coords = object->getRawCoordinateVector(); + MapCoordVectorF transformed_coords; + transformed_coords.reserve(element_coords.size()); + for (auto& coord : element_coords) + { + auto ex = coord.x(); + auto ey = coord.y(); + transformed_coords.emplace_back(ex * cosr - ey * sinr + offset_x, + ey * cosr + ex * sinr + offset_y); + + center += transformed_coords.back(); + } + + if (!transformed_coords.empty() && outline->contains(center/transformed_coords.size())) + { + // TODO: if this point is rotated, it has to pass it on to its children to make it work that rotatable point objects can be children. + // But currently only basic, rotationally symmetric points can be children, so it does not matter for now. + symbol->createRenderables(object, VirtualCoordVector(element_coords, transformed_coords), output, Symbol::RenderNormal); + } + } + } +} + + +void PointSymbol::createPrimitivesIfCompletelyInside(MapCoordF point_coord, const QPainterPath* outline, ObjectRenderables& output) const +{ + if (inner_color && inner_radius > 0) + { + auto r = inner_radius/1000.0; + if (outline->contains({point_coord.x()-r, point_coord.y()}) + && outline->contains({point_coord.x(), point_coord.y()-r}) + && outline->contains({point_coord.x()+r, point_coord.y()}) + && outline->contains({point_coord.x(), point_coord.y()+r}) ) + { + output.insertRenderable(new DotRenderable(this, point_coord)); + } + } + + if (outer_color && outer_width > 0) + { + auto r = inner_radius/1000.0 + outer_width/2000.0; + if (outline->contains({point_coord.x()-r, point_coord.y()}) + && outline->contains({point_coord.x(), point_coord.y()-r}) + && outline->contains({point_coord.x()+r, point_coord.y()}) + && outline->contains({point_coord.x(), point_coord.y()+r}) ) + { + output.insertRenderable(new CircleRenderable(this, point_coord)); + } + } +} + + +void PointSymbol::createRenderablesIfCompletelyInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const +{ + createPrimitivesIfCompletelyInside(point_coord, outline, output); + + if (!objects.empty()) + { + auto offset_x = point_coord.x(); + auto offset_y = point_coord.y(); + auto cosr = 1.0; + auto sinr = 0.0; + if (rotation != 0.0) + { + cosr = qCos(rotation); + sinr = qSin(rotation); + } + + // Add elements which possibly need to be moved and rotated + auto size = objects.size(); + for (auto i = 0u; i < size; ++i) + { + const auto symbol = symbols[i]; + const auto object = objects[i]; + // Point symbol elements should not be entered into the map, + // otherwise map settings like area hatching affect them + Q_ASSERT(!object->getMap()); + + if (symbol->getType() == Symbol::Point) + { + auto coord = object->getRawCoordinateVector().front(); + auto transformed_coord = MapCoordF{ coord.x() * cosr - coord.y() * sinr + offset_x, + coord.y() * cosr + coord.x() * sinr + offset_y}; + static_cast(symbol)->createPrimitivesIfCompletelyInside(transformed_coord, outline, output); + continue; + } + + const MapCoordVector& element_coords = object->getRawCoordinateVector(); + MapCoordVectorF transformed_coords; + transformed_coords.reserve(element_coords.size()); + for (auto& coord : element_coords) + { + auto ex = coord.x(); + auto ey = coord.y(); + transformed_coords.emplace_back(ex * cosr - ey * sinr + offset_x, + ey * cosr + ex * sinr + offset_y); + if (!outline->contains(transformed_coords.back())) + { + transformed_coords.clear(); + break; + } + } + + if (!transformed_coords.empty()) + { + // TODO: if this point is rotated, it has to pass it on to its children to make it work that rotatable point objects can be children. + // But currently only basic, rotationally symmetric points can be children, so it does not matter for now. + symbol->createRenderables(object, VirtualCoordVector(element_coords, transformed_coords), output, Symbol::RenderNormal); + } + } + } +} + + +void PointSymbol::createPrimitivesIfPartiallyInside(MapCoordF point_coord, const QPainterPath* outline, ObjectRenderables& output) const +{ + if (inner_color && inner_radius > 0) + { + auto r = inner_radius/1000.0; + if (outline->contains({point_coord.x()-r, point_coord.y()}) + || outline->contains({point_coord.x(), point_coord.y()-r}) + || outline->contains({point_coord.x()+r, point_coord.y()}) + || outline->contains({point_coord.x(), point_coord.y()+r}) ) + { + output.insertRenderable(new DotRenderable(this, point_coord)); + } + } + + if (outer_color && outer_width > 0) + { + auto r = inner_radius/1000.0 + outer_width/2000.0; + if (outline->contains({point_coord.x()-r, point_coord.y()}) + || outline->contains({point_coord.x(), point_coord.y()-r}) + || outline->contains({point_coord.x()+r, point_coord.y()}) + || outline->contains({point_coord.x(), point_coord.y()+r}) ) + { + output.insertRenderable(new CircleRenderable(this, point_coord)); + } + } +} + + +void PointSymbol::createRenderablesIfPartiallyInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const +{ + createPrimitivesIfPartiallyInside(point_coord, outline, output); + + if (!objects.empty()) + { + auto offset_x = point_coord.x(); + auto offset_y = point_coord.y(); + auto cosr = 1.0; + auto sinr = 0.0; + if (rotation != 0.0) + { + cosr = qCos(rotation); + sinr = qSin(rotation); + } + + // Add elements which possibly need to be moved and rotated + auto size = objects.size(); + for (auto i = 0u; i < size; ++i) + { + const auto symbol = symbols[i]; + const auto object = objects[i]; + // Point symbol elements should not be entered into the map, + // otherwise map settings like area hatching affect them + Q_ASSERT(!object->getMap()); + + if (symbol->getType() == Symbol::Point) + { + auto coord = object->getRawCoordinateVector().front(); + auto transformed_coord = MapCoordF{ coord.x() * cosr - coord.y() * sinr + offset_x, + coord.y() * cosr + coord.x() * sinr + offset_y}; + static_cast(symbol)->createPrimitivesIfPartiallyInside(transformed_coord, outline, output); + continue; + } + + bool is_partially_inside = false; + const MapCoordVector& element_coords = object->getRawCoordinateVector(); + MapCoordVectorF transformed_coords; + transformed_coords.reserve(element_coords.size()); + for (auto& coord : element_coords) + { + auto ex = coord.x(); + auto ey = coord.y(); + transformed_coords.emplace_back(ex * cosr - ey * sinr + offset_x, + ey * cosr + ex * sinr + offset_y); + if (!is_partially_inside) + is_partially_inside = outline->contains(transformed_coords.back()); + } + + if (is_partially_inside) + { + // TODO: if this point is rotated, it has to pass it on to its children to make it work that rotatable point objects can be children. + // But currently only basic, rotationally symmetric points can be children, so it does not matter for now. + symbol->createRenderables(object, VirtualCoordVector(element_coords, transformed_coords), output, Symbol::RenderNormal); + } + } + } +} + + + +int PointSymbol::getNumElements() const +{ + return (int)objects.size(); +} +void PointSymbol::addElement(int pos, Object* object, Symbol* symbol) +{ + objects.insert(objects.begin() + pos, object); + symbols.insert(symbols.begin() + pos, symbol); +} +Object* PointSymbol::getElementObject(int pos) +{ + return objects[pos]; +} +const Object* PointSymbol::getElementObject(int pos) const +{ + return objects[pos]; +} +Symbol* PointSymbol::getElementSymbol(int pos) +{ + return symbols[pos]; +} +const Symbol* PointSymbol::getElementSymbol(int pos) const +{ + return symbols[pos]; +} +void PointSymbol::deleteElement(int pos) +{ + delete objects[pos]; + objects.erase(objects.begin() + pos); + delete symbols[pos]; + symbols.erase(symbols.begin() + pos); +} + +bool PointSymbol::isEmpty() const +{ + return getNumElements() == 0 && (!inner_color || inner_radius == 0) && (!outer_color || outer_width == 0); +} +bool PointSymbol::isSymmetrical() const +{ + int num_elements = (int)objects.size(); + for (int i = 0; i < num_elements; ++i) + { + if (symbols[i]->getType() != Symbol::Point) + return false; + PointObject* point = reinterpret_cast(objects[i]); + if (point->getCoordF() != MapCoordF(0, 0)) + return false; + } + return true; +} + +void PointSymbol::colorDeleted(const MapColor* color) +{ + bool change = false; + + if (color == inner_color) + { + inner_color = nullptr; + change = true; + } + if (color == outer_color) + { + outer_color = nullptr; + change = true; + } + + int num_elements = (int)objects.size(); + for (int i = 0; i < num_elements; ++i) + { + symbols[i]->colorDeleted(color); + change = true; + } + + if (change) + resetIcon(); +} +bool PointSymbol::containsColor(const MapColor* color) const +{ + if (color == inner_color) + return true; + if (color == outer_color) + return true; + + int num_elements = (int)objects.size(); + for (int i = 0; i < num_elements; ++i) + { + if (symbols[i]->containsColor(color)) + return true; + } + + return false; +} + +const MapColor* PointSymbol::guessDominantColor() const +{ + bool have_inner_color = inner_color && inner_radius > 0; + bool have_outer_color = outer_color && outer_width > 0; + if (have_inner_color != have_outer_color) + return have_inner_color ? inner_color : outer_color; + else if (have_inner_color && have_outer_color) + { + if (inner_color->isWhite()) + return outer_color; + else if (outer_color->isWhite()) + return inner_color; + else + return (qPow(inner_radius, 2) * M_PI > qPow(inner_radius + outer_width, 2) * M_PI - qPow(inner_radius, 2) * M_PI) ? inner_color : outer_color; + } + else + { + // Hope that the first element's color is representative + if (symbols.size() > 0) + return symbols[0]->guessDominantColor(); + else + return nullptr; + } +} + +void PointSymbol::scale(double factor) +{ + inner_radius = qRound(inner_radius * factor); + outer_width = qRound(outer_width * factor); + + int size = (int)objects.size(); + for (int i = 0; i < size; ++i) + { + symbols[i]->scale(factor); + objects[i]->scale(MapCoordF(0, 0), factor); + } + + resetIcon(); +} + + +qreal PointSymbol::dimensionForIcon() const +{ + auto size = qreal(0); + if (getOuterColor()) + size = 0.002 * (getInnerRadius() + getOuterWidth()); + else if (getInnerColor()) + size = 0.002 * getInnerRadius(); + + QRectF extent; + for (int i = 0; i < getNumElements(); ++i) + { + auto object = std::unique_ptr(getElementObject(i)->duplicate()); + object->setSymbol(getElementSymbol(i), true); + object->update(); + rectIncludeSafe(extent, object->getExtent()); + object->clearRenderables(); + } + if (extent.isValid()) + { + auto w = 2 * std::max(std::abs(extent.left()), std::abs(extent.right())); + auto h = 2 * std::max(std::abs(extent.top()), std::abs(extent.bottom())); + return std::max(size, std::max(w, h)); + } + + return size; +} + + + +#ifndef NO_NATIVE_FILE_FORMAT + +bool PointSymbol::loadImpl(QIODevice* file, int version, Map* map) +{ + file->read((char*)&rotatable, sizeof(bool)); + + file->read((char*)&inner_radius, sizeof(int)); + int temp; + file->read((char*)&temp, sizeof(int)); + inner_color = (temp >= 0) ? map->getColor(temp) : nullptr; + + file->read((char*)&outer_width, sizeof(int)); + file->read((char*)&temp, sizeof(int)); + outer_color = (temp >= 0) ? map->getColor(temp) : nullptr; + + int num_elements; + file->read((char*)&num_elements, sizeof(int)); + objects.resize(num_elements); + symbols.resize(num_elements); + for (int i = 0; i < num_elements; ++i) + { + int save_type; + file->read((char*)&save_type, sizeof(int)); + symbols[i] = Symbol::getSymbolForType(static_cast(save_type)); + if (!symbols[i]) + return false; + if (!symbols[i]->load(file, version, map)) + return false; + + file->read((char*)&save_type, sizeof(int)); + objects[i] = Object::getObjectForType(static_cast(save_type), symbols[i]); + if (!objects[i]) + return false; + objects[i]->load(file, version, nullptr); + } + + return true; +} + +#endif + +void PointSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const +{ + xml.writeStartElement(QString::fromLatin1("point_symbol")); + if (rotatable) + xml.writeAttribute(QString::fromLatin1("rotatable"), QString::fromLatin1("true")); + xml.writeAttribute(QString::fromLatin1("inner_radius"), QString::number(inner_radius)); + xml.writeAttribute(QString::fromLatin1("inner_color"), QString::number(map.findColorIndex(inner_color))); + xml.writeAttribute(QString::fromLatin1("outer_width"), QString::number(outer_width)); + xml.writeAttribute(QString::fromLatin1("outer_color"), QString::number(map.findColorIndex(outer_color))); + int num_elements = (int)objects.size(); + xml.writeAttribute(QString::fromLatin1("elements"), QString::number(num_elements)); + for (int i = 0; i < num_elements; ++i) + { + xml.writeStartElement(QString::fromLatin1("element")); + symbols[i]->save(xml, map); + objects[i]->save(xml); + xml.writeEndElement(/*element*/); + } + xml.writeEndElement(/*point_symbol*/); +} + +bool PointSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) +{ + if (xml.name() != QLatin1String("point_symbol")) + return false; + + QXmlStreamAttributes attributes(xml.attributes()); + rotatable = (attributes.value(QLatin1String("rotatable")) == QLatin1String("true")); + inner_radius = attributes.value(QLatin1String("inner_radius")).toInt(); + int temp = attributes.value(QLatin1String("inner_color")).toInt(); + inner_color = map.getColor(temp); + outer_width = attributes.value(QLatin1String("outer_width")).toInt(); + temp = attributes.value(QLatin1String("outer_color")).toInt(); + outer_color = map.getColor(temp); + int num_elements = attributes.value(QLatin1String("elements")).toInt(); + + symbols.reserve(qMin(num_elements, 10)); // 10 is not a limit + objects.reserve(qMin(num_elements, 10)); // 10 is not a limit + for (int i = 0; xml.readNextStartElement(); ++i) + { + if (xml.name() == QLatin1String("element")) + { + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("symbol")) + symbols.push_back(Symbol::load(xml, map, symbol_dict)); + else if (xml.name() == QLatin1String("object")) + objects.push_back(Object::load(xml, nullptr, symbol_dict, symbols.back())); + else + xml.skipCurrentElement(); // unknown element + } + } + else + xml.skipCurrentElement(); // unknown element + } + return true; +} + +bool PointSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const +{ + const PointSymbol* point = static_cast(other); + + if (rotatable != point->rotatable) + return false; + if (!MapColor::equal(inner_color, point->inner_color)) + return false; + if (inner_color && inner_radius != point->inner_radius) + return false; + if (!MapColor::equal(outer_color, point->outer_color)) + return false; + if (outer_color && outer_width != point->outer_width) + return false; + + if (symbols.size() != point->symbols.size()) + return false; + // TODO: Comparing the contained elements in fixed order does not find every case of identical points symbols + // (but at least if the point symbol has not been changed, it is always seen as identical). Could be improved. + for (int i = 0, size = (int)objects.size(); i < size; ++i) + { + if (!symbols[i]->equals(point->symbols[i], case_sensitivity)) + return false; + if (!objects[i]->equals(point->objects[i], false)) + return false; + } + + return true; +} + + +} // namespace OpenOrienteering diff --git a/src/core/symbols/point_symbol.h b/src/core/symbols/point_symbol.h new file mode 100644 index 0000000..2ff4776 --- /dev/null +++ b/src/core/symbols/point_symbol.h @@ -0,0 +1,160 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_POINT_SYMBOL_H +#define OPENORIENTEERING_POINT_SYMBOL_H + +#include + +#include +#include + +#include "symbol.h" + +class QIODevice; +class QPainterPath; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class Map; +class MapColor; +class MapColorMap; +class MapCoordF; +class Object; +class ObjectRenderables; +class SymbolPropertiesWidget; +class SymbolSettingDialog; +class VirtualCoordVector; + + +/** + * Symbol for PointObjects. + * + * Apart from its own settings which are presented to the user as "[Midpoint + * symbol]" in the point symbol editor, this class can contain a list of + * elements together with a symbol for each element. All elements and the + * PointObject's own settings make up the appearance of the point symbol. + * The reason the own settings are left in is just efficiency, as for some + * symbols like crop land, a really huge number of point objects may be generated. + */ +class PointSymbol : public Symbol +{ +friend class PointSymbolSettings; +friend class PointSymbolEditorWidget; +friend class OCAD8FileImport; +friend class XMLImportExport; +public: + /** Constructs an empty point symbol. */ + PointSymbol() noexcept; + ~PointSymbol() override; + Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; + + bool validate() const override; + + void createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + RenderableOptions options ) const override; + + void createRenderablesScaled(MapCoordF coord, float rotation, ObjectRenderables& output, float coord_scale = 1.0f) const; + + void createRenderablesIfCenterInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const; + void createPrimitivesIfCompletelyInside(MapCoordF point_coord, const QPainterPath* outline, ObjectRenderables& output) const; + void createRenderablesIfCompletelyInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const; + void createPrimitivesIfPartiallyInside(MapCoordF point_coord, const QPainterPath* outline, ObjectRenderables& output) const; + void createRenderablesIfPartiallyInside(MapCoordF point_coord, qreal rotation, const QPainterPath* outline, ObjectRenderables& output) const; + + void colorDeleted(const MapColor* color) override; + bool containsColor(const MapColor* color) const override; + const MapColor* guessDominantColor() const override; + void scale(double factor) override; + + qreal dimensionForIcon() const override; + + // Contained objects and symbols (elements) + + /** Returns the number of contained elements. */ + int getNumElements() const; + /** Adds a new element consisting of object and symbol at the given index. */ + void addElement(int pos, Object* object, Symbol* symbol); + /** Returns the object of the i-th element. */ + Object* getElementObject(int pos); + /** Returns the object of the i-th element. */ + const Object* getElementObject(int pos) const; + /** Returns the symbol of the i-th element. */ + Symbol* getElementSymbol(int pos); + /** Returns the symbol of the i-th element. */ + const Symbol* getElementSymbol(int pos) const; + /** Deletes the i-th element. */ + void deleteElement(int pos); + + /** + * Returns true if the point contains no elements and does not create + * renderables itself. Useful to check if it can be deleted. + */ + bool isEmpty() const; + + /** + * Checks if the contained elements are rotationally symmetrical around + * the origin (this means, only point elements at (0,0) are allowed). + */ + bool isSymmetrical() const; + + // Getters / Setters + inline bool isRotatable() const {return rotatable;} + inline void setRotatable(bool enable) {rotatable = enable;} + inline int getInnerRadius() const {return inner_radius;} + inline void setInnerRadius(int value) {inner_radius = value;} + inline const MapColor* getInnerColor() const {return inner_color;} + inline void setInnerColor(const MapColor* color) {inner_color = color;} + inline int getOuterWidth() const {return outer_width;} + inline void setOuterWidth(int value) {outer_width = value;} + inline const MapColor* getOuterColor() const {return outer_color;} + inline void setOuterColor(const MapColor* color) {outer_color = color;} + + SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; + + +protected: +#ifndef NO_NATIVE_FILE_FORMAT + bool loadImpl(QIODevice* file, int version, Map* map) override; +#endif + void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; + bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; + bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; + + std::vector objects; + std::vector symbols; + + bool rotatable; + int inner_radius; // in 1/1000 mm + const MapColor* inner_color; + int outer_width; // in 1/1000 mm + const MapColor* outer_color; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/symbols/symbol.cpp b/src/core/symbols/symbol.cpp new file mode 100644 index 0000000..81e9078 --- /dev/null +++ b/src/core/symbols/symbol.cpp @@ -0,0 +1,842 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "symbol.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/map_view.h" +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/renderables/renderable.h" +#include "core/renderables/renderable_implementation.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/combined_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/text_symbol.h" +#include "fileformats/file_format.h" +#include "fileformats/file_import_export.h" +#include "gui/util_gui.h" +#include "util/util.h" +#include "settings.h" + +// IWYU pragma: no_include + + +namespace OpenOrienteering { + +Symbol::Symbol(Type type) noexcept + : type { type } + , number { -1, -1, -1 } + , is_helper_symbol(false) + , is_hidden(false) + , is_protected(false) +{ + // nothing else +} + + +Symbol::~Symbol() = default; + + + +bool Symbol::validate() const +{ + return true; +} + + + +bool Symbol::equals(const Symbol* other, Qt::CaseSensitivity case_sensitivity, bool compare_state) const +{ + if (type != other->type) + return false; + for (int i = 0; i < number_components; ++i) + { + if (number[i] != other->number[i]) + return false; + if (number[i] == -1 && other->number[i] == -1) + break; + } + if (is_helper_symbol != other->is_helper_symbol) + return false; + + if (compare_state && (is_hidden != other->is_hidden || is_protected != other->is_protected)) + return false; + + if (name.compare(other->name, case_sensitivity) != 0) + return false; + if (description.compare(other->description, case_sensitivity) != 0) + return false; + + return equalsImpl(other, case_sensitivity); +} + +const PointSymbol* Symbol::asPoint() const +{ + Q_ASSERT(type == Point); + return static_cast(this); +} +PointSymbol* Symbol::asPoint() +{ + Q_ASSERT(type == Point); + return static_cast(this); +} +const LineSymbol* Symbol::asLine() const +{ + Q_ASSERT(type == Line); + return static_cast(this); +} +LineSymbol* Symbol::asLine() +{ + Q_ASSERT(type == Line); + return static_cast(this); +} +const AreaSymbol* Symbol::asArea() const +{ + Q_ASSERT(type == Area); + return static_cast(this); +} +AreaSymbol* Symbol::asArea() +{ + Q_ASSERT(type == Area); + return static_cast(this); +} +const TextSymbol* Symbol::asText() const +{ + Q_ASSERT(type == Text); + return static_cast(this); +} +TextSymbol* Symbol::asText() +{ + Q_ASSERT(type == Text); + return static_cast(this); +} +const CombinedSymbol* Symbol::asCombined() const +{ + Q_ASSERT(type == Combined); + return static_cast(this); +} +CombinedSymbol* Symbol::asCombined() +{ + Q_ASSERT(type == Combined); + return static_cast(this); +} + +bool Symbol::isTypeCompatibleTo(const Object* object) const +{ + if (type == Point && object->getType() == Object::Point) + return true; + else if ((type == Line || type == Area || type == Combined) && object->getType() == Object::Path) + return true; + else if (type == Text && object->getType() == Object::Text) + return true; + + return false; +} + +bool Symbol::numberEquals(const Symbol* other, bool ignore_trailing_zeros) const +{ + if (ignore_trailing_zeros) + { + for (int i = 0; i < number_components; ++i) + { + if (number[i] == -1 && other->number[i] == -1) + return true; + if ((number[i] == 0 || number[i] == -1) && + (other->number[i] == 0 || other->number[i] == -1)) + continue; + if (number[i] != other->number[i]) + return false; + } + } + else + { + for (int i = 0; i < number_components; ++i) + { + if (number[i] != other->number[i]) + return false; + if (number[i] == -1) + return true; + } + } + return true; +} + +#ifndef NO_NATIVE_FILE_FORMAT + +bool Symbol::load(QIODevice* file, int version, Map* map) +{ + loadString(file, name); + for (int i = 0; i < number_components; ++i) + file->read((char*)&number[i], sizeof(int)); + loadString(file, description); + file->read((char*)&is_helper_symbol, sizeof(bool)); + if (version >= 10) + file->read((char*)&is_hidden, sizeof(bool)); + if (version >= 11) + file->read((char*)&is_protected, sizeof(bool)); + + return loadImpl(file, version, map); +} + +#endif + +void Symbol::save(QXmlStreamWriter& xml, const Map& map) const +{ + xml.writeStartElement(QString::fromLatin1("symbol")); + xml.writeAttribute(QString::fromLatin1("type"), QString::number(type)); + int id = map.findSymbolIndex(this); + if (id >= 0) + xml.writeAttribute(QString::fromLatin1("id"), QString::number(id)); // unique if given + xml.writeAttribute(QString::fromLatin1("code"), getNumberAsString()); // not always unique + if (!name.isEmpty()) + xml.writeAttribute(QString::fromLatin1("name"), name); + if (is_helper_symbol) + xml.writeAttribute(QString::fromLatin1("is_helper_symbol"), QString::fromLatin1("true")); + if (is_hidden) + xml.writeAttribute(QString::fromLatin1("is_hidden"), QString::fromLatin1("true")); + if (is_protected) + xml.writeAttribute(QString::fromLatin1("is_protected"), QString::fromLatin1("true")); + if (!description.isEmpty()) + xml.writeTextElement(QString::fromLatin1("description"), description); + saveImpl(xml, map); + xml.writeEndElement(/*symbol*/); +} + +Symbol* Symbol::load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) +{ + Q_ASSERT(xml.name() == QLatin1String("symbol")); + + int symbol_type = xml.attributes().value(QLatin1String("type")).toInt(); + Symbol* symbol = Symbol::getSymbolForType(static_cast(symbol_type)); + if (!symbol) + throw FileFormatException(::OpenOrienteering::ImportExport::tr("Error while loading a symbol of type %1 at line %2 column %3.").arg(symbol_type).arg(xml.lineNumber()).arg(xml.columnNumber())); + + QXmlStreamAttributes attributes = xml.attributes(); + QString code = attributes.value(QLatin1String("code")).toString(); + if (attributes.hasAttribute(QLatin1String("id"))) + { + QString id = attributes.value(QLatin1String("id")).toString(); + if (symbol_dict.contains(id)) + throw FileFormatException(::OpenOrienteering::ImportExport::tr("Symbol ID '%1' not unique at line %2 column %3.").arg(id).arg(xml.lineNumber()).arg(xml.columnNumber())); + + symbol_dict[id] = symbol; + + if (code.isEmpty()) + code = id; + } + + if (code.isEmpty()) + symbol->number[0] = -1; + else + { + for (int i = 0, index = 0; i < number_components && index >= 0; ++i) + { + if (index == -1) + symbol->number[i] = -1; + else + { + int dot = code.indexOf(QLatin1Char('.'), index+1); + int num = code.midRef(index, (dot == -1) ? -1 : (dot - index)).toInt(); + symbol->number[i] = num; + index = dot; + if (index != -1) + index++; + } + } + } + + symbol->name = attributes.value(QLatin1String("name")).toString(); + symbol->is_helper_symbol = (attributes.value(QLatin1String("is_helper_symbol")) == QLatin1String("true")); + symbol->is_hidden = (attributes.value(QLatin1String("is_hidden")) == QLatin1String("true")); + symbol->is_protected = (attributes.value(QLatin1String("is_protected")) == QLatin1String("true")); + + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("description")) + symbol->description = xml.readElementText(); + else + { + if (!symbol->loadImpl(xml, map, symbol_dict)) + xml.skipCurrentElement(); + } + } + + if (xml.error()) + { + delete symbol; + throw FileFormatException( + ::OpenOrienteering::ImportExport::tr("Error while loading a symbol of type %1 at line %2 column %3: %4") + .arg(symbol_type) + .arg(xml.lineNumber()) + .arg(xml.columnNumber()) + .arg(xml.errorString()) ); + } + + return symbol; +} + +bool Symbol::loadFinished(Map* map) +{ + Q_UNUSED(map); + return true; +} + +void Symbol::createRenderables(const PathObject*, const PathPartVector&, ObjectRenderables&, Symbol::RenderableOptions) const +{ + qWarning("Missing implementation of Symbol::createRenderables(const PathObject*, const PathPartVector&, ObjectRenderables&)"); +} + +void Symbol::createBaselineRenderables( + const PathObject*, + const PathPartVector& path_parts, + ObjectRenderables& output, + const MapColor* color) const +{ + Q_ASSERT((getContainedTypes() & ~(Symbol::Line | Symbol::Area | Symbol::Combined)) == 0); + + if (color) + { + // Insert line renderable + LineSymbol line_symbol; + line_symbol.setColor(color); + line_symbol.setLineWidth(0); + for (const auto& part : path_parts) + { + auto line_renderable = new LineRenderable(&line_symbol, part, false); + output.insertRenderable(line_renderable); + } + } +} + +bool Symbol::symbolChanged(const Symbol* old_symbol, const Symbol* new_symbol) +{ + Q_UNUSED(old_symbol); + Q_UNUSED(new_symbol); + return false; +} + +bool Symbol::containsSymbol(const Symbol* symbol) const +{ + Q_UNUSED(symbol); + return false; +} + +QImage Symbol::getIcon(const Map* map) const +{ + if (icon.isNull() && map) + icon = createIcon(*map, Settings::getInstance().getSymbolWidgetIconSizePx()); + + return icon; +} + +QImage Symbol::createIcon(const Map& map, int side_length, bool antialiasing, qreal zoom) const +{ + // Desktop default used to be 2x zoom at 8 mm side length, plus/minus + // a border of one white pixel around some objects. + // If the icon is bigger than the rectangle with this zoom factor, the zoom + // is reduced later to make the icon fit into the rectangle. + if (zoom <= 0) + zoom = map.symbolIconZoom(); + auto max_icon_mm_half = 0.5 / zoom; + + // Create image + QImage image(side_length, side_length, QImage::Format_ARGB32_Premultiplied); + QPainter painter(&image); + if (antialiasing) + painter.setRenderHint(QPainter::Antialiasing); + + // Make background transparent + auto mode = painter.compositionMode(); + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(image.rect(), Qt::transparent); + painter.setCompositionMode(mode); + painter.translate(0.5 * side_length, 0.5 * side_length); + + // Create geometry + Object* object = nullptr; + std::unique_ptr symbol_copy; + auto offset = MapCoord{}; + auto contained_types = getContainedTypes(); + if (type == Point) + { + max_icon_mm_half *= 0.90; // white border + object = new PointObject(static_cast(this)); + } + else if (type == Text) + { + max_icon_mm_half *= 0.95; // white border + auto text = new TextObject(this); + text->setText(static_cast(this)->getIconText()); + object = text; + } + else if (type == Area || (type == Combined && contained_types & Area)) + { + auto path = new PathObject(this); + path->addCoordinate(0, MapCoord(-max_icon_mm_half, -max_icon_mm_half)); + path->addCoordinate(1, MapCoord(max_icon_mm_half, -max_icon_mm_half)); + path->addCoordinate(2, MapCoord(max_icon_mm_half, max_icon_mm_half)); + path->addCoordinate(3, MapCoord(-max_icon_mm_half, max_icon_mm_half)); + path->parts().front().setClosed(true, true); + object = path; + } + else if (type == Line || type == Combined) + { + bool show_dash_symbol = false; + auto line_length_half = max_icon_mm_half; + if (type == Line) + { + auto line = static_cast(this); + if (line->getCapStyle() == LineSymbol::RoundCap) + { + offset.setNativeX(-line->getLineWidth()/3); + } + else if (line->getCapStyle() == LineSymbol::PointedCap) + { + line_length_half = std::max(line_length_half, 0.0012 * line->getPointedCapLength()); + } + + if (line->getDashSymbol() && !line->getDashSymbol()->isEmpty()) + { + line_length_half = std::max(line_length_half, line->getDashSymbol()->dimensionForIcon()); + show_dash_symbol = true; + } + + if (line->getMidSymbol() && !line->getMidSymbol()->isEmpty()) + { + auto icon_size = line->getMidSymbol()->dimensionForIcon(); + icon_size += line->getMidSymbolDistance() * (line->getMidSymbolsPerSpot() - 1) / 2000; + line_length_half = std::max(line_length_half, icon_size); + + if (!line->getShowAtLeastOneSymbol()) + { + if (!symbol_copy) + symbol_copy.reset(line->duplicate()); + auto icon_line = static_cast(symbol_copy.get()); + icon_line->setShowAtLeastOneSymbol(true); + } + } + + if (line->getStartSymbol() && !line->getStartSymbol()->isEmpty()) + { + line_length_half = std::max(line_length_half, line->getStartSymbol()->dimensionForIcon() / 2); + } + + if (line->getEndSymbol() && !line->getEndSymbol()->isEmpty()) + { + line_length_half = std::max(line_length_half, line->getEndSymbol()->dimensionForIcon() / 2); + } + + // If there are breaks in the line, scale them down so they fit into the icon exactly. + auto max_ideal_length = 0; + if (line->isDashed() && line->getBreakLength() > 0) + { + auto ideal_length = 2 * line->getDashesInGroup() * line->getDashLength() + + 2 * (line->getDashesInGroup() - 1) * line->getInGroupBreakLength() + + line->getBreakLength(); + if (max_ideal_length < ideal_length) + max_ideal_length = ideal_length; + } + if (line->hasBorder()) + { + auto& border = line->getBorder(); + if (border.dashed && border.break_length > 0) + { + auto ideal_length = 2 * border.dash_length + border.break_length; + if (max_ideal_length < ideal_length) + max_ideal_length = ideal_length; + } + auto& right_border = line->getRightBorder(); + if (line->areBordersDifferent() && right_border.dashed && right_border.break_length > 0) + { + auto ideal_length = 2 * right_border.dash_length + right_border.break_length; + if (max_ideal_length < ideal_length) + max_ideal_length = ideal_length; + } + } + if (max_ideal_length > 0) + { + auto ideal_length_half = qreal(max_ideal_length) / 2000; + auto factor = qMin(qreal(0.5), line_length_half / qMax(qreal(0.001), ideal_length_half)); + + if (!symbol_copy) + symbol_copy.reset(line->duplicate()); + + auto icon_line = static_cast(symbol_copy.get()); + icon_line->setDashLength(qRound(factor * icon_line->getDashLength())); + icon_line->setBreakLength(qRound(factor * icon_line->getBreakLength())); + icon_line->setInGroupBreakLength(qRound(factor * icon_line->getInGroupBreakLength())); + icon_line->setShowAtLeastOneSymbol(true); + icon_line->getBorder().dash_length *= factor; + icon_line->getBorder().break_length *= factor; + icon_line->getRightBorder().dash_length *= factor; + icon_line->getRightBorder().break_length *= factor; + } + } + else if (type == Combined) + { + auto max_ideal_length = 0; + auto combined = static_cast(this); + for (int i = 0; i < combined->getNumParts(); ++i) + { + auto part = combined->getPart(i); + if (part && part->getType() == Line) + { + auto line = static_cast(part); + auto dash_symbol = line->getDashSymbol(); + if (dash_symbol && !dash_symbol->isEmpty()) + show_dash_symbol = true; + + if (line->isDashed() && line->getBreakLength() > 0) + { + auto ideal_length = 2 * line->getDashesInGroup() * line->getDashLength() + + 2 * (line->getDashesInGroup() - 1) * line->getInGroupBreakLength() + + line->getBreakLength(); + if (max_ideal_length < ideal_length) + max_ideal_length = ideal_length; + } + if (line->hasBorder()) + { + auto& border = line->getBorder(); + if (border.dashed && border.break_length > 0) + { + auto ideal_length = 2 * border.dash_length + border.break_length; + if (max_ideal_length < ideal_length) + max_ideal_length = ideal_length; + } + auto& right_border = line->getRightBorder(); + if (line->areBordersDifferent() && right_border.dashed && right_border.break_length > 0) + { + auto ideal_length = 2 * right_border.dash_length + right_border.break_length; + if (max_ideal_length < ideal_length) + max_ideal_length = ideal_length; + } + } + } + } + // If there are breaks in the line, scale them down so they fit into the icon exactly. + if (max_ideal_length > 0) + { + auto ideal_length_half = qreal(max_ideal_length) / 2000; + auto factor = qMin(qreal(0.5), line_length_half / qMax(qreal(0.001), ideal_length_half)); + + symbol_copy.reset(new CombinedSymbol()); + static_cast(symbol_copy.get())->setNumParts(combined->getNumParts()); + + for (int i = 0; i < combined->getNumParts(); ++i) + { + auto proto = static_cast(combined)->getPart(i); + if (!proto) + continue; + + auto copy = proto->duplicate(); + if (copy->getType() == Line) + { + auto icon_line = static_cast(copy); + icon_line->setDashLength(qRound(factor * icon_line->getDashLength())); + icon_line->setBreakLength(qRound(factor * icon_line->getBreakLength())); + icon_line->setInGroupBreakLength(qRound(factor * icon_line->getInGroupBreakLength())); + icon_line->setShowAtLeastOneSymbol(true); + icon_line->getBorder().dash_length *= factor; + icon_line->getBorder().break_length *= factor; + icon_line->getRightBorder().dash_length *= factor; + icon_line->getRightBorder().break_length *= factor; + } + static_cast(symbol_copy.get())->setPart(i, copy, true); + } + } + } + + auto path = new PathObject(symbol_copy ? symbol_copy.get() : this); + path->addCoordinate(0, MapCoord(-line_length_half, 0.0)); + path->addCoordinate(1, MapCoord(line_length_half, 0.0)); + if (show_dash_symbol) + { + MapCoord dash_coord(0, 0); + dash_coord.setDashPoint(true); + path->addCoordinate(1, dash_coord); + } + object = path; + } + else + { + qWarning("Unhandled symbol: %s", qPrintable(getDescription())); + return image; + } + + // Create icon map and view + Map icon_map; + // const_cast promise: We won't change the colors, thus we won't change map. + icon_map.useColorsFrom(const_cast(&map)); + icon_map.setScaleDenominator(map.getScaleDenominator()); + icon_map.addObject(object); + + const auto& extent = object->getExtent(); + auto w = std::max(std::abs(extent.left()), std::abs(extent.right())); + auto h = std::max(std::abs(extent.top()), std::abs(extent.bottom())); + auto real_icon_mm_half = std::max(w, h); + auto final_zoom = side_length * zoom * std::min(qreal(1), max_icon_mm_half / real_icon_mm_half); + painter.scale(final_zoom, final_zoom); + + if (type == Text) + { + // Center text + painter.translate(-extent.center()); + } + else if (type == Point) + { + // Do not completely offset the symbols relative position + painter.translate(-extent.center() / 2); + } + else if (contained_types & Line && !(contained_types & Area)) + { + painter.translate(MapCoordF(-offset)); + } + + RenderConfig config = { map, QRectF(-10000, -10000, 20000, 20000), final_zoom, RenderConfig::HelperSymbols, 1.0 }; + bool was_hidden = is_hidden; + // Ensure that an icon is created for hidden symbols. + if (symbol_copy) + symbol_copy.get()->setHidden(false); + else + is_hidden = false; + icon_map.draw(&painter, config); + is_hidden = was_hidden; + + painter.end(); + + // Add shadow to dominant white on transparent + auto color = guessDominantColor(); + if (color && color->isWhite()) + { + for (int y = image.height() - 1; y >= 0; --y) + { + for (int x = image.width() - 1; x >= 0; --x) + { + if (qAlpha(image.pixel(x, y)) > 0) + continue; + + auto is_white = [](const QRgb& rgb) { + return rgb > 0 + && qAlpha(rgb) == qRed(rgb) + && qRed(rgb) == qGreen(rgb) + && qGreen(rgb) == qBlue(rgb); + }; + if (x > 0) + { + auto preceding = image.pixel(x-1, y); + if (is_white(preceding)) + { + image.setPixel(x, y, qPremultiply(qRgba(0, 0, 0, 127))); + continue; + } + } + if (y > 0) + { + auto preceding = image.pixel(x, y-1); + if (is_white(preceding)) + { + image.setPixel(x, y, qPremultiply(qRgba(0, 0, 0, 127))); + } + } + } + } + } + + return image; +} + + +void Symbol::resetIcon() +{ + icon = {}; +} + + +qreal Symbol::dimensionForIcon() const +{ + return 0; +} + + + +qreal Symbol::calculateLargestLineExtent() const +{ + return 0; +} + + + +QString Symbol::getNumberAsString() const +{ + QString str; + for (auto n : number) + { + if (n < 0) + break; + if (!str.isEmpty()) + str += QLatin1Char('.'); + str += QString::number(n); + } + return str; +} + +Symbol* Symbol::getSymbolForType(Symbol::Type type) +{ + if (type == Symbol::Point) + return new PointSymbol(); + else if (type == Symbol::Line) + return new LineSymbol(); + else if (type == Symbol::Area) + return new AreaSymbol(); + else if (type == Symbol::Text) + return new TextSymbol(); + else if (type == Symbol::Combined) + return new CombinedSymbol(); + else + { + Q_ASSERT(false); + return nullptr; + } +} + +#ifndef NO_NATIVE_FILE_FORMAT + +bool Symbol::loadSymbol(Symbol*& symbol, QIODevice* stream, int version, Map* map) +{ + int save_type; + stream->read((char*)&save_type, sizeof(int)); + symbol = Symbol::getSymbolForType(static_cast(save_type)); + if (!symbol) + return false; + if (!symbol->load(stream, version, map)) + return false; + return true; +} + +#endif + +bool Symbol::areTypesCompatible(Symbol::Type a, Symbol::Type b) +{ + return (getCompatibleTypes(a) & b) != 0; +} + +int Symbol::getCompatibleTypes(Symbol::Type type) +{ + if (type == Point) + return Point; + else if (type == Line || type == Area || type == Combined) + return Line | Area | Combined; + else if (type == Text) + return Text; + + Q_ASSERT(false); + return type; +} + +void Symbol::duplicateImplCommon(const Symbol* other) +{ + type = other->type; + name = other->name; + for (int i = 0; i < number_components; ++i) + number[i] = other->number[i]; + description = other->description; + is_helper_symbol = other->is_helper_symbol; + is_hidden = other->is_hidden; + icon = other->icon; +} + + +bool Symbol::compareByNumber(const Symbol* s1, const Symbol* s2) +{ + int n1 = s1->number_components, n2 = s2->number_components; + for (int i = 0; i < n1 && i < n2; i++) + { + if (s1->getNumberComponent(i) < s2->getNumberComponent(i)) return true; // s1 < s2 + if (s1->getNumberComponent(i) > s2->getNumberComponent(i)) return false; // s1 > s2 + // if s1 == s2, loop to the next component + } + return false; // s1 == s2 +} + +bool Symbol::compareByColorPriority(const Symbol* s1, const Symbol* s2) +{ + const MapColor* c1 = s1->guessDominantColor(); + const MapColor* c2 = s2->guessDominantColor(); + + if (c1 && c2) + return c1->comparePriority(*c2); + else if (c2) + return true; + else if (c1) + return false; + + return false; // s1 == s2 +} + +Symbol::compareByColor::compareByColor(Map* map) +{ + int next_priority = map->getNumColors() - 1; + // Iterating in reverse order so identical colors are at the position where they appear with lowest priority. + for (int i = map->getNumColors() - 1; i >= 0; --i) + { + QRgb color_code = QRgb(*map->getColor(i)); + if (!color_map.contains(color_code)) + { + color_map.insert(color_code, next_priority); + --next_priority; + } + } +} + +bool Symbol::compareByColor::operator() (const Symbol* s1, const Symbol* s2) +{ + const MapColor* c1 = s1->guessDominantColor(); + const MapColor* c2 = s2->guessDominantColor(); + + if (c1 && c2) + return color_map.value(QRgb(*c1)) < color_map.value(QRgb(*c2)); + else if (c2) + return true; + else if (c1) + return false; + + return false; // s1 == s2 +} + + +} // namespace OpenOrienteering diff --git a/src/core/symbols/symbol.h b/src/core/symbols/symbol.h new file mode 100644 index 0000000..0deb791 --- /dev/null +++ b/src/core/symbols/symbol.h @@ -0,0 +1,478 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_H +#define OPENORIENTEERING_SYMBOL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class QIODevice; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class AreaSymbol; +class CombinedSymbol; +class LineSymbol; +class Map; +class MapColor; +class MapColorMap; +class Object; +class ObjectRenderables; +class PathObject; +class PathPartVector; +class PointSymbol; +class Symbol; +class SymbolPropertiesWidget; +class SymbolSettingDialog; +class TextSymbol; +class VirtualCoordVector; + +typedef QHash SymbolDictionary; + + +// From gui/util_gui.h, but avoiding extra dependencies +namespace Util { + +QString plainText(QString maybe_markup); + + +} // namespace Util + + +/** + * Abstract base class for map symbols. + * + * Provides among other things a symbol number consisting of multiple parts, + * e.g. "2.4.12". Parts which are not set are assigned the value -1. + */ +class Symbol +{ +public: + /** Enumeration of all possible symbol types, + * must be able to be used as bits in a bitmask. */ + enum Type + { + Point = 1, + Line = 2, + Area = 4, + Text = 8, + Combined = 16, + + NoSymbol = 0, + AllSymbols = Point | Line | Area | Text | Combined + }; + + /** + * RenderableOptions denominate variations in painting symbols. + */ + enum RenderableOption + { + RenderBaselines = 1 << 0, ///< Paint cosmetique contours and baselines + RenderAreasHatched = 1 << 1, ///< Paint hatching instead of opaque fill + RenderNormal = 0 ///< Paint normally + }; + Q_DECLARE_FLAGS(RenderableOptions, RenderableOption) + + /** Constructs an empty symbol */ + Symbol(Type type) noexcept; + + Symbol(const Symbol&) = delete; + Symbol(Symbol&&) = delete; + + virtual ~Symbol(); + + virtual Symbol* duplicate(const MapColorMap* color_map = nullptr) const = 0; + + Symbol& operator=(const Symbol&) = delete; + Symbol& operator=(Symbol&&) = delete; + + virtual bool validate() const; + + + /** + * Checks for equality to the other symbol. + * @param other The symbol to compare with. + * @param case_sensitivity Comparison mode for strings, e.g. symbol names. + * @param compare_state If true, also compares symbol state (protected / hidden). + */ + bool equals(const Symbol* other, Qt::CaseSensitivity case_sensitivity = Qt::CaseSensitive, bool compare_state = false) const; + + + /** Returns the type of the symbol */ + inline Type getType() const {return type;} + + // Convenience casts with type checking + /** Case to PointSymbol with type checking */ + const PointSymbol* asPoint() const; + /** Case to PointSymbol with type checking */ + PointSymbol* asPoint(); + /** Case to LineSymbol with type checking */ + const LineSymbol* asLine() const; + /** Case to LineSymbol with type checking */ + LineSymbol* asLine(); + /** Case to AreaSymbol with type checking */ + const AreaSymbol* asArea() const; + /** Case to AreaSymbol with type checking */ + AreaSymbol* asArea(); + /** Case to TextSymbol with type checking */ + const TextSymbol* asText() const; + /** Case to TextSymbol with type checking */ + TextSymbol* asText(); + /** Case to CombinedSymbol with type checking */ + const CombinedSymbol* asCombined() const; + /** Case to CombinedSymbol with type checking */ + CombinedSymbol* asCombined(); + + /** Returns the or-ed together bitmask of all symbol types + * this symbol contains */ + virtual Type getContainedTypes() const {return getType();} + + + /** + * Checks if the symbol can be applied to the given object. + * TODO: refactor: use static areTypesCompatible() instead with the type of the object's symbol + */ + bool isTypeCompatibleTo(const Object* object) const; + + /** Returns if the symbol numbers are identical. */ + bool numberEquals(const Symbol* other, bool ignore_trailing_zeros) const; + + + // Saving and loading + + /** Loads the symbol in the old "native" file format. */ + bool load(QIODevice* file, int version, Map* map); + + /** + * Saves the symbol in xml format. + * @param xml Stream to save to. + * @param map Reference to the map containing the symbol. Needed to find + * symbol indices. + */ + void save(QXmlStreamWriter& xml, const Map& map) const; + /** + * Load the symbol in xml format. + * @param xml Stream to load from. + * @param map Reference to the map containing the symbol. + * @param symbol_dict Dictionary mapping symbol IDs to symbol pointers. + */ + static Symbol* load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict); + + /** + * Called after loading of the map is finished. + * Can do tasks that need to reference other symbols or map objects. + */ + virtual bool loadFinished(Map* map); + + + /** + * Creates renderables for a generic object. + * + * This will create the renderables according to the object's properties + * and the given coordinates. + * + * NOTE: methods which implement this should use the given coordinates + * instead of the object's coordinates, as those can be an updated, + * transformed version of the object's coords! + */ + virtual void createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + Symbol::RenderableOptions options) const = 0; + + /** + * Creates renderables for a path object. + * + * This will create the renderables according to the object's properties + * and the coordinates given by the path_parts. This allows the immediate + * use of precalculated meta-information on paths. + * + * \see createRenderables() + */ + virtual void createRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables &output, + Symbol::RenderableOptions options) const; + + /** + * Creates baseline renderables for a path object. + * + * Baseline renderables show the coordinate paths with minimum line width. + * + * \see createRenderables() + */ + virtual void createBaselineRenderables( + const PathObject* object, + const PathPartVector& path_parts, + ObjectRenderables &output, + const MapColor* color) const; + + /** + * Called by the map in which the symbol is to notify it of a color being + * deleted (pointer becomes invalid, indices change). + */ + virtual void colorDeleted(const MapColor* color) = 0; + + /** Must return if the given color is used by this symbol. */ + virtual bool containsColor(const MapColor* color) const = 0; + + /** + * Returns the dominant color of this symbol, or a guess for this color + * in case it is impossible to determine it uniquely. + */ + virtual const MapColor* guessDominantColor() const = 0; + + /** + * Called by the map in which the symbol is to notify it of a symbol being + * changed (pointer becomes invalid). + * If !new_symbol, the symbol is being deleted. + * Must return true if this symbol contained the deleted symbol. + */ + virtual bool symbolChanged(const Symbol* old_symbol, const Symbol* new_symbol); + + /** + * Must return if the given symbol is referenced by this symbol. + * Should NOT return true if the argument is itself. + */ + virtual bool containsSymbol(const Symbol* symbol) const; + + /** Scales the whole symbol */ + virtual void scale(double factor) = 0; + + + /** + * Returns the symbol's icon. + * + * This icon uses a default size and zoom, and it is cached, making + * repeated calls cheap. + */ + QImage getIcon(const Map* map) const; + + /** + * Creates a symbol icon with the given side length (pixels). + * + * If the zoom parameter is zero, the map's symbolIconZoom() is used. + * At a zoom of 1.0 (100%), a square symbol of 1 mm side length would fill + * 50% of the icon width and height. Larger symbols may be scaled down. + */ + QImage createIcon(const Map& map, int side_length, bool antialiasing = true, qreal zoom = 0) const; + + /** + * Clear the symbol's cached icon. + * + * It will be recreated the next time getIcon() gets called. + */ + void resetIcon(); + + /** + * Returns the dimension which shall considered when scaling the icon. + */ + virtual qreal dimensionForIcon() const; + + /** + * Returns the largest extent of all primitive lines which are part of the symbol. + * + * Effectively, this is the half line width. + */ + virtual qreal calculateLargestLineExtent() const; + + + // Getters / Setters + + /** Returns the symbol name. */ + inline const QString& getName() const {return name;} + /** Returns the symbol name after stripping all HTML. */ + QString getPlainTextName() const { return Util::plainText(name); } + /** Sets the symbol name. */ + inline void setName(const QString& new_name) {name = new_name;} + + /** Returns the symbol number as string. */ + QString getNumberAsString() const; + /** Returns the i-th component of the symbol number as int. */ + inline int getNumberComponent(int i) const {Q_ASSERT(i >= 0 && i < number_components); return number[i];} + /** Sets the i-th component of the symbol number. */ + inline void setNumberComponent(int i, int new_number) {Q_ASSERT(i >= 0 && i < number_components); number[i] = new_number;} + + /** Returns the symbol description. */ + inline const QString& getDescription() const {return description;} + /** Sets the symbol description. */ + inline void setDescription(const QString& new_description) {description = new_description;} + + /** Returns if this is a helper symbol (which is not printed in the final map) */ + inline bool isHelperSymbol() const {return is_helper_symbol;} + /** Sets if this is a helper symbol, see isHelperSymbol(). */ + inline void setIsHelperSymbol(bool value) {is_helper_symbol = value;} + + /** Returns if this symbol is hidden. */ + inline bool isHidden() const {return is_hidden;} + /** Sets the hidden state of this symbol. */ + inline void setHidden(bool value) {is_hidden = value;} + + /** Returns if this symbol is protected, i.e. objects with this symbol + * cannot be edited. */ + inline bool isProtected() const {return is_protected;} + /** Sets the protected state of this symbol. */ + inline void setProtected(bool value) {is_protected = value;} + + /** Creates a properties widget for the symbol. */ + virtual SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog); + + + // Static + + /** Returns a newly created symbol of the given type */ + static Symbol* getSymbolForType(Type type); + + /** Static read function; reads the type number, creates a symbol of + * this type and loads it. Returns true if successful. */ + static bool loadSymbol(Symbol*& symbol, QIODevice* stream, int version, Map* map); + + /** + * Returns if the symbol types can be applied to the same object types + */ + static bool areTypesCompatible(Type a, Type b); + + /** + * Returns a bitmask of all types which can be applied to + * the same objects as the given type. + */ + static int getCompatibleTypes(Type type); + + + /** + * @brief Compares two symbols by number. + * @return True if the number of s1 is less than the number of s2. + */ + static bool compareByNumber(const Symbol* s1, const Symbol* s2); + + /** + * @brief Compares two symbols by the dominant colors' priorities. + * @return True if s1's dominant color's priority is lower than s2's dominant color's priority. + */ + static bool compareByColorPriority(const Symbol* s1, const Symbol* s2); + + /** + * @brief Functor for comparing symbols by dominant colors. + * + * Other than compareByColorPriority(), this comparison uses the lowest + * priority which exists for a particular color in the map. All map colors + * are preprocessed in the constructor. Thus the functor becomes invalid as + * soon as colors are changed. + */ + struct compareByColor + { + /** + * @brief Constructs the functor. + * @param map The map which defines all colors. + */ + compareByColor(Map* map); + + /** + * @brief Operator which compares two symbols by dominant colors. + * @return True if s1's dominant color exists with lower prority then s2's dominant color. + */ + bool operator() (const Symbol* s1, const Symbol* s2); + + private: + /** + * @brief Maps color code to priority. + */ + QHash color_map; + }; + + /** + * Number of components of symbol numbers. + */ + static const int number_components = 3; + +protected: +#ifndef NO_NATIVE_FILE_FORMAT + /** + * Must be overridden to load type-specific symbol properties. See saveImpl() + */ + virtual bool loadImpl(QIODevice* file, int version, Map* map) = 0; +#endif + + /** + * Must be overridden to save type-specific symbol properties. + * The map pointer can be used to get persistent indices to any pointers on map data + */ + virtual void saveImpl(QXmlStreamWriter& xml, const Map& map) const = 0; + + /** + * Must be overridden to load type-specific symbol properties. See saveImpl(). + * Return false if the current xml tag does not belong to the symbol and + * should be skipped, true if the element has been read completely. + */ + virtual bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) = 0; + + /** + * Must be overridden to compare symbol-specific attributes. + */ + virtual bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const = 0; + + /** + * Duplicates properties which are common for all + * symbols from other to this object + */ + void duplicateImplCommon(const Symbol* other); + + +private: + /** Symbol icon. If icon.isNull() is true, it is not generated yet. */ + mutable QImage icon; + /** Symbol name */ + QString name; + /** Symbol description */ + QString description; + /** The symbol type, determined by the subclass */ + Type type; + /** Symbol number */ + int number[number_components]; + /** Helper symbol flag, see isHelperSymbol() */ + bool is_helper_symbol; + /** Hidden flag, see isHidden() */ + mutable bool is_hidden; + /** Protected flag, see isProtected() */ + bool is_protected; +}; + + +} // namespace OpenOrienteering + + +Q_DECLARE_METATYPE(const OpenOrienteering::Symbol*) + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::Symbol::RenderableOptions) + + +#endif diff --git a/src/core/symbols/symbol_icon_decorator.cpp b/src/core/symbols/symbol_icon_decorator.cpp new file mode 100644 index 0000000..0287e5b --- /dev/null +++ b/src/core/symbols/symbol_icon_decorator.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2014, 2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "symbol_icon_decorator.h" + +#include +#include + + +namespace OpenOrienteering { + +//### SymbolIconDecorator ### + +SymbolIconDecorator::~SymbolIconDecorator() = default; + + + +//### HiddenSymbolDecorator ### + +HiddenSymbolDecorator::HiddenSymbolDecorator(int icon_size) +: icon_size(icon_size) +, pen_width(qMax(1, qCeil(0.06*icon_size))) +, x_width(icon_size/3) +, offset(1 + pen_width, 1 + pen_width) +{ + ; // nothing else +} + +HiddenSymbolDecorator::~HiddenSymbolDecorator() = default; + +void HiddenSymbolDecorator::draw(QPainter& painter) const +{ + // Draw a lock symbol + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + + painter.setOpacity(0.6); + painter.fillRect(0, 0, icon_size, icon_size, QBrush(Qt::white)); + + painter.translate(offset); + painter.setOpacity(1.0); + QPen pen(Qt::red); + pen.setWidth(pen_width); + painter.setPen(pen); + painter.drawLine(QPoint(0, 0), QPoint(x_width, x_width)); + painter.drawLine(QPoint(x_width, 0), QPoint(0, x_width)); + + painter.restore(); +} + + + +//### ProtectedSymbolDecorator ### + +ProtectedSymbolDecorator::ProtectedSymbolDecorator(int icon_size) +: arc_size(qFloor(qMax(3.0, 0.15*icon_size))) +, pen_width(qMax(1, qCeil(0.4*arc_size))) +, box_width(arc_size + pen_width + qMax(1, qFloor(0.1*icon_size))) +, box_height(qMax(arc_size, qCeil(0.6*box_width))) +, offset(icon_size - 3 - box_width, 1 + pen_width) +{ + ; // nothing else +} + +ProtectedSymbolDecorator::~ProtectedSymbolDecorator() = default; + +void ProtectedSymbolDecorator::draw(QPainter& painter) const +{ + // Draw a lock symbol + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.translate(offset); + + painter.setOpacity(0.5); + QPen arc_background_pen(Qt::white); + arc_background_pen.setWidth(pen_width+2); + painter.setPen(arc_background_pen); + painter.drawRoundedRect((box_width-arc_size)/2, 0, arc_size, arc_size+pen_width, qreal(pen_width), qreal(pen_width)); + painter.fillRect(-1, arc_size-1, box_width+2, box_height+2, QBrush(Qt::white)); + + painter.setOpacity(1.0); + QPen arc_pen(Qt::darkGray); + arc_pen.setWidth(pen_width); + painter.setPen(arc_pen); + painter.drawRoundedRect((box_width-arc_size)/2, 0, arc_size, arc_size+pen_width, qreal(pen_width), qreal(pen_width)); + painter.fillRect(0, arc_size, box_width, box_height, QBrush(Qt::darkGray)); + + painter.restore(); +} + + +} // namespace OpenOrienteering diff --git a/src/core/symbols/symbol_icon_decorator.h b/src/core/symbols/symbol_icon_decorator.h new file mode 100644 index 0000000..be00d71 --- /dev/null +++ b/src/core/symbols/symbol_icon_decorator.h @@ -0,0 +1,94 @@ +/* + * Copyright 2014, 2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_ICON_DECORATOR_H +#define OPENORIENTEERING_SYMBOL_ICON_DECORATOR_H + +#include + +class QPainter; + +namespace OpenOrienteering { + + +/** + * An abstract interface for classes which draws icon decorations. + * + * The icon is expected to be at (0, 0) in the painter's coordinates. + */ +class SymbolIconDecorator +{ +public: + SymbolIconDecorator() noexcept = default; + SymbolIconDecorator(const SymbolIconDecorator&) = delete; + SymbolIconDecorator(SymbolIconDecorator&&) = delete; + SymbolIconDecorator& operator=(const SymbolIconDecorator&) = delete; + SymbolIconDecorator& operator=(SymbolIconDecorator&&) = delete; + virtual ~SymbolIconDecorator(); + virtual void draw(QPainter& p) const = 0; +}; + + + +/** + * Draws the decoration for a hidden symbol. + * + * A small red x is drawn in the top-left corner of the icon. + */ +class HiddenSymbolDecorator : public SymbolIconDecorator +{ +public: + explicit HiddenSymbolDecorator(int icon_size); + ~HiddenSymbolDecorator() override; + void draw(QPainter& p) const override; + +private: + int icon_size; + int pen_width; + int x_width; + QPoint offset; +}; + + + +/** + * Draws the decoration for a protected symbol. + * + * A small gray lock is drawn in the top-right corner of the icon. + */ +class ProtectedSymbolDecorator : public SymbolIconDecorator +{ +public: + explicit ProtectedSymbolDecorator(int icon_size); + ~ProtectedSymbolDecorator() override; + void draw(QPainter& p) const override; + +private: + int arc_size; + int pen_width; + int box_width; + int box_height; + QPoint offset; +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_SYMBOL_ICON_DECORATOR_H diff --git a/src/core/symbols/text_symbol.cpp b/src/core/symbols/text_symbol.cpp new file mode 100644 index 0000000..7196acb --- /dev/null +++ b/src/core/symbols/text_symbol.cpp @@ -0,0 +1,583 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "text_symbol.h" + +#include +#include +#include +// IWYU pragma: no_include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/renderables/renderable.h" +#include "core/renderables/renderable_implementation.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/symbol.h" +#include "core/virtual_coord_vector.h" +#include "core/virtual_path.h" +#include "util/util.h" + + +namespace OpenOrienteering { + +TextSymbol::TextSymbol() +: Symbol(Symbol::Text) +, metrics(QFont()) +{ + color = nullptr; + font_family = QString::fromLatin1("Arial"); + font_size = 4 * 1000; + bold = false; + italic = false; + underline = false; + line_spacing = 1; + paragraph_spacing = 0; + character_spacing = 0; + kerning = true; + icon_text = QString{}; + framing = false; + framing_color = nullptr; + framing_mode = LineFraming; + framing_line_half_width = 200; + framing_shadow_x_offset = 200; + framing_shadow_y_offset = 200; + line_below = false; + line_below_color = nullptr; + line_below_width = 0; + line_below_distance = 0; + updateQFont(); +} + + +TextSymbol::~TextSymbol() = default; + + +Symbol* TextSymbol::duplicate(const MapColorMap* color_map) const +{ + auto new_text = new TextSymbol(); + new_text->duplicateImplCommon(this); + new_text->color = color_map ? color_map->value(color) : color; + new_text->font_family = font_family; + new_text->font_size = font_size; + new_text->bold = bold; + new_text->italic = italic; + new_text->underline = underline; + new_text->line_spacing = line_spacing; + new_text->paragraph_spacing = paragraph_spacing; + new_text->character_spacing = character_spacing; + new_text->kerning = kerning; + new_text->icon_text = icon_text; + new_text->framing = framing; + new_text->framing_color = color_map ? color_map->value(framing_color) : framing_color; + new_text->framing_mode = framing_mode; + new_text->framing_line_half_width = framing_line_half_width; + new_text->framing_shadow_x_offset = framing_shadow_x_offset; + new_text->framing_shadow_y_offset = framing_shadow_y_offset; + new_text->line_below = line_below; + new_text->line_below_color = color_map ? color_map->value(line_below_color) : line_below_color; + new_text->line_below_width = line_below_width; + new_text->line_below_distance = line_below_distance; + new_text->custom_tabs = custom_tabs; + new_text->updateQFont(); + return new_text; +} + +void TextSymbol::createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + Symbol::RenderableOptions options) const +{ + Q_ASSERT(object); + + const TextObject* text_object = static_cast(object); + text_object->prepareLineInfos(); + + if (options.testFlag(Symbol::RenderBaselines)) + { + createBaselineRenderables(text_object, coords, output); + } + else + { + auto anchor = coords[0]; + double anchor_x = anchor.x(); + double anchor_y = anchor.y(); + + if (color) + output.insertRenderable(new TextRenderable(this, text_object, color, anchor_x, anchor_y)); + + if (line_below && line_below_color && line_below_width > 0) + createLineBelowRenderables(object, output); + + if (framing && framing_color) + { + if (framing_mode == LineFraming && framing_line_half_width > 0) + { + output.insertRenderable(new TextFramingRenderable(this, text_object, framing_color, anchor_x, anchor_y)); + } + else if (framing_mode == ShadowFraming) + { + output.insertRenderable(new TextRenderable(this, text_object, framing_color, anchor_x + 0.001 * framing_shadow_x_offset, anchor_y + 0.001 * framing_shadow_y_offset)); + } + } + } +} + +void TextSymbol::createBaselineRenderables( + const TextObject* text_object, + const VirtualCoordVector& coords, + ObjectRenderables& output) const +{ + const MapColor* dominant_color = guessDominantColor(); + if (dominant_color && text_object->getNumLines() > 0) + { + // Insert text boundary + LineSymbol line_symbol; + line_symbol.setColor(dominant_color); + line_symbol.setLineWidth(0); + + const TextObjectLineInfo* line = text_object->getLineInfo(0); + QRectF text_bbox(line->line_x, line->line_y - line->ascent, line->width, line->ascent + line->descent); + for (int i = 1; i < text_object->getNumLines(); ++i) + { + const TextObjectLineInfo* line = text_object->getLineInfo(i); + rectInclude(text_bbox, QRectF(line->line_x, line->line_y - line->ascent, line->width, line->ascent + line->descent)); + } + + Q_UNUSED(coords); // coords should be used for calcTextToMapTransform() + QTransform text_to_map = text_object->calcTextToMapTransform(); + PathObject path; + path.addCoordinate(MapCoord(text_to_map.map(text_bbox.topLeft()))); + path.addCoordinate(MapCoord(text_to_map.map(text_bbox.topRight()))); + path.addCoordinate(MapCoord(text_to_map.map(text_bbox.bottomRight()))); + path.addCoordinate(MapCoord(text_to_map.map(text_bbox.bottomLeft()))); + path.parts().front().setClosed(true, true); + path.updatePathCoords(); + + auto line_renderable = new LineRenderable(&line_symbol, path.parts().front(), false); + output.insertRenderable(line_renderable); + } +} + +void TextSymbol::createLineBelowRenderables(const Object* object, ObjectRenderables& output) const +{ + auto text_object = reinterpret_cast(object); + if (text_object->getNumLines()) + { + double scale_factor = calculateInternalScaling(); + AreaSymbol area_symbol; + area_symbol.setColor(line_below_color); + + MapCoordVector line_flags(4); + MapCoordVectorF line_coords(4); + VirtualPath line_path = { line_flags, line_coords }; + line_flags.back().setHolePoint(true); + + QTransform transform = text_object->calcTextToMapTransform(); + + for (int i = 0; i < text_object->getNumLines(); ++i) + { + const TextObjectLineInfo* line_info = text_object->getLineInfo(i); + if (!line_info->paragraph_end) + continue; + + double line_below_x0; + double line_below_x1; + if (text_object->hasSingleAnchor()) + { + line_below_x0 = line_info->line_x; + line_below_x1 = line_below_x0 + line_info->width; + } + else + { + double box_width = text_object->getBoxWidth() * scale_factor; + line_below_x0 = -0.5 * box_width; + line_below_x1 = line_below_x0 + box_width; + } + double line_below_y0 = line_info->line_y + getLineBelowDistance() * scale_factor; + double line_below_y1 = line_below_y0 + getLineBelowWidth() * scale_factor; + line_coords[0] = MapCoordF(transform.map(QPointF(line_below_x0, line_below_y0))); + line_coords[1] = MapCoordF(transform.map(QPointF(line_below_x1, line_below_y0))); + line_coords[2] = MapCoordF(transform.map(QPointF(line_below_x1, line_below_y1))); + line_coords[3] = MapCoordF(transform.map(QPointF(line_below_x0, line_below_y1))); + + line_path.path_coords.update(0); + output.insertRenderable(new AreaRenderable(&area_symbol, line_path)); + } + } +} + +void TextSymbol::colorDeleted(const MapColor* c) +{ + auto changes = 0; + if (c == color) + { + color = nullptr; + ++changes; + } + if (c == framing_color) + { + framing_color = nullptr; + ++changes; + } + if (c == line_below_color) + { + line_below_color = nullptr; + ++changes; + } + if (changes) + { + resetIcon(); + } +} + +bool TextSymbol::containsColor(const MapColor* c) const +{ + return c == color + || c == framing_color + || c == line_below_color; +} + +const MapColor* TextSymbol::guessDominantColor() const +{ + auto c = color; + if (!c) + c = framing_color; + if (!c) + c = line_below_color; + return c; +} + +void TextSymbol::scale(double factor) +{ + font_size = qMax(10, qRound(factor * font_size)); // minimum 0.01 mm + framing_line_half_width = qRound(factor * framing_line_half_width); + framing_shadow_x_offset = qRound(factor * framing_shadow_x_offset); + framing_shadow_y_offset = qRound(factor * framing_shadow_y_offset); + + updateQFont(); + + resetIcon(); +} + +void TextSymbol::updateQFont() +{ + qfont = QFont(); + qfont.setBold(bold); + qfont.setItalic(italic); + qfont.setUnderline(underline); + qfont.setPixelSize(internal_point_size); + qfont.setFamily(font_family); + qfont.setHintingPreference(QFont::PreferNoHinting); + qfont.setKerning(kerning); + metrics = QFontMetricsF(qfont); + qfont.setLetterSpacing(QFont::AbsoluteSpacing, metrics.width(QString(QLatin1String{" "})) * character_spacing); + + qfont.setStyleStrategy(QFont::ForceOutline); + + metrics = QFontMetricsF(qfont); + tab_interval = 8.0 * metrics.averageCharWidth(); +} + +#ifndef NO_NATIVE_FILE_FORMAT + +bool TextSymbol::loadImpl(QIODevice* file, int version, Map* map) +{ + int temp; + file->read((char*)&temp, sizeof(int)); + color = (temp >= 0) ? map->getColor(temp) : nullptr; + loadString(file, font_family); + file->read((char*)&font_size, sizeof(int)); + file->read((char*)&bold, sizeof(bool)); + file->read((char*)&italic, sizeof(bool)); + file->read((char*)&underline, sizeof(bool)); + file->read((char*)&line_spacing, sizeof(float)); + if (version >= 13) + file->read((char*)¶graph_spacing, sizeof(double)); + if (version >= 14) + file->read((char*)&character_spacing, sizeof(double)); + if (version >= 12) + file->read((char*)&kerning, sizeof(bool)); + if (version >= 19) + loadString(file, icon_text); + if (version >= 20) + { + file->read((char*)&framing, sizeof(bool)); + file->read((char*)&temp, sizeof(int)); + framing_color = map->getColor(temp); + file->read((char*)&framing_mode, sizeof(int)); + file->read((char*)&framing_line_half_width, sizeof(int)); + file->read((char*)&framing_shadow_x_offset, sizeof(int)); + file->read((char*)&framing_shadow_y_offset, sizeof(int)); + } + if (version >= 13) + { + file->read((char*)&line_below, sizeof(bool)); + file->read((char*)&temp, sizeof(int)); + line_below_color = (temp >= 0) ? map->getColor(temp) : nullptr; + file->read((char*)&line_below_width, sizeof(int)); + file->read((char*)&line_below_distance, sizeof(int)); + int num_custom_tabs; + file->read((char*)&num_custom_tabs, sizeof(int)); + custom_tabs.resize(num_custom_tabs); + for (int i = 0; i < num_custom_tabs; ++i) + file->read((char*)&custom_tabs[i], sizeof(int)); + } + + updateQFont(); + return true; +} + +#endif + +void TextSymbol::saveImpl(QXmlStreamWriter& xml, const Map& map) const +{ + xml.writeStartElement(QStringLiteral("text_symbol")); + xml.writeAttribute(QStringLiteral("icon_text"), icon_text); + + xml.writeStartElement(QStringLiteral("font")); + xml.writeAttribute(QStringLiteral("family"), font_family); + xml.writeAttribute(QStringLiteral("size"), QString::number(font_size)); + if (bold) + xml.writeAttribute(QStringLiteral("bold"), QStringLiteral("true")); + if (italic) + xml.writeAttribute(QStringLiteral("italic"), QStringLiteral("true")); + if (underline) + xml.writeAttribute(QStringLiteral("underline"), QStringLiteral("true")); + xml.writeEndElement(/*font*/); + + xml.writeStartElement(QStringLiteral("text")); + xml.writeAttribute(QStringLiteral("color"), QString::number(map.findColorIndex(color))); + xml.writeAttribute(QStringLiteral("line_spacing"), QString::number(line_spacing)); + xml.writeAttribute(QStringLiteral("paragraph_spacing"), QString::number(paragraph_spacing)); + xml.writeAttribute(QStringLiteral("character_spacing"), QString::number(character_spacing)); + if (kerning) + xml.writeAttribute(QStringLiteral("kerning"), QStringLiteral("true")); + xml.writeEndElement(/*text*/); + + if (framing) + { + xml.writeStartElement(QStringLiteral("framing")); + xml.writeAttribute(QStringLiteral("color"), QString::number(map.findColorIndex(framing_color))); + xml.writeAttribute(QStringLiteral("mode"), QString::number(framing_mode)); + xml.writeAttribute(QStringLiteral("line_half_width"), QString::number(framing_line_half_width)); + xml.writeAttribute(QStringLiteral("shadow_x_offset"), QString::number(framing_shadow_x_offset)); + xml.writeAttribute(QStringLiteral("shadow_y_offset"), QString::number(framing_shadow_y_offset)); + xml.writeEndElement(/*framing*/); + } + + if (line_below) + { + xml.writeStartElement(QStringLiteral("line_below")); + xml.writeAttribute(QStringLiteral("color"), QString::number(map.findColorIndex(line_below_color))); + xml.writeAttribute(QStringLiteral("width"), QString::number(line_below_width)); + xml.writeAttribute(QStringLiteral("distance"), QString::number(line_below_distance)); + xml.writeEndElement(/*line_below*/); + } + + int num_custom_tabs = getNumCustomTabs(); + if (num_custom_tabs > 0) + { + xml.writeStartElement(QStringLiteral("tabs")); + xml.writeAttribute(QStringLiteral("count"), QString::number(num_custom_tabs)); + for (int i = 0; i < num_custom_tabs; ++i) + xml.writeTextElement(QStringLiteral("tab"), QString::number(custom_tabs[i])); + xml.writeEndElement(/*tabs*/); + } + + xml.writeEndElement(/*text_symbol*/); +} + +bool TextSymbol::loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) +{ + Q_UNUSED(symbol_dict); + + if (xml.name() != QLatin1String("text_symbol")) + return false; + + icon_text = xml.attributes().value(QLatin1String("icon_text")).toString(); + framing = false; + line_below = false; + custom_tabs.clear(); + + while (xml.readNextStartElement()) + { + QXmlStreamAttributes attributes(xml.attributes()); + if (xml.name() == QLatin1String("font")) + { + font_family = xml.attributes().value(QLatin1String("family")).toString(); + font_size = attributes.value(QLatin1String("size")).toInt(); + bold = (attributes.value(QLatin1String("bold")) == QLatin1String("true")); + italic = (attributes.value(QLatin1String("italic")) == QLatin1String("true")); + underline = (attributes.value(QLatin1String("underline")) == QLatin1String("true")); + xml.skipCurrentElement(); + } + else if (xml.name() == QLatin1String("text")) + { + int temp = attributes.value(QLatin1String("color")).toInt(); + color = map.getColor(temp); + line_spacing = attributes.value(QLatin1String("line_spacing")).toFloat(); + paragraph_spacing = attributes.value(QLatin1String("paragraph_spacing")).toInt(); + character_spacing = attributes.value(QLatin1String("character_spacing")).toFloat(); + kerning = (attributes.value(QLatin1String("kerning")) == QLatin1String("true")); + xml.skipCurrentElement(); + } + else if (xml.name() == QLatin1String("framing")) + { + framing = true; + int temp = attributes.value(QLatin1String("color")).toInt(); + framing_color = map.getColor(temp); + framing_mode = attributes.value(QLatin1String("mode")).toInt(); + framing_line_half_width = attributes.value(QLatin1String("line_half_width")).toInt(); + framing_shadow_x_offset = attributes.value(QLatin1String("shadow_x_offset")).toInt(); + framing_shadow_y_offset = attributes.value(QLatin1String("shadow_y_offset")).toInt(); + xml.skipCurrentElement(); + } + else if (xml.name() == QLatin1String("line_below")) + { + line_below = true; + int temp = attributes.value(QLatin1String("color")).toInt(); + line_below_color = map.getColor(temp); + line_below_width = attributes.value(QLatin1String("width")).toInt(); + line_below_distance = attributes.value(QLatin1String("distance")).toInt(); + xml.skipCurrentElement(); + } + else if (xml.name() == QLatin1String("tabs")) + { + int num_custom_tabs = attributes.value(QLatin1String("count")).toInt(); + custom_tabs.reserve(num_custom_tabs % 20); // 20 is not the limit + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("tab")) + custom_tabs.push_back(xml.readElementText().toInt()); + else + xml.skipCurrentElement(); + } + } + else + xml.skipCurrentElement(); // unknown + } + + updateQFont(); + return true; +} + +bool TextSymbol::equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const +{ + Q_UNUSED(case_sensitivity); + + const TextSymbol* text = static_cast(other); + + if (!MapColor::equal(color, text->color)) + return false; + if (font_family.compare(text->font_family, Qt::CaseInsensitive) != 0) + return false; + if (font_size != text->font_size || + bold != text->bold || + italic != text->italic || + underline != text->underline || + qAbs(line_spacing - text->line_spacing) > 0.0005f || + paragraph_spacing != text->paragraph_spacing || + qAbs(character_spacing - text->character_spacing) > 0.0005f || + kerning != text->kerning || + line_below != text->line_below || + framing != text->framing) + return false; + if (framing) + { + if (!MapColor::equal(framing_color, text->framing_color)) + return false; + if (framing_mode != text->framing_mode || + (framing_mode == LineFraming && framing_line_half_width != text->framing_line_half_width) || + (framing_mode == ShadowFraming && (framing_shadow_x_offset != text->framing_shadow_x_offset || framing_shadow_y_offset != text->framing_shadow_y_offset))) + return false; + } + if (line_below) + { + if (!MapColor::equal(line_below_color, text->line_below_color)) + return false; + if (line_below_width != text->line_below_width || + line_below_distance != text->line_below_distance) + return false; + } + if (custom_tabs.size() != text->custom_tabs.size()) + return false; + for (size_t i = 0, end = custom_tabs.size(); i < end; ++i) + { + if (custom_tabs[i] != text->custom_tabs[i]) + return false; + } + + return true; +} + +QString TextSymbol::getIconText() const +{ + if (icon_text.isEmpty()) + return QCoreApplication::translate("OpenOrienteering::TextSymbolSettings", "A", "First capital letter of the local alphabet"); + return icon_text; +} + +double TextSymbol::getNextTab(double pos) const +{ + if (!custom_tabs.empty()) + { + double scaling = calculateInternalScaling(); + double map_pos = pos / scaling; + for (auto tab : custom_tabs) + { + if (0.001 * tab > map_pos) + return scaling * 0.001 * tab; + } + + // After the given positions, OCAD repeats the distance between the last two tab positions + double custom_tab_interval = (custom_tabs.size() > 1) ? + (custom_tabs[custom_tabs.size() - 1] - custom_tabs[custom_tabs.size() - 2]) : + custom_tabs[0]; + custom_tab_interval *= 0.001; + return scaling * (0.001 * custom_tabs[custom_tabs.size() - 1] + (floor((map_pos - 0.001 * custom_tabs[custom_tabs.size() - 1]) / custom_tab_interval) + 1.0) * custom_tab_interval); + } + + double next_tab = (floor(pos / tab_interval) + 1.0) * tab_interval; + Q_ASSERT(next_tab > pos); + return next_tab; +} + + +} // namespace OpenOrienteering diff --git a/src/core/symbols/text_symbol.h b/src/core/symbols/text_symbol.h new file mode 100644 index 0000000..202776e --- /dev/null +++ b/src/core/symbols/text_symbol.h @@ -0,0 +1,193 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_TEXT_SYMBOL_H +#define OPENORIENTEERING_TEXT_SYMBOL_H + +#include "symbol.h" + +#include + +#include +#include +#include +#include + +class QIODevice; +class QXmlStreamReader; +class QXmlStreamWriter; +// IWYU pragma: no_forward_declare QFontMetricsF + +namespace OpenOrienteering { + +class Map; +class MapColor; +class MapColorMap; +class Object; +class ObjectRenderables; +class SymbolPropertiesWidget; +class SymbolSettingDialog; +class TextObject; +class VirtualCoordVector; + + + +/** Symbol for text, can be applied to TextObjects. + * + * NOTE: Uses a hack to disable hinting for fonts: + * Uses a big internal font size, internal_point_size, where hinting is + * neglegible and scales it down for output. + * TODO: A maybe better solution would be to use FreeType directly. + */ +class TextSymbol : public Symbol +{ +friend class TextSymbolSettings; +friend class PointSymbolEditorWidget; +friend class OCAD8FileImport; +public: + /** Modes for text framing */ + enum FramingMode + { + /** Only the text itself is displayed. */ + NoFraming = 0, + + /** + * The text path is drawn with a QPainter with active QPen in addition + * to the normal fill, resulting in a line around the letters. + */ + LineFraming = 1, + + /** + * The text is drawn a second time with a slight offset to the main + * text, creating a shadow effect. + */ + ShadowFraming = 2 + }; + + /** Creates an empty text symbol. */ + TextSymbol(); + ~TextSymbol() override; + Symbol* duplicate(const MapColorMap* color_map = nullptr) const override; + + void createRenderables( + const Object *object, + const VirtualCoordVector &coords, + ObjectRenderables &output, + Symbol::RenderableOptions options) const override; + + using Symbol::createBaselineRenderables; + void createBaselineRenderables( + const TextObject* text_object, + const VirtualCoordVector &coords, + ObjectRenderables &output) const; + + void createLineBelowRenderables(const Object* object, ObjectRenderables& output) const; + void colorDeleted(const MapColor* color) override; + bool containsColor(const MapColor* color) const override; + const MapColor* guessDominantColor() const override; + void scale(double factor) override; + + /** Updates the internal QFont from the font settings. */ + void updateQFont(); + + /** Calculates the factor to convert from the real font size to the internal font size */ + inline double calculateInternalScaling() const {return internal_point_size / (0.001 * font_size);} + + // Getters + inline const MapColor* getColor() const {return color;} + inline void setColor(const MapColor* color) {this->color = color;} + inline const QString& getFontFamily() const {return font_family;} + inline double getFontSize() const {return 0.001 * font_size;} + inline bool isBold() const {return bold;} + inline bool isItalic() const {return italic;} + inline bool isUnderlined() const {return underline;} + inline float getLineSpacing() const {return line_spacing;} + inline double getParagraphSpacing() const {return 0.001 * paragraph_spacing;} + inline double getCharacterSpacing() const {return 0.001 * character_spacing;} + inline bool usesKerning() const {return kerning;} + QString getIconText() const; // returns a default text if no custom text is set. + inline bool usesFraming() const {return framing;} + inline const MapColor* getFramingColor() const {return framing_color;} + inline int getFramingMode() const {return framing_mode;} + inline int getFramingLineHalfWidth() const {return framing_line_half_width;} + inline int getFramingShadowXOffset() const {return framing_shadow_x_offset;} + inline int getFramingShadowYOffset() const {return framing_shadow_y_offset;} + inline bool hasLineBelow() const {return line_below;} + inline const MapColor* getLineBelowColor() const {return line_below_color;} + inline double getLineBelowWidth() const {return 0.001 * line_below_width;} + inline double getLineBelowDistance() const {return 0.001 * line_below_distance;} + inline int getNumCustomTabs() const {return (int)custom_tabs.size();} + inline int getCustomTab(int index) const {return custom_tabs[index];} + inline const QFont& getQFont() const {return qfont;} + inline const QFontMetricsF& getFontMetrics() const { return metrics; } + + double getNextTab(double pos) const; + + constexpr static qreal internal_point_size = 256; + + SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override; + +protected: +#ifndef NO_NATIVE_FILE_FORMAT + bool loadImpl(QIODevice* file, int version, Map* map) override; +#endif + void saveImpl(QXmlStreamWriter& xml, const Map& map) const override; + bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict) override; + bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override; + + QFont qfont; + QFontMetricsF metrics; + + const MapColor* color; + QString font_family; + int font_size; // this defines the font size in 1000 mm. How big the letters really are depends on the design of the font though + bool bold; + bool italic; + bool underline; + float line_spacing; // as factor of original line spacing + // (BEGIN) DON'T CHANGE THE ORDER OF THESE FIELDS BEFORE SOLVING [tickets:#428] + int paragraph_spacing; // in mm + float character_spacing; // as a factor of the space character width + bool kerning; + QString icon_text; // text to be drawn in the symbol's icon + // (END) DON'T CHANGE THE ORDER OF THESE FIELDS BEFORE SOLVING [tickets:#428] + + bool framing; + const MapColor* framing_color; + int framing_mode; + int framing_line_half_width; + int framing_shadow_x_offset; + int framing_shadow_y_offset; + + // OCAD compatibility features + bool line_below; + const MapColor* line_below_color; + int line_below_width; + int line_below_distance; + std::vector custom_tabs; + + double tab_interval; /// default tab interval length in text coordinates +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/virtual_coord_vector.cpp b/src/core/virtual_coord_vector.cpp new file mode 100644 index 0000000..3395ca3 --- /dev/null +++ b/src/core/virtual_coord_vector.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "virtual_coord_vector.h" + +#include "core/map_coord.h" + + +namespace OpenOrienteering { + +//### VirtualCoordVector ### + +MapCoordF VirtualCoordVector::fromMapCoordF(size_type index) const +{ + return (*coords)[index]; +} + +MapCoordF VirtualCoordVector::fromMapCoord(size_type index) const +{ + return MapCoordF(flags[index]); +} + + +} // namespace OpenOrienteering diff --git a/src/core/virtual_coord_vector.h b/src/core/virtual_coord_vector.h new file mode 100644 index 0000000..f980d23 --- /dev/null +++ b/src/core/virtual_coord_vector.h @@ -0,0 +1,244 @@ +/* + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_VIRTUAL_COORD_VECTOR_H +#define OPENORIENTEERING_VIRTUAL_COORD_VECTOR_H + +#include + +#include "map_coord.h" + +namespace OpenOrienteering { + +/** + * @brief The VirtualFlagsVector class provides read-only access to a MapCoordVector. + * + * This is an utility class which is used as a public member in VirtualCoordVector. + * Its public API provides read-only STL-style access to a map coordinates vector. + */ +class VirtualFlagsVector +{ + friend class VirtualCoordVector; + +private: + // The pointer to the vector coordinate of coordinates. Never nullptr. + const MapCoordVector* coords; + + // To be used by VirtualCoordVector only + VirtualFlagsVector(const VirtualFlagsVector&) = default; + + // To be used by VirtualCoordVector only + VirtualFlagsVector& operator=(const VirtualFlagsVector&) = default; + +public: + /** + * A reaonably sized unsigned integer type for map coord vector sizes and indexes. + */ + using size_type = quint32; + + /** + * Constructs a new accessor for the given vector of MapCoord. + */ + explicit VirtualFlagsVector(const MapCoordVector& coords); + + + /** + * Returns a direct reference to the underlying coordinates. + */ + const MapCoordVector& data() const; + + + // STL-style API (incomplete) + + bool empty() const; + + size_type size() const; + + MapCoordVector::const_reference operator[](size_type index) const; + + MapCoordVector::const_reference back() const; +}; + + + +/** + * @brief The VirtualCoordVector class provides read-only access to possible distinct coordinates and flags. + * + * Some algorithms in Mapper need to be applied not only to original integer coordinate lists, + * but sometimes also to transformed floating-point coordinates sharing the same flags. + * This class provides uniform access to coordinates and flags in two cases: + * + * (a) Coordinates and flags coming from the same MapCoordVector, and + * + * (b) Coordinates coming from a MapCoordVectorF, and flags from a MapCoordVector. + */ +class VirtualCoordVector +{ +public: + /** + * A reaonably sized unsigned integer type for map coord vector sizes and indexes. + */ + using size_type = VirtualFlagsVector::size_type; + + /** + * An read-only accessor to the flags which provides an operator[](size_type). + */ + // Provides the coordinates in case (a). + VirtualFlagsVector flags; + +private: + // A pointer to the MapCoordVectorF which provides the coordinates in case (b). + const MapCoordVectorF* coords; + + // A pointer to the actual coordinate access implemenation + MapCoordF (VirtualCoordVector::*coords_access)(size_type index) const; + +public: + /** + * Creates another accessor for the data managed by the given prototype. + */ + VirtualCoordVector(const VirtualCoordVector& prototype) = default; + + /** + * Creates an accessor for the flags and coordinates in coords (case a). + */ + explicit VirtualCoordVector(const MapCoordVector& coords); + + /** + * Creates an accessor for the given flags and the coordinates in coords (case b). + */ + explicit VirtualCoordVector(const MapCoordVector& flags, const MapCoordVectorF& coords); + + + /** + * Causes this accessor to serve the same data as rhs. + */ + VirtualCoordVector& operator=(const VirtualCoordVector& rhs) = default; + + + // STL-style API (incomplete, and with some differences, such as returning by value) + + bool empty() const; + + size_type size() const; + + MapCoordF operator[](size_type index) const; + + MapCoordF back() const; + +private: + // Returns a coordinate from coords vector. + MapCoordF fromMapCoordF(size_type index) const; + + // Returns a coordinate from the flags accessors. + MapCoordF fromMapCoord(size_type index) const; +}; + + + +// ### VirtualFlagsVector inline code ### + +inline +VirtualFlagsVector::VirtualFlagsVector(const MapCoordVector& coords) + : coords { &coords } +{ + // nothing else +} + +inline +const MapCoordVector& VirtualFlagsVector::data() const +{ + return *coords; +} + +inline +bool VirtualFlagsVector::empty() const +{ + return coords->empty(); +} + +inline +VirtualFlagsVector::size_type VirtualFlagsVector::size() const +{ + return coords->size(); +} + +inline +MapCoordVector::const_reference VirtualFlagsVector::operator[](size_type index) const +{ + return (*coords)[index]; +} + +inline +MapCoordVector::const_reference VirtualFlagsVector::back() const +{ + return coords->back(); +} + + + +// ### VirtualCoordVector inline code ### + +inline +VirtualCoordVector::VirtualCoordVector(const MapCoordVector& coords) + : flags { coords } + , coords { nullptr } + , coords_access(&VirtualCoordVector::fromMapCoord) +{ + // nothing else +} + +inline +VirtualCoordVector::VirtualCoordVector(const MapCoordVector& flags, const MapCoordVectorF& coords) + : flags { flags } + , coords { &coords } + , coords_access(&VirtualCoordVector::fromMapCoordF) +{ + // nothing else +} + +inline +bool VirtualCoordVector::empty() const +{ + return flags.empty(); +} + +inline +VirtualCoordVector::size_type VirtualCoordVector::size() const +{ + return flags.size(); +} + +inline +MapCoordF VirtualCoordVector::operator[](size_type index) const +{ + return (this->*coords_access)(index); +} + +inline +MapCoordF VirtualCoordVector::back() const +{ + return (this->*coords_access)(size()-1); +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/core/virtual_path.cpp b/src/core/virtual_path.cpp new file mode 100644 index 0000000..c659f3b --- /dev/null +++ b/src/core/virtual_path.cpp @@ -0,0 +1,845 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "virtual_path.h" + +#include "util/util.h" + + +namespace OpenOrienteering { + +namespace +{ + + /** + * Global position error threshold for approximating + * bezier curves with straight segments. + * TODO: make configurable + */ + const PathCoord::length_type bezier_error = 0.005; + + /** + * Global maximum length of generated PathCoord segments for curves. + * + * This ensures that even for flat curves, segments will be generated regularly. + * This is important because while a curve may be flat, the mapping from the + * curve parameter to real position is not linear, which would result in problems. + * This is counteracted by generating many segments. + */ + const PathCoord::length_type bezier_segment_maxlen_squared = 1.0; + + +} // namespace + + + +// ### PathCoord ### + +// static +PathCoord::length_type PathCoord::bezierError() +{ + return bezier_error; +} + + + +// ### PathCoordVector ### + +PathCoordVector::PathCoordVector(const MapCoordVector& coords) + : virtual_coords(coords) +{ + // nothing else +} + +PathCoordVector::PathCoordVector(const MapCoordVector& flags, const MapCoordVectorF& coords) + : virtual_coords(flags, coords) +{ + // nothing else +} + +PathCoordVector::PathCoordVector(const VirtualCoordVector& coords) + : virtual_coords(coords) +{ + // nothing else +} + +VirtualCoordVector::size_type PathCoordVector::update(VirtualCoordVector::size_type part_start) +{ + auto& flags = virtual_coords.flags; + auto part_end = virtual_coords.size() - 1; + if (part_start <= part_end) + { + Q_ASSERT(virtual_coords.size() == flags.size()); + + if (flags[part_start].isHolePoint()) + { + auto pos = virtual_coords[0]; + qWarning("PathCoordVector at %g %g (mm) has an invalid hole at index %d.", + pos.x(), -pos.y(), part_start); + } + + clear(); + if (empty() || (part_start > 0 && flags[part_start-1].isHolePoint())) + { + emplace_back(virtual_coords[part_start], part_start, 0.0, 0.0); + } + + for (auto index = part_start + 1; index <= part_end; ++index) + { + if (flags[index-1].isCurveStart()) + { + Q_ASSERT(index+2 <= part_end); + + // Add curve coordinates + curveToPathCoord(virtual_coords[index-1], virtual_coords[index], virtual_coords[index+1], virtual_coords[index+2], index-1, 0, 1); + index += 2; + } + + // Add end point + const PathCoord& prev = back(); + emplace_back(virtual_coords[index], index, 0.0, prev.clen + prev.pos.distanceTo(virtual_coords[index])); + + Q_ASSERT(back().index == index); + + if (index < part_end && flags[index].isHolePoint()) + { + part_end = index; + } + } + } + return part_end; +} + +bool PathCoordVector::isClosed() const +{ + return virtual_coords.flags[back().index].isClosePoint(); +} + +PathCoordVector::size_type PathCoordVector::findNextDashPoint(PathCoordVector::size_type first) const +{ + // Get behind the current point + auto prev_index = (*this)[first].index; + auto last = size() - 1; + + while (first != last && (*this)[first].index == prev_index) + { + ++first; + } + + // Find a dash point or hole point + while (first != last) + { + const auto& current_flags = virtual_coords.flags[(*this)[first].index]; + if (current_flags.isDashPoint() || current_flags.isHolePoint()) + break; + ++first; + } + + return first; +} + +PathCoord::length_type PathCoordVector::length() const +{ + Q_ASSERT(!empty()); + return back().clen; +} + +double PathCoordVector::calculateArea() const +{ + Q_ASSERT(!empty()); + + auto area = 0.0; + auto end_index = size() - 1; + auto j = end_index; // The last vertex is the 'previous' one to the first + + for (auto i = 0u; i <= end_index; ++i) + { + area += ((*this)[j].pos.x() + (*this)[i].pos.x()) * ((*this)[j].pos.y() - (*this)[i].pos.y()); + j = i; + } + return qAbs(area) / 2; +} + +QRectF PathCoordVector::calculateExtent() const +{ + QRectF extent(0.0, 0.0, -1.0, 0.0); + if (!empty()) + { + auto pc = begin(); + extent = QRectF(pc->pos.x(), pc->pos.y(), 0.0001, 0.0001); + + auto last = end(); + for (++pc; pc != last; ++pc) + { + rectInclude(extent, pc->pos); + } + + Q_ASSERT(extent.isValid()); + } + return extent; +} + +bool PathCoordVector::intersectsBox(const QRectF& box) const +{ + bool result = false; + if (!empty()) + { + auto last_pos = front().pos; + result = std::any_of(begin()+1, end(), [&box, &last_pos](const PathCoord& pc) + { + auto pos = pc.pos; + auto result = lineIntersectsRect(box, last_pos, pos); /// \todo Implement this here, used nowhere else + last_pos = pos; + return result; + }); + } + return result; +} + +bool PathCoordVector::isPointInside(MapCoordF coord) const +{ + bool inside = false; + if (size() > 2) + { + auto last_pos = back().pos; + for(const auto& path_coord : *this) + { + auto pos = path_coord.pos; + if ( ((pos.y() > coord.y()) != (last_pos.y() > coord.y())) && + (coord.x() < (last_pos.x() - pos.x()) * + (coord.y() - pos.y()) / (last_pos.y() - pos.y()) + pos.x()) ) + { + inside = !inside; + } + last_pos = pos; + } + } + return inside; +} + +void PathCoordVector::curveToPathCoord( + MapCoordF c0, + MapCoordF c1, + MapCoordF c2, + MapCoordF c3, + MapCoordVector::size_type edge_start, + float p0, + float p1 ) +{ + // Common + auto p_half = (p0 + p1) * 0.5; + MapCoordF c12((c1.x() + c2.x()) * 0.5, (c1.y() + c2.y()) * 0.5); + + auto inner_len_sq = c0.distanceSquaredTo(c3); + auto outer_len = [&]() { return c0.distanceTo(c1) + c1.distanceTo(c2) + c2.distanceTo(c3); }; + if (inner_len_sq <= bezier_segment_maxlen_squared && outer_len() - sqrt(inner_len_sq) <= bezier_error) + { + const PathCoord& prev = back(); + emplace_back(c12, edge_start, p_half, prev.clen + float(prev.pos.distanceTo(c12))); + } + else + { + // Split in two + MapCoordF c01((c0.x() + c1.x()) * 0.5f, (c0.y() + c1.y()) * 0.5f); + MapCoordF c23((c2.x() + c3.x()) * 0.5f, (c2.y() + c3.y()) * 0.5f); + MapCoordF c012((c01.x() + c12.x()) * 0.5f, (c01.y() + c12.y()) * 0.5f); + MapCoordF c123((c12.x() + c23.x()) * 0.5f, (c12.y() + c23.y()) * 0.5f); + MapCoordF c0123((c012.x() + c123.x()) * 0.5f, (c012.y() + c123.y()) * 0.5f); + + curveToPathCoord(c0, c01, c012, c0123, edge_start, p0, p_half); + curveToPathCoord(c0123, c123, c23, c3, edge_start, p_half, p1); + } +} + + + +//### VirtualPath ### + +VirtualPath::VirtualPath(const MapCoordVector& coords) + : VirtualPath(coords, 0, coords.size()-1) +{ + // nothing else +} + +VirtualPath::VirtualPath(const MapCoordVector& coords, size_type first, size_type last) + : coords(coords) + , path_coords(coords) + , first_index(first) + , last_index(last) +{ + // nothing else +} + +VirtualPath::VirtualPath(const VirtualCoordVector& coords) + : VirtualPath(coords, 0, coords.size()-1) +{ + // nothing else +} + +VirtualPath::VirtualPath(const VirtualCoordVector& coords, size_type first, size_type last) + : coords(coords) + , path_coords(coords) + , first_index(first) + , last_index(last) +{ + // nothing else +} + +VirtualPath::VirtualPath(const MapCoordVector& flags, const MapCoordVectorF& coords) + : VirtualPath(flags, coords, 0, coords.size()-1) +{ + // nothing else +} + +VirtualPath::VirtualPath(const MapCoordVector& flags, const MapCoordVectorF& coords, size_type first, size_type last) + : coords(flags, coords) + , path_coords(flags, coords) + , first_index(first) + , last_index(last) +{ + // nothing else +} + +VirtualPath::size_type VirtualPath::countRegularNodes() const +{ + size_type num_regular_points = 0; + for (auto i = first_index; i <= last_index; ++i) + { + ++num_regular_points; + if (coords.flags[i].isCurveStart()) + i += 2; + } + + if (num_regular_points && isClosed()) + --num_regular_points; + + return num_regular_points; +} + +bool VirtualPath::isClosed() const +{ + Q_ASSERT(last_index < coords.size()); + Q_ASSERT(last_index >= first_index); + return coords.flags[last_index].isClosePoint(); +} + +PathCoord VirtualPath::findClosestPointTo( + MapCoordF coord, + float& distance_squared, + float distance_bound_squared, + size_type start_index, + size_type end_index ) const +{ + Q_ASSERT(!path_coords.empty()); + + auto result = path_coords.front(); + + // Find upper bound for distance. + for (const auto& path_coord : path_coords) + { + if (path_coord.index > end_index) + break; + if (path_coord.index < start_index) + continue; + + auto to_coord = coord - path_coord.pos; + auto dist_sq = to_coord.lengthSquared(); + if (dist_sq < distance_bound_squared) + { + distance_bound_squared = dist_sq; + result = path_coord; + } + } + + // Check between this coord and the next one. + distance_squared = distance_bound_squared; + auto last = end(path_coords)-1; + for (auto pc = begin(path_coords); pc != last; ++pc) + { + if (pc->index > end_index) + break; + if (pc->index < start_index) + continue; + + auto pos = pc->pos; + auto next_pc = pc+1; + auto next_pos = next_pc->pos; + + auto tangent = next_pos - pos; + tangent.normalize(); + + auto to_coord = coord - pos; + float dist_along_line = MapCoordF::dotProduct(to_coord, tangent); + if (dist_along_line <= 0) + { + if (to_coord.lengthSquared() < distance_squared) + { + distance_squared = to_coord.lengthSquared(); + result = *pc; + } + continue; + } + + float line_length = next_pc->clen - pc->clen; + if (dist_along_line >= line_length) + { + if (coord.distanceSquaredTo(next_pos) < distance_squared) + { + distance_squared = coord.distanceSquaredTo(next_pos); + result = *next_pc; + } + continue; + } + + auto right = tangent.perpRight(); + + float dist_from_line = MapCoordF::dotProduct(right, to_coord); + auto dist_from_line_sq = dist_from_line * dist_from_line; + if (dist_from_line_sq < distance_squared) + { + distance_squared = dist_from_line_sq; + result.clen = pc->clen + dist_along_line; + result.index = pc->index; + auto factor = dist_along_line / line_length; + if (next_pc->index == pc->index) + result.param = pc->param + (next_pc->param - pc->param) * factor; + else + result.param = pc->param + (1.0 - pc->param) * factor; /// \todo verify + + if (coords.flags[result.index].isCurveStart()) + { + MapCoordF unused; + PathCoord::splitBezierCurve(MapCoordF(coords.flags[result.index]), MapCoordF(coords.flags[result.index+1]), + MapCoordF(coords.flags[result.index+2]), MapCoordF(coords.flags[result.index+3]), + result.param, unused, unused, result.pos, unused, unused); + } + else + { + result.pos = pos + (next_pos - pos) * factor; + } + } + } + return result; +} + +VirtualPath::size_type VirtualPath::prevCoordIndex(size_type base_index) const +{ + Q_ASSERT(base_index >= first_index); + + size_type result = first_index; + if (Q_UNLIKELY(base_index > last_index)) + { + result = last_index; + } + else if (base_index > first_index) + { + result = base_index - 1; + for (auto i = 1; i <= 3; ++i) + { + auto alternative = base_index - i; + if (alternative < first_index || alternative > last_index) + break; + + if (coords.flags[alternative].isCurveStart()) + result = alternative; + } + } + + return result; +} + +VirtualPath::size_type VirtualPath::nextCoordIndex(size_type base_index) const +{ + Q_ASSERT(base_index <= last_index); + + size_type result = base_index + 1; + if (Q_UNLIKELY(result > last_index)) + { + result = last_index; + } + + if (Q_UNLIKELY(result <= first_index)) + { + result = first_index; + } + else + { + for (auto i = 0; i < 2; ++i) + { + if (coords.flags[base_index].isCurveStart()) + { + result = base_index + 3; + break; + } + + if (base_index == first_index) + break; + + --base_index; + } + } + + return result; +} + +MapCoordF VirtualPath::calculateTangent(size_type i) const +{ + bool ok_to_coord, ok_to_next; + MapCoordF to_coord = calculateIncomingTangent(i, ok_to_coord); + MapCoordF to_next = calculateOutgoingTangent(i, ok_to_next); + + if (!ok_to_next) + { + to_next = to_coord; + } + else if (ok_to_coord) + { + to_coord.normalize(); + to_next.normalize(); + to_next += to_coord; + } + + return to_next; +} + +std::pair VirtualPath::calculateTangentScaling(size_type i) const +{ + bool ok_to_coord, ok_to_next; + MapCoordF to_coord = calculateIncomingTangent(i, ok_to_coord); + MapCoordF to_next = calculateOutgoingTangent(i, ok_to_next); + + auto scaling = 1.0; + if (!ok_to_next) + { + to_next = to_coord; + } + else if (ok_to_coord) + { + to_coord.normalize(); + to_next.normalize(); + if (to_next == -to_coord) + { + to_next = to_next.perpRight(); + scaling = std::numeric_limits::infinity(); + } + else + { + to_next += to_coord; + to_next.normalize(); + scaling = 1.0/MapCoordF::dotProduct(to_next, to_coord); + } + } + + return std::make_pair(to_next, scaling); +} + +MapCoordF VirtualPath::calculateTangent( + size_type i, + bool backward, + bool& ok) const +{ + if (backward) + return calculateIncomingTangent(i, ok); + else + return calculateOutgoingTangent(i, ok); +} + +MapCoordF VirtualPath::calculateIncomingTangent( + size_type i, + bool& ok) const +{ + ok = true; + MapCoordF tangent; + + for (auto k = i; k > first_index; ) /// \todo Implement unsigned + { + --k; + tangent = coords[i] - coords[k]; + if (tangent.lengthSquared() > PathCoord::tangentEpsilonSquared()) + return tangent; + } + + if (isClosed() && last_index > i+1) + { + // Continue from end + for (auto k = last_index; k > i; --k) + { + tangent = coords[i] - coords[k]; + if (tangent.lengthSquared() > PathCoord::tangentEpsilonSquared()) + return tangent; + } + } + + ok = false; + return tangent; +} + +MapCoordF VirtualPath::calculateOutgoingTangent( + size_type i, + bool& ok) const +{ + ok = true; + MapCoordF tangent; + + for (auto k = i+1; k <= last_index; ++k) + { + tangent = coords[k] - coords[i]; + if (tangent.lengthSquared() > PathCoord::tangentEpsilonSquared()) + return tangent; + } + + if (isClosed()) + { + // Continue from start + for (auto k = first_index; k < i; ++k) + { + tangent = coords[k] - coords[i]; + if (tangent.lengthSquared() > PathCoord::tangentEpsilonSquared()) + return tangent; + } + } + + ok = false; + return tangent; +} + +void VirtualPath::copy( + const SplitPathCoord& first, + const SplitPathCoord& last, + MapCoordVector& out_coords ) const +{ + Q_ASSERT(!empty()); + Q_ASSERT(first.path_coords == &path_coords); + Q_ASSERT(last.path_coords == &path_coords); + + auto& flags = coords.flags; + + // Handle first coordinate and its flags. + if (out_coords.empty() || + out_coords.back().isHolePoint() || + !out_coords.back().isPositionEqualTo(MapCoord(first.pos)) ) + { + out_coords.emplace_back(first.pos); + } + else + { + out_coords.back().setHolePoint(false); + out_coords.back().setClosePoint(false); + } + + if (first.index == last.index) + { + out_coords.back().setCurveStart(last.is_curve_end && last.param != first.param); + } + else + { + out_coords.back().setCurveStart(first.is_curve_start); + + auto stop_index = last.index; + if (last.param == 0.0) + { + stop_index -= last.is_curve_end ? 3 : 1; + } + + auto index = first.index + 1; + if (first.is_curve_start) + { + out_coords.emplace_back(first.curve_start[0]); + out_coords.emplace_back(first.curve_start[1]); + index += 2; + } + + for (; index <= stop_index; ++index) + { + out_coords.emplace_back(coords[index]); + out_coords.back().setFlags(flags[index].flags()); + } + } + + if (out_coords.back().isCurveStart()) + { + Q_ASSERT(last.is_curve_end); + out_coords.emplace_back(last.curve_end[0]); + out_coords.emplace_back(last.curve_end[1]); + } + + out_coords.emplace_back(last.pos); + if (last.param == 0.0) + { + out_coords.back().setHolePoint(flags[last.index].isHolePoint()); + out_coords.back().setClosePoint(flags[last.index].isClosePoint()); + } +} + +void VirtualPath::copy( + const SplitPathCoord& first, + const SplitPathCoord& last, + MapCoordVector& out_flags, + MapCoordVectorF& out_coords) const +{ + Q_ASSERT(!empty()); + Q_ASSERT(first.path_coords == &path_coords); + Q_ASSERT(last.path_coords == &path_coords); + + Q_ASSERT(out_coords.size() == out_flags.size()); + + auto& flags = coords.flags; + + // Handle first coordinate and its flags. + if (out_coords.empty() || + out_flags.back().isHolePoint() || + out_coords.back() != first.pos) + { + out_coords.emplace_back(first.pos); + out_flags.emplace_back(); + } + else + { + out_flags.back().setHolePoint(false); + out_flags.back().setClosePoint(false); + } + + if (first.index == last.index) + { + out_flags.back().setCurveStart(last.is_curve_end && last.param != first.param); + } + else + { + out_flags.back().setCurveStart(first.is_curve_start); + + auto stop_index = last.index; + if (last.param == 0.0) + { + stop_index -= last.is_curve_end ? 3 : 1; + } + + auto index = first.index + 1; + if (first.is_curve_start && index < stop_index) + { + out_flags.emplace_back(); + out_coords.emplace_back(first.curve_start[0]); + out_flags.emplace_back(); + out_coords.emplace_back(first.curve_start[1]); + index += 2; + } + + for (; index <= stop_index; ++index) + { + out_flags.emplace_back(flags[index]); + out_coords.emplace_back(coords[index]); + } + } + + if (out_flags.back().isCurveStart()) + { + Q_ASSERT(last.is_curve_end); + out_flags.emplace_back(); + out_coords.emplace_back(last.curve_end[0]); + out_flags.emplace_back(); + out_coords.emplace_back(last.curve_end[1]); + } + + out_flags.emplace_back(); + out_coords.emplace_back(last.pos); + if (last.param == 0.0) + { + out_flags.back().setHolePoint(flags[last.index].isHolePoint()); + out_flags.back().setClosePoint(flags[last.index].isClosePoint()); + } + + Q_ASSERT(out_coords.size() == out_flags.size()); +} + +// This algorithm must (nearly) match VirtualPath::copy(). +// (It will always copy the length of the starting point, but +// VirtualPath::copy() might skip this point in some cases.) +void VirtualPath::copyLengths( + const SplitPathCoord& first, + const SplitPathCoord& last, + std::vector& out_lengths ) const +{ + Q_ASSERT(!empty()); + Q_ASSERT(first.path_coords == &path_coords); + Q_ASSERT(last.path_coords == &path_coords); + + auto& flags = coords.flags; + + // Handle first coordinate + out_lengths.emplace_back(first.clen); + + bool after_curve_start = false; + + if (first.index == last.index) + { + after_curve_start = last.is_curve_end && last.param != first.param; + } + else + { + after_curve_start = first.is_curve_start; + + auto stop_index = last.index; + if (last.param == 0.0) + { + stop_index -= last.is_curve_end ? 3 : 1; + } + + auto index = first.index + 1; + if (first.is_curve_start && index < stop_index) + { + after_curve_start = false; + out_lengths.emplace_back(first.clen); + out_lengths.emplace_back(first.clen); + index += 2; + } + + auto path_coord_index = first.path_coord_index; + for (; index <= stop_index; ++index) + { + while (path_coords[path_coord_index].index < index) + ++path_coord_index; + + if (path_coords[path_coord_index].index == index) + { + // Normal node + out_lengths.emplace_back(path_coords[path_coord_index].clen); + after_curve_start = flags[index].isCurveStart(); + } + else + { + // Control point + out_lengths.emplace_back(out_lengths.back()); + after_curve_start = false; + } + } + } + + if (after_curve_start) + { + auto length = out_lengths.back(); + out_lengths.emplace_back(length); + out_lengths.emplace_back(length); + } + + out_lengths.push_back(last.clen); +} + + +} // namespace OpenOrienteering diff --git a/src/core/virtual_path.h b/src/core/virtual_path.h new file mode 100644 index 0000000..17fd315 --- /dev/null +++ b/src/core/virtual_path.h @@ -0,0 +1,425 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_VIRTUAL_PATH_H +#define OPENORIENTEERING_VIRTUAL_PATH_H + +#include +#include +#include + +#include + +#include "core/map_coord.h" +#include "core/path_coord.h" +#include "core/virtual_coord_vector.h" + +// IWYU pragma: no_forward_declare QRectF + +namespace OpenOrienteering { + +class PathCoordVector : public std::vector +{ +private: + friend class SplitPathCoord; + friend class VirtualPath; + + VirtualCoordVector virtual_coords; + +public: + PathCoordVector(const MapCoordVector& coords); + + PathCoordVector(const MapCoordVector& flags, const MapCoordVectorF& coords); + + PathCoordVector(const VirtualCoordVector& coords); + +private: + PathCoordVector(const PathCoordVector&) = default; + + PathCoordVector(PathCoordVector&&) = default; + + + PathCoordVector& operator=(const PathCoordVector&) = default; + + PathCoordVector& operator=(PathCoordVector&&) = default; + + + const VirtualFlagsVector& flags() const; + + const VirtualCoordVector& coords() const; + + bool isClosed() const; + + +public: + /** + * Updates the path coords from the flags/coords, starting at first. + * + * \return The index after the last element of this part. + */ + VirtualCoordVector::size_type update(VirtualCoordVector::size_type first); + + + /** + * Finds the index of the next dash point after first, or returns size()-1. + * + * \todo Consider return a SplitPathCoord (cf. actual usage). + */ + size_type findNextDashPoint(PathCoordVector::size_type first) const; + + + /** + * Finds the path coordinate index with or just before the given length. + */ + size_type lowerBound( + PathCoord::length_type length, + size_type first, + size_type last + ) const; + + /** + * Finds the path coordinate index with or just after the given length. + */ + size_type upperBound( + PathCoord::length_type length, + size_type first, + size_type last + ) const; + + + /** + * Returns the length of the path. + */ + PathCoord::length_type length() const; + + /** + * Calculates the area of this part. + */ + double calculateArea() const; + + QRectF calculateExtent() const; + + bool intersectsBox(const QRectF& box) const; + + bool isPointInside(MapCoordF coord) const; + +private: + /** + * Recursive approximation of a bezier curve by polygonal segments. + */ + void curveToPathCoord( + MapCoordF c0, + MapCoordF c1, + MapCoordF c2, + MapCoordF c3, + MapCoordVector::size_type edge_start, + float p0, + float p1 + ); +}; + + + +/** + * A VirtualPath class represents a single path out of a sequence of coords and flags. + * + * It provides a PathCoordVector which is is a polyline approximation of the path + * (i.e. no curves) and provides metadata such as length for each point of the path. + */ +class VirtualPath +{ +public: + /** A reaonably sized unsigned integer type for coord vector sizes and indexes. */ + using size_type = VirtualCoordVector::size_type; + + /** The underlying coordinates and flags. */ + VirtualCoordVector coords; + + /** The derived flattened coordinates and meta data. */ + PathCoordVector path_coords; + + /** Index of first coordinate of this path in the coords. */ + size_type first_index; + + /** Index of the last coordinate of this part in the coords. */ + size_type last_index; + + + explicit VirtualPath(const MapCoordVector& coords); + + VirtualPath(const MapCoordVector& coords, size_type first, size_type last); + + explicit VirtualPath(const VirtualCoordVector& coords); + + VirtualPath(const VirtualCoordVector& coords, size_type first, size_type last); + + VirtualPath(const MapCoordVector& flags, const MapCoordVectorF& coords); + + VirtualPath(const MapCoordVector& flags, const MapCoordVectorF& coords, size_type first, size_type last); + + VirtualPath(const VirtualPath&) = default; + + VirtualPath(VirtualPath&&) = default; + +protected: + VirtualPath& operator=(const VirtualPath&) = default; + + VirtualPath& operator=(VirtualPath&&) = default; + +public: + /** + * Returns true if there are no nodes in this path. + */ + bool empty() const; + + /** + * Returns the number of coordinates in this path. + * + * \see countRegularNodes() + */ + size_type size() const; + + /** + * Calculates the number of regular nodes in this path. + * + * Regular nodes exclude close points and curve handles. + */ + size_type countRegularNodes() const; + + + /** + * Returns true if the path is closed. + * + * For closed paths, the last coordinate is at the same position as the + * first coordinate of the path,and has the "close point" flag set. + * These coords will move together when moved by the user, appearing as + * just one coordinate. + * + * Parts of PathObjects can be closed and opened with PathPart::setClosed() + * or PathPart::connectEnds(). + * + * Objects with area symbols must always be closed. + */ + bool isClosed() const; + + + /** + * Returns the length of the path. + */ + PathCoord::length_type length() const; + + /** + * Calculates the area of this part. + */ + double calculateArea() const; + + QRectF calculateExtent() const; + + bool intersectsBox(const QRectF& box) const; + + bool isPointInside(MapCoordF coord) const; + + PathCoord findClosestPointTo( + MapCoordF coord, + float& distance_squared, + float distance_bound_squared, + size_type start_index, + size_type end_index) const; + + + /** + * Determines the index of the previous regular coordinate. + * + * Regular coordinates exclude bezier control points and close points. + * + * @param base_index The index of a regular coordinate from which to start. + */ + size_type prevCoordIndex(size_type base_index) const; + + /** + * Determines the index of the next regular coordinate. + * + * Regular coordinates exclude bezier control points and close points. + * + * @param base_index The index of a regular coordinate from which to start. + */ + size_type nextCoordIndex(size_type base_index) const; + + + MapCoordF calculateTangent(size_type i) const; + + std::pair calculateTangentScaling(size_type i) const; + + /** + * Calculates the path tangent at the given MapCoord index. + * + * Takes care of cases where successive points are at equal positions. + * + * @param i Index of the coordinate where to query the tangent. + * @param backward If false, returns the forward tangent, if true, returns + * the backward tangent. Makes a difference at sharp corners. + * @param ok Is set to true if the tangent can be found correctly, false if + * failing to find the tangent. + */ + MapCoordF calculateTangent( + size_type i, + bool backward, + bool& ok + ) const; + + /** + * Similar to calculateTangent(). + */ + MapCoordF calculateIncomingTangent( + size_type i, + bool& ok + ) const; + + /** + * Similar to calculateTangent(). + */ + MapCoordF calculateOutgoingTangent( + size_type i, + bool& ok + ) const; + + + void copy( + const SplitPathCoord& first, + const SplitPathCoord& last, + MapCoordVector& out_coords + ) const; + + void copy( + const SplitPathCoord& first, + const SplitPathCoord& last, + MapCoordVector& out_flags, + MapCoordVectorF& out_coords + ) const; + + + void copyLengths( + const SplitPathCoord& first, + const SplitPathCoord& last, + std::vector& out_lengths + ) const; + +}; + + + +// ### PathCoordVector inline code ### + +inline +const VirtualFlagsVector& PathCoordVector::flags() const +{ + return virtual_coords.flags; +} + +inline +const VirtualCoordVector& PathCoordVector::coords() const +{ + return virtual_coords; +} + +inline +PathCoordVector::size_type PathCoordVector::lowerBound( + PathCoord::length_type length, + PathCoordVector::size_type first, + PathCoordVector::size_type last ) const +{ + auto index = first; + while (index != last) + { + ++index; + if ((*this)[index].clen < length) + { + --index; + break; + } + } + return index; +} + +inline +PathCoordVector::size_type PathCoordVector::upperBound( + PathCoord::length_type length, + PathCoordVector::size_type first, + PathCoordVector::size_type last ) const +{ + auto index = first; + while (index != last && (*this)[index].clen < length) + { + ++index; + } + return index; +} + + + +// ### VirtualPath inline code ### + +inline +bool VirtualPath::empty() const +{ + return last_index+1 == first_index; +} + +inline +VirtualPath::size_type VirtualPath::size() const +{ + return last_index - first_index + 1; +} + +inline +PathCoord::length_type VirtualPath::length() const +{ + return path_coords.length(); +} + +inline +double VirtualPath::calculateArea() const +{ + return path_coords.calculateArea(); +} + +inline +QRectF VirtualPath::calculateExtent() const +{ + return path_coords.calculateExtent(); +} + +inline +bool VirtualPath::intersectsBox(const QRectF& box) const +{ + return path_coords.intersectsBox(box); +} + +inline +bool VirtualPath::isPointInside(MapCoordF coord) const +{ + return path_coords.isPointInside(coord); +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/fileformats/file_format.cpp b/src/fileformats/file_format.cpp new file mode 100644 index 0000000..c6299dd --- /dev/null +++ b/src/fileformats/file_format.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "file_format.h" + +#include + + +namespace OpenOrienteering { + +// ### FileFormatException ### + +// virtual +FileFormatException::~FileFormatException() noexcept = default; + +// virtual +const char* FileFormatException::what() const noexcept +{ + return msg_c.constData(); +} + + + +// ### FileFormat ### + +FileFormat::FileFormat(FileFormat::FileType file_type, const char* id, const QString& description, const QString& file_extension, FileFormat::FormatFeatures features) + : file_type(file_type), + format_id(id), + format_description(description), + format_features(features) +{ + Q_ASSERT(file_type != 0); + Q_ASSERT(qstrlen(id) > 0); + Q_ASSERT(!description.isEmpty()); + if (!file_extension.isEmpty()) + addExtension(file_extension); +} + +FileFormat::~FileFormat() = default; + + +void FileFormat::addExtension(const QString& file_extension) +{ + file_extensions << file_extension; + format_filter = QString::fromLatin1("%1 (*.%2)").arg(format_description, file_extensions.join(QString::fromLatin1(" *."))); +} + +bool FileFormat::understands(const unsigned char *buffer, std::size_t sz) const +{ + Q_UNUSED(buffer); + Q_UNUSED(sz); + return false; +} + +Importer *FileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const +{ + Q_UNUSED(stream); + Q_UNUSED(map); + Q_UNUSED(view); + throw FileFormatException(QCoreApplication::translate("OpenOrienteering::Importer", "Format (%1) does not support import").arg(description())); +} + +Exporter *FileFormat::createExporter(QIODevice* stream, Map *map, MapView *view) const +{ + Q_UNUSED(stream); + Q_UNUSED(map); + Q_UNUSED(view); + throw FileFormatException(QCoreApplication::translate("OpenOrienteering::Exporter", "Format (%1) does not support export").arg(description())); +} + + +} // namespace OpenOrienteering diff --git a/src/fileformats/file_format.h b/src/fileformats/file_format.h new file mode 100644 index 0000000..6dac9c6 --- /dev/null +++ b/src/fileformats/file_format.h @@ -0,0 +1,339 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_FILE_FORMAT_H +#define OPENORIENTEERING_FILE_FORMAT_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +class QIODevice; + +namespace OpenOrienteering { + +class Exporter; +class Importer; +class Map; +class MapView; + + +/** An exception type thrown by an importer or exporter if it encounters a fatal error. + */ +class FileFormatException : public std::exception // clazy:exclude=copyable-polymorphic +{ +public: + /** Creates a new exception with the given message + * \param message a text describing the exceptional event that has occurred. + */ + FileFormatException(const QString& message = QString()); + + /** Creates a new exception with the given message + * \param message a text describing the exceptional event that has occurred. + */ + FileFormatException(const char* message); + + /** Copy-constructor (C++ FAQ 17.17). + */ + FileFormatException(const FileFormatException& other) noexcept; + + /** Destroys the exception object. + */ + ~FileFormatException() noexcept override; + + /** Returns the message as a QString. + */ + const QString& message() const noexcept; + + /** Returns the message as a C string. + */ + const char* what() const noexcept override; + +private: + QString const msg; + QByteArray const msg_c; +}; + + +/** Describes a file format understood by this application. Each file format has an ID + * (an internal, non-translated string); a description (translated); a file extension + * (non-translated); and methods to indicate their support for import or export. Formats + * are installed into a FormatRegistry, and can be looked up in a variety of ways. + * + * A typical FileFormat subclass will look like this: + * + * \code + * class MyCustomFileFormat : public FileFormat { + * public: + * MyCustomFileFormat : FileFormat("custom", ::OpenOrienteering::ImportExport::tr("Custom file"), "custom", true, true) { + * } + * + * Importer *createImporter(QIODevice* stream, Map *map, MapView *view) const { + * return new CustomImporter(stream, map, view); + * } + * + * Exporter *createExporter(QIODevice* stream, Map *map, MapView *view) const { + * return new CustomExporter(stream, map, view); + * } + * } + * \endcode + */ +class FileFormat +{ +public: + /** File type enumeration. + * + * Each file type shall use a distinct bit so that they may be OR-combined. + * + * Currently the program is only used for mapping, and "Map" is the only + * element. "Course" or "Event" are possible additions. + */ + enum FileType + { + MapFile = 0x01, + OgrFile = 0x02, ///< Geospatial vector data supported by OGR + + AllFiles = MapFile, ///< All types which can be handled by an editor. + + EndOfFileTypes + }; + + /** A type which handles OR-combinations of file types. + */ + Q_DECLARE_FLAGS(FileTypes, FileType) + + /** File format features. + * + * Each feature shall use a distinct bit so that they may be OR-combined. + */ + enum FormatFeatureFlag + { + ImportSupported = 0x01, + ExportSupported = 0x02, + ExportLossy = 0x04, + + EndOfFormatFeatures + }; + + /** A type which handles OR-combinations of format implementation features. + */ + Q_DECLARE_FLAGS(FormatFeatures, FormatFeatureFlag) + + /** Creates a new file format with the given parameters. + * + * Don't use a leading dot on the file extension. + * + */ + FileFormat(FileType file_type, const char* id, const QString& description, const QString& file_extension, FormatFeatures features); + + FileFormat(const FileFormat&) = delete; + FileFormat(FileFormat&&) = delete; + + /** Destroys the file format information. */ + virtual ~FileFormat(); + + FileFormat& operator=(const FileFormat&) = delete; + FileFormat& operator=(FileFormat&&) = delete; + + /** Registers an alternative file name extension. + * It is used by the filter. + */ + void addExtension(const QString& file_extension); + + /** Returns the type of file. + */ + FileType fileType() const; + + /** Returns the internal ID of the file format. + */ + const char* id() const; + + /** Returns a short human-readable description of the file format. + */ + const QString& description() const; + + /** Returns the primary file name extension used by this file format. + * + * This shall only be used for constructing new file names. Otherwise + * you will probably need to use fileExtensions(). + */ + const QString& primaryExtension() const; + + /** Returns all file name extension supported by this file format. + */ + const QStringList& fileExtensions() const; + + /** Returns the filter that represents this format in file dialogs. + */ + const QString& filter() const; + + /** Returns true if this file format supports importing a map from its associated file type. + */ + bool supportsImport() const; + + /** Returns true if this file format supports exporting a map to its associated file type. + */ + bool supportsExport() const; + + /** Returns true if an exporter for this file format is potentially lossy, i.e., if the exported + * file cannot fully represent all aspects of the internal OO map objects. This flag is used by + * the application to warn the user before saving to a lossy file type. + */ + bool isExportLossy() const; + + /** Returns true if this file format believes it is capable of understanding a file that + * starts with the given byte sequence. "Magic" numbers and version information is commonly + * placed at the beginning of a file, and this method is used by the application to pre-screen + * for a suitable Importer. If there is any doubt about whether the file format can successfully + * process a file, this method should return false. + */ + virtual bool understands(const unsigned char *buffer, std::size_t sz) const; + + /** Creates an Importer that will read a map file from the given stream into the given map and view. + * The caller can then call doImport() in the returned object to start the import process. The caller + * is responsible for deleting the Importer when it's finished. + * + * If the Importer could not be created, then this method should throw a FormatException. + */ + virtual Importer* createImporter(QIODevice* stream, Map *map, MapView *view) const; + + /** Creates an Exporter that will save the given map and view into the given stream. + * The caller can then call doExport() in the returned object to start the export process. The caller + * is responsible for deleting the Exporter when it's finished. + * + * If the Exporter could not be created, then this method should throw a FormatException. + */ + virtual Exporter *createExporter(QIODevice* stream, Map *map, MapView *view) const; + +private: + FileType file_type; + const char* format_id; + QString format_description; + QStringList file_extensions; + QString format_filter; + FormatFeatures format_features; +}; + + +// ### FileFormatException inline and header code ### + +inline +FileFormatException::FileFormatException(const QString& message) + : msg(message) + , msg_c(message.toLocal8Bit()) +{ + // Nothing +} + +inline +FileFormatException::FileFormatException(const char* message) + : msg(QString::fromLatin1(message)) + , msg_c(message) +{ + // Nothing +} + +inline +FileFormatException::FileFormatException(const FileFormatException& other) noexcept + : msg(other.msg) + , msg_c(other.msg_c) +{ + // Nothing +} + +inline +const QString& FileFormatException::message() const noexcept +{ + return msg; +} + + +// ### FileFormat inline and header code ### + +inline +FileFormat::FileType FileFormat::fileType() const +{ + return file_type; +} + +inline +const char* FileFormat::id() const +{ + return format_id; +} + +inline +const QString& FileFormat::description() const +{ + return format_description; +} + +inline +const QString& FileFormat::primaryExtension() const +{ + Q_ASSERT(file_extensions.size() > 0); // by constructor + return file_extensions[0]; +} + +inline +const QStringList& FileFormat::fileExtensions() const +{ + return file_extensions; +} + +inline +const QString& FileFormat::filter() const +{ + return format_filter; +} + +inline +bool FileFormat::supportsImport() const +{ + return format_features.testFlag(FileFormat::ImportSupported); +} + +inline +bool FileFormat::supportsExport() const +{ + return format_features.testFlag(FileFormat::ExportSupported); +} + +inline +bool FileFormat::isExportLossy() const +{ + return format_features.testFlag(FileFormat::ExportLossy); +} + + +} // namespace OpenOrienteering + + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::FileFormat::FileTypes) + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::FileFormat::FormatFeatures) + + +#endif // OPENORIENTEERING_FILE_FORMAT_H diff --git a/src/fileformats/file_format_registry.cpp b/src/fileformats/file_format_registry.cpp new file mode 100644 index 0000000..9a78250 --- /dev/null +++ b/src/fileformats/file_format_registry.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * Copyright 2013, 2015, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "file_format_registry.h" + +#include + +#include "fileformats/file_format.h" + + +namespace OpenOrienteering { + +FileFormatRegistry FileFormats; + + +// ### FormatRegistry ### + +FileFormatRegistry::FileFormatRegistry() noexcept +: default_format_id{ nullptr } +{ + // nothing else +} + + +FileFormatRegistry::~FileFormatRegistry() +{ + for (auto format : fmts) + delete format; +} + + + +void FileFormatRegistry::registerFormat(FileFormat *format) +{ + fmts.push_back(format); + if (fmts.size() == 1) default_format_id = format->id(); + Q_ASSERT(findFormatForFilename(QLatin1String("filename.") + format->primaryExtension()) != nullptr); // There may be more than one format! + Q_ASSERT(findFormatByFilter(format->filter()) == format); // The filter shall be unique at least by description. +} + +std::unique_ptr FileFormatRegistry::unregisterFormat(const FileFormat* format) +{ + std::unique_ptr ret; + auto it = std::find(begin(fmts), end(fmts), format); + if (it != end(fmts)) + { + ret.reset(*it); + fmts.erase(it); + } + return ret; +} + +const FileFormat *FileFormatRegistry::findFormat(const char* id) const +{ + for (auto format : fmts) + { + if (qstrcmp(format->id(), id) == 0) return format; + } + return nullptr; +} + +const FileFormat *FileFormatRegistry::findFormatByFilter(const QString& filter) const +{ + for (auto format : fmts) + { + // Compare only before closing ')'. Needed for QTBUG 51712 workaround in + // file_dialog.cpp, and warranted by Q_ASSERT in registerFormat(). + if (filter.startsWith(format->filter().leftRef(format->filter().length()-1))) + return format; + } + return nullptr; +} + +const FileFormat *FileFormatRegistry::findFormatForFilename(const QString& filename) const +{ + QString file_extension = QFileInfo(filename).suffix(); + for (auto format : fmts) + { + if (format->fileExtensions().contains(file_extension, Qt::CaseInsensitive)) return format; + } + return nullptr; +} + + +} // namespace OpenOrienteering diff --git a/src/fileformats/file_format_registry.h b/src/fileformats/file_format_registry.h new file mode 100644 index 0000000..b74ae5c --- /dev/null +++ b/src/fileformats/file_format_registry.h @@ -0,0 +1,103 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * Copyright 2013, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_FILE_FORMAT_REGISTRY_H +#define OPENORIENTEERING_FILE_FORMAT_REGISTRY_H + +#include +#include + +#include + +namespace OpenOrienteering { + +class FileFormat; + + +/** Provides a registry for file formats, and takes ownership of the supported format objects. + */ +class FileFormatRegistry +{ +public: + /** Creates an empty file format registry. + */ + FileFormatRegistry() noexcept; + + FileFormatRegistry(const FileFormatRegistry&) = delete; + + /** Destroys a file format registry. + */ + ~FileFormatRegistry(); + + FileFormatRegistry& operator=(const FileFormatRegistry&) = delete; + + /** Returns an immutable list of the available file formats. + */ + inline const std::vector &formats() const { return fmts; } + + /** Finds a file format with the given internal ID, or returns nullptr if no format + * is found. + */ + const FileFormat *findFormat(const char* id) const; + + /** Finds a file format which implements the given filter, or returns nullptr if no + * format is found. + * + * Only the file format's filter string before the closing ')' is taken into + * account for matching, i.e. the given parameter 'filter' may contain + * additional extensions following the original ones. + */ + const FileFormat *findFormatByFilter(const QString& filter) const; + + /** Finds a file format whose file extension matches the file extension of the given + * path, or returns nullptr if no matching format is found. + */ + const FileFormat *findFormatForFilename(const QString& filename) const; + + /** Returns the ID of default file format for this registry. This will automatically + * be set to the first registered format. + */ + const char* defaultFormat() const { return default_format_id; } + + /** Registers a new file format. The registry takes ownership of the provided Format. + */ + void registerFormat(FileFormat *format); + + /** + * Unregisters a file format. + * + * Returns a non-const pointer to the file format and transfers ownership to the caller. + */ + std::unique_ptr unregisterFormat(const FileFormat *format); + +private: + std::vector fmts; + const char* default_format_id; +}; + +/** A FileFormatRegistry that is globally defined for convenience. Within the scope of a single + * application, you can simply use calls of the form "FileFormats.findFormat(...)". + */ +extern FileFormatRegistry FileFormats; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_FILE_FORMAT_REGISTRY_H diff --git a/src/fileformats/file_import_export.cpp b/src/fileformats/file_import_export.cpp new file mode 100644 index 0000000..1388fbe --- /dev/null +++ b/src/fileformats/file_import_export.cpp @@ -0,0 +1,178 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * Copyright 2013, 2014 Thomas Schöps + * Copyright 2013-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "file_import_export.h" + +#include + +#include "core/map.h" +#include "core/map_part.h" +#include "core/map_view.h" +#include "core/objects/object.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "fileformats/file_format.h" +#include "templates/template.h" + + +namespace OpenOrienteering { + +// ### ImportExport ### + +ImportExport::~ImportExport() = default; + + +QVariant ImportExport::option(const QString& name) const +{ + if (!options.contains(name)) + throw FileFormatException(::OpenOrienteering::ImportExport::tr("No such option: %1", "No such import / export option").arg(name)); + return options[name]; +} + + + +// ### Importer ### + +Importer::~Importer() = default; + + +void Importer::doImport(bool load_symbols_only, const QString& map_path) +{ + if (view) + view->setTemplateLoadingBlocked(true); + + import(load_symbols_only); + + // Object post processing: + // - make sure that there is no object without symbol + // - make sure that all area-only path objects are closed + // - make sure that there are no special points in wrong places (e.g. curve starts inside curves) + for (int p = 0; p < map->getNumParts(); ++p) + { + MapPart* part = map->getPart(p); + for (int o = 0; o < part->getNumObjects(); ++o) + { + Object* object = part->getObject(o); + if (object->getSymbol() == nullptr) + { + addWarning(::OpenOrienteering::Importer::tr("Found an object without symbol.")); + if (object->getType() == Object::Point) + object->setSymbol(map->getUndefinedPoint(), true); + else if (object->getType() == Object::Path) + object->setSymbol(map->getUndefinedLine(), true); + else + { + // There is no undefined symbol for this type of object, delete the object + part->deleteObject(o, false); + --o; + continue; + } + } + + if (object->getType() == Object::Path) + { + PathObject* path = object->asPath(); + Symbol::Type contained_types = path->getSymbol()->getContainedTypes(); + if (contained_types & Symbol::Area && !(contained_types & Symbol::Line)) + path->closeAllParts(); + + path->normalize(); + } + } + } + + if (auto deleted = map->deleteIrregularObjects()) + { + addWarning(tr("Dropped %n irregular object(s).", nullptr, int(deleted))); + } + + // Symbol post processing + for (int i = 0; i < map->getNumSymbols(); ++i) + { + if (!map->getSymbol(i)->loadFinished(map)) + throw FileFormatException(::OpenOrienteering::Importer::tr("Error during symbol post-processing.")); + } + + // Template loading: try to find all template files + if (view) + view->setTemplateLoadingBlocked(false); + bool have_lost_template = false; + for (int i = 0; i < map->getNumTemplates(); ++i) + { + Template* temp = map->getTemplate(i); + bool found_in_map_dir = false; + if (!temp->tryToFindTemplateFile(map_path, &found_in_map_dir)) + { + have_lost_template = true; + } + else if (!view || view->getTemplateVisibility(temp).visible) + { + if (!temp->loadTemplateFile(false)) + { + addWarning(tr("Failed to load template '%1', reason: %2") + .arg(temp->getTemplateFilename(), temp->errorString())); + } + else + { + auto error_string = temp->errorString(); + if (found_in_map_dir) + { + error_string.prepend( + ::OpenOrienteering::Importer::tr( + "Template \"%1\" has been loaded from the map's directory instead of" + " the relative location to the map file where it was previously." + ).arg(temp->getTemplateFilename()) + QLatin1Char('\n') ); + } + + if (!error_string.isEmpty()) + { + addWarning(tr("Warnings when loading template '%1':\n%2") + .arg(temp->getTemplateFilename(), temp->errorString())); + } + } + } + } + + if (have_lost_template) + { +#if defined(Q_OS_ANDROID) + addWarning(tr("At least one template file could not be found.")); +#else + addWarning(tr("At least one template file could not be found.") + QLatin1Char(' ') + + tr("Click the red template name(s) in the Templates -> Template setup window to locate the template file name(s).")); +#endif + } +} + +void Importer::finishImport() +{ + // Nothing, not inlined +} + + + +// ### Exporter ### + +Exporter::~Exporter() = default; + + +} // namespace OpenOrienteering diff --git a/src/fileformats/file_import_export.h b/src/fileformats/file_import_export.h new file mode 100644 index 0000000..59ae50c --- /dev/null +++ b/src/fileformats/file_import_export.h @@ -0,0 +1,266 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_IMPORT_EXPORT_H +#define OPENORIENTEERING_IMPORT_EXPORT_H + +#include + +#include +#include +#include +#include + +class QIODevice; + +namespace OpenOrienteering { + +class Map; +class MapView; + + +/** Abstract base class for both importer and exporters; provides support for configuring the map and + * view to manipulate, setting and retrieving options, and collecting a list of warnings. + * + * Subclasses should define default values for options they intend to use in their constructors, + * by calling setOption() with the relevant values. There is no such thing as an "implicit default" + * for options. + */ +class ImportExport +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::ImportExport) + +public: + /** Creates a new importer or exporter with the given input stream, map, and view. + */ + ImportExport(QIODevice* stream, Map *map, MapView *view); + + ImportExport(const ImportExport&) = delete; + ImportExport(ImportExport&&) = delete; + + /** Destroys an importer or exporter. + */ + virtual ~ImportExport(); + + /** Returns the current list of warnings collected by this object. + */ + const std::vector &warnings() const; + + /** Sets an option in this importer or exporter. + */ + void setOption(const QString& name, const QVariant& value); + + /** Retrieves the value of an options in this importer or exporter. If the option does not have + * a value - either a default value assigned in the constructor, or a custom value assigned + * through setOption() - then a FormatException will be thrown. + */ + QVariant option(const QString& name) const; + + protected: + /** Adds an import/export warning to the current list of warnings. The provided message + * should be translated. + */ + void addWarning(const QString& str); + +protected: + /// The input / output stream + QIODevice* stream; + + /// The Map to import or export + Map *map; + + /// The MapView to import or export + MapView *view; + +private: + /// A list of options for the import/export + QHash options; + + /// A list of warnings + std::vector warn; +}; + + +/** + * Represents an action that the user must take to successfully complete an import. + * TODO: Currently not fully implemented, as this should be done using the + * planned ProblemWidget instead, which works independently of import / export. + */ +class ImportAction +{ + // Nothing +}; + + +/** Base class for all importers. An Importer has the following lifecycle: + * -# The Importer is constructed, with pointers to the map and view. The Importer + * should also set default values for any options it will read. The base class + * will throw an exception if the importer reads an option that does not have a value. + * -# setOption() will be called zero or more times to customize the options. + * -# doImport() will be called to perform the initial import. The implementation of + * this method will try to populate the map and view from the given file, and may + * optionally register action items for the user via addAction(). + * -# If action items are present, then they will be presented to the user. Each + * action item will have its satisfy() method called with the user's choice. + * -# finishImport() will be called. If any action items were created, this method + * should finish the import based on the values supplied by the user. + */ +class Importer : public ImportExport +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::Importer) + +public: + /** Creates a new Importer with the given output stream, map, and view. + */ + Importer(QIODevice* stream, Map *map, MapView *view); + + /** Destroys this Importer. + */ + ~Importer() override; + + /** Returns the current list of action items. + */ + inline const std::vector &actions() const; + + /** Begins the import process. The implementation of this method should read the file + * and populate the map and view from it. If a fatal error is encountered (such as a + * missing or corrupt file), than it should throw a FormatException. If the import can + * proceed, but information might be lost in the process, than it should call + * addWarning() with a translated, useful description of the issue. The line between + * errors and warnings is somewhat of a judgement call on the part of the author, but + * generally an Importer should not succeed unless the map is populated sufficiently + * to be useful. + */ + void doImport(bool load_symbols_only, const QString& map_path = QString()); + + /** Once all action items are satisfied, this method should be called to complete the + * import process. This class defines a default implementation, that does nothing. + */ + virtual void finishImport(); + +protected: + /** Implementation of doImport(). + */ + virtual void import(bool load_symbols_only) = 0; + + /** Adds an action item to the current list. + */ + inline void addAction(const ImportAction &action); + +private: + /// A list of action items that must be resolved before the import can be completed + std::vector act; +}; + + +/** Base class for all exporters. An Exporter has the following lifecycle: + * + * 1. The Exporter is constructed, with pointers to the filename, map and view. The Exporter + * should also set default values for any options it will read. The base class + * will throw an exception if the exporter reads an option that does not have a value. + * 2. setOption() will be called zero or more times to customize the options. + * 3. doExport() will be called to perform the export. + */ +class Exporter : public ImportExport +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::Exporter) + +public: + /** Creates a new Importer with the given i/o stream, map, and view. + */ + Exporter(QIODevice* stream, Map *map, MapView *view); + + /** Destroys the current Exporter. + */ + ~Exporter() override; + + /** Exports the map and view to the given file. If a fatal error is encountered (such as a + * permission problem), than this method should throw a FormatException. If the export can + * proceed, but information might be lost in the process, than it should call + * addWarning() with a translated, useful description of the issue. + */ + virtual void doExport() = 0; +}; + + +// ### ImportExport inline code ### + +inline +ImportExport::ImportExport(QIODevice* stream, Map* map, MapView* view) + : stream(stream), map(map), view(view) +{ + // Nothing +} + +inline +const std::vector< QString >& ImportExport::warnings() const +{ + return warn; +} + +inline +void ImportExport::addWarning(const QString& str) +{ + warn.push_back(str); +} + +inline +void ImportExport::setOption(const QString& name, const QVariant& value) +{ + options[name] = value; +} + + + + +// ### Importer inline code ### + +inline +Importer::Importer(QIODevice* stream, Map* map, MapView* view) + : ImportExport(stream, map, view) +{ + // Nothing +} + +inline +const std::vector< ImportAction >& Importer::actions() const +{ + return act; +} + +inline +void Importer::addAction(const ImportAction& action) +{ + act.push_back(action); +} + + +// ### Exporter ### + +inline +Exporter::Exporter(QIODevice* stream, Map* map, MapView* view) + : ImportExport(stream, map, view) +{ + // Nothing +} + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_IMPORT_EXPORT_H diff --git a/src/fileformats/native_file_format.cpp b/src/fileformats/native_file_format.cpp new file mode 100644 index 0000000..d736f05 --- /dev/null +++ b/src/fileformats/native_file_format.cpp @@ -0,0 +1,432 @@ +/* + * Copyright 2012 Pete Curtis + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef NO_NATIVE_FILE_FORMAT + +#include "native_file_format.h" + +#include + +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_grid.h" +#include "core/map_printer.h" +#include "core/map_view.h" +#include "core/symbols/symbol.h" +#include "fileformats/file_import_export.h" +#include "templates/template.h" +#include "templates/template_image.h" +#include "undo/undo_manager.h" +#include "util/util.h" + + +namespace OpenOrienteering { + +// ### NativeFileImport declaration ### + +/** An Importer for the native file format. This class delegates to the load() and loadImpl() methods of the + * model objects, but long-term this can be refactored out of the model into this class. + * + * \deprecated + */ +class NativeFileImport : public Importer +{ +public: + /** Creates a new native file importer. + */ + NativeFileImport(QIODevice* stream, Map *map, MapView *view); + + /** Destroys this importer. + */ + ~NativeFileImport() override; + +protected: + /** Imports a native file. + */ + void import(bool load_symbols_only) override; +}; + + +// ### GPSProjectionParameters ### +/** + * Legacy parameters for an ellipsoid and an orthographic projection of + * ellipsoid coordinates to 2D map (template) coordinates. + */ +struct GPSProjectionParameters +{ + double a; // ellipsoidal semi-major axis + double b; // ellipsoidal semi-minor axis + double e_sq; // eccentricity of the ellipsoid (squared) + double center_latitude; // latitude which gives map coordinate zero + double center_longitude; // longitude which gives map coordinate zero + double sin_center_latitude; // sine of center_latitude + double cos_center_latitude; // cosine of center_latitude + double v0; // prime vertical radius of curvature at latitude of origin center_latitude +}; + + + +// ### NativeFileFormat ### + +const int NativeFileFormat::least_supported_file_format_version = 0; +const int NativeFileFormat::current_file_format_version = 30; +const char NativeFileFormat::magic_bytes[4] = {0x4F, 0x4D, 0x41, 0x50}; // "OMAP" + +NativeFileFormat::NativeFileFormat() + : FileFormat(FileFormat::MapFile, "native (deprecated)", ::OpenOrienteering::ImportExport::tr("OpenOrienteering Mapper").append(QLatin1String(" pre-0.5")), QString::fromLatin1("omap"), + FileFormat::ImportSupported) +{ + // Nothing +} + +bool NativeFileFormat::understands(const unsigned char *buffer, std::size_t sz) const +{ + // The first four bytes of the file must be 'OMAP'. + return (sz >= 4 && memcmp(buffer, magic_bytes, 4) == 0); +} + +Importer *NativeFileFormat::createImporter(QIODevice* stream, Map* map, MapView* view) const +{ + return new NativeFileImport(stream, map, view); +} + + + +// ### NativeFileImport ### + +NativeFileImport::NativeFileImport(QIODevice* stream, Map *map, MapView *view) : Importer(stream, map, view) +{ +} + +NativeFileImport::~NativeFileImport() +{ +} + +void NativeFileImport::import(bool load_symbols_only) +{ + addWarning(::OpenOrienteering::Importer::tr("This file uses an obsolete format. " + "Support for this format is to be removed from this program soon. " + "To be able to open the file in the future, save it again.")); + + MapCoord::boundsOffset().reset(true); + + char buffer[4]; + stream->read(buffer, 4); // read the magic + + int version; + stream->read((char*)&version, sizeof(int)); + if (version < 0) + { + addWarning(::OpenOrienteering::Importer::tr("Invalid file format version.")); + } + else if (version < NativeFileFormat::least_supported_file_format_version) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Unsupported old file format version. Please use an older program version to load and update the file.")); + } + else if (version > NativeFileFormat::current_file_format_version) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Unsupported new file format version. Some map features will not be loaded or saved by this version of the program. Consider updating.")); + } + + if (version <= 16) + { + Georeferencing georef; + stream->read((char*)&georef.scale_denominator, sizeof(int)); + + if (version >= 15) + loadString(stream, map->map_notes); + + bool gps_projection_params_set; // obsolete + stream->read((char*)&gps_projection_params_set, sizeof(bool)); + GPSProjectionParameters gps_projection_parameters; // obsolete + stream->read((char*)&gps_projection_parameters, sizeof(GPSProjectionParameters)); + if (gps_projection_params_set) + { + LatLon ref_point = LatLon::fromRadiant(gps_projection_parameters.center_latitude, gps_projection_parameters.center_longitude); + georef.setGeographicRefPoint(ref_point); + } + *map->georeferencing = georef; + } + else if (version >= 17) + { + loadString(stream, map->map_notes); + + Georeferencing georef; + stream->read((char*)&georef.scale_denominator, sizeof(int)); + double value; + if (version >= 18) + { + stream->read((char*)&value, sizeof(double)); + georef.declination = Georeferencing::roundDeclination(value); + } + stream->read((char*)&value, sizeof(double)); + georef.grivation = Georeferencing::roundDeclination(value); + georef.grivation_error = value - georef.grivation; + + double x,y; + stream->read((char*)&x, sizeof(double)); + stream->read((char*)&y, sizeof(double)); + georef.map_ref_point = MapCoord(x,y); + stream->read((char*)&x, sizeof(double)); + stream->read((char*)&y, sizeof(double)); + georef.projected_ref_point = QPointF(x,y); + loadString(stream, georef.projected_crs_id); + loadString(stream, georef.projected_crs_spec); + stream->read((char*)&y, sizeof(double)); + stream->read((char*)&x, sizeof(double)); + georef.geographic_ref_point = LatLon::fromRadiant(y, x); + QString geographic_crs_id, geographic_crs_spec; + loadString(stream, geographic_crs_id); // reserved for geographic crs id + loadString(stream, geographic_crs_spec); // reserved for full geographic crs specification + if (geographic_crs_spec != Georeferencing::geographic_crs_spec) + { + addWarning( + ::OpenOrienteering::Importer::tr("The geographic coordinate reference system of the map was \"%1\". This CRS is not supported. Using \"%2\"."). + arg(geographic_crs_spec, Georeferencing::geographic_crs_spec) + ); + } + if (version <= 17) + georef.initDeclination(); + // Correctly set georeferencing state + georef.setProjectedCRS(georef.projected_crs_id, georef.projected_crs_spec); + *map->georeferencing = georef; + } + + if (version >= 24) + map->setGrid(MapGrid().load(stream, version)); + + map->renderable_options = Symbol::RenderNormal; + if (version >= 25) + { + bool area_hatching_enabled, baseline_view_enabled; + stream->read((char*)&area_hatching_enabled, sizeof(bool)); + stream->read((char*)&baseline_view_enabled, sizeof(bool)); + if (area_hatching_enabled) + map->renderable_options |= Symbol::RenderAreasHatched; + if (baseline_view_enabled) + map->renderable_options |= Symbol::RenderBaselines; + } + + if (version >= 6) + { + bool print_params_set; + stream->read((char*)&print_params_set, sizeof(bool)); + if (print_params_set) + { + MapPrinterConfig printer_config(*map); + stream->read((char*)&printer_config.page_format.orientation, sizeof(int)); + stream->read((char*)&printer_config.page_format.paper_size, sizeof(int)); + + float resolution; + stream->read((char*)&resolution, sizeof(float)); + printer_config.options.resolution = qRound(resolution); + stream->read((char*)&printer_config.options.show_templates, sizeof(bool)); + if (version >= 24) + stream->read((char*)&printer_config.options.show_grid, sizeof(bool)); + else + printer_config.options.show_grid = false; + + stream->read((char*)&printer_config.center_print_area, sizeof(bool)); + + float print_area_left, print_area_top, print_area_width, print_area_height; + stream->read((char*)&print_area_left, sizeof(float)); + stream->read((char*)&print_area_top, sizeof(float)); + stream->read((char*)&print_area_width, sizeof(float)); + stream->read((char*)&print_area_height, sizeof(float)); + printer_config.print_area = QRectF(print_area_left, print_area_top, print_area_width, print_area_height); + + if (version >= 26) + { + bool print_different_scale_enabled; + stream->read((char*)&print_different_scale_enabled, sizeof(bool)); + stream->read((char*)&printer_config.options.scale, sizeof(int)); + if (!print_different_scale_enabled) + printer_config.options.scale = map->getScaleDenominator(); + } + map->setPrinterConfig(printer_config); + } + } + + if (version >= 16) + { + stream->read((char*)&map->image_template_use_meters_per_pixel, sizeof(bool)); + stream->read((char*)&map->image_template_meters_per_pixel, sizeof(double)); + stream->read((char*)&map->image_template_dpi, sizeof(double)); + stream->read((char*)&map->image_template_scale, sizeof(double)); + } + + // Load colors + int num_colors; + stream->read((char*)&num_colors, sizeof(int)); + map->color_set->colors.resize(num_colors); + + for (int i = 0; i < num_colors; ++i) + { + int priority; + stream->read((char*)&priority, sizeof(int)); + MapColor* color = new MapColor(priority); + + MapColorCmyk cmyk; + stream->read((char*)&cmyk.c, sizeof(float)); + stream->read((char*)&cmyk.m, sizeof(float)); + stream->read((char*)&cmyk.y, sizeof(float)); + stream->read((char*)&cmyk.k, sizeof(float)); + color->setCmyk(cmyk); + float opacity; + stream->read((char*)&opacity, sizeof(float)); + color->setOpacity(opacity); + + QString name; + loadString(stream, name); + color->setName(name); + + map->color_set->colors[i] = color; + } + + // Load symbols + int num_symbols; + stream->read((char*)&num_symbols, sizeof(int)); + map->symbols.resize(num_symbols); + + for (int i = 0; i < num_symbols; ++i) + { + QScopedValueRollback offset { MapCoord::boundsOffset() }; + MapCoord::boundsOffset().reset(false); + + int symbol_type; + stream->read((char*)&symbol_type, sizeof(int)); + + Symbol* symbol = Symbol::getSymbolForType(static_cast(symbol_type)); + if (!symbol) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Error while loading a symbol with type %2.").arg(symbol_type)); + } + + if (!symbol->load(stream, version, map)) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Error while loading a symbol.")); + } + map->symbols[i] = symbol; + } + + if (!load_symbols_only) + { + // Load templates + stream->read((char*)&map->first_front_template, sizeof(int)); + + int num_templates; + stream->read((char*)&num_templates, sizeof(int)); + map->templates.resize(num_templates); + + for (int i = 0; i < num_templates; ++i) + { + QString path; + loadString(stream, path); + auto temp = Template::templateForFile(path, map); + if (!temp) + temp.reset(new TemplateImage(path, map)); // fallback + + if (version >= 27) + { + loadString(stream, path); + temp->setTemplateRelativePath(path); + } + + temp->loadTemplateConfiguration(stream, version); + + map->templates[i] = temp.release(); + } + + if (version >= 28) + { + int num_closed_templates; + stream->read((char*)&num_closed_templates, sizeof(int)); + map->closed_templates.resize(num_closed_templates); + + for (int i = 0; i < num_closed_templates; ++i) + { + QString path; + loadString(stream, path); + auto temp = Template::templateForFile(path, map); + if (!temp) + temp.reset(new TemplateImage(path, map)); // fallback + + loadString(stream, path); + temp->setTemplateRelativePath(path); + + temp->loadTemplateConfiguration(stream, version); + + map->closed_templates[i] = temp.release(); + } + } + + // Restore widgets and views + if (view) + { + view->load(stream, version); + } + else + { + MapView tmp{ map }; + tmp.load(stream, version); + } + + // Load undo steps + if (version >= 7) + { + if (!map->undoManager().load(stream, version)) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Error while loading undo steps.")); + } + } + + // Load parts + stream->read((char*)&map->current_part_index, sizeof(int)); + + int num_parts; + if (stream->read((char*)&num_parts, sizeof(int)) < (int)sizeof(int)) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Error while reading map part count.")); + } + delete map->parts[0]; + map->parts.resize(num_parts); + + for (int i = 0; i < num_parts; ++i) + { + MapPart* part = new MapPart({}, map); + if (!part->load(stream, version, map)) + { + delete part; + throw FileFormatException(::OpenOrienteering::Importer::tr("Error while loading map part %2.").arg(i+1)); + } + map->parts[i] = part; + } + } + + emit map->currentMapPartIndexChanged(map->current_part_index); + emit map->currentMapPartChanged(map->getPart(map->current_part_index)); +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/fileformats/native_file_format.h b/src/fileformats/native_file_format.h new file mode 100644 index 0000000..6c1ef94 --- /dev/null +++ b/src/fileformats/native_file_format.h @@ -0,0 +1,84 @@ +/* + * Copyright 2012, 2013 Thomas Schöps, Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef NATIVE_FILE_FORMAT_H +#define NATIVE_FILE_FORMAT_H + +#ifndef NO_NATIVE_FILE_FORMAT + +#include + +#include "fileformats/file_format.h" + +class QIODevice; + +class Importer; + +namespace OpenOrienteering { + +class Map; +class MapView; + + +/** Provides a description of the old native file format. + * This is an (architecture-dependent) binary packed format + * with a file extension of "omap", and internal versioning. + * + * \deprecated + */ +class NativeFileFormat : public FileFormat +{ +public: + /** Creates a new file format representing the native type. + */ + NativeFileFormat(); + + /** Returns true if the file starts with the magic byte sequence "OMAP" (0x4f 0x4d 0x41 0x50). + */ + bool understands(const unsigned char *buffer, std::size_t sz) const override; + + /** Creates an importer for this file type. + */ + Importer *createImporter(QIODevice* stream, Map *map, MapView *view) const override; + +#ifdef MAPPER_ENABLE_NATIVE_EXPORTER + /** Creates an exporter for this file type. + */ + Exporter *createExporter(QIODevice* stream, Map *map, MapView *view) const; +#endif + + /** Constant describing the earliest OMAP version supported by this file format. + */ + static const int least_supported_file_format_version; + + /** Constant describing the current OMAP version used by this file format for saving. + */ + static const int current_file_format_version; + + /** The file magic: "OMAP" + */ + static const char magic_bytes[4]; +}; + + +} // namespace OpenOrienteering + +#endif // NO_NATIVE_FILE_FORMAT + +#endif // NATIVE_FILE_FORMAT_H diff --git a/src/fileformats/ocad8_file_format.cpp b/src/fileformats/ocad8_file_format.cpp new file mode 100644 index 0000000..87ac821 --- /dev/null +++ b/src/fileformats/ocad8_file_format.cpp @@ -0,0 +1,2880 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "ocad8_file_format.h" +#include "ocad8_file_format_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/map_part.h" +#include "core/map_view.h" +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/symbols/symbol.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/combined_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/text_symbol.h" +#include "fileformats/xml_file_format.h" +#include "fileformats/file_import_export.h" +#include "templates/template.h" +#include "templates/template_image.h" +#include "templates/template_map.h" +#include "util/util.h" +#include "util/encoding.h" + + +namespace OpenOrienteering { + +// ### OCAD8FileFormat ### + +OCAD8FileFormat::OCAD8FileFormat() + : FileFormat(MapFile, "OCAD78", ::OpenOrienteering::ImportExport::tr("OCAD Versions 7, 8"), QString::fromLatin1("ocd"), + ImportSupported | ExportSupported | ExportLossy) +{ + // Nothing +} + +bool OCAD8FileFormat::understands(const unsigned char* buffer, std::size_t sz) const +{ + // The first two bytes of the file must be AD 0C. + if (sz >= 2 && buffer[0] == 0xAD && buffer[1] == 0x0C) return true; + return false; +} + +Importer* OCAD8FileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const +{ + return new OCAD8FileImport(stream, map, view); +} + +Exporter* OCAD8FileFormat::createExporter(QIODevice* stream, Map* map, MapView* view) const +{ + return new OCAD8FileExport(stream, map, view); +} + +// ### OCAD8FileImport ### + +OCAD8FileImport::OCAD8FileImport(QIODevice* stream, Map* map, MapView* view) : Importer(stream, map, view), file(nullptr) +{ + ocad_init(); + const QByteArray enc_name = Settings::getInstance().getSetting(Settings::General_Local8BitEncoding).toByteArray(); + encoding_1byte = Util::codecForName(enc_name); + if (!encoding_1byte) encoding_1byte = QTextCodec::codecForLocale(); + encoding_2byte = QTextCodec::codecForName("UTF-16LE"); + offset_x = offset_y = 0; +} + +OCAD8FileImport::~OCAD8FileImport() +{ + ocad_shutdown(); +} + +bool OCAD8FileImport::isRasterImageFile(const QString &filename) +{ + int dot_pos = filename.lastIndexOf(QLatin1Char('.')); + if (dot_pos < 0) + return false; + QString extension = filename.right(filename.length() - dot_pos - 1).toLower(); + return QImageReader::supportedImageFormats().contains(extension.toLatin1()); +} + +void OCAD8FileImport::import(bool load_symbols_only) +{ + //qint64 start = QDateTime::currentMSecsSinceEpoch(); + + u32 size = stream->bytesAvailable(); + u8* buffer = (u8*)malloc(size); + if (!buffer) + throw FileFormatException(tr("Could not allocate buffer.")); + if (stream->read((char*)buffer, size) != size) + throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(stream->errorString())); + int err = ocad_file_open_memory(&file, buffer, size); + if (err != 0) throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(tr("libocad returned %1").arg(err))); + + if (file->header->major <= 5 || file->header->major >= 9) + throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(tr("OCAD files of version %1 are not supported!").arg(file->header->major))); + + //qDebug() << "file version is" << file->header->major << ", type is" + // << ((file->header->ftype == 2) ? "normal" : "other"); + //qDebug() << "map scale is" << file->setup->scale; + + // Scale and georeferencing parameters + Georeferencing georef; + georef.setScaleDenominator(file->setup->scale); + georef.setProjectedRefPoint(QPointF(file->setup->offsetx, file->setup->offsety)); + if (qAbs(file->setup->angle) >= 0.01) /* degrees */ + { + georef.setGrivation(file->setup->angle); + } + map->setGeoreferencing(georef); + + map->setMapNotes(convertCString((const char*)file->buffer + file->header->infopos, file->header->infosize, false)); + + // TODO: print parameters + + // Load the separations to a temporary stack + std::vector< MapColor* > separations; + int num_separations = ocad_separation_count(file); +#if 1 + addWarning(tr("%n color separation(s) were skipped, reason: Import disabled.", "", num_separations)); + num_separations = 0; +#endif + if (num_separations < 0) + { + addWarning(tr("Could not load the spot color definitions, error: %1").arg(num_separations)); + num_separations = 0; + } + separations.reserve(num_separations); + for (int i = 0; i < num_separations; i++) + { + const OCADColorSeparation *ocad_separation = ocad_separation_at(file, i); + MapColor* color = new MapColor(convertPascalString(ocad_separation->sep_name), MapColor::Reserved); + color->setSpotColorName(convertPascalString(ocad_separation->sep_name).toUpper()); + // OCD stores CMYK values as integers from 0-200. + const MapColorCmyk cmyk( + 0.005f * ocad_separation->cyan, + 0.005f * ocad_separation->magenta, + 0.005f * ocad_separation->yellow, + 0.005f * ocad_separation->black ); + color->setCmyk(cmyk); + color->setOpacity(1.0f); + separations.push_back(color); + } + + // Load colors + int num_colors = ocad_color_count(file); + for (int i = 0; i < num_colors; i++) + { + OCADColor *ocad_color = ocad_color_at(file, i); + MapColor* color = new MapColor(convertPascalString(ocad_color->name), map->color_set->colors.size()); + // OCD stores CMYK values as integers from 0-200. + MapColorCmyk cmyk( + 0.005f * ocad_color->cyan, + 0.005f * ocad_color->magenta, + 0.005f * ocad_color->yellow, + 0.005f * ocad_color->black ); + color->setCmyk(cmyk); + color->setOpacity(1.0f); + + SpotColorComponents components; + for (int j = 0; j < num_separations; ++j) + { + const u8& ocad_halftone = ocad_color->spot[j]; + if (ocad_halftone <= 200) + { + float halftone = 0.005f * ocad_halftone; + components.reserve(std::size_t(num_separations)); // reserves only once for same capacity + components.push_back(SpotColorComponent(separations[j], halftone)); // clazy:exclude=reserve-candidates + } + } + if (!components.empty()) + { + color->setSpotColorComposition(components); + const MapColorCmyk cmyk(color->getCmyk()); + color->setCmykFromSpotColors(); + if (cmyk != color->getCmyk()) + // The color's CMYK was customized. + color->setCmyk(cmyk); + } + + if (i == 0 && color->isBlack() && color->getName() == QLatin1String("Registration black") + && XMLFileFormat::active_version >= 6 ) + { + delete color; color = nullptr; + color_index[ocad_color->number] = Map::getRegistrationColor(); + addWarning(tr("Color \"Registration black\" is imported as a special color.")); + // NOTE: This does not make a difference in output + // as long as no spot colors are created, + // but as a special color, it is protected from modification, + // and it will be saved as number 0 in OCD export. + } + else + { + map->color_set->colors.push_back(color); + color_index[ocad_color->number] = color; + } + } + + // Insert the spot colors into the map + for (int i = 0; i < num_separations; ++i) + { + map->addColor(separations[i], map->color_set->colors.size()); + } + + // Load symbols + for (OCADSymbolIndex *idx = ocad_symidx_first(file); idx; idx = ocad_symidx_next(file, idx)) + { + for (int i = 0; i < 256; i++) + { + OCADSymbol *ocad_symbol = ocad_symbol_at(file, idx, i); + if (ocad_symbol && ocad_symbol->number != 0) + { + Symbol *symbol = nullptr; + if (ocad_symbol->type == OCAD_POINT_SYMBOL) + { + symbol = importPointSymbol((OCADPointSymbol *)ocad_symbol); + } + else if (ocad_symbol->type == OCAD_LINE_SYMBOL) + { + symbol = importLineSymbol((OCADLineSymbol *)ocad_symbol); + } + else if (ocad_symbol->type == OCAD_AREA_SYMBOL) + { + symbol = importAreaSymbol((OCADAreaSymbol *)ocad_symbol); + } + else if (ocad_symbol->type == OCAD_TEXT_SYMBOL) + { + symbol = importTextSymbol((OCADTextSymbol *)ocad_symbol); + } + else if (ocad_symbol->type == OCAD_RECT_SYMBOL) + { + RectangleInfo* rect = importRectSymbol((OCADRectSymbol *)ocad_symbol); + map->symbols.push_back(rect->border_line); + if (rect->has_grid) + { + map->symbols.push_back(rect->inner_line); + map->symbols.push_back(rect->text); + } + continue; + } + + + if (symbol) + { + map->symbols.push_back(symbol); + symbol_index[ocad_symbol->number] = symbol; + } + else + { + addWarning(tr("Unable to import symbol \"%3\" (%1.%2)") + .arg(ocad_symbol->number / 10).arg(ocad_symbol->number % 10) + .arg(convertPascalString(ocad_symbol->name))); + } + } + } + } + + if (!load_symbols_only) + { + // Load objects + + // Place all objects into a single OCAD import part + MapPart* part = new MapPart(tr("OCAD import layer"), map); + for (OCADObjectIndex *idx = ocad_objidx_first(file); idx; idx = ocad_objidx_next(file, idx)) + { + for (int i = 0; i < 256; i++) + { + OCADObjectEntry *entry = ocad_object_entry_at(file, idx, i); + OCADObject *ocad_obj = ocad_object(file, entry); + if (ocad_obj) + { + Object *object = importObject(ocad_obj, part); + if (object) { + part->objects.push_back(object); + } + } + } + } + delete map->parts[0]; + map->parts[0] = part; + map->current_part_index = 0; + + // Load templates + map->templates.clear(); + for (OCADStringIndex *idx = ocad_string_index_first(file); idx; idx = ocad_string_index_next(file, idx)) + { + for (int i = 0; i < 256; i++) + { + OCADStringEntry *entry = ocad_string_entry_at(file, idx, i); + if (entry->type != 0 && entry->size > 0) + importString(entry); + } + } + map->first_front_template = map->templates.size(); // Templates in front of the map are not supported by OCD + + // Fill view with relevant fields from OCD file + if (view) + { + if (file->setup->zoom >= MapView::zoom_out_limit && file->setup->zoom <= MapView::zoom_in_limit) + view->setZoom(file->setup->zoom); + + s32 buf[3]; + ocad_point(buf, &file->setup->center); + MapCoord center_pos; + convertPoint(center_pos, buf[0], buf[1]); + view->setCenter(center_pos); + } + + // TODO: read template visibilities + /* + int num_template_visibilities; + file->read((char*)&num_template_visibilities, sizeof(int)); + + for (int i = 0; i < num_template_visibilities; ++i) + { + int pos; + file->read((char*)&pos, sizeof(int)); + + TemplateVisibility* vis = getTemplateVisibility(map->getTemplate(pos)); + file->read((char*)&vis->visible, sizeof(bool)); + file->read((char*)&vis->opacity, sizeof(float)); + } + } + */ + + // Undo steps are not supported in OCAD + } + + ocad_file_close(file); + + //qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - start; + //qDebug() << "OCAD map imported:"<getNumSymbols()<<"symbols and"<getNumObjects()<<"objects in"<currentMapPartIndexChanged(map->current_part_index); + emit map->currentMapPartChanged(map->getPart(map->current_part_index)); +} + +void OCAD8FileImport::setStringEncodings(const char *narrow, const char *wide) { + encoding_1byte = QTextCodec::codecForName(narrow); + encoding_2byte = QTextCodec::codecForName(wide); +} + +Symbol *OCAD8FileImport::importPointSymbol(const OCADPointSymbol *ocad_symbol) +{ + PointSymbol *symbol = importPattern(ocad_symbol->ngrp, (OCADPoint *)ocad_symbol->pts); + fillCommonSymbolFields(symbol, (OCADSymbol *)ocad_symbol); + symbol->setRotatable(ocad_symbol->base_flags & 1); + + return symbol; +} + +Symbol *OCAD8FileImport::importLineSymbol(const OCADLineSymbol *ocad_symbol) +{ + LineSymbol* line_for_borders = nullptr; + + // Import a main line? + LineSymbol* main_line = nullptr; + if (ocad_symbol->dmode == 0 || ocad_symbol->width > 0) + { + main_line = new LineSymbol(); + line_for_borders = main_line; + fillCommonSymbolFields(main_line, (OCADSymbol *)ocad_symbol); + + // Basic line options + main_line->line_width = convertSize(ocad_symbol->width); + main_line->color = convertColor(ocad_symbol->color); + + // Cap and join styles + if (ocad_symbol->ends == 0) + { + main_line->cap_style = LineSymbol::FlatCap; + main_line->join_style = LineSymbol::BevelJoin; + } + else if (ocad_symbol->ends == 1) + { + main_line->cap_style = LineSymbol::RoundCap; + main_line->join_style = LineSymbol::RoundJoin; + } + else if (ocad_symbol->ends == 2) + { + main_line->cap_style = LineSymbol::PointedCap; + main_line->join_style = LineSymbol::BevelJoin; + } + else if (ocad_symbol->ends == 3) + { + main_line->cap_style = LineSymbol::PointedCap; + main_line->join_style = LineSymbol::RoundJoin; + } + else if (ocad_symbol->ends == 4) + { + main_line->cap_style = LineSymbol::FlatCap; + main_line->join_style = LineSymbol::MiterJoin; + } + else if (ocad_symbol->ends == 6) + { + main_line->cap_style = LineSymbol::PointedCap; + main_line->join_style = LineSymbol::MiterJoin; + } + + if (main_line->cap_style == LineSymbol::PointedCap) + { + if (ocad_symbol->bdist != ocad_symbol->edist) + addWarning(tr("In dashed line symbol %1, pointed cap lengths for begin and end are different (%2 and %3). Using %4.") + .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->bdist).arg(ocad_symbol->edist).arg((ocad_symbol->bdist + ocad_symbol->edist) / 2)); + main_line->pointed_cap_length = convertSize((ocad_symbol->bdist + ocad_symbol->edist) / 2); // FIXME: Different lengths for start and end length of pointed line ends are not supported yet, so take the average + main_line->join_style = LineSymbol::RoundJoin; // NOTE: while the setting may be different (see what is set in the first place), OCAD always draws round joins if the line cap is pointed! + } + + // Handle the dash pattern + if( ocad_symbol->gap > 0 || ocad_symbol->gap2 > 0 ) + { + main_line->dashed = true; + + // Detect special case + if (ocad_symbol->gap2 > 0 && ocad_symbol->gap == 0) + { + main_line->dash_length = convertSize(ocad_symbol->len - ocad_symbol->gap2); + main_line->break_length = convertSize(ocad_symbol->gap2); + if (!(ocad_symbol->elen >= ocad_symbol->len / 2 - 1 && ocad_symbol->elen <= ocad_symbol->len / 2 + 1)) + addWarning(tr("In dashed line symbol %1, the end length cannot be imported correctly.").arg(0.1 * ocad_symbol->number)); + if (ocad_symbol->egap != 0) + addWarning(tr("In dashed line symbol %1, the end gap cannot be imported correctly.").arg(0.1 * ocad_symbol->number)); + } + else + { + if (ocad_symbol->len != ocad_symbol->elen) + { + if (ocad_symbol->elen >= ocad_symbol->len / 2 - 1 && ocad_symbol->elen <= ocad_symbol->len / 2 + 1) + main_line->half_outer_dashes = true; + else + addWarning(tr("In dashed line symbol %1, main and end length are different (%2 and %3). Using %4.") + .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->len).arg(ocad_symbol->elen).arg(ocad_symbol->len)); + } + + main_line->dash_length = convertSize(ocad_symbol->len); + main_line->break_length = convertSize(ocad_symbol->gap); + if (ocad_symbol->gap2 > 0) + { + main_line->dashes_in_group = 2; + if (ocad_symbol->gap2 != ocad_symbol->egap) + addWarning(tr("In dashed line symbol %1, gaps D and E are different (%2 and %3). Using %4.") + .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->gap2).arg(ocad_symbol->egap).arg(ocad_symbol->gap2)); + main_line->in_group_break_length = convertSize(ocad_symbol->gap2); + main_line->dash_length = (main_line->dash_length - main_line->in_group_break_length) / 2; + } + } + } + else + { + main_line->segment_length = convertSize(ocad_symbol->len); + main_line->end_length = convertSize(ocad_symbol->elen); + } + } + + // Import a 'framing' line? + LineSymbol* framing_line = nullptr; + if (ocad_symbol->fwidth > 0) + { + framing_line = new LineSymbol(); + if (!line_for_borders) + line_for_borders = framing_line; + fillCommonSymbolFields(framing_line, (OCADSymbol *)ocad_symbol); + + // Basic line options + framing_line->line_width = convertSize(ocad_symbol->fwidth); + framing_line->color = convertColor(ocad_symbol->fcolor); + + // Cap and join styles + if (ocad_symbol->fstyle == 0) + { + framing_line->cap_style = LineSymbol::FlatCap; + framing_line->join_style = LineSymbol::BevelJoin; + } + else if (ocad_symbol->fstyle == 1) + { + framing_line->cap_style = LineSymbol::RoundCap; + framing_line->join_style = LineSymbol::RoundJoin; + } + else if (ocad_symbol->fstyle == 4) + { + framing_line->cap_style = LineSymbol::FlatCap; + framing_line->join_style = LineSymbol::MiterJoin; + } + } + + // Import a 'double' line? + bool has_border_line = ocad_symbol->lwidth > 0 || ocad_symbol->rwidth > 0; + LineSymbol *double_line = nullptr; + if (ocad_symbol->dmode != 0 && (ocad_symbol->dflags & 1 || (has_border_line && !line_for_borders))) + { + double_line = new LineSymbol(); + line_for_borders = double_line; + fillCommonSymbolFields(double_line, (OCADSymbol *)ocad_symbol); + + double_line->line_width = convertSize(ocad_symbol->dwidth); + if (ocad_symbol->dflags & 1) + double_line->color = convertColor(ocad_symbol->dcolor); + else + double_line->color = nullptr; + + double_line->cap_style = LineSymbol::FlatCap; + double_line->join_style = LineSymbol::MiterJoin; + + double_line->segment_length = convertSize(ocad_symbol->len); + double_line->end_length = convertSize(ocad_symbol->elen); + } + + // Border lines + if (has_border_line) + { + Q_ASSERT(line_for_borders); + line_for_borders->have_border_lines = true; + LineSymbolBorder& border = line_for_borders->getBorder(); + LineSymbolBorder& right_border = line_for_borders->getRightBorder(); + + // Border color and width + border.color = convertColor(ocad_symbol->lcolor); + border.width = convertSize(ocad_symbol->lwidth); + border.shift = convertSize(ocad_symbol->lwidth) / 2 + (convertSize(ocad_symbol->dwidth) - line_for_borders->line_width) / 2; + + right_border.color = convertColor(ocad_symbol->rcolor); + right_border.width = convertSize(ocad_symbol->rwidth); + right_border.shift = convertSize(ocad_symbol->rwidth) / 2 + (convertSize(ocad_symbol->dwidth) - line_for_borders->line_width) / 2; + + // The borders may be dashed + if (ocad_symbol->dgap > 0 && ocad_symbol->dmode > 1) + { + border.dashed = true; + border.dash_length = convertSize(ocad_symbol->dlen); + border.break_length = convertSize(ocad_symbol->dgap); + + // If ocad_symbol->dmode == 2, only the left border should be dashed + if (ocad_symbol->dmode > 2) + { + right_border.dashed = border.dashed; + right_border.dash_length = border.dash_length; + right_border.break_length = border.break_length; + } + } + } + + // Create point symbols along line; middle ("normal") dash, corners, start, and end. + LineSymbol* symbol_line = main_line ? main_line : double_line; // Find the line to attach the symbols to + if (!symbol_line) + { + main_line = new LineSymbol(); + symbol_line = main_line; + fillCommonSymbolFields(main_line, (OCADSymbol *)ocad_symbol); + + main_line->segment_length = convertSize(ocad_symbol->len); + main_line->end_length = convertSize(ocad_symbol->elen); + } + OCADPoint * symbolptr = (OCADPoint *)ocad_symbol->pts; + symbol_line->mid_symbol = importPattern( ocad_symbol->smnpts, symbolptr); + symbol_line->mid_symbols_per_spot = ocad_symbol->snum; + symbol_line->mid_symbol_distance = convertSize(ocad_symbol->sdist); + symbolptr += ocad_symbol->smnpts; + if( ocad_symbol->ssnpts > 0 ) + { + //symbol_line->dash_symbol = importPattern( ocad_symbol->ssnpts, symbolptr); + symbolptr += ocad_symbol->ssnpts; + } + if( ocad_symbol->scnpts > 0 ) + { + symbol_line->dash_symbol = importPattern( ocad_symbol->scnpts, symbolptr); + symbol_line->dash_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Dash symbol")); + symbolptr += ocad_symbol->scnpts; + } + if( ocad_symbol->sbnpts > 0 ) + { + symbol_line->start_symbol = importPattern( ocad_symbol->sbnpts, symbolptr); + symbol_line->start_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Start symbol")); + symbolptr += ocad_symbol->sbnpts; + } + if( ocad_symbol->senpts > 0 ) + { + symbol_line->end_symbol = importPattern( ocad_symbol->senpts, symbolptr); + } + // FIXME: not really sure how this translates... need test cases + symbol_line->minimum_mid_symbol_count = 0; //1 + ocad_symbol->smin; + symbol_line->minimum_mid_symbol_count_when_closed = 0; //1 + ocad_symbol->smin; + symbol_line->show_at_least_one_symbol = false; // NOTE: this works in a different way than OCAD's 'at least X symbols' setting (per-segment instead of per-object) + + // Suppress dash symbol at line ends if both start symbol and end symbol exist, + // but don't create a warning unless a dash symbol is actually defined + // and the line symbol is not Mapper's 799 Simple orienteering course. + if (symbol_line->start_symbol && symbol_line->end_symbol) + { + symbol_line->setSuppressDashSymbolAtLineEnds(true); + if (symbol_line->dash_symbol && symbol_line->getNumberComponent(0) != 799) + addWarning(tr("Line symbol %1: suppressing dash symbol at line ends.").arg(QString::number(0.1 * ocad_symbol->number) + QLatin1Char(' ') + symbol_line->getName())); + } + + // TODO: taper fields (tmode and tlast) + + if (!main_line && !framing_line) + return double_line; + else if (!double_line && !framing_line) + return main_line; + else if (!main_line && !double_line) + return framing_line; + else + { + CombinedSymbol* full_line = new CombinedSymbol(); + fillCommonSymbolFields(full_line, (OCADSymbol *)ocad_symbol); + full_line->setNumParts(3); + int part = 0; + if (main_line) + { + full_line->setPart(part++, main_line, true); + main_line->setHidden(false); + main_line->setProtected(false); + } + if (double_line) + { + full_line->setPart(part++, double_line, true); + double_line->setHidden(false); + double_line->setProtected(false); + } + if (framing_line) + { + full_line->setPart(part++, framing_line, true); + framing_line->setHidden(false); + framing_line->setProtected(false); + } + full_line->setNumParts(part); + return full_line; + } +} + +Symbol *OCAD8FileImport::importAreaSymbol(const OCADAreaSymbol *ocad_symbol) +{ + AreaSymbol *symbol = new AreaSymbol(); + fillCommonSymbolFields(symbol, (OCADSymbol *)ocad_symbol); + + // Basic area symbol fields: minimum_area, color + symbol->minimum_area = 0; + symbol->color = ocad_symbol->fill ? convertColor(ocad_symbol->color) : nullptr; + symbol->patterns.clear(); + AreaSymbol::FillPattern *pat = nullptr; + + // Hatching + if (ocad_symbol->hmode > 0) + { + int n = symbol->patterns.size(); symbol->patterns.resize(n + 1); pat = &(symbol->patterns[n]); + pat->type = AreaSymbol::FillPattern::LinePattern; + pat->angle = convertRotation(ocad_symbol->hangle1); + pat->flags = AreaSymbol::FillPattern::Option::Rotatable; + pat->line_spacing = convertSize(ocad_symbol->hdist + ocad_symbol->hwidth); + pat->line_offset = 0; + pat->line_color = convertColor(ocad_symbol->hcolor); + pat->line_width = convertSize(ocad_symbol->hwidth); + if (ocad_symbol->hmode == 2) + { + // Second hatch, same as the first, just a different angle + symbol->patterns.push_back(*pat); + symbol->patterns.back().angle = convertRotation(ocad_symbol->hangle2); + } + } + + if (ocad_symbol->pmode > 0) + { + // OCAD 8 has a "staggered" pattern mode, where successive rows are shifted width/2 relative + // to each other. We need to simulate this in Mapper with two overlapping patterns, each with + // twice the height. The second is then offset by width/2, height/2. + auto spacing = convertSize(ocad_symbol->pheight); + if (ocad_symbol->pmode == 2) spacing *= 2; + int n = symbol->patterns.size(); symbol->patterns.resize(n + 1); pat = &(symbol->patterns[n]); + pat->type = AreaSymbol::FillPattern::PointPattern; + pat->angle = convertRotation(ocad_symbol->pangle); + pat->flags = AreaSymbol::FillPattern::Option::Rotatable; + pat->point_distance = convertSize(ocad_symbol->pwidth); + pat->line_spacing = spacing; + pat->line_offset = 0; + pat->offset_along_line = 0; + // FIXME: somebody needs to own this symbol and be responsible for deleting it + // Right now it looks like a potential memory leak + pat->point = importPattern(ocad_symbol->npts, (OCADPoint *)ocad_symbol->pts); + if (ocad_symbol->pmode == 2) + { + int n = symbol->patterns.size(); symbol->patterns.resize(n + 1); pat = &(symbol->patterns[n]); + pat->type = AreaSymbol::FillPattern::PointPattern; + pat->angle = convertRotation(ocad_symbol->pangle); + pat->flags = AreaSymbol::FillPattern::Option::Rotatable; + pat->point_distance = convertSize(ocad_symbol->pwidth); + pat->line_spacing = spacing; + pat->line_offset = pat->line_spacing / 2; + pat->offset_along_line = pat->point_distance / 2; + pat->point = importPattern(ocad_symbol->npts, (OCADPoint *)ocad_symbol->pts); + } + } + + return symbol; +} + +Symbol *OCAD8FileImport::importTextSymbol(const OCADTextSymbol *ocad_symbol) +{ + TextSymbol *symbol = new TextSymbol(); + fillCommonSymbolFields(symbol, (OCADSymbol *)ocad_symbol); + + symbol->font_family = convertPascalString(ocad_symbol->font); // FIXME: font mapping? + symbol->color = convertColor(ocad_symbol->color); + double d_font_size = (0.1 * ocad_symbol->dpts) / 72.0 * 25.4; + symbol->font_size = qRound(1000 * d_font_size); + symbol->bold = (ocad_symbol->bold >= 550) ? true : false; + symbol->italic = (ocad_symbol->italic) ? true : false; + symbol->underline = false; + symbol->paragraph_spacing = convertSize(ocad_symbol->pspace); + symbol->character_spacing = ocad_symbol->cspace / 100.0; + symbol->kerning = false; + symbol->line_below = ocad_symbol->under; + symbol->line_below_color = convertColor(ocad_symbol->ucolor); + symbol->line_below_width = convertSize(ocad_symbol->uwidth); + symbol->line_below_distance = convertSize(ocad_symbol->udist); + symbol->custom_tabs.resize(ocad_symbol->ntabs); + for (int i = 0; i < ocad_symbol->ntabs; ++i) + symbol->custom_tabs[i] = convertSize(ocad_symbol->tab[i]); + + int halign = (int)TextObject::AlignHCenter; + if (ocad_symbol->halign == 0) + halign = (int)TextObject::AlignLeft; + else if (ocad_symbol->halign == 1) + halign = (int)TextObject::AlignHCenter; + else if (ocad_symbol->halign == 2) + halign = (int)TextObject::AlignRight; + else if (ocad_symbol->halign == 3) + { + // TODO: implement justified alignment + addWarning(tr("During import of text symbol %1: ignoring justified alignment").arg(0.1 * ocad_symbol->number)); + } + text_halign_map[symbol] = halign; + + if (ocad_symbol->bold != 400 && ocad_symbol->bold != 700) + { + addWarning(tr("During import of text symbol %1: ignoring custom weight (%2)") + .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->bold)); + } + if (ocad_symbol->cspace != 0) + { + addWarning(tr("During import of text symbol %1: custom character spacing is set, its implementation does not match OCAD's behavior yet") + .arg(0.1 * ocad_symbol->number)); + } + if (ocad_symbol->wspace != 100) + { + addWarning(tr("During import of text symbol %1: ignoring custom word spacing (%2%)") + .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->wspace)); + } + if (ocad_symbol->indent1 != 0 || ocad_symbol->indent2 != 0) + { + addWarning(tr("During import of text symbol %1: ignoring custom indents (%2/%3)") + .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->indent1).arg(ocad_symbol->indent2)); + } + + if (ocad_symbol->fmode > 0) + { + symbol->framing = true; + symbol->framing_color = convertColor(ocad_symbol->fcolor); + if (ocad_symbol->fmode == 1) + { + symbol->framing_mode = TextSymbol::ShadowFraming; + symbol->framing_shadow_x_offset = convertSize(ocad_symbol->fdx); + symbol->framing_shadow_y_offset = -1 * convertSize(ocad_symbol->fdy); + } + else if (ocad_symbol->fmode == 2) + { + symbol->framing_mode = TextSymbol::LineFraming; + symbol->framing_line_half_width = convertSize(ocad_symbol->fdpts); + } + else + { + addWarning(tr("During import of text symbol %1: ignoring text framing (mode %2)") + .arg(0.1 * ocad_symbol->number).arg(ocad_symbol->fmode)); + } + } + + symbol->updateQFont(); + + // Convert line spacing + double absolute_line_spacing = d_font_size * 0.01 * ocad_symbol->lspace; + symbol->line_spacing = absolute_line_spacing / (symbol->getFontMetrics().lineSpacing() / symbol->calculateInternalScaling()); + + return symbol; +} +OCAD8FileImport::RectangleInfo* OCAD8FileImport::importRectSymbol(const OCADRectSymbol* ocad_symbol) +{ + RectangleInfo rect; + rect.border_line = new LineSymbol(); + fillCommonSymbolFields(rect.border_line, (OCADSymbol *)ocad_symbol); + rect.border_line->line_width = convertSize(ocad_symbol->width); + rect.border_line->color = convertColor(ocad_symbol->color); + rect.border_line->cap_style = LineSymbol::FlatCap; + rect.border_line->join_style = LineSymbol::RoundJoin; + rect.corner_radius = 0.001 * convertSize(ocad_symbol->corner); + rect.has_grid = ocad_symbol->flags & 1; + + if (rect.has_grid) + { + rect.inner_line = new LineSymbol(); + fillCommonSymbolFields(rect.inner_line, (OCADSymbol *)ocad_symbol); + rect.inner_line->setNumberComponent(2, 1); + rect.inner_line->line_width = qRound(1000 * 0.15); + rect.inner_line->color = rect.border_line->color; + + rect.text = new TextSymbol(); + fillCommonSymbolFields(rect.text, (OCADSymbol *)ocad_symbol); + rect.text->setNumberComponent(2, 2); + rect.text->font_family = QString::fromLatin1("Arial"); + rect.text->font_size = qRound(1000 * (15 / 72.0 * 25.4)); + rect.text->color = rect.border_line->color; + rect.text->bold = true; + rect.text->updateQFont(); + + rect.number_from_bottom = ocad_symbol->flags & 2; + rect.cell_width = 0.001 * convertSize(ocad_symbol->cwidth); + rect.cell_height = 0.001 * convertSize(ocad_symbol->cheight); + rect.unnumbered_cells = ocad_symbol->gcells; + rect.unnumbered_text = convertPascalString(ocad_symbol->gtext); + } + + return &rectangle_info.insert(ocad_symbol->number, rect).value(); +} + +PointSymbol *OCAD8FileImport::importPattern(s16 npts, OCADPoint *pts) +{ + PointSymbol *symbol = new PointSymbol(); + symbol->rotatable = true; + OCADPoint *p = pts, *end = pts + npts; + while (p < end) { + OCADSymbolElement *elt = (OCADSymbolElement *)p; + int element_index = symbol->getNumElements(); + bool multiple_elements = p + (2 + elt->npts) < end || p > pts; + if (elt->type == OCAD_DOT_ELEMENT) + { + int inner_radius = (int)convertSize(elt->diameter) / 2; + if (inner_radius > 0) + { + PointSymbol* element_symbol = multiple_elements ? (new PointSymbol()) : symbol; + element_symbol->inner_color = convertColor(elt->color); + element_symbol->inner_radius = inner_radius; + element_symbol->outer_color = nullptr; + element_symbol->outer_width = 0; + if (multiple_elements) + { + element_symbol->rotatable = false; + PointObject* element_object = new PointObject(element_symbol); + element_object->coords.resize(1); + symbol->addElement(element_index, element_object, element_symbol); + } + } + } + else if (elt->type == OCAD_CIRCLE_ELEMENT) + { + int inner_radius = (int)convertSize(elt->diameter) / 2 - (int)convertSize(elt->width); + int outer_width = (int)convertSize(elt->width); + if (outer_width > 0 && inner_radius > 0) + { + PointSymbol* element_symbol = (multiple_elements) ? (new PointSymbol()) : symbol; + element_symbol->inner_color = nullptr; + element_symbol->inner_radius = inner_radius; + element_symbol->outer_color = convertColor(elt->color); + element_symbol->outer_width = outer_width; + if (multiple_elements) + { + element_symbol->rotatable = false; + PointObject* element_object = new PointObject(element_symbol); + element_object->coords.resize(1); + symbol->addElement(element_index, element_object, element_symbol); + } + } + } + else if (elt->type == OCAD_LINE_ELEMENT) + { + LineSymbol* element_symbol = new LineSymbol(); + element_symbol->line_width = convertSize(elt->width); + element_symbol->color = convertColor(elt->color); + PathObject* element_object = new PathObject(element_symbol); + fillPathCoords(element_object, false, elt->npts, elt->pts); + element_object->recalculateParts(); + symbol->addElement(element_index, element_object, element_symbol); + } + else if (elt->type == OCAD_AREA_ELEMENT) + { + AreaSymbol* element_symbol = new AreaSymbol(); + element_symbol->color = convertColor(elt->color); + PathObject* element_object = new PathObject(element_symbol); + fillPathCoords(element_object, true, elt->npts, elt->pts); + element_object->recalculateParts(); + symbol->addElement(element_index, element_object, element_symbol); + } + p += (2 + elt->npts); + } + return symbol; +} + + +void OCAD8FileImport::fillCommonSymbolFields(Symbol *symbol, const OCADSymbol *ocad_symbol) +{ + // common fields are name, number, description, helper_symbol, hidden/protected status + symbol->setName(convertPascalString(ocad_symbol->name)); + symbol->setNumberComponent(0, ocad_symbol->number / 10); + symbol->setNumberComponent(1, ocad_symbol->number % 10); + symbol->setNumberComponent(2, -1); + symbol->setIsHelperSymbol(false); // no such thing in OCAD + if (ocad_symbol->status & 1) + symbol->setProtected(true); + if (ocad_symbol->status & 2) + symbol->setHidden(true); +} + +Object *OCAD8FileImport::importObject(const OCADObject* ocad_object, MapPart* part) +{ + Symbol* symbol; + if (!symbol_index.contains(ocad_object->symbol)) + { + if (!rectangle_info.contains(ocad_object->symbol)) + { + if (ocad_object->type == 1) + symbol = map->getUndefinedPoint(); + else if (ocad_object->type == 2 || ocad_object->type == 3) + symbol = map->getUndefinedLine(); + else if (ocad_object->type == 4 || ocad_object->type == 5) + symbol = map->getUndefinedText(); + else + { + addWarning(tr("Unable to load object")); + return nullptr; + } + } + else + { + if (!importRectangleObject(ocad_object, part, rectangle_info[ocad_object->symbol])) + addWarning(tr("Unable to import rectangle object")); + return nullptr; + } + } + else + symbol = symbol_index[ocad_object->symbol]; + + if (symbol->getType() == Symbol::Point) + { + PointObject *p = new PointObject(); + p->symbol = symbol; + + // extra properties: rotation + PointSymbol* point_symbol = reinterpret_cast(symbol); + if (point_symbol->isRotatable()) + p->setRotation(convertRotation(ocad_object->angle)); + else if (ocad_object->angle != 0) + { + if (!point_symbol->isSymmetrical()) + { + point_symbol->setRotatable(true); + p->setRotation(convertRotation(ocad_object->angle)); + } + } + + // only 1 coordinate is allowed, enforce it even if the OCAD object claims more. + fillPathCoords(p, false, 1, ocad_object->pts); + p->setMap(map); + return p; + } + else if (symbol->getType() == Symbol::Text) + { + TextObject *t = new TextObject(symbol); + + // extra properties: rotation, horizontalAlignment, verticalAlignment, text + t->setRotation(convertRotation(ocad_object->angle)); + t->setHorizontalAlignment((TextObject::HorizontalAlignment)text_halign_map.value(symbol)); + t->setVerticalAlignment(TextObject::AlignBaseline); + + const char *text_ptr = (const char *)(ocad_object->pts + ocad_object->npts); + std::size_t text_len = sizeof(OCADPoint) * ocad_object->ntext; + if (ocad_object->unicode) t->setText(convertWideCString(text_ptr, text_len, true)); + else t->setText(convertCString(text_ptr, text_len, true)); + + // Text objects need special path translation + if (!fillTextPathCoords(t, reinterpret_cast(symbol), ocad_object->npts, (OCADPoint *)ocad_object->pts)) + { + addWarning(tr("Not importing text symbol, couldn't figure out path' (npts=%1): %2") + .arg(ocad_object->npts).arg(t->getText())); + delete t; + return nullptr; + } + t->setMap(map); + return t; + } + else if (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined) { + PathObject *p = new PathObject(symbol); + + p->setPatternRotation(convertRotation(ocad_object->angle)); + + // Normal path + fillPathCoords(p, symbol->getType() == Symbol::Area, ocad_object->npts, ocad_object->pts); + p->recalculateParts(); + p->setMap(map); + return p; + } + + return nullptr; +} + +bool OCAD8FileImport::importRectangleObject(const OCADObject* ocad_object, MapPart* part, const OCAD8FileImport::RectangleInfo& rect) +{ + if (ocad_object->npts != 4) + return false; + + // Convert corner points +#ifdef Q_CC_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warray-bounds" +#endif + s32 buf[3]; + ocad_point(buf, &(ocad_object->pts[3])); + MapCoord top_left; + convertPoint(top_left, buf[0], buf[1]); + ocad_point(buf, &(ocad_object->pts[0])); + MapCoord bottom_left; + convertPoint(bottom_left, buf[0], buf[1]); + ocad_point(buf, &(ocad_object->pts[2])); + MapCoord top_right; + convertPoint(top_right, buf[0], buf[1]); + ocad_point(buf, &(ocad_object->pts[1])); + MapCoord bottom_right; + convertPoint(bottom_right, buf[0], buf[1]); +#ifdef Q_CC_CLANG +#pragma clang diagnostic pop +#endif + + MapCoordF top_left_f = MapCoordF(top_left); + MapCoordF top_right_f = MapCoordF(top_right); + MapCoordF bottom_left_f = MapCoordF(bottom_left); + MapCoordF bottom_right_f = MapCoordF(bottom_right); + MapCoordF right = MapCoordF(top_right.x() - top_left.x(), top_right.y() - top_left.y()); + double angle = right.angle(); + MapCoordF down = MapCoordF(bottom_left.x() - top_left.x(), bottom_left.y() - top_left.y()); + right.normalize(); + down.normalize(); + + // Create border line + MapCoordVector coords; + if (rect.corner_radius == 0) + { + coords.emplace_back(top_left); + coords.emplace_back(top_right); + coords.emplace_back(bottom_right); + coords.emplace_back(bottom_left); + } + else + { + double handle_radius = (1 - BEZIER_KAPPA) * rect.corner_radius; + coords.emplace_back(top_right_f - right * rect.corner_radius, MapCoord::CurveStart); + coords.emplace_back(top_right_f - right * handle_radius); + coords.emplace_back(top_right_f + down * handle_radius); + coords.emplace_back(top_right_f + down * rect.corner_radius); + coords.emplace_back(bottom_right_f - down * rect.corner_radius, MapCoord::CurveStart); + coords.emplace_back(bottom_right_f - down * handle_radius); + coords.emplace_back(bottom_right_f - right * handle_radius); + coords.emplace_back(bottom_right_f - right * rect.corner_radius); + coords.emplace_back(bottom_left_f + right * rect.corner_radius, MapCoord::CurveStart); + coords.emplace_back(bottom_left_f + right * handle_radius); + coords.emplace_back(bottom_left_f - down * handle_radius); + coords.emplace_back(bottom_left_f - down * rect.corner_radius); + coords.emplace_back(top_left_f + down * rect.corner_radius, MapCoord::CurveStart); + coords.emplace_back(top_left_f + down * handle_radius); + coords.emplace_back(top_left_f + right * handle_radius); + coords.emplace_back(top_left_f + right * rect.corner_radius); + } + PathObject *border_path = new PathObject(rect.border_line, coords, map); + border_path->parts().front().setClosed(true, false); + part->objects.push_back(border_path); + + if (rect.has_grid && rect.cell_width > 0 && rect.cell_height > 0) + { + // Calculate grid sizes + double width = top_left.distanceTo(top_right); + double height = top_left.distanceTo(bottom_left); + int num_cells_x = qMax(1, qRound(width / rect.cell_width)); + int num_cells_y = qMax(1, qRound(height / rect.cell_height)); + + float cell_width = width / num_cells_x; + float cell_height = height / num_cells_y; + + // Create grid lines + coords.resize(2); + for (int x = 1; x < num_cells_x; ++x) + { + coords[0] = MapCoord(top_left_f + x * cell_width * right); + coords[1] = MapCoord(bottom_left_f + x * cell_width * right); + + PathObject *path = new PathObject(rect.inner_line, coords, map); + part->objects.push_back(path); + } + for (int y = 1; y < num_cells_y; ++y) + { + coords[0] = MapCoord(top_left_f + y * cell_height * down); + coords[1] = MapCoord(top_right_f + y * cell_height * down); + + PathObject *path = new PathObject(rect.inner_line, coords, map); + part->objects.push_back(path); + } + + // Create grid text + if (height >= rect.cell_height / 2) + { + for (int y = 0; y < num_cells_y; ++y) + { + for (int x = 0; x < num_cells_x; ++x) + { + int cell_num; + QString cell_text; + + if (rect.number_from_bottom) + cell_num = y * num_cells_x + x + 1; + else + cell_num = (num_cells_y - 1 - y) * num_cells_x + x + 1; + + if (cell_num > num_cells_x * num_cells_y - rect.unnumbered_cells) + cell_text = rect.unnumbered_text; + else + cell_text = QString::number(cell_num); + + TextObject* object = new TextObject(rect.text); + object->setMap(map); + object->setText(cell_text); + object->setRotation(-angle); + object->setHorizontalAlignment(TextObject::AlignLeft); + object->setVerticalAlignment(TextObject::AlignTop); + double position_x = (x + 0.07f) * cell_width; + double position_y = (y + 0.04f) * cell_height + rect.text->getFontMetrics().ascent() / rect.text->calculateInternalScaling() - rect.text->getFontSize(); + object->setAnchorPosition(top_left_f + position_x * right + position_y * down); + part->objects.push_back(object); + + //pts[0].Y -= rectinfo.gridText.FontAscent - rectinfo.gridText.FontEmHeight; + } + } + } + } + + return true; +} + +void OCAD8FileImport::importString(OCADStringEntry *entry) +{ + OCADCString *ocad_str = ocad_string(file, entry); + if (entry->type == 8) + { + // Template + importTemplate(ocad_str); + } + + // TODO: parse more types of strings, maybe the print parameters? +} + +Template *OCAD8FileImport::importTemplate(OCADCString* ocad_str) +{ + Template* templ = nullptr; + QByteArray data(ocad_str->str); // copies the data. + QString filename = encoding_1byte->toUnicode(data.left(data.indexOf('\t', 0))); + QString clean_path = QDir::cleanPath(QString(filename).replace(QLatin1Char('\\'), QLatin1Char('/'))); + QString extension = QFileInfo(clean_path).suffix(); + if (extension.compare(QLatin1String("ocd"), Qt::CaseInsensitive) == 0) + { + templ = new TemplateMap(clean_path, map); + } + else if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) + { + templ = new TemplateImage(clean_path, map); + } + else + { + addWarning(tr("Unable to import template: background \"%1\" doesn't seem to be a raster image").arg(filename)); + return nullptr; + } + + OCADBackground background = importBackground(data); + MapCoord c; + convertPoint(c, background.trnx, background.trny); + templ->setTemplatePosition(c); + templ->setTemplateRotation(M_PI / 180 * background.angle); + templ->setTemplateScaleX(convertTemplateScale(background.sclx)); + templ->setTemplateScaleY(convertTemplateScale(background.scly)); + + map->templates.insert(map->templates.begin(), templ); + + if (view) + { + auto opacity = qMax(0.0, qMin(1.0, 0.01 * (100 - background.dimming))); + view->setTemplateVisibility(templ, { float(opacity), bool(background.s) }); + } + + return templ; +} + +// A more flexible reimplementation of libocad's ocad_to_background(). +OCADBackground OCAD8FileImport::importBackground(const QByteArray& data) +{ + unsigned int num_angles = 0; + OCADBackground background; + background.filename = data.data(); // tab-terminated, not 0-terminated! + background.trnx = 0; + background.trny = 0; + background.angle = 0.0; + background.sclx = 100.0; + background.scly = 100.0; + background.dimming = 0; + background.s = 1; + + int i = data.indexOf('\t', 0); + while (i >= 0) + { + double value; + bool ok; + int next_i = data.indexOf('\t', i+1); + int len = (next_i > 0 ? next_i : data.length()) - i - 2; + switch (data[i+1]) + { + case 'x': + background.trnx = qRound(data.mid(i + 2, len).toDouble()); + break; + case 'y': + background.trny = qRound(data.mid(i + 2, len).toDouble()); + break; + case 'a': + case 'b': + // TODO: use the distinct angles correctly, not just the average + background.angle += data.mid(i + 2, len).toDouble(&ok); + if (ok) + ++num_angles; + break; + case 'u': + value = data.mid(i + 2, len).toDouble(&ok); + if (ok && qAbs(value) >= 0.0001) + background.sclx = value; + break; + case 'v': + value = data.mid(i + 2, len).toDouble(&ok); + if (ok && qAbs(value) >= 0.0001) + background.scly = value; + break; + case 'd': + background.dimming = data.mid(i + 2, len).toInt(); + break; + case 's': + background.s = data.mid(i + 2, len).toInt(); + break; + default: + ; // nothing + } + i = next_i; + } + + if (num_angles) + background.angle = background.angle / num_angles; + + return background; +} + +Template *OCAD8FileImport::importRasterTemplate(const OCADBackground &background) +{ + QString filename(encoding_1byte->toUnicode(background.filename)); + filename = QDir::cleanPath(filename.replace(QLatin1Char('\\'), QLatin1Char('/'))); + if (isRasterImageFile(filename)) + { + TemplateImage* templ = new TemplateImage(filename, map); + MapCoord c; + convertPoint(c, background.trnx, background.trny); + templ->setTemplateX(c.nativeX()); + templ->setTemplateY(c.nativeY()); + templ->setTemplateRotation(M_PI / 180 * background.angle); + templ->setTemplateScaleX(convertTemplateScale(background.sclx)); + templ->setTemplateScaleY(convertTemplateScale(background.scly)); + // FIXME: import template view parameters: background.dimming and background.transparent + // TODO: import template visibility + return templ; + } + else + { + addWarning(tr("Unable to import template: background \"%1\" doesn't seem to be a raster image").arg(filename)); + } + return nullptr; +} + +void OCAD8FileImport::setPathHolePoint(Object *object, int i) +{ + // Look for curve start points before the current point and apply hole point only if no such point is there. + // This prevents hole points in the middle of a curve caused by incorrect map objects. + if (i >= 1 && object->coords[i].isCurveStart()) + ; //object->coords[i-1].setHolePoint(true); + else if (i >= 2 && object->coords[i-1].isCurveStart()) + ; //object->coords[i-2].setHolePoint(true); + else if (i >= 3 && object->coords[i-2].isCurveStart()) + ; //object->coords[i-3].setHolePoint(true); + else + object->coords[i].setHolePoint(true); +} + +/** Translates the OCAD path given in the last two arguments into an Object. + */ +void OCAD8FileImport::fillPathCoords(Object *object, bool is_area, u16 npts, const OCADPoint *pts) +{ + object->coords.resize(npts); + s32 buf[3]; + for (int i = 0; i < npts; i++) + { + ocad_point(buf, &(pts[i])); + MapCoord &coord = object->coords[i]; + convertPoint(coord, buf[0], buf[1]); + // We can support CurveStart, HolePoint, DashPoint. + // CurveStart needs to be applied to the main point though, not the control point, and + // hole points need to bet set as the last point of a part of an area object instead of the first point of the next part + if (buf[2] & PX_CTL1 && i > 0) + object->coords[i-1].setCurveStart(true); + if ((buf[2] & (PY_DASH << 8)) || (buf[2] & (PY_CORNER << 8))) + coord.setDashPoint(true); + if (buf[2] & (PY_HOLE << 8)) + setPathHolePoint(object, is_area ? (i - 1) : i); + } + + // For path objects, create closed parts where the position of the last point is equal to that of the first point + if (object->getType() == Object::Path) + { + int start = 0; + for (int i = 0; i < (int)object->coords.size(); ++i) + { + if (!object->coords[i].isHolePoint() && i < (int)object->coords.size() - 1) + continue; + + if (object->coords[i].isPositionEqualTo(object->coords[start])) + { + MapCoord coord = object->coords[start]; + coord.setCurveStart(false); + coord.setHolePoint(true); + coord.setClosePoint(true); + object->coords[i] = coord; + } + + start = i + 1; + } + } +} + +/** Translates an OCAD text object path into a Mapper text object specifier, if possible. + * If successful, sets either 1 or 2 coordinates in the text object and returns true. + * If the OCAD path was not importable, leaves the TextObject alone and returns false. + */ +bool OCAD8FileImport::fillTextPathCoords(TextObject *object, TextSymbol *symbol, u16 npts, OCADPoint *pts) +{ + // text objects either have 1 point (free anchor) or 2 (midpoint/size) + // OCAD appears to always have 5 or 4 points (possible single anchor, then 4 corner coordinates going clockwise from anchor). + if (npts == 0) return false; + + if (npts == 4) + { + // Box text + s32 buf[3]; + ocad_point(buf, &(pts[3])); + MapCoord top_left; + convertPoint(top_left, buf[0], buf[1]); + ocad_point(buf, &(pts[0])); + MapCoord bottom_left; + convertPoint(bottom_left, buf[0], buf[1]); + ocad_point(buf, &(pts[2])); + MapCoord top_right; + convertPoint(top_right, buf[0], buf[1]); + + // According to Purple Pen source code: OCAD adds an extra internal leading (incorrectly). + QFontMetricsF metrics = symbol->getFontMetrics(); + double top_adjust = -symbol->getFontSize() + (metrics.ascent() + metrics.descent() + 0.5) / symbol->calculateInternalScaling(); + + MapCoordF adjust_vector = MapCoordF(top_adjust * sin(object->getRotation()), top_adjust * cos(object->getRotation())); + top_left = MapCoord(top_left.x() + adjust_vector.x(), top_left.y() + adjust_vector.y()); + top_right = MapCoord(top_right.x() + adjust_vector.x(), top_right.y() + adjust_vector.y()); + + object->setBox((bottom_left.nativeX() + top_right.nativeX()) / 2, (bottom_left.nativeY() + top_right.nativeY()) / 2, + top_left.distanceTo(top_right), top_left.distanceTo(bottom_left)); + + object->setVerticalAlignment(TextObject::AlignTop); + } + else + { + // Single anchor text + if (npts != 5) + addWarning(tr("Trying to import a text object with unknown coordinate format")); + + s32 buf[3]; + ocad_point(buf, &(pts[0])); // anchor point + + MapCoord coord; + convertPoint(coord, buf[0], buf[1]); + object->setAnchorPosition(coord.nativeX(), coord.nativeY()); + + object->setVerticalAlignment(TextObject::AlignBaseline); + } + + return true; +} + +/** Converts a single-byte-per-character, length-payload string to a QString. + * + * The byte sequence will be: LEN C0 C1 C2 C3... + * + * Obviously this will only hold up to 255 characters. By default, we interpret the + * bytes using Windows-1252, as that's the most likely candidate for OCAD files in + * the wild. + */ +QString OCAD8FileImport::convertPascalString(const char *p) { + int len = *((unsigned char *)p); + return encoding_1byte->toUnicode((p + 1), len); +} + +/** Converts a single-byte-per-character, zero-terminated string to a QString. + * + * The byte sequence will be: C0 C1 C2 C3 ... 00. n describes the maximum + * length (in bytes) that will be scanned for a zero terminator; if none is found, + * the string will be truncated at that location. + */ +QString OCAD8FileImport::convertCString(const char *p, std::size_t n, bool ignore_first_newline) { + size_t i = 0; + for (; i < n; i++) { + if (p[i] == 0) break; + } + if (ignore_first_newline && n >= 2 && p[0] == '\r' && p[1] == '\n') + { + // Remove "\r\n" at the beginning of texts, somehow OCAD seems to add this sometimes but ignores it + p += 2; + i -= 2; + } + return encoding_1byte->toUnicode(p, i); +} + +/** Converts a two-byte-per-character, zero-terminated string to a QString. By default, + * we interpret the bytes using UTF-16LE, as that's the most likely candidate for + * OCAD files in the wild. + * + * The byte sequence will be: L0 H0 L1 H1 L2 H2... 00 00. n describes the maximum + * length (in bytes) that will be scanned for a zero terminator; if none is found, + * the string will be truncated at that location. + */ +QString OCAD8FileImport::convertWideCString(const char *p, std::size_t n, bool ignore_first_newline) { + const u16 *q = (const u16 *)p; + size_t i = 0; + for (; i < n; i++) { + if (q[i] == 0) break; + } + if (ignore_first_newline && n >= 4 && p[0] == '\r' && p[2] == '\n') + { + // Remove "\r\n" at the beginning of texts, somehow OCAD seems to add this sometimes but ignores it + p += 4; + i -= 2; + } + return encoding_2byte->toUnicode(p, i * 2); +} + +float OCAD8FileImport::convertRotation(int angle) { + // OCAD uses tenths of a degree, counterclockwise + // BUG: if sin(rotation) is < 0 for a hatched area pattern, the pattern's createRenderables() will go into an infinite loop. + // So until that's fixed, we keep a between 0 and PI + double a = (M_PI / 180) * (0.1f * angle); + while (a < 0) a += 2 * M_PI; + //if (a < 0 || a > M_PI) qDebug() << "Found angle" << a; + return (float)a; +} + +void OCAD8FileImport::convertPoint(MapCoord &coord, s32 ocad_x, s32 ocad_y) +{ + // Recover from broken coordinate export from Mapper 0.6.2 ... 0.6.4 (#749) + // Cf. broken::convertPointMember below: + // The values -4 ... -1 (-0.004 mm ... -0.001 mm) were converted to 0x80000000u instead of 0. + // This is the maximum value. Thus it is okay to assume it won't occur in regular data, + // and we can safely replace it with 0 here. + // But the input parameter were already subject to right shift in ocad_point ... + constexpr auto invalid_value = s32(0x80000000u) >> 8; // ... so we use this value here. + if (ocad_x == invalid_value) + ocad_x = 0; + if (ocad_y == invalid_value) + ocad_y = 0; + + // OCAD uses hundredths of a millimeter. + // oo-mapper uses 1/1000 mm + coord.setNativeX(offset_x + (qint32)ocad_x * 10); + // Y-axis is flipped. + coord.setNativeY(offset_y + (qint32)ocad_y * (-10)); +} + +qint32 OCAD8FileImport::convertSize(int ocad_size) { + // OCAD uses hundredths of a millimeter. + // oo-mapper uses 1/1000 mm + return ((qint32)ocad_size) * 10; +} + +const MapColor *OCAD8FileImport::convertColor(int color) { + if (!color_index.contains(color)) + { + addWarning(tr("Color id not found: %1, ignoring this color").arg(color)); + return nullptr; + } + else + return color_index[color]; +} + +double OCAD8FileImport::convertTemplateScale(double ocad_scale) +{ + return ocad_scale * 0.01; // millimeters(on map) per pixel +} + + +// ### OCAD8FileExport ### + +OCAD8FileExport::OCAD8FileExport(QIODevice* stream, Map* map, MapView* view) + : Exporter(stream, map, view), + uses_registration_color(false), + file(nullptr) +{ + ocad_init(); + encoding_1byte = QTextCodec::codecForName("Windows-1252"); + encoding_2byte = QTextCodec::codecForName("UTF-16LE"); + + origin_point_object = new PointObject(); +} + +OCAD8FileExport::~OCAD8FileExport() +{ + delete origin_point_object; +} + +void OCAD8FileExport::doExport() +{ + uses_registration_color = map->isColorUsedByASymbol(map->getRegistrationColor()); + if (map->getNumColors() > (uses_registration_color ? 255 : 256)) + throw FileFormatException(tr("The map contains more than 256 colors which is not supported by ocd version 8.")); + + // Create struct in memory + int err = ocad_file_new(&file); + if (err != 0) throw FileFormatException(::OpenOrienteering::Exporter::tr("Could not create new file: %1").arg(tr("libocad returned %1").arg(err))); + + // Check for a neccessary offset (and add related warnings early). + auto area_offset = calculateAreaOffset(); + + // Fill header struct + OCADFileHeader* header = file->header; + *(((u8*)&header->magic) + 0) = 0xAD; + *(((u8*)&header->magic) + 1) = 0x0C; + header->ftype = 2; + header->major = 8; + header->minor = 0; + if (map->getMapNotes().size() > 0) + { + header->infosize = map->getMapNotes().length() + 1; + ocad_file_reserve(file, header->infosize); + header->infopos = &file->buffer[file->size] - file->buffer; + convertCString(map->getMapNotes(), &file->buffer[file->size], header->infosize); + file->size += header->infosize; + } + + // Fill setup struct + OCADSetup* setup = file->setup; + if (view) + { + setup->center = convertPoint(view->center() - area_offset); + setup->zoom = view->getZoom(); + } + else + setup->zoom = 1; + + // Scale and georeferencing parameters + const Georeferencing& georef = map->getGeoreferencing(); + setup->scale = georef.getScaleDenominator(); + const QPointF offset(georef.toProjectedCoords(area_offset)); + setup->offsetx = offset.x(); + setup->offsety = offset.y(); + setup->angle = georef.getGrivation(); + + // TODO: print parameters + + // Colors + int ocad_color_index = 0; + if (uses_registration_color) + { + addWarning(tr("Registration black is exported as a regular color.")); + + ++file->header->ncolors; + OCADColor *ocad_color = ocad_color_at(file, ocad_color_index); + ocad_color->number = ocad_color_index; + + const MapColor* color = Map::getRegistrationColor(); + const MapColorCmyk& cmyk = color->getCmyk(); + ocad_color->cyan = qRound(1 / 0.005f * cmyk.c); + ocad_color->magenta = qRound(1 / 0.005f * cmyk.m); + ocad_color->yellow = qRound(1 / 0.005f * cmyk.y); + ocad_color->black = qRound(1 / 0.005f * cmyk.k); + convertPascalString(QString::fromLatin1("Registration black"), ocad_color->name, 32); // not translated + + ++ocad_color_index; + } + for (int i = 0; i < map->getNumColors(); i++) + { + ++file->header->ncolors; + OCADColor *ocad_color = ocad_color_at(file, ocad_color_index); + ocad_color->number = ocad_color_index; + + const MapColor* color = map->getColor(i); + const MapColorCmyk& cmyk = color->getCmyk(); + ocad_color->cyan = qRound(1 / 0.005f * cmyk.c); + ocad_color->magenta = qRound(1 / 0.005f * cmyk.m); + ocad_color->yellow = qRound(1 / 0.005f * cmyk.y); + ocad_color->black = qRound(1 / 0.005f * cmyk.k); + convertPascalString(color->getName(), ocad_color->name, 32); + + ++ocad_color_index; + } + + // Symbols + for (int i = 0; i < map->getNumSymbols(); ++i) + { + const Symbol* symbol = map->getSymbol(i); + + s16 index = -1; + if (symbol->getType() == Symbol::Point) + index = exportPointSymbol(symbol->asPoint()); + else if (symbol->getType() == Symbol::Line) + index = exportLineSymbol(symbol->asLine()); + else if (symbol->getType() == Symbol::Area) + index = exportAreaSymbol(symbol->asArea()); + else if (symbol->getType() == Symbol::Text) + index = exportTextSymbol(symbol->asText()); + else if (symbol->getType() == Symbol::Combined) + ; // This is done as a second pass to ensure that all dependencies are added to the symbol_index + else + Q_ASSERT(false); + + if (index >= 0) + { + std::set number; + number.insert(index); + symbol_index.insert(symbol, number); + } + } + + // Separate pass for combined symbols + for (int i = 0; i < map->getNumSymbols(); ++i) + { + const Symbol* symbol = map->getSymbol(i); + if (symbol->getType() == Symbol::Combined) + symbol_index.insert(symbol, exportCombinedSymbol(static_cast(symbol))); + } + + // Objects + OCADObject* ocad_object = ocad_object_alloc(nullptr); + for (int l = 0; l < map->getNumParts(); ++l) + { + for (int o = 0; o < map->getPart(l)->getNumObjects(); ++o) + { + memset(ocad_object, 0, sizeof(OCADObject) - sizeof(OCADPoint) + 8 * (ocad_object->npts + ocad_object->ntext)); + Object* object = map->getPart(l)->getObject(o); + std::unique_ptr duplicate; + if (area_offset.nativeX() != 0 || area_offset.nativeY() != 0) + { + // Create a safely managed duplicate and move it as needed. + duplicate.reset(object->duplicate()); + duplicate->move(-area_offset); + object = duplicate.get(); + } + object->update(); + + // Fill some common entries of object struct + OCADPoint* coord_buffer = ocad_object->pts; + if (object->getType() != Object::Text) + ocad_object->npts = exportCoordinates(object->getRawCoordinateVector(), &coord_buffer, object->getSymbol()); + else + ocad_object->npts = exportTextCoordinates(static_cast(object), &coord_buffer); + + if (object->getType() == Object::Point) + { + PointObject* point = static_cast(object); + ocad_object->angle = convertRotation(point->getRotation()); + } + else if (object->getType() == Object::Path) + { + if (object->getSymbol()->getType() == Symbol::Area) + { + PathObject* path = static_cast(object); + // Known issue: In OCD format, pattern rotatability is all + // or nothing. In Mapper, it is an option per pattern. + if (path->getSymbol()->asArea()->hasRotatableFillPattern()) + ocad_object->angle = convertRotation(path->getPatternRotation()); + if (path->getPatternOrigin() != MapCoord(0, 0)) + addWarning(tr("Unable to export fill pattern shift for an area object")); + } + } + else if (object->getType() == Object::Text) + { + TextObject* text = static_cast(object); + ocad_object->unicode = 1; + ocad_object->angle = convertRotation(text->getRotation()); + int num_letters = convertWideCString(text->getText(), (unsigned char*)coord_buffer, 8 * (OCAD_MAX_OBJECT_PTS - ocad_object->npts)); + ocad_object->ntext = qCeil(num_letters / 4.0f); + } + + // Insert an object into the map for every symbol contained in the symbol_index + std::set index_set; + if (symbol_index.contains(object->getSymbol())) + index_set = symbol_index[object->getSymbol()]; + else + index_set.insert(-1); // export as undefined symbol + + for (const auto index : index_set) + { + s16 index_to_use = index; + + // For text objects, check if we have to change / create a new text symbol because of the formatting + if (object->getType() == Object::Text && symbol_index.contains(object->getSymbol())) + { + TextObject* text_object = static_cast(object); + const TextSymbol* text_symbol = static_cast(object->getSymbol()); + if (!text_format_map.contains(text_symbol)) + { + // Adjust the formatting in the first created symbol to this object + OCADTextSymbol* ocad_text_symbol = (OCADTextSymbol*)ocad_symbol(file, index); + setTextSymbolFormatting(ocad_text_symbol, text_object); + + TextFormatList new_list; + new_list.push_back(std::make_pair(text_object->getHorizontalAlignment(), index)); + text_format_map.insert(text_symbol, new_list); + } + else + { + // Check if this formatting has already been created as symbol. + // If yes, use this symbol, else create a new symbol + TextFormatList& format_list = text_format_map[text_symbol]; + bool found = false; + for (size_t i = 0, end = format_list.size(); i < end; ++i) + { + if (format_list[i].first == text_object->getHorizontalAlignment()) + { + index_to_use = format_list[i].second; + found = true; + break; + } + } + if (!found) + { + // Copy the symbol and adjust the formatting + // TODO: insert these symbols directly after the original symbols + OCADTextSymbol* ocad_text_symbol = (OCADTextSymbol*)ocad_symbol(file, index); + OCADTextSymbol* new_symbol = (OCADTextSymbol*)ocad_symbol_new(file, ocad_text_symbol->size); + // Get the pointer to the first symbol again as it might have changed during ocad_symbol_new() + ocad_text_symbol = (OCADTextSymbol*)ocad_symbol(file, index); + + memcpy(new_symbol, ocad_text_symbol, ocad_text_symbol->size); + setTextSymbolFormatting(new_symbol, text_object); + + // Give the new symbol a unique number + while (symbol_numbers.find(new_symbol->number) != symbol_numbers.end()) + ++new_symbol->number; + symbol_numbers.insert(new_symbol->number); + index_to_use = new_symbol->number; + + // Store packed new_symbol->number in separate variable first, + // otherwise when compiling for Android this causes the error: + // cannot bind packed field 'new_symbol->_OCADTextSymbol::number' to 'short int&' + s16 new_symbol_number = new_symbol->number; + format_list.push_back(std::make_pair(text_object->getHorizontalAlignment(), new_symbol_number)); + } + } + } + + ocad_object->symbol = index_to_use; + if (object->getType() == Object::Point) + ocad_object->type = 1; + else if (object->getType() == Object::Path) + { + OCADSymbol* ocad_sym = ocad_symbol(file, index_to_use); + if (!ocad_sym) + ocad_object->type = 2; // This case is for undefined lines; TODO: make another case for undefined areas, as soon as they are implemented + else if (ocad_sym->type == 2) + ocad_object->type = 2; // Line + else //if (ocad_symbol->type == 3) + ocad_object->type = 3; // Area + } + else if (object->getType() == Object::Text) + { + TextObject* text_object = static_cast(object); + if (text_object->hasSingleAnchor()) + ocad_object->type = 4; + else + ocad_object->type = 5; + } + + OCADObjectEntry* entry; + ocad_object_add(file, ocad_object, &entry); + // This is done internally by libocad (in a slightly more imprecise way using the extent specified in the symbol) + //entry->rect.min = convertPoint(MapCoord(object->getExtent().topLeft())); + //entry->rect.max = convertPoint(MapCoord(object->getExtent().bottomRight())); + entry->npts = ocad_object->npts + ocad_object->ntext; + //entry->symbol = index_to_use; + } + } + } + + // Templates + for (int i = map->getNumTemplates() - 1; i >= 0; --i) + { + const Template* temp = map->getTemplate(i); + + QString template_path = temp->getTemplatePath(); + + auto supported_by_ocd = false; + if (qstrcmp(temp->getTemplateType(), "TemplateImage") == 0) + { + supported_by_ocd = true; + + if (temp->isTemplateGeoreferenced()) + { + if (temp->getTemplateState() == Template::Unloaded) + { + // Try to load the template, so that the positioning gets set. + const_cast(temp)->loadTemplateFile(false); + } + + if (temp->getTemplateState() != Template::Loaded) + { + addWarning(tr("Unable to save correct position of missing template: \"%1\"") + .arg(temp->getTemplateFilename())); + } + } + } + else if (QFileInfo(template_path).suffix().compare(QLatin1String("ocd"), Qt::CaseInsensitive) == 0) + { + supported_by_ocd = true; + } + + if (supported_by_ocd) + { + // FIXME: export template view parameters + + double a = temp->getTemplateRotation() * 180 / M_PI; + int d = 0; + int o = 0; + int p = 0; + int s = 1; // enabled + int t = 0; + OCADPoint pos = convertPoint(temp->getTemplateX()-area_offset.nativeX(), temp->getTemplateY()-area_offset.nativeY()); + int x = pos.x >> 8; + int y = pos.y >> 8; + double u = convertTemplateScale(temp->getTemplateScaleX()); + double v = convertTemplateScale(temp->getTemplateScaleY()); + + template_path.replace(QLatin1Char('/'), QLatin1Char('\\')); + + QString string; + string.sprintf("\ts%d\tx%d\ty%d\ta%f\tu%f\tv%f\td%d\tp%d\tt%d\to%d", + s, x, y, a, u, v, d, p, t, o + ); + string.prepend(template_path); + + OCADStringEntry* entry = ocad_string_entry_new(file, string.length() + 1); + entry->type = 8; + convertCString(string, file->buffer + entry->ptr, entry->size); + } + else + { + addWarning(tr("Unable to export template: file type of \"%1\" is not supported yet").arg(temp->getTemplateFilename())); + } + } + + stream->write((char*)file->buffer, file->size); + + ocad_file_close(file); +} + + +MapCoord OCAD8FileExport::calculateAreaOffset() +{ + auto area_offset = QPointF{}; + + // Attention: When changing ocd_bounds, update the warning messages, too. + auto ocd_bounds = QRectF{QPointF{-2000, -2000}, QPointF{2000, 2000}}; + auto objects_extent = map->calculateExtent(); + if (!ocd_bounds.contains(objects_extent)) + { + if (objects_extent.width() < ocd_bounds.width() + && objects_extent.height() < ocd_bounds.height()) + { + // The extent fits into the limited area. + addWarning(tr("Coordinates are adjusted to fit into the OCAD 8 drawing area (-2 m ... 2 m).")); + area_offset = objects_extent.center(); + } + else + { + // The extent is too wide to fit. + + // Only move the objects if they are completely outside the drawing area. + // This avoids repeated moves on open/save/close cycles. + if (!objects_extent.intersects(ocd_bounds)) + { + addWarning(tr("Coordinates are adjusted to fit into the OCAD 8 drawing area (-2 m ... 2 m).")); + std::size_t count = 0; + auto calculate_average_center = [&area_offset, &count](Object* object) + { + area_offset *= qreal(count)/qreal(count+1); + ++count; + area_offset += object->getExtent().center() / count; + }; + map->applyOnAllObjects(calculate_average_center); + } + + addWarning(tr("Some coordinates remain outside of the OCAD 8 drawing area." + " They might be unreachable in OCAD.")); + } + + if (area_offset.manhattanLength() > 0) + { + // Round offset to 100 m in projected coordinates, to avoid crude grid offset. + constexpr auto unit = 100; + auto projected_offset = map->getGeoreferencing().toProjectedCoords(MapCoordF(area_offset)); + projected_offset.rx() = qreal(qRound(projected_offset.x()/unit)) * unit; + projected_offset.ry() = qreal(qRound(projected_offset.y()/unit)) * unit; + area_offset = map->getGeoreferencing().toMapCoordF(projected_offset); + } + } + + return MapCoord{area_offset}; +} + + +void OCAD8FileExport::exportCommonSymbolFields(const Symbol* symbol, OCADSymbol* ocad_symbol, int size) +{ + ocad_symbol->size = (s16)size; + convertPascalString(symbol->getPlainTextName(), ocad_symbol->name, 32); + ocad_symbol->number = symbol->getNumberComponent(0) * 10; + if (symbol->getNumberComponent(1) >= 0) + ocad_symbol->number += (symbol->getNumberComponent(1) % 10); + // Symbol number 0.0 is not valid + if (ocad_symbol->number == 0) + ocad_symbol->number = 1; + // Ensure uniqueness of the symbol number + while (symbol_numbers.find(ocad_symbol->number) != symbol_numbers.end()) + ++ocad_symbol->number; + symbol_numbers.insert(ocad_symbol->number); + + if (symbol->isProtected()) + ocad_symbol->status |= 1; + if (symbol->isHidden()) + ocad_symbol->status |= 2; + + // Set of used colors + u8 bitmask = 1; + u8* bitpos = ocad_symbol->colors; + for (int c = 0; c < map->getNumColors(); ++c) + { + if (symbol->containsColor(map->getColor(c))) + *bitpos |= bitmask; + + bitmask = bitmask << 1; + if (bitmask == 0) + { + bitmask = 1; + ++bitpos; + } + } + + exportSymbolIcon(symbol, ocad_symbol->icon); +} + +void OCAD8FileExport::exportSymbolIcon(const Symbol* symbol, u8 ocad_icon[]) +{ + // Icon: 22x22 with 4 bit color code, origin at bottom left + constexpr int icon_size = 22; + QImage image = symbol->createIcon(*map, icon_size, false) + .convertToFormat(QImage::Format_ARGB32_Premultiplied); + + auto process_pixel = [&image](int x, int y)->int + { + // Apply premultiplied pixel on white background + auto premultiplied = image.pixel(x, y); + auto alpha = qAlpha(premultiplied); + auto r = 255 - alpha + qRed(premultiplied); + auto g = 255 - alpha + qGreen(premultiplied); + auto b = 255 - alpha + qBlue(premultiplied); + auto pixel = qRgb(r, g, b); + + // Ordered dithering 2x2 threshold matrix, adjusted for o-map halftones + static int threshold[4] = { 24, 192, 136, 80 }; + auto palette_color = getOcadColor(pixel); + switch (palette_color) + { + case 0: + // Black to gray (50%) + return qGray(pixel) < 128-threshold[(x%2 + 2*(y%2))]/2 ? 0 : 7; + + case 7: + // Gray (50%) to light gray + return qGray(pixel) < 192-threshold[(x%2 + 2*(y%2))]/4 ? 7 : 8; + + case 8: + // Light gray to white + return qGray(pixel) < 256-threshold[(x%2 + 2*(y%2))]/4 ? 8 : 15; + + case 15: + // Pure white + return palette_color; + + default: + // Color to white + return QColor(pixel).saturation() >= threshold[(x%2 + 2*(y%2))] ? palette_color : 15; + } + }; + + for (int y = icon_size - 1; y >= 0; --y) + { + for (int x = 0; x < icon_size; x += 2) + { + auto first = process_pixel(x, y); + auto second = process_pixel(x+1, y); + *(ocad_icon++) = u8((first << 4) + second); + } + ocad_icon++; + } +} + +int OCAD8FileExport::getPatternSize(const PointSymbol* point) +{ + if (!point) + return 0; + + int npts = 0; + for (int i = 0; i < point->getNumElements(); ++i) + { + int factor = 1; + if (point->getElementSymbol(i)->getType() == Symbol::Point) + { + factor = 0; + const PointSymbol* point_symbol = static_cast(point->getElementSymbol(i)); + if (point_symbol->getInnerRadius() > 0 && point_symbol->getInnerColor()) + ++factor; + if (point_symbol->getOuterWidth() > 0 && point_symbol->getOuterColor()) + ++factor; + } + npts += factor * (2 + point->getElementObject(i)->getRawCoordinateVector().size()); + } + if (point->getInnerRadius() > 0 && point->getInnerColor()) + npts += 2 + 1; + if (point->getOuterWidth() > 0 && point->getOuterColor()) + npts += 2 + 1; + + return npts * sizeof(OCADPoint); +} + +s16 OCAD8FileExport::exportPattern(const PointSymbol* point, OCADPoint** buffer) +{ + if (!point) + return 0; + + s16 num_coords = exportSubPattern(origin_point_object, point, buffer); + for (int i = 0; i < point->getNumElements(); ++i) + { + num_coords += exportSubPattern(point->getElementObject(i), point->getElementSymbol(i), buffer); + } + return num_coords; +} + +s16 OCAD8FileExport::exportSubPattern(const Object* object, const Symbol* symbol, OCADPoint** buffer) +{ + s16 num_coords = 0; + OCADSymbolElement* element = (OCADSymbolElement*)*buffer; + + if (symbol->getType() == Symbol::Point) + { + const PointSymbol* point_symbol = static_cast(symbol); + if (point_symbol->getInnerRadius() > 0 && point_symbol->getInnerColor()) + { + element->type = 4; + element->color = convertColor(point_symbol->getInnerColor()); + element->diameter = convertSize(2 * point_symbol->getInnerRadius()); + (*buffer) += 2; + element->npts = exportCoordinates(object->getRawCoordinateVector(), buffer, point_symbol); + num_coords += 2 + element->npts; + } + if (point_symbol->getOuterWidth() > 0 && point_symbol->getOuterColor()) + { + element = (OCADSymbolElement*)*buffer; + element->type = 3; + element->color = convertColor(point_symbol->getOuterColor()); + element->width = convertSize(point_symbol->getOuterWidth()); + element->diameter = convertSize(2 * point_symbol->getInnerRadius() + 2 * point_symbol->getOuterWidth()); + (*buffer) += 2; + element->npts = exportCoordinates(object->getRawCoordinateVector(), buffer, point_symbol); + num_coords += 2 + element->npts; + } + } + else if (symbol->getType() == Symbol::Line) + { + const LineSymbol* line_symbol = static_cast(symbol); + element->type = 1; + if (line_symbol->getCapStyle() == LineSymbol::RoundCap) + element->flags |= 1; + else if (line_symbol->getJoinStyle() == LineSymbol::MiterJoin) + element->flags |= 4; + element->color = convertColor(line_symbol->getColor()); + element->width = convertSize(line_symbol->getLineWidth()); + (*buffer) += 2; + element->npts = exportCoordinates(object->getRawCoordinateVector(), buffer, line_symbol); + num_coords += 2 + element->npts; + } + else if (symbol->getType() == Symbol::Area) + { + const AreaSymbol* area_symbol = static_cast(symbol); + element->type = 2; + element->color = convertColor(area_symbol->getColor()); + (*buffer) += 2; + element->npts = exportCoordinates(object->getRawCoordinateVector(), buffer, area_symbol); + num_coords += 2 + element->npts; + } + else + Q_ASSERT(false); + return num_coords; +} + +s16 OCAD8FileExport::exportPointSymbol(const PointSymbol* point) +{ + int data_size = (sizeof(OCADPointSymbol) - sizeof(OCADPoint)) + getPatternSize(point); + OCADPointSymbol* ocad_symbol = (OCADPointSymbol*)ocad_symbol_new(file, data_size); + exportCommonSymbolFields(point, (OCADSymbol*)ocad_symbol, data_size); + + ocad_symbol->type = OCAD_POINT_SYMBOL; + ocad_symbol->extent = getPointSymbolExtent(point); + if (ocad_symbol->extent <= 0) + ocad_symbol->extent = 100; + if (point->isRotatable()) + ocad_symbol->base_flags |= 1; + ocad_symbol->ngrp = (data_size - (sizeof(OCADPointSymbol) - sizeof(OCADPoint))) / 8; + + OCADPoint* pattern_buffer = ocad_symbol->pts; + exportPattern(point, &pattern_buffer); + Q_ASSERT((u8*)ocad_symbol + data_size == (u8*)pattern_buffer); + return ocad_symbol->number; +} + +s16 OCAD8FileExport::exportLineSymbol(const LineSymbol* line) +{ + int data_size = (sizeof(OCADLineSymbol) - sizeof(OCADPoint)) + + getPatternSize(line->getStartSymbol()) + + getPatternSize(line->getEndSymbol()) + + getPatternSize(line->getMidSymbol()) + + getPatternSize(line->getDashSymbol()); + OCADLineSymbol* ocad_symbol = (OCADLineSymbol*)ocad_symbol_new(file, data_size); + exportCommonSymbolFields(line, (OCADSymbol*)ocad_symbol, data_size); + + // Basic settings + ocad_symbol->type = OCAD_LINE_SYMBOL; + s16 extent = convertSize(0.5f * line->getLineWidth()); + if (line->hasBorder()) + extent = qMax(extent, (s16)convertSize(0.5f * line->getLineWidth() + line->getBorder().shift + 0.5f * line->getBorder().width)); + extent = qMax(extent, getPointSymbolExtent(line->getStartSymbol())); + extent = qMax(extent, getPointSymbolExtent(line->getEndSymbol())); + extent = qMax(extent, getPointSymbolExtent(line->getMidSymbol())); + extent = qMax(extent, getPointSymbolExtent(line->getDashSymbol())); + ocad_symbol->extent = extent; + ocad_symbol->color = convertColor(line->getColor()); + if (line->getColor()) + ocad_symbol->width = convertSize(line->getLineWidth()); + + // Cap and Join + if (line->getCapStyle() == LineSymbol::FlatCap && line->getJoinStyle() == LineSymbol::BevelJoin) + ocad_symbol->ends = 0; + else if (line->getCapStyle() == LineSymbol::RoundCap && line->getJoinStyle() == LineSymbol::RoundJoin) + ocad_symbol->ends = 1; + else if (line->getCapStyle() == LineSymbol::PointedCap && line->getJoinStyle() == LineSymbol::BevelJoin) + ocad_symbol->ends = 2; + else if (line->getCapStyle() == LineSymbol::PointedCap && line->getJoinStyle() == LineSymbol::RoundJoin) + ocad_symbol->ends = 3; + else if (line->getCapStyle() == LineSymbol::FlatCap && line->getJoinStyle() == LineSymbol::MiterJoin) + ocad_symbol->ends = 4; + else if (line->getCapStyle() == LineSymbol::PointedCap && line->getJoinStyle() == LineSymbol::MiterJoin) + ocad_symbol->ends = 6; + else + { + addWarning(tr("In line symbol \"%1\", cannot represent cap/join combination.").arg(line->getPlainTextName())); + // Decide based on the caps + if (line->getCapStyle() == LineSymbol::FlatCap) + ocad_symbol->ends = 0; + else if (line->getCapStyle() == LineSymbol::RoundCap) + ocad_symbol->ends = 1; + else if (line->getCapStyle() == LineSymbol::PointedCap) + ocad_symbol->ends = 3; + else if (line->getCapStyle() == LineSymbol::SquareCap) + ocad_symbol->ends = 0; + } + + if (line->getCapStyle() == LineSymbol::PointedCap) + { + ocad_symbol->bdist = convertSize(line->getPointedCapLength()); + ocad_symbol->edist = convertSize(line->getPointedCapLength()); + } + + // Dash pattern + if (line->isDashed()) + { + if (line->getMidSymbol() && !line->getMidSymbol()->isEmpty()) + { + if (line->getDashesInGroup() > 1) + addWarning(tr("In line symbol \"%1\", neglecting the dash grouping.").arg(line->getPlainTextName())); + + ocad_symbol->len = convertSize(line->getDashLength() + line->getBreakLength()); + ocad_symbol->elen = ocad_symbol->len / 2; + ocad_symbol->gap2 = convertSize(line->getBreakLength()); + } + else + { + if (line->getDashesInGroup() > 1) + { + if (line->getDashesInGroup() > 2) + addWarning(tr("In line symbol \"%1\", the number of dashes in a group has been reduced to 2.").arg(line->getPlainTextName())); + + ocad_symbol->len = convertSize(2 * line->getDashLength() + line->getInGroupBreakLength()); + ocad_symbol->elen = convertSize(2 * line->getDashLength() + line->getInGroupBreakLength()); + ocad_symbol->gap = convertSize(line->getBreakLength()); + ocad_symbol->gap2 = convertSize(line->getInGroupBreakLength()); + ocad_symbol->egap = ocad_symbol->gap2; + } + else + { + ocad_symbol->len = convertSize(line->getDashLength()); + ocad_symbol->elen = ocad_symbol->len / (line->getHalfOuterDashes() ? 2 : 1); + ocad_symbol->gap = convertSize(line->getBreakLength()); + } + } + } + else + { + ocad_symbol->len = convertSize(line->getSegmentLength()); + ocad_symbol->elen = convertSize(line->getEndLength()); + } + + ocad_symbol->smin = line->getShowAtLeastOneSymbol() ? 0 : -1; + + // Double line + if (line->hasBorder() && (line->getBorder().isVisible() || line->getRightBorder().isVisible())) + { + ocad_symbol->dwidth = convertSize(line->getLineWidth() - line->getBorder().width + 2 * line->getBorder().shift); + if (line->getBorder().dashed && !line->getRightBorder().dashed) + ocad_symbol->dmode = 2; + else + ocad_symbol->dmode = line->getBorder().dashed ? 3 : 1; + // ocad_symbol->dflags + + ocad_symbol->lwidth = convertSize(line->getBorder().width); + ocad_symbol->rwidth = convertSize(line->getRightBorder().width); + + ocad_symbol->lcolor = convertColor(line->getBorder().color); + ocad_symbol->rcolor = convertColor(line->getRightBorder().color); + + if (line->getBorder().dashed) + { + ocad_symbol->dlen = convertSize(line->getBorder().dash_length); + ocad_symbol->dgap = convertSize(line->getBorder().break_length); + } + else if (line->getRightBorder().dashed) + { + ocad_symbol->dlen = convertSize(line->getRightBorder().dash_length); + ocad_symbol->dgap = convertSize(line->getRightBorder().break_length); + } + + if (((line->getBorder().dashed && line->getRightBorder().dashed) && + (line->getBorder().dash_length != line->getRightBorder().dash_length || + line->getBorder().break_length != line->getRightBorder().break_length)) || + (!line->getBorder().dashed && line->getRightBorder().dashed)) + { + addWarning(tr("In line symbol \"%1\", cannot export the borders correctly.").arg(line->getPlainTextName())); + } + } + + // Mid symbol + OCADPoint* pattern_buffer = ocad_symbol->pts; + ocad_symbol->smnpts = exportPattern(line->getMidSymbol(), &pattern_buffer); + ocad_symbol->snum = line->getMidSymbolsPerSpot(); + ocad_symbol->sdist = convertSize(line->getMidSymbolDistance()); + + // No secondary symbol + ocad_symbol->ssnpts = 0; + + // Export dash symbol as corner symbol + ocad_symbol->scnpts = exportPattern(line->getDashSymbol(), &pattern_buffer); + + // Start symbol + ocad_symbol->sbnpts = exportPattern(line->getStartSymbol(), &pattern_buffer); + + // End symbol + ocad_symbol->senpts = exportPattern(line->getEndSymbol(), &pattern_buffer); + + Q_ASSERT((u8*)ocad_symbol + data_size == (u8*)pattern_buffer); + return ocad_symbol->number; +} + +s16 OCAD8FileExport::exportAreaSymbol(const AreaSymbol* area) +{ + int data_size = (sizeof(OCADAreaSymbol) - sizeof(OCADPoint)); + for (int i = 0, end = area->getNumFillPatterns(); i < end; ++i) + { + if (area->getFillPattern(i).type == AreaSymbol::FillPattern::PointPattern) + { + data_size += getPatternSize(area->getFillPattern(i).point); + break; + } + } + OCADAreaSymbol* ocad_symbol = (OCADAreaSymbol*)ocad_symbol_new(file, data_size); + exportCommonSymbolFields(area, (OCADSymbol*)ocad_symbol, data_size); + + // Basic settings + ocad_symbol->type = OCAD_AREA_SYMBOL; + ocad_symbol->extent = 0; + if (area->getColor()) + { + ocad_symbol->fill = 1; + ocad_symbol->color = convertColor(area->getColor()); + } + + // Hatch + ocad_symbol->hmode = 0; + for (int i = 0, end = area->getNumFillPatterns(); i < end; ++i) + { + const AreaSymbol::FillPattern& pattern = area->getFillPattern(i); + if (pattern.type == AreaSymbol::FillPattern::LinePattern) + { + if ( (ocad_symbol->hmode == 1 && ocad_symbol->hcolor != convertColor(pattern.line_color)) || + ocad_symbol->hmode == 2 ) + { + addWarning(tr("In area symbol \"%1\", skipping a fill pattern.").arg(area->getPlainTextName())); + continue; + } + + if (pattern.rotatable()) + ocad_symbol->base_flags |= 1; + + ++ocad_symbol->hmode; + if (ocad_symbol->hmode == 1) + { + ocad_symbol->hcolor = convertColor(pattern.line_color); + ocad_symbol->hwidth = convertSize(pattern.line_width); + ocad_symbol->hdist = convertSize(pattern.line_spacing - pattern.line_width); + ocad_symbol->hangle1 = convertRotation(pattern.angle); + } + else if (ocad_symbol->hmode == 2) + { + ocad_symbol->hwidth = (ocad_symbol->hwidth + convertSize(pattern.line_width)) / 2; + ocad_symbol->hdist = (ocad_symbol->hdist + convertSize(pattern.line_spacing - pattern.line_width)) / 2; + ocad_symbol->hangle2 = convertRotation(pattern.angle); + } + } + } + + // Struct + PointSymbol* point_pattern = nullptr; + for (int i = 0, end = area->getNumFillPatterns(); i < end; ++i) + { + const AreaSymbol::FillPattern& pattern = area->getFillPattern(i); + if (pattern.type == AreaSymbol::FillPattern::PointPattern) + { + if (pattern.rotatable()) + ocad_symbol->base_flags |= 1; + + ++ocad_symbol->pmode; + if (ocad_symbol->pmode == 1) + { + ocad_symbol->pwidth = convertSize(pattern.point_distance); + ocad_symbol->pheight = convertSize(pattern.line_spacing); + ocad_symbol->pangle = convertRotation(pattern.angle); + point_pattern = pattern.point; + } + else if (ocad_symbol->pmode == 2) + { + // NOTE: This is only a heuristic which works for the orienteering symbol sets, not a real conversion, which would be impossible in most cases. + // There are no further checks done to find out if the conversion is applicable because with these checks, already a tiny (not noticeable) error + // in the symbol definition would make it take the wrong choice. + addWarning(tr("In area symbol \"%1\", assuming a \"shifted rows\" point pattern. This might be correct as well as incorrect.").arg(area->getPlainTextName())); + + if (pattern.line_offset != 0) + ocad_symbol->pheight /= 2; + else + ocad_symbol->pwidth /= 2; + + break; + } + } + } + + if (point_pattern) + { + OCADPoint* pattern_buffer = ocad_symbol->pts; + ocad_symbol->npts = exportPattern(point_pattern, &pattern_buffer); + Q_ASSERT((u8*)ocad_symbol + data_size == (u8*)pattern_buffer); + } + return ocad_symbol->number; +} + +s16 OCAD8FileExport::exportTextSymbol(const TextSymbol* text) +{ + int data_size = sizeof(OCADTextSymbol); + OCADTextSymbol* ocad_symbol = (OCADTextSymbol*)ocad_symbol_new(file, data_size); + exportCommonSymbolFields(text, (OCADSymbol*)ocad_symbol, data_size); + + ocad_symbol->type = OCAD_TEXT_SYMBOL; + ocad_symbol->subtype = 1; + ocad_symbol->extent = 0; + + convertPascalString(text->getFontFamily(), ocad_symbol->font, 32); + ocad_symbol->color = convertColor(text->getColor()); + ocad_symbol->dpts = qRound(10 * text->getFontSize() / 25.4 * 72.0); + ocad_symbol->bold = text->isBold() ? 700 : 400; + ocad_symbol->italic = text->isItalic() ? 1 : 0; + //ocad_symbol->charset + ocad_symbol->cspace = convertSize(1000 * text->getCharacterSpacing()); + if (ocad_symbol->cspace != 0) + addWarning(tr("In text symbol %1: custom character spacing is set, its implementation does not match OCAD's behavior yet").arg(text->getPlainTextName())); + ocad_symbol->wspace = 100; + ocad_symbol->halign = 0; // Default value, we might have to change this or even create copies of this symbol with other alignments later + double absolute_line_spacing = text->getLineSpacing() * (text->getFontMetrics().lineSpacing() / text->calculateInternalScaling()); + ocad_symbol->lspace = qRound(absolute_line_spacing / (text->getFontSize() * 0.01)); + ocad_symbol->pspace = convertSize(1000 * text->getParagraphSpacing()); + if (text->isUnderlined()) + addWarning(tr("In text symbol %1: ignoring underlining").arg(text->getPlainTextName())); + if (text->usesKerning()) + addWarning(tr("In text symbol %1: ignoring kerning").arg(text->getPlainTextName())); + + ocad_symbol->under = text->hasLineBelow() ? 1 : 0; + ocad_symbol->ucolor = convertColor(text->getLineBelowColor()); + ocad_symbol->uwidth = convertSize(1000 * text->getLineBelowWidth()); + ocad_symbol->udist = convertSize(1000 * text->getLineBelowDistance()); + + ocad_symbol->ntabs = text->getNumCustomTabs(); + for (int i = 0; i < qMin((s16)32, ocad_symbol->ntabs); ++i) + ocad_symbol->tab[i] = convertSize(text->getCustomTab(i)); + + if (text->getFramingMode() != TextSymbol::NoFraming && text->getFramingColor()) + { + ocad_symbol->fcolor = convertColor(text->getFramingColor()); + if (text->getFramingMode() == TextSymbol::ShadowFraming) + { + ocad_symbol->fmode = 1; + ocad_symbol->fdx = convertSize(text->getFramingShadowXOffset()); + ocad_symbol->fdy = -1 * convertSize(text->getFramingShadowYOffset()); + } + else if (text->getFramingMode() == TextSymbol::LineFraming) + { + ocad_symbol->fmode = 2; + ocad_symbol->fdpts = convertSize(text->getFramingLineHalfWidth()); + } + else + Q_ASSERT(false); + } + + return ocad_symbol->number; +} + +void OCAD8FileExport::setTextSymbolFormatting(OCADTextSymbol* ocad_symbol, TextObject* formatting) +{ + if (formatting->getHorizontalAlignment() == TextObject::AlignLeft) + ocad_symbol->halign = 0; + else if (formatting->getHorizontalAlignment() == TextObject::AlignHCenter) + ocad_symbol->halign = 1; + else if (formatting->getHorizontalAlignment() == TextObject::AlignRight) + ocad_symbol->halign = 2; +} + +std::set< s16 > OCAD8FileExport::exportCombinedSymbol(const CombinedSymbol* combination) +{ + // Insert public parts + std::vector map_bitfield; + map_bitfield.assign(map->getNumSymbols(), false); + map_bitfield[map->findSymbolIndex(combination)] = true; + map->determineSymbolUseClosure(map_bitfield); + + std::set result; + for (size_t i = 0, end = map_bitfield.size(); i < end; ++i) + { + if (map_bitfield[i] && symbol_index.contains(map->getSymbol(i))) + { + result.insert(symbol_index[map->getSymbol(i)].begin(), + symbol_index[map->getSymbol(i)].end()); + } + } + + // Insert private parts + for (int i = 0; i < combination->getNumParts(); ++i) + { + if (combination->isPartPrivate(i)) + { + const Symbol* part = combination->getPart(i); + int index = 0; + if (part->getType() == Symbol::Line) + index = exportLineSymbol(part->asLine()); + else if (part->getType() == Symbol::Area) + index = exportAreaSymbol(part->asArea()); + else + Q_ASSERT(false); + result.insert(index); + } + } + + return result; +} + +u16 OCAD8FileExport::exportCoordinates(const MapCoordVector& coords, OCADPoint** buffer, const Symbol* symbol) +{ + s16 num_points = 0; + bool curve_start = false; + bool hole_point = false; + bool curve_continue = false; + for (size_t i = 0, end = coords.size(); i < end; ++i) + { + const MapCoord& point = coords[i]; + OCADPoint p = convertPoint(point); + if (point.isDashPoint()) + { + if (!symbol || symbol->getType() != Symbol::Line) + p.y |= PY_CORNER; + else + { + const LineSymbol* line_symbol = static_cast(symbol); + if ((line_symbol->getDashSymbol() == nullptr || line_symbol->getDashSymbol()->isEmpty()) && line_symbol->isDashed()) + p.y |= PY_DASH; + else + p.y |= PY_CORNER; + } + } + if (curve_start) + p.x |= PX_CTL1; + if (hole_point) + p.y |= PY_HOLE; + if (curve_continue) + p.x |= PX_CTL2; + + curve_continue = curve_start; + curve_start = point.isCurveStart(); + hole_point = point.isHolePoint(); + + **buffer = p; + ++(*buffer); + ++num_points; + } + return num_points; +} + +u16 OCAD8FileExport::exportTextCoordinates(TextObject* object, OCADPoint** buffer) +{ + if (object->getNumLines() == 0) + return 0; + + QTransform text_to_map = object->calcTextToMapTransform(); + QTransform map_to_text = object->calcMapToTextTransform(); + + if (object->hasSingleAnchor()) + { + // Create 5 coordinates: + // 0 - baseline anchor point + // 1 - bottom left + // 2 - bottom right + // 3 - top right + // 4 - top left + + QPointF anchor = QPointF(object->getAnchorCoordF()); + QPointF anchor_text = map_to_text.map(anchor); + + TextObjectLineInfo* line0 = object->getLineInfo(0); + **buffer = convertPoint(MapCoord(text_to_map.map(QPointF(anchor_text.x(), line0->line_y)))); + ++(*buffer); + + QRectF bounding_box_text; + for (int i = 0; i < object->getNumLines(); ++i) + { + TextObjectLineInfo* info = object->getLineInfo(i); + rectIncludeSafe(bounding_box_text, QPointF(info->line_x, info->line_y - info->ascent)); + rectIncludeSafe(bounding_box_text, QPointF(info->line_x + info->width, info->line_y + info->descent)); + } + + **buffer = convertPoint(MapCoord(text_to_map.map(bounding_box_text.bottomLeft()))); + ++(*buffer); + **buffer = convertPoint(MapCoord(text_to_map.map(bounding_box_text.bottomRight()))); + ++(*buffer); + **buffer = convertPoint(MapCoord(text_to_map.map(bounding_box_text.topRight()))); + ++(*buffer); + **buffer = convertPoint(MapCoord(text_to_map.map(bounding_box_text.topLeft()))); + ++(*buffer); + + return 5; + } + else + { + // As OCD 8 only supports Top alignment, we have to replace the top box coordinates by the top coordinates of the first line + const TextSymbol* text_symbol = static_cast(object->getSymbol()); + QFontMetricsF metrics = text_symbol->getFontMetrics(); + double internal_scaling = text_symbol->calculateInternalScaling(); + TextObjectLineInfo* line0 = object->getLineInfo(0); + + double new_top = (object->getVerticalAlignment() == TextObject::AlignTop) ? (-object->getBoxHeight() / 2) : ((line0->line_y - line0->ascent) / internal_scaling); + // Account for extra internal leading + double top_adjust = -text_symbol->getFontSize() + (metrics.ascent() + metrics.descent() + 0.5) / internal_scaling; + new_top = new_top - top_adjust; + + QTransform transform; + transform.rotate(-object->getRotation() * 180 / M_PI); + **buffer = convertPoint(MapCoord(transform.map(QPointF(-object->getBoxWidth() / 2, object->getBoxHeight() / 2)) + object->getAnchorCoordF())); + ++(*buffer); + **buffer = convertPoint(MapCoord(transform.map(QPointF(object->getBoxWidth() / 2, object->getBoxHeight() / 2)) + object->getAnchorCoordF())); + ++(*buffer); + **buffer = convertPoint(MapCoord(transform.map(QPointF(object->getBoxWidth() / 2, new_top)) + object->getAnchorCoordF())); + ++(*buffer); + **buffer = convertPoint(MapCoord(transform.map(QPointF(-object->getBoxWidth() / 2, new_top)) + object->getAnchorCoordF())); + ++(*buffer); + + return 4; + } +} + +// static +int OCAD8FileExport::getOcadColor(QRgb rgb) +{ + static const QColor ocad_colors[16] = { + QColor( 0, 0, 0).toHsv(), + QColor(128, 0, 0).toHsv(), + QColor(0, 128, 0).toHsv(), + QColor(128, 128, 0).toHsv(), + QColor( 0, 0, 128).toHsv(), + QColor(128, 0, 128).toHsv(), + QColor( 0, 128, 128).toHsv(), + QColor(128, 128, 128).toHsv(), + QColor(192, 192, 192).toHsv(), + QColor(255, 0, 0).toHsv(), + QColor( 0, 255, 0).toHsv(), + QColor(255, 255, 0).toHsv(), + QColor( 0, 0, 255).toHsv(), + QColor(255, 0, 255).toHsv(), + QColor( 0, 255, 255).toHsv(), + QColor(255, 255, 255).toHsv() + }; + + Q_ASSERT(qAlpha(rgb) == 255); + + // Quick return for frequent values + if (rgb == qRgb(255, 255, 255)) + return 15; + else if (rgb == qRgb(0, 0, 0)) + return 0; + + QColor color = QColor(rgb).toHsv(); + if (color.hue() == -1 || color.saturation() < 32) + { + auto gray = qGray(rgb); // qGray is used for dithering + if (gray >= 192) + return 8; + if (gray >= 128) + return 7; + return 0; + } + + int best_index = 0; + auto best_distance = std::numeric_limits::max(); + for (auto i : { 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14 }) + { + // True color + int hue_dist = qAbs(color.hue() - ocad_colors[i].hue()); + hue_dist = qMin(hue_dist, 360 - hue_dist); + auto distance = qPow(hue_dist, 2) + + 0.1 * qPow(color.saturation() - ocad_colors[i].saturation(), 2) + + 0.1 * qPow(color.value() - ocad_colors[i].value(), 2); + + // (Too much) manual tweaking for orienteering colors + if (i == 1) + distance *= 1.5; // Dark red + else if (i == 3) + distance *= 2; // Olive + else if (i == 11) + distance *= 2; // Yellow + else if (i == 9) + distance *= 3; // Red is unlikely + + if (distance < best_distance) + { + best_distance = distance; + best_index = i; + } + } + return best_index; +} + +s16 OCAD8FileExport::getPointSymbolExtent(const PointSymbol* symbol) +{ + if (!symbol) + return 0; + QRectF extent; + for (int i = 0; i < symbol->getNumElements(); ++i) + { + QScopedPointer object(symbol->getElementObject(i)->duplicate()); + object->setSymbol(symbol->getElementSymbol(i), true); + object->update(); + rectIncludeSafe(extent, object->getExtent()); + object->clearRenderables(); + } + float float_extent = 0.5f * qMax(extent.width(), extent.height()); + if (symbol->getInnerColor()) + float_extent = qMax(float_extent, 0.001f * symbol->getInnerRadius()); + if (symbol->getOuterColor()) + float_extent = qMax(float_extent, 0.001f * (symbol->getInnerRadius() + symbol->getOuterWidth())); + return convertSize(1000 * float_extent); +} + +void OCAD8FileExport::convertPascalString(const QString& text, char* buffer, int buffer_size) +{ + Q_ASSERT(buffer_size <= 256); // not possible to store a bigger length in the first byte + int max_size = buffer_size - 1; + + if (text.length() > max_size) + addStringTruncationWarning(text, max_size); + + QByteArray data = encoding_1byte->fromUnicode(text); + int min_size = qMin(text.length(), max_size); + *((unsigned char *)buffer) = min_size; + memcpy(buffer + 1, data.data(), min_size); +} + +void OCAD8FileExport::convertCString(const QString& text, unsigned char* buffer, int buffer_size) +{ + if (text.length() + 1 > buffer_size) + addStringTruncationWarning(text, buffer_size - 1); + + QByteArray data = encoding_1byte->fromUnicode(text); + int min_size = qMin(buffer_size - 1, data.length()); + memcpy(buffer, data.data(), min_size); + buffer[min_size] = 0; +} + +int OCAD8FileExport::convertWideCString(const QString& text, unsigned char* buffer, int buffer_size) +{ + // Convert text to Windows-OCAD format: + // - if it starts with a newline, add another + // - convert \n to \r\n + QString exported_text; + if (text.startsWith(QLatin1Char('\n'))) + exported_text = QLatin1Char('\n') + text; + else + exported_text = text; + exported_text.replace(QLatin1Char('\n'), QLatin1String("\r\n")); + + if (2 * (exported_text.length() + 1) > buffer_size) + addStringTruncationWarning(exported_text, buffer_size - 1); + + // Do not add a byte order mark by using QTextCodec::IgnoreHeader + QTextEncoder* encoder = encoding_2byte->makeEncoder(QTextCodec::IgnoreHeader); + QByteArray data = encoder->fromUnicode(exported_text); + delete encoder; + + int min_size = qMin(buffer_size - 2, data.length()); + memcpy(buffer, data.data(), min_size); + buffer[min_size] = 0; + buffer[min_size + 1] = 0; + return min_size + 2; +} + +int OCAD8FileExport::convertRotation(float angle) +{ + return qRound(10 * (angle * 180 / M_PI)); +} + +namespace +{ + constexpr s32 convertPointMember(s32 value) + { + return (value < -5) ? s32(0x80000000u | ((0x7fffffu & u32((value-4)/10)) << 8)) : s32((0x7fffffu & u32((value+5)/10)) << 8); + } + + // convertPointMember() shall round half up. + Q_STATIC_ASSERT(convertPointMember(-16) == s32(0xfffffe00u)); // __ down __ + Q_STATIC_ASSERT(convertPointMember(-15) == s32(0xffffff00u)); // up + Q_STATIC_ASSERT(convertPointMember( -6) == s32(0xffffff00u)); // __ down __ + Q_STATIC_ASSERT(convertPointMember( -5) == s32(0x00000000u)); // up + Q_STATIC_ASSERT(convertPointMember( -1) == s32(0x00000000u)); // up + Q_STATIC_ASSERT(convertPointMember( 0) == s32(0x00000000u)); // unchanged + Q_STATIC_ASSERT(convertPointMember( +1) == s32(0x00000000u)); // down + Q_STATIC_ASSERT(convertPointMember( +4) == s32(0x00000000u)); // __ down __ + Q_STATIC_ASSERT(convertPointMember( +5) == s32(0x00000100u)); // up + Q_STATIC_ASSERT(convertPointMember(+14) == s32(0x00000100u)); // __ down __ + Q_STATIC_ASSERT(convertPointMember(+15) == s32(0x00000200u)); // up + +#ifdef MAPPER_DEVELOPMENT_BUILD + namespace broken + { + // Previous, broken implementation (#749) + // Left here for reference. + constexpr s32 convertPointMember(s32 value) + { + return (value < 0) ? (0x80000000 | ((0x7fffffu & ((value-5)/10)) << 8)) : ((0x7fffffu & ((value+5)/10)) << 8); + } + } + // Actual behaviour of the broken implementation + Q_STATIC_ASSERT(broken::convertPointMember(-16) == s32(0xfffffe00u)); // down + Q_STATIC_ASSERT(broken::convertPointMember(-15) == s32(0xfffffe00u)); // __ down __ (should be up) + Q_STATIC_ASSERT(broken::convertPointMember(-14) == s32(0xffffff00u)); // up + Q_STATIC_ASSERT(broken::convertPointMember( -6) == s32(0xffffff00u)); // down + Q_STATIC_ASSERT(broken::convertPointMember( -5) == s32(0xffffff00u)); // __ down __ (should be up) + Q_STATIC_ASSERT(broken::convertPointMember( -4) == s32(0x80000000u)); // wrong (should be 0x00000000u) + Q_STATIC_ASSERT(broken::convertPointMember( -3) == s32(0x80000000u)); // wrong (should be 0x00000000u) + Q_STATIC_ASSERT(broken::convertPointMember( -2) == s32(0x80000000u)); // wrong (should be 0x00000000u) + Q_STATIC_ASSERT(broken::convertPointMember( -1) == s32(0x80000000u)); // wrong (should be 0x00000000u) + Q_STATIC_ASSERT(broken::convertPointMember( 0) == s32(0x00000000u)); // unchanged + Q_STATIC_ASSERT(broken::convertPointMember( +1) == s32(0x00000000u)); // down + Q_STATIC_ASSERT(broken::convertPointMember( +4) == s32(0x00000000u)); // __ down __ + Q_STATIC_ASSERT(broken::convertPointMember( +5) == s32(0x00000100u)); // up + Q_STATIC_ASSERT(broken::convertPointMember(+14) == s32(0x00000100u)); // __ down __ + Q_STATIC_ASSERT(broken::convertPointMember(+15) == s32(0x00000200u)); // up +#endif +} + +OCADPoint OCAD8FileExport::convertPoint(qint32 x, qint32 y) +{ + return { convertPointMember(x), convertPointMember(-y) }; +} + +OCADPoint OCAD8FileExport::convertPoint(const MapCoord& coord) +{ + return convertPoint(coord.nativeX(), coord.nativeY()); +} + +s32 OCAD8FileExport::convertSize(qint32 size) +{ + return (s32)((size+5) / 10); +} + +s16 OCAD8FileExport::convertColor(const MapColor* color) const +{ + int index = map->findColorIndex(color); + if (index >= 0) + { + return uses_registration_color ? (index + 1) : index; + } + return 0; +} + +double OCAD8FileExport::convertTemplateScale(double mapper_scale) +{ + return mapper_scale * (1 / 0.01); +} + +void OCAD8FileExport::addStringTruncationWarning(const QString& text, int truncation_pos) +{ + QString temp = text; + temp.insert(truncation_pos, QLatin1String("|||")); + addWarning(tr("String truncated (truncation marked with three '|'): %1").arg(temp)); +} + + +} // namespace OpenOrienteering diff --git a/src/fileformats/ocad8_file_format.h b/src/fileformats/ocad8_file_format.h new file mode 100644 index 0000000..b44bfbc --- /dev/null +++ b/src/fileformats/ocad8_file_format.h @@ -0,0 +1,52 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCAD8_FILE_FORMAT_H +#define OPENORIENTEERING_OCAD8_FILE_FORMAT_H + +#include + +#include "file_format.h" + +class QIODevice; + +namespace OpenOrienteering { + +class Exporter; +class Importer; +class Map; +class MapView; + + +/** Representation of the format used by OCAD 8. + */ +class OCAD8FileFormat : public FileFormat +{ +public: + OCAD8FileFormat(); + + bool understands(const unsigned char *buffer, std::size_t sz) const override; + Importer* createImporter(QIODevice* stream, Map *map, MapView *view) const override; + Exporter* createExporter(QIODevice* stream, Map* map, MapView* view) const override; +}; + + +} // namespace OpenOrienteering + +#endif // OCAD8_FILE_IMPORT_H diff --git a/src/fileformats/ocad8_file_format_p.h b/src/fileformats/ocad8_file_format_p.h new file mode 100644 index 0000000..047fe3b --- /dev/null +++ b/src/fileformats/ocad8_file_format_p.h @@ -0,0 +1,241 @@ +/* + * Copyright 2012, 2013 Pete Curtis + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_FILE_FORMAT_OCAD_P_H +#define OPENORIENTEERING_FILE_FORMAT_OCAD_P_H + +#include + +#include +#include + +#include "core/map_coord.h" +#include "fileformats/file_import_export.h" +#include "libocad/libocad.h" + +namespace OpenOrienteering { + +class Map; +class MapColor; +class MapPart; +class Object; +class PointObject; +class TextObject; +class Symbol; +class AreaSymbol; +class CombinedSymbol; +class LineSymbol; +class PointSymbol; +class Template; +class TextSymbol; + + +/** Importer for OCD version 8 files. */ +class OCAD8FileImport : public Importer +{ + friend class OcdFileImport; + + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OCAD8FileImport) + +private: + /// Information about an OCAD rectangle symbol + struct RectangleInfo + { + LineSymbol* border_line; + double corner_radius; + bool has_grid; + + // Only valid if has_grid is true + LineSymbol* inner_line; + TextSymbol* text; + bool number_from_bottom; + double cell_width; + double cell_height; + int unnumbered_cells; + QString unnumbered_text; + }; + +public: + OCAD8FileImport(QIODevice* stream, Map *map, MapView *view); + ~OCAD8FileImport() override; + + void setStringEncodings(const char *narrow, const char *wide = "UTF-16LE"); + +protected: + void import(bool load_symbols_only) override; + + // Symbol import + Symbol *importPointSymbol(const OCADPointSymbol *ocad_symbol); + Symbol *importLineSymbol(const OCADLineSymbol *ocad_symbol); + Symbol *importAreaSymbol(const OCADAreaSymbol *ocad_symbol); + Symbol *importTextSymbol(const OCADTextSymbol *ocad_symbol); + RectangleInfo *importRectSymbol(const OCADRectSymbol *ocad_symbol); + + // Object import + Object *importObject(const OCADObject *ocad_object, MapPart* part); + bool importRectangleObject(const OCADObject* ocad_object, MapPart* part, const RectangleInfo& rect); + + // String import + virtual void importString(OCADStringEntry *entry); + + Template *importTemplate(OCADCString* ocad_str); + OCADBackground importBackground(const QByteArray& data); + /// @deprecated Replaced by Template *importTemplate(OCADCString* string). + Template *importRasterTemplate(const OCADBackground &background); + + // Some helper functions that are used in multiple places + PointSymbol *importPattern(s16 npts, OCADPoint *pts); + void fillCommonSymbolFields(Symbol *symbol, const OCADSymbol *ocad_symbol); + void setPathHolePoint(Object *object, int i); + void fillPathCoords(Object* object, bool is_area, u16 npts, const OCADPoint* pts); + bool fillTextPathCoords(TextObject* object, TextSymbol* symbol, u16 npts, OCADPoint* pts); + + + // Unit conversion functions + QString convertPascalString(const char *p); + QString convertCString(const char *p, std::size_t n, bool ignore_first_newline); + QString convertWideCString(const char *p, std::size_t n, bool ignore_first_newline); + float convertRotation(int angle); + void convertPoint(MapCoord &c, s32 ocad_x, s32 ocad_y); + qint32 convertSize(int ocad_size); + const MapColor *convertColor(int color); + double convertTemplateScale(double ocad_scale); + + static bool isRasterImageFile(const QString &filename); + +private: + /// Handle to the open OCAD file + OCADFile *file; + + /// Character encoding to use for 1-byte (narrow) strings + QTextCodec *encoding_1byte; + + /// Character encoding to use for 2-byte (wide) strings + QTextCodec *encoding_2byte; + + /// maps OCAD color number to oo-mapper color object + QHash color_index; + + /// maps OCAD symbol number to oo-mapper symbol object + QHash symbol_index; + + /// maps OO Mapper text symbol pointer to OCAD text symbol horizontal alignment (stored in objects instead of symbols in OO Mapper) + QHash text_halign_map; + + /// maps OCAD symbol number to rectangle information struct + QHash rectangle_info; + + /// Offset between OCAD map origin and Mapper map origin (in Mapper coordinates) + qint64 offset_x, offset_y; +}; + + +/** Exporter for OCD version 8 files. */ +class OCAD8FileExport : public Exporter +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OCAD8FileExport) + +public: + OCAD8FileExport(QIODevice* stream, Map *map, MapView *view); + ~OCAD8FileExport() override; + + void doExport() override; + + +protected: + // Determines an offset for moving objects to the OCD drawing area. + MapCoord calculateAreaOffset(); + + // Symbol export + void exportCommonSymbolFields(const Symbol* symbol, OCADSymbol* ocad_symbol, int size); + void exportSymbolIcon(const Symbol* symbol, u8 ocad_icon[]); + int getPatternSize(const PointSymbol* point); + s16 exportPattern(const PointSymbol* point, OCADPoint** buffer); // returns the number of written coordinates, including the headers + s16 exportSubPattern(const Object* object, const Symbol* symbol, OCADPoint** buffer); + + s16 exportPointSymbol(const PointSymbol* point); + s16 exportLineSymbol(const LineSymbol* line); + s16 exportAreaSymbol(const AreaSymbol* area); + s16 exportTextSymbol(const TextSymbol* text); + void setTextSymbolFormatting(OCADTextSymbol* ocad_symbol, TextObject* formatting); + std::set exportCombinedSymbol(const CombinedSymbol* combination); + + // Helper functions + /// Returns the number of exported coordinates. If not nullptr, the given symbol is used to determine the meaning of dash points. + u16 exportCoordinates(const MapCoordVector& coords, OCADPoint** buffer, const Symbol* symbol); + u16 exportTextCoordinates(TextObject* object, OCADPoint** buffer); + static int getOcadColor(QRgb rgb); + s16 getPointSymbolExtent(const PointSymbol* symbol); + + // Conversion functions + void convertPascalString(const QString& text, char* buffer, int buffer_size); + void convertCString(const QString& text, unsigned char* buffer, int buffer_size); + /// Returns the number of bytes written into buffer + int convertWideCString(const QString& text, unsigned char* buffer, int buffer_size); + int convertRotation(float angle); + OCADPoint convertPoint(qint32 x, qint32 y); + /// Attention: this ignores the coordinate flags! + OCADPoint convertPoint(const MapCoord& coord); + s32 convertSize(qint32 size); + s16 convertColor(const MapColor* color) const; + double convertTemplateScale(double mapper_scale); + +private: + /** Indicates that the map uses the special registration color. */ + bool uses_registration_color; + + /// Handle to the open OCAD file + OCADFile *file; + + /// Character encoding to use for 1-byte (narrow) strings + QTextCodec *encoding_1byte; + + /// Character encoding to use for 2-byte (wide) strings + QTextCodec *encoding_2byte; + + /// Set of used symbol numbers. Needed to ensure uniqueness of the symbol number as Mapper does not enforce it, + /// but the indexing of symbols in OCAD depends on it. + std::set symbol_numbers; + + /// Maps OO Mapper symbol pointer to a list of OCAD symbol numbers. + /// Usually the list contains only one entry, except for combined symbols, + /// for which it contains the indices of all basic parts + QHash > symbol_index; + + /// In .ocd 8, text alignment needs to be specified in the text symbols instead of objects, so it is possible + /// that multiple ocd text symbols have to be created for one native TextSymbol. + /// This structure maps text symbols to lists containing information about the already created ocd symbols. + /// The first member in each pair just gives information about the alignment option used for the symbol indexed by the + /// second part of the pair. + /// If there is no entry for a TextSymbol in this map yet, no object using this symbol has been encountered yet, + /// no no specific formatting was set in the corresponding symbol (which has to be looked up using symbol_index). + typedef std::vector< std::pair< int, s16 > > TextFormatList; + QHash text_format_map; + + /// Helper object for pattern export + PointObject* origin_point_object; + + void addStringTruncationWarning(const QString& text, int truncation_pos); +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/fileformats/ocd_file_export.cpp b/src/fileformats/ocd_file_export.cpp new file mode 100644 index 0000000..13167c4 --- /dev/null +++ b/src/fileformats/ocd_file_export.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2016 Kai Pastor + * + * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are + * Copyright 2012 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "ocd_file_export.h" + +#include "fileformats/ocad8_file_format_p.h" + + +namespace OpenOrienteering { + +OcdFileExport::OcdFileExport(QIODevice* stream, Map* map, MapView* view) +: Exporter { stream, map, view } +{ + // nothing else +} + + +OcdFileExport::~OcdFileExport() = default; + + +void OcdFileExport::doExport() +{ + OCAD8FileExport delegate { stream, map, view }; + delegate.doExport(); + for (auto&& w : delegate.warnings()) + { + addWarning(w); + } +} + + +} // namespace OpenOrienteering diff --git a/src/fileformats/ocd_file_export.h b/src/fileformats/ocd_file_export.h new file mode 100644 index 0000000..33f70e7 --- /dev/null +++ b/src/fileformats/ocd_file_export.h @@ -0,0 +1,62 @@ +/* + * Copyright 2016 Kai Pastor + * + * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are + * Copyright 2012 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_FILE_EXPORT_H +#define OPENORIENTEERING_OCD_FILE_EXPORT_H + +#include + +#include "fileformats/file_import_export.h" + +class QIODevice; + +namespace OpenOrienteering { + +class Map; +class MapView; + + +/** + * An exporter for OCD files. + */ +class OcdFileExport : public Exporter +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OcdFileExport) + +public: + OcdFileExport(QIODevice* stream, Map *map, MapView *view); + + ~OcdFileExport() override; + + /** + * Exports an OCD file. + * + * For now, this simply uses the OCAD8FileExport class. + */ + void doExport() override; + +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/fileformats/ocd_file_format.cpp b/src/fileformats/ocd_file_format.cpp new file mode 100644 index 0000000..c1e52cb --- /dev/null +++ b/src/fileformats/ocd_file_format.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2013-2016 Kai Pastor + * + * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are + * Copyright 2012 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "ocd_file_format.h" + +#include +#include +#include +#include + +#include "fileformats/file_import_export.h" +#include "fileformats/ocd_file_export.h" +#include "fileformats/ocd_file_import.h" + + +namespace OpenOrienteering { + +// ### OcdFileFormat ### + +OcdFileFormat::OcdFileFormat() +: FileFormat { MapFile, "OCD", ::OpenOrienteering::ImportExport::tr("OCAD"), QString::fromLatin1("ocd"), + ImportSupported | ExportSupported | ExportLossy } +{ + // Nothing +} + +bool OcdFileFormat::understands(const unsigned char* buffer, std::size_t sz) const +{ + // The first two bytes of the file must be 0x0cad in litte endian ordner. + // This test will refuse to understand OCD files on big endian systems: + // The importer's current implementation won't work there. + return (sz >= 2 && *reinterpret_cast(buffer) == 0x0cad); +} + +Importer* OcdFileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const +{ + return new OcdFileImport(stream, map, view); +} + +Exporter* OcdFileFormat::createExporter(QIODevice* stream, Map* map, MapView* view) const +{ + return new OcdFileExport(stream, map, view); +} + + +} // namespace OpenOrienteering diff --git a/src/fileformats/ocd_file_format.h b/src/fileformats/ocd_file_format.h new file mode 100644 index 0000000..124e446 --- /dev/null +++ b/src/fileformats/ocd_file_format.h @@ -0,0 +1,66 @@ +/* + * Copyright 2013, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_FILE_FORMAT +#define OPENORIENTEERING_OCD_FILE_FORMAT + +#include + +#include "fileformats/file_format.h" + +class QIODevice; + +namespace OpenOrienteering { + +class Exporter; +class Importer; +class Map; +class MapView; + + +/** + * The map file format known as OC*D. + */ +class OcdFileFormat : public FileFormat +{ +public: + /** + * Constructs a new OcdFileFormat. + */ + OcdFileFormat(); + + /** + * Detects whether the buffer may be the start of a valid OCD file. + * + * At the moment, it requires at least two bytes of data. + * It will return false if compiled for a big endian system. + */ + bool understands(const unsigned char *buffer, std::size_t sz) const override; + + /// \copydoc FileFormat::createImporter() + Importer* createImporter(QIODevice* stream, Map *map, MapView *view) const override; + + /// \copydoc FileFormat::createExporter() + Exporter* createExporter(QIODevice* stream, Map* map, MapView* view) const override; +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_OCD_FILE_FORMAT diff --git a/src/fileformats/ocd_file_import.cpp b/src/fileformats/ocd_file_import.cpp new file mode 100644 index 0000000..4a32cc0 --- /dev/null +++ b/src/fileformats/ocd_file_import.cpp @@ -0,0 +1,2174 @@ +/* + * Copyright 2013-2017 Kai Pastor + * + * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are + * Copyright 2012 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "ocd_file_import.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/crs_template.h" +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_grid.h" +#include "core/map_part.h" +#include "core/map_view.h" +#include "core/objects/text_object.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/combined_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "core/symbols/text_symbol.h" +#include "fileformats/file_format.h" +#include "fileformats/ocad8_file_format_p.h" +#include "fileformats/ocd_types_v10.h" +#include "fileformats/ocd_types_v11.h" +#include "fileformats/ocd_types_v12.h" +#include "fileformats/ocd_types_v9.h" +#include "templates/template.h" +#include "templates/template_image.h" +#include "templates/template_map.h" +#include "util/encoding.h" +#include "util/util.h" + + +namespace OpenOrienteering { + +namespace { + +static QTextCodec* codecFromSettings() +{ + const auto& settings = Settings::getInstance(); + const auto name = settings.getSetting(Settings::General_Local8BitEncoding).toByteArray(); + return Util::codecForName(name); +} + + +} // namespace + + + +OcdFileImport::OcdImportedPathObject::~OcdImportedPathObject() +{ + // nothing, not inlined +} + +OcdFileImport::OcdFileImport(QIODevice* stream, Map* map, MapView* view) + : Importer { stream, map, view } + , delegate { nullptr } + , custom_8bit_encoding { codecFromSettings() } +{ + if (!custom_8bit_encoding) + { + addWarning(tr("Encoding '%1' is not available. Check the settings.")); + custom_8bit_encoding = QTextCodec::codecForLocale(); + } +} + +OcdFileImport::~OcdFileImport() +{ + // nothing +} + + +void OcdFileImport::setCustom8BitEncoding(QTextCodec* encoding) +{ + custom_8bit_encoding = encoding; +} + + +QString OcdFileImport::convertOcdString(const QChar* src, uint maxlen) const +{ + auto last = src; + if (src) + { + while (maxlen && *last != 0) + { + last ++; + --maxlen; + } + } + QTextCodec* utf16 = QTextCodec::codecForName("UTF-16LE"); + Q_ASSERT(utf16); + auto decoder = std::unique_ptr(utf16->makeDecoder(QTextCodec::ConvertInvalidToNull)); + return decoder->toUnicode(reinterpret_cast(src), 2*int(last - src)); +} + + +void OcdFileImport::addSymbolWarning(const AreaSymbol* symbol, const QString& warning) +{ + addWarning( tr("In area symbol %1 '%2': %3"). + arg(symbol->getNumberAsString(), symbol->getName(), warning) ); +} + +void OcdFileImport::addSymbolWarning(const LineSymbol* symbol, const QString& warning) +{ + addWarning( tr("In line symbol %1 '%2': %3"). + arg(symbol->getNumberAsString(), symbol->getName(), warning) ); +} + +void OcdFileImport::addSymbolWarning(const TextSymbol* symbol, const QString& warning) +{ + addWarning( tr("In text symbol %1 '%2': %3"). + arg(symbol->getNumberAsString(), symbol->getName(), warning) ); +} + + +#ifndef NDEBUG + +// Heuristic detection of implementation errors +template< > +inline +qint64 OcdFileImport::convertLength< quint8 >(quint8 ocd_length) const +{ + // OC*D uses hundredths of a millimeter. + // oo-mapper uses 1/1000 mm + if (ocd_length > 200) + qDebug("quint8 has value %d, might be qint8 %d", ocd_length, qint8(ocd_length)); + return qint64(ocd_length) * 10; +} + +template< > +inline +qint64 OcdFileImport::convertLength< quint16 >(quint16 ocd_length) const +{ + // OC*D uses hundredths of a millimeter. + // oo-mapper uses 1/1000 mm + if (ocd_length > 50000) + qDebug("quint16 has value %d, might be qint16 %d", ocd_length, qint16(ocd_length)); + return qint64(ocd_length) * 10; +} + +template< > +inline +qint64 OcdFileImport::convertLength< quint32 >(quint32 ocd_length) const +{ + // OC*D uses hundredths of a millimeter. + // oo-mapper uses 1/1000 mm + if (ocd_length > 3000000) + qDebug("quint32 has value %d, might be qint32 %d", ocd_length, qint32(ocd_length)); + return qint64(ocd_length) * 10; +} + +#endif // !NDEBUG + + +void OcdFileImport::importImplementationLegacy(bool load_symbols_only) +{ + QBuffer new_stream(&buffer); + new_stream.open(QIODevice::ReadOnly); + delegate.reset(new OCAD8FileImport(&new_stream, map, view)); + + delegate->import(load_symbols_only); + + for (auto&& w : delegate->warnings()) + { + addWarning(w); + } + + for (auto&& a : delegate->actions()) + { + addAction(a); + } +} + +template< class F > +void OcdFileImport::importImplementation(bool load_symbols_only) +{ + const OcdFile file(buffer); +#ifdef MAPPER_DEVELOPMENT_BUILD + if (!qApp->applicationName().endsWith(QLatin1String("Test"))) + { + qDebug("*** OcdFileImport: Importing a version %d.%d file", file.header()->version, file.header()->subversion); + for (const auto& string : file.strings()) + { + qDebug(" %d \t%s", string.type, qPrintable(convertOcdString< typename F::Encoding >(file[string]))); + } + } +#endif + + map->setSymbolSetId(QStringLiteral("OCD")); + importGeoreferencing(file); + importColors(file); + importSymbols(file); + if (!load_symbols_only) + { + importExtras(file); + importObjects(file); + importTemplates(file); + if (view) + { + importView(file); + } + } +} + + +void OcdFileImport::importGeoreferencing(const OcdFile& file) +{ + const Ocd::FileHeaderV8* header = file.header(); + const Ocd::SetupV8* setup = reinterpret_cast< const Ocd::SetupV8* >(file.byteArray().data() + header->setup_pos); + + Georeferencing georef; + georef.setScaleDenominator(qRound(setup->map_scale)); + georef.setProjectedRefPoint(QPointF(setup->real_offset_x, setup->real_offset_y)); + if (std::abs(setup->real_angle) >= 0.01) /* degrees */ + { + georef.setGrivation(setup->real_angle); + } + map->setGeoreferencing(georef); +} + +template< class F > +void OcdFileImport::importGeoreferencing(const OcdFile< F >& file) +{ + handleStrings(file, { { 1039, &OcdFileImport::importGeoreferencing } }); +} + +void OcdFileImport::importGeoreferencing(const QString& param_string, int /*ocd_version*/) +{ + const QChar* unicode = param_string.unicode(); + + Georeferencing georef; + QString combined_grid_zone; + QPointF proj_ref_point; + bool x_ok = false, y_ok = false; + + int i = param_string.indexOf(QLatin1Char('\t'), 0); + ; // skip first word for this entry type + while (i >= 0) + { + bool ok; + int next_i = param_string.indexOf(QLatin1Char('\t'), i+1); + int len = (next_i > 0 ? next_i : param_string.length()) - i - 2; + const QString param_value = QString::fromRawData(unicode+i+2, len); // no copying! + switch (param_string[i+1].toLatin1()) + { + case 'm': + { + double scale = param_value.toDouble(&ok); + if (ok && scale >= 0) + georef.setScaleDenominator(qRound(scale)); + } + break; + case 'a': + { + double angle = param_value.toDouble(&ok); + if (ok && qAbs(angle) >= 0.01) + georef.setGrivation(angle); + } + break; + case 'x': + proj_ref_point.setX(param_value.toDouble(&x_ok)); + break; + case 'y': + proj_ref_point.setY(param_value.toDouble(&y_ok)); + break; + case 'd': + { + auto spacing = param_value.toDouble(&ok); + if (ok && spacing >= 0.001) + { + auto grid = map->getGrid(); + grid.setUnit(MapGrid::MetersInTerrain); + grid.setHorizontalSpacing(spacing); + grid.setVerticalSpacing(spacing); + map->setGrid(grid); + } + } + break; + case 'i': + combined_grid_zone = param_value; + break; + case '\t': + // empty item, fall through + default: + ; // nothing + } + i = next_i; + } + + if (!combined_grid_zone.isEmpty()) + { + applyGridAndZone(georef, combined_grid_zone); + } + + if (x_ok && y_ok) + { + georef.setProjectedRefPoint(proj_ref_point, false); + } + + map->setGeoreferencing(georef); +} + +void OcdFileImport::applyGridAndZone(Georeferencing& georef, const QString& combined_grid_zone) +{ + bool zone_ok = false; + const CRSTemplate* crs_template = nullptr; + QString id; + QString spec; + std::vector values; + + if (combined_grid_zone.startsWith(QLatin1String("20"))) + { + auto zone = combined_grid_zone.midRef(2).toUInt(&zone_ok); + zone_ok &= (zone >= 1 && zone <= 60); + if (zone_ok) + { + id = QLatin1String{"UTM"}; + crs_template = CRSTemplateRegistry().find(id); + values.reserve(1); + values.push_back(QString::number(zone)); + } + } + else if (combined_grid_zone.startsWith(QLatin1String("80"))) + { + auto zone = combined_grid_zone.midRef(2).toUInt(&zone_ok); + if (zone_ok) + { + id = QLatin1String{"Gauss-Krueger, datum: Potsdam"}; + crs_template = CRSTemplateRegistry().find(id); + values.reserve(1); + values.push_back(QString::number(zone)); + } + } + else if (combined_grid_zone == QLatin1String("6005")) + { + id = QLatin1String{"EPSG"}; + crs_template = CRSTemplateRegistry().find(id); + values.reserve(1); + values.push_back(QLatin1String{"3067"}); + } + else if (combined_grid_zone == QLatin1String("14001")) + { + id = QLatin1String{"EPSG"}; + crs_template = CRSTemplateRegistry().find(id); + values.reserve(1); + values.push_back(QLatin1String{"21781"}); + } + else if (combined_grid_zone == QLatin1String("1000")) + { + return; + } + + if (crs_template) + { + spec = crs_template->specificationTemplate(); + auto param = crs_template->parameters().begin(); + for (const auto& value : values) + { + for (const auto& spec_value : (*param)->specValues(value)) + { + spec = spec.arg(spec_value); + } + ++param; + } + } + + if (spec.isEmpty()) + { + addWarning(tr("Could not load the coordinate reference system '%1'.").arg(combined_grid_zone)); + } + else + { + georef.setProjectedCRS(id, spec, std::move(values)); + } +} + + +void OcdFileImport::importColors(const OcdFile& file) +{ + const Ocd::SymbolHeaderV8 & symbol_header = file.header()->symbol_header; + int num_colors = symbol_header.num_colors; + + for (int i = 0; i < num_colors && i < 256; i++) + { + const Ocd::ColorInfoV8& color_info = symbol_header.color_info[i]; + const QString name = convertOcdString(color_info.name); + int color_pos = map->getNumColors(); + auto color = new MapColor(name, color_pos); + + // OC*D stores CMYK values as integers from 0-200. + MapColorCmyk cmyk; + cmyk.c = 0.005f * color_info.cmyk.cyan; + cmyk.m = 0.005f * color_info.cmyk.magenta; + cmyk.y = 0.005f * color_info.cmyk.yellow; + cmyk.k = 0.005f * color_info.cmyk.black; + color->setCmyk(cmyk); + color->setOpacity(1.0f); + + map->addColor(color, color_pos); + color_index[color_info.number] = color; + } + + addWarning(OcdFileImport::tr("Spot color information was ignored.")); +} + +template< class F > +void OcdFileImport::importColors(const OcdFile< F >& file) +{ + handleStrings(file, { { 9, &OcdFileImport::importColor } }); + addWarning(OcdFileImport::tr("Spot color information was ignored.")); +} + +void OcdFileImport::importColor(const QString& param_string, int /*ocd_version*/) +{ + const QChar* unicode = param_string.unicode(); + + int i = param_string.indexOf(QLatin1Char('\t'), 0); + const QString name = param_string.left(qMax(-1, i)); // copied + + int number; + bool number_ok = false; + MapColorCmyk cmyk { 0.0, 0.0, 0.0, 0.0 }; + bool overprinting = false; + float opacity = 1.0f; + + while (i >= 0) + { + float f_value; + int i_value; + bool ok; + int next_i = param_string.indexOf(QLatin1Char('\t'), i+1); + int len = (next_i > 0 ? next_i : param_string.length()) - i - 2; + const QString param_value = QString::fromRawData(unicode+i+2, len); // no copying! + switch (param_string[i+1].toLatin1()) + { + case '\t': + // empty item + break; + case 'n': + number = param_value.toInt(&number_ok); + break; + case 'c': + f_value = param_value.toFloat(&ok); + if (ok && f_value >= 0 && f_value <= 100) + cmyk.c = 0.01f * f_value; + break; + case 'm': + f_value = param_value.toFloat(&ok); + if (ok && f_value >= 0 && f_value <= 100) + cmyk.m = 0.01f * f_value; + break; + case 'y': + f_value = param_value.toFloat(&ok); + if (ok && f_value >= 0 && f_value <= 100) + cmyk.y = 0.01f * f_value; + break; + case 'k': + f_value = param_value.toFloat(&ok); + if (ok && f_value >= 0 && f_value <= 100) + cmyk.k = 0.01f * f_value; + break; + case 'o': + i_value = param_value.toInt(&ok); + if (ok) + overprinting = i_value; + break; + case 't': + f_value = param_value.toFloat(&ok); + if (ok && f_value >= 0.f && f_value <= 100.f) + opacity = 0.01f * f_value; + break; + default: + ; // nothing + } + i = next_i; + } + + if (!number_ok) + return; + + int color_pos = map->getNumColors(); + auto color = new MapColor(name, color_pos); + color->setCmyk(cmyk); + color->setKnockout(!overprinting); + color->setOpacity(opacity); + map->addColor(color, color_pos); + color_index[number] = color; +} + +namespace { + quint16 symbolType(const Ocd::BaseSymbolV8& ocd_symbol) + { + if (ocd_symbol.type == Ocd::SymbolTypeLine && ocd_symbol.type2 == 1) + return Ocd::SymbolTypeLineText; + return ocd_symbol.type; + } + + template< class T > + quint8 symbolType(const T& ocd_symbol) + { + return ocd_symbol.type; + } +} + +template< class F > +void OcdFileImport::importSymbols(const OcdFile< F >& file) +{ + auto ocd_version = file.header()->version; + for (const auto& ocd_symbol : file.symbols()) + { + // When extra symbols are created, we want to insert the main symbol + // before them. That is why pos needs to be determined first. + auto pos = map->getNumSymbols(); + + Symbol* symbol = nullptr; + switch (symbolType(ocd_symbol)) + { + case Ocd::SymbolTypePoint: + symbol = importPointSymbol(reinterpret_cast(ocd_symbol), ocd_version); + break; + case Ocd::SymbolTypeLine: + symbol = importLineSymbol(reinterpret_cast(ocd_symbol), ocd_version); + break; + case Ocd::SymbolTypeArea: + symbol = importAreaSymbol(reinterpret_cast(ocd_symbol), ocd_version); + break; + case Ocd::SymbolTypeText: + symbol = importTextSymbol(reinterpret_cast(ocd_symbol), ocd_version); + break; + case Ocd::SymbolTypeRectangle_V8: + case Ocd::SymbolTypeRectangle_V9: + symbol = importRectangleSymbol(reinterpret_cast(ocd_symbol)); + break; + case Ocd::SymbolTypeLineText: + symbol = importLineTextSymbol(reinterpret_cast(ocd_symbol), ocd_version); + break; + default: + addWarning(OcdFileImport::tr("Unable to import symbol %1.%2 \"%3\": %4") . + arg(ocd_symbol.number / F::BaseSymbol::symbol_number_factor) . + arg(ocd_symbol.number % F::BaseSymbol::symbol_number_factor) . + arg(convertOcdString(ocd_symbol.description)). + arg(OcdFileImport::tr("Unsupported type \"%1\".").arg(ocd_symbol.type)) ); + continue; + } + + map->addSymbol(symbol, pos); + symbol_index[ocd_symbol.number] = symbol; + } + resolveSubsymbols(); +} + +void OcdFileImport::resolveSubsymbols() +{ + for (auto i = 0; i < map->getNumSymbols(); ++i) + { + auto symbol = map->getSymbol(i); + if (symbol->getType() == Symbol::Combined) + { + auto combined = symbol->asCombined(); + if (combined->getNumParts() == 2) + { + auto number = combined->getPart(1)->getNumberComponent(2); + if (number >= 0 && symbol_index.contains(static_cast(number))) + { + combined->setPart(1, symbol_index[static_cast(number)], false); + } + } + } + } +} + + +void OcdFileImport::importObjects(const OcdFile& file) +{ + auto ocd_version = file.header()->version; + MapPart* part = map->getCurrentPart(); + Q_ASSERT(part); + + for (const auto& object_entry : file.objects()) + { + if (object_entry.symbol) + { + if (auto object = importObject(file[object_entry], part, ocd_version)) + part->addObject(object, part->getNumObjects()); + } + } +} + +template< class F > +void OcdFileImport::importObjects(const OcdFile< F >& file) +{ + auto ocd_version = file.header()->version; + MapPart* part = map->getCurrentPart(); + Q_ASSERT(part); + + for (const auto& object_entry : file.objects()) + { + if ( object_entry.symbol + && object_entry.status != Ocd::ObjectDeleted + && object_entry.status != Ocd::ObjectDeletedForUndo ) + { + if (auto object = importObject(file[object_entry], part, ocd_version)) + part->addObject(object, part->getNumObjects()); + } + } +} + + +template< class F > +void OcdFileImport::importTemplates(const OcdFile< F >& file) +{ + handleStrings(file, { { 8, &OcdFileImport::importTemplate } }); +} + +void OcdFileImport::importTemplate(const QString& param_string, int ocd_version) +{ + const QChar* unicode = param_string.unicode(); + + int i = param_string.indexOf(QLatin1Char('\t'), 0); + const QString filename = QString::fromRawData(unicode, qMax(-1, i)); + const QString clean_path = QDir::cleanPath(QString(filename).replace(QLatin1Char('\\'), QLatin1Char('/'))); + const QString extension = QFileInfo(clean_path).suffix().toLower(); + + Template* templ = nullptr; + if (extension.compare(QLatin1String("ocd")) == 0) + { + templ = new TemplateMap(clean_path, map); + } + else if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) + { + templ = new TemplateImage(clean_path, map); + } + else + { + addWarning(tr("Unable to import template: \"%1\" is not a supported template type.").arg(filename)); + return; + } + + // 8 or 9 or 10 ? Only tested with 8 and 11 + double scale_factor = (ocd_version <= 8) ? 0.01 : 1.0; + unsigned int num_rotation_params = 0; + double rotation = 0.0; + double scale_x = 1.0; + double scale_y = 1.0; + int dimming = 0; + bool visible = false; + + while (i >= 0) + { + double value; + bool ok; + int next_i = param_string.indexOf(QLatin1Char('\t'), i+1); + int len = (next_i > 0 ? next_i : param_string.length()) - i - 2; + const QString param_value = QString::fromRawData(unicode+i+2, len); // no copying! + switch (param_string[i+1].toLatin1()) + { + case '\t': + // empty item + break; + case 'x': + value = param_value.toDouble(&ok); + if (ok) + templ->setTemplateX(qRound64(value*1000*scale_factor)); + break; + case 'y': + value = param_value.toDouble(&ok); + if (ok) + templ->setTemplateY(-qRound64(value*1000*scale_factor)); + break; + case 'a': + case 'b': + // TODO: use the distinct angles correctly, not just the average + rotation += param_value.toDouble(&ok); + if (ok) + ++num_rotation_params; + break; + case 'u': + value = param_value.toDouble(&ok); + if (ok && qAbs(value) >= 0.0000000001) + scale_x = value; + break; + case 'v': + value = param_value.toDouble(&ok); + if (ok && qAbs(value) >= 0.0000000001) + scale_y = value; + break; + case 'd': + dimming = param_value.toInt(); + break; + case 's': + visible = param_value.toInt(); + break; + default: + ; // nothing + } + i = next_i; + } + + if (num_rotation_params) + templ->setTemplateRotation(Georeferencing::degToRad(rotation / num_rotation_params)); + + templ->setTemplateScaleX(scale_x * scale_factor); + templ->setTemplateScaleY(scale_y * scale_factor); + + int template_pos = map->getFirstFrontTemplate(); + map->addTemplate(templ, 0); + map->setFirstFrontTemplate(template_pos+1); + + if (view) + { + auto opacity = qMax(0.0, qMin(1.0, 0.01 * (100 - dimming))); + view->setTemplateVisibility(templ, { float(opacity), visible }); + } +} + + +void OcdFileImport::importExtras(const OcdFile& file) +{ + const Ocd::FileHeaderV8* header = file.header(); + map->setMapNotes(convertOcdString< Ocd::FormatV8::Encoding >(file.byteArray().data() + header->info_pos, header->info_size)); +} + +template< class F > +void OcdFileImport::importExtras(const OcdFile< F >& file) +{ + map->setMapNotes({ }); + handleStrings(file, extraStringHandlers); +} + +const std::initializer_list OcdFileImport::extraStringHandlers = { + { 11, &OcdFileImport::appendNotes }, + { 1061, &OcdFileImport::appendNotes } +}; + +void OcdFileImport::appendNotes(const QString& param_string, int ocd_version) +{ + QString notes = map->getMapNotes(); + notes.append(param_string); + if (ocd_version <= 10) + notes.append(QLatin1Char('\n')); + map->setMapNotes(notes); +} + + +void OcdFileImport::importView(const OcdFile& file) +{ + if (view) + { + const Ocd::FileHeaderV8* header = file.header(); + const Ocd::SetupV8* setup = reinterpret_cast< const Ocd::SetupV8* >(file.byteArray().data() + header->setup_pos); + + if (setup->zoom >= MapView::zoom_out_limit && setup->zoom <= MapView::zoom_in_limit) + view->setZoom(setup->zoom); + + view->setCenter(convertOcdPoint(setup->center)); + } +} + +template< class F > +void OcdFileImport::importView(const OcdFile< F >& file) +{ + handleStrings(file, { { 1030, &OcdFileImport::importView } }); +} + +void OcdFileImport::importView(const QString& param_string, int /*ocd_version*/) +{ + const QChar* unicode = param_string.unicode(); + + bool zoom_ok = false; + double zoom=1.0, offset_x=0.0, offset_y=0.0; + + int i = param_string.indexOf(QLatin1Char('\t'), 0); + ; // skip first word for this entry type + while (i >= 0) + { + int next_i = param_string.indexOf(QLatin1Char('\t'), i+1); + int len = (next_i > 0 ? next_i : param_string.length()) - i - 2; + const QString param_value = QString::fromRawData(unicode+i+2, len); // no copying! + switch (param_string[i+1].toLatin1()) + { + case '\t': + // empty item + break; + case 'x': + { + offset_x = param_value.toDouble(); + break; + } + case 'y': + { + offset_y = param_value.toDouble(); + break; + } + case 'z': + { + zoom = param_value.toDouble(&zoom_ok); + break; + } + default: + ; // nothing + } + i = next_i; + } + + if (view) + { + view->setCenter(MapCoord(offset_x, -offset_y)); + if (zoom_ok) + { + view->setZoom(zoom); + } + } +} + + +template< class S > +void OcdFileImport::setupBaseSymbol(Symbol* symbol, const S& ocd_symbol) +{ + typedef typename S::BaseSymbol BaseSymbol; + const BaseSymbol& base_symbol = ocd_symbol.base; + // common fields are name, number, description, helper_symbol, hidden/protected status + symbol->setName(convertOcdString(base_symbol.description)); + symbol->setNumberComponent(0, base_symbol.number / BaseSymbol::symbol_number_factor); + symbol->setNumberComponent(1, base_symbol.number % BaseSymbol::symbol_number_factor); + symbol->setNumberComponent(2, -1); + symbol->setIsHelperSymbol(false); + symbol->setProtected(base_symbol.status & Ocd::SymbolProtected); + symbol->setHidden(base_symbol.status & Ocd::SymbolHidden); +} + +template< class S > +PointSymbol* OcdFileImport::importPointSymbol(const S& ocd_symbol, int ocd_version) +{ + auto symbol = new OcdImportedPointSymbol(); + setupBaseSymbol(symbol, ocd_symbol); + setupPointSymbolPattern(symbol, ocd_symbol.data_size, ocd_symbol.begin_of_elements, ocd_version); + symbol->setRotatable(ocd_symbol.base.flags & 1); + return symbol; +} + +template< class S > +Symbol* OcdFileImport::importLineSymbol(const S& ocd_symbol, int ocd_version) +{ + using LineStyle = Ocd::LineSymbolCommonV8; + + OcdImportedLineSymbol* line_for_borders = nullptr; + + // Import a main line? + OcdImportedLineSymbol* main_line = nullptr; + if (ocd_symbol.common.double_mode == 0 || ocd_symbol.common.line_width > 0) + { + main_line = importLineSymbolBase(ocd_symbol.common); + setupBaseSymbol(main_line, ocd_symbol); + line_for_borders = main_line; + } + + // Import a 'framing' line? + OcdImportedLineSymbol* framing_line = nullptr; + if (ocd_symbol.common.framing_width > 0 && ocd_version >= 7) + { + framing_line = importLineSymbolFraming(ocd_symbol.common, main_line); + setupBaseSymbol(framing_line, ocd_symbol); + if (!line_for_borders) + line_for_borders = framing_line; + } + + // Import a 'double' line? + bool has_border_line = + (ocd_symbol.common.double_mode != 0) && + (ocd_symbol.common.double_left_width > 0 || ocd_symbol.common.double_right_width > 0); + OcdImportedLineSymbol *double_line = nullptr; + if (ocd_symbol.common.double_flags & LineStyle::DoubleFillColorOn + || (has_border_line && !line_for_borders) ) + { + double_line = importLineSymbolDoubleBorder(ocd_symbol.common); + setupBaseSymbol(double_line, ocd_symbol); + line_for_borders = double_line; + } + else if (ocd_symbol.common.double_flags & LineStyle::DoubleBackgroundColorOn) + { + auto symbol = std::unique_ptr(importLineSymbolDoubleBorder(ocd_symbol.common)); + addSymbolWarning(symbol.get(), + OcdFileImport::tr("Unsupported line style '%1'.").arg(QLatin1String("LineStyle::DoubleBackgroundColorOn")) ); + } + + // Border lines + if (has_border_line) + { + Q_ASSERT(line_for_borders); + setupLineSymbolForBorder(line_for_borders, ocd_symbol.common); + } + + // Create point symbols along line; middle ("normal") dash, corners, start, and end. + OcdImportedLineSymbol* symbol_line = main_line ? main_line : double_line; // Find the line to attach the symbols to + if (!symbol_line) + { + main_line = new OcdImportedLineSymbol(); + symbol_line = main_line; + setupBaseSymbol(main_line, ocd_symbol); + + main_line->segment_length = convertLength(ocd_symbol.common.main_length); + main_line->end_length = convertLength(ocd_symbol.common.end_length); + } + + setupLineSymbolPointSymbol(symbol_line, ocd_symbol.common, ocd_symbol.begin_of_elements, ocd_version); + + // TODO: taper fields (tmode and tlast) + + if (!main_line && !framing_line) + { + return double_line; + } + else if (!double_line && !framing_line) + { + return main_line; + } + else if (!main_line && !double_line) + { + return framing_line; + } + else + { + auto full_line = new CombinedSymbol(); + setupBaseSymbol(full_line, ocd_symbol); + mergeLineSymbol(full_line, main_line, framing_line, double_line); + addSymbolWarning(symbol_line, OcdFileImport::tr("This symbol cannot be saved as a proper OCD symbol again.")); + return full_line; + } +} + +OcdFileImport::OcdImportedLineSymbol* OcdFileImport::importLineSymbolBase(const Ocd::LineSymbolCommonV8& attributes) +{ + using LineStyle = Ocd::LineSymbolCommonV8; + + // Basic line options + auto symbol = new OcdImportedLineSymbol(); + symbol->line_width = convertLength(attributes.line_width); + symbol->color = convertColor(attributes.line_color); + + // Cap and join styles + switch (attributes.line_style) + { + default: + addSymbolWarning( symbol, + tr("Unsupported line style '%1'."). + arg(attributes.line_style) ); + // fall through + case LineStyle::BevelJoin_FlatCap: + symbol->join_style = LineSymbol::BevelJoin; + symbol->cap_style = LineSymbol::FlatCap; + break; + case LineStyle::RoundJoin_RoundCap: + symbol->join_style = LineSymbol::RoundJoin; + symbol->cap_style = LineSymbol::RoundCap; + break; + case LineStyle::BevelJoin_PointedCap: + symbol->join_style = LineSymbol::BevelJoin; + symbol->cap_style = LineSymbol::PointedCap; + break; + case LineStyle::RoundJoin_PointedCap: + symbol->join_style = LineSymbol::RoundJoin; + symbol->cap_style = LineSymbol::PointedCap; + break; + case LineStyle::MiterJoin_FlatCap: + symbol->join_style = LineSymbol::MiterJoin; + symbol->cap_style = LineSymbol::FlatCap; + break; + case LineStyle::MiterJoin_PointedCap: + symbol->join_style = LineSymbol::MiterJoin; + symbol->cap_style = LineSymbol::PointedCap; + break; + } + + if (symbol->cap_style == LineSymbol::PointedCap) + { + auto ocd_length = attributes.dist_from_start; + if (attributes.dist_from_start != attributes.dist_from_end) + { + // FIXME: Different lengths for start and end length of pointed line ends are not supported yet, so take the average + ocd_length = (attributes.dist_from_start + attributes.dist_from_end) / 2; + addSymbolWarning( symbol, + tr("Different lengths for pointed caps at begin (%1 mm) and end (%2 mm) are not supported. Using %3 mm."). + arg(locale.toString(0.001f * convertLength(attributes.dist_from_start)), + locale.toString(0.001f * convertLength(attributes.dist_from_end)), + locale.toString(0.001f * convertLength(ocd_length))) ); + } + symbol->pointed_cap_length = convertLength(ocd_length); + symbol->join_style = LineSymbol::RoundJoin; // NOTE: while the setting may be different (see what is set in the first place), OC*D always draws round joins if the line cap is pointed! + } + else if (attributes.dist_from_start > 0 || attributes.dist_from_end > 0) + { + addSymbolWarning(symbol, + tr("Distances from start (%1 mm) or end (%2 mm) are not supported.") + .arg(locale.toString(0.001f * convertLength(attributes.dist_from_start)), + locale.toString(0.001f * convertLength(attributes.dist_from_end))) ); + } + + // Handle the dash pattern + if (attributes.main_gap || attributes.sec_gap) + { + if (!attributes.main_length) + { + // Invalid dash pattern + addSymbolWarning( symbol, + tr("The dash pattern cannot be imported correctly.") ); + } + else if (attributes.sec_gap && !attributes.main_gap) + { + // Special case main_gap == 0 + symbol->dashed = true; + symbol->dash_length = convertLength(attributes.main_length) - convertLength(attributes.sec_gap); + symbol->break_length = convertLength(attributes.sec_gap); + + if (attributes.end_length) + { + if (qAbs(qint32(attributes.main_length) - 2*attributes.end_length) > 1) + { + // End length not equal to 0.5 * main length + addSymbolWarning( symbol, + tr("The dash pattern's end length (%1 mm) cannot be imported correctly. Using %2 mm."). + arg(locale.toString(0.001f * convertLength(attributes.end_length)), + locale.toString(0.001f * symbol->dash_length)) ); + } + if (attributes.end_gap) + { + addSymbolWarning( symbol, + tr("The dash pattern's end gap (%1 mm) cannot be imported correctly. Using %2 mm."). + arg(locale.toString(0.001f * convertLength(attributes.end_gap)), + locale.toString(0.001f * symbol->break_length)) ); + } + } + } + else + { + // Standard case + symbol->dashed = true; + symbol->dash_length = convertLength(attributes.main_length); + symbol->break_length = convertLength(attributes.main_gap); + + if (attributes.end_length && attributes.end_length != attributes.main_length) + { + if (attributes.main_length && 0.75 >= double(attributes.end_length) / double(attributes.main_length)) + { + // End length max. 75 % of main length + symbol->half_outer_dashes = true; + } + + if (qAbs(qint32(attributes.main_length) - 2*attributes.end_length) > 1) + { + // End length not equal to 0.5 * main length + addSymbolWarning( symbol, + tr("The dash pattern's end length (%1 mm) cannot be imported correctly. Using %2 mm."). + arg(locale.toString(0.001f * convertLength(attributes.end_length)), + locale.toString(0.001f * (symbol->half_outer_dashes ? (symbol->dash_length/2) : symbol->dash_length))) ); + } + } + + if (attributes.sec_gap) + { + symbol->dashes_in_group = 2; + symbol->in_group_break_length = convertLength(attributes.sec_gap); + symbol->dash_length = (symbol->dash_length - symbol->in_group_break_length) / 2; + + if (attributes.end_length && attributes.end_gap != attributes.sec_gap) + { + addSymbolWarning( symbol, + tr("The dash pattern's end gap (%1 mm) cannot be imported correctly. Using %2 mm."). + arg(locale.toString(0.001f * convertLength(attributes.end_gap)), + locale.toString(0.001f * symbol->in_group_break_length)) ); + } + } + } + } + else + { + symbol->segment_length = convertLength(attributes.main_length); + symbol->end_length = convertLength(attributes.end_length); + } + + return symbol; +} + +OcdFileImport::OcdImportedLineSymbol* OcdFileImport::importLineSymbolFraming(const Ocd::LineSymbolCommonV8& attributes, const LineSymbol* main_line) +{ + using LineStyle = Ocd::LineSymbolCommonV8; + + // Basic line options + auto framing_line = new OcdImportedLineSymbol(); + framing_line->line_width = convertLength(attributes.framing_width); + framing_line->color = convertColor(attributes.framing_color); + + // Cap and join styles + switch (attributes.framing_style) + { + case LineStyle::BevelJoin_FlatCap: + framing_line->join_style = LineSymbol::BevelJoin; + framing_line->cap_style = LineSymbol::FlatCap; + break; + case LineStyle::RoundJoin_RoundCap: + framing_line->join_style = LineSymbol::RoundJoin; + framing_line->cap_style = LineSymbol::RoundCap; + break; + case LineStyle::MiterJoin_FlatCap: + framing_line->join_style = LineSymbol::MiterJoin; + framing_line->cap_style = LineSymbol::FlatCap; + break; + default: + addSymbolWarning( main_line, + tr("Unsupported framing line style '%1'."). + arg(attributes.line_style) ); + } + + return framing_line; +} + +OcdFileImport::OcdImportedLineSymbol* OcdFileImport::importLineSymbolDoubleBorder(const Ocd::LineSymbolCommonV8& attributes) +{ + using LineStyle = Ocd::LineSymbolCommonV8; + + auto double_line = new OcdImportedLineSymbol(); + double_line->line_width = convertLength(attributes.double_width); + double_line->cap_style = LineSymbol::FlatCap; + double_line->join_style = LineSymbol::MiterJoin; + double_line->segment_length = convertLength(attributes.main_length); + double_line->end_length = convertLength(attributes.end_length); + + if (attributes.double_flags & LineStyle::DoubleFillColorOn) + double_line->color = convertColor(attributes.double_color); + else + double_line->color = nullptr; + + return double_line; +} + +void OcdFileImport::setupLineSymbolForBorder(OcdFileImport::OcdImportedLineSymbol* line_for_borders, const Ocd::LineSymbolCommonV8& attributes) +{ + line_for_borders->have_border_lines = true; + LineSymbolBorder& border = line_for_borders->getBorder(); + LineSymbolBorder& right_border = line_for_borders->getRightBorder(); + + // Border color and width + border.color = convertColor(attributes.double_left_color); + border.width = convertLength(attributes.double_left_width); + border.shift = convertLength(attributes.double_left_width) / 2 + (convertLength(attributes.double_width) - line_for_borders->line_width) / 2; + + right_border.color = convertColor(attributes.double_right_color); + right_border.width = convertLength(attributes.double_right_width); + right_border.shift = convertLength(attributes.double_right_width) / 2 + (convertLength(attributes.double_width) - line_for_borders->line_width) / 2; + + // The borders may be dashed + if (attributes.double_gap > 0 && attributes.double_mode > 1) + { + border.dashed = true; + border.dash_length = convertLength(attributes.double_length); + border.break_length = convertLength(attributes.double_gap); + + // If ocd_symbol->dmode == 2, only the left border should be dashed + if (attributes.double_mode > 2) + { + right_border.dashed = border.dashed; + right_border.dash_length = border.dash_length; + right_border.break_length = border.break_length; + } + } +} + +void OcdFileImport::setupLineSymbolPointSymbol(OcdFileImport::OcdImportedLineSymbol* line_symbol, const Ocd::LineSymbolCommonV8& attributes, const Ocd::PointSymbolElementV8* elements, int ocd_version) +{ + const Ocd::OcdPoint32* coords = reinterpret_cast(elements); + + line_symbol->mid_symbols_per_spot = attributes.num_prim_sym; + line_symbol->mid_symbol_distance = convertLength(attributes.prim_sym_dist); + line_symbol->mid_symbol = new OcdImportedPointSymbol(); + setupPointSymbolPattern(line_symbol->mid_symbol, attributes.primary_data_size, elements, ocd_version); + coords += attributes.primary_data_size; + + if (attributes.secondary_data_size > 0) + { + //symbol_line->dash_symbol = importPattern( ocd_symbol->ssnpts, symbolptr); + coords += attributes.secondary_data_size; + addSymbolWarning(line_symbol, tr("Skipped secondary point symbol.")); + } + if (attributes.corner_data_size > 0) + { + line_symbol->dash_symbol = new OcdImportedPointSymbol(); + setupPointSymbolPattern(line_symbol->dash_symbol, attributes.corner_data_size, reinterpret_cast(coords), ocd_version); + line_symbol->dash_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Dash symbol")); + coords += attributes.corner_data_size; + } + if (attributes.start_data_size > 0) + { + line_symbol->start_symbol = new OcdImportedPointSymbol(); + setupPointSymbolPattern(line_symbol->start_symbol, attributes.start_data_size, reinterpret_cast(coords), ocd_version); + line_symbol->start_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "Start symbol")); + coords += attributes.start_data_size; + } + if (attributes.end_data_size > 0) + { + line_symbol->end_symbol = new OcdImportedPointSymbol(); + setupPointSymbolPattern(line_symbol->end_symbol, attributes.end_data_size, reinterpret_cast(coords), ocd_version); + line_symbol->end_symbol->setName(QCoreApplication::translate("OpenOrienteering::LineSymbolSettings", "End symbol")); + } + + // FIXME: not really sure how this translates... need test cases + line_symbol->minimum_mid_symbol_count = 0; //1 + ocd_symbol->smin; + line_symbol->minimum_mid_symbol_count_when_closed = 0; //1 + ocd_symbol->smin; + line_symbol->show_at_least_one_symbol = false; // NOTE: this works in a different way than OC*D's 'at least X symbols' setting (per-segment instead of per-object) + + // Suppress dash symbol at line ends if both start symbol and end symbol exist, + // but don't create a warning unless a dash symbol is actually defined + // and the line symbol is not Mapper's 799 Simple orienteering course. + if (line_symbol->start_symbol && line_symbol->end_symbol) + { + line_symbol->setSuppressDashSymbolAtLineEnds(true); + if (line_symbol->dash_symbol && line_symbol->getNumberComponent(0) != 799) + { + addSymbolWarning(line_symbol, tr("Suppressing dash symbol at line ends.")); + } + } +} + +void OcdFileImport::mergeLineSymbol(CombinedSymbol* full_line, LineSymbol* main_line, LineSymbol* framing_line, LineSymbol* double_line) +{ + full_line->setNumParts(3); // reserve + int part = 0; + if (main_line) + { + full_line->setPart(part++, main_line, true); + main_line->setHidden(false); + main_line->setProtected(false); + } + if (double_line) + { + full_line->setPart(part++, double_line, true); + double_line->setHidden(false); + double_line->setProtected(false); + } + if (framing_line) + { + full_line->setPart(part++, framing_line, true); + framing_line->setHidden(false); + framing_line->setProtected(false); + } + full_line->setNumParts(part); +} + +Symbol* OcdFileImport::importAreaSymbol(const Ocd::AreaSymbolV8& ocd_symbol, int ocd_version) +{ + Q_ASSERT(ocd_version <= 8); + auto symbol = new OcdImportedAreaSymbol(); + setupBaseSymbol(symbol, ocd_symbol); + setupAreaSymbolCommon( + symbol, + ocd_symbol.fill_on, + ocd_symbol.common, + ocd_symbol.data_size, + ocd_symbol.begin_of_elements, + ocd_version); + return symbol; +} + +template< class S > +Symbol* OcdFileImport::importAreaSymbol(const S& ocd_symbol, int ocd_version) +{ + Q_ASSERT(ocd_version >= 9); + auto symbol = new OcdImportedAreaSymbol(); + setupBaseSymbol(symbol, ocd_symbol); + setupAreaSymbolCommon( + symbol, + ocd_symbol.common.fill_on_V9, + ocd_symbol.common, + ocd_symbol.data_size, + ocd_symbol.begin_of_elements, + ocd_version); + if (!ocd_symbol.common.border_on_V9) + { + return symbol; + } + + if (ocd_symbol.border_symbol == ocd_symbol.base.number) + { + addSymbolWarning(symbol, OcdFileImport::tr("The border of this symbol could not be loaded.")); + return symbol; + } + + auto combined = new CombinedSymbol(); + setupBaseSymbol(combined, ocd_symbol); + combined->setNumParts(2); + combined->setPart(0, symbol, true); + auto border = map->getUndefinedLine()->duplicate(); + border->setNumberComponent(0, symbol->getNumberComponent(0)); + border->setNumberComponent(1, symbol->getNumberComponent(1)); + border->setNumberComponent(2, static_cast(ocd_symbol.border_symbol)); + combined->setPart(1, border, true); + addSymbolWarning(symbol, OcdFileImport::tr("This symbol cannot be saved as a proper OCD symbol again.")); + return combined; +} + +void OcdFileImport::setupAreaSymbolCommon(OcdImportedAreaSymbol* symbol, bool fill_on, const Ocd::AreaSymbolCommonV8& ocd_symbol, std::size_t data_size, const Ocd::PointSymbolElementV8* elements, int ocd_version) +{ + // Basic area symbol fields: minimum_area, color + symbol->minimum_area = 0; + symbol->color = fill_on ? convertColor(ocd_symbol.fill_color) : nullptr; + symbol->patterns.clear(); + symbol->patterns.reserve(4); + + // Hatching + if (ocd_symbol.hatch_mode != Ocd::HatchNone) + { + AreaSymbol::FillPattern pattern; + pattern.type = AreaSymbol::FillPattern::LinePattern; + pattern.angle = convertAngle(ocd_symbol.hatch_angle_1); + pattern.setRotatable(true); + pattern.line_spacing = convertLength(ocd_symbol.hatch_dist); + pattern.line_offset = 0; + pattern.line_color = convertColor(ocd_symbol.hatch_color); + pattern.line_width = convertLength(ocd_symbol.hatch_line_width); + if (ocd_version <= 8) + { + pattern.line_spacing += pattern.line_width; + } + symbol->patterns.push_back(pattern); + + if (ocd_symbol.hatch_mode == Ocd::HatchCross) + { + // Second hatch, same as the first, just a different angle + pattern.angle = convertAngle(ocd_symbol.hatch_angle_2); + symbol->patterns.push_back(pattern); + } + } + + if (ocd_symbol.structure_mode != Ocd::StructureNone) + { + AreaSymbol::FillPattern pattern; + pattern.type = AreaSymbol::FillPattern::PointPattern; + pattern.angle = convertAngle(ocd_symbol.structure_angle); + pattern.setRotatable(true); + pattern.point_distance = convertLength(ocd_symbol.structure_width); + pattern.line_spacing = convertLength(ocd_symbol.structure_height); + pattern.line_offset = 0; + pattern.offset_along_line = 0; + // FIXME: somebody needs to own this symbol and be responsible for deleting it + // Right now it looks like a potential memory leak + pattern.point = new OcdImportedPointSymbol(); + setupPointSymbolPattern(pattern.point, data_size, elements, ocd_version); + + // OC*D 8 has a "staggered" pattern mode, where successive rows are shifted width/2 relative + // to each other. We need to simulate this in Mapper with two overlapping patterns, each with + // twice the height. The second is then offset by width/2, height/2. + if (ocd_symbol.structure_mode == Ocd::StructureShiftedRows) + { + pattern.line_spacing *= 2; + symbol->patterns.push_back(pattern); + + pattern.line_offset = pattern.line_spacing / 2; + pattern.offset_along_line = pattern.point_distance / 2; + pattern.point = pattern.point->duplicate()->asPoint(); + } + symbol->patterns.push_back(pattern); + } +} + +template< class S > +TextSymbol* OcdFileImport::importTextSymbol(const S& ocd_symbol, int /*ocd_version*/) +{ + auto symbol = new OcdImportedTextSymbol(); + setupBaseSymbol(symbol, ocd_symbol); + setBasicAttributes(symbol, convertOcdString(ocd_symbol.font_name), ocd_symbol.basic); + setSpecialAttributes(symbol, ocd_symbol.special); + setFraming(symbol, ocd_symbol.framing); + return symbol; +} + +template< class S > +TextSymbol* OcdFileImport::importLineTextSymbol(const S& ocd_symbol, int /*ocd_version*/) +{ + auto symbol = new OcdImportedTextSymbol(); + setupBaseSymbol(symbol, ocd_symbol); + setBasicAttributes(symbol, convertOcdString(ocd_symbol.font_name), ocd_symbol.basic); + setFraming(symbol, ocd_symbol.framing); + + addSymbolWarning(symbol, OcdFileImport::tr("Line text symbols are not yet supported. Marking the symbol as hidden.")); + symbol->setHidden(true); + return symbol; +} + +template< class S > +LineSymbol* OcdFileImport::importRectangleSymbol(const S& ocd_symbol) +{ + auto symbol = new OcdImportedLineSymbol(); + setupBaseSymbol(symbol, ocd_symbol); + + symbol->line_width = convertLength(ocd_symbol.line_width); + symbol->color = convertColor(ocd_symbol.line_color); + symbol->cap_style = LineSymbol::RoundCap; + symbol->join_style = LineSymbol::RoundJoin; + + auto rect = RectangleInfo(); + rect.border_line = symbol; + rect.corner_radius = 0.001 * convertLength(ocd_symbol.corner_radius); + rect.has_grid = ocd_symbol.grid_flags & 1; + + if (rect.has_grid) + { + auto inner_line = new OcdImportedLineSymbol(); + setupBaseSymbol(inner_line, ocd_symbol); + inner_line->setNumberComponent(2, 1); // TODO: Dynamic + inner_line->line_width = qRound(1000 * 0.15); + inner_line->color = symbol->color; + map->addSymbol(inner_line, map->getNumSymbols()); + + auto text = new OcdImportedTextSymbol(); + setupBaseSymbol(text, ocd_symbol); + text->setNumberComponent(2, 2); // TODO: Dynamic + text->font_family = QString::fromLatin1("Arial"); + text->font_size = qRound(1000 * (15 / 72.0 * 25.4)); + text->color = symbol->color; + text->bold = true; + text->updateQFont(); + map->addSymbol(text, map->getNumSymbols()); + + rect.inner_line = inner_line; + rect.text = text; + rect.number_from_bottom = ocd_symbol.grid_flags & 2; + rect.cell_width = 0.001 * convertLength(ocd_symbol.cell_width); + rect.cell_height = 0.001 * convertLength(ocd_symbol.cell_height); + rect.unnumbered_cells = ocd_symbol.unnumbered_cells; + rect.unnumbered_text = convertOcdString(ocd_symbol.unnumbered_text); + } + rectangle_info.insert(ocd_symbol.base.number, rect); + + return symbol; +} + +void OcdFileImport::setupPointSymbolPattern(PointSymbol* symbol, std::size_t data_size, const Ocd::PointSymbolElementV8* elements, int version) +{ + Q_ASSERT(symbol); + + symbol->setRotatable(true); + bool base_symbol_used = false; + + for (std::size_t i = 0; i < data_size; i += 2) + { + const Ocd::PointSymbolElementV8* element = reinterpret_cast(&reinterpret_cast(elements)[i]); + const Ocd::OcdPoint32* const coords = reinterpret_cast(elements) + i + 2; + switch (element->type) + { + case Ocd::PointSymbolElementV8::TypeDot: + if (element->diameter > 0) + { + bool can_use_base_symbol = (!base_symbol_used && (!element->num_coords || (!coords[0].x && !coords[0].y))); + PointSymbol* working_symbol = can_use_base_symbol ? symbol : new PointSymbol(); + working_symbol->setInnerColor(convertColor(element->color)); + working_symbol->setInnerRadius(convertLength(element->diameter) / 2); + working_symbol->setOuterColor(nullptr); + working_symbol->setOuterWidth(0); + if (can_use_base_symbol) + { + base_symbol_used = true; + } + else + { + working_symbol->setRotatable(false); + auto element_object = new PointObject(working_symbol); + if (element->num_coords) + { + const MapCoord coord = convertOcdPoint(coords[0]); + element_object->setPosition(coord.nativeX(), coord.nativeY()); + } + symbol->addElement(symbol->getNumElements(), element_object, working_symbol); + } + } + break; + case Ocd::PointSymbolElementV8::TypeCircle: + { + decltype(element->diameter) element_radius = + (version <= 8) ? (element->diameter / 2 - element->line_width) + : ((element->diameter - element->line_width) / 2); + if (element_radius > 0 && element->line_width > 0) + { + bool can_use_base_symbol = (!base_symbol_used && (!element->num_coords || (!coords[0].x && !coords[0].y))); + PointSymbol* working_symbol = can_use_base_symbol ? symbol : new PointSymbol(); + working_symbol->setInnerColor(nullptr); + working_symbol->setInnerRadius(convertLength(element_radius)); + working_symbol->setOuterColor(convertColor(element->color)); + working_symbol->setOuterWidth(convertLength(element->line_width)); + if (can_use_base_symbol) + { + base_symbol_used = true; + } + else + { + working_symbol->setRotatable(false); + auto element_object = new PointObject(working_symbol); + if (element->num_coords) + { + const MapCoord coord = convertOcdPoint(coords[0]); + element_object->setPosition(coord.nativeX(), coord.nativeY()); + } + symbol->addElement(symbol->getNumElements(), element_object, working_symbol); + } + } + break; + } + case Ocd::PointSymbolElementV8::TypeLine: + if (element->line_width > 0) + { + auto element_symbol = new OcdImportedLineSymbol(); + element_symbol->line_width = convertLength(element->line_width); + element_symbol->color = convertColor(element->color); + // The flags variable doesn't seem to contain individual flags. + switch (element->flags) + { + default: + qDebug("Ocd::PointSymbolElementV8: Unknown flags value %d", element->flags); + // fall through + case Ocd::PointSymbolElementV8::NoFlags: + element_symbol->setCapStyle(LineSymbol::FlatCap); + element_symbol->setJoinStyle(LineSymbol::BevelJoin); + break; + case Ocd::PointSymbolElementV8::RoundStyle: + element_symbol->setCapStyle(LineSymbol::RoundCap); + element_symbol->setJoinStyle(LineSymbol::RoundJoin); + break; + case Ocd::PointSymbolElementV8::FlatMiterStyle: + element_symbol->setCapStyle(LineSymbol::FlatCap); + element_symbol->setJoinStyle(LineSymbol::MiterJoin); + break; + } + auto element_object = new OcdImportedPathObject(element_symbol); + fillPathCoords(element_object, false, element->num_coords, coords); + element_object->recalculateParts(); + symbol->addElement(symbol->getNumElements(), element_object, element_symbol); + } + break; + case Ocd::PointSymbolElementV8::TypeArea: + { + auto element_symbol = new OcdImportedAreaSymbol(); + element_symbol->color = convertColor(element->color); + auto element_object = new OcdImportedPathObject(element_symbol); + fillPathCoords(element_object, true, element->num_coords, coords); + element_object->recalculateParts(); + symbol->addElement(symbol->getNumElements(), element_object, element_symbol); + } + break; + default: + ; // TODO: not-supported warning + } + i += element->num_coords; + } +} + +template< class O > +Object* OcdFileImport::importObject(const O& ocd_object, MapPart* part, int ocd_version) +{ + Symbol* symbol = nullptr; + if (ocd_object.symbol >= 0) + { + symbol = symbol_index[ocd_object.symbol]; + } + + if (!symbol) + { + switch (ocd_object.type) + { + case 1: + symbol = map->getUndefinedPoint(); + break; + case 2: + case 3: + symbol = map->getUndefinedLine(); + break; + case 4: + case 5: + symbol = map->getUndefinedText(); + break; + default: + addWarning(OcdFileImport::tr("Unable to load object")); + qDebug() << "Undefined object type" << ocd_object.type << " for object of symbol" << ocd_object.symbol; + return nullptr; + } + } + + if (symbol->getType() == Symbol::Line && rectangle_info.contains(ocd_object.symbol)) + { + Object* object = importRectangleObject(ocd_object, part, rectangle_info[ocd_object.symbol]); + if (!object) + addWarning(OcdFileImport::tr("Unable to import rectangle object")); + return object; + } + + if (symbol->getType() == Symbol::Point) + { + auto p = new PointObject(); + p->setSymbol(symbol, true); + + // extra properties: rotation + auto point_symbol = reinterpret_cast(symbol); + if (point_symbol->isRotatable()) + { + p->setRotation(convertAngle(ocd_object.angle)); + } + else if (ocd_object.angle != 0) + { + if (!point_symbol->isSymmetrical()) + { + point_symbol->setRotatable(true); + p->setRotation(convertAngle(ocd_object.angle)); + } + } + + const MapCoord pos = convertOcdPoint(ocd_object.coords[0]); + p->setPosition(pos.nativeX(), pos.nativeY()); + + p->setMap(map); + return p; + } + else if (symbol->getType() == Symbol::Text) + { + auto t = new TextObject(symbol); + t->setText(getObjectText(ocd_object, ocd_version)); + t->setRotation(convertAngle(ocd_object.angle)); + t->setHorizontalAlignment(text_halign_map.value(symbol)); + // Vertical alignment is set in fillTextPathCoords(). + + // Text objects need special path translation + if (!fillTextPathCoords(t, reinterpret_cast(symbol), ocd_object.num_items, reinterpret_cast(ocd_object.coords))) + { + addWarning(OcdFileImport::tr("Not importing text symbol, couldn't figure out path' (npts=%1): %2") + .arg(ocd_object.num_items).arg(t->getText())); + delete t; + return nullptr; + } + t->setMap(map); + return t; + } + else if (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined) + { + auto p = new OcdImportedPathObject(symbol); + p->setPatternRotation(convertAngle(ocd_object.angle)); + + // Normal path + fillPathCoords(p, symbol->getType() == Symbol::Area, ocd_object.num_items, reinterpret_cast(ocd_object.coords)); + p->recalculateParts(); + p->setMap(map); + return p; + } + + return nullptr; +} + +QString OcdFileImport::getObjectText(const Ocd::ObjectV8& ocd_object, int ocd_version) const +{ + auto input = ocd_object.coords + ocd_object.num_items; + auto maxlen = uint(sizeof(Ocd::OcdPoint32) * ocd_object.num_text); + QString object_text; + if (ocd_object.unicode && ocd_version >= 8) + { + object_text = convertOcdString(reinterpret_cast(input), maxlen/2); + } + else + { + object_text = convertOcdString(reinterpret_cast(input), maxlen); + } + + // Remove leading "\r\n" + if (object_text.startsWith(QLatin1String("\r\n"))) + { + object_text.remove(0, 2); + } + + return object_text; +} + +template< class O > +inline +QString OcdFileImport::getObjectText(const O& ocd_object, int /*ocd_version*/) const +{ + auto data = reinterpret_cast(ocd_object.coords + ocd_object.num_items); + if (data[0] == QLatin1Char{'\r'} && data[1] == QLatin1Char{'\n'}) + data += 2; + return QString(data); +} + + +template< class O > +Object* OcdFileImport::importRectangleObject(const O& ocd_object, MapPart* part, const OcdFileImport::RectangleInfo& rect) +{ + if (ocd_object.num_items != 4) + { + qDebug() << "importRectangleObject called with num_items =" << ocd_object.num_items << "for object of symbol" << ocd_object.symbol; + if (ocd_object.num_items != 5) // 5 coords are handled like 4 coords now + return nullptr; + } + return importRectangleObject(ocd_object.coords, part, rect); +} + +Object* OcdFileImport::importRectangleObject(const Ocd::OcdPoint32* ocd_points, MapPart* part, const OcdFileImport::RectangleInfo& rect) +{ + // Convert corner points + MapCoord bottom_left = convertOcdPoint(ocd_points[0]); + MapCoord bottom_right = convertOcdPoint(ocd_points[1]); + MapCoord top_right = convertOcdPoint(ocd_points[2]); + MapCoord top_left = convertOcdPoint(ocd_points[3]); + + MapCoordF top_left_f = MapCoordF(top_left); + MapCoordF top_right_f = MapCoordF(top_right); + MapCoordF bottom_left_f = MapCoordF(bottom_left); + MapCoordF bottom_right_f = MapCoordF(bottom_right); + MapCoordF right = MapCoordF(top_right.x() - top_left.x(), top_right.y() - top_left.y()); + auto angle = right.angle(); + MapCoordF down = MapCoordF(bottom_left.x() - top_left.x(), bottom_left.y() - top_left.y()); + right.normalize(); + down.normalize(); + + // Create border line + MapCoordVector coords; + if (qIsNull(rect.corner_radius)) + { + coords.emplace_back(top_left); + coords.emplace_back(top_right); + coords.emplace_back(bottom_right); + coords.emplace_back(bottom_left); + } + else + { + double handle_radius = (1 - BEZIER_KAPPA) * rect.corner_radius; + coords.emplace_back(top_right_f - right * rect.corner_radius, MapCoord::CurveStart); + coords.emplace_back(top_right_f - right * handle_radius); + coords.emplace_back(top_right_f + down * handle_radius); + coords.emplace_back(top_right_f + down * rect.corner_radius); + coords.emplace_back(bottom_right_f - down * rect.corner_radius, MapCoord::CurveStart); + coords.emplace_back(bottom_right_f - down * handle_radius); + coords.emplace_back(bottom_right_f - right * handle_radius); + coords.emplace_back(bottom_right_f - right * rect.corner_radius); + coords.emplace_back(bottom_left_f + right * rect.corner_radius, MapCoord::CurveStart); + coords.emplace_back(bottom_left_f + right * handle_radius); + coords.emplace_back(bottom_left_f - down * handle_radius); + coords.emplace_back(bottom_left_f - down * rect.corner_radius); + coords.emplace_back(top_left_f + down * rect.corner_radius, MapCoord::CurveStart); + coords.emplace_back(top_left_f + down * handle_radius); + coords.emplace_back(top_left_f + right * handle_radius); + coords.emplace_back(top_left_f + right * rect.corner_radius); + } + auto border_path = new PathObject(rect.border_line, coords, map); + border_path->parts().front().setClosed(true, false); + + if (rect.has_grid && rect.cell_width > 0 && rect.cell_height > 0) + { + // Calculate grid sizes + auto width = top_left.distanceTo(top_right); + auto height = top_left.distanceTo(bottom_left); + int num_cells_x = qMax(1, qRound(width / rect.cell_width)); + int num_cells_y = qMax(1, qRound(height / rect.cell_height)); + + auto cell_width = width / num_cells_x; + auto cell_height = height / num_cells_y; + + // Create grid lines + coords.resize(2); + for (int x = 1; x < num_cells_x; ++x) + { + coords[0] = MapCoord(top_left_f + x * cell_width * right); + coords[1] = MapCoord(bottom_left_f + x * cell_width * right); + + auto path = new PathObject(rect.inner_line, coords, map); + part->addObject(path, part->getNumObjects()); + } + for (int y = 1; y < num_cells_y; ++y) + { + coords[0] = MapCoord(top_left_f + y * cell_height * down); + coords[1] = MapCoord(top_right_f + y * cell_height * down); + + auto path = new PathObject(rect.inner_line, coords, map); + part->addObject(path, part->getNumObjects()); + } + + // Create grid text + if (height >= rect.cell_height / 2) + { + for (int y = 0; y < num_cells_y; ++y) + { + for (int x = 0; x < num_cells_x; ++x) + { + int cell_num; + QString cell_text; + + if (rect.number_from_bottom) + cell_num = y * num_cells_x + x + 1; + else + cell_num = (num_cells_y - 1 - y) * num_cells_x + x + 1; + + if (cell_num > num_cells_x * num_cells_y - rect.unnumbered_cells) + cell_text = rect.unnumbered_text; + else + cell_text = QString::number(cell_num); + + auto object = new TextObject(rect.text); + object->setMap(map); + object->setText(cell_text); + object->setRotation(-angle); + object->setHorizontalAlignment(TextObject::AlignLeft); + object->setVerticalAlignment(TextObject::AlignTop); + auto position_x = (x + qreal(0.07)) * cell_width; + auto position_y = (y + qreal(0.04)) * cell_height + rect.text->getFontMetrics().ascent() / rect.text->calculateInternalScaling() - rect.text->getFontSize(); + object->setAnchorPosition(top_left_f + position_x * right + position_y * down); + part->addObject(object, part->getNumObjects()); + + //pts[0].Y -= rectinfo.gridText.FontAscent - rectinfo.gridText.FontEmHeight; + } + } + } + } + + return border_path; +} + +void OcdFileImport::setPathHolePoint(OcdImportedPathObject *object, quint32 pos) +{ + // Look for curve start points before the current point and apply hole point only if no such point is there. + // This prevents hole points in the middle of a curve caused by incorrect map objects. + if (pos >= 1 && object->coords[pos].isCurveStart()) + ; //object->coords[i-1].setHolePoint(true); + else if (pos >= 2 && object->coords[pos-1].isCurveStart()) + ; //object->coords[i-2].setHolePoint(true); + else if (pos >= 3 && object->coords[pos-2].isCurveStart()) + ; //object->coords[i-3].setHolePoint(true); + else if (pos > 0) // Don't start with hole point. + object->coords[pos].setHolePoint(true); +} + +void OcdFileImport::setPointFlags(OcdImportedPathObject* object, quint32 pos, bool is_area, const Ocd::OcdPoint32& ocd_point) +{ + // We can support CurveStart, HolePoint, DashPoint. + // CurveStart needs to be applied to the main point though, not the control point, and + // hole points need to bet set as the last point of a part of an area object instead of the first point of the next part + if (ocd_point.x & Ocd::OcdPoint32::FlagCtl1 && pos > 0) + object->coords[pos-1].setCurveStart(true); + if ((ocd_point.y & Ocd::OcdPoint32::FlagDash) || (ocd_point.y & Ocd::OcdPoint32::FlagCorner)) + object->coords[pos].setDashPoint(true); + if (ocd_point.y & Ocd::OcdPoint32::FlagHole) + { + if (!is_area) + setPathHolePoint(object, pos); + else if (pos > 0) + setPathHolePoint(object, pos - 1); + } +} + +/** Translates the OC*D path given in the last two arguments into an Object. + */ +void OcdFileImport::fillPathCoords(OcdImportedPathObject *object, bool is_area, quint32 num_points, const Ocd::OcdPoint32* ocd_points) +{ + object->coords.resize(num_points); + for (auto i = 0u; i < num_points; i++) + { + object->coords[i] = convertOcdPoint(ocd_points[i]); + setPointFlags(object, i, is_area, ocd_points[i]); + } + + // For path objects, create closed parts where the position of the last point is equal to that of the first point + if (object->getType() == Object::Path) + { + size_t start = 0; + for (size_t i = 0; i < object->coords.size(); ++i) + { + if (!object->coords[i].isHolePoint() && i < object->coords.size() - 1) + continue; + + if (object->coords[i].isPositionEqualTo(object->coords[start])) + { + MapCoord coord = object->coords[start]; + coord.setCurveStart(false); + coord.setHolePoint(true); + coord.setClosePoint(true); + object->coords[i] = coord; + } + + switch (i - start) + { + default: + object->coords[i-2].setCurveStart(false); + // fall through + case 1: + object->coords[i-1].setCurveStart(false); + // fall through + case 0: + ; // nothing + } + + start = i + 1; + } + } +} + +/** Translates an OCAD text object path into a Mapper text object specifier, if possible. + * If successful, sets either 1 or 2 coordinates in the text object and returns true. + * If the OCAD path was not importable, leaves the TextObject alone and returns false. + */ +bool OcdFileImport::fillTextPathCoords(TextObject *object, TextSymbol *symbol, quint32 npts, const Ocd::OcdPoint32 *ocd_points) +{ + // text objects either have 1 point (free anchor) or 2 (midpoint/size) + // OCAD appears to always have 5 or 4 points (possible single anchor, then 4 corner coordinates going clockwise from anchor). + if (npts == 0) return false; + + if (npts == 4) + { + // Box text + MapCoord bottom_left = convertOcdPoint(ocd_points[0]); + MapCoord top_right = convertOcdPoint(ocd_points[2]); + MapCoord top_left = convertOcdPoint(ocd_points[3]); + + // According to Purple Pen source code: OC*D adds an extra internal leading (incorrectly). + QFontMetricsF metrics = symbol->getFontMetrics(); + double top_adjust = -symbol->getFontSize() + (metrics.ascent() + metrics.descent() + 0.5) / symbol->calculateInternalScaling(); + + MapCoordF adjust_vector = MapCoordF(top_adjust * sin(object->getRotation()), top_adjust * cos(object->getRotation())); + top_left = MapCoord(top_left.x() + adjust_vector.x(), top_left.y() + adjust_vector.y()); + top_right = MapCoord(top_right.x() + adjust_vector.x(), top_right.y() + adjust_vector.y()); + + object->setBox((bottom_left.nativeX() + top_right.nativeX()) / 2, (bottom_left.nativeY() + top_right.nativeY()) / 2, + top_left.distanceTo(top_right), top_left.distanceTo(bottom_left)); + object->setVerticalAlignment(TextObject::AlignTop); + } + else + { + // Single anchor text + if (npts != 5) + addWarning(tr("Trying to import a text object with unknown coordinate format")); + + // anchor point + MapCoord coord = convertOcdPoint(ocd_points[0]); + object->setAnchorPosition(coord.nativeX(), coord.nativeY()); + object->setVerticalAlignment(text_valign_map.value(symbol)); + } + + return true; +} + +void OcdFileImport::setBasicAttributes(OcdFileImport::OcdImportedTextSymbol* symbol, const QString& font_name, const Ocd::BasicTextAttributesV8& attributes) +{ + symbol->font_family = font_name; + symbol->color = convertColor(attributes.color); + symbol->font_size = qRound(100.0 * attributes.font_size / 72.0 * 25.4); + symbol->bold = (attributes.font_weight>= 550); + symbol->italic = attributes.font_italic; + symbol->underline = false; + symbol->kerning = false; + symbol->line_below = false; + symbol->custom_tabs.resize(0); + + if (attributes.font_weight != 400 && attributes.font_weight != 700) + { + addSymbolWarning(symbol, tr("Ignoring custom weight (%1).").arg(attributes.font_weight)); + } + + switch (attributes.alignment & Ocd::HAlignMask) + { + case Ocd::HAlignLeft: + text_halign_map[symbol] = TextObject::AlignLeft; + break; + case Ocd::HAlignRight: + text_halign_map[symbol] = TextObject::AlignRight; + break; + case Ocd::HAlignJustified: + /// \todo Implement justified alignment + addSymbolWarning(symbol, tr("Justified alignment is not supported.")); + // fall through + default: + text_halign_map[symbol] = TextObject::AlignHCenter; + } + + switch (attributes.alignment & Ocd::VAlignMask) + { + case Ocd::VAlignTop: + text_valign_map[symbol] = TextObject::AlignTop; + break; + case Ocd::VAlignMiddle: + text_valign_map[symbol] = TextObject::AlignVCenter; + break; + default: + addSymbolWarning(symbol, tr("Vertical alignment '%1' is not supported.").arg(attributes.alignment & Ocd::VAlignMask)); + // fall through + case Ocd::VAlignBottom: + text_valign_map[symbol] = TextObject::AlignBaseline; + } + + if (attributes.char_spacing != 0) + { + symbol->character_spacing = attributes.char_spacing / 100.0f; + addSymbolWarning(symbol, tr("Custom character spacing may be incorrect.")); + } + + if (attributes.word_spacing != 100) + { + addSymbolWarning(symbol, tr("Ignoring custom word spacing (%1 %).").arg(attributes.word_spacing)); + } + + symbol->updateQFont(); +} + +void OcdFileImport::setSpecialAttributes(OcdFileImport::OcdImportedTextSymbol* symbol, const Ocd::SpecialTextAttributesV8& attributes) +{ + // Convert line spacing + double absolute_line_spacing = 0.00001 * symbol->font_size * attributes.line_spacing; + symbol->line_spacing = absolute_line_spacing / (symbol->getFontMetrics().lineSpacing() / symbol->calculateInternalScaling()); + symbol->paragraph_spacing = convertLength(attributes.para_spacing); + + symbol->line_below = attributes.line_below_on; + symbol->line_below_color = convertColor(attributes.line_below_color); + symbol->line_below_width = convertLength(attributes.line_below_width); + symbol->line_below_distance = convertLength(attributes.line_below_offset); + + symbol->custom_tabs.resize(attributes.num_tabs); + for (auto i = 0u; i < attributes.num_tabs; ++i) + symbol->custom_tabs[i] = convertLength(attributes.tab_pos[i]); + + if (attributes.indent_first_line != 0 || attributes.indent_other_lines != 0) + { + addSymbolWarning(symbol, tr("Ignoring custom indents (%1/%2).").arg(attributes.indent_first_line).arg(attributes.indent_other_lines)); + } +} + +void OcdFileImport::setFraming(OcdFileImport::OcdImportedTextSymbol* symbol, const Ocd::FramingAttributesV8& framing) +{ + switch (framing.mode) + { + case Ocd::FramingShadow: + symbol->framing = true; + symbol->framing_mode = TextSymbol::ShadowFraming; + symbol->framing_color = convertColor(framing.color); + symbol->framing_shadow_x_offset = convertLength(framing.offset_x); + symbol->framing_shadow_y_offset = -1 * convertLength(framing.offset_y); + break; + case Ocd::FramingLine: // since V7 + symbol->framing = true; + symbol->framing_mode = TextSymbol::LineFraming; + symbol->framing_line_half_width = convertLength(framing.line_width); + break; + case Ocd::FramingRectangle: + default: + addSymbolWarning(symbol, tr("Ignoring text framing (mode %1).").arg(framing.mode)); + // fall through + case Ocd::FramingNone: + symbol->framing = false; + } +} + +void OcdFileImport::import(bool load_symbols_only) +{ + Q_ASSERT(buffer.isEmpty()); + + buffer.clear(); + buffer.append(stream->readAll()); + if (buffer.isEmpty()) + throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(stream->errorString())); + + if (size_t(buffer.size()) < sizeof(Ocd::FormatGeneric::FileHeader)) + throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(tr("Invalid data."))); + + const OcdFile generic_file(buffer); + if (generic_file.header()->vendor_mark != 0x0cad) // This also tests correct endianess... + throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read file: %1").arg(tr("Invalid data."))); + + int version = generic_file.header()->version; + switch (version) + { + case 6: + case 7: + case 8: + // Note: Version 6 and 7 do have some differences, which will need to be + // handled in the version 8 implementation by looking up the + // actual format version in the file header. + if (Settings::getInstance().getSetting(Settings::General_NewOcd8Implementation).toBool()) + importImplementation< Ocd::FormatV8 >(load_symbols_only); + else + importImplementationLegacy(load_symbols_only); + break; + case 9: + case 10: + using FormatV10Assumption = std::is_same; + Q_STATIC_ASSERT(FormatV10Assumption::value); + importImplementation< Ocd::FormatV9 >(load_symbols_only); + break; + case 11: + importImplementation< Ocd::FormatV11 >(load_symbols_only); + break; + case 12: + importImplementation< Ocd::FormatV12 >(load_symbols_only); + break; + default: + throw FileFormatException( + ::OpenOrienteering::Importer::tr("Could not read file: %1"). + arg(tr("OCD files of version %1 are not supported!").arg(version)) + ); + } +} + +void OcdFileImport::finishImport() +{ + if (delegate) + { + // The current warnings and actions are already propagated. + auto warnings_size = ptrdiff_t(delegate->warnings().size()); + auto actions_size = ptrdiff_t(delegate->actions().size()); + + delegate->finishImport(); + + // Propagate new warnings and actions from the delegate to this importer. + std::for_each(begin(delegate->warnings()) + warnings_size, end(delegate->warnings()), [this](const QString& w) { addWarning(w); }); + std::for_each(begin(delegate->actions()) + actions_size, end(delegate->actions()), [this](const ImportAction& a) { addAction(a); }); + } +} + +template< class F > +void OcdFileImport::handleStrings(const OcdFile& file, std::initializer_list handlers) +{ + for (const auto& string : file.strings()) + { + for (const auto& handler : handlers) + { + if (string.type == handler.type) + { + (this->*handler.callback)(convertOcdString(file[string]), file.header()->version); + } + } + } +} + + +} // namespace OpenOrienteering diff --git a/src/fileformats/ocd_file_import.h b/src/fileformats/ocd_file_import.h new file mode 100644 index 0000000..a6fc543 --- /dev/null +++ b/src/fileformats/ocd_file_import.h @@ -0,0 +1,465 @@ +/* + * Copyright 2013-2016 Kai Pastor + * + * Some parts taken from file_format_oc*d8{.h,_p.h,cpp} which are + * Copyright 2012 Pete Curtis + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_FILE_IMPORT +#define OPENORIENTEERING_OCD_FILE_IMPORT + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map_coord.h" +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/text_symbol.h" +#include "fileformats/file_import_export.h" +#include "fileformats/ocd_types.h" +#include "fileformats/ocd_types_v8.h" // IWYU pragma: keep + +class QChar; +class QIODevice; + +namespace OpenOrienteering { + +class CombinedSymbol; +class Georeferencing; +class Map; +class MapColor; +class MapPart; +class MapView; +class OCAD8FileImport; +class Symbol; + + +/** + * An map file importer for OC*D files. + */ +class OcdFileImport : public Importer +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OcdFileImport) + +protected: + /// Information about an OC*D rectangle symbol + struct RectangleInfo + { + LineSymbol* border_line; + double corner_radius; + bool has_grid; + + // Only valid if has_grid is true + LineSymbol* inner_line; + TextSymbol* text; + bool number_from_bottom; + double cell_width; + double cell_height; + int unnumbered_cells; + QString unnumbered_text; + }; + + // Helper classes that provide to core classes' protected members + + class OcdImportedAreaSymbol : public AreaSymbol + { + friend class OcdFileImport; + }; + + class OcdImportedLineSymbol : public LineSymbol + { + friend class OcdFileImport; + }; + + class OcdImportedPointSymbol : public PointSymbol + { + friend class OcdFileImport; + }; + + class OcdImportedTextSymbol : public TextSymbol + { + friend class OcdFileImport; + }; + + class OcdImportedPathObject : public PathObject + { + friend class OcdFileImport; + + public: + OcdImportedPathObject(Symbol* symbol = nullptr) : PathObject(symbol) { } + OcdImportedPathObject(const OcdImportedPathObject&) = delete; + OcdImportedPathObject(OcdImportedPathObject&&) = delete; + OcdImportedPathObject& operator=(const OcdImportedPathObject&) = delete; + OcdImportedPathObject& operator=(OcdImportedPathObject&&) = delete; + ~OcdImportedPathObject() override; + }; + +public: + OcdFileImport(QIODevice* stream, Map *map, MapView *view); + + ~OcdFileImport() override; + + + void setCustom8BitEncoding(QTextCodec* encoding); + + + template< std::size_t N > + QString convertOcdString(const Ocd::PascalString& src) const; + + template< std::size_t N > + QString convertOcdString(const Ocd::Utf8PascalString& src) const; + + template< std::size_t N > + QString convertOcdString(const Ocd::Utf16PascalString& src) const; + + template< class E > + QString convertOcdString(const char* src, uint len) const; + + template< class E > + QString convertOcdString(const QByteArray& data) const; + + QString convertOcdString(const QChar* src, uint maxlen) const; + + MapCoord convertOcdPoint(const Ocd::OcdPoint32& ocd_point) const; + + float convertAngle(int ocd_angle) const; + + int convertLength(qint16 ocd_length) const; + + int convertLength(quint16 ocd_length) const; + + template< class T, class R = qint64 > + R convertLength(T ocd_length) const; + + MapColor* convertColor(int ocd_color); + + void addSymbolWarning(const AreaSymbol* symbol, const QString& warning); + + void addSymbolWarning(const LineSymbol* symbol, const QString& warning); + + void addSymbolWarning(const TextSymbol* symbol, const QString& warning); + + void finishImport() override; + +protected: + void import(bool load_symbols_only) override; + + void importImplementationLegacy(bool load_symbols_only); + + template< class F > + void importImplementation(bool load_symbols_only); + + + struct StringHandler + { + using Callback = void (OcdFileImport::*)(const QString&, int); + qint32 type; + Callback callback; + }; + + template< class F > + void handleStrings(const OcdFile< F >& file, std::initializer_list handlers); + + + void importGeoreferencing(const OcdFile& file); + + template< class F > + void importGeoreferencing(const OcdFile< F >& file); + + /// Imports string 1039. + void importGeoreferencing(const QString& param_string, int ocd_version); + + /// Imports string 1039 field i. + void applyGridAndZone(Georeferencing& georef, const QString& combined_grid_zone); + + + void importColors(const OcdFile& file); + + template< class F > + void importColors(const OcdFile< F >& file); + + void importColor(const QString& param_string, int ocd_version); + + + template< class F > + void importSymbols(const OcdFile< F >& file); + + void resolveSubsymbols(); + + + void importObjects(const OcdFile& file); + + template< class F > + void importObjects(const OcdFile< F >& file); + + + template< class F > + void importTemplates(const OcdFile< F >& file); + + void importTemplate(const QString& param_string, int ocd_version); + + + void importExtras(const OcdFile& file); + + template< class F > + void importExtras(const OcdFile< F >& file); + + static const std::initializer_list extraStringHandlers; + + void appendNotes(const QString& param_string, int ocd_version); + + + void importView(const OcdFile& file); + + template< class F > + void importView(const OcdFile< F >& file); + + void importView(const QString& param_string, int ocd_version); + + + // Symbol import + + template< class S > + PointSymbol* importPointSymbol(const S& ocd_symbol, int ocd_version); + + template< class S > + Symbol* importLineSymbol(const S& ocd_symbol, int ocd_version); + + OcdImportedLineSymbol* importLineSymbolBase(const Ocd::LineSymbolCommonV8& attributes); + + OcdImportedLineSymbol* importLineSymbolFraming(const Ocd::LineSymbolCommonV8& attributes, const LineSymbol* main_line); + + OcdImportedLineSymbol* importLineSymbolDoubleBorder(const Ocd::LineSymbolCommonV8& attributes); + + void setupLineSymbolForBorder(OcdImportedLineSymbol* line_for_borders, const Ocd::LineSymbolCommonV8& attributes); + + void setupLineSymbolPointSymbol(OcdImportedLineSymbol* line_symbol, const Ocd::LineSymbolCommonV8& attributes, const Ocd::PointSymbolElementV8* elements, int ocd_version); + + void mergeLineSymbol(CombinedSymbol* full_line, LineSymbol* main_line, LineSymbol* framing_line, LineSymbol* double_line); + + Symbol* importAreaSymbol(const Ocd::AreaSymbolV8& ocd_symbol, int ocd_version); + + template< class S > + Symbol* importAreaSymbol(const S& ocd_symbol, int ocd_version); + + void setupAreaSymbolCommon( + OcdImportedAreaSymbol* symbol, + bool fill_on, + const Ocd::AreaSymbolCommonV8& ocd_symbol, + std::size_t data_size, + const Ocd::PointSymbolElementV8* elements, + int ocd_version + ); + + template< class S > + TextSymbol* importTextSymbol(const S& ocd_symbol, int ocd_version); + + template< class S > + TextSymbol* importLineTextSymbol(const S& ocd_symbol, int ocd_version); + + template< class S > + LineSymbol* importRectangleSymbol(const S& ocd_symbol); + + template< class S > + void setupBaseSymbol(Symbol* symbol, const S& ocd_symbol); + + void setupPointSymbolPattern(PointSymbol* symbol, std::size_t data_size, const Ocd::PointSymbolElementV8* elements, int version); + + + // Object import + + template< class O > + Object* importObject(const O& ocd_object, MapPart* part, int ocd_version); + + QString getObjectText(const Ocd::ObjectV8& ocd_object, int ocd_version) const; + + template< class O > + QString getObjectText(const O& ocd_object, int ocd_version) const; + + template< class O > + Object* importRectangleObject(const O& ocd_object, MapPart* part, const OcdFileImport::RectangleInfo& rect); + + Object* importRectangleObject(const Ocd::OcdPoint32* ocd_points, MapPart* part, const OcdFileImport::RectangleInfo& rect); + + // Some helper functions that are used in multiple places + + void setPointFlags(OcdImportedPathObject* object, quint32 pos, bool is_area, const Ocd::OcdPoint32& ocd_point); + + void setPathHolePoint(OcdFileImport::OcdImportedPathObject* object, quint32 pos); + + void fillPathCoords(OcdFileImport::OcdImportedPathObject* object, bool is_area, quint32 num_points, const Ocd::OcdPoint32* ocd_points); + + bool fillTextPathCoords(TextObject* object, TextSymbol* symbol, quint32 npts, const Ocd::OcdPoint32* ocd_points); + + void setBasicAttributes(OcdImportedTextSymbol* symbol, const QString& font_name, const Ocd::BasicTextAttributesV8& attributes); + + void setSpecialAttributes(OcdImportedTextSymbol* symbol, const Ocd::SpecialTextAttributesV8& attributes); + + void setFraming(OcdImportedTextSymbol* symbol, const Ocd::FramingAttributesV8& framing); + +protected: + /// The locale is used for number formatting. + QLocale locale; + + QByteArray buffer; + + QScopedPointer< OCAD8FileImport > delegate; + + /// Character encoding to use for 1-byte (narrow) strings + QTextCodec *custom_8bit_encoding; + + /// maps OCD color number to oo-mapper color object + QHash color_index; + + /// maps OCD symbol number to oo-mapper symbol object + QHash symbol_index; + + /// maps OO Mapper text symbol pointer to OCD defined horizontal alignment (stored in objects instead of symbols in OO Mapper) + QHash text_halign_map; + + /// maps OO Mapper text symbol pointer to OCD defined vertical alignment (stored in objects instead of symbols in OO Mapper) + QHash text_valign_map; + + /// maps OCD symbol number to rectangle information struct + QHash rectangle_info; +}; + + + +// ### OcdFileImport inline code ### + +template< std::size_t N > +QString OcdFileImport::convertOcdString(const Ocd::PascalString& src) const +{ + return custom_8bit_encoding->toUnicode(src.data, src.length); +} + +template< std::size_t N > +QString OcdFileImport::convertOcdString(const Ocd::Utf8PascalString& src) const +{ + return QString::fromUtf8(src.data, src.length); +} + +template< std::size_t N > +QString OcdFileImport::convertOcdString(const Ocd::Utf16PascalString& src) const +{ + Q_STATIC_ASSERT(N <= std::numeric_limits::max() / 2); + return convertOcdString(src.data, N); +} + +template< > +inline +QString OcdFileImport::convertOcdString< Ocd::Custom8BitEncoding >(const char* src, uint len) const +{ + len = qMin(uint(std::numeric_limits::max()), qstrnlen(src, len)); + return custom_8bit_encoding->toUnicode(src, int(len)); +} + +template< > +inline +QString OcdFileImport::convertOcdString< Ocd::Utf8Encoding >(const char* src, uint len) const +{ + len = qMin(uint(std::numeric_limits::max()), qstrnlen(src, len)); + return QString::fromUtf8(src, int(len)); +} + +template< class E > +QString OcdFileImport::convertOcdString(const QByteArray& data) const +{ + return OcdFileImport::convertOcdString< E >(data.constData(), data.length()); +} + +inline +MapCoord OcdFileImport::convertOcdPoint(const Ocd::OcdPoint32& ocd_point) const +{ + qint32 ocad_x = ocd_point.x >> 8; + qint32 ocad_y = ocd_point.y >> 8; + // Recover from broken coordinate export from Mapper 0.6.2 ... 0.6.4 (#749) + // Cf. broken::convertPointMember in file_format_ocad8.cpp: + // The values -4 ... -1 (-0.004 mm ... -0.001 mm) were converted to 0x80000000u instead of 0. + // This is the maximum value. Thus it is okay to assume it won't occur in regular data, + // and we can safely replace it with 0 here. + // But the input parameter were already subject to right shift ... + constexpr auto invalid_value = qint32(0x80000000u) >> 8; // ... so we use this value here. + if (ocad_x == invalid_value) + ocad_x = 0; + if (ocad_y == invalid_value) + ocad_y = 0; + return MapCoord::fromNative(ocad_x * 10, ocad_y * -10); +} + +inline +float OcdFileImport::convertAngle(int ocd_angle) const +{ + // OC*D uses tenths of a degree, counterclockwise + // BUG: if sin(rotation) is < 0 for a hatched area pattern, the pattern's createRenderables() will go into an infinite loop. + // So until that's fixed, we keep a between 0 and PI + return (M_PI / 1800) * ((ocd_angle + 3600) % 3600); +} + +inline +int OcdFileImport::convertLength(qint16 ocd_length) const +{ + return convertLength(ocd_length); +} + +inline +int OcdFileImport::convertLength(quint16 ocd_length) const +{ + return convertLength(ocd_length); +} + +template< class T, class R > +inline +R OcdFileImport::convertLength(T ocd_length) const +{ + // OC*D uses hundredths of a millimeter. + // oo-mapper uses 1/1000 mm + return static_cast(ocd_length) * 10; +} + +inline +MapColor *OcdFileImport::convertColor(int ocd_color) +{ + if (!color_index.contains(ocd_color)) + { + addWarning(tr("Color id not found: %1, ignoring this color").arg(ocd_color)); + return nullptr; + } + + return color_index[ocd_color]; +} + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_OCD_FILE_IMPORT diff --git a/src/fileformats/ocd_types.cpp b/src/fileformats/ocd_types.cpp new file mode 100644 index 0000000..e4bb5b4 --- /dev/null +++ b/src/fileformats/ocd_types.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2013, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "ocd_types.h" +#include "ocd_types_v8.h" +#include "ocd_types_v9.h" +#include "ocd_types_v10.h" +#include "ocd_types_v11.h" +#include "ocd_types_v12.h" + +namespace Ocd +{ + // Verify at compile time that a double is 8 bytes big. + + Q_STATIC_ASSERT(sizeof(double) == 8); + + // Verify at compile time that data structures are packed, not aligned. + + Q_STATIC_ASSERT(sizeof(FileHeaderGeneric) == 8); + + Q_STATIC_ASSERT(sizeof(FormatV8::FileHeader) - sizeof(SymbolHeaderV8) == 48); + + Q_STATIC_ASSERT(sizeof(FormatV9::FileHeader) == 48); + + Q_STATIC_ASSERT(sizeof(FormatV10::FileHeader) == 48); + + Q_STATIC_ASSERT(sizeof(FormatV11::FileHeader) == 48); + + Q_STATIC_ASSERT(sizeof(FormatV12::FileHeader) == 60); +} diff --git a/src/fileformats/ocd_types.h b/src/fileformats/ocd_types.h new file mode 100644 index 0000000..36e3eee --- /dev/null +++ b/src/fileformats/ocd_types.h @@ -0,0 +1,872 @@ +/* + * Copyright 2013, 2015, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_TYPES_H +#define OPENORIENTEERING_OCD_TYPES_H + +#include + +#include +#include +#include + + +// Helper macro +#define RESERVED_MEMBER_CONCAT2(A,B) A ## B +// Helper macro +#define RESERVED_MEMBER_CONCAT(A,B) RESERVED_MEMBER_CONCAT2(A,B) + +/** + * A macro which generates a unique member name of the format + * reserved_member_LINE_NUMBER. + */ +#define RESERVED_MEMBER RESERVED_MEMBER_CONCAT( reserved_member_ , __LINE__ ) + + +namespace Ocd +{ + /** OCD filetypes */ + enum FileType + { + Map = 0x0000001, + CourseSetting = 0x0000002 + }; + +// This pragma should be supported by msvc, gcc, clang [-fms-compatibility] +#pragma pack(push, 1) + + /** + * A trait for strings and file formats that use a custom 8 bit encoding. + */ + struct Custom8BitEncoding + { + // nothing + }; + + /** + * A trait for strings and file formats that use UTF-8 encoding. + */ + struct Utf8Encoding + { + // nothing + }; + + /** + * A string of max. N characters with a pascal-style binary representation: + * the first byte indicates the length, + * the following N bytes contain the actual character data. + */ + template< std::size_t N > + struct PascalString + { + unsigned char length; + char data[N]; + }; + + /** + * A UTF-8-encoded string of max. N characters with a pascal-style binary representation: + * the first byte indicates the length, + * the following N bytes contain the actual character data. + */ + template< std::size_t N > + struct Utf8PascalString + { + unsigned char length; + char data[N]; + }; + + /** + * A UTF-16LE-encoded string of max. N characters, zero-terminated. + */ + template< std::size_t N > + struct Utf16PascalString + { + QChar data[N]; + }; + + /** + * The generic header at the beginning of all supported OCD file formats. + * + * For implementation efficiency, this header is generalized in comparison + * to the upstream documentation: + * + * - Until V8, the format actually used a 16 bit file type field called + * "section mark", but no file status field. + * - Until V9, the format actually used a 16 bit subversion field, but no + * subsubversion field. + */ + struct FileHeaderGeneric + { + quint16 vendor_mark; + quint8 file_type; /// aka "section mark" until V8 + quint8 file_status_V9; /// \since V9 + quint16 version; + quint8 subversion; + quint8 subsubversion_V10; /// \since V10 + }; + + /** + * An IndexBlock collects 256 index entries, and the file position of the + * next index block if more index entries exist. + */ + template< class E > + struct IndexBlock + { + typedef E IndexEntryType; + + quint32 next_block; + IndexEntryType entries[256]; + }; + + /** + * An index entry for a parameter string. + */ + struct ParameterStringIndexEntry + { + quint32 pos; + quint32 size; + qint32 type; + quint32 obj_index; + }; + + /** + * The parameter string trait. + * + * OCD strings are raw data, so this is more a trait rather than an actual + * structure. + */ + struct ParameterString + { + typedef ParameterStringIndexEntry IndexEntryType; + }; + + /** + * The OCD file point data type. + * + * The coordinates are not raw 32 bit signed integers but contain flags + * in the lowest 8 bits. + */ + struct OcdPoint32 + { + qint32 x; + qint32 y; + + // Flags in x coordinate + enum XFlags + { + FlagCtl1 = 0x01, + FlagCtl2 = 0x02, + FlagLeft = 0x04 + }; + + // Flags in Y coordinate + enum YFlags + { + FlagCorner = 0x01, + FlagHole = 0x02, + FlagRight = 0x04, + FlagDash = 0x08 + }; + }; + +#pragma pack(pop) + + /** + * Symbol type values. + */ + enum SymbolType + { + SymbolTypePoint = 1, + SymbolTypeLine = 2, + SymbolTypeArea = 3, + SymbolTypeText = 4, + SymbolTypeRectangle_V8 = 5, /// Until V8 + SymbolTypeLineText = 6, /// \since V9 + SymbolTypeRectangle_V9 = 7 /// \since V9 + }; + + /** + * Status flags for symbols. + */ + enum SymbolStatus + { + SymbolNormal = 0, + SymbolProtected = 1, + SymbolHidden = 2 + }; + + /** + * Status values for objects. + */ + enum ObjectStatus + { + ObjectDeleted = 0, + ObjectNormal = 1, + ObjectHidden = 2, + ObjectDeletedForUndo = 3 + }; + + /** + * Text alignment flags. + */ + enum TextAlignment + { + HAlignMask = 0x03, + HAlignLeft = 0x00, + HAlignCenter = 0x01, + HAlignRight = 0x02, + HAlignJustified = 0x03, /// All-line for line text symbols + VAlignMask = 0x0c, /// \since V10 + VAlignBottom = 0x00, /// \since V10 + VAlignMiddle = 0x04, /// \since V10 + VAlignTop = 0x08 /// \since V10 + }; + + /** + * Area hatch mode values. + */ + enum HatchMode + { + HatchNone = 0, + HatchSingle = 1, + HatchCross = 2 + }; + + /** + * Area pattern structure values. + */ + enum StructureMode + { + StructureNone = 0, + StructureAlignedRows = 1, + StructureShiftedRows = 2 + }; + + /** + * + */ + enum FramingMode + { + FramingNone = 0, + FramingShadow = 1, /// Somehow different in older versions + FramingLine = 2, /// \since V7 + FramingRectangle = 3 /// \since V8; Not for line text symbols + }; + + /** + * A generic OCD file format trait. + * + * It is suitable for detecting the actual format. + */ + struct FormatGeneric + { + static constexpr int version() { return -1; } + + typedef FileHeaderGeneric FileHeader; + + struct BaseSymbol { typedef quint32 IndexEntryType; }; + + struct Object { typedef quint32 IndexEntryType; }; + }; + +} + + + +// Forward declaration, needed for class FirstIndexBlock. +template< class F > +class OcdFile; + + + +/** + * A template class which provides an operator() returning the first index + * block for a particular OCD entity index. + * + * The generic template is not meant to be used but will trigger an error. + * Specializations must be provided for entity types to be supported. + * + * @param F: the type defining the file format version type + * @param T: the entity type (string, symbol, object) + */ +template< class F, class T > +class FirstIndexBlock +{ +public: + quint32 operator()(const OcdFile* file) const; +}; + +/** + * A template class which provides an operator() returning the first string + * index block. + * + * @param F: the type defining the file format version type + */ +template< class F > +class FirstIndexBlock +{ +public: + quint32 operator()(const OcdFile* file) const; +}; + +/** + * A template class which provides an operator() returning the first symbol + * index block. + * + * @param F: the type defining the file format version type + */ +template< class F > +class FirstIndexBlock +{ +public: + quint32 operator()(const OcdFile* file) const; +}; + +/** + * A template class which provides an operator() returning the first object + * index block. + * + * @param F: the type defining the file format version type + */ +template< class F > +class FirstIndexBlock +{ +public: + quint32 operator()(const OcdFile* file) const; +}; + + + +/** + * A template class which provides an iterator for OCD entity indices. + * + * @param F: the type defining the file format version type + * @param T: the entity type (string, symbol, object) + * @param E: the index entry type + */ +template< class F, class T, class E > +class OcdEntityIndexIterator +{ +public: + typedef F FileFormat; + typedef T EntityType; + typedef E EntryType; + typedef Ocd::IndexBlock IndexBlock; + + OcdEntityIndexIterator(const OcdFile* file, const IndexBlock* first_block); + + const OcdEntityIndexIterator& operator++(); + + const E& operator*() const; + + const E* operator->() const; + + bool operator==(const OcdEntityIndexIterator& rhs) const; + + bool operator!=(const OcdEntityIndexIterator& rhs) const; + +private: + const OcdFile* data; + const IndexBlock* block; + std::size_t index; +}; + + +/** + * A template class which provides an iterator for OCD entity indices, + * specialized for the case where the index entry is just the quint32 file + * position of the entity data. + * + * @param F: the type defining the file format version type + * @param T: the entity type (string, symbol, object) + */ +template< class F, class T > +class OcdEntityIndexIterator +{ +public: + typedef F FileFormat; + typedef T EntityType; + typedef quint32 EntryType; + typedef Ocd::IndexBlock IndexBlock; + + OcdEntityIndexIterator(const OcdFile* file, const IndexBlock* first_block); + + const OcdEntityIndexIterator& operator++(); + + const T& operator*() const; + + const T* operator->() const; + + bool operator==(const OcdEntityIndexIterator& rhs) const; + + bool operator!=(const OcdEntityIndexIterator& rhs) const; + +private: + const OcdFile* data; + const IndexBlock* block; + std::size_t index; +}; + + + +/** + * A template class for dealing with OCD entity indices. + * + * Instances of this data do not actually copy any data, but rather provide + * an STL container like interface for raw data. The interfaces allows for + * forward iterating over the index for the given entity type. + * + * @param F: the type defining the file format version type + * @param T: the entity type (string, symbol, object) + */ +template< class F, class T > +class OcdEntityIndex +{ +public: + /** The actual file format version type, reexported. */ + typedef F FileFormat; + + /** The actual entity type. */ + typedef T EntityType; + + /** The index entry type for the entity type. */ + typedef typename T::IndexEntryType EntryType; + + /** The index iterator type. */ + typedef OcdEntityIndexIterator iterator; + + /** + * Constructs an entity index object. + * + * You must call setData() before using the container interface. + */ + OcdEntityIndex(); + + /** + * Destroys the object. + */ + ~OcdEntityIndex(); + + /** + * Sets the raw file data to which the object provides access. + * + * The data is not copied and must not be deleted as long as the index + * and its iterators are in use. + */ + void setData(const OcdFile* file); + + /** + * Returns a forward iterator to the beginning. + */ + iterator begin() const; + + /** + * Returns a forward iterator to the end. + */ + iterator end() const; + +private: + const OcdFile* data; +}; + + + +/** + * A template class for dealing with OCD files. + * + * @param F: the type defining the actual file format version + */ +template< class F > +class OcdFile +{ +public: + /** The actual file format version type, reexported. */ + typedef F Format; + + /** The actual file header type. */ + typedef typename F::FileHeader FileHeader; + + /** The actual string index type. */ + typedef OcdEntityIndex< F, Ocd::ParameterString > StringIndex; + + /** The actual symbol index type. */ + typedef OcdEntityIndex< F, typename F::BaseSymbol > SymbolIndex; + + /** The actual object index type. */ + typedef OcdEntityIndex< F, typename F::Object > ObjectIndex; + + /** + * Constructs a new object for the file contents given by data. + * + * We try to avoid copying the data by using the implicit sharing provided + * by QByteArray. + */ + OcdFile(const QByteArray& data); + + /** + * Destructs the object. + */ + ~OcdFile() {} + + /** + * Returns the raw data. + */ + const QByteArray& byteArray() const; + + /** + * Returns a const reference to the byte specified by file_pos. + */ + const char& operator[](quint32 file_pos) const; + + /** + * Returns a pointer to the file header. + */ + const FileHeader* header() const; + + /** + * Returns a const reference to the parameter string index. + */ + const StringIndex& strings() const; + + /** + * Returns the raw data of the string. + */ + const QByteArray operator[](const typename StringIndex::EntryType& string) const; + + /** + * Returns a const reference to the symbol index. + */ + const SymbolIndex& symbols() const; + + /** + * Returns a const reference to the object index. + */ + const ObjectIndex& objects() const; + + /** + * Returns a const referenc to the object referenced by an object index iterator. + */ + const typename F::Object& operator[](const typename ObjectIndex::EntryType& object_entry) const; + +private: + QByteArray byte_array; + StringIndex string_index; + SymbolIndex symbol_index; + ObjectIndex object_index; +}; + + + +// ### FirstIndexBlock implementation ### + +// Unknown entity type T: Return 0. +template< class F, class T > +quint32 FirstIndexBlock::operator()(const OcdFile* file) const +{ + Q_UNUSED(file); + + T* valid_entity_type = nullptr; + Q_ASSERT(valid_entity_type); + + return 0; +} + +template< class F > +quint32 FirstIndexBlock::operator()(const OcdFile* file) const +{ + return (file->header()->version < 8) ? 0 : file->header()->first_string_block; +} + +template< class F > +quint32 FirstIndexBlock::operator()(const OcdFile* file) const +{ + return file->header()->first_symbol_block; +} + +template< class F > +quint32 FirstIndexBlock::operator()(const OcdFile* file) const +{ + return file->header()->first_object_block; +} + + +// ### OcdEntityIndexIterator implementation ### + +template< class F, class T, class E > +OcdEntityIndexIterator::OcdEntityIndexIterator(const OcdFile* file, const IndexBlock* first_block) + : data(nullptr) + , block(nullptr) + , index(0) +{ + if (file && first_block) + { + data = file; + block = first_block; + if (data && block->entries[index].pos == 0) + this->operator++(); + } +} + +template< class F, class T, class E > +const OcdEntityIndexIterator& OcdEntityIndexIterator::operator++() +{ + if (data) + { + do + { + ++index; + if (index == 256) + { + index = 0; + quint32 next_block = block->next_block; + if (next_block == 0) + { + block = nullptr; + data = nullptr; + } + else if (Q_UNLIKELY(next_block >= (unsigned int)data->byteArray().size())) + { + qWarning("OcdEntityIndexIterator: Next index block is out of bounds"); + block = nullptr; + data = nullptr; + } + else + { + block = reinterpret_cast(&(*data)[next_block]); + } + } + } + while (block && !block->entries[index].pos); + } + return *this; +} + +template< class F, class T, class E > +inline +const E& OcdEntityIndexIterator::operator*() const +{ + return block->entries[index]; +} + +template< class F, class T, class E > +inline +const E* OcdEntityIndexIterator::operator->() const +{ + return &(block->entries[index]); +} + +template< class F, class T, class E > +inline +bool OcdEntityIndexIterator::operator==(const OcdEntityIndexIterator& rhs) const +{ + return (data == rhs.data && block == rhs.block && index == rhs.index); +} + +template< class F, class T, class E > +inline +bool OcdEntityIndexIterator::operator!=(const OcdEntityIndexIterator& rhs) const +{ + return (data != rhs.data || block != rhs.block || index != rhs.index); +} + + + +// ### OcdEntityIndexIterator specialization ### + +template< class F, class T > +OcdEntityIndexIterator::OcdEntityIndexIterator(const OcdFile* file, const IndexBlock* first_block) + : data(nullptr), + block(nullptr), + index(0) +{ + if (file && first_block) + { + data = file; + block = first_block; + if (data && block->entries[index] == 0) + this->operator++(); + } +} + +template< class F, class T > +const OcdEntityIndexIterator& OcdEntityIndexIterator::operator++() +{ + if (data) + { + do + { + ++index; + if (index == 256) + { + index = 0; + quint32 next_block = block->next_block; + if (next_block) + { + block = reinterpret_cast(&(*data)[next_block]); + } + else + { + block = nullptr; + data = nullptr; + } + } + } + while ( data && !block->entries[index] ); + } + return *this; +} + +template< class F, class T > +inline +const T& OcdEntityIndexIterator::operator*() const +{ + return reinterpret_cast((*data)[block->entries[index]]); +} + +template< class F, class T > +inline +const T* OcdEntityIndexIterator::operator->() const +{ + return reinterpret_cast(&(*data)[block->entries[index]]); +} + +template< class F, class T > +inline +bool OcdEntityIndexIterator::operator==(const OcdEntityIndexIterator& rhs) const +{ + return (data == rhs.data && block == rhs.block && index == rhs.index); +} + +template< class F, class T > +inline +bool OcdEntityIndexIterator::operator!=(const OcdEntityIndexIterator& rhs) const +{ + return (data != rhs.data || block != rhs.block || index != rhs.index); +} + + + +// ### OcdEntityIndex implementation ### + +template< class F, class T > +OcdEntityIndex::OcdEntityIndex() + : data(nullptr) +{ +} + +template< class F, class T > +OcdEntityIndex::~OcdEntityIndex() +{ +} + +template< class F, class T > +void OcdEntityIndex::setData(const OcdFile< F >* file) +{ + data = file; +} + +template< class F, class T > +typename OcdEntityIndex::iterator OcdEntityIndex::begin() const +{ + Q_ASSERT(data); + + quint32 file_pos = FirstIndexBlock()(data); + iterator it(data, reinterpret_cast(file_pos ? &(*data)[file_pos] : nullptr)); + return it; +} + +template< class F, class T > +typename OcdEntityIndex::iterator OcdEntityIndex::end() const +{ + static iterator it(nullptr, nullptr); + return it; +} + + + +// ### OcdFile implementation ### + +template< class F > +inline +OcdFile::OcdFile(const QByteArray& data) +{ + byte_array = data; + Q_ASSERT(byte_array.constData() == data.constData()); // No deep copy + string_index.setData(this); + symbol_index.setData(this); + object_index.setData(this); +} + +template< class F > +inline +const QByteArray& OcdFile::byteArray() const +{ + return byte_array; +} + +template< class F > +inline +const char& OcdFile::operator[](quint32 file_pos) const +{ + return *(byte_array.constData() + file_pos); +} + +template< class F > +inline +const typename F::FileHeader* OcdFile::header() const +{ + return (byte_array.size() < int(sizeof(FileHeader))) ? nullptr : reinterpret_cast(byte_array.constData()); +} + +template< class F > +inline +const typename OcdFile::StringIndex& OcdFile::strings() const +{ + return string_index; +} + +template< class F > +inline +const QByteArray OcdFile::operator[](const typename OcdFile::StringIndex::EntryType& string) const +{ + return QByteArray::fromRawData(&(*this)[string.pos], string.size); +} + +template< class F > +inline +const typename OcdFile::SymbolIndex& OcdFile::symbols() const +{ + return symbol_index; +} + +template< class F > +inline +const typename OcdFile::ObjectIndex& OcdFile::objects() const +{ + return object_index; +} + +template< class F > +inline +const typename F::Object& OcdFile::operator[](const typename OcdFile::ObjectIndex::EntryType& object_entry) const +{ + return reinterpret_cast((*this)[object_entry.pos]); +} + +#endif // OPENORIENTEERING_OCD_TYPES_H diff --git a/src/fileformats/ocd_types_v10.h b/src/fileformats/ocd_types_v10.h new file mode 100644 index 0000000..837ab5a --- /dev/null +++ b/src/fileformats/ocd_types_v10.h @@ -0,0 +1,33 @@ +/* + * Copyright 2013, 2015, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_TYPES_V10 +#define OPENORIENTEERING_OCD_TYPES_V10 + +#include "ocd_types.h" +#include "ocd_types_v9.h" + +namespace Ocd +{ + + using FormatV10 = FormatV9; + +} + +#endif // OPENORIENTEERING_OCD_TYPES_V10_H diff --git a/src/fileformats/ocd_types_v11.h b/src/fileformats/ocd_types_v11.h new file mode 100644 index 0000000..83f80d3 --- /dev/null +++ b/src/fileformats/ocd_types_v11.h @@ -0,0 +1,159 @@ +/* + * Copyright 2013, 2015, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_TYPES_V11_H +#define OPENORIENTEERING_OCD_TYPES_V11_H + +#include "ocd_types.h" +#include "ocd_types_v10.h" + +namespace Ocd +{ + +#pragma pack(push, 1) + + struct BaseSymbolV11 + { + using IndexEntryType = quint32; + static const int symbol_number_factor = 1000; + + quint32 size; + quint32 number; + quint8 type; + quint8 flags; + quint8 selected; + quint8 status; + quint8 tool; + quint8 cs_mode; + quint8 cs_type; + quint8 cd_flags; + qint32 extent; + quint32 file_pos; + quint8 RESERVED_MEMBER[2]; + quint16 num_colors; + quint16 colors[14]; + Utf16PascalString<64> description; + quint8 icon_bits[484]; + quint16 group[64]; + }; + + struct PointSymbolV11 + { + using BaseSymbol = BaseSymbolV11; + using Element = FormatV10::PointSymbol::Element; + + BaseSymbol base; + + quint16 data_size; + quint16 RESERVED_MEMBER; + Element begin_of_elements[1]; + }; + + struct LineSymbolV11 + { + using BaseSymbol = BaseSymbolV11; + using Element = FormatV10::LineSymbol::Element; + + BaseSymbol base; + + LineSymbolCommonV8 common; + + Element begin_of_elements[1]; + }; + + struct AreaSymbolV11 + { + using BaseSymbol = BaseSymbolV11; + using Element = FormatV10::AreaSymbol::Element; + + BaseSymbol base; + + quint32 border_symbol; + AreaSymbolCommonV8 common; + quint16 RESERVED_MEMBER; + quint16 data_size; + + Element begin_of_elements[1]; + }; + + struct TextSymbolV11 + { + using BaseSymbol = BaseSymbolV11; + + BaseSymbol base; + + Utf8PascalString<31> font_name; + BasicTextAttributesV8 basic; + SpecialTextAttributesV8 special; + quint16 RESERVED_MEMBER; + FramingAttributesV8 framing; + }; + + struct LineTextSymbolV11 + { + using BaseSymbol = BaseSymbolV11; + + BaseSymbol base; + + Utf8PascalString<31> font_name; + BasicTextAttributesV8 basic; + FramingAttributesV8 framing; + }; + + struct RectangleSymbolV11 + { + using BaseSymbol = BaseSymbolV11; + + BaseSymbol base; + + quint16 line_color; + quint16 line_width; + quint16 corner_radius; + quint16 grid_flags; + quint16 cell_width; + quint16 cell_height; + quint16 RESERVED_MEMBER[2]; + quint16 unnumbered_cells; + Utf8PascalString<3> unnumbered_text; + quint16 line_style; + Utf8PascalString<31> RESERVED_MEMBER; + quint16 RESERVED_MEMBER; + quint16 font_size_V10; /// \since V10 + quint16 RESERVED_MEMBER[4]; + }; + +#pragma pack(pop) + + /** OCD file format version 11 trait. */ + struct FormatV11 + { + using FileHeader = FormatV10::FileHeader; + using BaseSymbol = BaseSymbolV11; + using PointSymbol = PointSymbolV11; + using LineSymbol = LineSymbolV11; + using AreaSymbol = AreaSymbolV11; + using TextSymbol = TextSymbolV11; + using LineTextSymbol = LineTextSymbolV11; + using RectangleSymbol = RectangleSymbolV11; + using Object = FormatV10::Object; + using Encoding = Utf8Encoding; + }; +} + +#endif // OPENORIENTEERING_OCD_TYPES_V11_H diff --git a/src/fileformats/ocd_types_v12.h b/src/fileformats/ocd_types_v12.h new file mode 100644 index 0000000..fa90883 --- /dev/null +++ b/src/fileformats/ocd_types_v12.h @@ -0,0 +1,99 @@ +/* + * Copyright 2013, 2015, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_TYPES_V12_H +#define OPENORIENTEERING_OCD_TYPES_V12_H + +#include "ocd_types.h" +#include "ocd_types_v11.h" + +namespace Ocd +{ + +#pragma pack(push, 1) + + struct FileHeaderV12 : public FormatV11::FileHeader + { + quint32 RESERVED_MEMBER[2]; + quint32 first_multi_rep_block; + }; + + struct AreaSymbolV12 + { + using BaseSymbol = FormatV11::BaseSymbol; + using Element = FormatV11::PointSymbol::Element; + + BaseSymbol base; + + quint32 border_symbol; + AreaSymbolCommonV8 common; + quint8 structure_variation_x; + quint8 structure_variation_y; + quint16 structure_minimum_dist; + quint16 RESERVED_MEMBER; + quint16 data_size; + + Element begin_of_elements[1]; + }; + + struct ObjectV12 + { + using IndexEntryType = FormatV11::Object::IndexEntryType; + + quint32 symbol; + quint8 type; + quint8 customer; + qint16 angle; + qint32 color; + quint16 line_width; + quint16 diam_flags; + quint32 server_object_id; + quint32 height; + quint64 creation_date; + quint32 multi_rep_id; + quint64 modification_date; + quint32 num_items; + quint16 num_text; + quint16 object_string_length; + quint16 db_link_length; + quint8 object_string_type; + quint8 RESERVED_MEMBER; + + OcdPoint32 coords[1]; + }; + +#pragma pack(pop) + + /** OCD file format version 11 trait. */ + struct FormatV12 + { + using FileHeader = FileHeaderV12; + using BaseSymbol = FormatV11::BaseSymbol; + using PointSymbol = FormatV11::PointSymbol; + using LineSymbol = FormatV11::LineSymbol; + using AreaSymbol = AreaSymbolV12; + using TextSymbol = FormatV11::TextSymbol; + using LineTextSymbol = FormatV11::LineTextSymbol; + using RectangleSymbol = FormatV11::RectangleSymbol; + using Object = ObjectV12; + using Encoding = Utf8Encoding; + }; +} + +#endif // OPENORIENTEERING_OCD_TYPES_V12_H diff --git a/src/fileformats/ocd_types_v8.h b/src/fileformats/ocd_types_v8.h new file mode 100644 index 0000000..02036fc --- /dev/null +++ b/src/fileformats/ocd_types_v8.h @@ -0,0 +1,454 @@ +/* + * Copyright 2013, 2015, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_TYPES_V8_H +#define OPENORIENTEERING_OCD_TYPES_V8_H + +#include "ocd_types.h" + +namespace Ocd +{ + +#pragma pack(push, 1) + + struct CmykV8 + { + quint8 cyan; + quint8 magenta; + quint8 yellow; + quint8 black; + }; + + struct ColorInfoV8 + { + quint16 number; + quint16 RESERVED_MEMBER; + CmykV8 cmyk; + PascalString<31> name; + quint8 separations[32]; + }; + + struct SeparationInfoV8 + { + PascalString<15> name; + CmykV8 cmyk; + quint16 raster_freq; + quint16 raster_angle; + }; + + struct SymbolHeaderV8 + { + quint16 num_colors; + quint16 num_separations; + quint16 cyan_freq; + quint16 cyan_angle; + quint16 magenta_freq; + quint16 magenta_angle; + quint16 yellow_freq; + quint16 yellow_angle; + quint16 black_freq; + quint16 black_angle; + quint16 RESERVED_MEMBER[2]; + ColorInfoV8 color_info[256]; + SeparationInfoV8 separation_info[32]; + }; + + struct FileHeaderV8 : public FileHeaderGeneric + { + quint32 first_symbol_block; + quint32 first_object_block; + quint32 setup_pos; + quint32 setup_size; + quint32 info_pos; + quint32 info_size; + quint32 first_string_block; + quint32 RESERVED_MEMBER[3]; + SymbolHeaderV8 symbol_header; + }; + + struct BaseSymbolV8 + { + using IndexEntryType = quint32; + static const int symbol_number_factor = 10; + + quint16 size; + quint16 number; + quint16 type; + quint8 type2; + quint8 flags; + quint16 extent; + quint8 selected; + quint8 status; + quint16 RESERVED_MEMBER[2]; + qint32 file_pos; + quint8 colors[32]; + PascalString<31> description; + quint8 icon_bits[264]; + }; + + struct PointSymbolElementV8 + { + quint16 type; + quint16 flags; + quint16 color; + qint16 line_width; + qint16 diameter; + quint16 num_coords; + quint32 RESERVED_MEMBER; + + enum PointSymbolElementTypes + { + TypeLine = 1, + TypeArea = 2, + TypeCircle = 3, + TypeDot = 4 + }; + + enum Flags + { + NoFlags = 0, + RoundStyle = 1, + FlatMiterStyle = 4, + }; + }; + + struct PointSymbolV8 + { + using BaseSymbol = BaseSymbolV8; + using Element = PointSymbolElementV8; + + BaseSymbol base; + + quint16 data_size; + quint16 RESERVED_MEMBER; + Element begin_of_elements[1]; + }; + + struct LineSymbolCommonV8 + { + quint16 line_color; + quint16 line_width; + quint16 line_style; + qint16 dist_from_start; + qint16 dist_from_end; + qint16 main_length; + qint16 end_length; + qint16 main_gap; + qint16 sec_gap; + qint16 end_gap; + qint16 min_sym; + qint16 num_prim_sym; + qint16 prim_sym_dist; + quint16 double_mode; + quint16 double_flags; + quint16 double_color; + quint16 double_left_color; + quint16 double_right_color; + qint16 double_width; + qint16 double_left_width; + qint16 double_right_width; + qint16 double_length; + qint16 double_gap; + quint16 double_background_color_V11; /// \since V11 + quint16 RESERVED_MEMBER[2]; + quint16 dec_mode; + quint16 dec_last; + quint16 RESERVED_MEMBER; + quint16 framing_color; + qint16 framing_width; + quint16 framing_style; + quint16 primary_data_size; + quint16 secondary_data_size; + quint16 corner_data_size; + quint16 start_data_size; + quint16 end_data_size; + quint8 active_symbols_V11; /// \since V11 + quint8 RESERVED_MEMBER; + + enum LineStyleFlag + { + BevelJoin_FlatCap = 0, + RoundJoin_RoundCap = 1, + BevelJoin_PointedCap = 2, + RoundJoin_PointedCap = 3, + MiterJoin_FlatCap = 4, + MiterJoin_PointedCap = 6 + }; + + enum DoubleLineFlag + { + DoubleFillColorOn = 1, + DoubleBackgroundColorOn = 2 + }; + }; + + struct LineSymbolV8 + { + using BaseSymbol = BaseSymbolV8; + using Element = PointSymbolElementV8; + + BaseSymbol base; + + LineSymbolCommonV8 common; + + Element begin_of_elements[1]; + }; + + struct AreaSymbolCommonV8 + { + quint16 fill_color; + quint16 hatch_mode; + quint16 hatch_color; + quint16 hatch_line_width; + quint16 hatch_dist; + qint16 hatch_angle_1; + qint16 hatch_angle_2; + quint8 fill_on_V9; /// \since V9 + quint8 border_on_V9; /// \since V9 + quint8 structure_mode; + quint8 structure_draw_V12; /// \since V12 + quint16 structure_width; + quint16 structure_height; + qint16 structure_angle; + }; + + struct AreaSymbolV8 + { + using BaseSymbol = BaseSymbolV8; + using Element = PointSymbolElementV8; + + BaseSymbol base; + + quint16 area_flags; + quint16 fill_on; + AreaSymbolCommonV8 common; + quint16 RESERVED_MEMBER; + quint16 data_size; + + Element begin_of_elements[1]; + }; + + struct BasicTextAttributesV8 + { + quint16 color; + quint16 font_size; + quint16 font_weight; + quint8 font_italic; + quint8 charset_V8_ONLY; /// V8 text symbols only + quint16 char_spacing; + quint16 word_spacing; + quint16 alignment; + }; + + struct SpecialTextAttributesV8 + { + quint16 line_spacing; + qint16 para_spacing; + quint16 indent_first_line; + quint16 indent_other_lines; + quint16 num_tabs; + quint32 tab_pos[32]; + quint16 line_below_on; + quint16 line_below_color; + quint16 line_below_width; + quint16 line_below_offset; + }; + + struct FramingAttributesV8 + { + quint8 mode; /// 16 bit in V8 + quint8 line_style_V9; /// \since V9 + quint8 point_symbol_on_V10; /// \since V10 + quint32 point_symbol_number_V10; /// \since V10 + char RESERVED_MEMBER[19]; + quint16 border_left_V9; /// \since V9; TextSymbol only + quint16 border_bottom_V9; /// \since V9; TextSymbol only + quint16 border_right_V9; /// \since V9; TextSymbol only + quint16 border_top_V9; /// \since V9; TextSymbol only + quint16 color; + quint16 line_width; + quint16 font_weight; /// TextSymbol only + quint16 italic; /// TextSymbol only + quint16 offset_x; + quint16 offset_y; + }; + + struct TextSymbolV8 + { + using BaseSymbol = BaseSymbolV8; + + BaseSymbol base; + + PascalString<31> font_name; + BasicTextAttributesV8 basic; + SpecialTextAttributesV8 special; + quint16 RESERVED_MEMBER; + FramingAttributesV8 framing; + }; + + struct LineTextSymbolV8 /// \todo use and test... + { + using BaseSymbol = BaseSymbolV8; + + BaseSymbol base; + + PascalString<31> font_name; + BasicTextAttributesV8 basic; + FramingAttributesV8 framing; + }; + + struct RectangleSymbolV8 + { + using BaseSymbol = BaseSymbolV8; + + BaseSymbol base; + + quint16 line_color; + quint16 line_width; + quint16 corner_radius; + quint16 grid_flags; + quint16 cell_width; + quint16 cell_height; + quint16 RESERVED_MEMBER[2]; + quint16 unnumbered_cells; + PascalString<3> unnumbered_text; + quint16 RESERVED_MEMBER; + PascalString<31> RESERVED_MEMBER; + quint16 RESERVED_MEMBER[6]; + }; + + struct ObjectIndexEntryV8 + { + OcdPoint32 bottom_left_bound; + OcdPoint32 top_right_bound; + quint32 pos; + quint16 size_MISC; /// Different interpretation for version < 8 + qint16 symbol; + }; + + struct ObjectV8 + { + using IndexEntryType = ObjectIndexEntryV8; + + quint16 symbol; + quint8 type; + quint8 unicode; + quint16 num_items; + quint16 num_text; + qint16 angle; + quint16 RESERVED_MEMBER; + quint32 RESERVED_MEMBER; + PascalString<15> reserved; + + OcdPoint32 coords[1]; + }; + + typedef double PascalDouble; + + struct GpsPointV8 + { + OcdPoint32 map_point; + PascalDouble lat; + PascalDouble lon; + PascalString<15> name; + }; + + struct PrintSetupV8 + { + OcdPoint32 bottom_left; + OcdPoint32 top_right; + quint16 grid_enabled; + qint16 grid_color; + qint16 overlap_x; + qint16 overlap_y; + PascalDouble scale; + quint16 intensity; + quint16 line_width; + quint16 RESERVED_MEMBER[4]; + }; + + struct ExportSetupV8 + { + OcdPoint32 bottom_left; + OcdPoint32 top_right; + }; + + struct ZoomRectV8 + { + PascalDouble zoom; + OcdPoint32 center; + }; + + struct SetupV8 + { + OcdPoint32 center; + PascalDouble grid_dist; + quint16 work_mode; + quint16 line_mode; + quint16 edit_mode; + qint16 selected_symbol; + PascalDouble map_scale; + PascalDouble real_offset_x; + PascalDouble real_offset_y; + PascalDouble real_angle; + PascalDouble real_grid; + PascalDouble gps_angle; + GpsPointV8 gps_adjustment[12]; + quint32 num_gps_adjustments; + PascalDouble RESERVED_MEMBER[2]; + OcdPoint32 RESERVED_MEMBER; + char RESERVED_MEMBER[256]; + quint16 RESERVED_MEMBER[2]; + PascalDouble RESERVED_MEMBER; + OcdPoint32 RESERVED_MEMBER; + PascalDouble RESERVED_MEMBER; + PrintSetupV8 print_setup; + ExportSetupV8 export_setup; + PascalDouble zoom; + ZoomRectV8 zoom_history[8]; + quint32 zoom_history_size; + // V6 header ends here, but Mapper doesn't use the following fields. + quint16 real_coords_IGNORED; + char filename_IGNORED[256]; + quint16 hatch_areas_IGNORED; + quint16 dim_templates_IGNORED; + quint16 hide_templates_IGNORED; + quint16 template_mode_IGNORED; + qint16 template_color_IGNORED; + }; + +#pragma pack(pop) + + /** OCD file format version 8 trait. */ + struct FormatV8 + { + using FileHeader = FileHeaderV8; + using BaseSymbol = BaseSymbolV8; + using PointSymbol = PointSymbolV8; + using LineSymbol = LineSymbolV8; + using AreaSymbol = AreaSymbolV8; + using TextSymbol = TextSymbolV8; + using LineTextSymbol = LineTextSymbolV8; + using RectangleSymbol = RectangleSymbolV8; + using Object = ObjectV8; + using Encoding = Custom8BitEncoding; + }; +} + +#endif // OPENORIENTEERING_OCD_TYPES_V8_H diff --git a/src/fileformats/ocd_types_v9.h b/src/fileformats/ocd_types_v9.h new file mode 100644 index 0000000..498d346 --- /dev/null +++ b/src/fileformats/ocd_types_v9.h @@ -0,0 +1,215 @@ +/* + * Copyright 2013, 2015, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OCD_TYPES_V9_H +#define OPENORIENTEERING_OCD_TYPES_V9_H + +#include "ocd_types.h" +#include "ocd_types_v8.h" + +namespace Ocd +{ + +#pragma pack(push, 1) + + struct FileHeaderV9 : public FileHeaderGeneric + { + quint32 first_symbol_block; + quint32 first_object_block; + quint32 offline_sync_serial_V11; /// \since V11 + quint32 current_file_version_V12; /// \since V12 + quint32 RESERVED_MEMBER[2]; + quint32 first_string_block; + quint32 file_name_pos; + quint32 file_name_size; + quint32 RESERVED_MEMBER; + }; + + struct BaseSymbolV9 + { + using IndexEntryType = quint32; + static const int symbol_number_factor = 1000; + + quint32 size; + quint32 number; + quint8 type; + quint8 flags; + quint8 selected; + quint8 status; + quint8 tool; + quint8 cs_mode; + quint8 cs_type; + quint8 cd_flags; + qint32 extent; + qint32 file_pos; + quint16 group; + quint16 num_colors; + quint16 colors[14]; + PascalString<31> description; + quint8 icon_bits[484]; + }; + + struct PointSymbolV9 + { + using BaseSymbol = BaseSymbolV9; + using Element = FormatV8::PointSymbol::Element; + + BaseSymbol base; + + quint16 data_size; + quint16 RESERVED_MEMBER; + Element begin_of_elements[1]; + }; + + struct LineSymbolV9 + { + using BaseSymbol = BaseSymbolV9; + using Element = FormatV8::LineSymbol::Element; + + BaseSymbol base; + + LineSymbolCommonV8 common; + + Element begin_of_elements[1]; + }; + + struct AreaSymbolV9 + { + using BaseSymbol = BaseSymbolV9; + using Element = FormatV8::AreaSymbol::Element; + + BaseSymbol base; + + quint32 border_symbol; + AreaSymbolCommonV8 common; + quint16 RESERVED_MEMBER; + quint16 data_size; + + Element begin_of_elements[1]; + }; + + struct TextSymbolV9 + { + using BaseSymbol = BaseSymbolV9; + + BaseSymbol base; + + PascalString<31> font_name; + BasicTextAttributesV8 basic; + SpecialTextAttributesV8 special; + quint16 RESERVED_MEMBER; + FramingAttributesV8 framing; + }; + + struct LineTextSymbolV9 + { + using BaseSymbol = BaseSymbolV9; + + BaseSymbol base; + + PascalString<31> font_name; + BasicTextAttributesV8 basic; + FramingAttributesV8 framing; + }; + + struct RectangleSymbolV9 + { + using BaseSymbol = BaseSymbolV9; + + BaseSymbol base; + + quint16 line_color; + quint16 line_width; + quint16 corner_radius; + quint16 grid_flags; + quint16 cell_width; + quint16 cell_height; + quint16 RESERVED_MEMBER[2]; + quint16 unnumbered_cells; + PascalString<3> unnumbered_text; + quint16 RESERVED_MEMBER; + PascalString<31> RESERVED_MEMBER; + quint16 RESERVED_MEMBER; + quint16 font_size_V10; /// \since V10 + quint16 RESERVED_MEMBER[4]; + }; + + struct ObjectIndexEntryV9 + { + OcdPoint32 bottom_left_bound; + OcdPoint32 top_right_bound; + quint32 pos; + quint32 size; + qint32 symbol; + quint8 type; + quint8 encryption_mode_V11; /// \since V11 + quint8 status; + quint8 view_type; + quint16 color; + quint16 group_V11; /// \since V11 + quint16 layer; + quint8 layout_font_V11_ONLY; /// only in V11 + quint8 RESERVED_MEMBER; + }; + + struct ObjectV9 + { + using IndexEntryType = ObjectIndexEntryV9; + + quint32 symbol; + quint8 type; + quint8 customer_V11; /// \since V11 + qint16 angle; + quint32 num_items; + quint16 num_text; + quint8 mark_V11; /// \since V11 + quint8 snapping_mark_V11; /// \since V11 + quint32 color; + quint16 line_width; + quint16 diam_flags; + // The usage of the following 16 bytes has changed significantly in the + // versions 9 to 12. This is an abstraction, capturing what seems most + // relevant. + quint32 RESERVED_MEMBER; /// V11: Server object ID + quint32 height_V11; /// \since V11; unit: 1/256 mm + quint32 RESERVED_MEMBER; + quint32 height_V10_ONLY; /// V10 only; unit: mm + + OcdPoint32 coords[1]; + }; + +#pragma pack(pop) + + /** OCD file format version 9 trait. */ + struct FormatV9 + { + using FileHeader = FileHeaderV9; + using BaseSymbol = BaseSymbolV9; + using PointSymbol = PointSymbolV9; + using LineSymbol = LineSymbolV9; + using AreaSymbol = AreaSymbolV9; + using TextSymbol = TextSymbolV9; + using LineTextSymbol = LineTextSymbolV9; + using RectangleSymbol = RectangleSymbolV9; + using Object = ObjectV9; + using Encoding = Custom8BitEncoding; + }; +} + +#endif // OPENORIENTEERING_OCD_TYPES_V9_H diff --git a/src/fileformats/xml_file_format.cpp b/src/fileformats/xml_file_format.cpp new file mode 100644 index 0000000..5730e86 --- /dev/null +++ b/src/fileformats/xml_file_format.cpp @@ -0,0 +1,1028 @@ +/* + * Copyright 2012 Pete Curtis + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "xml_file_format.h" +#include "xml_file_format_p.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/map_grid.h" +#include "core/map_part.h" +#include "core/map_printer.h" // IWYU pragma: keep +#include "core/map_view.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "fileformats/file_import_export.h" +#include "templates/template.h" +#include "undo/undo_manager.h" +#include "util/xml_stream_util.h" + + +namespace OpenOrienteering { + +// ### XMLFileFormat definition ### + +constexpr int XMLFileFormat::minimum_version = 2; +constexpr int XMLFileFormat::current_version = 7; + +int XMLFileFormat::active_version = 5; // updated by XMLFileExporter::doExport() + + + +namespace { + +const char* magic_string = "= len && qstrncmp(reinterpret_cast(buffer), magic_string, len) == 0); +} + +Importer *XMLFileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const +{ + return new XMLFileImporter(stream, map, view); +} + +Exporter *XMLFileFormat::createExporter(QIODevice* stream, Map *map, MapView *view) const +{ + return new XMLFileExporter(stream, map, view); +} + + + +// ### A namespace which collects various string constants of type QLatin1String. ### + +namespace literal +{ + static const QLatin1String map("map"); + static const QLatin1String version("version"); + static const QLatin1String notes("notes"); + + static const QLatin1String barrier("barrier"); + static const QLatin1String required("required"); + + static const QLatin1String count("count"); + static const QLatin1String current("current"); + + static const QLatin1String georeferencing("georeferencing"); + + static const QLatin1String colors("colors"); + static const QLatin1String color("color"); + static const QLatin1String priority("priority"); + static const QLatin1String name("name"); + static const QLatin1String method("method"); + static const QLatin1String spotcolor("spotcolor"); + static const QLatin1String cmyk("cmyk"); + static const QLatin1String rgb("rgb"); + static const QLatin1String custom("custom"); + static const QLatin1String spotcolors("spotcolors"); + static const QLatin1String knockout("knockout"); + static const QLatin1String namedcolor("namedcolor"); + static const QLatin1String component("component"); + static const QLatin1String factor("factor"); + static const QLatin1String c("c"); + static const QLatin1String m("m"); + static const QLatin1String y("y"); + static const QLatin1String k("k"); + static const QLatin1String r("r"); + static const QLatin1String g("g"); + static const QLatin1String b("b"); + static const QLatin1String opacity("opacity"); + + static const QLatin1String symbols("symbols"); + static const QLatin1String id("id"); + static const QLatin1String symbol("symbol"); + + static const QLatin1String parts("parts"); + static const QLatin1String part("part"); + + static const QLatin1String templates("templates"); + static const QLatin1String template_string("template"); + static const QLatin1String first_front_template("first_front_template"); + + static const QLatin1String defaults("defaults"); + static const QLatin1String use_meters_per_pixel("use_meters_per_pixel"); + static const QLatin1String meters_per_pixel("meters_per_pixel"); + static const QLatin1String dpi("dpi"); + static const QLatin1String scale("scale"); + + static const QLatin1String view("view"); + static const QLatin1String area_hatching_enabled("area_hatching_enabled"); + static const QLatin1String baseline_view_enabled("baseline_view_enabled"); + static const QLatin1String grid("grid"); + static const QLatin1String map_view("map_view"); + + static const QLatin1String print("print"); + + static const QLatin1String undo("undo"); + static const QLatin1String redo("redo"); +} + + + +// ### XMLFileExporter definition ### + +XMLFileExporter::XMLFileExporter(QIODevice* stream, Map *map, MapView *view) +: Exporter(stream, map, view), + xml(stream) +{ + // Determine auto-formatting default from filename, if possible. + auto file = qobject_cast(stream); + bool auto_formatting = (file && file->fileName().contains(QLatin1String(".xmap"))); + setOption(QString::fromLatin1("autoFormatting"), auto_formatting); +} + +void XMLFileExporter::doExport() +{ + if (option(QString::fromLatin1("autoFormatting")).toBool()) + xml.setAutoFormatting(true); + +#ifdef MAPPER_ENABLE_COMPATIBILITY + int current_version = XMLFileFormat::current_version; + bool retain_compatibility = Settings::getInstance().getSetting(Settings::General_RetainCompatiblity).toBool(); + XMLFileFormat::active_version = retain_compatibility ? 5 : current_version; + + if (XMLFileFormat::active_version < 6 && map->getNumParts() != 1) + { + throw FileFormatException(tr("Older versions of Mapper do not support multiple map parts. To save the map in compatibility mode, you must first merge all map parts.")); + } +#else + XMLFileFormat::active_version = XMLFileFormat::current_version; +#endif + + xml.writeDefaultNamespace(mapperNamespace()); + xml.writeStartDocument(); + writeLineBreak(xml); + + { + XmlElementWriter map_element(xml, literal::map); + map_element.writeAttribute(literal::version, XMLFileFormat::active_version); + writeLineBreak(xml); + + xml.writeTextElement(literal::notes, map->getMapNotes()); + writeLineBreak(xml); + + exportGeoreferencing(); + exportColors(); + writeLineBreak(xml); + + XmlElementWriter* barrier = nullptr; + if (XMLFileFormat::active_version >= 6) + { + // Prevent Mapper versions < 0.6.0 from crashing + // when compatibilty mode is NOT activated + // Incompatible feature: dense coordinates + barrier = new XmlElementWriter(xml, literal::barrier); + barrier->writeAttribute(literal::version, 6); + barrier->writeAttribute(literal::required, "0.6.0"); + writeLineBreak(xml); + } + exportSymbols(); + writeLineBreak(xml); + exportMapParts(); + writeLineBreak(xml); + exportTemplates(); + writeLineBreak(xml); + exportView(); + writeLineBreak(xml); + exportPrint(); + delete barrier; + writeLineBreak(xml); + + if (Settings::getInstance().getSetting(Settings::General_SaveUndoRedo).toBool()) + { + { + // Prevent Mapper versions < 0.6.0 from crashing + // when compatibilty mode IS activated + // Incompatible feature: new undo step types + XmlElementWriter barrier(xml, literal::barrier); + barrier.writeAttribute(literal::version, 6); + barrier.writeAttribute(literal::required, "0.6.0"); + writeLineBreak(xml); + exportUndo(); + exportRedo(); + } + writeLineBreak(xml); + } + } + + xml.writeEndDocument(); +} + +void XMLFileExporter::exportGeoreferencing() +{ + map->getGeoreferencing().save(xml); + writeLineBreak(xml); +} + +void XMLFileExporter::exportColors() +{ + XmlElementWriter all_colors_element(xml, literal::colors); + std::size_t num_colors = map->color_set->colors.size(); + all_colors_element.writeAttribute(literal::count, num_colors); + + for (std::size_t i = 0; i < num_colors; ++i) + { + writeLineBreak(xml); + MapColor* color = map->color_set->colors[i]; + const MapColorCmyk &cmyk = color->getCmyk(); + XmlElementWriter color_element(xml, literal::color); + color_element.writeAttribute(literal::priority, color->getPriority()); + color_element.writeAttribute(literal::name, color->getName()); + color_element.writeAttribute(literal::c, cmyk.c, 3); + color_element.writeAttribute(literal::m, cmyk.m, 3); + color_element.writeAttribute(literal::y, cmyk.y, 3); + color_element.writeAttribute(literal::k, cmyk.k, 3); + color_element.writeAttribute(literal::opacity, color->getOpacity(), 3); + + if (color->getSpotColorMethod() != MapColor::UndefinedMethod) + { + XmlElementWriter spotcolors_element(xml, literal::spotcolors); + spotcolors_element.writeAttribute(literal::knockout, color->getKnockout()); + SpotColorComponent component; + switch(color->getSpotColorMethod()) + { + case MapColor::SpotColor: + xml.writeTextElement(literal::namedcolor, color->getSpotColorName()); + break; + case MapColor::CustomColor: + for (auto&& component : color->getComponents()) + { + XmlElementWriter component_element(xml, literal::component); + component_element.writeAttribute(literal::factor, component.factor); + component_element.writeAttribute(literal::spotcolor, component.spot_color->getPriority()); + } + break; + default: + ; // nothing + } + } + + { + XmlElementWriter cmyk_element(xml, literal::cmyk); + switch(color->getCmykColorMethod()) + { + case MapColor::SpotColor: + cmyk_element.writeAttribute(literal::method, literal::spotcolor); + break; + case MapColor::RgbColor: + cmyk_element.writeAttribute(literal::method, literal::rgb); + break; + default: + cmyk_element.writeAttribute(literal::method, literal::custom); + } + } + + { + XmlElementWriter rgb_element(xml, literal::rgb); + switch(color->getRgbColorMethod()) + { + case MapColor::SpotColor: + rgb_element.writeAttribute(literal::method, literal::spotcolor); + break; + case MapColor::CmykColor: + rgb_element.writeAttribute(literal::method, literal::cmyk); + break; + default: + rgb_element.writeAttribute(literal::method, literal::custom); + } + const MapColorRgb &rgb = color->getRgb(); + rgb_element.writeAttribute(literal::r, rgb.r, 3); + rgb_element.writeAttribute(literal::g, rgb.g, 3); + rgb_element.writeAttribute(literal::b, rgb.b, 3); + } + } + writeLineBreak(xml); +} + +void XMLFileExporter::exportSymbols() +{ + XmlElementWriter symbols_element(xml, literal::symbols); + auto id = map->symbolSetId(); + int num_symbols = map->getNumSymbols(); + symbols_element.writeAttribute(literal::count, num_symbols); + if (!id.isEmpty()) + symbols_element.writeAttribute(literal::id, id); + for (int i = 0; i < num_symbols; ++i) + { + writeLineBreak(xml); + map->getSymbol(i)->save(xml, *map); + } + writeLineBreak(xml); +} + +void XMLFileExporter::exportMapParts() +{ + XmlElementWriter parts_element(xml, literal::parts); + + auto num_parts = std::size_t(map->getNumParts()); + parts_element.writeAttribute(literal::count, num_parts); + parts_element.writeAttribute(literal::current, map->current_part_index); + for (auto i = 0u; i < num_parts; ++i) + { + writeLineBreak(xml); + map->getPart(i)->save(xml); + } + writeLineBreak(xml); +} + +void XMLFileExporter::exportTemplates() +{ + // Update the relative paths of templates + if (auto file = qobject_cast(stream)) + { + auto filename = file->fileName(); + auto map_dir = QFileInfo(filename).absoluteDir(); + if (!filename.isEmpty() && map_dir.exists()) + { + for (int i = 0; i < map->getNumTemplates(); ++i) + { + auto temp = map->getTemplate(i); + if (temp->getTemplateState() != Template::Invalid) + temp->setTemplateRelativePath(map_dir.relativeFilePath(temp->getTemplatePath())); + } + for (int i = 0; i < map->getNumClosedTemplates(); ++i) + { + auto temp = map->getClosedTemplate(i); + if (temp->getTemplateState() != Template::Invalid) + temp->setTemplateRelativePath(map_dir.relativeFilePath(temp->getTemplatePath())); + } + } + } + + XmlElementWriter templates_element(xml, literal::templates); + + int num_templates = map->getNumTemplates() + map->getNumClosedTemplates(); + templates_element.writeAttribute(literal::count, num_templates); + templates_element.writeAttribute(literal::first_front_template, map->first_front_template); + for (int i = 0; i < map->getNumTemplates(); ++i) + { + writeLineBreak(xml); + map->getTemplate(i)->saveTemplateConfiguration(xml, true); + } + for (int i = 0; i < map->getNumClosedTemplates(); ++i) + { + writeLineBreak(xml); + map->getClosedTemplate(i)->saveTemplateConfiguration(xml, false); + } + + { + writeLineBreak(xml); + XmlElementWriter defaults_element(xml, literal::defaults); + defaults_element.writeAttribute(literal::use_meters_per_pixel, map->image_template_use_meters_per_pixel); + defaults_element.writeAttribute(literal::meters_per_pixel, map->image_template_meters_per_pixel); + defaults_element.writeAttribute(literal::dpi, map->image_template_dpi); + defaults_element.writeAttribute(literal::scale, map->image_template_scale); + } + writeLineBreak(xml); +} + +void XMLFileExporter::exportView() +{ + XmlElementWriter view_element(xml, literal::view); + view_element.writeAttribute(literal::area_hatching_enabled, bool(map->renderable_options & Symbol::RenderAreasHatched)); + view_element.writeAttribute(literal::baseline_view_enabled, bool(map->renderable_options & Symbol::RenderBaselines)); + + writeLineBreak(xml); + map->getGrid().save(xml); + + if (view) + { + writeLineBreak(xml); + view->save(xml, literal::map_view); + } + writeLineBreak(xml); +} + +void XMLFileExporter::exportPrint() +{ + if (map->hasPrinterConfig()) + { + map->printerConfig().save(xml, literal::print); + writeLineBreak(xml); + } +} + +void XMLFileExporter::exportUndo() +{ + map->undoManager().saveUndo(xml); + writeLineBreak(xml); +} + +void XMLFileExporter::exportRedo() +{ + map->undoManager().saveRedo(xml); + writeLineBreak(xml); +} + + + +// ### XMLFileImporter definition ### + +XMLFileImporter::XMLFileImporter(QIODevice* stream, Map *map, MapView *view) +: Importer(stream, map, view), + xml(stream) +{ + //NOP +} + +void XMLFileImporter::addWarningUnsupportedElement() +{ + addWarning(tr("Unsupported element: %1 (line %2 column %3)"). + arg(xml.name().toString()). + arg(xml.lineNumber()). + arg(xml.columnNumber()) + ); +} + +void XMLFileImporter::import(bool load_symbols_only) +{ + if (!xml.readNextStartElement() || xml.name() != literal::map) + { + xml.raiseError(::OpenOrienteering::Importer::tr("Unsupported file format.")); + } + + XmlElementReader map_element(xml); + const int version = map_element.attribute(literal::version); + if (version < 1) + xml.raiseError(::OpenOrienteering::Importer::tr("Invalid file format version.")); + else if (version < XMLFileFormat::minimum_version) + xml.raiseError(::OpenOrienteering::Importer::tr("Unsupported old file format version. Please use an older program version to load and update the file.")); + else if (version > XMLFileFormat::current_version) + addWarning(::OpenOrienteering::Importer::tr("Unsupported new file format version. Some map features will not be loaded or saved by this version of the program.")); + + QScopedValueRollback rollback { MapCoord::boundsOffset() }; + MapCoord::boundsOffset().reset(true); + georef_offset_adjusted = false; + importElements(load_symbols_only); + + auto offset = MapCoord::boundsOffset(); + if (!load_symbols_only && !offset.isZero()) + { + addWarning(tr("Some coordinates were out of bounds for printing. Map content was adjusted.")); + + MapCoordF offset_f { offset.x / 1000.0, offset.y / 1000.0 }; + + // Apply the offset + auto printer_config = map->printerConfig(); + auto& print_area = printer_config.print_area; + print_area.translate( -offset_f ); + + // Verify the adjusted print area, and readjust if necessary + if (print_area.top() <= -1000000.0 || print_area.bottom() > 1000000.0) + print_area.moveTop(-print_area.width() / 2); + if (print_area.left() <= -1000000.0 || print_area.right() > 1000000.0) + print_area.moveLeft(-print_area.width() / 2); + + map->setPrinterConfig(printer_config); + + if (!georef_offset_adjusted) + { + // We need to adjust the georeferencing. + auto georef = map->getGeoreferencing(); + auto ref_point = MapCoordF { georef.getMapRefPoint() }; + auto new_projected = georef.toProjectedCoords(ref_point + offset_f); + georef.setProjectedRefPoint(new_projected, false); + map->setGeoreferencing(georef); + } + } +} + +void XMLFileImporter::importElements(bool load_symbols_only) +{ + while (xml.readNextStartElement()) + { + const QStringRef name(xml.name()); + + if (name == literal::colors) + importColors(); + else if (name == literal::symbols) + importSymbols(); + else if (name == literal::georeferencing) + importGeoreferencing(load_symbols_only); + else if (name == literal::view) + importView(); + else if (name == literal::barrier) + { + XmlElementReader barrier(xml); + if (barrier.attribute(literal::version) > XMLFileFormat::current_version) + { + QString required_version = barrier.attribute(literal::required); + if (required_version.isEmpty()) + required_version = tr("unknown"); + addWarning(tr("Parts of this file cannot be read by this version of Mapper. Minimum required version: %1").arg(required_version)); + xml.skipCurrentElement(); + } + else + { + importElements(load_symbols_only); + } + } + else if (load_symbols_only) + xml.skipCurrentElement(); + /****************************************************** + * The remainder is skipped when loading a symbol set! * + ******************************************************/ + else if (name == literal::notes) + importMapNotes(); + else if (name == literal::parts) + importMapParts(); + else if (name == literal::templates) + importTemplates(); + else if (name == literal::print) + importPrint(); + else if (name == literal::undo) + importUndo(); + else if (name == literal::redo) + importRedo(); + else + { + addWarningUnsupportedElement(); + xml.skipCurrentElement(); + } + } + + if (xml.error()) + throw FileFormatException( + tr("Error at line %1 column %2: %3") + .arg(xml.lineNumber()) + .arg(xml.columnNumber()) + .arg(xml.errorString()) ); +} + +void XMLFileImporter::importMapNotes() +{ + auto recovery = XmlRecoveryHelper(xml); + map->setMapNotes(xml.readElementText()); + if (xml.hasError() && recovery()) + { + addWarning(tr("Some invalid characters had to be removed.")); + map->setMapNotes(xml.readElementText()); + } +} + +void XMLFileImporter::importGeoreferencing(bool load_symbols_only) +{ + Q_ASSERT(xml.name() == literal::georeferencing); + + bool check_for_offset = MapCoord::boundsOffset().check_for_offset; + + Georeferencing georef; + georef.load(xml, load_symbols_only); + map->setGeoreferencing(georef); + if (!georef.isValid()) + { + QString error_text = georef.getErrorText(); + if (error_text.isEmpty()) + error_text = tr("Unknown error"); + addWarning(tr("Unsupported or invalid georeferencing specification '%1': %2"). + arg(georef.getProjectedCRSSpec(), error_text)); + } + + if (MapCoord::boundsOffset().isZero()) + // Georeferencing was not adjusted on import. + MapCoord::boundsOffset().reset(check_for_offset); + else if (check_for_offset) + // Georeferencing was adjusted on import, before other coordinates. + georef_offset_adjusted = true; +} + +/** Helper for delayed actions */ +struct XMLFileImporterColorBacklogItem +{ + MapColor* color; // color which needs updating + SpotColorComponents components; // components of the color + bool knockout; + bool cmyk_from_spot; // determine CMYK from spot + bool rgb_from_spot; // determine RGB from spot + + XMLFileImporterColorBacklogItem(MapColor* color) + : color(color), knockout(false), cmyk_from_spot(false), rgb_from_spot(false) + {} +}; +typedef std::vector XMLFileImporterColorBacklog; + + +void XMLFileImporter::importColors() +{ + XmlElementReader all_colors_element(xml); + auto num_colors = all_colors_element.attribute(literal::count); + + Map::ColorVector& colors(map->color_set->colors); + colors.reserve(qMin(num_colors, std::size_t(100))); // 100 is not a limit + XMLFileImporterColorBacklog backlog; + backlog.reserve(colors.size()); + while (xml.readNextStartElement()) + { + if (xml.name() == literal::color) + { + XmlElementReader color_element(xml); + MapColor* color = new MapColor( + color_element.attribute(literal::name), + color_element.attribute(literal::priority) ); + if (color_element.hasAttribute(literal::opacity)) + color->setOpacity(color_element.attribute(literal::opacity)); + + MapColorCmyk cmyk; + cmyk.c = color_element.attribute(literal::c); + cmyk.m = color_element.attribute(literal::m); + cmyk.y = color_element.attribute(literal::y); + cmyk.k = color_element.attribute(literal::k); + + bool knockout = false; + SpotColorComponents components; + QString cmyk_method; + QString rgb_method; + MapColorRgb rgb; + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::spotcolors) + { + XmlElementReader spotcolors_element(xml); + knockout = spotcolors_element.attribute(literal::knockout); + + while(xml.readNextStartElement()) + { + if (xml.name() == literal::namedcolor) + { + color->setSpotColorName(xml.readElementText()); + color->setKnockout(knockout); + } + else if (xml.name() == literal::component) + { + XmlElementReader component_element(xml); + SpotColorComponent component; + component.factor = component_element.attribute(literal::factor); + // We can't know if the spot color is already loaded. Create a temporary proxy. + component.spot_color = new MapColor(component_element.attribute(literal::spotcolor)); + components.push_back(component); + } + else + xml.skipCurrentElement(); // unsupported + } + } + else if (xml.name() == literal::cmyk) + { + XmlElementReader cmyk_element(xml); + cmyk_method = cmyk_element.attribute(literal::method); + } + else if (xml.name() == literal::rgb) + { + XmlElementReader rgb_element(xml); + rgb_method = rgb_element.attribute(literal::method); + rgb.r = rgb_element.attribute(literal::r); + rgb.g = rgb_element.attribute(literal::g); + rgb.b = rgb_element.attribute(literal::b); + } + else + { + xml.skipCurrentElement(); // unsupported + } + } + + if (cmyk_method == literal::custom) + { + color->setCmyk(cmyk); + if (rgb_method == literal::cmyk) + color->setRgbFromCmyk(); + } + + if (rgb_method == literal::custom) + { + color->setRgb(rgb); + if (cmyk_method == literal::rgb) + color->setCmykFromRgb(); + } + + if (!components.empty()) + { + backlog.push_back(XMLFileImporterColorBacklogItem(color)); + XMLFileImporterColorBacklogItem& item = backlog.back(); + item.components = components; + item.knockout = knockout; + item.cmyk_from_spot = (cmyk_method == literal::spotcolor); + item.rgb_from_spot = (rgb_method == literal::spotcolor); + } + else if (knockout && !color->getKnockout()) + { + addWarning(tr("Could not set knockout property of color '%1'.").arg(color->getName())); + } + + colors.push_back(color); + } + else + { + addWarningUnsupportedElement(); + xml.skipCurrentElement(); + } + } + + if (num_colors > 0 && num_colors != colors.size()) + addWarning(tr("Expected %1 colors, found %2."). + arg(num_colors). + arg(colors.size()) + ); + + // All spot colors are loaded at this point. + // Now deal with depending color compositions from the backlog. + for (auto&& item : backlog) + { + // Process the list of spot color components. + SpotColorComponents out_components; + for (auto&& in_component : item.components) + { + const MapColor* out_color = map->getColor(in_component.spot_color->getPriority()); + if (!out_color || out_color->getSpotColorMethod() != MapColor::SpotColor) + { + addWarning(tr("Spot color %1 not found while processing %2 (%3)."). + arg(in_component.spot_color->getPriority()). + arg(item.color->getPriority()). + arg(item.color->getName()) + ); + continue; // Drop this color, invalid reference + } + + out_components.push_back(in_component); + SpotColorComponent& out_component = out_components.back(); + out_component.spot_color = out_color; // That is the major point! + delete in_component.spot_color; // Delete the temporary proxy. + } + + // Update the current color + item.color->setSpotColorComposition(out_components); + item.color->setKnockout(item.knockout); + if (item.cmyk_from_spot) + item.color->setCmykFromSpotColors(); + if (item.rgb_from_spot) + item.color->setRgbFromSpotColors(); + + if (item.knockout && !item.color->getKnockout()) + { + addWarning(tr("Could not set knockout property of color '%1'.").arg(item.color->getName())); + } + } +} + +void XMLFileImporter::importSymbols() +{ + QScopedValueRollback offset { MapCoord::boundsOffset() }; + MapCoord::boundsOffset().reset(false); + + XmlElementReader symbols_element(xml); + map->setSymbolSetId(symbols_element.attribute(literal::id)); + auto num_symbols = symbols_element.attribute(literal::count); + map->symbols.reserve(qMin(num_symbols, std::size_t(1000))); // 1000 is not a limit + + symbol_dict[QString::number(map->findSymbolIndex(map->getUndefinedPoint()))] = map->getUndefinedPoint(); + symbol_dict[QString::number(map->findSymbolIndex(map->getUndefinedLine()))] = map->getUndefinedLine(); + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::symbol) + { + map->symbols.push_back(Symbol::load(xml, *map, symbol_dict)); + } + else + { + addWarningUnsupportedElement(); + xml.skipCurrentElement(); + } + } + + if (num_symbols > 0 && num_symbols != map->symbols.size()) + addWarning(tr("Expected %1 symbols, found %2."). + arg(num_symbols). + arg(map->symbols.size()) + ); +} + +void XMLFileImporter::importMapParts() +{ + XmlElementReader mapparts_element(xml); + auto num_parts = mapparts_element.attribute(literal::count); + auto current_part_index = mapparts_element.attribute(literal::current); + map->parts.clear(); + map->parts.reserve(qMin(num_parts, std::size_t(20))); // 20 is not a limit + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::part) + { + auto recovery = XmlRecoveryHelper(xml); + auto part = MapPart::load(xml, *map, symbol_dict); + if (xml.hasError() && recovery()) + { + addWarning(tr("Some invalid characters had to be removed.")); + delete part; + part = MapPart::load(xml, *map, symbol_dict); + } + map->parts.push_back(part); + } + else + { + addWarningUnsupportedElement(); + xml.skipCurrentElement(); + } + } + + if (current_part_index < map->parts.size()) + map->current_part_index = current_part_index; + + if (num_parts > 0 && num_parts != map->parts.size()) + addWarning(tr("Expected %1 map parts, found %2."). + arg(num_parts). + arg(map->parts.size()) + ); + + emit map->currentMapPartIndexChanged(map->current_part_index); + emit map->currentMapPartChanged(map->getPart(map->current_part_index)); +} + +void XMLFileImporter::importTemplates() +{ + Q_ASSERT(xml.name() == literal::templates); + + XmlElementReader templates_element(xml); + int first_front_template = templates_element.attribute(literal::first_front_template); + + auto num_templates = templates_element.attribute(literal::count); + map->templates.reserve(qMin(num_templates, std::size_t(20))); // 20 is not a limit + map->closed_templates.reserve(qMin(num_templates, std::size_t(20))); // 20 is not a limit + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::template_string) + { + bool opened = true; + auto temp = Template::loadTemplateConfiguration(xml, *map, opened); + if (opened) + map->templates.push_back(temp.release()); + else + map->closed_templates.push_back(temp.release()); + } + else if (xml.name() == literal::defaults) + { + XmlElementReader defaults_element(xml); + map->image_template_use_meters_per_pixel = defaults_element.attribute(literal::use_meters_per_pixel); + map->image_template_meters_per_pixel = defaults_element.attribute(literal::meters_per_pixel); + map->image_template_dpi = defaults_element.attribute(literal::dpi); + map->image_template_scale = defaults_element.attribute(literal::scale); + } + else + { + qDebug("Unsupported element: %s", qPrintable(xml.qualifiedName().toString())); + xml.skipCurrentElement(); + } + } + + map->first_front_template = qMax(0, qMin(map->getNumTemplates(), first_front_template)); +} + +void XMLFileImporter::importView() +{ + Q_ASSERT(xml.name() == literal::view); + + XmlElementReader view_element(xml); + if (view_element.attribute(literal::area_hatching_enabled)) + map->renderable_options |= Symbol::RenderAreasHatched; + if (view_element.attribute(literal::baseline_view_enabled)) + map->renderable_options |= Symbol::RenderBaselines; + + while (xml.readNextStartElement()) + { + if (xml.name() == literal::grid) + { + map->setGrid(MapGrid().load(xml)); + } + else if (xml.name() == literal::map_view) + { + if (view) + view->load(xml); + } + else + { + xml.skipCurrentElement(); // unsupported + } + } +} + +void XMLFileImporter::importPrint() +{ + Q_ASSERT(xml.name() == literal::print); + + try + { + map->setPrinterConfig(MapPrinterConfig(*map, xml)); + } + catch (FileFormatException& e) + { + addWarning(::OpenOrienteering::ImportExport::tr("Error while loading the printing configuration at %1:%2: %3") + .arg(xml.lineNumber()).arg(xml.columnNumber()).arg(e.message())); + } +} + +void XMLFileImporter::importUndo() +{ + if (!Settings::getInstance().getSetting(Settings::General_SaveUndoRedo).toBool()) + { + xml.skipCurrentElement(); + return; + } + + try + { + map->undoManager().loadUndo(xml, symbol_dict); + } + catch (FileFormatException& e) + { + addWarning(::OpenOrienteering::ImportExport::tr("Error while loading the undo/redo steps at %1:%2: %3") + .arg(xml.lineNumber()).arg(xml.columnNumber()).arg(e.message())); + map->undoManager().clear(); + } +} + +void XMLFileImporter::importRedo() +{ + if (!Settings::getInstance().getSetting(Settings::General_SaveUndoRedo).toBool()) + { + xml.skipCurrentElement(); + return; + } + + try + { + map->undoManager().loadRedo(xml, symbol_dict); + } + catch (FileFormatException& e) + { + addWarning(::OpenOrienteering::ImportExport::tr("Error while loading the undo/redo steps at %1:%2: %3") + .arg(xml.lineNumber()).arg(xml.columnNumber()).arg(e.message())); + map->undoManager().clear(); + } +} + + +} // namespace OpenOrienteering diff --git a/src/fileformats/xml_file_format.h b/src/fileformats/xml_file_format.h new file mode 100644 index 0000000..ff3c607 --- /dev/null +++ b/src/fileformats/xml_file_format.h @@ -0,0 +1,79 @@ +/* + * Copyright 2012, 2013, 2014 Pete Curtis, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_FILE_FORMAT_XML_H +#define OPENORIENTEERING_FILE_FORMAT_XML_H + +#include + +#include "fileformats/file_format.h" + +class QIODevice; + +namespace OpenOrienteering { + +class Exporter; +class Importer; +class Map; +class MapView; + + +/** @brief Interface for dealing with XML files of maps. + */ +class XMLFileFormat : public FileFormat +{ +public: + /** @brief Creates a new file format of type XML. + */ + XMLFileFormat(); + + /** @brief Returns true if the file starts with the character sequence ". + */ + +#ifndef OPENORIENTEERING_FILE_FORMAT_XML_P_H +#define OPENORIENTEERING_FILE_FORMAT_XML_P_H + +#include +#include +#include + +#include "core/symbols/symbol.h" +#include "fileformats/file_import_export.h" + +namespace OpenOrienteering { + +/** Map exporter for the xml based map format. */ +class XMLFileExporter : public Exporter +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::XMLFileExporter) + +public: + XMLFileExporter(QIODevice* stream, Map *map, MapView *view); + ~XMLFileExporter() override {} + + void doExport() override; + +protected: + void exportGeoreferencing(); + void exportColors(); + void exportSymbols(); + void exportMapParts(); + void exportTemplates(); + void exportView(); + void exportPrint(); + void exportUndo(); + void exportRedo(); + +private: + QXmlStreamWriter xml; +}; + + +/** Map importer for the xml based map format. */ +class XMLFileImporter : public Importer +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::XMLFileImporter) + +public: + XMLFileImporter(QIODevice* stream, Map *map, MapView *view); + ~XMLFileImporter() override {} + +protected: + void import(bool load_symbols_only) override; + + void importElements(bool load_symbols_only); + + void addWarningUnsupportedElement(); + void importMapNotes(); + void importGeoreferencing(bool load_symbols_only); + void importColors(); + void importSymbols(); + void importMapParts(); + void importTemplates(); + void importView(); + void importPrint(); + void importUndo(); + void importRedo(); + + QXmlStreamReader xml; + SymbolDictionary symbol_dict; + bool georef_offset_adjusted; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gdal/CMakeLists.txt b/src/gdal/CMakeLists.txt new file mode 100644 index 0000000..6b6c93b --- /dev/null +++ b/src/gdal/CMakeLists.txt @@ -0,0 +1,63 @@ +# +# Copyright 2016-2017 Kai Pastor +# +# This file is part of OpenOrienteering. +# +# OpenOrienteering is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# OpenOrienteering is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenOrienteering. If not, see . + +find_package(GDAL REQUIRED) +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Widgets REQUIRED) +set(CMAKE_AUTOMOC ON) + +# Extra header to be shown in the IDE or to be translated +set(MAPPER_GDAL_HEADERS + ogr_file_format_p.h +) + +set(MAPPER_GDAL_SOURCES + gdal_manager.cpp + gdal_settings_page.cpp + ogr_file_format.cpp + ogr_template.cpp + mapper-osmconf.ini +) + +mapper_translations_sources(${MAPPER_GDAL_HEADERS} ${MAPPER_GDAL_SOURCES}) + +add_library(mapper-gdal STATIC ${MAPPER_GDAL_HEADERS} ${MAPPER_GDAL_SOURCES} "${PROJECT_BINARY_DIR}/gdal/mapper-osmconf.ini") + +target_compile_definitions(mapper-gdal PRIVATE + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + QT_USE_QSTRINGBUILDER +) + +target_compile_definitions(mapper-gdal INTERFACE MAPPER_USE_GDAL) + +target_include_directories(mapper-gdal PRIVATE "${GDAL_INCLUDE_DIR}" "${PROJECT_SOURCE_DIR}/src") + +target_link_libraries(mapper-gdal "${GDAL_LIBRARY}" Qt5::Core Qt5::Gui Qt5::Widgets) + +set_target_properties(mapper-gdal PROPERTIES PREFIX "") + +install(FILES mapper-osmconf.ini DESTINATION "${MAPPER_DATA_DESTINATION}/gdal") + +# Let mapper-osmconf.ini be found via "data:/gdal" during development. +add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gdal/mapper-osmconf.ini" + COMMAND "${CMAKE_COMMAND}" -E make_directory "gdal" + COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/mapper-osmconf.ini" "gdal/mapper-osmconf.ini" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" +) diff --git a/src/gdal/gdal_manager.cpp b/src/gdal/gdal_manager.cpp new file mode 100644 index 0000000..7ac3fa3 --- /dev/null +++ b/src/gdal/gdal_manager.cpp @@ -0,0 +1,394 @@ +/* + * Copyright 2016-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "gdal_manager.h" + +#include + +#include +#include // IWYU pragma: keep +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/backports.h" + + +namespace OpenOrienteering { + +class GdalManager::GdalManagerPrivate +{ +public: + const QString gdal_manager_group{ QStringLiteral("GdalManager") }; + const QString gdal_configuration_group{ QStringLiteral("GdalConfiguration") }; + const QString gdal_dxf_key{ QStringLiteral("dxf") }; + const QString gdal_gpx_key{ QStringLiteral("gpx") }; + const QString gdal_osm_key{ QStringLiteral("osm") }; + const QString gdal_hatch_key{ QStringLiteral("area_hatching") }; + const QString gdal_baseline_key{ QStringLiteral("baseline_view") }; + + GdalManagerPrivate() + : dirty{ true } + { + // GDAL 2.0: GDALAllRegister(); + OGRRegisterAll(); + } + + GdalManagerPrivate(const GdalManagerPrivate&) = delete; + + void configure() + { + if (dirty) + update(); + } + + + QVariant settingsValue(const QString& key, const QVariant& default_value) const + { + QSettings settings; + settings.beginGroup(gdal_manager_group); + return settings.value(key, default_value); + } + + void setSettingsValue(const QString& key, const QVariant& value) + { + QSettings settings; + settings.beginGroup(gdal_manager_group); + settings.setValue(key, value); + } + + + void setFormatEnabled(GdalManager::FileFormat format, bool enabled) + { + QString key; + switch (format) + { + case GdalManager::DXF: + key = gdal_dxf_key; + break; + + case GdalManager::GPX: + key = gdal_gpx_key; + break; + + case GdalManager::OSM: + key = gdal_osm_key; + break; + } + setSettingsValue(key, enabled); + dirty = true; + } + + bool isFormatEnabled(FileFormat format) const + { + QString key; + bool default_value = true; + switch (format) + { + case GdalManager::DXF: + key = gdal_dxf_key; + break; + + case GdalManager::GPX: + key = gdal_gpx_key; + default_value = false; + break; + + case GdalManager::OSM: + key = gdal_osm_key; + break; + } + return settingsValue(key, default_value).toBool(); + } + + const std::vector& supportedRasterExtensions() const + { + /// \todo + static std::vector ret; + return ret; + } + + const std::vector& supportedVectorExtensions() const + { + if (dirty) + const_cast(this)->update(); + return enabled_vector_extensions; + } + + QStringList parameterKeys() const + { + if (dirty) + const_cast(this)->update(); + return applied_parameters; + } + + QString parameterValue(const QString& key) const + { + if (dirty) + const_cast(this)->update(); + QSettings settings; + settings.beginGroup(gdal_configuration_group); + return settings.value(key).toString(); + } + + void setParameterValue(const QString& key, const QString& value) + { + QSettings settings; + settings.beginGroup(gdal_configuration_group); + settings.setValue(key, QVariant{ value }); + dirty = true; + } + + void unsetParameter(const QString& key) + { + QSettings settings; + settings.beginGroup(gdal_configuration_group); + settings.remove(key); + dirty = true; + } + +private: + void update() + { + QSettings settings; + +#ifdef GDAL_DMD_EXTENSIONS + // GDAL >= 2.0 + settings.beginGroup(gdal_manager_group); + auto count = GDALGetDriverCount(); + enabled_vector_extensions.clear(); + enabled_vector_extensions.reserve(std::size_t(count)); + for (auto i = 0; i < count; ++i) + { + auto driver_data = GDALGetDriver(i); + auto type = GDALGetMetadataItem(driver_data, GDAL_DCAP_VECTOR, nullptr); + if (qstrcmp(type, "YES") != 0) + continue; + + // Skip write-only drivers. + auto cap_open = GDALGetMetadataItem(driver_data, GDAL_DCAP_OPEN, nullptr); + if (qstrcmp(cap_open, "YES") != 0) + continue; + + auto extensions_raw = GDALGetMetadataItem(driver_data, GDAL_DMD_EXTENSIONS, nullptr); + auto extensions = QByteArray::fromRawData(extensions_raw, int(qstrlen(extensions_raw))); + for (auto pos = 0; pos >= 0; ) + { + auto start = pos ? pos + 1 : 0; + pos = extensions.indexOf(' ', start); + auto extension = extensions.mid(start, pos - start); + if (extension.isEmpty()) + continue; + if (extension == "dxf" && !settings.value(gdal_dxf_key, true).toBool()) + continue; + if (extension == "gpx" && !settings.value(gdal_gpx_key, false).toBool()) + continue; + if (extension == "osm" && !settings.value(gdal_osm_key, true).toBool()) + continue; + enabled_vector_extensions.emplace_back(extension); + } + } + settings.endGroup(); +#else + // GDAL < 2.0 does not provide the supported extensions + static const std::vector default_extensions = { + "shp", "dbf", + /* "dxf", */ + /* "gpx", */ + /* "osm", */ "pbf", + }; + enabled_vector_extensions.reserve(default_extensions.size() + 3); + enabled_vector_extensions = default_extensions; + + settings.beginGroup(gdal_manager_group); + if (settings.value(gdal_dxf_key, true).toBool()) + enabled_vector_extensions.push_back("dxf"); + if (settings.value(gdal_gpx_key, false).toBool()) + enabled_vector_extensions.push_back("gpx"); + if (settings.value(gdal_osm_key, true).toBool()) + enabled_vector_extensions.push_back("osm"); + settings.endGroup(); +#endif + + // Using osmconf.ini to detect a directory with data from gdal. The + // data:/gdal directory will always exist, due to mapper-osmconf.ini. + auto osm_conf_ini = QFileInfo(QLatin1String("data:/gdal/osmconf.ini")); + if (osm_conf_ini.exists()) + { + auto gdal_data = osm_conf_ini.absolutePath(); + Q_ASSERT(!gdal_data.contains(QStringLiteral("data:"))); + // The user may overwrite this default in the settings. + CPLSetConfigOption("GDAL_DATA", QDir::toNativeSeparators(gdal_data).toLocal8Bit()); + } + + settings.beginGroup(gdal_configuration_group); + + const char* defaults[][2] = { + { "CPL_DEBUG", "OFF" }, + { "USE_PROJ_480_FEATURES", "YES" }, + { "OSM_USE_CUSTOM_INDEXING", "NO" }, + { "GPX_ELE_AS_25D", "YES" }, + }; + for (const auto setting : defaults) + { + const auto key = QString::fromLatin1(setting[0]); + if (!settings.contains(key)) + { + settings.setValue(key, QVariant{QLatin1String(setting[1])}); + } + } + + osm_conf_ini = QFileInfo(QLatin1String("data:/gdal/mapper-osmconf.ini")); + if (osm_conf_ini.exists()) + { + auto osm_conf_ini_path = QDir::toNativeSeparators(osm_conf_ini.absoluteFilePath()).toLocal8Bit(); + auto key = QString::fromLatin1("OSM_CONFIG_FILE"); + auto update_settings = !settings.contains(key); + if (!update_settings) + { + auto current = settings.value(key).toByteArray(); + settings.beginGroup(QLatin1String("default")); + auto current_default = settings.value(key).toByteArray(); + settings.endGroup(); + update_settings = (current == current_default && current != osm_conf_ini_path); + } + if (update_settings) + { + settings.setValue(key, osm_conf_ini_path); + settings.beginGroup(QLatin1String("default")); + settings.setValue(key, osm_conf_ini_path); + settings.endGroup(); + } + } + + auto new_parameters = settings.childKeys(); + new_parameters.sort(); + for (const auto& parameter : qAsConst(new_parameters)) + { + CPLSetConfigOption(parameter.toLatin1().constData(), settings.value(parameter).toByteArray().constData()); + } + for (const auto& parameter : qAsConst(applied_parameters)) + { + if (!new_parameters.contains(parameter) + && parameter != QLatin1String{ "GDAL_DATA" }) + { + CPLSetConfigOption(parameter.toLatin1().constData(), nullptr); + } + } + applied_parameters.swap(new_parameters); + + CPLFinderClean(); // force re-initialization of file finding tools + dirty = false; + } + + mutable bool dirty; + + mutable std::vector enabled_vector_extensions; + + mutable QStringList applied_parameters; + +}; + + + +// ### GdalManager ### + +GdalManager::GdalManager() +{ + static GdalManagerPrivate manager; + p = &manager; +} + +void GdalManager::configure() +{ + p->configure(); +} + + +bool GdalManager::isAreaHatchingEnabled() const +{ + return p->settingsValue(p->gdal_hatch_key, false).toBool(); +} + +void GdalManager::setAreaHatchingEnabled(bool enabled) +{ + p->setSettingsValue(p->gdal_hatch_key, enabled); +} + +bool GdalManager::isBaselineViewEnabled() const +{ + return p->settingsValue(p->gdal_baseline_key, false).toBool(); +} + +void GdalManager::setBaselineViewEnabled(bool enabled) +{ + p->setSettingsValue(p->gdal_baseline_key, enabled); +} + + +void GdalManager::setFormatEnabled(GdalManager::FileFormat format, bool enabled) +{ + p->setFormatEnabled(format, enabled); +} + +bool GdalManager::isFormatEnabled(GdalManager::FileFormat format) const +{ + return p->isFormatEnabled(format); +} + +const std::vector&GdalManager::supportedRasterExtensions() const +{ + return p->supportedRasterExtensions(); +} + +const std::vector& GdalManager::supportedVectorExtensions() const +{ + return p->supportedVectorExtensions(); +} + +QStringList GdalManager::parameterKeys() const +{ + return p->parameterKeys(); +} + +QString GdalManager::parameterValue(const QString& key) const +{ + return p->parameterValue(key); +} + +void GdalManager::setParameterValue(const QString& key, const QString& value) +{ + p->setParameterValue(key, value); +} + +void GdalManager::unsetParameter(const QString& key) +{ + p->unsetParameter(key); +} + + +} // namespace OpenOrienteering diff --git a/src/gdal/gdal_manager.h b/src/gdal/gdal_manager.h new file mode 100644 index 0000000..2c0f06c --- /dev/null +++ b/src/gdal/gdal_manager.h @@ -0,0 +1,136 @@ +/* + * Copyright 2016, 2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_GDAL_MANAGER_H +#define OPENORIENTEERING_GDAL_MANAGER_H + + +#include + +class QByteArray; +class QString; +class QStringList; + + +namespace OpenOrienteering { + +/** + * A utility class which takes care of GDAL settings and options. + * + * This class provides lists of extensions supported via GDAL in Mapper. + * It sets and updates GDAL configuration parameters from Mapper's settings. + * + * There is no need to keep objects of this class for an extended life time: + * instantiation is cheap; the actual state is shared and retained. + */ +class GdalManager +{ +private: + class GdalManagerPrivate; + + GdalManagerPrivate* p; + +public: + enum FileFormat + { + DXF, + GPX, + OSM + }; + + /** + * Constructs a new manager object. + */ + GdalManager(); + + /** + * Sets the GDAL configuration from Mapper's defaults and settings. + */ + void configure(); + + + /** + * Returns the area hatching display setting. + */ + bool isAreaHatchingEnabled() const; + + /** + * Sets the area hatching template display setting. + */ + void setAreaHatchingEnabled(bool enabled); + + + /** + * Returns the baseline view template display setting. + */ + bool isBaselineViewEnabled() const; + + /** + * Sets the baseline view template display setting. + */ + void setBaselineViewEnabled(bool enabled); + + + /** + * Enables or disables handling of a particular file format by GDAL/OGR. + */ + void setFormatEnabled(FileFormat format, bool enabled); + + /** + * Returns if GDAL/OGR will handle a particular file format. + */ + bool isFormatEnabled(FileFormat format) const; + + + /** + * Returns the file name extensions for supported raster formats. + */ + const std::vector& supportedRasterExtensions() const; + + /** + * Returns the file name extensions for supported vector formats. + */ + const std::vector& supportedVectorExtensions() const; + + + /** + * Returns the list of GDAL configuration parameters. + */ + QStringList parameterKeys() const; + + /** + * Returns a GDAL configuration parameter value. + */ + QString parameterValue(const QString& key) const; + + /** + * Sets a GDAL configuration parameter value. + */ + void setParameterValue(const QString& key, const QString& value); + + /** + * Unsets a GDAL configuration parameter value. + */ + void unsetParameter(const QString& key); +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_GDAL_MANAGER_H diff --git a/src/gdal/gdal_settings_page.cpp b/src/gdal/gdal_settings_page.cpp new file mode 100644 index 0000000..f957c1b --- /dev/null +++ b/src/gdal/gdal_settings_page.cpp @@ -0,0 +1,253 @@ +/* + * Copyright 2016-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "gdal_settings_page.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "fileformats/file_format_registry.h" +#include "gdal/gdal_manager.h" +#include "gdal/ogr_file_format.h" +#include "gui/util_gui.h" +#include "util/backports.h" + + +namespace OpenOrienteering { + +GdalSettingsPage::GdalSettingsPage(QWidget* parent) +: SettingsPage(parent) +{ + auto form_layout = new QFormLayout(); + + form_layout->addRow(Util::Headline::create(tr("Import with GDAL/OGR:"))); + + import_dxf = new QCheckBox(tr("DXF")); + form_layout->addRow(import_dxf); + + import_gpx = new QCheckBox(tr("GPX")); + form_layout->addRow(import_gpx); + + import_osm = new QCheckBox(tr("OSM")); + form_layout->addRow(import_osm); + + + form_layout->addItem(Util::SpacerItem::create(this)); + form_layout->addRow(Util::Headline::create(tr("Templates"))); + + view_hatch = new QCheckBox(tr("Hatch areas")); + form_layout->addRow(view_hatch); + + view_baseline = new QCheckBox(tr("Baseline view")); + form_layout->addRow(view_baseline); + + + form_layout->addItem(Util::SpacerItem::create(this)); + form_layout->addRow(Util::Headline::create(tr("Configuration"))); + + auto layout = new QVBoxLayout(this); + + layout->addLayout(form_layout); + + parameters = new QTableWidget(1, 2); + parameters->verticalHeader()->hide(); + parameters->setHorizontalHeaderLabels({ tr("Parameter"), tr("Value") }); + auto header_view = parameters->horizontalHeader(); + header_view->setSectionResizeMode(0, QHeaderView::Stretch); + header_view->setSectionResizeMode(1, QHeaderView::Stretch); + header_view->setSectionsClickable(false); + layout->addWidget(parameters, 1); + + updateWidgets(); + + connect(parameters, &QTableWidget::cellChanged, this, &GdalSettingsPage::cellChange); +} + +GdalSettingsPage::~GdalSettingsPage() +{ + // nothing, not inlined +} + +QString GdalSettingsPage::title() const +{ + return tr("GDAL/OGR"); +} + +void GdalSettingsPage::apply() +{ + GdalManager manager; + manager.setFormatEnabled(GdalManager::DXF, import_dxf->isChecked()); + manager.setFormatEnabled(GdalManager::GPX, import_gpx->isChecked()); + manager.setFormatEnabled(GdalManager::OSM, import_osm->isChecked()); + manager.setAreaHatchingEnabled(view_hatch->isChecked()); + manager.setBaselineViewEnabled(view_baseline->isChecked()); + + // The file format constructor establishes the extensions. + auto format = new OgrFileFormat(); + FileFormats.unregisterFormat(FileFormats.findFormat(format->id())); + FileFormats.registerFormat(format); + + const auto old_parameters = manager.parameterKeys(); + + QStringList new_parameters; + new_parameters.reserve(parameters->rowCount()); + for (int row = 0, end = parameters->rowCount(); row < end; ++row) + { + auto key = parameters->item(row, 0)->text().trimmed(); + if (!key.isEmpty()) + { + new_parameters.append(key); + auto value = parameters->item(row, 1)->text(); + manager.setParameterValue(key, value.trimmed()); + } + } + for (const auto& key : qAsConst(old_parameters)) + { + if (!new_parameters.contains(key)) + { + manager.unsetParameter(key); + } + } + + Settings::getInstance().applySettings(); +} + +void GdalSettingsPage::reset() +{ + updateWidgets(); +} + +void GdalSettingsPage::updateWidgets() +{ + GdalManager manager; + import_dxf->setChecked(manager.isFormatEnabled(GdalManager::DXF)); + import_gpx->setChecked(manager.isFormatEnabled(GdalManager::GPX)); + import_osm->setChecked(manager.isFormatEnabled(GdalManager::OSM)); + view_hatch->setChecked(manager.isAreaHatchingEnabled()); + view_baseline->setChecked(manager.isBaselineViewEnabled()); + + auto options = manager.parameterKeys(); + options.sort(); + parameters->setRowCount(options.size() + 1); + + QSignalBlocker block(parameters); + auto row = 0; + for (const auto& item : qAsConst(options)) + { + auto key_item = new QTableWidgetItem(item); + parameters->setItem(row, 0, key_item); + auto value_item = new QTableWidgetItem(manager.parameterValue(item)); + parameters->setItem(row, 1, value_item); + ++row; + } + parameters->setRowCount(row+1); + parameters->setItem(row, 0, new QTableWidgetItem()); + parameters->setItem(row, 1, new QTableWidgetItem()); +} + +void GdalSettingsPage::cellChange(int row, int column) +{ + const QString key = parameters->item(row, 0)->text().trimmed(); + const QString value = parameters->item(row, 1)->text(); + + if (column == 1 && key.isEmpty()) + { + // Shall not happen + qWarning("Empty key for modified tag value!"); + } + else if (column == 0) + { + QSignalBlocker block(parameters); + parameters->item(row, 0)->setText(key); // trimmed + + auto last_row = parameters->rowCount() - 1; + int duplicate = findDuplicateKey(key, row); + if (key.isEmpty()) + { + if (row == last_row) + { + parameters->item(row, 1)->setText({ }); + } + else + { + parameters->model()->removeRow(row); + parameters->setCurrentCell(row, 0); + parameters->setFocus(); + } + } + else if (duplicate != row) + { + if (row == last_row) + { + parameters->item(row, 0)->setText({ }); + parameters->item(row, 0)->setText({ }); + } + else + { + parameters->model()->removeRow(row); + } + parameters->setCurrentCell(duplicate, 0); + parameters->setFocus(); + } + else + { + if (row == last_row) + { + parameters->setRowCount(last_row + 2); + parameters->setItem(last_row + 1, 0, new QTableWidgetItem()); + parameters->setItem(last_row + 1, 1, new QTableWidgetItem()); + } + + if (value.isEmpty()) + { + GdalManager manager; + parameters->item(row, 1)->setText(manager.parameterValue(key)); + } + } + } +} + +int GdalSettingsPage::findDuplicateKey(const QString& key, int row) const +{ + for (int i = 0, end = parameters->rowCount(); i < end; ++i) + { + if (i != row + && parameters->item(i, 0)->text() == key) + { + row = i; + break; + } + } + return row; +} + + +} // namespace OpenOrienteering diff --git a/src/gdal/gdal_settings_page.h b/src/gdal/gdal_settings_page.h new file mode 100644 index 0000000..0dec3b6 --- /dev/null +++ b/src/gdal/gdal_settings_page.h @@ -0,0 +1,68 @@ +/* + * Copyright 2016-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_GDAL_SETTINGS_PAGE_H +#define OPENORIENTEERING_GDAL_SETTINGS_PAGE_H + +#include +#include + +#include "gui/widgets/settings_page.h" + +class QCheckBox; +class QTableWidget; +class QWidget; + +namespace OpenOrienteering { + + +class GdalSettingsPage : public SettingsPage +{ +Q_OBJECT +public: + explicit GdalSettingsPage(QWidget* parent = nullptr); + + ~GdalSettingsPage() override; + + QString title() const override; + + void apply() override; + + void reset() override; + +protected: + void updateWidgets(); + + void cellChange(int row, int column); + + int findDuplicateKey(const QString& key, int row) const; + +private: + QCheckBox* import_dxf; + QCheckBox* import_gpx; + QCheckBox* import_osm; + QCheckBox* view_hatch; + QCheckBox* view_baseline; + QTableWidget* parameters; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gdal/mapper-osmconf.ini b/src/gdal/mapper-osmconf.ini new file mode 100644 index 0000000..877a3f0 --- /dev/null +++ b/src/gdal/mapper-osmconf.ini @@ -0,0 +1,123 @@ +# +# Configuration file for OSM import +# +# Customized for OpenOrienteering Mapper +# + +# put here the name of keys for ways that are assumed to be polygons if they are closed +# see https://wiki.openstreetmap.org/wiki/Map_Features +closed_ways_are_polygons=aeroway,amenity,boundary,building,craft,geological,historic,landuse,leisure,military,natural,office,place,shop,sport,tourism + +# comment to avoid laundering of keys ( ':' turned into '_' ) +#attribute_name_laundering=yes + +# uncomment to report all nodes, including the ones without any (significant) tag +#report_all_nodes=yes + +# uncomment to report all ways, including the ones without any (significant) tag +#report_all_ways=yes + +[points] +# common attributes +osm_id=yes +osm_version=no +osm_timestamp=yes +osm_uid=no +osm_user=no +osm_changeset=no + +# keys to report as OGR fields +attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density +# keys that, alone, are not significant enough to report a node as a OGR point +unsignificant=created_by,converted_by,source,time,ele,attribution +# keys that should NOT be reported in the "other_tags" field +ignore=created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME +# uncomment to avoid creation of "other_tags" field +#other_tags=no +# uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive +#all_tags=yes + +[lines] +# common attributes +osm_id=yes +osm_version=no +osm_timestamp=yes +osm_uid=no +osm_user=no +osm_changeset=no + +# keys to report as OGR fields +attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density + +# type of attribute 'foo' can be changed with something like +#foo_type=Integer/Real/String/DateTime + +# keys that should NOT be reported in the "other_tags" field +ignore=created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME +# uncomment to avoid creation of "other_tags" field +#other_tags=no +# uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive +#all_tags=yes + +#computed_attributes must appear before the keywords _type and _sql +computed_attributes=z_order +z_order_type=Integer +# Formula based on https://github.com/openstreetmap/osm2pgsql/blob/master/style.lua#L13 +# [foo] is substituted by value of tag foo. When substitution is not wished, the [ character can be escaped with \[ in literals +z_order_sql="SELECT (CASE [highway] WHEN 'minor' THEN 3 WHEN 'road' THEN 3 WHEN 'unclassified' THEN 3 WHEN 'residential' THEN 3 WHEN 'tertiary_link' THEN 4 WHEN 'tertiary' THEN 4 WHEN 'secondary_link' THEN 6 WHEN 'secondary' THEN 6 WHEN 'primary_link' THEN 7 WHEN 'primary' THEN 7 WHEN 'trunk_link' THEN 8 WHEN 'trunk' THEN 8 WHEN 'motorway_link' THEN 9 WHEN 'motorway' THEN 9 ELSE 0 END) + (CASE WHEN [bridge] IN ('yes', 'true', '1') THEN 10 ELSE 0 END) + (CASE WHEN [tunnel] IN ('yes', 'true', '1') THEN -10 ELSE 0 END) + (CASE WHEN [railway] IS NOT NULL THEN 5 ELSE 0 END) + (CASE WHEN [layer] IS NOT NULL THEN 10 * CAST([layer] AS INTEGER) ELSE 0 END)" + +[multipolygons] +# common attributes +# note: for multipolygons, osm_id=yes instantiates a osm_id field for the id of relations +# and a osm_way_id field for the id of closed ways. Both fields are exclusively set. +osm_id=yes +osm_version=no +osm_timestamp=yes +osm_uid=no +osm_user=no +osm_changeset=no + +# keys to report as OGR fields +attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density +# keys that should NOT be reported in the "other_tags" field +ignore=area,created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME +# uncomment to avoid creation of "other_tags" field +#other_tags=no +# uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive +#all_tags=yes + +[multilinestrings] +# common attributes +osm_id=yes +osm_version=no +osm_timestamp=yes +osm_uid=no +osm_user=no +osm_changeset=no + +# keys to report as OGR fields +attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density +# keys that should NOT be reported in the "other_tags" field +ignore=area,created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME +# uncomment to avoid creation of "other_tags" field +#other_tags=no +# uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive +#all_tags=yes + +[other_relations] +# common attributes +osm_id=yes +osm_version=no +osm_timestamp=yes +osm_uid=no +osm_user=no +osm_changeset=no + +# keys to report as OGR fields +attributes=access,aerialway,aeroway,amenity,area,barrier,building,cycleway,foot,geological,highway,historic,intermittent,landuse,leaf_type,leisure,location,man_made,military,name,natural,pipeline,power,railway,ruins,sport,surface,shop,smoothness,ref,tourism,tracktype,tunnel,waterway,wood:age,wood:density +# keys that should NOT be reported in the "other_tags" field +ignore=area,created_by,converted_by,source,time,note,openGeoDB:,fixme,FIXME +# uncomment to avoid creation of "other_tags" field +#other_tags=no +# uncomment to create "all_tags" field. "all_tags" and "other_tags" are exclusive +#all_tags=yes diff --git a/src/gdal/ogr_file_format.cpp b/src/gdal/ogr_file_format.cpp new file mode 100644 index 0000000..8b9521a --- /dev/null +++ b/src/gdal/ogr_file_format.cpp @@ -0,0 +1,1455 @@ +/* + * Copyright 2016-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "ogr_file_format.h" +#include "ogr_file_format_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/georeferencing.h" +#include "core/latlon.h" +#include "core/map.h" +#include "core/map_color.h" +#include "core/map_coord.h" +#include "core/map_part.h" +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "core/symbols/text_symbol.h" +#include "fileformats/file_import_export.h" +#include "gdal/gdal_manager.h" + +// IWYU pragma: no_forward_declare QFile + + +namespace OpenOrienteering { + +namespace ogr { + + class OGRDataSourceHDeleter + { + public: + void operator()(OGRDataSourceH data_source) const + { + OGRReleaseDataSource(data_source); + } + }; + + /** A convenience class for OGR C API datasource handles, similar to std::unique_ptr. */ + using unique_datasource = std::unique_ptr::type, OGRDataSourceHDeleter>; + + + class OGRFeatureHDeleter + { + public: + void operator()(OGRFeatureH feature) const + { + OGR_F_Destroy(feature); + } + }; + + /** A convenience class for OGR C API feature handles, similar to std::unique_ptr. */ + using unique_feature = std::unique_ptr::type, OGRFeatureHDeleter>; + + + class OGRGeometryHDeleter + { + public: + void operator()(OGRGeometryH geometry) const + { + OGR_G_DestroyGeometry(geometry); + } + }; + + /** A convenience class for OGR C API geometry handles, similar to std::unique_ptr. */ + using unique_geometry = std::unique_ptr::type, OGRGeometryHDeleter>; + +} // namespace ogr + + + +namespace { + + void applyPenWidth(OGRStyleToolH tool, LineSymbol* line_symbol) + { + int is_null; + auto pen_width = OGR_ST_GetParamDbl(tool, OGRSTPenWidth, &is_null); + if (!is_null) + { + Q_ASSERT(OGR_ST_GetUnit(tool) == OGRSTUMM); + + if (pen_width <= 0.01) + pen_width = 0.1; + + line_symbol->setLineWidth(pen_width); + } + } + + void applyPenCap(OGRStyleToolH tool, LineSymbol* line_symbol) + { + int is_null; + auto pen_cap = OGR_ST_GetParamStr(tool, OGRSTPenCap, &is_null); + if (!is_null) + { + switch (pen_cap[0]) + { + case 'p': + line_symbol->setCapStyle(LineSymbol::SquareCap); + break; + case 'r': + line_symbol->setCapStyle(LineSymbol::RoundCap); + break; + default: + ; + } + } + } + + void applyPenJoin(OGRStyleToolH tool, LineSymbol* line_symbol) + { + int is_null; + auto pen_join = OGR_ST_GetParamStr(tool, OGRSTPenJoin, &is_null); + if (!is_null) + { + switch (pen_join[0]) + { + case 'b': + line_symbol->setJoinStyle(LineSymbol::BevelJoin); + break; + case 'r': + line_symbol->setJoinStyle(LineSymbol::RoundJoin); + break; + default: + ; + } + } + } + + void applyPenPattern(OGRStyleToolH tool, LineSymbol* line_symbol) + { + int is_null; + auto raw_pattern = OGR_ST_GetParamStr(tool, OGRSTPenPattern, &is_null); + if (!is_null) + { + auto pattern = QString::fromLatin1(raw_pattern); + auto sub_pattern_re = QRegularExpression(QString::fromLatin1("([0-9.]+)([a-z]*) *([0-9.]+)([a-z]*)")); + auto match = sub_pattern_re.match(pattern); + double length_0{}, length_1{}; + bool ok = match.hasMatch(); + if (ok) + length_0 = match.capturedRef(1).toDouble(&ok); + if (ok) + length_1 = match.capturedRef(3).toDouble(&ok); + if (ok) + { + /// \todo Apply units from capture 2 and 4 + line_symbol->setDashed(true); + line_symbol->setDashLength(qMax(100, qRound(length_0 * 1000))); + line_symbol->setBreakLength(qMax(100, qRound(length_1 * 1000))); + } + else + { + qDebug("OgrFileFormat: Failed to parse dash pattern '%s'", raw_pattern); + } + } + } + +#if 0 + int getFontSize(const char* font_size_string) + { + auto pattern = QString::fromLatin1(font_size_string); + auto sub_pattern_re = QRegularExpression(QString::fromLatin1("([0-9.]+)([a-z]*)")); + auto match = sub_pattern_re.match(pattern); + double font_size; + bool ok = match.hasMatch(); + if (ok) + font_size = match.capturedRef(1).toDouble(&ok); + if (ok) + { + auto unit = match.capturedRef(2).toUtf8(); + if (!unit.isEmpty()) + { + if (unit == "pt") + { + + } + else if (unit == "px") + { + + } + else + { + qDebug("OgrFileFormat: Unsupported font size unit '%s'", unit.constData()); + } + } + } + else + { + qDebug("OgrFileFormat: Failed to parse font size '%s'", font_size_string); + font_size = 0; + } + return font_size; + } +#endif + + void applyLabelAnchor(int anchor, TextObject* text_object) + { + auto v_align = (anchor - 1) / 3; + switch (v_align) + { + case 0: + text_object->setVerticalAlignment(TextObject::AlignBaseline); + break; + case 1: + text_object->setVerticalAlignment(TextObject::AlignVCenter); + break; + case 2: + text_object->setVerticalAlignment(TextObject::AlignTop); + break; + case 3: + text_object->setVerticalAlignment(TextObject::AlignBottom); + break; + default: + Q_UNREACHABLE(); + } + auto h_align = (anchor - 1) % 3; + switch (h_align) + { + case 0: + text_object->setHorizontalAlignment(TextObject::AlignLeft); + break; + case 1: + text_object->setHorizontalAlignment(TextObject::AlignHCenter); + break; + case 2: + text_object->setHorizontalAlignment(TextObject::AlignRight); + break; + default: + Q_UNREACHABLE(); + } + } + + QString toPrettyWkt(OGRSpatialReferenceH spatial_reference) + { + char* srs_wkt_raw = nullptr; + OSRExportToPrettyWkt(spatial_reference, &srs_wkt_raw, 0); + auto srs_wkt = QString::fromLocal8Bit(srs_wkt_raw); + CPLFree(srs_wkt_raw); + return srs_wkt; + } + + class AverageLatLon + { + private: + double x = 0; + double y = 0; + unsigned num_coords = 0u; + + void handleGeometry(OGRGeometryH geometry) + { + auto const geometry_type = wkbFlatten(OGR_G_GetGeometryType(geometry)); + switch (geometry_type) + { + case OGRwkbGeometryType::wkbPoint: + case OGRwkbGeometryType::wkbLineString: + for (auto num_points = OGR_G_GetPointCount(geometry), i = 0; i < num_points; ++i) + { + x += OGR_G_GetX(geometry, i); + y += OGR_G_GetY(geometry, i); + ++num_coords; + } + break; + + case OGRwkbGeometryType::wkbPolygon: + case OGRwkbGeometryType::wkbMultiPoint: + case OGRwkbGeometryType::wkbMultiLineString: + case OGRwkbGeometryType::wkbMultiPolygon: + case OGRwkbGeometryType::wkbGeometryCollection: + for (auto num_geometries = OGR_G_GetGeometryCount(geometry), i = 0; i < num_geometries; ++i) + { + handleGeometry(OGR_G_GetGeometryRef(geometry, i)); + } + break; + + default: + ; // unsupported type, will be reported in importGeometry + } + } + + public: + AverageLatLon(OGRDataSourceH data_source) + { + auto geo_srs = ogr::unique_srs { OSRNewSpatialReference(nullptr) }; + OSRSetWellKnownGeogCS(geo_srs.get(), "WGS84"); + + auto num_layers = OGR_DS_GetLayerCount(data_source); + for (int i = 0; i < num_layers; ++i) + { + if (auto layer = OGR_DS_GetLayer(data_source, i)) + { + auto spatial_reference = OGR_L_GetSpatialRef(layer); + if (!spatial_reference) + continue; + + auto transformation = ogr::unique_transformation{ OCTNewCoordinateTransformation(spatial_reference, geo_srs.get()) }; + if (!transformation) + continue; + + OGR_L_ResetReading(layer); + while (auto feature = ogr::unique_feature(OGR_L_GetNextFeature(layer))) + { + auto geometry = OGR_F_GetGeometryRef(feature.get()); + if (!geometry || OGR_G_IsEmpty(geometry)) + continue; + + auto error = OGR_G_Transform(geometry, transformation.get()); + if (error) + continue; + + handleGeometry(geometry); + } + } + } + } + + operator LatLon() const + { + return num_coords ? LatLon{ y / num_coords, x / num_coords } : LatLon{}; + } + + }; +} // namespace + + + +// ### OgrFileFormat ### + +OgrFileFormat::OgrFileFormat() + : FileFormat(OgrFile, "OGR", ::OpenOrienteering::ImportExport::tr("Geospatial vector data"), QString{}, ImportSupported) +{ + for (const auto& extension : GdalManager().supportedVectorExtensions()) + addExtension(QString::fromLatin1(extension)); +} + +bool OgrFileFormat::understands(const unsigned char* /*buffer*/, std::size_t /*size*/) const +{ + return true; +} + +Importer* OgrFileFormat::createImporter(QIODevice* stream, Map *map, MapView *view) const +{ + return new OgrFileImport(stream, map, view); +} + + + +// ### OgrFileImport ### + +OgrFileImport::OgrFileImport(QIODevice* stream, Map* map, MapView* view, UnitType unit_type) + : Importer(stream, map, view) + , manager{ OGR_SM_Create(nullptr) } + , unit_type{ unit_type } + , georeferencing_import_enabled{ true } +{ + GdalManager().configure(); + + setOption(QLatin1String{ "Separate layers" }, QVariant{ false }); + + // OGR feature style defaults + default_pen_color = new MapColor(QLatin1String{"Purple"}, 0); + default_pen_color->setSpotColorName(QLatin1String{"PURPLE"}); + default_pen_color->setCmyk({0.35f, 0.85f, 0.0, 0.0}); + default_pen_color->setRgbFromCmyk(); + map->addColor(default_pen_color, 0); + + // 50% opacity of 80% Purple should result in app. 40% Purple (on white) in + // normal view and in an opaque Purple slightly lighter than lines and + // points in overprinting simulation mode. + auto default_brush_color = new MapColor(default_pen_color->getName() + QLatin1String(" 40%"), 0); + default_brush_color->setSpotColorComposition({ {default_pen_color, 0.8f} }); + default_brush_color->setCmykFromSpotColors(); + default_brush_color->setRgbFromSpotColors(); + default_brush_color->setOpacity(0.5f); + map->addColor(default_brush_color, 1); + + default_point_symbol = new PointSymbol(); + default_point_symbol->setName(tr("Point")); + default_point_symbol->setNumberComponent(0, 1); + default_point_symbol->setInnerColor(default_pen_color); + default_point_symbol->setInnerRadius(500); // (um) + map->addSymbol(default_point_symbol, 0); + + default_line_symbol = new LineSymbol(); + default_line_symbol->setName(tr("Line")); + default_line_symbol->setNumberComponent(0, 2); + default_line_symbol->setColor(default_pen_color); + default_line_symbol->setLineWidth(0.1); // (0.1 mm, nearly cosmetic) + default_line_symbol->setCapStyle(LineSymbol::FlatCap); + default_line_symbol->setJoinStyle(LineSymbol::MiterJoin); + map->addSymbol(default_line_symbol, 1); + + default_area_symbol = new AreaSymbol(); + default_area_symbol->setName(tr("Area")); + default_area_symbol->setNumberComponent(0, 3); + default_area_symbol->setColor(default_brush_color); + map->addSymbol(default_area_symbol, 2); + + default_text_symbol = new TextSymbol(); + default_text_symbol->setName(tr("Text")); + default_text_symbol->setNumberComponent(0, 4); + default_text_symbol->setColor(default_pen_color); + map->addSymbol(default_text_symbol, 3); +} + + +OgrFileImport::~OgrFileImport() = default; // not inlined + + + +void OgrFileImport::setGeoreferencingImportEnabled(bool enabled) +{ + georeferencing_import_enabled = enabled; +} + + + +ogr::unique_srs OgrFileImport::srsFromMap() +{ + auto srs = ogr::unique_srs(OSRNewSpatialReference(nullptr)); + auto& georef = map->getGeoreferencing(); + if (georef.isValid() && !georef.isLocal()) + { + OSRSetProjCS(srs.get(), "Projected map SRS"); + OSRSetWellKnownGeogCS(srs.get(), "WGS84"); + auto spec = QByteArray(georef.getProjectedCRSSpec().toLatin1() + " +wktext"); + auto error = OSRImportFromProj4(srs.get(), spec); + if (!error) + return srs; + + addWarning(tr("Unable to setup \"%1\" SRS for GDAL: %2") + .arg(QString::fromLatin1(spec), QString::number(error))); + srs.reset(OSRNewSpatialReference(nullptr)); + } + + OSRSetLocalCS(srs.get(), "Local SRS"); + return srs; +} + + + +void OgrFileImport::import(bool load_symbols_only) +{ + auto file = qobject_cast(stream); + if (!file) + { + throw FileFormatException("Internal error"); /// \todo Review design and/or message + } + + auto filename = file->fileName(); + // GDAL 2.0: ... = GDALOpenEx(template_path.toLatin1(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr); + auto data_source = ogr::unique_datasource(OGROpen(filename.toUtf8().constData(), 0, nullptr)); + if (!data_source) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read '%1': %2") + .arg(filename, QString::fromLatin1(CPLGetLastErrorMsg()))); + } + + if (auto driver = OGR_DS_GetDriver(data_source.get())) + { + if (auto driver_name = OGR_Dr_GetName(driver)) + { + map->setSymbolSetId(QString::fromLatin1(driver_name)); + } + } + + empty_geometries = 0; + no_transformation = 0; + failed_transformation = 0; + unsupported_geometry_type = 0; + too_few_coordinates = 0; + + if (georeferencing_import_enabled) + map_srs = importGeoreferencing(data_source.get()); + else + map_srs = srsFromMap(); + + importStyles(data_source.get()); + + if (!load_symbols_only) + { + QScopedValueRollback rollback { MapCoord::boundsOffset() }; + MapCoord::boundsOffset().reset(true); + + auto num_layers = OGR_DS_GetLayerCount(data_source.get()); + for (int i = 0; i < num_layers; ++i) + { + auto layer = OGR_DS_GetLayer(data_source.get(), i); + if (!layer) + { + addWarning(tr("Unable to load layer %1.").arg(i)); + continue; + } + + if (qstrcmp(OGR_L_GetName(layer), "track_points") == 0) + { + // Skip GPX track points as points. Track line is separate. + /// \todo Use hooks and delegates per file format + continue; + } + + auto part = map->getCurrentPart(); + if (option(QLatin1String("Separate layers")).toBool()) + { + if (num_layers > 0) + { + if (part->getNumObjects() == 0) + { + part->setName(QString::fromUtf8(OGR_L_GetName(layer))); + } + else + { + part = new MapPart(QString::fromUtf8(OGR_L_GetName(layer)), map); + auto index = std::size_t(map->getNumParts()); + map->addPart(part, index); + map->setCurrentPartIndex(index); + } + } + } + + importLayer(part, layer); + } + + const auto& offset = MapCoord::boundsOffset(); + if (!offset.isZero()) + { + // We need to adjust the georeferencing. + auto offset_f = MapCoordF { offset.x / 1000.0, offset.y / 1000.0 }; + auto georef = map->getGeoreferencing(); + auto ref_point = MapCoordF { georef.getMapRefPoint() }; + auto new_projected = georef.toProjectedCoords(ref_point + offset_f); + georef.setProjectedRefPoint(new_projected, false); + map->setGeoreferencing(georef); + } + } + + if (empty_geometries) + { + addWarning(tr("Unable to load %n objects, reason: %1", nullptr, empty_geometries) + .arg(tr("Empty geometry."))); + } + if (no_transformation) + { + addWarning(tr("Unable to load %n objects, reason: %1", nullptr, no_transformation) + .arg(tr("Can't determine the coordinate transformation: %1").arg(QString::fromUtf8(CPLGetLastErrorMsg())))); + } + if (failed_transformation) + { + addWarning(tr("Unable to load %n objects, reason: %1", nullptr, failed_transformation) + .arg(tr("Failed to transform the coordinates."))); + } + if (unsupported_geometry_type) + { + addWarning(tr("Unable to load %n objects, reason: %1", nullptr, unsupported_geometry_type) + .arg(tr("Unknown or unsupported geometry type."))); + } + if (too_few_coordinates) + { + addWarning(tr("Unable to load %n objects, reason: %1", nullptr, too_few_coordinates) + .arg(tr("Not enough coordinates."))); + } +} + + + +ogr::unique_srs OgrFileImport::importGeoreferencing(OGRDataSourceH data_source) +{ + auto no_srs = true; + auto local_srs = ogr::unique_srs { nullptr }; + auto suitable_srs = ogr::unique_srs { nullptr }; + char* projected_srs_spec = { nullptr }; + + auto orthographic = ogr::unique_srs { OSRNewSpatialReference(nullptr) }; + OSRSetProjCS(orthographic.get(), "Orthographic SRS"); + OSRSetWellKnownGeogCS(orthographic.get(), "WGS84"); + OSRSetOrthographic(orthographic.get(), 0.0, 0.0, 0.0, 0.0); + + // Find any SRS which can be transformed to our orthographic SRS, + // but prefer a projected SRS. + auto num_layers = OGR_DS_GetLayerCount(data_source); + for (int i = 0; i < num_layers; ++i) + { + auto layer = OGR_DS_GetLayer(data_source, i); + if (!layer) + continue; + + auto spatial_reference = OGR_L_GetSpatialRef(layer); + if (!spatial_reference) + continue; + + no_srs = false; + + if (OSRIsLocal(spatial_reference)) + { + if (!local_srs) + local_srs.reset(OSRClone(spatial_reference)); + continue; + } + + auto transformation = OCTNewCoordinateTransformation(spatial_reference, orthographic.get()); + if (!transformation) + { + addWarning(tr("Cannot use this spatial reference:\n%1").arg(toPrettyWkt(spatial_reference))); + continue; + } + OCTDestroyCoordinateTransformation(transformation); + + if (OSRIsProjected(spatial_reference)) + { + char *srs_spec = nullptr; + auto error = OSRExportToProj4(spatial_reference, &srs_spec); + if (!error) + { + projected_srs_spec = srs_spec; // transfer ownership + suitable_srs.reset(OSRClone(spatial_reference)); + break; + } + CPLFree(srs_spec); + } + + if (!suitable_srs) + suitable_srs.reset(OSRClone(spatial_reference)); + } + + if (projected_srs_spec) + { + // Found a suitable projected SRS + auto georef = map->getGeoreferencing(); // copy + georef.setProjectedCRS(QStringLiteral("PROJ.4"), QString::fromLatin1(projected_srs_spec)); + map->setGeoreferencing(georef); + CPLFree(projected_srs_spec); + return suitable_srs; + } + else if (suitable_srs) + { + // Found a suitable SRS but it is not projected. + // Setting up a local orthographic projection. + auto center = calcAverageLatLon(data_source); + auto latitude = 0.001 * qRound(1000 * center.latitude()); + auto longitude = 0.001 * qRound(1000 * center.longitude()); + auto ortho_georef = Georeferencing(); + ortho_georef.setScaleDenominator(int(map->getScaleDenominator())); + ortho_georef.setProjectedCRS(QString{}, + QString::fromLatin1("+proj=ortho +datum=WGS84 +ellps=WGS84 +units=m +lat_0=%1 +lon_0=%2 +no_defs") + .arg(latitude, 0, 'f') + .arg(longitude, 0, 'f') ); + ortho_georef.setProjectedRefPoint({}, false); + ortho_georef.setDeclination(map->getGeoreferencing().getDeclination()); + map->setGeoreferencing(ortho_georef); + return srsFromMap(); + } + else if (local_srs || no_srs) + { + auto georef = Georeferencing(); + georef.setScaleDenominator(int(map->getScaleDenominator())); + georef.setDeclination(map->getGeoreferencing().getDeclination()); + map->setGeoreferencing(georef); + return local_srs ? std::move(local_srs) : srsFromMap(); + } + else + { + throw FileFormatException(tr("The geospatial data has no suitable spatial reference.")); + } +} + + + +void OgrFileImport::importStyles(OGRDataSourceH data_source) +{ + //auto style_table = OGR_DS_GetStyleTable(data_source); + Q_UNUSED(data_source) +} + +void OgrFileImport::importLayer(MapPart* map_part, OGRLayerH layer) +{ + Q_ASSERT(map_part); + + auto feature_definition = OGR_L_GetLayerDefn(layer); + + OGR_L_ResetReading(layer); + while (auto feature = ogr::unique_feature(OGR_L_GetNextFeature(layer))) + { + auto geometry = OGR_F_GetGeometryRef(feature.get()); + if (!geometry || OGR_G_IsEmpty(geometry)) + { + ++empty_geometries; + continue; + } + + importFeature(map_part, feature_definition, feature.get(), geometry); + } +} + +void OgrFileImport::importFeature(MapPart* map_part, OGRFeatureDefnH feature_definition, OGRFeatureH feature, OGRGeometryH geometry) +{ + to_map_coord = &OgrFileImport::fromProjected; + auto new_srs = OGR_G_GetSpatialReference(geometry); + if (new_srs && data_srs != new_srs) + { + // New SRS, indeed. + auto transformation = ogr::unique_transformation{ OCTNewCoordinateTransformation(new_srs, map_srs.get()) }; + if (!transformation) + { + ++no_transformation; + return; + } + + // Commit change to data srs and coordinate transformation + data_srs = new_srs; + data_transform = std::move(transformation); + } + + if (new_srs) + { + auto error = OGR_G_Transform(geometry, data_transform.get()); + if (error) + { + ++failed_transformation; + return; + } + } + else if (unit_type == UnitOnPaper) + { + to_map_coord = &OgrFileImport::fromDrawing; + } + + auto objects = importGeometry(feature, geometry); + for (auto object : objects) + { + map_part->addObject(object); + if (!feature_definition) + continue; + + auto num_fields = OGR_FD_GetFieldCount(feature_definition); + for (int i = 0; i < num_fields; ++i) + { + auto value = OGR_F_GetFieldAsString(feature, i); + if (value && qstrlen(value) > 0) + { + auto field_definition = OGR_FD_GetFieldDefn(feature_definition, i); + object->setTag(QString::fromUtf8(OGR_Fld_GetNameRef(field_definition)), QString::fromUtf8(value)); + } + } + } +} + +OgrFileImport::ObjectList OgrFileImport::importGeometry(OGRFeatureH feature, OGRGeometryH geometry) +{ + ObjectList result; + auto geometry_type = wkbFlatten(OGR_G_GetGeometryType(geometry)); + switch (geometry_type) + { + case OGRwkbGeometryType::wkbPoint: + if (auto object = importPointGeometry(feature, geometry)) + result = { object }; + break; + + case OGRwkbGeometryType::wkbLineString: + if (auto object = importLineStringGeometry(feature, geometry)) + result = { object }; + break; + + case OGRwkbGeometryType::wkbPolygon: + if (auto object = importPolygonGeometry(feature, geometry)) + result = { object }; + break; + + case OGRwkbGeometryType::wkbGeometryCollection: + case OGRwkbGeometryType::wkbMultiLineString: + case OGRwkbGeometryType::wkbMultiPoint: + case OGRwkbGeometryType::wkbMultiPolygon: + result = importGeometryCollection(feature, geometry); + break; + + default: + qDebug("OgrFileImport: Unknown or unsupported geometry type: %d", geometry_type); + ++unsupported_geometry_type; + } + return result; +} + +OgrFileImport::ObjectList OgrFileImport::importGeometryCollection(OGRFeatureH feature, OGRGeometryH geometry) +{ + ObjectList result; + auto num_geometries = OGR_G_GetGeometryCount(geometry); + result.reserve(std::size_t(num_geometries)); + for (int i = 0; i < num_geometries; ++i) + { + auto tmp = importGeometry(feature, OGR_G_GetGeometryRef(geometry, i)); + result.insert(result.end(), begin(tmp), end(tmp)); + } + return result; +} + +Object* OgrFileImport::importPointGeometry(OGRFeatureH feature, OGRGeometryH geometry) +{ + auto style = OGR_F_GetStyleString(feature); + auto symbol = getSymbol(Symbol::Point, style); + if (symbol->getType() == Symbol::Point) + { + auto object = new PointObject(symbol); + object->setPosition(toMapCoord(OGR_G_GetX(geometry, 0), OGR_G_GetY(geometry, 0))); + return object; + } + else if (symbol->getType() == Symbol::Text) + { + const auto& description = symbol->getDescription(); + auto length = description.length(); + auto split = description.indexOf(QLatin1Char(' ')); + Q_ASSERT(split > 0); + Q_ASSERT(split < length); + + auto label = description.right(length - split - 1); + if (label.startsWith(QLatin1Char{'{'}) && label.endsWith(QLatin1Char{'}'})) + { + label.remove(0, 1); + label.chop(1); + int index = OGR_F_GetFieldIndex(feature, label.toLatin1().constData()); + if (index >= 0) + { + label = QString::fromUtf8(OGR_F_GetFieldAsString(feature, index)); + } + } + if (!label.isEmpty()) + { + auto object = new TextObject(symbol); + object->setAnchorPosition(toMapCoord(OGR_G_GetX(geometry, 0), OGR_G_GetY(geometry, 0))); + // DXF observation + label.replace(QRegularExpression(QString::fromLatin1("(\\\\[^;]*;)*"), QRegularExpression::MultilineOption), QString{}); + label.replace(QLatin1String("^I"), QLatin1String("\t")); + object->setText(label); + + bool ok; + auto anchor = QStringRef(&description, 1, 2).toInt(&ok); + if (ok) + { + applyLabelAnchor(anchor, object); + } + + auto angle = QStringRef(&description, 3, split-3).toFloat(&ok); + if (ok) + { + object->setRotation(qDegreesToRadians(angle)); + } + + return object; + } + } + + return nullptr; +} + +PathObject* OgrFileImport::importLineStringGeometry(OGRFeatureH feature, OGRGeometryH geometry) +{ + auto managed_geometry = ogr::unique_geometry(nullptr); + if (OGR_G_GetGeometryType(geometry) != wkbLineString) + { + geometry = OGR_G_ForceToLineString(OGR_G_Clone(geometry)); + managed_geometry.reset(geometry); + } + + auto num_points = OGR_G_GetPointCount(geometry); + if (num_points < 2) + { + ++too_few_coordinates; + return nullptr; + } + + auto style = OGR_F_GetStyleString(feature); + auto object = new PathObject(getSymbol(Symbol::Line, style)); + for (int i = 0; i < num_points; ++i) + { + object->addCoordinate(toMapCoord(OGR_G_GetX(geometry, i), OGR_G_GetY(geometry, i))); + } + return object; +} + +PathObject* OgrFileImport::importPolygonGeometry(OGRFeatureH feature, OGRGeometryH geometry) +{ + auto num_geometries = OGR_G_GetGeometryCount(geometry); + if (num_geometries < 1) + { + ++too_few_coordinates; + return nullptr; + } + + auto outline = OGR_G_GetGeometryRef(geometry, 0); + auto managed_outline = ogr::unique_geometry(nullptr); + if (OGR_G_GetGeometryType(outline) != wkbLineString) + { + outline = OGR_G_ForceToLineString(OGR_G_Clone(outline)); + managed_outline.reset(outline); + } + auto num_points = OGR_G_GetPointCount(outline); + if (num_points < 3) + { + ++too_few_coordinates; + return nullptr; + } + + auto style = OGR_F_GetStyleString(feature); + auto object = new PathObject(getSymbol(Symbol::Area, style)); + for (int i = 0; i < num_points; ++i) + { + object->addCoordinate(toMapCoord(OGR_G_GetX(outline, i), OGR_G_GetY(outline, i))); + } + + for (int g = 1; g < num_geometries; ++g) + { + bool start_new_part = true; + auto hole = /*OGR_G_ForceToLineString*/(OGR_G_GetGeometryRef(geometry, g)); + auto num_points = OGR_G_GetPointCount(hole); + for (int i = 0; i < num_points; ++i) + { + object->addCoordinate(toMapCoord(OGR_G_GetX(hole, i), OGR_G_GetY(hole, i)), start_new_part); + start_new_part = false; + } + } + + object->closeAllParts(); + return object; +} + +Symbol* OgrFileImport::getSymbol(Symbol::Type type, const char* raw_style_string) +{ + auto style_string = QByteArray::fromRawData(raw_style_string, qstrlen(raw_style_string)); + Symbol* symbol = nullptr; + switch (type) + { + case Symbol::Point: + case Symbol::Text: + symbol = point_symbols.value(style_string); + if (!symbol) + symbol = getSymbolForPointGeometry(style_string); + if (!symbol) + symbol = default_point_symbol; + break; + + case Symbol::Combined: + /// \todo + // fall through + case Symbol::Line: + symbol = line_symbols.value(style_string); + if (!symbol) + symbol = getLineSymbol(style_string); + if (!symbol) + symbol = default_line_symbol; + break; + + case Symbol::Area: + symbol = area_symbols.value(style_string); + if (!symbol) + symbol = getAreaSymbol(style_string); + if (!symbol) + symbol = default_area_symbol; + break; + + case Symbol::NoSymbol: + case Symbol::AllSymbols: + Q_UNREACHABLE(); + } + + Q_ASSERT(symbol); + return symbol; +} + +MapColor* OgrFileImport::makeColor(OGRStyleToolH tool, const char* color_string) +{ + auto key = QByteArray::fromRawData(color_string, qstrlen(color_string)); + auto color = colors.value(key); + if (!color) + { + int r, g, b, a; + auto success = OGR_ST_GetRGBFromString(tool, color_string, &r, &g, &b, &a); + if (!success) + { + color = default_pen_color; + } + else if (a > 0) + { + color = new MapColor(QString::fromUtf8(color_string), map->getNumColors()); + color->setRgb(QColor{ r, g, b }); + color->setCmykFromRgb(); + map->addColor(color, map->getNumColors()); + } + + key.detach(); + colors.insert(key, color); + } + + return color; +} + +void OgrFileImport::applyPenColor(OGRStyleToolH tool, LineSymbol* line_symbol) +{ + int is_null; + auto color_string = OGR_ST_GetParamStr(tool, OGRSTPenColor, &is_null); + if (!is_null) + { + auto color = makeColor(tool, color_string); + if (color) + line_symbol->setColor(color); + else + line_symbol->setHidden(true); + } +} + +void OgrFileImport::applyBrushColor(OGRStyleToolH tool, AreaSymbol* area_symbol) +{ + int is_null; + auto color_string = OGR_ST_GetParamStr(tool, OGRSTBrushFColor, &is_null); + if (!is_null) + { + auto color = makeColor(tool, color_string); + if (color) + area_symbol->setColor(color); + else + area_symbol->setHidden(true); + } +} + + +Symbol* OgrFileImport::getSymbolForPointGeometry(const QByteArray& style_string) +{ + if (style_string.isEmpty()) + return nullptr; + + auto manager = this->manager.get(); + + auto data = style_string.constData(); + if (!OGR_SM_InitStyleString(manager, data)) + return nullptr; + + auto num_parts = OGR_SM_GetPartCount(manager, data); + if (!num_parts) + return nullptr; + + Symbol* symbol = nullptr; + for (int i = 0; !symbol && i < num_parts; ++i) + { + auto tool = OGR_SM_GetPart(manager, i, nullptr); + if (!tool) + continue; + + OGR_ST_SetUnit(tool, OGRSTUMM, map->getScaleDenominator()); + + auto type = OGR_ST_GetType(tool); + switch (type) + { + case OGRSTCBrush: + case OGRSTCPen: + case OGRSTCSymbol: + symbol = getSymbolForOgrSymbol(tool, style_string); + break; + + case OGRSTCLabel: + symbol = getSymbolForLabel(tool, style_string); + break; + + default: + ; + } + + OGR_ST_Destroy(tool); + } + + return symbol; +} + +LineSymbol* OgrFileImport::getLineSymbol(const QByteArray& style_string) +{ + if (style_string.isEmpty()) + return nullptr; + + auto manager = this->manager.get(); + + auto data = style_string.constData(); + if (!OGR_SM_InitStyleString(manager, data)) + return nullptr; + + auto num_parts = OGR_SM_GetPartCount(manager, data); + if (!num_parts) + return nullptr; + + LineSymbol* symbol = nullptr; + for (int i = 0; !symbol && i < num_parts; ++i) + { + auto tool = OGR_SM_GetPart(manager, i, nullptr); + if (!tool) + continue; + + OGR_ST_SetUnit(tool, OGRSTUMM, map->getScaleDenominator()); + + auto type = OGR_ST_GetType(tool); + switch (type) + { + case OGRSTCPen: + symbol = getSymbolForPen(tool, style_string); + break; + + default: + ; + } + + OGR_ST_Destroy(tool); + } + + return symbol; +} + +AreaSymbol* OgrFileImport::getAreaSymbol(const QByteArray& style_string) +{ + if (style_string.isEmpty()) + return nullptr; + + auto manager = this->manager.get(); + + auto data = style_string.constData(); + if (!OGR_SM_InitStyleString(manager, data)) + return nullptr; + + auto num_parts = OGR_SM_GetPartCount(manager, data); + if (!num_parts) + return nullptr; + + AreaSymbol* symbol = nullptr; + for (int i = 0; !symbol && i < num_parts; ++i) + { + auto tool = OGR_SM_GetPart(manager, i, nullptr); + if (!tool) + continue; + + OGR_ST_SetUnit(tool, OGRSTUMM, map->getScaleDenominator()); + + auto type = OGR_ST_GetType(tool); + switch (type) + { + case OGRSTCBrush: + symbol = getSymbolForBrush(tool, style_string); + break; + + default: + ; + } + + OGR_ST_Destroy(tool); + } + + return symbol; +} + +PointSymbol* OgrFileImport::getSymbolForOgrSymbol(OGRStyleToolH tool, const QByteArray& style_string) +{ + auto raw_tool_key = OGR_ST_GetStyleString(tool); + auto tool_key = QByteArray::fromRawData(raw_tool_key, qstrlen(raw_tool_key)); + auto symbol = point_symbols.value(tool_key); + if (symbol && symbol->getType() == Symbol::Point) + return static_cast(symbol); + + int color_key; + switch (OGR_ST_GetType(tool)) + { + case OGRSTCBrush: + color_key = OGRSTBrushFColor; + break; + case OGRSTCPen: + color_key = OGRSTPenColor; + break; + case OGRSTCSymbol: + color_key = OGRSTSymbolColor; + break; + default: + return nullptr; + }; + + int is_null; + auto color_string = OGR_ST_GetParamStr(tool, color_key, &is_null); + if (is_null) + return nullptr; + + auto point_symbol = static_cast(default_point_symbol->duplicate()); + auto color = makeColor(tool, color_string); + if (color) + point_symbol->setInnerColor(color); + else + point_symbol->setHidden(true); + + auto key = style_string; + key.detach(); + point_symbols.insert(key, point_symbol); + + if (key != tool_key) + { + tool_key.detach(); + point_symbols.insert(tool_key, point_symbol); + } + + map->addSymbol(point_symbol, map->getNumSymbols()); + return point_symbol; +} + +TextSymbol* OgrFileImport::getSymbolForLabel(OGRStyleToolH tool, const QByteArray& /*style_string*/) +{ + Q_ASSERT(OGR_ST_GetType(tool) == OGRSTCLabel); + + int is_null; + auto label_string = OGR_ST_GetParamStr(tool, OGRSTLabelTextString, &is_null); + if (is_null) + return nullptr; + + auto color_string = OGR_ST_GetParamStr(tool, OGRSTLabelFColor, &is_null); + auto font_size_string = OGR_ST_GetParamStr(tool, OGRSTLabelSize, &is_null); + + // Don't use the style string as a key: The style contains the label. + QByteArray key; + key.reserve(qstrlen(color_string) + qstrlen(font_size_string) + 1); + key.append(color_string); + key.append(font_size_string); + auto text_symbol = static_cast(text_symbols.value(key)); + if (!text_symbol) + { + text_symbol = static_cast(default_text_symbol->duplicate()); + + auto color = makeColor(tool, color_string); + if (color) + text_symbol->setColor(color); + else + text_symbol->setHidden(true); + + auto font_size = OGR_ST_GetParamDbl(tool, OGRSTLabelSize, &is_null); + if (!is_null && font_size > 0.0) + text_symbol->scale(font_size / text_symbol->getFontSize()); + + key.detach(); + text_symbols.insert(key, text_symbol); + + map->addSymbol(text_symbol, map->getNumSymbols()); + } + + auto anchor = qBound(1, OGR_ST_GetParamNum(tool, OGRSTLabelAnchor, &is_null), 12); + if (is_null) + anchor = 1; + + auto angle = OGR_ST_GetParamDbl(tool, OGRSTLabelAngle, &is_null); + if (is_null) + angle = 0.0; + + QString description; + description.reserve(qstrlen(label_string) + 100); + description.append(QString::number(100 + anchor)); + description.append(QString::number(angle, 'g', 1)); + description.append(QLatin1Char(' ')); + description.append(QString::fromUtf8(label_string)); + text_symbol->setDescription(description); + + return text_symbol; +} + +LineSymbol* OgrFileImport::getSymbolForPen(OGRStyleToolH tool, const QByteArray& style_string) +{ + Q_ASSERT(OGR_ST_GetType(tool) == OGRSTCPen); + + auto raw_tool_key = OGR_ST_GetStyleString(tool); + auto tool_key = QByteArray::fromRawData(raw_tool_key, qstrlen(raw_tool_key)); + auto symbol = line_symbols.value(tool_key); + if (symbol && symbol->getType() == Symbol::Line) + return static_cast(symbol); + + auto line_symbol = static_cast(default_line_symbol->duplicate()); + applyPenColor(tool, line_symbol); + applyPenWidth(tool, line_symbol); + applyPenCap(tool, line_symbol); + applyPenJoin(tool, line_symbol); + applyPenPattern(tool, line_symbol); + + auto key = style_string; + key.detach(); + line_symbols.insert(key, line_symbol); + + if (key != tool_key) + { + tool_key.detach(); + line_symbols.insert(tool_key, line_symbol); + } + + map->addSymbol(line_symbol, map->getNumSymbols()); + return line_symbol; +} + +AreaSymbol* OgrFileImport::getSymbolForBrush(OGRStyleToolH tool, const QByteArray& style_string) +{ + Q_ASSERT(OGR_ST_GetType(tool) == OGRSTCBrush); + + auto raw_tool_key = OGR_ST_GetStyleString(tool); + auto tool_key = QByteArray::fromRawData(raw_tool_key, qstrlen(raw_tool_key)); + auto symbol = area_symbols.value(tool_key); + if (symbol && symbol->getType() == Symbol::Area) + return static_cast(symbol); + + auto area_symbol = static_cast(default_area_symbol->duplicate()); + applyBrushColor(tool, area_symbol); + + auto key = style_string; + key.detach(); + area_symbols.insert(key, area_symbol); + + if (key != tool_key) + { + tool_key.detach(); + area_symbols.insert(tool_key, area_symbol); + } + + map->addSymbol(area_symbol, map->getNumSymbols()); + return area_symbol; +} + + +MapCoord OgrFileImport::fromDrawing(double x, double y) const +{ + return MapCoord::load(x, -y, MapCoord::Flags{}); +} + +MapCoord OgrFileImport::fromProjected(double x, double y) const +{ + return MapCoord::load(map->getGeoreferencing().toMapCoordF(QPointF{ x, y }), MapCoord::Flags{}); +} + + +// static +bool OgrFileImport::checkGeoreferencing(QFile& file, const Georeferencing& georef) +{ + if (georef.isLocal() || !georef.isValid()) + return false; + + GdalManager(); + + auto filename = file.fileName(); + // GDAL 2.0: ... = GDALOpenEx(template_path.toLatin1(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr); + auto data_source = ogr::unique_datasource(OGROpen(filename.toUtf8().constData(), 0, nullptr)); + if (!data_source) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read '%1': %2") + .arg(filename, QString::fromLatin1(CPLGetLastErrorMsg()))); + } + + return checkGeoreferencing(data_source.get(), georef); +} + + +// static +bool OgrFileImport::checkGeoreferencing(OGRDataSourceH data_source, const Georeferencing& georef) +{ + auto spec = QByteArray(georef.getProjectedCRSSpec().toLatin1() + " +wktext"); + auto map_srs = ogr::unique_srs { OSRNewSpatialReference(nullptr) }; + OSRSetProjCS(map_srs.get(), "Projected map SRS"); + OSRSetWellKnownGeogCS(map_srs.get(), "WGS84"); + OSRImportFromProj4(map_srs.get(), spec.constData()); + + bool suitable_srs_found = false; + auto num_layers = OGR_DS_GetLayerCount(data_source); + for (int i = 0; i < num_layers; ++i) + { + if (auto layer = OGR_DS_GetLayer(data_source, i)) + { + if (auto spatial_reference = OGR_L_GetSpatialRef(layer)) + { + auto transformation = OCTNewCoordinateTransformation(spatial_reference, map_srs.get()); + if (!transformation) + { + qDebug("Failed to transform this SRS:\n%s", qPrintable(toPrettyWkt(spatial_reference))); + return false; + } + + OCTDestroyCoordinateTransformation(transformation); + suitable_srs_found = true; + } + } + } + + return suitable_srs_found; +} + + + +// static +LatLon OgrFileImport::calcAverageLatLon(QFile& file) +{ + GdalManager(); + + auto filename = file.fileName(); + // GDAL 2.0: ... = GDALOpenEx(template_path.toLatin1(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr); + auto data_source = ogr::unique_datasource(OGROpen(filename.toUtf8().constData(), 0, nullptr)); + if (!data_source) + { + throw FileFormatException(::OpenOrienteering::Importer::tr("Could not read '%1': %2") + .arg(filename, QString::fromLatin1(CPLGetLastErrorMsg()))); + } + + return calcAverageLatLon(data_source.get()); +} + + +// static +LatLon OgrFileImport::calcAverageLatLon(OGRDataSourceH data_source) +{ + return AverageLatLon(data_source); +} + + +} // namespace OpenOrienteering diff --git a/src/gdal/ogr_file_format.h b/src/gdal/ogr_file_format.h new file mode 100644 index 0000000..71413f6 --- /dev/null +++ b/src/gdal/ogr_file_format.h @@ -0,0 +1,69 @@ +/* + * Copyright 2016-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OGR_FILE_FORMAT_H +#define OPENORIENTEERING_OGR_FILE_FORMAT_H + +#include + +#include "fileformats/file_format.h" + +class QIODevice; + +namespace OpenOrienteering { + +class Importer; +class Map; +class MapView; + + +/** + * A FileFormat for geospatial vector data supported by OGR. + * + * Geospatial vector data cannot be loaded as a regular (OpenOrienteering) Map + * because it has no scale. However, it typically has a spatial reference, and + * so it can be imported into an existing map. This is the major reason for + * implementing the OGR support as a FileFormat. + */ +class OgrFileFormat : public FileFormat +{ +public: + /** + * Constructs a new OgrFileFormat. + */ + OgrFileFormat(); + + /** + * Always returns true. + * + * There is no cheap way to determine the answer via OGR. + */ + bool understands(const unsigned char* buffer, std::size_t size) const override; + + /** + * Creates an importer object and configures it for the given input stream + * and output map and view. + */ + Importer* createImporter(QIODevice* stream, Map *map, MapView *view) const override; +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_OGR_FILE_FORMAT_H diff --git a/src/gdal/ogr_file_format_p.h b/src/gdal/ogr_file_format_p.h new file mode 100644 index 0000000..ad541c2 --- /dev/null +++ b/src/gdal/ogr_file_format_p.h @@ -0,0 +1,278 @@ +/* + * Copyright 2016-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OGR_FILE_FORMAT_P_H +#define OPENORIENTEERING_OGR_FILE_FORMAT_P_H + +#include + +#include +#include +#include + +// The GDAL/OGR C API is more stable than the C++ API. +#include +#include + +#include "core/map_coord.h" +#include "core/symbols/symbol.h" +#include "fileformats/file_import_export.h" + +class QFile; + +namespace OpenOrienteering { + +class AreaSymbol; +class Georeferencing; +class LatLon; +class LineSymbol; +class MapColor; +class MapPart; +class Object; +class PathObject; +class PointObject; +class PointSymbol; +class TextSymbol; + + +namespace ogr +{ + class OGRCoordinateTransformationHDeleter + { + public: + void operator()(OGRCoordinateTransformationH ct) const + { + OCTDestroyCoordinateTransformation(ct); + } + }; + + /** + * A convenience class for OGR C API coordinate transformation handles, + * similar to std::unique_ptr. + */ + using unique_transformation = std::unique_ptr::type, OGRCoordinateTransformationHDeleter>; + + + class OGRSpatialReferenceHDeleter + { + public: + void operator()(OGRSpatialReferenceH srs) const + { + OSRDestroySpatialReference(srs); + } + }; + + /** + * A convenience class for OGR C API SRS handles, similar to std::unique_ptr. + */ + using unique_srs = std::unique_ptr::type, OGRSpatialReferenceHDeleter>; + + + class OGRStyleMgrHDeleter + { + public: + void operator()(OGRStyleMgrH manager) const + { + OGR_SM_Destroy(manager); + } + }; + + /** A convenience class for OGR C API feature handles, similar to std::unique_ptr. */ + using unique_stylemanager = std::unique_ptr::type, OGRStyleMgrHDeleter>; +} + + + +/** + * An Importer for geospatial vector data supported by OGR. + * + * OGR needs to know the filename. The filename can be either derived from + * a QFile passed as stream to the constructor, or set directly through the + * option "filename". + * + * The option "separate_layers" will cause OGR layers to be imported as distinct + * map parts if set to true. + */ +class OgrFileImport : public Importer +{ + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::OgrFileImport) + +public: + /** + * The unit type indicates the coordinate system the data units refers to. + */ + enum UnitType + { + UnitOnGround, ///< Data refers to real dimensions. Includes geograghic CS. + UnitOnPaper ///< Data refers to dimensions in the (printed) map. + }; + + /** + * A Pointer to a function which creates a MapCoordF from double coordinates. + */ + using MapCoordConstructor = MapCoord (OgrFileImport::*)(double, double) const; + + /** + * Constructs a new importer. + */ + OgrFileImport(QIODevice* stream, Map *map, MapView *view, UnitType unit_type = UnitOnGround); + + ~OgrFileImport() override; + + + /** + * Enables the import of georeferencing from the geospatial data. + * + * If this import is not enabled, the georeferencing of the Map given to + * the constructor will be used instead. + */ + void setGeoreferencingImportEnabled(bool enabled); + + + /** + * Tests if the file's spatial references can be used with the given georeferencing. + * + * This returns true only if all layers' spatial references can be + * transformed to the spatial reference systems represented by georef. + * It will always return false for a local or invalid Georeferencing. + */ + static bool checkGeoreferencing(QFile& file, const Georeferencing& georef); + + /** + * Calculates the average geographic coordinates (WGS84) of the file. + */ + static LatLon calcAverageLatLon(QFile& file); + + +protected: + ogr::unique_srs srsFromMap(); + + /** + * Tests if the file's spatial references can be used with the given georeferencing. + * + * This returns true only if all layers' spatial references can be + * transformed to the spatial reference systems represented by georef. + * It will always return false for a local or invalid Georeferencing. + */ + static bool checkGeoreferencing(OGRDataSourceH data_source, const Georeferencing& georef); + + void import(bool load_symbols_only) override; + + ogr::unique_srs importGeoreferencing(OGRDataSourceH data_source); + + void importStyles(OGRDataSourceH data_source); + + void importLayer(MapPart* map_part, OGRLayerH layer); + + void importFeature(MapPart* map_part, OGRFeatureDefnH feature_definition, OGRFeatureH feature, OGRGeometryH geometry); + + using ObjectList = std::vector; + + ObjectList importGeometry(OGRFeatureH feature, OGRGeometryH geometry); + + ObjectList importGeometryCollection(OGRFeatureH feature, OGRGeometryH geometry); + + Object* importPointGeometry(OGRFeatureH feature, OGRGeometryH geometry); + + PathObject* importLineStringGeometry(OGRFeatureH feature, OGRGeometryH geometry); + + PathObject* importPolygonGeometry(OGRFeatureH feature, OGRGeometryH geometry); + + + Symbol* getSymbol(Symbol::Type type, const char* raw_style_string); + + MapColor* makeColor(OGRStyleToolH tool, const char* color_string); + + void applyPenColor(OGRStyleToolH tool, LineSymbol* line_symbol); + + void applyBrushColor(OGRStyleToolH tool, AreaSymbol* area_symbol); + + + MapCoord toMapCoord(double x, double y) const; + + /** + * A MapCoordConstructor which interpretes the given coordinates in millimeters on paper. + */ + MapCoord fromDrawing(double x, double y) const; + + /** + * A MapCoordConstructor which interpretes the given coordinates as projected. + */ + MapCoord fromProjected(double x, double y) const; + + + static LatLon calcAverageLatLon(OGRDataSourceH data_source); + + +private: + Symbol* getSymbolForPointGeometry(const QByteArray& style_string); + LineSymbol* getLineSymbol(const QByteArray& style_string); + AreaSymbol* getAreaSymbol(const QByteArray& style_string); + + PointSymbol* getSymbolForOgrSymbol(OGRStyleToolH tool, const QByteArray& style_string); + TextSymbol* getSymbolForLabel(OGRStyleToolH tool, const QByteArray& style_string); + LineSymbol* getSymbolForPen(OGRStyleToolH tool, const QByteArray& style_string); + AreaSymbol* getSymbolForBrush(OGRStyleToolH tool, const QByteArray& style_string); + + QHash point_symbols; + PointSymbol* default_point_symbol; + QHash text_symbols; + TextSymbol* default_text_symbol; + QHash line_symbols; + LineSymbol* default_line_symbol; + QHash area_symbols; + AreaSymbol* default_area_symbol; + QHash colors; + MapColor* default_pen_color; + + MapCoordConstructor to_map_coord; + + ogr::unique_srs map_srs; + + OGRSpatialReferenceH data_srs; + + ogr::unique_transformation data_transform; + + ogr::unique_stylemanager manager; + + int empty_geometries; + int no_transformation; + int failed_transformation; + int unsupported_geometry_type; + int too_few_coordinates; + + UnitType unit_type; + + bool georeferencing_import_enabled; +}; + + + +// ### inline code ### + +inline +MapCoord OgrFileImport::toMapCoord(double x, double y) const +{ + return (this->*to_map_coord)(x, y); +} + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_OGR_FILE_FORMAT_P_H diff --git a/src/gdal/ogr_template.cpp b/src/gdal/ogr_template.cpp new file mode 100644 index 0000000..ea61c9e --- /dev/null +++ b/src/gdal/ogr_template.cpp @@ -0,0 +1,544 @@ +/* + * Copyright 2016-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "ogr_template.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/georeferencing.h" +#include "core/latlon.h" +#include "core/map.h" +#include "core/map_coord.h" +#include "core/objects/object.h" +#include "fileformats/file_format.h" +#include "gdal/gdal_manager.h" +#include "gdal/ogr_file_format_p.h" +#include "gui/georeferencing_dialog.h" +#include "sensors/gps_track.h" +#include "templates/template.h" +#include "templates/template_positioning_dialog.h" +#include "templates/template_track.h" + + +namespace OpenOrienteering { + +namespace { + + namespace literal + { + const auto crs_spec = QLatin1String("crs_spec"); + const auto georeferencing = QLatin1String("georeferencing"); + const auto projected_crs_spec = QLatin1String("projected_crs_spec"); + } + + + std::unique_ptr getDataGeoreferencing(QFile& file, const Georeferencing& initial_georef) + { + Map tmp_map; + tmp_map.setGeoreferencing(initial_georef); + OgrFileImport importer{ &file, &tmp_map, nullptr, OgrFileImport::UnitOnGround}; + importer.setGeoreferencingImportEnabled(true); + importer.doImport(true); + + return std::make_unique(tmp_map.getGeoreferencing()); + } + + + bool preserveRefPoints(Georeferencing& data_georef, const Georeferencing& initial_georef) + { + // Keep a configured local reference point from initial_georef? + auto data_crs_spec = data_georef.getProjectedCRSSpec(); + if ((!initial_georef.isValid() || initial_georef.isLocal()) + && data_georef.isValid() + && !data_georef.isLocal() + && data_georef.getProjectedRefPoint() == QPointF{} + && data_georef.getMapRefPoint() == MapCoord{} + && data_crs_spec.contains(QLatin1String("+proj=ortho")) + && !data_crs_spec.contains(QLatin1String("+x_0=")) + && !data_crs_spec.contains(QLatin1String("+y_0=")) ) + { + data_crs_spec.append(QString::fromLatin1(" +x_0=%1 +y_0=%2") + .arg(initial_georef.getProjectedRefPoint().x(), 0, 'f', 2) + .arg(initial_georef.getProjectedRefPoint().y(), 0, 'f', 2) ); + data_georef.setProjectedCRS({}, data_crs_spec); + data_georef.setProjectedRefPoint(initial_georef.getProjectedRefPoint()); + data_georef.setMapRefPoint(initial_georef.getMapRefPoint()); + return true; + } + return false; + } + +} // namespace + + + +const std::vector& OgrTemplate::supportedExtensions() +{ + return GdalManager().supportedVectorExtensions(); +} + + +OgrTemplate::OgrTemplate(const QString& path, Map* map) +: TemplateMap(path, map) +{ + connect(&Settings::getInstance(), &Settings::settingsChanged, this, &OgrTemplate::applySettings); + + const Georeferencing& georef = map->getGeoreferencing(); + connect(&georef, &Georeferencing::projectionChanged, this, &OgrTemplate::mapTransformationChanged); + connect(&georef, &Georeferencing::transformationChanged, this, &OgrTemplate::mapTransformationChanged); +} + +OgrTemplate::~OgrTemplate() +{ + if (template_state == Loaded) + unloadTemplateFile(); +} + + +const char* OgrTemplate::getTemplateType() const +{ + return "OgrTemplate"; +} + + + +std::unique_ptr OgrTemplate::makeOrthographicGeoreferencing(QFile& file) +{ + // Is the template's SRS orthographic, or can it be converted? + /// \todo Use the template's datum etc. instead of WGS84? + auto georef = std::make_unique(); + georef->setScaleDenominator(int(map->getGeoreferencing().getScaleDenominator())); + georef->setProjectedCRS(QString{}, QStringLiteral("+proj=ortho +datum=WGS84 +ellps=WGS84 +units=m +no_defs")); + if (OgrFileImport::checkGeoreferencing(file, *georef)) + { + auto center = OgrFileImport::calcAverageLatLon(file); + georef->setProjectedCRS(QString{}, + QString::fromLatin1("+proj=ortho +datum=WGS84 +ellps=WGS84 +units=m +lat_0=%1 +lon_0=%2 +no_defs") + .arg(center.latitude()).arg(center.longitude())); + georef->setProjectedRefPoint({}, false); + } + else + { + georef.reset(); + } + return georef; +} + + +bool OgrTemplate::preLoadConfiguration(QWidget* dialog_parent) +try +{ + is_georeferenced = false; + explicit_georef.reset(); + track_crs_spec.clear(); + transform = {}; + updateTransformationMatrices(); + + auto ends_with_any_of = [](const QString& path, const std::vector& list) -> bool { + using namespace std; + return any_of(begin(list), end(list), [path](const QByteArray& extension) { + return path.endsWith(QLatin1String(extension), Qt::CaseInsensitive); + } ); + }; + template_track_compatibility = ends_with_any_of(template_path, TemplateTrack::supportedExtensions()); + + QFile file{ template_path }; + + auto data_georef = std::unique_ptr(); + auto& initial_georef = map->getGeoreferencing(); + if (!initial_georef.isValid() || initial_georef.isLocal()) + { + // The map doesn't have a proper georeferencing. + // Is there a good SRS in the data? + try { + data_georef = getDataGeoreferencing(file, initial_georef); + } + catch (FileFormatException&) + {} + + if (data_georef && data_georef->isValid() && !data_georef->isLocal()) + { + // If yes, does the user want to use this for the map? + auto keep_projected = false; + if (template_track_compatibility) + data_georef->setGrivation(0); + else + keep_projected = preserveRefPoints(*data_georef, initial_georef); + GeoreferencingDialog dialog(dialog_parent, map, data_georef.get()); + if (keep_projected) + dialog.setKeepProjectedRefCoords(); + else + dialog.setKeepGeographicRefCoords(); + dialog.exec(); + } + } + + auto& georef = map->getGeoreferencing(); // initial_georef might be outdated. + if (georef.isValid() && !georef.isLocal()) + { + // The map has got a proper georeferencing. + // Can the template's SRS be converted to the map's CRS? + if (OgrFileImport::checkGeoreferencing(file, map->getGeoreferencing())) + { + is_georeferenced = true; + return true; + } + } + + // Is the template's SRS orthographic, or can it be converted? + if (data_georef && !data_georef->getProjectedCRSSpec().contains(QLatin1String("+proj=ortho"))) + { + data_georef.reset(); + } + if (!data_georef) + { + data_georef = makeOrthographicGeoreferencing(file); + } + if (data_georef) + { + if (template_track_compatibility) + data_georef->setGrivation(0); + else + preserveRefPoints(*data_georef, initial_georef); + explicit_georef = std::move(data_georef); + } + + TemplatePositioningDialog dialog(dialog_parent); + if (dialog.exec() == QDialog::Rejected) + return false; + + center_in_view = dialog.centerOnView(); + use_real_coords = dialog.useRealCoords(); + if (use_real_coords) + { + transform.template_scale_x = transform.template_scale_y = dialog.getUnitScale(); + updateTransformationMatrices(); + } + return true; +} +catch (FileFormatException& e) +{ + setErrorString(e.message()); + return false; +} + + +bool OgrTemplate::loadTemplateFileImpl(bool configuring) +try +{ + QFile file{ template_path }; + auto new_template_map = std::make_unique(); + auto unit_type = use_real_coords ? OgrFileImport::UnitOnGround : OgrFileImport::UnitOnPaper; + OgrFileImport importer{ &file, new_template_map.get(), nullptr, unit_type }; + + // Configure generation of renderables. + updateView(*new_template_map); + + const auto& map_georef = map->getGeoreferencing(); + + if (template_track_compatibility) + { + if (configuring) + { + if (is_georeferenced) + { + // Data is to be transformed to the map CRS directly. + track_crs_spec = Georeferencing::geographic_crs_spec; + projected_crs_spec.clear(); + } + else if (explicit_georef) + { + // Data is to be transformed to the projected CRS. + track_crs_spec = Georeferencing::geographic_crs_spec; + projected_crs_spec = explicit_georef->getProjectedCRSSpec(); + } + else + { + track_crs_spec.clear(); + projected_crs_spec.clear(); + } + } + else + { + if (is_georeferenced) + { + // Data is to be transformed to the map CRS directly. + Q_ASSERT(projected_crs_spec.isEmpty()); + } + else if (!track_crs_spec.contains(QLatin1String("+proj=latlong"))) + { + // Nothing to do with this configuration + Q_ASSERT(projected_crs_spec.isEmpty()); + } + else if (!explicit_georef) + { + // Data is to be transformed to the projected CRS. + if (projected_crs_spec.isEmpty()) + { + // Need to create an orthographic projection. + // For a transition period, copy behaviour from + // TemplateTrack::loadTemplateFileImpl(), + // TemplateTrack::calculateLocalGeoreferencing(). + // This is an extra expense (by loading the data twice), but + // only until the map (projected_crs_spec) is saved once. + TemplateTrack track{template_path, map}; + if (track.track.loadFrom(template_path, false)) + { + projected_crs_spec = track.calculateLocalGeoreferencing(); + } + else + { + // If the TemplateTrack approach failed, use local approach. + explicit_georef = makeOrthographicGeoreferencing(file); + projected_crs_spec = explicit_georef->getProjectedCRSSpec(); + } + } + + if (!explicit_georef) + { + explicit_georef.reset(new Georeferencing()); + explicit_georef->setScaleDenominator(int(map_georef.getScaleDenominator())); + explicit_georef->setProjectedCRS(QString{}, projected_crs_spec); + explicit_georef->setProjectedRefPoint({}, false); + } + } + } + } + + + if (is_georeferenced || !explicit_georef) + { + new_template_map->setGeoreferencing(map_georef); + } + else + { + new_template_map->setGeoreferencing(*explicit_georef); + } + + const auto pp0 = new_template_map->getGeoreferencing().getProjectedRefPoint(); + importer.setGeoreferencingImportEnabled(false); + importer.doImport(false, template_path); + + // MapCoord bounds handling may have moved the paper position of the + // template data during import. The template position might need to be + // adjusted accordingly. + // However, this will happen again the next time the template is loaded. + // So this adjustment must not affect the saved configuration. + const auto pm0 = new_template_map->getGeoreferencing().toMapCoords(pp0); + const auto pm1 = new_template_map->getGeoreferencing().getMapRefPoint(); + setTemplatePositionOffset(pm1 - pm0); + + setTemplateMap(std::move(new_template_map)); + + const auto& warnings = importer.warnings(); + if (!warnings.empty()) + { + QString message; + message.reserve((warnings.back().length()+1) * int(warnings.size())); + for (const auto& warning : warnings) + { + message.append(warning); + message.append(QLatin1String{"\n"}); + } + message.chop(1); + setErrorString(message); + } + + return true; +} +catch (FileFormatException& e) +{ + setErrorString(e.message()); + return false; +} + + +bool OgrTemplate::postLoadConfiguration(QWidget* dialog_parent, bool& out_center_in_view) +{ + Q_UNUSED(dialog_parent) + out_center_in_view = center_in_view; + return true; +} + + + +void OgrTemplate::mapProjectionChanged() +{ + if (is_georeferenced && template_state == Template::Loaded) + reloadLater(); +} + +void OgrTemplate::mapTransformationChanged() +{ + if (is_georeferenced) + { + if (template_state != Template::Loaded) + return; + + if (templateMap()->getScaleDenominator() != map->getScaleDenominator()) + { + // We can't know how to correctly scale symbol dimension. + reloadLater(); + return; + } + + QTransform t = templateMap()->getGeoreferencing().mapToProjected(); + t *= map->getGeoreferencing().projectedToMap(); + templateMap()->applyOnAllObjects([&t](Object* o) { o->transform(t); }); + templateMap()->setGeoreferencing(map->getGeoreferencing()); + } + else if (explicit_georef) + { + template_track_compatibility = false; + } + else if (template_state == Template::Loaded) + { + template_track_compatibility = false; + if (!explicit_georef) + { + explicit_georef = std::make_unique(templateMap()->getGeoreferencing()); + resetTemplatePositionOffset(); + } + } +} + + + +void OgrTemplate::reloadLater() +{ + if (reload_pending) + return; + + if (template_state == Loaded) + templateMap()->clear(); // no expensive operations before reloading + QTimer::singleShot(0, this, SLOT(reload())); + reload_pending = true; +} + +void OgrTemplate::reload() +{ + if (template_state == Loaded) + unloadTemplateFile(); + loadTemplateFile(false); + reload_pending = false; +} + + + +void OgrTemplate::applySettings() +{ + if (auto* template_map = templateMap()) + updateView(*template_map); +} + +void OgrTemplate::updateView(Map& template_map) +{ + GdalManager manager; + const auto enable_hatching = manager.isAreaHatchingEnabled(); + const auto enable_baseline = manager.isBaselineViewEnabled(); + + bool dirty = false; + if (template_map.isAreaHatchingEnabled() != enable_hatching) + { + template_map.setAreaHatchingEnabled(enable_hatching); + dirty = true; + } + if (template_map.isBaselineViewEnabled() != enable_baseline) + { + template_map.setBaselineViewEnabled(enable_baseline); + dirty = true; + } + + if (dirty && templateMap() == &template_map) + { + template_map.updateAllObjects(); + setTemplateAreaDirty(); + } +} + + + +OgrTemplate* OgrTemplate::duplicateImpl() const +{ + auto copy = new OgrTemplate(template_path, map); + if (template_state == Loaded) + copy->loadTemplateFileImpl(false); + return copy; +} + + +bool OgrTemplate::loadTypeSpecificTemplateConfiguration(QXmlStreamReader& xml) +{ + if (xml.name() == literal::georeferencing) + { + explicit_georef.reset(new Georeferencing()); + explicit_georef->load(xml, false); + } + else if (xml.name() == literal::crs_spec) + { + track_crs_spec = xml.readElementText(); + template_track_compatibility = true; + } + else if (xml.name() == literal::projected_crs_spec) + { + projected_crs_spec = xml.readElementText(); + template_track_compatibility = true; + } + else + { + xml.skipCurrentElement(); + } + + return true; +} + + +void OgrTemplate::saveTypeSpecificTemplateConfiguration(QXmlStreamWriter& xml) const +{ + if (template_track_compatibility) + { + xml.writeTextElement(literal::crs_spec, track_crs_spec); + if (!projected_crs_spec.isEmpty()) + xml.writeTextElement(literal::projected_crs_spec, projected_crs_spec); + } + else if (explicit_georef) + { + explicit_georef->save(xml); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gdal/ogr_template.h b/src/gdal/ogr_template.h new file mode 100644 index 0000000..67ef57b --- /dev/null +++ b/src/gdal/ogr_template.h @@ -0,0 +1,117 @@ +/* + * Copyright 2016-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_OGR_TEMPLATE_H +#define OPENORIENTEERING_OGR_TEMPLATE_H + +#include +#include + +#include +#include + +#include "templates/template_map.h" + +class QByteArray; +class QFile; +class QWidget; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace OpenOrienteering { + +class Georeferencing; +class Map; + + +/** + * A Template which displays a file supported by OGR + * (geospatial vector data). + */ +class OgrTemplate : public TemplateMap +{ +Q_OBJECT +public: + static const std::vector& supportedExtensions(); + + + OgrTemplate(const QString& path, Map* map); + + ~OgrTemplate() override; + + + const char* getTemplateType() const override; + + std::unique_ptr makeOrthographicGeoreferencing(QFile& file); + + bool preLoadConfiguration(QWidget* dialog_parent) override; + + /** + * Loads the geospatial vector data into the template_map. + * + * If is_georereferenced is true, the template_map will be configured to use + * the georeferencing of the map given in the constructor, and OgrFileFormat + * will let OGR do coordinate transformations as needed. + * + * If is_georeferenced is false and an explicit_georef is defined, the + * template_map will be configured to use this particular georeferencing + * to produce a projection of the original data. + * + * Otherwise, the data will be handled as raw map or paper data, depending on + * use_real_coords. + */ + bool loadTemplateFileImpl(bool configuring) override; + + bool postLoadConfiguration(QWidget* dialog_parent, bool& out_center_in_view) override; + +protected: + void reloadLater(); + +protected slots: + void reload(); + + void applySettings(); + +protected: + void updateView(Map& template_map); + + void mapProjectionChanged(); + + void mapTransformationChanged(); + + OgrTemplate* duplicateImpl() const override; + + bool loadTypeSpecificTemplateConfiguration(QXmlStreamReader& xml) override; + + void saveTypeSpecificTemplateConfiguration(QXmlStreamWriter& xml) const override; + +private: + std::unique_ptr explicit_georef; + QString track_crs_spec; // (limited) TemplateTrack compatibility + QString projected_crs_spec; // (limited) TemplateTrack compatibility + bool template_track_compatibility { false }; // transient + bool use_real_coords { true }; // transient + bool center_in_view { false }; // transient + bool reload_pending { false }; // transient +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_OGR_TEMPLATE_H diff --git a/src/global.cpp b/src/global.cpp new file mode 100644 index 0000000..2268a66 --- /dev/null +++ b/src/global.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "global.h" + +#include "mapper_config.h" // IWYU pragma: keep + +#include "fileformats/file_format_registry.h" +#include "fileformats/native_file_format.h" +#include "fileformats/xml_file_format.h" +#include "fileformats/ocd_file_format.h" +#include "gdal/ogr_file_format.h" + + +namespace OpenOrienteering { + +void doStaticInitializations() +{ + // Register the supported file formats + FileFormats.registerFormat(new XMLFileFormat()); +#ifndef MAPPER_BIG_ENDIAN + FileFormats.registerFormat(new OcdFileFormat()); +#endif +#ifdef MAPPER_USE_GDAL + FileFormats.registerFormat(new OgrFileFormat()); +#endif +#ifndef MAPPER_BIG_ENDIAN +#ifndef NO_NATIVE_FILE_FORMAT + FileFormats.registerFormat(new NativeFileFormat()); // TODO: Remove before release 1.0 +#endif +#endif +} + + +} // namespace OpenOrienteering diff --git a/src/global.h b/src/global.h new file mode 100644 index 0000000..d921933 --- /dev/null +++ b/src/global.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_GLOBAL_H +#define OPENORIENTEERING_GLOBAL_H + +namespace OpenOrienteering { + +/** + * This is called at startup in main() and by the test cases + * to do the global initializations. + */ +void doStaticInitializations(); + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/about_dialog.cpp b/src/gui/about_dialog.cpp new file mode 100644 index 0000000..1e2c91d --- /dev/null +++ b/src/gui/about_dialog.cpp @@ -0,0 +1,198 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "about_dialog.h" + +#include + +#include +#include + +#include "mapper_config.h" + + +namespace OpenOrienteering { + +namespace { + +/** + * @brief An URL identifying the main "about" page. + * + * The main page's text will be set directly, thus not having a valid URL. + * But an empty URL will be ignored by QTextBrowser's history, leading to + * unexpected behaviour of backward navigation. + */ +const QUrl& aboutPageUrl() +{ + static auto url = QUrl(QString::fromLatin1("#ABOUT")); + return url; +} + +/** + * Puts the items of a QStringList into an HTML block or a sequence of blocks. + */ +QString formatBlock(const QStringList& items) +{ +#if defined(Q_OS_ANDROID) // or any other small-screen device + QString block = QLatin1String("

    ") + + items.join(QString::fromLatin1(", ")) + + QLatin1String("

    "); +#else + QString block; + block.reserve(100 + 30 * items.size()); + block.append(QLatin1String("
    ")); + constexpr int columns = 3; + const int rows = (int)ceil((double)items.size() / columns); + for (int i = 0, row = 1; i < items.size(); ++i) + { + block.append(items[i]); + if (rows != row) + { + block.append(QString::fromLatin1("
    ")); + ++row; + } + else if (i < items.size()) + { + block.append(QString::fromLatin1("
       ")); + row = 1; + } + } + block.append(QString::fromLatin1("
    ")); +#endif + return block; +} + + +} // namespace + + + +AboutDialog::AboutDialog(QWidget* parent) + : TextBrowserDialog(aboutPageUrl(), parent) +{ + text_browser->setHtml(about()); + text_browser->document()->adjustSize(); + updateWindowTitle(); +} + +void AboutDialog::sourceChanged(const QUrl& url) +{ + if (url == aboutPageUrl()) + text_browser->setHtml(about()); +} + +void AboutDialog::updateWindowTitle() +{ + QString title = text_browser->documentTitle(); + if (title.isEmpty()) + title = tr("About %1").arg(APP_NAME); + setWindowTitle(title); +} + +QString AboutDialog::about() +{ + static QStringList developers_list( QStringList() + << QString::fromLatin1("Peter Curtis (2012-2013)") + << QString::fromLatin1("Kai Pastor") + << QString::fromUtf8("Thomas Schöps (2012-2014, %1)") + ); + + static QStringList contributors_list( QStringList() + << QString::fromLatin1("Arrizal Amin") + << QString::fromLatin1("Javier Arufe") + << QString::fromLatin1("Eric Boulet") + << QString::fromLatin1("Jon Cundill") + << QString::fromUtf8("Sławomir Cygler") + << QString::fromLatin1("Jan Dalheimer") + << QString::fromLatin1("Davide De Nardis") + << QString::fromLatin1("Eugeniy Fedirets") + << QString::fromLatin1("Joao Franco") + << QString::fromLatin1("Pavel Fric") + << QString::fromLatin1("Naofumi Fukue") + << QString::fromLatin1("Anders Gressli") + << QString::fromLatin1("Peter Hoban") + << QString::fromLatin1("Henrik Johansson") + << QString::fromLatin1("Panu Karhu") + << QString::fromLatin1("Oskar Karlin") + << QString::fromLatin1("Nikolay Korotkiy") + << QString::fromLatin1("Mitchell Krome") + << QString::fromUtf8("Matthias Kühlewein") + << QString::fromLatin1("Albin Larsson") + << QString::fromUtf8("István Marczis") + << QString::fromLatin1("Tojo Masaya") + << QString::fromLatin1("Yevhen Mazur") + << QString::fromLatin1("Fraser Mills") + << QString::fromLatin1("Vincent Poinsignon") + << QString::fromLatin1("Russell Porter") + << QString::fromLatin1("Adhika Setya Pramudita") + << QString::fromLatin1("Christopher Schive") + << QString::fromLatin1("Arif Suryawan") + << QString::fromLatin1("Jan-Gerard van der Toorn") + << QString::fromLatin1("Semyon Yakimov") + << QString::fromLatin1("Aivars Zogla") + ); + + QString mapper_about = QString::fromLatin1( + "" + "%0" + "" + "" + "" + "" + "
    " + "

    %1

    " + "

    " + "%3
    " + "%4

    " + "

    Copyright (C) 2018 The OpenOrienteering developers

    " + "

    %5

    " + "

    %6

    " + "

    %7

    " + "

    %8

    %9" + "

     
    %10

    %11" + "" + ).arg( + tr("About %1").arg(APP_NAME), // %0 + qApp->applicationDisplayName(), // %1 + tr("A free software for drawing orienteering maps"), // %3 + QString::fromLatin1("https://www.openorienteering.org/apps/mapper/"), // %4 + tr("This program is free software: you can redistribute it " + "and/or modify it under the terms of the " + "GNU General Public License (GPL), version 3, " + "as published by the Free Software Foundation.").arg(QString::fromLatin1("href=\"file:doc:/common-licenses/GPL-3.txt\"")), // %5 + // %6 + tr("This program is distributed in the hope that it will be useful, " + "but WITHOUT ANY WARRANTY; without even the implied warranty of " + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " + "GNU General Public License (GPL), version 3, for " + "more details.").arg(QString::fromLatin1("href=\"file:doc:/common-licenses/GPL-3.txt#L589\"")), // %6 + tr("All about licenses, copyright notices, conditions and disclaimers.").arg(QString::fromLatin1("href=\"file:doc:/licensing.html\"")) // %7 + ).arg( + tr("The OpenOrienteering developers in alphabetical order:"), // %8 + formatBlock(developers_list).arg(tr("(project initiator)").replace(QLatin1Char('('), QString{}).replace(QLatin1Char(')'), QString{})), // %9 + tr("For contributions, thanks to:"), // %10 + formatBlock(contributors_list) // %11 + ); + + return mapper_about; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/about_dialog.h b/src/gui/about_dialog.h new file mode 100644 index 0000000..5a2e41e --- /dev/null +++ b/src/gui/about_dialog.h @@ -0,0 +1,68 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_ABOUT_DIALOG_H +#define OPENORIENTEERING_ABOUT_DIALOG_H + +#include +#include + +class QUrl; +class QWidget; + +#include "gui/text_browser_dialog.h" + +namespace OpenOrienteering { + + +/** + * @brief A dialog which shows information about Mapper and its components. + */ +class AboutDialog : public TextBrowserDialog +{ +Q_OBJECT +public: + /** + * @brief Construct a new AboutDialog. + */ + AboutDialog(QWidget* parent = nullptr); + + /** + * @brief Returns the basic information about this software. + * The return string may contain HTML formatting. + */ + static QString about(); + +protected: + /** + * @brief Sets custom HTML content when the URL identifies the first page. + */ + void sourceChanged(const QUrl& url) override; + + /** + * @brief Updates the window title from the current document title. + */ + void updateWindowTitle() override; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/autosave_dialog.cpp b/src/gui/autosave_dialog.cpp new file mode 100644 index 0000000..767dd7a --- /dev/null +++ b/src/gui/autosave_dialog.cpp @@ -0,0 +1,198 @@ +/* + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "autosave_dialog.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "main_window.h" + + +namespace OpenOrienteering { + +AutosaveDialog::AutosaveDialog(const QString& path, const QString& autosave_path, const QString& actual_path, MainWindow* parent, Qt::WindowFlags f) +: QDialog(parent, f) +, main_window(parent) +, original_path(path) +, autosave_path(autosave_path) +, resolved(false) +{ + const QString text_template = QString::fromLatin1("%1
    %2
    %3"); + + QFileInfo autosaved_file_info(autosave_path); + autosaved_text.setHtml(text_template. + arg(tr("Autosaved file"), + autosaved_file_info.lastModified().toLocalTime().toString(), + tr("%n bytes", 0, autosaved_file_info.size()))); + + QFileInfo user_saved_file_info(path); + user_saved_text.setHtml(text_template. + arg(tr("File saved by the user"), + user_saved_file_info.lastModified().toLocalTime().toString(), + tr("%n bytes", 0, user_saved_file_info.size())) ); + + layout = new QVBoxLayout(); + setLayout(layout); + + setWindowTitle(tr("File recovery")); + + QString intro_text = tr("File %1 was not properly closed. At the moment, there are two versions:"); + QLabel* label = new QLabel(intro_text.arg(QString::fromLatin1("%1").arg(user_saved_file_info.fileName()))); + label->setWordWrap(true); + layout->addWidget(label); + + list_widget = new QListWidget(); + list_widget->setItemDelegate(new TextDocItemDelegate(this, this)); + list_widget->setSelectionMode(QAbstractItemView::SingleSelection); + QListWidgetItem* item = new QListWidgetItem(list_widget, QListWidgetItem::UserType); + item->setData(Qt::UserRole, QVariant(1)); + item = new QListWidgetItem(list_widget, QListWidgetItem::UserType); + item->setData(Qt::UserRole, QVariant(2)); + layout->addWidget(list_widget); + + label = new QLabel(tr("Save the active file to remove the conflicting version.")); + label->setWordWrap(true); + layout->addWidget(label); + + setSelectedPath(actual_path); + + connect(list_widget, &QListWidget::currentRowChanged, this, &AutosaveDialog::currentRowChanged, Qt::QueuedConnection); + +#if defined(Q_OS_ANDROID) + setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) + | Qt::WindowMaximized); +#endif +} + +AutosaveDialog::~AutosaveDialog() +{ + // Nothing +} + +// slot +int AutosaveDialog::exec() +{ + QDialogButtonBox button_box(QDialogButtonBox::Open | QDialogButtonBox::Cancel); + layout->addWidget(&button_box); + connect(&button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(&button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + const int result = QDialog::exec(); + resolved = true; + return result; +} + +// slot +void AutosaveDialog::autosaveConflictResolved() +{ + resolved = true; + if (!isModal()) + close(); +} + +void AutosaveDialog::closeEvent(QCloseEvent *event) +{ + if (main_window && !resolved) + event->setAccepted(main_window->closeFile()); + else + event->setAccepted(resolved); +} + +QString AutosaveDialog::selectedPath() const +{ + const int row = list_widget->currentRow(); + switch (row) + { + case 0: + return autosave_path; + break; + case 1: + return original_path; + break; + case -1: + break; // Nothing selected? + default: + qWarning("Undefined index"); + } + return QString(); +} + +// slot +void AutosaveDialog::setSelectedPath(const QString& path) +{ + if (path == original_path) + list_widget->setCurrentRow(1); + else if (path == autosave_path) + list_widget->setCurrentRow(0); + else + list_widget->setCurrentRow(-1); +} + +void AutosaveDialog::currentRowChanged(int row) +{ + switch (row) + { + case 0: + emit pathSelected(autosave_path); + break; + case 1: + emit pathSelected(original_path); + break; + case -1: + return; // Nothing selected? + default: + qWarning("Undefined index"); + } +} + +const QTextDocument* AutosaveDialog::textDoc(const QModelIndex& index) const +{ + const QTextDocument* ret = nullptr; + + bool ok = true; + int i = index.data(Qt::UserRole).toInt(&ok); + if (ok) + { + switch (i) + { + case 1: + ret = &autosaved_text; + break; + case 2: + ret = &user_saved_text; + break; + default: + qWarning("Undefined index"); + } + } + else + { + qWarning("Invalid data for UserRole"); + } + + return ret; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/autosave_dialog.h b/src/gui/autosave_dialog.h new file mode 100644 index 0000000..d0606a6 --- /dev/null +++ b/src/gui/autosave_dialog.h @@ -0,0 +1,141 @@ +/* + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_AUTOSAVE_DIALOG_H +#define OPENORIENTEERING_AUTOSAVE_DIALOG_H + +#include +#include +#include +#include +#include + +#include "util/item_delegates.h" + +class QBoxLayout; +class QCloseEvent; +class QListWidget; +class QModelIndex; + +namespace OpenOrienteering { + +class MainWindow; + + +/** + * @brief A dialog for selection of an autosaved file vs. a user-saved file. + * + * This dialog may be used a modal dialog (via exec()) or as a non-modal dialog + * (via show()). + */ +class AutosaveDialog : public QDialog, public TextDocItemDelegate::Provider +{ +Q_OBJECT +public: + /** + * @brief Constructs the dialog. + * + * @param original_path The path of the file which was originally saved by the user. + * @param autosave_path The path of the file which was autosaved. + * @param actual_path The path which is currently selected. + * @param parent The parent window. + */ + AutosaveDialog(const QString& original_path, const QString& autosave_path, const QString& actual_path, MainWindow* parent = nullptr, Qt::WindowFlags f = 0); + + /** + * Destructor. + */ + ~AutosaveDialog() override; + + /** + * @brief Returns the currently selected path. + */ + QString selectedPath() const; + + /** + * @brief Provides the text documents for the list widget items. + * + * @param index The model index for which the text documents is requested for. + * @return A QTextDocument representing the list item, or nullptr. + */ + const QTextDocument* textDoc(const QModelIndex& index) const override; + +public slots: + /** + * @brief Shows this dialog as a modal dialog. + * + * @override + * + * @return The result (QDialog::DialogCode). + */ + int exec() override; + + /** + * @brief Sets the selected item to the one representing the given path. + * + * If the path does not match either item, nothing is selected. + */ + void setSelectedPath(const QString& path); + + /** + * @brief Informs the dialog that the conflict is resolved. + * + * This will close a non-modal dialog. + */ + void autosaveConflictResolved(); + +signals: + /** + * @brief This signal is emitted when the user selects another item. + * + * @param path The path which belongs to the newly selected item. + */ + void pathSelected(const QString& path); + +protected: + /** + * @brief Ignores the QCloseEvent. + * + * @override + */ + void closeEvent(QCloseEvent* event) override; + +private slots: + void currentRowChanged(int row); + +private: + MainWindow* const main_window; + + const QString original_path; + const QString autosave_path; + + bool resolved; + + QTextDocument autosaved_text; + QTextDocument user_saved_text; + + QBoxLayout* layout; + QListWidget* list_widget; +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_AUTOSAVE_DIALOG_H diff --git a/src/gui/color_dialog.cpp b/src/gui/color_dialog.cpp new file mode 100644 index 0000000..012ce69 --- /dev/null +++ b/src/gui/color_dialog.cpp @@ -0,0 +1,797 @@ +/* + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "color_dialog.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_color.h" +#include "gui/util_gui.h" +#include "gui/widgets/color_dropdown.h" +#include "util/backports.h" +#include "util/util.h" +#include "util/translation_util.h" + + +namespace OpenOrienteering { + +namespace { + + constexpr QSize coloriconSize() + { + return QSize{32, 32}; + } + + +} // namespace + + + +ColorDialog::ColorDialog(const Map& map, const MapColor& source_color, QWidget* parent, Qt::WindowFlags f) +: QDialog(parent, f) +, map(map) +, source_color(source_color) +, color(source_color) +, color_modified(false) +, react_to_changes(true) +{ + setWindowTitle(tr("Edit map color")); + setSizeGripEnabled(true); + + color_preview_label = new QLabel(); + color_name_label = new QLabel(); + mc_name_edit = new QLineEdit(); + language_combo = new QComboBox(); + name_edit_button = new QPushButton(tr("Edit")); + + prof_color_layout = new QGridLayout(); + int col = 0; + prof_color_layout->setColumnStretch(col, 1); + prof_color_layout->setColumnStretch(col+1, 3); + + int row = 0; + prof_color_layout->addWidget(Util::Headline::create(tr("Spot color printing")), row, col, 1, 2); + + auto spot_color_options = new QButtonGroup(this); + + ++row; + full_tone_option = new QRadioButton(tr("Defines a spot color:")); + spot_color_options->addButton(full_tone_option, MapColor::SpotColor); + prof_color_layout->addWidget(full_tone_option, row, col, 1, 2); + + ++row; + sc_name_edit = new QLineEdit(); + prof_color_layout->addWidget(sc_name_edit, row, col, 1, 2); + + ++row; + composition_option = new QRadioButton(tr("Mixture of spot colors (screens and overprint):")); + spot_color_options->addButton(composition_option, MapColor::CustomColor); + prof_color_layout->addWidget(composition_option, row, col, 1, 2); + + std::size_t num_components = 0 /*color.getComponents().size()*/; // FIXME: cleanup + components_row0 = row+1; + components_col0 = col; + component_colors.resize(num_components+1); + component_halftone.resize(num_components+1); + for (std::size_t i = 0; i <= num_components; i++) + { + ++row; + component_colors[i] = new ColorDropDown(&map, &color, true); + component_colors[i]->removeColor(&source_color); + prof_color_layout->addWidget(component_colors[i], row, col); + component_halftone[i] = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); + prof_color_layout->addWidget(component_halftone[i], row, col+1); + } + + ++row; + knockout_option = new QCheckBox(tr("Knockout: erases lower colors")); + prof_color_layout->addWidget(knockout_option, row, col, 1, 2); + knockout_option->setEnabled(false); + + row = 0; col += 2; + prof_color_layout->setColumnStretch(col, 1); + + const int spacing = style()->pixelMetric(QStyle::PM_LayoutTopMargin); + prof_color_layout->addItem(new QSpacerItem(3*spacing, spacing), row, col, 7, 1); + + row = 0; col +=1; + prof_color_layout->setColumnStretch(col, 1); + prof_color_layout->setColumnStretch(col+1, 3); + prof_color_layout->addWidget(Util::Headline::create(tr("CMYK")), row, col, 1, 2); + + auto cmyk_color_options = new QButtonGroup(this); + + ++row; + cmyk_spot_color_option = new QRadioButton(tr("Calculate from spot colors")); + cmyk_color_options->addButton(cmyk_spot_color_option, MapColor::SpotColor); + prof_color_layout->addWidget(cmyk_spot_color_option, row, col, 1, 2); + + ++row; + evaluate_rgb_option = new QRadioButton(tr("Calculate from RGB color")); + cmyk_color_options->addButton(evaluate_rgb_option, MapColor::RgbColor); + prof_color_layout->addWidget(evaluate_rgb_option, row, col, 1, 2); + + ++row; + custom_cmyk_option = new QRadioButton(tr("Custom process color:")); + cmyk_color_options->addButton(custom_cmyk_option, MapColor::CustomColor); + prof_color_layout->addWidget(custom_cmyk_option, row, col, 1, 2); + + ++row; + c_edit = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); + prof_color_layout->addWidget(new QLabel(tr("Cyan")), row, col); + prof_color_layout->addWidget(c_edit, row, col+1); + + ++row; + m_edit = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); + prof_color_layout->addWidget(new QLabel(tr("Magenta")), row, col); + prof_color_layout->addWidget(m_edit, row, col+1); + + ++row; + y_edit = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); + prof_color_layout->addWidget(new QLabel(tr("Yellow")), row, col); + prof_color_layout->addWidget(y_edit, row, col+1); + + ++row; + k_edit = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); + prof_color_layout->addWidget(new QLabel(tr("Black")), row, col); + prof_color_layout->addWidget(k_edit, row, col+1); + + ++row; + stretch_row0 = row; + stretch_col0 = col; + stretch = new QWidget(); + prof_color_layout->addWidget(stretch, row, col); + prof_color_layout->setRowStretch(row, 1); + + auto prof_color_widget = new QWidget(); + prof_color_widget->setLayout(prof_color_layout); + prof_color_widget->setObjectName(QString::fromLatin1("professional")); + + + auto desktop_layout = new QGridLayout(); + col = 0; + desktop_layout->setColumnStretch(col, 1); + desktop_layout->setColumnStretch(col+1, 3); + + row = 0; + desktop_layout->addWidget(Util::Headline::create(tr("RGB")), row, col, 1, 2); + + auto rgb_color_options = new QButtonGroup(this); + + ++row; + rgb_spot_color_option = new QRadioButton(tr("Calculate from spot colors")); + rgb_color_options->addButton(rgb_spot_color_option, MapColor::SpotColor); + desktop_layout->addWidget(rgb_spot_color_option, row, col, 1, 2); + + ++row; + evaluate_cmyk_option = new QRadioButton(tr("Calculate from CMYK color")); + rgb_color_options->addButton(evaluate_cmyk_option, MapColor::CmykColor); + desktop_layout->addWidget(evaluate_cmyk_option, row, col, 1, 2); + + ++row; + custom_rgb_option = new QRadioButton(tr("Custom RGB color:")); + rgb_color_options->addButton(custom_rgb_option, MapColor::CustomColor); + desktop_layout->addWidget(custom_rgb_option, row, col, 1, 2); + + ++row; + r_edit = Util::SpinBox::create(1, 0.0, 255.0, {}, 5); + desktop_layout->addWidget(new QLabel(tr("Red")), row, col); + desktop_layout->addWidget(r_edit, row, col+1); + + ++row; + g_edit = Util::SpinBox::create(1, 0.0, 255.0, {}, 5); + desktop_layout->addWidget(new QLabel(tr("Green")), row, col); + desktop_layout->addWidget(g_edit, row, col+1); + + ++row; + b_edit = Util::SpinBox::create(1, 0.0, 255.0, {}, 5); + desktop_layout->addWidget(new QLabel(tr("Blue")), row, col); + desktop_layout->addWidget(b_edit, row, col+1); + + ++row; + html_edit = new QLineEdit(); + desktop_layout->addWidget(new QLabel(tr("#RRGGBB")), row, col); + desktop_layout->addWidget(html_edit, row, col+1); + + ++row; + desktop_layout->addWidget(new QWidget(), row, col); + desktop_layout->setRowStretch(row, 1); + + row = 0; col += 2; + desktop_layout->setColumnStretch(col, 7); + + desktop_layout->addItem(new QSpacerItem(3*spacing, spacing), row, col, 7, 1); + + auto desktop_color_widget = new QWidget(); + desktop_color_widget->setLayout(desktop_layout); + desktop_color_widget->setObjectName(QString::fromLatin1("desktop")); + + + properties_widget = new QTabWidget(); + properties_widget->addTab(desktop_color_widget, tr("Desktop")); + properties_widget->addTab(prof_color_widget, tr("Professional printing")); + + auto button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok | QDialogButtonBox::Reset | QDialogButtonBox::Help); + ok_button = button_box->button(QDialogButtonBox::Ok); + reset_button = button_box->button(QDialogButtonBox::Reset); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(button_box, &QDialogButtonBox::accepted, this, &ColorDialog::accept); + connect(reset_button, &QAbstractButton::clicked, this, &ColorDialog::reset); + connect(button_box->button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &ColorDialog::showHelp); + + auto layout = new QGridLayout(); + row = 0; col = 0; + layout->addWidget(color_preview_label, row, col); col++; + layout->addWidget(color_name_label, row, col, 1, 4); + row++; col = 0; + layout->addWidget(new QLabel(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Text source:")), row, col, 1, 2); col+=2; + layout->addWidget(language_combo, row, col); col++; + layout->addWidget(name_edit_button, row, col); + row++; col = 0; + layout->addWidget(new QLabel(tr("Name")), row, col, 1, 2); col+=2; + layout->addWidget(mc_name_edit, row, col, 1, 3); + row++; col = 0; + layout->addWidget(properties_widget, row, col, 1, 5); + row++; + layout->addWidget(button_box, row, col, 1, 5); + layout->setColumnStretch(4, 1); + setLayout(layout); + + reset(); + updateButtons(); + + connect(language_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &ColorDialog::languageChanged); + connect(name_edit_button, &QPushButton::clicked, this, &ColorDialog::editClicked); + connect(mc_name_edit, &QLineEdit::textChanged, this, &ColorDialog::mapColorNameChanged); + + connect(spot_color_options, QOverload::of(&QButtonGroup::buttonClicked), this, &ColorDialog::spotColorTypeChanged); + connect(sc_name_edit, &QLineEdit::textChanged, this, &ColorDialog::spotColorNameChanged); + for (std::size_t i = 0; i < component_colors.size(); i++) + { + connect(component_colors[i], QOverload::of(&ColorDropDown::currentIndexChanged), this, &ColorDialog::spotColorCompositionChanged); + connect(component_halftone[i], QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::spotColorCompositionChanged); + } + connect(knockout_option, &QAbstractButton::clicked, this, &ColorDialog::knockoutChanged); + + connect(cmyk_color_options, QOverload::of(&QButtonGroup::buttonClicked), this, &ColorDialog::cmykColorTypeChanged); + connect(c_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::cmykValueChanged); + connect(m_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::cmykValueChanged); + connect(y_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::cmykValueChanged); + connect(k_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::cmykValueChanged); + + connect(rgb_color_options, QOverload::of(&QButtonGroup::buttonClicked), this, &ColorDialog::rgbColorTypeChanged); + connect(r_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::rgbValueChanged); + connect(g_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::rgbValueChanged); + connect(b_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::rgbValueChanged); + + QSettings settings; + settings.beginGroup(QString::fromLatin1("ColorDialog")); + QString default_view = settings.value(QString::fromLatin1("view")).toString(); + settings.endGroup(); + properties_widget->setCurrentWidget(properties_widget->findChild(default_view)); +} + + +ColorDialog::~ColorDialog() = default; + + + +void ColorDialog::updateColorLabel() +{ + auto name = map.translate(color.getName()); + if (name.isEmpty()) + name = tr("- unnamed -"); + color_name_label->setText(QLatin1String("") + name + QLatin1String("")); +} + + +void ColorDialog::updateWidgets() +{ + react_to_changes = false; + + QPixmap pixmap(coloriconSize()); + pixmap.fill(colorWithOpacity(color)); + color_preview_label->setPixmap(pixmap); + + sc_name_edit->setText(color.getSpotColorName()); + + const MapColorCmyk& cmyk = color.getCmyk(); + c_edit->setValue(100.0 * double(cmyk.c)); + m_edit->setValue(100.0 * double(cmyk.m)); + y_edit->setValue(100.0 * double(cmyk.y)); + k_edit->setValue(100.0 * double(cmyk.k)); + + knockout_option->setChecked(color.getKnockout()); + + if (color.getSpotColorMethod() == MapColor::SpotColor) + { + full_tone_option->setChecked(true); + sc_name_edit->setEnabled(true); + knockout_option->setEnabled(true); + cmyk_spot_color_option->setEnabled(false); + if (cmyk_spot_color_option->isChecked()) + custom_cmyk_option->setChecked(true); + rgb_spot_color_option->setEnabled(false); + if (rgb_spot_color_option->isChecked()) + custom_rgb_option->setChecked(true); + } + else if (color.getSpotColorMethod() == MapColor::CustomColor) + { + composition_option->setChecked(true); + sc_name_edit->setEnabled(false); + knockout_option->setEnabled(true); + cmyk_spot_color_option->setEnabled(true); + rgb_spot_color_option->setEnabled(true); + } + else + { + composition_option->setChecked(true); + sc_name_edit->setEnabled(false); + cmyk_spot_color_option->setEnabled(false); + if (cmyk_spot_color_option->isChecked()) + { + custom_cmyk_option->setChecked(true); + } + cmyk_spot_color_option->setEnabled(false); + if (rgb_spot_color_option->isChecked()) + { + custom_rgb_option->setChecked(true); + } + rgb_spot_color_option->setEnabled(false); + } + + const SpotColorComponents& color_components = color.getComponents(); + auto num_components = color_components.size(); + auto num_editors = component_colors.size(); + + for (auto i = num_components+1; i < num_editors; ++i) + { + prof_color_layout->removeWidget(component_colors[i]); + delete component_colors[i]; + prof_color_layout->removeWidget(component_halftone[i]); + delete component_halftone[i]; + } + + if (num_editors != num_components+1) + { + prof_color_layout->removeWidget(knockout_option); + prof_color_layout->addWidget(knockout_option, components_row0+int(num_components)+1, components_col0); + } + + component_colors.resize(num_components+1); + component_halftone.resize(num_components+1); + for (auto i = num_editors; i <= num_components; ++i) + { + component_colors[i] = new ColorDropDown(&map, &color, true); + component_colors[i]->removeColor(&source_color); + prof_color_layout->addWidget(component_colors[i], components_row0+int(i), components_col0); + connect(component_colors[i], QOverload::of(&ColorDropDown::currentIndexChanged), this, &ColorDialog::spotColorCompositionChanged); + component_halftone[i] = Util::SpinBox::create(1, 0.0, 100.0, tr("%"), 10.0); + prof_color_layout->addWidget(component_halftone[i], components_row0+int(i), components_col0+1); + connect(component_halftone[i], QOverload::of(&QDoubleSpinBox::valueChanged), this, &ColorDialog::spotColorCompositionChanged); + } + + num_editors = component_colors.size(); + bool enable_component = composition_option->isChecked(); + for (std::size_t i = 0; i < num_editors; i++) + { + bool have_component = (i < num_components); + const MapColor* component_color = have_component ? color_components[i].spot_color : nullptr; + component_colors[i]->setEnabled(enable_component); + component_colors[i]->setColor(component_color); + + bool enable_halftone = enable_component && have_component; + auto component_factor = enable_halftone ? double(color_components[i].factor) : 0.0; + component_halftone[i]->setEnabled(enable_halftone); + component_halftone[i]->setValue(component_factor * 100.0); + + prof_color_layout->setRowStretch(components_row0 + int(i), 0); + + enable_component = enable_component && enable_halftone; + } + // At least one component must be editable to create a composition + if (color.getSpotColorMethod() == MapColor::UndefinedMethod) + component_colors[0]->setEnabled(true); + + auto stretch_row = qMax(stretch_row0, components_row0+int(num_editors)); + if (stretch_row != stretch_row0) + { + prof_color_layout->removeWidget(stretch); + prof_color_layout->addWidget(stretch, stretch_row, stretch_col0); + prof_color_layout->setRowStretch(stretch_row, 1); + } + + bool custom_cmyk = false; + if (color.getCmykColorMethod() == MapColor::SpotColor) + { + cmyk_spot_color_option->setChecked(true); + } + else if (color.getCmykColorMethod() == MapColor::RgbColor) + { + evaluate_rgb_option->setChecked(true); + } + else if (color.getCmykColorMethod() == MapColor::CustomColor) + { + custom_cmyk_option->setChecked(true); + custom_cmyk = true; + } + c_edit->setEnabled(custom_cmyk); + m_edit->setEnabled(custom_cmyk); + y_edit->setEnabled(custom_cmyk); + k_edit->setEnabled(custom_cmyk); + + + const MapColorRgb& rgb = color.getRgb(); + r_edit->setValue(255.0 * double(rgb.r)); + g_edit->setValue(255.0 * double(rgb.g)); + b_edit->setValue(255.0 * double(rgb.b)); + html_edit->setText(QColor(rgb).name()); + + bool custom_rgb = false; + if (color.getRgbColorMethod() == MapColor::SpotColor) + { + rgb_spot_color_option->setChecked(true); + } + else if (color.getRgbColorMethod() == MapColor::CmykColor) + { + evaluate_cmyk_option->setChecked(true); + } + else if (color.getRgbColorMethod() == MapColor::CustomColor) + { + custom_rgb_option->setChecked(true); + custom_rgb = true; + } + r_edit->setEnabled(custom_rgb); + g_edit->setEnabled(custom_rgb); + b_edit->setEnabled(custom_rgb); + html_edit->setEnabled(false); // TODO: Editor + + + react_to_changes = true; +} + +void ColorDialog::accept() +{ + QSettings settings; + settings.beginGroup(QString::fromLatin1("ColorDialog")); + settings.setValue(QString::fromLatin1("view"), properties_widget->currentWidget()->objectName()); + settings.endGroup(); + + QDialog::accept(); +} + +void ColorDialog::reset() +{ + color = source_color; + updateWidgets(); + + language_combo->clear(); + language_combo->addItem(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Map (%1)") + .arg(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "undefined language"))); + auto name = color.getName(); + auto display_name = map.raw_translation(name); + if (display_name.isEmpty()) + { + language_combo->setEnabled(false); + name_edit_button->setEnabled(false); + mc_name_edit->setText(name); + mc_name_edit->setEnabled(true); + } + else + { + auto language = TranslationUtil::languageFromSettings(QSettings()); + if (!language.isValid()) + { + language.displayName = QApplication::translate("OpenOrienteering::MapSymbolTranslation", "undefined language"); + } + + language_combo->addItem(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Translation (%1)").arg(language.displayName)); + language_combo->setCurrentIndex(1); + language_combo->setEnabled(true); + name_edit_button->setEnabled(true); + mc_name_edit->setText(display_name); + mc_name_edit->setEnabled(false); + } + + setColorModified(false); +} + +void ColorDialog::setColorModified(bool modified) +{ + updateColorLabel(); + if (color_modified != modified) + { + color_modified = modified; + updateButtons(); + } +} + +void ColorDialog::updateButtons() +{ + ok_button->setEnabled(color_modified); + reset_button->setEnabled(color_modified); +} + +void ColorDialog::showHelp() +{ + Util::showHelp(this, "color_dock_widget.html", "editor"); +} + + + +void ColorDialog::languageChanged() +{ + auto name = color.getName(); + if (language_combo->currentIndex() == 1) + { + name = map.raw_translation(name); + } + QSignalBlocker block(mc_name_edit); + mc_name_edit->setText(name); +} + + +void ColorDialog::editClicked() +{ + int result; + auto question = QString{}; + if (language_combo->currentIndex() == 1) + { + question = QApplication::translate("OpenOrienteering::MapSymbolTranslation", + "Before editing, the stored text will be " + "replaced with the current translation. " + "Do you want to continue?"); + } + else + { + question = QApplication::translate("OpenOrienteering::MapSymbolTranslation", + "After modifying the stored text, " + "the translation may no longer be found. " + "Do you want to continue?"); + } + result = QMessageBox::warning(this, tr("Warning"), question, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes); + if (result == QMessageBox::Yes) + { + language_combo->setEnabled(false); + name_edit_button->setEnabled(false); + mc_name_edit->setEnabled(true); + if (language_combo->currentIndex() == 1) + { + { + QSignalBlocker block(language_combo); + language_combo->setCurrentIndex(0); + } + auto name = mc_name_edit->text(); + if (name.isEmpty()) + { + QSignalBlocker block(mc_name_edit); + name = color.getName(); + mc_name_edit->setText(name); + } + color.setName(name); + } + setColorModified(true); + } +} + + + +// slot +void ColorDialog::mapColorNameChanged() +{ + if (!react_to_changes) + return; + + color.setName(mc_name_edit->text()); + + setColorModified(); +} + +void ColorDialog::spotColorTypeChanged(int id) +{ + if (!react_to_changes) + return; + + QString name; + switch (id) + { + case MapColor::SpotColor: + name = color.getName(); + if (name.isEmpty()) + name = QLatin1Char('?'); + color.setSpotColorName(name); + break; + case MapColor::CustomColor: + if (source_color.getSpotColorMethod() == MapColor::CustomColor) + color.setSpotColorComposition(source_color.getComponents()); + else + color.setSpotColorComposition({}); + break; + default: + ; // nothing + } + + updateWidgets(); + setColorModified(true); +} + +void ColorDialog::spotColorNameChanged() +{ + if (!react_to_changes) + return; + + Q_ASSERT(full_tone_option->isChecked()); + color.setSpotColorName(sc_name_edit->text()); + + setColorModified(); +} + +void ColorDialog::spotColorCompositionChanged() +{ + if (!react_to_changes) + return; + + SpotColorComponents components; + SpotColorComponent component; + auto num_editors = component_colors.size(); + components.reserve(num_editors); + for (std::size_t i = 0; i < num_editors; i++) + { + if (!component_colors[i]->isEnabled()) + break; + + const MapColor* spot_color = component_colors[i]->color(); + if (!spot_color) + continue; + + component.spot_color = spot_color; + component.factor = float(component_halftone[i]->value() / 100); + components.push_back(component); + } + color.setSpotColorComposition(components); + + updateWidgets(); + setColorModified(true); +} + +void ColorDialog::knockoutChanged() +{ + if (!react_to_changes) + return; + + color.setKnockout(knockout_option->isChecked()); + setColorModified(); +} + +void ColorDialog::cmykColorTypeChanged(int id) +{ + if (!react_to_changes) + return; + + switch (id) + { + case MapColor::SpotColor: + color.setCmykFromSpotColors(); + break; + case MapColor::RgbColor: +// color.setRgb(color.getRgb()); + color.setCmykFromRgb(); + break; + case MapColor::CustomColor: + color.setCmyk(color.getCmyk()); + break; + default: + ; // nothing + } + + updateWidgets(); + setColorModified(true); +} + +void ColorDialog::cmykValueChanged() +{ + if (!react_to_changes) + return; + + if (custom_cmyk_option->isChecked()) + { + color.setCmyk( MapColorCmyk( + float(c_edit->value()/100), float(m_edit->value()/100), float(y_edit->value()/100), float(k_edit->value()/100) + ) ); + updateWidgets(); + setColorModified(true); + } +} + +void ColorDialog::rgbColorTypeChanged(int id) +{ + if (!react_to_changes) + return; + + switch (id) + { + case MapColor::SpotColor: + color.setRgbFromSpotColors(); + break; + case MapColor::CmykColor: +// color.setCmyk(color.getCmyk()); + color.setRgbFromCmyk(); + break; + case MapColor::CustomColor: + color.setRgb(color.getRgb()); + break; + default: + ; // nothing + } + + updateWidgets(); + setColorModified(true); +} + +void ColorDialog::rgbValueChanged() +{ + if (!react_to_changes) + return; + + if (custom_rgb_option->isChecked()) + { + color.setRgb( MapColorRgb( + float(r_edit->value()/255), float(g_edit->value()/255), float(b_edit->value()/255) + ) ); + updateWidgets(); + setColorModified(true); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/color_dialog.h b/src/gui/color_dialog.h new file mode 100644 index 0000000..0e296ba --- /dev/null +++ b/src/gui/color_dialog.h @@ -0,0 +1,160 @@ +/* + * Copyright 2012, 2013, 2014, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_COLOR_DIALOG_H +#define OPENORIENTEERING_COLOR_DIALOG_H + +#include + +#include +#include +#include + +#include "core/map_color.h" + +class QAbstractButton; +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QGridLayout; +class QLabel; +class QLineEdit; +class QPushButton; +class QRadioButton; +class QTabWidget; +class QWidget; + +namespace OpenOrienteering { + +class ColorDropDown; +class Map; + + +/** + * A dialog for editing a single map color. + */ +class ColorDialog: public QDialog +{ +Q_OBJECT +public: + /** Constructs a new dialog for the given map and color. */ + ColorDialog(const Map& map, const MapColor& source_color, QWidget* parent = nullptr, Qt::WindowFlags f = 0); + + ~ColorDialog() override; + + /** + * Returns the edited color. + */ + const MapColor& getColor() const { return color; } + +protected slots: + void accept() override; + + void reset(); + + void showHelp(); + + void languageChanged(); + + void editClicked(); + + void mapColorNameChanged(); + + void spotColorTypeChanged(int id); + + void spotColorNameChanged(); + + void spotColorCompositionChanged(); + + void knockoutChanged(); + + void cmykColorTypeChanged(int id); + + void cmykValueChanged(); + + void rgbColorTypeChanged(int id); + + void rgbValueChanged(); + + void setColorModified() { setColorModified(true); } + +protected: + void setColorModified(bool modified); + + void updateColorLabel(); + + void updateWidgets(); + + void updateButtons(); + + const Map& map; + const MapColor& source_color; + + MapColor color; + bool color_modified; + + bool react_to_changes; + + QLabel* color_preview_label; + QLabel* color_name_label; + QLineEdit* mc_name_edit; + QComboBox* language_combo; + QPushButton* name_edit_button; + + QRadioButton* full_tone_option; + QRadioButton* composition_option; + QLineEdit* sc_name_edit; + QCheckBox* knockout_option; + + QRadioButton* cmyk_spot_color_option; + QRadioButton* evaluate_rgb_option; + QRadioButton* custom_cmyk_option; + QDoubleSpinBox* c_edit; + QDoubleSpinBox* m_edit; + QDoubleSpinBox* y_edit; + QDoubleSpinBox* k_edit; + + QRadioButton* rgb_spot_color_option; + QRadioButton* evaluate_cmyk_option; + QRadioButton* custom_rgb_option; + QDoubleSpinBox* r_edit; + QDoubleSpinBox* g_edit; + QDoubleSpinBox* b_edit; + QLineEdit* html_edit; + + QTabWidget* properties_widget; + + QAbstractButton* ok_button; + QAbstractButton* reset_button; + + std::vector< ColorDropDown* > component_colors; + std::vector< QDoubleSpinBox* > component_halftone; + int components_row0; + int components_col0; + QGridLayout* prof_color_layout; + int stretch_row0; + int stretch_col0; + QWidget* stretch; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/configure_grid_dialog.cpp b/src/gui/configure_grid_dialog.cpp new file mode 100644 index 0000000..5bfc190 --- /dev/null +++ b/src/gui/configure_grid_dialog.cpp @@ -0,0 +1,280 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014, 2016, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "configure_grid_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/georeferencing.h" +#include "core/map.h" +#include "gui/util_gui.h" +#include "util/backports.h" // IWYU pragma: keep + + +namespace OpenOrienteering { + +class MapCoordF; + + +ConfigureGridDialog::ConfigureGridDialog(QWidget* parent, const Map& map, bool grid_visible) +: QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) +, map(map) +, grid(map.getGrid()) +, grid_visible(grid_visible) +, current_color(grid.getColor()) +, current_unit(grid.getUnit()) +{ + setWindowTitle(tr("Configure grid")); + + show_grid_check = new QCheckBox(tr("Show grid")); + snap_to_grid_check = new QCheckBox(tr("Snap to grid")); + choose_color_button = new QPushButton(tr("Choose...")); + + display_mode_combo = new QComboBox(); + display_mode_combo->addItem(tr("All lines"), int(MapGrid::AllLines)); + display_mode_combo->addItem(tr("Horizontal lines"), int(MapGrid::HorizontalLines)); + display_mode_combo->addItem(tr("Vertical lines"), int(MapGrid::VerticalLines)); + + mag_north_radio = new QRadioButton(tr("Align with magnetic north")); + grid_north_radio = new QRadioButton(tr("Align with grid north")); + true_north_radio = new QRadioButton(tr("Align with true north")); + + auto rotate_label = new QLabel(tr("Additional rotation (counter-clockwise):")); + additional_rotation_edit = Util::SpinBox::create(Georeferencing::declinationPrecision(), -360, +360, trUtf8("°")); + additional_rotation_edit->setWrapping(true); + + + unit_combo = new QComboBox(); + unit_combo->addItem(tr("meters in terrain"), int(MapGrid::MetersInTerrain)); + unit_combo->addItem(tr("millimeters on map"), int(MapGrid::MillimetersOnMap)); + + auto horz_spacing_label = new QLabel(tr("Horizontal spacing:")); + horz_spacing_edit = Util::SpinBox::create(1, 0.1, Util::InputProperties::max()); + auto vert_spacing_label = new QLabel(tr("Vertical spacing:")); + vert_spacing_edit = Util::SpinBox::create(1, 0.1, Util::InputProperties::max()); + + origin_label = new QLabel(); + auto horz_offset_label = new QLabel(tr("Horizontal offset:")); + horz_offset_edit = Util::SpinBox::create(1, Util::InputProperties::min(), Util::InputProperties::max()); + auto vert_offset_label = new QLabel(tr("Vertical offset:")); + vert_offset_edit = Util::SpinBox::create(1, Util::InputProperties::min(), Util::InputProperties::max()); + + auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, Qt::Horizontal); + + + show_grid_check->setChecked(grid_visible); + snap_to_grid_check->setChecked(grid.isSnappingEnabled()); + display_mode_combo->setCurrentIndex(display_mode_combo->findData(int(grid.getDisplayMode()))); + if (grid.getAlignment() == MapGrid::MagneticNorth) + mag_north_radio->setChecked(true); + else if (grid.getAlignment() == MapGrid::GridNorth) + grid_north_radio->setChecked(true); + else // if (grid.getAlignment() == MapGrid::TrueNorth) + true_north_radio->setChecked(true); + additional_rotation_edit->setValue(grid.getAdditionalRotation() * 180 / M_PI); + unit_combo->setCurrentIndex(unit_combo->findData(current_unit)); + horz_spacing_edit->setValue(grid.getHorizontalSpacing()); + vert_spacing_edit->setValue(grid.getVerticalSpacing()); + horz_offset_edit->setValue(grid.getHorizontalOffset()); + vert_offset_edit->setValue(-1 * grid.getVerticalOffset()); + + auto layout = new QFormLayout(); + layout->addRow(show_grid_check); + layout->addRow(snap_to_grid_check); + layout->addRow(tr("Line color:"), choose_color_button); + layout->addRow(tr("Display:"), display_mode_combo); + layout->addItem(Util::SpacerItem::create(this)); + + layout->addRow(Util::Headline::create(tr("Alignment"))); + layout->addRow(mag_north_radio); + layout->addRow(grid_north_radio); + layout->addRow(true_north_radio); + layout->addRow(rotate_label, additional_rotation_edit); + layout->addItem(Util::SpacerItem::create(this)); + + layout->addRow(Util::Headline::create(tr("Positioning"))); + layout->addRow(tr("Unit:", "measurement unit"), unit_combo); + layout->addRow(horz_spacing_label, horz_spacing_edit); + layout->addRow(vert_spacing_label, vert_spacing_edit); + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(origin_label); + layout->addRow(horz_offset_label, horz_offset_edit); + layout->addRow(vert_offset_label, vert_offset_edit); + layout->addItem(Util::SpacerItem::create(this)); + + layout->addRow(button_box); + setLayout(layout); + + updateStates(); + updateColorDisplay(); + + connect(show_grid_check, &QAbstractButton::clicked, this, &ConfigureGridDialog::updateStates); + connect(choose_color_button, &QAbstractButton::clicked, this, &ConfigureGridDialog::chooseColor); + connect(display_mode_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &ConfigureGridDialog::updateStates); + connect(mag_north_radio, &QAbstractButton::clicked, this, &ConfigureGridDialog::updateStates); + connect(grid_north_radio, &QAbstractButton::clicked, this, &ConfigureGridDialog::updateStates); + connect(true_north_radio, &QAbstractButton::clicked, this, &ConfigureGridDialog::updateStates); + connect(unit_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &ConfigureGridDialog::unitChanged); + connect(button_box, &QDialogButtonBox::helpRequested, this, &ConfigureGridDialog::showHelp); + + connect(button_box, &QDialogButtonBox::accepted, this, &ConfigureGridDialog::okClicked); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +ConfigureGridDialog::~ConfigureGridDialog() = default; + + + +void ConfigureGridDialog::chooseColor() +{ + qDebug() << qAlpha(current_color); + QColor new_color = QColorDialog::getColor(current_color, this, tr("Choose grid line color"), QColorDialog::ShowAlphaChannel); + if (new_color.isValid()) + { + current_color = new_color.rgba(); + updateColorDisplay(); + } +} + +void ConfigureGridDialog::updateColorDisplay() +{ + int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); + QPixmap pixmap(icon_size, icon_size); + pixmap.fill(current_color); + QIcon icon(pixmap); + choose_color_button->setIcon(icon); +} + +void ConfigureGridDialog::unitChanged(int index) +{ + auto unit = MapGrid::Unit(unit_combo->itemData(index).toInt()); + if (unit != current_unit) + { + current_unit = unit; + double factor = 1.0; + switch (current_unit) + { + case MapGrid::MetersInTerrain: + factor = 0.001 * map.getScaleDenominator(); + break; + case MapGrid::MillimetersOnMap: + factor = 1000.0 / map.getScaleDenominator(); + break; + default: + Q_ASSERT(!"Illegal unit"); + } + + for (auto editor : { horz_spacing_edit, vert_spacing_edit, horz_offset_edit, vert_offset_edit }) + { + editor->setValue(editor->value() * factor); + } + } + updateStates(); +} + +void ConfigureGridDialog::okClicked() +{ + grid_visible = show_grid_check->isChecked(); + + grid.setSnappingEnabled(snap_to_grid_check->isChecked()); + grid.setColor(current_color); + grid.setDisplayMode(MapGrid::DisplayMode(display_mode_combo->itemData(display_mode_combo->currentIndex()).toInt())); + + if (mag_north_radio->isChecked()) + grid.setAlignment(MapGrid::MagneticNorth); + else if (grid_north_radio->isChecked()) + grid.setAlignment(MapGrid::GridNorth); + else // if (true_north_radio->isChecked()) + grid.setAlignment(MapGrid::TrueNorth); + grid.setAdditionalRotation(additional_rotation_edit->value() * M_PI / 180); + + grid.setUnit(current_unit); + + grid.setHorizontalSpacing(horz_spacing_edit->value()); + grid.setVerticalSpacing(vert_spacing_edit->value()); + grid.setHorizontalOffset(horz_offset_edit->value()); + grid.setVerticalOffset(-1 * vert_offset_edit->value()); + + accept(); +} + +void ConfigureGridDialog::updateStates() +{ + MapGrid::DisplayMode display_mode = MapGrid::DisplayMode(display_mode_combo->itemData(display_mode_combo->currentIndex()).toInt()); + choose_color_button->setEnabled(show_grid_check->isChecked()); + display_mode_combo->setEnabled(show_grid_check->isChecked()); + snap_to_grid_check->setEnabled(show_grid_check->isChecked()); + + mag_north_radio->setEnabled(show_grid_check->isChecked()); + grid_north_radio->setEnabled(show_grid_check->isChecked()); + true_north_radio->setEnabled(show_grid_check->isChecked()); + additional_rotation_edit->setEnabled(show_grid_check->isChecked()); + + unit_combo->setEnabled(show_grid_check->isChecked()); + + QString unit_suffix = QLatin1Char(' ') + ((current_unit == MapGrid::MetersInTerrain) ? tr("m", "meters") : tr("mm", "millimeters")); + horz_spacing_edit->setEnabled(show_grid_check->isChecked() && display_mode != MapGrid::HorizontalLines); + horz_spacing_edit->setSuffix(unit_suffix); + vert_spacing_edit->setEnabled(show_grid_check->isChecked() && display_mode != MapGrid::VerticalLines); + vert_spacing_edit->setSuffix(unit_suffix); + + QString origin_text = tr("Origin at: %1"); + if (mag_north_radio->isChecked() || true_north_radio->isChecked()) + origin_text = origin_text.arg(tr("paper coordinates origin")); + else // if (grid_north_radio->isChecked()) + origin_text = origin_text.arg(tr("projected coordinates origin")); + origin_label->setText(origin_text); + + horz_offset_edit->setEnabled(show_grid_check->isChecked() && display_mode != MapGrid::HorizontalLines); + horz_offset_edit->setSuffix(unit_suffix); + vert_offset_edit->setEnabled(show_grid_check->isChecked() && display_mode != MapGrid::VerticalLines); + vert_offset_edit->setSuffix(unit_suffix); +} + +void ConfigureGridDialog::showHelp() +{ + Util::showHelp(this, "grid.html"); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/configure_grid_dialog.h b/src/gui/configure_grid_dialog.h new file mode 100644 index 0000000..426435c --- /dev/null +++ b/src/gui/configure_grid_dialog.h @@ -0,0 +1,102 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_CONFIGURE_GRID_DIALOG_H +#define OPENORIENTEERING_CONFIGURE_GRID_DIALOG_H + +#include +#include +#include + +#include "core/map_grid.h" + +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QLabel; +class QPushButton; +class QRadioButton; +class QWidget; + +namespace OpenOrienteering { + +class Map; + + +class ConfigureGridDialog : public QDialog +{ +Q_OBJECT +public: + ConfigureGridDialog(QWidget* parent, const Map& map, bool grid_visible); + + ~ConfigureGridDialog() override; + + const MapGrid& resultGrid() const; + + bool gridVisible() const; + +private slots: + void chooseColor(); + void updateColorDisplay(); + void unitChanged(int index); + void okClicked(); + void updateStates(); + void showHelp(); + +private: + QCheckBox* show_grid_check; + QCheckBox* snap_to_grid_check; + QPushButton* choose_color_button; + QComboBox* display_mode_combo; + + QRadioButton* mag_north_radio; + QRadioButton* grid_north_radio; + QRadioButton* true_north_radio; + QDoubleSpinBox* additional_rotation_edit; + + QComboBox* unit_combo; + QDoubleSpinBox* horz_spacing_edit; + QDoubleSpinBox* vert_spacing_edit; + QLabel* origin_label; + QDoubleSpinBox* horz_offset_edit; + QDoubleSpinBox* vert_offset_edit; + + const Map& map; + MapGrid grid; + bool grid_visible; + QRgb current_color; + MapGrid::Unit current_unit; +}; + +inline const MapGrid& ConfigureGridDialog::resultGrid() const +{ + return grid; +} + +inline bool ConfigureGridDialog::gridVisible() const +{ + return grid_visible; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/file_dialog.cpp b/src/gui/file_dialog.cpp new file mode 100644 index 0000000..9a689fb --- /dev/null +++ b/src/gui/file_dialog.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "file_dialog.h" + +#include +#include + +#include +#include // IWYU pragma: keep +#include + +#ifndef QTBUG_51712_QUIRK_ENABLED +# if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(QT_TESTLIB_LIB) +# define QTBUG_51712_QUIRK_ENABLED 1 +# else +# define QTBUG_51712_QUIRK_ENABLED 0 +# endif +#endif + +#if QTBUG_51712_QUIRK_ENABLED +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + + +namespace +{ + constexpr int max_filter_length = 100; + +} // namespace + + + +namespace OpenOrienteering { + +bool FileDialog::needUpperCaseExtensions() +{ +#if QTBUG_51712_QUIRK_ENABLED + auto platform_theme = QGuiApplicationPrivate::platformTheme(); + if (platform_theme + && platform_theme->usePlatformNativeDialog(QPlatformTheme::DialogType::FileDialog)) + { + std::unique_ptr helper { + platform_theme->createPlatformDialogHelper(QPlatformTheme::DialogType::FileDialog) + }; + if (helper) + return qstrncmp(helper->metaObject()->className(), "QGtk", 4) == 0; + } +#endif + return false; +} + + +void FileDialog::adjustParameters(QString& filter, QFileDialog::Options& options) +{ + using std::begin; + using std::end; + + static const auto separator = QString::fromLatin1(";;"); +#if QT_VERSION >= 0x50400 + const auto filters = filter.splitRef(separator); +#else + const auto filters = filter.split(separator); +#endif + + bool has_long_filters = std::any_of(begin(filters), end(filters), [](auto&& item) { + return item.length() > max_filter_length; + }); + +#if QTBUG_51712_QUIRK_ENABLED + static auto need_upper_case = needUpperCaseExtensions(); + if (need_upper_case) + { + QStringList new_filters; + new_filters.reserve(filter.size()); + for (auto&& item : filters) + { + QString new_item; + new_item.reserve(2 * item.length()); + auto split_0 = item.indexOf(QLatin1Char('(')); + auto split_1 = item.lastIndexOf(QLatin1Char(')')); + new_item.append(item.left(split_1)); + new_item.append(QLatin1Char(' ')); +#if QT_VERSION >= 0x50400 + new_item.append(item.mid(split_0+1).toString().toUpper()); +#else + new_item.append(item.mid(split_0+1).toUpper()); +#endif + new_filters.append(new_item); + if (new_item.length() > max_filter_length) + has_long_filters = true; + } + filter = new_filters.join(separator); + } +#endif + + if (has_long_filters) + options |= QFileDialog::HideNameFilterDetails; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/file_dialog.h b/src/gui/file_dialog.h new file mode 100644 index 0000000..d0f9dbb --- /dev/null +++ b/src/gui/file_dialog.h @@ -0,0 +1,95 @@ +/* + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_UTIL_FILE_DIALOG_H +#define OPENORIENTEERING_UTIL_FILE_DIALOG_H + +#include +#include + +class QWidget; + + +namespace OpenOrienteering { + +/** + * A collection of file dialog utility functions. + */ +namespace FileDialog { + + /** + * Returns true if upper case extensions have to be added explicitly + * to filters in file dialogs. + */ + bool needUpperCaseExtensions(); + + + /** + * Adjusts filter and options for file dialogs. + * + * Adds upper case version of the extension when needed. + * Sets QFileDialog::HideNameFilterDetails when the length of any particular + * filter exceeds a certain treshold. + */ + void adjustParameters(QString& filter, QFileDialog::Options& options); + + + /** + * Calls QFileDialog::getOpenFileName with adjusted parameters. + * + * \see adjustParameters, QFileDialog::getOpenFileName + */ + inline + QString getOpenFileName(QWidget* parent = nullptr, + const QString& caption = {}, + const QString& dir = {}, + QString filter = {}, + QString* selected_filter = nullptr, + QFileDialog::Options options = {}) + { + adjustParameters(filter, options); + return QFileDialog::getOpenFileName(parent, caption, dir, filter, selected_filter, options); + } + + + /** + * Calls QFileDialog::getSaveFileName with adjusted parameters. + * + * \see adjustParameters, QFileDialog::getSaveFileName + */ + inline + QString getSaveFileName(QWidget* parent = nullptr, + const QString& caption = {}, + const QString& dir = {}, + QString filter = {}, + QString* selected_filter = nullptr, + QFileDialog::Options options = {}) + { + adjustParameters(filter, options); + return QFileDialog::getSaveFileName(parent, caption, dir, filter, selected_filter, options); + } + + +} // namespace FileDialog + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/georeferencing_dialog.cpp b/src/gui/georeferencing_dialog.cpp new file mode 100644 index 0000000..dd291f8 --- /dev/null +++ b/src/gui/georeferencing_dialog.cpp @@ -0,0 +1,772 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "georeferencing_dialog.h" + +#include + +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// IWYU pragma: no_include + +#if defined(QT_NETWORK_LIB) +#include +#include +#include +#endif + +#include "core/crs_template.h" +#include "core/georeferencing.h" +#include "core/latlon.h" +#include "core/map.h" +#include "gui/main_window.h" +#include "gui/map/map_dialog_rotate.h" +#include "gui/map/map_editor.h" +#include "gui/widgets/crs_selector.h" +#include "gui/util_gui.h" +#include "util/scoped_signals_blocker.h" + + +namespace OpenOrienteering { + +// ### GeoreferencingDialog ### + +GeoreferencingDialog::GeoreferencingDialog(MapEditorController* controller, const Georeferencing* initial, bool allow_no_georeferencing) + : GeoreferencingDialog(controller->getWindow(), controller, controller->getMap(), initial, allow_no_georeferencing) +{ + // nothing else +} + +GeoreferencingDialog::GeoreferencingDialog(QWidget* parent, Map* map, const Georeferencing* initial, bool allow_no_georeferencing) + : GeoreferencingDialog(parent, nullptr, map, initial, allow_no_georeferencing) +{ + // nothing else +} + +GeoreferencingDialog::GeoreferencingDialog( + QWidget* parent, + MapEditorController* controller, + Map* map, + const Georeferencing* initial, + bool allow_no_georeferencing ) + : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) + , controller(controller) + , map(map) + , initial_georef(initial ? initial : &map->getGeoreferencing()) + , georef(new Georeferencing(*initial_georef)) + , allow_no_georeferencing(allow_no_georeferencing) + , tool_active(false) + , declination_query_in_progress(false) + , grivation_locked(!initial_georef->isValid() || initial_georef->getState() != Georeferencing::Normal) + , original_declination(0.0) +{ + if (!grivation_locked) + original_declination = initial_georef->getDeclination(); + + setWindowTitle(tr("Map Georeferencing")); + setWindowModality(Qt::WindowModal); + + // Create widgets + auto map_crs_label = Util::Headline::create(tr("Map coordinate reference system")); + + crs_selector = new CRSSelector(*georef, nullptr); + crs_selector->addCustomItem(tr("- local -"), Georeferencing::Local); + + status_label = new QLabel(tr("Status:")); + status_field = new QLabel(); + + /*: The grid scale factor is the ratio between a length in the grid plane + and the corresponding length on the curved earth model. It is applied + as a factor to ground distances to get grid plane distances. */ + auto scale_factor_label = new QLabel(tr("Grid scale factor:")); + scale_factor_edit = Util::SpinBox::create(Georeferencing::scaleFactorPrecision(), 0.001, 1000.0); + + auto reference_point_label = Util::Headline::create(tr("Reference point")); + + ref_point_button = new QPushButton(tr("&Pick on map")); + int ref_point_button_width = ref_point_button->sizeHint().width(); + auto geographic_datum_label = new QLabel(tr("(Datum: WGS84)")); + int geographic_datum_label_width = geographic_datum_label->sizeHint().width(); + + map_x_edit = Util::SpinBox::create(tr("mm")); + map_y_edit = Util::SpinBox::create(tr("mm")); + ref_point_button->setEnabled(controller); + auto map_ref_layout = new QHBoxLayout(); + map_ref_layout->addWidget(map_x_edit, 1); + map_ref_layout->addWidget(new QLabel(tr("X", "x coordinate")), 0); + map_ref_layout->addWidget(map_y_edit, 1); + map_ref_layout->addWidget(new QLabel(tr("Y", "y coordinate")), 0); + if (ref_point_button_width < geographic_datum_label_width) + map_ref_layout->addSpacing(geographic_datum_label_width - ref_point_button_width); + map_ref_layout->addWidget(ref_point_button, 0); + + easting_edit = Util::SpinBox::create(tr("m")); + northing_edit = Util::SpinBox::create(tr("m")); + auto projected_ref_layout = new QHBoxLayout(); + projected_ref_layout->addWidget(easting_edit, 1); + projected_ref_layout->addWidget(new QLabel(tr("E", "west / east")), 0); + projected_ref_layout->addWidget(northing_edit, 1); + projected_ref_layout->addWidget(new QLabel(tr("N", "north / south")), 0); + projected_ref_layout->addSpacing(qMax(ref_point_button_width, geographic_datum_label_width)); + + projected_ref_label = new QLabel(); + lat_edit = Util::SpinBox::create(8, -90.0, +90.0, trUtf8("°")); + lon_edit = Util::SpinBox::create(8, -180.0, +180.0, trUtf8("°")); + auto geographic_ref_layout = new QHBoxLayout(); + geographic_ref_layout->addWidget(lat_edit, 1); + geographic_ref_layout->addWidget(new QLabel(tr("N", "north")), 0); + geographic_ref_layout->addWidget(lon_edit, 1); + geographic_ref_layout->addWidget(new QLabel(tr("E", "east")), 0); + if (geographic_datum_label_width < ref_point_button_width) + geographic_ref_layout->addSpacing(ref_point_button_width - geographic_datum_label_width); + geographic_ref_layout->addWidget(geographic_datum_label, 0); + + show_refpoint_label = new QLabel(tr("Show reference point in:")); + link_label = new QLabel(); + link_label->setOpenExternalLinks(true); + + keep_projected_radio = new QRadioButton(tr("Projected coordinates")); + keep_geographic_radio = new QRadioButton(tr("Geographic coordinates")); + if (georef->getState() == Georeferencing::Normal && georef->isValid()) + { + keep_geographic_radio->setChecked(true); + } + else + { + keep_geographic_radio->setEnabled(false); + keep_projected_radio->setCheckable(true); + } + + auto map_north_label = Util::Headline::create(tr("Map north")); + + declination_edit = Util::SpinBox::create(Georeferencing::declinationPrecision(), -180.0, +180.0, trUtf8("°")); + declination_button = new QPushButton(tr("Lookup...")); + auto declination_layout = new QHBoxLayout(); + declination_layout->addWidget(declination_edit, 1); + declination_layout->addWidget(declination_button, 0); + + grivation_label = new QLabel(); + + buttons_box = new QDialogButtonBox( + QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Reset | QDialogButtonBox::Help, + Qt::Horizontal); + reset_button = buttons_box->button(QDialogButtonBox::Reset); + reset_button->setEnabled(initial); + auto help_button = buttons_box->button(QDialogButtonBox::Help); + + auto edit_layout = new QFormLayout(); + + edit_layout->addRow(map_crs_label); + edit_layout->addRow(tr("&Coordinate reference system:"), crs_selector); + crs_selector->setDialogLayout(edit_layout); + edit_layout->addRow(status_label, status_field); + edit_layout->addRow(scale_factor_label, scale_factor_edit); + edit_layout->addItem(Util::SpacerItem::create(this)); + + edit_layout->addRow(reference_point_label); + edit_layout->addRow(tr("Map coordinates:"), map_ref_layout); + edit_layout->addRow(projected_ref_label, projected_ref_layout); + edit_layout->addRow(tr("Geographic coordinates:"), geographic_ref_layout); + edit_layout->addRow(show_refpoint_label, link_label); + edit_layout->addRow(show_refpoint_label, link_label); + edit_layout->addRow(tr("On CRS changes, keep:"), keep_projected_radio); + edit_layout->addRow({}, keep_geographic_radio); + edit_layout->addItem(Util::SpacerItem::create(this)); + + edit_layout->addRow(map_north_label); + edit_layout->addRow(tr("Declination:"), declination_layout); + edit_layout->addRow(tr("Grivation:"), grivation_label); + + auto layout = new QVBoxLayout(); + layout->addLayout(edit_layout); + layout->addStretch(); + layout->addSpacing(16); + layout->addWidget(buttons_box); + + setLayout(layout); + + connect(crs_selector, &CRSSelector::crsChanged, this, &GeoreferencingDialog::crsEdited); + + using TakingDoubleArgument = void (QDoubleSpinBox::*)(double); + connect(scale_factor_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::scaleFactorEdited); + + connect(map_x_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::mapRefChanged); + connect(map_y_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::mapRefChanged); + connect(ref_point_button, &QPushButton::clicked, this, &GeoreferencingDialog::selectMapRefPoint); + + connect(easting_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::eastingNorthingEdited); + connect(northing_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::eastingNorthingEdited); + + connect(lat_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::latLonEdited); + connect(lon_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::latLonEdited); + connect(keep_geographic_radio, &QRadioButton::toggled, this, &GeoreferencingDialog::keepCoordsChanged); + + connect(declination_edit, (TakingDoubleArgument)&QDoubleSpinBox::valueChanged, this, &GeoreferencingDialog::declinationEdited); + connect(declination_button, &QPushButton::clicked, this, &GeoreferencingDialog::requestDeclination); + + connect(buttons_box, &QDialogButtonBox::accepted, this, &GeoreferencingDialog::accept); + connect(buttons_box, &QDialogButtonBox::rejected, this, &GeoreferencingDialog::reject); + connect(reset_button, &QPushButton::clicked, this, &GeoreferencingDialog::reset); + connect(help_button, &QPushButton::clicked, this, &GeoreferencingDialog::showHelp); + + connect(georef.data(), &Georeferencing::stateChanged, this, &GeoreferencingDialog::georefStateChanged); + connect(georef.data(), &Georeferencing::transformationChanged, this, &GeoreferencingDialog::transformationChanged); + connect(georef.data(), &Georeferencing::projectionChanged, this, &GeoreferencingDialog::projectionChanged); + connect(georef.data(), &Georeferencing::declinationChanged, this, &GeoreferencingDialog::declinationChanged); + + transformationChanged(); + georefStateChanged(); + declinationChanged(); +} + +GeoreferencingDialog::~GeoreferencingDialog() +{ + if (tool_active) + controller->setOverrideTool(nullptr); +} + +// slot +void GeoreferencingDialog::georefStateChanged() +{ + const QSignalBlocker block(crs_selector); + + switch (georef->getState()) + { + case Georeferencing::Local: + crs_selector->setCurrentItem(Georeferencing::Local); + keep_geographic_radio->setEnabled(false); + keep_projected_radio->setChecked(true); + break; + default: + qDebug() << "Unhandled georeferencing state:" << georef->getState(); + // fall through + case Georeferencing::Normal: + projectionChanged(); + keep_geographic_radio->setEnabled(true); + } + + updateWidgets(); +} + +// slot +void GeoreferencingDialog::transformationChanged() +{ + ScopedMultiSignalsBlocker block( + map_x_edit, map_y_edit, + easting_edit, northing_edit, + scale_factor_edit + ); + + map_x_edit->setValue(georef->getMapRefPoint().x()); + map_y_edit->setValue(-1 * georef->getMapRefPoint().y()); + + easting_edit->setValue(georef->getProjectedRefPoint().x()); + northing_edit->setValue(georef->getProjectedRefPoint().y()); + + scale_factor_edit->setValue(georef->getGridScaleFactor()); + + updateGrivation(); +} + +// slot +void GeoreferencingDialog::projectionChanged() +{ + ScopedMultiSignalsBlocker block( + crs_selector, + lat_edit, lon_edit + ); + + if (georef->getState() == Georeferencing::Normal) + { + const std::vector< QString >& parameters = georef->getProjectedCRSParameters(); + auto temp = CRSTemplateRegistry().find(georef->getProjectedCRSId()); + if (!temp || temp->parameters().size() != parameters.size()) + { + // The CRS id is not there anymore or the number of parameters has changed. + // Enter as custom spec. + crs_selector->setCurrentCRS(CRSTemplateRegistry().find(QString::fromLatin1("PROJ.4")), { georef->getProjectedCRSSpec() }); + } + else + { + crs_selector->setCurrentCRS(temp, parameters); + } + } + + LatLon latlon = georef->getGeographicRefPoint(); + double latitude = latlon.latitude(); + double longitude = latlon.longitude(); + lat_edit->setValue(latitude); + lon_edit->setValue(longitude); + QString osm_link = + QString::fromLatin1("http://www.openstreetmap.org/?lat=%1&lon=%2&zoom=18&layers=M"). + arg(latitude).arg(longitude); + QString worldofo_link = + QString::fromLatin1("http://maps.worldofo.com/?zoom=15&lat=%1&lng=%2"). + arg(latitude).arg(longitude); + link_label->setText( + tr("OpenStreetMap | World of O Maps"). + arg(osm_link, worldofo_link) + ); + + QString error = georef->getErrorText(); + if (error.length() == 0) + status_field->setText(tr("valid")); + else + status_field->setText(QLatin1String("") + error + QLatin1String("")); +} + +// slot +void GeoreferencingDialog::declinationChanged() +{ + const QSignalBlocker block(declination_edit); + declination_edit->setValue(georef->getDeclination()); +} + +void GeoreferencingDialog::requestDeclination(bool no_confirm) +{ + if (georef->isLocal()) + return; + + /// \todo Move URL (template) to settings. + QString user_url(QString::fromLatin1("https://www.ngdc.noaa.gov/geomag-web/")); + QUrl service_url(user_url + QLatin1String("calculators/calculateDeclination")); + LatLon latlon(georef->getGeographicRefPoint()); + + if (!no_confirm) + { + int result = QMessageBox::question(this, tr("Online declination lookup"), + trUtf8("The magnetic declination for the reference point %1° %2° will now be retrieved from %3. Do you want to continue?"). + arg(latlon.latitude()).arg(latlon.longitude()).arg(user_url), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes ); + if (result != QMessageBox::Yes) + return; + } + + QUrlQuery query; + QDate today = QDate::currentDate(); + query.addQueryItem(QString::fromLatin1("lat1"), QString::number(latlon.latitude())); + query.addQueryItem(QString::fromLatin1("lon1"), QString::number(latlon.longitude())); + query.addQueryItem(QString::fromLatin1("startYear"), QString::number(today.year())); + query.addQueryItem(QString::fromLatin1("startMonth"), QString::number(today.month())); + query.addQueryItem(QString::fromLatin1("startDay"), QString::number(today.day())); + +#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) || defined(Q_OS_ANDROID) || !defined(QT_NETWORK_LIB) + // No QtNetwork or no OpenSSL: open result in system browser. + query.addQueryItem(QString::fromLatin1("resultFormat"), QString::fromLatin1("html")); + service_url.setQuery(query); + QDesktopServices::openUrl(service_url); +#else + // Use result directly + query.addQueryItem(QString::fromLatin1("resultFormat"), QString::fromLatin1("xml")); + service_url.setQuery(query); + + declination_query_in_progress = true; + updateDeclinationButton(); + + auto network = new QNetworkAccessManager(this); + connect(network, &QNetworkAccessManager::finished, this, &GeoreferencingDialog::declinationReplyFinished); + network->get(QNetworkRequest(service_url)); +#endif +} + +void GeoreferencingDialog::setMapRefPoint(MapCoord coords) +{ + georef->setMapRefPoint(coords); + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::setKeepProjectedRefCoords() +{ + keep_projected_radio->setChecked(true); + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::setKeepGeographicRefCoords() +{ + keep_geographic_radio->setChecked(true); + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::toolDeleted() +{ + tool_active = false; +} + +void GeoreferencingDialog::showHelp() +{ + Util::showHelp(parentWidget(), "georeferencing.html"); +} + +void GeoreferencingDialog::reset() +{ + grivation_locked = ( !initial_georef->isValid() || initial_georef->getState() != Georeferencing::Normal ); + if (!grivation_locked) + original_declination = initial_georef->getDeclination(); + *georef.data() = *initial_georef; + reset_button->setEnabled(false); +} + +void GeoreferencingDialog::accept() +{ + float declination_change_degrees = georef->getDeclination() - initial_georef->getDeclination(); + if ( !grivation_locked && + declination_change_degrees != 0 && + (map->getNumObjects() > 0 || map->getNumTemplates() > 0) ) + { + int result = QMessageBox::question(this, tr("Declination change"), tr("The declination has been changed. Do you want to rotate the map content accordingly, too?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + if (result == QMessageBox::Cancel) + { + return; + } + else if (result == QMessageBox::Yes) + { + RotateMapDialog dialog(this, map); + dialog.setWindowModality(Qt::WindowModal); + dialog.setRotationDegrees(declination_change_degrees); + dialog.setRotateAroundGeorefRefPoint(); + dialog.setAdjustDeclination(false); + dialog.showAdjustDeclination(false); + int result = dialog.exec(); + if (result == QDialog::Rejected) + return; + } + } + + map->setGeoreferencing(*georef); + QDialog::accept(); +} + +void GeoreferencingDialog::updateWidgets() +{ + ref_point_button->setEnabled(controller); + + if (crs_selector->currentCRSTemplate()) + projected_ref_label->setText(crs_selector->currentCRSTemplate()->coordinatesName(crs_selector->parameters()) + QLatin1Char(':')); + else + projected_ref_label->setText(tr("Local coordinates:")); + + bool geographic_coords_enabled = crs_selector->currentCustomItem() != Georeferencing::Local; + status_label->setVisible(geographic_coords_enabled); + status_field->setVisible(geographic_coords_enabled); + lat_edit->setEnabled(geographic_coords_enabled); + lon_edit->setEnabled(geographic_coords_enabled); + link_label->setEnabled(geographic_coords_enabled); + //keep_geographic_radio->setEnabled(geographic_coords_enabled); + + updateDeclinationButton(); + + buttons_box->button(QDialogButtonBox::Ok)->setEnabled(georef->isValid()); +} + +void GeoreferencingDialog::updateDeclinationButton() +{ + /* + bool dialog_enabled = crs_edit->getSelectedCustomItemId() != 0; + bool proj_spec_visible = crs_edit->getSelectedCustomItemId() == 1; + bool geographic_coords_enabled = + dialog_enabled && + (proj_spec_visible || + crs_edit->getSelectedCustomItemId() == -1); + */ + bool enabled = lat_edit->isEnabled() && !declination_query_in_progress; + declination_button->setEnabled(enabled); + declination_button->setText(declination_query_in_progress ? tr("Loading...") : tr("Lookup...")); +} + +void GeoreferencingDialog::updateGrivation() +{ + QString text = trUtf8("%1 °", "degree value").arg(QLocale().toString(georef->getGrivation(), 'f', Georeferencing::declinationPrecision())); + if (grivation_locked) + text.append(QString::fromLatin1(" (%1)").arg(tr("locked"))); + grivation_label->setText(text); +} + +void GeoreferencingDialog::crsEdited() +{ + Georeferencing georef_copy = *georef; + + auto crs_template = crs_selector->currentCRSTemplate(); + auto spec = crs_selector->currentCRSSpec(); + + auto selected_item_id = crs_selector->currentCustomItem(); + switch (selected_item_id) + { + default: + qWarning("Unsupported CRS item id"); + // fall through + case Georeferencing::Local: + // Local + georef_copy.setState(Georeferencing::Local); + break; + case -1: + // CRS from list + Q_ASSERT(crs_template); + georef_copy.setProjectedCRS(crs_template->id(), spec, crs_selector->parameters()); + georef_copy.setState(Georeferencing::Normal); // Allow invalid spec + if (keep_geographic_radio->isChecked()) + georef_copy.setGeographicRefPoint(georef->getGeographicRefPoint(), !grivation_locked); + else + georef_copy.setProjectedRefPoint(georef->getProjectedRefPoint(), !grivation_locked); + break; + } + + // Apply all changes at once + *georef = georef_copy; + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::scaleFactorEdited() +{ + const QSignalBlocker block{scale_factor_edit}; + georef->setGridScaleFactor(scale_factor_edit->value()); + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::selectMapRefPoint() +{ + if (controller) + { + controller->setOverrideTool(new GeoreferencingTool(this, controller)); + tool_active = true; + hide(); + } +} + +void GeoreferencingDialog::mapRefChanged() +{ + MapCoord coord(map_x_edit->value(), -1 * map_y_edit->value()); + setMapRefPoint(coord); +} + +void GeoreferencingDialog::eastingNorthingEdited() +{ + const QSignalBlocker block1(keep_geographic_radio), block2(keep_projected_radio); + double easting = easting_edit->value(); + double northing = northing_edit->value(); + georef->setProjectedRefPoint(QPointF(easting, northing), !grivation_locked); + keep_projected_radio->setChecked(true); + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::latLonEdited() +{ + const QSignalBlocker block1(keep_geographic_radio), block2(keep_projected_radio); + double latitude = lat_edit->value(); + double longitude = lon_edit->value(); + georef->setGeographicRefPoint(LatLon(latitude, longitude), !grivation_locked); + keep_geographic_radio->setChecked(true); + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::keepCoordsChanged() +{ + if (grivation_locked && keep_geographic_radio->isChecked()) + { + grivation_locked = false; + original_declination = georef->getDeclination(); + updateGrivation(); + } + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::declinationEdited(double value) +{ + if (grivation_locked) + { + grivation_locked = false; + original_declination = georef->getDeclination(); + updateGrivation(); + } + georef->setDeclination(value); + reset_button->setEnabled(true); +} + +void GeoreferencingDialog::declinationReplyFinished(QNetworkReply* reply) +{ +#if defined(QT_NETWORK_LIB) + declination_query_in_progress = false; + updateDeclinationButton(); + + QString error_string; + if (reply->error() != QNetworkReply::NoError) + { + error_string = reply->errorString(); + } + else + { + QXmlStreamReader xml(reply); + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("maggridresult")) + { + while(xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("result")) + { + while (xml.readNextStartElement()) + { + if (xml.name() == QLatin1String("declination")) + { + QString text = xml.readElementText(QXmlStreamReader::IncludeChildElements); + bool ok; + double declination = text.toDouble(&ok); + if (ok) + { + declination_edit->setValue(Georeferencing::roundDeclination(declination)); + return; + } + else + { + error_string = tr("Could not parse data.") + QLatin1Char(' '); + } + } + + xml.skipCurrentElement(); // child of result + } + } + + xml.skipCurrentElement(); // child of mapgridresult + } + } + else if (xml.name() == QLatin1String("errors")) + { + error_string.append(xml.readElementText(QXmlStreamReader::IncludeChildElements) + QLatin1Char(' ')); + } + + xml.skipCurrentElement(); // child of root + } + + if (xml.error() != QXmlStreamReader::NoError) + { + error_string.append(xml.errorString()); + } + else if (error_string.isEmpty()) + { + error_string = tr("Declination value not found."); + } + } + + int result = QMessageBox::critical(this, tr("Online declination lookup"), + tr("The online declination lookup failed:\n%1").arg(error_string), + QMessageBox::Retry | QMessageBox::Close, + QMessageBox::Close ); + if (result == QMessageBox::Retry) + requestDeclination(true); +#else + Q_UNUSED(reply) +#endif +} + + + +// ### GeoreferencingTool ### + +GeoreferencingTool::GeoreferencingTool(GeoreferencingDialog* dialog, MapEditorController* controller, QAction* action) + : MapEditorTool(controller, Other, action) + , dialog(dialog) +{ + // nothing +} + +GeoreferencingTool::~GeoreferencingTool() +{ + dialog->toolDeleted(); +} + +void GeoreferencingTool::init() +{ + setStatusBarText(tr("Click: Set the reference point. Right click: Cancel.")); + MapEditorTool::init(); +} + +bool GeoreferencingTool::mousePressEvent(QMouseEvent* event, MapCoordF /*map_coord*/, MapWidget* /*widget*/) +{ + bool handled = false; + switch (event->button()) + { + case Qt::LeftButton: + case Qt::RightButton: + handled = true; + break; + default: + ; // nothing + } + + return handled; +} + +bool GeoreferencingTool::mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* /*widget*/) +{ + bool handled = false; + switch (event->button()) + { + case Qt::LeftButton: + dialog->setMapRefPoint(MapCoord(map_coord)); + // fall through + case Qt::RightButton: + QTimer::singleShot(0, dialog, SIGNAL(exec())); // clazy:exclude=old-style-connect + handled = true; + break; + default: + ; // nothing + } + + return handled; +} + +const QCursor& GeoreferencingTool::getCursor() const +{ + static auto const cursor = scaledToScreen(QCursor{ QPixmap{ QString::fromLatin1(":/images/cursor-crosshair.png") }, 11, 11 }); + return cursor; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/georeferencing_dialog.h b/src/gui/georeferencing_dialog.h new file mode 100644 index 0000000..9f4b371 --- /dev/null +++ b/src/gui/georeferencing_dialog.h @@ -0,0 +1,333 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_GEOREFERENCING_DIALOG_H +#define OPENORIENTEERING_GEOREFERENCING_DIALOG_H + +#include +#include +#include + +#include "core/map_coord.h" +#include "tools/tool.h" + +class QAction; +class QCursor; +class QDialogButtonBox; +class QDoubleSpinBox; +class QLabel; +class QMouseEvent; +class QPushButton; +class QRadioButton; +class QNetworkReply; +class QWidget; + +namespace OpenOrienteering { + +class CRSSelector; +class Georeferencing; +class Map; +class MapEditorController; +class MapWidget; + + +/** + * A GeoreferencingDialog allows the user to adjust the georeferencing properties + * of a map. + */ +class GeoreferencingDialog : public QDialog +{ +Q_OBJECT +public: + /** + * Constructs a new georeferencing dialog for the map handled by the given + * controller. The optional parameter initial allows to override the current + * properties of the map's georeferencing. The parameter + * allow_no_georeferencing determines if the okay button can + * be clicked while "- none -" is selected. + */ + GeoreferencingDialog(MapEditorController* controller, const Georeferencing* initial = nullptr, bool allow_no_georeferencing = true); + + /** + * Constructs a new georeferencing dialog for the given map. The optional + * parameter initial allows to override the current properties of the map's + * georeferencing. Since the dialog will not know a MapEditorController, + * it will not allow to select a new reference point from the map. + * The parameter allow_no_georeferencing determines if the okay button can + * be clicked while "- none -" is selected. + */ + GeoreferencingDialog(QWidget* parent, Map* map, const Georeferencing* initial = nullptr, bool allow_no_georeferencing = true); + +protected: + /** + * Constructs a new georeferencing dialog. + * + * The map parameter must not be nullptr, and it must not be a different + * map than the one handled by controller. + * + * @param parent A parent widget. + * @param controller A controller which operates on the map. + * @param map The map. + * @param initial An override of the map's georeferencing + * @param allow_no_georeferencing Determines if the okay button can be + * be clicked while "- none -" is selected. + */ + GeoreferencingDialog( + QWidget* parent, + MapEditorController* controller, + Map* map, + const Georeferencing* initial, + bool allow_no_georeferencing + ); + +public: + /** + * Releases resources. + */ + ~GeoreferencingDialog() override; + + + /** + * Updates the dialog from georeferencing state changes. + */ + void georefStateChanged(); + + /** + * Moves transformation properties from the georeferencing to the widgets. + */ + void transformationChanged(); + + /** + * Moves projection properties from the georeferencing to the widgets. + */ + void projectionChanged(); + + /** + * Updates the declination widget from the georeferencing. + */ + void declinationChanged(); + + /** + * Triggers an online request for the magnetic declination. + * + * @param no_confirm If true, the user will not be asked for confirmation. + */ + void requestDeclination(bool no_confirm = false); + + /** + * Sets the map coordinates of the reference point + */ + void setMapRefPoint(MapCoord coords); + + /** + * Activates the "keep projected reference point coordinates on CRS changes" radio button. + */ + void setKeepProjectedRefCoords(); + + /** + * Activates the "keep geographic reference point coordinates on CRS changes" radio button. + */ + void setKeepGeographicRefCoords(); + + /** + * Notifies the dialog that the active GeoreferencingTool was deleted. + */ + void toolDeleted(); + + /** + * Opens this dialog's help page. + */ + void showHelp(); + + /** + * Resets all input fields to the values in the map's Georeferencing. + * + * This will also reset initial values passed to the constructor. + */ + void reset(); + + /** + * Pushes the changes from the dialog to the map's Georeferencing + * and closes the dialog. The dialog's result is set to QDialog::Accepted, + * and the active exec() function will return. + */ + void accept() override; + +protected: + /** + * Updates enabled / disabled states of all widgets. + */ + void updateWidgets(); + + /** + * Updates enabled / disabled state and text of the declination query button. + */ + void updateDeclinationButton(); + + + /** + * Notifies the dialog of a change in the CRS configuration. + */ + void crsEdited(); + + /** + * Notifies the dialog of a change in the grid scale factor. + */ + void scaleFactorEdited(); + + /** + * Hides the dialog and activates a GeoreferencingTool for selecting + * the reference point on the map. + */ + void selectMapRefPoint(); + + /** + * Notifies the dialog of a change in the map reference point fields. + */ + void mapRefChanged(); + + /** + * Notifies the dialog of a change in the easting / northing fields. + */ + void eastingNorthingEdited(); + + /** + * Notifies the dialog of change of the keep-coords buttons. + */ + void keepCoordsChanged(); + + /** + * Notifies the dialog of a change in the latitude / longitude fields. + */ + void latLonEdited(); + + /** + * Notifies the dialog of a change in the declination field. + */ + void declinationEdited(double value); + + /** + * Handles replies from the online declination service. + */ + void declinationReplyFinished(QNetworkReply* reply); + + /** + * Updates the grivation field from the underlying Georeferencing. + */ + void updateGrivation(); + +private: + /* Internal state */ + MapEditorController* const controller; + Map* const map; + const Georeferencing* initial_georef; + QScopedPointer georef; // A working copy of the current or given initial Georeferencing + bool allow_no_georeferencing; + bool tool_active; + bool declination_query_in_progress; + bool grivation_locked; + double original_declination; + + /* GUI elements */ + CRSSelector* crs_selector; + QLabel* status_label; + QLabel* status_field; + QDoubleSpinBox* scale_factor_edit; + + QDoubleSpinBox* map_x_edit; + QDoubleSpinBox* map_y_edit; + QPushButton* ref_point_button; + + QLabel* projected_ref_label; + QDoubleSpinBox* easting_edit; + QDoubleSpinBox* northing_edit; + + QDoubleSpinBox* lat_edit; + QDoubleSpinBox* lon_edit; + QLabel* show_refpoint_label; + QLabel* link_label; + + QRadioButton* keep_projected_radio; + QRadioButton* keep_geographic_radio; + + QDoubleSpinBox* declination_edit; + QPushButton* declination_button; + QLabel* grivation_label; + + QDialogButtonBox* buttons_box; + QPushButton* reset_button; +}; + + + +/** + * GeoreferencingTool is a helper to the GeoreferencingDialog which allows + * the user to select the position of the reference point on the map + * The GeoreferencingDialog hides when it activates this tool. The tool + * takes care of reactivating the dialog. + */ +class GeoreferencingTool : public MapEditorTool +{ +Q_OBJECT +public: + /** + * Constructs a new tool for the given dialog and controller. + */ + GeoreferencingTool( + GeoreferencingDialog* dialog, + MapEditorController* controller, + QAction* action = nullptr + ); + + /** + * Notifies the dialog that the tool is deleted. + */ + ~GeoreferencingTool() override; + + /** + * Activates the tool. + */ + void init() override; + + /** + * Consumes left and right clicks. They are handled in mouseReleaseEvent. + */ + bool mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; + + /** + * Reacts to the user activity by sending the reference point coordinates + * to the dialog (on left click) and reactivating the dialog. + */ + bool mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; + + /** + * Returns the mouse cursor that will be shown when the tool is active. + */ + const QCursor& getCursor() const override; + +private: + GeoreferencingDialog* const dialog; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/home_screen_controller.cpp b/src/gui/home_screen_controller.cpp new file mode 100644 index 0000000..7410dee --- /dev/null +++ b/src/gui/home_screen_controller.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2012, 2013 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "home_screen_controller.h" + +#include +#include + +#include "settings.h" +#include "gui/main_window.h" +#include "gui/widgets/home_screen_widget.h" + + +namespace OpenOrienteering { + +HomeScreenController::HomeScreenController() +: widget(nullptr) +, current_tip(-1) +{ + // nothing +} + +HomeScreenController::~HomeScreenController() = default; + + +void HomeScreenController::attach(MainWindow* window) +{ + this->window = window; + + if (MainWindow::mobileMode()) + { + widget = new HomeScreenWidgetMobile(this); + } + else + { + widget = new HomeScreenWidgetDesktop(this); + window->statusBar()->hide(); + window->setStatusBarText(QString{}); + } + + window->setCentralWidget(widget); + + connect(&Settings::getInstance(), &Settings::settingsChanged, this, &HomeScreenController::readSettings); + + readSettings(); +} + +void HomeScreenController::detach() +{ + if (!MainWindow::mobileMode()) + { + window->statusBar()->show(); + } + window->setCentralWidget(nullptr); + widget->deleteLater(); + + Settings::getInstance().setSetting(Settings::HomeScreen_CurrentTip, current_tip); +} + +void HomeScreenController::readSettings() +{ + Settings& settings = Settings::getInstance(); // FIXME: settings should be const + + widget->setRecentFiles(settings.getSettingCached(Settings::General_RecentFilesList).toStringList()); + widget->setOpenMRUFileChecked(settings.getSettingCached(Settings::General_OpenMRUFile).toBool()); + + bool tips_visible = settings.getSettingCached(Settings::HomeScreen_TipsVisible).toBool(); + widget->setTipsVisible(tips_visible); + if (tips_visible) + { + if (current_tip < 0) + { + current_tip = settings.getSettingCached(Settings::HomeScreen_CurrentTip).toInt(); + goToNextTip(); + } + else + { + // Settings changed. + goToTip(current_tip); + } + } +} + +void HomeScreenController::setOpenMRUFile(bool state) +{ + Settings::getInstance().setSetting(Settings::General_OpenMRUFile, state); +} + +void HomeScreenController::clearRecentFiles() +{ + Settings::getInstance().remove(Settings::General_RecentFilesList); +} + +void HomeScreenController::setTipsVisible(bool state) +{ + Settings::getInstance().setSetting(Settings::HomeScreen_TipsVisible, state); +} + +void HomeScreenController::goToNextTip() +{ + goToTip(current_tip + 1); +} + +void HomeScreenController::goToPreviousTip() +{ + goToTip(current_tip - 1); +} + +void HomeScreenController::goToTip(int index) +{ + static QStringList tips; + if (tips.isEmpty()) + { + // Normally, this will be read only once. + QFile file(QString::fromLatin1("doc:tip-of-the-day/tips.txt")); + if (file.open(QIODevice::ReadOnly)) + { + while (!file.atEnd()) + { + QString tip(QString::fromUtf8(file.readLine().constData())); + if (tip.endsWith(QLatin1Char('\n'))) + tip.chop(1); + if (!tip.isEmpty()) + tips.push_back(tip); + } + } + } + + if (tips.isEmpty()) + { + // Some error may have occurred during reading the tips file. + // Display a welcome text. + widget->setTipOfTheDay(QString::fromLatin1("

    %1

    ").arg(tr("Welcome to OpenOrienteering Mapper!"))); + } + else + { + Q_ASSERT(tips.count() > 0); + while (index < 0) + index += tips.count(); + current_tip = index % tips.count(); + widget->setTipOfTheDay(tips[current_tip]); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/home_screen_controller.h b/src/gui/home_screen_controller.h new file mode 100644 index 0000000..9ca6dba --- /dev/null +++ b/src/gui/home_screen_controller.h @@ -0,0 +1,88 @@ +/* + * Copyright 2012, 2013 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_HOME_SCREEN_CONTROLLER_H +#define OPENORIENTEERING_HOME_SCREEN_CONTROLLER_H + +#include "main_window_controller.h" + +#include + +namespace OpenOrienteering { + +class AbstractHomeScreenWidget; +class MainWindow; + + +/** + * The controller of the OpenOrienteering Mapper home screen. + * The OpenOrienteering Mapper home screen is shown when no document is open, + * for example after the program is started for the first time. + */ +class HomeScreenController : public MainWindowController +{ +Q_OBJECT +public: + /** Creates a new HomeScreenController. */ + HomeScreenController(); + + /** Destroys the HomeScreenController and its children. */ + ~HomeScreenController() override; + + /** Activates the HomeScreenController for the given main window. */ + void attach(MainWindow* window) override; + + /** Detaches the HomeScreenController from its main window. */ + void detach() override; + +public slots: + /** (Re-)reads the settings. */ + void readSettings(); + + /** Clears the application's list of recently opened files. */ + void clearRecentFiles(); + + /** Sets whether to open the most recently used file on startup. */ + void setOpenMRUFile(bool state); + + /** Sets the visiblity of the tip-of-the-day to state. */ + void setTipsVisible(bool state); + + /** Moves to the tip following the current tip-of-the-day. */ + void goToPreviousTip(); + + /** Moves to the tip preceding the current tip-of-the-day. */ + void goToNextTip(); + + /** Moves to the tip-of-the-day given by index. */ + void goToTip(int index); + +protected: + /** The widget owned and controlled by this HomeScreenController. */ + AbstractHomeScreenWidget* widget; + + /** The index of the tip-of-the-day currently displayed. */ + int current_tip; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/main_window.cpp b/src/gui/main_window.cpp new file mode 100644 index 0000000..241236f --- /dev/null +++ b/src/gui/main_window.cpp @@ -0,0 +1,1256 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps + * Copyright 2012-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "main_window.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_OS_ANDROID) +# include +# include +# include +# include +# include +#endif + +#include + +#include "settings.h" +#include "core/map.h" +#include "core/map_view.h" +#include "core/symbols/symbol.h" +#include "fileformats/file_format.h" +#include "fileformats/file_format_registry.h" +#include "gui/about_dialog.h" +#include "gui/autosave_dialog.h" +#include "gui/file_dialog.h" +#include "gui/home_screen_controller.h" +#include "gui/settings_dialog.h" +#include "gui/util_gui.h" +#include "gui/map/map_editor.h" +#include "gui/map/new_map_dialog.h" +#include "undo/undo_manager.h" +#include "util/util.h" +#include "util/backports.h" + + +namespace OpenOrienteering { + +constexpr int MainWindow::max_recent_files; + +int MainWindow::num_open_files = 0; + +MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) +: MainWindow { true, parent, flags } +{ + // nothing else +} + +MainWindow::MainWindow(bool as_main_window, QWidget* parent, Qt::WindowFlags flags) +: QMainWindow { parent, flags } +, controller { nullptr } +, create_menu { as_main_window } +, show_menu { create_menu && !mobileMode() } +, shortcuts_blocked { false } +, general_toolbar { nullptr } +, file_menu { nullptr } +, has_opened_file { false } +, has_unsaved_changes { false } +, has_autosave_conflict { false } +, maximized_before_fullscreen { false } +, homescreen_disabled { false } +{ + setWindowIcon(QIcon(QString::fromLatin1(":/images/mapper.png"))); + setAttribute(Qt::WA_DeleteOnClose); + + status_label = new QLabel(); + statusBar()->addWidget(status_label, 1); + statusBar()->setSizeGripEnabled(as_main_window); + if (mobileMode()) + statusBar()->hide(); + + central_widget = new QStackedWidget(this); + QMainWindow::setCentralWidget(central_widget); + + if (as_main_window) + loadWindowSettings(); + +#if defined(Q_OS_ANDROID) + // Needed to catch Qt::Key_Back, cf. MainWindow::eventFilter() + qApp->installEventFilter(this); +#else + installEventFilter(this); +#endif + + connect(&Settings::getInstance(), &Settings::settingsChanged, this, &MainWindow::settingsChanged); + connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::applicationStateChanged); +} + + + +MainWindow::~MainWindow() +{ + if (controller) + { + controller->detach(); + delete controller; + delete general_toolbar; + } +} + +void MainWindow::settingsChanged() +{ + updateRecentFileActions(); +} + + + +void MainWindow::applicationStateChanged() +{ +#ifdef Q_OS_ANDROID + // The Android app may be started or resumed when the user triggers a suitable "intent". + if (QGuiApplication::applicationState() == Qt::ApplicationActive) + { + auto activity = QtAndroid::androidActivity(); + auto intent_path = activity.callObjectMethod("takeIntentPath").toString(); + if (!intent_path.isEmpty()) + { + const auto local_file = QUrl(intent_path).toLocalFile(); + if (!hasOpenedFile()) + { + openPathLater(local_file); + } + else if (currentPath() != local_file) + { + showStatusBarMessage(tr("You must close the current file before you can open another one.")); + } + return; + } + } +#endif + + // Only on startup, we may need to load the most recently used file. + static bool starting_up = true; + if (starting_up) + { + starting_up = false; + QSettings settings; + if (path_backlog.isEmpty() + && settings.value(QLatin1String("openMRUFile")).toBool()) + { + const auto files = settings.value(QLatin1String("recentFileList")).toStringList(); + if (!files.isEmpty()) + openPathLater(files[0]); + } + } +} + + + +QString MainWindow::appName() const +{ + return APP_NAME; +} + +#ifndef Q_OS_ANDROID +bool MainWindow::mobileMode() +{ + static bool mobile_mode = qEnvironmentVariableIsSet("MAPPER_MOBILE_GUI") + ? (qgetenv("MAPPER_MOBILE_GUI") != "0") + : 0; + return mobile_mode; +} +#endif + +void MainWindow::setCentralWidget(QWidget* widget) +{ + if (widget) + { + // Main window shall not resize to central widget size hint. + widget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + + int index = central_widget->addWidget(widget); + central_widget->setCurrentIndex(index); + } + + if (central_widget->count() > 1) + { + QWidget* w = central_widget->widget(0); + central_widget->removeWidget(w); + w->deleteLater(); + } +} + +void MainWindow::setHomeScreenDisabled(bool disabled) +{ + homescreen_disabled = disabled; +} + +void MainWindow::setController(MainWindowController* new_controller) +{ + setController(new_controller, false); + setCurrentPath({}); +} + +void MainWindow::setController(MainWindowController* new_controller, const QString& path) +{ + setController(new_controller, true); + setCurrentPath(path); +} + +void MainWindow::setController(MainWindowController* new_controller, bool has_file) +{ + if (controller) + { + controller->detach(); + delete controller; + controller = nullptr; + + if (show_menu) + menuBar()->clear(); + delete general_toolbar; + general_toolbar = nullptr; + } + + has_opened_file = has_file; + shortcuts_blocked = false; + + if (create_menu) + createFileMenu(); + + controller = new_controller; + controller->attach(this); + + if (create_menu) + createHelpMenu(); + +#if defined(Q_OS_MACOS) + if (isVisible() && qApp->activeWindow() == this) + { + // Force a menu synchronisation, + // QCocoaMenuBar::updateMenuBarImmediately(), + // via QCocoaNativeInterface::onAppFocusWindowChanged(). + /// \todo Review in Qt > 5.6 + qApp->focusWindowChanged(qApp->focusWindow()); + } + +# if defined(MAPPER_DEVELOPMENT_BUILD) + { + // Qt's menu text heuristic can assign unexpected platform specific roles, + // which resulted in Mapper issue #1067. The only supported solution is + // assigning QAction::NoRole) before adding items to the menubar. + // Cf. QTBUG-30812. + // However, the heuristic is required for some platform-specific items. + // Cf. detectMenuRole() in qtbase/src/plugins/platforms/cocoa/messages.cpp + const auto platform_keywords = { + QCoreApplication::translate("QCocoaMenuItem", "Cut"), + QCoreApplication::translate("QCocoaMenuItem", "Copy"), + QCoreApplication::translate("QCocoaMenuItem", "Paste"), + QCoreApplication::translate("QCocoaMenuItem", "Select All") + }; + const auto menubar_actions = menuBar()->actions(); + for (auto menubar_action : menubar_actions) + { + if (const auto menu = menubar_action->menu()) + { + const auto menu_actions = menu->actions(); + for (auto action : menu_actions) + { + if (action->menuRole() != QAction::TextHeuristicRole + || action->isSeparator()) + continue; + const auto text = action->text().remove(QLatin1Char('&')); + if (std::none_of(begin(platform_keywords), end(platform_keywords), [&text](const auto& keyword) { + return keyword.compare(text, Qt::CaseInsensitive) == 0; + })) + { + // Such warnings may indiciate missing setting of QAction::NoRole + // on a (new) item, or incomplete translations for Mapper or Qt. + qDebug("Unexpected TextHeuristicRole for \"%s > %s\"", + qUtf8Printable(menubar_action->text()), + qUtf8Printable(action->text())); + } + } + } + } + } +# endif // MAPPER_DEVELOPMENT_BUILD +#endif // Q_OS_MACOS + + setHasAutosaveConflict(false); + setHasUnsavedChanges(false); +} + +void MainWindow::createFileMenu() +{ + QAction* new_act = new QAction(QIcon(QString::fromLatin1(":/images/new.png")), tr("&New"), this); + new_act->setMenuRole(QAction::NoRole); + new_act->setShortcuts(QKeySequence::New); + new_act->setStatusTip(tr("Create a new map")); + new_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); + connect(new_act, &QAction::triggered, this, &MainWindow::showNewMapWizard); + + QAction* open_act = new QAction(QIcon(QString::fromLatin1(":/images/open.png")), tr("&Open..."), this); + open_act->setMenuRole(QAction::NoRole); + open_act->setShortcuts(QKeySequence::Open); + open_act->setStatusTip(tr("Open an existing file")); + open_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); + connect(open_act, &QAction::triggered, this, &MainWindow::showOpenDialog); + + open_recent_menu = new QMenu(tr("Open &recent"), this); + open_recent_menu->menuAction()->setMenuRole(QAction::NoRole); + open_recent_menu->setWhatsThis(Util::makeWhatThis("file_menu.html")); + for (auto& action : recent_file_act) + { + action = new QAction(this); + connect(action, &QAction::triggered, this, &MainWindow::openRecentFile); + } + open_recent_menu_inserted = false; + + // NOTE: if you insert something between open_recent_menu and save_act, adjust updateRecentFileActions()! + + save_act = new QAction(QIcon(QString::fromLatin1(":/images/save.png")), tr("&Save"), this); + save_act->setMenuRole(QAction::NoRole); + save_act->setShortcuts(QKeySequence::Save); + save_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); + connect(save_act, &QAction::triggered, this, &MainWindow::save); + + auto save_as_act = new QAction(tr("Save &as..."), this); + save_as_act->setMenuRole(QAction::NoRole); + if (QKeySequence::keyBindings(QKeySequence::SaveAs).empty()) + save_as_act->setShortcut(tr("Ctrl+Shift+S")); + else + save_as_act->setShortcuts(QKeySequence::SaveAs); + save_as_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); + connect(save_as_act, &QAction::triggered, this, &MainWindow::showSaveAsDialog); + + settings_act = new QAction(tr("Settings..."), this); + settings_act->setMenuRole(QAction::PreferencesRole); + settings_act->setShortcut(QKeySequence::Preferences); + connect(settings_act, &QAction::triggered, this, &MainWindow::showSettings); + + close_act = new QAction(QIcon(QString::fromLatin1(":/images/close.png")), tr("Close"), this); + close_act->setMenuRole(QAction::NoRole); + close_act->setShortcut(QKeySequence::Close); + close_act->setStatusTip(tr("Close this file")); + close_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); + connect(close_act, &QAction::triggered, this, &MainWindow::closeFile); + + QAction* exit_act = new QAction(tr("E&xit"), this); + exit_act->setMenuRole(QAction::QuitRole); + exit_act->setShortcuts(QKeySequence::Quit); + exit_act->setStatusTip(tr("Exit the application")); + exit_act->setWhatsThis(Util::makeWhatThis("file_menu.html")); + connect(exit_act, &QAction::triggered, qApp, &QApplication::closeAllWindows); + + if (show_menu) + { + file_menu = menuBar()->addMenu(tr("&File")); + } + else + { + delete file_menu; + file_menu = new QMenu(this); + } + + file_menu->setWhatsThis(Util::makeWhatThis("file_menu.html")); + file_menu->addAction(new_act); + file_menu->addAction(open_act); + file_menu->addAction(save_act); + file_menu->addAction(save_as_act); + file_menu->addSeparator(); + file_menu->addAction(settings_act); + file_menu->addSeparator(); + file_menu->addAction(close_act); + file_menu->addAction(exit_act); + + general_toolbar = new QToolBar(tr("General")); + general_toolbar->setObjectName(QString::fromLatin1("General toolbar")); + general_toolbar->addAction(new_act); + general_toolbar->addAction(open_act); + general_toolbar->addAction(save_act); + + save_act->setEnabled(has_opened_file); + save_as_act->setEnabled(has_opened_file); + close_act->setEnabled(has_opened_file); + updateRecentFileActions(); +} + +void MainWindow::createHelpMenu() +{ + // Help menu + QAction* manualAct = new QAction(QIcon(QString::fromLatin1(":/images/help.png")), tr("Open &Manual"), this); + manualAct->setMenuRole(QAction::NoRole); + manualAct->setStatusTip(tr("Show the help file for this application")); + manualAct->setShortcut(QKeySequence::HelpContents); + connect(manualAct, &QAction::triggered, this, &MainWindow::showHelp); + + QAction* aboutAct = new QAction(tr("&About %1").arg(appName()), this); + aboutAct->setMenuRole(QAction::AboutRole); + aboutAct->setStatusTip(tr("Show information about this application")); + connect(aboutAct, &QAction::triggered, this, &MainWindow::showAbout); + + QAction* aboutQtAct = new QAction(tr("About &Qt"), this); + aboutQtAct->setMenuRole(QAction::AboutQtRole); + aboutQtAct->setStatusTip(tr("Show information about Qt")); + connect(aboutQtAct, &QAction::triggered, qApp, QApplication::aboutQt); + + if (show_menu) + { + QMenu* helpMenu = menuBar()->addMenu(tr("&Help")); + helpMenu->addAction(manualAct); + helpMenu->addAction([this] { + auto action = QWhatsThis::createAction(this); + action->setMenuRole(QAction::NoRole); + return action; + }()); + helpMenu->addSeparator(); + helpMenu->addAction(aboutAct); + helpMenu->addAction(aboutQtAct); + } +} + +void MainWindow::setCurrentPath(const QString& path) +{ + Q_ASSERT(has_opened_file || path.isEmpty()); + + if (path != current_path) + { + QString window_file_path; + current_path.clear(); + if (has_opened_file) + { + window_file_path = QFileInfo(path).canonicalFilePath(); + if (window_file_path.isEmpty()) + window_file_path = tr("Unsaved file"); + else + current_path = window_file_path; + } + setWindowFilePath(window_file_path); + } + else if (!windowFilePath().isEmpty() && !has_opened_file) + { + setWindowFilePath({}); + } +} + +void MainWindow::setMostRecentlyUsedFile(const QString& path) +{ + if (!path.isEmpty()) + { + Settings& settings = Settings::getInstance(); + + // Update least recently used directory + const QString open_directory = QFileInfo(path).canonicalPath(); + QSettings().setValue(QString::fromLatin1("openFileDirectory"), open_directory); + + // Update recent file lists + QStringList files = settings.getSettingCached(Settings::General_RecentFilesList).toStringList(); + files.removeAll(path); + files.prepend(path); + if (files.size() > max_recent_files) + files.erase(files.begin() + max_recent_files, files.end()); + settings.setSetting(Settings::General_RecentFilesList, files); + } +} + +void MainWindow::setHasUnsavedChanges(bool value) +{ + if (hasOpenedFile()) + { + has_unsaved_changes = value; + setAutosaveNeeded(has_unsaved_changes && !has_autosave_conflict); + } + setWindowModified(has_unsaved_changes); +} + +void MainWindow::setStatusBarText(const QString& text) +{ + status_label->setText(text); + status_label->setToolTip(text); +} + +void MainWindow::showStatusBarMessage(const QString& text, int timeout) +{ +#if defined(Q_OS_ANDROID) + QAndroidJniObject java_string = QAndroidJniObject::fromString(text); + QAndroidJniObject::callStaticMethod( + "org/openorienteering/mapper/MapperActivity", + "showToast", + "(Ljava/lang/String;I)V", + java_string.object(), timeout); +#else + statusBar()->showMessage(text, timeout); +#endif +} + +void MainWindow::clearStatusBarMessage() +{ +#if defined(Q_OS_ANDROID) + QAndroidJniObject::callStaticMethod( + "org/openorienteering/mapper/MapperActivity", + "hideToast", + "()V"); +#else + statusBar()->clearMessage(); +#endif +} + +void MainWindow::setShortcutsBlocked(bool blocked) +{ + shortcuts_blocked = blocked; +} + +bool MainWindow::closeFile() +{ + bool closed = !has_opened_file || showSaveOnCloseDialog(); + if (closed) + { + if (has_opened_file) + { + num_open_files--; + has_opened_file = false; + } + if (homescreen_disabled || num_open_files > 0) + close(); + else + setController(new HomeScreenController()); + } + return closed; +} + +bool MainWindow::event(QEvent* event) +{ + if (event->type() == QEvent::ShortcutOverride && shortcutsBlocked()) + event->accept(); + + return QMainWindow::event(event); +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + if (!has_opened_file) + { + saveWindowSettings(); + event->accept(); + } + else if (showSaveOnCloseDialog()) + { + if (has_opened_file) + { + num_open_files--; + has_opened_file = false; + } + saveWindowSettings(); + event->accept(); + } + else + { + event->ignore(); + } +} + +void MainWindow::keyPressEvent(QKeyEvent* event) +{ + if (controller && controller->keyPressEventFilter(event)) + { + // Event filtered, stop handling + return; + } + + QMainWindow::keyPressEvent(event); +} + +void MainWindow::keyReleaseEvent(QKeyEvent* event) +{ + if (controller && controller->keyReleaseEventFilter(event)) + { + // Event filtered, stop handling + return; + } + + QMainWindow::keyReleaseEvent(event); +} + +bool MainWindow::showSaveOnCloseDialog() +{ + if (has_opened_file && (has_unsaved_changes || has_autosave_conflict)) + { + // Show the window in case it is minimized + setWindowState( (windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + raise(); + activateWindow(); + + QMessageBox::StandardButton ret; + if (!has_unsaved_changes && actual_path != autosavePath(currentPath())) + { + ret = QMessageBox::warning(this, appName(), + tr("Do you want to remove the autosaved version?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + } + else + { + ret = QMessageBox::warning(this, appName(), + tr("The file has been modified.\n" + "Do you want to save your changes?"), + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + } + + switch (ret) + { + case QMessageBox::Cancel: + return false; + + case QMessageBox::Discard: + if (has_autosave_conflict) + setHasAutosaveConflict(false); + else + removeAutosaveFile(); + break; + + case QMessageBox::Save: + if (!save()) + return false; + // fall through + + case QMessageBox::Yes: + setHasAutosaveConflict(false); + removeAutosaveFile(); + break; + + case QMessageBox::No: + setHasAutosaveConflict(false); + break; + + default: + qWarning("Unsupported return value from message box"); + break; + } + + } + + return true; +} + +void MainWindow::saveWindowSettings() +{ +#if !defined(Q_OS_ANDROID) + QSettings settings; + + settings.beginGroup(QString::fromLatin1("MainWindow")); + settings.setValue(QString::fromLatin1("pos"), pos()); + settings.setValue(QString::fromLatin1("size"), size()); + settings.setValue(QString::fromLatin1("maximized"), isMaximized()); + settings.endGroup(); +#endif +} + +void MainWindow::loadWindowSettings() +{ +#if defined(Q_OS_ANDROID) + // Always show the window on the whole available area on Android + resize(QApplication::desktop()->availableGeometry().size()); +#else + QSettings settings; + + settings.beginGroup(QString::fromLatin1("MainWindow")); + QPoint pos = settings.value(QString::fromLatin1("pos"), QPoint(100, 100)).toPoint(); + QSize size = settings.value(QString::fromLatin1("size"), QSize(800, 600)).toSize(); + bool maximized = settings.value(QString::fromLatin1("maximized"), false).toBool(); + settings.endGroup(); + + move(pos); + resize(size); + if (maximized) + setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) + | Qt::WindowMaximized); // Cf. QWidget::showMaximized() +#endif +} + +MainWindow* MainWindow::findMainWindow(const QString& file_name) +{ + QString canonical_file_path = QFileInfo(file_name).canonicalFilePath(); + if (canonical_file_path.isEmpty()) + return nullptr; + + const auto top_level_widgets = qApp->topLevelWidgets(); + for (auto widget : top_level_widgets) + { + MainWindow* other = qobject_cast(widget); + if (other && other->currentPath() == canonical_file_path) + return other; + } + + return nullptr; +} + +void MainWindow::showNewMapWizard() +{ + NewMapDialog newMapDialog(this); + newMapDialog.setWindowModality(Qt::WindowModal); + newMapDialog.exec(); + + if (newMapDialog.result() == QDialog::Rejected) + return; + + Map* new_map = new Map(); + MapView tmp_view { nullptr, new_map }; + QString symbol_set_path = newMapDialog.getSelectedSymbolSetPath(); + if (symbol_set_path.isEmpty()) + { + new_map->setScaleDenominator(newMapDialog.getSelectedScale()); + } + else + { + new_map->loadFrom(symbol_set_path, this, &tmp_view, true); + if (new_map->getScaleDenominator() != newMapDialog.getSelectedScale()) + { + if (QMessageBox::question(this, tr("Warning"), tr("The selected map scale is 1:%1, but the chosen symbol set has a nominal scale of 1:%2.\n\nDo you want to scale the symbols to the selected scale?").arg(newMapDialog.getSelectedScale()).arg(new_map->getScaleDenominator()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + { + double factor = double(new_map->getScaleDenominator()) / newMapDialog.getSelectedScale(); + new_map->scaleAllSymbols(factor); + } + + new_map->setScaleDenominator(newMapDialog.getSelectedScale()); + } + + for (int i = new_map->getNumSymbols(); i > 0; i = qMin(i, new_map->getNumSymbols())) + { + --i; + auto symbol = new_map->getSymbol(i); + if (symbol->isHidden() + && !new_map->existsObjectWithSymbol(symbol)) + { + new_map->deleteSymbol(i); + } + } + } + + auto map_view = new MapView { new_map }; + map_view->setGridVisible(tmp_view.isGridVisible()); + + new_map->setHasUnsavedChanges(false); + new_map->undoManager().clear(); + + MainWindow* new_window = hasOpenedFile() ? new MainWindow() : this; + new_window->setWindowFilePath(tr("Unsaved file")); + new_window->setController(new MapEditorController(MapEditorController::MapEditor, new_map, map_view), QString()); + + new_window->show(); + new_window->raise(); + new_window->activateWindow(); + num_open_files++; +} + +void MainWindow::showOpenDialog() +{ + QString path = getOpenFileName(this, tr("Open file"), FileFormat::AllFiles); + if (!path.isEmpty()) + openPath(path); +} + +bool MainWindow::openPath(const QString &path) +{ + // Empty path does nothing. This also helps with the single instance application code. + if (path.isEmpty()) + return true; + +#ifdef Q_OS_ANDROID + showStatusBarMessage(tr("Opening %1").arg(QFileInfo(path).fileName())); +#else + MainWindow* const existing = findMainWindow(path); + if (existing) + { + existing->show(); + existing->raise(); + existing->activateWindow(); + return true; + } +#endif + + // Check a blocker that prevents immediate re-opening of crashing files. + // Needed for stopping auto-loading a crashing file on startup. + static const QString reopen_blocker = QString::fromLatin1("open_in_progress"); + QSettings settings; + const QString open_in_progress(settings.value(reopen_blocker).toString()); + if (open_in_progress == path) + { + int result = QMessageBox::warning(this, tr("Crash warning"), + tr("It seems that %1 crashed the last time this file was opened:
    " + "%2

    " + "Really retry to open it?") + .arg(appName(), path), + QMessageBox::Yes | QMessageBox::No); + settings.remove(reopen_blocker); + if (result == QMessageBox::No) + return false; + } + + settings.setValue(reopen_blocker, path); + settings.sync(); + + MainWindowController* const new_controller = MainWindowController::controllerForFile(path); + if (!new_controller) + { + QMessageBox::warning(this, tr("Error"), tr("Cannot open file:\n%1\n\nFile format not recognized.").arg(path)); + settings.remove(reopen_blocker); + return false; + } + + QString new_actual_path = path; + QString autosave_path = Autosave::autosavePath(path); + bool new_autosave_conflict = QFileInfo::exists(autosave_path); + if (new_autosave_conflict) + { +#if defined(Q_OS_ANDROID) + // Assuming small screen, showing dialog before opening the file + AutosaveDialog* autosave_dialog = new AutosaveDialog(path, autosave_path, autosave_path, this); + int result = autosave_dialog->exec(); + new_actual_path = (result == QDialog::Accepted) ? autosave_dialog->selectedPath() : QString(); + delete autosave_dialog; +#else + // Assuming large screen, dialog will be shown while the autosaved file is open + new_actual_path = autosave_path; +#endif + } + + if (new_actual_path.isEmpty() || !new_controller->load(new_actual_path, this)) + { + delete new_controller; + settings.remove(reopen_blocker); + return false; + } + + MainWindow* open_window = this; +#if !defined(Q_OS_ANDROID) + if (has_opened_file) + open_window = new MainWindow(); +#endif + + open_window->setController(new_controller, path); + open_window->actual_path = new_actual_path; + open_window->setHasAutosaveConflict(new_autosave_conflict); + open_window->setHasUnsavedChanges(false); + + open_window->setVisible(true); // Respect the window flags set by new_controller. + open_window->raise(); + num_open_files++; + settings.remove(reopen_blocker); + setMostRecentlyUsedFile(path); + +#if !defined(Q_OS_ANDROID) + // Assuming large screen. Android handled above. + if (new_autosave_conflict) + { + auto autosave_dialog = new AutosaveDialog(path, autosave_path, new_actual_path, open_window, Qt::WindowTitleHint | Qt::CustomizeWindowHint); + autosave_dialog->move(open_window->rect().right() - autosave_dialog->width(), open_window->rect().top()); + autosave_dialog->show(); + autosave_dialog->raise(); + + connect(autosave_dialog, &AutosaveDialog::pathSelected, open_window, &MainWindow::switchActualPath); + connect(open_window, &MainWindow::actualPathChanged, autosave_dialog, &AutosaveDialog::setSelectedPath); + connect(open_window, &MainWindow::autosaveConflictResolved, autosave_dialog, &AutosaveDialog::autosaveConflictResolved); + } +#endif + + open_window->activateWindow(); + + return true; +} + +void MainWindow::switchActualPath(const QString& path) +{ + if (path == actual_path) + { + return; + } + + int ret = QMessageBox::Ok; + if (has_unsaved_changes) + { + ret = QMessageBox::warning(this, appName(), + tr("The file has been modified.\n" + "Do you want to discard your changes?"), + QMessageBox::Discard | QMessageBox::Cancel); + } + + if (ret != QMessageBox::Cancel) + { + const QString& current_path = currentPath(); + MainWindowController* const new_controller = MainWindowController::controllerForFile(current_path); + if (new_controller && new_controller->load(path, this)) + { + setController(new_controller, current_path); + actual_path = path; + setHasUnsavedChanges(false); + } + } + + emit actualPathChanged(actual_path); + activateWindow(); +} + +void MainWindow::openPathLater(const QString& path) +{ + path_backlog.push_back(path); + QTimer::singleShot(10, this, SLOT(openPathBacklog())); // clazy:exclude=old-style-connect +} + +void MainWindow::openPathBacklog() +{ + for (const auto& path : qAsConst(path_backlog)) + openPath(path); + path_backlog.clear(); +} + +void MainWindow::openRecentFile() +{ + if (auto action = qobject_cast(sender())) + openPath(action->data().toString()); +} + +void MainWindow::updateRecentFileActions() +{ + if (! create_menu) + return; + + QStringList files = Settings::getInstance().getSettingCached(Settings::General_RecentFilesList).toStringList(); + + int num_recent_files = qMin(files.size(), max_recent_files); + + open_recent_menu->clear(); + for (int i = 0; i < num_recent_files; ++i) { + QString text = tr("&%1 %2").arg(i + 1).arg(QFileInfo(files[i]).fileName()); + recent_file_act[i]->setText(text); + recent_file_act[i]->setData(files[i]); + open_recent_menu->addAction(recent_file_act[i]); + } + + if (num_recent_files > 0 && !open_recent_menu_inserted) + file_menu->insertMenu(save_act, open_recent_menu); + else if (!(num_recent_files > 0) && open_recent_menu_inserted) + file_menu->removeAction(open_recent_menu->menuAction()); + open_recent_menu_inserted = num_recent_files > 0; +} + +void MainWindow::setHasAutosaveConflict(bool value) +{ + if (has_autosave_conflict != value) + { + has_autosave_conflict = value; + setAutosaveNeeded(has_unsaved_changes && !has_autosave_conflict); + if (!has_autosave_conflict) + emit autosaveConflictResolved(); + } +} + +bool MainWindow::removeAutosaveFile() const +{ + if (!currentPath().isEmpty() && !has_autosave_conflict) + { + QFile autosave_file(autosavePath(currentPath())); + return !autosave_file.exists() || autosave_file.remove(); + } + return false; +} + +Autosave::AutosaveResult MainWindow::autosave() +{ + QString path = currentPath(); + if (path.isEmpty() || !controller) + { + return Autosave::PermanentFailure; + } + else if (controller->isEditingInProgress()) + { + return Autosave::TemporaryFailure; + } + else + { + showStatusBarMessage(tr("Autosaving..."), 0); + if (controller->exportTo(autosavePath(currentPath()))) + { + // Success + clearStatusBarMessage(); + return Autosave::Success; + } + else + { + // Failure + showStatusBarMessage(tr("Autosaving failed!"), 6000); + return Autosave::PermanentFailure; + } + } +} + +bool MainWindow::save() +{ + return savePath(currentPath()); +} + +bool MainWindow::savePath(const QString &path) +{ + if (!controller) + return false; + + if (path.isEmpty()) + return showSaveAsDialog(); + + const FileFormat *format = FileFormats.findFormatForFilename(path); + if (format->isExportLossy()) + { + QString message = tr("This map is being saved as a \"%1\" file. Information may be lost.\n\nPress Yes to save in this format.\nPress No to choose a different format.").arg(format->description()); + int result = QMessageBox::warning(this, tr("Warning"), message, QMessageBox::Yes, QMessageBox::No); + if (result != QMessageBox::Yes) + return showSaveAsDialog(); + } + + if (!controller->save(path)) + return false; + + setMostRecentlyUsedFile(path); + + setHasAutosaveConflict(false); + removeAutosaveFile(); + + if (path != currentPath()) + { + setCurrentPath(path); + removeAutosaveFile(); + } + + setHasUnsavedChanges(false); + + return true; +} + +QString MainWindow::getOpenFileName(QWidget* parent, const QString& title, FileFormat::FileTypes types) +{ + // Get the saved directory to start in, defaulting to the user's home directory. + QSettings settings; + QString open_directory = settings.value(QString::fromLatin1("openFileDirectory"), QDir::homePath()).toString(); + + // Build the list of supported file filters based on the file format registry + QString filters, extensions; + + if (types.testFlag(FileFormat::MapFile)) + { + for (auto format : FileFormats.formats()) + { + if (format->supportsImport()) + { + if (filters.isEmpty()) + { + filters = format->filter(); + extensions = QLatin1String("*.") + format->fileExtensions().join(QString::fromLatin1(" *.")); + } + else + { + filters = filters + QLatin1String(";;") + format->filter(); + extensions = extensions + QLatin1String(" *.") + format->fileExtensions().join(QString::fromLatin1(" *.")); + } + } + } + filters = + tr("All maps") + QLatin1String(" (") + extensions + QLatin1String(");;") + + filters + QLatin1String(";;"); + } + + filters += tr("All files") + QLatin1String(" (*.*)"); + + QString path = FileDialog::getOpenFileName(parent, title, open_directory, filters); + QFileInfo info(path); + return info.canonicalFilePath(); +} + +bool MainWindow::showSaveAsDialog() +{ + if (!controller) + return false; + + // Try current directory first + QFileInfo current(currentPath()); + QString save_directory = current.canonicalPath(); + if (save_directory.isEmpty()) + { + // revert to least recently used directory or home directory. + QSettings settings; + save_directory = settings.value(QString::fromLatin1("openFileDirectory"), QDir::homePath()).toString(); + } + + // Build the list of supported file filters based on the file format registry + QString filters; + for (auto format : FileFormats.formats()) + { + if (format->supportsExport()) + { + if (filters.isEmpty()) + filters = format->filter(); + else + filters = filters + QLatin1String(";;") + format->filter(); + } + } + + QString filter; // will be set to the selected filter by QFileDialog + QString path = FileDialog::getSaveFileName(this, tr("Save file"), save_directory, filters, &filter); + + // On Windows, when the user enters "sample", we get "sample.omap *.xmap". + // (Fixed in upstream qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp + // Wednesday March 20 2013 in commit 426f2cc.) + // This results in an error later, because "*" is not a valid character. + // But it is reasonable to apply the workaround to all platforms, + // due to the special meaning of "*" in shell patterns. + const int extensions_quirk = path.indexOf(QLatin1String(" *.")); + if (extensions_quirk >= 0) + { + path.truncate(extensions_quirk); + } + + if (path.isEmpty()) + return false; + + const FileFormat *format = FileFormats.findFormatByFilter(filter); + if (!format) + { + QMessageBox::information(this, tr("Error"), + tr("File could not be saved:") + QLatin1Char('\n') + + tr("There was a problem in determining the file format.") + QLatin1Char('\n') + QLatin1Char('\n') + + tr("Please report this as a bug.") ); + return false; + } + + // Ensure that the provided filename has a correct file extension. + // Among other things, this will ensure that FileFormats.formatForFilename() + // returns the same thing the user selected in the dialog. +// QString selected_extension = "." + format->primaryExtension(); + QStringList selected_extensions(format->fileExtensions()); + selected_extensions.replaceInStrings(QRegExp(QString::fromLatin1("^")), QString::fromLatin1(".")); + bool has_extension = std::any_of(selected_extensions.constBegin(), selected_extensions.constEnd(), [&path](const auto& selected_extension) { + return path.endsWith(selected_extension, Qt::CaseInsensitive); + }); + if (!has_extension) + path += QLatin1Char('.') + format->primaryExtension(); + // Ensure that the file name matches the format. + Q_ASSERT(format->fileExtensions().contains(QFileInfo(path).suffix())); + // Fails when using different formats for import and export: + // Q_ASSERT(FileFormats.findFormatForFilename(path) == format); + + return savePath(path); +} + +void MainWindow::toggleFullscreenMode() +{ + if (isFullScreen()) + { + showNormal(); + if (maximized_before_fullscreen) + showMaximized(); + } + else + { + maximized_before_fullscreen = isMaximized(); + showFullScreen(); + } +} + +void MainWindow::showSettings() +{ + SettingsDialog dialog(this); + dialog.exec(); +} + +void MainWindow::showAbout() +{ + AboutDialog about_dialog(this); + about_dialog.exec(); +} + +void MainWindow::showHelp() +{ + Util::showHelp(this); +} + +void MainWindow::linkClicked(const QString &link) +{ + if (link.compare(QLatin1String("settings:"), Qt::CaseInsensitive) == 0) + showSettings(); + else if (link.compare(QLatin1String("help:"), Qt::CaseInsensitive) == 0) + showHelp(); + else if (link.compare(QLatin1String("about:"), Qt::CaseInsensitive) == 0) + showAbout(); + else if (link.startsWith(QLatin1String("examples:"), Qt::CaseInsensitive)) + openPathLater(QLatin1String("data:/examples/") + link.midRef(9)); + else + QDesktopServices::openUrl(link); +} + +bool MainWindow::eventFilter(QObject *object, QEvent *event) +{ + Q_UNUSED(object) + + switch(event->type()) + { + case QEvent::WhatsThisClicked: + { + QWhatsThisClickedEvent* e = static_cast(event); + Util::showHelp(this, e->href()); + }; + break; +#if defined(Q_OS_ANDROID) + case QEvent::KeyRelease: + if (static_cast(event)->key() == Qt::Key_Back && hasOpenedFile()) + { + /* Don't let Qt close the application in + * QGuiApplicationPrivate::processKeyEvent() while a file is opened. + * + * This must be the application-wide event filter in order to + * catch Qt::Key_Back from popup menus (such as template list, + * overflow actions). + * + * Any widgets that want to handle Qt::Key_Back need to watch + * for QEvent::KeyPress. + */ + event->accept(); + return true; + } + break; +#endif + default: + ; // nothing + } + + return false; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/main_window.h b/src/gui/main_window.h new file mode 100644 index 0000000..7da8b05 --- /dev/null +++ b/src/gui/main_window.h @@ -0,0 +1,575 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2013-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAIN_WINDOW_H +#define OPENORIENTEERING_MAIN_WINDOW_H + +#include +#include +#include +#include +#include + +#include "core/autosave.h" +#include "fileformats/file_format.h" + +class QAction; +class QCloseEvent; +class QEvent; +class QKeyEvent; +class QLabel; +class QMenu; +class QStackedWidget; +class QToolBar; +class QWidget; + +namespace OpenOrienteering { + +class MainWindowController; + + +/** + * The MainWindow class provides the generic application window. + * + * It always has an active controller (class MainWindowController) + * which provides the specific window content and behaviours. + * The controller can be exchanged while the window is visible. + */ +class MainWindow : public QMainWindow, private Autosave +{ +Q_OBJECT +public: + /** + * Creates a new main window. + */ + explicit MainWindow(QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + +private: + /** + * Creates a new main window. + * + * The flag as_main_window is a contradiction to the general intent of this + * class. The value fals is used only once, in SymbolSettingDialog. For this + * case, it disables some features such as the main menu. + * + * \todo Refactor to remove the flag as_main_window. + */ + explicit MainWindow(bool as_main_window, QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + + friend class SymbolSettingDialog; + +public: + /** Destroys a main window. */ + ~MainWindow() override; + + /** Returns the application's localized name. */ + QString appName() const; + + + /** + * Returns whether the window is operating in mobile mode. + * + * On the desktop, the default (desktop) mode may be overwritten by + * setting the environment variable MAPPER_MOBILE_GUI to 0 or 1. + * + * For Android, this evaluates to constexpr true so that the compiler + * may optimize away desktop code in conditional blocks. + */ +#ifndef Q_OS_ANDROID + static bool mobileMode(); +#else + static constexpr bool mobileMode() { return true; } +#endif + + + /** + * Changes the controller. + * + * The new controller does not edit a file. + */ + void setController(MainWindowController* new_controller); + + /** + * Changes the controller. + * + * The new controller edits the file with the given path. + * The path may be empty for a new (unnamed) file. + */ + void setController(MainWindowController* new_controller, const QString& path); + +private: + void setController(MainWindowController* new_controller, bool has_file); + +public: + /** Returns the current controller. */ + MainWindowController* getController() const; + + + /** Returns the canonical path of the currently open file or + * an empty string if no file is open. + */ + const QString& currentPath() const; + + /** Registers the given path as most recently used file. + * + * The path is added at (or moved to) the top of the list of most recently + * used files, and the directory is saved as most recently used directory. + */ + static void setMostRecentlyUsedFile(const QString& path); + + /** Returns true if a file is opened in this main window. */ + bool hasOpenedFile() const; + + + /** Returns true if the opened file is marked as having unsaved changes. */ + bool hasUnsavedChanges() const; + + + /** Sets the text in the status bar. */ + void setStatusBarText(const QString& text); + + /** Shows a temporary message in the status bar. */ + void showStatusBarMessage(const QString& text, int timeout = 0); + + /** Clears temporary messages set in the status bar with showStatusBarMessage(). */ + void clearStatusBarMessage(); + + + /** + * Blocks shortcuts. + * + * During text input, it may be neccessary to disable shortcuts. + * + * @param blocked true for blocking shortcuts, false for normal behaviour. + */ + void setShortcutsBlocked(bool blocked); + + /** Returns true if shortcuts are currently disabled. */ + bool shortcutsBlocked() const; + + + /** Returns the main window's file menu so that it can be extended. */ + QMenu* getFileMenu() const; + + /** Returns an QAction which serves as extension point in the file menu. */ + QAction* getFileMenuExtensionAct() const; + + /** Returns the save action. */ + QAction* getSaveAct() const; + + /** Returns the close action. */ + QAction* getCloseAct() const; + + + /** + * Returns a general toolbar with standard file actions (new, open, save). + * + * The MainWindowController is responsible to add it to the main window. + * It will be destroyed (and recreated) when the controller changes. + */ + QToolBar* getGeneralToolBar() const; + + + /** Open the file with the given path after all events have been processed. + * May open a new main window. + * If loading is successful, the selected path will become + * the [new] window's current path. + */ + void openPathLater(const QString &path); + + /** Save the content of the main window. + * @param path the path where to save. + */ + bool savePath(const QString &path); + + /** Shows the open file dialog for the given file type(s) and returns the chosen file + * or an empty string if the dialog is aborted. + */ + static QString getOpenFileName(QWidget* parent, const QString& title, FileFormat::FileTypes types); + + /** + * Sets the MainWindow's effective central widget. + * + * Any previously set widget will be hidden and scheduled for deletion. + * + * Hides an implementation in QMainWindow which causes problems with + * dock widgets when switching from home screen widget to map widget. + * NEVER call QMainWindow::setCentralWidget(...) on a MainWindow. + */ + void setCentralWidget(QWidget* widget); + + + /** + * Indicates whether the home screen is disabled. + * + * Normally the last main window will return to the home screen when a file + * is closed. When the home screen is disabled, the last window will be + * closed instead. + */ + bool homeScreenDisabled() const; + + /** + * Sets whether to show the home screen after closing the last file. + * + * @see homeScreenDisabled() + */ + void setHomeScreenDisabled(bool disabled); + +public slots: + /** + * Reacts to application state changes. + * + * On Android, when the application state becomes Qt::ApplicationActive, + * this method looks for the Android activity's current intent and triggers + * the loading of a given file (if there is not already another file loaded). + * + * In general, when called for the first time after application start, it + * opens the most recently used file, unless this feature is disabled in the + * settings, and unless other files are registered for opening (i.e. files + * given as command line parameters.) + */ + void applicationStateChanged(); + + /** + * Show a wizard for creating new maps. + * + * May open a new main window. + */ + void showNewMapWizard(); + + /** + * Show a file-open dialog and load the select file. + * + * May open a new main window. + * If loading is successful, the selected path will become + * the [new] window's current path. + */ + void showOpenDialog(); + + /** + * Show a file-save dialog. + * + * If saving is successful, the selected path will become + * this window's current path. + * + * @return true if saving was succesful, false otherwise + */ + bool showSaveAsDialog(); + + /** + * Open the file with the given path. + * + * May open a new main window. + * If loading is successful, the selected path will become + * the [new] window's current path. + * + * @return true if loading was succesful, false otherwise + */ + bool openPath(const QString &path); + + /** + * Open the file specified in the sending action's data. + * + * This is intended for opening recent files. + */ + void openRecentFile(); + + /** + * Notify the main window of a change to the list of recent files. + */ + void updateRecentFileActions(); + + /** + * Save the current content to the current path. + * + * This will trigger a file-save dialog if the current path is not set (i.e. empty). + */ + bool save(); + + /** Save the current content to the current path. + */ + Autosave::AutosaveResult autosave() override; + + /** + * Close the file currently opened. + * + * If there are changes to the current file, the user will be asked if he + * wants to save it - the user may even cancel the closing of the file. + * + * This will close the window unless this is the last window. + * + * @return True if the file was actually closed, false otherwise. + */ + bool closeFile(); + + /** Toggle between normal window and fullscreen mode. + */ + void toggleFullscreenMode(); + + /** Show the settings dialog. + */ + void showSettings(); + + /** Show the about dialog. + */ + void showAbout(); + + /** Show the index page of the manual in the help browser. + */ + void showHelp(); + + /** Open a link. + * This is called when the user clicks on a link in the UI, + * e.g. in the tip of the day. + * + * @param link the target URI + */ + void linkClicked(const QString &link); + + /** + * Notifies this window of unsaved changes. + * + * If the controller was set as having an opened file, setting this value to + * true will start the autosave countdown if the previous value was false. + * + * This will update the window title via QWidget::setWindowModified(). + */ + void setHasUnsavedChanges(bool value); + +signals: + /** + * This signal is emitted when the actual path changes. + * + * @see switchActualPath() + */ + void actualPathChanged(const QString &path); + + /** + * This signal is emitted when an autosave conflict gets resolved. + * + * @see setHasAutosaveConflict() + */ + void autosaveConflictResolved(); + +protected slots: + /** + * Switches to a different controller and loads the given path. + * + * This method is meant for switching between an original file and + * autosaved versions. It does not touch current_path. The class of the new + * controller is determined from the current_path (i.e. original file). + * + * If the given path is the current actual_path, no change is made. + * + * If the currently loaded file was modified, the user is asked whether he + * really wants to switch to another file which means loosing the changes + * he had made. + */ + void switchActualPath(const QString &path); + + /** + * Open the files which have been registered by openPathLater(). + */ + void openPathBacklog(); + + /** + * Listens to configuration changes. + */ + void settingsChanged(); + +protected: + /** + * Sets the path of the file edited by this windows' controller. + * + * This will update the window title via QWidget::setWindowFilePath(). + * + * If the controller was not set as having an opened file, + * the path must be empty. + */ + void setCurrentPath(const QString& path); + + /** + * Notifies the windows of autosave conflicts. + * + * An autosave conflict is the situation where a autosaved file exists + * when the original file is opened. This autosaved file indicates that + * the original file was not properly closed, i.e. the software crashed + * before closing. + */ + void setHasAutosaveConflict(bool value); + + /** + * Removes the autosave file if it exists. + * + * Returns true if the file was removed or didn't exist, false otherwise. + */ + bool removeAutosaveFile() const; + + bool event(QEvent* event) override; + void closeEvent(QCloseEvent *event) override; + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; + + bool eventFilter(QObject* object, QEvent* event) override; + +private: + static constexpr int max_recent_files = 10; + + /** + * Conditionally shows a dialog for saving pending changes. + * + * If this main window has an opened file with unsaved changes, shows + * a dialog which lets the user save the file, discard the changes or + * cancel. + * + * Returns true if the window can be closed, false otherwise. + */ + bool showSaveOnCloseDialog(); + + + /** Saves the window position and state. */ + void saveWindowSettings(); + + /** Loads the window position and state. */ + void loadWindowSettings(); + + + void createFileMenu(); + void createHelpMenu(); + + static MainWindow* findMainWindow(const QString& file_name); + + + /// The active controller + MainWindowController* controller; + const bool create_menu; + bool show_menu; + bool shortcuts_blocked; + + QToolBar* general_toolbar; + QMenu* file_menu; + QAction* save_act; + QMenu* open_recent_menu; + bool open_recent_menu_inserted; + QAction* recent_file_act[max_recent_files]; + QAction* settings_act; + QAction* close_act; + QLabel* status_label; + + /// Canonical path to the currently open file or an empty string if the file was not saved yet ("untitled") + QString current_path; + /// The actual path loaded by the editor. @see switchActualPath() + QString actual_path; + /// Does the main window display a file? If yes, new controllers will be opened in new main windows instead of replacing the active controller of this one + bool has_opened_file; + /// If this window has an opened file: does this file have unsaved changes? + bool has_unsaved_changes; + /// Indicates the presence of an autosave conflict. @see setHasAutosaveConflict() + bool has_autosave_conflict; + + /// Was the window maximized before going into fullscreen mode? In this case, we have to show it maximized again when leaving fullscreen mode. + bool maximized_before_fullscreen; + + bool homescreen_disabled; + + /// Number of active main windows. The last window shall not close on File > Close. + static int num_open_files; + + /// The central widget which never changes during a MainWindow's lifecycle + QStackedWidget* central_widget; + + /// A list of paths to be opened later + QStringList path_backlog; +}; + + +// ### MainWindow inline code ### + +inline +MainWindowController* MainWindow::getController() const +{ + return controller; +} + +inline +const QString& MainWindow::currentPath() const +{ + return current_path; +} + +inline +bool MainWindow::hasOpenedFile() const +{ + return has_opened_file; +} + +inline +bool MainWindow::hasUnsavedChanges() const +{ + return has_unsaved_changes; +} + +inline +bool MainWindow::shortcutsBlocked() const +{ + return shortcuts_blocked; +} + +inline +QMenu* MainWindow::getFileMenu() const +{ + return file_menu; +} + +inline +QAction* MainWindow::getFileMenuExtensionAct() const +{ + return settings_act; +} + +inline +QAction* MainWindow::getSaveAct() const +{ + return save_act; +} + +inline +QAction* MainWindow::getCloseAct() const +{ + return close_act; +} + +inline +QToolBar* MainWindow::getGeneralToolBar() const +{ + return general_toolbar; +} + +inline +bool MainWindow::homeScreenDisabled() const +{ + return homescreen_disabled; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/main_window_controller.cpp b/src/gui/main_window_controller.cpp new file mode 100644 index 0000000..38144e8 --- /dev/null +++ b/src/gui/main_window_controller.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "main_window_controller.h" + +#include + +#include "fileformats/file_format.h" +#include "fileformats/file_format_registry.h" +#include "gui/map/map_editor.h" + + +namespace OpenOrienteering { + +MainWindowController::~MainWindowController() = default; + + +bool MainWindowController::save(const QString& path) +{ + Q_UNUSED(path); + return false; +} + +bool MainWindowController::exportTo(const QString& path, const FileFormat* format) +{ + Q_UNUSED(path); + Q_UNUSED(format); + return false; +} + +bool MainWindowController::load(const QString& path, QWidget* dialog_parent) +{ + Q_UNUSED(path); + Q_UNUSED(dialog_parent); + return false; +} + +void MainWindowController::detach() +{ + // nothing +} + +bool MainWindowController::isEditingInProgress() const +{ + return false; +} + +bool MainWindowController::keyPressEventFilter(QKeyEvent* event) +{ + Q_UNUSED(event); + return false; +} + +bool MainWindowController::keyReleaseEventFilter(QKeyEvent* event) +{ + Q_UNUSED(event); + return false; +} + +MainWindowController* MainWindowController::controllerForFile(const QString& filename) +{ + const FileFormat* format = FileFormats.findFormatForFilename(filename); + if (format && format->supportsImport()) + return new MapEditorController(MapEditorController::MapEditor); + + return nullptr; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/main_window_controller.h b/src/gui/main_window_controller.h new file mode 100644 index 0000000..88c1982 --- /dev/null +++ b/src/gui/main_window_controller.h @@ -0,0 +1,139 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAIN_WINDOW_CONTROLLER_H +#define OPENORIENTEERING_MAIN_WINDOW_CONTROLLER_H + +#include +#include + +class QKeyEvent; +class QWidget; + +namespace OpenOrienteering { + +class MainWindow; +class FileFormat; + + +/** A MainWindowController provides the specific content and + * behaviours for a main window, for example map drawing or + * course setting functions. + */ +class MainWindowController : public QObject +{ +Q_OBJECT +public: + + ~MainWindowController() override; + + /** Save to a file. + * @param path the path to save to + * @return true if saving was sucessful, false on errors + */ + virtual bool save(const QString& path); + + /** Export to a file, but don't change modified state + * with regard to the original file. + * @param path the path to export to + * @param format the file format (automatically determined if nullptr) + * @return true if saving was sucessful, false on errors + */ + virtual bool exportTo(const QString& path, const FileFormat* format = nullptr); + + /** Load from a file. + * @param path the path to load from + * @param dialog_parent Alternative parent widget for all dialogs. + * If nullptr, implementations should use MainWindowController::window. + * @return true if loading was sucessful, false on errors + */ + virtual bool load(const QString& path, QWidget* dialog_parent = nullptr); + + /** Attach the controller to a main window. + * The controller should create its user interface here. + */ + virtual void attach(MainWindow* window) = 0; + + /** Detach the controller from a main window. + * The controller should delete its user interface here. + */ + virtual void detach(); + + /** + * Returns true when editing is in progress. + * + * "Editing in progress" means the file is an "unstable" state where no + * global operations like save, undo, redo shall not be applied. + */ + virtual bool isEditingInProgress() const; + + /** + * @brief Receives key press events from the main window. + * + * QKeyEvent starts with isAccepted() == true, so the return value of this + * function decides if the event shall be stopped from being handled further. + * + * The default implementation simply returns false. + * + * @return True if the event shall be stopped from being handled further, false otherwise. + */ + virtual bool keyPressEventFilter(QKeyEvent* event); + + /** + * @brief Receives key release events from the main window. + * + * QKeyEvent starts with isAccepted() == true, so the return value of this + * function decides if the event shall be stopped from being handled further. + * + * The default implementation simply returns false. + * + * @return True if the event shall be stopped from being handled further, false otherwise. + */ + virtual bool keyReleaseEventFilter(QKeyEvent* event); + + /** Get the main window this controller is attached to. + */ + inline MainWindow* getWindow() const; + + /** Get a controller suitable for a particular file. + * @param filename the name of the file + * @return a MainWindowController that is able to load the file + */ + static MainWindowController* controllerForFile(const QString& filename); + +protected: + MainWindow* window; +}; + + + +//### MainWindowController inline code ### + +inline +MainWindow* MainWindowController::getWindow() const +{ + return window; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/map/map_dialog_rotate.cpp b/src/gui/map/map_dialog_rotate.cpp new file mode 100644 index 0000000..476bba7 --- /dev/null +++ b/src/gui/map/map_dialog_rotate.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_dialog_rotate.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_coord.h" +#include "templates/template.h" +#include "gui/util_gui.h" + + +namespace OpenOrienteering { + +RotateMapDialog::RotateMapDialog(QWidget* parent, Map* map) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint), map(map) +{ + setWindowTitle(tr("Rotate map")); + + QFormLayout* layout = new QFormLayout(); + + layout->addRow(Util::Headline::create(tr("Rotation parameters"))); + + rotation_edit = Util::SpinBox::create(Georeferencing::declinationPrecision(), -180.0, +180.0, trUtf8("°")); + rotation_edit->setWrapping(true); + layout->addRow(tr("Angle (counter-clockwise):"), rotation_edit); + + layout->addRow(new QLabel(tr("Rotate around:"))); + + center_origin_radio = new QRadioButton(tr("Map coordinate system origin", "Rotation center point")); + center_origin_radio->setChecked(true); + layout->addRow(center_origin_radio); + + center_georef_radio = new QRadioButton(tr("Georeferencing reference point", "Rotation center point")); + if (!map->getGeoreferencing().isValid()) + center_georef_radio->setEnabled(false); + layout->addRow(center_georef_radio); + + center_other_radio = new QRadioButton(tr("Other point,", "Rotation center point")); + other_x_edit = Util::SpinBox::create(tr("mm")); + other_y_edit = Util::SpinBox::create(tr("mm")); + auto other_center_layout = new QHBoxLayout(); + other_center_layout->addWidget(center_other_radio); + other_center_layout->addWidget(new QLabel(tr("X:", "x coordinate")), 0); + other_center_layout->addWidget(other_x_edit, 1); + other_center_layout->addWidget(new QLabel(tr("Y:", "y coordinate")), 0); + other_center_layout->addWidget(other_y_edit, 1); + layout->addRow(other_center_layout); + + + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(Util::Headline::create(tr("Options"))); + + adjust_georeferencing_check = new QCheckBox(tr("Adjust georeferencing reference point")); + if (map->getGeoreferencing().isValid()) + adjust_georeferencing_check->setChecked(true); + else + adjust_georeferencing_check->setEnabled(false); + layout->addRow(adjust_georeferencing_check); + + adjust_declination_check = new QCheckBox(tr("Adjust georeferencing declination")); + if (map->getGeoreferencing().isValid()) + adjust_declination_check->setChecked(true); + else + adjust_declination_check->setEnabled(false); + layout->addRow(adjust_declination_check); + + adjust_templates_check = new QCheckBox(tr("Rotate non-georeferenced templates")); + bool have_non_georeferenced_template = false; + for (int i = 0; i < map->getNumTemplates() && !have_non_georeferenced_template; ++i) + have_non_georeferenced_template = !map->getTemplate(i)->isTemplateGeoreferenced(); + for (int i = 0; i < map->getNumClosedTemplates() && !have_non_georeferenced_template; ++i) + have_non_georeferenced_template = !map->getClosedTemplate(i)->isTemplateGeoreferenced(); + if (have_non_georeferenced_template) + adjust_templates_check->setChecked(true); + else + adjust_templates_check->setEnabled(false); + layout->addRow(adjust_templates_check); + + + layout->addItem(Util::SpacerItem::create(this)); + QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + layout->addRow(button_box); + + setLayout(layout); + + connect(center_origin_radio, &QAbstractButton::clicked, this, &RotateMapDialog::updateWidgets); + connect(center_georef_radio, &QAbstractButton::clicked, this, &RotateMapDialog::updateWidgets); + connect(center_other_radio, &QAbstractButton::clicked, this, &RotateMapDialog::updateWidgets); + connect(button_box, &QDialogButtonBox::accepted, this, &RotateMapDialog::okClicked); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + + updateWidgets(); +} + +void RotateMapDialog::setRotationDegrees(float rotation) +{ + rotation_edit->setValue(rotation); +} + +void RotateMapDialog::setRotateAroundGeorefRefPoint() +{ + if (center_georef_radio->isEnabled()) + { + center_georef_radio->setChecked(true); + updateWidgets(); + } +} + +void RotateMapDialog::setAdjustDeclination(bool adjust) +{ + adjust_declination_check->setChecked(adjust); +} + +void RotateMapDialog::showAdjustDeclination(bool show) +{ + adjust_declination_check->setVisible(show); +} + +void RotateMapDialog::updateWidgets() +{ + other_x_edit->setEnabled(center_other_radio->isChecked()); + other_y_edit->setEnabled(center_other_radio->isChecked()); + adjust_georeferencing_check->setEnabled(!center_georef_radio->isChecked()); +} + +void RotateMapDialog::okClicked() +{ + double rotation = M_PI * rotation_edit->value() / 180; + MapCoord center = MapCoord(0, 0); + if (center_georef_radio->isChecked()) + center = map->getGeoreferencing().getMapRefPoint(); + else if (center_other_radio->isChecked()) + center = MapCoord(other_x_edit->value(), -1 * other_y_edit->value()); + + map->rotateMap(rotation, center, adjust_georeferencing_check->isChecked(), adjust_declination_check->isChecked(), adjust_templates_check->isChecked()); + accept(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/map/map_dialog_rotate.h b/src/gui/map/map_dialog_rotate.h new file mode 100644 index 0000000..c3a8816 --- /dev/null +++ b/src/gui/map/map_dialog_rotate.h @@ -0,0 +1,78 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_DIALOG_ROTATE_H +#define OPENORIENTEERING_MAP_DIALOG_ROTATE_H + +#include +#include + +class QDoubleSpinBox; +class QCheckBox; +class QRadioButton; +class QWidget; + +namespace OpenOrienteering { + +class Map; + + +/** + * Dialog for rotating the whole map around a point. + */ +class RotateMapDialog : public QDialog +{ +Q_OBJECT +public: + /** Creates a new RotateMapDialog. */ + RotateMapDialog(QWidget* parent, Map* map); + + /** Sets the rotation angle in degrees in the corresponding widget. */ + void setRotationDegrees(float rotation); + /** Enables the setting to rotate around the georeferencing reference point. */ + void setRotateAroundGeorefRefPoint(); + /** Checks or unchecks the setting to adjust the georeferencing declination. */ + void setAdjustDeclination(bool adjust); + /** Sets the visibility of the setting to adjust the georeferencing declination. */ + void showAdjustDeclination(bool show); + +private slots: + void updateWidgets(); + void okClicked(); + +private: + QDoubleSpinBox* rotation_edit; + QRadioButton* center_origin_radio; + QRadioButton* center_georef_radio; + QRadioButton* center_other_radio; + QDoubleSpinBox* other_x_edit; + QDoubleSpinBox* other_y_edit; + + QCheckBox* adjust_georeferencing_check; + QCheckBox* adjust_declination_check; + QCheckBox* adjust_templates_check; + + Map* map; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/map/map_dialog_scale.cpp b/src/gui/map/map_dialog_scale.cpp new file mode 100644 index 0000000..6dffc1e --- /dev/null +++ b/src/gui/map/map_dialog_scale.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_dialog_scale.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "core/georeferencing.h" +#include "core/map.h" +#include "templates/template.h" +#include "gui/util_gui.h" + + +namespace OpenOrienteering { + +ScaleMapDialog::ScaleMapDialog(QWidget* parent, Map* map) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint), map(map) +{ + setWindowTitle(tr("Change map scale")); + + QFormLayout* layout = new QFormLayout(); + + layout->addRow(Util::Headline::create(tr("Scaling parameters"))); + + scale_edit = new QLineEdit(QString::number(map->getScaleDenominator())); + scale_edit->setValidator(new QIntValidator(1, 9999999, scale_edit)); + layout->addRow(tr("New scale: 1 :"), scale_edit); + + layout->addRow(new QLabel(tr("Scaling center:"))); + + center_origin_radio = new QRadioButton(tr("Map coordinate system origin", "Scaling center point")); + center_origin_radio->setChecked(true); + layout->addRow(center_origin_radio); + + center_georef_radio = new QRadioButton(tr("Georeferencing reference point", "Scaling center point")); + if (!map->getGeoreferencing().isValid()) + center_georef_radio->setEnabled(false); + layout->addRow(center_georef_radio); + + center_other_radio = new QRadioButton(tr("Other point,", "Scaling center point")); + other_x_edit = Util::SpinBox::create(tr("mm")); + other_y_edit = Util::SpinBox::create(tr("mm")); + QHBoxLayout* other_center_layout = new QHBoxLayout(); + other_center_layout->addWidget(center_other_radio); + other_center_layout->addWidget(new QLabel(tr("X:", "x coordinate")), 0); + other_center_layout->addWidget(other_x_edit, 1); + other_center_layout->addWidget(new QLabel(tr("Y:", "y coordinate")), 0); + other_center_layout->addWidget(other_y_edit, 1); + layout->addRow(other_center_layout); + + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(Util::Headline::create(tr("Options"))); + + adjust_symbols_check = new QCheckBox(tr("Scale symbol sizes")); + if (map->getNumSymbols() > 0) + adjust_symbols_check->setChecked(true); + else + adjust_symbols_check->setEnabled(false); + layout->addRow(adjust_symbols_check); + + adjust_objects_check = new QCheckBox(tr("Scale map object positions")); + if (map->getNumObjects() > 0) + adjust_objects_check->setChecked(true); + else + adjust_objects_check->setEnabled(false); + layout->addRow(adjust_objects_check); + + adjust_georeferencing_check = new QCheckBox(tr("Adjust georeferencing reference point")); + if (map->getGeoreferencing().isValid()) + adjust_georeferencing_check->setChecked(true); + else + adjust_georeferencing_check->setEnabled(false); + layout->addRow(adjust_georeferencing_check); + + adjust_templates_check = new QCheckBox(tr("Scale non-georeferenced templates")); + bool have_non_georeferenced_template = false; + for (int i = 0; i < map->getNumTemplates() && !have_non_georeferenced_template; ++i) + have_non_georeferenced_template = !map->getTemplate(i)->isTemplateGeoreferenced(); + for (int i = 0; i < map->getNumClosedTemplates() && !have_non_georeferenced_template; ++i) + have_non_georeferenced_template = !map->getClosedTemplate(i)->isTemplateGeoreferenced(); + if (have_non_georeferenced_template) + adjust_templates_check->setChecked(true); + else + adjust_templates_check->setEnabled(false); + layout->addRow(adjust_templates_check); + + + layout->addItem(Util::SpacerItem::create(this)); + QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + ok_button = button_box->button(QDialogButtonBox::Ok); + layout->addRow(button_box); + + setLayout(layout); + + connect(center_origin_radio, &QAbstractButton::clicked, this, &ScaleMapDialog::updateWidgets); + connect(center_georef_radio, &QAbstractButton::clicked, this, &ScaleMapDialog::updateWidgets); + connect(center_other_radio, &QAbstractButton::clicked, this, &ScaleMapDialog::updateWidgets); + connect(button_box, &QDialogButtonBox::accepted, this, &ScaleMapDialog::okClicked); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + + updateWidgets(); +} + +void ScaleMapDialog::updateWidgets() +{ + other_x_edit->setEnabled(center_other_radio->isChecked()); + other_y_edit->setEnabled(center_other_radio->isChecked()); + adjust_georeferencing_check->setEnabled(!center_georef_radio->isChecked()); +} + +void ScaleMapDialog::okClicked() +{ + int scale = scale_edit->text().toInt(); + MapCoord center = MapCoord(0, 0); + if (center_georef_radio->isChecked()) + center = map->getGeoreferencing().getMapRefPoint(); + else if (center_other_radio->isChecked()) + center = MapCoord(other_x_edit->value(), -1 * other_y_edit->value()); + + map->changeScale(scale, center, adjust_symbols_check->isChecked(), adjust_objects_check->isChecked(), adjust_georeferencing_check->isChecked(), adjust_templates_check->isChecked()); + accept(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/map/map_dialog_scale.h b/src/gui/map/map_dialog_scale.h new file mode 100644 index 0000000..e0f0982 --- /dev/null +++ b/src/gui/map/map_dialog_scale.h @@ -0,0 +1,73 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_DIALOG_SCALE_H +#define OPENORIENTEERING_MAP_DIALOG_SCALE_H + +#include +#include + +class QLineEdit; +class QCheckBox; +class QPushButton; +class QRadioButton; +class QDoubleSpinBox; +class QWidget; + +namespace OpenOrienteering { + +class Map; + + +/** + * Dialog for scaling the whole map. + */ +class ScaleMapDialog : public QDialog +{ +Q_OBJECT +public: + /** Creates a new ScaleMapDialog. */ + ScaleMapDialog(QWidget* parent, Map* map); + +private slots: + void updateWidgets(); + void okClicked(); + +private: + QLineEdit* scale_edit; + QRadioButton* center_origin_radio; + QRadioButton* center_georef_radio; + QRadioButton* center_other_radio; + QDoubleSpinBox* other_x_edit; + QDoubleSpinBox* other_y_edit; + + QCheckBox* adjust_symbols_check; + QCheckBox* adjust_objects_check; + QCheckBox* adjust_georeferencing_check; + QCheckBox* adjust_templates_check; + QPushButton* ok_button; + + Map* map; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/map/map_editor.cpp b/src/gui/map/map_editor.cpp new file mode 100644 index 0000000..6458d97 --- /dev/null +++ b/src/gui/map/map_editor.cpp @@ -0,0 +1,4102 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_editor.h" +#include "map_editor_p.h" + +#include +#include +#include +#include +#include +#include +#include +// IWYU pragma: no_include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include +// IWYU pragma: no_include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_ANDROID +#include +#endif + +#include "settings.h" +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_coord.h" +#include "core/map_part.h" +#include "core/map_view.h" +#include "core/objects/boolean_tool.h" +#include "core/objects/object.h" +#include "core/objects/object_operations.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/symbol.h" +#include "core/symbols/symbol_icon_decorator.h" +#include "fileformats/file_format.h" +#include "fileformats/file_format_registry.h" +#include "gui/configure_grid_dialog.h" +#include "gui/file_dialog.h" +#include "gui/georeferencing_dialog.h" +#include "gui/main_window.h" +#include "gui/print_widget.h" +#include "gui/text_browser_dialog.h" +#include "gui/util_gui.h" +#include "gui/map/map_dialog_rotate.h" +#include "gui/map/map_dialog_scale.h" +#include "gui/map/map_editor_activity.h" +#include "gui/map/map_find_feature.h" +#include "gui/map/map_widget.h" +#include "gui/symbols/replace_symbol_set_dialog.h" +#include "gui/widgets/action_grid_bar.h" +#include "gui/widgets/color_list_widget.h" +#include "gui/widgets/compass_display.h" +#include "gui/widgets/key_button_bar.h" // IWYU pragma: keep +#include "gui/widgets/measure_widget.h" +#include "gui/widgets/symbol_widget.h" +#include "gui/widgets/tags_widget.h" +#include "gui/widgets/template_list_widget.h" +#include "sensors/compass.h" +#include "sensors/gps_display.h" +#include "sensors/gps_temporary_markers.h" +#include "sensors/gps_track_recorder.h" +#include "templates/template.h" +#include "templates/template_dialog_reopen.h" +#include "templates/template_position_dock_widget.h" +#include "templates/template_tool_paint.h" +#include "templates/template_track.h" +#include "tools/cut_tool.h" +#include "tools/cut_hole_tool.h" +#include "tools/cutout_tool.h" +#include "tools/distribute_points_tool.h" +#include "tools/draw_circle_tool.h" +#include "tools/draw_freehand_tool.h" +#include "tools/draw_path_tool.h" +#include "tools/draw_point_tool.h" +#include "tools/draw_point_gps_tool.h" +#include "tools/draw_rectangle_tool.h" +#include "tools/draw_text_tool.h" +#include "tools/edit_point_tool.h" +#include "tools/edit_line_tool.h" +#include "tools/fill_tool.h" +#include "tools/pan_tool.h" +#include "tools/rotate_pattern_tool.h" +#include "tools/rotate_tool.h" +#include "tools/scale_tool.h" +#include "tools/tool.h" +#include "undo/map_part_undo.h" +#include "undo/object_undo.h" +#include "undo/undo.h" +#include "undo/undo_manager.h" +#include "util/backports.h" // IWYU pragma: keep + + +namespace OpenOrienteering { + +namespace { + + /** + * Creates a partial, resizable widget overlay over the main window. + * + * This widget is meant to be used as a dock widget replacement in the + * mobile app. In landscape mode, the child widget is placed on the right + * side, spanning the full height. In portrait mode, the child is placed + * on the top, spanning the full width. + */ + QSplitter* createDockWidgetSubstitute(MainWindow* window, QWidget* child) + { + auto splitter = new QSplitter(window); + splitter->setChildrenCollapsible(false); + + auto placeholder = new QWidget(); + + splitter->setAttribute(Qt::WA_NoSystemBackground, true); + placeholder->setAttribute(Qt::WA_NoSystemBackground, true); + child->setAutoFillBackground(true); + + auto geometry = window->geometry(); + splitter->setGeometry(geometry); + if (geometry.height() > geometry.width()) + { + splitter->setOrientation(Qt::Vertical); + splitter->addWidget(child); + splitter->addWidget(placeholder); + } + else + { + splitter->setOrientation(Qt::Horizontal); + splitter->addWidget(placeholder); + splitter->addWidget(child); + } + + return splitter; + } + + template + bool containsPathObject(const T& container) + { + return std::any_of(begin(container), end(container), [](const auto object) { + return object->getType() == Object::Path; + }); + } + + +} // namespace + + + +namespace MimeType { + +/// The MIME type of Mapper data +QString OpenOrienteeringObjects() +{ + return QStringLiteral("openorienteering/objects"); +} + + +} // namespace MimeType + + + +// ### MapEditorController ### + +MapEditorController::MapEditorController(OperatingMode mode, Map* map, MapView* map_view) +: MainWindowController() +, mobile_mode(MainWindow::mobileMode()) +, active_symbol(nullptr) +, template_list_widget(nullptr) +, mappart_remove_act(nullptr) +, mappart_merge_act(nullptr) +, mappart_merge_menu(nullptr) +, mappart_move_menu(nullptr) +, mappart_selector_box(nullptr) +, mappart_merge_mapper(new QSignalMapper(this)) +, mappart_move_mapper(new QSignalMapper(this)) +{ + this->mode = mode; + this->map = nullptr; + main_view = nullptr; + symbol_widget = nullptr; + window = nullptr; + editing_in_progress = false; + + cut_hole_menu = nullptr; + + if (map) + setMapAndView(map, map_view ? map_view : new MapView(this, map)); + + editor_activity = nullptr; + current_tool = nullptr; + override_tool = nullptr; + last_painted_on_template = nullptr; + + paste_act = nullptr; + reopen_template_act = nullptr; + overprinting_simulation_act = nullptr; + + toolbar_view = nullptr; + toolbar_mapparts = nullptr; + toolbar_drawing = nullptr; + toolbar_editing = nullptr; + toolbar_advanced_editing = nullptr; + print_dock_widget = nullptr; + measure_dock_widget = nullptr; + symbol_dock_widget = nullptr; + + statusbar_zoom_frame = nullptr; + statusbar_cursorpos_label = nullptr; + + gps_display = nullptr; + gps_track_recorder = nullptr; + compass_display = nullptr; + gps_marker_display = nullptr; + + actionsById[""] = new QAction(this); // dummy action + + connect(mappart_merge_mapper, QOverload::of(&QSignalMapper::mapped), this, &MapEditorController::mergeCurrentMapPartTo); + connect(mappart_move_mapper, QOverload::of(&QSignalMapper::mapped), this, &MapEditorController::reassignObjectsToMapPart); +} + +MapEditorController::~MapEditorController() +{ + paste_act = nullptr; + delete current_tool; + delete override_tool; + delete editor_activity; + delete toolbar_view; + delete toolbar_drawing; + delete toolbar_editing; + delete toolbar_advanced_editing; + delete toolbar_mapparts; + delete print_dock_widget; + delete measure_dock_widget; + if (color_dock_widget) + delete color_dock_widget; + delete symbol_dock_widget; + if (template_dock_widget) + delete template_dock_widget; + if (tags_dock_widget) + delete tags_dock_widget; + delete cut_hole_menu; + delete mappart_merge_act; + delete mappart_merge_menu; + delete mappart_move_menu; + for (TemplatePositionDockWidget* widget : qAsConst(template_position_widgets)) + delete widget; + delete gps_display; + delete gps_track_recorder; + delete compass_display; + delete map; +} + +bool MapEditorController::isInMobileMode() const +{ + return mobile_mode; +} + +void MapEditorController::setTool(MapEditorTool* new_tool) +{ + if (current_tool) + { + if (current_tool->editingInProgress()) + { + current_tool->finishEditing(); + } + current_tool->deleteLater(); + } + + if (!override_tool) + { + map->clearDrawingBoundingBox(); + window->setStatusBarText(QString{}); + } + + current_tool = new_tool; + if (current_tool && !override_tool) + { + current_tool->init(); + } + + if (!override_tool) + map_widget->setTool(current_tool); +} + +void MapEditorController::setEditTool() +{ + if (!current_tool || current_tool->toolType() != MapEditorTool::EditPoint) + setTool(new EditPointTool(this, edit_tool_act)); +} + +void MapEditorController::setOverrideTool(MapEditorTool* new_override_tool) +{ + if (override_tool == new_override_tool) + return; + + if (override_tool) + { + if (override_tool->editingInProgress()) + { + override_tool->finishEditing(); + } + delete override_tool; + } + + map->clearDrawingBoundingBox(); + window->setStatusBarText(QString{}); + + override_tool = new_override_tool; + if (override_tool) + { + override_tool->init(); + } + else if (current_tool) + { + current_tool->init(); + } + + map_widget->setTool(override_tool ? override_tool : current_tool); +} + +MapEditorTool* MapEditorController::getDefaultDrawToolForSymbol(const Symbol* symbol) +{ + if (!symbol) + return new EditPointTool(this, edit_tool_act); + else if (symbol->getType() == Symbol::Point) + return new DrawPointTool(this, draw_point_act); + else if (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Area || symbol->getType() == Symbol::Combined) + return new DrawPathTool(this, draw_path_act, false, true); + else if (symbol->getType() == Symbol::Text) + return new DrawTextTool(this, draw_text_act); + else + Q_ASSERT(false); + return nullptr; +} + + +void MapEditorController::setEditingInProgress(bool value) +{ + if (value != editing_in_progress) + { + editing_in_progress = value; + + // Widgets + map_widget->setGesturesEnabled(!editing_in_progress); + Q_ASSERT(symbol_widget); + symbol_widget->setEnabled(!editing_in_progress); + if (isInMobileMode()) + mobile_symbol_selector_action->setEnabled(!editing_in_progress); + if (color_dock_widget) + color_dock_widget->widget()->setEnabled(!editing_in_progress); + if (mappart_selector_box) + mappart_selector_box->setEnabled(!editing_in_progress); + + // Edit menu + undo_act->setEnabled(!editing_in_progress && map->undoManager().canUndo()); + redo_act->setEnabled(!editing_in_progress && map->undoManager().canRedo()); + updatePasteAvailability(); + select_all_act->setEnabled(!editing_in_progress); + select_nothing_act->setEnabled(!editing_in_progress); + invert_selection_act->setEnabled(!editing_in_progress); + select_by_current_symbol_act->setEnabled(!editing_in_progress); + find_feature->setEnabled(!editing_in_progress); + + // Map menu + georeferencing_act->setEnabled(!editing_in_progress); + scale_map_act->setEnabled(!editing_in_progress); + rotate_map_act->setEnabled(!editing_in_progress); + map_notes_act->setEnabled(!editing_in_progress); + + // Map menu, continued + const int num_parts = map->getNumParts(); + mappart_add_act->setEnabled(!editing_in_progress); + mappart_rename_act->setEnabled(!editing_in_progress && num_parts > 0); + mappart_remove_act->setEnabled(!editing_in_progress && num_parts > 1); + mappart_move_menu->setEnabled(!editing_in_progress && num_parts > 1); + mappart_merge_act->setEnabled(!editing_in_progress && num_parts > 1); + mappart_merge_menu->setEnabled(!editing_in_progress && num_parts > 1); + + // Symbol menu + symbol_set_id_act->setEnabled(!editing_in_progress); + scale_all_symbols_act->setEnabled(!editing_in_progress); + load_symbols_from_act->setEnabled(!editing_in_progress); + load_crt_act->setEnabled(!editing_in_progress); + + updateObjectDependentActions(); + updateSymbolDependentActions(); + updateSymbolAndObjectDependentActions(); + } +} + +bool MapEditorController::isEditingInProgress() const +{ + return editing_in_progress; +} + + +void MapEditorController::setEditorActivity(MapEditorActivity* new_activity) +{ + delete editor_activity; + map->clearActivityBoundingBox(); + + editor_activity = new_activity; + if (editor_activity) + editor_activity->init(); + + map_widget->setActivity(editor_activity); +} + +void MapEditorController::addTemplatePositionDockWidget(Template* temp) +{ + Q_ASSERT(!existsTemplatePositionDockWidget(temp)); + auto dock_widget = new TemplatePositionDockWidget(temp, this, window); + addFloatingDockWidget(dock_widget); + template_position_widgets.insert(temp, dock_widget); +} + +void MapEditorController::removeTemplatePositionDockWidget(Template* temp) +{ + emit templatePositionDockWidgetClosed(temp); + + delete getTemplatePositionDockWidget(temp); + int num_deleted = template_position_widgets.remove(temp); + Q_ASSERT(num_deleted == 1); + Q_UNUSED(num_deleted); +} + +void MapEditorController::showPopupWidget(QWidget* child_widget, const QString& title) +{ + if (mobile_mode) + { + // FIXME: This is used for KeyButtonBar only + // and not related to mobile_mode! + QSize size = child_widget->sizeHint(); + QRect map_widget_rect = map_widget->rect(); + + child_widget->setParent(map_widget); + child_widget->setGeometry( + qMax(0, qRound(map_widget_rect.center().x() - 0.5f * size.width())), + qMax(0, map_widget_rect.bottom() - size.height()), + qMin(size.width(), map_widget_rect.width()), + qMin(size.height(), map_widget_rect.height()) + ); + child_widget->show(); + } + else + { + auto dock_widget = new QDockWidget(title, window); + dock_widget->setFeatures(dock_widget->features() & ~QDockWidget::DockWidgetClosable); + dock_widget->setWidget(child_widget); + + // Show dock in floating state + dock_widget->setFloating(true); + dock_widget->show(); + dock_widget->setGeometry(window->geometry().left() + 40, window->geometry().top() + 100, dock_widget->width(), dock_widget->height()); + } +} + +void MapEditorController::deletePopupWidget(QWidget* child_widget) +{ + if (mobile_mode) + { + delete child_widget; + } + else + { + // Delete the dock widget + delete child_widget->parentWidget(); + } +} + +bool MapEditorController::save(const QString& path) +{ + if (map) + { + if (editing_in_progress) + { + QMessageBox::warning(window, tr("Editing in progress"), tr("The map is currently being edited. Please finish the edit operation before saving.")); + return false; + } + bool success = map->saveTo(path, main_view); + if (success) + window->showStatusBarMessage(tr("Map saved"), 1000); + return success; + } + else + return false; +} + +bool MapEditorController::exportTo(const QString& path, const FileFormat* format) +{ + if (map && !editing_in_progress) + { + return map->exportTo(path, main_view, format); + } + + return false; +} + +bool MapEditorController::load(const QString& path, QWidget* dialog_parent) +{ + if (!dialog_parent) + dialog_parent = window; + + if (!map) + { + map = new Map(); + main_view = new MapView(this, map); + } + + bool success = map->loadFrom(path, dialog_parent, main_view); + if (success) + { + setMapAndView(map, main_view); + } + else + { + delete map; + map = nullptr; + main_view = nullptr; + } + + return success; +} + +void MapEditorController::attach(MainWindow* window) +{ + print_dock_widget = nullptr; + measure_dock_widget = nullptr; + color_dock_widget = nullptr; + symbol_dock_widget = nullptr; + std::function zoom_display_function; + + this->window = window; + if (mode == MapEditor) + { + window->setHasUnsavedChanges(map->hasUnsavedChanges()); + } + connect(map, &Map::hasUnsavedChanged, window, &MainWindow::setHasUnsavedChanges); + +#ifdef Q_OS_ANDROID + QAndroidJniObject::callStaticMethod("org/openorienteering/mapper/MapperActivity", + "lockOrientation", + "()V"); +#endif + if (mobile_mode) + { + window->setWindowState(window->windowState() | Qt::WindowFullScreen); + zoom_display_function = [window](const QString& text) { + window->showStatusBarMessage(text, 1000); + }; + } + else + { + // Add zoom / cursor position field to status bar + auto statusbar_zoom_icon = new QLabel(); + auto fontmetrics = statusbar_zoom_icon->fontMetrics(); + auto pixmap = QPixmap(QLatin1String(":/images/magnifying-glass.png")); + auto scale = qreal(fontmetrics.height()) / pixmap.height(); + if (scale < 0.9 || scale > 1.1) + pixmap = pixmap.scaledToHeight(qRound(scale * pixmap.height()), Qt::SmoothTransformation); + statusbar_zoom_icon->setPixmap(pixmap); + + auto statusbar_zoom_label = new QLabel(); + statusbar_zoom_label->setMinimumWidth(fontmetrics.width(QLatin1String(" 0.333x"))); + statusbar_zoom_label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + statusbar_zoom_label->setFrameShape(QFrame::NoFrame); + + zoom_display_function = [statusbar_zoom_label](const QString& text) { + statusbar_zoom_label->setText(text); + }; + + statusbar_zoom_frame = new QFrame(); +#ifdef Q_OS_WIN + statusbar_zoom_frame->setFrameShape(QFrame::NoFrame); +#else + statusbar_zoom_frame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); +#endif + auto statusbar_zoom_frame_layout = new QHBoxLayout(); + statusbar_zoom_frame_layout->setMargin(0); + statusbar_zoom_frame_layout->setSpacing(0); + statusbar_zoom_frame_layout->addSpacing(1); + statusbar_zoom_frame_layout->addWidget(statusbar_zoom_icon); + statusbar_zoom_frame_layout->addWidget(statusbar_zoom_label); + statusbar_zoom_frame->setLayout(statusbar_zoom_frame_layout); + + statusbar_cursorpos_label = new QLabel(); +#ifdef Q_OS_WIN + statusbar_cursorpos_label->setFrameShape(QFrame::NoFrame); +#else + statusbar_cursorpos_label->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); +#endif + statusbar_cursorpos_label->setMinimumWidth(fontmetrics.width(QLatin1String("-3,333.33 -333.33 (mm)"))); + statusbar_cursorpos_label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + + window->statusBar()->addPermanentWidget(statusbar_zoom_frame); + window->statusBar()->addPermanentWidget(statusbar_cursorpos_label); + } + + // Create map widget + map_widget = new MapWidget(mode == MapEditor, mode == SymbolEditor); + map_widget->setMapView(main_view); + map_widget->setZoomDisplay(zoom_display_function); + + // Create menu and toolbar together, so actions can be inserted into one or both + if (mode == MapEditor) + { + gps_display = new GPSDisplay(map_widget, map->getGeoreferencing()); + gps_marker_display = new GPSTemporaryMarkers(map_widget, gps_display); + + createActions(); + if (mobile_mode) + { + createMobileGUI(); + compass_display = new CompassDisplay(map_widget->parentWidget()); + compass_display->setVisible(false); + } + else + { + map_widget->setCursorposLabel(statusbar_cursorpos_label); + window->setCentralWidget(map_widget); + + createMenuAndToolbars(); + restoreWindowState(); + } + } + else if (mode == SymbolEditor) + { + map_widget->setCursorposLabel(statusbar_cursorpos_label); + window->setCentralWidget(map_widget); + } + + // Update enabled/disabled state for the tools ... + updateWidgets(); + templateAvailabilityChanged(); + // ... and make sure it is kept up to date for copy/paste + connect(QApplication::clipboard(), &QClipboard::changed, this, &MapEditorController::clipboardChanged); + clipboardChanged(QClipboard::Clipboard); + + if (mode == MapEditor) + { + // Create/show the dock widgets + if (mobile_mode) + { + createSymbolWidget(window); + } + else + { + symbol_window_act->trigger(); + createColorWindow(); + createTemplateWindow(); + createTagEditor(); + + if (map->getNumColors() == 0) + QTimer::singleShot(0, color_dock_widget, SLOT(show())); // clazy:exclude=old-style-connect + } + + // Auto-select the edit tool + edit_tool_act->setChecked(true); + setEditTool(); + + // Set the coordinates display mode + coordsDisplayChanged(); + } + else + { + // Set the coordinates display mode + map_widget->setCoordsDisplay(MapWidget::MAP_COORDS); + } +} + +QAction* MapEditorController::newAction(const char* id, const QString &tr_text, QObject* receiver, const char* slot, const char* icon, const QString& tr_tip, const char* whats_this_link) +{ + Q_ASSERT(window); // Qt documentation recommends that actions are created as children of the window they are used in. + QAction* action = new QAction(icon ? QIcon(QLatin1String(":/images/") + QLatin1String(icon)) : QIcon(), tr_text, window); + if (!tr_tip.isEmpty()) action->setStatusTip(tr_tip); + if (whats_this_link) action->setWhatsThis(Util::makeWhatThis(whats_this_link)); + if (receiver) QObject::connect(action, SIGNAL(triggered()), receiver, slot); + actionsById[id] = action; + action->setMenuRole(QAction::NoRole); + return action; +} + +QAction* MapEditorController::newCheckAction(const char* id, const QString &tr_text, QObject* receiver, const char* slot, const char* icon, const QString& tr_tip, const char* whats_this_link) +{ + auto action = newAction(id, tr_text, nullptr, nullptr, icon, tr_tip, whats_this_link); + action->setCheckable(true); + if (receiver) QObject::connect(action, SIGNAL(triggered(bool)), receiver, slot); + return action; +} + +QAction* MapEditorController::newToolAction(const char* id, const QString &tr_text, QObject* receiver, const char* slot, const char* icon, const QString& tr_tip, const char* whats_this_link) +{ + Q_ASSERT(window); // Qt documentation recommends that actions are created as children of the window they are used in. + QAction* action = new MapEditorToolAction(icon ? QIcon(QLatin1String(":/images/") + QLatin1String(icon)) : QIcon(), tr_text, window); + if (!tr_tip.isEmpty()) action->setStatusTip(tr_tip); + if (whats_this_link) action->setWhatsThis(Util::makeWhatThis(whats_this_link)); + if (receiver) QObject::connect(action, SIGNAL(activated()), receiver, slot); + actionsById[id] = action; + return action; +} + +QAction* MapEditorController::findAction(const char* id) +{ + if (!actionsById.contains(id)) return actionsById[""]; + else return actionsById[id]; +} + +QAction* MapEditorController::getAction(const char* id) +{ + return actionsById.contains(id) ? actionsById[id] : nullptr; +} + +void MapEditorController::assignKeyboardShortcuts() +{ + // Standard keyboard shortcuts + findAction("print")->setShortcut(QKeySequence::Print); + findAction("undo")->setShortcut(QKeySequence::Undo); + findAction("redo")->setShortcut(QKeySequence::Redo); + findAction("cut")->setShortcut(QKeySequence::Cut); + findAction("copy")->setShortcut(QKeySequence::Copy); + findAction("paste")->setShortcut(QKeySequence::Paste); + + // Custom keyboard shortcuts + + // QKeySequence::Deselect is empty for Windows, so be explicit all select-* + findAction("select-all")->setShortcut(QKeySequence(tr("Ctrl+A"))); + findAction("select-nothing")->setShortcut(QKeySequence(tr("Ctrl+Shift+A"))); + findAction("invert-selection")->setShortcut(QKeySequence(tr("Ctrl+I"))); + + findAction("showgrid")->setShortcut(QKeySequence(tr("G"))); + findAction("zoomin")->setShortcuts(QList() << QKeySequence(Qt::Key_F7) << QKeySequence(Qt::Key_Plus) << QKeySequence(Qt::KeypadModifier + Qt::Key_Plus)); + findAction("zoomout")->setShortcuts(QList() << QKeySequence(Qt::Key_F8) << QKeySequence(Qt::Key_Minus) << QKeySequence(Qt::KeypadModifier + Qt::Key_Minus)); + findAction("hatchareasview")->setShortcut(QKeySequence(Qt::Key_F2)); + findAction("baselineview")->setShortcut(QKeySequence(Qt::Key_F3)); + findAction("hidealltemplates")->setShortcut(QKeySequence(Qt::Key_F10)); + findAction("overprintsimulation")->setShortcut(QKeySequence(Qt::Key_F4)); + findAction("fullscreen")->setShortcut(QKeySequence(Qt::Key_F11)); + tags_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_6)); + color_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_7)); + symbol_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_8)); + template_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_9)); + + findAction("editobjects")->setShortcut(QKeySequence(tr("E"))); + findAction("editlines")->setShortcut(QKeySequence(tr("L"))); + findAction("drawpoint")->setShortcut(QKeySequence(tr("S"))); + findAction("drawpath")->setShortcut(QKeySequence(tr("P"))); + findAction("drawcircle")->setShortcut(QKeySequence(tr("O"))); + findAction("drawrectangle")->setShortcut(QKeySequence(tr("Ctrl+R"))); + findAction("drawfill")->setShortcut(QKeySequence(tr("F"))); + findAction("drawtext")->setShortcut(QKeySequence(tr("T"))); + + findAction("duplicate")->setShortcut(QKeySequence(tr("D"))); + findAction("switchsymbol")->setShortcut(QKeySequence(tr("Ctrl+G"))); + findAction("fillborder")->setShortcut(QKeySequence(tr("Ctrl+F"))); + findAction("switchdashes")->setShortcut(QKeySequence(tr("Ctrl+D"))); + findAction("connectpaths")->setShortcut(QKeySequence(tr("C"))); + findAction("rotateobjects")->setShortcut(QKeySequence(tr("R"))); + findAction("scaleobjects")->setShortcut(QKeySequence(tr("Z"))); + findAction("cutobject")->setShortcut(QKeySequence(tr("K"))); + findAction("cuthole")->setShortcut(QKeySequence(tr("H"))); + findAction("measure")->setShortcut(QKeySequence(tr("M"))); + findAction("booleanunion")->setShortcut(QKeySequence(tr("U"))); + findAction("converttocurves")->setShortcut(QKeySequence(tr("N"))); + findAction("simplify")->setShortcut(QKeySequence(tr("Ctrl+M"))); +} + +void MapEditorController::createActions() +{ + // Define all the actions, saving them into variables as necessary. Can also get them by ID. +#ifdef QT_PRINTSUPPORT_LIB + auto print_act_mapper = new QSignalMapper(this); + connect(print_act_mapper, QOverload::of(&QSignalMapper::mapped), this, QOverload::of(&MapEditorController::printClicked)); + print_act = newAction("print", tr("Print..."), print_act_mapper, SLOT(map()), "print.png", QString{}, "file_menu.html"); + print_act_mapper->setMapping(print_act, PrintWidget::PRINT_TASK); + export_image_act = newAction("export-image", tr("&Image"), print_act_mapper, SLOT(map()), nullptr, QString{}, "file_menu.html"); + print_act_mapper->setMapping(export_image_act, PrintWidget::EXPORT_IMAGE_TASK); + export_pdf_act = newAction("export-pdf", tr("&PDF"), print_act_mapper, SLOT(map()), nullptr, QString{}, "file_menu.html"); + print_act_mapper->setMapping(export_pdf_act, PrintWidget::EXPORT_PDF_TASK); +#else + print_act = nullptr; + export_image_act = nullptr; + export_pdf_act = nullptr; +#endif + + undo_act = newAction("undo", tr("Undo"), this, SLOT(undo()), "undo.png", tr("Undo the last step"), "edit_menu.html"); + redo_act = newAction("redo", tr("Redo"), this, SLOT(redo()), "redo.png", tr("Redo the last step"), "edit_menu.html"); + cut_act = newAction("cut", tr("Cu&t"), this, SLOT(cut()), "cut.png", QString{}, "edit_menu.html"); + cut_act->setMenuRole(QAction::TextHeuristicRole); + copy_act = newAction("copy", tr("C&opy"), this, SLOT(copy()), "copy.png", QString{}, "edit_menu.html"); + copy_act->setMenuRole(QAction::TextHeuristicRole); + paste_act = newAction("paste", tr("&Paste"), this, SLOT(paste()), "paste", QString{}, "edit_menu.html"); + paste_act->setMenuRole(QAction::TextHeuristicRole); + delete_act = newAction("delete", tr("Delete"), this, SLOT(deleteClicked()), "delete.png", QString{}, "toolbars.html#delete"); + select_all_act = newAction("select-all", tr("Select all"), this, SLOT(selectAll()), nullptr, QString{}, "edit_menu.html"); + select_nothing_act = newAction("select-nothing", tr("Select nothing"), this, SLOT(selectNothing()), nullptr, QString{}, "edit_menu.html"); + invert_selection_act = newAction("invert-selection", tr("Invert selection"), this, SLOT(invertSelection()), nullptr, QString{}, "edit_menu.html"); + select_by_current_symbol_act = newAction("select-by-symbol", QApplication::translate("OpenOrienteering::SymbolRenderWidget", "Select all objects with selected symbols"), this, SLOT(selectByCurrentSymbols()), nullptr, QString{}, "edit_menu.html"); + find_feature.reset(new MapFindFeature(*this)); + + clear_undo_redo_history_act = newAction("clearundoredohistory", tr("Clear undo / redo history"), this, SLOT(clearUndoRedoHistory()), nullptr, tr("Clear the undo / redo history to reduce map file size."), "edit_menu.html"); + + show_grid_act = newCheckAction("showgrid", tr("Show grid"), this, SLOT(showGrid()), "grid.png", QString{}, "grid.html"); + configure_grid_act = newAction("configuregrid", tr("Configure grid..."), this, SLOT(configureGrid()), "grid.png", QString{}, "grid.html"); + pan_act = newToolAction("panmap", tr("Pan"), this, SLOT(pan()), "move.png", QString{}, "view_menu.html"); + move_to_gps_pos_act = newAction("movegps", tr("Move to my location"), this, SLOT(moveToGpsPos()), "move-to-gps.png", QString{}, "view_menu.html"); + move_to_gps_pos_act->setEnabled(false); + zoom_in_act = newAction("zoomin", tr("Zoom in"), this, SLOT(zoomIn()), "view-zoom-in.png", QString{}, "view_menu.html"); + zoom_out_act = newAction("zoomout", tr("Zoom out"), this, SLOT(zoomOut()), "view-zoom-out.png", QString{}, "view_menu.html"); + show_all_act = newAction("showall", tr("Show whole map"), this, SLOT(showWholeMap()), "view-show-all.png", QString{}, "view_menu.html"); + fullscreen_act = newAction("fullscreen", tr("Toggle fullscreen mode"), window, SLOT(toggleFullscreenMode()), nullptr, QString{}, "view_menu.html"); + custom_zoom_act = newAction("setzoom", tr("Set custom zoom factor..."), this, SLOT(setCustomZoomFactorClicked()), nullptr, QString{}, "view_menu.html"); + + hatch_areas_view_act = newCheckAction("hatchareasview", tr("Hatch areas"), this, SLOT(hatchAreas(bool)), nullptr, QString{}, "view_menu.html"); + baseline_view_act = newCheckAction("baselineview", tr("Baseline view"), this, SLOT(baselineView(bool)), nullptr, QString{}, "view_menu.html"); + hide_all_templates_act = newCheckAction("hidealltemplates", tr("Hide all templates"), this, SLOT(hideAllTemplates(bool)), nullptr, QString{}, "view_menu.html"); + overprinting_simulation_act = newCheckAction("overprintsimulation", tr("Overprinting simulation"), this, SLOT(overprintingSimulation(bool)), nullptr, QString{}, "view_menu.html"); + + symbol_window_act = newCheckAction("symbolwindow", tr("Symbol window"), this, SLOT(showSymbolWindow(bool)), "symbols.png", tr("Show/Hide the symbol window"), "symbol_dock_widget.html"); + color_window_act = newCheckAction("colorwindow", tr("Color window"), this, SLOT(showColorWindow(bool)), "colors.png", tr("Show/Hide the color window"), "color_dock_widget.html"); + symbol_set_id_act = newAction("symbol-set-id", tr("Symbol set ID..."), this, SLOT(symbolSetIdClicked()), nullptr, tr("Edit the symbol set ID"), nullptr); + load_symbols_from_act = newAction("loadsymbols", tr("Replace symbol set..."), this, SLOT(loadSymbolsFromClicked()), nullptr, tr("Replace the symbols with those from another map file"), "symbol_replace_dialog.html"); + load_crt_act = newAction("loadcrt", tr("Load CRT file..."), this, SLOT(loadCrtClicked()), nullptr, tr("Assign new symbols by cross-reference table"), "symbol_replace_dialog.html"); + /*QAction* load_colors_from_act = newAction("loadcolors", tr("Load colors from..."), this, SLOT(loadColorsFromClicked()), nullptr, tr("Replace the colors with those from another map file"));*/ + + scale_all_symbols_act = newAction("scaleall", tr("Scale all symbols..."), this, SLOT(scaleAllSymbolsClicked()), nullptr, tr("Scale the whole symbol set"), "map_menu.html"); + georeferencing_act = newAction("georef", tr("Georeferencing..."), this, SLOT(editGeoreferencing()), nullptr, QString{}, "georeferencing.html"); + scale_map_act = newAction("scalemap", tr("Change map scale..."), this, SLOT(scaleMapClicked()), "tool-scale.png", tr("Change the map scale and adjust map objects and symbol sizes"), "map_menu.html"); + rotate_map_act = newAction("rotatemap", tr("Rotate map..."), this, SLOT(rotateMapClicked()), "tool-rotate.png", tr("Rotate the whole map"), "map_menu.html"); + map_notes_act = newAction("mapnotes", tr("Map notes..."), this, SLOT(mapNotesClicked()), nullptr, QString{}, "map_menu.html"); + + template_window_act = newCheckAction("templatewindow", tr("Template setup window"), this, SLOT(showTemplateWindow(bool)), "templates", tr("Show/Hide the template window"), "templates_menu.html"); + //QAction* template_config_window_act = newCheckAction("templateconfigwindow", tr("Template configurations window"), this, SLOT(showTemplateConfigurationsWindow(bool)), "window-new", tr("Show/Hide the template configurations window")); + //QAction* template_visibilities_window_act = newCheckAction("templatevisibilitieswindow", tr("Template visibilities window"), this, SLOT(showTemplateVisbilitiesWindow(bool)), "window-new", tr("Show/Hide the template visibilities window")); + open_template_act = newAction("opentemplate", tr("Open template..."), this, SLOT(openTemplateClicked()), nullptr, QString{}, "templates_menu.html"); + reopen_template_act = newAction("reopentemplate", tr("Reopen template..."), this, SLOT(reopenTemplateClicked()), nullptr, QString{}, "templates_menu.html"); + + tags_window_act = newCheckAction("tagswindow", tr("Tag editor"), this, SLOT(showTagsWindow(bool)), "window-new", tr("Show/Hide the tag editor window"), "tag_editor.html"); + + edit_tool_act = newToolAction("editobjects", tr("Edit objects"), this, SLOT(editToolClicked()), "tool-edit.png", QString{}, "toolbars.html#tool_edit_point"); + edit_line_tool_act = newToolAction("editlines", tr("Edit lines"), this, SLOT(editLineToolClicked()), "tool-edit-line.png", QString{}, "toolbars.html#tool_edit_line"); + draw_point_act = newToolAction("drawpoint", tr("Set point objects"), this, SLOT(drawPointClicked()), "draw-point.png", QString{}, "toolbars.html#tool_draw_point"); + draw_path_act = newToolAction("drawpath", tr("Draw paths"), this, SLOT(drawPathClicked()), "draw-path.png", QString{}, "toolbars.html#tool_draw_path"); + draw_circle_act = newToolAction("drawcircle", tr("Draw circles and ellipses"), this, SLOT(drawCircleClicked()), "draw-circle.png", QString{}, "toolbars.html#tool_draw_circle"); + draw_rectangle_act = newToolAction("drawrectangle", tr("Draw rectangles"), this, SLOT(drawRectangleClicked()), "draw-rectangle.png", QString{}, "toolbars.html#tool_draw_rectangle"); + draw_freehand_act = newToolAction("drawfreehand", tr("Draw free-handedly"), this, SLOT(drawFreehandClicked()), "draw-freehand.png", QString{}, "toolbars.html#tool_draw_freehand"); // TODO: documentation + draw_fill_act = newToolAction("drawfill", tr("Fill bounded areas"), this, SLOT(drawFillClicked()), "tool-fill.png", QString{}, "toolbars.html#tool_draw_fill"); // TODO: documentation + draw_text_act = newToolAction("drawtext", tr("Write text"), this, SLOT(drawTextClicked()), "draw-text.png", QString{}, "toolbars.html#tool_draw_text"); + duplicate_act = newAction("duplicate", tr("Duplicate"), this, SLOT(duplicateClicked()), "tool-duplicate.png", QString{}, "toolbars.html#duplicate"); + switch_symbol_act = newAction("switchsymbol", tr("Switch symbol"), this, SLOT(switchSymbolClicked()), "tool-switch-symbol.png", QString{}, "toolbars.html#switch_symbol"); + fill_border_act = newAction("fillborder", tr("Fill / Create border"), this, SLOT(fillBorderClicked()), "tool-fill-border.png", QString{}, "toolbars.html#fill_create_border"); + switch_dashes_act = newAction("switchdashes", tr("Switch dash direction"), this, SLOT(switchDashesClicked()), "tool-switch-dashes", QString{}, "toolbars.html#switch_dashes"); + connect_paths_act = newAction("connectpaths", tr("Connect paths"), this, SLOT(connectPathsClicked()), "tool-connect-paths.png", QString{}, "toolbars.html#connect"); + + cut_tool_act = newToolAction("cutobject", tr("Cut object"), this, SLOT(cutClicked()), "tool-cut.png", QString{}, "toolbars.html#cut_tool"); + cut_hole_act = newToolAction("cuthole", tr("Cut free form hole"), this, SLOT(cutHoleClicked()), "tool-cut-hole.png", QString{}, "toolbars.html#cut_hole"); // cut hole using a path + cut_hole_circle_act = newToolAction("cutholecircle", tr("Cut round hole"), this, SLOT(cutHoleCircleClicked()), "tool-cut-hole.png", QString{}, "toolbars.html#cut_hole"); + cut_hole_rectangle_act = newToolAction("cutholerectangle", tr("Cut rectangular hole"), this, SLOT(cutHoleRectangleClicked()), "tool-cut-hole.png", QString{}, "toolbars.html#cut_hole"); + cut_hole_menu = new QMenu(tr("Cut hole")); + cut_hole_menu->menuAction()->setMenuRole(QAction::NoRole); + cut_hole_menu->setIcon(QIcon(QString::fromLatin1(":/images/tool-cut-hole.png"))); + cut_hole_menu->addAction(cut_hole_act); + cut_hole_menu->addAction(cut_hole_circle_act); + cut_hole_menu->addAction(cut_hole_rectangle_act); + + rotate_act = newToolAction("rotateobjects", tr("Rotate objects"), this, SLOT(rotateClicked()), "tool-rotate.png", QString{}, "toolbars.html#rotate"); + rotate_pattern_act = newToolAction("rotatepatterns", tr("Rotate pattern"), this, SLOT(rotatePatternClicked()), "tool-rotate-pattern.png", QString{}, "toolbars.html#tool_rotate_pattern"); + scale_act = newToolAction("scaleobjects", tr("Scale objects"), this, SLOT(scaleClicked()), "tool-scale.png", QString{}, "toolbars.html#scale"); + measure_act = newCheckAction("measure", tr("Measure lengths and areas"), this, SLOT(measureClicked(bool)), "tool-measure.png", QString{}, "toolbars.html#measure"); + boolean_union_act = newAction("booleanunion", tr("Unify areas"), this, SLOT(booleanUnionClicked()), "tool-boolean-union.png", QString{}, "toolbars.html#unify_areas"); + boolean_intersection_act = newAction("booleanintersection", tr("Intersect areas"), this, SLOT(booleanIntersectionClicked()), "tool-boolean-intersection.png", QString{}, "toolbars.html#intersect_areas"); + boolean_difference_act = newAction("booleandifference", tr("Cut away from area"), this, SLOT(booleanDifferenceClicked()), "tool-boolean-difference.png", QString{}, "toolbars.html#area_difference"); + boolean_xor_act = newAction("booleanxor", tr("Area XOr"), this, SLOT(booleanXOrClicked()), "tool-boolean-xor.png", QString{}, "toolbars.html#area_xor"); + boolean_merge_holes_act = newAction("booleanmergeholes", tr("Merge area holes"), this, SLOT(booleanMergeHolesClicked()), "tool-boolean-merge-holes.png", QString{}, "toolbars.html#area_merge_holes"); // TODO:documentation + convert_to_curves_act = newAction("converttocurves", tr("Convert to curves"), this, SLOT(convertToCurvesClicked()), "tool-convert-to-curves.png", QString{}, "toolbars.html#convert_to_curves"); + simplify_path_act = newAction("simplify", tr("Simplify path"), this, SLOT(simplifyPathClicked()), "tool-simplify-path.png", QString{}, "toolbars.html#simplify_path"); + cutout_physical_act = newToolAction("cutoutphysical", tr("Cutout"), this, SLOT(cutoutPhysicalClicked()), "tool-cutout-physical.png", QString{}, "toolbars.html#cutout_physical"); + cutaway_physical_act = newToolAction("cutawayphysical", tr("Cut away"), this, SLOT(cutawayPhysicalClicked()), "tool-cutout-physical-inner.png", QString{}, "toolbars.html#cutaway_physical"); + distribute_points_act = newAction("distributepoints", tr("Distribute points along path"), this, SLOT(distributePointsClicked()), "tool-distribute-points.png", QString{}, "toolbars.html#distribute_points"); // TODO: write documentation + + paint_on_template_act = new QAction(QIcon(QString::fromLatin1(":/images/pencil.png")), tr("Paint on template"), this); + paint_on_template_act->setMenuRole(QAction::NoRole); + paint_on_template_act->setCheckable(true); + paint_on_template_act->setWhatsThis(Util::makeWhatThis("toolbars.html#draw_on_template")); + connect(paint_on_template_act, &QAction::triggered, this, &MapEditorController::paintOnTemplateClicked); + + paint_on_template_settings_act = new QAction(QIcon(QString::fromLatin1(":/images/paint-on-template-settings.png")), tr("Paint on template settings"), this); + paint_on_template_settings_act->setMenuRole(QAction::NoRole); + paint_on_template_settings_act->setWhatsThis(Util::makeWhatThis("toolbars.html#draw_on_template")); + connect(paint_on_template_settings_act, &QAction::triggered, this, &MapEditorController::paintOnTemplateSelectClicked); + + touch_cursor_action = newCheckAction("touchcursor", tr("Enable touch cursor"), map_widget, SLOT(enableTouchCursor(bool)), "tool-touch-cursor.png", QString{}, "toolbars.html#touch_cursor"); // TODO: write documentation + gps_display_action = newCheckAction("gpsdisplay", tr("Enable GPS display"), this, SLOT(enableGPSDisplay(bool)), "tool-gps-display.png", QString{}, "toolbars.html#gps_display"); // TODO: write documentation + gps_display_action->setEnabled(map->getGeoreferencing().isValid() && ! map->getGeoreferencing().isLocal()); + gps_distance_rings_action = newCheckAction("gpsdistancerings", tr("Enable GPS distance rings"), this, SLOT(enableGPSDistanceRings(bool)), "gps-distance-rings.png", QString{}, "toolbars.html#gps_distance_rings"); // TODO: write documentation + gps_distance_rings_action->setEnabled(false); + draw_point_gps_act = newToolAction("drawpointgps", tr("Set point object at GPS position"), this, SLOT(drawPointGPSClicked()), "draw-point-gps.png", QString{}, "toolbars.html#tool_draw_point_gps"); // TODO: write documentation + draw_point_gps_act->setEnabled(false); + gps_temporary_point_act = newAction("gpstemporarypoint", tr("Set temporary marker at GPS position"), this, SLOT(gpsTemporaryPointClicked()), "gps-temporary-point.png", QString{}, "toolbars.html#gps_temporary_point"); // TODO: write documentation + gps_temporary_point_act->setEnabled(false); + gps_temporary_path_act = newCheckAction("gpstemporarypath", tr("Create temporary path at GPS position"), this, SLOT(gpsTemporaryPathClicked(bool)), "gps-temporary-path.png", QString{}, "toolbars.html#gps_temporary_path"); // TODO: write documentation + gps_temporary_path_act->setEnabled(false); + gps_temporary_clear_act = newAction("gpstemporaryclear", tr("Clear temporary GPS markers"), this, SLOT(gpsTemporaryClearClicked()), "gps-temporary-clear.png", QString{}, "toolbars.html#gps_temporary_clear"); // TODO: write documentation + gps_temporary_clear_act->setEnabled(false); + + compass_action = newCheckAction("compassdisplay", tr("Enable compass display"), this, SLOT(enableCompassDisplay(bool)), "compass.png", QString{}, "toolbars.html#compass_display"); // TODO: write documentation + align_map_with_north_act = newCheckAction("alignmapwithnorth", tr("Align map with north"), this, SLOT(alignMapWithNorth(bool)), "rotate-map.png", QString{}, "toolbars.html#align_map_with_north"); // TODO: write documentation + + mappart_add_act = newAction("addmappart", tr("Add new part..."), this, SLOT(addMapPart())); + mappart_rename_act = newAction("renamemappart", tr("Rename current part..."), this, SLOT(renameMapPart())); + mappart_remove_act = newAction("removemappart", tr("Remove current part"), this, SLOT(removeMapPart())); + mappart_merge_act = newAction("mergemapparts", tr("Merge all parts"), this, SLOT(mergeAllMapParts())); + + import_act = newAction("import", tr("Import..."), this, SLOT(importClicked()), nullptr, QString{}, "file_menu.html"); + + map_coordinates_act = new QAction(tr("Map coordinates"), this); + map_coordinates_act->setCheckable(true); + projected_coordinates_act = new QAction(tr("Projected coordinates"), this); + projected_coordinates_act->setCheckable(true); + geographic_coordinates_act = new QAction(tr("Latitude/Longitude (Dec)"), this); + geographic_coordinates_act->setCheckable(true); + geographic_coordinates_dms_act = new QAction(tr("Latitude/Longitude (DMS)"), this); + geographic_coordinates_dms_act->setCheckable(true); + auto coordinates_group = new QActionGroup(this); + coordinates_group->addAction(map_coordinates_act); + coordinates_group->addAction(projected_coordinates_act); + coordinates_group->addAction(geographic_coordinates_act); + coordinates_group->addAction(geographic_coordinates_dms_act); + QObject::connect(coordinates_group, &QActionGroup::triggered, this, &MapEditorController::coordsDisplayChanged); + map_coordinates_act->setChecked(true); + QObject::connect(&map->getGeoreferencing(), &Georeferencing::projectionChanged, this, &MapEditorController::projectionChanged); + projectionChanged(); + + copy_coords_act = newAction("copy-coords", tr("Copy position"), this, SLOT(copyDisplayedCoords()), "copy-coords.png", tr("Copy position to clipboard.")); +} + +void MapEditorController::createMenuAndToolbars() +{ + statusbar_cursorpos_label->setContextMenuPolicy(Qt::ActionsContextMenu); + statusbar_cursorpos_label->addAction(map_coordinates_act); + statusbar_cursorpos_label->addAction(projected_coordinates_act); + statusbar_cursorpos_label->addAction(geographic_coordinates_act); + statusbar_cursorpos_label->addAction(geographic_coordinates_dms_act); + + // Refactored so we can do custom key bindings in the future + assignKeyboardShortcuts(); + + // Extend file menu + QMenu* file_menu = window->getFileMenu(); + QAction* insertion_act = window->getFileMenuExtensionAct(); +#ifdef QT_PRINTSUPPORT_LIB + file_menu->insertAction(insertion_act, print_act); + file_menu->insertSeparator(insertion_act); + insertion_act = print_act; +#endif + file_menu->insertAction(insertion_act, import_act); +#ifdef QT_PRINTSUPPORT_LIB + QMenu* export_menu = new QMenu(tr("&Export as..."), file_menu); + export_menu->menuAction()->setMenuRole(QAction::NoRole); + export_menu->addAction(export_image_act); + export_menu->addAction(export_pdf_act); + file_menu->insertMenu(insertion_act, export_menu); +#endif + file_menu->insertSeparator(insertion_act); + + // Edit menu + QMenu* edit_menu = window->menuBar()->addMenu(tr("&Edit")); + edit_menu->setWhatsThis(Util::makeWhatThis("edit_menu.html")); + edit_menu->addAction(undo_act); + edit_menu->addAction(redo_act); + edit_menu->addSeparator(); + edit_menu->addAction(cut_act); + edit_menu->addAction(copy_act); + edit_menu->addAction(paste_act); + edit_menu->addAction(delete_act); + edit_menu->addSeparator(); + edit_menu->addAction(select_all_act); + edit_menu->addAction(select_nothing_act); + edit_menu->addAction(invert_selection_act); + edit_menu->addAction(select_by_current_symbol_act); + edit_menu->addSeparator(); + edit_menu->addAction(find_feature->showDialogAction()); + edit_menu->addAction(find_feature->findNextAction()); + edit_menu->addSeparator(); + edit_menu->addAction(clear_undo_redo_history_act); + + // View menu + QMenu* view_menu = window->menuBar()->addMenu(tr("&View")); + view_menu->setWhatsThis(Util::makeWhatThis("view_menu.html")); + view_menu->addAction(pan_act); + view_menu->addAction(zoom_in_act); + view_menu->addAction(zoom_out_act); + view_menu->addAction(show_all_act); + view_menu->addAction(custom_zoom_act); + view_menu->addSeparator(); + view_menu->addAction(show_grid_act); + view_menu->addAction(hatch_areas_view_act); + view_menu->addAction(baseline_view_act); + view_menu->addAction(overprinting_simulation_act); + view_menu->addAction(hide_all_templates_act); + view_menu->addSeparator(); + QMenu* coordinates_menu = new QMenu(tr("Display coordinates as..."), view_menu); + coordinates_menu->menuAction()->setMenuRole(QAction::NoRole); + coordinates_menu->addAction(map_coordinates_act); + coordinates_menu->addAction(projected_coordinates_act); + coordinates_menu->addAction(geographic_coordinates_act); + coordinates_menu->addAction(geographic_coordinates_dms_act); + view_menu->addMenu(coordinates_menu); + view_menu->addSeparator(); + view_menu->addAction(fullscreen_act); + view_menu->addSeparator(); + toolbars_menu = view_menu->addMenu(tr("Toolbars")); + view_menu->addAction(tags_window_act); + view_menu->addAction(color_window_act); + view_menu->addAction(symbol_window_act); + view_menu->addAction(template_window_act); + + // Tools menu + QMenu *tools_menu = window->menuBar()->addMenu(tr("&Tools")); + tools_menu->setWhatsThis(Util::makeWhatThis("tools_menu.html")); + tools_menu->addAction(edit_tool_act); + tools_menu->addAction(edit_line_tool_act); + tools_menu->addAction(draw_point_act); + tools_menu->addAction(draw_path_act); + tools_menu->addAction(draw_circle_act); + tools_menu->addAction(draw_rectangle_act); + tools_menu->addAction(draw_freehand_act); + tools_menu->addAction(draw_fill_act); + tools_menu->addAction(draw_text_act); + tools_menu->addAction(duplicate_act); + tools_menu->addAction(switch_symbol_act); + tools_menu->addAction(fill_border_act); + tools_menu->addAction(switch_dashes_act); + tools_menu->addAction(connect_paths_act); + tools_menu->addAction(boolean_union_act); + tools_menu->addAction(boolean_intersection_act); + tools_menu->addAction(boolean_difference_act); + tools_menu->addAction(boolean_xor_act); + tools_menu->addAction(boolean_merge_holes_act); + tools_menu->addAction(cut_tool_act); + tools_menu->addMenu(cut_hole_menu); + tools_menu->addAction(rotate_act); + tools_menu->addAction(rotate_pattern_act); + tools_menu->addAction(scale_act); + tools_menu->addAction(measure_act); + tools_menu->addAction(convert_to_curves_act); + tools_menu->addAction(simplify_path_act); + tools_menu->addAction(cutout_physical_act); + tools_menu->addAction(cutaway_physical_act); + tools_menu->addAction(distribute_points_act); + tools_menu->addAction(touch_cursor_action); + + // Map menu + QMenu* map_menu = window->menuBar()->addMenu(tr("M&ap")); + map_menu->setWhatsThis(Util::makeWhatThis("map_menu.html")); + map_menu->addAction(georeferencing_act); + map_menu->addAction(configure_grid_act); + map_menu->addSeparator(); + map_menu->addAction(scale_map_act); + map_menu->addAction(rotate_map_act); + map_menu->addAction(map_notes_act); + map_menu->addSeparator(); + updateMapPartsUI(); + map_menu->addAction(mappart_add_act); + map_menu->addAction(mappart_rename_act); + map_menu->addAction(mappart_remove_act); + map_menu->addMenu(mappart_move_menu); + map_menu->addMenu(mappart_merge_menu); + map_menu->addAction(mappart_merge_act); + + // Symbols menu + QMenu* symbols_menu = window->menuBar()->addMenu(tr("Sy&mbols")); + symbols_menu->setWhatsThis(Util::makeWhatThis("symbols_menu.html")); + symbols_menu->addAction(symbol_window_act); + symbols_menu->addAction(color_window_act); + symbols_menu->addSeparator(); + symbols_menu->addAction(symbol_set_id_act); + symbols_menu->addAction(scale_all_symbols_act); + symbols_menu->addAction(load_symbols_from_act); + symbols_menu->addAction(load_crt_act); + /*symbols_menu->addAction(load_colors_from_act);*/ + + // Templates menu + QMenu* template_menu = window->menuBar()->addMenu(tr("&Templates")); + template_menu->setWhatsThis(Util::makeWhatThis("templates_menu.html")); + template_menu->addAction(template_window_act); + /*template_menu->addAction(template_config_window_act); + template_menu->addAction(template_visibilities_window_act);*/ + template_menu->addSeparator(); + template_menu->addAction(open_template_act); + template_menu->addAction(reopen_template_act); + + // Extend and activate general toolbar + QToolBar* main_toolbar = window->getGeneralToolBar(); +#ifdef QT_PRINTSUPPORT_LIB + main_toolbar->addAction(print_act); + main_toolbar->addSeparator(); +#endif + main_toolbar->addAction(cut_act); + main_toolbar->addAction(copy_act); + main_toolbar->addAction(paste_act); + main_toolbar->addSeparator(); + main_toolbar->addAction(undo_act); + main_toolbar->addAction(redo_act); + window->addToolBar(main_toolbar); + + // View toolbar + toolbar_view = window->addToolBar(tr("View")); + toolbar_view->setObjectName(QString::fromLatin1("View toolbar")); + auto grid_button = new QToolButton(); + grid_button->setCheckable(true); + grid_button->setDefaultAction(show_grid_act); + grid_button->setPopupMode(QToolButton::MenuButtonPopup); + auto grid_menu = new QMenu(grid_button); + grid_menu->addAction(tr("Configure grid...")); + grid_button->setMenu(grid_menu); + connect(grid_menu, &QMenu::triggered, this, &MapEditorController::configureGrid); + toolbar_view->addWidget(grid_button); + toolbar_view->addSeparator(); + toolbar_view->addAction(pan_act); + toolbar_view->addAction(zoom_in_act); + toolbar_view->addAction(zoom_out_act); + toolbar_view->addAction(show_all_act); + + // MapParts toolbar + toolbar_mapparts = window->addToolBar(tr("Map parts")); + toolbar_mapparts->setObjectName(QString::fromLatin1("Map parts toolbar")); + if (!mappart_selector_box) + { + mappart_selector_box = new QComboBox(toolbar_mapparts); + mappart_selector_box->setToolTip(tr("Map parts")); + } + mappart_selector_box->setSizeAdjustPolicy(QComboBox::AdjustToContents); + connect(mappart_selector_box, QOverload::of(&QComboBox::currentIndexChanged), this, &MapEditorController::changeMapPart); + toolbar_mapparts->addWidget(mappart_selector_box); + + window->addToolBarBreak(); + + // Drawing toolbar + toolbar_drawing = window->addToolBar(tr("Drawing")); + toolbar_drawing->setObjectName(QString::fromLatin1("Drawing toolbar")); + toolbar_drawing->addAction(edit_tool_act); + toolbar_drawing->addAction(edit_line_tool_act); + toolbar_drawing->addAction(draw_point_act); + toolbar_drawing->addAction(draw_path_act); + toolbar_drawing->addAction(draw_circle_act); + toolbar_drawing->addAction(draw_rectangle_act); + toolbar_drawing->addAction(draw_freehand_act); + toolbar_drawing->addAction(draw_fill_act); + toolbar_drawing->addAction(draw_text_act); + toolbar_drawing->addSeparator(); + + auto paint_on_template_button = new QToolButton(); + paint_on_template_button->setCheckable(true); + paint_on_template_button->setDefaultAction(paint_on_template_act); + paint_on_template_button->setPopupMode(QToolButton::MenuButtonPopup); + auto paint_on_template_menu = new QMenu(paint_on_template_button); + paint_on_template_menu->addAction(tr("Select template...")); + paint_on_template_button->setMenu(paint_on_template_menu); + connect(paint_on_template_menu, &QMenu::triggered, this, &MapEditorController::paintOnTemplateSelectClicked); + toolbar_drawing->addWidget(paint_on_template_button); + + // Editing toolbar + toolbar_editing = window->addToolBar(tr("Editing")); + toolbar_editing->setObjectName(QString::fromLatin1("Editing toolbar")); + toolbar_editing->addAction(delete_act); + toolbar_editing->addAction(duplicate_act); + toolbar_editing->addAction(switch_symbol_act); + toolbar_editing->addAction(fill_border_act); + toolbar_editing->addAction(switch_dashes_act); + toolbar_editing->addAction(connect_paths_act); + toolbar_editing->addAction(boolean_union_act); + toolbar_editing->addAction(cut_tool_act); + + auto cut_hole_button = new QToolButton(); + cut_hole_button->setCheckable(true); + cut_hole_button->setToolButtonStyle(Qt::ToolButtonIconOnly); + cut_hole_button->setDefaultAction(cut_hole_act); + cut_hole_button->setPopupMode(QToolButton::MenuButtonPopup); + cut_hole_button->setMenu(cut_hole_menu); + toolbar_editing->addWidget(cut_hole_button); + + toolbar_editing->addAction(rotate_act); + toolbar_editing->addAction(rotate_pattern_act); + toolbar_editing->addAction(scale_act); + toolbar_editing->addAction(measure_act); + + // Advanced editing toolbar + toolbar_advanced_editing = window->addToolBar(tr("Advanced editing")); + toolbar_advanced_editing->setObjectName(QString::fromLatin1("Advanved editing toolbar")); + toolbar_advanced_editing->addAction(cutout_physical_act); + toolbar_advanced_editing->addAction(cutaway_physical_act); + toolbar_advanced_editing->addAction(convert_to_curves_act); + toolbar_advanced_editing->addAction(simplify_path_act); + toolbar_advanced_editing->addAction(distribute_points_act); + toolbar_advanced_editing->addAction(boolean_intersection_act); + toolbar_advanced_editing->addAction(boolean_difference_act); + toolbar_advanced_editing->addAction(boolean_xor_act); + toolbar_advanced_editing->addAction(boolean_merge_holes_act); + + toolbars_menu->addAction(main_toolbar->toggleViewAction()); + toolbars_menu->addAction(toolbar_view->toggleViewAction()); + toolbars_menu->addAction(toolbar_mapparts->toggleViewAction()); + toolbars_menu->addAction(toolbar_drawing->toggleViewAction()); + toolbars_menu->addAction(toolbar_editing->toggleViewAction()); + toolbars_menu->addAction(toolbar_advanced_editing->toggleViewAction()); + + QWidget* context_menu = map_widget->getContextMenu(); + context_menu->addAction(edit_tool_act); + context_menu->addAction(draw_point_act); + context_menu->addAction(draw_path_act); + context_menu->addAction(draw_rectangle_act); + context_menu->addAction(cut_tool_act); + context_menu->addAction(cut_hole_act); + context_menu->addAction(switch_dashes_act); + context_menu->addAction(connect_paths_act); + context_menu->addAction(copy_coords_act); +} + +void MapEditorController::createMobileGUI() +{ + // Create mobile-specific actions + mobile_symbol_selector_action = new QAction(tr("Select symbol"), this); + connect(mobile_symbol_selector_action, &QAction::triggered, this, &MapEditorController::mobileSymbolSelectorClicked); + + mobile_symbol_button_menu = new QMenu(window); + mobile_symbol_button_menu->addAction(QString{}); // reserved for symbol name + auto description_action = mobile_symbol_button_menu->addAction(QApplication::translate("OpenOrienteering::SymbolPropertiesWidget", "Description")); + connect(description_action, &QAction::triggered, this, [this]() { + auto symbol = symbol_widget->getSingleSelectedSymbol(); + auto document = QString{ symbol->getNumberAsString() + QLatin1Char(' ') + + QLatin1String("") + symbol->getName() + QLatin1String("\n\n") + + symbol->getDescription() }; + // Cf. SymbolToolTip::scheduleShow + document.replace(QLatin1Char('\n'), QStringLiteral("
    ")); + document.remove(QLatin1Char('\r')); + TextBrowserDialog description_dialog(document, window); + description_dialog.exec(); + }); + mobile_symbol_button_menu->addSeparator(); + auto hide_symbol_action = mobile_symbol_button_menu->addAction(QApplication::translate("OpenOrienteering::SymbolRenderWidget", "Hide objects with this symbol")); + hide_symbol_action->setCheckable(true); + connect(hide_symbol_action, &QAction::triggered, this, [this](bool value) { + auto symbol = symbol_widget->getSingleSelectedSymbol(); + symbol->setHidden(value); + if (!value && map->removeSymbolFromSelection(symbol, false)) + map->emitSelectionChanged(); + map->updateAllMapWidgets(); + map->setSymbolsDirty(); + selectedSymbolsChanged(); + }); + auto protected_symbol_action = mobile_symbol_button_menu->addAction(QApplication::translate("OpenOrienteering::SymbolRenderWidget", "Protect objects with this symbol")); + protected_symbol_action->setCheckable(true); + connect(protected_symbol_action, &QAction::triggered, this, [this](bool value) { + auto symbol = symbol_widget->getSingleSelectedSymbol(); + symbol->setProtected(value); + if (!value && map->removeSymbolFromSelection(symbol, false)) + map->emitSelectionChanged(); + map->setSymbolsDirty(); + selectedSymbolsChanged(); + }); + + QAction* hide_top_bar_action = new QAction(QIcon(QString::fromLatin1(":/images/arrow-thin-upleft.png")), tr("Hide top bar"), this); + connect(hide_top_bar_action, &QAction::triggered, this, &MapEditorController::hideTopActionBar); + + QAction* show_top_bar_action = new QAction(QIcon(QString::fromLatin1(":/images/arrow-thin-downright.png")), tr("Show top bar"), this); + connect(show_top_bar_action, &QAction::triggered, this, &MapEditorController::showTopActionBar); + + Q_ASSERT(mappart_selector_box); + QAction* mappart_action = new QAction(QIcon(QString::fromLatin1(":/images/map-parts.png")), tr("Map parts"), this); + connect(mappart_action, &QAction::triggered, this, [this, mappart_action]() { + auto mappart_button = top_action_bar->getButtonForAction(mappart_action); + if (!mappart_button) + mappart_button = top_action_bar->getButtonForAction(top_action_bar->getOverflowAction()); + mappart_selector_box->setGeometry(mappart_button->geometry()); + mappart_selector_box->showPopup(); + }); + connect(mappart_selector_box, QOverload::of(&QComboBox::currentIndexChanged), this, &MapEditorController::changeMapPart); + + // Create button for showing the top bar again after hiding it + int icon_size_pixel = qRound(Util::mmToPixelLogical(10)); + const int button_icon_size = icon_size_pixel - 12; + QSize icon_size = QSize(button_icon_size, button_icon_size); + + QIcon icon = show_top_bar_action->icon(); + QPixmap pixmap = icon.pixmap(icon_size, QIcon::Normal, QIcon::Off); + if (pixmap.width() < button_icon_size) + { + pixmap = pixmap.scaled(icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + icon.addPixmap(pixmap); + show_top_bar_action->setIcon(icon); + } + + show_top_bar_button = new QToolButton(window); + show_top_bar_button->setDefaultAction(show_top_bar_action); + show_top_bar_button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + show_top_bar_button->setAutoRaise(true); + show_top_bar_button->setIconSize(icon_size); + show_top_bar_button->setGeometry(0, 0, button_icon_size, button_icon_size); + + + // Create bottom action bar + bottom_action_bar = new ActionGridBar(ActionGridBar::Horizontal, 2); + + // Left side + int col = 0; + bottom_action_bar->addAction(zoom_in_act, 0, col); + bottom_action_bar->addAction(pan_act, 1, col++); + + bottom_action_bar->addAction(zoom_out_act, 0, col); + auto zoom_out_button = bottom_action_bar->getButtonForAction(zoom_out_act); + auto mobile_zoom_out_menu = new QMenu(zoom_out_button); + auto zoom_1x_action = mobile_zoom_out_menu->addAction(tr("1x zoom")); + connect(zoom_1x_action, &QAction::triggered, this, [this]() { + main_view->setZoom(1); + }); + auto zoom_2x_action = mobile_zoom_out_menu->addAction(tr("2x zoom")); + connect(zoom_2x_action, &QAction::triggered, this, [this]() { + main_view->setZoom(2); + }); + zoom_out_button->setMenu(mobile_zoom_out_menu); + + bottom_action_bar->addAction(move_to_gps_pos_act, 1, col++); + + bottom_action_bar->addAction(gps_temporary_path_act, 0, col); + bottom_action_bar->addAction(gps_temporary_point_act, 1, col++); + + bottom_action_bar->addAction(gps_temporary_clear_act, 0, col++); + + bottom_action_bar->addAction(paint_on_template_act, 0, col); + bottom_action_bar->addAction(paint_on_template_settings_act, 1, col++); + + // Right side + bottom_action_bar->addActionAtEnd(mobile_symbol_selector_action, 0, 1, 2, 2); + auto button = bottom_action_bar->getButtonForAction(mobile_symbol_selector_action); + button->setPopupMode(QToolButton::DelayedPopup); + + col = 2; + bottom_action_bar->addActionAtEnd(draw_point_act, 0, col); + bottom_action_bar->addActionAtEnd(draw_point_gps_act, 1, col++); + + bottom_action_bar->addActionAtEnd(draw_path_act, 0, col); + bottom_action_bar->addActionAtEnd(draw_freehand_act, 1, col++); + + bottom_action_bar->addActionAtEnd(draw_rectangle_act, 0, col); + bottom_action_bar->addActionAtEnd(draw_circle_act, 1, col++); + + //bottom_action_bar->addActionAtEnd(draw_fill_act, 0, col); + bottom_action_bar->addActionAtEnd(draw_text_act, 1, col++); + + + // Create top action bar + top_action_bar = new ActionGridBar(ActionGridBar::Horizontal, 2); + + // Left side + col = 0; + top_action_bar->addAction(hide_top_bar_action, 0, col); + top_action_bar->addAction(window->getSaveAct(), 1, col++); + + top_action_bar->addAction(compass_action, 0, col); + top_action_bar->addAction(gps_display_action, 1, col++); + + top_action_bar->addAction(gps_distance_rings_action, 0, col); + top_action_bar->addAction(align_map_with_north_act, 1, col++); + + top_action_bar->addAction(show_grid_act, 0, col); + top_action_bar->addAction(show_all_act, 1, col++); + + // Right side + col = 0; + top_action_bar->addActionAtEnd(window->getCloseAct(), 0, col); + top_action_bar->addActionAtEnd(top_action_bar->getOverflowAction(), 1, col++); + + top_action_bar->addActionAtEnd(redo_act, 0, col); + top_action_bar->addActionAtEnd(undo_act, 1, col++); + + top_action_bar->addActionAtEnd(touch_cursor_action, 0, col); + top_action_bar->addActionAtEnd(template_window_act, 1, col++); + + top_action_bar->addActionAtEnd(edit_tool_act, 0, col); + top_action_bar->addActionAtEnd(edit_line_tool_act, 1, col++); + + top_action_bar->addActionAtEnd(delete_act, 0, col); + top_action_bar->addActionAtEnd(duplicate_act, 1, col++); + + top_action_bar->addActionAtEnd(switch_symbol_act, 0, col); + top_action_bar->addActionAtEnd(fill_border_act, 1, col++); + + top_action_bar->addActionAtEnd(switch_dashes_act, 0, col); + top_action_bar->addActionAtEnd(boolean_union_act, 1, col++); + + top_action_bar->addActionAtEnd(cut_tool_act, 0, col); + top_action_bar->addActionAtEnd(connect_paths_act, 1, col++); + + top_action_bar->addActionAtEnd(rotate_act, 0, col); + top_action_bar->addActionAtEnd(cut_hole_act, 1, col++); + + top_action_bar->addActionAtEnd(scale_act, 0, col); + top_action_bar->addActionAtEnd(rotate_pattern_act, 1, col++); + + top_action_bar->addActionAtEnd(convert_to_curves_act, 0, col); + top_action_bar->addActionAtEnd(simplify_path_act, 1, col++); + + top_action_bar->addActionAtEnd(distribute_points_act, 0, col); + top_action_bar->addActionAtEnd(boolean_difference_act, 1, col++); + + top_action_bar->addActionAtEnd(measure_act, 0, col); + top_action_bar->addActionAtEnd(boolean_merge_holes_act, 1, col++); + + top_action_bar->addActionAtEnd(mappart_action, 1, col++); + + bottom_action_bar->setToUseOverflowActionFrom(top_action_bar); + + top_action_bar->setParent(map_widget); + + auto container_widget = new QWidget(); + auto layout = new QVBoxLayout(); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(map_widget, 1); + layout->addWidget(bottom_action_bar); + container_widget->setLayout(layout); + window->setCentralWidget(container_widget); +} + +void MapEditorController::detach() +{ + // Terminate all editing + setTool(nullptr); + setOverrideTool(nullptr); + + saveWindowState(); + + // Avoid a crash triggered by pressing Ctrl-W during loading. + if (nullptr != symbol_dock_widget) + window->removeDockWidget(symbol_dock_widget); + else if (nullptr != symbol_widget) + delete symbol_widget; + + delete gps_display; + gps_display = nullptr; + delete gps_track_recorder; + gps_track_recorder = nullptr; + delete compass_display; + compass_display = nullptr; + delete gps_marker_display; + gps_marker_display = nullptr; + + find_feature.reset(nullptr); + + window->setCentralWidget(nullptr); + delete map_widget; + + delete statusbar_zoom_frame; + delete statusbar_cursorpos_label; + +#ifdef Q_OS_ANDROID + QAndroidJniObject::callStaticMethod("org/openorienteering/mapper/MapperActivity", + "unlockOrientation", + "()V"); +#endif + + if (mobile_mode) + window->setWindowState(window->windowState() & ~Qt::WindowFullScreen); +} + +void MapEditorController::saveWindowState() +{ + if (!mobile_mode && mode != SymbolEditor) + { + QSettings settings; + settings.beginGroup(QString::fromUtf8(metaObject()->className())); + settings.setValue(QString::fromLatin1("state"), window->saveState()); + } +} + +void MapEditorController::restoreWindowState() +{ + if (!mobile_mode) + { + QSettings settings; + settings.beginGroup(QString::fromUtf8(metaObject()->className())); + window->restoreState(settings.value(QString::fromLatin1("state")).toByteArray()); + if (toolbar_mapparts && mappart_selector_box) + toolbar_mapparts->setVisible(mappart_selector_box->count() > 1); + } +} + +bool MapEditorController::keyPressEventFilter(QKeyEvent* event) +{ +#if defined(Q_OS_ANDROID) + if (event->key() == Qt::Key_Back) + { + if (symbol_widget && symbol_widget->isVisible()) + { + mobileSymbolSelectorFinished(); + return true; + } + + if (isEditingInProgress() && !map_widget->findChild()) + { + QKeyEvent escape_pressed{ QEvent::KeyPress, Qt::Key_Escape, event->modifiers() }; + QCoreApplication::sendEvent(window, &escape_pressed); + QKeyEvent escape_released{ QEvent::KeyRelease, Qt::Key_Escape, event->modifiers() }; + QCoreApplication::sendEvent(window, &escape_released); + return true; + } + } +#endif + + return map_widget->keyPressEventFilter(event); +} + +bool MapEditorController::keyReleaseEventFilter(QKeyEvent* event) +{ + return map_widget->keyReleaseEventFilter(event); +} + +void MapEditorController::printClicked(int task) +{ +#ifdef QT_PRINTSUPPORT_LIB + if (!print_dock_widget) + { + print_dock_widget = new EditorDockWidget(QString{}, nullptr, this, window); + print_dock_widget->setAllowedAreas(Qt::NoDockWidgetArea); + print_dock_widget->toggleViewAction()->setVisible(false); + print_widget = new PrintWidget(map, window, main_view, this, print_dock_widget); + connect(print_dock_widget, &QDockWidget::visibilityChanged, this, [this]() { + print_widget->setActive(print_dock_widget->isVisible()); + } ); + connect(print_widget, &PrintWidget::closeClicked, print_dock_widget, &QWidget::close); + connect(print_widget, &PrintWidget::finished, print_dock_widget, &QWidget::close); + connect(print_widget, &PrintWidget::taskChanged, print_dock_widget, &QWidget::setWindowTitle); + print_dock_widget->setWidget(print_widget); + print_dock_widget->setObjectName(QString::fromLatin1("Print dock widget")); + addFloatingDockWidget(print_dock_widget); + } + + print_widget->setActive(true); // Make sure to save the state before setting the task. + print_widget->setTask((PrintWidget::TaskFlags)task); + print_dock_widget->show(); + print_dock_widget->raise(); +#else + Q_UNUSED(task) + QMessageBox::warning(window, tr("Error"), tr("Print / Export is not available in this program version!")); +#endif +} + +void MapEditorController::undo() +{ + doUndo(false); +} + +void MapEditorController::redo() +{ + doUndo(true); +} + +void MapEditorController::doUndo(bool redo) +{ + if ((!redo && !map->undoManager().canUndo()) || + (redo && !map->undoManager().canRedo())) + { + // This should not happen as the action should be deactivated in this case! + QMessageBox::critical(window, tr("Error"), tr("No undo steps available.")); + return; + } + + if (redo) + map->undoManager().redo(window); + else + map->undoManager().undo(window); +} + +void MapEditorController::cut() +{ + copy(); + //: Past tense. Displayed when an Edit > Cut operation is completed. + window->showStatusBarMessage(tr("Cut %n object(s)", nullptr, map->getNumSelectedObjects()), 2000); + map->deleteSelectedObjects(); +} + +void MapEditorController::copy() +{ + if (map->getNumSelectedObjects() == 0) + return; + + // Create map containing required objects and their symbol and color dependencies + Map copy_map; + copy_map.setScaleDenominator(map->getScaleDenominator()); + + std::vector symbol_filter; + symbol_filter.assign(map->getNumSymbols(), false); + for (const auto object : map->selectedObjects()) + { + int symbol_index = map->findSymbolIndex(object->getSymbol()); + if (symbol_index >= 0) + symbol_filter[symbol_index] = true; + } + + // Copy all colors. This improves preservation of relative order during paste. + copy_map.importMap(map, Map::ColorImport, window); + + // Export symbols and colors into copy_map + QHash symbol_map; + copy_map.importMap(map, Map::MinimalSymbolImport, window, &symbol_filter, -1, true, &symbol_map); + + // Duplicate all selected objects into copy map + for (const auto object : map->selectedObjects()) + { + auto new_object = object->duplicate(); + if (symbol_map.contains(new_object->getSymbol())) + new_object->setSymbol(symbol_map.value(new_object->getSymbol()), true); + + copy_map.addObject(new_object); + } + + // Save map to memory + QBuffer buffer; + if (!copy_map.exportToIODevice(&buffer)) + { + QMessageBox::warning(nullptr, tr("Error"), tr("An internal error occurred, sorry!")); + return; + } + + // Put buffer into clipboard + auto mime_data = new QMimeData(); + mime_data->setData(MimeType::OpenOrienteeringObjects(), buffer.data()); + QApplication::clipboard()->setMimeData(mime_data); + + // Show message + window->showStatusBarMessage(tr("Copied %n object(s)", nullptr, map->getNumSelectedObjects()), 2000); +} + + +void MapEditorController::paste() +{ + if (editing_in_progress) + return; + if (!QApplication::clipboard()->mimeData()->hasFormat(MimeType::OpenOrienteeringObjects())) + { + QMessageBox::warning(nullptr, tr("Error"), tr("There are no objects in clipboard which could be pasted!")); + return; + } + + // Get buffer from clipboard + QByteArray byte_array = QApplication::clipboard()->mimeData()->data(MimeType::OpenOrienteeringObjects()); + QBuffer buffer(&byte_array); + buffer.open(QIODevice::ReadOnly); + + // Create map from buffer + Map paste_map; + if (!paste_map.importFromIODevice(&buffer)) + { + QMessageBox::warning(nullptr, tr("Error"), tr("An internal error occurred, sorry!")); + return; + } + + // Move objects in paste_map so their bounding box center is at this map's viewport center. + // This makes the pasted objects appear at the center of the viewport. + QRectF paste_extent = paste_map.calculateExtent(true, false, nullptr); + auto offset = main_view->center() - paste_extent.center(); + + MapPart* part = paste_map.getCurrentPart(); + for (int i = 0; i < part->getNumObjects(); ++i) + part->getObject(i)->move(offset); + + // Import pasted map. Do not blindly import all colors. + map->importMap(&paste_map, Map::MinimalObjectImport, window); + + // Show message + window->showStatusBarMessage(tr("Pasted %n object(s)", nullptr, paste_map.getNumObjects()), 2000); +} + + +void MapEditorController::clearUndoRedoHistory() +{ + map->undoManager().clear(); + map->setOtherDirty(); +} + +void MapEditorController::spotColorPresenceChanged(bool has_spot_colors) +{ + if (overprinting_simulation_act) + { + if (has_spot_colors) + { + overprinting_simulation_act->setEnabled(true); + } + else + { + overprinting_simulation_act->setChecked(false); + overprinting_simulation_act->setEnabled(false); + } + } +} + +void MapEditorController::showGrid() +{ + main_view->setGridVisible(show_grid_act->isChecked()); +} + +void MapEditorController::configureGrid() +{ + ConfigureGridDialog dialog(window, *map, show_grid_act->isChecked()); + dialog.setWindowModality(Qt::WindowModal); + if (dialog.exec() == QDialog::Accepted) + { + map->setGrid(dialog.resultGrid()); + if (dialog.gridVisible() != show_grid_act->isChecked()) + show_grid_act->trigger(); + } +} + +void MapEditorController::pan() +{ + setTool(new PanTool(this, pan_act)); +} + +void MapEditorController::moveToGpsPos() +{ + if (!gps_display->hasValidPosition()) + return; + auto cur_gps_pos = gps_display->getLatestGPSCoord(); + main_view->setCenter({ cur_gps_pos.x(), cur_gps_pos.y() }); + gps_display->startBlinking(3); +} + +void MapEditorController::zoomIn() +{ + main_view->zoomSteps(1); +} +void MapEditorController::zoomOut() +{ + main_view->zoomSteps(-1); +} +void MapEditorController::setCustomZoomFactorClicked() +{ + bool ok; + double factor = QInputDialog::getDouble(window, tr("Set custom zoom factor"), tr("Zoom factor:"), main_view->getZoom(), MapView::zoom_out_limit, MapView::zoom_in_limit, 3, &ok); + if (!ok || factor == main_view->getZoom()) + return; + + main_view->setZoom(factor); +} + +void MapEditorController::hatchAreas(bool checked) +{ + map->setAreaHatchingEnabled(checked); + // Update all areas + map->applyOnMatchingObjects(&Object::forceUpdate, ObjectOp::ContainsSymbolType{Symbol::Area}); +} + +void MapEditorController::baselineView(bool checked) +{ + map->setBaselineViewEnabled(checked); + map->updateAllObjects(); +} + +void MapEditorController::hideAllTemplates(bool checked) +{ + hide_all_templates_act->setChecked(checked); + main_view->setAllTemplatesHidden(checked); +} + +void MapEditorController::overprintingSimulation(bool checked) +{ + if (checked && !map->hasSpotColors()) + { + checked = false; + } + main_view->setOverprintingSimulationEnabled(checked); +} + +void MapEditorController::coordsDisplayChanged() +{ + if (geographic_coordinates_dms_act->isChecked()) + map_widget->setCoordsDisplay(MapWidget::GEOGRAPHIC_COORDS_DMS); + else if (geographic_coordinates_act->isChecked()) + map_widget->setCoordsDisplay(MapWidget::GEOGRAPHIC_COORDS); + else if (projected_coordinates_act->isChecked()) + map_widget->setCoordsDisplay(MapWidget::PROJECTED_COORDS); + else + map_widget->setCoordsDisplay(MapWidget::MAP_COORDS); +} + +void MapEditorController::copyDisplayedCoords() +{ + QApplication::clipboard()->setText(statusbar_cursorpos_label->text()); +} + +void MapEditorController::projectionChanged() +{ + const Georeferencing& geo(map->getGeoreferencing()); + + projected_coordinates_act->setText(geo.getProjectedCoordinatesName()); + + bool enable_geographic = !geo.isLocal(); + geographic_coordinates_act->setEnabled(enable_geographic); + geographic_coordinates_dms_act->setEnabled(enable_geographic); + if (!enable_geographic && + (map_widget->getCoordsDisplay() == MapWidget::GEOGRAPHIC_COORDS || + map_widget->getCoordsDisplay() == MapWidget::GEOGRAPHIC_COORDS_DMS)) + { + map_widget->setCoordsDisplay(MapWidget::MAP_COORDS); + map_coordinates_act->setChecked(true); + } +} + +void MapEditorController::showSymbolWindow(bool show) +{ + if (!symbol_dock_widget) + { + symbol_dock_widget = new EditorDockWidget(tr("Symbols"), symbol_window_act, this, window); + createSymbolWidget(); + symbol_dock_widget->setObjectName(QString::fromLatin1("Symbol dock widget")); + if (!window->restoreDockWidget(symbol_dock_widget)) + window->addDockWidget(Qt::RightDockWidgetArea, symbol_dock_widget, Qt::Vertical); + } + + symbol_dock_widget->setVisible(show); +} + +void MapEditorController::createColorWindow() +{ + Q_ASSERT(!color_dock_widget); + + color_dock_widget = new EditorDockWidget(tr("Colors"), color_window_act, this, window); + color_dock_widget->setWidget(new ColorListWidget(map, window, color_dock_widget)); + color_dock_widget->widget()->setEnabled(!editing_in_progress); + color_dock_widget->setObjectName(QString::fromLatin1("Color dock widget")); + if (!window->restoreDockWidget(color_dock_widget)) + window->addDockWidget(Qt::LeftDockWidgetArea, color_dock_widget, Qt::Vertical); + color_dock_widget->setVisible(false); +} + +void MapEditorController::showColorWindow(bool show) +{ + if (!color_dock_widget) + createColorWindow(); + + color_dock_widget->setVisible(show); +} + + +void MapEditorController::symbolSetIdClicked() +{ + bool ok; + auto id = QInputDialog::getText(window, tr("Symbol set ID"), + tr("Edit the symbol set ID:"), QLineEdit::Normal, + map->symbolSetId(), &ok); + if (ok) + map->setSymbolSetId(id); +} + + +void MapEditorController::loadSymbolsFromClicked() +{ + ReplaceSymbolSetDialog::showDialog(window, *map); +} + + +void MapEditorController::loadCrtClicked() +{ + ReplaceSymbolSetDialog::showDialogForCRT(window, *map, *map); +} + + +void MapEditorController::loadColorsFromClicked() +{ + // TODO +} + +void MapEditorController::scaleAllSymbolsClicked() +{ + bool ok; + double percent = QInputDialog::getDouble(window, tr("Scale all symbols"), tr("Scale to percentage:"), 100, 0, 999999, 6, &ok); + if (!ok || percent == 100) + return; + + map->scaleAllSymbols(percent / 100.0); +} + +void MapEditorController::scaleMapClicked() +{ + ScaleMapDialog dialog(window, map); + dialog.setWindowModality(Qt::WindowModal); + dialog.exec(); +} + +void MapEditorController::rotateMapClicked() +{ + RotateMapDialog dialog(window, map); + dialog.setWindowModality(Qt::WindowModal); + dialog.exec(); +} + +void MapEditorController::mapNotesClicked() +{ + QDialog dialog(window, Qt::WindowSystemMenuHint | Qt::WindowTitleHint); + dialog.setWindowTitle(tr("Map notes")); + dialog.setWindowModality(Qt::WindowModal); + + auto text_edit = new QTextEdit(); + text_edit->setPlainText(map->getMapNotes()); + QPushButton* cancel_button = new QPushButton(tr("Cancel")); + QPushButton* ok_button = new QPushButton(QIcon(QString::fromLatin1(":/images/arrow-right.png")), tr("OK")); + ok_button->setDefault(true); + + auto buttons_layout = new QHBoxLayout(); + buttons_layout->addWidget(cancel_button); + buttons_layout->addStretch(1); + buttons_layout->addWidget(ok_button); + + auto layout = new QVBoxLayout(); + layout->addWidget(text_edit); + layout->addLayout(buttons_layout); + dialog.setLayout(layout); + + connect(cancel_button, &QAbstractButton::clicked, &dialog, &QDialog::reject); + connect(ok_button, &QAbstractButton::clicked, &dialog, &QDialog::accept); + + if (dialog.exec() == QDialog::Accepted) + { + if (text_edit->toPlainText() != map->getMapNotes()) + { + map->setMapNotes(text_edit->toPlainText()); + map->setHasUnsavedChanges(true); + } + } +} + +void MapEditorController::createTemplateWindow() +{ + Q_ASSERT(!template_dock_widget); + + template_list_widget = new TemplateListWidget(map, main_view, this); + connect(hide_all_templates_act, &QAction::toggled, template_list_widget, &TemplateListWidget::setAllTemplatesHidden); + + if (isInMobileMode()) + { + template_dock_widget = createDockWidgetSubstitute(window, template_list_widget); + connect(template_list_widget, &TemplateListWidget::closeClicked, this, [this]() { showTemplateWindow(false); }); + } + else + { + auto dock_widget = new EditorDockWidget(tr("Templates"), template_window_act, this, window); + dock_widget->setWidget(template_list_widget); + dock_widget->setObjectName(QString::fromLatin1("Templates dock widget")); + if (!window->restoreDockWidget(dock_widget)) + window->addDockWidget(Qt::RightDockWidgetArea, dock_widget, Qt::Vertical); + dock_widget->setVisible(false); + + template_dock_widget = dock_widget; + } +} + +void MapEditorController::showTemplateWindow(bool show) +{ + if (!template_dock_widget) + createTemplateWindow(); + + template_window_act->setChecked(show); + template_dock_widget->setVisible(show); +} + +void MapEditorController::openTemplateClicked() +{ + auto new_template = TemplateListWidget::showOpenTemplateDialog(window, this); + if (new_template) + { + hideAllTemplates(false); + showTemplateWindow(true); + + // FIXME: this should be done through the core map, not through the UI + template_list_widget->addTemplateAt(new_template.release(), -1); + } +} + +void MapEditorController::reopenTemplateClicked() +{ + hideAllTemplates(false); + + QString map_directory = window->currentPath(); + if (!map_directory.isEmpty()) + map_directory = QFileInfo(map_directory).canonicalPath(); + auto dialog = new ReopenTemplateDialog(window, map, map_directory); + dialog->setWindowModality(Qt::WindowModal); + dialog->exec(); + delete dialog; +} + +void MapEditorController::templateAvailabilityChanged() +{ + // Nothing +} + +void MapEditorController::closedTemplateAvailabilityChanged() +{ + if (reopen_template_act) + reopen_template_act->setEnabled(map->getNumClosedTemplates() > 0); +} + +void MapEditorController::createTagEditor() +{ + Q_ASSERT(!tags_dock_widget); + + auto tags_widget = new TagsWidget(map, main_view, this); + tags_dock_widget = new EditorDockWidget(tr("Tag Editor"), tags_window_act, this, window); + tags_dock_widget->setWidget(tags_widget); + tags_dock_widget->setObjectName(QString::fromLatin1("Tag editor dock widget")); + if (!window->restoreDockWidget(tags_dock_widget)) + window->addDockWidget(Qt::RightDockWidgetArea, tags_dock_widget, Qt::Vertical); + tags_dock_widget->setVisible(false); +} + +void MapEditorController::showTagsWindow(bool show) +{ + if (!tags_dock_widget) + createTagEditor(); + + tags_window_act->setChecked(show); + tags_dock_widget->setVisible(show); +} + + +void MapEditorController::editGeoreferencing() +{ + if (georeferencing_dialog.isNull()) + { + auto dialog = new GeoreferencingDialog(this); + georeferencing_dialog.reset(dialog); + connect(dialog, &QDialog::finished, this, &MapEditorController::georeferencingDialogFinished); + } + georeferencing_dialog->exec(); +} + +void MapEditorController::georeferencingDialogFinished() +{ + georeferencing_dialog.take()->deleteLater(); + map->updateAllMapWidgets(); + + bool gps_display_possible = map->getGeoreferencing().isValid() && ! map->getGeoreferencing().isLocal(); + if (!gps_display_possible) + { + gps_display_action->setChecked(false); + if (gps_display) + enableGPSDisplay(false); + } + gps_display_action->setEnabled(gps_display_possible); +} + +void MapEditorController::selectedSymbolsChanged() +{ + Symbol* symbol = symbol_widget->getSingleSelectedSymbol(); + + if (mobile_mode) + { + auto symbol_button = bottom_action_bar->getButtonForAction(mobile_symbol_selector_action); + + // (Re-)create the mobile_symbol_selector_action icon + QSize icon_size = bottom_action_bar->getIconSize(2, 2); + QPixmap pixmap(icon_size); + pixmap.fill(Qt::white); + if (symbol_widget->selectedSymbolsCount() != 1) + { + QFont font(window->font()); + font.setPixelSize(icon_size.height() / 5); + QPainter painter(&pixmap); + painter.setFont(font); + QString text = (symbol_widget->selectedSymbolsCount() == 0) ? + //: Keep it short. Should not be much longer per line than the longest word in the original. + tr("No\nsymbol\nselected") : + //: Keep it short. Should not be much longer per line than the longest word in the original. + tr("Multiple\nsymbols\nselected"); + painter.drawText(pixmap.rect(), Qt::AlignCenter, text); + + symbol_button->setMenu(nullptr); + } + else //if (symbol_widget->getNumSelectedSymbols() == 1) + { + auto image = symbol->createIcon(*map, qMin(icon_size.width(), icon_size.height())); + if (symbol->isHidden() || symbol->isProtected()) + { + QPainter p(&image); + if (symbol->isHidden()) + HiddenSymbolDecorator(icon_size.width()).draw(p); + if (symbol->isProtected()) + ProtectedSymbolDecorator(icon_size.width()).draw(p); + } + pixmap = QPixmap::fromImage(image); + + symbol_button->setMenu(mobile_symbol_button_menu); + const auto actions = mobile_symbol_button_menu->actions(); + int i = 0; + actions[i]->setText(symbol->getNumberAsString() + QLatin1Char(' ') + symbol->getPlainTextName()); + actions[++i]->setVisible(!symbol->getDescription().isEmpty()); + ++i; // separator + actions[++i]->setChecked(symbol->isHidden()); + actions[++i]->setChecked(symbol->isProtected()); + } + mobile_symbol_selector_action->setIcon(QIcon(pixmap)); + } + + // FIXME: Postpone switch of active symbol while editing is progress + if (active_symbol != symbol) + { + active_symbol = symbol; + + if (symbol && !symbol->isHidden() && !symbol->isProtected() && current_tool) + { + // Auto-switch to a draw tool when selecting a symbol under certain conditions + if (current_tool->toolType() == MapEditorTool::Pan + || ((current_tool->toolType() == MapEditorTool::EditLine || current_tool->toolType() == MapEditorTool::EditPoint) && map->getNumSelectedObjects() == 0)) + { + current_tool->switchToDefaultDrawTool(active_symbol); + } + } + + if (symbol) + window->showStatusBarMessage(symbol->getNumberAsString() + QLatin1Char(' ') + symbol->getPlainTextName(), 1000); + } + + // Even when the symbol (pointer) hasn't changed, + // the symbol's visibility may constitute a change. + emit activeSymbolChanged(active_symbol); + + updateSymbolDependentActions(); + updateSymbolAndObjectDependentActions(); +} + +void MapEditorController::objectSelectionChanged() +{ + if (mode != MapEditor) + return; + + updateObjectDependentActions(); + + // Automatic symbol selection of selected objects + if (symbol_widget && !editing_in_progress) + { + bool uniform_symbol_selected = true; + const Symbol* uniform_symbol = nullptr; + for (const auto object : map->selectedObjects()) + { + const auto symbol = object->getSymbol(); + if (!uniform_symbol) + { + uniform_symbol = symbol; + } + else if (uniform_symbol != symbol) + { + uniform_symbol_selected = false; + break; + } + } + if (uniform_symbol_selected && Settings::getInstance().getSettingCached(Settings::MapEditor_ChangeSymbolWhenSelecting).toBool()) + symbol_widget->selectSingleSymbol(uniform_symbol); + } + + updateSymbolAndObjectDependentActions(); +} + +void MapEditorController::updateSymbolDependentActions() +{ + const Symbol* symbol = activeSymbol(); + const Symbol::Type type = (symbol && !editing_in_progress) ? symbol->getType() : Symbol::NoSymbol; + + updateDrawPointGPSAvailability(); + draw_point_act->setEnabled(type == Symbol::Point && !symbol->isHidden()); + draw_point_act->setStatusTip(tr("Place point objects on the map.") + (draw_point_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a point symbol to be able to use this tool.")))); + draw_path_act->setEnabled((type == Symbol::Line || type == Symbol::Area || type == Symbol::Combined) && !symbol->isHidden()); + draw_path_act->setStatusTip(tr("Draw polygonal and curved lines.") + (draw_path_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); + draw_circle_act->setEnabled(draw_path_act->isEnabled()); + draw_circle_act->setStatusTip(tr("Draw circles and ellipses.") + (draw_circle_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); + draw_rectangle_act->setEnabled(draw_path_act->isEnabled()); + draw_rectangle_act->setStatusTip(tr("Draw rectangles.") + (draw_rectangle_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); + draw_freehand_act->setEnabled(draw_path_act->isEnabled()); + draw_freehand_act->setStatusTip(tr("Draw paths free-handedly.") + (draw_freehand_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); + draw_fill_act->setEnabled(draw_path_act->isEnabled()); + draw_fill_act->setStatusTip(tr("Fill bounded areas.") + (draw_fill_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a line, area or combined symbol to be able to use this tool.")))); + draw_text_act->setEnabled(type == Symbol::Text && !symbol->isHidden()); + draw_text_act->setStatusTip(tr("Write text on the map.") + (draw_text_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a text symbol to be able to use this tool.")))); +} + +void MapEditorController::updateObjectDependentActions() +{ + bool have_multiple_parts = map->getNumParts() > 1; + bool have_selection = map->getNumSelectedObjects() > 0 && !editing_in_progress; + bool single_object_selected = map->getNumSelectedObjects() == 1 && !editing_in_progress; + bool have_line = false; + bool have_area = false; + bool have_area_with_holes = false; + bool have_rotatable_pattern = false; + bool have_rotatable_point = false; + int num_selected_paths = 0; + bool first_selected_is_path = have_selection && map->getFirstSelectedObject()->getType() == Object::Path; + bool uniform_symbol_selected = true; + const Symbol* uniform_symbol = nullptr; + const Symbol* first_selected_symbol= have_selection ? map->getFirstSelectedObject()->getSymbol() : nullptr; + std::vector< bool > symbols_in_selection(map->getNumSymbols(), false); + + if (!editing_in_progress) + { + for (const auto object : map->selectedObjects()) + { + const auto symbol = object->getSymbol(); + int symbol_index = map->findSymbolIndex(symbol); + if (symbol_index >= 0 && symbol_index < (int)symbols_in_selection.size()) + symbols_in_selection[symbol_index] = true; + + if (uniform_symbol_selected) + { + if (!uniform_symbol) + { + uniform_symbol = symbol; + } + else if (uniform_symbol != symbol) + { + uniform_symbol = nullptr; + uniform_symbol_selected = false; + } + } + + if (symbol->getType() == Symbol::Point) + { + have_rotatable_point |= symbol->asPoint()->isRotatable(); + } + else if (Symbol::areTypesCompatible(symbol->getType(), Symbol::Area)) + { + ++num_selected_paths; + + if (symbol->getType() == Symbol::Area) + { + have_rotatable_pattern |= symbol->asArea()->hasRotatableFillPattern(); + } + + int const contained_types = symbol->getContainedTypes(); + + if (contained_types & Symbol::Line) + { + have_line = true; + } + + if (contained_types & Symbol::Area) + { + have_area = true; + have_area_with_holes |= object->asPath()->parts().size() > 1; + } + } + } + + if (have_area && !have_rotatable_pattern) + { + map->determineSymbolUseClosure(symbols_in_selection); + for (std::size_t i = 0, end = symbols_in_selection.size(); i < end; ++i) + { + if (symbols_in_selection[i]) + { + Symbol* symbol = map->getSymbol(i); + if (symbol->getType() == Symbol::Area) + { + have_rotatable_pattern = symbol->asArea()->hasRotatableFillPattern(); + break; + } + } + } + } + } + + // have_selection + cut_act->setEnabled(have_selection); + copy_act->setEnabled(have_selection); + delete_act->setEnabled(have_selection); + delete_act->setStatusTip(tr("Deletes the selected objects.") + (delete_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool.")))); + duplicate_act->setEnabled(have_selection); + duplicate_act->setStatusTip(tr("Duplicate the selected objects.") + (duplicate_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool.")))); + rotate_act->setEnabled(have_selection); + rotate_act->setStatusTip(tr("Rotate the selected objects.") + (rotate_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool.")))); + scale_act->setEnabled(have_selection); + scale_act->setStatusTip(tr("Scale the selected objects.") + (scale_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool.")))); + mappart_move_menu->setEnabled(have_selection && have_multiple_parts); + + // have_rotatable_pattern || have_rotatable_point + rotate_pattern_act->setEnabled(have_rotatable_pattern || have_rotatable_point); + rotate_pattern_act->setStatusTip(tr("Set the direction of area fill patterns or point objects.") + (rotate_pattern_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select an area object with rotatable fill pattern or a rotatable point object to activate this tool.")))); + + // have_line + switch_dashes_act->setEnabled(have_line); + switch_dashes_act->setStatusTip(tr("Switch the direction of symbols on line objects.") + (switch_dashes_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one line object to activate this tool.")))); + connect_paths_act->setEnabled(have_line); + connect_paths_act->setStatusTip(tr("Connect endpoints of paths which are close together.") + (connect_paths_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one line object to activate this tool.")))); + + // have_are || have_line + cut_tool_act->setEnabled(have_area || have_line); + cut_tool_act->setStatusTip(tr("Cut the selected objects into smaller parts.") + (cut_tool_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one line or area object to activate this tool.")))); + convert_to_curves_act->setEnabled(have_area || have_line); + convert_to_curves_act->setStatusTip(tr("Turn paths made of straight segments into smooth bezier splines.") + (convert_to_curves_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a path object to activate this tool.")))); + simplify_path_act->setEnabled(have_area || have_line); + simplify_path_act->setStatusTip(tr("Reduce the number of points in path objects while trying to retain their shape.") + (simplify_path_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a path object to activate this tool.")))); + + // cut_hole_enabled + const bool cut_hole_enabled = single_object_selected && have_area; + cut_hole_act->setEnabled(cut_hole_enabled); + cut_hole_act->setStatusTip(tr("Cut a hole into the selected area object.") + (cut_hole_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a single area object to activate this tool.")))); + cut_hole_circle_act->setEnabled(cut_hole_enabled); + cut_hole_circle_act->setStatusTip(cut_hole_act->statusTip()); + cut_hole_rectangle_act->setEnabled(cut_hole_enabled); + cut_hole_rectangle_act->setStatusTip(cut_hole_act->statusTip()); + cut_hole_menu->setEnabled(cut_hole_enabled); + + // boolean_prerequisite [&& x] + bool const boolean_prerequisite = first_selected_is_path && num_selected_paths >= 2; + QString const extra_status_tip = QLatin1Char(' ') + + ( boolean_prerequisite + ? tr("Resulting symbol: %1 %2.").arg(first_selected_symbol->getNumberAsString(), first_selected_symbol->getPlainTextName()) + : tr("Select at least two area or path objects activate this tool.") ); + boolean_union_act->setEnabled(boolean_prerequisite); + boolean_union_act->setStatusTip(tr("Unify overlapping objects.") + extra_status_tip); + boolean_intersection_act->setEnabled(boolean_prerequisite); + boolean_intersection_act->setStatusTip(tr("Remove all parts which are not overlaps with the first selected object.") + extra_status_tip); + boolean_difference_act->setEnabled(boolean_prerequisite); + boolean_difference_act->setStatusTip(tr("Remove overlapped parts of the first selected object.") + (boolean_prerequisite ? QString{} : extra_status_tip)); + boolean_xor_act->setEnabled(boolean_prerequisite); + boolean_xor_act->setStatusTip(tr("Remove all parts which overlap the first selected object.") + extra_status_tip); + + // special + boolean_merge_holes_act->setEnabled(single_object_selected && have_area_with_holes); + boolean_merge_holes_act->setStatusTip(tr("Merge area holes together, or merge holes with the object boundary to cut out this part.") + (boolean_merge_holes_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select one area object with holes to activate this tool.")))); + + // cutout_enabled + bool const cutout_enabled = single_object_selected && (have_area || have_line) && !have_area_with_holes && (*(map->selectedObjectsBegin()))->asPath()->parts().front().isClosed(); + cutout_physical_act->setEnabled(cutout_enabled); + cutout_physical_act->setStatusTip(tr("Create a cutout of some objects or the whole map.") + (cutout_physical_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a closed path object as cutout shape to activate this tool.")))); + cutaway_physical_act->setEnabled(cutout_enabled); + cutaway_physical_act->setStatusTip(tr("Cut away some objects or everything in a limited area.") + (cutaway_physical_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select a closed path object as cutout shape to activate this tool.")))); +} + +void MapEditorController::updateSymbolAndObjectDependentActions() +{ + const Symbol* single_symbol = activeSymbol(); + bool single_symbol_compatible = false; + bool single_symbol_different = false; + if (!editing_in_progress) + { + map->getSelectionToSymbolCompatibility(single_symbol, single_symbol_compatible, single_symbol_different); + } + + switch_symbol_act->setEnabled(single_symbol_compatible && single_symbol_different); + switch_symbol_act->setStatusTip(tr("Switches the symbol of the selected objects to the selected symbol.") + (switch_symbol_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object and a fitting, different symbol to activate this tool.")))); + fill_border_act->setEnabled(single_symbol_compatible && single_symbol_different); + fill_border_act->setStatusTip(tr("Fill the selected lines or create a border for the selected areas.") + (fill_border_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object and a fitting, different symbol to activate this tool.")))); + distribute_points_act->setEnabled(single_symbol && single_symbol->getType() == Symbol::Point + && containsPathObject(map->selectedObjects())); + distribute_points_act->setStatusTip(tr("Places evenly spaced point objects along an existing path object") + (distribute_points_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one path object and a single point symbol to activate this tool.")))); +} + +void MapEditorController::undoStepAvailabilityChanged() +{ + if (mode != MapEditor) + return; + + undo_act->setEnabled(map->undoManager().canUndo()); + redo_act->setEnabled(map->undoManager().canRedo()); + clear_undo_redo_history_act->setEnabled(undo_act->isEnabled() || redo_act->isEnabled()); +} + +void MapEditorController::clipboardChanged(QClipboard::Mode mode) +{ + if (mode == QClipboard::Clipboard) + updatePasteAvailability(); +} + +void MapEditorController::updatePasteAvailability() +{ + if (paste_act) + { + paste_act->setEnabled( + QApplication::clipboard()->mimeData() + && QApplication::clipboard()->mimeData()->hasFormat(MimeType::OpenOrienteeringObjects()) + && !editing_in_progress); + } +} + +void MapEditorController::showWholeMap() +{ + QRectF map_extent = map->calculateExtent(true, !main_view->areAllTemplatesHidden(), main_view); + map_widget->adjustViewToRect(map_extent, MapWidget::ContinuousZoom); +} + +void MapEditorController::editToolClicked() +{ + setEditTool(); +} + +void MapEditorController::editLineToolClicked() +{ + if (!current_tool || current_tool->toolType() != MapEditorTool::EditLine) + setTool(new EditLineTool(this, edit_line_tool_act)); +} + +void MapEditorController::drawPointClicked() +{ + setTool(new DrawPointTool(this, draw_point_act)); +} + +void MapEditorController::drawPathClicked() +{ + setTool(new DrawPathTool(this, draw_path_act, false, true)); +} + +void MapEditorController::drawCircleClicked() +{ + setTool(new DrawCircleTool(this, draw_circle_act, false)); +} + +void MapEditorController::drawRectangleClicked() +{ + setTool(new DrawRectangleTool(this, draw_rectangle_act, false)); +} + +void MapEditorController::drawFreehandClicked() +{ + setTool(new DrawFreehandTool(this, draw_freehand_act, false)); +} + +void MapEditorController::drawFillClicked() +{ + setTool(new FillTool(this, draw_fill_act)); +} + +void MapEditorController::drawTextClicked() +{ + setTool(new DrawTextTool(this, draw_text_act)); +} + +void MapEditorController::deleteClicked() +{ + if (editing_in_progress) + return; + map->deleteSelectedObjects(); +} + +void MapEditorController::duplicateClicked() +{ + Q_ASSERT(!map->selectedObjects().empty()); + + std::vector new_objects; + new_objects.reserve(map->getNumSelectedObjects()); + + for (const auto object : map->selectedObjects()) + { + Object* duplicate = object->duplicate(); + map->addObject(duplicate); + new_objects.push_back(duplicate); + } + + auto undo_step = new DeleteObjectsUndoStep(map); + MapPart* part = map->getCurrentPart(); + + map->clearObjectSelection(false); + for (const auto object : new_objects) + { + undo_step->addObject(part->findObjectIndex(object)); + map->addObjectToSelection(object, object == new_objects.back()); + } + + map->setObjectsDirty(); + map->push(undo_step); + setEditTool(); + window->showStatusBarMessage(tr("Duplicated %n object(s)", nullptr, int(new_objects.size())), 2000); +} + +void MapEditorController::switchSymbolClicked() +{ + SwitchSymbolUndoStep* switch_step = nullptr; + ReplaceObjectsUndoStep* replace_step = nullptr; + AddObjectsUndoStep* add_step = nullptr; + DeleteObjectsUndoStep* delete_step = nullptr; + std::vector old_objects; + std::vector new_objects; + MapPart* part = map->getCurrentPart(); + Symbol* symbol = activeSymbol(); + + bool close_paths = false, split_up = false; + Symbol::Type contained_types = symbol->getContainedTypes(); + if (contained_types & Symbol::Area && !(contained_types & Symbol::Line)) + close_paths = true; + else if (contained_types & Symbol::Line && !(contained_types & Symbol::Area)) + split_up = true; + + if (close_paths) + { + replace_step = new ReplaceObjectsUndoStep(map); + } + else if (split_up) + { + add_step = new AddObjectsUndoStep(map); + delete_step = new DeleteObjectsUndoStep(map); + } + else + { + switch_step = new SwitchSymbolUndoStep(map); + } + + for (const auto object : map->selectedObjects()) + { + if (close_paths) + { + replace_step->addObject(part->findObjectIndex(object), object->duplicate()); + } + else if (split_up) + { + add_step->addObject(part->findObjectIndex(object), object); + old_objects.push_back(object); + } + else + { + switch_step->addObject(part->findObjectIndex(object), object->getSymbol()); + } + + if (!split_up) + { + object->setSymbol(symbol, true); + } + + if (object->getType() == Object::Path) + { + PathObject* path_object = object->asPath(); + if (close_paths) + { + path_object->closeAllParts(); + } + else if (split_up) + { + for (const auto& part : path_object->parts()) + { + auto new_object = new PathObject { part }; + new_object->setSymbol(symbol, true); + new_objects.push_back(new_object); + } + + Q_ASSERT(!new_objects.empty()); + if (!new_objects.empty()) + { + new_objects.front()->setTags(path_object->tags()); + } + } + } + + object->update(); + } + + if (split_up) + { + map->clearObjectSelection(false); + for (auto object : old_objects) + { + map->deleteObject(object, true); + } + for (auto object : new_objects) + { + map->addObject(object); + map->addObjectToSelection(object, false); + } + map->emitSelectionChanged(); + // Do not merge this loop into the upper one; + // theoretically undo step indices could be wrong this way. + for (auto object : new_objects) + { + delete_step->addObject(part->findObjectIndex(object)); + } + } + + map->setObjectsDirty(); + if (close_paths) + { + map->push(replace_step); + } + else if (split_up) + { + auto combined_step = new CombinedUndoStep(map); + combined_step->push(add_step); + combined_step->push(delete_step); + map->push(combined_step); + } + else + { + map->push(switch_step); + } + map->emitSelectionEdited(); + // Also emit selectionChanged, as symbols of selected objects changed + map->emitSelectionChanged(); +} + +void MapEditorController::fillBorderClicked() +{ + Symbol* symbol = activeSymbol(); + std::vector new_objects; + new_objects.reserve(map->getNumSelectedObjects()); + + bool close_paths = false, split_up = false; + Symbol::Type contained_types = symbol->getContainedTypes(); + if (contained_types & Symbol::Area && !(contained_types & Symbol::Line)) + close_paths = true; + else if (contained_types & Symbol::Line && !(contained_types & Symbol::Area)) + split_up = true; + + auto undo_step = new DeleteObjectsUndoStep(map); + MapPart* part = map->getCurrentPart(); + + for (const auto object : map->selectedObjects()) + { + if (split_up && object->getType() == Object::Path) + { + PathObject* path_object = object->asPath(); + for (const auto& part : path_object->parts()) + { + auto new_object = new PathObject { part }; + new_object->setSymbol(symbol, true); + map->addObject(new_object); + new_objects.push_back(new_object); + } + } + else + { + Object* duplicate = object->duplicate(); + duplicate->setSymbol(symbol, true); + if (close_paths && duplicate->getType() == Object::Path) + { + PathObject* path_object = duplicate->asPath(); + path_object->closeAllParts(); + } + map->addObject(duplicate); + new_objects.push_back(duplicate); + } + } + + map->setObjectsDirty(); + map->clearObjectSelection(false); + for (int i = 0; i < (int)new_objects.size(); ++i) + { + map->addObjectToSelection(new_objects[i], i == (int)new_objects.size() - 1); + undo_step->addObject(part->findObjectIndex(new_objects[i])); + } + map->push(undo_step); +} +void MapEditorController::selectObjectsClicked(bool select_exclusively) +{ + bool selection_changed = false; + if (select_exclusively) + { + if (map->getNumSelectedObjects() > 0) + selection_changed = true; + map->clearObjectSelection(false); + } + + bool object_selected = false; + MapPart* part = map->getCurrentPart(); + for (int i = 0, size = part->getNumObjects(); i < size; ++i) + { + Object* object = part->getObject(i); + if (symbol_widget->isSymbolSelected(object->getSymbol()) && !(!select_exclusively && map->isObjectSelected(object))) + { + map->addObjectToSelection(object, false); + object_selected = true; + } + } + + selection_changed |= object_selected; + if (selection_changed) + map->emitSelectionChanged(); + + if (object_selected) + { + if (current_tool && current_tool->isDrawTool()) + setEditTool(); + } + else + { + QMessageBox::warning(window, tr("Object selection"), tr("No objects were selected because there are no objects with the selected symbols.")); + } +} + +void MapEditorController::deselectObjectsClicked() +{ + bool selection_changed = false; + + MapPart* part = map->getCurrentPart(); + for (int i = 0, size = part->getNumObjects(); i < size; ++i) + { + Object* object = part->getObject(i); + if (symbol_widget->isSymbolSelected(object->getSymbol()) && map->isObjectSelected(object)) + { + map->removeObjectFromSelection(object, false); + selection_changed = true; + } + } + + if (selection_changed) + { + map->emitSelectionChanged(); + + if (current_tool && current_tool->isDrawTool()) + setEditTool(); + } +} + +void MapEditorController::selectAll() +{ + auto num_selected_objects = map->getNumSelectedObjects(); + map->clearObjectSelection(false); + map->getCurrentPart()->applyOnAllObjects([this](Object* object) { + map->addObjectToSelection(object, false); + }); + + if (map->getNumSelectedObjects() != num_selected_objects) + { + map->emitSelectionChanged(); + if (current_tool && current_tool->isDrawTool()) + setEditTool(); + } +} + +void MapEditorController::selectNothing() +{ + if (map->getNumSelectedObjects() > 0) + map->clearObjectSelection(true); +} + +void MapEditorController::invertSelection() +{ + auto selection = Map::ObjectSelection{ map->selectedObjects() }; + map->clearObjectSelection(false); + map->getCurrentPart()->applyOnAllObjects([this, &selection](Object* object) { + if (selection.find(object) == end(selection)) + map->addObjectToSelection(object, false); + }); + + if (map->getCurrentPart()->getNumObjects() > 0) + { + map->emitSelectionChanged(); + if (current_tool && current_tool->isDrawTool()) + setEditTool(); + } +} + +void MapEditorController::selectByCurrentSymbols() +{ + selectObjectsClicked(true); +} + +void MapEditorController::switchDashesClicked() +{ + auto undo_step = new SwitchDashesUndoStep(map); + MapPart* part = map->getCurrentPart(); + + for (const auto object : map->selectedObjects()) + { + if (object->getSymbol()->getContainedTypes() & Symbol::Line) + { + PathObject* path = reinterpret_cast(object); + path->reverse(); + object->update(); + + undo_step->addObject(part->findObjectIndex(object)); + } + } + + map->setObjectsDirty(); + map->push(undo_step); + map->emitSelectionEdited(); +} + +/// \todo Review use of container API +float connectPaths_FindClosestEnd(const std::vector& objects, PathObject* a, int a_index, PathPartVector::size_type path_part_a, bool path_part_a_begin, PathObject** out_b, int* out_b_index, int* out_path_part_b, bool* out_path_part_b_begin) +{ + float best_dist_sq = std::numeric_limits::max(); + for (int i = a_index; i < (int)objects.size(); ++i) + { + PathObject* b = reinterpret_cast(objects[i]); + if (b->getSymbol() != a->getSymbol()) + continue; + + auto num_parts = b->parts().size(); + for (PathPartVector::size_type path_part_b = (a == b) ? path_part_a : 0; path_part_b < num_parts; ++path_part_b) + { + PathPart& part = b->parts()[path_part_b]; + if (!part.isClosed()) + { + for (int begin = 0; begin < 2; ++begin) + { + bool path_part_b_begin = (begin == 0); + if (a == b && path_part_a == path_part_b && path_part_a_begin == path_part_b_begin) + continue; + + MapCoord& coord_a = a->getCoordinate(path_part_a_begin ? a->parts()[path_part_a].first_index : (a->parts()[path_part_a].last_index)); + MapCoord& coord_b = b->getCoordinate(path_part_b_begin ? b->parts()[path_part_b].first_index : (b->parts()[path_part_b].last_index)); + float distance_sq = coord_a.distanceSquaredTo(coord_b); + if (distance_sq < best_dist_sq) + { + best_dist_sq = distance_sq; + *out_b = b; + *out_b_index = i; + *out_path_part_b = path_part_b; + *out_path_part_b_begin = path_part_b_begin; + if (distance_sq == 0) + return 0; + } + } + } + } + } + return best_dist_sq; +} + +void MapEditorController::connectPathsClicked() +{ + std::vector objects; + std::vector undo_objects; + std::vector deleted_objects; + + // Collect all objects in question + objects.reserve(map->getNumSelectedObjects()); + undo_objects.reserve(map->getNumSelectedObjects()); + for (const auto object : map->selectedObjects()) + { + if (object->getSymbol()->getContainedTypes() & Symbol::Line && object->getType() == Object::Path) + { + object->update(); + objects.push_back(object); + undo_objects.push_back(nullptr); + } + } + + AddObjectsUndoStep* add_step = nullptr; + MapPart* part = map->getCurrentPart(); + + while (true) + { + // Find the closest pair of open ends of objects with the same symbol, + // which is closer than a threshold + PathObject* best_object_a = nullptr; + PathObject* best_object_b = nullptr; + int best_object_a_index = 0; + int best_object_b_index = 0; + auto best_part_a = PathPartVector::size_type { 0 }; + auto best_part_b = PathPartVector::size_type { 0 }; + bool best_part_a_begin = false; + bool best_part_b_begin = false; + float best_dist_sq = std::numeric_limits::max(); + + for (int i = 0; i < (int)objects.size(); ++i) + { + PathObject* a = reinterpret_cast(objects[i]); + + // Choose connection threshold as maximum of 0.35mm, 1.5 * largest line extent, and 6 pixels + // TODO: instead of 6 pixels, use a physical size as soon as screen dpi is in the settings + auto close_distance_sq = qMax(0.35, 1.5 * a->getSymbol()->calculateLargestLineExtent()); + close_distance_sq = qMax(close_distance_sq, 0.001 * main_view->pixelToLength(6)); + close_distance_sq = qPow(close_distance_sq, 2); + + auto num_parts = a->parts().size(); + for (PathPartVector::size_type path_part_a = 0; path_part_a < num_parts; ++path_part_a) + { + PathPart& part = a->parts()[path_part_a]; + if (!part.isClosed()) + { + PathObject* b; + int b_index; + int path_part_b; + bool path_part_b_begin; + float distance_sq; + + for (int begin = 0; begin < 2; ++begin) + { + bool path_part_a_begin = (begin == 0); + distance_sq = connectPaths_FindClosestEnd(objects, a, i, path_part_a, path_part_a_begin, &b, &b_index, &path_part_b, &path_part_b_begin); + if (distance_sq <= close_distance_sq && distance_sq < best_dist_sq) + { + best_dist_sq = distance_sq; + best_object_a = a; + best_object_b = b; + best_object_a_index = i; + best_object_b_index = b_index; + best_part_a = path_part_a; + best_part_b = path_part_b; + best_part_a_begin = path_part_a_begin; + best_part_b_begin = path_part_b_begin; + } + } + } + } + } + + // Abort if no possible connections found + if (best_dist_sq == std::numeric_limits::max()) + break; + + // Create undo objects for a and b + if (!undo_objects[best_object_a_index]) + undo_objects[best_object_a_index] = best_object_a->duplicate(); + if (!undo_objects[best_object_b_index]) + undo_objects[best_object_b_index] = best_object_b->duplicate(); + + // Connect the best parts + if (best_part_a_begin && best_part_b_begin) + { + best_object_b->parts()[best_part_b].reverse(); + best_object_a->connectPathParts(best_part_a, best_object_b, best_part_b, true); + } + else if (best_part_a_begin && !best_part_b_begin) + { + if (best_object_a == best_object_b && best_part_a == best_part_b) + best_object_a->parts()[best_part_a].connectEnds(); + else + best_object_a->connectPathParts(best_part_a, best_object_b, best_part_b, true); + } + else if (!best_part_a_begin && best_part_b_begin) + { + if (best_object_a == best_object_b && best_part_a == best_part_b) + best_object_a->parts()[best_part_a].connectEnds(); + else + best_object_a->connectPathParts(best_part_a, best_object_b, best_part_b, false); + } + else //if (!best_part_a_begin && !best_part_b_begin) + { + best_object_b->parts()[best_part_b].reverse(); + best_object_a->connectPathParts(best_part_a, best_object_b, best_part_b, false); + } + + if (best_object_a != best_object_b) + { + // Copy all remaining parts of object b over to a + best_object_a->getCoordinate(best_object_a->getCoordinateCount() - 1).setHolePoint(true); + for (auto i = PathPartVector::size_type { 0 }; i < best_object_b->parts().size(); ++i) + { + if (i != best_part_b) + best_object_a->appendPathPart(best_object_b->parts()[i]); + } + + // Create an add step for b + int b_index_in_part = part->findObjectIndex(best_object_b); + deleted_objects.push_back(best_object_b); + if (!add_step) + add_step = new AddObjectsUndoStep(map); + add_step->addObject(b_index_in_part, undo_objects[best_object_b_index]); + undo_objects[best_object_b_index] = nullptr; + + // Delete b from the active list + objects.erase(objects.begin() + best_object_b_index); + undo_objects.erase(undo_objects.begin() + best_object_b_index); + } + else + { + // Delete the part which has been merged with another part + if (best_part_a != best_part_b) + best_object_a->deletePart(best_part_b); + } + } + + // Create undo step? + ReplaceObjectsUndoStep* replace_step = nullptr; + for (int i = 0; i < (int)undo_objects.size(); ++i) + { + if (undo_objects[i]) + { + // The object was changed, update the new version + objects[i]->forceUpdate(); /// @todo get rid of force if possible + + // Add the old version to the undo step + if (!replace_step) + replace_step = new ReplaceObjectsUndoStep(map); + replace_step->addObject(part->findObjectIndex(objects[i]), undo_objects[i]); + } + } + + if (add_step) + { + for (auto object : deleted_objects) + { + map->removeObjectFromSelection(object, false); + map->getCurrentPart()->deleteObject(object, false); + } + } + + if (add_step || replace_step) + { + auto undo_step = new CombinedUndoStep(map); + if (replace_step) + undo_step->push(replace_step); + if (add_step) + undo_step->push(add_step); + map->push(undo_step); + map->setObjectsDirty(); + } + + map->emitSelectionChanged(); + map->emitSelectionEdited(); +} +void MapEditorController::cutClicked() +{ + setTool(new CutTool(this, cut_tool_act)); +} +void MapEditorController::cutHoleClicked() +{ + setTool(new CutHoleTool(this, cut_hole_act, CutHoleTool::Path)); +} + +void MapEditorController::cutHoleCircleClicked() +{ + setTool(new CutHoleTool(this, cut_hole_circle_act, CutHoleTool::Circle)); +} + +void MapEditorController::cutHoleRectangleClicked() +{ + setTool(new CutHoleTool(this, cut_hole_rectangle_act, CutHoleTool::Rect)); +} + +void MapEditorController::rotateClicked() +{ + setTool(new RotateTool(this, rotate_act)); +} + +void MapEditorController::rotatePatternClicked() +{ + setTool(new RotatePatternTool(this, rotate_pattern_act)); +} + +void MapEditorController::scaleClicked() +{ + setTool(new ScaleTool(this, scale_act)); +} + +void MapEditorController::measureClicked(bool checked) +{ + if (!measure_dock_widget) + { + measure_dock_widget = new EditorDockWidget(tr("Measure"), measure_act, this, window); + measure_dock_widget->toggleViewAction()->setVisible(false); + auto measure_widget = new MeasureWidget(map); + measure_dock_widget->setWidget(measure_widget); + measure_dock_widget->setObjectName(QString::fromLatin1("Measure dock widget")); + addFloatingDockWidget(measure_dock_widget); + } + + measure_dock_widget->setVisible(checked); +} + +void MapEditorController::booleanUnionClicked() +{ + if (!BooleanTool(BooleanTool::Union, map).executePerSymbol()) + QMessageBox::warning(window, tr("Error"), tr("Unification failed.")); +} + +void MapEditorController::booleanIntersectionClicked() +{ + if (!BooleanTool(BooleanTool::Intersection, map).execute()) + QMessageBox::warning(window, tr("Error"), tr("Intersection failed.")); +} + +void MapEditorController::booleanDifferenceClicked() +{ + if (!BooleanTool(BooleanTool::Difference, map).execute()) + QMessageBox::warning(window, tr("Error"), tr("Difference failed.")); +} + +void MapEditorController::booleanXOrClicked() +{ + if (!BooleanTool(BooleanTool::XOr, map).execute()) + QMessageBox::warning(window, tr("Error"), tr("XOr failed.")); +} + +void MapEditorController::booleanMergeHolesClicked() +{ + if (map->getNumSelectedObjects() != 1) + return; + + if (!BooleanTool(BooleanTool::MergeHoles, map).execute()) + QMessageBox::warning(window, tr("Error"), tr("Merging holes failed.")); +} + +void MapEditorController::convertToCurvesClicked() +{ + auto undo_step = new ReplaceObjectsUndoStep(map); + MapPart* part = map->getCurrentPart(); + + for (const auto object : map->selectedObjects()) + { + if (object->getType() != Object::Path) + continue; + + PathObject* path = object->asPath(); + PathObject* undo_duplicate = nullptr; + if (path->convertToCurves(&undo_duplicate)) + { + undo_step->addObject(part->findObjectIndex(path), undo_duplicate); + // TODO: make threshold configurable? + const auto threshold = 0.08; + path->simplify(nullptr, threshold); + } + path->update(); + } + + if (undo_step->isEmpty()) + delete undo_step; + else + { + map->setObjectsDirty(); + map->push(undo_step); + map->emitSelectionEdited(); + } +} + +void MapEditorController::simplifyPathClicked() +{ + // TODO: make threshold configurable! + const auto threshold = 0.1; + + auto undo_step = new ReplaceObjectsUndoStep(map); + MapPart* part = map->getCurrentPart(); + + for (const auto object : map->selectedObjects()) + { + if (object->getType() != Object::Path) + continue; + + PathObject* path = object->asPath(); + PathObject* undo_duplicate = nullptr; + if (path->simplify(&undo_duplicate, threshold)) + undo_step->addObject(part->findObjectIndex(path), undo_duplicate); + path->update(); + } + + if (undo_step->isEmpty()) + delete undo_step; + else + { + map->setObjectsDirty(); + map->push(undo_step); + map->emitSelectionEdited(); + } +} + +void MapEditorController::cutoutPhysicalClicked() +{ + setTool(new CutoutTool(this, cutout_physical_act, false)); +} + +void MapEditorController::cutawayPhysicalClicked() +{ + setTool(new CutoutTool(this, cutaway_physical_act, true)); +} + +void MapEditorController::distributePointsClicked() +{ + Q_ASSERT(activeSymbol()->getType() == Symbol::Point); + PointSymbol* point = activeSymbol()->asPoint(); + + DistributePointsTool::Settings settings; + if (!DistributePointsTool::showSettingsDialog(window, point, settings)) + return; + + // Create points along paths + std::vector created_objects; + for (const auto object : map->selectedObjects()) + { + if (object->getType() == Object::Path) + DistributePointsTool::execute(object->asPath(), point, settings, created_objects); + } + if (created_objects.empty()) + return; + + // Add points to map + for (auto o : created_objects) + map->addObject(o); + + // Create undo step and select new objects + map->clearObjectSelection(false); + MapPart* part = map->getCurrentPart(); + auto delete_step = new DeleteObjectsUndoStep(map); + for (std::size_t i = 0; i < created_objects.size(); ++i) + { + Object* object = created_objects[i]; + delete_step->addObject(part->findObjectIndex(object)); + map->addObjectToSelection(object, i == created_objects.size() - 1); + } + map->push(delete_step); + map->setObjectsDirty(); +} + +void MapEditorController::addFloatingDockWidget(QDockWidget* dock_widget) +{ + if (!window->restoreDockWidget(dock_widget)) + { + dock_widget->setFloating(true); + // We must set the size from the sizeHint() ourselves, + // otherwise QDockWidget may use the minimum size. + QRect geometry(window->pos(), dock_widget->sizeHint()); + geometry.translate(window->width() - geometry.width(), window->centralWidget()->y()); + const int max_height = window->centralWidget()->height(); + if (geometry.height() > max_height) + geometry.setHeight(max_height); + dock_widget->setGeometry(geometry); + connect(dock_widget, &QDockWidget::dockLocationChanged, this, &MapEditorController::saveWindowState); + } +} + +void MapEditorController::paintOnTemplateClicked(bool checked) +{ + if (!checked) + finishPaintOnTemplate(); + else if (last_painted_on_template) + paintOnTemplate(last_painted_on_template); + else + paintOnTemplateSelectClicked(); +} + +void MapEditorController::paintOnTemplateSelectClicked() +{ + PaintOnTemplateSelectDialog paintDialog(map, main_view, last_painted_on_template, window); + paintDialog.setWindowModality(Qt::WindowModal); + if (paintDialog.exec() == QDialog::Accepted) + { + last_painted_on_template = paintDialog.getSelectedTemplate(); + paintOnTemplate(last_painted_on_template); + } +} + +void MapEditorController::enableGPSDisplay(bool enable) +{ + if (enable) + { + gps_display->startUpdates(); + + // Create gps_track_recorder if we can determine a template track filename + constexpr int gps_track_draw_update_interval = 10 * 1000; // in milliseconds + if (! window->currentPath().isEmpty()) + { + // Find or create a template for the track with a specific name + QString gpx_file_path = + QFileInfo(window->currentPath()).absoluteDir().canonicalPath() + + QLatin1Char('/') + + QFileInfo(window->currentPath()).completeBaseName() + + QLatin1String(" - GPS-") + + QDate::currentDate().toString(Qt::ISODate) + + QLatin1String(".gpx"); + + bool new_template = true; // Indicates that we really add a new template. + TemplateTrack* track = nullptr; + int template_index = 0; + for ( ; template_index < map->getNumTemplates(); ++template_index) + { + auto temp = map->getTemplate(template_index); + if (temp->getTemplatePath() == gpx_file_path) + { + // There is a template for this track. + new_template = false; + track = qobject_cast(temp); + break; + } + } + + // Derive new visibility from previous/default one. + auto visibility = main_view->getTemplateVisibility(track); + visibility.opacity = std::max(0.5f, visibility.opacity); + visibility.visible = true; + + if (!track) + { + if (!new_template) + { + // Need to replace the template at template_index + map->setTemplateAreaDirty(template_index); + map->deleteTemplate(template_index); + } + track = new TemplateTrack(gpx_file_path, map); + // This will set the state to 'Loaded', so we need to reset it + // to `Unloaded` to allow for loading the file when it becomes + // visible for the first time. + track->configureForGPSTrack(); + if (QFileInfo::exists(gpx_file_path)) + { + track->unloadTemplateFile(); + track->loadTemplateFile(false); + } + map->addTemplate(track, template_index); + // When the map is saved, the new track must be saved even if it is empty. + track->setHasUnsavedChanges(true); + map->setTemplatesDirty(); + } + + main_view->setTemplateVisibility(track, visibility); + map->setTemplateAreaDirty(template_index); + + gps_track_recorder = new GPSTrackRecorder(gps_display, track, gps_track_draw_update_interval, map_widget); + } + } + else + { + gps_display->stopUpdates(); + + delete gps_track_recorder; + gps_track_recorder = nullptr; + } + gps_display->setVisible(enable); + + if (! enable) + { + gps_marker_display->stopPath(); + gps_temporary_path_act->setChecked(false); + } + + gps_distance_rings_action->setEnabled(enable); + gps_temporary_point_act->setEnabled(enable); + gps_temporary_path_act->setEnabled(enable); + updateDrawPointGPSAvailability(); +} + +void MapEditorController::enableGPSDistanceRings(bool enable) +{ + gps_display->enableDistanceRings(enable); +} + +void MapEditorController::updateDrawPointGPSAvailability() +{ + const Symbol* symbol = activeSymbol(); + draw_point_gps_act->setEnabled(symbol && gps_display->isVisible() && symbol->getType() == Symbol::Point && !symbol->isHidden()); + move_to_gps_pos_act->setEnabled(gps_display->isVisible()); +} + +void MapEditorController::drawPointGPSClicked() +{ + setTool(new DrawPointGPSTool(gps_display, this, draw_point_gps_act)); +} + +void MapEditorController::gpsTemporaryPointClicked() +{ + if (gps_marker_display->addPoint()) + gps_temporary_clear_act->setEnabled(true); +} + +void MapEditorController::gpsTemporaryPathClicked(bool enable) +{ + if (enable) + { + gps_marker_display->startPath(); + gps_temporary_clear_act->setEnabled(true); + } + else + gps_marker_display->stopPath(); +} + +void MapEditorController::gpsTemporaryClearClicked() +{ + if (QMessageBox::question(window, tr("Clear temporary markers"), tr("Are you sure you want to delete all temporary GPS markers? This cannot be undone."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + + gps_marker_display->stopPath(); + gps_temporary_path_act->setChecked(false); + gps_marker_display->clear(); + gps_temporary_clear_act->setEnabled(false); +} + +void MapEditorController::enableCompassDisplay(bool enable) +{ + if (!compass_display || !show_top_bar_button || !top_action_bar) + { + Q_ASSERT(!"Prerequisites must be initialized"); + } + else if (enable) + { + auto size = compass_display->sizeHint(); + if (top_action_bar->isVisible()) + compass_display->setGeometry(0, top_action_bar->size().height(), size.width(), size.height()); + else + compass_display->setGeometry(show_top_bar_button->size().width(), 0, size.width(), size.height()); + connect(&Compass::getInstance(), &Compass::azimuthChanged, compass_display, &CompassDisplay::setAzimuth); + compass_display->show(); + } + else + { + disconnect(&Compass::getInstance(), &Compass::azimuthChanged, compass_display, &CompassDisplay::setAzimuth); + compass_display->hide(); + } +} + +void MapEditorController::alignMapWithNorth(bool enable) +{ + const int update_interval = 1000; // milliseconds + + if (enable) + { + Compass::getInstance().startUsage(); + gps_display->enableHeadingIndicator(true); + + connect(&align_map_with_north_timer, &QTimer::timeout, this, &MapEditorController::alignMapWithNorthUpdate); + align_map_with_north_timer.start(update_interval); + alignMapWithNorthUpdate(); + } + else + { + align_map_with_north_timer.disconnect(); + align_map_with_north_timer.stop(); + + gps_display->enableHeadingIndicator(false); + Compass::getInstance().stopUsage(); + + main_view->setRotation(0); + } +} + +void MapEditorController::alignMapWithNorthUpdate() +{ + // Time in milliseconds for which the rotation should not be updated after + // the user interacted with the map widget + const int interaction_time_threshold = 1500; + if (map_widget->getTimeSinceLastInteraction() < interaction_time_threshold) + return; + + // Set map rotation + main_view->setRotation(M_PI / -180.0 * Compass::getInstance().getCurrentAzimuth()); +} + +void MapEditorController::hideTopActionBar() +{ + top_action_bar->hide(); + show_top_bar_button->show(); + show_top_bar_button->raise(); + + compass_display->move(show_top_bar_button->size().width(), 0); +} + +void MapEditorController::showTopActionBar() +{ + show_top_bar_button->hide(); + top_action_bar->show(); + top_action_bar->raise(); + + compass_display->move(0, top_action_bar->size().height()); +} + +void MapEditorController::mobileSymbolSelectorClicked() +{ + // NOTE: this does not handle window resizes, however it is assumed that in + // mobile mode there is no window, instead the application is running fullscreen. + symbol_widget->setGeometry(window->rect()); + symbol_widget->raise(); + symbol_widget->show(); + connect(symbol_widget, &SymbolWidget::selectedSymbolsChanged, this, &MapEditorController::mobileSymbolSelectorFinished); +} + +void MapEditorController::mobileSymbolSelectorFinished() +{ + disconnect(symbol_widget, &SymbolWidget::selectedSymbolsChanged, this, &MapEditorController::mobileSymbolSelectorFinished); + symbol_widget->hide(); +} + +void MapEditorController::updateMapPartsUI() +{ + if (!mappart_selector_box) + { + mappart_selector_box = new QComboBox(); + mappart_selector_box->setToolTip(tr("Map parts")); + } + + if (!mappart_merge_menu) + { + mappart_merge_menu = new QMenu(); + mappart_merge_menu->menuAction()->setMenuRole(QAction::NoRole); + mappart_merge_menu->setTitle(tr("Merge this part with")); + } + + if (!mappart_move_menu) + { + mappart_move_menu = new QMenu(); + mappart_move_menu->menuAction()->setMenuRole(QAction::NoRole); + mappart_move_menu->setTitle(tr("Move selected objects to")); + } + + const QSignalBlocker selector_box_blocker(mappart_selector_box); + mappart_selector_box->clear(); + mappart_merge_menu->clear(); + mappart_move_menu->clear(); + + const int count = map ? map->getNumParts() : 0; + const bool have_multiple_parts = (count > 1); + mappart_merge_menu->setEnabled(have_multiple_parts); + mappart_move_menu->setEnabled(have_multiple_parts && map->getNumSelectedObjects() > 0); + if (mappart_remove_act) + { + mappart_remove_act->setEnabled(have_multiple_parts); + mappart_merge_act->setEnabled(have_multiple_parts); + } + if (toolbar_mapparts && !toolbar_mapparts->isVisible()) + { + toolbar_mapparts->setVisible(have_multiple_parts); + } + + if (count > 0) + { + const int current = map->getCurrentPartIndex(); + for (int i = 0; i < count; ++i) + { + QString part_name = map->getPart(i)->getName(); + mappart_selector_box->addItem(part_name); + + if (i != current) + { + auto action = new QAction(part_name, this); + mappart_merge_mapper->setMapping(action, i); + connect(action, QOverload::of(&QAction::triggered), mappart_merge_mapper, QOverload<>::of(&QSignalMapper::map)); + mappart_merge_menu->addAction(action); + + action = new QAction(part_name, this); + mappart_move_mapper->setMapping(action, i); + connect(action, QOverload::of(&QAction::triggered), mappart_move_mapper, QOverload<>::of(&QSignalMapper::map)); + mappart_move_menu->addAction(action); + } + } + + mappart_selector_box->setCurrentIndex(current); + } +} + +void MapEditorController::addMapPart() +{ + bool accepted = false; + QString name = QInputDialog::getText( + window, + tr("Add new part..."), + tr("Enter the name of the map part:"), + QLineEdit::Normal, + QString(), + &accepted ); + if (accepted && !name.isEmpty()) + { + auto part = new MapPart(name, map); + map->addPart(part, map->getCurrentPartIndex() + 1); + map->setCurrentPart(part); + map->push(new MapPartUndoStep(map, MapPartUndoStep::RemoveMapPart, part)); + } +} + +void MapEditorController::removeMapPart() +{ + auto part = map->getCurrentPart(); + + QMessageBox::StandardButton button = + QMessageBox::question( + window, + tr("Remove current part"), + tr("Do you want to remove map part \"%1\" and all its objects?").arg(part->getName()), + QMessageBox::Yes | QMessageBox::No ); + + if (button == QMessageBox::Yes) + { + auto index = map->getCurrentPartIndex(); + UndoStep* undo_step = new MapPartUndoStep(map, MapPartUndoStep::AddMapPart, index); + + auto i = part->getNumObjects(); + if (i > 0) + { + auto add_step = new AddObjectsUndoStep(map); + do + { + --i; + auto object = part->getObject(i); + add_step->addObject(i, object); + part->deleteObject(object, true); + } + while (i > 0); + + auto combined_step = new CombinedUndoStep(map); + combined_step->push(add_step); + combined_step->push(undo_step); + undo_step = combined_step; + } + + map->push(undo_step); + map->removePart(index); + } +} + +void MapEditorController::renameMapPart() +{ + bool accepted = false; + QString name = + QInputDialog::getText( + window, + tr("Rename current part..."), + tr("Enter the name of the map part:"), + QLineEdit::Normal, + map->getCurrentPart()->getName(), + &accepted ); + if (accepted && !name.isEmpty()) + { + map->push(new MapPartUndoStep(map, MapPartUndoStep::ModifyMapPart, map->getCurrentPartIndex())); + map->getCurrentPart()->setName(name); + } +} + +void MapEditorController::changeMapPart(int index) +{ + if (index >= 0) + { + map->setCurrentPartIndex(std::size_t(index)); + window->showStatusBarMessage(tr("Switched to map part '%1'.").arg(map->getCurrentPart()->getName()), 1000); + } +} + +void MapEditorController::reassignObjectsToMapPart(int target) +{ + auto current = map->getCurrentPartIndex(); + auto* current_part = map->getPart(current); + std::vector objects(map->selectedObjects().size()); + std::transform(map->selectedObjectsBegin(), map->selectedObjectsEnd(), begin(objects), [current_part](auto* object) { + return current_part->findObjectIndex(object); + }); + std::sort(objects.rbegin(), objects.rend()); + map->reassignObjectsToMapPart(begin(objects), end(objects), current, target); + + auto undo = new SwitchPartUndoStep(map, target, current); + for (auto i : objects) + undo->addObject(i); + map->push(undo); +} + +void MapEditorController::mergeCurrentMapPartTo(int target) +{ + MapPart* const source_part = map->getCurrentPart(); + MapPart* const target_part = map->getPart(target); + const QMessageBox::StandardButton button = + QMessageBox::question( + window, + tr("Merge map parts"), + tr("Do you want to move all objects from map part \"%1\" to \"%2\", " + "and to remove \"%1\"?") + .arg(source_part->getName(), target_part->getName()), + QMessageBox::Yes | QMessageBox::No ); + + if (button == QMessageBox::Yes) + { + // Beware that the source part is removed, and + // the target part's index might change during merge. + auto source = map->getCurrentPartIndex(); + UndoStep* add_part_step = new MapPartUndoStep(map, MapPartUndoStep::AddMapPart, source); + + auto first = map->mergeParts(source, target); + + auto switch_part_undo = new SwitchPartUndoStep(map, target, source); + for (auto i = target_part->getNumObjects(); i > first; --i) + switch_part_undo->addObject(0); + + auto undo = new CombinedUndoStep(map); + undo->push(switch_part_undo); + undo->push(add_part_step); + map->push(undo); + } +} + +void MapEditorController::mergeAllMapParts() +{ + QString const name = map->getCurrentPart()->getName(); + const QMessageBox::StandardButton button = + QMessageBox::question( + window, + tr("Merge map parts"), + tr("Do you want to move all objects to map part \"%1\", and to remove all other map parts?").arg(name), + QMessageBox::Yes | QMessageBox::No ); + + if (button == QMessageBox::Yes) + { + auto undo = new CombinedUndoStep(map); + + // For simplicity, we merge to the first part, + // but keep the properties (i.e. name) of the current part. + map->setCurrentPartIndex(0); + MapPart* target_part = map->getPart(0); + + for (auto i = map->getNumParts() - 1; i > 0; --i) + { + UndoStep* add_part_step = new MapPartUndoStep(map, MapPartUndoStep::AddMapPart, i); + auto first = map->mergeParts(i, 0); + auto switch_part_undo = new SwitchPartUndoStep(map, 0, i); + for (auto j = target_part->getNumObjects(); j > first; --j) + switch_part_undo->addObject(0); + undo->push(switch_part_undo); + undo->push(add_part_step); + } + + undo->push(new MapPartUndoStep(map, MapPartUndoStep::ModifyMapPart, 0)); + target_part->setName(name); + + map->push(undo); + } +} + + +void MapEditorController::paintOnTemplate(Template* temp) +{ + auto tool = qobject_cast(getTool()); + if (!tool) + { + tool = new PaintOnTemplateTool(this, paint_on_template_act); + setTool(tool); + } + + hideAllTemplates(false); + auto vis = main_view->getTemplateVisibility(temp); + vis.visible = true; + main_view->setTemplateVisibility(temp, vis); + temp->setTemplateAreaDirty(); + + tool->setTemplate(temp); +} + +void MapEditorController::finishPaintOnTemplate() +{ + if (auto tool = qobject_cast(current_tool)) + { + tool->deactivate(); + } +} + +void MapEditorController::templateAdded(int /*pos*/, const Template* /*temp*/) +{ + if (map->getNumTemplates() == 1) + templateAvailabilityChanged(); +} + +void MapEditorController::templateDeleted(int /*pos*/, const Template* /*temp*/) +{ + if (map->getNumTemplates() == 0) + templateAvailabilityChanged(); +} + +void MapEditorController::setMapAndView(Map* map, MapView* map_view) +{ + Q_ASSERT(map); + Q_ASSERT(map_view); + + if (this->map) + { + this->map->disconnect(this); + if (symbol_widget) + { + this->map->disconnect(symbol_widget); + } + updateMapPartsUI(); + } + + this->map = map; + this->main_view = map_view; + + connect(&map->undoManager(), &UndoManager::canRedoChanged, this, &MapEditorController::undoStepAvailabilityChanged); + connect(&map->undoManager(), &UndoManager::canUndoChanged, this, &MapEditorController::undoStepAvailabilityChanged); + connect(map, &Map::objectSelectionChanged, this, &MapEditorController::objectSelectionChanged); + connect(map, &Map::templateAdded, this, &MapEditorController::templateAdded); + connect(map, &Map::templateDeleted, this, &MapEditorController::templateDeleted); + connect(map, &Map::closedTemplateAvailabilityChanged, this, &MapEditorController::closedTemplateAvailabilityChanged); + connect(map, &Map::spotColorPresenceChanged, this, &MapEditorController::spotColorPresenceChanged); + connect(map, &Map::currentMapPartChanged, this, &MapEditorController::updateMapPartsUI); + connect(map, &Map::mapPartAdded, this, &MapEditorController::updateMapPartsUI); + connect(map, &Map::mapPartChanged, this, &MapEditorController::updateMapPartsUI); + connect(map, &Map::mapPartDeleted, this, &MapEditorController::updateMapPartsUI); + + if (symbol_widget) + { + delete symbol_widget; + symbol_widget = nullptr; + createSymbolWidget(); + } + + if (window) + { + updateWidgets(); + } +} + +void MapEditorController::updateWidgets() +{ + undoStepAvailabilityChanged(); + objectSelectionChanged(); + if (mode == MapEditor) + { + if (main_view) + { + show_grid_act->setChecked(main_view->isGridVisible()); + hide_all_templates_act->setChecked(main_view->areAllTemplatesHidden()); + overprinting_simulation_act->setChecked(main_view->isOverprintingSimulationEnabled()); + } + if (map) + { + hatch_areas_view_act->setChecked(map->isAreaHatchingEnabled()); + baseline_view_act->setChecked(map->isBaselineViewEnabled()); + closedTemplateAvailabilityChanged(); + spotColorPresenceChanged(map->hasSpotColors()); + updateMapPartsUI(); + } + } +} + +void MapEditorController::createSymbolWidget(QWidget* parent) +{ + if (!symbol_widget) + { + symbol_widget = new SymbolWidget(map, mobile_mode, parent); + connect(symbol_widget, &SymbolWidget::switchSymbolClicked, this, &MapEditorController::switchSymbolClicked); + connect(symbol_widget, &SymbolWidget::fillBorderClicked, this, &MapEditorController::fillBorderClicked); + connect(symbol_widget, &SymbolWidget::selectObjectsClicked, this, &MapEditorController::selectObjectsClicked); + connect(symbol_widget, &SymbolWidget::deselectObjectsClicked, this, &MapEditorController::deselectObjectsClicked); + connect(symbol_widget, &SymbolWidget::selectedSymbolsChanged, this, &MapEditorController::selectedSymbolsChanged); + if (symbol_dock_widget) + { + Q_ASSERT(!parent); + symbol_dock_widget->setWidget(symbol_widget); + } + + selectedSymbolsChanged(); + } +} + +void MapEditorController::importClicked() +{ + QSettings settings; + QString import_directory = settings.value(QString::fromLatin1("importFileDirectory"), QDir::homePath()).toString(); + + QStringList map_names; + QStringList map_extensions; + for (auto format : FileFormats.formats()) + { + if (!format->supportsImport()) + continue; + + map_names.push_back(format->primaryExtension().toUpper()); + map_extensions.append(format->fileExtensions()); + } + map_names.removeDuplicates(); + map_extensions.removeDuplicates(); + + QString filename = FileDialog::getOpenFileName( + window, + tr("Import %1, GPX, OSM or DXF file").arg( + map_names.join(QString::fromLatin1(", "))), + import_directory, + QString::fromLatin1("%1 (%2 *.gpx *.osm *.dxf);;%3 (*.*)").arg( + tr("Importable files"), QLatin1String("*.") + map_extensions.join(QString::fromLatin1(" *.")), tr("All files")) ); + if (filename.isEmpty() || filename.isNull()) + return; + + settings.setValue(QString::fromLatin1("importFileDirectory"), QFileInfo(filename).canonicalPath()); + + bool success = false; + auto map_format = FileFormats.findFormatForFilename(filename); + if (map_format) + { + // Map format recognized by filename extension + importMapFile(filename, true); // Error reporting in Map::loadFrom() + return; + } + else if (filename.endsWith(QLatin1String(".dxf"), Qt::CaseInsensitive) + || filename.endsWith(QLatin1String(".gpx"), Qt::CaseInsensitive) + || filename.endsWith(QLatin1String(".osm"), Qt::CaseInsensitive)) + { + // Fallback: Legacy geo file import + importGeoFile(filename); + return; // Error reporting in Track::import() + } + else if (importMapFile(filename, false)) + { + // Last resort: Map format recognition by try-and-error + success = true; + } + else + { + QMessageBox::critical(window, tr("Error"), tr("Cannot import the selected file because its file format is not supported.")); + return; + } + + if (!success) + { + /// \todo Reword message (not a map file here). Requires new translations + QMessageBox::critical(window, tr("Error"), tr("Cannot import the selected map file because it could not be loaded.")); + } +} + +bool MapEditorController::importGeoFile(const QString& filename) +{ + TemplateTrack temp(filename, map); + return !temp.configureAndLoad(window, main_view) + || temp.import(window); +} + +bool MapEditorController::importMapFile(const QString& filename, bool show_errors) +{ + Map imported_map; + imported_map.setScaleDenominator(map->getScaleDenominator()); // for non-scaled geodata + + if (!imported_map.loadFrom(filename, window, nullptr, false, show_errors)) + return false; + + if (imported_map.symbolSetId() != map->symbolSetId()) + { + auto crt_filename = filename; + crt_filename.replace(filename.lastIndexOf(QLatin1Char('.')), 4, QLatin1String(".crt")); + if (!QFileInfo::exists(crt_filename)) + crt_filename.replace(filename.lastIndexOf(QLatin1Char('.')), 4, QLatin1String(".CRT")); + if (!QFileInfo::exists(crt_filename)) + crt_filename = ReplaceSymbolSetDialog::discoverCrtFile(imported_map.symbolSetId(), map->symbolSetId()); + + if (QFileInfo::exists(crt_filename)) + { + QFile crt_file{ crt_filename }; + if (!crt_file.open(QFile::ReadOnly)) + { + QMessageBox::warning(window, + ::OpenOrienteering::Map::tr("Error"), + ::OpenOrienteering::Map::tr("Cannot open file:\n%1\nfor reading.").arg(crt_filename)); + return false; + } + if (!ReplaceSymbolSetDialog::showDialogForCRT(window, imported_map, *map, crt_file)) + { + auto choice = QMessageBox::question(window, + ::OpenOrienteering::Map::tr("Import..."), + ::OpenOrienteering::Map::tr("Symbol replacement was canceled.\n" + "Import the data anyway?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (choice == QMessageBox::No) + return false; + } + } + } + + map->importMap(&imported_map, Map::MinimalObjectImport | Map::GeorefImport, window); + return true; +} + +// slot +void MapEditorController::setViewOptionsEnabled(bool enabled) +{ + pan_act->setEnabled(enabled); + show_grid_act->setEnabled(enabled); +// hatch_areas_view_act->setEnabled(enabled); +// baseline_view_act->setEnabled(enabled); + overprinting_simulation_act->setEnabled(enabled); + hide_all_templates_act->setEnabled(enabled); +} + + +// ### EditorDockWidget ### + +EditorDockWidget::EditorDockWidget(const QString& title, QAction* action, MapEditorController* editor, QWidget* parent) +: QDockWidget(title, parent) +, action(action) +, editor(editor) +{ + if (editor) + { + connect(this, &EditorDockWidget::dockLocationChanged, editor, &MapEditorController::saveWindowState); + } + + if (action) + { + connect(toggleViewAction(), &QAction::toggled, action, &QAction::setChecked); + } + +#ifdef Q_OS_ANDROID + size_grip = new QSizeGrip(this); +#endif +} + +bool EditorDockWidget::event(QEvent* event) +{ + switch (event->type()) + { + case QEvent::ShortcutOverride: + if (editor->getWindow()->shortcutsBlocked()) + event->accept(); + break; + + case QEvent::Show: + QTimer::singleShot(0, this, SLOT(raise())); // clazy:exclude=old-style-connect + break; + + default: + ; // nothing + } + + return QDockWidget::event(event); +} + +void EditorDockWidget::resizeEvent(QResizeEvent* event) +{ +#ifdef Q_OS_ANDROID + QRect rect(QPoint(0,0), size_grip->sizeHint()); + rect.moveBottomRight(geometry().bottomRight() - geometry().topLeft()); + int fw = style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, this); + rect.translate(-fw, -fw); + size_grip->setGeometry(rect); + size_grip->raise(); +#endif + + QDockWidget::resizeEvent(event); +} + + + +// ### MapEditorToolAction ### + +MapEditorToolAction::MapEditorToolAction(const QIcon& icon, const QString& text, QObject* parent) + : QAction(icon, text, parent) +{ + setCheckable(true); + setMenuRole(QAction::NoRole); + connect(this, &QAction::triggered, this, &MapEditorToolAction::triggeredImpl); +} + +void MapEditorToolAction::triggeredImpl(bool checked) +{ + if (checked) + emit activated(); + else + setChecked(true); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/map/map_editor.h b/src/gui/map/map_editor.h new file mode 100644 index 0000000..97f6735 --- /dev/null +++ b/src/gui/map/map_editor.h @@ -0,0 +1,796 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_EDITOR_H +#define OPENORIENTEERING_MAP_EDITOR_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gui/main_window_controller.h" + +class QAction; +class QByteArray; +class QComboBox; +class QDockWidget; +class QFrame; +class QKeyEvent; +class QLabel; +class QMenu; +class QSignalMapper; +// IWYU pragma: no_forward_declare QString +class QToolBar; +class QToolButton; +class QWidget; + +namespace OpenOrienteering { + +class ActionGridBar; +class CompassDisplay; +class EditorDockWidget; +class FileFormat; +class GPSDisplay; +class GPSTemporaryMarkers; +class GPSTrackRecorder; +class GeoreferencingDialog; +class MainWindow; +class Map; +class MapEditorActivity; +class MapEditorTool; +class MapFindFeature; +class MapView; +class MapWidget; +class PrintWidget; +class ReopenTemplateDialog; +class Symbol; +class SymbolWidget; +class Template; +class TemplateListWidget; +class TemplatePositionDockWidget; + + +/** + * MainWindowController for editing a map. + * + * Creates menus and toolbars, manages editing tools, + * dock widgets, and much more. + */ +class MapEditorController : public MainWindowController +{ +friend class Map; +Q_OBJECT +public: + /** See MapEditorController constructor. */ + enum OperatingMode + { + MapEditor = 0, + SymbolEditor = 1 + }; + + /** + * Constructs a new MapEditorController for a map. + * + * @param mode Normally, MapEditor should be used. However, as a HACK the + * MapEditorController is also used in the symbol editor for the preview. + * In this case, SymbolEditor is passed to disable showing the menus, + * toolbars, etc. + * @param map A Map which is to be edited by the controller. + * @param map_view A MapView for the given map. + * + * \todo Review/remove mode hack. + * \todo Document and fix ownership of map and map_view. Double deletes waiting... + */ + MapEditorController(OperatingMode mode, Map* map = nullptr, MapView* map_view = nullptr); + + /** Destroys the MapEditorController. */ + ~MapEditorController() override; + + /** Returns if the editor is in mobile mode. */ + bool isInMobileMode() const; + + /** + * Changes to new_tool as the new active tool. + * If there is a current tool before, calls deleteLater() on it. + * new_tool may be nullptr, but it is unusual to have no active tool, so + * consider setEditTool() instead. + */ + void setTool(MapEditorTool* new_tool); + + /** + * Shortcut to change to the point edit tool as new active tool. + * See setTool(). + */ + void setEditTool(); + + /** + * Sets new_override_tool as the new active override tool. + * This takes precedence over all tools set via setTool(). + * new_override_tool may be nullptr, which disables using an override tool + * and re-enables the normal tool set via setTool(). + */ + void setOverrideTool(MapEditorTool* new_override_tool); + + /** Returns the current tool. */ + inline MapEditorTool* getTool() const {return current_tool;} + + /** Returns the default drawing tool for a given symbol. */ + MapEditorTool* getDefaultDrawToolForSymbol(const Symbol* symbol); + + + /** + * @brief Returns the active symbol, or nullptr. + * + * The active symbol is the single symbol which is to be used by drawing + * tools and actions. + * + * It there is no active symbol, this function returns nullptr. + */ + Symbol* activeSymbol() const; + + + /** + * If this is set to true (usually by the current tool), + * undo/redo and saving the map is deactivated. + * + * This is important if the map is in an "unstable" state temporarily. + */ + void setEditingInProgress(bool value); + + /** + * Returns true when editing is in progress. + * @see setEditingInProgress + */ + bool isEditingInProgress() const override; + + /** + * Adds a a floating dock widget to the main window. + * Adjusts some geometric properties. + */ + void addFloatingDockWidget(QDockWidget* dock_widget); + + /** + * Sets the current editor activity. + * new_activity may be nullptr to disable the current editor activity. + */ + void setEditorActivity(MapEditorActivity* new_activity); + + /** Returns the current editor activity. */ + inline MapEditorActivity* getEditorActivity() const {return editor_activity;} + + /** Returns the map on which this controller operates. */ + inline Map* getMap() const {return map;} + /** Returns the main map widget (which is currently the only map widget). */ + inline MapWidget* getMainWidget() const {return map_widget;} + /** Returns this controller's symbol widget, where the symbol selection happens. */ + inline SymbolWidget* getSymbolWidget() const {return symbol_widget;} + + /** Returns if a template position dock widget exists for a template. */ + inline bool existsTemplatePositionDockWidget(Template* temp) const {return template_position_widgets.contains(temp);} + /** Returns the template position dock widget for a template. */ + inline TemplatePositionDockWidget* getTemplatePositionDockWidget(Template* temp) const {return template_position_widgets.value(temp);} + /** Adds a template position dock widget for the given template. */ + void addTemplatePositionDockWidget(Template* temp); + /** + * Removes the template position dock widget for the template. + * + * Should be called by the dock widget if it is closed or the + * template deleted; deletes the dock widget. + */ + void removeTemplatePositionDockWidget(Template* temp); + + + /** + * Shows the given widget in a popup window with specified title. + * + * In the desktop version, the widget is shown inside a dock widget. + * In the mobile version, the widget is shown as a popup over the map, + * ignoring the title. + * + * Make sure that the child widget has a reasonable size hint. + */ + void showPopupWidget(QWidget* child_widget, const QString& title); + + /** + * Deletes the given popup widget, which was previously shown with + * showPopupWidget(). + */ + void deletePopupWidget(QWidget* child_widget); + + + /** + * Returns the action identified by id if it exists, or nullptr. + * This allows the reuse of the controller's actions in dock widgets etc. + */ + QAction* getAction(const char* id); + + /** Override from MainWindowController */ + bool save(const QString& path) override; + /** Override from MainWindowController */ + bool exportTo(const QString& path, const FileFormat* format = nullptr) override; + /** Override from MainWindowController */ + bool load(const QString& path, QWidget* dialog_parent = nullptr) override; + + /** Override from MainWindowController */ + void attach(MainWindow* window) override; + /** Override from MainWindowController */ + void detach() override; + + /** + * @copybrief MainWindowController::keyPressEventFilter + * This implementation passes the event to MapWidget::keyPressEventFilter. + */ + bool keyPressEventFilter(QKeyEvent* event) override; + + /** + * @copybrief MainWindowController::keyReleaseEventFilter + * This implementation passes the event to MapWidget::keyReleaseEventFilter. + */ + bool keyReleaseEventFilter(QKeyEvent* event) override; + +public slots: + /** + * Makes the print/export dock widget visible, and configures it for + * the given task (which is of type PrintWidget::TaskFlags). + */ + void printClicked(int task); + + /** Undoes the last object edit step. */ + void undo(); + /** Redoes the last object edit step */ + void redo(); + /** Cuts the selected object(s). */ + void cut(); + /** Copies the selecte object(s). */ + void copy(); + /** Pastes the object(s) from the clipboard. */ + void paste(); + /** Empties the undo / redo history to save space. */ + void clearUndoRedoHistory(); + + /** Toggles visivbility of the map grid. */ + void showGrid(); + /** Shows the map grid configuration dialog. */ + void configureGrid(); + + /** Activates the pan tool. */ + void pan(); + /** Moves view to GPS position. */ + void moveToGpsPos(); + /** Zooms in in the current map widget. */ + void zoomIn(); + /** Zooms out in the current map widget. */ + void zoomOut(); + /** Shows the dialog to set a custom zoom factor in the current map widget. */ + void setCustomZoomFactorClicked(); + + /** Sets the hatch areas view option. */ + void hatchAreas(bool checked); + /** Sets the baseline view option. */ + void baselineView(bool checked); + /** Sets the "hide all templates" view option. */ + void hideAllTemplates(bool checked); + /** Sets the overprinting simulation view option. */ + void overprintingSimulation(bool checked); + + /** Adjusts the coordinates display of the map widget to the selected option. */ + void coordsDisplayChanged(); + /** Copies the displayed coordinates to the clipboard. */ + void copyDisplayedCoords(); + + /** Shows or hides the symbol pane. */ + void showSymbolWindow(bool show); + /** Shows or hides the color dock widget. */ + void showColorWindow(bool show); + /** Shows a dialog for changing the symbol set ID. */ + void symbolSetIdClicked(); + /** Shows the "load symbols from" dialog. */ + void loadSymbolsFromClicked(); + /** Loads a CRT file and shows the symbol replacement dialog. */ + void loadCrtClicked(); + /** TODO: not implemented yet. */ + void loadColorsFromClicked(); + /** Shows the "scale all symbols" dialog. */ + void scaleAllSymbolsClicked(); + + /** Shows the ScaleMapDialog. */ + void scaleMapClicked(); + /** Shows the RotateMapDialog. */ + void rotateMapClicked(); + /** Shows the dialog to enter map notes. */ + void mapNotesClicked(); + + /** Shows or hides the template setup dock widget. */ + void showTemplateWindow(bool show); + /** Shows a file selector to open a template. */ + void openTemplateClicked(); + /** Shows the ReopenTemplateDialog. */ + void reopenTemplateClicked(); + /** Adjusts action availability based on the presence of templates */ + void templateAvailabilityChanged(); + /** Adjusts action availability based on the presence of closed templates */ + void closedTemplateAvailabilityChanged(); + + /** Shows or hides the tags editor dock widget. */ + void showTagsWindow(bool show); + + /** Shows the GeoreferencingDialog. */ + void editGeoreferencing(); + + /** + * Makes the editor aware of a change of the selected symbols. + */ + void selectedSymbolsChanged(); + + /** + * Makes the editor aware of a change of the selected object. + */ + void objectSelectionChanged(); + + /** Adjusts the enabled state of the undo / redo actions. */ + void undoStepAvailabilityChanged(); + /** Adjusts the enabled state of the paste action (specific signature required). */ + void clipboardChanged(QClipboard::Mode mode); + /** Adjusts the enabled state of the paste action. */ + void updatePasteAvailability(); + + /** + * Checks the presence of spot colors, + * and to disables overprinting simulation if there are no spot colors. + */ + void spotColorPresenceChanged(bool has_spot_colors); + + /** Adjusts the view in the current map widget to show the whole map. */ + void showWholeMap(); + + /** Activates the point edit tool. */ + void editToolClicked(); + /** Activates the line edit tool. */ + void editLineToolClicked(); + /** Activates the draw point tool. */ + void drawPointClicked(); + /** Activates the draw path tool. */ + void drawPathClicked(); + /** Activates the draw circle tool. */ + void drawCircleClicked(); + /** Activates the draw rectangle tool. */ + void drawRectangleClicked(); + /** Activates the draw freehand tool. */ + void drawFreehandClicked(); + /** Activates the draw fill tool. */ + void drawFillClicked(); + /** Activates the draw text tool. */ + void drawTextClicked(); + + /** Deletes the selected object(s) */ + void deleteClicked(); + /** Duplicates the selected object(s) */ + void duplicateClicked(); + /** Switches the symbol of the selected object(s) to the selected symbol. */ + void switchSymbolClicked(); + /** Creates duplicates of the selected object(s) and assigns them the selected symbol. */ + void fillBorderClicked(); + /** Selects all objects with the selected symbol(s) */ + void selectObjectsClicked(bool select_exclusively); + /** Deselects all objects with the selected symbol(s) */ + void deselectObjectsClicked(); + + /** Selects all objects in the current map part. */ + void selectAll(); + /** Clears the object selection. */ + void selectNothing(); + /** Inverts in the object selection in the current map part. */ + void invertSelection(); + /** Selects all objects having the current selected symbols. */ + void selectByCurrentSymbols(); + + /** + * Reverses the selected object(s) direcction(s), + * thus switching dash directions for lines. + */ + void switchDashesClicked(); + /** Connects close endpoints of selected lines */ + void connectPathsClicked(); + /** Activates the cut tool */ + void cutClicked(); + /** Activates the cut hole tool */ + void cutHoleClicked(); + /** Activates the cut circular hole tool */ + void cutHoleCircleClicked(); + /** Activates the cut rectangular hole tool */ + void cutHoleRectangleClicked(); + /** Activates the rotate tool */ + void rotateClicked(); + /** Activates the rotate pattern tool */ + void rotatePatternClicked(); + /** Activates the scale tool */ + void scaleClicked(); + /** Shows or hides the MeasureWidget */ + void measureClicked(bool checked); + /** Calculates the union of selected same-symbol area objects */ + void booleanUnionClicked(); + /** Calculates the intersection of selected same-symbol area objects */ + void booleanIntersectionClicked(); + /** Calculates the difference of selected area objects from the first selected area object */ + void booleanDifferenceClicked(); + /** Calculates the boolean XOr of selected same-symbol area objects */ + void booleanXOrClicked(); + /** Merges holes of the (single) selected area object */ + void booleanMergeHolesClicked(); + /** Converts selected polygonal paths to curves */ + void convertToCurvesClicked(); + /** Tries to remove points of selected paths while retaining their shape */ + void simplifyPathClicked(); + /** Activates the physical cutout tool */ + void cutoutPhysicalClicked(); + /** Activates the physical cutout tool (inversed) */ + void cutawayPhysicalClicked(); + /** Executes the "distribute points along path" action. + * The prerequisites for using the tool must be given. */ + void distributePointsClicked(); + + /** Shows or hides the paint-on-template widget */ + void paintOnTemplateClicked(bool checked); + /** Shows the template selection dialog for for the paint-on-template functionality */ + void paintOnTemplateSelectClicked(); + + /** Enables or disables GPS display. */ + void enableGPSDisplay(bool enable); + /** Enables or disables showing distance rings when GPS display is active. */ + void enableGPSDistanceRings(bool enable); + /** Updates availability of the GPS point drawing tool. */ + void updateDrawPointGPSAvailability(); + /** Switches to the GPS point drawing tool. */ + void drawPointGPSClicked(); + /** Sets a temporary marker at the GPS position. */ + void gpsTemporaryPointClicked(); + /** Draws a temporary path at the GPS position. */ + void gpsTemporaryPathClicked(bool enable); + /** Clears temporary GPS markers. */ + void gpsTemporaryClearClicked(); + + /** Enables or disables digital compass display. */ + void enableCompassDisplay(bool enable); + /** Enables or disables map auto-rotation according to compass. */ + void alignMapWithNorth(bool enable); + /** Called regularly after enabled with alignMapWithNorth() to update the map rotation. */ + void alignMapWithNorthUpdate(); + + /** For mobile UI: hides the top action bar. */ + void hideTopActionBar(); + /** For mobile UI: shows the top action bar again after hiding it. */ + void showTopActionBar(); + /** For mobile UI: shows the symbol selection screen. */ + void mobileSymbolSelectorClicked(); + /** Counterpart to mobileSymbolSelectorClicked(). */ + void mobileSymbolSelectorFinished(); + + /** Creates and adds a new map part */ + void addMapPart(); + /** Removes the current map part */ + void removeMapPart(); + /** Renames the current map part */ + void renameMapPart(); + /** Moves all selected objects to a different map part */ + void reassignObjectsToMapPart(int target); + /** Merges the current map part with another one */ + void mergeCurrentMapPartTo(int target); + /** Merges all map parts into the current one. */ + void mergeAllMapParts(); + + /** Updates action enabled states after a template has been added */ + void templateAdded(int pos, const Template* temp); + /** Updates action enabled states after a template has been deleted */ + void templateDeleted(int pos, const Template* temp); + + /** Imports a track file (GPX, DXF, OSM, ...) into the map */ + bool importGeoFile(const QString& filename); + /** Imports a map file into the loaded map */ + bool importMapFile(const QString& filename, bool show_errors); + /** Shows the import file selector and imports the selected file, if any. */ + void importClicked(); + + /** Sets the enabled state of actions which change how the map is rendered, + * such as with grid, with templates, with overprinting simulation. */ + void setViewOptionsEnabled(bool enabled = true); + + /** Save the current toolbar and dock widget positions */ + void saveWindowState(); + + /** Restore previously saved toolbar and dock widget positions */ + void restoreWindowState(); + +signals: + /** + * @brief Indicates a change of the active symbol. + * @param symbol The new active symbol, or nullptr. + */ + void activeSymbolChanged(const Symbol* symbol); + + void templatePositionDockWidgetClosed(Template* temp); + +protected: + /** + * Adjusts the enabled state of various actions + * after the selected symbol(s) have changed. + * + * In addition, it disables actions as long as some editing is in progress. + * + * The caller shall also call updateSymbolAndObjectDependentActions(). + */ + void updateSymbolDependentActions(); + + /** + * Adjusts the enabled state of various actions + * after the selected object(s) have changed. + * + * In addition, it disables actions as long as some editing is in progress. + * + * The caller shall also call updateSymbolAndObjectDependentActions(). + */ + void updateObjectDependentActions(); + + /** + * Adjusts the enabled state of various actions + * after the selected symbol(s) or object(s) have changed. + * + * In addition, it disables actions as long as some editing is in progress. + */ + void updateSymbolAndObjectDependentActions(); + +protected slots: + void projectionChanged(); + void georeferencingDialogFinished(); + + /** + * Sets the map's current part. + */ + void changeMapPart(int index); + + /** + * Updates all UI components related to map parts. + */ + void updateMapPartsUI(); + +private: + void setMapAndView(Map* map, MapView* map_view); + + /// Updates enabled state of all widgets + void updateWidgets(); + + void createSymbolWidget(QWidget* parent = nullptr); + + void createColorWindow(); + + void createTemplateWindow(); + + void createTagEditor(); + + QAction* newAction(const char* id, const QString& tr_text, QObject* receiver, const char* slot, const char* icon = nullptr, const QString& tr_tip = QString{}, const char* whats_this_link = nullptr); + QAction* newCheckAction(const char* id, const QString& tr_text, QObject* receiver, const char* slot, const char* icon = nullptr, const QString& tr_tip = QString{}, const char* whats_this_link = nullptr); + QAction* newToolAction(const char* id, const QString& tr_text, QObject* receiver, const char* slot, const char* icon = nullptr, const QString& tr_tip = QString{}, const char* whats_this_link = nullptr); + QAction* findAction(const char* id); + void assignKeyboardShortcuts(); + void createActions(); + void createMenuAndToolbars(); + void createMobileGUI(); + + void paintOnTemplate(Template* temp); + void finishPaintOnTemplate(); + + void doUndo(bool redo); + + Map* map; + MapView* main_view; + MapWidget* map_widget; + + OperatingMode mode; + bool mobile_mode; + + MapEditorTool* current_tool; + MapEditorTool* override_tool; + MapEditorActivity* editor_activity; + + Symbol* active_symbol; + + bool editing_in_progress; + + // Action handling + QHash actionsById; + + EditorDockWidget* print_dock_widget; + PrintWidget* print_widget; + + QAction* print_act; + QAction* export_image_act; + QAction* export_pdf_act; + + QAction* undo_act; + QAction* redo_act; + QAction* cut_act; + QAction* copy_act; + QAction* paste_act; + QAction* delete_act; + QAction* select_all_act; + QAction* select_nothing_act; + QAction* invert_selection_act; + QAction* select_by_current_symbol_act; + std::unique_ptr find_feature; + QAction* clear_undo_redo_history_act; + + QAction* pan_act; + QAction* move_to_gps_pos_act; + QAction* zoom_in_act; + QAction* zoom_out_act; + QAction* show_all_act; + QAction* fullscreen_act; + QAction* custom_zoom_act; + QAction* show_grid_act; + QAction* configure_grid_act; + QAction* hatch_areas_view_act; + QAction* baseline_view_act; + QAction* hide_all_templates_act; + QAction* overprinting_simulation_act; + + QAction* map_coordinates_act; + QAction* projected_coordinates_act; + QAction* geographic_coordinates_act; + QAction* geographic_coordinates_dms_act; + + QMenu* toolbars_menu = nullptr; + + QAction* scale_all_symbols_act; + QAction* georeferencing_act; + QAction* scale_map_act; + QAction* rotate_map_act; + QAction* map_notes_act; + QAction* symbol_set_id_act; + + QAction* color_window_act; + QPointer color_dock_widget; + QAction* load_symbols_from_act; + QAction* load_crt_act; + + QAction* symbol_window_act; + EditorDockWidget* symbol_dock_widget; + SymbolWidget* symbol_widget; + + QAction* template_window_act; + QPointer template_dock_widget; + TemplateListWidget* template_list_widget; + QAction* open_template_act; + QAction* reopen_template_act; + + QAction* tags_window_act; + QPointer tags_dock_widget; + + QAction* edit_tool_act; + QAction* edit_line_tool_act; + QAction* draw_point_act; + QAction* draw_path_act; + QAction* draw_circle_act; + QAction* draw_rectangle_act; + QAction* draw_freehand_act; + QAction* draw_fill_act; + QAction* draw_text_act; + + QAction* duplicate_act; + QAction* switch_symbol_act; + QAction* fill_border_act; + QAction* switch_dashes_act; + QAction* connect_paths_act; + QAction* cut_tool_act; + QMenu* cut_hole_menu; + QAction* cut_hole_act; + QAction* cut_hole_circle_act; + QAction* cut_hole_rectangle_act; + QAction* rotate_act; + QAction* rotate_pattern_act; + QAction* scale_act; + QAction* measure_act; + EditorDockWidget* measure_dock_widget; + QAction* boolean_union_act; + QAction* boolean_intersection_act; + QAction* boolean_difference_act; + QAction* boolean_xor_act; + QAction* boolean_merge_holes_act; + QAction* convert_to_curves_act; + QAction* simplify_path_act; + QAction* cutout_physical_act; + QAction* cutaway_physical_act; + QAction* distribute_points_act; + + QAction* paint_on_template_act; + QAction* paint_on_template_settings_act; + Template* last_painted_on_template; + + QAction* touch_cursor_action; + QAction* gps_display_action; + QAction* gps_distance_rings_action; + QAction* draw_point_gps_act; + QAction* gps_temporary_point_act; + QAction* gps_temporary_path_act; + QAction* gps_temporary_clear_act; + GPSTemporaryMarkers* gps_marker_display; + GPSDisplay* gps_display; + GPSTrackRecorder* gps_track_recorder; + QAction* compass_action; + CompassDisplay* compass_display; + QAction* align_map_with_north_act; + QTimer align_map_with_north_timer; + + QAction* mappart_add_act; + QAction* mappart_rename_act; + QAction* mappart_remove_act; + QAction* mappart_merge_act; + QMenu* mappart_merge_menu; + QMenu* mappart_move_menu; + + QAction* import_act; + + QFrame* statusbar_zoom_frame; + QLabel* statusbar_cursorpos_label; + QAction* copy_coords_act; + + QToolBar* toolbar_view; + QToolBar* toolbar_drawing; + QToolBar* toolbar_editing; + QToolBar* toolbar_advanced_editing; + QToolBar* toolbar_mapparts; + + // For mobile UI + ActionGridBar* bottom_action_bar; + ActionGridBar* top_action_bar; + QToolButton* show_top_bar_button; + QAction* mobile_symbol_selector_action; + QMenu* mobile_symbol_button_menu; + + QComboBox* mappart_selector_box; + + QScopedPointer georeferencing_dialog; + QScopedPointer reopen_template_dialog; + + QHash template_position_widgets; + + QSignalMapper* mappart_merge_mapper; + QSignalMapper* mappart_move_mapper; +}; + + + +//### MapEditorController inline code ### + +inline +Symbol* MapEditorController::activeSymbol() const +{ + return active_symbol; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/map/map_editor_activity.cpp b/src/gui/map/map_editor_activity.cpp new file mode 100644 index 0000000..a483cfc --- /dev/null +++ b/src/gui/map/map_editor_activity.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_editor_activity.h" + + +namespace OpenOrienteering { + +MapEditorActivity::~MapEditorActivity() = default; + +void MapEditorActivity::init() +{ + // nothing +} + +void MapEditorActivity::draw(QPainter*, MapWidget*) +{ + // nothing +} + + +} // namespace OpenOrienteering diff --git a/src/gui/map/map_editor_activity.h b/src/gui/map/map_editor_activity.h new file mode 100644 index 0000000..a1e6753 --- /dev/null +++ b/src/gui/map/map_editor_activity.h @@ -0,0 +1,97 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_EDITOR_ACTIVITY_H +#define OPENORIENTEERING_MAP_EDITOR_ACTIVITY_H + +#include + +class QPainter; + +namespace OpenOrienteering { + +class MapWidget; + + +/** + * Represents a type of editing activity, e.g. template position adjustment. + * Only one activity can be active at a time. + * + * This is for example used to close the template adjustment window when + * selecting an edit tool. + * It can also be used to paint activity-specific graphics onto the map. + */ +class MapEditorActivity : public QObject +{ +Q_OBJECT +public: + ~MapEditorActivity() override; + + /** + * All initializations apart from setting variables like the activity object + * should be done here instead of in the constructor, as at the time init() + * is called, the old activity was properly destroyed + * (including reseting the activity drawing). + */ + virtual void init(); + + /** + * Sets the "activity object", which is a void pointer which can be + * used for various purposes (such as identifying the activity). + */ + void setActivityObject(void* address); + + /** + * Returns the "activity object". + * @see setActivityObject() + */ + inline void* getActivityObject() const; + + /** + * All dynamic drawings must be drawn here using the given painter. + * Drawing is only possible in the area specified + * by calling map->setActivityBoundingBox(). + */ + virtual void draw(QPainter* painter, MapWidget* widget); + +protected: + void* activity_object; +}; + + + +//### MapEditorActivity inline code ### + +inline +void MapEditorActivity::setActivityObject(void* address) +{ + activity_object = address; +} + +inline +void* MapEditorActivity::getActivityObject() const +{ + return activity_object; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/map/map_editor_p.h b/src/gui/map/map_editor_p.h new file mode 100644 index 0000000..755316a --- /dev/null +++ b/src/gui/map/map_editor_p.h @@ -0,0 +1,87 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps + * Copyright 2013, 2014, 2015, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_EDITOR_P_H +#define OPENORIENTEERING_MAP_EDITOR_P_H + +#include +#include + +class QEvent; +class QIcon; +class QObject; +class QResizeEvent; +class QSizeGrip; // IWYU pragma: keep +class QString; +class QWidget; + +namespace OpenOrienteering { + +class MapEditorController; +class Template; + + +/** + * Custom QDockWidget which unchecks the associated menu action when closed + * and delivers a notification to its child + */ +class EditorDockWidget : public QDockWidget +{ +Q_OBJECT +public: + EditorDockWidget(const QString& title, QAction* action, + MapEditorController* editor, QWidget* parent = nullptr); + +protected: + bool event(QEvent* event) override; + void resizeEvent(QResizeEvent *event) override; + +private: + QAction* action; + MapEditorController* editor; + +#ifdef Q_OS_ANDROID + QSizeGrip* size_grip; +#endif +}; + + + +/** + * Helper class which disallows deselecting the checkable action by the user + */ +class MapEditorToolAction : public QAction +{ +Q_OBJECT +public: + MapEditorToolAction(const QIcon& icon, const QString& text, QObject* parent); + +signals: + void activated(); + +private slots: + void triggeredImpl(bool checked); +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/map/map_find_feature.cpp b/src/gui/map/map_find_feature.cpp new file mode 100644 index 0000000..c34bd04 --- /dev/null +++ b/src/gui/map/map_find_feature.cpp @@ -0,0 +1,262 @@ +/* + * Copyright 2017, 2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_find_feature.h" + +#include + +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_part.h" +#include "core/objects/object_query.h" +#include "gui/main_window.h" +#include "gui/util_gui.h" +#include "gui/map/map_editor.h" +#include "gui/widgets/tag_select_widget.h" + + +namespace OpenOrienteering { + +class Object; + +MapFindFeature::MapFindFeature(MapEditorController& controller) +: QObject{nullptr} +, controller{controller} +{ + show_action = new QAction(tr("&Find..."), this); + show_action->setMenuRole(QAction::NoRole); + // QKeySequence::Find may be Ctrl+F, which conflicts with "Fill / Create Border" + //show_action->setShortcut(QKeySequence::Find); + //action->setStatusTip(tr_tip); + show_action->setWhatsThis(Util::makeWhatThis("edit_menu.html")); + connect(show_action, &QAction::triggered, this, &MapFindFeature::showDialog); + + find_next_action = new QAction(tr("Find &next"), this); + find_next_action->setMenuRole(QAction::NoRole); + // QKeySequence::FindNext may be F3, which conflicts with "Baseline view" + //find_next_action->setShortcut(QKeySequence::FindNext); + //action->setStatusTip(tr_tip); + find_next_action->setWhatsThis(Util::makeWhatThis("edit_menu.html")); + connect(find_next_action, &QAction::triggered, this, &MapFindFeature::findNext); +} + + +MapFindFeature::~MapFindFeature() = default; // not inlined + + + +void MapFindFeature::setEnabled(bool enabled) +{ + show_action->setEnabled(enabled); + find_next_action->setEnabled(enabled); +} + + + +void MapFindFeature::showDialog() +{ + auto window = controller.getWindow(); + if (!window) + return; + + if (!find_dialog) + { + find_dialog = new QDialog(window); + find_dialog->setWindowTitle(tr("Find objects")); + + text_edit = new QTextEdit; + text_edit->setLineWrapMode(QTextEdit::WidgetWidth); + + tag_selector = new TagSelectWidget; + + auto find_next = new QPushButton(tr("&Find next")); + connect(find_next, &QPushButton::clicked, this, &MapFindFeature::findNext); + + auto find_all = new QPushButton(tr("Find &all")); + connect(find_all, &QPushButton::clicked, this, &MapFindFeature::findAll); + + auto tags_button = new QPushButton(tr("Query editor")); + tags_button->setCheckable(true); + + tag_selector_buttons = tag_selector->makeButtons(); + tag_selector_buttons->setEnabled(false); + + auto button_box = new QDialogButtonBox(QDialogButtonBox::Close | QDialogButtonBox::Help); + connect(button_box, &QDialogButtonBox::rejected, &*find_dialog, &QDialog::hide); + connect(button_box->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &MapFindFeature::showHelp); + + editor_stack = new QStackedLayout(); + editor_stack->addWidget(text_edit); + editor_stack->addWidget(tag_selector); + + connect(tags_button, &QAbstractButton::toggled, this, &MapFindFeature::tagSelectorToggled); + + auto layout = new QGridLayout; + layout->addLayout(editor_stack, 0, 0, 6, 1); + layout->addWidget(find_next, 0, 1, 1, 1); + layout->addWidget(find_all, 1, 1, 1, 1); + layout->addWidget(tags_button, 3, 1, 1, 1); + layout->addWidget(tag_selector_buttons, 5, 1, 1, 1); + layout->addWidget(button_box, 6, 0, 1, 2); + + find_dialog->setLayout(layout); + } + + find_dialog->show(); + find_dialog->raise(); + find_dialog->activateWindow(); +} + + + +ObjectQuery MapFindFeature::makeQuery() const +{ + auto query = ObjectQuery{}; + if (find_dialog) + { + if (editor_stack->currentIndex() == 0) + { + auto text = text_edit->toPlainText().trimmed(); + if (!text.isEmpty()) + { + query = ObjectQueryParser().parse(text); + if (!query || query.getOperator() == ObjectQuery::OperatorSearch) + query = ObjectQuery{ ObjectQuery(ObjectQuery::OperatorSearch, text), + ObjectQuery::OperatorOr, + ObjectQuery(ObjectQuery::OperatorObjectText, text) }; + + } + } + else + { + query = tag_selector->makeQuery(); + } + } + return query; +} + + +void MapFindFeature::findNext() +{ + auto map = controller.getMap(); + auto first_object = map->getFirstSelectedObject(); + map->clearObjectSelection(false); + + Object* next_object = nullptr; + auto query = makeQuery(); + if (!query) + { + if (auto window = controller.getWindow()) + window->showStatusBarMessage(OpenOrienteering::TagSelectWidget::tr("Invalid query"), 2000); + return; + } + + auto search = [&first_object, &next_object, &query](Object* object) { + if (!next_object) + { + if (first_object) + { + if (object == first_object) + first_object = nullptr; + } + else if (query(object)) + { + next_object = object; + } + } + }; + + // Start from selected object + map->getCurrentPart()->applyOnAllObjects(search); + if (!next_object) + { + // Start from first object + first_object = nullptr; + map->getCurrentPart()->applyOnAllObjects(search); + } + + map->clearObjectSelection(false); + if (next_object) + map->addObjectToSelection(next_object, false); + map->emitSelectionChanged(); + + if (!map->selectedObjects().empty()) + controller.setEditTool(); +} + + +void MapFindFeature::findAll() +{ + auto map = controller.getMap(); + map->clearObjectSelection(false); + + auto query = makeQuery(); + if (!query) + { + controller.getWindow()->showStatusBarMessage(OpenOrienteering::TagSelectWidget::tr("Invalid query"), 2000); + return; + } + + map->getCurrentPart()->applyOnMatchingObjects([map](auto object) { + map->addObjectToSelection(object, false); + }, std::cref(query)); + map->emitSelectionChanged(); + controller.getWindow()->showStatusBarMessage(OpenOrienteering::TagSelectWidget::tr("%n object(s) selected", nullptr, map->getNumSelectedObjects()), 2000); + + if (!map->selectedObjects().empty()) + controller.setEditTool(); +} + + + +void MapFindFeature::showHelp() const +{ + Util::showHelp(controller.getWindow(), "find_objects.html"); +} + + + +// slot +void MapFindFeature::tagSelectorToggled(bool active) +{ + editor_stack->setCurrentIndex(active ? 1 : 0); + tag_selector_buttons->setEnabled(active); + if (!active) + { + auto text = tag_selector->makeQuery().toString(); + if (!text.isEmpty()) + text_edit->setText(text); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/map/map_find_feature.h b/src/gui/map/map_find_feature.h new file mode 100644 index 0000000..3f0aca1 --- /dev/null +++ b/src/gui/map/map_find_feature.h @@ -0,0 +1,90 @@ +/* + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_MAP_FIND_FEATURE_H +#define OPENORIENTEERING_MAP_FIND_FEATURE_H + +#include +#include +#include + +class QAction; +class QDialog; +class QStackedLayout; +class QTextEdit; +class QWidget; + +namespace OpenOrienteering { + +class MapEditorController; +class ObjectQuery; +class TagSelectWidget; + + +/** + * Provides an interactive feature for finding objects in the map. + * + * The search text from the provided dialog is trimmed and parsed as an + * ObjectQuery. If parsing fails, the trimmed text is used to find any matching + * text in tag keys, tag values, text object content or symbol name. + */ +class MapFindFeature : public QObject +{ + Q_OBJECT + +public: + MapFindFeature(MapEditorController& controller); + + ~MapFindFeature() override; + + void setEnabled(bool enabled); + + QAction* showDialogAction() { return show_action; } + + QAction* findNextAction() { return find_next_action; } + +private: + void showDialog(); + + ObjectQuery makeQuery() const; + + void findNext(); + + void findAll(); + + void showHelp() const; + + void tagSelectorToggled(bool active); + + MapEditorController& controller; + QPointer find_dialog; // child of controller's window + QStackedLayout* editor_stack = nullptr; // child of find_dialog + QTextEdit* text_edit = nullptr; // child of find_dialog + TagSelectWidget* tag_selector = nullptr; // child of find_dialog + QWidget* tag_selector_buttons = nullptr; // child of find_dialog + QAction* show_action = nullptr; // child of this + QAction* find_next_action = nullptr; // child of this + + Q_DISABLE_COPY(MapFindFeature) +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/map/map_widget.cpp b/src/gui/map/map_widget.cpp new file mode 100644 index 0000000..a99a62c --- /dev/null +++ b/src/gui/map/map_widget.cpp @@ -0,0 +1,1361 @@ +/* + * Copyright 2012-2014 Thomas Schöps + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "map_widget.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/georeferencing.h" +#include "core/latlon.h" +#include "core/map.h" +#include "core/renderables/renderable.h" +#include "gui/touch_cursor.h" +#include "gui/map/map_editor_activity.h" +#include "gui/widgets/action_grid_bar.h" +#include "gui/widgets/key_button_bar.h" +#include "gui/widgets/pie_menu.h" +#include "sensors/gps_display.h" +#include "sensors/gps_temporary_markers.h" +#include "templates/template.h" // IWYU pragma: keep +#include "tools/tool.h" +#include "util/backports.h" // IWYU pragma: keep +#include "util/util.h" + +class QGesture; +// IWYU pragma: no_forward_declare QPinchGesture + + +namespace OpenOrienteering { + +MapWidget::MapWidget(bool show_help, bool force_antialiasing, QWidget* parent) + : QWidget(parent) + , view(nullptr) + , tool(nullptr) + , activity(nullptr) + , coords_type(MAP_COORDS) + , cursorpos_label(nullptr) + , show_help(show_help) + , force_antialiasing(force_antialiasing) + , dragging(false) + , pinching(false) + , pinching_factor(1.0) + , below_template_cache_dirty_rect(rect()) + , above_template_cache_dirty_rect(rect()) + , map_cache_dirty_rect(rect()) + , drawing_dirty_rect_border(0) + , activity_dirty_rect_border(0) + , last_mouse_release_time(QTime::currentTime()) + , current_pressed_buttons(0) + , gps_display(nullptr) + , marker_display(nullptr) +{ + context_menu = new PieMenu(this); +// context_menu->setMinimumActionCount(8); +// context_menu->setIconSize(24); + + setAttribute(Qt::WA_OpaquePaintEvent); + setAttribute(Qt::WA_AcceptTouchEvents, true); + setGesturesEnabled(true); + setAutoFillBackground(false); + setMouseTracking(true); + setFocusPolicy(Qt::ClickFocus); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); +} + +MapWidget::~MapWidget() +{ + // nothing, not inlined +} + +void MapWidget::setMapView(MapView* view) +{ + if (this->view != view) + { + if (this->view) + { + auto map = view->getMap(); + map->removeMapWidget(this); + + disconnect(this->view, &MapView::viewChanged, this, &MapWidget::viewChanged); + disconnect(this->view, &MapView::panOffsetChanged, this, &MapWidget::setPanOffset); + disconnect(this->view, &MapView::visibilityChanged, this, &MapWidget::updateEverything); + } + + this->view = view; + + if (view) + { + connect(this->view, &MapView::viewChanged, this, &MapWidget::viewChanged); + connect(this->view, &MapView::panOffsetChanged, this, &MapWidget::setPanOffset); + connect(this->view, &MapView::visibilityChanged, this, &MapWidget::updateEverything); + + auto map = this->view->getMap(); + map->addMapWidget(this); + } + + update(); + } +} + +void MapWidget::setTool(MapEditorTool* tool) +{ + // Redraw if touch cursor usage changes + bool redrawTouchCursor = (touch_cursor && this->tool && tool + && (this->tool->usesTouchCursor() || tool->usesTouchCursor())); + + this->tool = tool; + + if (tool) + setCursor(tool->getCursor()); + else + unsetCursor(); + if (redrawTouchCursor) + touch_cursor->updateMapWidget(false); +} + +void MapWidget::setActivity(MapEditorActivity* activity) +{ + this->activity = activity; +} + + +void MapWidget::setGesturesEnabled(bool enabled) +{ + gestures_enabled = enabled; + if (enabled) + { + grabGesture(Qt::PinchGesture); + } + else + { + ungrabGesture(Qt::PinchGesture); + } +} + + +void MapWidget::applyMapTransform(QPainter* painter) const +{ + painter->translate(width() / 2.0 + getMapView()->panOffset().x(), + height() / 2.0 + getMapView()->panOffset().y()); + painter->setWorldTransform(getMapView()->worldTransform(), true); +} + +QRectF MapWidget::viewportToView(const QRect& input) const +{ + return QRectF(input.left() - 0.5*width() - pan_offset.x(), input.top() - 0.5*height() - pan_offset.y(), input.width(), input.height()); +} + +QPointF MapWidget::viewportToView(QPoint input) const +{ + return QPointF(input.x() - 0.5*width() - pan_offset.x(), input.y() - 0.5*height() - pan_offset.y()); +} + +QPointF MapWidget::viewportToView(QPointF input) const +{ + return QPointF(input.x() - 0.5*width() - pan_offset.x(), input.y() - 0.5*height() - pan_offset.y()); +} + +QRectF MapWidget::viewToViewport(const QRectF& input) const +{ + return QRectF(input.left() + 0.5*width() + pan_offset.x(), input.top() + 0.5*height() + pan_offset.y(), input.width(), input.height()); +} + +QRectF MapWidget::viewToViewport(const QRect& input) const +{ + return QRectF(input.left() + 0.5*width() + pan_offset.x(), input.top() + 0.5*height() + pan_offset.y(), input.width(), input.height()); +} + +QPointF MapWidget::viewToViewport(QPoint input) const +{ + return QPointF(input.x() + 0.5*width() + pan_offset.x(), input.y() + 0.5*height() + pan_offset.y()); +} + +QPointF MapWidget::viewToViewport(QPointF input) const +{ + return QPointF(input.x() + 0.5*width() + pan_offset.x(), input.y() + 0.5*height() + pan_offset.y()); +} + + +MapCoord MapWidget::viewportToMap(QPoint input) const +{ + return view->viewToMap(viewportToView(input)); +} + +MapCoordF MapWidget::viewportToMapF(QPoint input) const +{ + return view->viewToMapF(viewportToView(input)); +} + +MapCoordF MapWidget::viewportToMapF(QPointF input) const +{ + return view->viewToMapF(viewportToView(input)); +} + +QPointF MapWidget::mapToViewport(MapCoord input) const +{ + return viewToViewport(view->mapToView(input)); +} + +QPointF MapWidget::mapToViewport(MapCoordF input) const +{ + return viewToViewport(view->mapToView(input)); +} + +QRectF MapWidget::mapToViewport(const QRectF& input) const +{ + QRectF result; + rectIncludeSafe(result, mapToViewport(input.topLeft())); + rectIncludeSafe(result, mapToViewport(input.bottomRight())); + if (view->getRotation() != 0) + { + rectIncludeSafe(result, mapToViewport(input.topRight())); + rectIncludeSafe(result, mapToViewport(input.bottomLeft())); + } + return result; +} + +void MapWidget::viewChanged(MapView::ChangeFlags changes) +{ + setDrawingBoundingBox(drawing_dirty_rect_map, drawing_dirty_rect_border, true); + setActivityBoundingBox(activity_dirty_rect_map, activity_dirty_rect_border, true); + updateEverything(); + if (changes.testFlag(MapView::ZoomChange)) + updateZoomDisplay(); +} + +void MapWidget::setPanOffset(QPoint offset) +{ + pan_offset = offset; + update(); +} + +void MapWidget::startDragging(QPoint cursor_pos) +{ + Q_ASSERT(!dragging); + Q_ASSERT(!pinching); + dragging = true; + drag_start_pos = cursor_pos; + normal_cursor = cursor(); + setCursor(Qt::ClosedHandCursor); +} + +void MapWidget::updateDragging(QPoint cursor_pos) +{ + Q_ASSERT(dragging); + view->setPanOffset(cursor_pos - drag_start_pos); +} + +void MapWidget::finishDragging(QPoint cursor_pos) +{ + Q_ASSERT(dragging); + dragging = false; + view->finishPanning(cursor_pos - drag_start_pos); + setCursor(normal_cursor); +} + +void MapWidget::cancelDragging() +{ + dragging = false; + view->setPanOffset(QPoint()); + setCursor(normal_cursor); +} + +qreal MapWidget::startPinching(QPoint center) +{ + Q_ASSERT(!dragging); + Q_ASSERT(!pinching); + pinching = true; + drag_start_pos = center; + pinching_center = center; + pinching_factor = 1.0; + return pinching_factor; +} + +void MapWidget::updatePinching(QPoint center, qreal factor) +{ + Q_ASSERT(pinching); + pinching_center = center; + pinching_factor = factor; + updateZoomDisplay(); + update(); +} + +void MapWidget::finishPinching(QPoint center, qreal factor) +{ + pinching = false; + view->finishPanning(center - drag_start_pos); + view->setZoom(factor * view->getZoom(), viewportToView(center)); +} + +void MapWidget::cancelPinching() +{ + pinching = false; + pinching_factor = 1.0; + update(); +} + +void MapWidget::moveMap(int steps_x, int steps_y) +{ + if (steps_x != 0 || steps_y != 0) + { + try + { + constexpr auto move_factor = 0.25; + auto offset = MapCoord::fromNative64( qRound64(view->pixelToLength(width() * steps_x * move_factor)), + qRound64(view->pixelToLength(height() * steps_y * move_factor)) ); + view->setCenter(view->center() + offset); + } + catch (std::range_error&) + { + // Do nothing + } + } +} + +void MapWidget::ensureVisibilityOfRect(QRectF map_rect, ZoomOption zoom_option) +{ + // Amount in pixels that is scrolled "too much" if the rect is not completely visible + // TODO: change to absolute size using dpi value + const int pixel_border = 70; + auto viewport_rect = mapToViewport(map_rect).toAlignedRect(); + + // TODO: this method assumes that the viewport is not rotated. + + if (rect().contains(viewport_rect.topLeft()) && rect().contains(viewport_rect.bottomRight())) + return; + + auto offset = MapCoordF{ 0, 0 }; + + if (viewport_rect.left() < 0) + offset.rx() = view->pixelToLength(viewport_rect.left() - pixel_border) / 1000.0; + else if (viewport_rect.right() > width()) + offset.rx() = view->pixelToLength(viewport_rect.right() - width() + pixel_border) / 1000.0; + + if (viewport_rect.top() < 0) + offset.ry() = view->pixelToLength(viewport_rect.top() - pixel_border) / 1000.0; + else if (viewport_rect.bottom() > height()) + offset.ry() = view->pixelToLength(viewport_rect.bottom() - height() + pixel_border) / 1000.0; + + if (!qIsNull(offset.lengthSquared())) + view->setCenter(view->center() + offset); + + // If the rect is still not completely in view, we have to zoom out + viewport_rect = mapToViewport(map_rect).toAlignedRect(); + if (!(rect().contains(viewport_rect.topLeft()) && rect().contains(viewport_rect.bottomRight()))) + adjustViewToRect(map_rect, zoom_option); +} + +void MapWidget::adjustViewToRect(QRectF map_rect, ZoomOption zoom_option) +{ + view->setCenter(MapCoord{ map_rect.center() }); + + if (map_rect.isValid()) + { + // NOTE: The loop is an inelegant way to fight inaccuracies that occur somewhere ... + const int pixel_border = 15; + const float initial_zoom = view->getZoom(); + for (int i = 0; i < 10; ++i) + { + float zoom_factor = qMin(height() / (view->lengthToPixel(1000.0 * map_rect.height()) + 2*pixel_border), + width() / (view->lengthToPixel(1000.0 * map_rect.width()) + 2*pixel_border)); + float zoom = view->getZoom() * zoom_factor; + if (zoom_option == DiscreteZoom) + { + zoom = pow(2, 0.5 * floor(2.0 * (std::log2(zoom) - std::log2(initial_zoom))) + std::log2(initial_zoom)); + } + view->setZoom(zoom); + } + } +} + +void MapWidget::moveDirtyRect(QRect& dirty_rect, qreal x, qreal y) +{ + if (dirty_rect.isValid()) + dirty_rect = dirty_rect.translated(x, y).intersected(rect()); +} + +void MapWidget::markTemplateCacheDirty(const QRectF& view_rect, int pixel_border, bool front_cache) +{ + QRect& cache_dirty_rect = front_cache ? above_template_cache_dirty_rect : below_template_cache_dirty_rect; + QRectF viewport_rect = viewToViewport(view_rect); + QRect integer_rect = QRect(viewport_rect.left() - (1+pixel_border), viewport_rect.top() - (1+pixel_border), + viewport_rect.width() + 2*(1+pixel_border), viewport_rect.height() + 2*(1+pixel_border)); + + if (!integer_rect.intersects(rect())) + return; + + if (cache_dirty_rect.isValid()) + cache_dirty_rect = cache_dirty_rect.united(integer_rect); + else + cache_dirty_rect = integer_rect; + + update(integer_rect); +} + +void MapWidget::markObjectAreaDirty(const QRectF& map_rect) +{ + updateMapRect(map_rect, 0, map_cache_dirty_rect); +} + +void MapWidget::setDrawingBoundingBox(QRectF map_rect, int pixel_border, bool do_update) +{ + Q_UNUSED(do_update); + clearDrawingBoundingBox(); + if (map_rect.isValid()) + { + drawing_dirty_rect_map = map_rect; + drawing_dirty_rect_border = pixel_border; + updateMapRect(drawing_dirty_rect_map, drawing_dirty_rect_border, drawing_dirty_rect); + } +} + +void MapWidget::clearDrawingBoundingBox() +{ + drawing_dirty_rect_map.setWidth(0); + if (drawing_dirty_rect.isValid()) + { + update(drawing_dirty_rect); + drawing_dirty_rect.setWidth(0); + } +} + +void MapWidget::setActivityBoundingBox(QRectF map_rect, int pixel_border, bool do_update) +{ + Q_UNUSED(do_update); + clearActivityBoundingBox(); + if (map_rect.isValid()) + { + activity_dirty_rect_map = map_rect; + activity_dirty_rect_border = pixel_border; + updateMapRect(activity_dirty_rect_map, activity_dirty_rect_border, activity_dirty_rect); + } +} + +void MapWidget::clearActivityBoundingBox() +{ + activity_dirty_rect_map.setWidth(0); + if (activity_dirty_rect.isValid()) + { + update(activity_dirty_rect); + activity_dirty_rect.setWidth(0); + } +} + +void MapWidget::updateDrawing(const QRectF& map_rect, int pixel_border) +{ + QRect viewport_rect = calculateViewportBoundingBox(map_rect, pixel_border); + + if (viewport_rect.intersects(rect())) + update(viewport_rect); +} + +void MapWidget::updateMapRect(const QRectF& map_rect, int pixel_border, QRect& cache_dirty_rect) +{ + QRect viewport_rect = calculateViewportBoundingBox(map_rect, pixel_border); + updateViewportRect(viewport_rect, cache_dirty_rect); +} + +void MapWidget::updateViewportRect(QRect viewport_rect, QRect& cache_dirty_rect) +{ + if (viewport_rect.intersects(rect())) + { + if (cache_dirty_rect.isValid()) + cache_dirty_rect = cache_dirty_rect.united(viewport_rect); + else + cache_dirty_rect = viewport_rect; + + update(viewport_rect); + } +} + +void MapWidget::updateDrawingLater(const QRectF& map_rect, int pixel_border) +{ + QRect viewport_rect = calculateViewportBoundingBox(map_rect, pixel_border); + + if (viewport_rect.intersects(rect())) + { + if (!cached_update_rect.isValid()) + { + // Start the update timer + QTimer::singleShot(15, this, SLOT(updateDrawingLaterSlot())); // clazy:exclude=old-style-connect + } + + // NOTE: this may require a mutex for concurrent access with updateDrawingLaterSlot()? + rectIncludeSafe(cached_update_rect, viewport_rect); + } +} + +void MapWidget::updateDrawingLaterSlot() +{ + updateEverythingInRect(cached_update_rect); + cached_update_rect = QRect(); +} + +void MapWidget::updateEverything() +{ + map_cache_dirty_rect = rect(); + below_template_cache_dirty_rect = map_cache_dirty_rect; + above_template_cache_dirty_rect = map_cache_dirty_rect; + update(map_cache_dirty_rect); +} + +void MapWidget::updateEverythingInRect(const QRect& dirty_rect) +{ + rectIncludeSafe(map_cache_dirty_rect, dirty_rect); + rectIncludeSafe(below_template_cache_dirty_rect, dirty_rect); + rectIncludeSafe(above_template_cache_dirty_rect, dirty_rect); + update(dirty_rect); +} + +QRect MapWidget::calculateViewportBoundingBox(const QRectF& map_rect, int pixel_border) const +{ + QRectF view_rect = view->calculateViewBoundingBox(map_rect); + view_rect.adjust(-pixel_border, -pixel_border, +pixel_border, +pixel_border); + return viewToViewport(view_rect).toAlignedRect(); +} + +void MapWidget::setZoomDisplay(std::function setter) +{ + this->zoom_display = setter; + updateZoomDisplay(); +} + +void MapWidget::setCursorposLabel(QLabel* cursorpos_label) +{ + this->cursorpos_label = cursorpos_label; +} + +void MapWidget::updateZoomDisplay() +{ + if (zoom_display) + { + auto zoom = view->getZoom(); + if (pinching) + zoom *= pinching_factor; + zoom_display(tr("%1x", "Zoom factor").arg(zoom, 0, 'g', 3)); + } +} + +void MapWidget::setCoordsDisplay(CoordsType type) +{ + coords_type = type; + updateCursorposLabel(last_cursor_pos); +} + +void MapWidget::updateCursorposLabel(const MapCoordF pos) +{ + last_cursor_pos = pos; + + if (!cursorpos_label) + return; + + if (coords_type == MAP_COORDS) + { + cursorpos_label->setText( QStringLiteral("%1 %2 (%3)"). + arg(locale().toString(pos.x(), 'f', 2), + locale().toString(-pos.y(), 'f', 2), + tr("mm", "millimeters")) ); + } + else + { + const Georeferencing& georef = view->getMap()->getGeoreferencing(); + bool ok = true; + if (coords_type == PROJECTED_COORDS) + { + const QPointF projected_point(georef.toProjectedCoords(pos)); + if (qAbs(georef.getGridScaleFactor() - 1.0) < 0.02) + { + // Grid unit differs less than 2% from meter. + cursorpos_label->setText( + QStringLiteral("%1 %2 (%3)"). + arg(QString::number(projected_point.x(), 'f', 0), + QString::number(projected_point.y(), 'f', 0), + tr("m", "meters")) + ); + } + else + { + cursorpos_label->setText( + QStringLiteral("%1 %2"). + arg(QString::number(projected_point.x(), 'f', 0), + QString::number(projected_point.y(), 'f', 0)) + ); + } + } + else if (coords_type == GEOGRAPHIC_COORDS) + { + const LatLon lat_lon(georef.toGeographicCoords(pos, &ok)); + cursorpos_label->setText( + QString::fromUtf8("%1° %2°"). + arg(locale().toString(lat_lon.latitude(), 'f', 6), + locale().toString(lat_lon.longitude(), 'f', 6)) + ); + } + else if (coords_type == GEOGRAPHIC_COORDS_DMS) + { + const LatLon lat_lon(georef.toGeographicCoords(pos, &ok)); + cursorpos_label->setText( + QStringLiteral("%1 %2"). + arg(georef.degToDMS(lat_lon.latitude()), + georef.degToDMS(lat_lon.longitude())) + ); + } + else + { + // shall never happen + ok = false; + } + + if (!ok) + cursorpos_label->setText(tr("Error")); + } +} + +int MapWidget::getTimeSinceLastInteraction() +{ + if (current_pressed_buttons != 0) + return 0; + else + return last_mouse_release_time.msecsTo(QTime::currentTime()); +} + +void MapWidget::setGPSDisplay(GPSDisplay* gps_display) +{ + this->gps_display = gps_display; +} + +void MapWidget::setTemporaryMarkerDisplay(GPSTemporaryMarkers* marker_display) +{ + this->marker_display = marker_display; +} + +QWidget* MapWidget::getContextMenu() +{ + return context_menu; +} + +QSize MapWidget::sizeHint() const +{ + return QSize(640, 480); +} + +void MapWidget::showHelpMessage(QPainter* painter, const QString& text) const +{ + painter->fillRect(rect(), QColor(Qt::gray)); + + QFont font = painter->font(); + int pixel_size = font.pixelSize(); + if (pixel_size > 0) + { + font.setPixelSize(pixel_size * 2); + } + else + { + pixel_size = font.pointSize(); + font.setPointSize(pixel_size * 2); + } + font.setBold(true); + painter->setFont(font); + painter->drawText(QRect(0, 0, width(), height()), Qt::AlignCenter, text); +} + +bool MapWidget::event(QEvent* event) +{ + switch (event->type()) + { + case QEvent::Gesture: + gestureEvent(static_cast(event)); + return event->isAccepted(); + + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + case QEvent::TouchCancel: + if (static_cast(event)->touchPoints().count() >= 2) + return true; + break; + + case QEvent::KeyPress: + // No focus changing in QWidget::event if Tab is handled by tool. + if (static_cast(event)->key() == Qt::Key_Tab + && keyPressEventFilter(static_cast(event))) + return true; + break; + + default: + ; // nothing + } + + return QWidget::event(event); +} + +void MapWidget::gestureEvent(QGestureEvent* event) +{ + if (tool && tool->gestureEvent(event, this)) + { + event->accept(); + return; + } + + if (QGesture* gesture = event->gesture(Qt::PinchGesture)) + { + QPinchGesture* pinch = static_cast(gesture); + QPoint center = pinch->centerPoint().toPoint(); + qreal factor = pinch->totalScaleFactor(); + switch (pinch->state()) + { + case Qt::GestureStarted: + if (dragging) + cancelDragging(); + if (pinching) + cancelPinching(); + if (tool) + tool->gestureStarted(); + factor = startPinching(center); + pinch->setTotalScaleFactor(factor); + break; + case Qt::GestureUpdated: + updatePinching(center, factor); + break; + case Qt::GestureFinished: + finishPinching(center, factor); + break; + case Qt::GestureCanceled: + cancelPinching(); + break; + default: + Q_UNREACHABLE(); // unknown gesture state + } + event->accept(); + } + else + { + event->ignore(); + } +} + +void MapWidget::paintEvent(QPaintEvent* event) +{ + // Draw on the widget + QPainter painter(this); + QRect exposed = event->rect(); + + if (!view) + { + painter.fillRect(exposed, QColor(Qt::gray)); + return; + } + + // No colors, symbols, or objects? Provide a litte help message ... + bool no_contents = view->getMap()->getNumObjects() == 0 && view->getMap()->getNumTemplates() == 0 && !view->isGridVisible(); + + QTransform transform = painter.worldTransform(); + + // Update all dirty caches + // TODO: It would be an idea to do these updates in a background thread and use the old caches in the meantime + updateAllDirtyCaches(); + + QRect target = exposed; + if (pinching) + { + // Just draw the scaled map and templates + painter.fillRect(exposed, QColor(Qt::gray)); + painter.translate(pinching_center.x(), pinching_center.y()); + painter.scale(pinching_factor, pinching_factor); + painter.translate(-drag_start_pos.x(), -drag_start_pos.y()); + } + else if (pan_offset != QPoint()) + { + // Background color + if (pan_offset.x() > 0) + painter.fillRect(QRect(0, pan_offset.y(), pan_offset.x(), height() - pan_offset.y()), QColor(Qt::gray)); + else if (pan_offset.x() < 0) + painter.fillRect(QRect(width() + pan_offset.x(), pan_offset.y(), -pan_offset.x(), height() - pan_offset.y()), QColor(Qt::gray)); + + if (pan_offset.y() > 0) + painter.fillRect(QRect(0, 0, width(), pan_offset.y()), QColor(Qt::gray)); + else if (pan_offset.y() < 0) + painter.fillRect(QRect(0, height() + pan_offset.y(), width(), -pan_offset.y()), QColor(Qt::gray)); + + target.translate(pan_offset); + } + + if (!view->areAllTemplatesHidden() && isBelowTemplateVisible() && !below_template_cache.isNull() && view->getMap()->getFirstFrontTemplate() > 0) + { + painter.drawImage(target, below_template_cache, exposed); + } + else if (show_help && no_contents) + { + painter.save(); + painter.setTransform(transform); + if (view->getMap()->getNumColors() == 0) + showHelpMessage(&painter, tr("Empty map!\n\nStart by defining some colors:\nSelect Symbols -> Color window to\nopen the color dialog and\ndefine the colors there.")); + else if (view->getMap()->getNumSymbols() == 0) + showHelpMessage(&painter, tr("No symbols!\n\nNow define some symbols:\nRight-click in the symbol bar\nand select \"New symbol\"\nto create one.")); + else + showHelpMessage(&painter, tr("Ready to draw!\n\nStart drawing or load a base map.\nTo load a base map, click\nTemplates -> Open template...") + QLatin1String("\n\n") + tr("Hint: Hold the middle mouse button to drag the map,\nzoom using the mouse wheel, if available.")); + painter.restore(); + } + else + { + painter.fillRect(target, Qt::white); + } + + const auto map_visibility = view->effectiveMapVisibility(); + if (!map_cache.isNull() && map_visibility.visible) + { + qreal saved_opacity = painter.opacity(); + painter.setOpacity(map_visibility.opacity); + painter.drawImage(target, map_cache, exposed); + painter.setOpacity(saved_opacity); + } + + if (!view->areAllTemplatesHidden() && isAboveTemplateVisible() && !above_template_cache.isNull() && view->getMap()->getNumTemplates() - view->getMap()->getFirstFrontTemplate() > 0) + painter.drawImage(target, above_template_cache, exposed); + + //painter.setClipRect(exposed); + + // Show current drawings + if (activity_dirty_rect.isValid()) + activity->draw(&painter, this); + + if (drawing_dirty_rect.isValid()) + tool->draw(&painter, this); + + + // Draw temporary GPS marker display + if (marker_display) + marker_display->paint(&painter); + + // Draw GPS display + if (gps_display) + gps_display->paint(&painter); + + // Draw touch cursor + if (touch_cursor && tool && tool->usesTouchCursor()) + touch_cursor->paint(&painter); + + + painter.setWorldTransform(transform, false); +} + +void MapWidget::resizeEvent(QResizeEvent* event) +{ + map_cache_dirty_rect = rect(); + below_template_cache_dirty_rect = map_cache_dirty_rect; + above_template_cache_dirty_rect = map_cache_dirty_rect; + + if (map_cache.width() < map_cache_dirty_rect.width() || + map_cache.height() < map_cache_dirty_rect.height()) + { + map_cache = QImage(); + below_template_cache = QImage(); + above_template_cache = QImage(); + } + + for (QObject* const child : children()) + { + if (QWidget* child_widget = qobject_cast(child)) + { + child_widget->resize(event->size().width(), child_widget->sizeHint().height()); + } + else if (QWidget* child_widget = qobject_cast(child)) + { + QSize size = child_widget->sizeHint(); + QRect map_widget_rect = rect(); + child_widget->setGeometry( + qMax(0, qRound(map_widget_rect.center().x() - 0.5f * size.width())), + qMax(0, map_widget_rect.bottom() - size.height()), + qMin(size.width(), map_widget_rect.width()), + qMin(size.height(), map_widget_rect.height()) ); + } + } + + QWidget::resizeEvent(event); +} + +void MapWidget::mousePressEvent(QMouseEvent* event) +{ + current_pressed_buttons = event->buttons(); + if (touch_cursor && tool && tool->usesTouchCursor()) + { + touch_cursor->mousePressEvent(event); + if (event->type() == QEvent::MouseMove) + { + _mouseMoveEvent(event); + return; + } + } + _mousePressEvent(event); +} + +void MapWidget::_mousePressEvent(QMouseEvent* event) +{ + if (dragging || pinching) + { + event->accept(); + return; + } + + if (tool && tool->mousePressEvent(event, view->viewToMapF(viewportToView(event->pos())), this)) + { + event->accept(); + return; + } + + if (event->button() == Qt::MiddleButton) + { + startDragging(event->pos()); + event->accept(); + } + else if (event->button() == Qt::RightButton) + { + if (!context_menu->isEmpty()) + context_menu->popup(event->globalPos()); + } +} + +void MapWidget::mouseMoveEvent(QMouseEvent* event) +{ + if (touch_cursor && tool && tool->usesTouchCursor()) + { + if (!touch_cursor->mouseMoveEvent(event)) + return; + } + _mouseMoveEvent(event); +} + +void MapWidget::_mouseMoveEvent(QMouseEvent* event) +{ + if (pinching) + { + event->accept(); + return; + } + else if (dragging) + { + updateDragging(event->pos()); + return; + } + else + { + updateCursorposLabel(view->viewToMapF(viewportToView(event->pos()))); + } + + if (tool && tool->mouseMoveEvent(event, view->viewToMapF(viewportToView(event->pos())), this)) + { + event->accept(); + return; + } +} + +void MapWidget::mouseReleaseEvent(QMouseEvent* event) +{ + current_pressed_buttons = event->buttons(); + last_mouse_release_time = QTime::currentTime(); + if (touch_cursor && tool && tool->usesTouchCursor()) + { + if (!touch_cursor->mouseReleaseEvent(event)) + return; + } + _mouseReleaseEvent(event); +} + +void MapWidget::_mouseReleaseEvent(QMouseEvent* event) +{ + if (dragging) + { + finishDragging(event->pos()); + event->accept(); + return; + } + + if (tool && tool->mouseReleaseEvent(event, view->viewToMapF(viewportToView(event->pos())), this)) + { + event->accept(); + return; + } +} + +void MapWidget::mouseDoubleClickEvent(QMouseEvent* event) +{ + if (touch_cursor && tool && tool->usesTouchCursor()) + { + if (!touch_cursor->mouseDoubleClickEvent(event)) + return; + } + _mouseDoubleClickEvent(event); +} + +void MapWidget::_mouseDoubleClickEvent(QMouseEvent* event) +{ + if (tool && tool->mouseDoubleClickEvent(event, view->viewToMapF(viewportToView(event->pos())), this)) + { + event->accept(); + return; + } + + QWidget::mouseDoubleClickEvent(event); +} + +void MapWidget::wheelEvent(QWheelEvent* event) +{ + if (event->orientation() == Qt::Vertical) + { + if (view) + { + auto degrees = event->delta() / 8.0; + auto num_steps = degrees / 15.0; + auto cursor_pos_view = viewportToView(event->pos()); + bool preserve_cursor_pos = (event->modifiers() & Qt::ControlModifier) == 0; + if (num_steps < 0 && !Settings::getInstance().getSettingCached(Settings::MapEditor_ZoomOutAwayFromCursor).toBool()) + preserve_cursor_pos = !preserve_cursor_pos; + if (preserve_cursor_pos) + { + view->zoomSteps(num_steps, cursor_pos_view); + } + else + { + view->zoomSteps(num_steps); + updateCursorposLabel(view->viewToMapF(cursor_pos_view)); + } + + // Send a mouse move event to the current tool as zooming out can move the mouse position on the map + if (tool) + { + QMouseEvent mouse_event{ QEvent::HoverMove, event->pos(), Qt::NoButton, QApplication::mouseButtons(), Qt::NoModifier }; + tool->mouseMoveEvent(&mouse_event, view->viewToMapF(cursor_pos_view), this); + } + } + + event->accept(); + } + else + event->ignore(); +} + +void MapWidget::leaveEvent(QEvent* event) +{ + if (tool) + tool->leaveEvent(event); +} + +bool MapWidget::keyPressEventFilter(QKeyEvent* event) +{ + if (tool && tool->keyPressEvent(event)) + { + return true; + } + + switch (event->key()) + { + case Qt::Key_F6: + if (dragging) + finishDragging(mapFromGlobal(QCursor::pos())); + else + startDragging(mapFromGlobal(QCursor::pos())); + return true; + + case Qt::Key_Up: + moveMap(0, -1); + return true; + + case Qt::Key_Down: + moveMap(0, 1); + return true; + + case Qt::Key_Left: + moveMap(-1, 0); + return true; + + case Qt::Key_Right: + moveMap(1, 0); + return true; + + default: + return false; + } +} + +bool MapWidget::keyReleaseEventFilter(QKeyEvent* event) +{ + if (tool && tool->keyReleaseEvent(event)) + { + return true; // NOLINT + } + + return false; +} + +QVariant MapWidget::inputMethodQuery(Qt::InputMethodQuery property) const +{ + return inputMethodQuery(property, {}); +} + +QVariant MapWidget::inputMethodQuery(Qt::InputMethodQuery property, const QVariant& argument) const +{ + QVariant result; + if (tool) + result = tool->inputMethodQuery(property, argument); + if (!result.isValid()) + result = QWidget::inputMethodQuery(property); + return result; +} + +void MapWidget::inputMethodEvent(QInputMethodEvent* event) +{ + if (tool) + tool->inputMethodEvent(event); +} + +void MapWidget::enableTouchCursor(bool enabled) +{ + if (enabled && !touch_cursor) + { + touch_cursor.reset(new TouchCursor(this)); + } + else if (!enabled && touch_cursor) + { + touch_cursor->updateMapWidget(false); + touch_cursor.reset(nullptr); + } +} + +void MapWidget::focusOutEvent(QFocusEvent* event) +{ + if (tool) + tool->focusOutEvent(event); + QWidget::focusOutEvent(event); +} + +void MapWidget::contextMenuEvent(QContextMenuEvent* event) +{ + if (event->reason() == QContextMenuEvent::Mouse) + { + // HACK: Ignore context menu events caused by the mouse, because right click + // events need to be sent to the current tool first. + event->ignore(); + return; + } + + if (!context_menu->isEmpty()) + context_menu->popup(event->globalPos()); + + event->accept(); +} + +bool MapWidget::containsVisibleTemplate(int first_template, int last_template) const +{ + if (first_template > last_template) + return false; // no template visible + + Map* map = view->getMap(); + for (int i = first_template; i <= last_template; ++i) + { + if (view->isTemplateVisible(map->getTemplate(i))) + return true; + } + + return false; +} + +inline +bool MapWidget::isAboveTemplateVisible() const +{ + return containsVisibleTemplate(view->getMap()->getFirstFrontTemplate(), view->getMap()->getNumTemplates() - 1); +} + +inline +bool MapWidget::isBelowTemplateVisible() const +{ + return containsVisibleTemplate(0, view->getMap()->getFirstFrontTemplate() - 1); +} + +void MapWidget::updateTemplateCache(QImage& cache, QRect& dirty_rect, int first_template, int last_template, bool use_background) +{ + Q_ASSERT(containsVisibleTemplate(first_template, last_template)); + + if (cache.isNull()) + { + // Lazy allocation of cache image + cache = QImage(size(), QImage::Format_ARGB32_Premultiplied); + dirty_rect = rect(); + } + else + { + // Make sure not to use a bigger draw rect than necessary + dirty_rect = dirty_rect.intersected(rect()); + } + + // Start drawing + QPainter painter(&cache); + painter.setClipRect(dirty_rect); + + // Fill with background color (TODO: make configurable) + if (use_background) + painter.fillRect(dirty_rect, Qt::white); + else + { + QPainter::CompositionMode mode = painter.compositionMode(); + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(dirty_rect, Qt::transparent); + painter.setCompositionMode(mode); + } + + // Draw templates + painter.translate(width() / 2.0, height() / 2.0); + painter.setWorldTransform(view->worldTransform(), true); + + Map* map = view->getMap(); + QRectF map_view_rect = view->calculateViewedRect(viewportToView(dirty_rect)); + + map->drawTemplates(&painter, map_view_rect, first_template, last_template, view, true); + + dirty_rect.setWidth(-1); // => !dirty_rect.isValid() +} + +void MapWidget::updateMapCache(bool use_background) +{ + if (map_cache.isNull()) + { + // Lazy allocation of cache image + map_cache = QImage(size(), QImage::Format_ARGB32_Premultiplied); + map_cache_dirty_rect = rect(); + } + else + { + // Make sure not to use a bigger draw rect than necessary + map_cache_dirty_rect = map_cache_dirty_rect.intersected(rect()); + } + + // Start drawing + QPainter painter; + painter.begin(&map_cache); + painter.setClipRect(map_cache_dirty_rect); + + // Fill with background color (TODO: make configurable) + if (use_background) + { + painter.fillRect(map_cache_dirty_rect, Qt::white); + } + else + { + QPainter::CompositionMode mode = painter.compositionMode(); + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(map_cache_dirty_rect, Qt::transparent); + painter.setCompositionMode(mode); + } + + RenderConfig::Options options(RenderConfig::Screen | RenderConfig::HelperSymbols); + bool use_antialiasing = force_antialiasing || Settings::getInstance().getSettingCached(Settings::MapDisplay_Antialiasing).toBool(); + if (use_antialiasing) + painter.setRenderHint(QPainter::Antialiasing); + else + options |= RenderConfig::DisableAntialiasing | RenderConfig::ForceMinSize; + + Map* map = view->getMap(); + QRectF map_view_rect = view->calculateViewedRect(viewportToView(map_cache_dirty_rect)); + + RenderConfig config = { *map, map_view_rect, view->calculateFinalZoomFactor(), options, 1.0 }; + + painter.translate(width() / 2.0, height() / 2.0); + painter.setWorldTransform(view->worldTransform(), true); +#ifndef Q_OS_ANDROID + if (view->isOverprintingSimulationEnabled()) + map->drawOverprintingSimulation(&painter, config); + else +#endif + map->draw(&painter, config); + + if (view->isGridVisible()) + map->drawGrid(&painter, map_view_rect); + + // Finish drawing + painter.end(); + + map_cache_dirty_rect.setWidth(-1); // => !map_cache_dirty_rect.isValid() +} + +void MapWidget::updateAllDirtyCaches() +{ + if (map_cache_dirty_rect.isValid()) + updateMapCache(false); + + if (!view->areAllTemplatesHidden()) + { + if (below_template_cache_dirty_rect.isValid() && isBelowTemplateVisible()) + updateTemplateCache(below_template_cache, below_template_cache_dirty_rect, 0, view->getMap()->getFirstFrontTemplate() - 1, true); + + if (above_template_cache_dirty_rect.isValid() && isAboveTemplateVisible()) + updateTemplateCache(above_template_cache, above_template_cache_dirty_rect, view->getMap()->getFirstFrontTemplate(), view->getMap()->getNumTemplates() - 1, false); + } +} + +void MapWidget::shiftCache(int sx, int sy, QImage& cache) +{ + if (!cache.isNull()) + { + QImage new_cache(cache.size(), cache.format()); + QPainter painter(&new_cache); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.drawImage(sx, sy, cache); + painter.end(); + cache = new_cache; + } +} + +void MapWidget::shiftCache(int sx, int sy, QPixmap& cache) +{ + if (!cache.isNull()) + { + cache.scroll(sx, sy, cache.rect()); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/map/map_widget.h b/src/gui/map/map_widget.h new file mode 100644 index 0000000..43b564f --- /dev/null +++ b/src/gui/map/map_widget.h @@ -0,0 +1,598 @@ +/* + * Copyright 2012-2014 Thomas Schöps + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAP_WIDGET_H +#define OPENORIENTEERING_MAP_WIDGET_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map_coord.h" +#include "core/map_view.h" + +class QContextMenuEvent; +class QEvent; +class QFocusEvent; +class QGestureEvent; +class QInputMethodEvent; +class QKeyEvent; +class QLabel; +class QMouseEvent; +class QPaintEvent; +class QPainter; +class QPixmap; +class QResizeEvent; +class QWheelEvent; + +namespace OpenOrienteering { + +class GPSDisplay; +class GPSTemporaryMarkers; +class MapEditorActivity; +class MapEditorTool; +class PieMenu; +class TouchCursor; + + +/** + * QWidget for displaying a map. Needs a pointer to a MapView which defines + * the view properties. + * + * For faster display, the widget keeps some cached image internally which + * are of the same size as the widget area. If then for example the map changes, + * the other caches do not need to be redrawn. + *
      + *
    • The map cache contains the currently visible part of the map
    • + *
    • The below template cache contains the currently + * visible part of all templates below the map
    • + *
    • The above template cache contains the currently + * visible part of all templates above the map
    • + *
    + */ +class MapWidget : public QWidget +{ +Q_OBJECT +friend class MapView; +public: + /** Describes different display formats for coordinates. */ + enum CoordsType + { + /** Map coordinates: millimeters on map paper */ + MAP_COORDS, + /** Projected coordinates, e.g. UTM */ + PROJECTED_COORDS, + /** Geographic WGS84 coordinates */ + GEOGRAPHIC_COORDS, + /** Geographic WGS84 coordinates in degrees, minutes, seconds */ + GEOGRAPHIC_COORDS_DMS + }; + + /** Describes how a zoom level can be determined. */ + enum ZoomOption + { + ContinuousZoom, ///< Allow any zoom value in the valid range. + DiscreteZoom, ///< Adjust the zoom to the closes valid step. + }; + + /** + * Constructs a new MapWidget. + * + * @param show_help If set to true, the map widget shows help texts for + * empty maps. + * @param force_antialiasing If set to true, the map widget uses antialiasing + * for display, even if it is disabled in the program settings. + * Useful for the symbol editor. + * @param parent Optional QWidget parent. + */ + MapWidget(bool show_help, bool force_antialiasing, QWidget* parent = nullptr); + + /** Destructs the MapWidget. */ + ~MapWidget() override; + + /** Sets the map view to use for display. Does not take ownership of the view. */ + void setMapView(MapView* view); + + /** Returns the map view used for display. */ + MapView* getMapView() const; + + + /** Sets the tool to use in this widget. Does not take ownership of the tool. */ + void setTool(MapEditorTool* tool); + + /** Sets the activity to use in this widget. Does not take ownership of the activity. */ + void setActivity(MapEditorActivity* activity); + + + /** + * @brief Enables or disables gesture recognition. + * + * MapWidget can recognize gestures, such as two-finger gestures for panning + * and zooming. However, this may disturb the work with editing tools. So gestures + * may be disabled. + * + * @param enabled If true, enables gesture recognition. Otherwise gestures are disabled. + */ + void setGesturesEnabled(bool enabled); + + /** + * @brief Returns true if gesture recognition is enabled. + * + */ + bool gesturesEnabled() const; + + + /** + * Applies the complete transform to the painter which enables to draw + * map objects with map coordinates and have them correctly displayed in + * the widget with the settings of the used MapView. + */ + void applyMapTransform(QPainter* painter) const; + + // Coordinate transformations + + /** Maps viewport (GUI) coordinates to view coordinates (see MapView). */ + QRectF viewportToView(const QRect& input) const; + /** Maps viewport (GUI) coordinates to view coordinates (see MapView). */ + QPointF viewportToView(QPoint input) const; + /** Maps viewport (GUI) coordinates to view coordinates (see MapView). */ + QPointF viewportToView(QPointF input) const; + /** Maps view coordinates (see MapView) to viewport (GUI) coordinates. */ + QRectF viewToViewport(const QRectF& input) const; + /** Maps view coordinates (see MapView) to viewport (GUI) coordinates. */ + QRectF viewToViewport(const QRect& input) const; + /** Maps view coordinates (see MapView) to viewport (GUI) coordinates. */ + QPointF viewToViewport(QPoint input) const; + /** Maps view coordinates (see MapView) to viewport (GUI) coordinates. */ + QPointF viewToViewport(QPointF input) const; + + /** Maps viewport (GUI) coordinates to map coordinates. */ + MapCoord viewportToMap(QPoint input) const; + /** Maps viewport (GUI) coordinates to map coordinates. */ + MapCoordF viewportToMapF(QPoint input) const; + /** Maps viewport (GUI) coordinates to map coordinates. */ + MapCoordF viewportToMapF(QPointF input) const; + /** Maps map coordinates to viewport (GUI) coordinates. */ + QPointF mapToViewport(MapCoord input) const; + /** Maps map coordinates to viewport (GUI) coordinates. */ + QPointF mapToViewport(MapCoordF input) const; + /** Maps map coordinates to viewport (GUI) coordinates. */ + QPointF mapToViewport(QPointF input) const; + /** Maps map coordinates to viewport (GUI) coordinates. */ + QRectF mapToViewport(const QRectF& input) const; + + + /** Notifies the MapWidget of the view having zoomed, moved or rotated. */ + void viewChanged(MapView::ChangeFlags changes); + + + /** + * Returns the current offset (in pixel) during a map pan operation. + */ + QPoint panOffset() const; + + /** + * Sets the current offset (in pixel) during a map pan operation. + */ + void setPanOffset(QPoint offset); + + + /** + * Adjusts the viewport so the given rect is inside the view. + */ + void ensureVisibilityOfRect(QRectF map_rect, ZoomOption zoom_option); // clazy:exclude=function-args-by-ref + + /** + * Sets the view so the rect is centered and zooomed to fill the widget. + */ + void adjustViewToRect(QRectF map_rect, ZoomOption zoom_option); // clazy:exclude=function-args-by-ref + + /** + * Mark a rectangular region of a template cache as "dirty", i.e. redraw needed. + * This rect is united with possible previous dirty rects of that cache. + * @param view_rect Affected rect in view coordinates. + * @param pixel_border Additional affected extent around the view rect in + * pixels. Allows to specify zoom-independent extents. + * @param front_cache If set to true, invalidates the cache for templates + * in front of the map, else invalidates the cache for templates behind the map. + */ + void markTemplateCacheDirty(const QRectF& view_rect, int pixel_border, bool front_cache); + + /** + * Mark a rectangular region given in map coordinates of the map cache + * as dirty, i.e. redraw needed. + * This rect is united with possible previous dirty rects of that cache. + */ + void markObjectAreaDirty(const QRectF& map_rect); + + /** + * Set the given rect as bounding box for the current drawing, i.e. the + * graphical display of the active tool. + * NOTE: Unlike for markTemplateCacheDirty(), multiple calls to + * these methodsdo not result in uniting all given rects, + * instead only the last rect is used! + * Pass QRect() to disable the current drawing. + * @param map_rect Affected rect in map coordinates. + * @param pixel_border Additional affected extent around the map rect in + * pixels. Allows to specify zoom-independent extents. + * @param do_update If set to true, triggers a redraw of the widget. + */ + void setDrawingBoundingBox(QRectF map_rect, int pixel_border, bool do_update); // clazy:exclude=function-args-by-ref + /** + * Removes the area set with setDrawingBoundingBox() and triggers a redraw + * of the widget, if needed. + */ + void clearDrawingBoundingBox(); + + /** Analogon to setDrawingBoundingBox() for activities. */ + void setActivityBoundingBox(QRectF map_rect, int pixel_border, bool do_update); // clazy:exclude=function-args-by-ref + /** Analogon to clearDrawingBoundingBox() for activities. */ + void clearActivityBoundingBox(); + + /** + * Triggers a redraw of the MapWidget at the given area. + * @param map_rect Affected rect in map coordinates. + * @param pixel_border Additional affected extent around the map rect in + * pixels. Allows to specify zoom-independent extents. + */ + void updateDrawing(const QRectF& map_rect, int pixel_border); + /** + * Triggers a redraw of the MapWidget at the given area. + */ + void updateMapRect(const QRectF& map_rect, int pixel_border, QRect& cache_dirty_rect); + /** + * Triggers a redraw of the MapWidget at the given area. + */ + void updateViewportRect(QRect viewport_rect, QRect& cache_dirty_rect); + /** + * Variant of updateDrawing() which waits for some milliseconds before + * calling update() in order to avoid excessive redraws. + */ + void updateDrawingLater(const QRectF& map_rect, int pixel_border); + + /** + * Invalidates all caches and redraws the whole widget. Very slow, try to + * avoid this. + */ + void updateEverything(); + /** + * Sets all "dirty" region markers to the given rect in viewport coordinates + * and triggers a redraw of the MapWidget there. + */ + void updateEverythingInRect(const QRect& dirty_rect); + + /** + * Sets the function which will be called to display zoom information. + */ + void setZoomDisplay(std::function setter); + + /** Specify the label where the MapWidget will display cursor position information. */ + void setCursorposLabel(QLabel* cursorpos_label); + /** + * Specify the system and format for displaying coordinates in + * the cursorpos label. See CoordsType for the available types. + */ + void setCoordsDisplay(CoordsType type); + /** Returns the coordinate display type set by setCoordsDisplay(). */ + inline CoordsType getCoordsDisplay() const; + + /** Returns the time in milliseconds since the last user interaction + * (mouse press or drag) with the widget. */ + int getTimeSinceLastInteraction(); + + /** Sets the GPS display to use. This is called internally by the GPSDisplay constructor. */ + void setGPSDisplay(GPSDisplay* gps_display); + /** Sets the GPS temporary markers display to use. This is called internally by the GPSTemporaryMarkers constructor. */ + void setTemporaryMarkerDisplay(GPSTemporaryMarkers* marker_display); + + /** Returns the widget's context menu widget. */ + QWidget* getContextMenu(); + + /** Returns the widget's preferred size. */ + QSize sizeHint() const override; + + /** + * @copybrief MainWindowController::keyPressEventFilter + * Delegates the keyPress to the active tool, or handles some shortcuts itself. + */ + bool keyPressEventFilter(QKeyEvent* event); + + /** + * @copybrief MainWindowController::keyPressEventFilter + * Delegates the keyRelease to the active tool. + */ + bool keyReleaseEventFilter(QKeyEvent* event); + + /** + * Support function for input methods. + */ + QVariant inputMethodQuery(Qt::InputMethodQuery property) const override; + +public slots: + /** + * Support function for input methods. + * + * This two-argument form is undocumented but attempted to call in + * QInputMethod::queryFocusObject before doing the query via an event. + */ + QVariant inputMethodQuery(Qt::InputMethodQuery property, const QVariant& argument) const; + + /** Enables or disables the touch cursor. */ + void enableTouchCursor(bool enabled); + +signals: + /** + * Support function for input methods. + */ + void cursorPositionChanged(); + +private slots: + void updateDrawingLaterSlot(); + +protected: + bool event(QEvent *event) override; + + virtual void gestureEvent(QGestureEvent* event); + + void paintEvent(QPaintEvent* event) override; + void resizeEvent(QResizeEvent* event) override; + + // Mouse input + void mousePressEvent(QMouseEvent* event) override; + void _mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event) override; + void _mouseMoveEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent* event) override; + void _mouseReleaseEvent(QMouseEvent* event); + void mouseDoubleClickEvent(QMouseEvent* event) override; + void _mouseDoubleClickEvent(QMouseEvent* event); + void wheelEvent(QWheelEvent* event) override; + void leaveEvent(QEvent* event) override; + + // Key input (see also slots) + void inputMethodEvent(QInputMethodEvent *event) override; + void focusOutEvent(QFocusEvent* event) override; + + void contextMenuEvent(QContextMenuEvent* event) override; + +private: + /** Checks if there is a visible template in the range + * from first_template to last_template. */ + bool containsVisibleTemplate(int first_template, int last_template) const; + /** Checks if there is any visible template above the map. */ + bool isAboveTemplateVisible() const; + /** Checks if there is any visible template below the map. */ + bool isBelowTemplateVisible() const; + /** + * Redraws the template cache. + * @param cache Reference to pointer to the cache. + * @param dirty_rect Rectangle of the cache to redraw, in viewport coordinates. + * @param first_template Lowest template index to draw. + * @param last_template Highest template index to draw. + * @param use_background If set to true, fills the cache with white before + * drawing the templates, else makes it transparent. + */ + void updateTemplateCache(QImage& cache, QRect& dirty_rect, int first_template, int last_template, bool use_background); + /** + * Redraws the map cache in the map cache dirty rect. + * @param use_background If set to true, fills the cache with white before + * drawing the map, else makes it transparent. + */ + void updateMapCache(bool use_background); + /** Redraws all dirty caches. */ + void updateAllDirtyCaches(); + /** Shifts the content in the cache by the given amount of pixels. */ + void shiftCache(int sx, int sy, QImage& cache); + void shiftCache(int sx, int sy, QPixmap& cache); + + /** + * Calculates the bounding box of the given map coordinates rect and + * additional pixel extent in integer viewport coordinates. + */ + QRect calculateViewportBoundingBox(const QRectF& map_rect, int pixel_border) const; + /** Internal method for setting a part of a cache as dirty. */ + void setDynamicBoundingBox(QRectF map_rect, int pixel_border, QRect& dirty_rect_old, QRectF& dirty_rect_new, int& dirty_rect_new_border, bool do_update); + /** Internal method for removing the dirty state of a cache. */ + void clearDynamicBoundingBox(QRect& dirty_rect_old, QRectF& dirty_rect_new, int& dirty_rect_new_border); + + /** Moves the dirty rect by the given amount of pixels. */ + void moveDirtyRect(QRect& dirty_rect, qreal x, qreal y); + + /** Starts a dragging interaction at the given cursor position. */ + void startDragging(QPoint cursor_pos); + /** Submits a new cursor position during a dragging interaction. */ + void updateDragging(QPoint cursor_pos); + /** Ends a dragging interaction at the given cursor position. */ + void finishDragging(QPoint cursor_pos); + /** Cancels a dragging interaction. */ + void cancelDragging(); + + /** Starts a pinching interaction at the given cursor position. + * Returns the initial zoom factor. */ + qreal startPinching(QPoint center); + /** Updates a pinching interaction at the given cursor position. */ + void updatePinching(QPoint center, qreal factor); + /** Ends a pinching interaction at the given cursor position. */ + void finishPinching(QPoint center, qreal factor); + /** Cancels a pinching interaction. */ + void cancelPinching(); + + /** Moves the map a given number of big "steps" in x and/or y direction. */ + void moveMap(int steps_x, int steps_y); + + /** Draws a help message at the center of the MapWidget. */ + void showHelpMessage(QPainter* painter, const QString& text) const; + + /** + * Updates the content of the zoom display. + * + * \see setZoomDisplay() + */ + void updateZoomDisplay(); + /** Updates the content of the cursorpos label, set by setCursorposLabel(). */ + void updateCursorposLabel(const MapCoordF pos); + + MapView* view; + MapEditorTool* tool; + MapEditorActivity* activity; + + CoordsType coords_type; + + std::function zoom_display; + QLabel* cursorpos_label; + QLabel* objecttag_label; + MapCoordF last_cursor_pos; + + bool show_help; + bool force_antialiasing; + + // Dragging (interaction) + bool dragging; + QPoint drag_start_pos; + /** Cursor used when not dragging */ + QCursor normal_cursor; + + // Pinching (interaction) + bool pinching; + qreal pinching_factor; + QPoint pinching_center; + + // Panning (operation) + QPoint pan_offset; + + // Template caches + /** Cache for templates below map layer */ + QImage below_template_cache; + QRect below_template_cache_dirty_rect; + + /** Cache for templates above map layer */ + QImage above_template_cache; + QRect above_template_cache_dirty_rect; + + /** Map layer cache */ + QImage map_cache; + QRect map_cache_dirty_rect; + + // Dirty regions for drawings (tools) and activities + /** Dirty rect for the current tool, in viewport coordinates (pixels). */ + QRect drawing_dirty_rect; + + /** Dirty rect for the current tool, in map coordinates. */ + QRectF drawing_dirty_rect_map; + + /** Additional pixel border for the tool dirty rect, in pixels. */ + int drawing_dirty_rect_border; + + /** Dirty rect for the current activity, in viewport coordinates (pixels). */ + QRect activity_dirty_rect; + + /** Dirty rect for the current activity, in map coordinates. */ + QRectF activity_dirty_rect_map; + + /** Additional pixel border for the activity dirty rect, in pixels. */ + int activity_dirty_rect_border; + + /** Cached updates */ + QRect cached_update_rect; + + /** Right-click menu */ + PieMenu* context_menu; + + /** Optional touch cursor for mobile devices */ + QScopedPointer touch_cursor; + + /** For checking for interaction with the widget: the last QTime where + * a mouse release event happened. Check for current_pressed_buttons == 0 + * and a last_mouse_release_time a given time interval in the past to check + * whether the user interacts or recently interacted with the widget. */ + QTime last_mouse_release_time; + int current_pressed_buttons; + + /** Optional GPS display */ + GPSDisplay* gps_display; + /** Optional temporary GPS marker display. */ + GPSTemporaryMarkers* marker_display; + + /** @brief Indicates whether gesture recognition is enabled. */ + bool gestures_enabled; +}; + + + +// ### MapWidget inline code ### + +inline +MapView* MapWidget::getMapView() const +{ + return view; +} + +inline +bool MapWidget::gesturesEnabled() const +{ + return gestures_enabled; +} + +inline +QPointF MapWidget::mapToViewport(QPointF input) const +{ + // This is a convenience method for situations when we have got a plain QPointF. + // We rely on MapCoordF adding nothing but functions to its base, QPointF. + static_assert(std::is_base_of::value, + "MapCoordF must be derived from QPointF"); + static_assert(!std::has_virtual_destructor::value, + "MapCoordF and its base must not have virtual members"); + static_assert(sizeof(QPointF) == sizeof(MapCoordF), + "MapCoordF must have the same size as QPointF"); + return mapToViewport(static_cast(input)); +} + +inline +QPoint MapWidget::panOffset() const +{ + return pan_offset; +} + +inline +MapWidget::CoordsType MapWidget::getCoordsDisplay() const +{ + return coords_type; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/map/new_map_dialog.cpp b/src/gui/map/new_map_dialog.cpp new file mode 100644 index 0000000..6a462b8 --- /dev/null +++ b/src/gui/map/new_map_dialog.cpp @@ -0,0 +1,317 @@ +/* + * Copyright 2011-2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "new_map_dialog.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fileformats/file_format.h" +#include "fileformats/file_format_registry.h" +#include "gui/file_dialog.h" +#include "gui/util_gui.h" +#include "util/util.h" + +// IWYU pragma: no_forward_declare QLabel +// IWYU pragma: no_forward_declare QVBoxLayout + + +namespace OpenOrienteering { + +NewMapDialog::NewMapDialog(QWidget* parent) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) +{ + this->setWhatsThis(Util::makeWhatThis("new_map.html")); + setWindowTitle(tr("Create new map")); + + auto form_layout = new QFormLayout(); + + form_layout->addRow(new QLabel(tr("Choose the scale and symbol set for the new map."))); + + form_layout->addItem(Util::SpacerItem::create(this)); + + scale_combo = new QComboBox(); + scale_combo->setEditable(true); + scale_combo->setValidator(new QIntValidator(1, 9999999, scale_combo)); + form_layout->addRow(tr("Scale: 1 : "), scale_combo); /// \todo Fix form layout dependency + + auto layout = new QVBoxLayout(); + layout->addLayout(form_layout); + + layout->addWidget(new QLabel(tr("Symbol sets:"))); + + symbol_set_list = new QListWidget(); + layout->addWidget(symbol_set_list, 1); + + symbol_set_matching = new QCheckBox(tr("Only show symbol sets matching the selected scale")); + layout->addWidget(symbol_set_matching); + + layout->addItem(Util::SpacerItem::create(this)); + + auto button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + create_button = button_box->button(QDialogButtonBox::Ok); + create_button->setIcon(QIcon(QString::fromLatin1(":/images/arrow-right.png"))); + create_button->setText(tr("Create")); + layout->addWidget(button_box); + + setLayout(layout); + + loadSymbolSetMap(); + for (auto& item : symbol_set_map) + { + if (item.first.toInt() != 0) + scale_combo->addItem(item.first); + } + + QSettings settings; + settings.beginGroup(QString::fromLatin1("NewMapDialog")); + const auto default_scale = settings.value(QString::fromLatin1("DefaultScale"), QVariant(10000)).toString(); + const auto matching = settings.value(QString::fromLatin1("OnlyMatchingSymbolSets"), QVariant(true)).toBool(); + settings.endGroup(); + + scale_combo->setEditText(default_scale); + symbol_set_matching->setChecked(matching); + connect(scale_combo, &QComboBox::editTextChanged, this, &NewMapDialog::updateSymbolSetList); + connect(symbol_set_list, &QListWidget::itemDoubleClicked, this, &NewMapDialog::symbolSetDoubleClicked); + connect(symbol_set_matching, &QCheckBox::stateChanged, this, &NewMapDialog::updateSymbolSetList); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(button_box, &QDialogButtonBox::accepted, this, &NewMapDialog::createClicked); + updateSymbolSetList(); +} + + +NewMapDialog::~NewMapDialog() = default; + + + +unsigned int NewMapDialog::getSelectedScale() const +{ + return scale_combo->currentText().toUInt(); +} + +QString NewMapDialog::getSelectedSymbolSetPath() const +{ + QListWidgetItem* item = symbol_set_list->currentItem(); + if (! item || ! item->data(Qt::UserRole).isValid()) + { + // FIXME: add proper error handling for release builds, or remove. + Q_ASSERT(false); + return QString{}; + } + + return item->data(Qt::UserRole).toString(); +} + +void NewMapDialog::accept() +{ + QSettings settings; + settings.beginGroup(QString::fromLatin1("NewMapDialog")); + settings.setValue(QString::fromLatin1("DefaultScale"), getSelectedScale()); + settings.setValue(QString::fromLatin1("OnlyMatchingSymbolSets"), symbol_set_matching->isChecked()); + settings.endGroup(); + + QDialog::accept(); +} + +void NewMapDialog::updateSymbolSetList() +{ + QString scale = scale_combo->currentText(); + if (scale.toInt() == 0) + { + create_button->setEnabled(false); + symbol_set_list->setEnabled(false); + return; + } + + create_button->setEnabled(true); + symbol_set_list->setEnabled(true); + symbol_set_list->clear(); + + auto item = new QListWidgetItem(tr("Empty symbol set")); + item->setData(Qt::UserRole, QVariant(QString{})); + item->setIcon(QIcon(QString::fromLatin1(":/images/new.png"))); + symbol_set_list->addItem(item); + + QIcon control(QString::fromLatin1(":/images/control.png")); + auto it = symbol_set_map.find(scale); + if (it != symbol_set_map.end()) + { + for (auto&& symbol_set : it->second) + { + item = new QListWidgetItem(symbol_set.completeBaseName()); + item->setData(Qt::UserRole, symbol_set.canonicalFilePath()); + item->setIcon(control); + symbol_set_list->addItem(item); + } + } + + if (! symbol_set_matching->isChecked()) + { + for (it = symbol_set_map.begin(); it != symbol_set_map.end(); ++it ) + { + if (it->first == scale) + continue; + + bool is_scale = (it->first.toInt() > 0); + QString remark = QLatin1String(" (") + QLatin1String(is_scale ? ("1 : ") : "") + it->first + QLatin1Char(')'); + + for (auto&& symbol_set : it->second) + { + item = new QListWidgetItem(symbol_set.completeBaseName() + remark); + item->setData(Qt::UserRole, symbol_set.canonicalFilePath()); + item->setIcon(control); + symbol_set_list->addItem(item); + } + } + } + + load_from_file = new QListWidgetItem(tr("Load symbol set from a file...")); + load_from_file->setData(Qt::UserRole, qVariantFromValue(nullptr)); + load_from_file->setIcon(QIcon(QString::fromLatin1(":/images/open.png"))); + symbol_set_list->addItem(load_from_file); + + // Select second row, which usually is the first (and only) symbol set + symbol_set_list->setCurrentRow(1); +} + +void NewMapDialog::symbolSetDoubleClicked(QListWidgetItem* item) +{ + symbol_set_list->setCurrentItem(item); + if (item == load_from_file) + showFileDialog(); + else + accept(); +} + +void NewMapDialog::createClicked() +{ + QListWidgetItem* item = symbol_set_list->currentItem(); + if (item == load_from_file) + showFileDialog(); + else + accept(); +} + +void NewMapDialog::showFileDialog() +{ + // Get the saved directory to start in, defaulting to the user's home directory. + QString open_directory = QSettings().value(QString::fromLatin1("openFileDirectory"), QDir::homePath()).toString(); + + // Build the list of supported file filters based on the file format registry + QString filters, extensions; + for (auto format : FileFormats.formats()) + { + if (format->supportsImport()) + { + if (filters.isEmpty()) + { + filters = format->filter(); + extensions = QLatin1String("*.") + format->fileExtensions().join(QString::fromLatin1(" *.")); + } + else + { + filters = filters + QLatin1String(";;") + format->filter(); + extensions = extensions + QLatin1String(" *.") + format->fileExtensions().join(QString::fromLatin1(" *.")); + } + } + } + filters = + tr("All symbol set files") + QLatin1String(" (") + extensions + QLatin1String(");;") + + filters + QLatin1String(";;") + + tr("All files") + QLatin1String(" (*.*)"); + + QString path = FileDialog::getOpenFileName(this, tr("Load symbol set from a file..."), open_directory, filters); + path = QFileInfo(path).canonicalFilePath(); + if (path.isEmpty()) + return; + + load_from_file->setData(Qt::UserRole, path); + accept(); +} + +void NewMapDialog::loadSymbolSetMap() +{ + loadSymbolSetDir(QDir(QDir::homePath() + QLatin1String("/my symbol sets"))); + + const auto locations = QDir::searchPaths(QLatin1String("data")); + for (const auto& symbol_set_dir : locations) + { + loadSymbolSetDir(QDir(symbol_set_dir + QLatin1String("/symbol sets"))); + } +} + +void NewMapDialog::loadSymbolSetDir(const QDir& symbol_set_dir) +{ + QStringList subdirs = symbol_set_dir.entryList(QDir::Dirs | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDir::NoSort); + for (auto&& dir_name : subdirs) + { + //int scale = dir_name.toInt(); + //if (scale == 0) + //{ + // qDebug() << dir_name + ": not a valid map scale denominator, using it as group name."; + //} + + QDir subdir(symbol_set_dir); + if (!subdir.cd(dir_name)) + { + qDebug("%s: cannot access this directory.", qPrintable(dir_name)); + continue; + } + + QStringList symbol_set_filters; + for (auto format : FileFormats.formats()) + { + if (format->supportsImport()) + symbol_set_filters << QStringList(format->fileExtensions()).replaceInStrings(QRegExp(QString(QLatin1Char{'^'})), QString::fromLatin1("*.")); + } + subdir.setNameFilters(symbol_set_filters); + + QFileInfoList symbol_set_files = subdir.entryInfoList(QDir::Files | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDir::Name); + auto item = symbol_set_map.emplace(dir_name, QFileInfoList{}).first; + item->second.append(symbol_set_files); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/map/new_map_dialog.h b/src/gui/map/new_map_dialog.h new file mode 100644 index 0000000..4df5d49 --- /dev/null +++ b/src/gui/map/new_map_dialog.h @@ -0,0 +1,138 @@ +/* + * Copyright 2011-2013 Thomas Schöps + * Copyright 2012, 2013, 2015, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_NEW_MAP_DIALOG_H +#define OPENORIENTEERING_NEW_MAP_DIALOG_H + +#include + +#include +#include +#include +#include + +class QCheckBox; +class QComboBox; +class QDir; +class QListWidget; +class QListWidgetItem; +class QPushButton; +class QWidget; + +namespace OpenOrienteering { + + +/** + * Dialog for creating a new map. + * Shows scale and symbol set selection. + */ +class NewMapDialog : public QDialog +{ +Q_OBJECT +public: + /** Constructs a dialog for scale and symbol set of a new map. */ + NewMapDialog(QWidget* parent = nullptr); + + ~NewMapDialog() override; + + /** Get the denominator of the chosen map scale. + * For a scale of 1:10000, this function returns 10000. + */ + unsigned int getSelectedScale() const; + + /** Get the full path of the selected symbol set. + * Returns an empty string if the map shall be created with an empty symbol set. + */ + QString getSelectedSymbolSetPath() const; + +public slots: + /** Updates the list of symbol sets for the chosen map scale [denominator]. */ + void updateSymbolSetList(); + + /** Accepts a selected symbol set, or triggers the file dialog. */ + void symbolSetDoubleClicked(QListWidgetItem* item); + + /** Accepts the selected symbol set, or triggers the file dialog. */ + void createClicked(); + + /** Hides the dialog and accepts the input. */ + void accept() override; + +protected: + struct SymbolSetKeyCompare + { + bool operator() (const QString& a, const QString& b) const + { + bool ok1, ok2; + int a_int = a.toInt(&ok1); + int b_int = b.toInt(&ok2); + if (ok1) + { + if (ok2) + return a_int < b_int; + else + return true; + } + else + { + if (ok2) + return false; + else + return a.compare(b); + } + } + }; + + /** A type for mapping map scales to lists of symbol sets. */ + typedef std::map SymbolSetMap; + + /** Loads all available symbol. */ + void loadSymbolSetMap(); + + /** Adds the symbol sets from a particular base directory. */ + void loadSymbolSetDir(const QDir& symbol_set_dir); + + /** Open a dialog for loading a symbol set from a file. */ + void showFileDialog(); + +private: + /** A mapping from map scales to lists of matching symbol set. */ + SymbolSetMap symbol_set_map; // scale to vector of symbol set names; TODO: store that globally / dir watcher + + /** The map scale input widget. */ + QComboBox* scale_combo; + + /** The symbol set selection widget. */ + QListWidget* symbol_set_list; + + /** The list item for loading a symbol set from a file. */ + QListWidgetItem* load_from_file; + + /** This check box controls whether only matching or all symbol sets are displayed. */ + QCheckBox* symbol_set_matching; + + /** The button for accepting the selected map scale and symbol set. */ + QPushButton* create_button; +}; + + +} // namespace OpenOrienteering +#endif diff --git a/src/gui/modifier_key.cpp b/src/gui/modifier_key.cpp new file mode 100644 index 0000000..58c0588 --- /dev/null +++ b/src/gui/modifier_key.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2013, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "modifier_key.h" + +#include +#include +#include + + +namespace OpenOrienteering { + +ModifierKey::ModifierKey(int key) + : native_text { QKeySequence((int)key).toString(QKeySequence::NativeText) } +{ + if (native_text.endsWith(QLatin1Char('+'))) + { + native_text.chop(1); + } +} + +ModifierKey::ModifierKey(Qt::KeyboardModifiers keys) + : ModifierKey { (int)keys } +{ + // nothing else +} + +ModifierKey::ModifierKey(Qt::Key key) + : ModifierKey { (int)key } +{ + // nothing else +} + +const ModifierKey& ModifierKey::alt() +{ + static const ModifierKey key(Qt::AltModifier); + return key; +} + +const ModifierKey& ModifierKey::control() +{ + static const ModifierKey key(Qt::ControlModifier); + return key; +} + +const ModifierKey& ModifierKey::controlShift() +{ + static const ModifierKey key(Qt::ControlModifier | Qt::ShiftModifier); + return key; +} + +const ModifierKey& ModifierKey::meta() +{ + static const ModifierKey key(Qt::MetaModifier); + return key; +} + +const ModifierKey& ModifierKey::shift() +{ + static const ModifierKey key(Qt::ShiftModifier); + return key; +} + +const ModifierKey& ModifierKey::space() +{ + static const ModifierKey key(Qt::Key_Space); + return key; +} + +const ModifierKey& ModifierKey::return_key() +{ + static const ModifierKey key(Qt::Key_Return); + return key; +} + +const ModifierKey& ModifierKey::backspace() +{ + static const ModifierKey key(Qt::Key_Backspace); + return key; +} + +const ModifierKey& ModifierKey::escape() +{ + static const ModifierKey key(Qt::Key_Escape); + return key; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/modifier_key.h b/src/gui/modifier_key.h new file mode 100644 index 0000000..08135bd --- /dev/null +++ b/src/gui/modifier_key.h @@ -0,0 +1,118 @@ +/* + * Copyright 2013, 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_MODIFIER_KEY_H +#define OPENORIENTEERING_MODIFIER_KEY_H + +#include +#include + +namespace OpenOrienteering { + + +/** + * A class that helps to deal efficiently with platform and localization issues + * of modifier keys. + * + * It is based on QKeySequence::toString(QKeySequence::NativeText) which provides + * localization and deals with swapping Ctrl and Cmd on Mac OS X. In contrast + * to QKeySequence, ModifierKey has an implicit operator for casting to QString, + * and it removes the trailing '+' from pseudo key sequences which consist of + * modifier keys only. Static methods provide efficient translations of the + * pure modifier keys. + * + * For true QKeySequences, call QKeySequence::toString(QKeySequence::NativeText) + * directly. + * + * On Windows and Linux, the keys will be displayed as word ("Shift" etc.). + * On OS X, the keys will be displayed as graphical symbols ("⇧", i.e. + * Unicode character U+21E7, etc.). + * + * Synopsis: + * + * QString text = tr("%1+Click to add a point.").arg(ModifierKey::control()); + * QString more = tr("%1+Click to select a point.").arg(ModifierKey(Qt::ALT + Qt::ShiftModifier)); + * + * // BUT: + * QString help = help_action.shortcut().toString(QKeySequence::NativeText); + */ +class ModifierKey +{ +protected: + /** Constructs a new ModifierKey for the given key. */ + explicit ModifierKey(int key); + +public: + /** Constructs a new ModifierKey for the given combination of KeyboardModifiers. */ + explicit ModifierKey(Qt::KeyboardModifiers keys); + + /** Constructs a new ModifierKey for the given key. */ + explicit ModifierKey(Qt::Key key); + + /** Returns a string representation for user interface purposes. + * + * This operator is intented to be used for implicit type casts. */ + operator QString() const; + + /** Returns a shared Alt modifier key. */ + static const ModifierKey& alt(); + + /** Returns a shared Control modifier key. */ + static const ModifierKey& control(); + + /** Returns a shared Control+Shift modifier key. */ + static const ModifierKey& controlShift(); + + /** Returns a shared Meta modifier key. */ + static const ModifierKey& meta(); + + /** Returns a shared Shift modifier key. */ + static const ModifierKey& shift(); + + /** Returns a shared Space key. */ + static const ModifierKey& space(); + + /** Returns a shared Return key. */ + static const ModifierKey& return_key(); + + /** Returns a shared Backspace key. */ + static const ModifierKey& backspace(); + + /** Returns a shared Escape modifier key. */ + static const ModifierKey& escape(); + +private: + /** The native text (localized, adapted to the system). */ + QString native_text; +}; + + + +// Inline implementation + +inline +ModifierKey::operator QString() const +{ + return native_text; +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/print_progress_dialog.cpp b/src/gui/print_progress_dialog.cpp new file mode 100644 index 0000000..cab6b11 --- /dev/null +++ b/src/gui/print_progress_dialog.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifdef QT_PRINTSUPPORT_LIB + +#include "print_progress_dialog.h" + +#include +#include +#include + +#include "core/map_printer.h" + + +namespace OpenOrienteering { + +PrintProgressDialog::PrintProgressDialog(MapPrinter* map_printer, QWidget* parent, Qt::WindowFlags f) + : QProgressDialog(parent, f) + , map_printer(map_printer) +{ + setWindowModality(Qt::ApplicationModal); // Required for OSX, cf. QTBUG-40112 + setRange(0, 100); + setMinimumDuration(0); + setValue(0); + + Q_ASSERT(map_printer); + connect(map_printer, &MapPrinter::printProgress, this, &PrintProgressDialog::setProgress); + connect(this, &PrintProgressDialog::canceled, map_printer, &MapPrinter::cancelPrintMap); +} + +PrintProgressDialog::~PrintProgressDialog() +{ + // nothing, not inlined +} + +void PrintProgressDialog::paintRequested(QPrinter* printer) +{ + if (!map_printer->printMap(printer)) + { + QMessageBox::warning( + parentWidget(), tr("Printing", "PrintWidget"), + tr("An error occurred during processing.", "PrintWidget"), + QMessageBox::Ok, QMessageBox::Ok ); + } +} + +void PrintProgressDialog::setProgress(int value, const QString& status) +{ + setLabelText(status); + setValue(value); + if (!isVisible() && value < maximum()) + { + show(); + } + + QApplication::processEvents(); // Drawing and Cancel events +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/print_progress_dialog.h b/src/gui/print_progress_dialog.h new file mode 100644 index 0000000..4980295 --- /dev/null +++ b/src/gui/print_progress_dialog.h @@ -0,0 +1,94 @@ +/* + * Copyright 2013-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifdef QT_PRINTSUPPORT_LIB + +#ifndef OPENORIENTEERING_PRINT_WIDGET_P_H +#define OPENORIENTEERING_PRINT_WIDGET_P_H + +#include +#include +#include +#include + +class QPrinter; +class QWidget; + +namespace OpenOrienteering { + +class MapPrinter; + + +/** + * PrintProgressDialog is a variation of QProgressDialog to be used with MapPrinter. + * + * PrintProgressDialog connects to the MapPrint::printMapProgress() signal. + * It provides a paintRequested slot which is to be connected to the + * corresponding QPrintPreviewDialog signal. + * + * This dialog is modal (for the application) by default. + */ +class PrintProgressDialog : public QProgressDialog +{ +Q_OBJECT +public: + /** + * Constructs a new dialog for the given MapPrinter. + * + * map_printer must not be nullptr. + */ + PrintProgressDialog(MapPrinter* map_printer, QWidget* parent = nullptr, Qt::WindowFlags f = 0); + + /** + * Destructor. + */ + ~PrintProgressDialog() override; + +public slots: + /** + * Listens to and forwards paint requests. + * + * Shows an error message if printing fails. + */ + void paintRequested(QPrinter* printer); + +protected slots: + /** + * Listens to printing progress messages. + * + * Shows the dialog if it was hidden, and processes events before returning. + * This makes it possible to react on the dialog's Cancel button, and to + * draw UI updates. + * + * @param value The progress, from 0 (not started) to 100 (finished). + * @param message The text to be shown as a label to the progress. + */ + void setProgress(int value, const QString& message); + +private: + MapPrinter* const map_printer; +}; + +#endif + + +} // namespace OpenOrienteering + +#endif // QT_PRINTSUPPORT_LIB diff --git a/src/gui/print_tool.cpp b/src/gui/print_tool.cpp new file mode 100644 index 0000000..60b6d64 --- /dev/null +++ b/src/gui/print_tool.cpp @@ -0,0 +1,382 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifdef QT_PRINTSUPPORT_LIB + +#include "print_tool.h" + +#include +#include + +#include "core/map.h" +#include "core/map_printer.h" +#include "gui/map/map_editor.h" +#include "gui/map/map_widget.h" + + +namespace OpenOrienteering { + +PrintTool::PrintTool(MapEditorController* editor, MapPrinter* map_printer) +: MapEditorTool { editor, Other, nullptr } +, map_printer { map_printer } +, region { Unknown } +, dragging { false } +{ + Q_ASSERT(editor); + Q_ASSERT(map_printer); + + connect(map_printer, &MapPrinter::printAreaChanged, this, &PrintTool::updatePrintArea); + connect(map_printer, &MapPrinter::pageFormatChanged, this, &PrintTool::updatePrintArea); + // Page breaks may change upon scale changes. + connect(map_printer, &MapPrinter::optionsChanged, this, &PrintTool::updatePrintArea); +} + +PrintTool::~PrintTool() +{ + // nothing, not inlined +} + +void PrintTool::init() +{ + setStatusBarText(tr("Drag: Move the map, the print area or the area's borders. ")); + updatePrintArea(); + + MapEditorTool::init(); +} + +const QCursor& PrintTool::getCursor() const +{ + static auto const cursor = QCursor{ Qt::ArrowCursor }; + return cursor; +} + +bool PrintTool::mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) +{ + if (event->button() == Qt::LeftButton) + { + mouseMoved(map_coord, widget); + dragging = true; + click_pos = event->pos(); + click_pos_map = map_coord; + if (region == Inside || region == Outside) + widget->setCursor(Qt::ClosedHandCursor); + return true; + } + + if (event->button() == Qt::RightButton) + { + return true; // disable context menu + } + + return false; +} + +bool PrintTool::mouseMoveEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) +{ + if (dragging && event->buttons() & Qt::LeftButton) + { + if (region == Outside) + { + mapWidget()->getMapView()->setPanOffset(event->pos() - click_pos); + } + else + { + updateDragging(map_coord); + } + return true; + } + + mouseMoved(map_coord, widget); + return false; +} + +bool PrintTool::mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) +{ + if (dragging && event->button() == Qt::LeftButton) + { + if (region == Outside) + { + mapWidget()->getMapView()->finishPanning(event->pos() - click_pos); + } + else + { + updateDragging(map_coord); + } + dragging = false; + region = Unknown; // forces mouseMoved() to update cursor and status + mouseMoved(map_coord, widget); + return true; + } + + return false; +} + +void PrintTool::draw(QPainter* painter, MapWidget* widget) +{ + painter->save(); + + QRect view_area = QRect(0, 0, widget->width(), widget->height()); + QRect print_area = widget->mapToViewport(map_printer->getPrintArea()).toRect(); + qreal scale_adjustment = map_printer->getScaleAdjustment(); + QSizeF page_size = widget->mapToViewport(map_printer->getPageFormat().page_rect).size() / scale_adjustment; + + // Strongly darken the region outside the print area + painter->setBrush(QColor(0, 0, 0, 160)); + painter->setPen(Qt::NoPen); + QPainterPath outside_path; + outside_path.addRect(view_area); + outside_path.addRect(view_area.intersected(print_area)); + painter->drawPath(outside_path); + + Q_ASSERT(!map_printer->horizontalPagePositions().empty()); + Q_ASSERT(!map_printer->verticalPagePositions().empty()); + QPointF outer_top_left = widget->mapToViewport( QPointF( + map_printer->horizontalPagePositions().front(), + map_printer->verticalPagePositions().front() ) ); + QPointF outer_bottom_right = widget->mapToViewport( QPointF( + map_printer->horizontalPagePositions().back(), + map_printer->verticalPagePositions().back() ) ); + outer_bottom_right += QPointF(page_size.width(), page_size.height()); + QRectF outer_rect(outer_top_left, outer_bottom_right); + + // Draw red lines for page breaks + QColor top_left_margin_color(255, 0, 0, 160); + QColor bottom_right_margin_color(255, 128, 128, 160); + painter->setBrush(Qt::NoBrush); + + // The relative length of the page dimensions to be actual drawn. + QSizeF drawing_size(page_size * scale_adjustment); + if (map_printer->horizontalPagePositions().size() > 1) + { + drawing_size.setWidth(drawing_size.width() * 0.9 * ( + map_printer->horizontalPagePositions()[1] - + map_printer->horizontalPagePositions()[0] ) / + map_printer->getPageFormat().page_rect.width() ); + } + if (map_printer->verticalPagePositions().size() > 1) + { + drawing_size.setHeight(drawing_size.height() * 0.9 * ( + map_printer->verticalPagePositions()[1] - + map_printer->verticalPagePositions()[0] ) / + map_printer->getPageFormat().page_rect.height() ); + } + + int h = 0; + for (auto hpos : map_printer->horizontalPagePositions()) + { + ++h; + if (h > 100) // Don't visualize too many pages. + break; + + int v = 0; + for (auto vpos : map_printer->verticalPagePositions()) + { + ++v; + if (h+v > 100) // Don't visualize too many pages. + break; + + QPointF pos = widget->mapToViewport(MapCoordF(hpos, vpos)); + painter->setPen(top_left_margin_color); + // Left vertical line + painter->drawLine(QLineF{pos.x(), pos.y(), pos.x(), pos.y()+drawing_size.height()}); + // Top horizontal line + painter->drawLine(QLineF{pos.x(), pos.y(), pos.x()+drawing_size.width(), pos.y()}); + + pos += QPointF(page_size.width(), page_size.height()); + painter->setPen(bottom_right_margin_color); + // Right vertical line + painter->drawLine(QLineF{pos.x(), pos.y()-drawing_size.height(), pos.x(), pos.y()}); + // Bottom horizontal line + painter->drawLine(QLineF{pos.x()-drawing_size.width(), pos.y(), pos.x(), pos.y()}); + } + } + + painter->setPen(top_left_margin_color); + painter->drawLine(QLineF{outer_rect.left(), outer_rect.top(), outer_rect.left(), outer_rect.bottom()}); + painter->drawLine(QLineF{outer_rect.left(), outer_rect.top(), outer_rect.right(), outer_rect.top()}); + painter->setPen(bottom_right_margin_color); + painter->drawLine(QLineF{outer_rect.right(), outer_rect.top(), outer_rect.right(), outer_rect.bottom()}); + painter->drawLine(QLineF{outer_rect.left(), outer_rect.bottom(), outer_rect.right(), outer_rect.bottom()}); + + QRectF print_area_f(print_area); + QPen marker(Qt::red); + marker.setWidth(4); + painter->setPen(marker); + painter->setOpacity(0.5); + if (region == Inside) + { + painter->drawRect(print_area_f); + } + if (region & LeftBorder) + { + painter->drawLine(print_area_f.topLeft(), print_area_f.bottomLeft()); + } + if (region & TopBorder) + { + painter->drawLine(print_area_f.topLeft(), print_area_f.topRight()); + } + if (region & RightBorder) + { + painter->drawLine(print_area_f.topRight(), print_area_f.bottomRight()); + } + if (region & BottomBorder) + { + painter->drawLine(print_area_f.bottomLeft(), print_area_f.bottomRight()); + } + + painter->restore(); +} + +void PrintTool::updatePrintArea() +{ + // The print area visualization is updated by redrawing the whole map. + // TODO: Replace with a more explicit way of marking the whole map area as dirty. + editor->getMap()->setDrawingBoundingBox(QRectF(-1000000, -1000000, 2000000, 2000000), 0); +} + +void PrintTool::updateDragging(MapCoordF mouse_pos_map) +{ + QPointF delta = QPointF(mouse_pos_map - click_pos_map); + QRectF area = map_printer->getPrintArea(); + switch (region) + { + case Inside: + area.moveTopLeft(area.topLeft() + delta); + break; + case LeftBorder: + area.setLeft(area.left() + delta.rx()); + break; + case TopLeftCorner: + area.setTopLeft(area.topLeft() + delta); + break; + case TopBorder: + area.setTop(area.top() + delta.ry()); + break; + case TopRightCorner: + area.setTopRight(area.topRight() + delta); + break; + case RightBorder: + area.setRight(area.right() + delta.rx()); + break; + case BottomRightCorner: + area.setBottomRight(area.bottomRight() + delta); + break; + case BottomBorder: + area.setBottom(area.bottom() + delta.ry()); + break; + case BottomLeftCorner: + area.setBottomLeft(area.bottomLeft() + delta); + break; + case Outside: + Q_ASSERT(false); // Handled outside. + case Unknown: + ; // Nothing + } + + if (area.left() < area.right() && area.top() < area.bottom()) + { + map_printer->setPrintArea(area); + click_pos_map = mouse_pos_map; + } +} + +void PrintTool::mouseMoved(MapCoordF mouse_pos_map, MapWidget* widget) +{ + Q_ASSERT(!dragging); // No change while dragging! + + static const qreal margin_width = 16.0; + static const qreal outer_margin = 8.0; + + QRectF print_area = widget->mapToViewport(map_printer->getPrintArea()); + print_area.adjust(-outer_margin, -outer_margin, outer_margin, outer_margin); + QPointF mouse_pos = widget->mapToViewport(mouse_pos_map); + + int new_region = Outside; + if (print_area.contains(mouse_pos)) + { + new_region = Inside; + + if (mouse_pos.rx() < print_area.left() + margin_width) + { + new_region |= LeftBorder; + } + else if (mouse_pos.rx() > print_area.right() - margin_width) + { + new_region |= RightBorder; + } + + if (mouse_pos.ry() < print_area.top() + margin_width) + { + new_region |= TopBorder; + } + else if (mouse_pos.ry() > print_area.bottom() - margin_width) + { + new_region |= BottomBorder; + } + } + + if (new_region != region) + { + region = InteractionRegion(new_region); + + switch (region) + { + case Inside: + setStatusBarText(tr("Drag: Move the print area. ")); + widget->setCursor(Qt::OpenHandCursor); + break; + case Outside: + setStatusBarText(tr("Drag: Move the map. ")); + widget->setCursor(Qt::ArrowCursor); + break; + case LeftBorder: + case RightBorder: + setStatusBarText(tr("Drag: Move the print area's border. ")); + widget->setCursor(Qt::SizeHorCursor); + break; + case TopBorder: + case BottomBorder: + setStatusBarText(tr("Drag: Move the print area's border. ")); + widget->setCursor(Qt::SizeVerCursor); + break; + case TopLeftCorner: + case BottomRightCorner: + setStatusBarText(tr("Drag: Move the print area's borders. ")); + widget->setCursor(Qt::SizeFDiagCursor); + break; + case TopRightCorner: + case BottomLeftCorner: + setStatusBarText(tr("Drag: Move the print area's borders. ")); + widget->setCursor(Qt::SizeBDiagCursor); + break; + case Unknown: + setStatusBarText(tr("Drag: Move the map, the print area or the area's borders. ")); + widget->setCursor(Qt::ArrowCursor); + } + + updatePrintArea(); + } +} + + +} // namespace OpenOrienteering + +#endif // QT_PRINTSUPPORT_LIB diff --git a/src/gui/print_tool.h b/src/gui/print_tool.h new file mode 100644 index 0000000..115c558 --- /dev/null +++ b/src/gui/print_tool.h @@ -0,0 +1,131 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_PRINT_TOOL_H +#define OPENORIENTEERING_PRINT_TOOL_H + +#ifdef QT_PRINTSUPPORT_LIB + +#include +#include + +#include "core/map_coord.h" +#include "tools/tool.h" + +class QCursor; +class QMouseEvent; +class QPainter; + +namespace OpenOrienteering { + +class MapEditorController; +class MapPrinter; +class MapWidget; + + +/** + * The PrintTool lets the user see and modify the print area on the map + * by dragging + * + * It interacts with a MapEditorController and a PrintWidget which are set in + * the constructor. + */ +class PrintTool : public MapEditorTool +{ +Q_OBJECT +public: + /** Constructs a new PrintTool to configure the given map printer in the + * context of the editor. + * + * The parameters must not be null. */ + PrintTool(MapEditorController* editor, MapPrinter* map_printer); + + ~PrintTool() override; + + /** Notifies the tool that it becomes active. */ + void init() override; + + /** Always returns the tool's default cursor. */ + const QCursor& getCursor() const override; + + /** Starts a dragging interaction. */ + bool mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; + + /** Updates the state of a running dragging interaction. When not dragging, + * it will update the cursor to indicate a possible interaction. */ + bool mouseMoveEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; + + /** Finishes dragging interactions. */ + bool mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) override; + + /** Draws a visualization of the print area the map widget. */ + void draw(QPainter* painter, MapWidget* widget) override; + +public slots: + /** Updates the print area visualization in the map editors. */ + void updatePrintArea(); + +protected: + /** Modifies the print area while dragging. + * This must not be called when the region is Outside. */ + void updateDragging(MapCoordF mouse_pos_map); + + /** Updates the current interaction region. + * This must not be called during dragging. */ + void mouseMoved(MapCoordF mouse_pos_map, MapWidget* widget); + + /** Regions of interaction with the print area. */ + enum InteractionRegion { + Inside = 0x00, + Outside = 0x01, + LeftBorder = 0x02, + TopLeftCorner = 0x06, + TopBorder = 0x04, + TopRightCorner = 0x0c, + RightBorder = 0x08, + BottomRightCorner = 0x18, + BottomBorder = 0x10, + BottomLeftCorner = 0x12, + Unknown = 0xFF + }; + + /** The map printer this tool is operation on. */ + MapPrinter* const map_printer; + + /** The region of the print area where the current interaction takes place. */ + InteractionRegion region; + + /** Indicates whether an interaction is taking place at the moment. */ + bool dragging; + + /** The screen position where the initial click was made. */ + QPoint click_pos; + + /** The map position where the initial click was made. */ + MapCoordF click_pos_map; +}; + +#endif // QT_PRINTSUPPORT_LIB + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_PRINT_TOOL_H diff --git a/src/gui/print_widget.cpp b/src/gui/print_widget.cpp new file mode 100644 index 0000000..91038c5 --- /dev/null +++ b/src/gui/print_widget.cpp @@ -0,0 +1,1412 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2018 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifdef QT_PRINTSUPPORT_LIB + +#include "print_widget.h" + +#include +#include + +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/map_coord.h" +#include "core/map_printer.h" +#include "core/map_view.h" +#include "gui/file_dialog.h" +#include "gui/main_window.h" +#include "gui/print_progress_dialog.h" +#include "gui/print_tool.h" +#include "gui/util_gui.h" +#include "gui/map/map_editor.h" +#include "gui/map/map_widget.h" +#include "templates/template.h" // IWYU pragma: keep +#include "templates/world_file.h" +#include "util/backports.h" +#include "util/scoped_signals_blocker.h" + + +namespace OpenOrienteering { + +namespace { + + QToolButton* createPrintModeButton(const QIcon& icon, const QString& label, QWidget* parent = nullptr) + { + static const QSize icon_size(48,48); + auto button = new QToolButton(parent); + button->setAutoRaise(true); + button->setCheckable(true); + button->setIconSize(icon_size); + button->setIcon(icon); + button->setText(label); + button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + return button; + } + + +} // namespace + + +//### PrintWidget ### + +PrintWidget::PrintWidget(Map* map, MainWindow* main_window, MapView* main_view, MapEditorController* editor, QWidget* parent) +: QWidget { parent } +, task { UNDEFINED_TASK } +, map { map } +, map_printer { new MapPrinter(*map, main_view) } +, main_window { main_window } +, main_view { main_view } +, editor { editor } +, print_tool { nullptr } +, active { false } +{ + Q_ASSERT(main_window); + + layout = new QFormLayout(); + + target_combo = new QComboBox(); + target_combo->setMinimumWidth(1); // Not zero, but not as long as the items + layout->addRow(Util::Headline::create(tr("Printer:")), target_combo); + + if (PlatformPrinterProperties::dialogSupported()) + { + printer_properties_button = new QToolButton(); + printer_properties_button->setText(tr("Properties")); + layout->addRow(nullptr, printer_properties_button); + } + else + { + printer_properties_button = nullptr; + } + + paper_size_combo = new QComboBox(); + layout->addRow(tr("Page format:"), paper_size_combo); + + auto page_size_widget = new QWidget(); + auto page_size_layout = new QHBoxLayout(); + page_size_widget->setLayout(page_size_layout); + page_size_layout->setMargin(0); + page_width_edit = Util::SpinBox::create(1, 0.1, 1000.0, tr("mm"), 1.0); + page_width_edit->setEnabled(false); + page_size_layout->addWidget(page_width_edit, 1); + page_size_layout->addWidget(new QLabel(QString::fromLatin1("x")), 0); + page_height_edit = Util::SpinBox::create(1, 0.1, 1000.0, tr("mm"), 1.0); + page_height_edit->setEnabled(false); + page_size_layout->addWidget(page_height_edit, 1); + layout->addRow({}, page_size_widget); + + page_orientation_widget = new QWidget(); + auto page_orientation_layout = new QHBoxLayout(); + page_orientation_layout->setContentsMargins(QMargins()); + page_orientation_widget->setLayout(page_orientation_layout); + auto portrait_button = new QRadioButton(tr("Portrait")); + page_orientation_layout->addWidget(portrait_button); + auto landscape_button = new QRadioButton(tr("Landscape")); + page_orientation_layout->addWidget(landscape_button); + page_orientation_group = new QButtonGroup(this); + page_orientation_group->addButton(portrait_button, QPrinter::Portrait); + page_orientation_group->addButton(landscape_button, QPrinter::Landscape); + layout->addRow(tr("Page orientation:"), page_orientation_widget); + + copies_edit = Util::SpinBox::create(1, 99999); + layout->addRow(tr("Copies:"), copies_edit); + + layout->addItem(Util::SpacerItem::create(this)); + + policy_combo = new QComboBox(); + policy_combo->addItem(tr("Single page"), SinglePage); + policy_combo->addItem(tr("Custom area"), CustomArea); + layout->addRow(Util::Headline::create(tr("Map area:")), policy_combo); // or print/export area + + center_check = new QCheckBox(tr("Center print area")); + layout->addRow(center_check); + + left_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); + layout->addRow(tr("Left:"), left_edit); + + top_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); + layout->addRow(tr("Top:"), top_edit); + + width_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); + layout->addRow(tr("Width:"), width_edit); + + height_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); + layout->addRow(tr("Height:"), height_edit); + + overlap_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm"), 1.0); + layout->addRow(tr("Page overlap:"), overlap_edit); + + layout->addItem(Util::SpacerItem::create(this)); + + layout->addRow(Util::Headline::create(tr("Options"))); + + auto mode_widget = new QWidget(); + auto mode_layout = new QHBoxLayout(); + mode_widget->setLayout(mode_layout); + mode_layout->setMargin(0); + + vector_mode_button = createPrintModeButton(QIcon(QString::fromLatin1(":/images/print-mode-vector.png")), tr("Vector\ngraphics")); + raster_mode_button = createPrintModeButton(QIcon(QString::fromLatin1(":/images/print-mode-raster.png")), tr("Raster\ngraphics")); + separations_mode_button = createPrintModeButton(QIcon(QString::fromLatin1(":/images/print-mode-separations.png")), tr("Color\nseparations")); + vector_mode_button->setChecked(true); + + auto mode_button_group = new QButtonGroup(this); + mode_button_group->addButton(vector_mode_button); + mode_button_group->addButton(raster_mode_button); + mode_button_group->addButton(separations_mode_button); + + mode_layout->addWidget(vector_mode_button); + mode_layout->addWidget(raster_mode_button); + mode_layout->addWidget(separations_mode_button); + mode_layout->addStretch(1); + + layout->addRow(tr("Mode:"), mode_widget); + + color_mode_combo = new QComboBox(); + color_mode_combo->setEditable(false); + color_mode_combo->addItem(tr("Default"), QVariant()); + color_mode_combo->addItem(tr("Device CMYK"), QVariant(true)); + layout->addRow(tr("Color mode:"), color_mode_combo); + + dpi_combo = new QComboBox(); + dpi_combo->setEditable(true); + dpi_combo->setValidator(new QRegExpValidator(QRegExp(QLatin1String("^[1-9]\\d{0,4}$|^[1-9]\\d{0,4} ")+tr("dpi")+QLatin1Char('$')), dpi_combo)); + // TODO: Implement spinbox-style " dpi" suffix + layout->addRow(tr("Resolution:"), dpi_combo); + + different_scale_check = new QCheckBox(tr("Print in different scale:")); + // Limit the difference between nominal and printing scale in order to limit the number of page breaks. + int min_scale = qMax(1, int(map->getScaleDenominator() / 10000) * 100); + different_scale_edit = Util::SpinBox::create(min_scale, std::numeric_limits::max(), {}, 500); + different_scale_edit->setPrefix(QString::fromLatin1("1 : ")); + different_scale_edit->setEnabled(false); + int different_scale_height = qMax( + different_scale_edit->minimumSizeHint().height(), + different_scale_check->minimumSizeHint().height() ); + different_scale_check->setMinimumHeight(different_scale_height); + different_scale_edit->setMinimumHeight(different_scale_height); + layout->addRow(different_scale_check, different_scale_edit); + + // this must be created before its value is used to determine the default setting of page_orientation_combo + show_templates_check = new QCheckBox(tr("Show templates")); + auto templates_warning_layout = new QHBoxLayout(); + QIcon warning_icon = style()->standardIcon(QStyle::SP_MessageBoxWarning); + templates_warning_icon = new QLabel(); + int pixmap_size = qBound(8, style()->pixelMetric(QStyle::PM_IndicatorHeight), 32); + templates_warning_icon->setPixmap(warning_icon.pixmap(QSize(pixmap_size, pixmap_size))); + templates_warning_layout->addWidget(templates_warning_icon); + templates_warning_text = new QLabel(tr("Template appearance may differ.")); + templates_warning_layout->addWidget(templates_warning_text, 1); + layout->addRow(show_templates_check, templates_warning_layout); + + show_grid_check = new QCheckBox(tr("Show grid")); + layout->addRow(show_grid_check); + + overprinting_check = new QCheckBox(tr("Simulate overprinting")); + layout->addRow(overprinting_check); + + world_file_check = new QCheckBox(tr("Save world file")); + layout->addRow(world_file_check); + world_file_check->hide(); + + scrolling_content = new QWidget(); + scrolling_content->setLayout(layout); + + auto outer_layout = new QVBoxLayout(); + outer_layout->setContentsMargins(QMargins()); + + scroll_area = new QScrollArea(); + scroll_area->setWidget(scrolling_content); + scroll_area->setWidgetResizable(true); + scroll_area->setMinimumWidth((scrolling_content->sizeHint() + scroll_area->verticalScrollBar()->sizeHint()).width()); + outer_layout->addWidget(scroll_area); + + button_box = new QDialogButtonBox(); + QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget); + button_box->layout()->setContentsMargins( + style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option), + style()->pixelMetric(QStyle::PM_LayoutTopMargin, &style_option), + style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option), + style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) + ); + preview_button = new QPushButton(tr("Preview...")); + button_box->addButton(preview_button, QDialogButtonBox::ActionRole); + print_button = new QPushButton(tr("Print")); + button_box->addButton(print_button, QDialogButtonBox::ActionRole); + // Use a distinct export button. + // Changing the text at runtime causes distortions on Mac OS X. + export_button = new QPushButton(tr("Export...")); + export_button->hide(); + button_box->addButton(export_button, QDialogButtonBox::ActionRole); + auto close_button = button_box->addButton(QDialogButtonBox::Close); + outer_layout->addWidget(button_box); + + setLayout(outer_layout); + + connect(target_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &PrintWidget::targetChanged); + if (printer_properties_button) + connect(printer_properties_button, &QAbstractButton::clicked, this, &PrintWidget::propertiesClicked, Qt::QueuedConnection); + connect(paper_size_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &PrintWidget::paperSizeChanged); + connect(page_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::paperDimensionsChanged); + connect(page_orientation_group, QOverload::of(&QButtonGroup::buttonClicked), this, &PrintWidget::pageOrientationChanged); + connect(page_height_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::paperDimensionsChanged); + + connect(top_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::printAreaMoved); + connect(left_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::printAreaMoved); + connect(width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::printAreaResized); + connect(height_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::printAreaResized); + connect(overlap_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PrintWidget::overlapEdited); + + connect(mode_button_group, QOverload::of(&QButtonGroup::buttonClicked), this, &PrintWidget::printModeChanged); + connect(dpi_combo->lineEdit(), &QLineEdit::textEdited, this, &PrintWidget::resolutionEdited); + connect(dpi_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &PrintWidget::resolutionEdited); + connect(different_scale_check, &QAbstractButton::clicked, this, &PrintWidget::differentScaleClicked); + connect(different_scale_edit, QOverload::of(&QSpinBox::valueChanged), this, &PrintWidget::differentScaleEdited); + connect(show_templates_check, &QAbstractButton::clicked, this, &PrintWidget::showTemplatesClicked); + connect(show_grid_check, &QAbstractButton::clicked, this, &PrintWidget::showGridClicked); + connect(overprinting_check, &QAbstractButton::clicked, this, &PrintWidget::overprintingClicked); + connect(color_mode_combo, &QComboBox::currentTextChanged, this, &PrintWidget::colorModeChanged); + + connect(preview_button, &QAbstractButton::clicked, this, &PrintWidget::previewClicked); + connect(print_button, &QAbstractButton::clicked, this, &PrintWidget::printClicked); + connect(export_button, &QAbstractButton::clicked, this, &PrintWidget::printClicked); + connect(close_button, &QAbstractButton::clicked, this, &PrintWidget::closeClicked); + + policy = map_printer->config().single_page_print_area ? SinglePage : CustomArea; + policy_combo->setCurrentIndex(policy_combo->findData(policy)); + connect(policy_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &PrintWidget::printAreaPolicyChanged); + + center_check->setChecked(map_printer->config().center_print_area); + connect(center_check, &QAbstractButton::clicked, this, &PrintWidget::applyCenterPolicy); + + setPageFormat(map_printer->getPageFormat()); + connect(map_printer, &MapPrinter::pageFormatChanged, this, &PrintWidget::setPageFormat); + + connect(map_printer, &MapPrinter::optionsChanged, this, &PrintWidget::setOptions); + spotColorPresenceChanged(map->hasSpotColors()); + connect(map, &Map::spotColorPresenceChanged, this, &PrintWidget::spotColorPresenceChanged); + + setPrintArea(map_printer->getPrintArea()); + connect(map_printer, &MapPrinter::printAreaChanged, this, &PrintWidget::setPrintArea); + + connect(map_printer, &MapPrinter::targetChanged, this, &PrintWidget::setTarget); + + connect(this, &PrintWidget::finished, this, &PrintWidget::savePrinterConfig); +} + +PrintWidget::~PrintWidget() +{ + delete map_printer; +} + +QSize PrintWidget::sizeHint() const +{ + QSize size = QWidget::sizeHint(); + size.setHeight(scrolling_content->sizeHint().height() + + 2 * scroll_area->frameWidth() + + button_box->sizeHint().height() + + layout->horizontalSpacing() ); + return size; +} + +// slot +void PrintWidget::setTask(PrintWidget::TaskFlags type) +{ + if (task != type) + { + task = type; + bool is_print_task = type==PRINT_TASK; + bool is_multipage = type.testFlag(MULTIPAGE_FLAG); + layout->labelForField(target_combo)->setVisible(is_print_task); + target_combo->setVisible(is_print_task); + layout->labelForField(copies_edit)->setVisible(is_multipage); + copies_edit->setVisible(is_multipage); + policy_combo->setVisible(is_multipage); + updateTargets(); + switch (type) + { + case PRINT_TASK: + // Reset values which are typically modified for exporting + if (policy == SinglePage && !map->printerConfig().single_page_print_area) + { + policy = CustomArea; + policy_combo->setCurrentIndex(policy_combo->findData(policy)); + } + if (map_printer->getPageFormat().paper_size == QPrinter::Custom) + { + map_printer->setPaperSize(map->printerConfig().page_format.paper_size); + } + // TODO: Set target to most recently used printer + emit taskChanged(tr("Print")); + break; + + case EXPORT_PDF_TASK: + map_printer->setTarget(MapPrinter::pdfTarget()); + if (active) + setOptions(map_printer->getOptions()); + emit taskChanged(tr("PDF export")); + break; + + case EXPORT_IMAGE_TASK: + map_printer->setTarget(MapPrinter::imageTarget()); + if (active) + setOptions(map_printer->getOptions()); + policy = SinglePage; + if (policy_combo->itemData(policy_combo->currentIndex()) != policy) + { + map_printer->setCustomPaperSize(map_printer->getPrintAreaPaperSize()); + policy_combo->setCurrentIndex(policy_combo->findData(policy)); + } + emit taskChanged(tr("Image export")); + break; + + default: + emit taskChanged(QString{}); + } + } +} + +// slot +void PrintWidget::savePrinterConfig() const +{ + MapPrinterConfig printer_config(map_printer->config()); + printer_config.center_print_area = center_check->isChecked(); + if (task.testFlag(MULTIPAGE_FLAG)) + { + printer_config.single_page_print_area = policy == SinglePage; + } + if (task.testFlag(EXPORT_IMAGE_TASK)) + { + // Don't override the printer page format from the custom image format. + printer_config.page_format = map->printerConfig().page_format; + } + map->setPrinterConfig(printer_config); +} + +// slot +void PrintWidget::setActive(bool active) +{ + if (this->active != active) + { + this->active = active; + + if (active) + { + // Save the current state of the map view. + saved_view_state.clear(); + QXmlStreamWriter writer(&saved_view_state); + main_view->save(writer, QLatin1String("saved_view"), false); + + editor->setViewOptionsEnabled(false); + + // Printers may have been added or removed. + updateTargets(); + + // Update the map view from the current options + setOptions(map_printer->getOptions()); + connect(main_view, &MapView::visibilityChanged, this, &PrintWidget::onVisibilityChanged); + + // Set reasonable zoom. + bool zoom_to_map = true; + if (zoom_to_map) + { + // Ensure the visibility of the whole map. + auto map_extent = map->calculateExtent(true, !main_view->areAllTemplatesHidden(), main_view); + editor->getMainWidget()->ensureVisibilityOfRect(map_extent, MapWidget::ContinuousZoom); + } + else + { + // Ensure the visibility of the print area. + auto print_area(map_printer->getPrintArea()); + editor->getMainWidget()->ensureVisibilityOfRect(print_area, MapWidget::ContinuousZoom); + } + + // Activate PrintTool. + if (!print_tool) + { + print_tool = new PrintTool(editor, map_printer); + } + editor->setOverrideTool(print_tool); + editor->setEditingInProgress(true); + } + else + { + disconnect(main_view, &MapView::visibilityChanged, this, &PrintWidget::onVisibilityChanged); + + editor->setEditingInProgress(false); + editor->setOverrideTool(nullptr); + print_tool = nullptr; + + // Restore view + QXmlStreamReader reader(saved_view_state); + reader.readNextStartElement(); + main_view->load(reader); + + editor->setViewOptionsEnabled(true); + } + } +} + + + +void PrintWidget::updateTargets() +{ + QVariant current_target = target_combo->itemData(target_combo->currentIndex()); + const auto saved_printer = map_printer->getTarget(); + const QString saved_printer_name = saved_printer ? saved_printer->printerName() : QString{}; + int saved_target_index = -1; + int default_printer_index = -1; + { + const QSignalBlocker block(target_combo); + target_combo->clear(); + + if (task == PRINT_TASK) + { + // Exporters + target_combo->addItem(tr("Save to PDF"), QVariant(int(PdfExporter))); + target_combo->insertSeparator(target_combo->count()); + target_combo->setCurrentIndex(0); + + // Printers + auto default_printer_name = QPrinterInfo::defaultPrinterName(); + printers = QPrinterInfo::availablePrinterNames(); + for (int i = 0; i < printers.size(); ++i) + { + const QString& name = printers[i]; + if (name == saved_printer_name) + saved_target_index = target_combo->count(); + if (name == default_printer_name) + default_printer_index = target_combo->count(); + target_combo->addItem(name, i); + } + } + + // Restore selected target if possible and exit on success + if (current_target.isValid()) + { + int index = target_combo->findData(current_target); + if (index >= 0) + { + target_combo->setCurrentIndex(index); + return; + } + } + + if (saved_target_index >= 0) + // Restore saved target if possible + target_combo->setCurrentIndex(saved_target_index); + else if (default_printer_index >= 0) + // Set default printer as current target + target_combo->setCurrentIndex(default_printer_index); + + // Explicitly invoke signal handler + targetChanged(target_combo->currentIndex()); + } +} + +// slot +void PrintWidget::setTarget(const QPrinterInfo* target) +{ + int target_index = printers.size()-1; + if (target == MapPrinter::pdfTarget()) + target_index = PdfExporter; + else if (target == MapPrinter::imageTarget()) + target_index = ImageExporter; + else + { + for (; target_index >= 0; target_index--) + { + if (target && printers[target_index] == target->printerName()) + break; + else if (!target) + break; + } + } + target_combo->setCurrentIndex(target_combo->findData(QVariant(target_index))); + + updatePaperSizes(target); + updateResolutions(target); + + bool supports_pages = (target != MapPrinter::imageTarget()); + bool supports_copies = (supports_pages && target && QPrinter(*target).supportsMultipleCopies()); + copies_edit->setEnabled(supports_copies); + layout->labelForField(copies_edit)->setEnabled(supports_copies); + + bool is_printer = map_printer->isPrinter(); + print_button->setVisible(is_printer); + print_button->setDefault(is_printer); + export_button->setVisible(!is_printer); + export_button->setDefault(!is_printer); + if (printer_properties_button) + printer_properties_button->setEnabled(is_printer); + + bool is_image_target = target == MapPrinter::imageTarget(); + vector_mode_button->setEnabled(!is_image_target); + separations_mode_button->setEnabled(!is_image_target && map->hasSpotColors()); + if (is_image_target) + { + raster_mode_button->setChecked(true); + printModeChanged(raster_mode_button); + } + + world_file_check->setVisible(is_image_target); + // If MapCoord (0,0) maps to projected (0,0), then there is probably + // no point in writing a world file. + world_file_check->setChecked(!map->getGeoreferencing().toProjectedCoords(MapCoordF{}).isNull()); + + updateColorMode(); +} + +// slot +void PrintWidget::targetChanged(int index) const +{ + if (index < 0) + return; + + int target_index = target_combo->itemData(index).toInt(); + Q_ASSERT(target_index >= -2); + Q_ASSERT(target_index < printers.size()); + + if (target_index == PdfExporter) + map_printer->setTarget(MapPrinter::pdfTarget()); + else if (target_index == ImageExporter) + map_printer->setTarget(MapPrinter::imageTarget()); + else + { + auto info = QPrinterInfo::printerInfo(printers[target_index]); + map_printer->setTarget(&info); + } +} + +// slot +void PrintWidget::propertiesClicked() +{ + if (map_printer && map_printer->isPrinter()) + { + std::shared_ptr buffer; // must not be destroyed before printer. + auto printer = map_printer->makePrinter(); + Q_ASSERT(printer->outputFormat() == QPrinter::NativeFormat); + if (PlatformPrinterProperties::execDialog(printer.get(), buffer, this) == QDialog::Accepted) + map_printer->takePrinterSettings(printer.get()); + } +} + +void PrintWidget::updatePaperSizes(const QPrinterInfo* target) const +{ + QString prev_paper_size_name = paper_size_combo->currentText(); + bool have_custom_size = false; + + { + const QSignalBlocker block(paper_size_combo); + + paper_size_combo->clear(); + QList size_list; + if (target) + size_list = target->supportedPaperSizes(); + if (size_list.isEmpty()) + size_list = defaultPaperSizes(); + + for (auto size : qAsConst(size_list)) + { + if (size == QPrinter::Custom) + have_custom_size = true; // add it once after all other entires + else + paper_size_combo->addItem(toString(size), size); + } + + if (have_custom_size) + paper_size_combo->addItem(toString(QPrinter::Custom), QPrinter::Custom); + + int paper_size_index = paper_size_combo->findData(map_printer->getPageFormat().paper_size); + if (!prev_paper_size_name.isEmpty()) + { + paper_size_index = paper_size_combo->findText(prev_paper_size_name); + } + paper_size_combo->setCurrentIndex(qMax(0, paper_size_index)); + paperSizeChanged(paper_size_combo->currentIndex()); + } +} + +// slot +void PrintWidget::setPageFormat(const MapPrinterPageFormat& format) +{ + ScopedMultiSignalsBlocker block( + paper_size_combo, page_orientation_group, + page_width_edit, page_height_edit, + overlap_edit + ); + paper_size_combo->setCurrentIndex(paper_size_combo->findData(format.paper_size)); + page_orientation_group->button(format.orientation)->setChecked(true); + page_width_edit->setValue(format.paper_dimensions.width()); + page_width_edit->setEnabled(format.paper_size == QPrinter::Custom); + page_height_edit->setValue(format.paper_dimensions.height()); + page_height_edit->setEnabled(format.paper_size == QPrinter::Custom); + // We only have a single overlap edit field, but MapPrinter supports + // distinct horizontal and vertical overlap. Choose the minimum. + overlap_edit->setValue(qMin(format.h_overlap, format.v_overlap)); + applyPrintAreaPolicy(); +} + +// slot +void PrintWidget::paperSizeChanged(int index) const +{ + if (index >= 0) + { + QPrinter::PaperSize paper_size = QPrinter::PaperSize(paper_size_combo->itemData(index).toInt()); + map_printer->setPaperSize(paper_size); + } +} + +// slot +void PrintWidget::paperDimensionsChanged() const +{ + const QSizeF dimensions(page_width_edit->value(), page_height_edit->value()); + map_printer->setCustomPaperSize(dimensions); +} + +// slot +void PrintWidget::pageOrientationChanged(int id) const +{ + if (id == QPrinter::Portrait || id == QPrinter::Landscape) + { + map_printer->setPageOrientation((id == QPrinter::Portrait) ? MapPrinterPageFormat::Portrait : MapPrinterPageFormat::Landscape); + } +} + + +// slot +void PrintWidget::printAreaPolicyChanged(int index) +{ + policy = PrintAreaPolicy(policy_combo->itemData(index).toInt()); + applyPrintAreaPolicy(); +} + + +// slot +void PrintWidget::applyPrintAreaPolicy() const +{ + if (policy == SinglePage) + { + setOverlapEditEnabled(false); + auto print_area = map_printer->getPrintArea(); + print_area.setSize(map_printer->getPageRectPrintAreaSize()); + if (center_check->isChecked()) + centerOnMap(print_area); + map_printer->setPrintArea(print_area); + } + else + { + setOverlapEditEnabled(true); + } +} + +// slot +void PrintWidget::applyCenterPolicy() const +{ + if (center_check->isChecked()) + { + auto print_area = map_printer->getPrintArea(); + centerOnMap(print_area); + map_printer->setPrintArea(print_area); + } +} + +void PrintWidget::centerOnMap(QRectF& area) const +{ + auto map_extent = map->calculateExtent(false, show_templates_check->isChecked(), main_view); + area.moveLeft(map_extent.center().x() - area.width() / 2); + area.moveTop(map_extent.center().y() - area.height() / 2); +} + + + +// slot +void PrintWidget::setPrintArea(const QRectF& area) +{ + ScopedMultiSignalsBlocker block( + left_edit, top_edit, + width_edit, height_edit + ); + + left_edit->setValue(area.left()); + top_edit->setValue(-area.top()); // Flip sign! + width_edit->setValue(area.width()); + height_edit->setValue(area.height()); + + if (center_check->isChecked()) + { + auto centered_area = map_printer->getPrintArea(); + centerOnMap(centered_area); + if ( qAbs(centered_area.left() - area.left()) > 0.005 || + qAbs(centered_area.top() - area.top()) > 0.005 ) + { + // No longer centered. + center_check->setChecked(false); + } + } + + if (policy == SinglePage) + { + if (map_printer->getPageFormat().paper_size == QPrinter::Custom || !task.testFlag(MULTIPAGE_FLAG)) + { + // Update custom paper size from print area size + QSizeF area_dimensions = area.size() * map_printer->getScaleAdjustment(); + if (map_printer->getPageFormat().page_rect.size() != area_dimensions) + { + // Don't force a custom paper size unless necessary + map_printer->setCustomPaperSize(area_dimensions); + } + } + else + { + QSizeF page_dimensions = map_printer->getPageRectPrintAreaSize(); + if ( qAbs(area.width() - page_dimensions.width()) > 0.005 || + qAbs(area.height() - page_dimensions.height()) > 0.005 ) + { + // No longer single page. + block << policy_combo; + policy = CustomArea; + policy_combo->setCurrentIndex(policy_combo->findData(policy)); + center_check->setChecked(false); + setOverlapEditEnabled(true); + } + } + } +} + +// slot +void PrintWidget::printAreaMoved() +{ + auto area = map_printer->getPrintArea(); + area.moveLeft(left_edit->value()); + area.moveTop(-top_edit->value()); // Flip sign! + map_printer->setPrintArea(area); +} + +// slot +void PrintWidget::printAreaResized() +{ + auto area = map_printer->getPrintArea(); + area.setWidth(width_edit->value()); + area.setHeight(height_edit->value()); + map_printer->setPrintArea(area); +} + +// slot +void PrintWidget::overlapEdited(double overlap) +{ + map_printer->setOverlap(overlap, overlap); +} + +void PrintWidget::setOverlapEditEnabled(bool state) const +{ + overlap_edit->setEnabled(state); + layout->labelForField(overlap_edit)->setEnabled(state); +} + + +// slot +void PrintWidget::setOptions(const MapPrinterOptions& options) +{ + using namespace Util::TristateCheckbox; + + ScopedMultiSignalsBlocker block( + dpi_combo->lineEdit(), + show_templates_check, + show_grid_check, + overprinting_check, + color_mode_combo, + vector_mode_button, + raster_mode_button, + separations_mode_button, + different_scale_check, + different_scale_edit + ); + + switch (options.mode) + { + default: + qWarning("Unhandled MapPrinterMode"); + // fall through in release build + case MapPrinterOptions::Vector: + vector_mode_button->setChecked(true); + setEnabledAndChecked(show_templates_check, options.show_templates); + setEnabledAndChecked(show_grid_check, options.show_grid); + setDisabledAndChecked(overprinting_check, options.simulate_overprinting); + main_view->setAllTemplatesHidden(!options.show_templates); + main_view->setGridVisible(options.show_grid); + main_view->setOverprintingSimulationEnabled(false); + break; + case MapPrinterOptions::Raster: + raster_mode_button->setChecked(true); + setEnabledAndChecked(show_templates_check, options.show_templates); + setEnabledAndChecked(show_grid_check, options.show_grid); + setEnabledAndChecked(overprinting_check, options.simulate_overprinting); + main_view->setAllTemplatesHidden(!options.show_templates); + main_view->setGridVisible(options.show_grid); + main_view->setOverprintingSimulationEnabled(options.simulate_overprinting); + break; + case MapPrinterOptions::Separations: + separations_mode_button->setChecked(true); + setDisabledAndChecked(show_templates_check, options.show_templates); + setDisabledAndChecked(show_grid_check, options.show_grid); + setDisabledAndChecked(overprinting_check, options.simulate_overprinting); + main_view->setAllTemplatesHidden(true); + main_view->setGridVisible(false); + main_view->setOverprintingSimulationEnabled(true); + break; + } + + switch (options.color_mode) + { + default: + qWarning("Unhandled ColorMode"); + // fall through in release build + case MapPrinterOptions::DefaultColorMode: + color_mode_combo->setCurrentIndex(0); + break; + case MapPrinterOptions::DeviceCmyk: + color_mode_combo->setCurrentIndex(1); + break; + } + + checkTemplateConfiguration(); + updateColorMode(); + + static QString dpi_template(QLatin1String("%1 ") + tr("dpi")); + dpi_combo->setEditText(dpi_template.arg(options.resolution)); + + if (options.scale != map->getScaleDenominator()) + { + different_scale_check->setChecked(true); + different_scale_edit->setEnabled(true); + } + + auto scale = int(options.scale); + different_scale_edit->setValue(scale); + differentScaleEdited(scale); + + if (options.mode != MapPrinterOptions::Raster + && map_printer->engineWillRasterize()) + { + QMessageBox::warning(this, tr("Error"), + tr("The map contains transparent elements" + " which require the raster mode.")); + map_printer->setMode(MapPrinterOptions::Raster); + } +} + +void PrintWidget::onVisibilityChanged() +{ + map_printer->setPrintTemplates(!main_view->areAllTemplatesHidden()); + map_printer->setPrintGrid(main_view->isGridVisible()); + map_printer->setSimulateOverprinting(main_view->isOverprintingSimulationEnabled()); +} + +void PrintWidget::updateResolutions(const QPrinterInfo* target) const +{ + static const QList default_resolutions(QList() << 150 << 300 << 600 << 1200); + + // Numeric resolution list + QList supported_resolutions; + if (target) + { + QPrinter pr(*target, QPrinter::HighResolution); + supported_resolutions = pr.supportedResolutions(); + if (supported_resolutions.size() == 1 && supported_resolutions[0] == 72) + { + // X11/CUPS + supported_resolutions.clear(); + } + } + if (supported_resolutions.isEmpty()) + supported_resolutions = default_resolutions; + + // Resolution list item with unit "dpi" + static QString dpi_template(QLatin1String("%1 ") + tr("dpi")); + QStringList resolutions; + resolutions.reserve(supported_resolutions.size()); + for (auto resolution : qAsConst(supported_resolutions)) + resolutions << dpi_template.arg(resolution); + + QString dpi_text = dpi_combo->currentText(); + { + const QSignalBlocker block(dpi_combo); + dpi_combo->clear(); + dpi_combo->addItems(resolutions); + } + dpi_combo->lineEdit()->setText(dpi_text.isEmpty() ? dpi_template.arg(600) : dpi_text); +} + +void PrintWidget::updateColorMode() +{ + bool enable = map_printer->getTarget() == MapPrinter::pdfTarget() + && !raster_mode_button->isChecked(); + color_mode_combo->setEnabled(enable); + layout->labelForField(color_mode_combo)->setEnabled(enable); + if (!enable) + color_mode_combo->setCurrentIndex(0); +} + +// slot +void PrintWidget::resolutionEdited() +{ + auto resolution_text = dpi_combo->currentText(); + auto index_of_space = resolution_text.indexOf(QLatin1Char(' ')); + auto dpi_value = resolution_text.leftRef(index_of_space).toInt(); + if (dpi_value > 0) + { + auto pos = dpi_combo->lineEdit()->cursorPosition(); + map_printer->setResolution(dpi_value); + dpi_combo->lineEdit()->setCursorPosition(pos); + } +} + +// slot +void PrintWidget::differentScaleClicked(bool checked) +{ + if (!checked) + different_scale_edit->setValue(int(map->getScaleDenominator())); + + different_scale_edit->setEnabled(checked); +} + +// slot +void PrintWidget::differentScaleEdited(int value) +{ + map_printer->setScale(static_cast(value)); + applyPrintAreaPolicy(); + + if (different_scale_edit->value() < 500) + { + different_scale_edit->setSingleStep(500 - different_scale_edit->value()); + } + else + { + different_scale_edit->setSingleStep(500); + } +} + +// slot +void PrintWidget::spotColorPresenceChanged(bool has_spot_colors) +{ + separations_mode_button->setEnabled(has_spot_colors); + if (!has_spot_colors && separations_mode_button->isChecked()) + { + map_printer->setMode(MapPrinterOptions::Vector); + } +} + +// slot +void PrintWidget::printModeChanged(QAbstractButton* button) +{ + if (button == vector_mode_button) + { + map_printer->setMode(MapPrinterOptions::Vector); + } + else if (button == raster_mode_button) + { + map_printer->setMode(MapPrinterOptions::Raster); + } + else + { + map_printer->setMode(MapPrinterOptions::Separations); + } +} + +// slot +void PrintWidget::showTemplatesClicked(bool checked) +{ + map_printer->setPrintTemplates(checked); + checkTemplateConfiguration(); +} + +void PrintWidget::checkTemplateConfiguration() +{ + bool visibility = map_printer->engineMayRasterize() && show_templates_check->isChecked(); + templates_warning_icon->setVisible(visibility); + templates_warning_text->setVisible(visibility); +} + +// slot +void PrintWidget::showGridClicked(bool checked) +{ + map_printer->setPrintGrid(checked); +} + +// slot +void PrintWidget::overprintingClicked(bool checked) +{ + map_printer->setSimulateOverprinting(checked); +} + +void PrintWidget::colorModeChanged() +{ + if (color_mode_combo->currentData().toBool()) + map_printer->setColorMode(MapPrinterOptions::DeviceCmyk); + else + map_printer->setColorMode(MapPrinterOptions::DefaultColorMode); +} + +// slot +void PrintWidget::previewClicked() +{ +#if defined(Q_OS_ANDROID) + // Qt for Android has no QPrintPreviewDialog + QMessageBox::warning(this, tr("Error"), tr("Not supported on Android.")); +#else + if (checkForEmptyMap()) + return; + + auto printer = map_printer->makePrinter(); + if (!printer) + { + QMessageBox::warning(this, tr("Error"), tr("Failed to prepare the preview.")); + return; + } + + printer->setCreator(main_window->appName()); + printer->setDocName(QFileInfo(main_window->currentPath()).baseName()); + + QPrintPreviewDialog preview(printer.get(), editor->getWindow()); + preview.setWindowModality(Qt::ApplicationModal); // Required for OSX, cf. QTBUG-40112 + + PrintProgressDialog progress(map_printer, editor->getWindow()); + progress.setWindowTitle(tr("Print Preview Progress")); + connect(&preview, &QPrintPreviewDialog::paintRequested, &progress, &PrintProgressDialog::paintRequested); + // Doesn't work as expected, on OSX at least. + //connect(&progress, &QProgressDialog::canceled, &preview, &QPrintPreviewDialog::reject); + + preview.exec(); +#endif +} + +// slot +void PrintWidget::printClicked() +{ + if (checkForEmptyMap()) + return; + + if (map->isAreaHatchingEnabled() || map->isBaselineViewEnabled()) + { + if (QMessageBox::question(this, tr("Warning"), + tr("A non-standard view mode is activated. " + "Are you sure to print / export the map like this?"), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) + return; + } + + if (map_printer->getTarget() == MapPrinter::imageTarget()) + exportToImage(); + else if (map_printer->getTarget() == MapPrinter::pdfTarget()) + exportToPdf(); + else + print(); +} + +void PrintWidget::exportToImage() +{ + static const QString filter_template(QString::fromLatin1("%1 (%2)")); + QStringList filters = { filter_template.arg(tr("PNG"), QString::fromLatin1("*.png")), + filter_template.arg(tr("BMP"), QString::fromLatin1("*.bmp")), + filter_template.arg(tr("TIFF"), QString::fromLatin1("*.tif *.tiff")), + filter_template.arg(tr("JPEG"), QString::fromLatin1("*.jpg *.jpeg")), + tr("All files (*.*)") }; + QString path = FileDialog::getSaveFileName(this, tr("Export map ..."), {}, filters.join(QString::fromLatin1(";;"))); + if (path.isEmpty()) + return; + + if (!path.endsWith(QLatin1String(".png"), Qt::CaseInsensitive) + && !path.endsWith(QLatin1String(".bmp"), Qt::CaseInsensitive) + && !path.endsWith(QLatin1String(".tif"), Qt::CaseInsensitive) && !path.endsWith(QLatin1String(".tiff"), Qt::CaseInsensitive) + && !path.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) && !path.endsWith(QLatin1String(".jpeg"), Qt::CaseInsensitive) ) + { + path.append(QString::fromLatin1(".png")); + } + + qreal pixel_per_mm = map_printer->getOptions().resolution / 25.4; + int print_width = qRound(map_printer->getPrintAreaPaperSize().width() * pixel_per_mm); + int print_height = qRound(map_printer->getPrintAreaPaperSize().height() * pixel_per_mm); + QImage image(print_width, print_height, QImage::Format_ARGB32_Premultiplied); + if (image.isNull()) + { + QMessageBox::warning(this, tr("Error"), tr("Failed to prepare the image. Not enough memory.")); + return; + } + + int dots_per_meter = qRound(pixel_per_mm * 1000); + image.setDotsPerMeterX(dots_per_meter); + image.setDotsPerMeterY(dots_per_meter); + + image.fill(QColor(Qt::white)); + +#if 0 // Pointless unless drawPage drives the event loop and sends progress + PrintProgressDialog progress(map_printer, main_window); + progress.setWindowTitle(tr("Export map ...")); +#endif + + // Export the map + QPainter p(&image); + map_printer->drawPage(&p, map_printer->getPrintArea(), &image); + p.end(); + if (!image.save(path)) + { + QMessageBox::warning(this, tr("Error"), tr("Failed to save the image. Does the path exist? Do you have sufficient rights?")); + } + else + { + main_window->showStatusBarMessage(tr("Exported successfully to %1").arg(path), 4000); + if (world_file_check->isChecked()) + exportWorldFile(path); /// \todo Handle errors + emit finished(0); + } + return; +} + +void PrintWidget::exportWorldFile(const QString& path) const +{ + const auto& georef = map->getGeoreferencing(); + const auto& ref_transform = georef.mapToProjected(); + + qreal pixel_per_mm = (map_printer->getOptions().resolution / 25.4) * map_printer->getScaleAdjustment(); + const auto center_of_pixel = QPointF{0.5/pixel_per_mm, 0.5/pixel_per_mm}; + const auto top_left = georef.toProjectedCoords(MapCoord{map_printer->getPrintArea().topLeft() + center_of_pixel}); + + + const auto xscale = ref_transform.m11() / pixel_per_mm; + const auto yscale = ref_transform.m22() / pixel_per_mm; + const auto xskew = ref_transform.m12() / pixel_per_mm; + const auto yskew = ref_transform.m21() / pixel_per_mm; + + QTransform final_wld(xscale, yskew, 0, xskew, yscale, 0, top_left.x(), top_left.y()); + WorldFile wld(final_wld); + wld.save(WorldFile::pathForImage(path)); +} + +void PrintWidget::exportToPdf() +{ + auto printer = map_printer->makePrinter(); + if (!printer) + { + QMessageBox::warning(this, tr("Error"), tr("Failed to prepare the PDF export.")); + return; + } + + printer->setOutputFormat(QPrinter::PdfFormat); + printer->setNumCopies(copies_edit->value()); + printer->setCreator(main_window->appName()); + printer->setDocName(QFileInfo(main_window->currentPath()).baseName()); + + static const QString filter_template(QString::fromLatin1("%1 (%2)")); + QStringList filters = { filter_template.arg(tr("PDF"), QString::fromLatin1("*.pdf")), + tr("All files (*.*)") }; + QString path = FileDialog::getSaveFileName(this, tr("Export map ..."), {}, filters.join(QString::fromLatin1(";;"))); + if (path.isEmpty()) + { + return; + } + else if (!path.endsWith(QLatin1String(".pdf"), Qt::CaseInsensitive)) + { + path.append(QLatin1String(".pdf")); + } + printer->setOutputFileName(path); + + PrintProgressDialog progress(map_printer, main_window); + progress.setWindowTitle(tr("Export map ...")); + + // Export the map + if (!map_printer->printMap(printer.get())) + { + QFile(path).remove(); + QMessageBox::warning(this, tr("Error"), tr("Failed to finish the PDF export.")); + } + else if (!progress.wasCanceled()) + { + main_window->showStatusBarMessage(tr("Exported successfully to %1").arg(path), 4000); + emit finished(0); + } + else + { + QFile(path).remove(); + main_window->showStatusBarMessage(tr("Canceled."), 4000); + } +} + +void PrintWidget::print() +{ + auto printer = map_printer->makePrinter(); + if (!printer) + { + QMessageBox::warning(this, tr("Error"), tr("Failed to prepare the printing.")); + return; + } + + printer->setNumCopies(copies_edit->value()); + printer->setCreator(main_window->appName()); + printer->setDocName(QFileInfo(main_window->currentPath()).baseName()); + + PrintProgressDialog progress(map_printer, main_window); + progress.setWindowTitle(tr("Printing Progress")); + + // Print the map + if (!map_printer->printMap(printer.get())) + { + QMessageBox::warning(main_window, tr("Error"), tr("An error occurred during printing.")); + } + else if (!progress.wasCanceled()) + { + main_window->showStatusBarMessage(tr("Successfully created print job"), 4000); + emit finished(0); + } + else if (printer->abort()) + { + main_window->showStatusBarMessage(tr("Canceled."), 4000); + } + else + { + QMessageBox::warning(main_window, tr("Error"), tr("The print job could not be stopped.")); + } +} + +QList PrintWidget::defaultPaperSizes() const +{ + // TODO: Learn from user's past choices, present reduced list unless asked for more. + static QList default_paper_sizes(QList() + << QPrinter::A4 + << QPrinter::Letter + << QPrinter::Legal + << QPrinter::Executive + << QPrinter::A0 + << QPrinter::A1 + << QPrinter::A2 + << QPrinter::A3 + << QPrinter::A5 + << QPrinter::A6 + << QPrinter::A7 + << QPrinter::A8 + << QPrinter::A9 + << QPrinter::B0 + << QPrinter::B1 + << QPrinter::B10 + << QPrinter::B2 + << QPrinter::B3 + << QPrinter::B4 + << QPrinter::B5 + << QPrinter::B6 + << QPrinter::B7 + << QPrinter::B8 + << QPrinter::B9 + << QPrinter::C5E + << QPrinter::Comm10E + << QPrinter::DLE + << QPrinter::Folio + << QPrinter::Ledger + << QPrinter::Tabloid + << QPrinter::Custom + ); + return default_paper_sizes; +} + +QString PrintWidget::toString(QPrinter::PaperSize size) +{ +#if defined(Q_OS_ANDROID) + // Qt for Android has no QPrintDialog + Q_UNUSED(size); + return tr("Unknown", "Paper size"); +#else + const QHash< int, const char*>& paper_size_names = MapPrinter::paperSizeNames(); + if (paper_size_names.contains(size)) + // These translations are not used in QPrintDialog, + // but in Qt's qpagesetupdialog_unix.cpp. + return QPrintDialog::tr(paper_size_names[size]); + else + return tr("Unknown", "Paper size"); +#endif +} + +bool PrintWidget::checkForEmptyMap() +{ + if (map_printer->isOutputEmpty()) + { + QMessageBox::warning(this, tr("Error"), tr("The map area is empty. Output canceled.")); + return true; + } + return false; +} + + +} // namespace OpenOrienteering + +#endif // QT_PRINTSUPPORT_LIB diff --git a/src/gui/print_widget.h b/src/gui/print_widget.h new file mode 100644 index 0000000..4183042 --- /dev/null +++ b/src/gui/print_widget.h @@ -0,0 +1,345 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifdef QT_PRINTSUPPORT_LIB + +#ifndef OPENORIENTEERING_PRINT_WIDGET_H +#define OPENORIENTEERING_PRINT_WIDGET_H + +#include +#include +#include +#include +// IWYU pragma: no_include +#include +#include +#include +#include + + +class QAbstractButton; +class QButtonGroup; +class QCheckBox; +class QComboBox; +class QDialogButtonBox; +class QDoubleSpinBox; +class QFormLayout; +class QLabel; +class QPushButton; +class QPrinterInfo; +class QRectF; +class QScrollArea; +class QSpinBox; +class QToolButton; + +namespace OpenOrienteering { + +class MainWindow; +class Map; +class MapEditorController; +class MapPrinter; +class MapPrinterOptions; +class MapPrinterPageFormat; +class MapView; +class PrintTool; + + +/** + * The print widget lets the user adjust targets and parameters + * for printing and export. + */ +class PrintWidget : public QWidget +{ +Q_OBJECT +public: + enum TaskFlag + { + EXPORT_FLAG = 0x02, + MULTIPAGE_FLAG = 0x04, + + UNDEFINED_TASK = 0x00, + PRINT_TASK = 0x14, // 0x10 | 0x04 + EXPORT_PDF_TASK = 0x26, // 0x20 | 0x04 | 0x02 + EXPORT_IMAGE_TASK = 0x42, // 0x40 | 0x02 + + END_JOB_TYPE + }; + Q_DECLARE_FLAGS(TaskFlags, TaskFlag) + + /** Constructs a new print widget. */ + PrintWidget(Map* map, MainWindow* main_window, MapView* main_view, MapEditorController* editor, QWidget* parent = nullptr); + + /** Destroys the widget. */ + ~PrintWidget() override; + + /** Indicates the default widget size. */ + QSize sizeHint() const override; + + /** Returns a translated name for the given paper size. */ + static QString toString(QPrinter::PaperSize size); + +public slots: + /** Changes the type of the print or export task. */ + void setTask(TaskFlags type); + + /** Saves the print or export settings. */ + void savePrinterConfig() const; + + /** + * Sets the active state of the print widget. + * + * When the widget becomes active, it saves the map view state and + * activates a tool on the map editor which allows to move the print area. + * When the widget becomes inactive, the tool is removed, and the map view + * state is restored. + */ + void setActive(bool active); + + /** Sets the widget's (print/export) target. */ + void setTarget(const QPrinterInfo* target); + + /** Sets the format of a single page. */ + void setPageFormat(const MapPrinterPageFormat& format); + + /** Sets the exported area. */ + void setPrintArea(const QRectF& area); + + /** Sets output options: resolution, overprinting. */ + void setOptions(const MapPrinterOptions& options); + + /** Listens to view feature changes. */ + void onVisibilityChanged(); + +signals: + /** + * This signal is emitted when the type of task changes. + * It may be used to set a window title. + */ + void taskChanged(const QString& name); + + /** + * This signal is emitted when a print or export job has been started + * and finished. + * + * The signal is not emitted when the widget is hidden + * (cf. QDialog::finished(int result) ). + */ + void finished(int result); + + /** + * This signal is emitted when the dialog's close button is clicked. + */ + void closeClicked(); + +protected slots: + /** This slot reacts to changes of the target combobox. */ + void targetChanged(int index) const; + + /** Opens a dialog to change printer properties. */ + void propertiesClicked(); + + /** This slot reacts to changes of the paper size combobox. */ + void paperSizeChanged(int index) const; + + /** This slot reacts to changes of the paper dimension widgets. */ + void paperDimensionsChanged() const; + + /** This slot reacts to changes of the page orientation widget. */ + void pageOrientationChanged(int id) const; + + /** This slot reacts to changes of the print area policy combobox. */ + void printAreaPolicyChanged(int index); + + /** This slot applies the map area policy to the current area. */ + void applyPrintAreaPolicy() const; + + /** This slot applies the center map area policy to the current area. */ + void applyCenterPolicy() const; + + /** This slot reacts to changes of print area position widgets. */ + void printAreaMoved(); + + /** This slot reacts to changes of print area size widget. */ + void printAreaResized(); + + /** This slot reacts to changes to the page overlap widget. */ + void overlapEdited(double overlap); + + /** This slot is called when the resolution widget signals that editing finished. */ + void resolutionEdited(); + + /** This slot enables the alternative scale widget, or resets it. */ + void differentScaleClicked(bool checked); + + /** This slot reacts to changes in the alternative scale widget. */ + void differentScaleEdited(int value); + + /** This slot reacts to changes in the presence of spot colors in the map. + * The following features are enabled only when spot colors are present: + * - spot color separations + * - overprinting simulation + */ + void spotColorPresenceChanged(bool has_spot_colors); + + /** This slot reacts to changes in the print mode buttons. */ + void printModeChanged(QAbstractButton* button); + + /** This slot reacts to changes of the "Show template" option. */ + void showTemplatesClicked(bool checked); + + /** This slot reacts to changes of the "Show grid" option. */ + void showGridClicked(bool checked); + + /** This slot reacts to changes of the "Simulate overprinting" option. */ + void overprintingClicked(bool checked); + + /** This slot reacts to changes of the "Color mode" option. */ + void colorModeChanged(); + + /** Opens a preview window. */ + void previewClicked(); + + /** Starts printing and terminates this dialog. */ + void printClicked(); + +protected: + /** Alternative policies of handling the print area. */ + enum PrintAreaPolicy + { + SinglePage = 2, + CustomArea = 4 + }; + + /** Re-initializes the list of print/export targets. */ + void updateTargets(); + + /** Updates the list of paper sizes from the given target. */ + void updatePaperSizes(const QPrinterInfo* target) const; + + /** Updates the list of resolutions from the given target. */ + void updateResolutions(const QPrinterInfo* target) const; + + /** Updates the color mode combobox from target and mode settings. */ + void updateColorMode(); + + /** A list of paper sizes which is used when the target does not specify + * supported paper sizes. */ + QList defaultPaperSizes() const; + + /** Moves the given rectangle to a position where it is centered on the + * map for the current output options. */ + void centerOnMap(QRectF& area) const; + + /** Shows a warning and returns true if the output would be empty. */ + bool checkForEmptyMap(); + + /** Sets the enabled state of the page overlap field. */ + void setOverlapEditEnabled(bool state = true) const; + + /** Checks whether the template order warning needs to be displayed. */ + void checkTemplateConfiguration(); + + /** Exports to an image file. */ + void exportToImage(); + + /** Export a world file */ + void exportWorldFile(const QString& path) const; + + /** Exports to a PDF file. */ + void exportToPdf(); + + /** Print to a printer. */ + void print(); + +private: + enum Exporters + { + PdfExporter = -1, + ImageExporter = -2 + }; + TaskFlags task; + + QFormLayout* layout; + QWidget* scrolling_content; + QScrollArea* scroll_area; + QDialogButtonBox* button_box; + + QComboBox* target_combo; + QToolButton* printer_properties_button; + QComboBox* paper_size_combo; + QWidget* page_orientation_widget; + QButtonGroup* page_orientation_group; + QSpinBox* copies_edit; + QDoubleSpinBox* page_width_edit; + QDoubleSpinBox* page_height_edit; + + QComboBox* dpi_combo; + QCheckBox* show_templates_check; + QLabel* templates_warning_icon; + QLabel* templates_warning_text; + QCheckBox* show_grid_check; + QCheckBox* overprinting_check; + QCheckBox* world_file_check; + QCheckBox* different_scale_check; + QSpinBox* different_scale_edit; + QComboBox* color_mode_combo; + + QComboBox* policy_combo; + QCheckBox* center_check; + QDoubleSpinBox* left_edit; + QDoubleSpinBox* top_edit; + QDoubleSpinBox* width_edit; + QDoubleSpinBox* height_edit; + QDoubleSpinBox* overlap_edit; + + QToolButton* vector_mode_button; + QToolButton* raster_mode_button; + QToolButton* separations_mode_button; + + QPushButton* preview_button; + QPushButton* print_button; + QPushButton* export_button; + + QStringList printers; + + PrintAreaPolicy policy; + + Map* map; + MapPrinter* map_printer; + MainWindow* main_window; + MapView* main_view; + MapEditorController* editor; + PrintTool* print_tool; + QString saved_view_state; + bool active; +}; + + +#endif + + +} // namespace OpenOrienteering + + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::PrintWidget::TaskFlags) + + +#endif diff --git a/src/gui/select_crs_dialog.cpp b/src/gui/select_crs_dialog.cpp new file mode 100644 index 0000000..99566df --- /dev/null +++ b/src/gui/select_crs_dialog.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "select_crs_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/georeferencing.h" +#include "gui/util_gui.h" +#include "gui/widgets/crs_selector.h" + + +namespace OpenOrienteering { + +namespace { + +enum SpecialCRS { + SameAsMap = 1, + Local = 2, + Geographic = 3 +}; + + +} // namespace + + + +SelectCRSDialog::SelectCRSDialog( + const Georeferencing& georef, + QWidget* parent, + GeorefAlternatives alternatives, + const QString& description ) + : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) + , georef(georef) +{ + setWindowModality(Qt::WindowModal); + setWindowTitle(tr("Select coordinate reference system")); + + crs_selector = new CRSSelector(georef, nullptr); + if (georef.isLocal()) + crs_selector->clear(); + + if (alternatives.testFlag(TakeFromMap) && !georef.isLocal()) + { + crs_selector->addCustomItem(tr("Same as map"), SpecialCRS::SameAsMap); + crs_selector->setCurrentIndex(0); // TakeFromMap + } + + if (alternatives.testFlag(Local) || georef.isLocal()) + { + crs_selector->addCustomItem(tr("Local"), SpecialCRS::Local); + crs_selector->setCurrentIndex(0); // TakeFromMap or Local, both is fine. + } + + if (alternatives.testFlag(Geographic) && !georef.isLocal()) + crs_selector->addCustomItem(tr("Geographic coordinates (WGS84)"), SpecialCRS::Geographic); + + status_label = new QLabel(); + button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + + auto form_layout = new QFormLayout(); + if (!description.isEmpty()) + { + form_layout->addRow(new QLabel(description)); + form_layout->addItem(Util::SpacerItem::create(this)); + } + form_layout->addRow(QCoreApplication::translate("OpenOrienteering::GeoreferencingDialog", "&Coordinate reference system:"), crs_selector); + form_layout->addRow(tr("Status:"), status_label); + form_layout->addItem(Util::SpacerItem::create(this)); + crs_selector->setDialogLayout(form_layout); + + auto layout = new QVBoxLayout(); + layout->addLayout(form_layout, 1); + layout->addWidget(button_box, 0); + setLayout(layout); + + connect(crs_selector, &CRSSelector::crsChanged, this, &SelectCRSDialog::updateWidgets); + connect(button_box, &QDialogButtonBox::accepted, this, &SelectCRSDialog::accept); + connect(button_box, &QDialogButtonBox::rejected, this, &SelectCRSDialog::reject); + + updateWidgets(); +} + +QString SelectCRSDialog::currentCRSSpec() const +{ + QString spec; + switch (crs_selector->currentCustomItem()) + { + case SpecialCRS::SameAsMap: + spec = georef.getProjectedCRSSpec(); + break; + case SpecialCRS::Local: + // nothing + break; + case SpecialCRS::Geographic: + spec = Georeferencing::geographic_crs_spec; + break; + default: + spec = crs_selector->currentCRSSpec(); + } + + return spec; +} + +void SelectCRSDialog::updateWidgets() +{ + Georeferencing georef; + auto spec = currentCRSSpec(); + auto valid = spec.isEmpty() || georef.setProjectedCRS({}, spec); + + button_box->button(QDialogButtonBox::Ok)->setEnabled(valid); + if (valid) + status_label->setText(tr("valid")); + else + status_label->setText(QLatin1String("") + georef.getErrorText() + QLatin1String("")); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/select_crs_dialog.h b/src/gui/select_crs_dialog.h new file mode 100644 index 0000000..8a7a573 --- /dev/null +++ b/src/gui/select_crs_dialog.h @@ -0,0 +1,99 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SELECT_CRS_DIALOG_H +#define OPENORIENTEERING_SELECT_CRS_DIALOG_H + +#include +#include +#include +#include + +class QDialogButtonBox; +class QLabel; +class QWidget; + +namespace OpenOrienteering { + +class CRSSelector; +class Georeferencing; + + +/** Dialog to select a coordinate reference system (CRS) */ +class SelectCRSDialog : public QDialog +{ +Q_OBJECT +public: + + /** + * Georeferencing alternatives + */ + enum GeorefAlternative + { + TakeFromMap = 1 << 0, + Local = 1 << 1, + Geographic = 1 << 2, + None = 0 + }; + Q_DECLARE_FLAGS(GeorefAlternatives, GeorefAlternative) + + /** + * Creates a SelectCRSDialog. + * + * @param georef A default georeferencing (usually the map's one). + * @param parent The parent widget. + * @param alternatives The georeferencing alternatives to be offered. + * @param description Optional description text for the dialog. + * Should explain what the selected CRS will be used for. + */ + SelectCRSDialog( + const Georeferencing& georef, + QWidget* parent, + GeorefAlternatives alternatives, + const QString& description = QString() + ); + + /** + * Returns the current CRS specification string. + */ + QString currentCRSSpec() const; + +protected: + /** + * Update the status field and enables/disables the OK button. + */ + void updateWidgets(); + +private: + const Georeferencing& georef; + CRSSelector* crs_selector; + QLabel* status_label; + QDialogButtonBox* button_box; +}; + + +} // namespace OpenOrienteering + + +Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::SelectCRSDialog::GeorefAlternatives) + + +#endif diff --git a/src/gui/settings_dialog.cpp b/src/gui/settings_dialog.cpp new file mode 100644 index 0000000..85a84c5 --- /dev/null +++ b/src/gui/settings_dialog.cpp @@ -0,0 +1,231 @@ +/* + * Copyright 2012, 2013 Jan Dalheimer + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "settings_dialog.h" + +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gui/main_window.h" +#include "gui/util_gui.h" +#include "gui/widgets/editor_settings_page.h" +#include "gui/widgets/general_settings_page.h" +#include "gui/widgets/settings_page.h" +#include "util/backports.h" // IWYU pragma: keep + +#ifdef MAPPER_USE_GDAL +# include "gdal/gdal_settings_page.h" +#endif + + +namespace OpenOrienteering { + +SettingsDialog::SettingsDialog(QWidget* parent) + : QDialog { parent } + , tab_widget { nullptr } + , stack_widget { nullptr } +{ + setWindowTitle(tr("Settings")); + + auto layout = new QVBoxLayout(this); + + auto buttons = QDialogButtonBox::StandardButtons{ QDialogButtonBox::Ok }; + if (MainWindow::mobileMode()) + { + if (parent) + setGeometry(parent->geometry()); + + stack_widget = new QStackedWidget(); + layout->addWidget(stack_widget, 1); + + auto menu_widget = new QToolBar(); + menu_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + menu_widget->setOrientation(Qt::Vertical); + stack_widget->addWidget(menu_widget); + } + else + { + buttons |= QDialogButtonBox::Reset | QDialogButtonBox::Cancel | QDialogButtonBox::Help; + + tab_widget = new QTabWidget(); +#ifndef Q_OS_MACOS + tab_widget->setDocumentMode(true); +#endif + layout->addWidget(tab_widget, 1); + } + + button_box = new QDialogButtonBox(buttons, Qt::Horizontal); + connect(button_box, &QDialogButtonBox::clicked, this, &SettingsDialog::buttonPressed); + if (MainWindow::mobileMode()) + { + int left, top, right, bottom; + layout->getContentsMargins(&left, &top, &right, &bottom); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + auto l = new QVBoxLayout(); + l->setContentsMargins(left, top, right, bottom); + l->addWidget(button_box); + layout->addLayout(l); + } + else + { + layout->addWidget(button_box); + } + + addPages(); +} + +SettingsDialog::~SettingsDialog() = default; + + + +void SettingsDialog::closeEvent(QCloseEvent* event) +{ + if (MainWindow::mobileMode()) + callOnAllPages(&SettingsPage::apply); + QDialog::closeEvent(event); +} + +void SettingsDialog::keyPressEvent(QKeyEvent* event) +{ + switch (event->key()) + { + case Qt::Key_Back: + case Qt::Key_Escape: + if (MainWindow::mobileMode() && stack_widget->currentIndex() > 0) + { + stack_widget->setCurrentIndex(0); + auto buttons = button_box->standardButtons(); + button_box->setStandardButtons((buttons & ~QDialogButtonBox::Reset) | QDialogButtonBox::Help); + return; + } + break; + default: + ; // nothing + } + QDialog::keyPressEvent(event); +} + +void SettingsDialog::addPages() +{ + addPage(new GeneralSettingsPage(this)); + addPage(new EditorSettingsPage(this)); +#ifdef MAPPER_USE_GDAL + addPage(new GdalSettingsPage(this)); +#endif +} + +void SettingsDialog::addPage(SettingsPage* page) +{ + if (MainWindow::mobileMode()) + { + if (auto form_layout = qobject_cast(page->layout())) + { + form_layout->setRowWrapPolicy(QFormLayout::WrapAllRows); + auto labels = page->findChildren(); + for (auto label : qAsConst(labels)) + label->setWordWrap(true); + } + page->setMaximumWidth(width()); + + auto scrollarea = new QScrollArea(); + scrollarea->setFrameShape(QFrame::NoFrame); + QScroller::grabGesture(scrollarea, QScroller::TouchGesture); + scrollarea->setWidget(page); + stack_widget->addWidget(scrollarea); + + auto menu_widget = qobject_cast(stack_widget->widget(0)); + auto action = menu_widget->addAction(page->title()); + connect(action, &QAction::triggered, this, [this, scrollarea]() + { + stack_widget->setCurrentWidget(scrollarea); + scrollarea->ensureVisible(0, 0); + button_box->setStandardButtons(button_box->standardButtons() | QDialogButtonBox::Reset); + } ); + } + else + { + tab_widget->addTab(page, page->title()); + } +} + +void SettingsDialog::callOnAllPages(void (SettingsPage::*member)()) +{ + auto pages = MainWindow::mobileMode() ? stack_widget->findChildren() + : tab_widget->findChildren(); + for (auto page : qAsConst(pages)) + (page->*member)(); +} + +void SettingsDialog::buttonPressed(QAbstractButton* button) +{ + QDialogButtonBox::StandardButton id = button_box->standardButton(button); + switch (id) + { + case QDialogButtonBox::Ok: + callOnAllPages(&SettingsPage::apply); + if (MainWindow::mobileMode() && stack_widget->currentIndex() > 0) + { + stack_widget->setCurrentIndex(0); + button_box->setStandardButtons(button_box->standardButtons() & ~QDialogButtonBox::Reset); + } + else + { + this->accept(); + } + break; + + case QDialogButtonBox::Reset: + callOnAllPages(&SettingsPage::reset); + break; + + case QDialogButtonBox::Cancel: + this->reject(); + break; + + case QDialogButtonBox::Help: + Util::showHelp(this, QLatin1String("settings.html")); + break; + + default: + qDebug("%s: Unexpected button '0x%x'", Q_FUNC_INFO, id); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/settings_dialog.h b/src/gui/settings_dialog.h new file mode 100644 index 0000000..b4314fd --- /dev/null +++ b/src/gui/settings_dialog.h @@ -0,0 +1,107 @@ +/* + * Copyright 2012, 2013 Jan Dalheimer + * Copyright 2013-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_SETTINGS_DIALOG_H +#define OPENORIENTEERING_SETTINGS_DIALOG_H + +#include +#include + +class QAbstractButton; +class QCloseEvent; +class QDialogButtonBox; +class QKeyEvent; +class QStackedWidget; +class QTabWidget; +class QWidget; + +namespace OpenOrienteering { + +class SettingsPage; + + +/** + * A dialog for editing Mapper's settings. + */ +class SettingsDialog : public QDialog +{ +Q_OBJECT +public: + /** + * Constructs a new settings dialog. + */ + explicit SettingsDialog(QWidget* parent = nullptr); + + /** + * Destroys the settings dialog. + */ + ~SettingsDialog() override; + +protected: + /** + * Adds all known pages to the dialog. + * + * This function is called from the constructor. It may be overridden to + * provide dialogs with different pages. + */ + virtual void addPages(); + + /** + * Adds a single page to the dialog. + */ + void addPage(SettingsPage* page); + + /** + * Calls a SettingsPage member function on all pages. + */ + void callOnAllPages(void (SettingsPage::*member)()); + + + void closeEvent(QCloseEvent* event) override; + + void keyPressEvent(QKeyEvent* event) override; + +private slots: + /** + * Reacts to dialog buttons (OK, Cancel, Rest). + */ + void buttonPressed(QAbstractButton* button); + +private: + /** + * A tab widget which holds all pages in desktop mode. + */ + QTabWidget* tab_widget; + + /** + * A stack widget which holds all pages in mobile mode. + */ + QStackedWidget* stack_widget; + + /** + * The box of standard dialog buttons. + */ + QDialogButtonBox* button_box; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/symbols/area_symbol_settings.cpp b/src/gui/symbols/area_symbol_settings.cpp new file mode 100644 index 0000000..c7e0ac5 --- /dev/null +++ b/src/gui/symbols/area_symbol_settings.cpp @@ -0,0 +1,577 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "area_symbol_settings.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/symbols/symbol.h" +#include "core/symbols/point_symbol.h" +#include "gui/symbols/point_symbol_editor_widget.h" +#include "gui/symbols/symbol_setting_dialog.h" +#include "gui/widgets/color_dropdown.h" +#include "gui/util_gui.h" +#include "util/backports.h" +#include "util/scoped_signals_blocker.h" + + +namespace OpenOrienteering { + +// ### AreaSymbol ### + +SymbolPropertiesWidget* AreaSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) +{ + return new AreaSymbolSettings(this, dialog); +} + + + +// ### AreaSymbolSettings ### + +AreaSymbolSettings::AreaSymbolSettings(AreaSymbol* symbol, SymbolSettingDialog* dialog) + : SymbolPropertiesWidget(symbol, dialog), symbol(symbol) +{ + map = dialog->getPreviewMap(); + controller = dialog->getPreviewController(); + + + auto general_layout = new QFormLayout(); + + color_edit = new ColorDropDown(map); + general_layout->addRow(tr("Area color:"), color_edit); + + minimum_size_edit = Util::SpinBox::create(1, 0.0, 999999.9, trUtf8("mm²")); + general_layout->addRow(tr("Minimum size:"), minimum_size_edit); + + general_layout->addItem(Util::SpacerItem::create(this)); + + + auto fill_patterns_list_layout = new QVBoxLayout(); + + fill_patterns_list_layout->addWidget(Util::Headline::create(tr("Fills")), 0); + + pattern_list = new QListWidget(); + fill_patterns_list_layout->addWidget(pattern_list, 1); + + del_pattern_button = new QPushButton(QIcon(QString::fromLatin1(":/images/minus.png")), QString{}); + add_pattern_button = new QToolButton(); + add_pattern_button->setIcon(QIcon(QString::fromLatin1(":/images/plus.png"))); + add_pattern_button->setToolButtonStyle(Qt::ToolButtonIconOnly); + add_pattern_button->setPopupMode(QToolButton::InstantPopup); + add_pattern_button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + add_pattern_button->setMinimumSize(del_pattern_button->sizeHint()); + auto add_fill_button_menu = new QMenu(add_pattern_button); + add_fill_button_menu->addAction(tr("Line fill"), this, SLOT(addLinePattern())); + add_fill_button_menu->addAction(tr("Pattern fill"), this, SLOT(addPointPattern())); + add_pattern_button->setMenu(add_fill_button_menu); + + auto buttons_layout = new QHBoxLayout(); + buttons_layout->addStretch(1); + buttons_layout->addWidget(add_pattern_button); + buttons_layout->addWidget(del_pattern_button); + buttons_layout->addStretch(1); + fill_patterns_list_layout->addLayout(buttons_layout, 0); + + + auto fill_pattern_layout = new QFormLayout(); + + pattern_name_edit = new QLabel(); + fill_pattern_layout->addRow(pattern_name_edit); + + fill_pattern_layout->addItem(Util::SpacerItem::create(this)); + + + /* From here, stacked widgets are used to unify the layout of pattern type dependant fields. */ + auto single_line_headline = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_headline, &QStackedWidget::setCurrentIndex); + fill_pattern_layout->addRow(single_line_headline); + + auto single_line_label1 = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_label1, &QStackedWidget::setCurrentIndex); + auto single_line_edit1 = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_edit1, &QStackedWidget::setCurrentIndex); + fill_pattern_layout->addRow(single_line_label1, single_line_edit1); + + auto single_line_label2 = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_label2, &QStackedWidget::setCurrentIndex); + auto single_line_edit2 = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_edit2, &QStackedWidget::setCurrentIndex); + fill_pattern_layout->addRow(single_line_label2, single_line_edit2); + + auto single_line_label3 = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, single_line_label3, &QStackedWidget::setCurrentIndex); + pattern_line_offset_edit = Util::SpinBox::create(3, -999999.9, 999999.9, tr("mm")); + fill_pattern_layout->addRow(single_line_label3, pattern_line_offset_edit); + + fill_pattern_layout->addItem(Util::SpacerItem::create(this)); + + auto parallel_lines_headline = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, parallel_lines_headline, &QStackedWidget::setCurrentIndex); + fill_pattern_layout->addRow(parallel_lines_headline); + + auto parallel_lines_label1 = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, parallel_lines_label1, &QStackedWidget::setCurrentIndex); + pattern_spacing_edit = Util::SpinBox::create(3, 0.0, 999999.9, tr("mm")); + fill_pattern_layout->addRow(parallel_lines_label1, pattern_spacing_edit); + + /* Stacked widgets, index 0: line pattern fields */ + single_line_headline->addWidget(Util::Headline::create(tr("Single line"))); + + auto pattern_linewidth_label = new QLabel(tr("Line width:")); + pattern_linewidth_edit = Util::SpinBox::create(3, 0.0, 999999.9, tr("mm")); + single_line_label1->addWidget(pattern_linewidth_label); + single_line_edit1 ->addWidget(pattern_linewidth_edit); + + auto pattern_color_label = new QLabel(tr("Line color:")); + pattern_color_edit = new ColorDropDown(map); + single_line_label2->addWidget(pattern_color_label); + single_line_edit2 ->addWidget(pattern_color_edit); + + single_line_label3->addWidget(new QLabel(tr("Line offset:"))); + + parallel_lines_headline->addWidget(Util::Headline::create(tr("Parallel lines"))); + + parallel_lines_label1->addWidget(new QLabel(tr("Line spacing:"))); + + /* Stacked widgets, index 1: point pattern fields */ + single_line_headline->addWidget(Util::Headline::create(tr("Single row"))); + + auto pattern_pointdist_label = new QLabel(tr("Pattern interval:")); + pattern_pointdist_edit = Util::SpinBox::create(3, 0, 999999.9, tr("mm")); + single_line_label1->addWidget(pattern_pointdist_label); + single_line_edit1 ->addWidget(pattern_pointdist_edit); + + auto pattern_offset_along_line_label = new QLabel(tr("Pattern offset:")); + pattern_offset_along_line_edit = Util::SpinBox::create(3, -999999.9, 999999.9, tr("mm")); + single_line_label2->addWidget(pattern_offset_along_line_label); + single_line_edit2 ->addWidget(pattern_offset_along_line_edit); + + single_line_label3->addWidget(new QLabel(tr("Row offset:"))); + + parallel_lines_headline->addWidget(Util::Headline::create(tr("Parallel rows"))); + + parallel_lines_label1->addWidget(new QLabel(tr("Row spacing:"))); + + /* General fields again. Not stacked. */ + fill_pattern_layout->addItem(Util::SpacerItem::create(this)); + + + fill_pattern_layout->addRow(Util::Headline::create(tr("Fill rotation"))); + + pattern_angle_edit = Util::SpinBox::create(1, 0.0, 360.0, trUtf8("°")); + pattern_angle_edit->setWrapping(true); + fill_pattern_layout->addRow(tr("Angle:"), pattern_angle_edit); + + pattern_rotatable_check = new QCheckBox(tr("adjustable per object")); + fill_pattern_layout->addRow(QString{}, pattern_rotatable_check); + + + fill_pattern_layout->addItem(Util::SpacerItem::create(this)); + + /* Stacked widgets again. */ + auto clipping_headline = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, clipping_headline, &QStackedWidget::setCurrentIndex); + fill_pattern_layout->addRow(clipping_headline); + + auto clipping_edit = new QStackedWidget(); + connect(this, &AreaSymbolSettings::switchPatternEdits, clipping_edit, &QStackedWidget::setCurrentIndex); + fill_pattern_layout->addRow(clipping_edit); + + // Line pattern: Clipping settings not used + clipping_headline->addWidget(new QWidget()); + clipping_edit->addWidget(new QWidget()); + + // Point pattern clipping + clipping_headline->addWidget(Util::Headline::create(tr("Element drawing at boundary"))); + pattern_clipping_edit = new QComboBox(); + pattern_clipping_edit->addItem(tr("Clip elements at the boundary."), AreaSymbol::FillPattern::Default); + pattern_clipping_edit->addItem(tr("Draw elements if the center is inside the boundary."), AreaSymbol::FillPattern::NoClippingIfCenterInside); + pattern_clipping_edit->addItem(tr("Draw elements if any point is inside the boundary."), AreaSymbol::FillPattern::NoClippingIfPartiallyInside); + pattern_clipping_edit->addItem(tr("Draw elements if all points are inside the boundary."), AreaSymbol::FillPattern::NoClippingIfCompletelyInside); + clipping_edit->addWidget(pattern_clipping_edit); + + + auto fill_patterns_layout = new QHBoxLayout(); + fill_patterns_layout->addLayout(fill_patterns_list_layout, 1); + fill_patterns_layout->addItem(Util::SpacerItem::create(this)); + fill_patterns_layout->addLayout(fill_pattern_layout, 3); + + auto layout = new QVBoxLayout(); + layout->addLayout(general_layout); + layout->addLayout(fill_patterns_layout); + + auto area_tab = new QWidget(); + area_tab->setLayout(layout); + addPropertiesGroup(tr("Area settings"), area_tab); + + connect(color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &AreaSymbolSettings::colorChanged); + connect(minimum_size_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::minimumSizeChanged); + connect(del_pattern_button, &QAbstractButton::clicked, this, &AreaSymbolSettings::deleteActivePattern); + connect(pattern_list, &QListWidget::currentRowChanged, this, &AreaSymbolSettings::selectPattern); + connect(pattern_angle_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternAngleChanged); + connect(pattern_rotatable_check, &QAbstractButton::clicked, this, &AreaSymbolSettings::patternRotatableClicked); + connect(pattern_spacing_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternSpacingChanged); + connect(pattern_line_offset_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternLineOffsetChanged); + connect(pattern_offset_along_line_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternOffsetAlongLineChanged); + + connect(pattern_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &AreaSymbolSettings::patternColorChanged); + connect(pattern_linewidth_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternLineWidthChanged); + connect(pattern_pointdist_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AreaSymbolSettings::patternPointDistChanged); + + connect(pattern_clipping_edit, QOverload::of(&QComboBox::currentIndexChanged), this, [this](){ + active_pattern->setClipping(AreaSymbol::FillPattern::Options(pattern_clipping_edit->currentData(Qt::UserRole).toInt())); + emit propertiesModified(); + }); + + updateAreaGeneral(); + updatePatternWidgets(); + loadPatterns(); +} + + +AreaSymbolSettings::~AreaSymbolSettings() = default; + + + +void AreaSymbolSettings::reset(Symbol* symbol) +{ + Q_ASSERT(symbol->getType() == Symbol::Area); + + clearPatterns(); + + SymbolPropertiesWidget::reset(symbol); + this->symbol = reinterpret_cast(symbol); + + updateAreaGeneral(); + loadPatterns(); +} + + +void AreaSymbolSettings::updateAreaGeneral() +{ + ScopedMultiSignalsBlocker block(color_edit, minimum_size_edit); + color_edit->setColor(symbol->getColor()); + minimum_size_edit->setValue(0.001 * symbol->minimum_area); +} + + +void AreaSymbolSettings::clearPatterns() +{ + pattern_list->clear(); + for (const auto& pattern : symbol->patterns) + { + if (pattern.type == AreaSymbol::FillPattern::PointPattern) + removePropertiesGroup(2); + } +} + + +void AreaSymbolSettings::loadPatterns() +{ + Q_ASSERT(pattern_list->count() == 0); + Q_ASSERT(count() == 2); // General + Area tab + + for (const auto& pattern : symbol->patterns) + { + pattern_list->addItem(pattern.name); + if (pattern.type == AreaSymbol::FillPattern::PointPattern) + { + auto editor = new PointSymbolEditorWidget(controller, pattern.point, 16); + connect(editor, &PointSymbolEditorWidget::symbolEdited, this, &SymbolPropertiesWidget::propertiesModified ); + addPropertiesGroup(pattern.name, editor); + } + } + updatePatternNames(); + pattern_list->setCurrentRow(0); +} + + +void AreaSymbolSettings::updatePatternNames() +{ + int i = 0, line_pattern_num = 0, point_pattern_num = 0; + for (auto& pattern : symbol->patterns) + { + if (pattern.type == AreaSymbol::FillPattern::PointPattern) + { + ++point_pattern_num; + QString name = tr("Pattern fill %1").arg(point_pattern_num); + pattern.name = name; + pattern.point->setName(name); + pattern_list->item(i)->setText(name); + renamePropertiesGroup(point_pattern_num + 1, name); + } + else + { + ++line_pattern_num; + QString name = tr("Line fill %1").arg(line_pattern_num); + pattern.name = name; + pattern_list->item(i)->setText(pattern.name); + } + ++i; + } +} + + +void AreaSymbolSettings::updatePatternWidgets() +{ + int index = pattern_list->currentRow(); + bool pattern_active = (index >= 0); + if (pattern_active) + active_pattern = symbol->patterns.begin() + index; + + del_pattern_button->setEnabled(pattern_active); + + auto pattern = pattern_active ? &*active_pattern : new AreaSymbol::FillPattern(); + pattern_name_edit->setText(pattern_active ? (QLatin1String("") + active_pattern->name + QLatin1String("")) : tr("No fill selected")); + + { + ScopedMultiSignalsBlocker block( + pattern_angle_edit, + pattern_rotatable_check, + pattern_spacing_edit, + pattern_line_offset_edit + ); + pattern_angle_edit->setValue(qRadiansToDegrees(pattern->angle)); + pattern_angle_edit->setEnabled(pattern_active); + pattern_rotatable_check->setChecked(pattern->rotatable()); + pattern_rotatable_check->setEnabled(pattern_active); + pattern_spacing_edit->setValue(0.001 * pattern->line_spacing); + pattern_spacing_edit->setEnabled(pattern_active); + pattern_line_offset_edit->setValue(0.001 * pattern->line_offset); + pattern_line_offset_edit->setEnabled(pattern_active); + } + + switch (pattern->type) + { + case AreaSymbol::FillPattern::LinePattern: + { + emit switchPatternEdits(0); + + ScopedMultiSignalsBlocker block(pattern_linewidth_edit, pattern_color_edit); + pattern_linewidth_edit->setValue(0.001 * pattern->line_width); + pattern_linewidth_edit->setEnabled(pattern_active); + pattern_color_edit->setColor(pattern->line_color); + pattern_color_edit->setEnabled(pattern_active); + break; + } + case AreaSymbol::FillPattern::PointPattern: + { + emit switchPatternEdits(1); + + ScopedMultiSignalsBlocker block( + pattern_offset_along_line_edit, + pattern_pointdist_edit, + pattern_clipping_edit + ); + pattern_offset_along_line_edit->setValue(0.001 * pattern->offset_along_line); + pattern_offset_along_line_edit->setEnabled(pattern_active); + pattern_pointdist_edit->setValue(0.001 * pattern->point_distance); + pattern_pointdist_edit->setEnabled(pattern_active); + pattern_clipping_edit->setCurrentIndex(pattern_clipping_edit->findData(int(pattern->clipping()))); + break; + } + } + + if (!pattern_active) + delete pattern; +} + + +void AreaSymbolSettings::addLinePattern() +{ + addPattern(AreaSymbol::FillPattern::LinePattern); +} + + +void AreaSymbolSettings::addPointPattern() +{ + addPattern(AreaSymbol::FillPattern::PointPattern); +} + + +void AreaSymbolSettings::addPattern(AreaSymbol::FillPattern::Type type) +{ + int index = pattern_list->currentRow() + 1; + if (index < 0) + index = int(symbol->patterns.size()); + + active_pattern = symbol->patterns.insert(symbol->patterns.begin() + index, AreaSymbol::FillPattern()); + auto temp_name = QString::fromLatin1("new"); + pattern_list->insertItem(index, temp_name); + Q_ASSERT(int(symbol->patterns.size()) == pattern_list->count()); + + active_pattern->type = type; + if (type == AreaSymbol::FillPattern::PointPattern) + { + active_pattern->point = new PointSymbol(); + active_pattern->point->setRotatable(true); + auto editor = new PointSymbolEditorWidget(controller, active_pattern->point, 16); + connect(editor, &PointSymbolEditorWidget::symbolEdited, this, &SymbolPropertiesWidget::propertiesModified ); + if (pattern_list->currentRow() == int(symbol->patterns.size()) - 1) + { + addPropertiesGroup(temp_name, editor); + } + else + { + int group_index = indexOfPropertiesGroup(symbol->patterns[pattern_list->currentRow()+1].name); + insertPropertiesGroup(group_index, temp_name, editor); + } + } + else if (map->getNumColors() > 0) + { + // Default color for lines + active_pattern->line_color = map->getColor(0); + } + updatePatternNames(); + emit propertiesModified(); + + pattern_list->setCurrentRow(index); +} + + +void AreaSymbolSettings::deleteActivePattern() +{ + int index = pattern_list->currentRow(); + if (index < 0) + { + qWarning("AreaSymbolSettings::deleteActivePattern(): no fill selected."); + return; + } + + if (active_pattern->type == AreaSymbol::FillPattern::PointPattern) + { + removePropertiesGroup(active_pattern->name); + delete active_pattern->point; + active_pattern->point = nullptr; + } + symbol->patterns.erase(active_pattern); + + delete pattern_list->takeItem(index); + updatePatternNames(); + updatePatternWidgets(); + + emit propertiesModified(); + + index = pattern_list->currentRow(); + selectPattern(index); +} + + +void AreaSymbolSettings::selectPattern(int index) +{ + active_pattern = symbol->patterns.begin() + index; + updatePatternWidgets(); +} + + + +void AreaSymbolSettings::colorChanged() +{ + symbol->color = color_edit->color(); + emit propertiesModified(); +} + +void AreaSymbolSettings::minimumSizeChanged(double value) +{ + symbol->minimum_area = qRound(1000.0 * value); + emit propertiesModified(); +} + +void AreaSymbolSettings::patternAngleChanged(double value) +{ + active_pattern->angle = qDegreesToRadians(value); + emit propertiesModified(); +} + +void AreaSymbolSettings::patternRotatableClicked(bool checked) +{ + active_pattern->setRotatable(checked); + emit propertiesModified(); +} + +void AreaSymbolSettings::patternSpacingChanged(double value) +{ + active_pattern->line_spacing = qRound(1000.0 * value); + emit propertiesModified(); +} + +void AreaSymbolSettings::patternLineOffsetChanged(double value) +{ + active_pattern->line_offset = qRound(1000.0 * value); + emit propertiesModified(); +} + +void AreaSymbolSettings::patternOffsetAlongLineChanged(double value) +{ + Q_ASSERT(active_pattern->type == AreaSymbol::FillPattern::PointPattern); + active_pattern->offset_along_line = qRound(1000.0 * value); + emit propertiesModified(); +} + +void AreaSymbolSettings::patternColorChanged() +{ + Q_ASSERT(active_pattern->type == AreaSymbol::FillPattern::LinePattern); + active_pattern->line_color = pattern_color_edit->color(); + emit propertiesModified(); +} + +void AreaSymbolSettings::patternLineWidthChanged(double value) +{ + Q_ASSERT(active_pattern->type == AreaSymbol::FillPattern::LinePattern); + active_pattern->line_width = qRound(1000.0 * value); + emit propertiesModified(); +} + +void AreaSymbolSettings::patternPointDistChanged(double value) +{ + Q_ASSERT(active_pattern->type == AreaSymbol::FillPattern::PointPattern); + active_pattern->point_distance = qRound(1000.0 * value); + emit propertiesModified(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/area_symbol_settings.h b/src/gui/symbols/area_symbol_settings.h new file mode 100644 index 0000000..040242b --- /dev/null +++ b/src/gui/symbols/area_symbol_settings.h @@ -0,0 +1,124 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_AREA_SYMBOL_SETTINGS_H +#define OPENORIENTEERING_AREA_SYMBOL_SETTINGS_H + +#include + +#include + +#include "core/symbols/area_symbol.h" +#include "gui/symbols/symbol_properties_widget.h" + +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QLabel; +class QListWidget; +class QPushButton; +class QToolButton; + +namespace OpenOrienteering { + +class ColorDropDown; +class Map; +class MapEditorController; +class Symbol; +class SymbolSettingDialog; + + +class AreaSymbolSettings : public SymbolPropertiesWidget +{ +Q_OBJECT +public: + AreaSymbolSettings(AreaSymbol* symbol, SymbolSettingDialog* dialog); + + ~AreaSymbolSettings() override; + + + void reset(Symbol* symbol) override; + + /** + * Updates the general area fields (not related to patterns) + */ + void updateAreaGeneral(); + + void addPattern(AreaSymbol::FillPattern::Type type); + +signals: + void switchPatternEdits(int layer); + +public slots: + void selectPattern(int index); + void addLinePattern(); + void addPointPattern(); + void deleteActivePattern(); + +protected: + void clearPatterns(); + void loadPatterns(); + void updatePatternNames(); + void updatePatternWidgets(); + +protected slots: + void colorChanged(); + void minimumSizeChanged(double value); + void patternAngleChanged(double value); + void patternRotatableClicked(bool checked); + void patternSpacingChanged(double value); + void patternLineOffsetChanged(double value); + void patternOffsetAlongLineChanged(double value); + void patternColorChanged(); + void patternLineWidthChanged(double value); + void patternPointDistChanged(double value); + +private: + AreaSymbol* symbol; + Map* map; + MapEditorController* controller; + std::vector::iterator active_pattern; + + ColorDropDown* color_edit; + QDoubleSpinBox* minimum_size_edit; + + QListWidget* pattern_list; + QToolButton* add_pattern_button; + QPushButton* del_pattern_button; + + QLabel* pattern_name_edit; + QDoubleSpinBox* pattern_angle_edit; + QCheckBox* pattern_rotatable_check; + QDoubleSpinBox* pattern_spacing_edit; + QDoubleSpinBox* pattern_line_offset_edit; + QDoubleSpinBox* pattern_offset_along_line_edit; + + ColorDropDown* pattern_color_edit; + QDoubleSpinBox* pattern_linewidth_edit; + QDoubleSpinBox* pattern_pointdist_edit; + + QComboBox* pattern_clipping_edit; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/symbols/combined_symbol_settings.cpp b/src/gui/symbols/combined_symbol_settings.cpp new file mode 100644 index 0000000..819e8d9 --- /dev/null +++ b/src/gui/symbols/combined_symbol_settings.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "combined_symbol_settings.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/symbols/combined_symbol.h" +#include "core/symbols/symbol.h" +#include "gui/symbols/symbol_setting_dialog.h" +#include "gui/widgets/symbol_dropdown.h" +#include "util/backports.h" + + +namespace OpenOrienteering { + +// ### CombinedSymbol ### + +SymbolPropertiesWidget* CombinedSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) +{ + return new CombinedSymbolSettings(this, dialog); +} + + + +// ### CombinedSymbolSettings ### + +struct CombinedSymbolSettings::Widgets +{ + QLabel* label; + SymbolDropDown* edit; + QPushButton* button; +}; + + + +CombinedSymbolSettings::CombinedSymbolSettings(CombinedSymbol* symbol, SymbolSettingDialog* dialog) +: SymbolPropertiesWidget(symbol, dialog) +, symbol(symbol) +{ + auto source_symbol = static_cast(dialog->getUnmodifiedSymbol()); + auto source_map = dialog->getSourceMap(); + + auto layout = new QFormLayout(); + + number_edit = new QSpinBox(); + number_edit->setRange(2, qMax(5, symbol->getNumParts())); + number_edit->setValue(symbol->getNumParts()); + connect(number_edit, QOverload::of(&QSpinBox::valueChanged), this, &CombinedSymbolSettings::numberChanged); + layout->addRow(tr("&Number of parts:"), number_edit); + + widgets.resize(std::size_t(number_edit->maximum())); + for (auto i = 0u; i < widgets.size(); ++i) + { + auto label = new QLabel(tr("Symbol %1:").arg(i+1)); + + auto edit = new SymbolDropDown(source_map, Symbol::Line | Symbol::Area | Symbol::Combined, (symbol->parts.size() > i) ? symbol->parts[i] : nullptr, source_symbol); + edit->addCustomItem(tr("- Private line symbol -"), 1); + edit->addCustomItem(tr("- Private area symbol -"), 2); + connect(edit, QOverload::of(&SymbolDropDown::currentIndexChanged), this, &CombinedSymbolSettings::symbolChanged); + + auto button = new QPushButton(tr("Edit private symbol...")); + connect(button, QOverload::of(&QAbstractButton::clicked), this, QOverload<>::of(&CombinedSymbolSettings::editButtonClicked)); + + auto row_layout = new QHBoxLayout(); + row_layout->addWidget(edit); + row_layout->addWidget(button); + layout->addRow(label, row_layout); + + widgets[i] = { label, edit, button }; + } + updateContents(); + + auto widget = new QWidget; + widget->setLayout(layout); + addPropertiesGroup(tr("Combination settings"), widget); +} + + +CombinedSymbolSettings::~CombinedSymbolSettings() = default; + + + +void CombinedSymbolSettings::reset(Symbol* symbol) +{ + if (Q_UNLIKELY(symbol->getType() != Symbol::Combined)) + { + qWarning("Not a combined symbol: %s", symbol ? "nullptr" : qPrintable(symbol->getPlainTextName())); + return; + } + + SymbolPropertiesWidget::reset(symbol); + this->symbol = static_cast(symbol); + updateContents(); +} + + + +void CombinedSymbolSettings::updateContents() +{ + auto num_parts = symbol->parts.size(); + for (auto i = 0u; i < num_parts; ++i) + { + auto& w = widgets[i]; + auto is_private = symbol->isPartPrivate(int(i)); + QSignalBlocker block{w.edit}; + w.label->setVisible(true); + if (is_private) + w.edit->setCustomItem((symbol->parts[i]->getType() == Symbol::Line) ? 1 : 2); + else + w.edit->setSymbol(symbol->parts[i]); + w.edit->setVisible(true); + w.button->setEnabled(is_private); + w.button->setVisible(true); + } + for (auto i = num_parts; i < widgets.size(); ++i) + { + auto& w = widgets[i]; + w.label->setVisible(false); + w.edit->setSymbol(nullptr); + w.edit->setVisible(false); + w.button->setVisible(false); + } + + QSignalBlocker block{number_edit}; + number_edit->setValue(int(num_parts)); +} + + + +void CombinedSymbolSettings::numberChanged(int value) +{ + int old_num_items = symbol->getNumParts(); + if (old_num_items == value) + return; + + value = qMin(int(widgets.size()), value); + symbol->setNumParts(value); + for (auto i = std::size_t(old_num_items); i < std::size_t(value); ++i) + { + // This item appears now. + auto& w = widgets[i]; + QSignalBlocker block{w.edit}; + w.label->setVisible(true); + w.edit->setSymbol(nullptr); + w.edit->setVisible(true); + w.button->setEnabled(false); + w.button->setVisible(true); + } + for (auto i = std::size_t(value); i < std::size_t(old_num_items); ++i) + { + // This item disappears now. + auto& w = widgets[i]; + w.label->setVisible(false); + w.edit->setVisible(false); + w.button->setVisible(false); + } + emit propertiesModified(); +} + + + +void CombinedSymbolSettings::symbolChanged() +{ + const auto edit = sender(); + auto widget = std::find_if(begin(widgets), end(widgets), [edit](const auto& w) { + return w.edit == edit; + }); + if (widget == end(widgets)) + return; + + auto index = int(std::distance(begin(widgets), widget)); + if (widget->edit->symbol()) + { + symbol->setPart(index, widget->edit->symbol(), false); + } + else if (widget->edit->customID() > 0) + { + // Changing to a private symbol + Symbol::Type new_symbol_type; + if (widget->edit->customID() == 1) + new_symbol_type = Symbol::Line; + else // if (symbol_edits[index]->customID() == 2) + new_symbol_type = Symbol::Area; + + Symbol* new_symbol = nullptr; + if (symbol->getPart(index) && new_symbol_type == symbol->getPart(index)->getType()) + { + if (QMessageBox::question(this, tr("Change from public to private symbol"), + tr("Take the old symbol as template for the private symbol?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + { + new_symbol = symbol->getPart(index)->duplicate(); + } + } + + if (!new_symbol) + new_symbol = Symbol::getSymbolForType(new_symbol_type); + + symbol->setPart(index, new_symbol, true); + showEditDialog(index); + } + else + { + symbol->setPart(index, nullptr, false); + } + + widget->button->setEnabled(symbol->isPartPrivate(index)); + emit propertiesModified(); +} + + + +void CombinedSymbolSettings::editButtonClicked() +{ + const auto button = sender(); + auto widget = std::find_if(begin(widgets), end(widgets), [button](const auto& w) { + return w.button == button; + }); + if (widget != end(widgets)) + showEditDialog(int(std::distance(begin(widgets), widget))); +} + + +void CombinedSymbolSettings::showEditDialog(int index) +{ + if (Q_UNLIKELY(!symbol->isPartPrivate(index))) + { + qWarning("Symbol settings dialog requested for non-private subsymbol %d", index); + return; + } + + auto part = std::unique_ptr(symbol->getPart(index)->duplicate()); + SymbolSettingDialog sub_dialog(part.get(), dialog->getSourceMap(), this); + sub_dialog.setWindowModality(Qt::WindowModal); + if (sub_dialog.exec() == QDialog::Accepted) + { + symbol->setPart(index, sub_dialog.getNewSymbol().release(), true); + emit propertiesModified(); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/combined_symbol_settings.h b/src/gui/symbols/combined_symbol_settings.h new file mode 100644 index 0000000..4a417e3 --- /dev/null +++ b/src/gui/symbols/combined_symbol_settings.h @@ -0,0 +1,73 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_COMBINED_SYMBOL_SETTINGS_H +#define OPENORIENTEERING_COMBINED_SYMBOL_SETTINGS_H + +#include + +#include + +#include "gui/symbols/symbol_properties_widget.h" + +class QSpinBox; + +namespace OpenOrienteering { + +class CombinedSymbol; +class Symbol; +class SymbolSettingDialog; + + +class CombinedSymbolSettings : public SymbolPropertiesWidget +{ +Q_OBJECT +public: + CombinedSymbolSettings(CombinedSymbol* symbol, SymbolSettingDialog* dialog); + ~CombinedSymbolSettings() override; + + void reset(Symbol* symbol) override; + + /** + * Updates the content and state of all input fields. + */ + void updateContents(); + + +protected: + void addRow(unsigned int index); + void numberChanged(int value); + void symbolChanged(); + void editButtonClicked(); + void showEditDialog(int index); + +private: + CombinedSymbol* symbol; + + QSpinBox* number_edit; + struct Widgets; + std::vector widgets; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/symbols/line_symbol_settings.cpp b/src/gui/symbols/line_symbol_settings.cpp new file mode 100644 index 0000000..687d095 --- /dev/null +++ b/src/gui/symbols/line_symbol_settings.cpp @@ -0,0 +1,791 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "line_symbol_settings.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/symbols/symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "gui/util_gui.h" +#include "gui/symbols/point_symbol_editor_widget.h" +#include "gui/symbols/symbol_setting_dialog.h" +#include "gui/widgets/color_dropdown.h" +#include "util/backports.h" // IWYU pragma: keep +#include "util/util.h" + + +namespace OpenOrienteering { + +// ### LineSymbol ### + +SymbolPropertiesWidget* LineSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) +{ + return new LineSymbolSettings(this, dialog); +} + + +// ### LineSymbolSettings ### + +LineSymbolSettings::LineSymbolSettings(LineSymbol* symbol, SymbolSettingDialog* dialog) +: SymbolPropertiesWidget(symbol, dialog), symbol(symbol), dialog(dialog) +{ + auto map = dialog->getPreviewMap(); + + auto line_tab = new QWidget(); + auto layout = new QGridLayout(); + layout->setColumnStretch(1, 1); + line_tab->setLayout(layout); + + auto width_label = new QLabel(tr("Line width:")); + width_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + auto color_label = new QLabel(tr("Line color:")); + color_edit = new ColorDropDown(map, symbol->getColor()); + + int row = 0, col = 0; + layout->addWidget(width_label, row, col++); + layout->addWidget(width_edit, row, col); + + row++; col = 0; + layout->addWidget(color_label, row, col++); + layout->addWidget(color_edit, row, col, 1, -1); + + + auto minimum_length_label = new QLabel(tr("Minimum line length:")); + minimum_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + auto line_cap_label = new QLabel(tr("Line cap:")); + line_cap_combo = new QComboBox(); + line_cap_combo->addItem(tr("flat"), QVariant(LineSymbol::FlatCap)); + line_cap_combo->addItem(tr("round"), QVariant(LineSymbol::RoundCap)); + line_cap_combo->addItem(tr("square"), QVariant(LineSymbol::SquareCap)); + line_cap_combo->addItem(tr("pointed"), QVariant(LineSymbol::PointedCap)); + + pointed_cap_length_label = new QLabel(tr("Cap length:")); + pointed_cap_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + auto line_join_label = new QLabel(tr("Line join:")); + line_join_combo = new QComboBox(); + line_join_combo->addItem(tr("miter"), QVariant(LineSymbol::MiterJoin)); + line_join_combo->addItem(tr("round"), QVariant(LineSymbol::RoundJoin)); + line_join_combo->addItem(tr("bevel"), QVariant(LineSymbol::BevelJoin)); + + dashed_check = new QCheckBox(tr("Line is dashed")); + + border_check = new QCheckBox(tr("Enable border lines")); + + line_settings_list = { + minimum_length_label, minimum_length_edit, + line_cap_label, line_cap_combo, + line_join_label, line_join_combo, + pointed_cap_length_label, pointed_cap_length_edit, + border_check, + }; + + row++; col = 0; + layout->addWidget(minimum_length_label, row, col++); + layout->addWidget(minimum_length_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(line_cap_label, row, col++); + layout->addWidget(line_cap_combo, row, col, 1, -1); + row++; col = 0; + layout->addWidget(pointed_cap_length_label, row, col++); + layout->addWidget(pointed_cap_length_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(line_join_label, row, col++); + layout->addWidget(line_join_combo, row, col, 1, -1); + + row++; col = 0; + layout->addWidget(new QWidget(), row, col, 1, -1); + row++; col = 0; + layout->addWidget(Util::Headline::create(tr("Dashed line")), row, col++, 1, -1); + row++; col = 0; + layout->addWidget(dashed_check, row, col, 1, -1); + + auto dash_length_label = new QLabel(tr("Dash length:")); + dash_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + auto break_length_label = new QLabel(tr("Break length:")); + break_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + auto dash_group_label = new QLabel(tr("Dashes grouped together:")); + dash_group_combo = new QComboBox(); + dash_group_combo->addItem(tr("none"), QVariant(1)); + dash_group_combo->addItem(tr("2"), QVariant(2)); + dash_group_combo->addItem(tr("3"), QVariant(3)); + dash_group_combo->addItem(tr("4"), QVariant(4)); + + in_group_break_length_label = new QLabel(tr("In-group break length:")); + in_group_break_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + half_outer_dashes_check = new QCheckBox(tr("Half length of first and last dash")); + + dashed_widget_list = { + dash_length_label, dash_length_edit, + break_length_label, break_length_edit, + dash_group_label, dash_group_combo, + in_group_break_length_label, in_group_break_length_edit, + half_outer_dashes_check, + }; + + row++; col = 0; + layout->addWidget(dash_length_label, row, col++); + layout->addWidget(dash_length_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(break_length_label, row, col++); + layout->addWidget(break_length_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(dash_group_label, row, col++); + layout->addWidget(dash_group_combo, row, col, 1, -1); + row++; col = 0; + layout->addWidget(in_group_break_length_label, row, col++); + layout->addWidget(in_group_break_length_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(half_outer_dashes_check, row, col, 1, -1); + + + row++; col = 0; + layout->addWidget(new QWidget(), row, col, 1, -1); + row++; col = 0; + layout->addWidget(Util::Headline::create(tr("Mid symbols")), row, col, 1, -1); + + + auto mid_symbol_per_spot_label = new QLabel(tr("Mid symbols per spot:")); + mid_symbol_per_spot_edit = Util::SpinBox::create(1, 99); + + mid_symbol_distance_label = new QLabel(tr("Mid symbol distance:")); + mid_symbol_distance_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + mid_symbol_widget_list = { + mid_symbol_per_spot_label, mid_symbol_per_spot_edit, + mid_symbol_distance_label, mid_symbol_distance_edit, + }; + + row++; col = 0; + layout->addWidget(mid_symbol_per_spot_label, row, col++); + layout->addWidget(mid_symbol_per_spot_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(mid_symbol_distance_label, row, col++); + layout->addWidget(mid_symbol_distance_edit, row, col, 1, -1); + + + auto segment_length_label = new QLabel(tr("Distance between spots:")); + segment_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + auto end_length_label = new QLabel(tr("Distance from line end:")); + end_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + show_at_least_one_symbol_check = new QCheckBox(tr("Show at least one mid symbol")); + + auto minimum_mid_symbol_count_label = new QLabel(tr("Minimum mid symbol count:")); + minimum_mid_symbol_count_edit = Util::SpinBox::create(0, 99); + + auto minimum_mid_symbol_count_when_closed_label = new QLabel(tr("Minimum mid symbol count when closed:")); + minimum_mid_symbol_count_when_closed_edit = Util::SpinBox::create(0, 99); + + undashed_widget_list = { + segment_length_label, segment_length_edit, + end_length_label, end_length_edit, + show_at_least_one_symbol_check, + minimum_mid_symbol_count_label, minimum_mid_symbol_count_edit, + minimum_mid_symbol_count_when_closed_label, minimum_mid_symbol_count_when_closed_edit, + }; + + row++; col = 0; + layout->addWidget(segment_length_label, row, col++); + layout->addWidget(segment_length_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(end_length_label, row, col++); + layout->addWidget(end_length_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(show_at_least_one_symbol_check, row, col, 1, -1); + row++; col = 0; + layout->addWidget(minimum_mid_symbol_count_label, row, col++); + layout->addWidget(minimum_mid_symbol_count_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(minimum_mid_symbol_count_when_closed_label, row, col++); + layout->addWidget(minimum_mid_symbol_count_when_closed_edit, row, col, 1, -1); + + + row++; col = 0; + layout->addWidget(new QWidget(), row, col, 1, -1); + row++; col = 0; + layout->addWidget(Util::Headline::create(tr("Borders")), row, col++, 1, -1); + row++; col = 0; + layout->addWidget(border_check, row, col, 1, -1); + + different_borders_check = new QCheckBox(tr("Different borders on left and right sides")); + row++; col = 0; + layout->addWidget(different_borders_check, row, col, 1, -1); + + auto left_border_label = Util::Headline::create(tr("Left border:")); + row++; col = 0; + layout->addWidget(left_border_label, row, col++, 1, -1); + createBorderWidgets(symbol->getBorder(), map, row, col, layout, border_widgets); + + auto right_border_label = Util::Headline::create(tr("Right border:")); + row++; col = 0; + layout->addWidget(right_border_label, row, col++, 1, -1); + createBorderWidgets(symbol->getRightBorder(), map, row, col, layout, right_border_widgets); + + border_widget_list.reserve(1 + border_widgets.widget_list.size()); + border_widget_list.push_back(different_borders_check); + border_widget_list.insert(border_widget_list.end(), begin(border_widgets.widget_list), end(border_widgets.widget_list)); + + different_borders_widget_list.reserve(2 + right_border_widgets.widget_list.size()); + different_borders_widget_list.push_back(left_border_label); + different_borders_widget_list.push_back(right_border_label); + different_borders_widget_list.insert(different_borders_widget_list.end(), begin(right_border_widgets.widget_list), end(right_border_widgets.widget_list)); + + + row++; col = 0; + layout->addWidget(new QWidget(), row, col, 1, -1); + row++; col = 0; + layout->addWidget(Util::Headline::create(tr("Dash symbol")), row, col++, 1, -1); + + supress_dash_symbol_check = new QCheckBox(tr("Suppress the dash symbol at line start and line end")); + row++; col = 0; + layout->addWidget(supress_dash_symbol_check, row, col, 1, -1); + + scale_dash_symbol_check = new QCheckBox(tr("Scale the dash symbol at corners")); + row++; col = 0; + layout->addWidget(scale_dash_symbol_check, row, col, 1, -1); + + row++; + layout->setRowStretch(row, 1); + + const int line_tab_width = line_tab->sizeHint().width(); + + scroll_area = new QScrollArea(); + scroll_area->setWidget(line_tab); + scroll_area->setWidgetResizable(true); + scroll_area->setFrameShape(QFrame::NoFrame); + scroll_area->setMinimumWidth(line_tab_width + scroll_area->verticalScrollBar()->sizeHint().width()); + addPropertiesGroup(tr("Line settings"), scroll_area); + + PointSymbolEditorWidget* point_symbol_editor = nullptr; + auto controller = dialog->getPreviewController(); + + symbol->ensurePointSymbols(tr("Start symbol"), tr("Mid symbol"), tr("End symbol"), tr("Dash symbol")); + for (auto point_symbol : { symbol->getStartSymbol(), symbol->getMidSymbol(), symbol->getEndSymbol(), symbol->getDashSymbol() }) + { + point_symbol_editor = new PointSymbolEditorWidget(controller, point_symbol, 16); + addPropertiesGroup(point_symbol->getName(), point_symbol_editor); + connect(point_symbol_editor, &PointSymbolEditorWidget::symbolEdited, this, &LineSymbolSettings::pointSymbolEdited); + } + + updateStates(); + updateContents(); + + connect(width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::widthChanged); + connect(color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::colorChanged); + connect(minimum_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::minimumDimensionsEdited); + connect(line_cap_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::lineCapChanged); + connect(line_join_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::lineJoinChanged); + connect(pointed_cap_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::pointedLineCapLengthChanged); + connect(dashed_check, &QAbstractButton::clicked, this, &LineSymbolSettings::dashedChanged); + connect(segment_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::segmentLengthChanged); + connect(end_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::endLengthChanged); + connect(show_at_least_one_symbol_check, &QAbstractButton::clicked, this, &LineSymbolSettings::showAtLeastOneSymbolChanged); + connect(minimum_mid_symbol_count_edit, QOverload::of(&QSpinBox::valueChanged), this, &LineSymbolSettings::minimumDimensionsEdited); + connect(minimum_mid_symbol_count_when_closed_edit, QOverload::of(&QSpinBox::valueChanged), this, &LineSymbolSettings::minimumDimensionsEdited); + connect(dash_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::dashLengthChanged); + connect(break_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::breakLengthChanged); + connect(dash_group_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::dashGroupsChanged); + connect(in_group_break_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::inGroupBreakLengthChanged); + connect(half_outer_dashes_check, &QAbstractButton::clicked, this, &LineSymbolSettings::halfOuterDashesChanged); + connect(mid_symbol_per_spot_edit, QOverload::of(&QSpinBox::valueChanged), this, &LineSymbolSettings::midSymbolsPerDashChanged); + connect(mid_symbol_distance_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::midSymbolDistanceChanged); + connect(border_check, &QAbstractButton::clicked, this, &LineSymbolSettings::borderCheckClicked); + connect(different_borders_check, &QAbstractButton::clicked, this, &LineSymbolSettings::differentBordersClicked); + connect(supress_dash_symbol_check, &QAbstractButton::clicked, this, &LineSymbolSettings::suppressDashSymbolClicked); + connect(scale_dash_symbol_check, &QCheckBox::clicked, this, &LineSymbolSettings::scaleDashSymbolClicked); +} + +LineSymbolSettings::~LineSymbolSettings() = default; + + + +void LineSymbolSettings::pointSymbolEdited() +{ + emit propertiesModified(); + updateStates(); +} + +void LineSymbolSettings::widthChanged(double value) +{ + symbol->line_width = qRound(1000 * value); + emit propertiesModified(); + updateStates(); +} + +void LineSymbolSettings::colorChanged() +{ + symbol->color = color_edit->color(); + emit propertiesModified(); + updateStates(); +} + +void LineSymbolSettings::minimumDimensionsEdited() +{ + symbol->minimum_length = qRound(1000.0 * minimum_length_edit->value()); + symbol->minimum_mid_symbol_count = minimum_mid_symbol_count_edit->value(); + symbol->minimum_mid_symbol_count_when_closed = minimum_mid_symbol_count_when_closed_edit->value(); + emit propertiesModified(); +} + +void LineSymbolSettings::lineCapChanged(int index) +{ + symbol->cap_style = LineSymbol::CapStyle(line_cap_combo->itemData(index).toInt()); + emit propertiesModified(); + updateStates(); +} + +void LineSymbolSettings::lineJoinChanged(int index) +{ + symbol->join_style = LineSymbol::JoinStyle(line_join_combo->itemData(index).toInt()); + emit propertiesModified(); +} + +void LineSymbolSettings::pointedLineCapLengthChanged(double value) +{ + symbol->pointed_cap_length = qRound(1000.0 * value); + emit propertiesModified(); +} + +void LineSymbolSettings::dashedChanged(bool checked) +{ + symbol->dashed = checked; + if (!symbol->color) + { + symbol->break_length = 0; + break_length_edit->setValue(0); + } + emit propertiesModified(); + updateStates(); + if (checked && symbol->color) + ensureWidgetVisible(half_outer_dashes_check); +} + +void LineSymbolSettings::segmentLengthChanged(double value) +{ + symbol->segment_length = qRound(1000.0 * value); + emit propertiesModified(); +} + +void LineSymbolSettings::endLengthChanged(double value) +{ + symbol->end_length = qRound(1000.0 * value); + emit propertiesModified(); + updateStates(); +} + +void LineSymbolSettings::showAtLeastOneSymbolChanged(bool checked) +{ + symbol->show_at_least_one_symbol = checked; + emit propertiesModified(); +} + +void LineSymbolSettings::dashLengthChanged(double value) +{ + symbol->dash_length = qRound(1000.0 * value); + emit propertiesModified(); +} + +void LineSymbolSettings::breakLengthChanged(double value) +{ + symbol->break_length = qRound(1000.0 * value); + emit propertiesModified(); +} + +void LineSymbolSettings::dashGroupsChanged(int index) +{ + symbol->dashes_in_group = dash_group_combo->itemData(index).toInt(); + if (symbol->dashes_in_group > 1) + { + symbol->half_outer_dashes = false; + half_outer_dashes_check->setChecked(false); + } + emit propertiesModified(); + updateStates(); +} + +void LineSymbolSettings::inGroupBreakLengthChanged(double value) +{ + symbol->in_group_break_length = qRound(1000.0 * value); + emit propertiesModified(); +} + +void LineSymbolSettings::halfOuterDashesChanged(bool checked) +{ + symbol->half_outer_dashes = checked; + emit propertiesModified(); +} + +void LineSymbolSettings::midSymbolsPerDashChanged(int value) +{ + symbol->mid_symbols_per_spot = qMax(1, value); + emit propertiesModified(); + updateStates(); +} + +void LineSymbolSettings::midSymbolDistanceChanged(double value) +{ + symbol->mid_symbol_distance = qRound(1000.0 * value); + emit propertiesModified(); +} + +void LineSymbolSettings::borderCheckClicked(bool checked) +{ + symbol->have_border_lines = checked; + emit propertiesModified(); + updateStates(); + if (checked) + { + if (symbol->areBordersDifferent()) + ensureWidgetVisible(right_border_widgets.break_length_edit); + else + ensureWidgetVisible(border_widgets.break_length_edit); + } +} + +void LineSymbolSettings::differentBordersClicked(bool checked) +{ + if (checked) + { + updateStates(); + blockSignalsRecursively(this, true); + updateBorderContents(symbol->getRightBorder(), right_border_widgets); + blockSignalsRecursively(this, false); + ensureWidgetVisible(right_border_widgets.break_length_edit); + } + else + { + symbol->getRightBorder().assign(symbol->getBorder(), nullptr); + emit propertiesModified(); + updateStates(); + } +} + +void LineSymbolSettings::borderChanged() +{ + updateBorder(symbol->getBorder(), border_widgets); + if (different_borders_check->isChecked()) + updateBorder(symbol->getRightBorder(), right_border_widgets); + else + symbol->getRightBorder().assign(symbol->getBorder(), nullptr); + + emit propertiesModified(); +} + +void LineSymbolSettings::suppressDashSymbolClicked(bool checked) +{ + symbol->suppress_dash_symbol_at_ends = checked; + emit propertiesModified(); +} + +void LineSymbolSettings::scaleDashSymbolClicked(bool checked) +{ + symbol->setScaleDashSymbol(checked); + emit propertiesModified(); +} + +void LineSymbolSettings::createBorderWidgets(LineSymbolBorder& border, Map* map, int& row, int col, QGridLayout* layout, BorderWidgets& widgets) +{ + auto width_label = new QLabel(tr("Border width:")); + widgets.width_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + auto color_label = new QLabel(tr("Border color:")); + widgets.color_edit = new ColorDropDown(map, border.color); + + auto shift_label = new QLabel(tr("Border shift:")); + widgets.shift_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + widgets.dashed_check = new QCheckBox(tr("Border is dashed")); + widgets.dashed_check->setChecked(border.dashed); + + widgets.widget_list = { + width_label, widgets.width_edit, + color_label, widgets.color_edit, + shift_label, widgets.shift_edit, + widgets.dashed_check + }; + + row++; col = 0; + layout->addWidget(width_label, row, col++); + layout->addWidget(widgets.width_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(color_label, row, col++); + layout->addWidget(widgets.color_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(shift_label, row, col++); + layout->addWidget(widgets.shift_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(widgets.dashed_check, row, col, 1, -1); + + auto dash_length_label = new QLabel(tr("Border dash length:")); + widgets.dash_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + auto break_length_label = new QLabel(tr("Border break length:")); + widgets.break_length_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + + widgets.dash_widget_list = { + dash_length_label, widgets.dash_length_edit, + break_length_label, widgets.break_length_edit, + }; + + row++; col = 0; + layout->addWidget(dash_length_label, row, col++); + layout->addWidget(widgets.dash_length_edit, row, col, 1, -1); + row++; col = 0; + layout->addWidget(break_length_label, row, col++); + layout->addWidget(widgets.break_length_edit, row, col, 1, -1); + + + connect(widgets.width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::borderChanged); + connect(widgets.color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &LineSymbolSettings::borderChanged); + connect(widgets.shift_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::borderChanged); + connect(widgets.dashed_check, &QAbstractButton::clicked, this, &LineSymbolSettings::borderChanged); + connect(widgets.dash_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::borderChanged); + connect(widgets.break_length_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &LineSymbolSettings::borderChanged); +} + +void LineSymbolSettings::updateBorder(LineSymbolBorder& border, LineSymbolSettings::BorderWidgets& widgets) +{ + bool ensure_last_widget_visible = false; + border.width = qRound(1000.0 * widgets.width_edit->value()); + border.color = widgets.color_edit->color(); + border.shift = qRound(1000.0 * widgets.shift_edit->value()); + if (widgets.dashed_check->isChecked() && !border.dashed) + ensure_last_widget_visible = true; + border.dashed = widgets.dashed_check->isChecked(); + border.dash_length = qRound(1000.0 * widgets.dash_length_edit->value()); + border.break_length = qRound(1000.0 * widgets.break_length_edit->value()); + + updateStates(); + if (ensure_last_widget_visible) + ensureWidgetVisible(widgets.break_length_edit); +} + +void LineSymbolSettings::updateBorderContents(LineSymbolBorder& border, LineSymbolSettings::BorderWidgets& widgets) +{ + Q_ASSERT(this->signalsBlocked()); + + widgets.width_edit->setValue(0.001 * border.width); + widgets.color_edit->setColor(border.color); + widgets.shift_edit->setValue(0.001 * border.shift); + widgets.dashed_check->setChecked(border.dashed); + + widgets.dash_length_edit->setValue(0.001 * border.dash_length); + widgets.break_length_edit->setValue(0.001 * border.break_length); +} + +void LineSymbolSettings::ensureWidgetVisible(QWidget* widget) +{ + widget_to_ensure_visible = widget; + QTimer::singleShot(0, this, SLOT(ensureWidgetVisible())); // clazy:exclude=old-style-connect (needs Qt 5.4) +} + +void LineSymbolSettings::ensureWidgetVisible() +{ + scroll_area->ensureWidgetVisible(widget_to_ensure_visible, 5, 5); +} + +void LineSymbolSettings::updateStates() +{ + const bool symbol_active = symbol->line_width > 0; + color_edit->setEnabled(symbol_active); + + const bool line_active = symbol_active && symbol->color; + for (auto line_settings_widget : line_settings_list) + { + line_settings_widget->setEnabled(line_active); + } + if (line_active && symbol->cap_style != LineSymbol::PointedCap) + { + pointed_cap_length_label->setEnabled(false); + pointed_cap_length_edit->setEnabled(false); + } + + const bool line_dashed = symbol->dashed && symbol->color; + if (line_dashed) + { + for (auto undashed_widget : undashed_widget_list) + { + undashed_widget->setVisible(false); + } + for (auto dashed_widget : dashed_widget_list) + { + dashed_widget->setVisible(true); + dashed_widget->setEnabled(line_active); + } + in_group_break_length_label->setEnabled(line_active && symbol->dashes_in_group > 1); + in_group_break_length_edit->setEnabled(line_active && symbol->dashes_in_group > 1); + half_outer_dashes_check->setEnabled(line_active && symbol->dashes_in_group == 1); + } + else + { + for (auto undashed_widget : undashed_widget_list) + { + undashed_widget->setVisible(true); + undashed_widget->setEnabled(!symbol->mid_symbol->isEmpty()); + } + show_at_least_one_symbol_check->setEnabled(show_at_least_one_symbol_check->isEnabled() && symbol->end_length > 0); + for (auto dashed_widget : dashed_widget_list) + { + dashed_widget->setVisible(false); + } + } + + for (auto mid_symbol_widget : mid_symbol_widget_list) + { + mid_symbol_widget->setEnabled(!symbol->mid_symbol->isEmpty()); + } + mid_symbol_distance_label->setEnabled(mid_symbol_distance_label->isEnabled() && symbol->mid_symbols_per_spot > 1); + mid_symbol_distance_edit->setEnabled(mid_symbol_distance_edit->isEnabled() && symbol->mid_symbols_per_spot > 1); + + const bool border_active = symbol_active && symbol->have_border_lines; + for (auto border_widget : border_widget_list) + { + border_widget->setVisible(border_active); + border_widget->setEnabled(border_active); + } + for (auto border_dash_widget : border_widgets.dash_widget_list) + { + border_dash_widget->setVisible(border_active); + border_dash_widget->setEnabled(border_active && symbol->getBorder().dashed); + } + const bool different_borders = border_active && different_borders_check->isChecked(); + for (auto different_border_widget : different_borders_widget_list) + { + different_border_widget->setVisible(different_borders); + different_border_widget->setEnabled(different_borders); + } + for (auto border_dash_widget : right_border_widgets.dash_widget_list) + { + border_dash_widget->setVisible(different_borders); + border_dash_widget->setEnabled(different_borders && symbol->getRightBorder().dashed); + } +} + +void LineSymbolSettings::updateContents() +{ + blockSignalsRecursively(this, true); + width_edit->setValue(0.001 * symbol->getLineWidth()); + color_edit->setColor(symbol->getColor()); + + minimum_length_edit->setValue(0.001 * symbol->minimum_length); + line_cap_combo->setCurrentIndex(line_cap_combo->findData(symbol->cap_style)); + pointed_cap_length_edit->setValue(0.001 * symbol->pointed_cap_length); + line_join_combo->setCurrentIndex(line_join_combo->findData(symbol->join_style)); + dashed_check->setChecked(symbol->dashed); + border_check->setChecked(symbol->have_border_lines); + different_borders_check->setChecked(symbol->areBordersDifferent()); + + dash_length_edit->setValue(0.001 * symbol->dash_length); + break_length_edit->setValue(0.001 * symbol->break_length); + dash_group_combo->setCurrentIndex(dash_group_combo->findData(QVariant(symbol->dashes_in_group))); + in_group_break_length_edit->setValue(0.001 * symbol->in_group_break_length); + half_outer_dashes_check->setChecked(symbol->half_outer_dashes); + + mid_symbol_per_spot_edit->setValue(symbol->mid_symbols_per_spot); + mid_symbol_distance_edit->setValue(0.001 * symbol->mid_symbol_distance); + + segment_length_edit->setValue(0.001 * symbol->segment_length); + end_length_edit->setValue(0.001 * symbol->end_length); + show_at_least_one_symbol_check->setChecked(symbol->show_at_least_one_symbol); + minimum_mid_symbol_count_edit->setValue(symbol->minimum_mid_symbol_count); + minimum_mid_symbol_count_when_closed_edit->setValue(symbol->minimum_mid_symbol_count_when_closed); + + updateBorderContents(symbol->getBorder(), border_widgets); + updateBorderContents(symbol->getRightBorder(), right_border_widgets); + + supress_dash_symbol_check->setChecked(symbol->getSuppressDashSymbolAtLineEnds()); + scale_dash_symbol_check->setChecked(symbol->getScaleDashSymbol()); + + blockSignalsRecursively(this, false); +/* + PointSymbolEditorWidget* point_symbol_editor = nullptr; + auto controller = dialog->getPreviewController(); + + QList point_symbols; + point_symbols << symbol->getStartSymbol() << symbol->getMidSymbol() << symbol->getEndSymbol() << symbol->getDashSymbol(); + for (auto point_symbol : point_symbols) + { + point_symbol_editor = new PointSymbolEditorWidget(controller, point_symbol, 16); + addPropertiesGroup(point_symbol->getName(), point_symbol_editor); + connect(point_symbol_editor, SIGNAL(symbolEdited()), this, SLOT(pointSymbolEdited())); + } */ + + updateStates(); +} + +void LineSymbolSettings::reset(Symbol* symbol) +{ + Q_ASSERT(symbol->getType() == Symbol::Line); + + SymbolPropertiesWidget::reset(symbol); + + auto old_symbol = this->symbol; + this->symbol = reinterpret_cast(symbol); + + PointSymbolEditorWidget* point_symbol_editor = nullptr; + auto controller = dialog->getPreviewController(); + + int current = currentIndex(); + setUpdatesEnabled(false); + this->symbol->ensurePointSymbols(tr("Start symbol"), tr("Mid symbol"), tr("End symbol"), tr("Dash symbol")); + for (auto point_symbol : { this->symbol->getStartSymbol(), this->symbol->getMidSymbol(), this->symbol->getEndSymbol(), this->symbol->getDashSymbol() }) + { + point_symbol_editor = new PointSymbolEditorWidget(controller, point_symbol, 16); + connect(point_symbol_editor, &PointSymbolEditorWidget::symbolEdited, this, &LineSymbolSettings::pointSymbolEdited); + + int index = indexOfPropertiesGroup(point_symbol->getName()); // existing symbol editor + removePropertiesGroup(index); + insertPropertiesGroup(index, point_symbol->getName(), point_symbol_editor); + if (index == current) + setCurrentIndex(current); + } + updateContents(); + setUpdatesEnabled(true); + old_symbol->cleanupPointSymbols(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/line_symbol_settings.h b/src/gui/symbols/line_symbol_settings.h new file mode 100644 index 0000000..313c437 --- /dev/null +++ b/src/gui/symbols/line_symbol_settings.h @@ -0,0 +1,195 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_LINE_SYMBOL_SETTINGS_H +#define OPENORIENTEERING_LINE_SYMBOL_SETTINGS_H + +#include + +#include + +#include "gui/symbols/symbol_properties_widget.h" + +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QGridLayout; +class QLabel; +class QScrollArea; +class QSpinBox; +class QWidget; + +namespace OpenOrienteering { + +class ColorDropDown; +class LineSymbol; +struct LineSymbolBorder; +class Map; +class Symbol; +class SymbolSettingDialog; + + +class LineSymbolSettings : public SymbolPropertiesWidget +{ +Q_OBJECT +public: + LineSymbolSettings(LineSymbol* symbol, SymbolSettingDialog* dialog); + + ~LineSymbolSettings() override; + + void reset(Symbol* symbol) override; + + /** + * Updates the content and state of all input fields. + */ + void updateContents(); + +protected: + struct BorderWidgets + { + std::vector widget_list; + QDoubleSpinBox* width_edit; + ColorDropDown* color_edit; + QDoubleSpinBox* shift_edit; + QCheckBox* dashed_check; + + std::vector dash_widget_list; + QDoubleSpinBox* dash_length_edit; + QDoubleSpinBox* break_length_edit; + }; + + /** + * Creates the widgets for one border. + */ + void createBorderWidgets(LineSymbolBorder& border, Map* map, int& row, int col, QGridLayout* layout, BorderWidgets& widgets); + + /** + * Updates the border settings from the values in the widgets. + */ + void updateBorder(LineSymbolBorder& border, BorderWidgets& widgets); + + void updateBorderContents(LineSymbolBorder& border, BorderWidgets& widgets); + + /** + * Ensures that a particular widget is visible in the scoll area. + */ + void ensureWidgetVisible(QWidget* widget); + + /** + * Adjusts the visibility and enabled state of all UI parts. + * There is a large number of dependencies between different elements + * of the line settings. This method handles them all. + */ + void updateStates(); + +protected slots: + /** + * Notifies this settings widget that one of the symbols has been modified. + */ + void pointSymbolEdited(); + + void widthChanged(double value); + void colorChanged(); + void minimumDimensionsEdited(); + void lineCapChanged(int index); + void lineJoinChanged(int index); + void pointedLineCapLengthChanged(double value); + void dashedChanged(bool checked); + void segmentLengthChanged(double value); + void endLengthChanged(double value); + void showAtLeastOneSymbolChanged(bool checked); + void dashLengthChanged(double value); + void breakLengthChanged(double value); + void dashGroupsChanged(int index); + void inGroupBreakLengthChanged(double value); + void halfOuterDashesChanged(bool checked); + void midSymbolsPerDashChanged(int value); + void midSymbolDistanceChanged(double value); + void borderCheckClicked(bool checked); + void differentBordersClicked(bool checked); + void borderChanged(); + void suppressDashSymbolClicked(bool checked); + void scaleDashSymbolClicked(bool checked); + +private slots: + /** Ensure that a predetermined widget is visible in the scoll area. + * The widget is set in advance by ensureWidgetVisible(QWidget* widget). + */ + void ensureWidgetVisible(); + +private: + LineSymbol* symbol; + SymbolSettingDialog* dialog; + + QDoubleSpinBox* width_edit; + ColorDropDown* color_edit; + QDoubleSpinBox* minimum_length_edit; + + // enabled if line_width > 0 && color + std::vector line_settings_list; + QComboBox* line_cap_combo; + QComboBox* line_join_combo; + QLabel* pointed_cap_length_label; + QDoubleSpinBox* pointed_cap_length_edit; + QCheckBox* dashed_check; + + // dashed == false && mid_symbol + std::vector undashed_widget_list; + QDoubleSpinBox* segment_length_edit; + QDoubleSpinBox* end_length_edit; + QCheckBox* show_at_least_one_symbol_check; + QSpinBox* minimum_mid_symbol_count_edit; + QSpinBox* minimum_mid_symbol_count_when_closed_edit; + + // dashed == true + std::vector dashed_widget_list; + QDoubleSpinBox* dash_length_edit; + QDoubleSpinBox* break_length_edit; + QComboBox* dash_group_combo; + QLabel* in_group_break_length_label; + QDoubleSpinBox* in_group_break_length_edit; + QCheckBox* half_outer_dashes_check; + + // mid_symbol + std::vector mid_symbol_widget_list; + QSpinBox* mid_symbol_per_spot_edit; + QLabel* mid_symbol_distance_label; + QDoubleSpinBox* mid_symbol_distance_edit; + + // enabled if line_width > 0 + std::vector border_widget_list; + QCheckBox* border_check; + QCheckBox* different_borders_check; + std::vector different_borders_widget_list; + BorderWidgets border_widgets; + BorderWidgets right_border_widgets; + + QCheckBox* supress_dash_symbol_check; + QCheckBox* scale_dash_symbol_check; + + QScrollArea* scroll_area; + QWidget* widget_to_ensure_visible; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/symbols/point_symbol_editor_widget.cpp b/src/gui/symbols/point_symbol_editor_widget.cpp new file mode 100644 index 0000000..897e6e7 --- /dev/null +++ b/src/gui/symbols/point_symbol_editor_widget.cpp @@ -0,0 +1,1057 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "point_symbol_editor_widget.h" + +#include +#include +#include +// IWYU pragma: no_include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/objects/object.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "gui/modifier_key.h" +#include "gui/map/map_editor.h" +#include "gui/map/map_widget.h" +#include "gui/util_gui.h" +#include "gui/widgets/color_dropdown.h" +#include "util/backports.h" + +// IWYU pragma: no_forward_declare QBoxLayout +// IWYU pragma: no_forward_declare QGridLayout +// IWYU pragma: no_forward_declare QHBoxLayout +// IWYU pragma: no_forward_declare QMenu +// IWYU pragma: no_forward_declare QTableWidgetItem +// IWYU pragma: no_forward_declare QVBoxLayout + + +namespace OpenOrienteering { + +PointSymbolEditorWidget::PointSymbolEditorWidget(MapEditorController* controller, PointSymbol* symbol, qreal offset_y, bool permanent_preview, QWidget* parent) +: QWidget(parent) +, symbol(symbol) +, object_origin_coord(0, offset_y) +, offset_y(offset_y) +, controller(controller) +, permanent_preview(permanent_preview) +{ + map = controller->getMap(); + + midpoint_object = nullptr; + if (permanent_preview) + { + midpoint_object = new PointObject(symbol); + midpoint_object->setPosition(object_origin_coord); + map->addObject(midpoint_object); + } + + oriented_to_north = new QCheckBox(tr("Always oriented to north (not rotatable)")); + oriented_to_north->setChecked(!symbol->rotatable); + + QLabel* elements_label = Util::Headline::create(tr("Elements")); + element_list = new QListWidget(); + initElementList(); + + delete_element_button = new QPushButton(QIcon(QString::fromLatin1(":/images/minus.png")), QString{}); + QToolButton* add_element_button = new QToolButton(); + add_element_button->setIcon(QIcon(QString::fromLatin1(":/images/plus.png"))); + add_element_button->setToolButtonStyle(Qt::ToolButtonIconOnly); + add_element_button->setPopupMode(QToolButton::InstantPopup); + add_element_button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + add_element_button->setMinimumSize(delete_element_button->sizeHint()); + QMenu* add_element_button_menu = new QMenu(add_element_button); + add_element_button_menu->addAction(tr("Point"), this, SLOT(addPointClicked())); + add_element_button_menu->addAction(tr("Line"), this, SLOT(addLineClicked())); + add_element_button_menu->addAction(tr("Area"), this, SLOT(addAreaClicked())); + add_element_button->setMenu(add_element_button_menu); + + center_all_elements_button = new QPushButton(tr("Center all elements")); + + QLabel* current_element_label = Util::Headline::create(tr("Current element")); + + element_properties_widget = new QStackedWidget(); + + // Point (circle) + point_properties = new QWidget(); + QLabel* point_inner_radius_label = new QLabel(tr("Diameter a:")); + point_inner_radius_edit = Util::SpinBox::create(2, 0.0, 99999.9, tr("mm")); + + QLabel* point_inner_color_label = new QLabel(tr("Inner color:")); + point_inner_color_edit = new ColorDropDown(map); + + QLabel* point_outer_width_label = new QLabel(tr("Outer width b:")); + point_outer_width_edit = Util::SpinBox::create(2, 0.0, 99999.9, tr("mm")); + + QLabel* point_outer_color_label = new QLabel(tr("Outer color:")); + point_outer_color_edit = new ColorDropDown(map); + + QLabel* explanation_label = new QLabel(); + explanation_label->setPixmap(QPixmap(QString::fromLatin1(":/images/symbol_point_explanation.png"))); + + QGridLayout* point_layout = new QGridLayout(); + point_layout->setContentsMargins(0, 0, 0, 0); + point_layout->addWidget(point_inner_radius_label, 0, 0); + point_layout->addWidget(point_inner_radius_edit, 0, 1); + point_layout->addWidget(point_inner_color_label, 1, 0); + point_layout->addWidget(point_inner_color_edit, 1, 1); + point_layout->addWidget(new QWidget(), 2, 0, 1, -1); + point_layout->addWidget(point_outer_width_label, 3, 0); + point_layout->addWidget(point_outer_width_edit, 3, 1); + point_layout->addWidget(point_outer_color_label, 4, 0); + point_layout->addWidget(point_outer_color_edit, 4, 1); + point_layout->addWidget(explanation_label, 0, 2, 5, 1); + point_layout->setRowStretch(6, 1); + point_layout->setColumnStretch(1,1); + point_properties->setLayout(point_layout); + element_properties_widget->addWidget(point_properties); + + // Line + line_properties = new QWidget(); + QLabel* line_width_label = new QLabel(tr("Line width:")); + line_width_edit = Util::SpinBox::create(3, 0.0, 99999.9, tr("mm")); + + QLabel* line_color_label = new QLabel(tr("Line color:")); + line_color_edit = new ColorDropDown(map); + + QLabel* line_cap_label = new QLabel(tr("Line cap:")); + line_cap_edit = new QComboBox(); + line_cap_edit->addItem(tr("flat"), QVariant(LineSymbol::FlatCap)); + line_cap_edit->addItem(tr("round"), QVariant(LineSymbol::RoundCap)); + line_cap_edit->addItem(tr("square"), QVariant(LineSymbol::SquareCap)); + //line_cap_edit->addItem(tr("pointed"), QVariant(LineSymbol::PointedCap)); // this would require another input field for the cap length + + QLabel* line_join_label = new QLabel(tr("Line join:")); + line_join_edit = new QComboBox(); + line_join_edit->addItem(tr("miter"), QVariant(LineSymbol::MiterJoin)); + line_join_edit->addItem(tr("round"), QVariant(LineSymbol::RoundJoin)); + line_join_edit->addItem(tr("bevel"), QVariant(LineSymbol::BevelJoin)); + + line_closed_check = new QCheckBox(tr("Line closed")); + + QGridLayout* line_layout = new QGridLayout(); + line_layout->setContentsMargins(0, 0, 0, 0); + line_layout->addWidget(line_width_label, 0, 0); + line_layout->addWidget(line_width_edit, 0, 1); + line_layout->addWidget(line_color_label, 1, 0); + line_layout->addWidget(line_color_edit, 1, 1); + line_layout->addWidget(line_cap_label, 2, 0); + line_layout->addWidget(line_cap_edit, 2, 1); + line_layout->addWidget(line_join_label, 3, 0); + line_layout->addWidget(line_join_edit, 3, 1); + line_layout->addWidget(line_closed_check, 4, 0, 1, 2); + line_layout->setRowStretch(5, 1); + line_layout->setColumnStretch(1,1); + line_properties->setLayout(line_layout); + element_properties_widget->addWidget(line_properties); + + // Area + area_properties = new QWidget(); + QLabel* area_color_label = new QLabel(tr("Area color:")); + area_color_edit = new ColorDropDown(map); + + QGridLayout* area_layout = new QGridLayout(); + area_layout->setContentsMargins(0, 0, 0, 0); + area_layout->addWidget(area_color_label, 0, 0); + area_layout->addWidget(area_color_edit, 0, 1); + area_layout->setRowStretch(1, 1); + area_layout->setColumnStretch(1,1); + area_properties->setLayout(area_layout); + element_properties_widget->addWidget(area_properties); + + // Coordinates + coords_label = new QLabel(tr("Coordinates:")); + + coords_table = new QTableWidget(0, 3); + coords_table->setEditTriggers(QAbstractItemView::AllEditTriggers); + coords_table->setSelectionBehavior(QAbstractItemView::SelectRows); + coords_table->setHorizontalHeaderLabels(QStringList() << tr("X") << tr("Y") << tr("Curve start")); + coords_table->verticalHeader()->setVisible(false); + + QHeaderView* header_view = coords_table->horizontalHeader(); + header_view->setSectionResizeMode(0, QHeaderView::Interactive); + header_view->setSectionResizeMode(1, QHeaderView::Interactive); + header_view->setSectionResizeMode(2, QHeaderView::ResizeToContents); + header_view->setSectionsClickable(false); + + add_coord_button = new QPushButton(QIcon(QString::fromLatin1(":/images/plus.png")), QString{}); + delete_coord_button = new QPushButton(QIcon(QString::fromLatin1(":/images/minus.png")), QString{}); + center_coords_button = new QPushButton(tr("Center by coordinate average")); + + // Layout + QBoxLayout* left_layout = new QVBoxLayout(); + + left_layout->addWidget(elements_label); + left_layout->addWidget(element_list, 1); + QGridLayout* element_buttons_layout = new QGridLayout(); + element_buttons_layout->setColumnStretch(0, 1); + element_buttons_layout->addWidget(add_element_button, 1, 2); + element_buttons_layout->addWidget(delete_element_button, 1, 3); + element_buttons_layout->addWidget(center_all_elements_button, 2, 1, 1, 4); + element_buttons_layout->setColumnStretch(5, 1); + left_layout->addLayout(element_buttons_layout); + + QBoxLayout* right_layout = new QVBoxLayout(); + right_layout->setMargin(0); + + right_layout->addWidget(current_element_label); + right_layout->addWidget(element_properties_widget); + right_layout->addSpacing(16); + + right_layout->addWidget(coords_label); + right_layout->addWidget(coords_table); + QHBoxLayout* coords_buttons_layout = new QHBoxLayout(); + coords_buttons_layout->addWidget(add_coord_button); + coords_buttons_layout->addWidget(delete_coord_button); + coords_buttons_layout->addStretch(1); + coords_buttons_layout->addWidget(center_coords_button); + right_layout->addLayout(coords_buttons_layout); + + QBoxLayout* columns_layout = new QHBoxLayout; + columns_layout->addLayout(left_layout); + columns_layout->addSpacing(16); + columns_layout->addLayout(right_layout); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget(oriented_to_north); + layout->addSpacerItem(Util::SpacerItem::create(this)); + layout->addLayout(columns_layout); + setLayout(layout); + + // Connections + connect(oriented_to_north, &QCheckBox::clicked, this, &PointSymbolEditorWidget::orientedToNorthClicked); + + connect(element_list, &QListWidget::currentRowChanged, this, &PointSymbolEditorWidget::changeElement); + connect(delete_element_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::deleteCurrentElement); + connect(center_all_elements_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::centerAllElements); + + connect(point_inner_radius_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PointSymbolEditorWidget::pointInnerRadiusChanged); + connect(point_inner_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::pointInnerColorChanged); + connect(point_outer_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PointSymbolEditorWidget::pointOuterWidthChanged); + connect(point_outer_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::pointOuterColorChanged); + + connect(line_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &PointSymbolEditorWidget::lineWidthChanged); + connect(line_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::lineColorChanged); + connect(line_cap_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::lineCapChanged); + connect(line_join_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::lineJoinChanged); + connect(line_closed_check, &QCheckBox::clicked, this, &PointSymbolEditorWidget::lineClosedClicked); + + connect(area_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &PointSymbolEditorWidget::areaColorChanged); + + connect(coords_table, &QTableWidget::currentCellChanged, this, &PointSymbolEditorWidget::updateDeleteCoordButton); + connect(coords_table, &QTableWidget::cellChanged, this, &PointSymbolEditorWidget::coordinateChanged); + connect(add_coord_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::addCoordClicked); + connect(delete_coord_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::deleteCoordClicked); + connect(center_coords_button, &QPushButton::clicked, this, &PointSymbolEditorWidget::centerCoordsClicked); +} + +PointSymbolEditorWidget::~PointSymbolEditorWidget() +{ + if (isVisible()) + setEditorActive(false); + if (permanent_preview) + map->deleteObject(midpoint_object, false); +} + +void PointSymbolEditorWidget::setEditorActive(bool active) +{ + if (active) + { + if (!permanent_preview && !midpoint_object) + { + midpoint_object = new PointObject(symbol); + midpoint_object->setPosition(object_origin_coord); + map->addObject(midpoint_object); + } + map->updateAllObjectsWithSymbol(symbol); + controller->setTool(new PointSymbolEditorTool(controller, this)); + activity = new PointSymbolEditorActivity(map, this); + controller->setEditorActivity(activity); + changeElement(element_list->currentRow()); + } + else + { + controller->setTool(nullptr); + controller->setEditorActivity(nullptr); + if (!permanent_preview && midpoint_object) + { + map->deleteObject(midpoint_object, false); + midpoint_object = nullptr; + } + } +} + +void PointSymbolEditorWidget::setVisible(bool visible) +{ + setEditorActive(visible); + QWidget::setVisible(visible); +} + +bool PointSymbolEditorWidget::changeCurrentCoordinate(MapCoordF new_coord) +{ + Object* object = getCurrentElementObject(); + if (object == midpoint_object) + return false; + + if (object->getType() == Object::Point) + { + auto point = static_cast(object); + MapCoordF coord = point->getCoordF(); + coord.setX(new_coord.x()); + coord.setY(new_coord.y() - offset_y); + point->setPosition(coord); + } + else + { + auto table_row = coords_table->currentRow(); + if (table_row < 0) + return false; + + PathObject* path = object->asPath(); + + auto coord_index = MapCoordVector::size_type(table_row); + Q_ASSERT(coord_index < path->getCoordinateCount()); + + MapCoord coord = path->getCoordinate(coord_index); + coord.setX(new_coord.x()); + coord.setY(new_coord.y() - offset_y); + path->setCoordinate(coord_index, coord); + } + + updateCoordsTable(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); + return true; +} + +bool PointSymbolEditorWidget::addCoordinate(MapCoordF new_coord) +{ + Object* object = getCurrentElementObject(); + if (object == midpoint_object) + return false; + + if (object->getType() == Object::Point) + return changeCurrentCoordinate(new_coord); + Q_ASSERT(object->getType() == Object::Path); + auto path = static_cast(object); + + auto table_row = coords_table->currentRow(); + if (table_row < 0) + table_row = coords_table->rowCount(); + else + ++table_row; + + auto coord_index = MapCoordVector::size_type(table_row); + path->addCoordinate(coord_index, { new_coord.x(), new_coord.y() - offset_y }); + + if (path->getCoordinateCount() == 1) + { + if (object->getSymbol()->getType() == Symbol::Area) + path->parts().front().setClosed(true, false); + } + + updateCoordsTable(); + coords_table->setCurrentItem(coords_table->item(table_row, (coords_table->currentColumn() < 0) ? 0 : coords_table->currentColumn())); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); + return true; +} + +void PointSymbolEditorWidget::initElementList() +{ + element_list->clear(); + element_list->addItem(tr("[Midpoint]")); // NOTE: Is that item needed? + for (int i = 0; i < symbol->getNumElements(); ++i) + { + Symbol* element_symbol = symbol->getElementSymbol(i); + element_list->addItem(getLabelForSymbol(element_symbol)); + } + element_list->setCurrentRow(0); +} + +void PointSymbolEditorWidget::orientedToNorthClicked(bool checked) +{ + symbol->rotatable = !checked; + emit symbolEdited(); +} + +void PointSymbolEditorWidget::changeElement(int row) +{ + delete_element_button->setEnabled(row > 0); // must not remove first row + center_all_elements_button->setEnabled(symbol->getNumElements() > 0); + + if (row >= 0) + { + Symbol* element_symbol = getCurrentElementSymbol(); + Object* object = getCurrentElementObject(); + + if (object->getType() == Object::Path) + { + auto path = static_cast(object); + if (element_symbol->getType() == Symbol::Line) + { + line_width_edit->blockSignals(true); + line_color_edit->blockSignals(true); + line_cap_edit->blockSignals(true); + line_join_edit->blockSignals(true); + line_closed_check->blockSignals(true); + + auto line = static_cast(element_symbol); + line_width_edit->setValue(0.001 * line->getLineWidth()); + line_color_edit->setColor(line->getColor()); + line_cap_edit->setCurrentIndex(line_cap_edit->findData(QVariant(line->getCapStyle()))); + line_join_edit->setCurrentIndex(line_join_edit->findData(QVariant(line->getJoinStyle()))); + + const auto& parts = path->parts(); + line_closed_check->setChecked(!parts.empty() && parts.front().isClosed()); + line_closed_check->setEnabled(!parts.empty()); + + line_width_edit->blockSignals(false); + line_color_edit->blockSignals(false); + line_cap_edit->blockSignals(false); + line_join_edit->blockSignals(false); + line_closed_check->blockSignals(false); + + element_properties_widget->setCurrentWidget(line_properties); + } + else if (element_symbol->getType() == Symbol::Area) + { + area_color_edit->blockSignals(true); + + auto area = static_cast(element_symbol); + area_color_edit->setColor(area->getColor()); + + area_color_edit->blockSignals(false); + + element_properties_widget->setCurrentWidget(area_properties); + } + + coords_label->setEnabled(true); + coords_table->setEnabled(true); + coords_table->setColumnHidden(2, false); + add_coord_button->setEnabled(true); + center_coords_button->setEnabled(path->getCoordinateCount() > 0); + } + else + { + point_inner_radius_edit->blockSignals(true); + point_inner_color_edit->blockSignals(true); + point_outer_width_edit->blockSignals(true); + point_outer_width_edit->blockSignals(true); + + auto point = static_cast(element_symbol); + point_inner_radius_edit->setValue(2 * 0.001 * point->getInnerRadius()); + point_inner_color_edit->setColor(point->getInnerColor()); + point_outer_width_edit->setValue(0.001 * point->getOuterWidth()); + point_outer_color_edit->setColor(point->getOuterColor()); + + point_inner_radius_edit->blockSignals(false); + point_inner_color_edit->blockSignals(false); + point_outer_width_edit->blockSignals(false); + point_outer_width_edit->blockSignals(false); + + element_properties_widget->setCurrentWidget(point_properties); + + coords_table->setColumnHidden(2, true); + coords_label->setEnabled(row > 0); + coords_table->setEnabled(row > 0); + add_coord_button->setEnabled(false); + center_coords_button->setEnabled(row > 0); + } + + coords_table->clearContents(); + } + + if (row > 0) + updateCoordsTable(); + else + coords_table->setRowCount(0); + + delete_coord_button->setEnabled(false); +} + +void PointSymbolEditorWidget::addPointClicked() +{ + PointSymbol* new_point = new PointSymbol(); + PointObject* new_object = new PointObject(new_point); + + insertElement(new_object, new_point); +} + +void PointSymbolEditorWidget::addLineClicked() +{ + LineSymbol* new_line = new LineSymbol(); + PathObject* new_object = new PathObject(new_line); + + insertElement(new_object, new_line); +} + +void PointSymbolEditorWidget::addAreaClicked() +{ + AreaSymbol* new_area = new AreaSymbol(); + PathObject* new_object = new PathObject(new_area); + + insertElement(new_object, new_area); +} + +void PointSymbolEditorWidget::deleteCurrentElement() +{ + int row = element_list->currentRow(); + Q_ASSERT(row > 0); + delete element_list->item(row); + symbol->deleteElement(row - 1); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::centerAllElements() +{ + bool has_coordinates = false; + auto min_x = std::numeric_limits::max(); + auto max_x = std::numeric_limits::min(); + auto min_y = std::numeric_limits::max(); + auto max_y = std::numeric_limits::min(); + + for (int i = 0; i < symbol->getNumElements(); ++i) + { + Object* object = symbol->getElementObject(i); + for (const auto& coord : object->getRawCoordinateVector()) + { + min_x = std::min(min_x, coord.nativeX()); + min_y = std::min(min_y, coord.nativeY()); + max_x = std::max(max_x, coord.nativeX()); + max_y = std::max(max_y, coord.nativeY()); + has_coordinates = true; + } + } + + if (has_coordinates) + { + auto center_x = (min_x + max_x) / 2; + auto center_y = (min_y + max_y) / 2; + + for (int i = 0; i < symbol->getNumElements(); ++i) + { + auto object = symbol->getElementObject(i); + object->move(-center_x, -center_y); + object->update(); + } + } + + if (element_list->currentRow() > 0) + updateCoordsTable(); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::pointInnerRadiusChanged(double value) +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->inner_radius = qRound(1000 * 0.5 * value); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::pointInnerColorChanged() +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->inner_color = point_inner_color_edit->color(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::pointOuterWidthChanged(double value) +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->outer_width = qRound(1000 * value); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::pointOuterColorChanged() +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->outer_color = point_outer_color_edit->color(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::lineWidthChanged(double value) +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->line_width = qRound(1000 * value); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::lineColorChanged() +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->color = line_color_edit->color(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::lineCapChanged(int index) +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->cap_style = static_cast(line_cap_edit->itemData(index).toInt()); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::lineJoinChanged(int index) +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->join_style = static_cast(line_join_edit->itemData(index).toInt()); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::lineClosedClicked(bool checked) +{ + Object* object = getCurrentElementObject(); + Q_ASSERT(object->getType() == Object::Path); + auto path = static_cast(object); + + if (!checked && path->getCoordinateCount() >= 4 && path->getCoordinate(path->getCoordinateCount() - 4).isCurveStart()) + path->getCoordinate(path->getCoordinateCount() - 4).setCurveStart(false); + + Q_ASSERT(!path->parts().empty()); + path->parts().front().setClosed(checked, true); + if (!checked) + path->deleteCoordinate(path->getCoordinateCount() - 1, false); + + updateCoordsTable(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::areaColorChanged() +{ + auto symbol = static_cast(getCurrentElementSymbol()); + symbol->color = area_color_edit->color(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::coordinateChanged(int row, int column) +{ + Object* object = getCurrentElementObject(); + if (!object || !midpoint_object) + return; + + auto coord_index = MapCoordVector::size_type(row); + + if (column < 2) + { + auto coord = MapCoord{}; + if (object->getType() == Object::Point) + { + auto point = static_cast(object); + coord = point->getCoord(); + } + else if (object->getType() == Object::Path) + { + auto path = static_cast(object); + Q_ASSERT(coord_index < path->getCoordinateCount()); + coord = path->getCoordinate(coord_index); + } + + QLocale locale; + auto ok = false; + auto new_value = qRound(1000 * locale.toDouble(coords_table->item(row, column)->text(), &ok)); + + if (ok) + { + if (column == 0) + coord.setNativeX(new_value); + else + coord.setNativeY(-new_value); + + if (object->getType() == Object::Point) + { + auto point = static_cast(object); + point->setPosition(coord); + } + else if (object->getType() == Object::Path) + { + auto path = static_cast(object); + path->setCoordinate(coord_index, coord); + } + + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); + } + + // Update, needed in cases of rounding and error + coords_table->blockSignals(true); + coords_table->item(row, column)->setText(locale.toString((column == 0) ? coord.x() : -coord.y(), 'f', 3)); + coords_table->blockSignals(false); + } + else + { + Q_ASSERT(object->getType() == Object::Path); + auto path = static_cast(object); + Q_ASSERT(coord_index < path->getCoordinateCount()); + MapCoord coord = path->getCoordinate(coord_index); + coord.setCurveStart(coords_table->item(row, column)->checkState() == Qt::Checked); + path->setCoordinate(coord_index, coord); + + updateCoordsTable(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); + } +} + +void PointSymbolEditorWidget::addCoordClicked() +{ + Object* object = getCurrentElementObject(); + Q_ASSERT(object->getType() == Object::Path); + auto path = static_cast(object); + + if (coords_table->currentRow() < 0) + path->addCoordinate(MapCoordVector::size_type(coords_table->rowCount()), MapCoord(0, 0)); + else + path->addCoordinate(MapCoordVector::size_type(coords_table->currentRow()) + 1, path->getCoordinate(MapCoordVector::size_type(coords_table->currentRow()))); + + int row = (coords_table->currentRow() < 0) ? coords_table->rowCount() : (coords_table->currentRow() + 1); + updateCoordsTable(); // NOTE: incremental updates (to the curve start boxes) would be possible but mean some implementation effort + coords_table->setCurrentItem(coords_table->item(row, coords_table->currentColumn())); +} + +void PointSymbolEditorWidget::deleteCoordClicked() +{ + Object* object = getCurrentElementObject(); + Q_ASSERT(object->getType() == Object::Path); + auto path = static_cast(object); + + int row = coords_table->currentRow(); + if (row < 0) + return; + + path->deleteCoordinate(MapCoordVector::size_type(row), false); + + updateCoordsTable(); // NOTE: incremental updates (to the curve start boxes) would be possible but mean some implementation effort + center_coords_button->setEnabled(path->getCoordinateCount() > 0); + updateDeleteCoordButton(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::centerCoordsClicked() +{ + Object* object = getCurrentElementObject(); + + if (object->getType() == Object::Point) + { + auto point = static_cast(object); + point->setPosition(0, 0); + } + else + { + Q_ASSERT(object->getType() == Object::Path); + auto path = static_cast(object); + + MapCoordF center = MapCoordF(0, 0); + auto size = path->getCoordinateCount(); + auto change_size = (!path->parts().empty() && path->parts().front().isClosed()) ? (size - 1) : size; + + Q_ASSERT(change_size > 0); + for (auto i = 0u; i < change_size; ++i) + center = MapCoordF(center.x() + path->getCoordinate(i).x(), center.y() + path->getCoordinate(i).y()); + center = MapCoordF(center.x() / change_size, center.y() / change_size); + + path->move(MapCoord{-center}); + path->update(); + } + + updateCoordsTable(); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +void PointSymbolEditorWidget::updateCoordsTable() +{ + Object* object = getCurrentElementObject(); + int num_rows; + if (object->getType() == Object::Point) + num_rows = 1; + else + { + auto path = static_cast(object); + num_rows = int(path->getCoordinateCount()); + if (num_rows > 0 && path->parts().front().isClosed()) + --num_rows; + if (path->getSymbol()->getType() == Symbol::Line) + line_closed_check->setEnabled(num_rows > 0); + } + + coords_table->setRowCount(num_rows); + for (int i = 0; i < num_rows; ++i) + addCoordsRow(i); + + center_coords_button->setEnabled(num_rows > 0); +} + +void PointSymbolEditorWidget::addCoordsRow(int row) +{ + coords_table->setRowHeight(row, coords_table->fontMetrics().height() + 2); + + coords_table->blockSignals(true); + + for (int c = 0; c < 3; ++c) + { + if (!coords_table->item(row, c)) + { + QTableWidgetItem* item = new QTableWidgetItem(); + if (c < 2) + { + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); + } + coords_table->setItem(row, c, item); + } + } + updateCoordsRow(row); + + coords_table->blockSignals(false); +} + +void PointSymbolEditorWidget::updateCoordsRow(int row) +{ + Q_ASSERT(element_list->currentRow() > 0); + Object* object = getCurrentElementObject(); + auto coord_index = MapCoordVector::size_type(row); + + MapCoordF coordF(0, 0); + if (object->getType() == Object::Point) + coordF = static_cast(object)->getCoordF(); + else if (object->getType() == Object::Path) + coordF = MapCoordF(static_cast(object)->getCoordinate(coord_index)); + + QLocale locale; + coords_table->item(row, 0)->setText(locale.toString(coordF.x(), 'f', 3)); + coords_table->item(row, 1)->setText(locale.toString(-coordF.y(), 'f', 3)); + + if (object->getType() == Object::Path) + { + auto path = static_cast(object); + bool has_curve_start_box = coord_index+3 < path->getCoordinateCount() + && (!path->getCoordinate(coord_index+1).isCurveStart() && !path->getCoordinate(coord_index+2).isCurveStart()) + && (row <= 0 || !path->getCoordinate(coord_index-1).isCurveStart()) + && (row <= 1 || !path->getCoordinate(coord_index-2).isCurveStart()); + if (has_curve_start_box) + { + coords_table->item(row, 2)->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); + coords_table->item(row, 2)->setCheckState(path->getCoordinate(coord_index).isCurveStart() ? Qt::Checked : Qt::Unchecked); + return; + } + } + + if (coords_table->item(row, 2)->flags() & Qt::ItemIsUserCheckable) + coords_table->setItem(row, 2, new QTableWidgetItem()); // remove checkbox by replacing the item with a new one - is there a better way? + coords_table->item(row, 2)->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); +} + +void PointSymbolEditorWidget::updateDeleteCoordButton() +{ + const bool has_coords = coords_table->rowCount() > 0; + const bool is_point = getCurrentElementObject()->getType() == Object::Point; + bool part_of_curve = false; + + if (!is_point) + { + auto path = static_cast(getCurrentElementObject()); + for (int i = 1; i < 4; i++) + { + int row = coords_table->currentRow() - i; + if (row >= 0 && path->getCoordinate(MapCoordVector::size_type(row)).isCurveStart()) + part_of_curve = true; + } + } + + delete_coord_button->setEnabled(has_coords && !is_point && !part_of_curve); +} + +void PointSymbolEditorWidget::insertElement(Object* object, Symbol* element_symbol) +{ + int row = (element_list->currentRow() < 0) ? element_list->count() : (element_list->currentRow() + 1); + int pos = row - 1; + symbol->addElement(pos, object, element_symbol); + element_list->insertItem(row, getLabelForSymbol(element_symbol)); + element_list->setCurrentRow(row); + map->updateAllObjectsWithSymbol(symbol); + emit symbolEdited(); +} + +QString PointSymbolEditorWidget::getLabelForSymbol(const Symbol* symbol) const +{ + if (symbol->getType() == Symbol::Point) + return tr("Point"); // FIXME: This is rather a circle. + else if (symbol->getType() == Symbol::Line) + return tr("Line"); + else if (symbol->getType() == Symbol::Area) + return tr("Area"); + + Q_ASSERT(false); + return tr("Unknown"); +} + +Object* PointSymbolEditorWidget::getCurrentElementObject() +{ + if (element_list->currentRow() > 0) + return symbol->getElementObject(element_list->currentRow() - 1); + else + return midpoint_object; +} + +Symbol* PointSymbolEditorWidget::getCurrentElementSymbol() +{ + if (element_list->currentRow() > 0) + return symbol->getElementSymbol(element_list->currentRow() - 1); + else + return symbol; +} + + + +// ### PointSymbolEditorTool ### + +PointSymbolEditorTool::PointSymbolEditorTool(MapEditorController* editor, PointSymbolEditorWidget* symbol_editor) +: MapEditorTool(editor, Other) +, symbol_editor(symbol_editor) +{ + // nothing +} + +PointSymbolEditorTool::~PointSymbolEditorTool() = default; + +void PointSymbolEditorTool::init() +{ + setStatusBarText(tr("Click: Add a coordinate. %1+Click: Change the selected coordinate. ").arg(ModifierKey::control())); + + MapEditorTool::init(); +} + +bool PointSymbolEditorTool::mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* map_widget) +{ + Q_UNUSED(map_widget); + if (event->button() == Qt::LeftButton) + { + if (event->modifiers() & Qt::ControlModifier) + symbol_editor->changeCurrentCoordinate(map_coord); + else + symbol_editor->addCoordinate(map_coord); + return true; + } + return false; +} + +const QCursor& PointSymbolEditorTool::getCursor() const +{ + static auto const cursor = scaledToScreen(QCursor{ QPixmap(QString::fromLatin1(":/images/cursor-crosshair.png")), 11, 11 }); + return cursor; +} + + + +// ### PointSymbolEditorActivity ### + +const int PointSymbolEditorActivity::cross_radius = 5; + +PointSymbolEditorActivity::PointSymbolEditorActivity(Map* map, PointSymbolEditorWidget* symbol_editor) +: MapEditorActivity() +, map(map) +, symbol_editor(symbol_editor) +{ + // nothing +} + +PointSymbolEditorActivity::~PointSymbolEditorActivity() = default; + +void PointSymbolEditorActivity::init() +{ + update(); +} + +void PointSymbolEditorActivity::update() +{ + QRectF rect = QRectF(0.0, symbol_editor->offset_y, 0.0, 0.0); + map->setActivityBoundingBox(rect, cross_radius + 1); +} + +void PointSymbolEditorActivity::draw(QPainter* painter, MapWidget* map_widget) +{ + QPoint midpoint = map_widget->mapToViewport(symbol_editor->object_origin_coord).toPoint(); + + QPen pen = QPen(Qt::white); + pen.setWidth(3); + painter->setPen(pen); + painter->drawLine(midpoint + QPoint(0, -cross_radius), midpoint + QPoint(0, cross_radius)); + painter->drawLine(midpoint + QPoint(-cross_radius, 0), midpoint + QPoint(cross_radius, 0)); + + pen.setWidth(0); + pen.setColor(Qt::black); + painter->setPen(pen); + painter->drawLine(midpoint + QPoint(0, -cross_radius), midpoint + QPoint(0, cross_radius)); + painter->drawLine(midpoint + QPoint(-cross_radius, 0), midpoint + QPoint(cross_radius, 0)); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/point_symbol_editor_widget.h b/src/gui/symbols/point_symbol_editor_widget.h new file mode 100644 index 0000000..6de37cf --- /dev/null +++ b/src/gui/symbols/point_symbol_editor_widget.h @@ -0,0 +1,229 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_POINT_EDITOR_H +#define OPENORIENTEERING_SYMBOL_POINT_EDITOR_H + +#include +#include +#include +#include + +#include "core/map_coord.h" +#include "gui/map/map_editor_activity.h" +#include "tools/tool.h" + +class QCheckBox; +class QComboBox; +class QCursor; +class QDoubleSpinBox; +class QLabel; +class QListWidget; +class QMouseEvent; +class QPainter; +class QPushButton; +class QStackedWidget; +class QTableWidget; + +namespace OpenOrienteering { + +class ColorDropDown; +class Map; +class MapEditorController; +class MapWidget; +class Object; +class PointObject; +class PointSymbol; +class PointSymbolEditorActivity; +class Symbol; + + +/** A Widget for editing point symbol definitions */ +class PointSymbolEditorWidget : public QWidget +{ +Q_OBJECT +friend class PointSymbolEditorActivity; +public: + /** Construct a new widget. + * @param controller The controller of the preview map + * @param symbol The point symbol to be edited + * @param offset_y The vertical offset of the point symbol preview/editor from the origin + * @param permanent_preview A flag indicating wheter the preview shall be visible even if the editor is not visible + */ + PointSymbolEditorWidget(MapEditorController* controller, PointSymbol* symbol, qreal offset_y = 0, bool permanent_preview = false, QWidget* parent = 0); + + ~PointSymbolEditorWidget() override; + + /** Add a coordinate to the current element. + * @return true if successful + */ + bool addCoordinate(MapCoordF new_coord); + + /** Change the current coordinate of the current element. + * @return true if successful + */ + bool changeCurrentCoordinate(MapCoordF new_coord); + + /** Activate the editor in the map preview. */ + void setEditorActive(bool active); + + /** Request to hide or show the editor. */ + void setVisible(bool visible) override; + +signals: + /** This signal gets emitted whenever the symbol appearance is modified. */ + void symbolEdited(); + +private slots: + void orientedToNorthClicked(bool checked); + + void changeElement(int row); + + void addPointClicked(); + void addLineClicked(); + void addAreaClicked(); + void deleteCurrentElement(); + void centerAllElements(); + + void pointInnerRadiusChanged(double value); + void pointInnerColorChanged(); + void pointOuterWidthChanged(double value); + void pointOuterColorChanged(); + + void lineWidthChanged(double value); + void lineColorChanged(); + void lineCapChanged(int index); + void lineJoinChanged(int index); + void lineClosedClicked(bool checked); + + void areaColorChanged(); + + void updateDeleteCoordButton(); + void coordinateChanged(int row, int column); + void addCoordClicked(); + void deleteCoordClicked(); + void centerCoordsClicked(); + +private: + void initElementList(); + void updateCoordsTable(); + void addCoordsRow(int row); + void updateCoordsRow(int row); + + void insertElement(Object* object, Symbol* symbol); + QString getLabelForSymbol(const Symbol* symbol) const; + + Symbol* getCurrentElementSymbol(); + Object* getCurrentElementObject(); + + PointSymbol* const symbol; + PointObject* midpoint_object; + const MapCoordF object_origin_coord; + + QCheckBox* oriented_to_north; + + QListWidget* element_list; + QPushButton* delete_element_button; + QPushButton* center_all_elements_button; + + QStackedWidget* element_properties_widget; + + QWidget* point_properties; + QDoubleSpinBox* point_inner_radius_edit; + ColorDropDown* point_inner_color_edit; + QDoubleSpinBox* point_outer_width_edit; + ColorDropDown* point_outer_color_edit; + + QWidget* line_properties; + QDoubleSpinBox* line_width_edit; + ColorDropDown* line_color_edit; + QComboBox* line_cap_edit; + QComboBox* line_join_edit; + QCheckBox* line_closed_check; + + QWidget* area_properties; + ColorDropDown* area_color_edit; + + QLabel* coords_label; + QTableWidget* coords_table; + QPushButton* add_coord_button; + QPushButton* delete_coord_button; + QPushButton* center_coords_button; + + const qreal offset_y; + PointSymbolEditorActivity* activity; + Map* map; + MapEditorController* controller; + const bool permanent_preview; +}; + + + +/** + * PointSymbolEditorActivity allows to add or modify coordinates of point symbol elements + * by clicking in the map. + */ +class PointSymbolEditorTool : public MapEditorTool +{ +Q_OBJECT + +public: + PointSymbolEditorTool(MapEditorController* editor, PointSymbolEditorWidget* symbol_editor); + ~PointSymbolEditorTool() override; + + void init() override; + bool mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* map_widget) override; + const QCursor& getCursor() const override; + +private: + PointSymbolEditorWidget* const symbol_editor; +}; + + + +/** + * PointSymbolEditorActivity draws a small cross in the origin of the map coordinate system. + * + * \todo Fix that thes cross may cover the symbol at small scales. + */ +class PointSymbolEditorActivity : public MapEditorActivity +{ + Q_OBJECT + +public: + PointSymbolEditorActivity(Map* map, PointSymbolEditorWidget* symbol_editor); + ~PointSymbolEditorActivity() override; + + void init() override; + void update(); + void draw(QPainter* painter, MapWidget* map_widget) override; + +private: + Map* const map; + PointSymbolEditorWidget* const symbol_editor; + + static const int cross_radius; // NOTE: This could be a configuration option. +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/symbols/point_symbol_settings.cpp b/src/gui/symbols/point_symbol_settings.cpp new file mode 100644 index 0000000..b864f83 --- /dev/null +++ b/src/gui/symbols/point_symbol_settings.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "point_symbol_settings.h" + +#include +#include +#include +#include + +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "gui/symbols/point_symbol_editor_widget.h" +#include "gui/symbols/symbol_setting_dialog.h" + + +namespace OpenOrienteering { + +// ### PointSymbol ### + +SymbolPropertiesWidget* PointSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) +{ + return new PointSymbolSettings(this, dialog); +} + + + +// ### PointSymbolSettings ### + +PointSymbolSettings::PointSymbolSettings(PointSymbol* symbol, SymbolSettingDialog* dialog) +: SymbolPropertiesWidget(symbol, dialog), + symbol(symbol) +{ + symbol_editor = new PointSymbolEditorWidget(dialog->getPreviewController(), symbol, 0, true); + connect(symbol_editor, &PointSymbolEditorWidget::symbolEdited, this, &SymbolPropertiesWidget::propertiesModified ); + + layout = new QVBoxLayout(); + layout->addWidget(symbol_editor); + + point_tab = new QWidget(); + point_tab->setLayout(layout); + addPropertiesGroup(tr("Point symbol"), point_tab); + + connect(this, &QTabWidget::currentChanged, this, &PointSymbolSettings::tabChanged); +} + + +PointSymbolSettings::~PointSymbolSettings() = default; + + + +void PointSymbolSettings::reset(Symbol* symbol) +{ + if (Q_UNLIKELY(symbol->getType() != Symbol::Point)) + { + qWarning("Not a point symbol: %s", symbol ? "nullptr" : qPrintable(symbol->getPlainTextName())); + return; + } + + SymbolPropertiesWidget::reset(symbol); + this->symbol = static_cast(symbol); + + layout->removeWidget(symbol_editor); + delete(symbol_editor); + + symbol_editor = new PointSymbolEditorWidget(dialog->getPreviewController(), this->symbol, 0, true); + connect(symbol_editor, &PointSymbolEditorWidget::symbolEdited, this, &SymbolPropertiesWidget::propertiesModified ); + layout->addWidget(symbol_editor); +} + + + +void PointSymbolSettings::tabChanged(int /*index*/) +{ + symbol_editor->setEditorActive( currentWidget()==point_tab ); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/point_symbol_settings.h b/src/gui/symbols/point_symbol_settings.h new file mode 100644 index 0000000..98b31c1 --- /dev/null +++ b/src/gui/symbols/point_symbol_settings.h @@ -0,0 +1,63 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_POINT_SYMBOL_SETTINGS_H +#define OPENORIENTEERING_POINT_SYMBOL_SETTINGS_H + +#include + +#include "gui/symbols/symbol_properties_widget.h" + +class QVBoxLayout; +class QWidget; + +namespace OpenOrienteering { + +class PointSymbol; +class PointSymbolEditorWidget; +class Symbol; +class SymbolSettingDialog; + + +class PointSymbolSettings : public SymbolPropertiesWidget +{ +Q_OBJECT +public: + PointSymbolSettings(PointSymbol* symbol, SymbolSettingDialog* dialog); + ~PointSymbolSettings() override; + + void reset(Symbol* symbol) override; + +public slots: + void tabChanged(int index); + +private: + PointSymbol* symbol; + PointSymbolEditorWidget* symbol_editor; + QVBoxLayout* layout; + QWidget* point_tab; + +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/symbols/replace_symbol_set_dialog.cpp b/src/gui/symbols/replace_symbol_set_dialog.cpp new file mode 100644 index 0000000..96ce0be --- /dev/null +++ b/src/gui/symbols/replace_symbol_set_dialog.cpp @@ -0,0 +1,709 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "replace_symbol_set_dialog.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/map.h" +#include "core/map_color.h" +#include "core/objects/object.h" +#include "core/objects/object_query.h" +#include "core/objects/symbol_rule_set.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "core/symbols/text_symbol.h" +#include "fileformats/file_format.h" +#include "gui/file_dialog.h" +#include "gui/main_window.h" +#include "gui/util_gui.h" +#include "gui/widgets/symbol_dropdown.h" +#include "util/util.h" +#include "util/backports.h" + +// IWYU pragma: no_forward_declare QColor +// IWYU pragma: no_forward_declare QFormLayout +// IWYU pragma: no_forward_declare QLabel +// IWYU pragma: no_forward_declare QTableWidgetItem + + +namespace OpenOrienteering { + +ReplaceSymbolSetDialog::ReplaceSymbolSetDialog(QWidget* parent, Map& object_map, const Map& symbol_set, SymbolRuleSet& replacements, Mode mode) + : QDialog{ parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint } + , object_map{ object_map } + , symbol_set{ symbol_set } + , replacements{ replacements } + , import_all_check{ nullptr } + , delete_unused_symbols_check{ nullptr } + , delete_unused_colors_check{ nullptr } + , preserve_symbol_states_check{ nullptr } + , mode( &object_map==&symbol_set ? AssignByPattern : mode ) +{ + QFormLayout* form_layout = nullptr; + auto mapping_menu = new QMenu(this); + + QStringList horizontal_headers; + horizontal_headers.reserve(2); + if (mode == ReplaceSymbolSet) + { + setWindowTitle(tr("Replace symbol set")); + form_layout = new QFormLayout(); + QLabel* desc_label = new QLabel(tr("Configure how the symbols should be replaced, and which.")); + form_layout->addRow(desc_label); + form_layout->addItem(Util::SpacerItem::create(this)); + import_all_check = new QCheckBox(tr("Import all new symbols, even if not used as replacement")); + import_all_check->setChecked(true); + form_layout->addRow(import_all_check); + preserve_symbol_states_check = new QCheckBox(tr("Keep the symbols' hidden / protected states of the old symbol set")); + preserve_symbol_states_check->setChecked(true); + form_layout->addRow(preserve_symbol_states_check); + delete_unused_symbols_check = new QCheckBox(tr("Delete original symbols which are unused after the replacement")); + delete_unused_symbols_check->setChecked(true); + form_layout->addRow(delete_unused_symbols_check); + delete_unused_colors_check = new QCheckBox(tr("Delete unused colors after the replacement")); + delete_unused_colors_check->setChecked(true); + form_layout->addRow(delete_unused_colors_check); + id_edit = new QComboBox(); + id_edit->setEditable(true); + auto new_id = symbol_set.symbolSetId(); + if (!new_id.isEmpty()) + id_edit->addItem(new_id); + auto old_id = object_map.symbolSetId(); + if (!old_id.isEmpty() && old_id != new_id) + id_edit->addItem(old_id); + id_edit->setCurrentIndex(0); + form_layout->addRow(tr("Edit the symbol set ID:"), id_edit); + form_layout->addItem(Util::SpacerItem::create(this)); + + horizontal_headers << tr("Original") << tr("Replacement"); + + auto action = mapping_menu->addAction(tr("Match replacement symbols by symbol number")); + connect(action, &QAction::triggered, this, &ReplaceSymbolSetDialog::matchByNumber); + action = mapping_menu->addAction(tr("Match by symbol name")); + connect(action, &QAction::triggered, this, &ReplaceSymbolSetDialog::matchByName); + } + else + { + setWindowTitle(tr("Assign new symbols")); + horizontal_headers << tr("Pattern") << tr("Replacement"); + } + + mapping_table = new QTableWidget(); + mapping_table->verticalHeader()->setVisible(false); + mapping_table->setColumnCount(2); + mapping_table->setHorizontalHeaderLabels(horizontal_headers); + mapping_table->horizontalHeader()->setSectionsClickable(false); + mapping_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + mapping_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + mapping_table->setEditTriggers(QAbstractItemView::AllEditTriggers); + + auto action = mapping_menu->addAction(tr("Clear replacements")); + connect(action, &QAction::triggered, this, &ReplaceSymbolSetDialog::resetReplacements); + mapping_menu->addSeparator(); + action = mapping_menu->addAction(QIcon{QLatin1String{":/images/open.png"}}, tr("Open CRT file...")); + connect(action, &QAction::triggered, this, QOverload<>::of(&ReplaceSymbolSetDialog::openCrtFile)); + action = mapping_menu->addAction(QIcon{QLatin1String{":/images/save.png"}}, tr("Save CRT file...")); + connect(action, &QAction::triggered, this, &ReplaceSymbolSetDialog::saveCrtFile); + + auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, Qt::Horizontal); + auto mapping_button = button_box->addButton(tr("Symbol mapping:").replace(QLatin1Char{':'}, QString{}), QDialogButtonBox::ActionRole); + void(tr("Symbol mapping")); /// \todo Switch translation + mapping_button->setMenu(mapping_menu); + + auto layout = new QVBoxLayout(this); + if (form_layout) + layout->addLayout(form_layout); + layout->addWidget(mapping_table); + layout->addItem(Util::SpacerItem::create(this)); + layout->addWidget(button_box); + setLayout(layout); + + connect(button_box, &QDialogButtonBox::helpRequested, this, &ReplaceSymbolSetDialog::showHelp); + connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + + if (replacements.empty() && mode == ReplaceSymbolSet) + { + this->replacements = SymbolRuleSet::forOriginalSymbols(object_map); + this->replacements.matchQuerySymbolName(symbol_set); + } + + updateMappingTable(); +} + + +ReplaceSymbolSetDialog::~ReplaceSymbolSetDialog() +{ + // nothing, not inlined +} + + + +void ReplaceSymbolSetDialog::showHelp() +{ + Util::showHelp(this, "symbol_replace_dialog.html"); +} + + + +void ReplaceSymbolSetDialog::matchByName() +{ + replacements.matchQuerySymbolName(symbol_set); + updateMappingTable(); +} + + +void ReplaceSymbolSetDialog::matchByNumber() +{ + replacements.matchQuerySymbolNumber(symbol_set); + updateMappingTable(); +} + + +void ReplaceSymbolSetDialog::resetReplacements() +{ + for (auto& item : replacements) + { + item.symbol = nullptr; + item.type = SymbolRule::NoAssignment; + } + updateMappingTable(); +} + + + +void ReplaceSymbolSetDialog::openCrtFile() +{ + auto dir = QLatin1String{"data:/symbol sets"}; + auto filter = QString{tr("CRT file") + QLatin1String{" (*.crt)"}}; + QString path = FileDialog::getOpenFileName(this, tr("Open CRT file..."), dir, filter); + if (!path.isEmpty()) + openCrtFile(path); +} + +void ReplaceSymbolSetDialog::openCrtFile(const QString& path) +{ + QFile crt_file{path}; + crt_file.open(QIODevice::ReadOnly); + QTextStream stream{ &crt_file }; + auto new_replacements = SymbolRuleSet::loadCrt(stream, symbol_set); + if (stream.status() == QTextStream::Ok) + { + // Postprocess CRT + for (auto& item : new_replacements) + { + if (item.type == SymbolRule::NoAssignment + || item.query.getOperator() != ObjectQuery::OperatorSearch) + continue; + Q_ASSERT(item.symbol); + + auto operands = item.query.tagOperands(); + if (!operands || operands->value.isEmpty()) + continue; + + // Find original symbol number matching the pattern + for (int i = 0; i < object_map.getNumSymbols(); ++i) + { + auto symbol = object_map.getSymbol(i); + if (symbol->getNumberAsString() == operands->value) + { + if (Symbol::areTypesCompatible(symbol->getType(), item.symbol->getType())) + item.query = { symbol }; + break; + } + } + + // Find inconsistencies + if (item.query.getOperator() == ObjectQuery::OperatorSymbol) + { + auto has_conflict = [&item](const auto& other)->bool { + return &other != &item + && other.type != SymbolRule::NoAssignment + && item.query == other.query + && item.symbol != other.symbol; + }; + if (std::any_of(begin(new_replacements), end(new_replacements), has_conflict)) + { + stream.setStatus(QTextStream::ReadCorruptData); + auto error_msg = tr("There are multiple replacements for symbol %1.") + .arg(item.query.symbolOperand()->getNumberAsString()); + QMessageBox::warning(this, ::OpenOrienteering::Map::tr("Error"), + tr("Cannot open file:\n%1\n\n%2").arg(path, error_msg) ); + return; + } + } + } + + // Apply + for (auto& item : new_replacements) + { + for (auto& current : replacements) + { + if (item.query == current.query) + { + if (item.type != SymbolRule::NoAssignment) + { + current.symbol = item.symbol; + current.type = item.type; + item = {}; + } + break; + } + } + } + if (mode == AssignByPattern) + { + for (auto&& item : new_replacements) + { + if (item.query.getOperator() != ObjectQuery::OperatorInvalid) + { + replacements.emplace_back(std::move(item)); + } + } + } + + updateMappingTable(); + } +} + + +bool ReplaceSymbolSetDialog::saveCrtFile() +{ + /// \todo Choose user-writable directory. + auto dir = QLatin1String{"data:/symbol sets"}; + auto filter = QString{tr("CRT file") + QLatin1String{" (*.crt)"}}; + QString path = FileDialog::getSaveFileName(this, tr("Save CRT file..."), dir, filter); + if (!path.isEmpty()) + { + updateMappingFromTable(); + QFile crt_file{path}; + crt_file.open(QIODevice::WriteOnly); + QTextStream stream{ &crt_file }; + replacements.writeCrt(stream); + if (stream.pos() != -1) + { + return true; + } + /// \todo Reused translation, consider generalized context + QMessageBox::warning(this, ::OpenOrienteering::Map::tr("Error"), + tr("Cannot save file:\n%1\n\n%2") + .arg(path, crt_file.errorString()) ); + } + return false; +} + + + +void ReplaceSymbolSetDialog::done(int r) +{ + updateMappingFromTable(); + if (std::any_of(begin(replacements), end(replacements), [](const auto& item) { + return item.type == SymbolRule::ManualAssignment; + })) + { + auto save_crt = QMessageBox::warning(this, qApp->applicationDisplayName(), + tr("The cross reference table has been modified.\n" + "Do you want to save your changes?"), + // QGnomeThema label for Discard: "Close without saving" + QMessageBox::Save | QMessageBox::No | QMessageBox::Cancel); + switch (save_crt) + { + case QMessageBox::Cancel: + return; + + case QMessageBox::Discard: + case QMessageBox::No: + break; + + case QMessageBox::Save: + if (!saveCrtFile()) + return; + break; + + default: + Q_UNREACHABLE(); + } + } + QDialog::done(r); +} + + + +void ReplaceSymbolSetDialog::updateMappingTable() +{ + mapping_table->setUpdatesEnabled(false); + + mapping_table->clearContents(); + mapping_table->setRowCount(int(replacements.size())); + + symbol_widget_delegates.clear(); + symbol_widget_delegates.resize(replacements.size()); + + std::size_t row = 0; + for (const auto& item : replacements) + { + const Symbol* original_symbol = nullptr; + auto original_icon = QImage{}; + auto original_string = QString{}; + auto compatible_symbols = int(Symbol::AllSymbols); + const auto replacement_symbol = item.symbol; + + if (item.query.getOperator() == ObjectQuery::OperatorSymbol + && item.query.symbolOperand()) + { + original_symbol = item.query.symbolOperand(); + original_string = Util::plainText(object_map.translate(original_symbol->getName())); + original_string.prepend(original_symbol->getNumberAsString() + QLatin1Char(' ')); + original_icon = original_symbol->getIcon(&object_map); + compatible_symbols = Symbol::getCompatibleTypes(original_symbol->getType()); + } + else + { + if (replacement_symbol) + { + switch (replacement_symbol->getType()) + { + case Symbol::Area: + original_icon = object_map.getUndefinedLine()->getIcon(&object_map); + original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Area"); + compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); + break; + case Symbol::Combined: + original_icon = object_map.getUndefinedLine()->getIcon(&object_map); + original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Combined"); + compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); + break; + case Symbol::Line: + original_icon = object_map.getUndefinedLine()->getIcon(&object_map); + original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Line"); + compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); + break; + case Symbol::Point: + original_icon = object_map.getUndefinedPoint()->getIcon(&object_map); + original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Point"); + compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); + break; + case Symbol::Text: + original_icon = object_map.getUndefinedText()->getIcon(&object_map); + original_string = QGuiApplication::translate("OpenOrienteering::SymbolRenderWidget", "Text"); + compatible_symbols = Symbol::getCompatibleTypes(replacement_symbol->getType()); + break; + case Symbol::AllSymbols: + case Symbol::NoSymbol: + ; // no icon, no string + } + } + + const Symbol* unique_symbol = nullptr; + auto matching_types = int(Symbol::NoSymbol); + auto update_matching = [&unique_symbol, &matching_types](Object* object) { + if (auto symbol = object->getSymbol()) + { + if (matching_types == Symbol::NoSymbol) + { + unique_symbol = symbol; + matching_types = Symbol::getCompatibleTypes(symbol->getType()); + } + else + { + if (symbol != unique_symbol) + unique_symbol = nullptr; + matching_types |= Symbol::getCompatibleTypes(symbol->getType()); + } + } + }; + object_map.applyOnMatchingObjects(update_matching, std::cref(item.query)); + if (matching_types != Symbol::NoSymbol) + { + compatible_symbols = matching_types; + if (unique_symbol) + { + original_symbol = unique_symbol; + original_string = Util::plainText(object_map.translate(original_symbol->getName())); + original_string.prepend(original_symbol->getNumberAsString() + QLatin1Char(' ')); + original_icon = original_symbol->getIcon(&object_map); + } + } + + if (original_icon.isNull()) + { + auto side_length = Settings::getInstance().getSymbolWidgetIconSizePx(); + original_icon = QImage{side_length, side_length, QImage::Format_ARGB32_Premultiplied}; + auto color = object_map.getUndefinedColor()->operator const QColor &(); + color.setAlphaF(0.5); + original_icon.fill(color); + } + + switch (item.query.getOperator()) + { + case ObjectQuery::OperatorInvalid: + case ObjectQuery::OperatorSymbol: + break; + + default: + original_string = item.query.toString(); + } + } + + auto original_item = new QTableWidgetItem(original_string); + original_item->setFlags(Qt::ItemIsEnabled); // make item non-editable + QVariantList original_item_data = + QVariantList() << qVariantFromValue(&object_map) << qVariantFromValue(original_symbol); + original_item->setData(Qt::UserRole, QVariant(original_item_data)); + original_item->setData(Qt::DecorationRole, original_icon); + mapping_table->setItem(int(row), 0, original_item); + + // Note on const: this is not a const method. + QTableWidgetItem* replacement_item; + if (replacement_symbol) + replacement_item = new QTableWidgetItem(replacement_symbol->getNumberAsString() + QLatin1Char(' ') + + Util::plainText(symbol_set.translate(replacement_symbol->getName()))); + else + replacement_item = new QTableWidgetItem(tr("- None -")); + QVariantList replacement_item_data = + QVariantList() << qVariantFromValue(&symbol_set) << qVariantFromValue(replacement_symbol); + replacement_item->setData(Qt::UserRole, QVariant(replacement_item_data)); + if (replacement_symbol) + replacement_item->setData(Qt::DecorationRole, replacement_symbol->getIcon(&symbol_set)); + mapping_table->setItem(int(row), 1, replacement_item); + + //auto hide = (mode == ModeCRT && original_symbol->isHidden()); + //mapping_table->setRowHidden(row, hide); + + symbol_widget_delegates[row].reset(new SymbolDropDownDelegate(compatible_symbols)); + mapping_table->setItemDelegateForRow(int(row), symbol_widget_delegates[row].get()); + + ++row; + } + + mapping_table->setUpdatesEnabled(true); +} + + +void ReplaceSymbolSetDialog::updateMappingFromTable() +{ + Q_ASSERT(int(replacements.size()) == mapping_table->rowCount()); + auto item = begin(replacements); + for (int row = 0; row < mapping_table->rowCount(); ++row) + { + auto replacement_symbol = mapping_table->item(row, 1)->data(Qt::UserRole).toList().at(1).value(); + if (item->symbol != replacement_symbol) + { + item->symbol = replacement_symbol; + item->type = SymbolRule::ManualAssignment; + } + ++item; + } +} + + + +// static +bool ReplaceSymbolSetDialog::showDialog(QWidget* parent, Map& object_map) +{ + auto symbol_set = std::unique_ptr{}; + while (!symbol_set) + { + auto path = MainWindow::getOpenFileName(parent, tr("Choose map file to load symbols from"), + FileFormat::MapFile); + if (path.isEmpty()) + return false; + + symbol_set.reset(new Map); + if (!symbol_set->loadFrom(path, parent, nullptr, true)) + { + QMessageBox::warning(parent, tr("Error"), tr("Cannot load map file, aborting.")); + return false; + } + + if (symbol_set->getScaleDenominator() != object_map.getScaleDenominator()) + { + if (QMessageBox::warning(parent, tr("Warning"), + tr("The chosen symbol set has a scale of 1:%1, while the map scale is 1:%2. Do you really want to choose this set?").arg(symbol_set->getScaleDenominator()).arg(object_map.getScaleDenominator()), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + { + symbol_set.reset(nullptr); + } + } + } + + auto replacements = SymbolRuleSet::forOriginalSymbols(object_map); + replacements.matchQuerySymbolName(*symbol_set); + + auto flags_from_dialog = [](const ReplaceSymbolSetDialog& dialog){ + auto options = SymbolRuleSet::Options{}; + if (dialog.import_all_check->isChecked()) + options |= SymbolRuleSet::ImportAllSymbols; + if (dialog.preserve_symbol_states_check->isChecked()) + options |= SymbolRuleSet::PreserveSymbolState; + if (!dialog.delete_unused_symbols_check->isChecked()) + options |= SymbolRuleSet::KeepUnusedSymbols; + if (!dialog.delete_unused_colors_check->isChecked()) + options |= SymbolRuleSet::KeepUnusedColors; + return options; + }; + + ReplaceSymbolSetDialog dialog(parent, object_map, *symbol_set, replacements, ReplaceSymbolSet); + dialog.setWindowModality(Qt::WindowModal); + auto crt_file = discoverCrtFile(object_map.symbolSetId(), symbol_set->symbolSetId()); + if (QFileInfo::exists(crt_file)) + { + dialog.show(); + dialog.openCrtFile(crt_file); + } + auto result = dialog.exec(); + switch (result) + { + case QDialog::Accepted: + replacements.squeezed().apply(object_map, *symbol_set, flags_from_dialog(dialog)); + object_map.setSymbolSetId(dialog.id_edit->currentText()); + return true; + + case QDialog::Rejected: + return false; + } + + Q_UNREACHABLE(); +} + + +// static +bool ReplaceSymbolSetDialog::showDialogForCRT(QWidget* parent, Map& object_map, const Map& symbol_set) +{ + auto replacements = SymbolRuleSet{}; + ReplaceSymbolSetDialog dialog(parent, object_map, symbol_set, replacements, AssignByPattern); + dialog.setWindowModality(Qt::WindowModal); + dialog.show(); + dialog.openCrtFile(); + auto result = dialog.exec(); + switch (result) + { + case QDialog::Accepted: + replacements.squeezed().apply(object_map, symbol_set); + return true; + + case QDialog::Rejected: + return false; + } + + Q_UNREACHABLE(); +} + + +// static +bool ReplaceSymbolSetDialog::showDialogForCRT(QWidget* parent, Map& object_map, const Map& symbol_set, QIODevice& crt_file) +{ + QTextStream stream{ &crt_file }; + auto replacements = SymbolRuleSet::loadCrt(stream, symbol_set); + if (stream.status() != QTextStream::Ok) + { + QMessageBox::warning(parent, tr("Error"), tr("Cannot load CRT file, aborting.")); + return false; + } + + for (auto& item : replacements) + { + if (item.query.getOperator() != ObjectQuery::OperatorSearch) + continue; + + auto operands = item.query.tagOperands(); + if (operands && !operands->value.isEmpty()) + { + // Construct layer queries + item.query = { QLatin1String("Layer"), ObjectQuery::OperatorIs, operands->value }; + } + } + + /// \todo Collect source symbols for all patterns, for source icon in dialog + + ReplaceSymbolSetDialog dialog(parent, object_map, symbol_set, replacements, AssignByPattern); + dialog.setWindowModality(Qt::WindowModal); + auto result = dialog.exec(); + switch (result) + { + case QDialog::Accepted: + replacements.squeezed().apply(object_map, symbol_set); + return true; + + case QDialog::Rejected: + return false; + } + + Q_UNREACHABLE(); +} + + +// static +QString ReplaceSymbolSetDialog::discoverCrtFile(const QString& source_id, const QString& target_id) +{ + QString name = QLatin1String("data:/symbol sets/") + + source_id + QLatin1Char('-') + + target_id + QLatin1String(".crt"); +#ifdef MAPPER_DEVELOPMENT_BUILD + if (!QFileInfo::exists(name)) + name = QLatin1String("data:/symbol sets/COPY_OF_") + + source_id + QLatin1Char('-') + + target_id + QLatin1String(".crt"); +#endif + return name; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/replace_symbol_set_dialog.h b/src/gui/symbols/replace_symbol_set_dialog.h new file mode 100644 index 0000000..0dd22a9 --- /dev/null +++ b/src/gui/symbols/replace_symbol_set_dialog.h @@ -0,0 +1,124 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_REPLACE_SYMBOL_SET_DIALOG_H +#define OPENORIENTEERING_REPLACE_SYMBOL_SET_DIALOG_H + +#include +#include + +#include +#include + +class QCheckBox; +class QIODevice; +class QComboBox; +class QTableWidget; +class QWidget; + +namespace OpenOrienteering { + +class Map; +class SymbolDropDownDelegate; +class SymbolRuleSet; + + +/** + * Dialog and tool for replacing the map's symbol set with another. + * + * Lets the user choose options and possibly even choose the replacement + * for every single symbol. + */ +class ReplaceSymbolSetDialog : public QDialog +{ +Q_OBJECT +public: + /** + * Lets the user select a symbol set file, and shows the dialog. + * + * Returns true if the replacement has been finished, false if aborted. + */ + static bool showDialog(QWidget* parent, Map& object_map); + + /** + * Lets the user select a CRT file, and shows the dialog. + * + * Returns true if the replacement has been finished, false if aborted. + */ + static bool showDialogForCRT(QWidget* parent, Map& object_map, const Map& symbol_set); + + /** + * Shows the dialog for a given object map, symbol map, and cross reference file. + * + * Returns true if the replacement has been finished, false if aborted. + */ + static bool showDialogForCRT(QWidget* parent, Map& object_map, const Map& symbol_set, QIODevice& crt_file); + + /** + * Returns the suggested path (including name) for finding a CRT file + * for the given symbol set IDs. + */ + static QString discoverCrtFile(const QString& source_id, const QString& target_id); + +private: + enum Mode + { + ReplaceSymbolSet, ///< Replace current current symbols + AssignByPattern, ///< Assign new symbols based on a pattern + }; + + ReplaceSymbolSetDialog(QWidget* parent, Map& object_map, const Map& symbol_set, SymbolRuleSet& replacements, Mode mode); + ~ReplaceSymbolSetDialog() override; + + void showHelp(); + + void matchByName(); + void matchByNumber(); + void resetReplacements(); + + void openCrtFile(); + void openCrtFile(const QString& path); + bool saveCrtFile(); + + void done(int r) override; + + void updateMappingTable(); + void updateMappingFromTable(); + + Map& object_map; + const Map& symbol_set; + SymbolRuleSet& replacements; + + QCheckBox* import_all_check; + QCheckBox* delete_unused_symbols_check; + QCheckBox* delete_unused_colors_check; + QCheckBox* preserve_symbol_states_check; + QComboBox* id_edit; + QTableWidget* mapping_table; + std::vector> symbol_widget_delegates; + + Mode mode; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/symbols/symbol_properties_widget.cpp b/src/gui/symbols/symbol_properties_widget.cpp new file mode 100644 index 0000000..f8ed644 --- /dev/null +++ b/src/gui/symbols/symbol_properties_widget.cpp @@ -0,0 +1,354 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "symbol_properties_widget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/symbols/symbol.h" +#include "gui/symbols/symbol_setting_dialog.h" +#include "util/translation_util.h" +#include "util/backports.h" + +// IWYU pragma: no_forward_declare QGridLayout +// IWYU pragma: no_forward_declare QLabel + + +namespace OpenOrienteering { + +// ### Symbol ### + +SymbolPropertiesWidget* Symbol::createPropertiesWidget(SymbolSettingDialog* dialog) +{ + return new SymbolPropertiesWidget(this, dialog); +} + + + +// ### SymbolPropertiesWidget ### + +SymbolPropertiesWidget::SymbolPropertiesWidget(Symbol* symbol, SymbolSettingDialog* dialog) +: QTabWidget() +, dialog(dialog) +{ + auto generalTab = new QWidget(); + + auto layout = new QGridLayout(); + generalTab->setLayout(layout); + + auto number_label = new QLabel(tr("Number:")); + number_editors.resize(Symbol::number_components, nullptr); + for (auto& editor : number_editors) + { + editor = new QLineEdit{}; + editor->setMaximumWidth(60); + editor->setValidator(new QIntValidator(0, 99999, editor)); + } + + auto language_label = new QLabel(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Text source:")); + language_combo = new QComboBox(); + edit_button = new QPushButton(tr("Edit")); + auto name_label = new QLabel(tr("Name:")); + name_edit = new QLineEdit(); + auto description_label = new QLabel(tr("Description:")); + description_edit = new QTextEdit(); + helper_symbol_check = new QCheckBox(tr("Helper symbol (not shown in finished map)")); + SymbolPropertiesWidget::reset(symbol); + + for (auto editor : number_editors) + connect(editor, &QLineEdit::textEdited, this, &SymbolPropertiesWidget::numberChanged); + connect(language_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &SymbolPropertiesWidget::languageChanged); + connect(edit_button, &QPushButton::clicked, this, &SymbolPropertiesWidget::editClicked); + connect(name_edit, &QLineEdit::textEdited, this, &SymbolPropertiesWidget::nameChanged); + connect(description_edit, &QTextEdit::textChanged, this, &SymbolPropertiesWidget::descriptionChanged); + connect(helper_symbol_check, &QAbstractButton::clicked, this, &SymbolPropertiesWidget::helperSymbolChanged); + connect(this, &SymbolPropertiesWidget::propertiesModified, dialog, [dialog]() { dialog->setSymbolModified(true); }); + + int row = 0, col = 0; + // 1st col + layout->addWidget(number_label, row, col++); + // 5 cols + for (auto editor : number_editors) + { + layout->addWidget(editor, row, col++); + if (editor != number_editors.back()) + layout->addWidget(new QLabel(QString(QLatin1Char{'.'})), row, col++); + } + // 7th col + ++col; + // 8th col + layout->setColumnStretch(col++, 1); + + auto num_col = col; + + row++; col = 0; + layout->addWidget(language_label, row, col++); + layout->addWidget(language_combo, row, col++, 1, num_col-3); + layout->addWidget(edit_button, row, num_col-2); + + row++; col = 0; + layout->addWidget(name_label, row, col++); + layout->addWidget(name_edit, row, col++, 1, num_col-1); + + row++; col = 0; + layout->addWidget(description_label, row, col, 1, num_col); + + row++; col = 0; + layout->addWidget(description_edit, row, col, 1, num_col); + + row++; col = 0; + layout->addWidget(helper_symbol_check, row, col, 1, num_col); + + addPropertiesGroup(tr("General"), generalTab); +} + + +SymbolPropertiesWidget::~SymbolPropertiesWidget() = default; + + + +void SymbolPropertiesWidget::addPropertiesGroup(const QString& name, QWidget* widget) +{ + addTab(widget, name); +} + +void SymbolPropertiesWidget::insertPropertiesGroup(int index, const QString& name, QWidget* widget) +{ + insertTab(index, widget, name); +} + +void SymbolPropertiesWidget::removePropertiesGroup(int index) +{ + auto tab_contents = widget(index); + removeTab(index); + delete tab_contents; +} + +void SymbolPropertiesWidget::removePropertiesGroup(const QString& name) +{ + auto index = indexOfPropertiesGroup(name); + removePropertiesGroup(index); +} + +void SymbolPropertiesWidget::renamePropertiesGroup(int index, const QString& new_name) +{ + setTabText(index, new_name); +} + +void SymbolPropertiesWidget::renamePropertiesGroup(const QString& old_name, const QString& new_name) +{ + auto index = indexOfPropertiesGroup(old_name); + setTabText(index, new_name); +} + +int SymbolPropertiesWidget::indexOfPropertiesGroup(const QString& name) const +{ + for (int i = 0; i < count(); i++) + { + if (tabText(i) == name) + return i; + } + return -1; +} + +void SymbolPropertiesWidget::numberChanged(const QString& text) +{ + bool editors_enabled = true; + int i = 0; + for (auto editor : number_editors) + { + editor->setEnabled(editors_enabled); + if (editor == sender()) + symbol->setNumberComponent(i, text.isEmpty() ? -1 : text.toInt()); + if (editors_enabled && editor->text().isEmpty()) + editors_enabled = false; + ++i; + } + emit propertiesModified(); +} + +void SymbolPropertiesWidget::languageChanged() +{ + updateTextEdits(); +} + +void SymbolPropertiesWidget::editClicked() +{ + int result; + auto question = QString{}; + if (language_combo->currentIndex() == 1) + { + question = QApplication::translate("OpenOrienteering::MapSymbolTranslation", + "Before editing, the stored text will be " + "replaced with the current translation. " + "Do you want to continue?"); + } + else + { + question = QApplication::translate("OpenOrienteering::MapSymbolTranslation", + "After modifying the stored text, " + "the translation may no longer be found. " + "Do you want to continue?"); + } + result = QMessageBox::warning(dialog, tr("Warning"), question, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes); + if (result == QMessageBox::Yes) + { + language_combo->setEnabled(false); + edit_button->setEnabled(false); + name_edit->setEnabled(true); + description_edit->setEnabled(true); + if (language_combo->currentIndex() == 1) + { + { + QSignalBlocker block(language_combo); + language_combo->setCurrentIndex(0); + } + auto name = name_edit->text(); + if (name.isEmpty()) + { + QSignalBlocker block(name_edit); + name = symbol->getName(); + name_edit->setText(name); + } + if (description_edit->document()->isEmpty()) + { + QSignalBlocker block(description_edit); + description_edit->setText(symbol->getDescription()); + } + symbol->setName(name); + symbol->setDescription(description_edit->toPlainText()); + } + emit propertiesModified(); + } +} + +void SymbolPropertiesWidget::nameChanged(const QString& text) +{ + symbol->setName(text); + emit propertiesModified(); +} + +void SymbolPropertiesWidget::descriptionChanged() +{ + symbol->setDescription(description_edit->toPlainText()); + emit propertiesModified(); +} + +void SymbolPropertiesWidget::helperSymbolChanged(bool checked) +{ + symbol->setIsHelperSymbol(checked); + emit propertiesModified(); +} + +void SymbolPropertiesWidget::updateTextEdits() +{ + auto name = symbol->getName(); + auto description = symbol->getDescription(); + if (language_combo->currentIndex() == 1) + { + name = dialog->getSourceMap()->raw_translation(name); + description = dialog->getSourceMap()->raw_translation(description); + } + { + QSignalBlocker block(name_edit); + name_edit->setText(name); + } + { + QSignalBlocker block(description_edit); + description_edit->setText(description); + } +} + +void SymbolPropertiesWidget::reset(Symbol* symbol) +{ + QSignalBlocker block(this); + this->symbol = symbol; + bool editors_enabled = true; + int i = 0; + for (auto editor : number_editors) + { + editor->setEnabled(editors_enabled); + + int value = symbol->getNumberComponent(i); + if (value >= 0 && editors_enabled) + { + editor->setText(QString::number(value)); + } + else + { + editor->setText({}); + editors_enabled = false; + } + ++i; + } + language_combo->clear(); + auto name_translated = dialog->getSourceMap()->raw_translation(symbol->getName()); + auto description_translated = dialog->getSourceMap()->raw_translation(symbol->getDescription()); + ///: The language of the symbol name in the map file is not defined explicitly. + language_combo->addItem(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Map (%1)"). + arg(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "undefined language"))); + if (name_translated.isEmpty() && description_translated.isEmpty()) + { + language_combo->setEnabled(false); + edit_button->setEnabled(false); + name_edit->setEnabled(true); + description_edit->setEnabled(true); + } + else + { + auto language = TranslationUtil::languageFromSettings(QSettings()); + if (!language.isValid()) + { + language.displayName = QApplication::translate("OpenOrienteering::MapSymbolTranslation", "undefined language"); + } + + language_combo->addItem(QApplication::translate("OpenOrienteering::MapSymbolTranslation", "Translation (%1)").arg(language.displayName)); + language_combo->setEnabled(true); + language_combo->setCurrentIndex(1); + edit_button->setEnabled(true); + name_edit->setEnabled(false); + description_edit->setEnabled(false); + } + updateTextEdits(); + helper_symbol_check->setChecked(symbol->isHelperSymbol()); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/symbol_properties_widget.h b/src/gui/symbols/symbol_properties_widget.h new file mode 100644 index 0000000..bd39351 --- /dev/null +++ b/src/gui/symbols/symbol_properties_widget.h @@ -0,0 +1,113 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2014, 2016, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_PROPERTIES_WIDGET_H +#define OPENORIENTEERING_SYMBOL_PROPERTIES_WIDGET_H + + +#include + +#include +#include +#include + +class QCheckBox; +class QComboBox; +class QLineEdit; +class QPushButton; +class QTextEdit; +class QWidget; + +namespace OpenOrienteering { + +class Symbol; +class SymbolSettingDialog; + + +/** + * A widget for editing groups of properties of a symbol. + */ +class SymbolPropertiesWidget : public QTabWidget +{ +Q_OBJECT +public: + /** Construct a new widget containing only a general properties group. + * @param symbol the symbol to be customized + */ + SymbolPropertiesWidget(Symbol* symbol, SymbolSettingDialog* dialog); + + ~SymbolPropertiesWidget() override; + + /** Add a widget as a named group of properties */ + void addPropertiesGroup(const QString& name, QWidget* widget); + + void insertPropertiesGroup(int index, const QString& name, QWidget* widget); + + void removePropertiesGroup(int index); + + void removePropertiesGroup(const QString& name); + + void renamePropertiesGroup(int index, const QString& new_name); + + void renamePropertiesGroup(const QString& old_name, const QString& new_name); + + int indexOfPropertiesGroup(const QString& name) const; + + /** Get the edited symbol */ + inline Symbol* getSymbol() { return symbol; } + + QString getHelpSection() const { return QString(); } + + /** + * Changes the edited symbol and resets the input values. + * When overriding this method, make sure to call SymbolPropertiesWidget::reset(). + */ + virtual void reset(Symbol* symbol); + +signals: + void propertiesModified(); + +protected slots: + void numberChanged(const QString& text); + void languageChanged(); + void editClicked(); + void nameChanged(const QString& text); + void descriptionChanged(); + void helperSymbolChanged(bool checked); + +protected: + void updateTextEdits(); + + Symbol* symbol; + SymbolSettingDialog* const dialog; + + std::vector number_editors; + QComboBox* language_combo; + QPushButton* edit_button; + QLineEdit* name_edit; + QTextEdit* description_edit; + QCheckBox* helper_symbol_check; +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_SYMBOL_PROPERTIES_WIDGET_H diff --git a/src/gui/symbols/symbol_setting_dialog.cpp b/src/gui/symbols/symbol_setting_dialog.cpp new file mode 100644 index 0000000..dcd5f21 --- /dev/null +++ b/src/gui/symbols/symbol_setting_dialog.cpp @@ -0,0 +1,506 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "symbol_setting_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/objects/object.h" +#include "core/objects/text_object.h" +#include "core/symbols/combined_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/symbol.h" +#include "core/symbols/text_symbol.h" +#include "gui/main_window.h" +#include "gui/util_gui.h" +#include "gui/map/map_editor.h" +#include "gui/map/map_widget.h" +#include "gui/symbols/symbol_properties_widget.h" +#include "gui/widgets/template_list_widget.h" +#include "templates/template.h" +#include "templates/template_image.h" + + +namespace OpenOrienteering { + +SymbolSettingDialog::SymbolSettingDialog(const Symbol* source_symbol, Map* source_map, QWidget* parent) +: QDialog(parent, Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMaximizeButtonHint) +, source_map(source_map) +, source_symbol(source_symbol) +, source_symbol_copy(source_symbol->duplicate()) // don't rely on external entity +, symbol(source_symbol->duplicate()) +, symbol_modified(false) +{ + setWindowTitle(tr("Symbol settings")); + setSizeGripEnabled(true); + + symbol->setHidden(false); + + symbol_icon_label = new QLabel(); + symbol_icon_label->setPixmap(QPixmap::fromImage(symbol->getIcon(source_map))); + + symbol_text_label = new QLabel(); + updateSymbolLabel(); + + auto button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok | QDialogButtonBox::Reset | QDialogButtonBox::Help); + ok_button = button_box->button(QDialogButtonBox::Ok); + reset_button = button_box->button(QDialogButtonBox::Reset); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(reset_button, &QAbstractButton::clicked, this, &SymbolSettingDialog::reset); + connect(button_box->button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &SymbolSettingDialog::showHelp); + + preview_map = new Map(); + preview_map->setSymbolSetId(source_map->symbolSetId()); + preview_map->useColorsFrom(source_map); + preview_map->setScaleDenominator(source_map->getScaleDenominator()); + + createPreviewMap(); + + preview_widget = new MainWindow(false); + preview_controller = new MapEditorController(MapEditorController::SymbolEditor, preview_map); + preview_widget->setController(preview_controller); + preview_map_view = preview_controller->getMainWidget()->getMapView(); + double zoom_factor = 1; + if (symbol->getType() == Symbol::Point) + zoom_factor = 8; + else if (symbol->getType() == Symbol::Line || symbol->getType() == Symbol::Combined) + zoom_factor = 2; + preview_map_view->setZoom(zoom_factor * preview_map_view->getZoom()); + + properties_widget.reset(symbol->createPropertiesWidget(this)); + + QVBoxLayout* preview_layout = nullptr; + if (symbol->getType() == Symbol::Point) + { + Q_UNUSED(QT_TR_NOOP("Template:")) /// \todo Switch the following line to Util::Headline::create + auto template_label = new QLabel(tr("Template: ")); + template_file_label = new QLabel(tr("(none)")); + auto load_template_button = new QPushButton(tr("Open...")); + + center_template_button = new QToolButton(); + center_template_button->setText(tr("Center template...")); + center_template_button->setToolButtonStyle(Qt::ToolButtonTextOnly); + center_template_button->setPopupMode(QToolButton::InstantPopup); + center_template_button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + auto center_template_menu = new QMenu(center_template_button); + center_template_menu->addAction(tr("bounding box on origin"), this, SLOT(centerTemplateBBox())); + center_template_menu->addAction(tr("center of gravity on origin"), this, SLOT(centerTemplateGravity())); + center_template_button->setMenu(center_template_menu); + + center_template_button->setEnabled(false); + + auto template_layout = new QHBoxLayout(); + template_layout->addWidget(template_label); + template_layout->addWidget(template_file_label, 1); + template_layout->addWidget(load_template_button); + template_layout->addWidget(center_template_button); + + preview_layout = new QVBoxLayout(); + preview_layout->setContentsMargins(0, 0, 0, 0); + preview_layout->addLayout(template_layout); + preview_layout->addWidget(preview_widget); + + connect(load_template_button, &QAbstractButton::clicked, this, &SymbolSettingDialog::loadTemplateClicked); + } + else + { + template_file_label = nullptr; + center_template_button = nullptr; + } + + auto left_layout = new QGridLayout(); + left_layout->addWidget(symbol_icon_label, 0, 0); + left_layout->addWidget(symbol_text_label, 0, 1); + left_layout->addWidget(&*properties_widget, 1, 0, 1, 2); + left_layout->addWidget(button_box, 2, 0, 1, 2); + left_layout->setColumnStretch(1, 1); + + auto left = new QWidget(); + left->setLayout(left_layout); + left->layout(); + + QWidget* right = preview_widget; + if (preview_layout) + { + right = new QWidget(); + right->setLayout(preview_layout); + } + else + { + right->setMinimumWidth(300); + } + + auto splitter = new QSplitter(); + splitter->addWidget(left); + splitter->setCollapsible(0, false); + splitter->addWidget(right); + splitter->setCollapsible(1, true); + + auto layout = new QHBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(splitter); + setLayout(layout); + + updateButtons(); +} + +SymbolSettingDialog::~SymbolSettingDialog() = default; // not inlined + + + +std::unique_ptr SymbolSettingDialog::getNewSymbol() const +{ + auto clone = std::unique_ptr(symbol->duplicate()); + clone->setHidden(source_symbol_copy->isHidden()); + return clone; +} + +void SymbolSettingDialog::updatePreview() +{ + symbol->resetIcon(); + symbol_icon_label->setPixmap(QPixmap::fromImage(symbol->getIcon(source_map))); + preview_map->updateAllObjects(); +} + +void SymbolSettingDialog::loadTemplateClicked() +{ + auto new_template = TemplateListWidget::showOpenTemplateDialog(this, preview_controller); + if (new_template) + { + if (preview_map->getNumTemplates() > 0) + { + // Delete old template + preview_map->setTemplateAreaDirty(0); + preview_map->deleteTemplate(0); + } + + preview_map->setFirstFrontTemplate(1); + + auto temp = new_template.release(); // avoid double release after addTemplate + preview_map->addTemplate(temp, 0); + preview_map_view->setTemplateVisibility(temp, { 1, true }); + preview_map->setTemplateAreaDirty(0); + + template_file_label->setText(temp->getTemplateFilename()); + center_template_button->setEnabled(qstrcmp(temp->getTemplateType(), "TemplateImage") == 0); + } +} + +void SymbolSettingDialog::centerTemplateBBox() +{ + Q_ASSERT(preview_map->getNumTemplates() == 1); + auto temp = preview_map->getTemplate(0); + + preview_map->setTemplateAreaDirty(0); + temp->setTemplatePosition(temp->templatePosition() - MapCoord { temp->calculateTemplateBoundingBox().center() } ); + preview_map->setTemplateAreaDirty(0); +} + +void SymbolSettingDialog::centerTemplateGravity() +{ + Q_ASSERT(preview_map->getNumTemplates() == 1); + auto temp = preview_map->getTemplate(0); + auto image = reinterpret_cast(temp); + + QColor background_color = QColorDialog::getColor(Qt::white, this, tr("Select background color")); + if (!background_color.isValid()) + return; + + auto map_center_current = MapCoord { temp->templateToMap(image->calcCenterOfGravity(background_color.rgb())) }; + + preview_map->setTemplateAreaDirty(0); + temp->setTemplatePosition(temp->templatePosition() - map_center_current); + preview_map->setTemplateAreaDirty(0); +} + +void SymbolSettingDialog::createPreviewMap() +{ + symbol_icon_label->setPixmap(QPixmap::fromImage(symbol->getIcon(source_map))); + + for (auto& object : preview_objects) + preview_map->deleteObject(object, false); + + preview_objects.clear(); + + if (symbol->getType() == Symbol::Line) + { + auto line = reinterpret_cast(&*symbol); + + const int num_lines = 15; + const auto min_length = 0.5; + const auto max_length = 15.0; + const auto x_offset = -0.5 * max_length; + const auto y_offset = (0.001 * qMax(600, line->getLineWidth())) * 3.5; + + auto y_start = 0 - (y_offset * (num_lines - 1)); + + for (int i = 0; i < num_lines; ++i) + { + auto length = min_length + (max_length - min_length) * i / (num_lines - 1); + auto y = y_start + i * y_offset; + + auto path = new PathObject(line); + path->addCoordinate(0, { x_offset - length, y }); + path->addCoordinate(1, { x_offset, y }); + preview_map->addObject(path); + + preview_objects.push_back(path); + } + + const int num_circular_lines = 12; + const auto inner_radius = 4; + auto center = MapCoordF { 2*inner_radius + 0.5 * max_length, 0.5 * y_start }; + + for (int i = 0; i < num_circular_lines; ++i) + { + auto angle = M_PI * 2 * i / num_circular_lines; + auto direction = MapCoordF::fromPolar(1.0, angle); + auto length = min_length + (max_length - min_length) * i / (num_circular_lines - 1); + + auto path = new PathObject(line); + path->addCoordinate(0, MapCoord(center + direction * inner_radius)); + path->addCoordinate(1, MapCoord(center + direction * (inner_radius + length))); + preview_map->addObject(path); + + preview_objects.push_back(path); + } + + const auto snake_min_x = -1.5 * max_length; + const auto snake_max_x = 1.5 * max_length; + const auto snake_max_y = qMin(0.5 * y_start - inner_radius - max_length, y_start) - 4; + const auto snake_min_y = snake_max_y - 6; + const int snake_steps = 8u; + + auto path = new PathObject(line); + for (auto i = 0u; i < snake_steps; ++i) + { + MapCoord coord(snake_min_x + (snake_max_x - snake_min_x) * i / (snake_steps-1), snake_min_y + (i % 2) * (snake_max_y - snake_min_y)); + coord.setDashPoint(true); + path->addCoordinate(i, coord); + } + preview_map->addObject(path); + preview_objects.push_back(path); + + const auto curve_min_x = snake_min_x; + const auto curve_max_x = snake_max_x; + const auto curve_max_y = snake_min_y - 4; + const auto curve_min_y = curve_max_y - 6; + + path = new PathObject(line); + MapCoord coord = MapCoord(curve_min_x, curve_min_y); + coord.setCurveStart(true); + path->addCoordinate(coord); + coord = MapCoord(curve_min_x + (curve_max_x - curve_min_x) / 6, curve_max_y); + path->addCoordinate(coord); + coord = MapCoord(curve_min_x + 2 * (curve_max_x - curve_min_x) / 6, curve_max_y); + path->addCoordinate(coord); + coord = MapCoord(curve_min_x + 3 * (curve_max_x - curve_min_x) / 6, 0.5 * (curve_min_y + curve_max_y)); + coord.setCurveStart(true); + path->addCoordinate(coord); + coord = MapCoord(curve_min_x + 4 * (curve_max_x - curve_min_x) / 6, curve_min_y); + path->addCoordinate(coord); + coord = MapCoord(curve_min_x + 5 * (curve_max_x - curve_min_x) / 6, curve_min_y); + path->addCoordinate(coord); + coord = MapCoord(curve_max_x, curve_max_y); + path->addCoordinate(coord); + preview_map->addObject(path); + preview_objects.push_back(path); + + // Debug objects +#if 0 + // Closed path, rectangular + PathObject* path; + MapCoord coord; + + path = new PathObject(preview_map, line); + coord = MapCoord(-20, -10); + //coord.setCurveStart(true); + path->addCoordinate(coord); + coord = MapCoord(20, -10); + path->addCoordinate(coord); + coord = MapCoord(20, 10); + path->addCoordinate(coord); + coord = MapCoord(-20, 10); + path->addCoordinate(coord); + path->setPathClosed(true); + preview_map->addObject(path); + preview_objects.push_back(path); + + // Closed path, curve + PathObject* path; + MapCoord coord; + + path = new PathObject(preview_map, line); + path->setPathClosed(true); + coord = MapCoord(-20, 10); + coord.setCurveStart(true); + path->addCoordinate(coord); + coord = MapCoord(-10, 0); + path->addCoordinate(coord); + coord = MapCoord(-10, 10); + path->addCoordinate(coord); + + preview_map->addObject(path); + preview_objects.push_back(path); + + // Path with holes + PathObject* path; + MapCoord coord; + + path = new PathObject(preview_map, line); + coord = MapCoord(-20, 10); + coord.setCurveStart(true); + path->addCoordinate(coord); + coord = MapCoord(-10, 0); + path->addCoordinate(coord); + coord = MapCoord(0, -10); + path->addCoordinate(coord); + coord = MapCoord(10, 10); + path->addCoordinate(coord); + coord = MapCoord(20, 10); + coord.setHolePoint(true); + path->addCoordinate(coord); + coord = MapCoord(30, 10); + path->addCoordinate(coord); + coord = MapCoord(40, 10); + path->addCoordinate(coord); + + preview_map->addObject(path); + preview_objects.push_back(path); +#endif + } + else if (symbol->getType() == Symbol::Area) + { + const qreal half_radius = 8; + const qreal offset_y = 0; + + auto path = new PathObject(&*symbol); + path->addCoordinate(0, { -half_radius, -half_radius + offset_y }); + path->addCoordinate(1, { 0.0, -half_radius + offset_y }); + path->addCoordinate(2, { half_radius, offset_y }); + path->addCoordinate(3, { half_radius, half_radius + offset_y }); + path->addCoordinate(4, { -half_radius, half_radius + offset_y }); + preview_map->addObject(path); + + preview_objects.push_back(path); + } + else if (symbol->getType() == Symbol::Text) + { + auto text_symbol = reinterpret_cast(&*symbol); + + const QString string = tr("The quick brown fox\ntakes the routechoice\nto jump over the lazy dog\n1234567890"); + + auto object = new TextObject(text_symbol); + object->setAnchorPosition(0, 0); + object->setText(string); + object->setHorizontalAlignment(TextObject::AlignHCenter); + object->setVerticalAlignment(TextObject::AlignVCenter); + preview_map->addObject(object); + + preview_objects.push_back(object); + } + else if (symbol->getType() == Symbol::Combined) + { + const auto radius = 5.0; + + auto flags = MapCoord::Flags{}; + const auto* combined = symbol->asCombined(); + for (int i = 0; i < combined->getNumParts(); ++i) + { + auto part = combined->getPart(i); + if (part && part->getType() == Symbol::Line && part->asLine()->getDashSymbol()) + { + flags |= MapCoord::DashPoint; + break; + } + } + + auto path = new PathObject(&*symbol); + for (auto i = 0u; i < 5u; ++i) + path->addCoordinate(i, MapCoord(sin(2*M_PI * i/5.0) * radius, -cos(2*M_PI * i/5.0) * radius, flags)); + path->parts().front().setClosed(true, false); + preview_map->addObject(path); + + preview_objects.push_back(path); + } +} + +void SymbolSettingDialog::showHelp() +{ + QByteArray fragment; + fragment.reserve(100); + fragment = "editor"; + if (properties_widget->currentIndex() > 0) + { + fragment = "symbol-type-"; + fragment.append(QByteArray::number(symbol->getType())); + } + Util::showHelp(preview_controller->getWindow(), "symbol_dock_widget.html", fragment); +} + +void SymbolSettingDialog::reset() +{ + auto duplicate = std::unique_ptr(source_symbol_copy->duplicate()); + swap(symbol, duplicate); + symbol->setHidden(false); + createPreviewMap(); + properties_widget->reset(&*symbol); + setSymbolModified(false); +} + +void SymbolSettingDialog::setSymbolModified(bool modified) +{ + symbol_modified = modified; + updateSymbolLabel(); + updatePreview(); + updateButtons(); +} + +void SymbolSettingDialog::updateSymbolLabel() +{ + QString number = symbol->getNumberAsString(); + if (number.isEmpty()) + number = QString::fromLatin1("???"); + QString name = source_map->translate(symbol->getName()); + if (name.isEmpty()) + name = tr("- unnamed -"); + symbol_text_label->setText(QLatin1String("") + number + QLatin1String(" ") + name + QLatin1String("")); +} + +void SymbolSettingDialog::updateButtons() +{ + ok_button->setEnabled(symbol_modified && symbol->getNumberComponent(0)>=0 && !symbol->getName().isEmpty()); + reset_button->setEnabled(symbol_modified); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/symbol_setting_dialog.h b/src/gui/symbols/symbol_setting_dialog.h new file mode 100644 index 0000000..9748ec6 --- /dev/null +++ b/src/gui/symbols/symbol_setting_dialog.h @@ -0,0 +1,186 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_SETTING_DIALOG_H +#define OPENORIENTEERING_SYMBOL_SETTING_DIALOG_H + +#include +#include + +#include +#include + +class QLabel; +class QPushButton; +class QToolButton; +class QWidget; + +namespace OpenOrienteering { + +class MainWindow; +class Map; +class MapEditorController; +class MapView; +class Object; +class Symbol; +class SymbolPropertiesWidget; + + +/** + * A dialog for editing symbol properties. + * + * The specific symbol properties must be edited in tabs provided by the symbol. + */ +class SymbolSettingDialog : public QDialog +{ +Q_OBJECT +public: + /** + * Constructs a new dialog for a given symbol and map. + */ + SymbolSettingDialog(const Symbol* source_symbol, Map* source_map, QWidget* parent); + + /** + * Destructs the dialog and cleans up temporary objects. + */ + ~SymbolSettingDialog() override; + + /** + * Returns a copy of the currently edited symbol. + */ + std::unique_ptr getNewSymbol() const; + + /** + * Returns a pointer to the unmodifed symbol which is currently edited. + * Use getUnmodifiedSymbolCopy() instead if you need to access the object! + */ + inline const Symbol* getUnmodifiedSymbol() const { return source_symbol; } + + /** + * Returns a pointer to a copy of the unmodifed symbol which is currently edited. + */ + inline const Symbol* getUnmodifiedSymbolCopy() const { return &*source_symbol_copy; } + + /** + * Returns true if the edited symbol has modfications. + */ + inline bool isSymbolModified() const { return symbol_modified; } + + /** + * Returns the Map of the unmodified symbol. + */ + inline Map* getSourceMap() { return source_map; } + + /** + * Returns the preview map of this dialog. + */ + inline Map* getPreviewMap() { return preview_map; } + + /** + * Return the MapEditorController which handles this dialog's preview map. + */ + inline MapEditorController* getPreviewController() { return preview_controller; } + +public slots: + /** + * Shows this dialog's help page. + */ + void showHelp(); + + /** + * Resets the edited symbol to the state of the unmodified symbol from the map. + */ + void reset(); + + /** + * Sets the modification status of the dialog, and triggers a screen update. + */ + void setSymbolModified(bool modified = true); + + /** + * Updates the label which is the headline to the dialog. + */ + void updateSymbolLabel(); + + /** + * Updates the preview from the current symbol settings. + */ + void updatePreview(); + + /** + * Updates the state (enabled/disabled) of the dialog buttons. + */ + void updateButtons(); + +protected slots: + /** + * Opens a dialog for loading a template to the preview map. + */ + void loadTemplateClicked(); + + /** + * Centers the preview map template relative to its bounding box. + */ + void centerTemplateBBox(); + + /** + * Centers the preview map template relative to its center of gravity, + * leaving out a selected background color. + */ + void centerTemplateGravity(); + +protected: + /** + * Populates the preview map for the symbol. + */ + void createPreviewMap(); + +private: + Map* source_map; + const Symbol* source_symbol; + std::unique_ptr source_symbol_copy; + + // properties_widget must be deleted before symbol. + std::unique_ptr symbol; + std::unique_ptr properties_widget; + + Map* preview_map; + MainWindow* preview_widget; + MapView* preview_map_view; + MapEditorController* preview_controller; + std::vector preview_objects; + + QLabel* template_file_label; + QToolButton* center_template_button; + + QPushButton* ok_button; + QPushButton* reset_button; + + QLabel* symbol_icon_label; + QLabel* symbol_text_label; + + bool symbol_modified; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/symbols/text_symbol_settings.cpp b/src/gui/symbols/text_symbol_settings.cpp new file mode 100644 index 0000000..e3c0db1 --- /dev/null +++ b/src/gui/symbols/text_symbol_settings.cpp @@ -0,0 +1,605 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "text_symbol_settings.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/symbols/symbol.h" +#include "core/symbols/text_symbol.h" +#include "gui/util_gui.h" +#include "gui/symbols/symbol_setting_dialog.h" +#include "gui/widgets/color_dropdown.h" +#include "util/backports.h" + + +namespace OpenOrienteering { + +// ### DetermineFontSizeDialog ### + +/** + * \todo Move translation items to TextSymbolSettings class. + */ +class DetermineFontSizeDialog +{ +public: + Q_DECLARE_TR_FUNCTIONS(OpenOrienteering::DetermineFontSizeDialog) +}; + + + +// ### TextSymbol ### + +SymbolPropertiesWidget* TextSymbol::createPropertiesWidget(SymbolSettingDialog* dialog) +{ + return new TextSymbolSettings(this, dialog); +} + + +// ### TextSymbolSettings ### + +TextSymbolSettings::TextSymbolSettings(TextSymbol* symbol, SymbolSettingDialog* dialog) +: SymbolPropertiesWidget(symbol, dialog), + symbol(symbol), + dialog(dialog) +{ + auto map = dialog->getPreviewMap(); + react_to_changes = true; + + auto text_tab = new QWidget(); + addPropertiesGroup(tr("Text settings"), text_tab); + + auto layout = new QFormLayout(); + text_tab->setLayout(layout); + + font_edit = new QFontComboBox(); + layout->addRow(tr("Font family:"), font_edit); + + // 0.04 pt font size is app. 0.01 mm letter height, the non-zero minimum + font_size_edit = Util::SpinBox::create(2, 0.04, 40000.0, tr("pt")); + layout->addRow(tr("Font size:"), font_size_edit); + + auto letter_size_layout = new QHBoxLayout(); + letter_size_layout->setMargin(0); + + letter_size_layout->addWidget(new QLabel(::OpenOrienteering::DetermineFontSizeDialog::tr("Letter:"))); + //: "A" is the default letter which is used for determining letter height. + letter_edit = new QLineEdit(DetermineFontSizeDialog::tr("A")); + letter_edit->setMaxLength(3); + letter_size_layout->addWidget(letter_edit); + + letter_size_layout->addSpacing(8); + + letter_size_layout->addWidget(new QLabel(::OpenOrienteering::DetermineFontSizeDialog::tr("Height:"))); + letter_size_edit = Util::SpinBox::create(2, 0.01, 10000.0, tr("mm")); + letter_size_layout->addWidget(letter_size_edit); + + layout->addRow(new QWidget(), letter_size_layout); + + layout->addItem(Util::SpacerItem::create(this)); + + color_edit = new ColorDropDown(map, symbol->getColor()); + layout->addRow(tr("Text color:"), color_edit); + + auto text_style_layout = new QVBoxLayout(); + bold_check = new QCheckBox(tr("bold")); + text_style_layout->addWidget(bold_check); + italic_check = new QCheckBox(tr("italic")); + text_style_layout->addWidget(italic_check); + underline_check = new QCheckBox(tr("underlined")); + text_style_layout->addWidget(underline_check); + + layout->addRow(tr("Text style:"), text_style_layout); + + layout->addItem(Util::SpacerItem::create(this)); + + line_spacing_edit = Util::SpinBox::create(1, 0.0, 999999.9, tr("%")); + layout->addRow(tr("Line spacing:"), line_spacing_edit); + + paragraph_spacing_edit = Util::SpinBox::create(2, -999999.9, 999999.9, tr("mm")); + layout->addRow(tr("Paragraph spacing:"), paragraph_spacing_edit); + + character_spacing_edit = Util::SpinBox::create(1, -999999.9, 999999.9, tr("%")); + layout->addRow(tr("Character spacing:"), character_spacing_edit); + + kerning_check = new QCheckBox(tr("Kerning")); + layout->addRow(QString{}, kerning_check); + + layout->addItem(Util::SpacerItem::create(this)); + + icon_text_edit = new QLineEdit(); + icon_text_edit->setMaxLength(3); + layout->addRow(tr("Symbol icon text:"), icon_text_edit); + + layout->addItem(Util::SpacerItem::create(this)); + + framing_check = new QCheckBox(tr("Framing")); + layout->addRow(framing_check); + + ocad_compat_check = new QCheckBox(tr("OCAD compatibility settings")); + layout->addRow(ocad_compat_check); + + + framing_widget = new QWidget(); + addPropertiesGroup(tr("Framing"), framing_widget); + + auto framing_layout = new QFormLayout(); + framing_widget->setLayout(framing_layout); + + framing_color_edit = new ColorDropDown(map, symbol->getFramingColor()); + framing_layout->addRow(tr("Framing color:"), framing_color_edit); + + framing_line_radio = new QRadioButton(tr("Line framing")); + framing_layout->addRow(framing_line_radio); + + framing_line_half_width_edit = Util::SpinBox::create(1, 0.0, 999999.9); + framing_layout->addRow(tr("Width:"), framing_line_half_width_edit); + + framing_shadow_radio = new QRadioButton(tr("Shadow framing")); + framing_layout->addRow(framing_shadow_radio); + + framing_shadow_x_offset_edit = Util::SpinBox::create(1, -999999.9, 999999.9); + framing_layout->addRow(tr("Left/Right Offset:"), framing_shadow_x_offset_edit); + + framing_shadow_y_offset_edit = Util::SpinBox::create(1, -999999.9, 999999.9); + framing_layout->addRow(tr("Top/Down Offset:"), framing_shadow_y_offset_edit); + + + ocad_compat_widget = new QWidget(); + addPropertiesGroup(tr("OCAD compatibility"), ocad_compat_widget); + + auto ocad_compat_layout = new QFormLayout(); + ocad_compat_widget->setLayout(ocad_compat_layout); + + ocad_compat_layout->addRow(Util::Headline::create(tr("Line below paragraphs"))); + + line_below_check = new QCheckBox(tr("enabled")); + ocad_compat_layout->addRow(line_below_check); + + line_below_width_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + ocad_compat_layout->addRow(tr("Line width:"), line_below_width_edit); + + line_below_color_edit = new ColorDropDown(map); + ocad_compat_layout->addRow(tr("Line color:"), line_below_color_edit); + + line_below_distance_edit = Util::SpinBox::create(2, 0.0, 999999.9, tr("mm")); + ocad_compat_layout->addRow(tr("Distance from baseline:"), line_below_distance_edit); + + ocad_compat_layout->addItem(Util::SpacerItem::create(this)); + + ocad_compat_layout->addRow(Util::Headline::create(tr("Custom tabulator positions"))); + + custom_tab_list = new QListWidget(); + ocad_compat_layout->addRow(custom_tab_list); + + auto custom_tabs_button_layout = new QHBoxLayout(); + custom_tab_add = new QPushButton(QIcon(QStringLiteral(":/images/plus.png")), QString{}); + custom_tabs_button_layout->addWidget(custom_tab_add); + custom_tab_remove = new QPushButton(QIcon(QStringLiteral(":/images/minus.png")), QString{}); + custom_tab_remove->setEnabled(false); + custom_tabs_button_layout->addWidget(custom_tab_remove); + custom_tabs_button_layout->addStretch(1); + + ocad_compat_layout->addRow(custom_tabs_button_layout); + + + updateGeneralContents(); + updateFramingContents(); + updateCompatibilityContents(); + + connect(font_edit, &QFontComboBox::currentFontChanged, this, &TextSymbolSettings::fontChanged); + connect(font_size_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::fontSizeChanged); + connect(letter_edit, &QLineEdit::textEdited, this, &TextSymbolSettings::letterSizeChanged); + connect(letter_size_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::letterSizeChanged); + connect(color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &TextSymbolSettings::colorChanged); + connect(bold_check, &QAbstractButton::clicked, this, &TextSymbolSettings::checkToggled); + connect(italic_check, &QAbstractButton::clicked, this, &TextSymbolSettings::checkToggled); + connect(underline_check, &QAbstractButton::clicked, this, &TextSymbolSettings::checkToggled); + connect(line_spacing_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::spacingChanged); + connect(paragraph_spacing_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::spacingChanged); + connect(character_spacing_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::spacingChanged); + connect(kerning_check, &QAbstractButton::clicked, this, &TextSymbolSettings::checkToggled); + connect(icon_text_edit, &QLineEdit::textEdited, this, &TextSymbolSettings::iconTextEdited); + connect(framing_check, &QAbstractButton::clicked, this, &TextSymbolSettings::framingCheckClicked); + connect(ocad_compat_check, &QAbstractButton::clicked, this, &TextSymbolSettings::ocadCompatibilityButtonClicked); + + connect(framing_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &TextSymbolSettings::framingColorChanged); + connect(framing_line_radio, &QAbstractButton::clicked, this, &TextSymbolSettings::framingModeChanged); + connect(framing_line_half_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::framingSettingChanged); + connect(framing_shadow_radio, &QAbstractButton::clicked, this, &TextSymbolSettings::framingModeChanged); + connect(framing_shadow_x_offset_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::framingSettingChanged); + connect(framing_shadow_y_offset_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::framingSettingChanged); + + connect(line_below_check, &QAbstractButton::clicked, this, &TextSymbolSettings::lineBelowCheckClicked); + connect(line_below_color_edit, QOverload::of(&QComboBox::currentIndexChanged), this, &TextSymbolSettings::lineBelowSettingChanged); + connect(line_below_width_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::lineBelowSettingChanged); + connect(line_below_distance_edit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &TextSymbolSettings::lineBelowSettingChanged); + connect(custom_tab_list, &QListWidget::currentRowChanged, this, &TextSymbolSettings::customTabRowChanged); + connect(custom_tab_add, &QAbstractButton::clicked, this, &TextSymbolSettings::addCustomTabClicked); + connect(custom_tab_remove, &QAbstractButton::clicked, this, &TextSymbolSettings::removeCustomTabClicked); +} + +TextSymbolSettings::~TextSymbolSettings() = default; + + + +void TextSymbolSettings::fontChanged(const QFont& font) +{ + if (!react_to_changes) + return; + + symbol->font_family = font.family(); + symbol->updateQFont(); + updateLetterSizeEdit(); + emit propertiesModified(); +} + +void TextSymbolSettings::fontSizeChanged(double value) +{ + if (!react_to_changes) + return; + + symbol->font_size = qRound(value * 25400.0 / 72.0); + symbol->updateQFont(); + updateLetterSizeEdit(); + emit propertiesModified(); +} + +void TextSymbolSettings::updateFontSizeEdit() +{ + QSignalBlocker block { font_size_edit }; + font_size_edit->setValue(0.01 * qRound(symbol->font_size * 7.2 / 25.4)); +} + +void TextSymbolSettings::letterSizeChanged() +{ + auto letter_height = calculateLetterHeight(); + if (letter_height > 0.0) + { + symbol->font_size = qRound(1000.0 * letter_size_edit->value() / letter_height); + symbol->updateQFont(); + updateFontSizeEdit(); + emit propertiesModified(); + } +} + +qreal TextSymbolSettings::calculateLetterHeight() const +{ + QPainterPath path; + path.addText(0.0, 0.0, symbol->getQFont(), letter_edit->text()); + return path.boundingRect().height() / TextSymbol::internal_point_size; +} + +void TextSymbolSettings::updateLetterSizeEdit() +{ + auto letter_height = calculateLetterHeight(); + if (letter_height > 0.0) + { + QSignalBlocker block { letter_size_edit }; + letter_size_edit->setValue(0.01 * qRound(symbol->font_size * letter_height / 10.0)); + } +} + +void TextSymbolSettings::colorChanged() +{ + if (!react_to_changes) + return; + + symbol->color = color_edit->color(); + symbol->updateQFont(); + emit propertiesModified(); +} + +void TextSymbolSettings::checkToggled(bool checked) +{ + Q_UNUSED(checked); + + if (!react_to_changes) + return; + + symbol->bold = bold_check->isChecked(); + symbol->italic = italic_check->isChecked(); + symbol->underline = underline_check->isChecked(); + symbol->kerning = kerning_check->isChecked(); + symbol->updateQFont(); + emit propertiesModified(); +} + +void TextSymbolSettings::spacingChanged(double value) +{ + Q_UNUSED(value); + + if (!react_to_changes) + return; + + symbol->line_spacing = 0.01 * line_spacing_edit->value(); + symbol->paragraph_spacing = qRound(1000.0 * paragraph_spacing_edit->value()); + symbol->character_spacing = 0.01 * character_spacing_edit->value(); + symbol->updateQFont(); + emit propertiesModified(); +} + +void TextSymbolSettings::iconTextEdited(const QString& text) +{ + if (!react_to_changes) + return; + + symbol->icon_text = text; + emit propertiesModified(); +} + +void TextSymbolSettings::framingColorChanged() +{ + if (!react_to_changes) + return; + + symbol->framing_color = framing_color_edit->color(); + updateFramingContents(); + emit propertiesModified(); +} + +void TextSymbolSettings::framingModeChanged() +{ + if (!react_to_changes) + return; + + if (framing_line_radio->isChecked()) + symbol->framing_mode = TextSymbol::LineFraming; + else if (framing_shadow_radio->isChecked()) + symbol->framing_mode = TextSymbol::ShadowFraming; + updateFramingContents(); + emit propertiesModified(); +} + +void TextSymbolSettings::framingSettingChanged() +{ + if (!react_to_changes) + return; + + symbol->framing_line_half_width = qRound(1000.0 * framing_line_half_width_edit->value()); + symbol->framing_shadow_x_offset = qRound(1000.0 * framing_shadow_x_offset_edit->value()); + symbol->framing_shadow_y_offset = qRound(-1000.0 * framing_shadow_y_offset_edit->value()); + emit propertiesModified(); +} + +void TextSymbolSettings::framingCheckClicked(bool checked) +{ + if (!react_to_changes) + return; + + setTabEnabled(indexOf(framing_widget), checked); + symbol->framing = checked; + emit propertiesModified(); +} + +void TextSymbolSettings::ocadCompatibilityButtonClicked(bool checked) +{ + if (!react_to_changes) + return; + + setTabEnabled(indexOf(ocad_compat_widget), checked); + updateCompatibilityCheckEnabled(); +} + +void TextSymbolSettings::lineBelowCheckClicked(bool checked) +{ + if (!react_to_changes) + return; + + symbol->line_below = checked; + if (checked) + { + // Set defaults such that the line becomes visible + if (symbol->line_below_width == 0) + line_below_width_edit->setValue(0.1); + if (!symbol->line_below_color) + line_below_color_edit->setCurrentIndex(1); + } + + symbol->updateQFont(); + emit propertiesModified(); + + line_below_color_edit->setEnabled(checked); + line_below_width_edit->setEnabled(checked); + line_below_distance_edit->setEnabled(checked); + updateCompatibilityCheckEnabled(); +} + +void TextSymbolSettings::lineBelowSettingChanged() +{ + if (!react_to_changes) + return; + + symbol->line_below_color = line_below_color_edit->color(); + symbol->line_below_width = qRound(1000.0 * line_below_width_edit->value()); + symbol->line_below_distance = qRound(1000.0 * line_below_distance_edit->value()); + symbol->updateQFont(); + emit propertiesModified(); + updateCompatibilityCheckEnabled(); +} + +void TextSymbolSettings::customTabRowChanged(int row) +{ + custom_tab_remove->setEnabled(row >= 0); +} + +void TextSymbolSettings::addCustomTabClicked() +{ + bool ok = false; + // FIXME: Unit of measurement display in unusual way + double position = QInputDialog::getDouble(dialog, + tr("Add custom tabulator"), + QStringLiteral("%1 (%2)").arg(tr("Position:"), tr("mm")), + 0, 0, 999999, 3, &ok); + if (ok) + { + int int_position = qRound(1000 * position); + + int row = symbol->getNumCustomTabs(); + for (int i = 0; i < symbol->getNumCustomTabs(); ++i) + { + if (int_position == symbol->custom_tabs[i]) + return; // do not add a double position + else if (int_position < symbol->custom_tabs[i]) + { + row = i; + break; + } + } + + custom_tab_list->insertItem(row, locale().toString(position, 'g', 3) + QLatin1Char(' ') + tr("mm")); + custom_tab_list->setCurrentRow(row); + symbol->custom_tabs.insert(symbol->custom_tabs.begin() + row, int_position); + emit propertiesModified(); + updateCompatibilityCheckEnabled(); + } +} + +void TextSymbolSettings::removeCustomTabClicked() +{ + if (auto item = custom_tab_list->currentItem()) + { + int row = custom_tab_list->row(item); + delete custom_tab_list->item(row); + custom_tab_list->setCurrentRow(row - 1); + symbol->custom_tabs.erase(symbol->custom_tabs.begin() + row); + emit propertiesModified(); + updateCompatibilityCheckEnabled(); + } +} + +void TextSymbolSettings::updateGeneralContents() +{ + react_to_changes = false; + font_edit->setCurrentFont(QFont(symbol->font_family)); + updateFontSizeEdit(); + updateLetterSizeEdit(); + color_edit->setColor(symbol->color); + bold_check->setChecked(symbol->bold); + italic_check->setChecked(symbol->italic); + underline_check->setChecked(symbol->underline); + line_spacing_edit->setValue(100.0 * symbol->line_spacing); + paragraph_spacing_edit->setValue(0.001 * symbol->paragraph_spacing); + character_spacing_edit->setValue(100 * symbol->character_spacing); + kerning_check->setChecked(symbol->kerning); + icon_text_edit->setText(symbol->getIconText()); + framing_check->setChecked(symbol->framing); + ocad_compat_check->setChecked(symbol->line_below || symbol->getNumCustomTabs() > 0); + react_to_changes = true; + + framingCheckClicked(framing_check->isChecked()); + ocadCompatibilityButtonClicked(ocad_compat_check->isChecked()); +} + +void TextSymbolSettings::updateCompatibilityCheckEnabled() +{ + ocad_compat_check->setEnabled(!symbol->line_below && symbol->getNumCustomTabs() == 0); +} + +void TextSymbolSettings::updateFramingContents() +{ + react_to_changes = false; + + framing_color_edit->setColor(symbol->framing_color); + framing_line_radio->setChecked(symbol->framing_mode == TextSymbol::LineFraming); + framing_line_half_width_edit->setValue(0.001 * symbol->framing_line_half_width); + framing_shadow_radio->setChecked(symbol->framing_mode == TextSymbol::ShadowFraming); + framing_shadow_x_offset_edit->setValue(0.001 * symbol->framing_shadow_x_offset); + framing_shadow_y_offset_edit->setValue(-0.001 * symbol->framing_shadow_y_offset); + + + framing_line_radio->setEnabled(symbol->framing_color); + framing_shadow_radio->setEnabled(symbol->framing_color); + if (!symbol->framing_color) + { + framing_line_half_width_edit->setEnabled(false); + framing_shadow_x_offset_edit->setEnabled(false); + framing_shadow_y_offset_edit->setEnabled(false); + } + else + { + framing_line_half_width_edit->setEnabled(symbol->framing_mode == TextSymbol::LineFraming); + framing_shadow_x_offset_edit->setEnabled(symbol->framing_mode == TextSymbol::ShadowFraming); + framing_shadow_y_offset_edit->setEnabled(symbol->framing_mode == TextSymbol::ShadowFraming); + } + + react_to_changes = true; +} + +void TextSymbolSettings::updateCompatibilityContents() +{ + react_to_changes = false; + line_below_check->setChecked(symbol->line_below); + line_below_width_edit->setValue(0.001 * symbol->line_below_width); + line_below_color_edit->setColor(symbol->line_below_color); + line_below_distance_edit->setValue(0.001 * symbol->line_below_distance); + + line_below_color_edit->setEnabled(symbol->line_below); + line_below_width_edit->setEnabled(symbol->line_below); + line_below_distance_edit->setEnabled(symbol->line_below); + + custom_tab_list->clear(); + for (int i = 0; i < symbol->getNumCustomTabs(); ++i) + custom_tab_list->addItem(locale().toString(0.001 * symbol->getCustomTab(i), 'g', 3) + QLatin1Char(' ') + tr("mm")); + + react_to_changes = true; +} + +void TextSymbolSettings::reset(Symbol* symbol) +{ + Q_ASSERT(symbol->getType() == Symbol::Text); + + SymbolPropertiesWidget::reset(symbol); + this->symbol = reinterpret_cast(symbol); + + updateGeneralContents(); + updateFramingContents(); + updateCompatibilityContents(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/symbols/text_symbol_settings.h b/src/gui/symbols/text_symbol_settings.h new file mode 100644 index 0000000..15b92c1 --- /dev/null +++ b/src/gui/symbols/text_symbol_settings.h @@ -0,0 +1,135 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_TEXT_SYMBOL_SETTINGS_H +#define OPENORIENTEERING_TEXT_SYMBOL_SETTINGS_H + +#include +#include +#include + +#include "gui/symbols/symbol_properties_widget.h" + +class QCheckBox; +class QDoubleSpinBox; +class QFont; +class QFontComboBox; +class QLineEdit; +class QListWidget; +class QPushButton; +class QRadioButton; +class QWidget; + +namespace OpenOrienteering { + +class ColorDropDown; +class Symbol; +class SymbolSettingDialog; +class TextSymbol; + + +class TextSymbolSettings : public SymbolPropertiesWidget +{ +Q_OBJECT +public: + TextSymbolSettings(TextSymbol* symbol, SymbolSettingDialog* dialog); + ~TextSymbolSettings() override; + + void reset(Symbol* symbol) override; + + void updateGeneralContents(); + + void updateFramingContents(); + + void updateCompatibilityCheckEnabled(); + + void updateCompatibilityContents(); + +protected slots: + void fontChanged(const QFont& font); + void fontSizeChanged(double value); + void letterSizeChanged(); + void colorChanged(); + void checkToggled(bool checked); + void spacingChanged(double value); + void iconTextEdited(const QString& text); + + void framingCheckClicked(bool checked); + void framingColorChanged(); + void framingModeChanged(); + void framingSettingChanged(); + + void ocadCompatibilityButtonClicked(bool checked); + void lineBelowCheckClicked(bool checked); + void lineBelowSettingChanged(); + void customTabRowChanged(int row); + void addCustomTabClicked(); + void removeCustomTabClicked(); + +protected: + void updateFontSizeEdit(); + void updateLetterSizeEdit(); + qreal calculateLetterHeight() const; + +private: + TextSymbol* symbol; + SymbolSettingDialog* dialog; + + ColorDropDown* color_edit; + QFontComboBox* font_edit; + QDoubleSpinBox* font_size_edit; + QLineEdit* letter_edit; + QDoubleSpinBox* letter_size_edit; + QCheckBox* bold_check; + QCheckBox* italic_check; + QCheckBox* underline_check; + QDoubleSpinBox* line_spacing_edit; + QDoubleSpinBox* paragraph_spacing_edit; + QDoubleSpinBox* character_spacing_edit; + QCheckBox* kerning_check; + QLineEdit* icon_text_edit; + QCheckBox* framing_check; + QCheckBox* ocad_compat_check; + + QWidget* framing_widget; + ColorDropDown* framing_color_edit; + QRadioButton* framing_line_radio; + QDoubleSpinBox* framing_line_half_width_edit; + QRadioButton* framing_shadow_radio; + QDoubleSpinBox* framing_shadow_x_offset_edit; + QDoubleSpinBox* framing_shadow_y_offset_edit; + + QWidget* ocad_compat_widget; + QCheckBox* line_below_check; + QDoubleSpinBox* line_below_width_edit; + ColorDropDown* line_below_color_edit; + QDoubleSpinBox* line_below_distance_edit; + QListWidget* custom_tab_list; + QPushButton* custom_tab_add; + QPushButton* custom_tab_remove; + + bool react_to_changes; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/task_dialog.cpp b/src/gui/task_dialog.cpp new file mode 100644 index 0000000..50722b4 --- /dev/null +++ b/src/gui/task_dialog.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "task_dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/backports.h" + + +namespace OpenOrienteering { + +TaskDialog::TaskDialog(QWidget* parent, const QString& title, const QString& text, QDialogButtonBox::StandardButtons buttons) + : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) +{ + setWindowTitle(title); + + QLabel* text_label = nullptr; + if (!text.isEmpty()) + text_label = new QLabel(text); + + button_box = nullptr; + if (buttons != QDialogButtonBox::NoButton) + button_box = new QDialogButtonBox(buttons); + + layout = new QVBoxLayout(); + if (text_label) + layout->addWidget(text_label); + if (button_box) + layout->addWidget(button_box); + setLayout(layout); + + signal_mapper = new QSignalMapper(this); + connect(signal_mapper, QOverload::of(&QSignalMapper::mapped), this, QOverload::of(&TaskDialog::buttonClicked)); + connect(button_box, &QDialogButtonBox::clicked, this, QOverload::of(&TaskDialog::buttonClicked)); +} + +QCommandLinkButton* TaskDialog::addCommandButton(const QString& text, const QString& description) +{ + QCommandLinkButton* button = new QCommandLinkButton(text, description); + signal_mapper->setMapping(button, button); + connect(button, QOverload::of(&QAbstractButton::clicked), signal_mapper, QOverload<>::of(&QSignalMapper::map)); + + layout->insertWidget(layout->count() - (button_box ? 1 : 0), button); + return button; +} + +void TaskDialog::buttonClicked(QWidget* button) +{ + buttonClicked(static_cast(button)); +} +void TaskDialog::buttonClicked(QAbstractButton* button) +{ + clicked_button = button; + if (button_box->buttonRole(clicked_button) == QDialogButtonBox::RejectRole) + reject(); + else + accept(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/task_dialog.h b/src/gui/task_dialog.h new file mode 100644 index 0000000..a0d1587 --- /dev/null +++ b/src/gui/task_dialog.h @@ -0,0 +1,115 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_UTIL_TASK_DIALOG_H +#define OPENORIENTEERING_UTIL_TASK_DIALOG_H + +#include +#include +#include +#include + +class QAbstractButton; +class QCommandLinkButton; +class QPushButton; +class QSignalMapper; +class QVBoxLayout; +class QWidget; + +namespace OpenOrienteering { + + +/** + * Shows a dialog similar to a message box, but with the option to add + * one or multiple command buttons to be able to offer more and well-described + * actions to the user. + * + * Using this class might look like this: + * TaskDialog dialog(parent, "Title", "Text", QDialogButtonBox::Abort); + * QAbstractButton* do_button = dialog.addCommandButton("Do it", "description ..."); + * QAbstractButton* dont_button = dialog.addCommandButton("Don't do it", "description ..."); + * dialog.exec(); + * if (dialog.clicked_button() == do_button) + * ... + * else if (dialog.clicked_button() == dont_button) + * ... + * else // if (dialog.clicked_button() == dialog.button(QDialogButtonBox::Abort)) + * ... + */ +class TaskDialog : public QDialog +{ +Q_OBJECT +public: + /** + * Constructs a TaskDialog. + * + * @param parent QWidget parent for the dialog. + * @param title Dialog title. + * @param text Text which will be displayed in a label at the top. + * @param buttons Dialog standard buttons. + */ + TaskDialog( + QWidget* parent, + const QString& title, + const QString& text, + QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::NoButton + ); + + /** + * Adds a command button to the dialog. Returns a pointer to the created button. + * + * @param text The action text, will be emphasized + * @param description Additional description text for the action, optional + */ + QCommandLinkButton* addCommandButton( + const QString& text, + const QString& description + ); + + + /** After the dialog has been shown, returns the button which + * was clicked by the user. */ + inline QAbstractButton* clickedButton() const + { + return clicked_button; + } + + /** Returns a button pointer for a specific standard dialog button. + * Use this for comparing the pointer with the result of clickedButton(). */ + inline QPushButton* button(QDialogButtonBox::StandardButton which) const + { + return button_box->button(which); + } + +private slots: + void buttonClicked(QWidget* button); + void buttonClicked(QAbstractButton* button); + +private: + QAbstractButton* clicked_button; + QVBoxLayout* layout; + QDialogButtonBox* button_box; + QSignalMapper* signal_mapper; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/text_browser_dialog.cpp b/src/gui/text_browser_dialog.cpp new file mode 100644 index 0000000..90404a6 --- /dev/null +++ b/src/gui/text_browser_dialog.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "text_browser_dialog.h" + +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include + +#include "gui/widgets/text_browser.h" +#include "util/backports.h" + + +namespace OpenOrienteering { + +TextBrowserDialog::TextBrowserDialog(QWidget* parent) +: QDialog(parent) +, text_browser(new TextBrowser()) +{ + if (parent) + { + setWindowModality(Qt::WindowModal); + } + + QVBoxLayout* layout = new QVBoxLayout(); + setLayout(layout); + + int left, top, right, bottom; + layout->getContentsMargins(&left, &top, &right, &bottom); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + text_browser->setOpenExternalLinks(true); + layout->addWidget(text_browser); + + QHBoxLayout* buttons_layout = new QHBoxLayout(); + buttons_layout->setContentsMargins(left, top, right, bottom); + + QPushButton* back_button = new QPushButton(QIcon(QStringLiteral(":/images/arrow-left.png")), QApplication::translate("QFileDialog", "Back")); + back_button->setEnabled(false); + buttons_layout->addWidget(back_button); + + buttons_layout->addStretch(1); + + QPushButton* close_button = new QPushButton(QApplication::translate("QPlatformTheme", "Close")); + close_button->setDefault(true); + buttons_layout->addWidget(close_button); + + layout->addLayout(buttons_layout); + + connect(text_browser, &QTextBrowser::sourceChanged, this, &TextBrowserDialog::sourceChanged); + connect(text_browser, &QTextBrowser::textChanged, this, &TextBrowserDialog::updateWindowTitle); + connect(text_browser, &QTextBrowser::backwardAvailable, back_button, &TextBrowserDialog::setEnabled); + connect(text_browser, QOverload::of(&QTextBrowser::highlighted), this, &TextBrowserDialog::highlighted); + connect(back_button, &QPushButton::clicked, text_browser, &QTextBrowser::backward); + connect(close_button, &QPushButton::clicked, this, &TextBrowserDialog::accept); + +#if defined(Q_OS_ANDROID) + QScroller::grabGesture(text_browser->viewport(), QScroller::TouchGesture); + // Disable selection, so that it doesn't interfere with scrolling + text_browser->setTextInteractionFlags(Qt::TextInteractionFlags(Qt::TextBrowserInteraction) & ~Qt::TextSelectableByMouse); + // Note: Only the above combination of QScroller::LeftMouseButtonGesture + // and ~Qt::TextSelectableByMouse seems to achieve the desired behaviour + // (touch-scrolling without selecting text.) + + setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) + | Qt::WindowMaximized); +#endif +} + + +TextBrowserDialog::TextBrowserDialog(const QUrl& initial_url, QWidget* parent) +: TextBrowserDialog(parent) +{ + text_browser->setSource(initial_url); + text_browser->document()->adjustSize(); // needed for sizeHint() +} + + +TextBrowserDialog::TextBrowserDialog(const QString& text, QWidget* parent) + : TextBrowserDialog(parent) +{ + text_browser->setText(text); + text_browser->document()->adjustSize(); // needed for sizeHint() +} + + +TextBrowserDialog::~TextBrowserDialog() = default; + + + +QSize TextBrowserDialog::sizeHint() const +{ + QSize size = text_browser->document()->size().toSize(); + if (text_browser->verticalScrollBar()) + size.rwidth() += text_browser->verticalScrollBar()->width(); + return size; +} + +void TextBrowserDialog::sourceChanged(const QUrl&) +{ + ; // Nothing, to be overriden in subclasses +} + +void TextBrowserDialog::updateWindowTitle() +{ + setWindowTitle(text_browser->documentTitle()); +} + +void TextBrowserDialog::highlighted(const QString& link) +{ + if (link.isEmpty()) + { + QToolTip::hideText(); + } + else + { + /// @todo: Position near mouse pointer + auto tooltip_pos = pos() + text_browser->pos(); + tooltip_pos.ry() += text_browser->height(); + QToolTip::showText(tooltip_pos, link, this, {}); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/text_browser_dialog.h b/src/gui/text_browser_dialog.h new file mode 100644 index 0000000..e75ff02 --- /dev/null +++ b/src/gui/text_browser_dialog.h @@ -0,0 +1,90 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps + * Copyright 2012-2015, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_TEXT_BROWSER_DIALOG_H +#define OPENORIENTEERING_TEXT_BROWSER_DIALOG_H + +#include +#include +#include +#include + +class QWidget; + +class QTextBrowser; +class QUrl; + + +namespace OpenOrienteering { + +/** + * @brief A dialog for basic browsing of HTML pages. + */ +class TextBrowserDialog : public QDialog +{ +Q_OBJECT +protected: + explicit TextBrowserDialog(QWidget* parent = nullptr); + +public: + /** + * @brief Construct a new dialog and loads the text from the initial_url. + */ + TextBrowserDialog(const QUrl& initial_url, QWidget* parent = nullptr); + + /** + * @brief Construct a new dialog and sets the given text. + */ + TextBrowserDialog(const QString& text, QWidget* parent = nullptr); + + ~TextBrowserDialog() override; + +protected slots: + /** + * @brief Sets custom HTML content when the URL identifies the first page. + */ + virtual void sourceChanged(const QUrl& url); + + /** + * @brief Updates the window title from the current document title. + */ + virtual void updateWindowTitle(); + + /** + * @brief Displays a tooltip showing the link if is an external document. + */ + void highlighted(const QString& link); + +protected: + /** + * @brief Returns a size hint based on the text browser's content. + */ + QSize sizeHint() const override; + + /** + * @brief The TextBrowser is the main widget of this dialog. + */ + QTextBrowser* const text_browser; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/touch_cursor.cpp b/src/gui/touch_cursor.cpp new file mode 100644 index 0000000..bd63ced --- /dev/null +++ b/src/gui/touch_cursor.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2013 Thomas Schöps + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "touch_cursor.h" + +#include +#include + +#include "gui/map/map_widget.h" +#include "gui/util_gui.h" + + +namespace OpenOrienteering { + +// TODO: convert these to settings +const float touch_pos_offset_mm = 25; +const float control_ring_radius_mm = 9.5f; + +TouchCursor::TouchCursor(MapWidget* map_widget) +: visible(false) +, left_button_pressed(false) +, last_pressed_button(NoButton) +, map_widget(map_widget) +{ + // nothing +} + +void TouchCursor::mousePressEvent(QMouseEvent* event) +{ + if (event->button() != Qt::LeftButton) + return; + last_touch_pos = event->pos(); + first_move_event_received = false; + + ControlID control_id = NoButton; + if (!visible || !touchedControl(event->pos(), &control_id)) + { + // Jump to position + updateMapWidget(false); + + QPoint cursor_pos = event->pos() - QPoint(0, touchPosOffsetPx()); + last_cursor_pos = cursor_pos; + cursor_coord = map_widget->viewportToMapF(cursor_pos); + visible = true; + + updateMapWidget(false); + + *event = QMouseEvent( + QEvent::MouseMove, cursor_pos, + Qt::NoButton, event->buttons() & ~Qt::LeftButton, event->modifiers()); + last_pressed_button = NoButton; + } + else if (control_id == LeftButton) + { + *event = QMouseEvent( + QEvent::MouseButtonPress, map_widget->mapToViewport(cursor_coord), + event->button(), event->buttons(), event->modifiers()); + left_button_pressed = true; + last_pressed_button = LeftButton; + + last_cursor_pos = map_widget->mapToViewport(cursor_coord); + } +} + +bool TouchCursor::mouseMoveEvent(QMouseEvent* event) +{ + if (!(event->buttons() & Qt::LeftButton)) + return false; + + if (last_pressed_button != NoButton && !first_move_event_received) + { + first_move_event_received = true; + last_touch_pos = event->pos(); + return false; + } + + updateMapWidget(true); + + QPointF cursor_pos; + if (last_pressed_button == LeftButton) + cursor_pos = last_cursor_pos + (event->pos() - last_touch_pos); + else + cursor_pos = event->pos() - QPoint(0, touchPosOffsetPx()); + last_touch_pos = event->pos(); + last_cursor_pos = cursor_pos; + cursor_coord = map_widget->viewportToMapF(cursor_pos); + + *event = QMouseEvent( + QEvent::MouseMove, cursor_pos, + left_button_pressed ? event->button() : Qt::NoButton, + left_button_pressed ? event->buttons() : (event->buttons() & ~Qt::LeftButton), + event->modifiers()); + + updateMapWidget(true); + return true; +} + +bool TouchCursor::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() != Qt::LeftButton) + return true; + + if (left_button_pressed) + { + *event = QMouseEvent( + QEvent::MouseButtonRelease, map_widget->mapToViewport(cursor_coord), + event->button(), event->buttons(), event->modifiers()); + left_button_pressed = false; + return true; + } + else + return false; +} + +bool TouchCursor::mouseDoubleClickEvent(QMouseEvent* event) +{ + if (last_pressed_button == LeftButton) + { + *event = QMouseEvent( + QEvent::MouseButtonDblClick, map_widget->mapToViewport(cursor_coord), + event->button(), event->buttons(), event->modifiers()); + return true; + } + else + return false; +} + +void TouchCursor::paint(QPainter* painter) +{ + if (!visible) + return; + + QPointF cursor_pos = map_widget->mapToViewport(cursor_coord); + + // Draw cursor + QPixmap cursor_pixmap = map_widget->cursor().pixmap(); + if (!cursor_pixmap.isNull()) + painter->drawPixmap(cursor_pos - map_widget->cursor().hotSpot(), cursor_pixmap); + else + { + // TODO: better standard "cursor"? + float cursor_radius = standardCursorRadiusPx(); + + painter->setPen(QPen(Qt::gray, controlRingStrokeRadiusPx())); + painter->setBrush(Qt::NoBrush); + painter->drawLine(cursor_pos - QPointF(cursor_radius, 0), cursor_pos + QPointF(cursor_radius, 0)); + painter->drawLine(cursor_pos - QPointF(0, cursor_radius), cursor_pos + QPointF(0, cursor_radius)); + } + + // Draw move handle / left button + painter->setPen(QPen(Qt::gray, controlRingStrokeRadiusPx())); + painter->setBrush(Qt::NoBrush); + painter->drawEllipse(cursor_pos + QPointF(0, touchPosOffsetPx()), controlRingRadiusPx(), controlRingRadiusPx()); +} + +bool TouchCursor::touchedControl(QPoint pos, TouchCursor::ControlID* out_id) +{ + QPointF cursor_pos = map_widget->mapToViewport(cursor_coord); + QPointF control_ring_center = cursor_pos + QPointF(0, touchPosOffsetPx()); + + QPointF dist_to_center = pos - control_ring_center; + if (dist_to_center.x()*dist_to_center.x() + dist_to_center.y()*dist_to_center.y() < controlRingRadiusPx()*controlRingRadiusPx()) + { + *out_id = LeftButton; + return true; + } + + return false; +} + +void TouchCursor::updateMapWidget(bool delayed) +{ + QRectF fake_rect = QRectF(cursor_coord.x(), cursor_coord.y(), 0.0001f, 0.0001f); + float pixel_border = qMax(touchPosOffsetPx() + controlRingRadiusPx(), standardCursorRadiusPx()) + controlRingStrokeRadiusPx() + 1; + + if (delayed) + map_widget->updateDrawingLater(fake_rect, pixel_border); + else + map_widget->updateDrawing(fake_rect, pixel_border); +} + +float TouchCursor::touchPosOffsetPx() const +{ + return Util::mmToPixelLogical(touch_pos_offset_mm); +} + +float TouchCursor::controlRingRadiusPx() const +{ + return Util::mmToPixelLogical(control_ring_radius_mm); +} + +float TouchCursor::controlRingStrokeRadiusPx() const +{ + return Util::mmToPixelLogical(0.5f); +} + +float TouchCursor::standardCursorRadiusPx() const +{ + return Util::mmToPixelLogical(1.5f); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/touch_cursor.h b/src/gui/touch_cursor.h new file mode 100644 index 0000000..6e690cc --- /dev/null +++ b/src/gui/touch_cursor.h @@ -0,0 +1,112 @@ +/* + * Copyright 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_TOUCH_CURSOR_H +#define OPENORIENTEERING_TOUCH_CURSOR_H + +#include +#include + +#include "core/map_coord.h" +class QMouseEvent; +class QPainter; + +namespace OpenOrienteering { + +class MapWidget; + + +/** + * Handles drawing and controlling a helper cursor inside the map widget for the mobile UI. + */ +class TouchCursor +{ +public: + /** List of IDs for controls attached to the cursor. */ + enum ControlID + { + LeftButton = 0, + + NoButton + }; + + /** Constructs a touch cursor for a map widget. */ + TouchCursor(MapWidget* map_widget); + + /** + * Notifies the cursor of the event, possibly modifying it. + * Attention: the event type may be changed to a mouse move event. + */ + void mousePressEvent(QMouseEvent* event); + + /** + * Notifies the cursor of the event, possibly modifying it. + * Returns true if the map widget should further process event. + */ + bool mouseMoveEvent(QMouseEvent* event); + + /** + * Notifies the cursor of the event, possibly modifying it. + * Returns true if the map widget should further process event. + */ + bool mouseReleaseEvent(QMouseEvent* event); + + /** + * Notifies the cursor of the event, possibly modifying it. + * Returns true if the map widget should further process event. + */ + bool mouseDoubleClickEvent(QMouseEvent* event); + + /** Paints the cursor. */ + void paint(QPainter* painter); + + /** Issues a (delayed) redraw of the cursor. */ + void updateMapWidget(bool delayed); + +private: + /** + * Checks if a touch at pos is inside a control. If yes, returns true and + * sets out_id to the ID of the touched control. + */ + bool touchedControl(QPoint pos, ControlID* out_id); + + /** Returns the touch point offset from the cursor in pixels. */ + float touchPosOffsetPx() const; + + /** Returns the radius of the control ring in pixels */ + float controlRingRadiusPx() const; + float controlRingStrokeRadiusPx() const; + + float standardCursorRadiusPx() const; + + bool visible; + bool left_button_pressed; + bool first_move_event_received; + ControlID last_pressed_button; + MapCoordF cursor_coord; + QPointF last_cursor_pos; + QPoint last_touch_pos; + MapWidget* map_widget; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/util_gui.cpp b/src/gui/util_gui.cpp new file mode 100644 index 0000000..27d5a3f --- /dev/null +++ b/src/gui/util_gui.cpp @@ -0,0 +1,351 @@ +/* + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "util_gui.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mapper_config.h" +#include "settings.h" +#include "gui/text_browser_dialog.h" // IWYU pragma: keep + + +namespace OpenOrienteering { + +DoubleValidator::DoubleValidator(double bottom, double top, QObject* parent, int decimals) +: QDoubleValidator(bottom, top, decimals, parent) +{ + // Don't cause confusion, accept only English formatting + setLocale(QLocale(QLocale::English)); +} + + +DoubleValidator::~DoubleValidator() = default; + + +QValidator::State DoubleValidator::validate(QString& input, int& pos) const +{ + // Transform thousands group separators into decimal points to avoid confusion + input.replace(QLatin1Char(','), QLatin1Char('.')); + + return QDoubleValidator::validate(input, pos); +} + + + +namespace Util { + + // Implementation moved to settings.cpp + qreal mmToPixelPhysical(qreal millimeters); + + // Implementation moved to settings.cpp + qreal pixelToMMPhysical(qreal pixels); + + + // Implementation moved to settings.cpp + qreal mmToPixelLogical(qreal millimeters); + + // Implementation moved to settings.cpp + qreal pixelToMMLogical(qreal pixels); + + + // Implementation moved to settings.cpp + bool isAntialiasingRequired(qreal ppi); + + // Implementation moved to settings.cpp + bool isAntialiasingRequired(); + + + + void showHelp(QWidget* dialog_parent, const char* filename_latin1, const char* anchor_latin1) + { + showHelp(dialog_parent, QString::fromLatin1(filename_latin1) + QLatin1Char('#') + QString::fromLatin1(anchor_latin1)); + } + + void showHelp(QWidget* dialog_parent, const char* file_and_anchor_latin1) + { + showHelp(dialog_parent, QString::fromLatin1(file_and_anchor_latin1)); + } + + void showHelp(QWidget* dialog_parent, const QString& file_and_anchor) + { + #if defined(Q_OS_ANDROID) + const QString manual_path = QLatin1String("doc:/manual/") + file_and_anchor; + const QUrl help_url = QUrl::fromLocalFile(manual_path); + TextBrowserDialog help_dialog(help_url, dialog_parent); + help_dialog.exec(); + return; + #else + const auto base_url = QLatin1String("qthelp://" MAPPER_HELP_NAMESPACE "/manual/"); + + static QProcess assistant_process; + if (assistant_process.state() == QProcess::Running) + { + QString command = QLatin1String("setSource ") + base_url + file_and_anchor + QLatin1Char('\n'); + assistant_process.write(command.toLatin1()); + } + else + { + auto help_collection = QFileInfo{ QString::fromUtf8("doc:Mapper " APP_VERSION " Manual.qhc") }; + auto compiled_help = QFileInfo{ QString::fromUtf8("doc:Mapper " APP_VERSION " Manual.qch") }; + if (!help_collection.exists() || !compiled_help.exists()) + { + QMessageBox::warning(dialog_parent, QApplication::translate("OpenOrienteering::Util", "Error"), QApplication::translate("OpenOrienteering::Util", "Failed to locate the help files.")); + return; + } + + #if defined(Q_OS_MACOS) + const auto assistant = QString::fromLatin1("Assistant"); + #else + const auto assistant = QString::fromLatin1("assistant"); + #endif + auto assistant_path = QStandardPaths::findExecutable(assistant, { QCoreApplication::applicationDirPath() }); + if (assistant_path.isEmpty()) + assistant_path = QStandardPaths::findExecutable(assistant); + if (assistant_path.isEmpty()) + { + QMessageBox::warning(dialog_parent, QApplication::translate("OpenOrienteering::Util", "Error"), QApplication::translate("OpenOrienteering::Util", "Failed to locate the help browser (\"Qt Assistant\").")); + return; + } + + // Try to start the Qt Assistant process + QStringList args; + args << QLatin1String("-collectionFile") + << QDir::toNativeSeparators(help_collection.absoluteFilePath()) + << QLatin1String("-showUrl") + << (base_url + file_and_anchor) + << QLatin1String("-enableRemoteControl"); + + if ( QGuiApplication::platformName() == QLatin1String("xcb") || + QGuiApplication::platformName().isEmpty() ) + { + // Use the modern 'fusion' style instead of the default "windows" + // style on X11. + args << QLatin1String("-style") << QLatin1String("fusion"); + } + + #if defined(Q_OS_LINUX) + auto env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("QT_SELECT"), QLatin1String("5")); // #541 + env.insert(QLatin1String("LANG"), Settings::getInstance().getSetting(Settings::General_Language).toString()); + assistant_process.setProcessEnvironment(env); + #endif + + assistant_process.start(assistant_path, args); + + // FIXME: Calling waitForStarted() from the main thread might cause the user interface to freeze. + if (!assistant_process.waitForStarted()) + { + QMessageBox msg_box; + msg_box.setIcon(QMessageBox::Warning); + msg_box.setWindowTitle(QApplication::translate("OpenOrienteering::Util", "Error")); + msg_box.setText(QApplication::translate("OpenOrienteering::Util", "Failed to launch the help browser (\"Qt Assistant\").")); + msg_box.setStandardButtons(QMessageBox::Ok); + auto details = assistant_process.readAllStandardError(); + if (! details.isEmpty()) + msg_box.setDetailedText(QString::fromLocal8Bit(details)); + + msg_box.exec(); + } + } + #endif + } + + + + QString makeWhatThis(const char* reference_latin1) + { + //: This "See more" is displayed as a link to the manual in What's-this tooltips. + return QStringLiteral("%2").arg( + QString::fromLatin1(reference_latin1), QApplication::translate("OpenOrienteering::Util", "See more...") ); + } + + + + QString InputProperties::unit() + { + return QCoreApplication::translate("OpenOrienteering::UnitOfMeasurement", "mm", "millimeters"); + } + + QString InputProperties::unit() + { + return QCoreApplication::translate("OpenOrienteering::UnitOfMeasurement", "m", "meters"); + } + + + + QLabel* Headline::create(const QString& text) + { + return new QLabel(QLatin1String("") + text + QLatin1String("")); + } + + QLabel* Headline::create(const char* text_utf8) + { + return create(QString::fromUtf8(text_utf8)); + } + + + + QSpacerItem* SpacerItem::create(const QWidget* widget) + { + const int spacing = widget->style()->pixelMetric(QStyle::PM_LayoutTopMargin); + return new QSpacerItem(spacing, spacing); + } + + + namespace SpinBox + { + +#ifndef NDEBUG + /** + * Returns the maximum number of digits in a spinbox which is regarded + * as normal. Exceedings this number in Util::SpinBox::create() will + * print a runtime warning in development builds. + */ + constexpr int max_digits() + { + return 13; + } +#endif + + QSpinBox* create(int min, int max, const QString &unit, int step) + { + auto box = new QSpinBox(); + box->setRange(min, max); + const auto space = QLatin1Char{' '}; + if (unit.startsWith(space)) + box->setSuffix(unit); + else if (unit.length() > 0) + box->setSuffix(space + unit); + if (step > 0) + box->setSingleStep(step); +#ifndef NDEBUG + if (box->locale().toString(min).remove(box->locale().groupSeparator()).length() > max_digits()) + qDebug().nospace() + << "WARNING: Util::SpinBox::create() will create a very large widget because of min=" + << box->locale().toString(min); + if (box->locale().toString(max).remove(box->locale().groupSeparator()).length() > max_digits()) + qDebug().nospace() + << "WARNING: Util::SpinBox::create() will create a very large widget because of max=" + << box->locale().toString(max); +#endif + return box; + } + + QDoubleSpinBox* create(int decimals, double min, double max, const QString &unit, double step) + { + auto box = new QDoubleSpinBox(); + box->setDecimals(decimals); + box->setRange(min, max); + const auto space = QLatin1Char{' '}; + if (unit.startsWith(space)) + box->setSuffix(unit); + else if (unit.length() > 0) + box->setSuffix(space + unit); + if (step > 0.0) + box->setSingleStep(step); + else + { + switch (decimals) + { + case 0: box->setSingleStep(1.0); break; + case 1: box->setSingleStep(0.1); break; + default: box->setSingleStep(5.0 * pow(10.0, -decimals)); + } + } + #ifndef NDEBUG + if (box->textFromValue(min).remove(box->locale().groupSeparator()).length() > max_digits()) + qDebug().nospace() + << "WARNING: Util::SpinBox::create() will create a very large widget because of min=" + << box->locale().toString(min, 'f', decimals); + if (box->textFromValue(max).remove(box->locale().groupSeparator()).length() > max_digits()) + qDebug().nospace() + << "WARNING: Util::SpinBox::create() will create a very large widget because of max=" + << box->locale().toString(max, 'f', decimals); + #endif + return box; + } + + } + + namespace TristateCheckbox + { + void setDisabledAndChecked(QCheckBox* checkbox, bool checked) + { + Q_ASSERT(checkbox); + checkbox->setEnabled(false); + checkbox->setTristate(true); + checkbox->setCheckState(checked ? Qt::PartiallyChecked : Qt::Unchecked); + } + + void setEnabledAndChecked(QCheckBox* checkbox, bool checked) + { + Q_ASSERT(checkbox); + checkbox->setEnabled(true); + checkbox->setChecked(checked); + checkbox->setTristate(false); + } + } + + + + QString plainText(QString maybe_markup) + { + if (maybe_markup.contains(QLatin1Char('<'))) + { + QTextDocument doc; + doc.setHtml(maybe_markup); + maybe_markup = doc.toPlainText(); + } + return maybe_markup; + } + + +} // namespace Util + + +} // namespace OpenOrienteering diff --git a/src/gui/util_gui.h b/src/gui/util_gui.h new file mode 100644 index 0000000..dc13131 --- /dev/null +++ b/src/gui/util_gui.h @@ -0,0 +1,323 @@ +/* + * Copyright 2012, 2013, 2015, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_UTIL_GUI_H +#define OPENORIENTEERING_UTIL_GUI_H + +#include +#include +#include +#include + +class QCheckBox; +class QDoubleSpinBox; +class QLabel; +class QObject; +class QSpacerItem; +class QSpinBox; +class QWidget; + +namespace OpenOrienteering { + +class MapCoordF; + + +/** Double validator for line edit widgets, + * ensures that only valid doubles can be entered. */ +class DoubleValidator : public QDoubleValidator // clazy:exclude=missing-qobject-macro +{ +public: + DoubleValidator(double bottom, double top = 10e10, QObject* parent = nullptr, int decimals = 20); + + ~DoubleValidator() override; + + State validate(QString& input, int& pos) const override; +}; + + + +/** + * A collection of GUI utility functions. + */ +namespace Util +{ + + /** + * Converts millimeters to pixels using the physical dpi setting of + * Mapper's settings. This should be used to calculate sizes of map elements. + * @sa mmToPixelLogical() + */ + qreal mmToPixelPhysical(qreal millimeters); + + /** Inverse of mmToPixelPhysical(). */ + qreal pixelToMMPhysical(qreal pixels); + + + /** + * Converts millimeters to pixels using the "logical" dpi setting of + * the operating system. This should be used to calculate sizes of UI + * elements. + * @sa mmToPixelPhysical() + */ + qreal mmToPixelLogical(qreal millimeters); + + /** Inverse of mmToPixelLogical(). */ + qreal pixelToMMLogical(qreal pixels); + + + /** Returns true for low-dpi screens, false for high-dpi screens. */ + bool isAntialiasingRequired(); + + /** Returns true for low-dpi screens, false for high-dpi screens. */ + bool isAntialiasingRequired(qreal ppi); + + + + /** + * Show the manual in Qt assistant. + * + * @param filename_latin1 the name of the manual page html file + * @param anchor_latin1 the anchor in the specified file to jump to + */ + void showHelp(QWidget* dialog_parent, const char* filename_latin1, const char* anchor_latin1); + + /** + * Show the manual in Qt assistant. + * + * The anchor may be left out or given with the filename. + * + * @param file_and_anchor_latin1 the name of the manual page html file, optionally including an anchor + */ + void showHelp(QWidget* dialog_parent, const char* file_and_anchor_latin1 = "index.html"); + + /** + * Show the manual in Qt assistant. + * + * The anchor may be left out or given with the filename. + * + * @param file_and_anchor the name of the manual page html file, optionally including an anchor + */ + void showHelp(QWidget* dialog_parent, const QString& file_and_anchor); + + + + /** + * Creates a What's-this text "See more" linking to the given page and + * fragment in the manual. + */ + QString makeWhatThis(const char* reference_latin1); + + + + /** + * Provides information about the properties of Mapper types + * for the purpose of customizing input widgets. + * + * The generic class is empty. + * Template specializations provide the actual values. + * See InputProperties for an example. + */ + template< class T > + struct InputProperties + { + // intentionally left empty + }; + + + /** + * Provides information about the properties of MapCoordF + * for the purpose of customizing input widgets. + */ + template< > + struct InputProperties< MapCoordF > + { + /** The underlying fundamental type. */ + typedef double basetype; + + /** The minimum input value. */ + constexpr static double min() noexcept { return -99999999.99; } + + /** The maximum input value. */ + constexpr static double max() noexcept { return +99999999.99; } + + /** The spinbox step width. */ + constexpr static double step() noexcept { return 1.0; } + + /** The number of decimals. */ + constexpr static int decimals() noexcept { return 2; } + + /** The unit of measurement, translated in context UnitOfMeasurement. */ + static QString unit(); + }; + + + /** Identifies the type double representing real meters */ + struct RealMeters + { + // intentionally left empty + }; + + + /** + * Provides information about the type double representing real meters + * for the purpose of customizing input widgets. + */ + template< > + struct InputProperties< RealMeters > + { + /** The underlying fundamental type. */ + typedef double basetype; + + /** The minimum input value. */ + static constexpr double min() noexcept { return -99999999.99; } + + /** The maximum input value. */ + constexpr static double max() noexcept { return +99999999.99; } + + /** The spinbox step width. */ + constexpr static double step() noexcept { return 1.0; } + + /** The number of decimals. */ + constexpr static int decimals() noexcept { return 2; } + + /** The unit of measurement, translated in context UnitOfMeasurement. */ + static QString unit(); + }; + + + + namespace Headline + { + /** + * Creates a QLabel which is styled as a headline. + * + * This headline is intended for use in dialogs. + */ + QLabel* create(const QString& text); + + /** + * Creates a QLabel which is styled as a headline. + * + * This headline is intended for use in dialogs. + */ + QLabel* create(const char* text_utf8); + } + + + + namespace SpacerItem + { + /** + * Creates a QSpacerItem which takes up a style dependent width + * and height. + * + * This spacer item is intended for use with QFormLayout which + * does not offer a direct mean for extra spacing. + */ + QSpacerItem* create(const QWidget* widget); + } + + + + namespace SpinBox + { + /** + * Creates and initializes a QSpinBox. + * + * This method allows to initialize the most frequent options of + * QSpinBox in a single call: + * the lower and upper bound of the valid range, + * the unit of measurement (optional), + * the step width of the spinbox buttons (optional). + */ + QSpinBox* create(int min, int max, const QString &unit = {}, int step = 0); + + /** + * Creates and initializes a QDoubleSpinBox. + * + * This method allows to initialize the most frequent options of + * QDoubleSpinBox in a single call: + * the number of decimals, + * the lower and upper bound of the valid range, + * the unit of measurement (optional), + * the step width of the spinbox buttons (optional; dependent on + * the number of decimals if not specified). + */ + QDoubleSpinBox* create(int decimals, double min, double max, const QString &unit = {}, double step = 0.0); + + /** + * Creates and initializes a QDoubleSpinBox. + * + * This method allows to initialize the most frequent options of + * QDoubleSpinBox in a single call, determining the actual properties + * via InputProperties. + */ + template< class T > + QDoubleSpinBox* create() + { + typedef InputProperties P; + return create(P::decimals(), P::min(), P::max(), P::unit(), P::step()); + } + + /** + * @deprecated Transitional method. + * + * Creates and initializes a QDoubleSpinBox. + * + * This method allows to initialize the most frequent options of + * QDoubleSpinBox in a single call, determining the actual properties + * via InputProperties. + * + * The unit of measurement is taken from the actual parameter. This is + * meant to support the transition from code where the translation of + * units still exists in the context of the client code, instead of + * in the context UnitOfMeasurement. + */ + template< class T > + QDoubleSpinBox* create(const QString& unit) + { + typedef InputProperties P; + return create(P::decimals(), P::min(), P::max(), unit, P::step()); + } + } + + + + namespace TristateCheckbox + { + void setDisabledAndChecked(QCheckBox* checkbox, bool checked); + + void setEnabledAndChecked(QCheckBox* checkbox, bool checked); + } + + + + /** + * Remove any HTML markup from the input text. + * + * \see QTextDocument::toPlainText + */ + QString plainText(QString maybe_markup); + +} + + +} // namespace OpenOrienteering +#endif diff --git a/src/gui/widgets/action_grid_bar.cpp b/src/gui/widgets/action_grid_bar.cpp new file mode 100644 index 0000000..2475630 --- /dev/null +++ b/src/gui/widgets/action_grid_bar.cpp @@ -0,0 +1,274 @@ +/* + * Copyright 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "action_grid_bar.h" + +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "gui/util_gui.h" + + +namespace OpenOrienteering { + +ActionGridBar::ActionGridBar(Direction direction, int rows, QWidget* parent) +: QWidget(parent) +{ + this->direction = direction; + this->rows = rows; + next_id = 0; + + // Will be calculated in resizeEvent() + cols = 1; + + if (direction == Horizontal) + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + else + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + + // Create overflow action + overflow_action = new QAction(QIcon(QString::fromLatin1(":/images/three-dots.png")), tr("Show remaining items"), this); + connect(overflow_action, &QAction::triggered, this, &ActionGridBar::overflowActionClicked); + overflow_button = nullptr; + overflow_menu = new QMenu(this); + include_overflow_from_list.push_back(this); +} + +int ActionGridBar::getRows() const +{ + return rows; +} + +int ActionGridBar::getCols() const +{ + return cols; +} + +void ActionGridBar::addAction(QAction* action, int row, int col, int row_span, int col_span, bool at_end) +{ + // Determine icon size (important for high-dpi screens). + // Use a somewhat smaller size than what would cover the whole icon to + // account for the (assumed) button border. + QSize icon_size = getIconSize(row_span, col_span); + + // Ensure that the icon of the given action is big enough. If not, scale it up. + // NOTE: Here, row_span == col_span is assumed. + QIcon icon = action->icon(); + QPixmap pixmap = icon.pixmap(icon_size, QIcon::Normal, QIcon::Off); + if (! pixmap.isNull() && pixmap.width() < icon_size.width()) + { + pixmap = pixmap.scaled(icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + icon.addPixmap(pixmap); + action->setIcon(icon); + } + + // Add the item + GridItem newItem; + newItem.id = next_id ++; + newItem.action = action; + newItem.button = new QToolButton(); + newItem.button->setDefaultAction(action); + newItem.button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + newItem.button->setAutoRaise(true); + newItem.button->setIconSize(icon_size); + newItem.button_hidden = false; + newItem.row = row; + newItem.col = col; + newItem.row_span = row_span; + newItem.col_span = col_span; + newItem.at_end = at_end; + items.push_back(newItem); + + // If this is the overflow action, remember the button. + if (action == overflow_action) + overflow_button = newItem.button; +} + +void ActionGridBar::addActionAtEnd(QAction* action, int row, int col, int row_span, int col_span) +{ + addAction(action, row, col, row_span, col_span, true); +} + +QSize ActionGridBar::getIconSize(int row_span, int col_span) const +{ + int icon_size_pixel_row = qRound(row_span * Util::mmToPixelLogical(Settings::getInstance().getSettingCached(Settings::ActionGridBar_ButtonSizeMM).toFloat())); + int icon_size_pixel_col = qRound(col_span * Util::mmToPixelLogical(Settings::getInstance().getSettingCached(Settings::ActionGridBar_ButtonSizeMM).toFloat())); + const int button_icon_size_row = icon_size_pixel_row - 12; + const int button_icon_size_col = icon_size_pixel_col - 12; + if (direction == Horizontal) + return QSize(button_icon_size_row, button_icon_size_col); + else + return QSize(button_icon_size_col, button_icon_size_row); +} + +QAction* ActionGridBar::getOverflowAction() const +{ + return overflow_action; +} + +void ActionGridBar::setToUseOverflowActionFrom(ActionGridBar* other_bar) +{ + other_bar->include_overflow_from_list.push_back(this); +} + +QToolButton* ActionGridBar::getButtonForAction(QAction* action) +{ + for (auto& item : items) + { + if (item.action == action) + return item.button_hidden ? nullptr : item.button; + } + return nullptr; +} + +QSize ActionGridBar::sizeHint() const +{ + int height_px = Util::mmToPixelLogical(rows * Settings::getInstance().getSettingCached(Settings::ActionGridBar_ButtonSizeMM).toFloat()); + if (direction == Horizontal) + return QSize(100, height_px); + else + return QSize(height_px, 100); +} + +bool ActionGridBar::compareItemPtrId(ActionGridBar::GridItem* a, ActionGridBar::GridItem* b) +{ + return a->id < b->id; +} + +void ActionGridBar::overflowActionClicked() +{ + overflow_menu->clear(); + for (const auto source_bar : include_overflow_from_list) + { + for (const auto hidden_item : source_bar->hidden_items) + overflow_menu->addAction(hidden_item->action); + } + if (overflow_button) + overflow_menu->popup(overflow_button->mapToGlobal(QPoint(0, overflow_button->height()))); + else + overflow_menu->popup(mapToGlobal(QPoint(0, height()))); +} + +void ActionGridBar::resizeEvent(QResizeEvent* event) +{ + hidden_items.clear(); + + int length_px = (direction == Horizontal) ? width() : height(); + float length_millimeters = Util::pixelToMMLogical(length_px); + cols = qMax(1, qFloor(length_millimeters / Settings::getInstance().getSettingCached(Settings::ActionGridBar_ButtonSizeMM).toFloat())); + + delete layout(); + QGridLayout* new_layout = new QGridLayout(this); + new_layout->setContentsMargins(0, 0, 0, 0); + new_layout->setSpacing(0); + for (size_t i = 0, end = items.size(); i < end; ++ i) + { + GridItem& item = items[i]; + int resulting_col = item.at_end ? (cols - 1 - item.col) : item.col; + bool hidden = item.row >= rows || item.col >= cols; + if (! hidden) + { + // Check for collisions with other items + for (size_t k = 0; k < items.size(); ++ k) + { + if (i == k) + continue; + GridItem& other = items[k]; + int resulting_col_other = other.at_end ? (cols - 1 - other.col) : other.col; + if (item.row == other.row && resulting_col == resulting_col_other) + { + // Check which item "wins" this spot and which will be hidden + if (item.at_end == other.at_end) + qDebug("Warning: two items set to same position in ActionGridBar, this case is not handled!"); + if ((item.at_end && resulting_col <= cols / 2) + || (! item.at_end && resulting_col > cols / 2)) + { + hidden = true; + break; + } + } + } + } + if (hidden) + { + item.button->hide(); + item.button_hidden = true; + hidden_items.push_back(&item); + continue; + } + + if (direction == Horizontal) + { + new_layout->addWidget( + item.button, + item.row, + resulting_col, + qMin(item.row_span, rows - item.row), + qMin(item.col_span, cols - resulting_col) + ); + } + else + { + new_layout->addWidget( + item.button, + resulting_col, + item.row, + qMin(item.col_span, cols - resulting_col), + qMin(item.row_span, rows - item.row) + ); + } + if (item.button_hidden) + { + item.button->setVisible(true); + item.button_hidden = false; + } + item.button->updateGeometry(); + } + + // Set row/col strech. The first and last row/col acts as margin in case + // the available area is not a multiple of the button size. + if (direction == Horizontal) + { + for (int i = 0; i < cols; ++ i) + new_layout->setColumnStretch(i, 1); + for (int i = 0; i < rows; ++ i) + new_layout->setRowStretch(i, 1); + } + else + { + for (int i = 0; i < cols; ++ i) + new_layout->setRowStretch(i, 1); + for (int i = 0; i < rows; ++ i) + new_layout->setColumnStretch(i, 1); + } + + overflow_action->setEnabled(! hidden_items.empty()); + std::sort(hidden_items.begin(), hidden_items.end(), compareItemPtrId); + + event->accept(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/action_grid_bar.h b/src/gui/widgets/action_grid_bar.h new file mode 100644 index 0000000..7ef7a8a --- /dev/null +++ b/src/gui/widgets/action_grid_bar.h @@ -0,0 +1,128 @@ +/* + * Copyright 2013 Thomas Schöps + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_ACTION_GRID_BAR_H +#define OPENORIENTEERING_ACTION_GRID_BAR_H + +#include + +#include +#include +#include + +class QAction; +class QMenu; +class QResizeEvent; +class QToolButton; + + +namespace OpenOrienteering { + +/** + * A toolbar with a grid layout, whose button size depends on the ppi. + */ +class ActionGridBar : public QWidget +{ +Q_OBJECT +public: + enum Direction + { + Horizontal = 0, + Vertical + }; + + /** + * Constructs a new ActionGridBar. + * + * After constructions, add items and either insert the overflow action + * with addAction(getOverflowAction(), ...) or set another ActionGridBar + * to include the overflow items. + * + * @param direction Direction of the toolbar. + * @param height_items Number of rows in the direction opposite to the main + * toolbar direction. + */ + ActionGridBar(Direction direction, int height_items, QWidget* parent = nullptr); + + /** Returns the number of grid rows. */ + int getRows() const; + + /** Returns the number of grid columns. */ + int getCols() const; + + /** Adds an action to the grid. */ + void addAction(QAction* action, int row, int col, int row_span = 1, int col_span = 1, bool at_end = false); + + /** Adds an action to the grid, starting from the opposite direction. */ + void addActionAtEnd(QAction* action, int row, int col, int row_span = 1, int col_span = 1); + + /** Returns the size of the button icons. */ + QSize getIconSize(int row_span = 1, int col_span = 1) const; + + /** Returns the overflow action (to be inserted into the action bar with addAction()). + * The overflow action is enabled if there are items which do not fit into + * the action bar. On click, it shows a list of those actions. */ + QAction* getOverflowAction() const; + + /** Configures this bar to put its overflow actions into another bar. */ + void setToUseOverflowActionFrom(ActionGridBar* other_bar); + + /** Finds and returns the button corresponding to the given action or nullptr + * if either the action has not been inserted into the action bar, + * or the button is hidden because of a collision. */ + QToolButton* getButtonForAction(QAction* action); + + QSize sizeHint() const override; + +protected slots: + void overflowActionClicked(); + +protected: + struct GridItem + { + int id; // sequential id for sorting in overflow item chooser + int row; + int col; + int row_span; + int col_span; + bool at_end; + QAction* action; + QToolButton* button; + bool button_hidden; + }; + + static bool compareItemPtrId(GridItem* a, GridItem* b); + void resizeEvent(QResizeEvent* event) override; + + Direction direction; + int rows; + int cols; + std::vector< GridItem > items; + int next_id; + QAction* overflow_action; + QToolButton* overflow_button; + QMenu* overflow_menu; + std::vector< GridItem* > hidden_items; + std::vector< ActionGridBar* > include_overflow_from_list; +}; + + +} // namespace OpenOrienteering +#endif diff --git a/src/gui/widgets/color_dropdown.cpp b/src/gui/widgets/color_dropdown.cpp new file mode 100644 index 0000000..135057f --- /dev/null +++ b/src/gui/widgets/color_dropdown.cpp @@ -0,0 +1,174 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2015, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "color_dropdown.h" + +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_color.h" + + +namespace OpenOrienteering { + +ColorDropDown::ColorDropDown(const Map* map, const MapColor* initial_color, bool spot_colors_only, QWidget* parent) +: QComboBox(parent) +, map(map) +, spot_colors_only(spot_colors_only) +{ + addItem(tr("- none -"), QVariant::fromValue(nullptr)); + + int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); + QPixmap pixmap(icon_size, icon_size); + + int initial_index = 0; + int num_colors = map->getNumColors(); + for (int i = 0; i < num_colors; ++i) + { + const MapColor* color = map->getColor(i); + if (spot_colors_only && color->getSpotColorMethod() != MapColor::SpotColor) + continue; + + if (initial_color == color) + initial_index = count(); + + pixmap.fill(colorWithOpacity(*color)); + QString name = spot_colors_only ? color->getSpotColorName() : map->translate(color->getName()); + addItem(QIcon(pixmap), name, QVariant::fromValue(color)); + } + if (!spot_colors_only) + { + const int count = this->count(); + if (count > 0) + { + insertSeparator(count); + } + const MapColor* color = Map::getRegistrationColor(); + pixmap.fill(*color); + addItem(QIcon(pixmap), color->getName(), QVariant::fromValue(color)); + } + setCurrentIndex(initial_index); + + connect(map, &Map::colorAdded, this, &ColorDropDown::onColorAdded); + connect(map, &Map::colorChanged, this, &ColorDropDown::onColorChanged); + connect(map, &Map::colorDeleted, this, &ColorDropDown::onColorDeleted); +} + + +ColorDropDown::~ColorDropDown() = default; + + + +const MapColor* ColorDropDown::color() const +{ + return itemData(currentIndex()).value(); +} + +void ColorDropDown::setColor(const MapColor* color) +{ + setCurrentIndex(findData(QVariant::fromValue(color))); +} + +void ColorDropDown::addColor(const MapColor* color) +{ + if (!spot_colors_only || color->getSpotColorMethod() == MapColor::SpotColor) + { + int pos = 0; + for (; pos < count(); ++pos) + { + const MapColor* c = itemData(pos).value(); + if (c && c->getPriority() > color->getPriority()) + break; + } + int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); + QPixmap pixmap(icon_size, icon_size); + pixmap.fill(*color); + insertItem(pos, map->translate(color->getName()), QVariant::fromValue(color)); + setItemData(pos, pixmap, Qt::DecorationRole); + } +} + +void ColorDropDown::updateColor(const MapColor* color) +{ + if (!spot_colors_only || color->getSpotColorMethod() == MapColor::SpotColor) + { + int pos = 0; + for (; pos < count(); ++pos) + { + if (itemData(pos).value() == color) + break; + } + + if (pos < count()) + { + int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize); + QPixmap pixmap(icon_size, icon_size); + pixmap.fill(*color); + setItemText(pos, map->translate(color->getName())); + setItemData(pos, pixmap, Qt::DecorationRole); + } + else + { + addColor(color); + } + } + else + { + removeColor(color); + } +} + +void ColorDropDown::removeColor(const MapColor* color) +{ + for (int pos = 0; pos < count(); ++pos) + { + if (itemData(pos).value() == color) + { + if (currentIndex() == pos) + setCurrentIndex(0); + removeItem(pos); + break; + } + } +} + +void ColorDropDown::onColorAdded(int, const MapColor* color) +{ + addColor(color); +} + +void ColorDropDown::onColorChanged(int, const MapColor* color) +{ + updateColor(color); +} + +void ColorDropDown::onColorDeleted(int, const MapColor* color) +{ + removeColor(color); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/color_dropdown.h b/src/gui/widgets/color_dropdown.h new file mode 100644 index 0000000..9a41647 --- /dev/null +++ b/src/gui/widgets/color_dropdown.h @@ -0,0 +1,79 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2015, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_COLOR_DROPDOWN_H +#define OPENORIENTEERING_COLOR_DROPDOWN_H + +#include +#include + +class QWidget; + +namespace OpenOrienteering { + +class Map; +class MapColor; + + +/** + * A combobox which lets the user select a map color. + */ +class ColorDropDown : public QComboBox +{ +Q_OBJECT +public: + /** + * Constructs a new ColorDropDown for the colors of the given map. + * If spot_colors_only is true, it will only display fulltone spot colors. + */ + ColorDropDown(const Map* map, const MapColor* initial_color = nullptr, bool spot_colors_only = false, QWidget* parent = nullptr); + + /** Destructor. */ + ~ColorDropDown() override; + + /** Returns the selected color or nullptr if no color selected. */ + const MapColor* color() const; + + /** Sets the selection to the given color. */ + void setColor(const MapColor* color); + + /** Adds a color to the list. */ + void addColor(const MapColor* color); + + /** Updates a color in the list. */ + void updateColor(const MapColor* color); + + /** Removes a color from the list. */ + void removeColor(const MapColor* color); + +protected: + void onColorAdded(int, const MapColor* color); + void onColorChanged(int, const MapColor* color); + void onColorDeleted(int, const MapColor* color); + + const Map* map; + const bool spot_colors_only; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/color_list_widget.cpp b/src/gui/widgets/color_list_widget.cpp new file mode 100644 index 0000000..db8b2e6 --- /dev/null +++ b/src/gui/widgets/color_list_widget.cpp @@ -0,0 +1,505 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "color_list_widget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/map_color.h" +#include "gui/color_dialog.h" +#include "gui/main_window.h" +#include "gui/util_gui.h" +#include "gui/widgets/segmented_button_layout.h" +#include "util/item_delegates.h" +#include "util/util.h" + +// IWYU pragma: no_forward_declare QTableWidgetItem + + +namespace OpenOrienteering { + +ColorListWidget::ColorListWidget(Map* map, MainWindow* window, QWidget* parent) +: QWidget(parent) +, map(map) +, window(window) +{ + react_to_changes = true; + + setWhatsThis(Util::makeWhatThis("color_dock_widget.html")); + + // Color table + color_table = new QTableWidget(map->getNumColors(), 7); + color_table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::AnyKeyPressed); + color_table->setSelectionMode(QAbstractItemView::SingleSelection); + color_table->setSelectionBehavior(QAbstractItemView::SelectRows); + color_table->verticalHeader()->setVisible(false); + color_table->setHorizontalHeaderLabels(QStringList() << + QString{} << tr("Name") << tr("Spot color") << tr("CMYK") << tr("RGB") << tr("K.o.") << tr("Opacity") ); + color_table->setItemDelegateForColumn(0, new ColorItemDelegate(this)); + color_table->setItemDelegateForColumn(6, new PercentageDelegate(this)); + color_table->setColumnHidden(6, true); + + auto new_button_menu = new QMenu(this); + (void) new_button_menu->addAction(tr("New"), this, SLOT(newColor())); + duplicate_action = new_button_menu->addAction(tr("Duplicate"), this, SLOT(duplicateColor())); + duplicate_action->setIcon(QIcon(QString::fromLatin1(":/images/tool-duplicate.png"))); + + // Buttons + auto new_button = newToolButton(QIcon(QString::fromLatin1(":/images/plus.png")), tr("New")); + new_button->setPopupMode(QToolButton::DelayedPopup); // or MenuButtonPopup + new_button->setMenu(new_button_menu); + delete_button = newToolButton(QIcon(QString::fromLatin1(":/images/minus.png")), tr("Delete")); + + auto add_remove_layout = new SegmentedButtonLayout(); + add_remove_layout->addWidget(new_button); + add_remove_layout->addWidget(delete_button); + + move_up_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-up.png")), tr("Move Up")); + move_up_button->setAutoRepeat(true); + move_down_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-down.png")), tr("Move Down")); + move_down_button->setAutoRepeat(true); + + auto up_down_layout = new SegmentedButtonLayout(); + up_down_layout->addWidget(move_up_button); + up_down_layout->addWidget(move_down_button); + + // TODO: In Mapper >= 0.6, switch to ColorWidget (or generic) translation context. + edit_button = newToolButton(QIcon(QString::fromLatin1(":/images/settings.png")), QApplication::translate("OpenOrienteering::MapEditorController", "&Edit").remove(QLatin1Char('&'))); + edit_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + auto help_button = newToolButton(QIcon(QString::fromLatin1(":/images/help.png")), tr("Help")); + help_button->setAutoRaise(true); + + // The buttons row layout + auto buttons_group_layout = new QHBoxLayout(); + buttons_group_layout->addLayout(add_remove_layout); + buttons_group_layout->addLayout(up_down_layout); + buttons_group_layout->addWidget(edit_button); + buttons_group_layout->addWidget(new QLabel(QString::fromLatin1(" ")), 1); + buttons_group_layout->addWidget(help_button); + + // The layout of all components below the table + auto bottom_layout = new QVBoxLayout(); + QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget); + bottom_layout->setContentsMargins( + style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, + 0, // Covered by the main layout's spacing. + style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2 + ); + bottom_layout->addLayout(buttons_group_layout); + bottom_layout->addWidget(new QLabel(tr("Double-click a color value to open a dialog."))); + + // The main layout + auto layout = new QVBoxLayout(); + layout->setContentsMargins(QMargins()); + layout->addWidget(color_table, 1); + layout->addLayout(bottom_layout); + setLayout(layout); + + for (int i = 0; i < map->getNumColors(); ++i) + addRow(i); + + auto header_view = color_table->horizontalHeader(); + header_view->setSectionResizeMode(QHeaderView::Interactive); + header_view->resizeSections(QHeaderView::ResizeToContents); + header_view->setSectionResizeMode(0, QHeaderView::Fixed); // Color + header_view->resizeSection(0, 32); + header_view->setSectionResizeMode(1, QHeaderView::Stretch); // Name + header_view->setSectionResizeMode(1, QHeaderView::Interactive); // Spot colors + header_view->setSectionResizeMode(5, QHeaderView::Fixed); // Knockout + header_view->resizeSection(5, 32); + header_view->setSectionsClickable(false); + + currentCellChange(color_table->currentRow(), 0, 0, 0); // enable / disable move color buttons + + // Connections + connect(color_table, &QTableWidget::cellChanged, this, &ColorListWidget::cellChange); + connect(color_table, &QTableWidget::currentCellChanged, this, &ColorListWidget::currentCellChange); + connect(color_table, &QTableWidget::cellDoubleClicked, this, &ColorListWidget::editCurrentColor); + + connect(new_button, &QAbstractButton::clicked, this, &ColorListWidget::newColor); + connect(delete_button, &QAbstractButton::clicked, this, &ColorListWidget::deleteColor); + connect(move_up_button, &QAbstractButton::clicked, this, &ColorListWidget::moveColorUp); + connect(move_down_button, &QAbstractButton::clicked, this, &ColorListWidget::moveColorDown); + connect(edit_button, &QAbstractButton::clicked, this, &ColorListWidget::editCurrentColor); + connect(help_button, &QAbstractButton::clicked, this, &ColorListWidget::showHelp); + + connect(map, &Map::colorAdded, this, &ColorListWidget::colorAdded); + connect(map, &Map::colorChanged, this, &ColorListWidget::colorChanged); + connect(map, &Map::colorDeleted, this, &ColorListWidget::colorDeleted); +} + + +ColorListWidget::~ColorListWidget() = default; + + + +void ColorListWidget::showEvent(QShowEvent* event) +{ + if (!event->spontaneous()) + { + // Update name, because translation may be changed with new symbol set + for (int i = 0, count = color_table->rowCount(); i < count; ++i) + { + auto color = map->getColor(i); + auto item = color_table->item(i, 1); + item->setText(map->translate(color->getName())); + } + } +} + + + +QToolButton* ColorListWidget::newToolButton(const QIcon& icon, const QString& text) +{ + auto button = new QToolButton(); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setToolTip(text); + button->setIcon(icon); + button->setText(text); + button->setWhatsThis(whatsThis()); + return button; +} + +void ColorListWidget::newColor() +{ + int row = color_table->currentRow(); + if (row < 0) + row = color_table->rowCount(); + map->addColor(new MapColor(), row); + + map->updateAllObjects(); + + editCurrentColor(); +} + +void ColorListWidget::deleteColor() +{ + int row = color_table->currentRow(); + Q_ASSERT(row >= 0); + if (row < 0) return; // In release mode + + // Show a warning if the color is used + if (map->isColorUsedByASymbol(map->getColor(row))) + { + if (QMessageBox::warning(this, tr("Confirmation"), tr("The map contains symbols with this color. Deleting it will remove the color from these objects! Do you really want to do that?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + } + + map->deleteColor(row); + + map->setColorsDirty(); + map->updateAllObjects(); +} + +void ColorListWidget::duplicateColor() +{ + int row = color_table->currentRow(); + Q_ASSERT(row >= 0); + if (row < 0) return; // In release mode + + auto new_color = new MapColor(*map->getColor(row)); + //: Future replacement for COLOR_NAME + " (Duplicate)", for better localization. + void(tr("%1 (duplicate)")); /// \todo Switch translation + new_color->setName(map->translate(new_color->getName()) + tr(" (Duplicate)")); + map->addColor(new_color, row); + + map->updateAllObjects(); + + editCurrentColor(); +} + +void ColorListWidget::moveColorUp() +{ + int row = color_table->currentRow(); + Q_ASSERT(row >= 1); + if (row < 1) return; // In release mode + + auto above_color = map->getMapColor(row - 1); + auto cur_color = map->getMapColor(row); + map->setColor(cur_color, row - 1); + map->setColor(above_color, row); + updateRow(row - 1); + updateRow(row); + + color_table->setCurrentCell(row - 1, color_table->currentColumn()); + + map->setColorsDirty(); + map->updateAllObjects(); +} + +void ColorListWidget::moveColorDown() +{ + int row = color_table->currentRow(); + Q_ASSERT(row < color_table->rowCount() - 1); + if (row >= color_table->rowCount() - 1) return; // In release mode + + auto below_color = map->getMapColor(row + 1); + auto cur_color = map->getMapColor(row); + map->setColor(cur_color, row + 1); + map->setColor(below_color, row); + updateRow(row + 1); + updateRow(row); + + color_table->setCurrentCell(row + 1, color_table->currentColumn()); + + map->setColorsDirty(); + map->updateAllObjects(); +} + +// slot +void ColorListWidget::editCurrentColor() +{ + int row = color_table->currentRow(); + if (row >= 0) + { + auto color = map->getMapColor(row); + ColorDialog dialog(*map, *color, this); + dialog.setWindowModality(Qt::WindowModal); + int result = dialog.exec(); + if (result == QDialog::Accepted) + { + *color = dialog.getColor(); + map->setColor(color, row); // trigger colorChanged signal + map->setColorsDirty(); + map->updateAllObjects(); + } + } +} + +void ColorListWidget::showHelp() const +{ + Util::showHelp(window, "color_dock_widget.html"); +} + +void ColorListWidget::cellChange(int row, int column) +{ + if (!react_to_changes) + return; + + react_to_changes = false; + + auto color = map->getMapColor(row); + auto text = color_table->item(row, column)->text().trimmed(); + + if (column == 1) + { + color->setName(text); + react_to_changes = true; + } + else if (column == 6) // Opacity + { + auto opacity = color_table->item(row, column)->data(Qt::DisplayRole).toFloat(); + if (!qFuzzyCompare(1.0f+opacity, 1.0f+color->getOpacity())) + { + color->setOpacity(qBound(0.0f, opacity, 1.0f)); + updateRow(row); + } + react_to_changes = true; + } + else + { + react_to_changes = true; + return; + } + + map->setColor(color, row); // trigger colorChanged signal + map->setColorsDirty(); + map->updateAllObjects(); +} + +void ColorListWidget::currentCellChange(int current_row, int current_column, int previous_row, int previous_column) +{ + Q_UNUSED(current_column); + Q_UNUSED(previous_row); + Q_UNUSED(previous_column); + if (!react_to_changes) + return; + + bool valid_row = (current_row >= 0); + delete_button->setEnabled(valid_row); + duplicate_action->setEnabled(valid_row); + move_up_button->setEnabled(valid_row && current_row >= 1); + move_down_button->setEnabled(valid_row && current_row < color_table->rowCount() - 1); + edit_button->setEnabled(valid_row); +} + +void ColorListWidget::colorAdded(int index, const MapColor* color) +{ + Q_UNUSED(color); + color_table->insertRow(index); + addRow(index); + if (index < color_table->rowCount() - 1) + { + updateRow(index + 1); + } + color_table->setCurrentCell(index, 1); +} + +void ColorListWidget::colorChanged(int index, const MapColor* color) +{ + Q_UNUSED(color); + updateRow(index); +} + +void ColorListWidget::colorDeleted(int index, const MapColor* color) +{ + Q_UNUSED(color); + color_table->removeRow(index); + currentCellChange(color_table->currentRow(), color_table->currentColumn(), -1, -1); +} + +void ColorListWidget::addRow(int row) +{ + react_to_changes = false; + + QTableWidgetItem* item; + for (int col = 0; col < color_table->columnCount(); ++col) + { + item = new QTableWidgetItem(); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + // TODO: replace "define" with "edit" + item->setToolTip(tr("Double click to define the color")); + color_table->setItem(row, col, item); + } + +#if 0 // Interfers with translation + // Name + item = color_table->item(row, 1); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable); + item->setToolTip(tr("Click to select the name and click again to edit.")); +#endif + + // Opacity + item = color_table->item(row, 6); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable); + + react_to_changes = true; + + updateRow(row); +} + +void ColorListWidget::updateRow(int row) +{ + react_to_changes = false; + + const auto color = map->getColor(row); + auto color_with_opacity = colorWithOpacity(*color); + + // Color preview + auto item = color_table->item(row, 0); + item->setBackground(color_with_opacity); + + // Name + item = color_table->item(row, 1); + item->setText(map->translate(color->getName())); + + // Spot color + item = color_table->item(row, 2); + item->setText(color->getSpotColorName()); + switch (color->getSpotColorMethod()) + { + case MapColor::SpotColor: + item->setData(Qt::DecorationRole, color_with_opacity); + break; + default: + item->setData(Qt::DecorationRole, QColor(Qt::transparent)); + } + + // CMYK + item = color_table->item(row, 3); + item->setToolTip(tr("Double click to define the color")); + const MapColorCmyk& cmyk = color->getCmyk(); + QLocale l; + item->setText(QString::fromLatin1("%1/%2/%3/%4").arg( + l.toString(100*cmyk.c, 'g', 3), l.toString(100*cmyk.m, 'g', 3), + l.toString(100*cmyk.y, 'g', 3), l.toString(100*cmyk.k, 'g', 3))); + switch (color->getCmykColorMethod()) + { + case MapColor::SpotColor: + case MapColor::RgbColor: + item->setForeground(palette().color(QPalette::Disabled, QPalette::Text)); + item->setData(Qt::DecorationRole, QColor(Qt::transparent)); + break; + default: + item->setForeground(palette().color(QPalette::Active, QPalette::Text)); + item->setData(Qt::DecorationRole, colorWithOpacity(color->getCmyk(), color->getOpacity())); + } + + // RGB + item = color_table->item(row, 4); + item->setText(QColor(color->getRgb()).name()); + item->setToolTip(item->text()); + switch (color->getRgbColorMethod()) + { + case MapColor::SpotColor: + case MapColor::CmykColor: + item->setForeground(palette().color(QPalette::Disabled, QPalette::Text)); + item->setData(Qt::DecorationRole, QColor(Qt::transparent)); + break; + default: + item->setForeground(palette().color(QPalette::Active, QPalette::Text)); + item->setData(Qt::DecorationRole, colorWithOpacity(color->getRgb(), color->getOpacity())); + } + + // Knockout + item = color_table->item(row, 5); + item->setCheckState(color->getKnockout() ? Qt::Checked : Qt::Unchecked); + item->setForeground(palette().color(QPalette::Disabled, QPalette::Text)); + + // Opacity + item = color_table->item(row, 6); + item->setData(Qt::DisplayRole, color->getOpacity()); + + react_to_changes = true; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/color_list_widget.h b/src/gui/widgets/color_list_widget.h new file mode 100644 index 0000000..2d409b2 --- /dev/null +++ b/src/gui/widgets/color_list_widget.h @@ -0,0 +1,101 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012, 2013, 2014, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_COLOR_LIST_WIDGET_H +#define OPENORIENTEERING_COLOR_LIST_WIDGET_H + +#include +#include +#include + +class QAction; +class QIcon; +class QShowEvent; +class QTableWidget; +class QToolButton; + +namespace OpenOrienteering { + +class MainWindow; +class Map; +class MapColor; + + +/** + * A widget showing the list of map colors and some edit buttons at the bottom. + * Enables to define new colors and edit or delete existing colors. + * This widget is used inside a dock widget. + */ +class ColorListWidget : public QWidget +{ +Q_OBJECT +public: + /** Creates a new ColorWidget for a given map and MainWindow. */ + ColorListWidget(Map* map, MainWindow* window, QWidget* parent = nullptr); + + /** Destroys the ColorWidget. */ + ~ColorListWidget() override; + +protected slots: + void newColor(); + void deleteColor(); + void duplicateColor(); + void moveColorUp(); + void moveColorDown(); + void editCurrentColor(); + void showHelp() const; + + void cellChange(int row, int column); + void currentCellChange(int current_row, int current_column, int previous_row, int previous_column); + + void colorAdded(int index, const MapColor* color); + void colorChanged(int index, const MapColor* color); + void colorDeleted(int index, const MapColor* color); + +protected: + void showEvent(QShowEvent* event) override; + + QToolButton* newToolButton(const QIcon& icon, const QString& text); + +private: + void addRow(int row); + void updateRow(int row); + + // Color list + QTableWidget* color_table; + + QAction* duplicate_action; + + // Buttons + QToolButton* delete_button; + QToolButton* move_up_button; + QToolButton* move_down_button; + QToolButton* edit_button; + + Map* const map; + MainWindow* const window; + bool react_to_changes; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/compass_display.cpp b/src/gui/widgets/compass_display.cpp new file mode 100644 index 0000000..df36b6a --- /dev/null +++ b/src/gui/widgets/compass_display.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2013 Thomas Schöps + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "compass_display.h" + +#include +#include + +#include "gui/util_gui.h" + + +namespace OpenOrienteering { + +CompassDisplay::CompassDisplay(QWidget* parent) + : QWidget(parent) + , azimuth(qSNaN()) + , last_update_time(QTime::currentTime()) +{ + setAttribute(Qt::WA_NoSystemBackground, true); +} + +CompassDisplay::~CompassDisplay() +{ + // nothing, not inlined +} + +void CompassDisplay::setAzimuth(float azimuth_deg) +{ + constexpr int update_interval = 200; + QTime current_time = QTime::currentTime(); + if (qAbs(last_update_time.msecsTo(current_time)) >= update_interval + && azimuth != azimuth_deg) + { + last_update_time = current_time; + azimuth = azimuth_deg; + update(); + if (auto parent = parentWidget()) + parent->update(geometry()); + } +} + +QSize CompassDisplay::sizeHint() const +{ + auto width = qRound(Util::mmToPixelLogical(20.0)); + return QSize(width, width); +} + +void CompassDisplay::showEvent(QShowEvent*) +{ + azimuth = qSNaN(); +} + +void CompassDisplay::hideEvent(QHideEvent*) +{ + // nothing +} + +void CompassDisplay::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + auto margin = Util::mmToPixelLogical(1.0f); + QRectF bounding_box = { {0,0}, size() }; + bounding_box.adjust(margin, margin, -margin, -margin); + QPointF midpoint = bounding_box.center(); + QPointF endpoint = QPointF(midpoint.x(), bounding_box.top()); + QLineF line(midpoint, endpoint); + + auto have_value = !qIsNaN(azimuth); + + // Draw alignment cue + constexpr qreal max_alignment_angle = 10; + if (have_value && qAbs(azimuth) < max_alignment_angle) + { + float alignment_factor = (max_alignment_angle - qAbs(azimuth)) / max_alignment_angle; + float radius = alignment_factor * 0.25f * bounding_box.height(); + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::green); + painter.drawEllipse(line.pointAt(0.5f), radius, radius); + } + + // Draw up marker + float line_width = Util::mmToPixelLogical(0.3f); + + painter.setPen(QPen(Qt::white, line_width)); + painter.setBrush(Qt::NoBrush); + painter.drawLine(line); + + painter.setPen(QPen(Qt::black, line_width)); + painter.drawLine(midpoint + QPointF(line_width, 0), endpoint + QPointF(line_width, 0)); + painter.drawLine(midpoint + QPointF(-1 * line_width, 0), endpoint + QPointF(-1 * line_width, 0)); + + // Draw needle + if (have_value) + { + painter.setPen(QPen(Qt::red, line_width)); + line.setAngle(90 + azimuth); + painter.drawLine(line); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/compass_display.h b/src/gui/widgets/compass_display.h new file mode 100644 index 0000000..c451ba5 --- /dev/null +++ b/src/gui/widgets/compass_display.h @@ -0,0 +1,82 @@ +/* + * Copyright 2013 Thomas Schöps + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_COMPASS_DISPLAY_H +#define OPENORIENTEERING_COMPASS_DISPLAY_H + +#include +#include +#include +#include +#include + +class QHideEvent; +class QPaintEvent; +class QShowEvent; + +namespace OpenOrienteering { + + +/** + * A widget which displays a compass. + * + * By default, this widget is transparent (Qt::WA_NoSystemBackground), i.e. it + * it does not draw a background. For proper background drawing, it shall not be + * a child but a sibling of the background widget. + */ +class CompassDisplay : public QWidget +{ +Q_OBJECT +public: + /** + * Creates a compass display. + */ + CompassDisplay(QWidget* parent = nullptr); + + /** + * Destructor. + */ + ~CompassDisplay() override; + + /** + * Sets the compass direction, and updates the widget. + * + * This does nothing unless at least 200 ms elapsed since the last change. + */ + void setAzimuth(float azimuth_deg); + + QSize sizeHint() const override; + +protected: + void showEvent(QShowEvent* event) override; + + void hideEvent(QHideEvent* event) override; + + void paintEvent(QPaintEvent* event) override; + + qreal azimuth; + QTime last_update_time; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/crs_param_widgets.cpp b/src/gui/widgets/crs_param_widgets.cpp new file mode 100644 index 0000000..b227960 --- /dev/null +++ b/src/gui/widgets/crs_param_widgets.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "crs_param_widgets.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/crs_template.h" +#include "core/crs_template_implementation.h" +#include "core/georeferencing.h" + +// IWYU pragma: no_forward_declare QCompleter +// IWYU pragma: no_forward_declare QHBoxLayout +// IWYU pragma: no_forward_declare QPushButton + + +namespace OpenOrienteering { + +UTMZoneEdit::UTMZoneEdit(CRSParameterWidgetObserver& observer, QWidget* parent) + : QWidget(parent) + , observer(observer) +{ + static const QRegExp zone_regexp(QString::fromLatin1("(?:[0-5]?[1-9]|[1-6]0)(?: [NS])?")); + static QStringList zone_list; + if (zone_list.isEmpty()) + { + zone_list.reserve((60 + 9) * 2); + for (int i = 1; i <= 60; ++i) + { + QString zone = QString::number(i); + zone_list << QString::fromLatin1("%1 N").arg(zone) << QString::fromLatin1("%1 S").arg(zone); + if (i < 10) + zone_list << QString::fromLatin1("0%1 N").arg(zone) << QString::fromLatin1("0%1 S").arg(zone); + } + } + + line_edit = new QLineEdit(); + line_edit->setValidator(new QRegExpValidator(zone_regexp, line_edit)); + QCompleter* completer = new QCompleter(zone_list, line_edit); + completer->setMaxVisibleItems(4); + line_edit->setCompleter(completer); + connect(line_edit, &QLineEdit::textChanged, this, &UTMZoneEdit::textEdited); + + QPushButton* button = new QPushButton(tr("Calculate")); + connect(button, &QPushButton::clicked, this, &UTMZoneEdit::calculateValue); + + QHBoxLayout* layout = new QHBoxLayout(); + layout->setMargin(0); + layout->addWidget(line_edit, 1); + layout->addWidget(button, 0); + setLayout(layout); + + calculateValue(); +} + +UTMZoneEdit::~UTMZoneEdit() +{ + // nothing, not inlined +} + +QString UTMZoneEdit::text() const +{ + return line_edit->text(); +} + +void UTMZoneEdit::setText(const QString& value) +{ + line_edit->setText(value); +} + +bool UTMZoneEdit::calculateValue() +{ + auto georef = observer.georeferencing(); + auto zone = CRSTemplates::UTMZoneParameter::calculateUTMZone(georef.getGeographicRefPoint()); + if (!zone.isNull()) + { + setText(zone.toString()); + } + + return !zone.isNull(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/crs_param_widgets.h b/src/gui/widgets/crs_param_widgets.h new file mode 100644 index 0000000..98575d1 --- /dev/null +++ b/src/gui/widgets/crs_param_widgets.h @@ -0,0 +1,57 @@ +/* + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_CRS_PARAM_WIDGETS_H +#define OPENORIENTEERING_CRS_PARAM_WIDGETS_H + +#include +#include +#include + +class QLineEdit; + +namespace OpenOrienteering { + +class CRSParameterWidgetObserver; + + +class UTMZoneEdit : public QWidget +{ + Q_OBJECT +public: + UTMZoneEdit(CRSParameterWidgetObserver& observer, QWidget* parent = nullptr); + ~UTMZoneEdit() override; + + QString text() const; + void setText(const QString& text); + bool calculateValue(); + +signals: + void textEdited(const QString& text); // TODO: rename to textChanged, see QLineEdit + +private: + CRSParameterWidgetObserver& observer; + QLineEdit* line_edit; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/crs_selector.cpp b/src/gui/widgets/crs_selector.cpp new file mode 100644 index 0000000..b17fc4c --- /dev/null +++ b/src/gui/widgets/crs_selector.cpp @@ -0,0 +1,390 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "crs_selector.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/crs_template.h" +#include "core/georeferencing.h" + + +namespace OpenOrienteering { + +// Helper functions for parameter widgets +namespace { + +static const char* crsParameterWidgetProperty = "CRS parameter widget"; +static const char* crsParameterKeyProperty = "CRS parameter key"; + +inline +void setParameterWidget(QWidget* w, bool value = true) +{ + w->setProperty(crsParameterWidgetProperty, QVariant(value)); +} + +inline +bool isParameterWidget(const QWidget* w) +{ + return w->property(crsParameterWidgetProperty).toBool(); +} + +inline +void setParameterKey(QWidget* w, const QString& value) +{ + w->setProperty(crsParameterKeyProperty, QVariant(value)); +} + +inline +QString ParameterKey(const QWidget* w) +{ + return w->property(crsParameterKeyProperty).toString(); +} + +} // namespace + + + +CRSSelector::CRSSelector(const Georeferencing& georef, QWidget* parent) + : QComboBox(parent) + , georef(georef) + , dialog_layout(nullptr) + , num_custom_items(0) + , configured_crs(nullptr) +{ + for (auto&& crs : CRSTemplateRegistry().list()) + { + addItem(crs->name(), QVariant(crs->id())); + } +} + +CRSSelector::~CRSSelector() = default; + +void CRSSelector::setDialogLayout(QFormLayout* dialog_layout) +{ + Q_ASSERT(dialog_layout && !this->dialog_layout); + + this->dialog_layout = dialog_layout; + + using TakingIntArgument = void (QComboBox::*)(int); + connect(this, (TakingIntArgument)&QComboBox::currentIndexChanged, + this, &CRSSelector::crsSelectionChanged); +} + + +void CRSSelector::addCustomItem(const QString& text, unsigned short id) +{ + insertItem(num_custom_items, text, QVariant(int(id))); + if (num_custom_items == 0) + insertSeparator(1); + ++num_custom_items; +} + + +const CRSTemplate* CRSSelector::currentCRSTemplate() const +{ + const CRSTemplate* crs = nullptr; + auto item_data = itemData(currentIndex()); + if (item_data.type() == QVariant::String) + crs = CRSTemplateRegistry().find(item_data.toString()); + return crs; +} + +QString CRSSelector::currentCRSSpec() const +{ + QString spec; + if (auto crs = currentCRSTemplate()) + { + spec = crs->specificationTemplate(); + auto field_values = parameters(); + Q_ASSERT(field_values.size() == crs->parameters().size()); + + auto field_value = begin(field_values); + for (auto&& param : crs->parameters()) + { + for (auto&& value : param->specValues(*field_value)) + spec = spec.arg(value); + ++field_value; + } + } + return spec; +} + +int CRSSelector::currentCustomItem() const +{ + int id = -1; + QVariant item_data = itemData(currentIndex()); + if (item_data.type() == QVariant::Int) + id = item_data.toInt(); + return id; +} + +void CRSSelector::setCurrentCRS(const CRSTemplate* crs, const std::vector& values) +{ + Q_ASSERT(crs); + if (crs) + { + Q_ASSERT(crs->parameters().size() == values.size()); + + int index = findData(QVariant(crs->id())); + setCurrentIndex(index); + + // Explicit call because signals may be blocked. + if (crs == currentCRSTemplate()) + { + configureParameterFields(crs, values); + } + } +} + +void CRSSelector::setCurrentItem(unsigned short id) +{ + QSignalBlocker block(this); + int index = findData(QVariant(id)); + setCurrentIndex(index); + + // Explicit call because signals may be blocked. + if (currentCustomItem() == int(id)) + { + configureParameterFields(nullptr, {}); + } +} + + +std::vector CRSSelector::parameters() const + +{ + std::vector values; + auto crs = currentCRSTemplate(); + if (crs && configured_crs == crs) + { + values.reserve(crs->parameters().size()); + + int row; + QFormLayout::ItemRole role; + dialog_layout->getWidgetPosition(const_cast(this), &row, &role); + + for (auto&& param : crs->parameters()) + { + ++row; + auto field = dialog_layout->itemAt(row, QFormLayout::FieldRole)->widget(); + Q_ASSERT(isParameterWidget(field)); + values.push_back(param->value(field)); + } + } + else if (crs) + { + values.resize(crs->parameters().size()); + } + + return values; +} + +const Georeferencing& CRSSelector::georeferencing() const +{ + return georef; +} + +void CRSSelector::crsSelectionChanged() +{ + configureParameterFields(); + emit crsChanged(); +} + +void CRSSelector::crsParameterEdited() +{ + emit crsChanged(); +} + + +void CRSSelector::configureParameterFields() +{ + if (dialog_layout) + { + auto crs = currentCRSTemplate(); + if (!crs) + configureParameterFields(crs, {}); + else if (crs->id() == georef.getProjectedCRSId()) + configureParameterFields(crs, georef.getProjectedCRSParameters()); + else + configureParameterFields(crs, std::vector(crs->parameters().size())); + } +} + +void CRSSelector::configureParameterFields(const CRSTemplate* crs, const std::vector& values) +{ + if (crs != configured_crs && dialog_layout) + { + removeParameterFields(); + addParameterFields(crs); + } + + if (crs && configured_crs == crs && !values.empty()) + { + int row; + QFormLayout::ItemRole role; + dialog_layout->getWidgetPosition(this, &row, &role); + + // Set the new parameter values. + auto parameters = crs->parameters(); + Q_ASSERT(parameters.size() == values.size()); + + auto parameter = begin(parameters); + auto value = begin(values); + for (++row; dialog_layout->rowCount() > row && value != end(values); ++row) + { + auto field_item = dialog_layout->itemAt(row, QFormLayout::FieldRole); + if (!field_item) + continue; + + auto field = field_item->widget(); + if (!field || !isParameterWidget(field)) + continue; + + QSignalBlocker block(field); + (*parameter)->setValue(field, *value); + ++value; + ++parameter; + } + } +} + +void CRSSelector::addParameterFields(const CRSTemplate* crs) +{ + Q_ASSERT(!configured_crs); + + if (dialog_layout && crs && crs != configured_crs) + { + int row; + QFormLayout::ItemRole role; + dialog_layout->getWidgetPosition(this, &row, &role); + + // Add the labels and fields of the new parameters. + for (auto&& parameter : crs->parameters()) + { + ++row; + auto field = parameter->createEditor(*this); + setParameterWidget(field, true); + setParameterKey(field, parameter->id()); + auto label = new QLabel(parameter->name() + QLatin1Char(':')); + if (dialog_layout->itemAt(row, QFormLayout::FieldRole)) + { + dialog_layout->insertRow(row, label, field); + } + else + { + dialog_layout->setWidget(row, QFormLayout::LabelRole, label); + dialog_layout->setWidget(row, QFormLayout::FieldRole, field); + } + } + + configured_crs = crs; + } +} + +void CRSSelector::removeParameterFields() +{ + if (dialog_layout) + { + int crs_row; + QFormLayout::ItemRole role; + dialog_layout->getWidgetPosition(this, &crs_row, &role); + + // Remove the labels and fields of the old parameters. + for (int row = dialog_layout->rowCount()-1; row > crs_row; --row) + { + auto field_item = dialog_layout->itemAt(row, QFormLayout::FieldRole); + if (!field_item) + continue; + + auto field = field_item->widget(); + if (!field || !isParameterWidget(field)) + continue; + + auto label_item = dialog_layout->itemAt(row, QFormLayout::LabelRole); + if (auto label = label_item->widget()) + { + delete dialog_layout->takeAt(dialog_layout->indexOf(label)); + delete label; + } + + delete dialog_layout->takeAt(dialog_layout->indexOf(field)); + delete field; + } + + configured_crs = nullptr; + } +} + +void CRSSelector::changeEvent(QEvent* event) +{ + switch (event->type()) + { + case QEvent::EnabledChange: + if (dialog_layout) + { + bool enabled = isEnabled(); + if (auto label = dialog_layout->labelForField(this)) + label->setEnabled(enabled); + + int row; + QFormLayout::ItemRole role; + dialog_layout->getWidgetPosition(this, &row, &role); + + // Remove the labels and fields of the old parameters. + for (++row; dialog_layout->rowCount() > row; ++row) + { + auto field_item = dialog_layout->itemAt(row, QFormLayout::FieldRole); + if (!field_item) + continue; + + auto field = field_item->widget(); + if (!field || !isParameterWidget(field)) + continue; + + auto label_item = dialog_layout->itemAt(row, QFormLayout::LabelRole); + if (auto label = label_item->widget()) + label->setEnabled(enabled); + + field->setEnabled(enabled); + } + + configured_crs = nullptr; + } + break; + default: + ; // nothing + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/crs_selector.h b/src/gui/widgets/crs_selector.h new file mode 100644 index 0000000..433ad6b --- /dev/null +++ b/src/gui/widgets/crs_selector.h @@ -0,0 +1,194 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_CRS_SELECTOR_H +#define OPENORIENTEERING_CRS_SELECTOR_H + +#include + +#include +#include +#include + +#include "core/crs_template.h" + +class QEvent; +class QFormLayout; +class QWidget; + +namespace OpenOrienteering { + +class Georeferencing; + + +/** + * Combobox for selecting projected coordinate reference system (CRS). + * + * It operates on the list of CRS templates in the CRSTemplateRegistry. + * However, it accepts custom items which are prepended to and separated from + * the default items. + * + * This is more than just a simple widget. CRSSelector is meant to be inserted + * into a QFormLayout. Upon CRS selection changes it will add and remove extra + * lines below itself, for editing CRS parameters. + * + * \todo Consider making this a QWidget which has got a QCombobox - the public + * QComboBox API should better not be exposed. The combobox' signals could + * no longer be block from clients. + */ +class CRSSelector : public QComboBox, public CRSParameterWidgetObserver +{ +Q_OBJECT +public: + /** + * Constructor. + * + * The dialog parameter must not be nullptr. It is passed to parameter widgets. + * Ownership is taken only by the parent widget if given. + */ + CRSSelector(const Georeferencing& georef, QWidget* parent = nullptr); + + /** + * Destructor. + */ + ~CRSSelector() override; + + + /** + * Sets the QFormLayout which this field is part of. + * + * When the selected CRS is changed, or when configureParameterFields() + * is called explicitly, CRSSelector will add extra lines below its own row + * for editing CRS parameters. + * + * This is to be called once. However, it will not create the parameter + * fields for the current selection. + */ + void setDialogLayout(QFormLayout* dialog_layout); + + + /** + * Adds a custom item with the given text and id at the top of the list. + */ + void addCustomItem(const QString& text, unsigned short id); + + + /** + * Returns the selected CRS template, or nullptr if a custom item is selected. + */ + const CRSTemplate* currentCRSTemplate() const; + + /** + * Returns the selected CRS specification string, + * or an empty string if a custom item is selected. + */ + QString currentCRSSpec() const; + + /** + * Returns the id of the selected custom item, + * or -1 if a normal item is selected. + */ + int currentCustomItem() const; + + + /** + * Selects the given standard item, and sets the parameters. + */ + void setCurrentCRS(const CRSTemplate* crs, const std::vector& values); + + /** + * Selects the given custom item. + */ + void setCurrentItem(unsigned short id); + + + /** + * Returns the list of CRS configuration parameter values. + */ + std::vector parameters() const; + + + /** + * Provides the current georeferencing. + */ + const Georeferencing& georeferencing() const override; + + +signals: + /** + * Emitted when the user changes the CRS or its parameters. + */ + void crsChanged(); + + +protected: + /** + * Listens to changes of the selected CRS. + */ + void crsSelectionChanged(); + + /** + * Listens to changes of CRS parameters. + */ + void crsParameterEdited() override; + + /** + * Updates the parameter fields in the dialog_layout, + * according to the selected CRS. + */ + void configureParameterFields(); + + /** + * Updates the parameter fields in the dialog_layout, + * according to the given CRS and values. + */ + void configureParameterFields(const CRSTemplate* crs, const std::vector& values); + + /** + * Adds parameter fields to the dialog_layout, + * according to the given crs. + * + * There must be no other parameter fields in the dialog, + * i.e. removeParameterFields() needs to be called before. + */ + void addParameterFields(const CRSTemplate* crs); + + /** + * Removes all parameter fields from the dialog_layout. + */ + void removeParameterFields(); + + /** + * Propagates enabling/disabling to the parameter widgets. + */ + void changeEvent(QEvent* event) override; + +private: + const Georeferencing& georef; + QFormLayout* dialog_layout; + int num_custom_items; + const CRSTemplate* configured_crs; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/editor_settings_page.cpp b/src/gui/widgets/editor_settings_page.cpp new file mode 100644 index 0000000..90c9a9a --- /dev/null +++ b/src/gui/widgets/editor_settings_page.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2012, 2013 Jan Dalheimer + * Copyright 2012-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "editor_settings_page.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "gui/modifier_key.h" +#include "gui/util_gui.h" +#include "gui/widgets/settings_page.h" + + +namespace OpenOrienteering { + +EditorSettingsPage::EditorSettingsPage(QWidget* parent) + : SettingsPage(parent) +{ + auto layout = new QFormLayout(this); + + icon_size = Util::SpinBox::create(1, 25, tr("mm", "millimeters")); + layout->addRow(tr("Symbol icon size:"), icon_size); + + antialiasing = new QCheckBox(tr("High quality map display (antialiasing)"), this); + antialiasing->setToolTip(tr("Antialiasing makes the map look much better, but also slows down the map display")); + layout->addRow(antialiasing); + + text_antialiasing = new QCheckBox(tr("High quality text display in map (antialiasing), slow"), this); + text_antialiasing->setToolTip(tr("Antialiasing makes the map look much better, but also slows down the map display")); + layout->addRow(text_antialiasing); + + tolerance = Util::SpinBox::create(0, 50, tr("mm", "millimeters")); + layout->addRow(tr("Click tolerance:"), tolerance); + + snap_distance = Util::SpinBox::create(0, 100, tr("mm", "millimeters")); + layout->addRow(tr("Snap distance (%1):").arg(ModifierKey::shift()), snap_distance); + + fixed_angle_stepping = Util::SpinBox::create(1, 180, trUtf8("°", "Degree sign for angles")); + layout->addRow(tr("Stepping of fixed angle mode (%1):").arg(ModifierKey::control()), fixed_angle_stepping); + + select_symbol_of_objects = new QCheckBox(tr("When selecting an object, automatically select its symbol, too")); + layout->addRow(select_symbol_of_objects); + + zoom_out_away_from_cursor = new QCheckBox(tr("Zoom away from cursor when zooming out")); + layout->addRow(zoom_out_away_from_cursor); + + draw_last_point_on_right_click = new QCheckBox(tr("Drawing tools: set last point on finishing with right click")); + layout->addRow(draw_last_point_on_right_click); + + keep_settings_of_closed_templates = new QCheckBox(tr("Templates: keep settings of closed templates")); + layout->addRow(keep_settings_of_closed_templates); + + + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(Util::Headline::create(tr("Edit tool:"))); + + edit_tool_delete_bezier_point_action = new QComboBox(); + edit_tool_delete_bezier_point_action->addItem(tr("Retain old shape"), (int)Settings::DeleteBezierPoint_RetainExistingShape); + edit_tool_delete_bezier_point_action->addItem(tr("Reset outer curve handles"), (int)Settings::DeleteBezierPoint_ResetHandles); + edit_tool_delete_bezier_point_action->addItem(tr("Keep outer curve handles"), (int)Settings::DeleteBezierPoint_KeepHandles); + layout->addRow(tr("Action on deleting a curve point with %1:").arg(ModifierKey::control()), edit_tool_delete_bezier_point_action); + + edit_tool_delete_bezier_point_action_alternative = new QComboBox(); + edit_tool_delete_bezier_point_action_alternative->addItem(tr("Retain old shape"), (int)Settings::DeleteBezierPoint_RetainExistingShape); + edit_tool_delete_bezier_point_action_alternative->addItem(tr("Reset outer curve handles"), (int)Settings::DeleteBezierPoint_ResetHandles); + edit_tool_delete_bezier_point_action_alternative->addItem(tr("Keep outer curve handles"), (int)Settings::DeleteBezierPoint_KeepHandles); + layout->addRow(tr("Action on deleting a curve point with %1:").arg(ModifierKey::controlShift()), edit_tool_delete_bezier_point_action_alternative); + + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(Util::Headline::create(tr("Rectangle tool:"))); + + rectangle_helper_cross_radius = Util::SpinBox::create(0, 999999, tr("mm", "millimeters")); + layout->addRow(tr("Radius of helper cross:"), rectangle_helper_cross_radius); + + rectangle_preview_line_width = new QCheckBox(tr("Preview the width of lines with helper cross")); + layout->addRow(rectangle_preview_line_width); + + + connect(antialiasing, &QAbstractButton::toggled, text_antialiasing, &QCheckBox::setEnabled); + + updateWidgets(); +} + +EditorSettingsPage::~EditorSettingsPage() +{ + // nothing, not inlined +} + +QString EditorSettingsPage::title() const +{ + return tr("Editor"); +} + +void EditorSettingsPage::apply() +{ + setSetting(Settings::SymbolWidget_IconSizeMM, icon_size->value()); + setSetting(Settings::MapDisplay_Antialiasing, antialiasing->isChecked()); + setSetting(Settings::MapDisplay_TextAntialiasing, text_antialiasing->isChecked()); + setSetting(Settings::MapEditor_ClickToleranceMM, tolerance->value()); + setSetting(Settings::MapEditor_SnapDistanceMM, snap_distance->value()); + setSetting(Settings::MapEditor_FixedAngleStepping, fixed_angle_stepping->value()); + setSetting(Settings::MapEditor_ChangeSymbolWhenSelecting, select_symbol_of_objects->isChecked()); + setSetting(Settings::MapEditor_ZoomOutAwayFromCursor, zoom_out_away_from_cursor->isChecked()); + setSetting(Settings::MapEditor_DrawLastPointOnRightClick, draw_last_point_on_right_click->isChecked()); + setSetting(Settings::Templates_KeepSettingsOfClosed, keep_settings_of_closed_templates->isChecked()); + setSetting(Settings::EditTool_DeleteBezierPointAction, edit_tool_delete_bezier_point_action->currentData()); + setSetting(Settings::EditTool_DeleteBezierPointActionAlternative, edit_tool_delete_bezier_point_action_alternative->currentData()); + setSetting(Settings::RectangleTool_HelperCrossRadiusMM, rectangle_helper_cross_radius->value()); + setSetting(Settings::RectangleTool_PreviewLineWidth, rectangle_preview_line_width->isChecked()); +} + +void EditorSettingsPage::reset() +{ + updateWidgets(); +} + +void EditorSettingsPage::updateWidgets() +{ + icon_size->setValue(getSetting(Settings::SymbolWidget_IconSizeMM).toInt()); + antialiasing->setChecked(getSetting(Settings::MapDisplay_Antialiasing).toBool()); + text_antialiasing->setEnabled(antialiasing->isChecked()); + text_antialiasing->setChecked(getSetting(Settings::MapDisplay_TextAntialiasing).toBool()); + tolerance->setValue(getSetting(Settings::MapEditor_ClickToleranceMM).toInt()); + snap_distance->setValue(getSetting(Settings::MapEditor_SnapDistanceMM).toInt()); + fixed_angle_stepping->setValue(getSetting(Settings::MapEditor_FixedAngleStepping).toInt()); + select_symbol_of_objects->setChecked(getSetting(Settings::MapEditor_ChangeSymbolWhenSelecting).toBool()); + zoom_out_away_from_cursor->setChecked(getSetting(Settings::MapEditor_ZoomOutAwayFromCursor).toBool()); + draw_last_point_on_right_click->setChecked(getSetting(Settings::MapEditor_DrawLastPointOnRightClick).toBool()); + keep_settings_of_closed_templates->setChecked(getSetting(Settings::Templates_KeepSettingsOfClosed).toBool()); + + edit_tool_delete_bezier_point_action->setCurrentIndex(edit_tool_delete_bezier_point_action->findData(getSetting(Settings::EditTool_DeleteBezierPointAction).toInt())); + edit_tool_delete_bezier_point_action_alternative->setCurrentIndex(edit_tool_delete_bezier_point_action_alternative->findData(getSetting(Settings::EditTool_DeleteBezierPointActionAlternative).toInt())); + + rectangle_helper_cross_radius->setValue(getSetting(Settings::RectangleTool_HelperCrossRadiusMM).toInt()); + rectangle_preview_line_width->setChecked(getSetting(Settings::RectangleTool_PreviewLineWidth).toBool()); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/editor_settings_page.h b/src/gui/widgets/editor_settings_page.h new file mode 100644 index 0000000..f1e8c79 --- /dev/null +++ b/src/gui/widgets/editor_settings_page.h @@ -0,0 +1,76 @@ +/* + * Copyright 2012, 2013 Jan Dalheimer + * Copyright 2013-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_EDITOR_SETTINGS_PAGE_H +#define OPENORIENTEERING_EDITOR_SETTINGS_PAGE_H + +#include +#include + +#include "settings_page.h" + +class QCheckBox; +class QComboBox; +class QSpinBox; +class QWidget; + + +namespace OpenOrienteering { + +class EditorSettingsPage : public SettingsPage +{ +Q_OBJECT +public: + explicit EditorSettingsPage(QWidget* parent = nullptr); + + ~EditorSettingsPage() override; + + QString title() const override; + + void apply() override; + + void reset() override; + +protected: + void updateWidgets(); + +private: + QSpinBox* icon_size; + QCheckBox* antialiasing; + QCheckBox* text_antialiasing; + QSpinBox* tolerance; + QSpinBox* snap_distance; + QSpinBox* fixed_angle_stepping; + QCheckBox* select_symbol_of_objects; + QCheckBox* zoom_out_away_from_cursor; + QCheckBox* draw_last_point_on_right_click; + QCheckBox* keep_settings_of_closed_templates; + + QComboBox* edit_tool_delete_bezier_point_action; + QComboBox* edit_tool_delete_bezier_point_action_alternative; + + QSpinBox* rectangle_helper_cross_radius; + QCheckBox* rectangle_preview_line_width; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/general_settings_page.cpp b/src/gui/widgets/general_settings_page.cpp new file mode 100644 index 0000000..1c15f5b --- /dev/null +++ b/src/gui/widgets/general_settings_page.cpp @@ -0,0 +1,427 @@ +/* + * Copyright 2012, 2013 Jan Dalheimer + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "general_settings_page.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "gui/file_dialog.h" +#include "gui/main_window.h" +#include "gui/util_gui.h" +#include "gui/widgets/home_screen_widget.h" +#include "gui/widgets/settings_page.h" +#include "util/translation_util.h" + + +namespace OpenOrienteering { + +GeneralSettingsPage::GeneralSettingsPage(QWidget* parent) +: SettingsPage(parent) +, translation_file(getSetting(Settings::General_TranslationFile).toString()) +{ + auto layout = new QFormLayout(this); + + layout->addRow(Util::Headline::create(tr("Appearance"))); + + auto language_widget = new QWidget(); + auto language_layout = new QHBoxLayout(language_widget); + language_layout->setContentsMargins({}); + layout->addRow(tr("Language:"), language_widget); + + language_box = new QComboBox(this); + language_layout->addWidget(language_box); + + auto language_file_button = new QToolButton(); + if (MainWindow::mobileMode()) + { + language_file_button->setVisible(false); + } + else + { + language_file_button->setIcon(QIcon(QLatin1String(":/images/open.png"))); + } + language_layout->addWidget(language_file_button); + + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(Util::Headline::create(tr("Screen"))); + + auto ppi_widget = new QWidget(); + auto ppi_layout = new QHBoxLayout(ppi_widget); + ppi_layout->setContentsMargins({}); + layout->addRow(tr("Pixels per inch:"), ppi_widget); + + ppi_edit = Util::SpinBox::create(2, 0.01, 9999); + ppi_layout->addWidget(ppi_edit); + + auto ppi_calculate_button = new QToolButton(); + ppi_calculate_button->setIcon(QIcon(QLatin1String(":/images/settings.png"))); + ppi_layout->addWidget(ppi_calculate_button); + + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(Util::Headline::create(tr("Program start"))); + + open_mru_check = new QCheckBox(::OpenOrienteering::AbstractHomeScreenWidget::tr("Open most recently used file")); + layout->addRow(open_mru_check); + + tips_visible_check = new QCheckBox(::OpenOrienteering::AbstractHomeScreenWidget::tr("Show tip of the day")); + layout->addRow(tips_visible_check); + + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(Util::Headline::create(tr("Saving files"))); + + compatibility_check = new QCheckBox(tr("Retain compatibility with Mapper %1").arg(QLatin1String("0.5"))); +#ifdef MAPPER_ENABLE_COMPATIBILITY + layout->addRow(compatibility_check); +#else + // Let compatibility_check be valid, but not leak + connect(this, &QObject::destroyed, compatibility_check, &QObject::deleteLater); +#endif + + undo_check = new QCheckBox(tr("Save undo/redo history")); + layout->addRow(undo_check); + + autosave_check = new QCheckBox(tr("Save information for automatic recovery")); + layout->addRow(autosave_check); + + autosave_interval_edit = Util::SpinBox::create(1, 120, tr("min", "unit minutes"), 1); + layout->addRow(tr("Recovery information saving interval:"), autosave_interval_edit); + + layout->addItem(Util::SpacerItem::create(this)); + layout->addRow(Util::Headline::create(tr("File import and export"))); + + QStringList available_codecs; + available_codecs.append(tr("Default")); + encoding_box = new QComboBox(); + encoding_box->setEditable(true); + encoding_box->addItem(available_codecs.first()); + encoding_box->addItem(QString::fromLatin1("Windows-1252")); // Serves as an example, not translated. + const auto available_codecs_raw = QTextCodec::availableCodecs(); + available_codecs.reserve(available_codecs_raw.size()); + for (const QByteArray& item : available_codecs_raw) + { + available_codecs.append(QString::fromUtf8(item)); + } + if (available_codecs.size() > 1) + { + available_codecs.sort(Qt::CaseInsensitive); + available_codecs.removeDuplicates(); + encoding_box->addItem(tr("More...")); + } + auto completer = new QCompleter(available_codecs, this); + completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + completer->setCaseSensitivity(Qt::CaseInsensitive); + completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); + encoding_box->setCompleter(completer); + layout->addRow(tr("8-bit encoding:"), encoding_box); + + ocd_importer_check = new QCheckBox(tr("Use the new OCD importer also for version 8 files").replace(QLatin1Char('8'), QString::fromLatin1("6-8"))); + layout->addRow(ocd_importer_check); + + updateWidgets(); + + connect(language_file_button, &QAbstractButton::clicked, this, &GeneralSettingsPage::openTranslationFileDialog); + connect(ppi_calculate_button, &QAbstractButton::clicked, this, &GeneralSettingsPage::openPPICalculationDialog); + connect(encoding_box, &QComboBox::currentTextChanged, this, &GeneralSettingsPage::encodingChanged); + connect(autosave_check, &QAbstractButton::toggled, autosave_interval_edit, &QWidget::setEnabled); + connect(autosave_check, &QAbstractButton::toggled, layout->labelForField(autosave_interval_edit), &QWidget::setEnabled); + +} + +GeneralSettingsPage::~GeneralSettingsPage() = default; + + + +QString GeneralSettingsPage::title() const +{ + return tr("General"); +} + +void GeneralSettingsPage::apply() +{ + auto language = language_box->currentData().toString(); + if (language != getSetting(Settings::General_Language).toString() + || translation_file != getSetting(Settings::General_TranslationFile).toString()) + { + // Show an message box in the new language. + TranslationUtil translation(language, translation_file); + auto new_language = QLocale(translation.code()).language(); + switch (new_language) + { + case QLocale::AnyLanguage: + case QLocale::C: + case QLocale::English: + QMessageBox::information(window(), QLatin1String("Notice"), QLatin1String("The program must be restarted for the language change to take effect!")); + break; + + default: + qApp->installEventFilter(this); + qApp->installTranslator(&translation.getQtTranslator()); + qApp->installTranslator(&translation.getAppTranslator()); + QMessageBox::information(window(), tr("Notice"), tr("The program must be restarted for the language change to take effect!")); + qApp->removeTranslator(&translation.getAppTranslator()); + qApp->removeTranslator(&translation.getQtTranslator()); + qApp->removeEventFilter(this); + } + + setSetting(Settings::General_Language, translation.code()); + setSetting(Settings::General_TranslationFile, translation_file); +#if defined(Q_OS_MACOS) + // The native [file] dialogs will use the first element of the + // AppleLanguages array in the application's .plist file - + // and this file is also the one used by QSettings. + QSettings().setValue(QString::fromLatin1("AppleLanguages"), QStringList{ translation.code() }); +#endif + } + + setSetting(Settings::General_OpenMRUFile, open_mru_check->isChecked()); + setSetting(Settings::HomeScreen_TipsVisible, tips_visible_check->isChecked()); + setSetting(Settings::General_NewOcd8Implementation, ocd_importer_check->isChecked()); + setSetting(Settings::General_RetainCompatiblity, compatibility_check->isChecked()); + setSetting(Settings::General_SaveUndoRedo, undo_check->isChecked()); + setSetting(Settings::General_PixelsPerInch, ppi_edit->value()); + + auto encoding = encoding_box->currentText().toLatin1(); + if (QLatin1String(encoding) == encoding_box->itemText(0) + || !QTextCodec::codecForName(encoding)) + { + encoding = "Default"; + } + setSetting(Settings::General_Local8BitEncoding, encoding); + + int interval = autosave_interval_edit->value(); + if (!autosave_check->isChecked()) + interval = -interval; + setSetting(Settings::General_AutosaveInterval, interval); +} + +void GeneralSettingsPage::reset() +{ + translation_file = getSetting(Settings::General_TranslationFile).toString(); + updateWidgets(); +} + +void GeneralSettingsPage::updateLanguageBox(QVariant code) +{ + auto languages = TranslationUtil::availableLanguages(); + std::sort(begin(languages), end(languages)); + + const QSignalBlocker block(language_box); + language_box->clear(); + for (const auto& language : languages) + language_box->addItem(language.displayName, language.code); + + // If there is an explicit translation file, make sure it is in the box. + auto language = TranslationUtil::languageFromFilename(translation_file); + if (language.isValid()) + { + auto index = language_box->findData(language.code); + if (index < 0) + language_box->addItem(language.displayName, language.code); + } + + // Select current language + auto index = language_box->findData(code); + if (index < 0) + { + code = QString::fromLatin1("en"); + index = language_box->findData(code); + } + language_box->setCurrentIndex(index); +} + +void GeneralSettingsPage::updateWidgets() +{ + updateLanguageBox(getSetting(Settings::General_Language)); + + ppi_edit->setValue(getSetting(Settings::General_PixelsPerInch).toDouble()); + open_mru_check->setChecked(getSetting(Settings::General_OpenMRUFile).toBool()); + tips_visible_check->setChecked(getSetting(Settings::HomeScreen_TipsVisible).toBool()); + compatibility_check->setChecked(getSetting(Settings::General_RetainCompatiblity).toBool()); + undo_check->setChecked(getSetting(Settings::General_SaveUndoRedo).toBool()); + int autosave_interval = getSetting(Settings::General_AutosaveInterval).toInt(); + autosave_check->setChecked(autosave_interval > 0); + autosave_interval_edit->setEnabled(autosave_interval > 0); + autosave_interval_edit->setValue(qAbs(autosave_interval)); + + auto encoding = getSetting(Settings::General_Local8BitEncoding).toByteArray(); + if (encoding != "Default" + && QTextCodec::codecForName(encoding)) + { + encoding_box->setCurrentText(QString::fromLatin1(encoding)); + } + + ocd_importer_check->setChecked(getSetting(Settings::General_NewOcd8Implementation).toBool()); +} + +// slot +void GeneralSettingsPage::encodingChanged(const QString& input) +{ + if (input == tr("More...")) + { + const QSignalBlocker block(encoding_box); + encoding_box->setCurrentText(last_encoding_input); + encoding_box->completer()->setCompletionPrefix(last_encoding_input); + encoding_box->completer()->complete(); + } + else + { + // Inline completition, in addition to UnfilteredPopupCompletition + // Don't complete after pressing Backspace or Del + if (!last_encoding_input.startsWith(input)) + { + if (last_matching_completition.startsWith(input)) + encoding_box->completer()->setCompletionPrefix(last_matching_completition); + + auto text = encoding_box->completer()->currentCompletion(); + auto line_edit = encoding_box->lineEdit(); + if (text.startsWith(input) + && line_edit) + { + const QSignalBlocker block(encoding_box); + auto pos = line_edit->cursorPosition(); + line_edit->setText(text); + line_edit->setSelection(text.length(), pos - text.length()); + last_encoding_input = input.left(pos); + last_matching_completition = text; + return; + } + } + last_encoding_input = input; + } +} + +// slot +void GeneralSettingsPage::openTranslationFileDialog() +{ + QString filename = translation_file; + if (filename.isEmpty()) + filename = getSetting(Settings::General_TranslationFile).toString(); + + filename = FileDialog::getOpenFileName(this, + tr("Open translation"), filename, tr("Translation files (*.qm)")); + if (!filename.isNull()) + { + auto language = TranslationUtil::languageFromFilename(filename); + if (!language.isValid()) + { + QMessageBox::critical(this, tr("Open translation"), + tr("The selected file is not a valid translation.") ); + } + else + { + translation_file = filename; + updateLanguageBox(language.code); + } + } +} + +// slot +void GeneralSettingsPage::openPPICalculationDialog() +{ + int primary_screen_width = QApplication::primaryScreen()->size().width(); + int primary_screen_height = QApplication::primaryScreen()->size().height(); + double screen_diagonal_pixels = double(qSqrt(primary_screen_width*primary_screen_width + primary_screen_height*primary_screen_height)); + + double old_ppi = ppi_edit->value(); + double old_screen_diagonal_inches = screen_diagonal_pixels / old_ppi; + + auto dialog = new QDialog(window(), Qt::WindowSystemMenuHint | Qt::WindowTitleHint); + if (MainWindow::mobileMode()) + { + dialog->setGeometry(window()->geometry()); + } + + auto layout = new QVBoxLayout(dialog); + + auto form_layout = new QFormLayout(); + + auto resolution_display = new QLabel(tr("%1 x %2").arg(primary_screen_width).arg(primary_screen_height)); + form_layout->addRow(tr("Primary screen resolution in pixels:"), resolution_display); + + auto size_edit = Util::SpinBox::create(2, 0.01, 9999); + size_edit->setValue(old_screen_diagonal_inches); + form_layout->addRow(tr("Primary screen size in inches (diagonal):"), size_edit); + + layout->addLayout(form_layout); + + layout->addItem(Util::SpacerItem::create(this)); + + auto button_box = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + layout->addWidget(button_box); + + connect(button_box, &QDialogButtonBox::accepted, dialog, &QDialog::accept); + connect(button_box, &QDialogButtonBox::rejected, dialog, &QDialog::reject); + dialog->exec(); + + if (dialog->result() == QDialog::Accepted) + { + ppi_edit->setValue(screen_diagonal_pixels / size_edit->value()); + } +} + +bool GeneralSettingsPage::eventFilter(QObject* /* watched */, QEvent* event) +{ + if (event->type() == QEvent::LanguageChange) + return true; // NOLINT + + return false; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/general_settings_page.h b/src/gui/widgets/general_settings_page.h new file mode 100644 index 0000000..1fec962 --- /dev/null +++ b/src/gui/widgets/general_settings_page.h @@ -0,0 +1,98 @@ +/* + * Copyright 2012, 2013 Jan Dalheimer + * Copyright 2013-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_GENERAL_SETTINGS_PAGE_H +#define OPENORIENTEERING_GENERAL_SETTINGS_PAGE_H + +#include +#include +#include + +#include "settings_page.h" + +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QEvent; +class QSpinBox; +class QWidget; + + +namespace OpenOrienteering { + +class GeneralSettingsPage : public SettingsPage +{ +Q_OBJECT +public: + explicit GeneralSettingsPage(QWidget* parent = nullptr); + + ~GeneralSettingsPage() override; + + QString title() const override; + + void apply() override; + + void reset() override; + +protected: + /** + * Adds the available languages to the language combo box, + * and sets the current element. + */ + void updateLanguageBox(QVariant code); + + void updateWidgets(); + + /** + * This event filter stops LanguageChange events. + */ + bool eventFilter(QObject* watched, QEvent* event) override; + +private slots: + void openTranslationFileDialog(); + + void openPPICalculationDialog(); + + void encodingChanged(const QString& input); + +private: + QString translation_file; + QString last_encoding_input; + QString last_matching_completition; + + QComboBox* language_box; + + QDoubleSpinBox* ppi_edit; + QCheckBox* open_mru_check; + QCheckBox* tips_visible_check; + + QCheckBox* compatibility_check; + QCheckBox* undo_check; + QCheckBox* autosave_check; + QSpinBox* autosave_interval_edit; + + QComboBox* encoding_box; + QCheckBox* ocd_importer_check; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/home_screen_widget.cpp b/src/gui/widgets/home_screen_widget.cpp new file mode 100644 index 0000000..e4be539 --- /dev/null +++ b/src/gui/widgets/home_screen_widget.cpp @@ -0,0 +1,592 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "home_screen_widget.h" + +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/storage_location.h" // IWYU pragma: keep +#include "fileformats/file_format_registry.h" +#include "gui/home_screen_controller.h" +#include "gui/main_window.h" +#include "gui/settings_dialog.h" +#include "gui/util_gui.h" + + +namespace OpenOrienteering { + +//### AbstractHomeScreenWidget ### + +AbstractHomeScreenWidget::AbstractHomeScreenWidget(HomeScreenController* controller, QWidget* parent) +: QWidget(parent), + controller(controller) +{ + Q_ASSERT(controller->getWindow()); +} + +AbstractHomeScreenWidget::~AbstractHomeScreenWidget() +{ + // nothing +} + +QLabel* AbstractHomeScreenWidget::makeHeadline(const QString& text, QWidget* parent) const +{ + QLabel* title_label = new QLabel(text, parent); + QFont title_font = title_label->font(); + int pixel_size = title_font.pixelSize(); + if (pixel_size > 0) + { + title_font.setPixelSize(pixel_size * 2); + } + else + { + pixel_size = title_font.pointSize(); + title_font.setPointSize(pixel_size * 2); + } + title_font.setBold(true); + title_label->setFont(title_font); + title_label->setAlignment(Qt::AlignLeft | Qt::AlignTop); + return title_label; +} + +QAbstractButton* AbstractHomeScreenWidget::makeButton(const QString& text, QWidget* parent) const +{ + QAbstractButton* button = new QCommandLinkButton(text, parent); + QFont button_font = button->font(); + int pixel_size = button_font.pixelSize(); + if (pixel_size > 0) + { + button_font.setPixelSize(pixel_size * 3 / 2); + } + else + { + pixel_size = button_font.pointSize(); + button_font.setPointSize(pixel_size * 3 / 2); + } + button->setFont(button_font); + return button; +} + +QAbstractButton* AbstractHomeScreenWidget::makeButton(const QString& text, const QIcon& icon, QWidget* parent) const +{ + QAbstractButton* button = makeButton(text, parent); + button->setIcon(icon); + return button; +} + + + +//### HomeScreenWidgetDesktop ### + +HomeScreenWidgetDesktop::HomeScreenWidgetDesktop(HomeScreenController* controller, QWidget* parent) +: AbstractHomeScreenWidget(controller, parent) +{ + QLabel* title_label = new QLabel(QString::fromLatin1("")); + title_label->setAlignment(Qt::AlignCenter); + QWidget* menu_widget = makeMenuWidget(controller, parent); + QWidget* recent_files_widget = makeRecentFilesWidget(controller, parent); + QWidget* tips_widget = makeTipsWidget(controller, parent); + + QGridLayout* layout = new QGridLayout(); + layout->setSpacing(2 * layout->spacing()); + layout->addWidget(title_label, 0, 0, 1, 2); + layout->addWidget(menu_widget, 1, 0, 2, 1); + layout->addWidget(recent_files_widget, 1, 1); + layout->setRowStretch(1, 4); + layout->addWidget(tips_widget, 2, 1); + layout->setRowStretch(2, 3); + setLayout(layout); + + setAutoFillBackground(false); +} + +HomeScreenWidgetDesktop::~HomeScreenWidgetDesktop() +{ + // nothing +} + +QWidget* HomeScreenWidgetDesktop::makeMenuWidget(HomeScreenController* controller, QWidget* parent) +{ + MainWindow* window = controller->getWindow(); + + QVBoxLayout* menu_layout = new QVBoxLayout(); + + QLabel* menu_headline = makeHeadline(tr("Activities")); + menu_layout->addWidget(menu_headline); + QAbstractButton* button_new_map = makeButton( + tr("Create a new map ..."), QIcon(QString::fromLatin1(":/images/new.png"))); + menu_layout->addWidget(button_new_map); + QAbstractButton* button_open_map = makeButton( + tr("Open map ..."), QIcon(QString::fromLatin1(":/images/open.png"))); + menu_layout->addWidget(button_open_map); + + menu_layout->addStretch(1); + + QAbstractButton* button_settings = makeButton( + tr("Settings"), QIcon(QString::fromLatin1(":/images/settings.png"))); + menu_layout->addWidget(button_settings); + QAbstractButton* button_about = makeButton( + tr("About %1", "As in 'About OpenOrienteering Mapper'").arg(window->appName()), QIcon(QString::fromLatin1(":/images/about.png"))); + menu_layout->addWidget(button_about); + QAbstractButton* button_help = makeButton( + tr("Help"), QIcon(QString::fromLatin1(":/images/help.png"))); + menu_layout->addWidget(button_help); + QAbstractButton* button_exit = makeButton( + tr("Exit"), QIcon(QString::fromLatin1(":/qt-project.org/styles/commonstyle/images/standardbutton-close-32.png"))); // From Qt5 + menu_layout->addWidget(button_exit); + + connect(button_new_map, &QAbstractButton::clicked, window, &MainWindow::showNewMapWizard); + connect(button_open_map, &QAbstractButton::clicked, window, &MainWindow::showOpenDialog); + connect(button_settings, &QAbstractButton::clicked, window, &MainWindow::showSettings); + connect(button_about, &QAbstractButton::clicked, window, &MainWindow::showAbout); + connect(button_help, &QAbstractButton::clicked, window, &MainWindow::showHelp); + connect(button_exit, &QAbstractButton::clicked, qApp, &QApplication::closeAllWindows); + + QWidget* menu_widget = new QWidget(parent); + menu_widget->setLayout(menu_layout); + menu_widget->setAutoFillBackground(true); + return menu_widget; +} + +QWidget* HomeScreenWidgetDesktop::makeRecentFilesWidget(HomeScreenController* controller, QWidget* parent) +{ + QGridLayout* recent_files_layout = new QGridLayout(); + + QLabel* recent_files_headline = makeHeadline(tr("Recent maps")); + recent_files_layout->addWidget(recent_files_headline, 0, 0, 1, 2); + + recent_files_list = new QListWidget(); + QFont list_font = recent_files_list->font(); + int pixel_size = list_font.pixelSize(); + if (pixel_size > 0) + { + list_font.setPixelSize(pixel_size * 3 / 2); + } + else + { + pixel_size = list_font.pointSize(); + list_font.setPointSize(pixel_size * 3 / 2); + } + recent_files_list->setFont(list_font); + recent_files_list->setSpacing(pixel_size/2); + recent_files_list->setCursor(Qt::PointingHandCursor); + recent_files_list->setStyleSheet(QString::fromLatin1(" \ + QListWidget::item:hover { \ + color: palette(highlighted-text); \ + background: palette(highlight); \ + } ")); + recent_files_layout->addWidget(recent_files_list, 1, 0, 1, 2); + + open_mru_file_check = new QCheckBox(tr("Open most recently used file on start")); + recent_files_layout->addWidget(open_mru_file_check, 2, 0, 1, 1); + + QPushButton* clear_list_button = new QPushButton(tr("Clear list")); + recent_files_layout->addWidget(clear_list_button, 2, 1, 1, 1); + + recent_files_layout->setRowStretch(1, 1); + recent_files_layout->setColumnStretch(0, 1); + + connect(recent_files_list, &QListWidget::itemClicked, this, &HomeScreenWidgetDesktop::recentFileClicked); + connect(open_mru_file_check, &QAbstractButton::clicked, controller, &HomeScreenController::setOpenMRUFile); + connect(clear_list_button, &QAbstractButton::clicked, controller, &HomeScreenController::clearRecentFiles); + + QWidget* recent_files_widget = new QWidget(parent); + recent_files_widget->setLayout(recent_files_layout); + recent_files_widget->setAutoFillBackground(true); + return recent_files_widget; +} + +QWidget* HomeScreenWidgetDesktop::makeTipsWidget(HomeScreenController* controller, QWidget* parent) +{ + QGridLayout* tips_layout = new QGridLayout(); + QWidget* tips_headline = makeHeadline(tr("Tip of the day")); + tips_layout->addWidget(tips_headline, 0, 0, 1, 3); + tips_label = new QLabel(); + tips_label->setWordWrap(true); + tips_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + tips_check = new QCheckBox(tr("Show tip of the day")); + tips_check->setChecked(true); + tips_layout->addWidget(tips_check, 2, 0, 1, 1); + tips_layout->addWidget(tips_label, 1, 0, 1, 3); + QPushButton* prev_button = new QPushButton(QIcon(QString::fromLatin1(":/images/arrow-left.png")), tr("Previous")); + tips_layout->addWidget(prev_button, 2, 1, 1, 1); + QPushButton* next_button = new QPushButton(QIcon(QString::fromLatin1(":/images/arrow-right.png")), tr("Next")); + tips_layout->addWidget(next_button, 2, 2, 1, 1); + + tips_layout->setRowStretch(1, 1); + tips_layout->setColumnStretch(0, 1); + + tips_children.reserve(4); + tips_children.push_back(tips_headline); + tips_children.push_back(tips_label); + tips_children.push_back(prev_button); + tips_children.push_back(next_button); + + MainWindow* window = controller->getWindow(); + connect(tips_label, &QLabel::linkActivated, window, &MainWindow::linkClicked); + connect(tips_check, &QAbstractButton::clicked, controller, &HomeScreenController::setTipsVisible); + connect(prev_button, &QAbstractButton::clicked, controller, &HomeScreenController::goToPreviousTip); + connect(next_button, &QAbstractButton::clicked, controller, &HomeScreenController::goToNextTip); + + QWidget* tips_widget = new QWidget(parent); + tips_widget->setLayout(tips_layout); + tips_widget->setAutoFillBackground(true); + return tips_widget; +} + +void HomeScreenWidgetDesktop::setRecentFiles(const QStringList& files) +{ + recent_files_list->clear(); + for (auto&& file : files) + { + QListWidgetItem* new_item = new QListWidgetItem(QFileInfo(file).fileName()); + new_item->setData(pathRole(), file); + new_item->setToolTip(file); + recent_files_list->addItem(new_item); + } +} + +void HomeScreenWidgetDesktop::recentFileClicked(QListWidgetItem* item) +{ + setEnabled(false); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + QString path = item->data(pathRole()).toString(); + controller->getWindow()->openPath(path); + setEnabled(true); +} + +void HomeScreenWidgetDesktop::paintEvent(QPaintEvent*) +{ + // Background + QPainter p(this); + p.setPen(Qt::NoPen); + p.setBrush(Qt::gray); + p.drawRect(rect()); +} + +void HomeScreenWidgetDesktop::setOpenMRUFileChecked(bool state) +{ + open_mru_file_check->setChecked(state); +} + +void HomeScreenWidgetDesktop::setTipOfTheDay(const QString& text) +{ + tips_label->setText(text); +} + +void HomeScreenWidgetDesktop::setTipsVisible(bool state) +{ + QGridLayout* layout = qobject_cast(this->layout()); + for (auto widget : tips_children) + { + widget->setVisible(state); + } + if (layout) + layout->setRowStretch(2, state ? 3 : 0); + + tips_check->setChecked(state); +} + + + +//### HomeScreenWidgetMobile ### + +HomeScreenWidgetMobile::HomeScreenWidgetMobile(HomeScreenController* controller, QWidget* parent) +: AbstractHomeScreenWidget(controller, parent) +{ + auto* layout = new QVBoxLayout(); + layout->setSpacing(2 * layout->spacing()); + + title_pixmap = QPixmap::fromImage(QImage(QString::fromLatin1(":/images/title.png"))); + title_label = new QLabel(); + title_label->setPixmap(title_pixmap); + title_label->setAlignment(Qt::AlignCenter); + layout->addWidget(title_label); + + file_list_widget = makeFileListWidget(); + connect(file_list_widget, &QListWidget::itemClicked, this, &HomeScreenWidgetMobile::fileClicked); + layout->addWidget(file_list_widget, 1); + + auto settings_button = new QPushButton(HomeScreenWidgetDesktop::tr("Settings")); + connect(settings_button, &QPushButton::clicked, controller->getWindow(), &MainWindow::showSettings); + QPushButton* help_button = new QPushButton(HomeScreenWidgetDesktop::tr("Help")); + connect(help_button, &QPushButton::clicked, controller->getWindow(), &MainWindow::showHelp); + QPushButton* about_button = new QPushButton(tr("About Mapper")); + connect(about_button, &QPushButton::clicked, controller->getWindow(), &MainWindow::showAbout); + QHBoxLayout* buttons_layout = new QHBoxLayout(); + buttons_layout->setContentsMargins(0, 0, 0, 0); + buttons_layout->addWidget(settings_button); + buttons_layout->addStretch(1); + buttons_layout->addWidget(help_button); + buttons_layout->addWidget(about_button); + layout->addLayout(buttons_layout); + + setLayout(layout); + setAutoFillBackground(false); + + updateFileListWidget(); +} + +HomeScreenWidgetMobile::~HomeScreenWidgetMobile() = default; + + +void HomeScreenWidgetMobile::resizeEvent(QResizeEvent* /*event*/) +{ + adjustTitlePixmapSize(); +} + +void HomeScreenWidgetMobile::adjustTitlePixmapSize() +{ + auto label_size = title_label->size(); + auto scaled_width = qRound(title_pixmap.devicePixelRatio() * label_size.width()); + if (title_pixmap.width() > scaled_width) + { + if (title_label->pixmap()->width() != scaled_width) + { + label_size.setHeight(title_pixmap.height()); + title_label->setPixmap(title_pixmap.scaled(label_size, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + } + } + else if (title_label->pixmap()->width() != title_pixmap.width()) + { + title_label->setPixmap(title_pixmap); + } +} + +void HomeScreenWidgetMobile::setRecentFiles(const QStringList& /*files*/) +{ + // nothing +} + +void HomeScreenWidgetMobile::setOpenMRUFileChecked(bool /*state*/) +{ + // nothing +} + +void HomeScreenWidgetMobile::setTipOfTheDay(const QString& /*text*/) +{ + // nothing +} + +void HomeScreenWidgetMobile::setTipsVisible(bool /*state*/) +{ + // nothing +} + +void HomeScreenWidgetMobile::showSettings() +{ + auto window = this->window(); + + SettingsDialog dialog(window); + dialog.setGeometry(window->geometry()); + dialog.exec(); +} + +void HomeScreenWidgetMobile::fileClicked(QListWidgetItem* item) +{ + auto file_path = item->data(pathRole()).toString(); + if (file_path == QLatin1String("doc:")) + { +#ifdef Q_OS_ANDROID + Util::showHelp(window(), "android-storage.html"); +#endif + } + else if (file_path == QLatin1String("..")) + { + if (!history.empty()) + history.pop_back(); + updateFileListWidget(); + } + else if (QFileInfo(file_path).isDir()) + { + history.push_back({file_path, StorageLocation::Hint(item->data(hintRole()).toInt())}); + updateFileListWidget(); + } + else + { + auto hint_text = item->data(hintRole()).toString(); + if (!hint_text.isEmpty()) + QMessageBox::warning(this, ::OpenOrienteering::MainWindow::tr("Warning"), hint_text.arg(item->data(Qt::DisplayRole).toString())); + + setEnabled(false); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + controller->getWindow()->openPath(file_path); + setEnabled(true); + } +} + +QListWidget* HomeScreenWidgetMobile::makeFileListWidget() +{ + file_list_widget = new QListWidget(); + QScroller::grabGesture(file_list_widget->viewport(), QScroller::TouchGesture); + QFont list_font = file_list_widget->font(); + int pixel_size = list_font.pixelSize(); + if (pixel_size > 0) + { + list_font.setPixelSize(pixel_size * 3 / 2); + } + else + { + pixel_size = list_font.pointSize(); + list_font.setPointSize(pixel_size * 3 / 2); + } + file_list_widget->setFont(list_font); + file_list_widget->setSpacing(pixel_size/2); + file_list_widget->setCursor(Qt::PointingHandCursor); + file_list_widget->setStyleSheet(QString::fromLatin1(" \ + QListWidget::item:hover { \ + color: palette(highlighted-text); \ + background: palette(highlight); \ + } ")); + + return file_list_widget; +} + +void HomeScreenWidgetMobile::updateFileListWidget() +{ + file_list_widget->clear(); + if (history.empty()) + { + // First screen. + // Recent files first. + Settings& settings = Settings::getInstance(); + auto recent_files = settings.getSetting(Settings::General_RecentFilesList).toStringList(); + for (auto& file_path : recent_files) + { + auto file_info = QFileInfo(file_path); + if (file_info.exists()) + addItemToFileList(file_info); + } + +#ifdef Q_OS_ANDROID + // If there are no recent files, offer a link to the Android storage manual page. + if (recent_files.isEmpty()) + { + auto* help_item = new QListWidgetItem(tr("Help")); + help_item->setData(pathRole(), QLatin1String("doc:")); + help_item->setIcon(file_list_widget->style()->standardIcon(QStyle::SP_DialogHelpButton)); + file_list_widget->addItem(help_item); + } +#endif + + // Device-specific locations next. + // For disambiguation, using the full path for the label. + StorageLocation::refresh(); + const auto locations = StorageLocation::knownLocations(); + for (const auto& location : *locations) + { + auto file_info = QFileInfo(location.path()); + auto icon = file_list_widget->style()->standardIcon(QStyle::SP_DirIcon); + addItemToFileList(location.path(), file_info, location.hint(), icon); + } + + // Examples last. + // The examples path isn't writable, so the hint will be overridden. + auto file_info = QFileInfo(QLatin1String("data:/examples")); + addItemToFileList(tr("Examples"), file_info); + } + else + { + // Backwards navigation on top. + auto* parent_item = new QListWidgetItem(QLatin1String("..")); + parent_item->setData(pathRole(), QLatin1String("..")); + parent_item->setIcon(file_list_widget->style()->standardIcon(QStyle::SP_FileDialogToParent)); + file_list_widget->addItem(parent_item); + + // Contents of selected location, files first. + const auto& location = history.back(); + QIcon icon; + switch (location.hint()) + { + case StorageLocation::HintApplication: + icon = file_list_widget->style()->standardIcon(QStyle::SP_MessageBoxInformation); + break; + case StorageLocation::HintReadOnly: + icon = file_list_widget->style()->standardIcon(QStyle::SP_MessageBoxWarning); + break; + case StorageLocation::HintNormal: + case StorageLocation::HintInvalid: + break; + } + + constexpr auto filters = QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot; + constexpr auto flags = QDir::DirsLast | QDir::Name | QDir::IgnoreCase | QDir::LocaleAware; + auto info_list = QDir(location.path()).entryInfoList(filters, flags); + for (const auto& file_info : info_list) + { + addItemToFileList(file_info, location.hint(), icon); + } + } +} + + +void HomeScreenWidgetMobile::addItemToFileList(const QFileInfo& file_info, int hint, const QIcon& icon) +{ + addItemToFileList(file_info.fileName(), file_info, hint, icon); +} + +void HomeScreenWidgetMobile::addItemToFileList(const QString& label, const QFileInfo& file_info, int hint, const QIcon& icon) +{ + const auto file_path = file_info.filePath(); + const auto* format = FileFormats.findFormatForFilename(file_path); + if (file_info.isDir() || + (format && format->fileType() == FileFormat::MapFile)) + { + auto* new_item = new QListWidgetItem(label); + new_item->setData(pathRole(), file_path); + new_item->setToolTip(file_path); + if (file_info.isDir()) + { + // Use dir icon, numerical hint. + new_item->setIcon(icon.isNull() ? file_list_widget->style()->standardIcon(QStyle::SP_DirIcon) : icon); + new_item->setData(hintRole(), hint); + } + else if (hint == StorageLocation::HintReadOnly + || (file_info.isWritable() && format->supportsExport())) + { + // Use icon/hint as-is. + new_item->setIcon(icon); + new_item->setData(hintRole(), StorageLocation(file_path, StorageLocation::Hint(hint)).hintText()); + } + else + { + // Override with read-only warning. + new_item->setIcon(file_list_widget->style()->standardIcon(QStyle::SP_MessageBoxWarning)); + new_item->setData(hintRole(), StorageLocation(file_path, StorageLocation::HintReadOnly).hintText()); + } + file_list_widget->addItem(new_item); + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/home_screen_widget.h b/src/gui/widgets/home_screen_widget.h new file mode 100644 index 0000000..f236cae --- /dev/null +++ b/src/gui/widgets/home_screen_widget.h @@ -0,0 +1,220 @@ +/* + * Copyright 2012, 2013, 2014 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_HOME_SCREEN_WIDGET_H +#define OPENORIENTEERING_HOME_SCREEN_WIDGET_H + +#include + +#include +#include +#include +#include +#include +#include + +class QAbstractButton; +class QCheckBox; +class QFileInfo; +class QIcon; +class QLabel; +class QListWidget; +class QListWidgetItem; +class QPaintEvent; +class QPushButton; +class QResizeEvent; +class QStackedLayout; + +namespace OpenOrienteering { + +class HomeScreenController; +class StorageLocation; + + +/** + * The user interface of the OpenOrienteering Mapper home screen. + * The OpenOrienteering Mapper home screen is shown when no document is open, + * for example after the program is started for the first time. + */ +class AbstractHomeScreenWidget : public QWidget +{ +Q_OBJECT +public: + /** Creates a new AbstractHomeScreenWidget for the given controller. */ + AbstractHomeScreenWidget(HomeScreenController* controller, QWidget* parent = nullptr); + + /** Destroys the AbstractHomeScreenWidget. */ + ~AbstractHomeScreenWidget() override; + +public slots: + /** Sets the list of recent files. */ + virtual void setRecentFiles(const QStringList& files) = 0; + + /** Sets the "checked" state of the control for opening + * the most recently used file on startup. */ + virtual void setOpenMRUFileChecked(bool state) = 0; + + /** Sets the text of the the tip-of-the-day. */ + virtual void setTipOfTheDay(const QString& text) = 0; + + /** Sets the visiblity of the tip-of-the-day, and + * sets the "checked" state of the control for displaying the tip. */ + virtual void setTipsVisible(bool state) = 0; + +protected: + /** Returns a QLabel for displaying a headline in the home screen. */ + QLabel* makeHeadline(const QString& text, QWidget* parent = nullptr) const; + + /** Returns a button with the given text + * for triggering a top level activity in the home screen. + * There will be no icon or a default icon. */ + QAbstractButton* makeButton(const QString& text, QWidget* parent = nullptr) const; + + /** Returns a button with the given text and icon + * for triggering a top level activity in the home screen. */ + QAbstractButton* makeButton(const QString& text, const QIcon& icon, QWidget* parent = nullptr) const; + + /** Returns the role to be used for storing paths. */ + constexpr static int pathRole() { return Qt::UserRole; } + + HomeScreenController* controller; +}; + + +/** + * Implementation of AbstractHomeScreenWidget for desktop PCs. + */ +class HomeScreenWidgetDesktop : public AbstractHomeScreenWidget +{ +Q_OBJECT +public: + /** Creates a new HomeScreenWidgetDesktop for the given controller. */ + HomeScreenWidgetDesktop(HomeScreenController* controller, QWidget* parent = nullptr); + + /** Destroys the HomeScreenWidgetDesktop. */ + ~HomeScreenWidgetDesktop() override; + +public slots: + /** Sets the list of recent files. */ + void setRecentFiles(const QStringList& files) override; + + /** Sets the "checked" state of the control for opening + * the most recently used file on startup. */ + void setOpenMRUFileChecked(bool state) override; + + /** Sets the text of the the tip-of-the-day. */ + void setTipOfTheDay(const QString& text) override; + + /** Sets the visiblity of the tip-of-the-day, and + * sets the "checked" state of the control for displaying the tip. */ + void setTipsVisible(bool state) override; + +protected slots: + /** Opens a file when its is list item is clicked. */ + void recentFileClicked(QListWidgetItem* item); + +protected: + /** Draws the home screen background when a paint event occurs. */ + void paintEvent(QPaintEvent* event) override; + + /** Creates the activities widget. */ + QWidget* makeMenuWidget(HomeScreenController* controller, QWidget* parent = nullptr); + + /** Creates the recent files widget. */ + QWidget* makeRecentFilesWidget(HomeScreenController* controller, QWidget* parent = nullptr); + + /** Creates the tip-of-the-day widget. */ + QWidget* makeTipsWidget(HomeScreenController* controller, QWidget* parent = nullptr); + + + QListWidget* recent_files_list; + QCheckBox* open_mru_file_check; + QLabel* tips_label; + QCheckBox* tips_check; + std::vector tips_children; +}; + + +/** + * Implementation of AbstractHomeScreenWidget for mobile devices. + */ +class HomeScreenWidgetMobile : public AbstractHomeScreenWidget +{ +Q_OBJECT +public: + /** Creates a new HomeScreenWidgetMobile for the given controller. */ + HomeScreenWidgetMobile(HomeScreenController* controller, QWidget* parent = nullptr); + + /** Destroys the HomeScreenWidgetMobile. */ + ~HomeScreenWidgetMobile() override; + +public slots: + /** Sets the list of recent files. */ + void setRecentFiles(const QStringList& files) override; + + /** Sets the "checked" state of the control for opening + * the most recently used file on startup. */ + void setOpenMRUFileChecked(bool state) override; + + /** Sets the text of the the tip-of-the-day. */ + void setTipOfTheDay(const QString& text) override; + + /** Sets the visiblity of the tip-of-the-day, and + * sets the "checked" state of the control for displaying the tip. */ + void setTipsVisible(bool state) override; + + /** Shows the settings dialog, adjusted for small screens. */ + void showSettings(); + +protected: + /** Opens a file when its is list item is clicked. */ + void fileClicked(QListWidgetItem* item); + + /** Triggers title image adjustment on resize events. */ + void resizeEvent(QResizeEvent* event) override; + + /** Resizes the title image to fit both the labels width and height */ + void adjustTitlePixmapSize(); + + /** Creates the file list widget. */ + QListWidget* makeFileListWidget(); + + /** Updates the file list widget. */ + void updateFileListWidget(); + + /** Add a single file or dir item to the file list. */ + void addItemToFileList(const QFileInfo& file_info, int hint = 0, const QIcon& icon = {}); + + /** Add a single item to the file list, using a custom display name. */ + void addItemToFileList(const QString& label, const QFileInfo& file_info, int hint = 0, const QIcon& icon = {}); + + /** Returns the role to be used for storing hints (number or text). */ + constexpr static int hintRole() { return Qt::UserRole + 1; } + +private: + QPixmap title_pixmap; + QLabel* title_label; + QListWidget* file_list_widget; + std::vector history; +}; + + +} // namespace OpenOrienteering +#endif diff --git a/src/gui/widgets/key_button_bar.cpp b/src/gui/widgets/key_button_bar.cpp new file mode 100644 index 0000000..a547a57 --- /dev/null +++ b/src/gui/widgets/key_button_bar.cpp @@ -0,0 +1,307 @@ +/* + * Copyright 2014 Thomas Schöps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "key_button_bar.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace OpenOrienteering { + +namespace { + + int modifierFromKeyCode(Qt::Key key_code) + { + switch(key_code) + { + case Qt::Key_Control: + return Qt::ControlModifier; + + case Qt::Key_Shift: + return Qt::ShiftModifier; + + case Qt::Key_Alt: + return Qt::AltModifier; + + default: + return Qt::NoModifier; + } + + Q_UNREACHABLE(); + } + + + int keyFromModifierCode(Qt::KeyboardModifier modifier) + { + switch(modifier) + { + case Qt::ControlModifier: + return Qt::Key_Control; + + case Qt::ShiftModifier: + return Qt::Key_Shift; + + case Qt::AltModifier: + return Qt::Key_Alt; + + default: + return 0; + } + + Q_UNREACHABLE(); + } + + +} // namespace + + + +KeyButtonBar::KeyButtonBar(QWidget* receiver, QWidget* parent, Qt::WindowFlags f) +: QWidget(parent, f) +, receiver(receiver) +, active_modifiers(QGuiApplication::keyboardModifiers()) +{ + layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(layout->spacing() / 2); + setLayout(layout); + + receiver->installEventFilter(this); +} + + +KeyButtonBar::~KeyButtonBar() = default; + + + +QToolButton* KeyButtonBar::addKeyButton(Qt::Key key_code, const QString& text, const QIcon& icon) +{ + return addKeyButton(key_code, Qt::NoModifier, text, icon); +} + + +QToolButton* KeyButtonBar::addKeyButton(Qt::Key key_code, Qt::KeyboardModifier modifier_code, const QString& text, const QIcon& icon) +{ + Q_ASSERT(key_code); + Q_ASSERT(!modifier_code || !modifierFromKeyCode(key_code)); + auto button = new QToolButton(); + button->setText(text); + if (!icon.isNull()) + button->setIcon(icon); + layout->addWidget(button); + button_infos.push_back({button, key_code, modifier_code}); + connect(button, &QAbstractButton::clicked, this, &KeyButtonBar::sendKeyPressAndRelease); + return button; +} + + +QToolButton* KeyButtonBar::addModifierButton(Qt::KeyboardModifier modifier_code, const QString& text, const QIcon& icon) +{ + Q_ASSERT(modifier_code); + auto button = new QToolButton(); + button->setText(text); + button->setCheckable(true); + if (!icon.isNull()) + button->setIcon(icon); + layout->addWidget(button); + auto key_code = keyFromModifierCode(modifier_code); + Q_ASSERT(key_code); + button_infos.push_back({button, Qt::Key(key_code), modifier_code}); + connect(button, &QAbstractButton::clicked, this, &KeyButtonBar::sendModifierChange); + return button; +} + + + +bool KeyButtonBar::eventFilter(QObject* /*watched*/, QEvent* event) +{ + if (!active) + return false; + + using std::begin; + using std::end; + auto find_info = [this](int key_code) -> ButtonInfo* { + auto info = std::find_if(begin(button_infos), end(button_infos), + [key_code](const auto& info) { return info.key == key_code; }); + return info == end(button_infos) ? nullptr : &*info; + }; + + switch (event->type()) + { + case QEvent::KeyPress: + { + auto final_modifiers = active_modifiers; + + auto key_event = static_cast(event); + const auto key = Qt::Key(key_event->key()); + if (auto modifier = modifierFromKeyCode(key)) + { + if (active_modifiers & modifier) + return true; // Modifier already active. Consume this event. + + final_modifiers |= Qt::KeyboardModifier(modifier); + + if (auto info = find_info(key)) + info->button->setChecked(true); + } + key_event->setModifiers(Qt::KeyboardModifiers(active_modifiers)); + active_modifiers = final_modifiers; + } + break; + + case QEvent::KeyRelease: + { + auto final_modifiers = active_modifiers; + + auto key_event = static_cast(event); + const auto key = Qt::Key(key_event->key()); + if (auto modifier = modifierFromKeyCode(key)) + { + if (!(active_modifiers & modifier)) + return true; // Modifier already inactive. Consume this event. + + final_modifiers &= ~Qt::KeyboardModifier(modifier); + + if (auto info = find_info(key)) + info->button->setChecked(false); + } + key_event->setModifiers(Qt::KeyboardModifiers(active_modifiers)); + active_modifiers = final_modifiers; + } + break; + + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + // Probably add more input events later. + static_cast(event)->setModifiers(Qt::KeyboardModifiers(active_modifiers)); + break; + + default: + ; // nothing + } + + return false; +} + + + +void KeyButtonBar::showEvent(QShowEvent* event) +{ + if (!event->spontaneous()) + active = true; +} + + +void KeyButtonBar::hideEvent(QHideEvent* event) +{ + if (!event->spontaneous()) + active = false; +} + + + +void KeyButtonBar::sendKeyPressAndRelease() +{ + using std::begin; + using std::end; + auto button = reinterpret_cast(sender()); + auto info = std::find_if(begin(button_infos), end(button_infos), + [button](const auto& info) { return info.button == button; }); + + Q_ASSERT(info != end(button_infos)); + + auto original_modifiers = active_modifiers; + if (info->modifier && !active_modifiers.testFlag(info->modifier)) + { + sendKeyPressEvent(Qt::Key(keyFromModifierCode(info->modifier))); + Q_ASSERT(active_modifiers & info->modifier); + } + sendKeyPressEvent(info->key); + sendKeyReleaseEvent(info->key); + if (active_modifiers != original_modifiers) + { + sendKeyReleaseEvent(Qt::Key(keyFromModifierCode(info->modifier))); + Q_ASSERT(!(active_modifiers & info->modifier)); + } +} + + +void KeyButtonBar::sendModifierChange(bool checked) +{ + using std::begin; + using std::end; + auto button = reinterpret_cast(sender()); + auto info = std::find_if(begin(button_infos), end(button_infos), + [button](const auto& info) { return info.button == button; }); + + Q_ASSERT(info != end(button_infos)); + Q_ASSERT(info->modifier); + + if (checked) + { + if (!active_modifiers.testFlag(info->modifier)) + { + sendKeyPressEvent(info->key); + } + Q_ASSERT(active_modifiers & info->modifier); + } + else + { + if (active_modifiers.testFlag(info->modifier)) + { + sendKeyReleaseEvent(info->key); + } + } + + Q_ASSERT(active_modifiers.testFlag(info->modifier) == checked); +} + + + +void KeyButtonBar::sendKeyPressEvent(Qt::Key key_code) +{ + QKeyEvent event(QEvent::KeyPress, key_code, active_modifiers); + QCoreApplication::sendEvent(receiver, &event); +} + + +void KeyButtonBar::sendKeyReleaseEvent(Qt::Key key_code) +{ + QKeyEvent event(QEvent::KeyRelease, key_code, active_modifiers); + QCoreApplication::sendEvent(receiver, &event); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/key_button_bar.h b/src/gui/widgets/key_button_bar.h new file mode 100644 index 0000000..4ec9439 --- /dev/null +++ b/src/gui/widgets/key_button_bar.h @@ -0,0 +1,151 @@ +/* + * Copyright 2014 Thomas Schöps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_KEY_BUTTON_BAR_H +#define OPENORIENTEERING_KEY_BUTTON_BAR_H + +#include +#include +#include +#include +#include +#include +#include + +class QEvent; +class QHBoxLayout; +class QHideEvent; +class QShowEvent; +class QToolButton; + +namespace OpenOrienteering { + + +/** + * Shows a set of buttons for simulating keyboard input. + * + * This is used to simulate keys in Mapper's mobile GUI where a physical + * keyboard is not present. + * + * For modifier keys, the GUI buttons indicate wether the keys are in pressed + * state or not. However, the labels are not expected to display the name of the + * key but the associated tool behaviour. The tools need to make sure that the + * actual behaviour is consistent with the indicated state. + */ +class KeyButtonBar : public QWidget +{ +Q_OBJECT +public: + /** + * Constructs a new button bar acting on the given receiver. + * + * The constructor will install this object as an event filter on the given + * receiver, independent of object ownership. + */ + KeyButtonBar(QWidget* receiver, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + + ~KeyButtonBar() override; + + + /** + * Adds a button for a regular key. + */ + QToolButton* addKeyButton(Qt::Key key_code, const QString& text, const QIcon& icon = {}); + + /** + * Adds a button for a combination of a regular key with a modifier key. + * + * By default, the button is not checkable and sends a sequence of a press + * event and a relase event for the given key_code. If a modifier_code + * different from Qt::NoModifier is given, and this modifier key is not + * already active, a press event for the corresponding modifier key code is + * sent first, and a release event for the modifier key code is send after + * the the event for the key_code. + * + * Application code may turn this into a checkable button if it also takes + * care of maintaining the state. + */ + QToolButton* addKeyButton(Qt::Key key_code, Qt::KeyboardModifier modifier_code, const QString& text, const QIcon& icon = {}); + + + /** Adds a checkable button for a modifier key. + * + * The button sends a key press event when the user activates the checked + * state, and it sends a key release event when the user unsets the checked + * state. + * + * Upon regular input events for the corresponding modifier key, the checked + * state of the button is updated accordingly. + */ + QToolButton* addModifierButton(Qt::KeyboardModifier modifier_code, const QString& text, const QIcon& icon = {}); + + + /** + * Returns the currently active set of modifier keys. + */ + Qt::KeyboardModifiers keyboardModifiers() const { return active_modifiers; } + + + /** + * The event filter synchronizes the buttons' state and changes with input events. + * + * It acts on the receiver widget given in the KeyButtonBar constructor. + */ + bool eventFilter(QObject* watched, QEvent* event) override; + + +protected: + void showEvent(QShowEvent* event) override; + + void hideEvent(QHideEvent* event) override; + + +private: + void sendKeyPressAndRelease(); + + void sendModifierChange(bool checked); + + void sendKeyPressEvent(Qt::Key key_code); + + void sendKeyReleaseEvent(Qt::Key key_code); + + + struct ButtonInfo + { + QToolButton* button; + Qt::Key key; + Qt::KeyboardModifier modifier; + }; + + QVarLengthArray button_infos; + QWidget* receiver; + QHBoxLayout* layout; + Qt::KeyboardModifiers active_modifiers; + bool active = false; + + + Q_DISABLE_COPY(KeyButtonBar) +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_KEY_BUTTON_BAR_H diff --git a/src/gui/widgets/mapper_proxystyle.cpp b/src/gui/widgets/mapper_proxystyle.cpp new file mode 100644 index 0000000..90d5955 --- /dev/null +++ b/src/gui/widgets/mapper_proxystyle.cpp @@ -0,0 +1,188 @@ +/* + * Copyright 2013-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "mapper_proxystyle.h" + +#include +#include +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include +#include + +#include "segmented_button_layout.h" + + +namespace OpenOrienteering { + +MapperProxyStyle::MapperProxyStyle(QStyle* base_style) + : QProxyStyle(base_style) +{ + ; // Nothing +} + +MapperProxyStyle::~MapperProxyStyle() +{ + ; // Nothing, not inlined +} + +void MapperProxyStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + auto overridden_element = element; + switch (element) + { + case PE_IndicatorButtonDropDown: + overridden_element = PE_PanelButtonTool; // Enforce same appearence. Workaround QWindowsStyle quirk. + // fall through + case PE_PanelButtonCommand: + case PE_PanelButtonBevel: + case PE_PanelButtonTool: + if (int segment = widget ? widget->property("segment").toInt() : 0) + { + drawSegmentedButton(segment, overridden_element, option, painter, widget); + return; + } + break; + +#ifdef Q_OS_ANDROID + case QStyle::PE_IndicatorItemViewItemCheck: + if (option->state.testFlag(QStyle::State_NoChange) + || !option->state.testFlag(QStyle::State_Enabled)) + { + auto item = qstyleoption_cast(option); + auto o = QStyleOptionViewItem{ *item }; + o.state |= QStyle::State_Enabled; + if (option->state.testFlag(QStyle::State_NoChange)) + { + o.state &= ~QStyle::State_NoChange; + o.state |= QStyle::State_On; + } + auto opacity = painter->opacity(); + painter->setOpacity(0.4); + QProxyStyle::drawPrimitive(element, &o, painter, widget); + painter->setOpacity(opacity); + return; + } + break; +#endif + + default: + ; // Nothing + } + + QProxyStyle::drawPrimitive(element, option, painter, widget); +} + +void MapperProxyStyle::drawSegmentedButton(int segment, QStyle::PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +{ + painter->save(); + + // Background (to be clipped by the widget) + int left_adj = (segment & SegmentedButtonLayout::LeftNeighbor) ? 4 : 0; + int right_adj = (segment & SegmentedButtonLayout::RightNeighbor) ? 4 : 0; + + if (option->rect.left()) + { + // Subcomponent clipping + painter->setClipRect(option->rect, Qt::IntersectClip); + left_adj = 4; + } + + QStyleOption mod_option(*option); + mod_option.rect.adjust(-left_adj, 0, right_adj, 0); + QProxyStyle::drawPrimitive(element, &mod_option, painter, widget); + + // Segment separators + painter->setOpacity((option->state & QStyle::State_Enabled) ? 0.5 : 0.2); + int frame_width = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); + mod_option.rect = option->rect.adjusted(0, frame_width, 0, -frame_width); + + if (left_adj) + { + if (option->state & QStyle::State_Sunken) + painter->setPen(option->palette.dark().color()); + else + painter->setPen(option->palette.light().color()); + painter->drawLine(option->rect.left(), option->rect.top(), option->rect.left(), option->rect.bottom()); + } + + if (right_adj) + { + if (option->state & QStyle::State_Sunken) + painter->setPen(option->palette.light().color()); + else + painter->setPen(option->palette.dark().color()); + painter->drawLine(option->rect.right(), option->rect.top(), option->rect.right(), option->rect.bottom()); + } + + painter->restore(); +} + +int MapperProxyStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const +{ + switch (metric) + { +#ifdef Q_OS_ANDROID + case QStyle::PM_ButtonIconSize: + { + static int s = qMax(QProxyStyle::pixelMetric(metric), QProxyStyle::pixelMetric(QStyle::PM_IndicatorWidth)); + return s; + } + case QStyle::PM_SplitterWidth: + { + static int s = (QProxyStyle::pixelMetric(metric), QProxyStyle::pixelMetric(QStyle::PM_IndicatorWidth)) / 2; + return s; + } +#endif +#ifdef Q_OS_MACOS + case QStyle::PM_ToolBarIconSize: + { + static int s = (QProxyStyle::pixelMetric(metric) + QProxyStyle::pixelMetric(QStyle::PM_SmallIconSize)) / 2; + return s; + } +#endif + default: + break; + } + + return QProxyStyle::pixelMetric(metric, option, widget); +} + +int MapperProxyStyle::styleHint(QStyle::StyleHint hint, const QStyleOption* option, const QWidget* widget, QStyleHintReturn* return_data) const +{ + switch (hint) + { +#ifdef Q_OS_ANDROID + case QStyle::SH_FormLayoutWrapPolicy: + return QFormLayout::WrapLongRows; +#endif + default: + break; + } + + return QProxyStyle::styleHint(hint, option, widget, return_data); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/mapper_proxystyle.h b/src/gui/widgets/mapper_proxystyle.h new file mode 100644 index 0000000..6a4c888 --- /dev/null +++ b/src/gui/widgets/mapper_proxystyle.h @@ -0,0 +1,99 @@ +/* + * Copyright 2013, 2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MAPPER_PROXYSTYLE_H +#define OPENORIENTEERING_MAPPER_PROXYSTYLE_H + +#include +#include +#include +#include + +class QPainter; +class QStyleHintReturn; +class QStyleOption; +class QWidget; + +namespace OpenOrienteering { + + +/** + * MapperProxyStyle customizes the platform's base style. + * + * It supports the implementation of missing UI elements, and + * it adjustes the size of some elements to more practical values. + */ +class MapperProxyStyle : public QProxyStyle +{ +Q_OBJECT +public: + /** + * Constructs a new MapperProxyStyle. + * + * Being a QProxyStyle, MapperProxyStyle takes ownership of the base style, + * if given. + */ + MapperProxyStyle(QStyle* base_style = nullptr); + + /** + * Destroys the object. + */ + ~MapperProxyStyle() override; + + /** + * Draws the given primitive element. + * + * Implements rendering of segmented buttons for all platforms. + * + * On Android: + * - QStyle::PE_IndicatorItemViewItemCheck is modified for disabled and tristate checkboxes. + */ + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = nullptr) const override; + + /** + * Returns some adjusted pixel metrics. + * + * On OS X: + * - QStyle::PM_ToolBarIconSize is adjusted (reduced) towards QStyle::PM_SmallIconSize. + * + * On Android: + * - QStyle::PM_ButtonIconSize is enlarged to QStyle::PM_IndicatorWidth (checkbox size), + * - QStyle::PM_ToolBarIconSize is adjusted (enlarged) towards QStyle::PM_SmallIconSize. + */ + int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override; + + /** + * Returns adjusted style hints. + * + * On Android: + * - QStyle::SH_FormLayoutWrapPolicy is QFormLayout::QFormLayout::WrapLongRows. + */ + int styleHint(StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* return_data = nullptr) const override; + +private: + void drawSegmentedButton(int segment, PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const; + + Q_DISABLE_COPY(MapperProxyStyle) +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_MAPPER_PROXYSTYLE_H diff --git a/src/gui/widgets/measure_widget.cpp b/src/gui/widgets/measure_widget.cpp new file mode 100644 index 0000000..716b127 --- /dev/null +++ b/src/gui/widgets/measure_widget.cpp @@ -0,0 +1,165 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "measure_widget.h" + +#include +#include + +#include "core/map.h" +#include "core/objects/object.h" +#include "core/symbols/symbol.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/line_symbol.h" + + +namespace OpenOrienteering { + +MeasureWidget::MeasureWidget(Map* map, QWidget* parent) +: QTextBrowser(parent) +, map(map) +{ + QScroller::grabGesture(viewport(), QScroller::TouchGesture); + + connect(map, &Map::objectSelectionChanged, this, &MeasureWidget::objectSelectionChanged); + connect(map, &Map::selectedObjectEdited, this, &MeasureWidget::objectSelectionChanged); + connect(map, &Map::symbolChanged, this, &MeasureWidget::objectSelectionChanged); + + objectSelectionChanged(); +} + +MeasureWidget::~MeasureWidget() = default; + + +void MeasureWidget::objectSelectionChanged() +{ + QString headline; // inline HTML + QString body; // HTML blocks + QString extra_text; // inline HTML + + auto& selected_objects = map->selectedObjects(); + if (selected_objects.empty()) + { + extra_text = tr("No object selected."); + } + else if (selected_objects.size() > 1) + { + extra_text = tr("%1 objects selected.").arg(locale().toString(map->getNumSelectedObjects())); + } + else + { + const Object* object = *begin(selected_objects); + const Symbol* symbol = object->getSymbol(); + headline = symbol->getNumberAsString() + QLatin1Char(' ') + symbol->getName(); + + if (object->getType() != Object::Path) + { + extra_text = tr("The selected object is not a path."); + } + else + { + body = QLatin1String{ "" }; + static const QString table_row{ QLatin1String{ + "" + } }; + + double paper_to_real = 0.001 * map->getScaleDenominator(); + + object->update(); + const PathPartVector& parts = static_cast(object)->parts(); + Q_ASSERT(!parts.empty()); + + auto paper_length = parts.front().length(); + auto real_length = paper_length * paper_to_real; + + auto paper_length_text = locale().toString(paper_length, 'f', 2); + auto real_length_text = locale().toString(real_length, 'f', 0); + + if (symbol->getContainedTypes() & Symbol::Area) + { + body.append(table_row.arg(tr("Boundary length:"), + paper_length_text, tr("mm", "millimeters"), + real_length_text, tr("m", "meters"))); + + auto paper_area = parts.front().calculateArea(); + if (parts.size() > 1) + { + paper_area *= 2; + for (const auto& part : parts) + paper_area -= part.calculateArea(); + } + double real_area = paper_area * paper_to_real * paper_to_real; + + auto paper_area_text = locale().toString(paper_area, 'f', 2); + auto real_area_text = locale().toString(real_area, 'f', 0); + + body.append(table_row.arg(tr("Area:"), + paper_area_text, trUtf8("mm²", "square millimeters"), + real_area_text , trUtf8("m²", "square meters"))); + + auto minimum_area = 0.0; + auto minimum_area_text = QString{ }; + if (symbol->getType() == Symbol::Area) + { + minimum_area = 0.001 * static_cast(symbol)->getMinimumArea(); + minimum_area_text = locale().toString(minimum_area, 'f', 2); + } + + if (paper_area < minimum_area && paper_area_text != minimum_area_text) + { + extra_text = QLatin1String("") + tr("This object is too small.") + QLatin1String("
    ") + + tr("The minimimum area is %1 %2.").arg(minimum_area_text, trUtf8("mm²")) + + QLatin1String("
    "); + } + extra_text.append(tr("Note: Boundary length and area are correct only if there are no self-intersections and holes are used as such.")); + } + else + { + body.append(table_row.arg(tr("Length:"), + paper_length_text, tr("mm", "millimeters"), + real_length_text, tr("m", "meters"))); + + auto minimum_length = 0.0; + auto minimum_length_text = QString{ }; + if (symbol->getType() == Symbol::Line) + { + minimum_length = 0.001 * static_cast(symbol)->getMinimumLength(); + minimum_length_text = locale().toString(minimum_length, 'f', 2); + } + + if (paper_length < minimum_length && paper_length_text != minimum_length_text) + { + extra_text = QLatin1String("") + tr("This line is too short.") + QLatin1String("
    ") + + tr("The minimum length is %1 %2.").arg(minimum_length_text, tr("mm")); + } + } + + body.append(QLatin1String("
    %1%2 %3(%4 %5)
    ")); + } + } + + if (!extra_text.isEmpty()) + body.append(QLatin1String("

    ") + extra_text + QLatin1String("

    ")); + setHtml(QLatin1String("

    ") + headline + QLatin1String("

    ") + body); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/measure_widget.h b/src/gui/widgets/measure_widget.h new file mode 100644 index 0000000..9057bdc --- /dev/null +++ b/src/gui/widgets/measure_widget.h @@ -0,0 +1,63 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2015 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_MEASURE_WIDGET_H +#define OPENORIENTEERING_MEASURE_WIDGET_H + +#include +#include + +class QWidget; + +namespace OpenOrienteering { + +class Map; + + +/** + * The widget which is shown in a dock widget when the measure tool is active. + * Displays information about the currently selected objects. + */ +class MeasureWidget : public QTextBrowser +{ +Q_OBJECT +public: + /** Creates a new MeasureWidget for a given map. */ + MeasureWidget(Map* map, QWidget* parent = nullptr); + + /** Destroys the MeasureWidget. */ + ~MeasureWidget() override; + +protected slots: + /** + * Is called when the object selection in the map changes. + * Updates the widget content. + */ + void objectSelectionChanged(); + +private: + Map* map; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/pie_menu.cpp b/src/gui/widgets/pie_menu.cpp new file mode 100644 index 0000000..d10299a --- /dev/null +++ b/src/gui/widgets/pie_menu.cpp @@ -0,0 +1,395 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "pie_menu.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "util/backports.h" + + +namespace OpenOrienteering { + +PieMenu::PieMenu(QWidget* parent) +: QWidget(parent, Qt::Popup | Qt::FramelessWindowHint), // NOTE: use Qt::Window for debugging to avoid mouse grab + minimum_action_count(3), + icon_size(24), + active_action(nullptr), + actions_changed(true), + clicked(false) +{ + setCursor(QCursor(Qt::ArrowCursor)); + setAttribute(Qt::WA_OpaquePaintEvent); + setAttribute(Qt::WA_ShowWithoutActivating); + setAutoFillBackground(false); + setMouseTracking(true); + + auto scale = Settings::getInstance().getSetting(Settings::General_PixelsPerInch).toReal() / 96.0; + if (scale > 1.5) + { + icon_size = qRound(icon_size * scale); + } + + updateCachedState(); +} + +int PieMenu::minimumActionCount() const +{ + return minimum_action_count; +} + +void PieMenu::setMinimumActionCount(int count) +{ + if (count >= 3 && count != minimum_action_count) + { + minimum_action_count = count; + actions_changed = true; + update(); + } +} + +int PieMenu::visibleActionCount() const +{ + const auto actions = this->actions(); + return int(std::count_if(actions.begin(), actions.end(), [](const auto action) { + return action->isVisible() && !action->isSeparator(); + })); +} + +bool PieMenu::isEmpty() const +{ + return visibleActionCount() == 0; +} + +void PieMenu::clear() +{ + const auto actions = this->actions(); + for (auto action : actions) + removeAction(action); +} + +int PieMenu::iconSize() const +{ + return icon_size; +} + +void PieMenu::setIconSize(int icon_size) +{ + if (icon_size > 0) + { + this->icon_size = icon_size; + actions_changed = true; + update(); + } +} + +QAction* PieMenu::actionAt(const QPoint& pos) const +{ + const auto actions = this->actions(); + for (auto action : actions) + { + if (geometries.contains(action) && + geometries[action].area.containsPoint(pos, Qt::WindingFill)) + { + return action; + } + } + return nullptr; +} + +PieMenu::ItemGeometry PieMenu::actionGeometry(QAction* action) const +{ + ItemGeometry geometry; + if (geometries.contains(action)) + geometry = geometries[action]; + + return geometry; +} + +QAction* PieMenu::activeAction() const +{ + return active_action; +} + +void PieMenu::setActiveAction(QAction* action) +{ + QAction* const prev_action = active_action; + active_action = (action && action->isEnabled() && action->isVisible() && !action->isSeparator()) ? action : nullptr; + if (isVisible()) + { + if (active_action && active_action != prev_action) + { + active_action->activate(QAction::Hover); + emit hovered(active_action); + active_action->showStatusText(0); + } + else if (prev_action && !active_action) + { + QString empty_string; + QStatusTipEvent e(empty_string); + QApplication::sendEvent(parent(), &e); + } + update(); + } +} + +void PieMenu::popup(const QPoint pos) +{ + updateCachedState(); // We need the current total_radius. + + QPoint cursor_pos = QCursor::pos(); + QRect screen_rect = qApp->desktop()->availableGeometry(cursor_pos); + + if (cursor_pos.x() > screen_rect.right() - total_radius) + cursor_pos.setX(screen_rect.right() - total_radius); + else if (cursor_pos.x() < total_radius) + cursor_pos.setX(total_radius); + + if (cursor_pos.y() > screen_rect.bottom() - total_radius) + cursor_pos.setY(screen_rect.bottom() - total_radius); + else if (cursor_pos.y() < total_radius) + cursor_pos.setY(total_radius); + + setGeometry(pos.x() - total_radius, pos.y() - total_radius, 2 * total_radius, 2 * total_radius); + + clicked = false; + active_action = nullptr; + + emit aboutToShow(); + show(); +} + +void PieMenu::actionEvent(QActionEvent* event) +{ + if (event->type() == QEvent::ActionRemoved) + { + QAction* const action = event->action(); + geometries.remove(action); + if (action == active_action) + setActiveAction(nullptr); + } + + actions_changed = true; + update(); + QWidget::actionEvent(event); +} + +void PieMenu::hideEvent(QHideEvent* event) +{ + if (!event->spontaneous()) + { + emit aboutToHide(); + setActiveAction(nullptr); + QString empty_string; + QStatusTipEvent e(empty_string); + QApplication::sendEvent(parent(), &e); + } + QWidget::hideEvent(event); +} + +void PieMenu::mouseMoveEvent(QMouseEvent* event) +{ + setActiveAction(actionAt(event->pos())); + event->accept(); +} + +void PieMenu::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) + { + if (mask().contains(event->pos())) + { + clicked = true; + setActiveAction(actionAt(event->pos())); + } + else + { + hide(); + } + event->accept(); + return; + } + + QWidget::mousePressEvent(event); +} + +void PieMenu::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) + { + if (active_action) + { + Q_ASSERT(active_action->isEnabled()); + Q_ASSERT(active_action->isVisible()); + Q_ASSERT(!active_action->isSeparator()); + active_action->activate(QAction::Trigger); + emit triggered(active_action); + hide(); + } + else if (clicked && !mask().contains(event->pos())) + { + hide(); + } + + clicked = false; + event->accept(); + return; + } + + QWidget::mouseReleaseEvent(event); +} + +void PieMenu::paintEvent(QPaintEvent* event) +{ + updateCachedState(); + + QStyleOptionMenuItem option; + option.initFrom(this); + QPalette& palette = option.palette; + palette.setCurrentColorGroup(QPalette::Active); + + // Draw on the widget + QPainter painter(this); + painter.setClipRect(event->rect()); + painter.setRenderHint(QPainter::Antialiasing, true); + + // Background + QPen pen(palette.color(QPalette::Dark)); + pen.setWidth(1); + painter.setPen(pen); + painter.setBrush(palette.brush(QPalette::Button)); + painter.drawConvexPolygon(outer_border); + painter.setBrush(Qt::NoBrush); + painter.drawConvexPolygon(inner_border); + + // Items + const auto actions = this->actions(); + for (auto action : actions) + { + if (!geometries.contains(action)) + continue; + + QPolygon area = geometries[action].area; + QIcon::Mode mode = QIcon::Normal; + if (action->isChecked()) + { + mode = QIcon::Selected; + QPen pen(palette.color(QPalette::Dark)); + pen.setWidth(1); + painter.setPen(pen); + painter.setBrush(palette.color(QPalette::Button).darker(120)); + painter.setOpacity(1.0); + painter.drawConvexPolygon(area); + } + else if (action->isEnabled() && action == active_action) + { + mode = QIcon::Active; + QPen pen(palette.color(QPalette::Dark)); + pen.setWidth(1); + painter.setPen(pen); + painter.setBrush(clicked ? palette.color(QPalette::Button).darker(120) : palette.color(QPalette::Button).lighter(120)); + painter.setOpacity(1.0); + painter.drawConvexPolygon(area); + } + else if (!action->isEnabled()) + { + mode = QIcon::Disabled; + painter.setOpacity(0.4); + } + else + { + painter.setOpacity(1.0); + } + + // Draw icon + QPixmap pixmap = action->icon().pixmap(icon_size, mode); + QPoint midpoint = geometries[action].icon_pos; + painter.drawPixmap(QPoint(midpoint.x() - pixmap.width() / 2, midpoint.y() - pixmap.height() / 2), pixmap); + } +} + +void PieMenu::updateCachedState() +{ + if (actions_changed) + { + int action_count = qMax(minimum_action_count, visibleActionCount()); + outer_border.resize(action_count); + inner_border.resize(action_count); + + double half_angle = M_PI / action_count; + double inner_radius = 0.5 * icon_size; + double inner_corner_radius = inner_radius / cos(half_angle); + + double icon_padding_inner = 0.7 * icon_size; + double icon_radius = inner_radius + icon_padding_inner + 0.5 * icon_size; + + double icon_padding_outer = 0.2 * icon_size; + double outer_corner_radius = (inner_radius + icon_padding_inner + icon_size + icon_padding_outer) / cos(half_angle); + + total_radius = qCeil(outer_corner_radius); + + // The total shape + for (int i = 0; i < action_count; ++i) + { + double angle = (2*i + 1) * half_angle; + inner_border.setPoint(i, getPoint(inner_corner_radius, angle)); + outer_border.setPoint(i, getPoint(outer_corner_radius, angle)); + } + + setMask(outer_border.subtracted(inner_border)); + + // The items + int i = 0; + const auto actions = this->actions(); + for (auto action : actions) + { + if (action->isVisible() && !action->isSeparator()) + { + ItemGeometry geometry; + + geometry.area.resize(4); + double angle = (2*i - 1) * half_angle; + geometry.area.setPoint(0, getPoint(inner_corner_radius, angle)); + geometry.area.setPoint(1, inner_border.point(i)); + geometry.area.setPoint(2, outer_border.point(i)); + geometry.area.setPoint(3, getPoint(outer_corner_radius, angle)); + + angle = 2*i * half_angle; + geometry.icon_pos = getPoint(icon_radius, angle); + + geometries[action] = geometry; + ++i; + } + } + + actions_changed = false; + } +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/pie_menu.h b/src/gui/widgets/pie_menu.h new file mode 100644 index 0000000..ef2ef81 --- /dev/null +++ b/src/gui/widgets/pie_menu.h @@ -0,0 +1,172 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014 Thomas Schöps, Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_PIE_MENU_H +#define OPENORIENTEERING_PIE_MENU_H + +#include + +#include +#include +#include +#include +#include +#include + +class QAction; +class QActionEvent; +class QHideEvent; +class QMouseEvent; +class QPaintEvent; + +namespace OpenOrienteering { + + +/** + * Displays a pie menu. + * + * This class has an API and behavior similar to QMenu, + * but is neither a subclass nor a full replacement. + */ +class PieMenu : public QWidget +{ +Q_OBJECT +public: + /** Stores the geometry of a particular item in the PieMenu. */ + struct ItemGeometry + { + /** The area covered by the item. */ + QPolygon area; + + /** The position of the center of the item. */ + QPoint icon_pos; + }; + + /** Constructs a pie menu with default settings. */ + PieMenu(QWidget* parent = nullptr); + + /** Returns the minimum number of actions in the menu. */ + int minimumActionCount() const; + + /** Sets the minimum number of actions in the menu (must be at least three). */ + void setMinimumActionCount(int count); + + /** Returns the current number of visible actions. */ + int visibleActionCount() const; + + /** Removes all actions from the menu. + * Other than QMenu::clear(), this will not immediately delete any action, + * even if owned by this object. (Actions owned by this object will be + * deleted when this objected is deleted.) + * @see QMenu::clear() */ + void clear(); + + /** Returns true if there are no visible actions in the menu, + * false otherwise. + * @see QMenu::isEmpty */ + bool isEmpty() const; + + /** Returns the icon size (single dimension). */ + int iconSize() const; + + /** Sets the icon size (single dimension). + * @param icon_size the new size (at least 1) */ + void setIconSize(int icon_size); + + /** Returns the action at pos. + * Returns nullptr if there is no action at this place. */ + QAction* actionAt(const QPoint& pos) const; + + /** Returns the geometry of the given action. + * The action must be enabled and visible. + * Otherwise the geometry will be empty. + * @see QMenu::actionGeometry */ + ItemGeometry actionGeometry(QAction* action) const; + + /** Returns the currently highlighted action. + * Returns nullptr if no action is highlighted. + * @see QMenu::activeAction */ + QAction* activeAction() const; + + /** Sets the currently highlighted action. + * The action must be enabled and visible. + * Otherwise there will be no highlighted action. + * @see QMenu::setActiveAction */ + void setActiveAction(QAction* action); + + /** Shows the menu at the given absolute position. + * @see QMenu::popup */ + void popup(const QPoint pos); + +signals: + /** This signal is emitted just before the menu is shown. + * @see QMenu::aboutToShow */ + void aboutToShow(); + + /** This signal is emitted just before the menu is hidden. + * @see QMenu::aboutToHide */ + void aboutToHide(); + + /** This signal is emitted when a menu item is highlighted. + * @see QMenu::hovered */ + void hovered(QAction* action); + + /** This signal is emitted when a menu item's action is triggered. + * @see QMenu::triggered */ + void triggered(QAction* action); + +protected: + void actionEvent(QActionEvent* event) override; + void hideEvent(QHideEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void paintEvent(QPaintEvent* event) override; + + void updateCachedState(); + + QPoint getPoint(double radius, double angle) const; + + int minimum_action_count; + int icon_size; + QAction* active_action; + + bool actions_changed; + bool clicked; + int total_radius; + QPolygon outer_border; + QPolygon inner_border; + QHash< QAction*, ItemGeometry > geometries; +}; + + +// ### PieMenu inline code ### + +inline +QPoint PieMenu::getPoint(double radius, double angle) const +{ + return QPoint(total_radius + qRound(radius * -sin(angle)), total_radius + qRound(radius * -cos(angle))); +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/segmented_button_layout.cpp b/src/gui/widgets/segmented_button_layout.cpp new file mode 100644 index 0000000..c06a2e4 --- /dev/null +++ b/src/gui/widgets/segmented_button_layout.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2013 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "segmented_button_layout.h" + +#include +#include // IWYU pragma: keep +#include + + +namespace OpenOrienteering { + +SegmentedButtonLayout::SegmentedButtonLayout() + : QHBoxLayout() +{ + setContentsMargins(0, 0, 0, 0); + setSpacing(0); +} + +SegmentedButtonLayout::SegmentedButtonLayout(QWidget* parent) + : QHBoxLayout(parent) +{ + setContentsMargins(0, 0, 0, 0); + setSpacing(0); +} + +SegmentedButtonLayout::~SegmentedButtonLayout() +{ + ; // Nothing, not inlined +} + +void SegmentedButtonLayout::invalidate() +{ + int num_items = count(); + QWidget* first_widget = nullptr; + QWidget* widget = nullptr; + for (int i = 0; i < num_items; ++i) + { + widget = itemAt(i)->widget(); + if (!widget) + continue; + +// widget->setStyle(segmented_style); + + if (!first_widget) + { + first_widget = widget; + widget->setProperty("segment", RightNeighbor); + } + else + { + widget->setProperty("segment", BothNeighbors); + } + } + + if (widget) + widget->setProperty("segment", (widget == first_widget) ? NoNeighbors : LeftNeighbor); + + QHBoxLayout::invalidate(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/segmented_button_layout.h b/src/gui/widgets/segmented_button_layout.h new file mode 100644 index 0000000..7949d0d --- /dev/null +++ b/src/gui/widgets/segmented_button_layout.h @@ -0,0 +1,84 @@ +/* + * Copyright 2013 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SEGMENTED_BUTTON_LAYOUT_H +#define OPENORIENTEERING_SEGMENTED_BUTTON_LAYOUT_H + +#include +#include +#include + +class QWidget; + +namespace OpenOrienteering { + + +/** + * SegmentedButtonLayout is a horizontal box layout with no margin and no + * spacing which will mark the contained widgets as being segments having a + * left and/or right neighbor. + * + * MapperProxyStyle uses this information to make buttons from a single + * SegmentedButtonLayout appear as a single segmented button. + */ +class SegmentedButtonLayout : public QHBoxLayout +{ +Q_OBJECT +public: + /** + * Constructs a new SegmentedButtonLayout. + */ + SegmentedButtonLayout(); + + /** + * Constructs a new SegmentedButtonLayout for the given parent. + */ + explicit SegmentedButtonLayout(QWidget* parent); + + /** + * Destroys the object. + */ + ~SegmentedButtonLayout() override; + + /** + * Resets the information about neighboring segments and any other cached + * information about the layout. + */ + void invalidate() override; + + /** + * Types of segment neighborhood. + */ + enum Segment + { + NoNeighbors = 0x00, + RightNeighbor = 0x01, + LeftNeighbor = 0x02, + BothNeighbors = RightNeighbor | LeftNeighbor + }; + +private: + Q_DISABLE_COPY(SegmentedButtonLayout) +}; + + +} // namespace OpenOrienteering + +#endif // OPENORIENTEERING_SEGMENTED_BUTTON_LAYOUT_H diff --git a/src/gui/widgets/settings_page.cpp b/src/gui/widgets/settings_page.cpp new file mode 100644 index 0000000..d14dff7 --- /dev/null +++ b/src/gui/widgets/settings_page.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2012 Jan Dalheimer + * Copyright 2012-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#include "settings_page.h" + + +namespace OpenOrienteering { + +SettingsPage::SettingsPage(QWidget* parent) +: QWidget(parent) +{ + // nothing +} + +SettingsPage::~SettingsPage() = default; + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/settings_page.h b/src/gui/widgets/settings_page.h new file mode 100644 index 0000000..b6923da --- /dev/null +++ b/src/gui/widgets/settings_page.h @@ -0,0 +1,98 @@ +/* + * Copyright 2012 Jan Dalheimer + * Copyright 2013-2016 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + +#ifndef OPENORIENTEERING_SETTINGS_PAGE_H +#define OPENORIENTEERING_SETTINGS_PAGE_H + +#include +#include +#include +#include + +#include "settings.h" + +namespace OpenOrienteering { + + +/** + * A widget which serves as a page in the SettingsDialog. + */ +class SettingsPage : public QWidget +{ +Q_OBJECT +public: + /** + * Constructs a new settings page. + */ + explicit SettingsPage(QWidget* parent = nullptr); + + /** + * Destructor. + */ + ~SettingsPage() override; + + /** + * Returns the title of this page. + */ + virtual QString title() const = 0; + +public slots: + /** + * This slot is to transfer the input fields to the settings. + */ + virtual void apply() = 0; + + /** + * This slot is to reset all input widgets to the state of the settings. + */ + virtual void reset() = 0; + +protected: + /** + * Convenience function for retrieving a setting. + */ + static QVariant getSetting(Settings::SettingsEnum setting); + + /** + * Convenience function for changing a setting. + */ + template + static void setSetting(Settings::SettingsEnum setting, T value); + +}; + + + +inline +QVariant SettingsPage::getSetting(Settings::SettingsEnum setting) +{ + return Settings::getInstance().getSetting(setting); +} + +template +void SettingsPage::setSetting(Settings::SettingsEnum setting, T value) +{ + Settings::getInstance().setSetting(setting, QVariant{ value }); +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/symbol_dropdown.cpp b/src/gui/widgets/symbol_dropdown.cpp new file mode 100644 index 0000000..6adb55d --- /dev/null +++ b/src/gui/widgets/symbol_dropdown.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012, 2013, 2014, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "symbol_dropdown.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/symbols/combined_symbol.h" +#include "core/symbols/symbol.h" +#include "gui/util_gui.h" +#include "util/backports.h" + + +// allow explicit use of Symbol pointers in QVariant +Q_DECLARE_METATYPE(OpenOrienteering::Symbol*) + + + +namespace OpenOrienteering { + +// ### SymbolDropDown ### + + +SymbolDropDown::SymbolDropDown(const Map* map, int filter, const Symbol* initial_symbol, const Symbol* excluded_symbol, QWidget* parent) + : QComboBox(parent) +{ + num_custom_items = 0; + addItem(tr("- none -"), QVariant::fromValue(nullptr)); + + int size = map->getNumSymbols(); + for (int i = 0; i < size; ++i) + { + const auto symbol = map->getSymbol(i); + if (!(symbol->getType() & filter)) + continue; + if (symbol == excluded_symbol) + continue; + if (symbol->getType() == Symbol::Combined) // TODO: if point objects start to be able to contain objects of other ordinary symbols, add a check for these here, too, to prevent circular references + { + auto combined_symbol = static_cast(symbol); + if (combined_symbol->containsSymbol(excluded_symbol)) + continue; + } + + auto symbol_name = QString{symbol->getNumberAsString() + QLatin1Char(' ') + + Util::plainText(map->translate(symbol->getName()))}; + addItem(QPixmap::fromImage(symbol->getIcon(map)), symbol_name, QVariant::fromValue(symbol)); + } + setSymbol(initial_symbol); +} + + +SymbolDropDown::~SymbolDropDown() = default; + + + +const Symbol* SymbolDropDown::symbol() const +{ + auto data = itemData(currentIndex()); + if (data.canConvert()) + return data.value(); + else + return nullptr; +} + +void SymbolDropDown::setSymbol(const Symbol* symbol) +{ + setCurrentIndex(findData(QVariant::fromValue(symbol))); +} + +void SymbolDropDown::addCustomItem(const QString& text, int id) +{ + insertItem(1 + num_custom_items, text, QVariant{id}); + ++num_custom_items; +} + +int SymbolDropDown::customID() const +{ + auto data = itemData(currentIndex()); + if (data.canConvert()) + return data.value(); + else + return -1; +} + +void SymbolDropDown::setCustomItem(int id) +{ + setCurrentIndex(findData(QVariant{id})); +} + + + +// ### SymbolDropDownDelegate ### + +SymbolDropDownDelegate::SymbolDropDownDelegate(int symbol_type_filter, QObject* parent) +: QItemDelegate{parent} +, symbol_type_filter{symbol_type_filter} +{ + // nothing else +} + + +SymbolDropDownDelegate::~SymbolDropDownDelegate() = default; + + + +QWidget* SymbolDropDownDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const +{ + auto list = index.data(Qt::UserRole).toList(); + auto widget = new SymbolDropDown(list.at(0).value(), symbol_type_filter, + list.at(1).value(), nullptr, parent); + + connect(widget, QOverload::of(&QComboBox::currentIndexChanged), this, &SymbolDropDownDelegate::emitCommitData); + return widget; +} + +void SymbolDropDownDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const +{ + auto widget = static_cast(editor); + auto symbol = index.data(Qt::UserRole).toList().at(1).value(); + widget->setSymbol(symbol); +} + +void SymbolDropDownDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +{ + auto widget = static_cast(editor); + auto symbol = widget->symbol(); + auto list = index.data(Qt::UserRole).toList(); + list[1] = qVariantFromValue(symbol); + model->setData(index, list, Qt::UserRole); + + if (symbol) + { + auto map = list.at(0).value(); + auto text = QString{symbol->getNumberAsString() + QLatin1Char(' ') + + Util::plainText(map->translate(symbol->getName()))}; + model->setData(index, QVariant{text}, Qt::EditRole); + model->setData(index, symbol->getIcon(list[0].value()), Qt::DecorationRole); + } + else + { + model->setData(index, tr("- None -"), Qt::EditRole); + model->setData(index, {}, Qt::DecorationRole); + } +} + +void SymbolDropDownDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex&) const +{ + editor->setGeometry(option.rect); +} + +void SymbolDropDownDelegate::emitCommitData() +{ + emit commitData(qobject_cast(sender())); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/symbol_dropdown.h b/src/gui/widgets/symbol_dropdown.h new file mode 100644 index 0000000..601ad12 --- /dev/null +++ b/src/gui/widgets/symbol_dropdown.h @@ -0,0 +1,114 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012, 2013, 2014, 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_DROPDOWN_H +#define OPENORIENTEERING_SYMBOL_DROPDOWN_H + +#include +#include +#include +#include +// IWYU pragma: no_include + +class QAbstractItemModel; +class QModelIndex; +class QStyleOptionViewItem; +class QWidget; + +namespace OpenOrienteering { + +class Map; +class Symbol; + + +/** + * Drop down combobox for selecting a symbol. + */ +class SymbolDropDown : public QComboBox +{ +Q_OBJECT +public: + /** + * Creates a SymbolDropDown. + * @param map Map in which to choose a symbol. + * @param filter Bitwise-or combination of the allowed Symbol::Type types. + * @param initial_symbol Initial choice or nullptr for "- none -". + * @param excluded_symbol Symbol to exclude from the list or nullptr. + * @param parent QWidget parent. + */ + SymbolDropDown(const Map* map, int filter, const Symbol* initial_symbol = nullptr, const Symbol* excluded_symbol = nullptr, QWidget* parent = nullptr); + + ~SymbolDropDown() override; + + + /** Returns the selected symbol or nullptr if no symbol selected */ + const Symbol* symbol() const; + + /** Sets the selection to the given symbol */ + void setSymbol(const Symbol* symbol); + + /** Adds a custom text item below the topmost "- none -" which + * can be identified by the given id. */ + void addCustomItem(const QString& text, int id); + + /** Returns the id of the current item if it is a custom item, + * or -1 otherwise */ + int customID() const; + + /** Sets the selection to the custom item with the given id */ + void setCustomItem(int id); + +protected slots: + // TODO: react to changes in the map (not important as long as that cannot + // happen as long as a SymbolDropDown is shown, which is the case currently) + +private: + int num_custom_items; +}; + + + +/** + * Qt item delegate for SymbolDropDown. + */ +class SymbolDropDownDelegate : public QItemDelegate +{ +Q_OBJECT +public: + SymbolDropDownDelegate(int symbol_type_filter, QObject* parent = nullptr); + ~SymbolDropDownDelegate() override; + + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + void setEditorData(QWidget* editor, const QModelIndex& index) const override; + void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; + void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private slots: + void emitCommitData(); + +private: + const int symbol_type_filter; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/symbol_render_widget.cpp b/src/gui/widgets/symbol_render_widget.cpp new file mode 100644 index 0000000..f12d540 --- /dev/null +++ b/src/gui/widgets/symbol_render_widget.cpp @@ -0,0 +1,1120 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "symbol_render_widget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/map.h" +#include "core/objects/object.h" +#include "core/symbols/area_symbol.h" +#include "core/symbols/combined_symbol.h" +#include "core/symbols/line_symbol.h" +#include "core/symbols/point_symbol.h" +#include "core/symbols/symbol.h" +#include "core/symbols/symbol_icon_decorator.h" +#include "core/symbols/text_symbol.h" +#include "gui/symbols/symbol_setting_dialog.h" +#include "gui/widgets/symbol_tooltip.h" +#include "util/backports.h" +#include "util/overriding_shortcut.h" + + +namespace OpenOrienteering { + +namespace MimeType { + +/// The index of a symbol during drag-and-drop +const QString OpenOrienteeringSymbolIndex() +{ + return QStringLiteral("openorienteering/symbol_index"); +} + +/// Symbol definitions +const QString OpenOrienteeringSymbols() +{ + return QStringLiteral("openorienteering/symbols"); +} + + +} // namespace MimeType + + + +//### SymbolRenderWidget ### + +SymbolRenderWidget::SymbolRenderWidget(Map* map, bool mobile_mode, QWidget* parent) +: QWidget(parent) +, map(map) +, mobile_mode(mobile_mode) +, selection_locked(false) +, dragging(false) +, current_symbol_index(-1) +, hover_symbol_index(-1) +, last_drop_pos(-1) +, last_drop_row(-1) +, icon_size(Settings::getInstance().getSymbolWidgetIconSizePx()) +, icons_per_row(6) +, num_rows(5) +, preferred_size(icons_per_row * icon_size, num_rows * icon_size) +, hidden_symbol_decoration(new HiddenSymbolDecorator(icon_size)) +, protected_symbol_decoration(new ProtectedSymbolDecorator(icon_size)) +{ + setBackgroundRole(QPalette::Base); + setMouseTracking(true); + setFocusPolicy(Qt::ClickFocus); + setAcceptDrops(true); + + QShortcut* description_shortcut = new OverridingShortcut( + QKeySequence(tr("F1", "Shortcut for displaying the symbol's description")), + window() + ); + tooltip = new SymbolToolTip(this, description_shortcut); + // TODO: Use a placeholder in the literal and pass the actual shortcut's string representation. + setStatusTip(tr("For symbols with description, press F1 while the tooltip is visible to show it")); + + context_menu = new QMenu(this); + + QMenu* new_menu = new QMenu(tr("New symbol"), context_menu); + new_menu->setIcon(QIcon(QStringLiteral(":/images/plus.png"))); + /*QAction* new_point_action =*/ new_menu->addAction(tr("Point"), this, SLOT(newPointSymbol())); + /*QAction* new_line_action =*/ new_menu->addAction(tr("Line"), this, SLOT(newLineSymbol())); + /*QAction* new_area_action =*/ new_menu->addAction(tr("Area"), this, SLOT(newAreaSymbol())); + /*QAction* new_text_action =*/ new_menu->addAction(tr("Text"), this, SLOT(newTextSymbol())); + /*QAction* new_combined_action =*/ new_menu->addAction(tr("Combined"), this, SLOT(newCombinedSymbol())); + context_menu->addMenu(new_menu); + + edit_action = context_menu->addAction(tr("Edit"), this, SLOT(editSymbol())); + duplicate_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-duplicate.png")), tr("Duplicate"), this, SLOT(duplicateSymbol())); + delete_action = context_menu->addAction(QIcon(QStringLiteral(":/images/minus.png")), tr("Delete"), this, SLOT(deleteSymbols())); + scale_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-scale.png")), tr("Scale..."), this, SLOT(scaleSymbol())); + context_menu->addSeparator(); + copy_action = context_menu->addAction(QIcon(QStringLiteral(":/images/copy.png")), tr("Copy"), this, SLOT(copySymbols())); + paste_action = context_menu->addAction(QIcon(QStringLiteral(":/images/paste.png")), tr("Paste"), this, SLOT(pasteSymbols())); + context_menu->addSeparator(); + switch_symbol_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-switch-symbol.png")), tr("Switch symbol of selected objects"), this, SIGNAL(switchSymbolClicked())); + fill_border_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-fill-border.png")), tr("Fill / Create border for selected objects"), this, SIGNAL(fillBorderClicked())); + // text will be filled in by updateContextMenuState() + select_objects_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-edit.png")), {}, this, SLOT(selectObjectsExclusively())); + select_objects_additionally_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-edit.png")), {}, this, SLOT(selectObjectsAdditionally())); + deselect_objects_action = context_menu->addAction(QIcon(QStringLiteral(":/images/tool-edit.png")), {}, this, SLOT(deselectObjects())); + context_menu->addSeparator(); + hide_action = context_menu->addAction({}, this, SLOT(setSelectedSymbolVisibility(bool))); + hide_action->setCheckable(true); + protect_action = context_menu->addAction({}, this, SLOT(setSelectedSymbolProtection(bool))); + protect_action->setCheckable(true); + context_menu->addSeparator(); + + QMenu* select_menu = new QMenu(tr("Select symbols"), context_menu); + select_menu->addAction(tr("Select all"), this, SLOT(selectAll())); + select_menu->addAction(tr("Select unused"), this, SLOT(selectUnused())); + select_menu->addSeparator(); + select_menu->addAction(tr("Invert selection"), this, SLOT(invertSelection())); + context_menu->addMenu(select_menu); + + QMenu* sort_menu = new QMenu(tr("Sort symbols"), context_menu); + sort_menu->addAction(tr("Sort by number"), this, SLOT(sortByNumber())); + sort_menu->addAction(tr("Sort by primary color"), this, SLOT(sortByColor())); + sort_menu->addAction(tr("Sort by primary color priority"), this, SLOT(sortByColorPriority())); + sort_manual_action = sort_menu->addAction(tr("Enable drag and drop")); + sort_manual_action->setCheckable(true); + context_menu->addMenu(sort_menu); + + connect(map, &Map::colorDeleted, this, QOverload<>::of(&QWidget::update)); + connect(map, &Map::symbolAdded, this, &SymbolRenderWidget::updateAll); + connect(map, &Map::symbolDeleted, this, &SymbolRenderWidget::symbolDeleted); + connect(map, &Map::symbolChanged, this, &SymbolRenderWidget::symbolChanged); + connect(map, &Map::symbolIconChanged, this, &SymbolRenderWidget::updateSingleIcon); + connect(map, &Map::symbolIconZoomChanged, this, &SymbolRenderWidget::updateAll); + connect(&Settings::getInstance(), &Settings::settingsChanged, this, &SymbolRenderWidget::settingsChanged); +} + +SymbolRenderWidget::~SymbolRenderWidget() +{ + ; // nothing +} + +void SymbolRenderWidget::symbolDeleted(int pos, const Symbol *old_symbol) +{ + Q_UNUSED(old_symbol); + + std::set::iterator it = selected_symbols.find(pos); + if (it != selected_symbols.end()) + { + // Remove pos + std::set::iterator new_it = it; + ++new_it; + selected_symbols.erase(it); + // Renumber successors + for (it = new_it; it != selected_symbols.end(); it = ++new_it) + { + new_it = selected_symbols.insert(*it - 1).first; + selected_symbols.erase(it); + } + emitGuarded_selectedSymbolsChanged(); + } + + adjustLayout(); + update(); +} + +void SymbolRenderWidget::symbolChanged(int pos, const Symbol* new_symbol, const Symbol* old_symbol) +{ + Q_UNUSED(new_symbol); + Q_UNUSED(old_symbol); + + updateSingleIcon(pos); + + if (isSymbolSelected(pos)) + emitGuarded_selectedSymbolsChanged(); +} + +void SymbolRenderWidget::updateAll() +{ + adjustLayout(); + update(); +} + +void SymbolRenderWidget::updateSingleIcon(int i) +{ + if (i >= 0) + { + QPoint pos = iconPosition(i); + update(pos.x(), pos.y(), icon_size, icon_size); + } +} + +void SymbolRenderWidget::settingsChanged() +{ + const auto new_size = Settings::getInstance().getSymbolWidgetIconSizePx(); + if (icon_size != new_size) + { + for (int i = 0; i < map->getNumSymbols(); ++i) + { + auto symbol = map->getSymbol(i); + if (symbol->getIcon(map).width() != new_size) + symbol->resetIcon(); + } + updateAll(); + } +} + + + +QSize SymbolRenderWidget::sizeHint() const +{ + return preferred_size; +} + +void SymbolRenderWidget::adjustLayout() +{ + auto old_icon_size = icon_size; + icon_size = Settings::getInstance().getSymbolWidgetIconSizePx() + 1; + // Allow symbol widget to be that much wider than the viewport + int overflow = icon_size / 3; + icons_per_row = qMax(1, (width() + overflow) / icon_size); + num_rows = qMax(1, (map->getNumSymbols() + icons_per_row -1) / icons_per_row); + setFixedHeight(num_rows * icon_size); + + if (old_icon_size != icon_size) + { + hidden_symbol_decoration.reset(new HiddenSymbolDecorator(icon_size)); + protected_symbol_decoration.reset(new ProtectedSymbolDecorator(icon_size)); + } +} + +void SymbolRenderWidget::emitGuarded_selectedSymbolsChanged() +{ + QScopedValueRollback save(selection_locked); + selection_locked = true; + emit selectedSymbolsChanged(); +} + +const Symbol* SymbolRenderWidget::singleSelectedSymbol() const +{ + if (selected_symbols.size() != 1) + return nullptr; + return static_cast(map)->getSymbol(*(selected_symbols.begin())); +} + +Symbol* SymbolRenderWidget::singleSelectedSymbol() +{ + if (selected_symbols.size() != 1) + return nullptr; + return map->getSymbol(*(selected_symbols.begin())); +} + +bool SymbolRenderWidget::isSymbolSelected(const Symbol* symbol) const +{ + return isSymbolSelected(map->findSymbolIndex(symbol)); +} + +bool SymbolRenderWidget::isSymbolSelected(int i) const +{ + return selected_symbols.find(i) != selected_symbols.end(); +} + +void SymbolRenderWidget::selectSingleSymbol(const Symbol *symbol) +{ + int index = map->findSymbolIndex(symbol); + if (index >= 0) + selectSingleSymbol(index); +} + +void SymbolRenderWidget::selectSingleSymbol(int i) +{ + if (selection_locked) + return; + + if (selected_symbols.size() == 1 && *selected_symbols.begin() == i) + return; // Symbol already selected as only selection + + updateSelectedIcons(); + selected_symbols.clear(); + if (i >= 0) + { + selected_symbols.insert(i); + updateSingleIcon(i); + } + current_symbol_index = i; + + emitGuarded_selectedSymbolsChanged(); +} + +void SymbolRenderWidget::hover(QPoint pos) +{ + int i = symbolIndexAt(pos); + + if (i != hover_symbol_index) + { + updateSingleIcon(hover_symbol_index); + hover_symbol_index = i; + updateSingleIcon(hover_symbol_index); + + if (hover_symbol_index >= 0) + { + Symbol* symbol = map->getSymbol(hover_symbol_index); + if (tooltip->getSymbol() != symbol) + { + const QRect icon_rect(mapToGlobal(iconPosition(hover_symbol_index)), QSize(icon_size, icon_size)); + tooltip->scheduleShow(symbol, map, icon_rect, mobile_mode); + } + } + else + { + tooltip->reset(); + } + } +} + +QPoint SymbolRenderWidget::iconPosition(int i) const +{ + return QPoint((i % icons_per_row) * icon_size, (i / icons_per_row) * icon_size); +} + +int SymbolRenderWidget::symbolIndexAt(QPoint pos) const +{ + int i = -1; + + int icon_in_row = pos.x() / icon_size; + if (icon_in_row < icons_per_row) + { + i = icon_in_row + icons_per_row * (pos.y() / icon_size); + if (i >= map->getNumSymbols()) + i = -1; + } + + return i; +} + +void SymbolRenderWidget::updateSelectedIcons() +{ + for (auto symbol_index : selected_symbols) + updateSingleIcon(symbol_index); +} + +bool SymbolRenderWidget::dropPosition(QPoint pos, int& row, int& pos_in_row) +{ + row = pos.y() / icon_size; + if (row >= num_rows) + { + row = -1; + pos_in_row = -1; + return false; + } + + pos_in_row = (pos.x() + icon_size / 2) / icon_size; + if (pos_in_row > icons_per_row) + { + pos_in_row = icons_per_row; + } + + int global_pos = row * icons_per_row + pos_in_row; + if (global_pos > map->getNumSymbols()) + { + row = -1; + pos_in_row = -1; + return false; + } + + return true; +} + +QRect SymbolRenderWidget::dropIndicatorRect(int row, int pos_in_row) +{ + const int rect_width = 3; + return QRect(pos_in_row * icon_size - rect_width / 2 - 1, row * icon_size - 1, rect_width, icon_size + 2); +} + +void SymbolRenderWidget::drawIcon(QPainter &painter, int i) const +{ + painter.save(); + + Symbol* symbol = map->getSymbol(i); + painter.drawImage(0, 0, symbol->getIcon(map)); + + if (isSymbolSelected(i) || i == current_symbol_index) + { + painter.setOpacity(0.5); + QPen pen(Qt::white); + pen.setWidth(2); + pen.setJoinStyle(Qt::MiterJoin); + painter.setPen(pen); + painter.drawRect(QRectF(1.5f, 1.5f, icon_size - 5, icon_size - 5)); + + painter.setOpacity(1.0); + } + + if (symbol->isProtected()) + protected_symbol_decoration->draw(painter); + + if (symbol->isHidden()) + hidden_symbol_decoration->draw(painter); + + if (isSymbolSelected(i)) + { + QPen pen(qRgb(12, 0, 255)); + pen.setWidth(2); + pen.setJoinStyle(Qt::MiterJoin); + painter.setPen(pen); + painter.drawRect(QRectF(0.5f, 0.5f, icon_size - 3, icon_size - 3)); + + if (i == hover_symbol_index) + { + QPen pen(qRgb(255, 150, 0)); + pen.setWidth(2); + pen.setDashPattern(QVector() << 1 << 2); + pen.setJoinStyle(Qt::MiterJoin); + painter.setPen(pen); + painter.drawRect(QRectF(0.5f, 0.5f, icon_size - 3, icon_size - 3)); + } + else if (i == current_symbol_index) + { + QPen pen(Qt::white); + pen.setDashPattern(QVector() << 2 << 2); + painter.setPen(pen); + painter.drawRect(1, 1, icon_size - 4, icon_size - 4); + } + } + else if (i == hover_symbol_index) + { + QPen pen(qRgb(255, 150, 0)); + pen.setWidth(2); + pen.setJoinStyle(Qt::MiterJoin); + painter.setPen(pen); + painter.drawRect(QRectF(0.5f, 0.5f, icon_size - 3, icon_size - 3)); + } + + painter.setPen(Qt::gray); + painter.drawLine(QPoint(0, icon_size - 1), QPoint(icon_size - 1, icon_size - 1)); + painter.drawLine(QPoint(icon_size - 1, 0), QPoint(icon_size - 1, icon_size - 2)); + + painter.restore(); +} + +void SymbolRenderWidget::paintEvent(QPaintEvent* event) +{ + QRect event_rect = event->rect().adjusted(-icon_size, -icon_size, 0, 0); + + QPainter painter(this); + painter.setPen(Qt::gray); + + int x = 0; + int y = 0; + for (int i = 0; i < map->getNumSymbols(); ++i) + { + if (event_rect.contains(x,y)) + { + painter.save(); + painter.translate(x, y); + drawIcon(painter, i); + painter.restore(); + } + + x += icon_size; + if (x >= icons_per_row * icon_size) + { + x = 0; + y += icon_size; + } + } + + // Drop indicator? + if (last_drop_pos >= 0 && last_drop_row >= 0) + { + QRect drop_rect = dropIndicatorRect(last_drop_row, last_drop_pos); + painter.setPen(qRgb(255, 150, 0)); + painter.setBrush(Qt::NoBrush); + painter.drawRect(drop_rect.left(), drop_rect.top(), drop_rect.width() - 1, drop_rect.height() - 1); + } + + painter.end(); +} + +void SymbolRenderWidget::resizeEvent(QResizeEvent* event) +{ + if (width() != event->oldSize().width()) + { + adjustLayout(); + } +} + +void SymbolRenderWidget::mouseMoveEvent(QMouseEvent* event) +{ + if (mobile_mode) + { + if (event->buttons() & Qt::LeftButton) + { + hover(event->pos()); + + if ((event->pos() - last_click_pos).manhattanLength() < Settings::getInstance().getStartDragDistancePx()) + return; + dragging = sort_manual_action->isChecked(); + } + } + else + { + if (event->buttons() & Qt::LeftButton + && current_symbol_index >= 0 + && sort_manual_action->isChecked()) + { + if ((event->pos() - last_click_pos).manhattanLength() < Settings::getInstance().getStartDragDistancePx()) + return; + + tooltip->hide(); + + QDrag* drag = new QDrag(this); + QMimeData* mime_data = new QMimeData(); + + QByteArray data; + data.append((const char*)¤t_symbol_index, sizeof(int)); + mime_data->setData(MimeType::OpenOrienteeringSymbolIndex(), data); + drag->setMimeData(mime_data); + + drag->exec(Qt::MoveAction); + } + else if (event->button() == Qt::NoButton) + { + hover(event->pos()); + } + } + event->accept(); +} + +void SymbolRenderWidget::mousePressEvent(QMouseEvent* event) +{ + dragging = false; + + if (mobile_mode) + { + tooltip->hide(); + + last_click_pos = event->pos(); + hover(event->pos()); + return; + } + + updateSingleIcon(current_symbol_index); + int old_symbol_index = current_symbol_index; + current_symbol_index = symbolIndexAt(event->pos()); + updateSingleIcon(current_symbol_index); + + if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) + { + if (event->modifiers() & Qt::ControlModifier) + { + if (current_symbol_index >= 0) + { + if (!isSymbolSelected(current_symbol_index)) + selected_symbols.insert(current_symbol_index); + else + selected_symbols.erase(current_symbol_index); + emitGuarded_selectedSymbolsChanged(); + } + } + else if (event->modifiers() & Qt::ShiftModifier) + { + if (current_symbol_index >= 0) + { + bool insert = !isSymbolSelected(current_symbol_index); + int i = (old_symbol_index >= 0) ? old_symbol_index : current_symbol_index; + while (true) + { + if (insert) + selected_symbols.insert(i); + else + selected_symbols.erase(i); + updateSingleIcon(i); + + if (current_symbol_index > i) + ++i; + else if (current_symbol_index < i) + --i; + else + break; + } + emitGuarded_selectedSymbolsChanged(); + } + } + else + { + if (event->button() == Qt::LeftButton || + (event->button() == Qt::RightButton && + current_symbol_index >= 0 && !isSymbolSelected(current_symbol_index))) + selectSingleSymbol(current_symbol_index); + } + } + + else if (event->button() == Qt::LeftButton && current_symbol_index >= 0 && !(event->modifiers() & Qt::ShiftModifier)) + { + last_click_pos = event->pos(); + last_drop_pos = -1; + last_drop_row = -1; + } +} + +void SymbolRenderWidget::mouseReleaseEvent(QMouseEvent* event) +{ + if (mobile_mode) + { + if (dragging) + { + dragging = false; + return; + } + + hover(event->pos()); + if (tooltip->isVisible()) + return; + + updateSingleIcon(current_symbol_index); + current_symbol_index = symbolIndexAt(event->pos()); + updateSingleIcon(current_symbol_index); + + if (current_symbol_index >= 0 && !isSymbolSelected(current_symbol_index)) + selectSingleSymbol(current_symbol_index); + else + emitGuarded_selectedSymbolsChanged(); // HACK, will close the symbol selection screen + } +} + +void SymbolRenderWidget::mouseDoubleClickEvent(QMouseEvent* event) +{ + if (mobile_mode) + return; + + int i = symbolIndexAt(event->pos()); + if (i < 0) + return; + + updateSingleIcon(current_symbol_index); + current_symbol_index = i; + updateSingleIcon(current_symbol_index); + editSymbol(); +} + +void SymbolRenderWidget::leaveEvent(QEvent* event) +{ + Q_UNUSED(event); + updateSingleIcon(hover_symbol_index); + hover_symbol_index = -1; + + tooltip->reset(); +} + +void SymbolRenderWidget::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasFormat(MimeType::OpenOrienteeringSymbolIndex())) + event->acceptProposedAction(); +} + +void SymbolRenderWidget::dragMoveEvent(QDragMoveEvent* event) +{ + if (event->mimeData()->hasFormat(MimeType::OpenOrienteeringSymbolIndex())) + { + int row, pos_in_row; + if (!dropPosition(event->pos(), row, pos_in_row)) + { + if (last_drop_pos >= 0 && last_drop_row >= 0) + { + update(dropIndicatorRect(last_drop_row, last_drop_pos)); + last_drop_pos = -1; + last_drop_row = -1; + } + return; + } + + if (row != last_drop_row || pos_in_row != last_drop_pos) + { + if (last_drop_row >= 0 && last_drop_pos >= 0) + update(dropIndicatorRect(last_drop_row, last_drop_pos)); + + last_drop_row = row; + last_drop_pos = pos_in_row; + + if (last_drop_row >= 0 && last_drop_pos >= 0) + update(dropIndicatorRect(last_drop_row, last_drop_pos)); + } + + event->acceptProposedAction(); + } +} + +void SymbolRenderWidget::dropEvent(QDropEvent* event) +{ + last_drop_row = -1; + last_drop_pos = -1; + + if (event->source() != this || current_symbol_index < 0) + return; + + if (event->proposedAction() == Qt::MoveAction) + { + int row, pos_in_row; + if (!dropPosition(event->pos(), row, pos_in_row)) + return; + + int pos = row * icons_per_row + pos_in_row; + if (pos == current_symbol_index) + return; + + event->acceptProposedAction(); + + // save selection + std::set sel; + for (auto symbol_index : selected_symbols) { + sel.insert(map->getSymbol(symbol_index)); + } + + map->moveSymbol(current_symbol_index, pos); + update(); + + + if (pos > current_symbol_index) + --pos; + current_symbol_index = pos; + selectSingleSymbol(pos); + } +} + +void SymbolRenderWidget::selectObjectsExclusively() +{ + emit selectObjectsClicked(true); +} + +void SymbolRenderWidget::selectObjectsAdditionally() +{ + emit selectObjectsClicked(false); +} + +void SymbolRenderWidget::deselectObjects() +{ + emit deselectObjectsClicked(); +} + +void SymbolRenderWidget::newPointSymbol() +{ + newSymbol(new PointSymbol()); +} + +void SymbolRenderWidget::newLineSymbol() +{ + newSymbol(new LineSymbol()); +} + +void SymbolRenderWidget::newAreaSymbol() +{ + newSymbol(new AreaSymbol()); +} + +void SymbolRenderWidget::newTextSymbol() +{ + newSymbol(new TextSymbol()); +} + +void SymbolRenderWidget::newCombinedSymbol() +{ + newSymbol(new CombinedSymbol()); +} + +void SymbolRenderWidget::editSymbol() +{ + Q_ASSERT(current_symbol_index >= 0); + + Symbol* symbol = map->getSymbol(current_symbol_index); + SymbolSettingDialog dialog(symbol, map, this); + dialog.setWindowModality(Qt::WindowModal); + if (dialog.exec() == QDialog::Accepted) + { + map->setSymbol(dialog.getNewSymbol().release(), current_symbol_index); + } +} + +void SymbolRenderWidget::scaleSymbol() +{ + Q_ASSERT(!selected_symbols.empty()); + + bool ok; + double percent = QInputDialog::getDouble(this, tr("Scale symbols"), tr("Scale to percentage:"), 100, 0, 999999, 6, &ok); + if (!ok || percent == 100) + return; + + for (auto symbol_index : selected_symbols) + { + auto symbol = map->getSymbol(symbol_index); + symbol->scale(percent / 100.0); + updateSingleIcon(current_symbol_index); + map->changeSymbolForAllObjects(symbol, symbol); // update the objects + } + + map->setSymbolsDirty(); +} + +void SymbolRenderWidget::deleteSymbols() +{ + // save selected symbols + std::vector saved_selection; + saved_selection.reserve(selected_symbols.size()); + for (auto symbol_index : selected_symbols) + { + saved_selection.push_back(map->getSymbol(symbol_index)); + } + + // delete symbols in order + for (const auto symbol : saved_selection) + { + if (map->existsObjectWithSymbol(symbol)) + { + if (QMessageBox::warning(this, tr("Confirmation"), tr("The map contains objects with the symbol \"%1\". Deleting it will delete those objects and clear the undo history! Do you really want to do that?").arg(symbol->getName()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + continue; + } + map->deleteSymbol(map->findSymbolIndex(symbol)); + } + + if (selected_symbols.empty() && map->getFirstSelectedObject()) + selectSingleSymbol(map->getFirstSelectedObject()->getSymbol()); +} + +void SymbolRenderWidget::duplicateSymbol() +{ + Q_ASSERT(current_symbol_index >= 0); + + map->addSymbol(map->getSymbol(current_symbol_index)->duplicate(), current_symbol_index + 1); + selectSingleSymbol(current_symbol_index + 1); +} + +void SymbolRenderWidget::copySymbols() +{ + // Create map containing all colors and the selected symbols. + // Copying all colors improves preservation of relative order during paste. + Map* copy_map = new Map(); + copy_map->setScaleDenominator(map->getScaleDenominator()); + + copy_map->importMap(map, Map::ColorImport, this); + + std::vector selection(map->getNumSymbols(), false); + for (auto symbol_index : selected_symbols) + selection[symbol_index] = true; + + copy_map->importMap(map, Map::MinimalSymbolImport, this, &selection); + + // Save map to memory + QBuffer buffer; + if (!copy_map->exportToIODevice(&buffer)) + { + delete copy_map; + QMessageBox::warning(nullptr, tr("Error"), tr("An internal error occurred, sorry!")); + return; + } + delete copy_map; + + // Put buffer into clipboard + QMimeData* mime_data = new QMimeData(); + mime_data->setData(MimeType::OpenOrienteeringSymbols(), buffer.data()); + QApplication::clipboard()->setMimeData(mime_data); +} + +void SymbolRenderWidget::pasteSymbols() +{ + if (!QApplication::clipboard()->mimeData()->hasFormat(MimeType::OpenOrienteeringSymbols())) + { + QMessageBox::warning(nullptr, tr("Error"), tr("There are no symbols in clipboard which could be pasted!")); + return; + } + + // Get buffer from clipboard + QByteArray byte_array = QApplication::clipboard()->mimeData()->data(MimeType::OpenOrienteeringSymbols()); + QBuffer buffer(&byte_array); + buffer.open(QIODevice::ReadOnly); + + // Create map from buffer + Map* paste_map = new Map(); + if (!paste_map->importFromIODevice(&buffer)) + { + QMessageBox::warning(nullptr, tr("Error"), tr("An internal error occurred, sorry!")); + return; + } + + // Ensure that a change in selection is detected + selectSingleSymbol(-1); + + // Import pasted map + map->importMap(paste_map, Map::MinimalSymbolImport, this, nullptr, current_symbol_index, false); + delete paste_map; + + selectSingleSymbol(current_symbol_index); +} + +void SymbolRenderWidget::setSelectedSymbolVisibility(bool checked) +{ + bool selection_changed = false; + for (auto symbol_index : selected_symbols) + { + auto symbol = map->getSymbol(symbol_index); + if (symbol->isHidden() != checked) + { + symbol->setHidden(checked); + updateSingleIcon(symbol_index); + if (checked) + selection_changed |= map->removeSymbolFromSelection(symbol, false); + } + } + if (selection_changed) + map->emitSelectionChanged(); + map->updateAllMapWidgets(); + map->setSymbolsDirty(); + emitGuarded_selectedSymbolsChanged(); +} + +void SymbolRenderWidget::setSelectedSymbolProtection(bool checked) +{ + bool selection_changed = false; + for (auto symbol_index : selected_symbols) + { + auto symbol = map->getSymbol(symbol_index); + if (symbol->isProtected() != checked) + { + symbol->setProtected(checked); + updateSingleIcon(symbol_index); + if (checked) + selection_changed |= map->removeSymbolFromSelection(symbol, false); + } + } + if (selection_changed) + map->emitSelectionChanged(); + map->setSymbolsDirty(); + emitGuarded_selectedSymbolsChanged(); +} + +void SymbolRenderWidget::selectAll() +{ + for (int i = 0; i < map->getNumSymbols(); ++i) + selected_symbols.insert(i); + emitGuarded_selectedSymbolsChanged(); + update(); +} + +void SymbolRenderWidget::selectUnused() +{ + std::vector symbols_in_use; + map->determineSymbolsInUse(symbols_in_use); + + updateSelectedIcons(); + selected_symbols.clear(); + for (size_t i = 0, end = symbols_in_use.size(); i < end; ++i) + { + if (!symbols_in_use[i]) + { + selected_symbols.insert(i); + updateSingleIcon(i); + } + } + emitGuarded_selectedSymbolsChanged(); +} + +void SymbolRenderWidget::invertSelection() +{ + std::set new_set; + for (int i = 0; i < map->getNumSymbols(); ++i) + { + if (!isSymbolSelected(i)) + new_set.insert(i); + } + swap(selected_symbols, new_set); + emitGuarded_selectedSymbolsChanged(); + update(); +} + +void SymbolRenderWidget::sortByNumber() +{ + sort(Symbol::compareByNumber); +} + +void SymbolRenderWidget::sortByColor() +{ + sort(Symbol::compareByColor(map)); +} + +void SymbolRenderWidget::sortByColorPriority() +{ + sort(Symbol::compareByColorPriority); +} + +void SymbolRenderWidget::showContextMenu(QPoint global_pos) +{ + updateContextMenuState(); + context_menu->popup(global_pos); +} + +void SymbolRenderWidget::contextMenuEvent(QContextMenuEvent* event) +{ + showContextMenu(event->globalPos()); + event->accept(); +} + +void SymbolRenderWidget::updateContextMenuState() +{ + bool have_selection = selectedSymbolsCount() > 0; + bool single_selection = selectedSymbolsCount() == 1 && current_symbol_index >= 0; + const Symbol* single_symbol = singleSelectedSymbol(); + bool all_symbols_hidden = have_selection; + bool all_symbols_protected = have_selection; + for (auto symbol_index : selected_symbols) + { + if (!map->getSymbol(symbol_index)->isHidden()) + all_symbols_hidden = false; + if (!map->getSymbol(symbol_index)->isProtected()) + all_symbols_protected = false; + if (!all_symbols_hidden && !all_symbols_protected) + break; + } + + bool single_symbol_compatible; + bool single_symbol_different; + map->getSelectionToSymbolCompatibility(single_symbol, single_symbol_compatible, single_symbol_different); + + edit_action->setEnabled(single_selection); + scale_action->setEnabled(have_selection); + copy_action->setEnabled(have_selection); + paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(MimeType::OpenOrienteeringSymbols())); + switch_symbol_action->setEnabled(single_symbol_compatible && single_symbol_different); + fill_border_action->setEnabled(single_symbol_compatible && single_symbol_different); + hide_action->setEnabled(have_selection); + hide_action->setChecked(all_symbols_hidden); + protect_action->setEnabled(have_selection); + protect_action->setChecked(all_symbols_protected); + duplicate_action->setEnabled(single_selection); + delete_action->setEnabled(have_selection); + + if (single_selection) + { + select_objects_action->setText(tr("Select all objects with this symbol")); + select_objects_additionally_action->setText(tr("Add all objects with this symbol to selection")); + deselect_objects_action->setText(tr("Remove all objects with this symbol from selection")); + hide_action->setText(tr("Hide objects with this symbol")); + protect_action->setText(tr("Protect objects with this symbol")); + } + else + { + select_objects_action->setText(tr("Select all objects with selected symbols")); + select_objects_additionally_action->setText(tr("Add all objects with selected symbols to selection")); + deselect_objects_action->setText(tr("Remove all objects with selected symbols from selection")); + hide_action->setText(tr("Hide objects with selected symbols")); + protect_action->setText(tr("Protect objects with selected symbols")); + } + select_objects_action->setEnabled(have_selection); + select_objects_additionally_action->setEnabled(have_selection); + deselect_objects_action->setEnabled(have_selection); +} + +bool SymbolRenderWidget::newSymbol(Symbol* prototype) +{ + SymbolSettingDialog dialog(prototype, map, this); + dialog.setWindowModality(Qt::WindowModal); + delete prototype; + if (dialog.exec() == QDialog::Rejected) + return false; + + int pos = (current_symbol_index >= 0) ? current_symbol_index : map->getNumSymbols(); + map->addSymbol(dialog.getNewSymbol().release(), pos); + // Ensure that a change in selection is detected + selectSingleSymbol(-1); + selectSingleSymbol(pos); + return true; +} + +template +void SymbolRenderWidget::sort(T compare) +{ + // save selection + std::set saved_selection; + for (auto symbol_index : selected_symbols) + { + saved_selection.insert(map->getSymbol(symbol_index)); + } + + map->sortSymbols(compare); + + // restore selection + selected_symbols.clear(); + for (int i = 0; i < map->getNumSymbols(); ++i) + { + if (saved_selection.find(map->getSymbol(i)) != saved_selection.end()) + selected_symbols.insert(i); + } + + update(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/symbol_render_widget.h b/src/gui/widgets/symbol_render_widget.h new file mode 100644 index 0000000..21de631 --- /dev/null +++ b/src/gui/widgets/symbol_render_widget.h @@ -0,0 +1,382 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_RENDER_WIDGET_H +#define OPENORIENTEERING_SYMBOL_RENDER_WIDGET_H + +#include + +#include +#include +#include +#include +#include +#include + +class QAction; +class QContextMenuEvent; +class QDragEnterEvent; +class QDragMoveEvent; +class QDropEvent; +class QEvent; +class QMenu; +class QMouseEvent; +class QPaintEvent; +class QPainter; +class QResizeEvent; + +namespace OpenOrienteering { + +class Map; +class Symbol; +class SymbolIconDecorator; +class SymbolToolTip; + + +/** + * @brief Shows all symbols from a map in a size-constrained widget. + * + * SymbolRenderWidget lets the user select symbols and perform actions on them. + * It is a plain widget which does not implement scrolling but takes as much + * height as neccessary for given width and number of symbols. Scrolling is + * realized by SymbolWidget. + */ +class SymbolRenderWidget : public QWidget +{ +Q_OBJECT +public: + /** + * @brief Constructs a new SymbolRenderWidget. + * @param map The map which provides the symbols. Must not be nullptr. + * @param mobile_mode If true, enables a special mode for mobile devices. + * @param parent The parent QWidget. + */ + SymbolRenderWidget(Map* map, bool mobile_mode, QWidget* parent = nullptr); + + /** + * @brief Destroys the SymbolRenderWidget. + */ + ~SymbolRenderWidget() override; + + /** + * @brief Returns the number of selected symbols. + */ + int selectedSymbolsCount() const; + + /** + * @brief Checks if the symbol is selected. + */ + bool isSymbolSelected(const Symbol* symbol) const; + + /** + * @brief Checks if the symbol with given index is selected. + */ + bool isSymbolSelected(int i) const; + + /** + * @brief If exactly one symbol is selected, returns this symbol. + * + * Otherwise returns nullptr. + */ + const Symbol* singleSelectedSymbol() const; + + /** + * @brief If exactly one symbol is selected, returns this symbol. + * + * Otherwise returns nullptr. + */ + Symbol* singleSelectedSymbol(); + + /** + * @brief Selects the given symbol exclusively. + * + * Deselects other symbols, if there was a different selection before. + */ + void selectSingleSymbol(const Symbol* symbol); + + /** + * @brief Selects the given symbol exclusively. + * + * Selects the symbol with the given number. + * Deselects other symbols, if there was a different selection before. + */ + void selectSingleSymbol(int i); + + /** + * @brief Returns the recommended size for the widget. + * + * Reimplemented from QWidget::sizeHint(). + */ + QSize sizeHint() const override; + + /** + * @brief Opens the context menu at the given global position. + */ + void showContextMenu(QPoint global_pos); + +public slots: + /** + * @brief Updates the layout and marks all icons for repainting. + */ + void updateAll(); + + /** + * @brief Marks the icon with the given index for repainting. + */ + void updateSingleIcon(int i); + + /** + * Observes settings changes related to symbol display. + */ + void settingsChanged(); + +signals: + /** + * @brief The collection of selected symbols changed. + */ + void selectedSymbolsChanged(); + + /** + * @brief The user triggered "Switch symbol". + * @todo Merge with/Reuse corresponding action in MapEditorController. + */ + void switchSymbolClicked(); + + /** + * @brief The user triggered "Fill/Create border". + * @todo Merge with/Reuse corresponding action in MapEditorController. + */ + void fillBorderClicked(); + + /** + * @brief The user triggered selecting objects with the active symbol. + * @param exclusively If true, an existing selection is replaced, + * otherwise it is extend. + */ + void selectObjectsClicked(bool exclusively); + + /** + * @brief The user triggered deselecting objects with the active symbol. + */ + void deselectObjectsClicked(); + +protected slots: + /** + * @brief Updates icon and selection when a symbol changes. + * + * @see Map::symbolChanged() + */ + void symbolChanged(int pos, const Symbol* new_symbol, const Symbol* old_symbol); + + /** + * @brief Updates the widget and the current selection. + * + * @see Map::symbolDeleted() + */ + void symbolDeleted(int pos, const Symbol* old_symbol); + + void newPointSymbol(); + void newLineSymbol(); + void newAreaSymbol(); + void newTextSymbol(); + void newCombinedSymbol(); + void editSymbol(); + void scaleSymbol(); + void deleteSymbols(); + void duplicateSymbol(); + void copySymbols(); + void pasteSymbols(); + void setSelectedSymbolVisibility(bool checked); + void setSelectedSymbolProtection(bool checked); + void selectObjectsExclusively(); + void selectObjectsAdditionally(); + void deselectObjects(); + void selectAll(); + void selectUnused(); + void invertSelection(); + void sortByNumber(); + void sortByColor(); + void sortByColorPriority(); + +protected: + void resizeEvent(QResizeEvent* event) override; + + /** + * @brief Recalculates the layout and size. + * + * This function reads the icon size from the settings and the widget's + * current width and adjusts the number of icons per row, the number of + * rows and the widget's height to fit the current number of symbols. + */ + void adjustLayout(); + + /** + * @brief Returns the top-left coordinates of a symbol's icon. + * @param i The index of the symbol. + */ + QPoint iconPosition(int i) const; + + /** + * @brief Returns the index of the symbol represented at a particular location + * @param pos The location + */ + int symbolIndexAt(QPoint pos) const; + + + void paintEvent(QPaintEvent* event) override; + + /** + * @brief Draws the icon and its decoration (hidden, protected). + * + * The icon is drawn at (0, 0) with the current icon size. + * @param painter The QPainter to be used (must be active). + * @param i The index of the symbol. + */ + void drawIcon(QPainter& painter, int i) const; + + + void mouseMoveEvent(QMouseEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; + void leaveEvent(QEvent* event) override; + + /** + * @brief Handles hovering over the icons, i.e. controlling the tool tip. + * @param pos The current location of the pointing device. + */ + void hover(QPoint pos); + + + void dragEnterEvent(QDragEnterEvent* event) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void dropEvent(QDropEvent* event) override; + + /** + * @brief Determines the drop location for a given pointing device position. + * @param pos The pointing device position. + * @param row The icon row where to insert the dropped symbol. + * @param pos_in_row The position in the row where to insert the dropped symbol. + * @return If there is a valid drop position, returns true, otherwise false. + */ + bool dropPosition(QPoint pos, int& row, int& pos_in_row); + + /** + * @brief Determines a drop indicator rectangle for a given location in the icons. + * @param row The icon row where to insert the dropped symbol. + * @param pos_in_row The position in the row where to insert the dropped symbol. + */ + QRect dropIndicatorRect(int row, int pos_in_row); + + + /** + * @brief Takes care of editing and inserting a newly created symbol. + * @param prototype The symbol which will be edited and inserted in the map. + * @return If editing is cancelled, returns false. Otherwise returns true. + */ + bool newSymbol(Symbol* prototype); + + /** + * @brief Sorts the map's symbol set by arbitrary criteria. + */ + template void sort(T compare); + + /** + * @brief Marks the icons of the selected symbols for repainting. + */ + void updateSelectedIcons(); + + /** + * @brief Emits selectedSymbolsChanged() while temporarily locking the symbol selection against changes. + * + * An active tool could catch the selectedSymbolsChanged() signal and + * finish its editing for that reason. It would than insert a new object to + * the map and so trigger another change of selection. Emitting + * selectedSymbolsChanged() via this method supresses behavior by setting + * the selection_locked flag before emitting the actual signal and resetting + * the flag on return. + */ + void emitGuarded_selectedSymbolsChanged(); + + /** + * @brief Receives context menu events and opens the context menu. + */ + void contextMenuEvent(QContextMenuEvent* event) override; + + /** + * @brief Updates the state of the actions in the context menu. + */ + void updateContextMenuState(); + +private: + Map* map; + bool mobile_mode; + + bool selection_locked; + bool dragging; + + int current_symbol_index; + int hover_symbol_index; + std::set selected_symbols; + + QPoint last_click_pos; + int last_drop_pos; + int last_drop_row; + + int icon_size; + int icons_per_row; + int num_rows; + QSize preferred_size; + + QMenu* context_menu; + QAction* edit_action; + QAction* scale_action; + QAction* copy_action; + QAction* paste_action; + QAction* switch_symbol_action; + QAction* fill_border_action; + QAction* hide_action; + QAction* protect_action; + QAction* duplicate_action; + QAction* delete_action; + QAction* select_objects_action; + QAction* select_objects_additionally_action; + QAction* deselect_objects_action; + QAction* sort_manual_action; + + SymbolToolTip* tooltip; + + QScopedPointer hidden_symbol_decoration; + QScopedPointer protected_symbol_decoration; +}; + +//### SymbolRenderWidget inline code ### + +inline +int SymbolRenderWidget::selectedSymbolsCount() const +{ + return (int)selected_symbols.size(); +} + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/symbol_tooltip.cpp b/src/gui/widgets/symbol_tooltip.cpp new file mode 100644 index 0000000..1717883 --- /dev/null +++ b/src/gui/widgets/symbol_tooltip.cpp @@ -0,0 +1,243 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "symbol_tooltip.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/symbols/symbol.h" + +// IWYU pragma: no_forward_declare QWidget + + +namespace OpenOrienteering { + +SymbolToolTip::SymbolToolTip(QWidget* parent, QShortcut* shortcut) +: QWidget{ parent } +, shortcut{ shortcut } +, symbol{ nullptr } +, description_shown{ false } +{ + setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint); + setAttribute(Qt::WA_OpaquePaintEvent); + + QPalette text_palette; + text_palette.setColor(QPalette::Window, text_palette.color(QPalette::Base)); + text_palette.setColor(QPalette::WindowText, text_palette.color(QPalette::Text)); + setPalette(text_palette); + + name_label = new QLabel(); + description_label = new QLabel(); + description_label->setWordWrap(true); + description_label->hide(); + + auto layout = new QVBoxLayout(); + QStyleOption style_option; + layout->setContentsMargins( + style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutTopMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2 + ); + layout->addWidget(name_label); + layout->addWidget(description_label); + setLayout(layout); + + tooltip_timer.setSingleShot(true); + connect(&tooltip_timer, &QTimer::timeout, this, &QWidget::show); + + if (shortcut) + { + shortcut->setEnabled(false); + connect(shortcut, &QShortcut::activated, this, &SymbolToolTip::showDescription); + } +} + +void SymbolToolTip::showDescription() +{ + if (symbol && !description_shown && isVisible()) + { + description_label->show(); + adjustSize(); + adjustPosition(false); + description_shown = true; + } + + if (shortcut) + shortcut->setEnabled(false); +} + +void SymbolToolTip::reset() +{ + symbol = nullptr; + tooltip_timer.stop(); + hide(); + description_label->hide(); + description_shown = false; + + if (shortcut) + shortcut->setEnabled(false); +} + +void SymbolToolTip::enterEvent(QEvent* event) +{ + Q_UNUSED(event); + hide(); +} + +void SymbolToolTip::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + + QPainter painter(this); + + painter.setBrush(palette().color(QPalette::Window)); + painter.setPen(palette().color(QPalette::WindowText)); + + QRect rect(0, 0, width() - 1, height() - 1); + painter.drawRect(rect); + + painter.end(); +} + +void SymbolToolTip::adjustPosition(bool mobile_mode) +{ + auto size = this->size(); + auto desktop = QApplication::desktop()->screenGeometry(QCursor::pos()); + + const int margin = 3; + bool has_room_to_left = (icon_rect.left() - size.width() - margin >= desktop.left()); + bool has_room_to_right = (icon_rect.right() + size.width() + margin <= desktop.right()); + bool has_room_above = (icon_rect.top() - size.height() - margin >= desktop.top()); + bool has_room_below = (icon_rect.bottom() + size.height() + margin <= desktop.bottom()); + if (!has_room_above && !has_room_below && !has_room_to_left && !has_room_to_right) { + return; + } + + if (mobile_mode) + { + // Change precedence. + if (has_room_above) + { + has_room_below = false; + } + else if (has_room_to_left) + { + has_room_below = false; + has_room_to_right = false; + } + else if (has_room_to_right) + { + has_room_below = false; + } + } + + int x = 0; + int y = 0; + + if (has_room_below || has_room_above) + { + y = has_room_below ? icon_rect.bottom() + margin : icon_rect.top() - size.height() - margin; + x = qMin(qMax(desktop.left() + margin, icon_rect.center().x() - size.width() / 2), desktop.right() - size.width() - margin); + } else { + x = has_room_to_right ? icon_rect.right() + margin : icon_rect.left() - size.width() - margin; + y = qMin(qMax(desktop.top() + margin, icon_rect.center().y() - size.height() / 2), desktop.bottom() - size.height() - margin); + } + + move(QPoint(x, y)); +} + +void SymbolToolTip::scheduleShow(const Symbol* symbol, const Map* map, QRect icon_rect, bool mobile_mode) +{ + this->icon_rect = icon_rect; + this->symbol = symbol; + + Q_ASSERT(map); + name_label->setText(symbol->getNumberAsString() + + QLatin1String(" ") + + map->translate(symbol->getName()) + + QLatin1String("")); + + auto help_text = map->translate(symbol->getDescription()); + if (help_text.isEmpty()) + { + help_text = tr("No description!"); + } + else + { + help_text.replace(QLatin1Char('\n'), QStringLiteral("
    ")); + help_text.remove(QLatin1Char('\r')); + } + description_label->setText(help_text); + description_label->hide(); + description_shown = false; + shortcut->setEnabled(isVisible()); + + adjustSize(); + adjustPosition(mobile_mode); + + static const int delay = 150; + tooltip_timer.start(delay); +} + +void SymbolToolTip::showEvent(QShowEvent* event) +{ + if (shortcut && !description_shown && !event->spontaneous()) + shortcut->setEnabled(true); + + QWidget::showEvent(event); +} + +void SymbolToolTip::hideEvent(QHideEvent* event) +{ + if (!event->spontaneous()) + reset(); + + QWidget::hideEvent(event); +} + +const Symbol* SymbolToolTip::getSymbol() const +{ + return symbol; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/symbol_tooltip.h b/src/gui/widgets/symbol_tooltip.h new file mode 100644 index 0000000..9d19039 --- /dev/null +++ b/src/gui/widgets/symbol_tooltip.h @@ -0,0 +1,148 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_TOOLTIP_H +#define OPENORIENTEERING_SYMBOL_TOOLTIP_H + +#include +#include +#include +#include + +class QEvent; +class QHideEvent; +class QLabel; +class QPaintEvent; +class QShortcut; +class QShowEvent; +// IWYU pragma: no_forward_declare QWidget + +namespace OpenOrienteering { + +class Map; +class Symbol; + + +/** + * @brief A SymbolToolTip displays the name and description of a symbol. + * + * The tooltip is normally not displayed immediately but scheduled to be + * displayed after a short delay, by a call to scheduleShow(). Initially, the + * tooltip displays the symbol's number and name only. The description can be + * made visible by a call to showDescription(). + * + * A QShortcut may be set which will trigger the display of the description + * while the tooltip is already visible. The shortcut does not need to be + * unique: SymbolToolTip enables the shortcut only when the tooltip is visible + * and the description is not yet shown. SymbolToolTip does not take ownership + * of the shortcut. + * + * To permanently hide a SymbolToolTip it is not enough to call hide() because + * the widget may be hidden but scheduled to show up. Call reset() to hide the + * widget and stop the timer if scheduled. + * + * SymbolToolTip does not use the standard tooltip colors but the text editor + * colors. + */ +class SymbolToolTip : public QWidget +{ +Q_OBJECT +public: + /** + * Constructs a new SymbolToolTip. + * The optional shortcut will trigger the description to be shown. + */ + SymbolToolTip(QWidget* parent = nullptr, QShortcut* shortcut = nullptr); + + /** + * Schedules the tooltip for the symbol to be shown close to + * but not covering the region given by rect. + * + * In mobile mode, tooltip placement relative to the symbol icon is tried + * in the order: above, left, right, below. + * Otherwise, placement is tried in the order: below, above, right, left. + * (desktop mode). + */ + void scheduleShow(const Symbol* symbol, const Map* map, QRect rect, bool mobile_mode); + + /** + * Resets the tooltip. + * It hides the widget, stops the timer and disables the shortcut. + */ + void reset(); + + /** + * Returns the symbol for which the tooltip is currently shown or + * scheduled to be shown, or nullptr. + */ + const Symbol* getSymbol() const; + +public slots: + /** + * Expands the symbol's description in the tooltip. + * Disables the shortcut. + */ + void showDescription(); + +protected: + /** + * Hides the tooltip when the mouse enters it. + * This is neccessary to let the user select another symbol. + */ + void enterEvent(QEvent* event) override; + + /** + * Enables the shortcut when the tooltip is shown. + */ + void showEvent(QShowEvent* event) override; + + /** + * Resets the tooltip's state on hiding the tooltip. + * Disables the shortcut. + */ + void hideEvent(QHideEvent* event) override; + + /** + * Draws the tooltip's background. + */ + void paintEvent(QPaintEvent* event) override; + +private: + /** + * Moves the tooltip so that it is nicely placed close the region given + * to scheduleShow(). + */ + void adjustPosition(bool mobile_mode); + + QTimer tooltip_timer; /// The timer which triggers the delayed showing. + QShortcut* shortcut; /// An optional shortcut for showing the description. + const Symbol* symbol; /// The current symbol, or nullptr. + QLabel* name_label; /// The label displaying the symbol's name. + QLabel* description_label; /// The label displaying the symbol's description. + QRect icon_rect; /// The region to be considered when determining position. + bool description_shown; /// If true, the full description is visible. + +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/symbol_widget.cpp b/src/gui/widgets/symbol_widget.cpp new file mode 100644 index 0000000..2b4f0d0 --- /dev/null +++ b/src/gui/widgets/symbol_widget.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "symbol_widget.h" + +#include +#include +#include + +#include "symbol_render_widget.h" + +// IWYU pragma: no_forward_declare QContextMenuEvent +// IWYU pragma: no_forward_declare QWidget + + +namespace OpenOrienteering { + +SymbolWidget::SymbolWidget(Map* map, bool mobile_mode, QWidget* parent) +: QScrollArea(parent) +{ + render_widget = new SymbolRenderWidget(map, mobile_mode, this); + setWidget(render_widget); + + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setWidgetResizable(true); + setBackgroundRole(QPalette::Base); + + // Relay render_widget signals + connect(render_widget, &SymbolRenderWidget::selectedSymbolsChanged, this, &SymbolWidget::selectedSymbolsChanged); + connect(render_widget, &SymbolRenderWidget::fillBorderClicked, this, &SymbolWidget::fillBorderClicked); + connect(render_widget, &SymbolRenderWidget::switchSymbolClicked, this, &SymbolWidget::switchSymbolClicked); + connect(render_widget, &SymbolRenderWidget::selectObjectsClicked, this, &SymbolWidget::selectObjectsClicked); + connect(render_widget, &SymbolRenderWidget::deselectObjectsClicked, this, &SymbolWidget::deselectObjectsClicked); +} + +SymbolWidget::~SymbolWidget() +{ + ; // nothing +} + +const Symbol* SymbolWidget::getSingleSelectedSymbol() const +{ + return static_cast(render_widget)->singleSelectedSymbol(); +} + +Symbol* SymbolWidget::getSingleSelectedSymbol() +{ + return render_widget->singleSelectedSymbol(); +} + +int SymbolWidget::selectedSymbolsCount() const +{ + return render_widget->selectedSymbolsCount(); +} + +bool SymbolWidget::isSymbolSelected(const Symbol* symbol) const +{ + return render_widget->isSymbolSelected(symbol); +} + +void SymbolWidget::selectSingleSymbol(const Symbol* symbol) +{ + render_widget->selectSingleSymbol(symbol); +} + +void SymbolWidget::contextMenuEvent(QContextMenuEvent* event) +{ + render_widget->showContextMenu(event->globalPos()); + event->accept(); +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/symbol_widget.h b/src/gui/widgets/symbol_widget.h new file mode 100644 index 0000000..02281f8 --- /dev/null +++ b/src/gui/widgets/symbol_widget.h @@ -0,0 +1,136 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2014 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_SYMBOL_WIDGET_H +#define OPENORIENTEERING_SYMBOL_WIDGET_H + +#include +#include + +class QContextMenuEvent; +class QWidget; + +namespace OpenOrienteering { + +class Map; +class Symbol; +class SymbolRenderWidget; + + +/** + * @brief Shows all symbols from a map in a scrollable widget. + * + * SymbolWidget shows all symbols from a map's symbol set. + * It lets the user select symbols and perform actions on symbols. + * + * The implementation is based on SymbolRenderWidget and QScrollArea. + * Normally it is used inside a dock widget. + */ +class SymbolWidget : public QScrollArea +{ +Q_OBJECT +public: + /** + * @brief Constructs a new SymbolWidget. + * @param map The map which provides the symbols. Must not be nullptr. + * @param mobile_mode If true, enables a special mode for mobile devices. + * @param parent The parent QWidget. + */ + SymbolWidget(Map* map, bool mobile_mode, QWidget* parent = nullptr); + + /** + * @brief Destroys the SymbolWidget. + */ + ~SymbolWidget() override; + + /** + * @brief If exactly one symbol is selected, returns this symbol. + * + * Otherwise returns nullptr. + */ + const Symbol* getSingleSelectedSymbol() const; + + /** + * @brief If exactly one symbol is selected, returns this symbol. + * + * Otherwise returns nullptr. + */ + Symbol* getSingleSelectedSymbol(); + + /** + * @brief Returns the number of selected symbols. + */ + int selectedSymbolsCount() const; + + /** + * @brief Checks if the symbol is selected. + */ + bool isSymbolSelected(const Symbol* symbol) const; + + /** + * @brief Selects the symbol exclusively, deselecting all other symbols. + */ + void selectSingleSymbol(const Symbol* symbol); + +signals: + /** + * @brief The collection of selected symbols changed. + */ + void selectedSymbolsChanged(); + + /** + * @brief The user triggered "Switch symbol". + * @todo Merge with/Reuse corresponding action in MapEditorController. + */ + void switchSymbolClicked(); + + /** + * @brief The user triggered "Fill/Create border". + * @todo Merge with/Reuse corresponding action in MapEditorController. + */ + void fillBorderClicked(); + + /** + * @brief The user triggered selecting objects with the active symbol. + * @param select_exclusively If true, an existing selection is replaced, + * otherwise it is extend. + */ + void selectObjectsClicked(bool select_exclusively); + + /** + * @brief The user triggered deselecting objects with the active symbol. + */ + void deselectObjectsClicked(); + +protected: + /** + * @brief Receives context menu events and opens the context menu. + */ + void contextMenuEvent(QContextMenuEvent* event) override; + +private: + SymbolRenderWidget* render_widget; +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/tag_select_widget.cpp b/src/gui/widgets/tag_select_widget.cpp new file mode 100644 index 0000000..c5fa32e --- /dev/null +++ b/src/gui/widgets/tag_select_widget.cpp @@ -0,0 +1,379 @@ +/* + * Copyright 2016 Mitchell Krome + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "tag_select_widget.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/objects/object_query.h" +#include "gui/util_gui.h" +#include "gui/widgets/segmented_button_layout.h" + + +namespace OpenOrienteering { + +// ### TagSelectWidget ### + +TagSelectWidget::TagSelectWidget(QWidget* parent) +: QTableWidget{1, 4, parent} +{ + setWhatsThis(Util::makeWhatThis("find_objects.html#query-editor")); + + setEditTriggers(QAbstractItemView::AllEditTriggers); + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::SingleSelection); + setHorizontalHeaderLabels(QStringList() << tr("Relation") << tr("Key") << tr("Comparison") << tr("Value")); + verticalHeader()->setVisible(false); + + auto header_view = horizontalHeader(); + header_view->setSectionsClickable(false); + header_view->setSectionResizeMode(0, QHeaderView::Fixed); + header_view->setSectionResizeMode(1, QHeaderView::Stretch); + header_view->setSectionResizeMode(2, QHeaderView::Fixed); + header_view->setSectionResizeMode(3, QHeaderView::Stretch); + header_view->resizeSections(QHeaderView::ResizeToContents); + setMinimumWidth(10 + + header_view->sectionSize(0) + + header_view->sectionSize(1) + + header_view->sectionSize(2) + + header_view->sectionSize(3)); + + addRowItems(0); + + connect(this, &QTableWidget::cellChanged, this, &TagSelectWidget::onCellChanged); +} + + +TagSelectWidget::~TagSelectWidget() = default; + + + +QWidget* TagSelectWidget::makeButtons(QWidget* parent) +{ + auto add_button = newToolButton(QIcon(QString::fromLatin1(":/images/plus.png")), tr("Add Row")); + auto delete_button = newToolButton(QIcon(QString::fromLatin1(":/images/minus.png")), tr("Remove Row")); + + auto add_remove_layout = new SegmentedButtonLayout(); + add_remove_layout->addWidget(add_button); + add_remove_layout->addWidget(delete_button); + + auto move_up_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-up.png")), tr("Move Up")); + move_up_button->setAutoRepeat(true); + auto move_down_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-down.png")), tr("Move Down")); + move_down_button->setAutoRepeat(true); + + auto up_down_layout = new SegmentedButtonLayout(); + up_down_layout->addWidget(move_up_button); + up_down_layout->addWidget(move_down_button); + + auto list_buttons_layout = new QHBoxLayout(); + list_buttons_layout->setContentsMargins(0,0,0,0); + list_buttons_layout->addLayout(add_remove_layout); + list_buttons_layout->addLayout(up_down_layout); + + auto list_buttons_group = new QWidget(parent); + list_buttons_group->setLayout(list_buttons_layout); + + // Connections + connect(add_button, &QAbstractButton::clicked, this, &TagSelectWidget::addRow); + connect(delete_button, &QAbstractButton::clicked, this, &TagSelectWidget::deleteRow); + connect(move_up_button, &QAbstractButton::clicked, this, &TagSelectWidget::moveRowUp); + connect(move_down_button, &QAbstractButton::clicked, this, &TagSelectWidget::moveRowDown); + + return list_buttons_group; +} + + + +QToolButton* TagSelectWidget::newToolButton(const QIcon& icon, const QString& text) +{ + auto button = new QToolButton(); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setToolTip(text); + button->setIcon(icon); + button->setText(text); + button->setWhatsThis(Util::makeWhatThis("find_objects.html#query-editor")); + return button; +} + + + +void TagSelectWidget::showEvent(QShowEvent* event) +{ + if (!event->spontaneous()) + { + auto last_row = rowCount() - 1; + setCurrentCell(last_row, 1); + if (!currentItem()->text().isEmpty()) + setCurrentCell(last_row, 3); + editItem(currentItem()); + } +} + + + +void TagSelectWidget::addRowItems(int row) +{ + auto item = new QTableWidgetItem(); + setItem(row, 1, item); + item = new QTableWidgetItem(); + setItem(row, 3, item); + + auto compare_op = new QComboBox(); + for (auto op : { ObjectQuery::OperatorIs, ObjectQuery::OperatorIsNot, ObjectQuery::OperatorContains }) + compare_op->addItem(ObjectQuery::labelFor(op), QVariant::fromValue(op)); + setCellWidget(row, 2, compare_op); + + if (row == 0) + { + // The first row doesn't use a logical operator + item = new QTableWidgetItem(); + item->setFlags(Qt::NoItemFlags); + item->setBackground(QBrush(QGuiApplication::palette().window())); + setItem(row, 0, item); + } + else + { + auto logical_op = new QComboBox(); + auto and_label = QString { QLatin1String(" ") + ObjectQuery::labelFor(ObjectQuery::OperatorAnd) }; + logical_op->addItem(and_label, QVariant::fromValue(ObjectQuery::OperatorAnd)); + logical_op->addItem(ObjectQuery::labelFor(ObjectQuery::OperatorOr), QVariant::fromValue(ObjectQuery::OperatorOr)); + setCellWidget(row, 0, logical_op); + } +} + + + +void TagSelectWidget::onCellChanged(int row, int column) +{ + switch(column) + { + case 1: + case 3: + { + auto item = this->item(row, column); + item->setText(item->text().trimmed()); + break; + } + default: + ; // nothing + } +} + + + +void TagSelectWidget::addRow() +{ + int row = currentRow() + 1; + + // When nothing is selected, add to the end + if (row == 0) + row = rowCount(); + + insertRow(row); + addRowItems(row); + + // Move the selection to the new row + int col = currentColumn(); + setCurrentCell(row, col); +} + + +void TagSelectWidget::deleteRow() +{ + // Always need one row + if (rowCount() == 1) + return; + + int row = currentRow(); + + // When nothing is selected, delete from the end + if (row == -1) + row = rowCount() - 1; + + removeRow(row); + + // If we delete first row, need to fix logical operator + if (row == 0) + { + removeCellWidget(row, 0); + auto item = new QTableWidgetItem(); + item->setFlags(Qt::NoItemFlags); + item->setBackground(QBrush(QGuiApplication::palette().window())); + setItem(row, 0, item); + } +} + + +void TagSelectWidget::moveRow(bool up) +{ + int row = currentRow(); + int max_row = rowCount(); + + // When nothing is selected, move the last row + if (row == -1) + row = max_row - 1; + + // Cant move first row up or last row down + if ((up && row < 1) || (!up && row == max_row - 1)) + return; + + // Moving the current row down is just moving the row below up.. + if (!up) + row += 1; + + // Col 1, 3 are items + auto top_item = takeItem(row - 1, 1); + auto bottom_item = takeItem(row, 1); + setItem(row, 1, top_item); + setItem(row - 1, 1, bottom_item); + top_item = takeItem(row - 1, 3); + bottom_item = takeItem(row, 3); + setItem(row, 3, top_item); + setItem(row - 1, 3, bottom_item); + + // Col 2 is comparison combobox + auto top_combo = qobject_cast(cellWidget(row - 1, 2)); + auto bottom_combo = qobject_cast(cellWidget(row, 2)); + auto top_selection = top_combo->currentIndex(); + auto bottom_selection = bottom_combo->currentIndex(); + top_combo->setCurrentIndex(bottom_selection); + bottom_combo->setCurrentIndex(top_selection); + + // Ignore the swap on the first row because there is nothing to swap + if (row - 1 != 0 ) + { + // Col 0 is relation combobox + top_combo = qobject_cast(cellWidget(row - 1, 0)); + bottom_combo = qobject_cast(cellWidget(row, 0)); + top_selection = top_combo->currentIndex(); + bottom_selection = bottom_combo->currentIndex(); + top_combo->setCurrentIndex(bottom_selection); + bottom_combo->setCurrentIndex(top_selection); + } + + // Keep the moved row selected + int col = currentColumn(); + // For the down case we already adjusted the row + if (up) + row -= 1; + setCurrentCell(row, col); +} + + +void TagSelectWidget::moveRowDown() +{ + moveRow(false); +} + + +void TagSelectWidget::moveRowUp() +{ + moveRow(true); +} + + + +ObjectQuery TagSelectWidget::makeQuery() const +{ + // If there is at least one OR, query will become an OR expression. + ObjectQuery query; + // AND has precedence over OR, so its terms are collected separately. + ObjectQuery and_expression; + + for (int row = 0; row < rowCount(); ++row) + { + auto key = item(row, 1)->text(); + auto compare_op = qobject_cast(cellWidget(row, 2))->currentData().value(); + auto value = item(row, 3)->text(); + + auto comparison = ObjectQuery(key, compare_op, value); + if (row == 0) + { + // The first term at all + and_expression = std::move(comparison); + } + else + { + auto logical_op = qobject_cast(cellWidget(row, 0))->currentData().value(); + if (logical_op == ObjectQuery::OperatorAnd) + { + // Add another term to the AND expression + and_expression = ObjectQuery(std::move(comparison), ObjectQuery::OperatorAnd, std::move(and_expression)); + } + else if (logical_op == ObjectQuery::OperatorOr) + { + if (query) + // Add another term to the OR expression + query = ObjectQuery(std::move(and_expression), ObjectQuery::OperatorOr, std::move(query)); + else + // The first term of an OR expression + query = std::move(and_expression); + + // The first term of the next AND expression + and_expression = std::move(comparison); + } + else + { + Q_UNREACHABLE(); + } + } + + // The validness of and_expression is determined by the current line. + if (!and_expression) + break; + } + + if (query && and_expression) + { + // Add the last AND expression to the OR expression + query = ObjectQuery(std::move(and_expression), ObjectQuery::OperatorOr, std::move(query)); + } + else + { + // There was no OR, or the last visited row was not a valid expression. + query = std::move(and_expression); + } + + return query; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/tag_select_widget.h b/src/gui/widgets/tag_select_widget.h new file mode 100644 index 0000000..d1f76e5 --- /dev/null +++ b/src/gui/widgets/tag_select_widget.h @@ -0,0 +1,83 @@ +/* + * Copyright 2016 Mitchell Krome + * Copyright 2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_TAG_SELECT_WIDGET_H +#define OPENORIENTEERING_TAG_SELECT_WIDGET_H + +#include +#include +#include +#include +#include + +class QShowEvent; +class QToolButton; +class QWidget; + +namespace OpenOrienteering { + +class ObjectQuery; + + +/** + * This widget allows the user to make selections based on an objects tags. + */ +class TagSelectWidget : public QTableWidget +{ +Q_OBJECT +public: + TagSelectWidget(QWidget* parent = nullptr); + ~TagSelectWidget() override; + + QWidget* makeButtons(QWidget* parent = nullptr); + + /** + * Builds a query based on the current state of the query table. + * + * Returns an invalid query on error. + */ + ObjectQuery makeQuery() const; + +protected: + void showEvent(QShowEvent* event) override; + +private: + /** + * Returns a new QToolButton with a unified appearance. + */ + QToolButton* newToolButton(const QIcon& icon, const QString& text); + + void addRow(); + void deleteRow(); + void moveRow(bool up); + void moveRowDown(); + void moveRowUp(); + + void addRowItems(int row); + void onCellChanged(int row, int column); + + Q_DISABLE_COPY(TagSelectWidget) +}; + + +} // namespace OpenOrienteering + +#endif diff --git a/src/gui/widgets/tags_widget.cpp b/src/gui/widgets/tags_widget.cpp new file mode 100644 index 0000000..04897cb --- /dev/null +++ b/src/gui/widgets/tags_widget.cpp @@ -0,0 +1,269 @@ +/* + * Copyright 2013-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "tags_widget.h" + +#include +#include +#include +#include +#include +#include + +#include "core/map.h" +#include "core/objects/object.h" +#include "gui/main_window.h" +#include "gui/util_gui.h" +#include "gui/map/map_editor.h" +#include "undo/object_undo.h" + + +namespace OpenOrienteering { + +TagsWidget::TagsWidget(Map* map, MapView* main_view, MapEditorController* controller, QWidget* parent) + : QWidget(parent), + map(map), + main_view(main_view), + controller(controller) +{ + react_to_changes = false; + + auto layout = new QVBoxLayout(); + layout->setMargin(0); + + tags_table = new QTableWidget(1, 2); + tags_table->setEditTriggers(QAbstractItemView::AllEditTriggers); + tags_table->setSelectionBehavior(QAbstractItemView::SelectRows); + tags_table->setSelectionMode(QAbstractItemView::SingleSelection); + tags_table->setHorizontalHeaderLabels(QStringList() << tr("Key") << tr("Value")); + tags_table->verticalHeader()->setVisible(false); + + auto header_view = tags_table->horizontalHeader(); + header_view->setSectionResizeMode(0, QHeaderView::Stretch); + header_view->setSectionResizeMode(1, QHeaderView::Stretch); + header_view->setSectionsClickable(false); + + layout->addWidget(tags_table); + + auto help_button = newToolButton(QIcon(QString::fromLatin1(":/images/help.png")), tr("Help")); + help_button->setAutoRaise(true); + + auto all_buttons_layout = new QHBoxLayout(); + QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget); + all_buttons_layout->setContentsMargins( + style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2 + ); + all_buttons_layout->addWidget(new QLabel(QString::fromLatin1(" ")), 1); + all_buttons_layout->addWidget(help_button); + + layout->addLayout(all_buttons_layout); + + setLayout(layout); + + connect(tags_table, &QTableWidget::cellChanged, this, &TagsWidget::cellChange); + + connect(map, &Map::objectSelectionChanged, this, &TagsWidget::objectTagsChanged); + connect(map, &Map::selectedObjectEdited, this, &TagsWidget::objectTagsChanged); + + react_to_changes = true; + objectTagsChanged(); +} + +TagsWidget::~TagsWidget() = default; + + + +QToolButton* TagsWidget::newToolButton(const QIcon& icon, const QString& text) +{ + auto button = new QToolButton(); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setToolTip(text); + button->setIcon(icon); + button->setText(text); + return button; +} + +// slot +void TagsWidget::showHelp() +{ + Util::showHelp(controller->getWindow(), "tag_editor.html"); +} + +void TagsWidget::setupLastRow() +{ + const int row = tags_table->rowCount() - 1; + tags_table->setItem(row, 0, new QTableWidgetItem()); + auto value_item = new QTableWidgetItem(); + value_item->setFlags(value_item->flags() & ~Qt::ItemIsEnabled); + tags_table->setItem(row, 1, value_item); +} + +void TagsWidget::createUndoStep(Object* object) +{ + Q_ASSERT(object); + + auto undo_step = new ObjectTagsUndoStep(map); + undo_step->addObject(map->getCurrentPart()->findObjectIndex(object)); + map->push(undo_step); +} + +// slot +void TagsWidget::objectTagsChanged() +{ + if (!react_to_changes) + return; + + react_to_changes = false; + + int row = 0; + const Object* object = map->getFirstSelectedObject(); + if (object) + { + const Object::Tags& tags = object->tags(); + tags_table->clearContents(); + tags_table->setRowCount(tags.size() + 1); + for (Object::Tags::const_iterator tag = tags.constBegin(), end = tags.constEnd(); tag != end; ++tag) + { + tags_table->setItem(row, 0, new QTableWidgetItem(tag.key())); + tags_table->item(row, 0)->setData(Qt::UserRole, tag.key()); + tags_table->setItem(row, 1, new QTableWidgetItem(tag.value())); + ++row; + } + tags_table->sortItems(0); + setupLastRow(); + } + else + { + tags_table->setRowCount(0); + } + + react_to_changes = true; +} + +// slot +void TagsWidget::cellChange(int row, int column) +{ + auto object = map->getFirstSelectedObject(); + if (!react_to_changes || !object) + return; + + react_to_changes = false; + const QString key = tags_table->item(row, 0)->text().trimmed(); + const QString value = tags_table->item(row, 1)->text(); + + if (column == 1 && key.isEmpty()) + { + // Shall not happen + qWarning("Empty key for modified tag value!"); + } + else if (column == 1) + { + // Value edited: update the tag + createUndoStep(object); + object->setTag(key, value); + if (row + 1 == tags_table->rowCount()) + { + // Append new row, jump to key + tags_table->setRowCount(row + 2); + setupLastRow(); + } + + if (!tags_table->item(row + 1, 1)->flags().testFlag(Qt::ItemIsEnabled)) + { + // Jump to key + tags_table->setCurrentCell(row + 1, 0); + } + else + { + // Jump to value + tags_table->setCurrentCell(row + 1, 1); + } + } + else if (column == 0) + { + const QString old_key = tags_table->item(row, 0)->data(Qt::UserRole).toString(); + if (old_key == key && !key.isEmpty()) + { + // Key edited but not changed: do nothing + } + else if (!old_key.isEmpty() && key.isEmpty()) + { + // Key deleted: remove tag + createUndoStep(object); + object->removeTag(old_key); + tags_table->removeRow(row); + if (row > 0) + { + // Jump to previous row + tags_table->setCurrentCell(row - 1, 1); + } + else + { + // Reset current row + tags_table->item(row, 1)->setText({}); + auto value_item = tags_table->item(row, 1); + value_item->setFlags(value_item->flags() & ~Qt::ItemIsEnabled); + } + } + else if (!key.isEmpty()) + { + // Key edited: update the tags + if (object->tags().contains(key)) + { + QMessageBox::critical(window(), tr("Key exists"), + tr("The key \"%1\" already exists and must not be used twice.").arg(key) + ); + tags_table->item(row, column)->setText(old_key); + tags_table->setCurrentCell(row, column); + } + else + { + if (value.isEmpty() && old_key.isEmpty()) + { + // New key, empty value - dont insert yet + tags_table->item(row, 0)->setData(Qt::UserRole, QLatin1String("ABOUT TO INSERT NEW VALUE")); + tags_table->setCurrentCell(row, 1); + } + else + { + // New key with value - update tags + createUndoStep(object); + object->removeTag(old_key); + object->setTag(key, value); + } + tags_table->item(row, 0)->setData(Qt::UserRole, key); + if (tags_table->rowCount() == row + 1) + { + auto value_item = tags_table->item(row, 1); + value_item->setFlags(value_item->flags() | Qt::ItemIsEnabled); + } + tags_table->setCurrentCell(row, 1); + } + } + } + + react_to_changes = true; +} + + +} // namespace OpenOrienteering diff --git a/src/gui/widgets/tags_widget.h b/src/gui/widgets/tags_widget.h new file mode 100644 index 0000000..f96f142 --- /dev/null +++ b/src/gui/widgets/tags_widget.h @@ -0,0 +1,103 @@ +/* + * Copyright 2013 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#ifndef OPENORIENTEERING_TAGS_WIDGET_H +#define OPENORIENTEERING_TAGS_WIDGET_H + +#include +#include +#include + +class QIcon; +class QTableWidget; +class QToolButton; + +namespace OpenOrienteering { + +class Map; +class MapEditorController; +class MapView; +class Object; + + +/** + * This widget allows for display and editing of tags, i.e. key-value pairs. + */ +class TagsWidget : public QWidget +{ +Q_OBJECT +public: + /** Constructs a new tags widget for the given map. */ + explicit TagsWidget(Map* map, MapView* main_view, MapEditorController* controller, QWidget* parent = nullptr); + + /** Destroys the widget. */ + ~TagsWidget() override; + +protected slots: + /** + * Updates the widget from the current object's tags. + * + * Does nothing if react_to_changes is set to false. + */ + void objectTagsChanged(); + + /** + * Updates the current object's tags from the widget. + * + * Does nothing if react_to_changes is set to false. + */ + void cellChange(int row, int column); + + /** + * Opens the help page for this widget. + */ + void showHelp(); + +protected: + /** + * Returns a new QToolButton with a unified appearance. + */ + QToolButton* newToolButton(const QIcon& icon, const QString& text); + + /** + * Sets up the last row: blank cells, right one not editable. + */ + void setupLastRow(); + + /** + * Creates and registers an undo step for the given object. + * + * @param object must not be null. + */ + void createUndoStep(Object* object); + +private: + Map* map; + MapView* main_view; + MapEditorController* controller; + bool react_to_changes; + + QTableWidget* tags_table; +}; + + +} // namespace OpenOrienteering + +#endif // TAGS_WIDGET_H diff --git a/src/gui/widgets/template_list_widget.cpp b/src/gui/widgets/template_list_widget.cpp new file mode 100644 index 0000000..56a82c2 --- /dev/null +++ b/src/gui/widgets/template_list_widget.cpp @@ -0,0 +1,1340 @@ +/* + * Copyright 2012, 2013 Thomas Schöps + * Copyright 2012-2017 Kai Pastor + * + * This file is part of OpenOrienteering. + * + * OpenOrienteering is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenOrienteering is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenOrienteering. If not, see . + */ + + +#include "template_list_widget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "core/georeferencing.h" +#include "core/map.h" +#include "core/objects/object.h" +#include "gui/file_dialog.h" +#include "gui/main_window.h" +#include "gui/util_gui.h" +#include "gui/map/map_editor.h" +#include "gui/map/map_widget.h" +#include "gui/widgets/segmented_button_layout.h" +#include "templates/template.h" +#include "templates/template_adjust.h" +#include "templates/template_map.h" +#include "templates/template_tool_move.h" +#include "util/item_delegates.h" + + +namespace OpenOrienteering { + +// ### ApplyTemplateTransform ### + +/** + * A local functor which is used when importing map templates into the current map. + */ +struct ApplyTemplateTransform +{ + const TemplateTransform& transform; + + void operator()(Object* object) const + { + object->rotate(transform.template_rotation); + object->scale(transform.template_scale_x, transform.template_scale_y); + object->move(transform.template_x, transform.template_y); + } +}; + + + +// ### TemplateListWidget ### + +// Template grouping implementation is incomplete +#define NO_TEMPLATE_GROUP_SUPPORT + +TemplateListWidget::TemplateListWidget(Map* map, MapView* main_view, MapEditorController* controller, QWidget* parent) +: QWidget(parent) +, map(map) +, main_view(main_view) +, controller(controller) +, mobile_mode(controller->isInMobileMode()) +, name_column(3) +{ + Q_ASSERT(main_view); + Q_ASSERT(controller); + + setWhatsThis(Util::makeWhatThis("templates.html#setup")); + + QStyleOption style_option(QStyleOption::Version, QStyleOption::SO_DockWidget); + + // Wrap the checkbox in a widget and layout to force a margin. + auto top_bar_widget = new QWidget(); + auto top_bar_layout = new QHBoxLayout(top_bar_widget); + + // Reuse the translation from MapEditorController action. + all_hidden_check = new QCheckBox(::OpenOrienteering::MapEditorController::tr("Hide all templates")); + top_bar_layout->addWidget(all_hidden_check); + + if (mobile_mode) + { + auto close_action = new QAction(QIcon(QString::fromLatin1(":/images/close.png")), ::OpenOrienteering::MainWindow::tr("Close"), this); + connect(close_action, &QAction::triggered, this, &TemplateListWidget::closeClicked ); + + auto close_button = new QToolButton(); + close_button->setDefaultAction(close_action); + close_button->setAutoRaise(true); + + top_bar_layout->addWidget(close_button); + } + + top_bar_layout->setContentsMargins( + style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutTopMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, + 0 // Covered by the main layout's spacing. + ); + + // Template table + template_table = new QTableWidget(map->getNumTemplates() + 1, 4); + QScroller::grabGesture(template_table->viewport(), QScroller::TouchGesture); + template_table->installEventFilter(this); + template_table->setEditTriggers(QAbstractItemView::AllEditTriggers); + template_table->setSelectionBehavior(QAbstractItemView::SelectRows); + template_table->setSelectionMode(QAbstractItemView::SingleSelection); + template_table->verticalHeader()->setVisible(false); +#ifdef NO_TEMPLATE_GROUP_SUPPORT + // Template grouping is not yet implemented. + template_table->hideColumn(2); +#endif + + auto header_view = template_table->horizontalHeader(); + if (mobile_mode) + { + header_view->setVisible(false); + template_table->setShowGrid(false); + template_table->hideColumn(3); + name_column = 0; + } + else + { + template_table->setHorizontalHeaderLabels(QStringList() << QString{} << tr("Opacity") << tr("Group") << tr("Filename")); + template_table->horizontalHeaderItem(0)->setData(Qt::ToolTipRole, tr("Show")); + + header_view->setSectionResizeMode(0, QHeaderView::Fixed); + + QStyleOptionButton option; + auto geometry = style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, nullptr); + template_table->setColumnWidth(0, geometry.width() * 14 / 10); + + auto header_check_size = geometry.size(); + if (header_check_size.isValid()) + { + QCheckBox header_check; + header_check.setChecked(true); + header_check.setEnabled(false); + QPixmap pixmap(header_check_size); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + QStyleOptionViewItem option; + option.rect = { {0, 0}, geometry.size() }; + style()->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &option, &painter, nullptr); + painter.end(); + template_table->horizontalHeaderItem(0)->setData(Qt::DecorationRole, pixmap); + } + } + + auto percentage_delegate = new PercentageDelegate(this, 5); + template_table->setItemDelegateForColumn(1, percentage_delegate); + + for (int i = 1; i < 3; ++i) + header_view->setSectionResizeMode(i, QHeaderView::ResizeToContents); + header_view->setSectionResizeMode(name_column, QHeaderView::Stretch); + header_view->setSectionsClickable(false); + + for (int i = 0; i < map->getNumTemplates() + 1; ++i) + addRowItems(i); + + all_templates_layout = new QVBoxLayout(); + all_templates_layout->setMargin(0); + all_templates_layout->addWidget(top_bar_widget); + all_templates_layout->addWidget(template_table, 1); + + auto new_button_menu = new QMenu(this); + if (!mobile_mode) + { + new_button_menu->addAction(QIcon(QString::fromLatin1(":/images/open.png")), tr("Open..."), this, SLOT(openTemplate())); + new_button_menu->addAction(controller->getAction("reopentemplate")); + } + duplicate_action = new_button_menu->addAction(QIcon(QString::fromLatin1(":/images/tool-duplicate.png")), tr("Duplicate"), this, SLOT(duplicateTemplate())); +#if 0 + current_action = new_button_menu->addAction(tr("Sketch")); + current_action->setDisabled(true); + current_action = new_button_menu->addAction(tr("GPS")); + current_action->setDisabled(true); +#endif + + auto new_button = newToolButton(QIcon(QString::fromLatin1(":/images/plus.png")), tr("Add template...")); + new_button->setPopupMode(QToolButton::InstantPopup); + new_button->setMenu(new_button_menu); + + delete_button = newToolButton(QIcon(QString::fromLatin1(":/images/minus.png")), (tr("Remove"), tr("Close"))); /// \todo Use "Remove instead of "Close" + + auto add_remove_layout = new SegmentedButtonLayout(); + add_remove_layout->addWidget(new_button); + add_remove_layout->addWidget(delete_button); + + move_up_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-up.png")), tr("Move Up")); + move_up_button->setAutoRepeat(true); + move_down_button = newToolButton(QIcon(QString::fromLatin1(":/images/arrow-down.png")), tr("Move Down")); + move_down_button->setAutoRepeat(true); + + auto up_down_layout = new SegmentedButtonLayout(); + up_down_layout->addWidget(move_up_button); + up_down_layout->addWidget(move_down_button); + + // TODO: Fix string + georef_button = newToolButton(QIcon(QString::fromLatin1(":/images/grid.png")), tr("Georeferenced: %1").remove(QLatin1String(": %1"))); + georef_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + georef_button->setCheckable(true); + georef_button->setChecked(true); + georef_button->setEnabled(false); // TODO + move_by_hand_action = new QAction(QIcon(QString::fromLatin1(":/images/move.png")), tr("Move by hand"), this); + move_by_hand_action->setCheckable(true); + move_by_hand_button = newToolButton(move_by_hand_action->icon(), move_by_hand_action->text()); + move_by_hand_button->setDefaultAction(move_by_hand_action); + adjust_button = newToolButton(QIcon(QString::fromLatin1(":/images/georeferencing.png")), tr("Adjust...")); + adjust_button->setCheckable(true); + + auto edit_menu = new QMenu(this); + position_action = edit_menu->addAction(tr("Positioning...")); + position_action->setCheckable(true); + import_action = edit_menu->addAction(tr("Import and remove"), this, SLOT(importClicked())); + + edit_button = newToolButton(QIcon(QString::fromLatin1(":/images/settings.png")), + ::OpenOrienteering::MapEditorController::tr("&Edit").remove(QLatin1Char('&'))); + edit_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + edit_button->setPopupMode(QToolButton::InstantPopup); + edit_button->setMenu(edit_menu); + + // The buttons row layout + auto list_buttons_layout = new QHBoxLayout(); + list_buttons_layout->setContentsMargins(0,0,0,0); + list_buttons_layout->addLayout(add_remove_layout); + list_buttons_layout->addLayout(up_down_layout); + list_buttons_layout->addWidget(adjust_button); + list_buttons_layout->addWidget(move_by_hand_button); + list_buttons_layout->addWidget(georef_button); + list_buttons_layout->addWidget(edit_button); + + list_buttons_group = new QWidget(); + list_buttons_group->setLayout(list_buttons_layout); + + auto all_buttons_layout = new QHBoxLayout(); + all_buttons_layout->setContentsMargins( + style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2, + 0, // Covered by the main layout's spacing. + style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2, + style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2 + ); + all_buttons_layout->addWidget(list_buttons_group); + all_buttons_layout->addWidget(new QLabel(QString::fromLatin1(" ")), 1); + + if (!mobile_mode) + { + auto help_button = newToolButton(QIcon(QString::fromLatin1(":/images/help.png")), tr("Help")); + help_button->setAutoRaise(true); + all_buttons_layout->addWidget(help_button); + connect(help_button, &QAbstractButton::clicked, this, &TemplateListWidget::showHelp); + } + + all_templates_layout->addLayout(all_buttons_layout); + + setLayout(all_templates_layout); + + //group_button = new QPushButton(QIcon(QString::fromLatin1(":/images/group.png")), tr("(Un)group")); + /*more_button = new QToolButton(); + more_button->setText(tr("More...")); + more_button->setPopupMode(QToolButton::InstantPopup); + more_button->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); + QMenu* more_button_menu = new QMenu(more_button); + more_button_menu->addAction(QIcon(QString::fromLatin1(":/images/window-new.png")), tr("Numeric transformation window")); + more_button_menu->addAction(tr("Set transparent color...")); + more_button_menu->addAction(tr("Trace lines...")); + more_button->setMenu(more_button_menu);*/ + + updateButtons(); + + setAllTemplatesHidden(main_view->areAllTemplatesHidden()); + + // Connections + connect(all_hidden_check, &QAbstractButton::toggled, controller, &MapEditorController::hideAllTemplates); + + connect(template_table, &QTableWidget::cellChanged, this, &TemplateListWidget::cellChange); + connect(template_table->selectionModel(), &QItemSelectionModel::selectionChanged, this, &TemplateListWidget::updateButtons); + connect(template_table, &QTableWidget::cellClicked, this, &TemplateListWidget::cellClicked, Qt::QueuedConnection); + connect(template_table, &QTableWidget::cellDoubleClicked, this, &TemplateListWidget::cellDoubleClicked, Qt::QueuedConnection); + + connect(delete_button, &QAbstractButton::clicked, this, &TemplateListWidget::deleteTemplate); + connect(move_up_button, &QAbstractButton::clicked, this, &TemplateListWidget::moveTemplateUp); + connect(move_down_button, &QAbstractButton::clicked, this, &TemplateListWidget::moveTemplateDown); + + connect(move_by_hand_action, &QAction::triggered, this, &TemplateListWidget::moveByHandClicked); + connect(adjust_button, &QAbstractButton::clicked, this, &TemplateListWidget::adjustClicked); + connect(position_action, &QAction::triggered, this, &TemplateListWidget::positionClicked); + + //connect(group_button, SIGNAL(clicked(bool)), this, SLOT(groupClicked())); + //connect(more_button_menu, SIGNAL(triggered(QAction*)), this, SLOT(moreActionClicked(QAction*))); + + connect(main_view, &MapView::visibilityChanged, this, &TemplateListWidget::updateVisibility); + connect(controller, &MapEditorController::templatePositionDockWidgetClosed, this, &TemplateListWidget::templatePositionDockWidgetClosed); +} + +TemplateListWidget::~TemplateListWidget() = default; + + + +QToolButton* TemplateListWidget::newToolButton(const QIcon& icon, const QString& text) +{ + auto button = new QToolButton(); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setToolTip(text); + button->setIcon(icon); + button->setText(text); + button->setWhatsThis(Util::makeWhatThis("templates.html#setup")); + return button; +} + +// slot +void TemplateListWidget::setAllTemplatesHidden(bool value) +{ + all_hidden_check->setChecked(value); + + bool enabled = !value; + template_table->setEnabled(enabled); + list_buttons_group->setEnabled(enabled); + updateButtons(); +} + +void TemplateListWidget::addTemplateAt(Template* new_template, int pos) +{ + /*int row; + if (pos >= 0) + row = template_table->rowCount() - 1 - ((pos >= map->getFirstFrontTemplate()) ? (pos + 1) : pos); + else + row = template_table->rowCount() - 1 - map->getFirstFrontTemplate();*/ + + if (pos < map->getFirstFrontTemplate()) + map->setFirstFrontTemplate(map->getFirstFrontTemplate() + 1); + if (pos < 0) + pos = map->getFirstFrontTemplate() - 1; + + map->addTemplate(new_template, pos); + map->setTemplateAreaDirty(pos); + + map->setTemplatesDirty(); +} + +std::unique_ptr

    $ZJFF-^K?~FNUUn@#f_Qmd8Ts6z{Hjx31J$jr)Gp7z;St8=q5 zGnX#>Y$xIsQWh+ThR~ixE~ncIf4m--6XQAPa=JWjkJI4}IQ^kfI)y-(7$;I0bkZ1c ze2hSu7#kZUOpFeXkbcPf%FT>MoJzUI9}Wk@M!C!o^oI2Ut=Hv{3VffS-tdqka^mpd zD9K>4az_USM;HbhXK+NV6%Jihjc?uOAvngylqU8dO(X0l>ogPBc-DiQfkBFPc(_*+ zb*NoL$yJHnW}#hG5QlVTqh@f}$G9@sKYFdN+v?$|on(50#%A@9Ps_VH+$Mvg{d{=z z6s4_U@KTpu%HX((lQlyYxAH=xq_>W3w=!!wJpIQdluAjPh|p5_g_r2tc;LF$M{u6A@?wG&1s1+#N1 z(ML%8tj|2rI_CC!gXEUZu1k~awmjlzw48TGBi)Q0@CAlzv~@t^_jx>CJwxFdpo!Q9 zCtnzi3OAhD1??PKzb@c)qdf=R99gHyZ*Z0dl zdh9Q6ZvT!u{6t$cGSNFhSALA#A8~ue-+SVVOX`DRlvaMkO9)7KCgLl;6lG?tIKA!+cj6YbF=IV z9Tz+LdSx*QXPgtQT_lyuX|p+1*P7aWQCn{#zPPBkToRk1nSma)+wHX595!O}fY1}2 z9nJq{*UyzbllGVfOcz}~2iktnVeV;@*^E8Equ?n(9Vp8mDhS$ z&cNi5-)VD1^(VJ|Q$3`x%dTGW?LocgXTZQrN|j!RRzIpz>Wl?1?W`k6bVjYp?AZ(V zKe6ieRkz)C>#Zwqd0_SZkCwlll9YY>>ObCl>usx6-I;}Y-T!I+8w=ii1&vy`>Vr5O z3YCp{W6|qxy;t-9!uY9V28T)l<0q3@3=)w-q%cW@i7_&rI&b`QyuuyzrrA(9bmg0G z`vQSz?beo%$Aa6+-3i7|nLB;g0#xGTM3?@nr6|g=3og@a>eX zDb1l^Q}Qd*AE0{6vZy;wwv0cjXWN$Y^|G!NR|tF^1koHM=wulFp7V5aSQIO(VVBRtztw>bwe`%HkY5 zFSqa}huiRPBbtvm+;i4r2ejdEYwe6!Vi$;PotD}m5m)HHo_$`SBPo&EB_ zfxRO=S08b7|8PQb^s(h9={~GVf;>) zGZ?Ws)otB7dP63Qv3d`ur@nmbhVeUHfhk7*`fJip?z!i!PrqUh|EreNcA}F^7RO90 z;&hLJ@pq^WP7{A1bQ~+@DTty=)wSdOJFk8Y1EfVGVEnSsbmYnx%z!@_w6R53E|jzs zy?0XP)Y>}}jon@bI`}!>Ny7BX-z%}ad-|{_Hv&HJ5^DZ}| z_no>tLN{JFe!q68?3)tZR=3sSa7yd0klIV@doFeKG;+5C0DZ>Z^ewtY;Bi^3ZeiJ% zg|yzrs-`1*KmX`6#uw1em?_2Zy*{Vi>aaC`S;()Oj!2f*CG#v~D2MY=Y{Jz=d z-IoGxbIC5j7l`%Go?G|eeUH9Ic4&WoeDwp5e=84sbN^EmTf?LGbdwS57pz_1GIZdP z&ouQ9f7IXm{+}yD2lxK{@o)JXp8E4;#QN!_57Y;|<{f|SfA3!`w}bwV*QWOVb^Y2c zPe1!CI|$a#+I#D;*EId(tryon<22}OPyH?O?Onys-uL+j-}(^ir!?KO&l+ZLc#^#H z!Sg znyYic`lrb|{(9G%@~$nT7vH?^zE{rceLI)6+qC_MGVXsCvHqY_PZ;6j-=_pcXGcdR zR)cM#TWy*=_e9EFFM?+1VViSK`}J0NIE=?c7+kwbX{|ZV8QGPdT!SnP+FU?Zya8wP zyQYqb_rMIjhFz-Og4-JASa)841QQ^u^nkvAa4u6>^x?MmHP{@aB`1tqNniEG~aXAW;Xr}tWHNzWCM27V_Wf5D{nq%Rp{G&5*lXiZ`&wxK>=(=R zzu&H1v;eh*L7#sy;KlSuon{4}1#gjyYZfj*EzQZ!&C6S{@|Ig}z4exRJ+%GrJbw3W zD_5+@%gfEnUGxj_HKaYG4qZTX_p`*wjB{~ZC z{&N#;BWe>G{yTB~FK*+4msa553w>zOLKKSOjG|u7M`0Gi`-!6HvbovmNGRa*LbU1i z1;SI)v$MzN;%8?f;h^8^LA&F<{y;baR}1FSW~ZmZp+LYN2!xPJ1#=lQa3y%-%FJvu zy0I9wkQklDg_nndez(J-SII;IzCa{X>CHB$$9LGDAgv zmj~xv>T%gjI<-P3mC02)lg;VId6&7JRyZ}u;hI9NGuvIWH!v{aJOsmLQ1aOvzC^B& zizZntfzn_F9IPMf%kz4iR)b2!q%tN&Qkj^?q|*5cqz@0qn}OUY<}#=hDuW}C=`BvU znCWp?bW#DEPNC44lMK+?fzb9sG!obPV0 z+iubbX)J}sW-;h=2D8no;ZnJ=5qV)SCZ)sez}n*+9+gmMbz!`5E{m3DbUQHi1c$?I zQJAKp8;Vg&VFJT2(H@(Y$JRM?0w#`{LSsqH4mn+*H9I_BtS{N?vgu_Ew$Y*HF>usO zI#+JB@~J{-c6&W`o!HH0Vp$o?Ne@e6uzS2-w@okLxtMSWXauX z28Bw?rzg{>6pqO`$x@me9*i%=>$2*ERE}EPg4M=XAn(JWX)6&Um zJymITc|b-P*(Ro7X$jDag{AaGUo1wYfQoz}fSO7cStevAyWJ=vIoT8} zHIYIgu?!Ou=k)W%s3c-^%4<_IEjl9kBqg0hamtt)tIO>$is)`Jv4oO5!S-@^daFrB zvC#|RqeaM2n@mcIQagdAB$3Ia38rpbU<!na8i1e#`)XZAj|XW-=`q$|H!!gG-) z3P^E80+9$gz5-G*VbV!cm@Hbh)J!NOEr~S{3rUINDm`7Ll~8QdBKV+D5CuGfNld_! zmQG9%iDMM?Fvp;+j7EbEcNd`IiBXT1WfKtcNl8Q^PYY3&<3^giI(PB}_Ote1(W?qZGk=vTK4c%CZr$q)ep$@lmpJh^1Bsr<^AHPnO*NiROd`!-_{vmT39Dioi842y~*L`^Ise-uYZ z9b>pTBeY2#dty{;9Lpypj2Z-V4%to}hsIHnlQcr(aOk5%n{2dz5NBs_xFjp-Fk#7< zt&gl9#S*f|$H&G-h6lzZSINrhfZd=FvPhKPEDUPNcvQ`_5%R~<2~Gx4tT7n%s>xxE za(Em&7C$nnA0@Jl!?>~Z5r%V&BGu^i8X0ZK#vQ?p#Sg0_6y}7LczA5d2*YNb7{!id zkB*KE4-H%$7WNUv9$0!}E_HmU1Hl=al?Zi1xY4CU3i+7KCV>Tks;PRmZaXyEJDo_t%N8(3^OoSdieHc5EF-$j& zN=z=F-|usqrDIy&5Zptw5Xj@!aqP&V(UFnS;*sp37))RHRc-J0v4JHXBcEa##0|v{7-?gpmeE1X(4s-2 zX7JEZ_5ew94N!W!+Pk>2hzMUfcIn$^wtSKgC;^UXunXIr(9RQ6wNBU$(Ewvtk_;oA z*zUz0e0H}!zk5lChB;|);{e1gA{%>f-SHh7#@ML&au=q1aaZ3IyS=D8yWM7J>$uq3 z(%f){@AdW`X&pZD^27HmK~RDmLu(zjKCX%=1zG&)KteF+vnk2?w%An)yGLDr3R}OV zTEvOr#Cc3YO6*d6wUjwNrayBEQ@^CH**CHk9&qDuk)htH0wjuV8DPZVa$fO3`hDnC323H?n zr=$_b=b=uuNQk29{Q8`_Chb*zc~u3z^k@m>#Hp42L)i8A-@6#FMy9yB{yJGim#rS(z{MP-3q+Uv5& z$5j*c`Ati7V^_3S8?a3ojm^dhuFe5lxYubEjhVaPV#1kDBat>?IM;}2if?SwwhbE3 z!rScnD~6V9!m8S;^5ad@(b?(g=-jo9_uo+jwmK)9R5jtwEj`<78(}GpRtNzURH~_^ z8GA1NY_AD&4XiV`bLnSD`VoQFVzXKG(s7Zb5qB=(3`s*~k&G9bG3Vl&+x1s_DP(-&gmd5= zt~I`yt)wv7`t$j%X)OxnFiWCP$|lD+)(-f3!Z{V6%_W=KFJM~pE_4}&38VJDme$s@ zY-RrtL2#Sr)H*_b0NPYrIll6j*f%d<=e+w3p(P>9D$f_ z8EM0Hq_(#!jg)aB)STqzKDdy0@tTb;7En!H?U;_-_RB^Blg!t*b+lcvjZ?-a1g9Hn z8+-`Nk~wEvBd@13)~JwADkZd-P2Jvg)kNY6$!2CphgL}Cl8F4q#%jU*%*~CTuDty)g5%aO zHInXvD+#?lLnP`%f6wLoE6IJ=>|CkItY$lDJCcWOPe^ zALdF<-;kLiR*5EEeRLaLB%}~{XB&EF=ci14_VgRYs6=AaXOhvJl&gaU1E_xVwW5L4 zD_S*I?{M1%W44k0LEJ$6;6VSiYyAW8HhHjr&>;{SjB>hV7(1}4f80!!>2)Hb!7h;~ zC6qDVxwF=o46~u$n&u~B00Enx$8{5jOGncNB{rtY9&*wy9KB*687Ub}9vL!f*jlSa z!L$<)&rh;YOcF`wU`lMZE9DiHC-G<7H4ZvlP9@Q7P$4tBy>@dEnS-UQ zA~QiAo5$mF*sK=4lr&6N%xiFV^i&1MyrvkH20;~UgeE0((ye7s>4zA(G%DX|WJvAm z=F;l&szHdE*>aDJMxz%omeOf7sYl6Fm|^R3+76KUSVk6|W9MqX;-PwKv*;z{0d-7; z@li!n`6Zzx7#MU5H|V(0hhI#&%-iT(Ya2MK86XIE3KF^6rR^=u)C&~s&YcKxG{za zha)(HFxXm$O(z!%1wyIH$Le}${{tS?3_LLkxJRUW6!q!BS_ zJaAl)eS`sUn0%GVL&H~8Rx)R%FrmDF*J&|zeP4J{Yq29=(MmXMvDV@QA04_i=yMni zW{pyzP-#s%gB{KwF64}-6i$MhL{BG1t>-JxGWo{9bJsyJ-XuiFCas*$qSG;qG!{>) zF**DbWfkQ$;@L1Rln7FK+Ye(K1Z)f|kRw-7)K7*c4ipm=Cy+Vm9c5sNn z$AX{??d~O z15+VZExxMsqH{WUIJ9K?3LZZ^iwP|ZM`qBo3#P-^P+XL^WBvBYC?>cptm!#bQE}>u z!D}_^)e_kZqUw~W`Fwf1Os+OKe%*VT-ye$TJIg96O8fnhAdE6I9J)|}pM+ZE^laEB z=fI&dID-!TX-4v3dd|`Zgfqe5l)3t3MR_^?M2*a0)GDCP8`}t{2kNT1a;4T+!bZ-q zHzi1hve4m6a9c6;f6;%~w6%7}=TdOK7s|?f8 z(o~Qm^>7p$Trd?u2NT;47j@`F$c_OBQA&L|MXoTKpF}MbBAJZq6P4-@hjfFrCFK<* zP@|YGgo(Osx))zv1>s?J>x9xBj)awsm1Tt4h~t&CjI4lM-(L!@v)*+e7J#Z9Wo`%F%VJI%*I$eq9&J=_) zA`!4+uw8HvuuP>>h$UpqdD^SailvVBR3Pq*%*-HTk8Lb4a7I%}S$Sz$3)49j#Kiio z1PdnMt4=wl@tD@bj8amX(*_ z+lAh#fX_GIVMQomMq6D~HXe;eu%TsO33X?+VG!PKF>0i$m_Lumx;&-RC~H+wA&B}PI+;cj+X5=xr(bmMEvTfDx<^1zOlo`xAnn;s~u ztREU}J6e2EK8K7oJF0GK+<>};V>X&CHiy#z|H9E1QJ4hut;bAOoLzTSQk`fj@rq$gzTaV@yjBPBtEd6<_Z%S*&(6>{p9!^a&Z< zNYTw6Pr$}yKZ}U&oeo7DAK#Mma5pl{U@$^1IdS~x;lh)X&?9;}C(1cfdnamrkI@9f zwA*9GMXL0d`@WZHd(yjJ^;P{a0NU1`9&zQ!}%W&kl!XK>y_v(^J?G$P}H%K!Ad5tUkBH zjH}vZCPv$&i$WVF9^ZLmmc;Q5`K~>dcM4RG_cBKHljMPLdYS`dAs5Vd&ezYYzVG10Wy86-pm1C&g>f5A zCZo|{L>uBvx_WJF>c8>k$7#07}XYANi&CCqdRWZa; zC>I#e`lULpR-@HgNX5r>GtYw@3(<>E3x9>PBoK{C^8biKEnfJ*#}rsCs99_@{&GDY zU;p8Q35QT=FGmADIC1PhIxcs`{81Vb`Spp#a+OA>)9SQnb&5)Dq#rLnu0=2wVtxsc z?~C&Xqtj#CQXxN?@YG>?AUcPv)#<_Vnra=SU!U?HP;|I3ba!}$SYA^$ z3~q30E*hP)3&yVuOz>2W`OU?k7F%K~AOQ&q31YEKiB@N+07n7-cBuGJ@nNDj{J&5b zA3*t={suBF`)C9P3!8UAC?PyU0;6v8PvIbDoSuUfH#ZG*0A$P2%H**C@s6murJ_wJ zl*pATrBW`J$)pN#TS3vGLn9_<=zo#oe7-<5(DWC`A>MaL2{PpeVPg-^P|9m6njOzdjH!Q zcXT>X7{nsIQ_HI>&e~>qgwQ=&mL(O7L^AIAd~5;cBvq?Y zJA%LNzQDW4=k0kK3&j45p=cN#C<>x)^xtHeDGs56=s+?A4#=h~7sn#QJ0LQwKEve+ z#bSv>3^T>+IeHL_MW5y>vw6bFfg0?OKmWY%r_w$Ki^1aX1R|+I zWka-q0|+P_g#uCTE2!nVFvg35`XkgJ1Gr$qw6~|My8N6C0C2%ni0M4YSyd$!#Wsw) zks$F5Ym!H9Kk@VapMKi=GrpHXrO@b1HkU7w%9S=m6$ro*Vl?W)ra`3r(X0;*$2DY* zu)%~GYb(CGtkoIo|8KGA7J^WXoNt?VhY&PY5owoeaQlD!dH)aC+AAayfk2|r7_1n8 z>Lvi}r(=J{pcbxf4Pj#4;{wUSU}#3s0R4A*rjhZ(Ar3j9BBx-{yrTaA5oA@}>B1j> z{OQLZ_8mGqNE#m<9UGs3x+)Wvums}38xlZV@iqkUsRtcVEC3+$lM;-CDOD9!Wdp&f zAUKfx;GF@0fTaXsuatjX%~O5#B|q=o|I@x74whdUB@Cm7mJJS%f)I2jXYwWlW)bWm zSfo<+Zxm`1I~vT7Nr4F@g=S2hWmOdw)JPZu&LcmV9s>cJ)2rpYd8p_yc}o%SrDsdy}qJxdb%(sM@k?(?IzXYtMR9$GcZdS z_-_G}1^iH^MLeHEB#0?H9fHof!+Z7}YUmpqA0E7R6*7ufuMG^1O^|7f7>Fu1S&J4e zTmU5iVzf&f^%Vg`x*tf`ktqwgv8<}Htjji26hr|AI`B`(TF+P0<-GYc;mq84ec6$^ zp3(6UsFGZ{+}qRBd%5rGwZTy$2|##4saiV^vTy-_fYPI(Vn`;W!|6O|;dIxQRaTXq zVumB&s36sk4y45j_BOLgFCQO^+4lWW_1TJ+QNrk8|FtW9y**uBot<6Xm;0^_j1Yi( z$mYxBwDU@2(^!a}hX{F!y_w+0!(oq#+)-PG2PtX>tka>QV1D4A$VmtCA5Od7DjlrG z%qRRHKCP`88t(5$MCk77Xm7vReyOvk?`kDC`gpoP|>c zoX^Y7j2a{10QeDH0OMZ>VxT>VR$j+R><``1CmwK-b3|wigI&|P?Y(aiu zjsBM)h2D%nP<*kxtfCTMR@E{rc7&&b1z_t~kOSlUbI3xdJ5qeO0Da(k#DnBUyLtiO zV%z!i=gu}YHa4}iUAolTP@2F0`yYNnAB-=mA5%EM=iZ#G0&jZA+*5{}ZEGN@?gbtP z#$Om%5R8Fj_IRStE$7ZQHq6=3E!_Xj zS6_Ye{oZ~1&?W6Og;s-XP&aY%z3E}`sS*(69MuFi4w){T{|LzOG{ntr%Bf=|Cl4QK z7-n-IDuNQnY_Pkw8YzeYKvUD1Q>uSa{P&% z0dU@=@MHzPqVyc!8xH0JKt5;#3=rdA<_iY-O(#lAkDhE}$V60CEY^7dqTTZ5)^p8g z&o(twA3d<^llQj0|G_6foBQHB4F19(g@y1^WJ-+#*4IrKtT!nnfn-z#(G&K03w`;p z(_;M#e2_QGm@;*jmXsYY?c!?{dw<<_0G2U-v9T< zU+ynH)qZuDFhQoXz_G~HTIbC?CB~Z&u+-zrtA%jtj$wcig3j*@1erA@_>(0aVzU+) zDx~YijwCwIHdYpV_sO<*FmJ8g^47LbzQLBAX^So5QFsQ3gUF#ZxFR<)p};x`UMZ=l z5KiSo|A(RP%#gje1dl&)cG6~4%Y+=VAg1m=MrBRa<%PRG-1^Sex8Hp2wYNX`^2fs! z^=F#TUAWlQdv$P(1jC2TT&*{{XKth>(a^gJGJJ%oLg*dmNBb7|;eTtx>c*25B^Bcq zn;syzbP^Z`qHcd@eeow-x4!$%Td!}~w*5Qwk&|T=Rkf!Yn=iC?_w^4GsLYrE8iUDu zGyMq|q2UsIliOe9EkaKHz8nZ2!u;yeijysJw^^@Ih$fli%9yGjjM5q^zj%A=TU)k% z^7W4xT*0B@V<+*Iwe^kXT045L4UJFG*png&q!x^3{~U;say=aJUBH);!iT(v5D74_ z;bw&xWo7u%0lUj=fE+N7O`^rr3<=McQwO2{?RySj(C7oVVG0f%IZ=i{w6u3!8626Q zGP#JFfe>U3APLtS5Y97ryd-oOAW&X3AUqdlmf}mx>2AB(pi@Z%9O~G0HBS$mKK|bO z-=J~mANTFuxBq8M{-LAC0pfIXYgb?Y2!YIqAwVv}>VOmXe;`~<_%c5D_aaC;EDuS_ z@uk&5ugz?P%~i;yjZMZhodf}G_5M8t1wZfo9{t@N-|zeBV1Ch&<7HL#O&4H}h6!Zw zw~(kry0<&SH&d0Mh`^NHcYpo$?mc^dJb)`Ya-zJp>3m1;)uC}R9n=6TRgXZpAr*5yS&1l{1@418 zbrg~ZXm2i(Cck{Lyo~3!Sxtx{_$KwiNH=?1Sami>ys zfN_VERrqrJm?w19i#QJNCJ@KpURv4&sVTE@e#%IL*CV&cwffTUckcN3qmMrP;G<7J z|LU6`e#RX>S=G>dvAb^&W(-UN;mJWt2ypzNSUNBUg!LwdgjM*8k`_rYcmfe7pe!#h z;ec9@wF~}(Iy|rXzd(3f^V7#4eenK!@4oxq2cLZY^`4(FhfY?5EZuzrFk>88xgZPF zd|bXz(4h$4Ou*v2$zer9No5(nR~-!b4j~m5C}ISMOUjxYaMHNG0ElBjWC1(^ac=#g zkKWt*&f8nweCyp0w}1KFeoRqGbpuRV-{2^+fTiH4Kr=4C-^l8zuW;N%%J-&*Y<*?; z%F@bip(`Bp6#&*|D51EU%TA6XLK)|U8qvhO%1m_Pbj6Ohw!HcJtFOHF)_b4s{B|$; zFaiZW1@r@KKtw$jo7->WbXJ#?;47Rru?oCtfuQglTq`YW8iS`K{kVBBpAPmgnH!da zK`jR^;l#ugc(6zW!rxPSUwQ52mo{yD>5X?k`r;eFDg~^zo~uIyDvKwULn_GX@@Pj- zmz040mDG7|f)#qxeIZB%m%=t&QhAYS3kT3%3?f>9Us_scMrKi`Lj2_zCpr^>MCR1x z)5l)lxZ(NdU)cECJ0E?%doTJ3z83sU&oyu}Y^a85^uV2y_Ew&N>~d-SRi1V3CfXrL z%ZFXuj;d08MOkV6umK2-XrOp*3{X#&v^WsSG)f5{)`mJd6Ap!DdFN_BUbBAP+VwBK z{LTlT?b?SuQeM~Cda1X64Adn=wiTO0*im+(tn6fE7ta})h6KZnB118x=R*N4;cP5F zUp-`nr&iJK^?~t|CpsYe4wNbI4$O%WR}>M;N?;uM=TlEVyY_{b-}?8C-TU)TRMj_M z?7RXS0C*lq(pqf1wvyvzr6*4f>q8O9H7xuO)G<(5*e&j>ErohU{bUfKt*!}-o;=a% z0-K7>AA>-O%!Wf#9tM5*^~axjX3cXiZTa{1T|Xa!RngMk+mEb2A)Mxn7GCR#W2Gf0 z8_2e)5E@cxzrZ=+P4R_7CSrX_MS1C{--~uX8z7uGei2SXU{o>zhd#ywqGLG7rS`q> zm#3b3X8mRW`Q{+ZT2pIh-!Kups7R*J8F_8Tk3#134Ba^u0A3H~7a;CZ&r1+_jZ~aG zQQYr=+zEluBkI5i_-J&EwBJyMDuK+dkh{T-n%ud1!*khy7MJ?}dH9IEFM4 zYVK$hwW%1jV8B!3M!T@ie?pDfhBy%o`~U)%Dvd_m^rMeI^7s=^J^#wvAAO5GS<`&6 z7n~ps4rh>30K%|Zt2aO{7oIkS`ZU^!bz$5Kys#CbvTIi!U4hO^%FWHm>WEHpb|x%F zCnP5(C8wk<%~-Z9Giy0I8=Hg4#pE4dfnB-amZV`+!jg_=>U)sUT;PYiP~~H*Q47!u zF$>(NC0jquLlS%oyr?CMx84R{2%#1%&WfUl|9`pIh0)BpUz4#z2pRj>T;?oLv=KHo z)Q#O9ADjcHXMlU1j&QI4!^WPO#my}PhjfFF9fXQ4BmhAvNH!lAS`KMam))#aN`-t# z`anI|WCg15VI+2%PtU>n;o2kc^uClewejYPudFqse?D3y9^S*|Mma}l)H7O7 z9^8kk=;50*Y>omUfO{d{c?imUMm3)x z=Chn|3tWuP{i{x_xk1W?Vxr$=R=H{iO zwuv_~QCFc~c$X=ejhGn430RmyGTeOoLXHiaCUh#_bWoaMl#SA5oOwDpG z_qJTzinQEpf{>%Nx||j@UDKGFzaoD5BXYXTeS?<`b0uec4y9)uU$HEG)fEfSlpQgN zCnv4jl5&o%NXtupLZV=*%{G&i#kmcnOikZpBT0=mn?Xp^Jd_#hQc4!z;AMwBRu#*G zP0Bfuw{*p_>?;O_+TwKC4MLXvwZxLVl+=f1BCgJ2ga-+4%_+!BPH9rpR7RtMY(9`w zkQblz7YUIcqh%x6K!=WFsY}i&%}ZUjiY);?x7{SCIX_P<%}ZI4@{E}!*6XFr`F=7V z(op#-C5OvekzJ6Nl=T(7{xI{udD$XWPZ|z}oc^b7gPcDp)JpedWaIJ@vmQ_}gc7>> zU{XFz<_4-x%;gI8tT$5tFm;QSD&o;WgrdCo)UD=@Ui}jpg?USJGasHDq)GpWlWlzi zAkvopO(q+qawpjn6OOu!99&*Pa;=KWrYTl0hXJI#XJ?I2*|5pconNKE0CI9DlWYzx z1`?N>-ZR2IkzACQk(>Dtr=O(w6(<`gk?KkIEjie{^z2*cB7#V5FzA(&!}f#FQGEJl z(-=W~4&J6`JZXaGpw&8^T1p+WZ%RkTfYXr~R6seDwIXZ(rMEeKg!YstT$WltJkNC&W`>vRN}#REAFAQB6hq-+>-Tv9o=mvc<--}+oP@;P z8j8jNq;-VWZc~s|kHOWr#BW7c#+JjTza*(ez%{sV{y4Wu$RNXn<;24!09v^mle0MU z9`6^4g*l6}Q^$J~G1+O$m)^|^`1+1t96kQZpYB{d52Si14Vx7gzf((A*&!8_5D0j! z3X19x>WKk)%o3{L_6!8*2)~b(y$pGG zNJ$4$Iwla0@Tw;}lQS_{37KgF90KV&*1Zh&skr>CCFyre63@h=Gt-u(tzg(iU3H@) z1<&1k_jQ`K@wFrv*q~b^GIS%aWGnY-{Xzj4MdmIMgxZ$xh?DB;4M>X(t zJ3B8EvphcIUP1dfb@?*P@|>jS4d=%idMh^Ey9x@&K-HFtJCgt| z?J*O9ses?PvFPO#ECWhotA=>R0E+p)W!pQmbT7^>A7xa_oqUG6awUn-aK z2z2YSDY)!}gndRbgFl?U9GjiKJhjO(!i1;Tq}&lH`}SqfoYXF3aTuHxS(xlQmfb5J z8y`3PH7z?ke!ubBAgv)WD=Y3jua??z=~7SGu3dkQk+n@s^Y`(%?9}B+-&ur&aj5es z6q}Q9+3;nmfG?t3o=t_3EX%65v5CY9BF#vAAT*W2(`8kP+A3B&S zHBa4=n4goJnfi90M5~n(f5=UTwuCAJS0QG_tn2p7`@~e9fb9AxA*EeOVNWnlB`%LY z5>Sq{U%J#)^z~;shml&2OUhg-*)mAZENO^F9e(q!fx|*>Ab&LIIgxmzex7vw_`t z=~8#yH(xy&dr->Ux9nDOoSRp1;YZ8`T_DHY6GPro)vjdOEDAob1Zc@zu zLwa6GZhFGkZkEy(v2^Tvv#C2{dESZK)V$@XXRIu(#VqG=5hsUq%8(f$UiMjJ8jX}p zsX=Y`X(%u^-6datJOt!js5k~8`eLfdcc-1m%g9OG?&L`A!GW)~Y}x#H)9H-N6M3mw zX%!BJ(&Dr#>GWH3FnOz%ua+?77WmZ)cs|jnlaffKD}+`=v)#?#e{$6PZ0u>hkPCji zhOXg0nUu4lI4^re#!|GCBX*jKUf8&4?Mr*`=FU4(@>U#%Ff1?iw(|~#93K6%Y5Iw` z%_B%TN-Of)JXVWYCz&7>7x@*VFc+PLJ3psc=Na5V*f@;}hSc~2%ySVu`kI;i_+=MM z?B?%SyZQM|C&ex!JjeBB`qJE@6&ZO;(_iJ7nR0lr&*#uzBd*HDtjNk3=H8WCmCXqJGQyV`#GY!5j`;+_YE6BTg0=>jvae#e@J{&45&$6iJE>vU|M)^~aH zi<{P;@FKJocqUUkN$eafdG{X=-gVc5|JZtrWL84tX?6Sj0f@}CnmylOzFW2XXQSHW zL@4oAvq2@KFwJTbEQ6jCUw_;>d4t9sgkO`&gG!)L4S}D+sFw?Q617n;X7QA!K--Jx z4GY`mBA8$vFsIG7(uZHgTXZskK&CV4glv%p9xC;tgR28xyWZ#psxYvl-Fm$ZPAyQY zhUA)%r8n@qVUaX_|49++CXG7?XS7=pMH@9Tc*L2-;lR0R5^x=%GtX^$@#Wztu#s~> zQ2)=*Jh?~A;ZE|n9OQ6lw!4vsvsObBJXj~@bCJh@4e)@l8#v*>C6@E#X2meFX7az- zIl`CRAaV!YSV-=~L3P-uQ>)O*G!2rP^am@}Z+`v__~nYiU}o6%{tIi`T{*~5HcP++Li+O zV)wKwf8FNi-sMFP1v6$j8#lfp3BvF60C5mDB!u9)I!5G1ZgP8q;cy^E7dPwSIcKd= zd<9lX>yO)iWC05#93?`7 zo`-0=anHvk3V3weeuK$9-#b)o#m$cf>mrfKjds1-^J;T~9O^o{NSw6J6&! znZ78H%)<80bLY=>+8|F6QM~-@#*G_ZcxDU31x$UR$}ArO1z*B_@ijxP(D+W?pjppj zB!}P^yIxwqY29m`GgD}QNS(61zhMJA8uc*icE9lgdR@YcpA7?%Ey6g5ZoBUsEfNR> z!p?>Lhf^h`wbeB{gR!@z4c{esDjAf)*s zQ-YIktlhA2%^S6rIj9mB1y_Y;4zAz4X~T2tUi#`3%@Udp)85~>raBt(4K|-^zUBpm zuZ+<+0+)90EK`dNdM<=V-3{M;RIZe%-M{2=2O;CL)E5a*Fq_wc46l``qG7+k0MwB( zH@fbH&D(ywAPz?7fWny`ty{OgU@jQ;i>Lx$7#O*M6$p{T1wYVcR0&lk*&t}U1M~TA zwp{5c`X!ee@giydY1g%HUs$*C#Wh~NQoA&Uw0x3jFbrc zWC1#OH?Rf}GD^&0NF`PTYiJf4cmanH*N3SvF^D|&#!&IK|32p zNYVv}VT9)9W~M>~!MJJDI~&(^M+<`Y%m88Cf1RfTrlvCrFGJ*We&459iPYYnf?rKQl83X}>^T8g_9G`KqiNkW3k&X=h~~aIFhpJh%{`EPnnboE+PEZf`e0pIRH8RmuCINcmCcss&PFz$ z%AM6$wc6SypNAML#H>^{ln5i)%T6bAO3IodfadN4TO#4I8PMEcZHRW3Rxas0SV$HX zc#jJ-H;rU(6`u3qU1$xT!kye#w$g4@Sq~!5YAcJ28>>X|>d}3l%3)cl`^9ZOrF|?S zuZ2sNSIw=7$CG$`SVp1`m3f!DsCUqtaXlg|Ast zKNOfcT`Vev^^b;54&tsyXl{rhZ|ZOS%C*ota^krgPRkix`)wgz`091xo#t6 z-s0j1tM)_n57rIvQ~lq)4+w09jV%-UbM4?!Ay`R1$?T2ruv7&8St~PAX=E4ZU}9!6a|{j2UURQ zt^=UE8Fs9qe}3b+xgG8!h28+>xZ|*;J?VB&AB8K)<2BZFLggpGGsWuV(VHcknttQCe--Ht11c8wS%4Es4_9!{!ol+CU@x63&gTQdH6DuX z>Vy!fqq_tGg(y*QdGKRJE#jjaC{41!UN<)Wj&uKB@)$03Czy`9lESjqUIBB3J?WZ& z=Ox~2=KfWtD@Tk6G&dLDGk|Dr z%mlr?EA+{S`XOuV$(;yLA+NVWNOOPe7>XD7pUY#S1y4wmH_+=PSs!zMq1?me(qLXK z1Dthh%fcC_?(@e%lbs&Hat#KJA}eUSCs^9i9g(Row%AGyuGdn};I7 z-Pei{BU4MMQ>;Z4bahSJHFKs&WSHe17NM{u`vl#gLYCVfHeJc`8K2`bVuQA-DR>N{ z+|B}MRZfu*<&IcT{QS7$~`P2$rcETenYvrir@+i zdniuWlXnfl+ztX*Nny2|yBbqg-;QEG42u0;76W`xiBRz>T7_IwxDC;&0BQU}xsilx zmlOnQz0t!ccPg+AaGUZ6u956*(gXrdp8p8S?Z_8y9_(b*7X1qUE&>nOM(lH0 zV~DKx-47shS? zV|fA}l0E5g2YTvydIzY2nFGCC(Rc%!PJcy+Hw$6;tqk$vU{KAhEOaxaSjfC;{x`~v zl%%@4*aG&;5tO^f6#=q5fv+XBPtCwpE`$tdH45<3vXKChcJN42K_Rn*QmS0c1U|ho zY?~3$)aqTszff*&hezkwL0-TTgmS-tb!EWW0FUJf{Gjw5m4d6>MWT2?c%^;?hr3=C z<`)7~R?I9cC@fERwJ;$Xt_iOzuMmRPBcwQrw!m-svk2wp!lpj92ij`E6Zk{<+sXxA zTrr@|bRc&n9Fc{gA%qQY&+ogG1FSGOxEJOZl;%8iG$fcB9Z#=9fbzdkZlod38A7@F zLJZo2BL^ds;3Z6Owv>X#IAPR-R_x#!{T>~LL*~AaTA)&#^<;yADM5ev6DB;Kr2s*X zkk-QKfqSRSAVRs{_6Rr>NU?fA7c=-A6<-X+FB29oB*kIk0!wQ{q2cb^`GAh*0|k`g zz24B++|c}7EHf`3DC_b+QEn7!b@lgBJEhrxa(BbSMgSYeXrJkk$z0Zl{V+Za^?l{Dfd=8A0V?m;)ZFA4KIZ6Oz_|idUB8myAS= zhoh2xovri?O%3&I&j*1^0bqnFKq&u7T0={VQ0})}s<3!EZ0+Ur2$D9zjPP9R04iS> z(W3}rNo1)OH4cni0I_9|A=z84$wdVT^|KbKhJ-; z&2rLJb|0D!yK<(WZOw>It}bWhLNkk^$s;?>;o2MMk&ir&N{FIH;3EOD2L3sVkq?`8 zgbM#AxjC-FN>JHyJVEhU(sCOc|2{qy`VLplWTA!-7ca>x9F8xY584?S>X~o9@iieX zf*P*;{d+_t)B{8i*C`VDgXHe*MZG)h?uESpEze=zY`HO3026S)#W_NPaS}q((Y!Th zB;Gu{cB#n**TC5L$RFRrLPIHGYJhSBWew0JBd@4f)ZD*N?m|JA2WVh{$lwk1micZa z+S!uMR|z~gVjx0cPeOhK!fZ=#gq5QaKj zuM+fla9r6_kvkCNKiXQ_+wy$(nTE9ug{Dp?enf?bhar$Vm=dV={zFjk=a8`PAd<=` zOn-HlXr9|U0u6IJJUdmo_Qy3%+N zwNscA{c`*09^Xi0*AomRH*nkm0q;J1gqnPhNlA}?dug?aDT&1B8v&rvV4>92%SpYy zg-o=D6aaY>J-lhKmj>uCqE?NOMmMki;}J^}3*+U*O+%sUKI(UlvGw7+M~FThjgu=0>9i zwzq;pCfQjL9nZea@9gh`X{b9Qppgt7M%});=Nrr|t<23fU5#w1fzWm~gtmKbd=-kk z@_YH*$Jg(buQ%0crRmbex`xIE`h>05BP(jFP^UNqbE9+>wl8kIg&o;)|C`dDejd9E zn9QpnVb;~%*Y;+UIoZmb?EE&9QQa^U<{mf^2+TVO`1!wl?(K16v!$Mn&SG7?WlQwP zySzSU!{#p-qJF{LDB&RA7iMc^YvJ^xqrXSETh>*C5mURDcJX)-dkJJ~g5BlFGG;oX zX*d(Q_u+ehxnI2&R>5T9+<6NYEncXz#BANE2capH92r2D$|`@Oxltlft~buc+S7kP2y`id+w*a?g&m1} zIjyAzsH>!+rlD~6&|%+Kw@<7wnm>E?+<9OQtz+zX$m7|E&mpiUAu28z0jy|`<}l8U zB&2xm1Us9^0ap<4gu<138)z0*Hs(%IZH6olKK)L(e|26DJ2>ufGBRU}!@G%6G)(zv_D5osdlWl89d^+F`r6c}UNUak4vv&~e zhz`#i+mVf9c4}OC!%&9v=vIR{a~FWXe4`b6&wJhR2G@dTzWxCpK81uw#wMo^RiJ(t z=N4-8-6L4-Zex3NdZ1Z)FeK?ZOtyGAaHaDKssXjj8lsfm zK)@(Tx0*W|dgE=Z z$*W?yLjDk--qhHXk(B3wwR>GXE}c1b^2CYLXD?i)yL;WZ^WfP_xJbafC#IlgK)DiN zhlM;hSTqVRQ_ndPiPqb76^*x@w!ooQZ6aH(OyR=HLk;lUIq8W}6%B=EOOyJGO4!yh(_3pS(cvxb@KI^?TSW5DgGK5Vde5MPo~AJG+fRy2rGqGu27-93zS@7R0z#5p7cwSmV3 zgus;*?I&+)VfApzK2bN@R*fjRb}IJPx>`a`((Y1JTVGEXB+EPOVJU^RpG)QxZhZX`9UW6kzHh1_P`?>r^nW5z)x$@v-e~`JX~QK^FDU_SKFn?3dfwuO30V zVXwvKu!z{?48X@B9|WjO;p6Qj5`-rfYwJhE+&i&dy{r#Ai6mPqi{&SR>iW2pPAaNe z&rb+4YGB?bgw?ZCqtm;Zb7GRBK3qDo$8ot0*~;41apTSd&SxlB?>_MbR{dL4d`f0s z5o}Fq=;%yyUuj_tIp$67VPIA@++bHAJKxtDa>fBFXKAtF9{3!Tsj)szFO4!)B0oy@p|?a*^BqFwx)?FnS)7Wv z#Dv$rw-)OlsvDr|ea9|)-Fxcy_G1Vz{-_TEiX{Gmm4z+YV%LY3elBo8RL%%G&Sl-{ zaNzXSK3HyUu6F}W$J%)Tx`sPNeQFUSGbO5&of)6>;ko+^>DfZ6d#lq)>UH!W0oRYl z0vOTC!jGHk!8r;$KeY6MF_uVS81UtLb8UMCSvPOqxLN@VH2geIuLt1P2Ba=5u4X2+ zG{h%=_PMfj+|)Vd#N_~WyLfs(e-#8z4s1|B`?0HP!`&`l@w)062svSJp`!HAcw-^+ z2=^Icx)+Qv-6e(HxryP=DdvU{vkIB+tBC22{DbLc_q+a=KzCtVa+L20y(Rx$pgW*7 z?)6$3$^RPY4!rfkb_nQR`ao~`2++N8w>nDm34!kQsEXgSH5@w$oWsU=UkZSyoIbl7e6u z`EkR0Kq$gi3?`*W1_tZ8rq*Gc8>ZKqNTac)l#!Xkgam9=SwU7hqeRGS(>iG#nqZy> zJ|B6hDOu1*!4Q;_nvzpo31zfXS$On|#kpB&u#ufnP*hdl0&gmVm3L)fK~6?0OvHhN zs%ad?xnW^-JR6LlYfE76P@DT5oU|Uli)Pl9Wn`ArfE`;$C%r=lautAdCg)aHrhoPI zzUT8Xv8=W*IaBzJU?IMxuC%eZu%IA6zaYQ55;57J*xFE$U0)`Av!JlFp`=zA?Eucr zM@tCcPneU_nEzytwJFV5!OZ^H`--YGMg;)mELw+xkl{$lZAiJgnPfsSRy8rUSmW}& zzBnnj0$6wiJ{OlRx2DD86jZHx$HS1Wl-bK^Ds&4sBYwax8Rq}5QFo6?wd8nTb@u@cGDWOqbM2GD8X$JbcK zD6%2Z$ylRn%}FKo;PeaNNMeh#DV;2Bv9U6875aX-Bxx2Nkxh5~I z^`yaVvVzHm%Dk+qhPt8@D9nv4Z}hb^rL3;DB%$tsp(|O&lCZHjmeGuCwt?s7G*@TT ze>AeZLslf%78d4IH#UQ@cB_ZMU9!CKzUH)onwsHq2pejW80Dp46;Fn1s$dBPF{878 z1G>BFz_WsE;c6*OGBq?dF*34nvy?a9-<(=pTVIi0_1VD9QkrODXkub$Omwr9)4$c6 zTv}0(QfNbTx0E$_SR2d8j%hw;sLd2S`&HbMIZ?3y8 zt??8RGTB^F7@3rnoe>w?9ArYIlVtP*OOi8^Djm(~Bt^rEt*Mbo85t0uwt5=SNwP$X zoUDw@WYD=nk~EI~k>RiJ3L1C|(=|EY;{OBKU7l4yCelb)3vzC5On!MyZFO08MAHK( zN!r-CE;=?p6n?~-Zf}fFC@in4DlbZoYB_3*zQm)7gw!OFjLG(vVmkt=ijswe$?D9I zSZ3G1%(jb40`=)coW6HW6fjuevDH+S9aFc_j7F3u*cL^9tE3yi$Hq~)@x`@Z;M-JJ z3P-J3SkQ<#a1sZ1$&uSc_xA(*G7#>N$p&8Ze+2{!g-G>b9l zx5k*SqY&`sW~PQK5~7-W67GLU@qapmclTsfZ#Drneb)+998&9SVkVTWhF_M_d%*ad zMMS##LHX%`@zS~^+iG*tlT7Ir(ne94QL!Q|vvomcS1in1hbN^0bOR=A&mx zUrrO&is0_-iZzBbQ<)`K%M!sW7Nu;YI-5%qDmNO?Or@4w%ln#QY(g`YTpF4K=O@s+ zq}uE9QnJk8EopcwDmLH3m})9%WZUh!*v!pT-9$G%X0eH}k)gp-5~HUp{LaVNTdQ=Y zitz62iWQ4#dQ$V~6^Z3QUBgD8Z^<#|fIdY}wK!i)XJ-?qoiDm;Q3A;cSr!cUH~1MNsB~ z!&R{*jVz%O3yvr#A}mGvPqM#c8$hvAb03wYRso3LE!EXlla>8Lho&c^yErZ*HhxHp zoSF81uB)E<5<-47h%H^RWZ@FOuQzG_Nl_lwv!;vi?(Dk5Iy8OhrB-FJd4RaWP1xC5 zotYK6kghK^|5MtR66>Y#vCiRI^j*;P?P#t@EW0<4rZ1ywl$#u%kZ-I@)t6dkmH)D4 z{{p(c+Pr|&;7{{V)n=cF^|h0?3VgLqZ?=f;&aJvRpKgHD3C@R25opguYdtf%joQaNF0A{E*FS>5s5}JX`(pA-QNs0L;dQ<~xUF)LvQQ7+N zquRnfb#Eer=jkq6x_}h=>8!R2X2MF_c@u>`+|2wRi|B^9rRy4^(|~VmY^W|y%*-*; zrx{8wU0WR$S$=x~-4LsDx%p>qWqlJYqZLM%zh6imoJ>B_Rqu%aR1{zfvs~{z$yX({Ht}mpUC>k%h+LRj;9TO8@ zTzPOY-9*;NFq^?BNNL=;jBX-fXc*L#L5Y=&Nvg?k(1lO+*4CxwGg1o(MpTnYhIS=! zsqxIHWyX4|YT;1Wlt3N*r3P=pJZ2~Z_lm{J3r(_{^Ro&G#;&Ggmu&x1M5&a>^Rr)O zMl+Q&TjJ7^URIG)7P5qHs%U1Udpf2RdffDzX8OX97r(8}Dk(@SKo6Lv(ejd{?A(Ob zOG|XWmn3G!r+ipwpmQPSxh;;OK2`~%I94dtUY=V2aEY6_jDgSeO#BnYlH!#pwl>W>kWPsY6LhQB`GL^VhtRg3`i-#J7u<-T!%SDIV&8 zlUcY_h+(rE;iT&ldt=gVB1YG%J-w{Cx9-vUg;(QD%x@DF2!@{<(kmP5OEL<{1S(P8 zd}V1WvkpS%&W7U3ilT&sfVumk-)vQ)z=jiy)C-t%0bRv`I4rF++uqpnI!W1*sOQ?2 zQPj!}AkF$`ioDlnn1x=4@!j0`9r$oJv^JHdhs)T7btz$4(T*$N3w4 z2z8x?niePS&(GTF3Tuwgi|IUIRor-e4NkOXdf$ROGzbp<;XS>*Udqm;3cVr zjdv_fEU#E;SQu@KYfUTcN;Sr4D`_3e?x;_%E;(yzM)t76lg&&|7S*MdfL%1u<4L)8 zmQ<^Wq}AmNFd7G_7--$9g81n0;Gkm~!qyZ&Qa@zP&1pw-fwHW$#vC^XQ!}C^g{(&+ zm{{z3S6`M|-2HW?k~YRXUchT>sL0H3j=f}OYEHBylP!tnCRWEITJqs+8??u^~_+81DhOItlfU< zQDSXndQK(#RT?3`R8ms?C>ovuZ4AsjQmP;G7*J6ySt;grnX&p zBvDf`pi!dC$L}gEakD6;r3m92hi~`~OtsUG2 zP}(auJnShEZW$T8och=N!eOkN(@tYi+NB^j2C2+4N+|}Wn+?s~+_N|>m1#c&9Cx-d zw_&jw?r}$TWjSCvkW+*mwP3*W4=ACGT5T<$bYO#lDhA)Wq&Ok;!(lb(J5{jaXG;r8 zhp}!>yDLi?CPV1^fZ?YCq(IyIT6SQymF6Z1uCVa~aT=PFvN-_c0hEX4=G`9bS!hKQ zex<#G!|MU22elVi{4&0My}3XMJy?=U7);=1hx}=f1ieEHCXjNSY(b_ub{tkWSK!H( z65PhB61utc?kj8w4wud4(7ToUd&`rOt9m(buLEvT6cy&b7H$DKB}G|3aOkZWhPpZJ zZY)X90hi8DEdO4xBk>W><3FZYLzhT;W)goDESz*6p09 z_T&rIX`vr4j2E@`_9|w1`ws989CqmDK;j42)yEFsIu5I;ft`A?5TMsCH)xNEf{$1g z?fGkN!?AUPe8>a}E@N;z2ictN`pUX4lsfIkf(Z13t#Eu={`b&_MySie%T7uvEn&F> z)onKH(2Y6~Ojm#F8(USpmO5tU+5AE9MMNU9oUZSoso-7S58a52zzK&vB7nP!BIKv0 z0WyMWA?zM_{gW>| z?hP1hPJ3s6>q9+^vX+vP*`vBaplDFR58x?w!D`GX zdmpEyfC;Loc#f!U-###5hVdMg*u#Efj8W0VsXBaM4FI_+D)ByCfH-X}#j~&&qCVF98z6(fD8 z8nTF;KDT(kxaK^-ae4qLX5a}+lYYu{O% zLt&_8H*n$1f!1Yecy9r(`|-=BvY(+ZQ1q#wgjGWi*7W5=sp8r7V1YVo@Bl`0?m60e6&2^`@Svf16DuvIoly%*&wxuK zTKYGtJD0_E8!7V^8!sp_!i>Y1mk)3%v)~HkcEEMT_9ba*sAI4=O|0_6ANNp9)BOA&*^ zVKGbYMhc@$d6-a$iIG0lAioq^lVl?h*djA=D3a6utH>K{8D1L!r;R(t=|gF0PJ3$? z6gdGN(eCcg=Qpp~<((Y;X(+sum$iIT(*PbU4PbiR(D)nF{i{g#ZZ|fT%ZL3NV9O{t ztvC)(8RXH~;v6pAsPIkU>Ev=cSl#76LWPl~oRYlC%c^QfR@c|l!SK4i@i&9+--{i? z25JY>Uc<~Uf$Nj1rG-5Nu9oa6P$Zy29Cp>0{dQr)apag&wLoWq14*68=UTF%tnnAr z{i|5l?y+3(yQH%5oW9L!Ky{DT+iQeTHRTC-98dIhJGi{E_fG$aiMdA#!$nz~)ckCq zl`Ct2iKf>|vYw^>g1U#xg_B6SWqJgHmIxOobT!8aR=bN~$ISKoQ3nbBfnGkp7qD?k zw?r2|3_;xz*73r)P!{6Crxoan*HCK3fw!hqOEr`lr2PqX|5B0X4hrht3Zd>|XgvwR zQCR&z2Hh5?{E|v*tC*Q#xjk@s`dEeEfH)TT~y3EXV3*VPSF`fJ$-OeLsM?_ z*Kcvzr7bAZMBcH*(V@bqa3Vy7FG|6PwydlinPwL=%W7|HDXS?z=Kc%nMpD!ciLSv; zUm?`J^Cw5p2Sc|TTZSv3VYP9@HW+0dgEM*M*gN z2XLw?Dz{tyO?4wFDR5#N=<;0%-1SV4Qo$exIdFoKJ5C$FtaRWi8$t%OCqIhzZbjUO`3jL3Nyl!u+86(y~9PZWL{e?O=T}MF!pP zfw-rDRU&vRa0Om?EhU{iffrj0s3#o=8(?Q-dGT+tp&smJ%`Ye@EXd0*%XFKgsHTir zomd6U^KYsfX^3~%BZF?k4?Tn35K4G-%fZMb@W5#)&u4%J_^k>f33QE0Ad(P9htd!o z`WI&B=?-U5&PP zItRL7Ag4i7$ng5kL1?fb8)i6E6dMX}CA8-?TofBlf!HvLk(HCnEDSk1TTw|Jt8Du^ z50(_cqPw`X<#&f}(BSc(`Swy-W4pNh-4vmOTenOv=O9KKzlZ@7G#Vg)!g~o((agAC z(ILGM&X3A|wtAd`vMNS}{5T^U79PP-8EGKMe#oJl(lK0k9R!4@g4I`-GP8cgh{o9k1>Y|j;1yL>6f{>pO-)Zs0tanm(anTP{N~Wz zD#W^X_5rSd0w{!zgI_!WW`TqXVfk1T-3r4yWJ6YR4QI1cUby8!GI6FPKXYVM?$k=t5k(aj-p4wC%8zne$B)PENyPz`{aF1*$HcfF z-@b)?3lIMh5e2Xbu$Snr6<#UIFNL0 zKH0F>AvJU;x>(@REe0Z#E-YlUQG-;LmKBDF2?NmC@MteN_&h2x8qB8=75(Y+ zmoK3JL?hT9Y|{%NOY;ZWJ;?Tg$t|{V2EZ)t9C%e+BSri)UbGjK99!lJ^$kOG3D z-@khw81xBDx_?H;rQ{SPd)uku(_nYg5JAjo_hhMb^!C>T9@f*qDr`P zew85p1S9j0NaD>ae?LF}H*dihGV;f}D;wr3E2^mDum;TD-&EXLcy7<<U+%LsnsmD%^!9w0jCK$%hyHHl<; zc<+rcYMPjF+rN|*XMPhULuV^vWG{d8_w#@K`o*2I8;zza$;)H#ctv^a1cU8YKY^7P z>f?bR|K-z-a*1+%Uo{~6=i1ia$#Ms3o!Uj~)I|tR*I?cjWfd*#xP4J|6^!)gk@Tme z#fMjK{JhVvUp5{iE2jvvE_v)E!!7ieVKI~hIlz_~z<5;-liet{A;V?PQBj*1IM_`U zYQTdCoSzD(=kW}jwvy_Wn3kF%Mn+S3}QNl5c|D{86-3OeYZ;0dK$^j7b{scT{h!EH@IM`osc4;xBz zVq{kxU8XK4hk?#ZPGyGKmh-m&?MAy3gf`m4;CRh2+Kuwc(mnASnpo;!hZ_`+kjOEe z@E_tpf-ZKP(xfZ(EJR>trbK@Y8A^0waO33UaaehI&9)Q@d`Zo zze7U+Vh^+(vL_v8yM=kC`o*d$A-$fUA;v<4rCR?Ja9tetn9~9ZrO?tz(O<%Z$pKILX^r+c`Z2DJP5DX_Svxp`i{egDaeH();v z#RHuV<1M1on_Gt2Zd85;cQp>by|3dc8gp5;?d_bB`KszuBX|Ib0=)(JI64e>DDx>Z z)6L`J$>YvPj~sD6dFGr8)!p+3_-Ma@cK9Bh06UzJEi@Qu19vSn$P{J{;Fjupg3(fa zDF6{VTqI3!N9oyx)lz@Y2TEDg5{XftzX`)z86}K@6ZO>5Lx(5_rh<$1X{2)X_QU6h zQwGW&#=$%^6BR=0?Za$0x~F8inoaQP+OGz>ufjZs%9_^R&JR`9#=7vE>P43;6E4?C zkSlF;+4-Q;KFXeHaEyTSsdJa;S8hIh=KtSGqVHQ*eub_VD$i7`WckI}SrLwT@ILF7(rIVpMM0o~;`y+5P4NaD<1)BvL3O+5yqd;}ksII!}{1qU6d_>?_}?1er;#jX_OtK&~6o zYJ>BSO;vHOs8VPv0X#90iFt2qWg$ro&%OPYwQE+bT;aHG%kG26&Ro6%ygI^J>F199~tccti_wAEDb``L7n7&~*KhGt~f5bK|v78l*n zbZ?5;OjT8^?EDRm4)%7o%U5sOc>rnM1~B=ph>ZvDS+p&plaqOQF%G8*PL4AwM#S79 z_utoge;F39p^DM6dsNZSrLd@|YP>)twFTwqDvEIHsFbFP_qT#i;uICsmfHi{ZDqT1 z!w#pTXD)le<^|y6kxxAGAuq40>*$QyI|&1;hxoa>{R@TpV&M&V1## z=WH6ptd#B%wwu#_4K8C(fAg2!Qz5EQobw9O-AIXo#ck~bh#a}AXQjl(G**7_^?&Yp z^|CeWHzruxuid)$=mqy%2qORV4emfN(=IM+YzbSZ2CIaaCD$tk`7nw8L+K_o6yER+)pP*d35B(3+A_2CRz=&4--a$cJ0fW(f(Ad`n%Z_l0 z3{<6i7`^AS?{NU{jyvjn!#LTf;$gK|@qH6QkGb2@a$>0yh23*KTBIOA+ASTzmf-GjrH-6aKW-+dp^51Sbg zVB;mS?EVw)79P&P9{exy?$rFMS3d4L^_Kp3@os;{b5AR2ss9r1miRz*Ga16W7vI;P zGJ@I@-m_e@YnU*805o&D8P}@ z8~*F{Z)zGEZDN;<#{Jjd0n2`(R~Z(*fcRhTfBRvXbLdqu8v4WH@c+H&`v2|v|I&{I zo@o^I`CukGn)(mn#O+bkubnJ0s``h<$V#C8ca-kG1e}7ju>bx0`l$cuwUkpFEqy`X zfZ&O|lDugA`}_3=Pr|BMc$}=HFaY}7TTBW3dkrh3Ce?R^d@bXq;@e0yI zm;diy|BgIIMY#KhF8^QGA85~8ArE(-u=9uU=Wq8v18uqXG9!22sOT@3g1c`x1@ian z&#;x0hPlsB3T71aZN{SNkDk8O7`Xd}#sk5E!jlj#B1ma{Lj}28{RjHLrXQow{yA3SF!>(6_6L1P{f(yn*wNLu2K|u-@bBM$4AB480gZ;f z1n3VxfPY=TjbS4JxzGQ}`jRrktN){se}Vi5q@PCd`j4`HuC3Il)<4&7boCjdTz`h$ zDChq`e>CrZX#Y{ofBt{{(ZBxx|Na}j@#p`I`+qn9ABFf!*#C{T{^+$ojDMrr|Nl>q zzl8Ds$OLp0?tdGYe~qU8=+6Jz7%2ZRdh_459IH{yzqFzJ(@6FIe*UK|N0@&bwe_#o z9AW-<^z=pf=TX!j-SvmoQP2OQ`VU|JzkmO?j`I4~|JR50zmeu2o%R>*{1APIzdrt> z_1DqWCycIs*8gz*Z1dmu|Bb@^_lLgNU*7)#`yc<*f5a6c$$#xXZ8?PBufN#s#%}N# zhJWGsS~mLkzj$%bi-*4MV1NI>KtH_tz_*dd>*1o8@MCn2_J8sM8Fpu7qRH?2V>D;Z zo;_#o+I^}rLLx`gvY}6J~>%wke7gcmhcjlFf1|hHU5u&K)(&;j2$Z}DJf=GUmp4H+VRb{ z=DIp_W=@|nah#S0)LRLBRu$ysP{olllKtfYamjamuKsvhb2YFSyvKv0+(hxXyq4%Kzm*!0-tTG1RQT z{UogO|CyH4E**WbvD+Kqw-gSnm^4XI&O;7-LS0DgpjC8VX%-x0&^I(q21=*GsuAE)fvy=Si&oCF82F%a*dPtd>O!rv3dKTzB* zfrDVB*dp|ohKfT!3Sa;FvxM}RF(dy7c8g6F8)I>N&yJHvj~h&~*>PgUp5v3&?m2$K z>FAM33+LvGZt z>lW(X@*T4e8Qt(8l*BgKamVBQC-{yFy8X!W&X;G-UuDE&my}j{2l7_4HXM9-Q!@c` z;poOry9J3xHuETVBP=DJ;qkj)+@EegabL;NLOHh`UrLO5uHLyJ?{VoL7knP$UUk!> z0q=VAMAzlssk={|b2Xdb_bi=OV(@V9;IoSC=S_@5v1Vs34ag>Y2YlCgsg=2Us#EjI z9I8gN=GoV*?kLQycUP=V-fXzdJFmHWZB9v`K>eql>{R`KTzlvHcv*Vh^|g6^;QoY@ zvu@S*4t`b6jefluk8Ql~v)cB1*w3r8c7J_F_4TcAzYdqIiGafX8Sv^_`4>0J>to>#xz=HQ@ajZhn3Q{{+@ zPSMv%;nDqPm#&01DcN!+Bf6L9yh8RwmXyRveC{qM+3hZG)HBqq z_c<(fKJNWV_rQd#8HZ-1RJh+36WuK@`u803l2AT4zVG{kUuD>e4w)dsJ<3n`Um9*`{$ouyCa=Mm{uEqc>-c-gY0# zPFn@XjJUgvNsJ#si{4LNcCKFcJhzm&uVUKfaEZNb)}?mc7d6H?R?=MdZ1Ie^cA&KM z;3LWjmw6GU6>fXg&c_{^6gI8 z7o#I^Abzsl!4Eb~)QQ>Zc3F@6G<-g}6sMaI8vjY`0$+07T*@9lSwf)h6bB_+hlYR5 zKRTys`NxP2T&>yhRHJ{E#(P=ut<$K47p~`nHr#4i8`Dz1(`J9Hl0smQ-+|`M^&zyY z!KDrfe*c*1$GUdCeEwKcUa*hlnJ#;cZf9R^XmsD8TE6j&c7H4`xK}oBK)jE$F&7gx zIaVjhWA))qUk3*}YI4Z+#=&|&$M1uy&mQ}6Iy|%DKKn@8;#YU>xU|@Ob9Jd((5dQX zKW$RSb==|$p~s3!lFqY}PT$gsm~EixfBfFZiMN`r-RLwdnr1rq&W1GoqLM9~~SUS}+xJa&Thn;cq?{9g|P7v`_vhyfmqkb;B>M zsps^8JST^yZGns4gnNB|xoL6#pn6Ya!|?{6hIGd{HSyfkZORgmjq$DWCDuD-?% z>th+$#udFgcVA9fE$E1t#^rr4RZpya{iy2A^v;2=i`@LPTjrg`FS2&2<2xCAS>};D z=9HrtS4HYkLwU=|$jZ(0?-x8D``UHAgH!0VP5oo-osYNgs+)N9c4_#^YXw_l!#{4l z_v}+kH0^2N;{m6cYl8i@uCL#5;a=>+t=qAT#H^07gv5(la|!71(M5U2rI6DX^USqBb6QoNhBmCy zUO3y@z0EvDrhzcgKz!CY0p@V2$KrQy=dcaWde1&Q>)?3bc}j}OCIoM1!rRtWUpTH`O}F6o$pZxaPxG}`;`q;f=SNFdBX&$rSPSIM^pZa}U0(Ek0X0Vr?b~xvinJ@c!%c=lZ z%j;(weNHb9yz)|_Y2m(`{oZy#*Gg1W_(A0a)WJ&h5b1FGaJ6}^L=@TB*>|M*4mtcxJYwb5_{Yz_Amv^^LKQDO~^1$TU zg6`YZji2ksEX5q~KmA-eVq(CT7KxSD&o5bia=|l?ddp~k=dyva>N)%Hu^+rsKewN* zzC2-y)Mm4(tve^|y0L4D3F9?0BzMN*e{!l8mK@U1*izJ#?U|nKLp7{6$<1sc&(afL zK6g#R8v2n|$Ei(zudp?%A1XhZBwa&@q~a$Ay@b9}wVuDdJm%;Y!SU3V2=#2Q5``rp zKWCq6zfoVIG~0>QmOGJey*Li&9v9EjBW}RFev6e6w2B8C62G= zyUa{g(q~SM4tN!OK#B12=AALU$9J~eBfRbK!^<)xF6LT@Q+91PeRKKT{8F62Gp3l3*I~q8y}anzZNKW#qvxy#2gLC9lDl_utYl++9tFLA zBzEKZG0mE4tq;!*2HG4vNWVCLN0tI^57m!9?&3bP`@#KLksbrF`%f)j*&ATrvpLT{ z-aA08^MvQ%Yy&UZc>&Wuep$eD*IeK=MeMrW9o5?()ch1>*B;xvXB=w=(U~H-U(w5| zMrQDFrQ@z!xEcN1?}&SzjB2=*c=c2BsskM?v6bpQN?Xn?o=NUBFM0fUwKY47=Q3B7 zc27rPLGx1a=51R%;wpTuZ1cJ4kVQ1}!FLkk-B(WS|Fnv<{0cAh>D;<+J5rCnFI%9* zIeusgCL`yIyUUq_Z7X(m>>ZqK5iw(MbMAsyt%(U(yJyZmno*vHlR|b7)t{@{Rn^rW z&c$o)N;EouFXyz4S?KX2A0Ajwd8?A=UX^;>Xw2)0G=AZNi?b7U+6BC>(Jr=7nPA;> zGKBc$2yfE49d*a(>ZkMc{hi>TYnf|_e+$<89=@Cg^_UB~eea_=Hnocn(Axp@a zuBeurGJmOfeXQ})8@Xv#%xbyMN=pRdU~K_8jgOn>}kx!eiGnGwglDpLi;_);%Yle*5;2 z5hnce_zRBmv%9bI#gaWHP5E3G+C^(MJ6hvGbo})8(d#$For(j#3l*MkGTYLy=+6E( zD=3VqhIQe~&+Va-+~}2Y#lG=}HG^VX8x)L_e=@=y8MY7Zm|copbn?vJjJWY+1AFUP zKd(ytHB7Zfh2i;O79 zmTtUs>H@1S-P-*We%SV0Qo~~B1lMB90aBp2Z;n}+r9@N>y^M_`V!*;$su|mVZ z&&y&ZPI>n2iVsH;#y`5ZLhP8+PlI#!8lSx`c{cWy*7%Suslej9ncIF!4Uhd&pb%mtFX$UCY;-W@mfwx_%;4X2&e?rZbNXWX~RQ`M!nl zsIvEA)e&**LdLDEpG6DyF38_&)V${Mpv>`UKQ>JCc_W*^xIP9auf2z^IE81}aOeBP zyS!xqlc|NST$bU%lFOw#mOff&rycW`|Iu@SBFqR~~*Z&sb@7LX)`RS}0^L0Xc+r|+7^O{-y>hdkm zU+j^~^xVD0rC%&;Ee~5}tz~`x{?!yt6<4X@VyvPZU-#uZ+OE>mX$0$x*Xu6cIeSu$ z#LrJB2YA}#^-6WG-gn~6H6PpK15rDdwbiDLo#1DeUKf;87#CHM6>b+{oXhI3-14f! zE2nr?PW_{T@;SkoC$?QwTM)?XVqg*;^UB7~j9skdKc5smF0iXPIhUTJUD zC=Sde-uxlK`FeJzSEr(p&y}Mo>juKMs0Lh`KRw88jYjST8Lw@3C1$Su{#G*4ZqMcF zsctnvzP_wGzK_jwE_h6?zu}Z;Lu{D%DCF+C4TVQ-xBK;v4WYG`%YLL-);v^>+cm50 zX=NYRl(SV?chaJD>e&Z^{}%u(K-0fqi(dpP(PuA1R5MKAuH+mnL2)Og1iLrvo`(f} z!`Wi<$T=?jec{ES}9KCxxbK6SM7xK{S zHIT|b-u+4G~fRJB}=xqq`GH1_IK>v9lW(|6w-Rr-xfhL!ULA%)2*I2w_iW#s3Y!CjBb985uXura6G!`i5hk_&i>$s{piX8{=BbA|!29Q^%Cs#PV{!FKe;;_gpKN??bE z!9|s6PV%7hPK&#z!=83dGt;TW{IAJZ8TO4h2TE_cNtRVG{41+@BZ=k*v;qY}e6cNk z^v%U0a$8y=imf6m8?%c z(AL+Z%}>&nuZh;B^p)DWh4k#Rm)l3gEG%^OkKE8wHkkbQN(iGF*F<=b;CxzjMtj6s zKh3NwSE|*2DN_jcK3qP#J0GiGWR5ODWWWm<<~9%$6L||5x%oF0b!8G$_e%dxstK4%w4J6FXDK1Au|gmtX@=XRAG!g`qN;+NT25p-9n^7&!HlR>S1x0) zrSQjTbeu;LbLl89DHKeCmEAtMZ6dp^AwnM+2DyD-i z!P{R3#hORC_-)~Jj4>b#Y56xWXd|J)k3)xe<3mkUF^iakt0B1IQ^Od=wXWyi1iVM( zd1JziCx*1;=$%etEpbJue-JTndVYgSE}hyX?^|ae4IHp^p7%1#*2>|c6wM%VC6Fsg zf(W!%kG26y3&!A85F5arYdZt1{p=IaeX^JA=eu^Sct#(p`>rRnyZO9BhMsQ95yZW;(huhpa3hC>hcY;_Cp{hb>A6* zJNRrhQC`DN_Gp@aro~ogx%$%LB{j1tx*Y`uz66{<$T48He?pWMpzoQ9Zj7rTqvFU& zQxv5Gysms9DP+MAFfdEkTQ71>>4fUteGHL9?CUFib*$ukETk?2`2KTGtlU!P;gNJ< z<5k)lqWeL($l0HA7Eq~LYLGg2gn{wBbfKMV?|2k>!#jtVH-?*h^2Qu(bD?W~b~Jk$ zYQa&+qJjHlFz{nyEx*e!A!Dm1PZK^9Jsm6czl0v<5Qbdj)Vl0XVtv2|vHRYg4$WbHSR$3$S+oEapXI)+6>$P8gcV8ioU) z>1OThh6iw!Ql1J@g^~sHV~6KQr_P9#XBOJ^%_%b}tJ#^1lPPP3iI(a)je5Y>?m0fo z0+okE{8RcLOGi)_A=UGR{c)5F14-)u`W&~v{UN_Krin8M6u#JY8_us~xA6&&Tzacj znPKp77DP2<-m#-Qqv9sY+E=dqO!!R~F+&9pd%v~e6ZVE*C*ALGrMGSLbI~9R4}aIg z@!AudZ--iiutUHyC4HQ3`0k0OEAsS#y~t&eL(~g0d!uU}W3!>AhAN6$iO)pnW`snV zFB9Kx4!u_tdO+jgTqo`?jfa5amO@8Z5^>5b9&0ZQ)U_8!x2EZ3MgB3Gl%xoQGCQ*= zsvDF~c9@|@7)uJdajO}20L?(z9lvi@TPFc{nAt_tlg^^O28G9LFKla&OYIITW-6wd zjk5SWVyJF&B1j*d`nI75+n9RK$5(!Wh}-I#_>u&cqbq+RaUixCJ2mX8S@{)D2w9qR z@UrNk6e_gD2}DDxFu*6jJ`z+&n+f+o+grn!`&}wv#!;(vd@NYx_44&%?9Z^X&RR4$VM{FJR11hqe-XE)vxgJuAVu;&+wO zn9LXa@Qk&gByH;Af;}WC88ENn)?#yschRhPE6gavEZB`=(Ro_ukyYJ9wP%#`Sg~L~ zn#*daMq5k55V&t_RH`v38P^7TVpfdeFF6@GYSW#C+={`+F~KvLFLk8 zj4o(ANaxLsU#hl}(TuDk&BMLEpX@hLhgtsB2*+}MchPzE0c%8qct?)*z z#ZoR(iIzE`bt1WOJD`XYN*lH?Q*X1!>jD|}^%Ea<<=ZF2O*=)-_XJK(hUN&xA1mrGi|e7iGBd0jR{9Z3)` z#>_g{PoDbI2!Cy$whar3Gz|~V?nm*Vz`@ze^7FkSWmH66=!nm(R$~@Y*_lQa?kKcM zD~SV;lSj{BS&+YsbCRp5Q{nJej%)Kq1oazO(o(51zzHP1g}8d@#r1TaSW&x6S%4PA z20r`*cWb1*td=dH4M{CSA+M#Ocu@GAlwdtz_Cdnt5m(033iOc$eYF<>y+ zPJ+3zEN%|TU?zLqQ~13=)pgL55tYRt!j$9XYxn{)0Q=asTK_1?IxDhs3K-~Db8GdT zp??!n0*U)&Z1Y}G<}dIN^YX2e%(5P>YcoMd(AL+ZbW!9zAr8+>e^KKr1;PYpCamt} z)^h`;wBEvh)Kti>yhF*6%TtFQb_rC-;B9hU}z zWD^H-C8HUmLc%4{&s17P`kAAA@J>D~1LdVT59F%~-QUt{ganQfh#myn9P3+1NXW!$!Y_a+i?6reQ1J?0P^ z(*&j3C4xa5M;~{Us^|cDG|Ndy(ZxL7=HOZS*`Ky;ckd0`)T5K-tBLd#tb5CpSgLf` zl3s-(B%W9JCYks`)`$ZL=ym?%cy_EG z?2m~+@4|3TY7pw2{*jdf{@A2jz|Gi>pGb6L@LL$!MxW9IvhEx_ZOa4`Qf0;lW;7LU z$3PrHz2oEr77vpJdjO?X^vEm6eR{q%V03z=ly~*ZS2G<4SaN^@f6%nK`K>p2B*8=B zsVyeSBwb?Y$&~9q?&7U>DCc6lm?!!(yyLD=OP?OQ;4XDS(ZT3L$Rr8&r#C}JG!X3@ z!EX#?XoU%cLFMEBHPSb=wdKQBiT%#&%-AcJM#1j5Vrr_gk-I0n7spmKy&Zttl$}3w z=h!L1O!+*-2K&_B);}T^IZOu;>o|huDyg|R&uZQonL2w3Yw2)go8C-8ak!8kgNkAz zx)Fssf_XSYxTxa`to!Hhd#KMI-V?aMZFtD?CSKvXYZ)-4dAy_fpB9Xjx>YW?l+Dr_ z6VnE_8|5xz>edEbUFq)dqYFrSOn_9}PgAL*eWcM|%lXb<+3liJ$jMIIQ`3n;3B+)qM_vBj!wb3Vv-cu+Z0jSfgA2Vm1u zE;R?6=+2YDYU9Bv)NG_W(IaG-1SUn^P#k{BTfIUiiOG-32V>XWeRF$k&HbhU@B24}$9#h0F;E#$7){+cEr=(!!g zW(ozjDCs6&6s|0x~w z-InEmvNwS@kz6L(Ti>X65KUVdc1|12lz#4 zy|3Pi6dj_w^d(;_-nizpXY09XJ)z6Tr|yHw_r6#>+KX?1 zYgPQVqmp=q(1iZVkR{UGi6}K6PLv0NPn4C+8r(2f2DKVv*@36p2Lq5NdSJ+Wo_4nn zS`X|6hG6xU2)vjB)P65huzdbWt{{X0yTuf0K`ZMq8nOs=oN-F5`Os7-#T)-q9}83C z(h1^UKV6nXXI}=O&I6;{Nrb21FOmAkf$q*^R^z0`aM57}!-VEr*wN3ZxRCQ%tkx9JmMq8SRNAq$Asjs$GQ^UL!F1X6-yL?~Qi2;v9c zlqio4JjPm@nR3RXxU5f&pvAeUFe5tjrp!mm7_qLT*5}OYt zkyM^5heUU;My}SXo4pOs!?io}?LI#a&;W`rVNZ@HF+<{B>*LMBE=$`usTuWcgY2pa zYFY~b^HOA2O>3{VoU^L7f`&ShI@{aPs`zSh3LkTpl`kW8{S`TzRrW(xGC^aubaj9H z#q@-9h10U+x>Sp74XJT6ImSu;F1f=rj}4SsQ}Wz(DrO^k0Zt2Z6|Irtaf3&8?&c^N-#x~}mljhv zP4*_i#KZR#H~k_#uHoEphT0r8ZP`YN*)b_I1&khzW;94vrP|gDPMWJ)*%~6GHu^zc zq4cz>SqI1rOmE%{pOq=t~_gUaeYLRj|#}vnAosV-ve~$$0sEqOPG|P8} z3Q?FTJw{}Ta7XqP&v1)SGayg5&3tLcNFF&CZ|)4da>?~k_+028k>9t-w%tDZ=e*YF= zF(iGHovcZOC2Z>$rSj^F_6-wFXktE81%2B4x)s|GN$xvBfz$3`5~P`6aSc9-l>($2 z&NCOp+z9(TM`XBhBn41RAEd#p>Y?;tL#Bk^8{~b^h;VH3uh)JE5;@gcY>lD$hS_1} zl-8Jj!_dLWuE`K`DqZB|OKzQNag*zA5eBC^O^Q*jKF z4Q|w4R(jBba}@<%@T)^7>io4dG|UCR>ng4+Qq@o!PAM8B(Th;URd_4#VIF$bL1onea)Lg-q&*?JwgEct4g&8vz%k3A)O2gwicP=x5m5|__O13K(`AL&UPztGm#qnBPj ztG2TsrPOIQY3Op4^c#WXfxM$HHMr%mTMQ>R z1D7{PJj64YBuJ`}+f|`n@5ZO9HB<#L0(*W22;+<(XwXf$UCTo#X4M!S6<#1kvie{Z#tqF)|*uV6sJa@u z{yurT&r+8rduOS1DMPy6%_OY`|Z31k!9_2Jen`{rl*cGl-q8e3m%PF>(KoZTs*4>Zz`D)bpOPw!G; zt&W;*_aQXw2&5+>&n;m79K?tAIc0vTgEs<-nkdC2=Tr+N2OpusB6)I?XUy4}1ehwNg;ed^$$WiRpl$)E|~+Eq8*#Vgp`V zN6%-%Ec+&Fm!jw`AVzf6{i{Ld@q0VbQtscL2m4v&u^M&q0Rqd_VWx*6!=~2vzIWus z+Ea^NO1~zFTEDjI?3+(8A-hlI)aVai@1MuGK(8wwUd6#}|qZA&a zkA=3OEXtkNAE+a3T8lNI__~m>1PYSCS+K<>jx=@Da{b@2vYekcQoC%c3`sf(nwsS_ zFJxx~mZ~!*hg9WOC*~btai`jvN4@|VoB%dG!~;~>6v2Kf&AOWnyPmlF3>R&*rdm>8 z|3C<^&i?WQ*r@`Lwy;Y?OxiJ5vwk0p@XQY^FsP*xFE8vmnPu1SH)2$#ppkl4fo7>7 zeDR75)TD=*ksVNgfDScsEuCC)d`8s4#qP5sHcpzQjaX|!^7W+~lO1KkiiHxtoatIFzWI%+tpqE2Z2LSZURNs0qq!CR!O6w<* zme3qp18B`522q~JrMsbFFUDsH{~iP(;1zr)FYTFQ!?DcE&P8YNKk|) z$^G_Lf?Lo%D{{L*JTl-)lh_AnQ^~^D?HC*#rI&%QI7?gM*AuZPX;}=Zi_n(Sb=T`yhIk>Uxs+%H> zDgL`jGcQS)clnorG3716GH9t0I#IPLDxfhA1<;Wslm9v_6C{&coO23y*}c(uKAs?z3*|3vKHXL(Rq!qSXWJ%qpMS~mkMW=Q++ z@Gphc&?5#<-}Jy<27lr-SW?YGKj%G)ok$g1MV1@i8d_l-F~+uJpb0`OgE`QUj8ZxI zHkA;JPoeUh;w2+V0kqJM&!ugOd=J-AI7dRpVW&=jqd<;-v6k|q#PXeIj>J4sk3eIH z=Rg|%P5prQfK=4~Kt4lh$9Zq`4Ii9%J4#hUSpt16Ym>!RcsZURZF$EpieF=u?LqxH zk8{Fe(40-*&%%nIg(p8X;H!Ntxe;6D>I^okCpQcPB)&&ij>;*kjX%hCizc#5d8Xmb zSH&^ZRKymHx^b!XrKCXnDo|~jWVF+=BYout`E!k{AERP4@d2xhbx2XM$eE#uO8iH~ zfi%8VBEv>=T1L0Qr=XsA(J;&3|PT?HZ&W<^$?AH~a$C!@?3X8n6HW01sk@EyS{D zh2RjzfwmDPmnwpn@}Z9CEaEjeqQw>{e$y>GIinBf;8zSpOr`6bA(yRNK@a1Zh=A84 zCovC(e5JG;^d&Psml!xsfgZg$Oa%Wsn+8!tfNO@I;#7%g_Wd=4^w?Mei*@i@3y|w$ zp5U(lfF`Owqj0OuAzCuMTn7X=H31i4+y1!!fh&%@|@pNG)pABWIRt1;;bZq4+o zDU|;)WQo$&S(;6v#d%sN_!AkIt_bSa4~aYfEB3>GI&O^=n3MwigvEqhnrZY&#F5w> zRVs#hHMRq7Hk!_nGbP&1PXUzaa29B4H;i(-1~A-zwi&x@f$aTEJxFj)4;UdQ7T;MF zH8?-h{Sa|meNbkK`SG!e3~gF1qC=Rb9{nT{b9)ygb8VS362n`Z9|hp2J&CkDzYIRO z_nBu+ZWmm?e-0EaaLPh8hc$j*UsK?{{I*8k7xc1y@1O&0iP%!2euWy|=_xXLT*a%k zpU-~HT}s5_S&l}!aC$0=5#Li4Cw{TcmWKuZ80KXMA?uTGh-xxjP3~(=OXw~+wg+qD z+~_KsRxY6?SxZ<`g)D6a0*)9ZQ`{vy%UH^JYAwY-b3Uwwqy$~zllljwjAbM;we`Qe zHx0PB-v3ionFs*#VFNgOAG)0M!FnFG>&oa(KSadnV=;~yF>gRnFPB6d{7OoT{jS^8q zewqufA53t=9ilM44!7MP^)Ro;Pz11JR8vMey6{3;KBD?xM1L|e@oS?;Cfb~WQGKmy zK3^Q7OyYo;Ls%V{sU49$k#%GTT~07(N00-h&Cd-dqgNhCzIM%VQIic^PZaC&>m&i} z54P7MEzCBX&Z=D=7wUT{sb+`$;z@}wZ}*w%JWPC~kd-P6N7iKz2#1Spr#*z-()jdJmPrE+eUZ!2+a^iX) z*r7zm5e~H69gs#d_QpcpAwaHO!uhGU3~%s3rC9RDfuZZC?goi9{_l5I=c%o~lN}_} z#{Kq^%e;d^hs|!3#iYN-%-{0Dj!;>Mpich@Vd~o83jWvN96DK7ig|r#dyWoW1;V7l zcKd$z9L`k0`GRjI?JGbrHFy&UN!*PdOSg_I%#z< zE#xQ#&}me7c#e((09I3K_mVRGv)M;O5?iB+b2`KTM1e$&!YAim;*(vS>LBVyjqdNZk5}}YMTUG z1;4GBj8E|`7vEO>d=-<9s@lG2{q;kOWEJ_vbj4$$i6}ea`0EL>fMJFJ#W_UrV~l6x zpsDJpmio!g35-9W!71=Wl2)KkDSqI`(5J86jrN#W^_kp2?kVLuz~9 zB6~LA8*!vzVf^F!W9xAQTj&QmX~jmYaL^#MGIr@~vQiu33dGkz%g?9`4lv0ybM-8s z*gO_hv7A-ou50+!#J+%(mBKxe(>pq-mPn5E#_=LHbPvrxI_d#N1zCc)=rsj<4pUob zZI2NVu}yedz67Zm1Y)n%$qBr7Kz$#Yg1Un6e7JX_U5KULnP&sI>pkIn$Xm55s(Wuy z64xw2{9(&Wfy2+{I3h1Pj1qe9P7owyO_e;=vK=lTdR?$aK{0}9%D0*_iU4(raVXxd zdI(*p<3u!PCpFdlM!LIXp)FR~>5lBc2;{y=R#5&pfifApnqJ7{3wf}lO%$A3pb}iB z!Q}%9c7G0s`Re}tGESb^T=qj6{d?eJzPSZ?4nDFl2=Y-aCeU)#ub@EP@AN)8cwfa_XP&F>d23E>71C+S^+2?q zt6_*G1IIfT$lhZ?&!@@JSx+C%X79FJr&xjMv@}VUuuL_@fIK#jtJoZ4K996n9G)!| zcv<0N!>`n0kiA187S_Cg+bYQPR8IaR&_T<v?@41Gc>SJ2Z57f2q6VNyT<`440m0DM)YbLg8m&Ac=EeR=j8-GOrA{|ah zLUFapdK$pj+PF$s;;umDkR z>e~rlK%Snc?0bUMs47l*w7Jnpg>-5O8EBlA1Nm2i>*qK+B)&8p;rUqMornyk8p@8_ zR8JL92iIltY$N-dL6->8er>oP&WIax1^n{k$4yJfDS8hnHpna4a$Gs=K8=Q~2y2M1 z=Zd86hN~@G)D;msCqhzoP&uJo-CAyQe3K#Z?)h&Uy%W3^FP;R}LH=ws>Pz`nYu!iM z*6N;))awyS`-jjf(AJcj-NPj(-GnKWetH2CYDKG0jj6$AHZHfqt*w)AGEU6zKkrC1 zK}MsQbJ>F-iv-}ygIg%IA}%vJ!r%wVLr;6QLWwZ%9OdULxWQ3!xg-#8LFa{gYN0zm zNYC?|{YRQ4@e_IEu<6BrhAr(9y$^4z;*au(&@hj{5LSKO&+!=u9|pO2?ZMo8)H1pp zfD$~S>M5?Z?cauYzqGI9?htg1mjP*F&+LCuBBw-Qk3s93cbrbLx`9Uvv_9y|pn}17 zn`y8Ztjp%)_9Kn3L`|{7pk)Y|w=UR7WYBoUf1zmZAK|59|31S}u$tt+wDPSJNmMy_ zWLRl{R*2VG4;N12>5b2a{-}>8`G}H1HKb!FxIK2Pr3c<13!1WGL)LRLmrv{5!eyV$ zlYm;0Ns>Ll>mRQ_v9k4JxzgNQaV@SZxT=JUov*x%^*Jy^4DC>L-@p1EUF2EIGK*%w z-yv?V#g!HsIxOApI~7z_B*o3e{(o@?wU9{X>RCo_Ek(Gw#e}QrkZ!PtPf<1;6D~%$ zg|FNV@n+Y!Mdwu}8o;;koV4P)@7DZlvBl2uJVCn@lU2Ph(N zTUZDHQ${nMQg2gT2>(0r{D2BsI%s?0}jCAk@cDDXo1FD-p%-zqbHS^M) zi}j3k=Qdthb~!kL>OW5Ib*hSpT1XM&MlbUdrlqRt3A<|TB*%-#E$=bpD%x5R7l|+j zPz{Nc%rDl(n5>+R&r9VwyXXC44WAULkeicPenu#;yk?M=-pxy*J7CMqqkPK{{@q5x zLkYSc-QKcOTCv_atP2ie`xAX`6hp8Sxwn1`LUH(-aumSNl=*-!_z04I&rqP!sHJF|`2bS$V8x9yq@f%?ggZGu zMLcB~^Hvj#1*Hr8>m-9#eZGzZ0eo3IL%rr@7wV<2};fV+#B?DuA|SC>R*3 zVOlC+U<0!EuXca-D?(RUbaO}?tCNMm#yq_Zh1N-Uay9g+45vJ+}LgikDqMQ>>c#T5q{nk)qJqJ zk1zNDd&a2FfO(;#Bk3=ICe$Deg8jv6#!$PW7MH{RAB4Z3V(hF)_{12{k@oGcd|`#h zNfPsAf7-sB8M|J2=vGk}b!#=f1xxefncSr7?;Jq&Hs3#BoUHdE{eolP9A8%i;0aBL!SA8-a74nV8vHm0x9BtR2MUZ0JIf#xyPG$@NUU{z$V@`6vU1=t)9rsHbR+QEWZ{kFL%IxV@{@Q- zQhB*WlCGeC)bmbPlc=(kS$EPC+v=QxAj6MI!Eqv<_Vv2tqaD|^QI7TqK2P#WX5MqcdA*aMvH zRr8d$Q#_*Wn=H8i+eYVbT1#uVcWNs}5E0VYWsn$2H&b%I3?r(UorBJ}bqIqvBdId~ zK7_0pPlipLZM)ovQ?Ce3v^c?ADbZ0ueSvDVL0AQ<>_#%B0lM3qWDoBW|g=?85kCv~H#(7LOrv z=c#H6tKZFoqMFV>At|L9}kQxlUIwmGCm-3WyMl zU%4pXzYP>P1ZmsR3dF_`vl0Ha!YH!lC-?5@J1c5hn$NoGkbYclCW#S>MzL99hS(+_IlZK$gXHk)q*#NrB z#-1%6s)w?Bxd>cTzJce`qkbx+wI)p-@`3y|4Nii`+Zd}y?kK`N=CI6W&WUEl&>+N` zGJ?s`9?J*+Kg%oPppi^?65}NN?$%vjxRi2}OU!-`MOHqzeydaCq;b3`4EQ|bofYrAevh7Jx~GWc;H&m zy>BhaC}G1-`V}KCxs(xtNLGDgV?=6gZ_RgBN&Mw*AGHu-69lcr+!IU|!pM}t@i3V-O8ciV!#*Ll&`;>mb^G zVQ)6*H2B`LgYntq>VaRC zp&25JUCXFP;BK?M*|tp_T)}nqO`u%dIN)xNv5&N?RWT z43xB{fddOKQFj_zO*Fv1?;A3lS<`(j?0xX*03&QEiLfNuY)ijq!96D>*5j$(w=8k+ zO>;8PR`qz@%v{n?77C3lvop2jiH@U6IpbW%OTM-(AFre(EvrHZE&clTJf_MS9i!)H z`f*JV^r~qIXJb&{wS#kAVVo_Vpj>7TQ^?{4wU2O>1;c$#D>P9x_IKIUkBlqr&WySZ z!|Qw1riW%g{<29@KRYktJ{nNR?Oz3k>R>|#uJP8^mETy!+1pp7ldW2vrxai5NAF&R z3oa53r?o)QF+i7B&9gYJ6ou;(1bm-G?e<5G^eRW`(HLWBMtZ z_UGrmKd5N9q*q5g?qwlz4r-ZAS^`wDj^y4%xhB>=o&7M?nJBrt|p=T4YpM#J+*_2bBtEm$*WUg$OoEzC)oVJ38KEE#Z zxER(3ZH#%kxz@R?Mjn%E-xZ(S{-lfEF=-4>b(}e666RI$B(I?t%U3hcT`WFoM)E4S zwkIo)&uxPJ;D6waZlMYG;A{a=EBs_ffK={m^inIqSA^Dg1}0JjT2T>nyBFHZ|0$2J zSB4GCsizXBE)1XUOCxcIP?Slw_0$rHXS~?!29bFTnB+la}RIr``QOj7S zYJ7EX33@#SBU_Ple^xm>h&i>$?ESsle+%=NyjXv5wbrYlbKkXNGZMqbyFWwUC*uUuD zyNm2=PKs?_HSaTNha$+AJ~XIYyf&eE3xGcTEDcO!e|{PsFE>0QzF{ zGkSwI_J|P&5*}&GFN0O*Z!VP}XY3qjK~npM_(h24+w>!QdNqiE^=vjEASU?3ZXqYN zAhlVHhU%!iH2SyZD1;|G0Zs~Ns1>N;L{oc-i-&XQL4-p-it0j~v!n{i$f)f@Wt4DBP9d{$#>NE>m4%_Z4_(E*!R zxS15dtU(Ai4H-C-M@pp2N)(2et}7x_HQN_=JN0nGnnb|6A(|~&=fvJ_U$||wcN=no z-3j3!6>N~-V*ISW$oVkkBYJza8cVioZqTl!3I}yY$dEeU8L_Lbrg_00vb9bheQS53 zZ7*030i5~8V`|w0G=B^He)V2byZ2J)Pr(hTMc5g7gM9nK zLuAHhq~s>D!Ghm~HtVGu4DR8iwI}#AZBxiR;-jk;yIYm}R^w}@RnRKre7W|C-#FP7 zKBBieKf%pkD0FCph&?$MjbYqgA`?Pk)UOfgOkn~IiMHCUMFp%Me$hXXP`!x@>o)M?2JR_kX$fDEa3(wl|X5$x<*>cfAcW41+H;VsQll{{F zNHUNgb7A? zAt*};g0}n-$nbEwv!n-C4K(H==}C$_wZ5%~D2YPi|!_VoVsb>@oV3g)RK@ zj+we}s^u>5nNDK66y3108;T6AdUZUw?2PWy+1<5i8!0mXDK14lQN9)$SR?Srk{|GW zyOPnr5z$&igw-M4Gn&k(s|9F)UA%f8SdA{=zc{V{$rTvi>uTeZA3!2XCDtMsrPkq+`m zw6>HF!LEQXQamzo`#Ov72!-J+@2LWy{XsNl)gzrmY^XrNR zB~Dh9iOYgfWM|y4$v4F4ZR;>-SNXc3igJjGMkOd%oTEpl|5Xexyd1K;F6Dz`Wj3;-yZEX3Ew6z+v$$wrR%}LW<|ck_ zukArHd9_0JotTW_+KnjogD-PRH8{}+@DNfsz7!D!ii&;$RYF^eMSU281tUF|WhZgM z50T8m6?4XDO^Emj;)PO1cQ2hC0X5Deas_eKjC5wSNYV)(b|M8O!&~@+ zBg?3QLZ$O9XtQM*w*iU<^)jcoL&T)A#bK<3HM8xzGuRA91zbe#yJ#h;l00zw8i!y- z!FrB7*Rw!_^iL_P(75LHt5&x{W%}py)GT#9=4r8v0qktmA-1W9mySmLZz5WkB!rbi z|3`4={433O~t0Y%@L=w9!f~PMVQ1fr+(bN3#QN%Z2WNE8WIqI3`byqPz#iBgi!(ToB7|I z?5AHz+xJbfX9lxBm{rq^LTsGCMUmJ{|5Urt)soKkX7cZz{ew1ZYZ5vV@_+v1!^o`u zmXK0Kx;@cXpZ`1(JxV&Gk;xrXrJFCOTx;vne?n(x!U`m(8mY3Mfu6=hvoI-)yorSX zMRY}YON?FAxc%{eK?=5>S7`SBh$X<{!?UKSo$m)Ekw$c$npxY}{j!?pS`WZ&{D0h% zeEB3VQLZA{02Tu^q5p3x#L%xD0pxFrM;a9bs>|`vIdg+Y%t(=!$K{AV%h}r>$=G8X zwaZ7G@hft?+EmYuSx)wD?Uv-+Xk(~1t%Kx4TmUzig42ejW4D02MohwHkoL@_|oyB187L zZ3!1(jD>XncVbs?dY6!eW}K&~-zVQ6R0ohM&k=_h*wp#O)J~3$x#e0~6b}i6d%xee zR+gjF$c3IrkBNIV9;dcT>6NQCJvKiy;(B)dtwGq-{Bxy+<%ssoD`J1@JX-DDH+9h_ ztT5ZJp$rE=@VF%slVpAF(*ymss%ODF0JpM!UTA$~ru-;K+-nJ|{#7|;_~5Yk$01cA z`Qh#z>%#DwoPjpTCrL|Jlb4)aZLA@wIG$qg~H^oYXSlH=kjZ5Wzj!Je>m*Pp0P)bsK^1+b7=N-zL z13d=H;7wkdAqO|xvr+Z9j^l+bi#e57g`YgYkZT*4B%ciM8IW?I&mNQ1mf?5He?Uex0*E`J`?p4e1gYFFqKb4?pK+$vcV#9$kEZ76( zxy3~OQ#x)#5|G4&!Zw`MtvFl}vP_Z$OU{XCiz!~VNGD@a-TC=hPpXH5D(zGk;&JJc zK6UJHDTvzkclBe=2egSdQEBT)zhb3#k`|@X!q82zB?CDtu1)TLWza~&A;Y{(74~X= zhSB2#J`!-`z9GPehZL4Gguff&66bYulUX@~Kg2oU?23pQ`?4?$$hPi^`X84;dzSdP zh$IYbDQ7UKn$fKJe0*v#IRIN2-iZ0VcGDq(iBs>bQorb z<0?-wW8Wy5Gcq?Dy!}lN{Q63;dZ^3i_0M!RkR4wLZ!uqD9+FYCFw-XwYSM zsAfh>M}*7u)%R;&@?~~oME_33?C)QyhK!7a&7%T9FFpK5V>HaqcK)GZZus+jMj`^s$y@RJ7+b21oZ8m|e46U<4kBEeaY;rV=x z7;VlG;ckUY!m29(-gR~75#KEu1S^8FLlT5`PcBUoZKDr;@1ap5+>ci==p#@8Y@|n& z{cW^lfIZ4!aWd4}7)??F7G)9uIzYw06ZiUBc{$IKud{|S9I%0sY-vf>FS@N%HhBp> z^iE*^EX;c#SnT@LJS?QoZfK4W7b4;dAlWqwXZ4q$@>FDSj5c|oX2$mCS4XQTNsSq_ z_y#3j??1k~YhshcoEYBscP02FJK!wzZH(<1z^dq6?NC@NqMyUsffH zF=0Psv~`~`X2`CaFzZaW#$l9rIpM2Q8JGooc0l4I=9zZeS#4BZSqnto%R@9i=YGgh zRc5mx-Z_1a?RYa6AG!SmOd{!-4+3Pq^yr39gi8oh=;KS=_jct=+84)_VT59u5a68I z21DWdq=DI_6EH|vm4mC;iyP*|)7jy*ppGpS{&6jBz}q&v;zYPo=>fTc_mT2e2K!$) zie8*^59!MwtEjQ2z5hx15>O%goeKsZS+(1Vi9{)G zQh(1 z>7Bfq{n=jc+*58!A}Fcm!PnjYINuxWKMMgqfaxHgW@v$H-lS$ zw2G4xgLHe6^nqXiFmvkeYSpOi7BP&fOG1}Pde!uZB70-!UTEf78W!yFA ziyCHGjz?%d@%(vs8wk;St#PZSZZs)M-S*JG)2faOLDd_r^iIR5=tqcsTTurb-F=b5 z=E`#^$OUpB(itJvC4CT(s|&0bYB2wD%F4{v)c=E{m z?=FWU(RDbcr}Y*-u#IL{x)7)V%Jm7fEA{eJ2u0_kTlW%%$2|vxBt$BQ3YV5`kZ7y& z4L{|oaJOx-{3dn=WK<6Lh>_)l@g^TYD()siUp3nAKd!E-HnNeXK~*MRl4CU#1FDfl zjQ-GnychL_^eNk_nplfH&Q@|6rNec>ekGv33S3>RBp9pNzN08IVPbZBl5G(WxQE9t zQ_Td?t>663!bt_yOzKY<%%CH@iGjI5PC4y^HQRG%caC7 zj%zKMo63kfcF{!f4$EY(6()*3@jG*!XN+KN6*fl@wBiV%CCBm1EDx{NBK8o@qx(c= zZ_8^@{OyYYgoh?s+hzr2DbGdkL{G!o$|!3=;@`t}fYi}JTF?jV@qXyp7x`Hoff2(Z zqh~{iu_-7j6q3Rbaq>HI6%9yPEWZ>LmHEHIJhvV-zZAm{SddzcJhZ*jXTcm2bd!qt zQyO%!2rgE@>C2t!uOGr<6(F@!)^}_}sn&o}UD%$oq=ZWY$ITLmFb*Bw99P`)!+;lD z;YIBw#Wk+omcH)WEtvQI=}zmL=%SFdV#ie$;0Dd)0wt2OTYIH!l)9;fDr*=hD10sj zRrW`%f8^F!kwqNR!vOxC(^dr-W~UnUX`7lka53nbh2+HAwtE@j4PHMajMB5X_BHD- zxKpZEjBJx7ym|S)8Ej310PCr?68~u)vSZW~Ti|5!6yKH-DfeCe#{w6hPOR(Lz<5!L zt_bkDlQE8I9QQe*r8*9F;WE#Gx(Y1I`Da_kzxbr>;z|HKAgD+seMzYlK1jPl;W~4S9nWNut!2BB31yLtjaIJtn@-CG zl$Ef=DL(`j`eZ+i6J5{$C_naxRyvJJI({0BDA zjJZ3MIn_e)^wgUuo^ha2K?{GgDB2D}&SsGTTK2u?sXoSlG6~iFxc2t6j`H{H%D?}4b(IZ^uyg!C`0afjq;+CI7IzQXSB1X#rs1~j;P5P zk874^6FNQsJ?}UdMxLUqvBF>hbN^^JvYXfv+>7jwNqB)?8m@JpEG6|5(rMMLK{?dn zf_cyo7}%5Hic{%OOeacyNTlerxKeRqGwT zw1I|>tqEK{BS`FO=%}N7%kwoZET@W?94~?71=YHr0^c9P+gB6Z$iTNEAEF` z)|+USanPZ>vd=nfiKGkmCKccbG)SVGt26QCHR#2lwPMnmae(Qrm1ZUZd!0_dX`R8n9b>q}-3Oul^}F7#_T2RMKw6wk>1-pQ$@cn!=daTQ+b3Uz$$ zIC+V6=q2_lfq8i-IZ-oVupNX;Eg57RnF_O;wHW_CHiXGsY8!RAeI50VFt>T}7Ev3v ztwmVbnvrCLOu-IH<~={n<+tGS=c3b4v=<0D^tfj{ZM8Mc5NZQZ-O$JrRd}hq>c>2f z5vbWhbb1KJ)$bjd`p_J{eVvZ^LN5F`;>xp5SO^rh=};hpB*4BNA7^@B3jri+ycs~) z-aW{9%DHyAac+inJ3O)*_fiC7 zT|cuZ$~na}yO<@alFSd^#PckW{?!%EKX+hlI7OP$5yZa!mbxlNyRE%W5N&<&)4P!b zMjoI$gUkVR0SSypcUd0TmOB_egW^^`8gl7xES1kQ6#+m?@xgmtW9s#wzugy?BN6Hy z5fOJfnIW#hJio;sY8nG4%Ab&8URDHX!I!r%Ex1=oW<5Ll#x7;Wmb(?!eklaYu~OHp zx95(cVa$u8ftsAV?lJwL1PP|>W63-GEFD)wRUF{i3GyRzGNL5#SN^`a71G&I77zAS zupR5UZ?&&wRU9nDJkUq(^O|6gOACPGXB<-O=NeHG1k7B4mUC~P&U|tYSEMCKVqg?V z(I>5?8^>9gk?aZmD3%QJBYIb$C%BsA1Z41Am4377)a7ba4Gt$;UW0_lOj|d#6NG?WxxLB-6k5hfR$vdLw_A5z)}OPu@H2k`uUc)jv5`LUaVCl!|}7G>hnInDwM`^fME z1qPsgF>DJf(&0|%unM@`Knw|+^t9!Ljv|L6f9)T?#e4}?D{1eB2Z1<^_*iRGER##2 zL)?ZV)gW26@+VaD_&-zE)(vZ>}m&>kH?PkRu#Q;?Zl0i)tKzy;xoN%wcZOn`da z)jPs}9bjPkr!MMUbu%MQi=Iwo3X+V_k=S1w)KcTrdpUV~rY#7N7BZsK&wRfZoTU)g zX>EdyzPSzj6Ps1~a7p3>e;D?*=-$NfE_?;Vw-*xJb-W$fqd2&i7$T+26Qp($8U{vYOJX@r4r|MzRn>AZXoBl)b16{>t&O=gE3cb*R2W{sV;a2hH9XcmBHHV!fh=_RruC-lA1UC*hu&c<7QceOAHX&&{2yD4 z#-WY>3vhg7N#Es<=x*!&;MiL61^qxyVlApIyz~_qTstlZC3gQbvpVW5%*7CA}o*Zu84W8j*j`!|7|E zhti=RhtcY*Fkr3}gVP2snk@x(ngBL2g^E4aRj`GOT@2+#y%nmtvqc!t$;Wd~-^NWQ zB&pF~9Tx)xYX3~0y~5$3F6mIvZh!9LC=bM##NVtp1F~^|#VUDPQp#=*YUh6#sp3P} zfw1tDYy&y+J`u{qPbdivW%F_=yJs{?dKx&q=h{}cC##?c_HKWy@5yBt*E8^}6J_Fz z57QWDa~~x$(OV#mLf(Ivm=ao@veu~0W@1+xr%!furevPIsC1NE`p+SokEe^gpxDg2 zcz!Z8d``G5bZPdM_IVf_WzZ(5fo$b8r{407E#aH@SwhQVIq*Si3{TtK?~7okCsp6X zBIrkh_BzVT6Oeaz$&KbORT#_(16>q!hP>5TiR8^u){Lt2y&-3b=VjM+Sj`CL2uUv|FlfsZ7#%4+uDx(1UZ#pgZ4 zl;2%b&9(siI)mgm@#yHquiU`7%DBkC%xZXILqPEx38hdUl~I@Jg2kDL@_FBXPDKn~ z2Kf#?Z%L6T@0p`%tP3^A*<4LiY8HKFyqgSG8Ufx1j55i%$q!PX%NuQuZiCIlz@&{? z-tNQZQiM+X=Z^?MwKXu3i@Ao(bmrqgj= zc)E8+L9+Zdfc{FKG;M9n-Ti$D(i||`I$Zf^$|J*3HE((U8za-pT|Lf`Hm`kNP9~sl z=mL;Ol86)9bGsNrXjvqrWC~&jHj=|T|1SU~u;_`ls(TSDUh_(B)IcQ}o&g*fD3|r& z`FmJjXmSF6VM}hQ8<=wd6#rC`=SLRa_tE+Y&P=1wI|H1_5P%QQvE}7+#<6b+aP&;j z7~Dig!KSCVET!ipPS0PecB!B|FHI2O6q0litKDlLto9WMJx*RxTfpFUmV5@o^G|^njD$_ealx z*KpjHo@_yuBa&JL2OFTj^yGhY?FV@1YzCc;yQ_r~Mm#gxFmc@i^rqAX38*Suzo~3Z z7w)r!#)PpT^YR$x28PL}4ZaBCM%47lIcS=K=joTLIq@whZH;ZTXTBu+ND!ytMyNI# zYHrh5NpCyQ*7ioH8eq~0?S|i>X_KiVq7uELWGtjcc6~e1w^QRDOsy2}eDpGFL_a=C z8)}FW(JaIk*nm1h*<1kW3OP}_Y?TBf;kX)kF2eO1`kLZ~^Eks)dt26&i+n8OWR1up zF^^-FHA4?t-A^D|_w#0P^Rl);3u~1%kZBfKm{rtmm}o%p2|MT`5ExQFZKbFvS17o+ z3OAb#4acvzn#9GDnX5T#*B2+->imx(|3$d}d7!=&II8mQL9gJy*}4cdljZ=G#xFQg zS1Jh%(!lf?iefB$t#4Fb4ViePUDR0Zdms3U0L7<9mz}5crQyzd1ipEp28oxb*0L}b zVV>QXTK$vUVUQLUd#Z-X5-35E9#ggmy(($=B!_Z<3Uwkj#?#2tR#ml{M@vKt!39nz zPrD3rczS=kOVWVTBXSLa<8NjTV&SM?Z`OyJ4AqoP)cx_*Lby%i1I z9V+7I5V!)x8ziR8AO#EEj@LS)v5X3-*C-q)>}Wmc-8$6hYbtUdRbuG2X^ zZOjAs4+H2RzO|Uc`YUD%>M(4vow09HG`M>vLmZu;<<*ZSgR?mtc?C6;2t~3>sq8Wi zP|P5bEl8Z$bqP-@)NyxzD&ZmvqNgZl_!`?Z@Oo16EnlPxszZnLWw}d{L0fq^Fy+QC zdWel&lM7&XK*^6NwG8r>OL(}G6Z3d;bwCYt=}TDxPEXm}-QnOTz0zQ@SDTn`s!hwU zDt=J^Og!4^_m84D2t^@b7!59f-4UZms%(9t9&}eM%0Zbau||)v>&y|<rtW@{3oL zXIYj_oOknwHGtB2HYkod5 zOsZdq#i{9llnN$^l3$3>;f+mM8`v4Tm_m?T&6N}-Uk&DLuRLO= zR&0q9YMFrhWf{~Z4V{ogqlUB|U#?HKfgqWuD{IiFdvBrmJZHs&%siPJwR?{B{OBUc z$&zjcpXyI`kswo0J)87}7fd?iN!NvjEl3^LP=&UP#OL1-z7eH#IG3nmk;pLabZf|7 zKYS#L9GL$;JV|s=hi3Ha-=#!u{aZ*s z*35*EXGO6Y^qTPKu-ukUtUKY8rkHpodDSpiy18C+jj2=v81e~vPm1!>7Y|}dwb&tJ zoIP)p1%X}s5qhcLkL>Gc{a>Qw3`F;Rpnn^@{X&JiZ#%(#k+%jF8q&ous;zukYm6xr z(!8^I+Np$5qV*_Q6N@r4W9_(8;H%LXzv;s@%GAgSy`Mu;p@OLFAAn761@mG!mP31@ zXvoo!i2123ULr#VA$_2TRPLt!1?&a9KiGaKc>srppYR3kW1wu1oPwOw9Dku|Tv~5|srg zsyFBq5T{I~Sfl2B+k=0?y!Fj*GRU9ruKb`tdy*YEmt#{i{N@M+$yR$;xl0B)F^^U8 zhyrkA-M#iijy_bAMPb-RTl8P5bTAI}nG$?Uikbzg5tsA67aU|G4#wKH?n?XKgq$?%htU#Q;2qH?m4`btv4F4Z(!2U~iaD-O4f0@yc z-=pjnWOZe@=WPZ=`%*^slRRKG^k-!j###r)8V6Mwh5*?N`8HEvCj<02Eto^>T}UAlxXxyvk)rvds&FEx21yT}5zqD8d%gwyjxY6Oj~uE?KzYN?+-) z*T*~hm9nf~J8h?@Gg3!Xy1nk`UijZL?q|u2{3FO{N^OC5o@n2s*iB+T6*>L^#WR&S zpP+F{uQOfWTwGmDU%vfG6hpEm2Vk&~pZ?AK1miZ*N&28;DM+1~DAtmt7u}HbFK7dH z4ZQU6RW$F{KG+-_-ka~@Q&?XD|4(!pOcQ)|wA`aVoXn$>BVlv&WksG3wRMLjx$kzb zZ#Ejn%%g$rbhkl{B}Fv7<0~%7;37BeD*M8Z>wjUa6)WT*F?)uTgICBV(bgq8t`F1GrAM2*vR# z9qWW^u>Ozelj*RcZtn~mI_fV8UL>BLtNSP^pVuUDpG@KaBDYx~s7CKp-ch6h)yo1k zYQr2wzt$0GQR_ec87i8G|5h&CIP3h~6!P+iLo@$n4N45S%4jPW0?8OG+xd5t8XOQt zRM#+p&JH1{RM+`_O(jKtkUc1H@&9JE!&&U79!>~-=ca0jwk3m(b1#bQM&ZFD$odZ2 z;C$xd=#>=g{>Om6?AfGP0uCVmFX#Op)h)h_Q)NQDi%hzZk=BF}*uIw`$yN*jV!`AG zIA;ft_uzb9@FNTpN~yt7{LaKM-L+yOaaOHhIwLqFH56qLFNqG`SsRl2>ES-eo4!Th zS{KuWx{Yy`0z(t5Xb@A@!GpqozC5^-o=)V_LF>#OD%B3NaFQ@A|46T;$mv=k>Ll@~ z)lCS`-SiT^XX&Jp75QR^I$U*QIiV;SZLPWu!e=9%Kl*r7*(6ag3Qwoo>G!KugSlCU zSY{9xN1}nugf)smu8k?8>Uu8s>!s;v^nM_fwY_85T#lx2&58dF%7iC#X;N&&3;Ij( zk#l{jL{LcHmb-rJKDmahoh_X~9+P>4mdV9k_c(U&>Sh7c5BHnqw3p(;E;&K;$`jAa zkclPu>0^lXOgflhE0QQgjqyVk8z0gGzCvibK!txC@T{akK2(DJ?gT_1N@4fEsVn#a zs3uR!=%82owlH?Ob&*N}OGj@MmWT2h=En)PcTRB@-phtV`QfGIs#bHZY^q7JE_7{@ zNU*-%*=VFD?UlO+&F2p^_81pPG8wsge7e8Ycr&>0aUVHrBvyT~I z!tkZj!2=7LPo$HT(}F$_v^8U-*y?|E;xTiYgPIa^v)t+D2P5>M2@FQ;)V8(8<9Ui+ zn@~tgUlM%v%Gpy_VnoJ+<3*vKxTcpE5+bRt!AuGXdX+W!y)_7o5Z-{T)##Y%S5vkY?Z zoR9bT%+v_rxTd13d&y6baO%UAb|?O~@@&-0LC{uPssDbIdd7=*mf8i@cJ_5GN-U2b z;iF3l11dECV+;O{(h&7u*RbqI`F1Ungg zZ-gcSM}q;Xe=uOdFa2B)SATspz zX7~x!pr?12XLl2-jTc`nKs*GNLVui&3&K)Skb~Kdj=2`a{<5*as3>{V_6f?tl)f@y z4}_PCKRfW>3`N~C!0T@EJhnrXA0_}`e_sBgY&wQfJ!5#eW(gdPhaseD(oxR#0T}y* zHs#!MQIq#jt>E=x?+;>8S%cJ}QyZ0y?fnreYGf3$re%w-rD&)?q6_3AD$&?pI8r#> zbcOk9zaK_KV@D@8DI^RZH)acO+8+%R(*vgfM8GjzoFzmoaLC#(weiaC1F!SWj{y!Q zi!n%c_YRO-IosXHZC%X5wO$LoxCt8eE^e~?8*Jf>BFiKp_YH9lDyV*njAA(xaR*s?Bcu5@e35}7*uRuEAQNH1-%;5{s&ZZ~ zY5t>UB^WgYM(U#l zpY(^eYA0v9QshwwgJ~KjwzkL5{rR2kPpZ?mXYAQ2Y7Tatc*?JH9tl5Rn6r!*< zBFL@6%xOW=@o}N3jXzEX?(To{!rlnN^y6h67}1fBHcz?H%UH@qFPi*Goj50au$8%c zh22UB?Rq$29HIwB-~{&cp5zt{e5?NMv6=ECygRR5VYWr=7H4Eg4115NgSr9o5;NqC#`k+J{xoiI!KFIo7CO&3p zrKXRxgSBn^`eH9V-*PRWNVz_z)e-Mwbyk<_hDMmtrbOrc#dQRpf+Awx!lu$T34H>0a#JbO`IEp}kBg6S zcx>^g+@Oe-75e$snJYdm-Q@@;7e9BDd7H|2bI&bATmOrJ@rux(Nfap=V{T$w!P)U{ zLe~2#A(Kn|v;36n5q*lz!J&U|ttrXBi>?`Y3hzIe6_%$jvW`C@-NF9@K|9FIAMvxe zfo*m_1XKgQw-~tVM|bVPpYt`40x`qZ^fE|;0^4^Le-*7^(ITE#h#pmYvL&Ev_yi45}T3Gn7lW7 z$>btpU)-JNM#g z#?!W&rtU*_)hA}K;Pl?p76R|AeP(xjwx;dwZoH*BZD#A@6fZKRE$@48}d8Xhj3xzz_GRx%FrG<>c~bZFiKIr8O8OK;}fVK zF)+>O(CWR9fRGo+mKon{-UwpB!zr~ISS~dbzpS;2?Fc8XP|#G6(X4Hep(?VT_Cb07 zP1aq0z(nLQF`=$$6YmF|&3;j8Yf#nMwjqe0_5#ixw?}H|(IJO7<_v@(%n%wOIUay{ zdjrT_?T2tCyYROoLY_Ag^_-QEiVKJI2CLvD>7(7mu@~aff!HtXNNs7ri%H)3{+0Ph zVbP3Ai_!CD+2v5Aq_TUZ@z5CNblap;AY*T5i~ubNtPB$R6X~NAVz6%2AszO=)h!SZ z$a%dH-Ec&0$*WqZc{ue~(pe>kEKnxs6L*6INb=((Nbh@xYvXFxXC{Yj(O}J)Ym8-N z@`N9c&<0uiufYZjaL;vIo$4=!c1m!E2~s6&l|+LSz9eSye=PMv5nPD%#yssY9wUjR zxwf%MgDr~3=dWgUZ)hpXn^_{w!8lXf&{#zoeBj#MR!TFzL?<>l2Nsl zgkAc8Znb>jAQmkHwEYMJda3OeDxH-;bFatqo~2gD`a)`?;*G30yB29pAT`9ZW*y6Y zCzA?~u#@3d1ug@ii?N1J9GQJUtw<;;o7XVfarHl`eg=6xs`S5fYTFAc_j-+0)aBrJ zb9l=+GPXL;+bJbbBxSCgTG!CUF?7)%T}tmOreG$o2Q(aQceSyI-0yH7#H>~@jb37| zv5U^TOPJnuoAZTjWps{Nl9jL?F!2+VX|$?M(9;m#zzxY5N&d=Oy~(-Y1t`Kao{8F zxs#p%_Vr|q;D~_3R|N+1uoqB&El*=N^D~vpI73Ub+~gr;y0BQ^Ie{EoJZm<`)xveY zwwDyPXV*ee`fa}TuC5cU=VH%bbOzUtl17W6sQ|&cw!mKKOnUdLRu!c^H%JJAH_=*H zzE8eL0FK0}pk#r68h6wJt`{E_(_F&A+uu4*e=AmFR%l8u&x;3W8_CQT=~n8AbVrAP zg=Ekxx9gxQ?7pJg4-UlPPlOj1uS!gvWr{urF1~v$)2AY&D@L&8P%-5V_bswkG@|`G z!bQ13uT|I$FWLa%i8xrU%Zya+Sr8ZWgmPVK+X_D(oKS`|4Jg2nACK`fU4a4S{2*mw z?RdsVcV~IUu??5{gaCIa!CAZHK;0X?6TBm6i6t-roxfV)GgDk{M43ESQYv7yL}4Jy z^BF(xP5NEDpKUXP9T?TSO59)xP5M@65s`#wL<5?h3KNWVYGEv&_e~VhTS@iF+M4i> zC|$XWDp6EGO|gRnoW?^~cX4mQAhG8*MCQr__u=qBfI65n#oAd_(P_m`O5{aB7%B7c zMfKZF_*BqeiI*G%VlBdwi0lu0B+k(XybN^!$Fohr4^LB%+V^plhP5dgFQ9EOmQC4S zfBoICyQ)SIgF6Kufe!ri8lvN-#e;`kU}#ee3XRrg`u`64 zdZZ*)rxvrUv4ZB9VQt0k<8rl|U?bnXPU6uDk%)&rQ~A!~tfO87x*C^ZxIa)ql?= zZCmB``j7tyw5Ra&E+dg%WLq_6Py92%mrhC-SK zW0|ICCKQufySXwQcleQo$-~3%?Urp$_;p4zdK#*l8N=VRFlzP(J3~H%-1vgBOe=4( zVMeHBMOa@gRLhVqOd&KT7?fv59-IU;2VfLkL!8#;vk&8BCS_UNca-{9T z=t(Q>8wYUXNUP$0(Mgo7UZbON`3@!yDDy(Rv9t~GHxXw*w;0uj#=g#tmz(|GrDN|? z1Mzmz%9U(<#->@NZN@b}v!37XRi}RdQtqK-x{QOYLmMW~zksmgstx`jf zAWE=TBV$0!U>^TG14NPlrKyavW-M;E06Kys#0Mvt1s^lp8((q~?UvE1*RW4LI!qHOY3`%@CwV(--!aci3nRVdnNS6G98bB3+sD5h+ zJw7e~;{B6OAHC&McZ7-|V}5#ak;eJ_ZQDRF>AyA+-U*wK2Ln!Nw^HlpLRPnpV=9;= znF~e!El+kw%7_bk6Ac-yb+MxXdc|WqT>S!}=YX5Fnl7(@#v;#T@ZaHsDmxu<(*qeQ z7PUi?vPuor=Wc3j^(xbm-Jj`q_A3*Z9iPPKU^vat{!wVHm}I0mrMw>xlAYC>>BASN zwfmoRD}@#TxgcaSVyTzy9r6-TkF8Ezs5~BU-b*Z9s1nFZ{oRm>cbn(eAB#R*vr2hdQHmbn z)i4875I;B0^p%kv!F4PmXB9q$t5=^hvGdR zg|I_+)R>*2IzF*g!btny=#W_*-cW}#(GES>GT17b`X$2a1NRf)cs<#<4I=Tou=MBQD_BJ(hfS(+ud=*skIJcfaBJrL+6GQA9Q6C74WKg81JjJkFZD@MBlN z|3z_8Yri790>F~xi+|?WZutzLrzq1q%b{#DJn%WMNCIhCNj3nLRRBaZg*ew3xvrs} z%#en0OE_H~UzKErGIPN9LGiD4IN3VMKHd)C!Vb5!d@J#E@ciA=-J*DQVD9+GY2}H< zx{R_-esW!O>jn1KvpX!DYRJY*6cP*RQv9ppdxN9cN?fV8X)8Db&$3bSBiOzZFS+#% zlMP{h?&(`D0};QIOW8)0V8FJV8Wblrn&EG7v@mGw;da$C^8yjR@8ODu40_G-8s8<=rl^b5X`ltYw45qO(Pg!T5b*YhZe>!zk zuag|?z>NSK9L_|P;sx7yqY;i#j7~0f%Spb)dOE=HU3E2nW|2o98~eLN;PBb_f!Gmb zLJb5zsQ+&o0VXEge}&V7`3Y}^l;Xnw67&2H9Xj4Eg=c=sF*}SW*c47j4!0ImdR_sM zX&`nk%-=+IA`;AfC6exmP#YD@PFa-$%&cW8`tD{mjWLf-n6kpUX9C&&OiME*lkE@G zw`{w2;&4F;Atp4zQTrZpceKxw(49==`rIfzz*S_+Nu29uUIWrt2R>j}!-=rNXdJ0L zFw@cBmeF0%oNic$#L-smo0U+YxM`PH_VZKXsj98qQF4SBL=e^Kdt!HGf|PrYC2k1O z9XshH{V^r=9f-VM1mv8CefNSn3r;!VtRXI+ONG-Xk_l3>yMkPQ5L{>105#qah5p)I z4IKI3;8F!L6P~d2S+|d;E(?6sY7f~KfR#?+KSBWqeIBq&SvDO~?w4O?-+239utH5UcWt-}v2Ku@q-z$uuMD!B&jzB*k=PUv=>; zqQsK!?UxAz|RHOc%Lf`#xaI}t5cunm$KZbyuy%_#0AkosIyOS$lwFn)W z;!#+ED5iK;^0tmPYFIq}?FYdlWho-YnOAw}7iiP3zH}6sQM=9Itb}0P`AM{&OQo{j zCe(GIxMNX6p*FdBPp-2X0rttz#gTx3Ks&7zM2O&%~~%R_@Ib znbe)yON9W`#|cxxDkL$bBse;_-6cL=D=%BcmQ|dEHJ869I@+klQdLZi__w3tc54!s z?_brDvX1Se=U{@^DO%dUStIN-Hg9eL2QzP*fey7WDko9plxZ3%hiH zQs)%5)RsJZITkNf-*0(;ey4CU)_CZCcs5nxR;*JWfv?Beu!K&L) z`v+->eECeCLTMk{U1j?Hw@uBZuXQvTB~%w;d!ccS=fG}<@Qa1alD`YT83-aoXke%d zTl_t^07iVR%O5Q{1cM1|Y*C+8BICcI_n&T3D{)|AqWO?mVz3edScuFWX6T^3)AM&o zl)YTg-!2_aCq1WRgegwSXfeDTPQXnhUZ{{Ia~gWNlVU`DhV&Q9WjKlG@P zEdL6YSg8|NEv#|J4sunhP+^SriuHa-)E6E^7S*Ka5bO$AS|DAtg_ut%q#vso6Bav# zCvlByHX}Z_Zp{*Saiuvnsj>{}<({!(#oau6NP?U6gNq_>oAX)!165fKGJxI<{eS@QZa}~#Rc0SrhM(@3{v=U0VY1yy!R^eVc&YC2!Y4ejJ*xEE)ID+-)Zau}Z38+rh9`GY!>D_4-eNT_)wfk!HoDhNEle4;)fcarUfJN1T_M-b0-Gfq0ro zCz9K!(@P@-BM!L!(;pOLleONQ7|{-N@G0kN;d%DNOj_F_$!10EVNY! zeUCgZ%{{;T5$?UEApIe3J{*70xqq;Poc&fL=si|$X+p3(#5c41xf)5$YePr%(PQUT z=z^PyP8AG2xWkUmBr~y5E#Yb?3c%hamhwvVnEV}Z>%Koyll$Q@Q3djdtHECsgnlBz zLV-Xe_ja4tNZb*#;<49qK**b3vQshoGt3xPA-m}rfKU}wnhLe-OR^l&ak9~EFqDfr zecOxFtmL5s0#os)9zhYdsd{u-hFb*Xw~I;|kg?#b=gk0ZDbMzan2ke>$<}-N&zCvk z%n)13T$8K?v=}>Myos43<`Cq0iq7O$igNTI+6}>cxO%MA;}af72q;?e@_$2T>rc+Z zqum3gdM9D`B9&KqAg#mdjiwZlvGKp1oBK!42UUA)hF_>fwfokOvWs-JN3~%t326vO zyIF}Z=)?#mG|Ww~n=*As%u@l`vr+*Q{g=td z>-|_tu?)~Sk4B1Rl*ZNif1$jTITr+PcIg*^FfULe@3z~rON9)LEdfh_*E{=^F_Vl} zw0e*4M!HMQdNE;MUDtc^$NG#zxKHm|2>PQc#2tx@Q1%;}LEN{u*}+TJ z**IQ>cK|u1c4`WyLlSbHxI7~>s~N$Q=J@QqQ1+c@bXrQ|@>e=Q86mUp+@2N#8_u8_ z!ykPJ_BelL%^D-}j>FbmRgx2r%F@pI!65JF%|{l>d-DgQl*-7G@z!Pz?VEk13DY{i zzEzUh(!H3xi3EgM=t?C1!l^_W}-BxKR&H8rUdofD4@;UuXo_Ip^fmPP%?k018$1(*pC1{K*7J4HyrJ)){$TX9j8Y- zL^!ZsQD&6z@>1TUCH*4jQLN{BBHX_JH7bCl&(TsM8y(M2B090+0BoGD8xxX;^^8ma z_(BlH{$9VUv~u-Rvt9!;m|^;vGtgY{PV)rE^(1Q^Lkh_Y=L1w&mLPd6sFU+vcW^%w zVrO=*uOKt_7nvN59lv^)v5*)JklU|*2_YjqAsnD%H_M=#FsisL?RfquWIW91LOZ)g z;I8(;ZY%|$~Is0d3*WBf?R#e>T0$Lvi+yQjbL80`#E!}(U!EW zl4XBqBqK1!{ZBUL222sa3QMa?`)J6SQsN&5ALg`z2g~S&L>;91?XKCWDIsB?x_wBi zjwEsOc!*mV>}hVM0Np#n2kjog-DVp2a{>%wO95sBCwq|RGWrKJ5*hc+RJt5lpyMVCf`6AE6{2y5Jh9w(zNh0bLdkvt3p>J4sQ^YLyxYN1=I#iva>a zcJB+GF8gD>=YIXaOOYBq; za;ei)w02^xUZUv-k5nzzo&(=M(ro2Tx6=`XsyJF~3Q_tHH+ULY=bA1jdt2>TUgC8} zpK)ud^?FBK$U%3=uf)1=%_brnSPbGmwA9qWH=RhPk~1FFC&_PVs-2C4@;ZnVu*M<9ggjZjxb5)?p?LI|g%7_FkDoxplOqTD} z{1|u+9d=1tHT7t`24(xvpdn~fIVi~Q&Z&O;laDRgX{B17OwZRg+=0Y{=uYTXid@wq z06FF3AFHFd9+|vRfp;9xoq<5Vh5NJo_&;RVkvSG>B{hI`|67qs5hxDTp^kpl0XDkg z@{toEfV66~tw85%pIgP=%sOy|RQ^$el9bAYepGhY$6~tj`*fr_l1>S|XvlT5t+{4M zSJ2#UgoPezp}6KtG~)T3pz6D-4=R)Botl06LGN$x0W$JW=?dIL+p8F4nL_rLCQacx z*p?KZ)O;p5$0t?QD>>K-s6tZ3ur{T80%7t*>M{E3-~OI>KK=5zh4jBVXhXxh`C4MC zXm@L5SQHt{u4-u+I5}ds?4W2+XiG6=jLoUicVdsmz88q<3K*Q zug#kT#{?t!ToIIR6--N8lS{V9Fivv;_}{o~K;%|L-^orVO_THi28xB&91T8<4-%VE z--$ts&aAa$hNVzM)D%i}Q}c|<3+$~%P!;L#aD}x9Q6)|yufrNf@raG5I99+O;=HKy zjNZHM0>@Q(>WgdREA&Kpbt?aV{$Yn;Y6A)gW@npfx7+C! z;^kCKeVnebr)V#UMy{-u&=B3c5yRo(K{K=UPi{@MK*fHR+xE6TS^m@0lcSp??I7y^ zAe4Pe^SG0{Inw=`b#&v;7M%1&xT6B819m6@zX+YLd}R4Jyogl0{j5j0N9c}(z6*>(A_LVS5A&6Y>-Ie zK9)$uQ+>EOJ0j+68G-q8DF1))wV`Aw#)tZ9DrPCWdJJX43Rf^#*8X-?B-`E`DBQ{C znPah%K@|6z(x9_GmtbdBN`$T63V}D&O|nIeIF7|;1zXXa8%#p@dysOZsGuKniBpr1 z9tKv_%^VGK>k2=or~qkxMK|g15Hnw9cE4gj2zRoJ4JfvVJJ=>EbjB&VevTfcb=GXe z+C}@)(@i5IM&oPT@HW z9y;NE*SMHNtc;mg)_w4Y`}xQkRMwu7nVp4z|6HtO+BZLbzg?Y2p2ppriKlNCUNv5_ z!=0Q0jl}S_C^L92^m>fvNZUe;aj=A+dgMBUqM@i(Q~8_#bVh8KjB-^j)wMZcGSaJq zAN@_zGCC-f2E11Uu3-pBqNmzE*Hprm7<(*^67d+iNLb~{2B5>exX_n(49(kw|8LLG z`}KDBad!FVe6UXg@6F94%5Kqs5_da^^?INkD*Wdgc*)T|D~T~0x0lcjfS76!0nbKy zFgGG7KbUFsaiH6}SKxKQo7?Ozval8q3Xk4b$W)$I+Kc$gW47Mrq`E7o-k*}g1^LH! z^3MK`@p~Bd2GZ~K3R2pa zQKix+;d}~ng`W#rr-_KU97#b^$Yi+7Kh!)(n{PabYc|#11^NFCY84!98R7l0;gK>m zl8CHX|9{l%E03lsG}lucBUJ*Lr%gMK zl=_4c0q~uy^aq5Yo&QLZxku4j1i6xq2NvK%d+p@{uXk6oS-+1{4J_}9^@Wl1DtqP5 zQ0t9AttOMSapvTNmP@b=@gv{oa8)V{#2*0A{>9PuSd-uXe`dnCC>XQL>}rrP415cs z5=1s2Ci$^b<4tlgr1XwXI%8--vT}YW^q?PqZsgdW$-l3f6ZBh59@jD6O>*{wZySw?J zO7;VXXSapaRc$#K?$DZS^-2g^r)G^~`>s|0aHMYYQt)ez4^#qL4v9~p7INe`n3jya z1m%i6Cz~HX89sqp8OBu16}l2PuxnZ4o+I@qeX+byMU$=OE2oJv$4_0o{96XsR6KR& z+>H-Ua-o13Occ0B?$BnmzShg<>?7iOWaUF)DEetU_!zEFyJ#C)(abA8SKg79vJ?Pj zwytf8ToI#%`A@b>>cLT0UJdq1r43rl!)_DL1Q8bafDuQ|4D4h1`8CY1M*+#Vn@L(Z zMH#MG5N?&9fYMd+@LE5X&1wq~WlNY^kzR+_Mf>e4zwlj`7ODEyGw1SL@n26&_E*&m7sc=76mvza|#WTG3RAOw6)!xHhXXajDn}x>R zW<^Km@gYtf;-G`_$-1*dd*q*zGMoV>#=DzMl)l=JUdR=-d92PsfH{IKw-2y3TLy;T zT>PWBF$Li$7<_swr2hsTK*F&Fsv3vq`>A^7B+^znMLN=EU!XIp3P*>12A42w=tPy@ zO#O(?j7`WGxB>by%K**sS{lg!65ecF10^-aA9sD~Z*+aLQMiK5Hn{6x<7`O0VZaPJ zf}EI61+3Mho?W+w?+^fa0o9-ZQS_dEpmIvkFOeWuJRPl%q_QLKfSpak&N1OofR2#$ zff{JMCVW#gJ=TrYOMQ zLHN56Ap(RfC>DP+TZ|w73)~$9MBnr+o;CesyYfE%cKK(67N`R^V(_8j56fF{H_ub> zT@en;YZOf+wj<53h@?f#b|=iVMbBI7>UnwR+V4n@T+RL<3hv7ud>y{Oiy)3z@MiEk zfg=71s(-fFsSI$LrpmVWUn|fnCMtX;sn}NXFPw;%oXkIi)B*`_vaKRAffQ#7cfWcLOKx=oB z7=JaNneLFtBM@J{w_jm2b&6as{#;z>6^1eUfdNPj%rcR8dv$B81ajGTSPeQ_Ah#j` z)Dz)U5ZO^YJKEgLOyz?ct?C}&S8Q7k3jZ>g39!TnDLj$gF3>^@v#)Se9q?5FzzMHx z(;o~i2SU4~U;9;`!`WnWWEb3mL}+?u)3X(i3ofsmMcbVPgq#d{)#LHpyR_$JVd{qb zDldS$uxAZFVd33izpn#hd-{Qx${qfe+FxG_I+@5Sqz==a6RPUOKqLCSqipXp@8RL7 z7ya97H;qHU@2HadAtJ0Zj=-3=FvThyhmS13TZ79_;r>-PUz#jp-K zme47k=3{VWD|j<1$mVL67r6J014VdYQ6&4I5@kUd+hbOJ1k)^T?c1r>Dvqw8e@|ByTF>vlrKnX)lyc3d zm>MX8C`Ev912YbJq2!Be)gHiD9Hu~8ebcJ5M>5mP;Pc^@9t zSfrMr{Z!8wN^h3q;z$|tl1=Vw($uXgQ~1z>^wNEz)@Gm>MWs=8*?h8vi$KecsQg^J z;j_P;5?c>x;Ep$aF&2k((`d_ieQ{Z?57EbUx@EcJ*yHNx%sJn;W0A-5j8K3V-r6*^ zA8ybDklp>+@%%QCOmg)6Vc(92O$PW0G3K5T^LMhJerh$vbpno{3Z~~hwcymwS0RT( zK!RTH;^cLjDKKu{s$*I3L{9 zG)Q$t{~o`DIx=>se)iaza_qZYih1mUKh;#%B6Tnf_J5#b{!Yc=vYB>(eYf3bg<^-x zb**EO<32cRYo1g*)tG~v!xEtJWuGy}ynh|f94c~;yg0;od*ZDM1~J`j?Ch)j|b zBgPg2NoS7$lB6%VG5jTwNQW8O=u1m6Cz=V!S2Z(Y)z7Pntl7(UJR*mEkW~ttW5p^T zL*|z6kxM^{l7cY&7M)bx;r{E~sRJ0-MR`M#cXjwIe8O0fjpE@RZ)$QQH3n^0VR)DJxxSeZfZI+Q`5n>k`6k8yg|^ zz$W(+uwHW0btDeow-34@nmRCs8q?Agm`lc1p{*Z2*Bw4{jspYB2$*_m;-Ytvi7y_e zuk^7PqQVF-txpPFNAnhe$pr!a4eq?T997aFyuME*YRL5cPlujx{YtJ7ya(=$b)*r! znwK&!TQl?zPm#X-sX`_F7XL4-caWO%-z$;y^!y6QDQGdyaQUH~Zy`Lt4;)~C!%1Za zYN$MItxcL1?dtuhUNzQAPsr>#gP@cMGMi^}>A?%XU<&p(PlEpYYFdokK7}z8er?$F zJWG#KG?ssYzXJajhOfI}as6ilrY67Wv7aO6xUJ#IvX`P8qx`q!v7)%Gj>PPL&U+L|Fv7}zssPenvyu1W)e=_@Bt<>)*;T?6`?TVY&{`IS&Lr+% zpZXrY!jqgen^l9ErQJqWt5&(xkwV98=3ijT6|pfSkbtm8tqRMt`Xmf!V zZn0?pOGvW+7L%Q~)P&LWE{hbxvv+9!9II3qPb|yG`_(T(-IfEO!>Wah7DwLUO}~3B z^-`P7SpQ#O5s^?LgW9O=Gys|-HJ#3;-@*0aDbWFBZbbq}lPDOkLX9 zr)9YvpnkZOjbF(w1@L$QjJy$Xv#5uOPZ)gOuMNJxOdRff*1-}Oq3x)*cXwdYN3*;h zhJrsw8=;!G8d`jxsT~z~K3|ogpp~Zso1xiOl$dWYz$J-_g6=DxTaqhAP*Q`+%XvN% zwfo(oH(eSO#p9C@fAtyV5Is7Q_2|r-wApa)Cj1=X|B!zL7KeH4(s~LSK|t%C?7k);o{H*?n_KotxJ-PB`nB2XfH^<$^@FnWA7YSOYq)e| zmW02!B=PA*(P1dA_s~X!GaIh~ko8Z6%{HK>HasF~o{Q3HGz+n16`&nu6}HXvF*KA= zZMaj=N-tMA4}&q{AM#1sp#+4cP3}WO?bKqm{iV{bWzi{EnY?m$1s%{o=Jb4@&PlBzw0;}!4xB$Y-wMkroolXYz3|64tl2p$bCaGQMHYS6Topm?gaAmxhH7I5@1$P)};w zeVGZH%{p$2m+Ub-->cLkdQveJ?6_j=EjhlxsbTYqGUL|D6Fv?i63TiMb6iyHanEM*KA*B`nb!2zCrZV z4o;%C?AFOa%NMN@MM;vKNAG~C6&C-1>HtguYupRL{Og5EpZg@bM6;s5s&kO)%PF}h z22vPS=ksT_D#yYaGloFoqjaO~9c)|tgr+$#dq9(58@4Funbg{(zw_;@Wx?UxcX9ws z?gERI=kfIldOQfLh_0?bjPeKZedtodQJ4M?mZTHU#;11Wks(L+nZ?xLM#4Swo#M+m z+NE`oslJFvZJ*kV*Vk7y{KG$@!-&JN4<2y+G0MQHZz|wwA+lsjH-Lx$3((9Uqbq}> z7AU_DaYQr{jw4FQ!i{p2rdr#hJORccNNwQ;hzhK@tMw&9uA(2l3fI8r;VfAtGmzRl zl%l^qW1jA~-M|W0rxvchAfH)PIkK7BO!p=Q7e*UTypJZm@4#m!Oqvp-d6c1;EpCd- z?}x&6a~}uQyLgY)T+y5Mu_eL|%EtU*LYG4>!#g=$p7`4``L2m~wea9_BB z6(=LV=-2auR;L{pp_C!T;PE})2H1hTg>BmHf^g_R4$0AuZ*fa!EUi(-@1wFLi7b8o z{S~FFqg;y$AbC9q65l?t7(Y7w%JB8Own5MQ!&bT85HE;!4(0uA}J z+saj##U{K~Fj^p2@&Z(D?}cq;go--eR&i3BVu2Eh-yzkAgdY3{dqX4 zi7W_Hq5pgZjci*@B3kR5QdC)xaKVQmMGelYERer6<_1~)> zF`E`yZwD(678L_^e=q+7CB+rAl`Zpw^`o{v4{0)@FjG$;egj2nHPNA;q zK{5`1adsZBzUcFzYX`mnS02CF%hg*)W6VECMR*Y1iq>2Frjl5g9>x?$kc}lX`a_AEft=`T zCmw)}Tm`KbS=^dU5vd;!NuJL}|pSI>9X+ru>GE^z>fm(#>gu zyg!f=U!!S~B13rb&YWWcP;P`oa{@bKWtpe}bPL-XeV_DG~TMDfNld`SXd@ zQoI*`@L!l08JYETG<|g|b_~ECe|bb~G@z(3ol%W3*+wN&H?0ijFwX}3ED6rXGWhF^ z(}_42DGW>licM@g9v^)R#I?)xx?h|(ej5#Wrj)ZmZriM4&585Fm}x+aw(r?O911kI z?jsPF_e!MsXKDmNEE=Yb;0u8l--nRbiIdapJ*%>$GpZb}B^?%T`OLL&&l8au+iiy0 zQ^bG=fzB`FaGO-1Tnf(b{UCR!Cm+gFi5bfXR&Vnb`T#x7;gsJV6bcH9F3udk-yG4K z-iK>E6aw^l_d)Whn2*b@6V+J$o?wJ0_>3qxP)F+Dkb`kY>M+eOj=|)r7bg9d;@I4 z{IO@-j%pzT-FyiHgfMkcEEem0KHw5p)oZG+-nPPMhIKslrUU-j8}zC)?CzpV_x7?N zhzbOW4C*Q%(RPc|G?kfe@qE2peI%knxj2(|WkF^IKv zfFSa%6=sBJ3om`Y@1T|~a#*hSXuWm-)IqYuz}V|}kY34ccaY0d%~WZqBXAdlnh|I& zAd(48_UCy^6bqYnTmY{M+Nm)B*iTDQ)6^IQUjcb-HtjN87Q_R%?NdXBMGNC@9};CO zp=mLEg9%uWdeM#cedMcS0*nC|3VuHiP!!3FF*?Jcwb!yTLy*V!3Xu{hg=Vx^icQO? z>(QX5TocpeJXq-3X+!GG5Gd{T)u(kzdsLZI(|=`|FXu%winDInQ}u(>FRWiO;b?<_ zAOBo;>!#7{)zfV!#U4rbij_=yacHrEU)yLMg^8ln?u|ZDKpFSypkxf8eOW2xf%^*Z zK8kPdQ~pO=Ve#KN`}LP&BRJ_7h%26;WnOu?cptz~qO_vZKMv`uns6ZV3E2#+p%U>f zQ{N$|d!MlJb`{9#Fa^0IoQ?=(G9G#k z<@vq_FU!VXwY!t8Pq9|))l8<%2PHle`cVxm2zkVGKp=o4Z|u8wMJSSw8sqoxiUScq zq`TB*(Heq|&{KQ5ojb>`UdVyid|Cb%J#iG^rv{a~T`oM2807_Hq_GvwHLxlbzzLz) zGa@IbZ4oBtZbM*H_H4cG=dxK<)10`2kysq|lC|`T#Dvwdr8Q#bIj9i(b(IIv^c?u* zKbEt^oFW6%D$DSrG2U=C$5#SLJc;|@fy3lR5>{D}l|Gh!iOvXU0_HD(+)ub6=ft()1;?~B z7|;^4+F+4&2PNUr1JEjr58Vzt<4YB>@(dMB1qhUwrY$&_PICtzjB?k}UQq#YRwIzh z`QDHw_O%$ikNdysO#9aPv^H}a^gwKjAIXXd>zIhB)WkKIr{O1uU)1Sj8oSdLRzo%s5F&i{=FF~8|zhp|1`IU zjYm{p%6EFs1shMkh?2M&O@F-_o~IeuT9>^e3H<2^blTuOZ}AnWf(s3xzBRx~woyV< z2QMc8xl)xbq7#Aj79T*%1~9BULP9nG>*;!At!nTN+CNWLdr3ggzmVE;11#cPqe{ui<`4B3FE7qJNVd3cE%`$k&60fi?}*f`DwV>RUF1_E zIH`rp9OuiC4(+M{@Q<Go63?FaZ=Er|w1v2F9i6-0$T6T4uq+W8m z+K@I@hK`Wg3;h!mYC@Ek9=ZFnXja0O)3J8;(eRU)*E&dNABr`aIWp^)icq9wH`Z}S zAu!~(Z%e-z%3Dh11G&vkeJ+9N{@ZJA9;m5_m&s(dc zA4g$$M*#g9~KsO;}4WYi8r8HRP*X(?&7Ua6e~N{qg@zq^^Zr zfyx*0%N9C9o<|N0sgJZ-tAU)p?AI$uz znF{T4f_;3U*{PjvV_zj&YF|YQi?@0%rV37NeM41nG~KHe2*hG2rM7Bgt{JQ|UQI}; z*u~ktKoa?#MW!9h&{Ut!|1QPb;r&RVb-O9@=+_J7?6vBKjJYsOOW?=n-GisVp^J-JLKz3f?8PTC zsRhEXAf&C2@ltgEz(#y*xk~G8!8;j6jnY+9zvEj1cq@@34LweH4V?H*XzYh-*v^uf18VClWTHq-fRlex+ z2)SQY+v4FL`K_X;9GgJ@d}CLCv$u2(q#SO3QCL;?IIEUT9aB_Q=u}oaLk3rhI=qmC zmZw`QZF=!8gS6H5wJsbJYPpV+dlW)*Z-21NNjH>U{~o>CQcuZ9N;^TU2!=47yRXG+$^@qJ^j}yR^w$J%a9()X<7DSX{H7-gQhtHR* zHO}yYf(t*KKqhzWW}&I6L|C+4hqAn%?R|)mz%_(H7OQ1;CoSQ>*{os=3maXz)t$Pe z4*3>285aYG%9xadm_Jk9CX{HhcqSl*pY(?ha=aLIhCRx>!n}VdajP)vX5`AM_fPdA zHs1J;%s*Se)bWjsBC{!jkBcsN>`lE4)rdV2AW>@cB!{XYPF8IE=-`q^2dVdcOLZbb zofSmj-=AELo;%m%KbzKg2dY)6iw65ko!fuE_uhSl86WEP5Nms~BLFrqjNzrzr}*ol zDXBLk%gFawp(3H5{Fr-~X4q0@KS%&#k0zd56TUnKODP5^eFI|Hd7{^kyx~#@JNhVQ z)rD(6&o!FWs>Aj_b96>la}6g)?~RCPM6WKO;xkgNL8P^lzQPo1hR;J<@exibV9lX{ z8u(49Gk4d-DHZw>pLI==h7?H?@(8F#ebR1)pO4aQzv|ipENvROj!%=|tfMo2P0l63 z9BfBKu@01K>8+9e>oEjZ{>Gweew*^_l3QdH)F7iniFj=rI-E~q2*-$)sl8VW0d%@f z1x+TIo;JT|-zdj~g#b7pl{~!W9uN^wF*PpAP<&@w{Fi6sr97V}KHs3dj_V z4%O`jT^%Cz{NL&F)k$u6AAdqt!4hm6XE5@94R?J~w(bT@#~N--oa5#VA?( zwM0KmoYU6>^@ALWOsYwFoahK(;7nqVw8?+Onu1{8AC}pj*62zc`9SziTI>v)BI<&9 zGsEz?iwP2dp8E`=*F=dVSyF7bSFl=f$04lHh-Ql&oCz!)YKilued0=M9ak;x-E=FK z5eMPCA`h(GC;35#tN7A7ZH7n}Rh9+e=VKviNZ>qeced9YUDe+Zk>|xlbk{I@pl}~L z(}0E=H2uIiMO;|Gp*E;yJa*qLA-9?wtfM!TIx@wFSq7@4@<#t;L5_I+L2TU3=1swt zw7kEa4pP!W@$B9!Ulm{O2=Iian~(HTDTGud0^hO@&^!r9^w+03R}lfn@dFBWvppB# zTe?A8NiLCT+1Cp@J^SJGtKNsb|9|K3_3Hi}-hac@U-)|x=kWFf{vOAl!_))# zdmdx>dbi!3>Wj>NwWVwqInw4JP5c}GY?E+qE6>ZKIRvWnhlzLn*!FWO8f0O$Y zn*MN`PPY;aJFN~n^pwCl2Qc8u3uJ2^mbpwV+_2wg+V~S=2%C9+{G%jW$%fUMBss;D zV^UyuWL$n4^%hZJ?ounGG3Py$5eu%Z4Xz?sy_&yKna)$;GLRakgLXBzR4@Y~UMo;2 zuDpdVibleqaQ^_0(*Pdvp;7}~h~*%R7c9sq6}X*?u!nR_*88@rGhkdWCsV-o*3%om zkg-PK>%S=(v&(>;3fgeLD=p!du%Av5v)>lIr-4-I7VK#QwUS`4>u?fNlAfIEVOPFS zpNbeXqXC;XzcD%dQMu0sGdZ8Xb^jEDttkVfdE`!5?lN8z6v@-c$CT^a#wB&EJ2IKc zo?AWw8H<&9kTAc8 zq%M*LHQ>T5IkV%X!{M3jzL&9%DtP=dIZU8s-_n}|S=J!`RSeV323)5^nHR7hOjOc@ zlQYnm6^^J-G!@+9a42z;rVW;NRBxvXR&NRZ=YfNyKGOrgY1g)itIU3VuLZwZDrzLg zcY!YV0V7D>heOW#K~)tXHzL!nE*D1{#-*qi<79Mo-O-Z8&MhIFp!DLvb(IIaJ2bQa zREL_}-a@TXR@u*W3}-c1I8t>inUom|kxzmr(*}}%*McBzif#NF7NV4-6a64&J8ANR zKVG4z_=GshNvoBw^2_?ZOYOAVouF;xhtE`nyx?zlCjRKhaM-_KCAX=BePIkiol4VX zl`fxtYx99ZZWo=&$^e?@kV-Vyfv;gCe%CF!fwWz3*TtrQV&>Gb!pvDygFp!lo=gT5 z#X@)LRJu06|9WfYgDD5LhAYEO=Hq%ijJ8nuYeTQ&^%DaD)#>FyHJ)|LyMGy{{EH)! zukl2eQUmAaCay{p4phRJaSZ<}K$zZ@voLs9Wr}@7sO=ivZN!uLJ70mdKQyWti2c2* z_;ZoBs1YQsW-oAW`3x)VhRqZ&UlhQXGYpjtl^fkj|w9M236@zJ`mTtL6)AAQRK_MiyxAjS;5ch?FUZ>0m{)I^@QM= zE*ru%V5Aa{?2!4+;s`N78hH~8HsD3?2podr<@j6^Xi$5d!^y@c2H97JxRScw#9c6E zXrqq;_fvh)56EWj1jEtP^E+kqGAlX(RZwL|$T;vPJ4Bz)a_{uQ&fc;JEL)_7!B>8l z%vq{n??|HB+BZz531lyp{>}QUMBjn%I_5^7L{!Dn+Vy+`c0`Kv#J=wVtkzeVr>=xP zVpQtl7b3_&+g1d51${Y-z{%Buk-+bW?`+^fmZ9x3<^~KBx zJwyX+{0Ub1^abWwdkJO?CozP%lFB$KHtr3FYF+Ur0}y=(+jyycIl30S;=-#^1#X&L^Jo(4 z6Yc^$iXnUs9K{mS|9Y@>l9lj|;AB@Y24+8$Rp^svs=xPSDJilW_!_m~FFvnY2E6IL zKGIOCnowNm13AQ18x_@YYuaCljyW%*!N9c4GdWMNdcH!kj9d)H=` zz)jDO1C85XD4Jt}BR4`-(_JgO|258Vl8F5g#!Q;1L=gt zW8WZzNKyqsDepqs&_t=vV6b-%kl0CK4@Hi!+ScPRETh9$=;b0YZGQvl>vm`Ezm3wl zNpTjJ?Q*2$)2o>F%>u=}n2ohtlYv_V=+MaMlPHO`L-2dF9hY<9DJ=R zLR3n~uTej|A&fPKZ#+*xrgemAo^C~8_;=Fl2b(Lv!<)Hq0BfHeCYO7JO$In$JSO@^ z_$XR&fd*xn)3A}<3l%}CkJ5uir3pZt+x2`(SsTeAt$yB&;f_HY)Cz5uL89joW$83H z41hai;%+!t>SE9u_*S>T1;1%RldzhA<2;!s5F6)OX3*hUjxFEu>@mA)%^}RZ5v!)V z;IJQLxRTN(O$W5V6Qk0p&qN$34vPtD$b94`7UL{+j_;GM8!(Q8SyH%a)#B&YV`Y_5JrY_)N9khVvi>*` zXAPad9&fg1Y02Hdz+)xei( z`iJoXj=q%~c#eBXjrj>uV}Bz4>VYQh6lY7E7Y-8PZ@aeP9#?Lrm27r9W3#oQpkFOi z3^H^Q%~_g^cq|}lNrpEM;3AJ6mvg=sB=~UJ-;L?F$26Q9Cb2z_Obhor+OdJBU+GEO zobWz;+CEc0iCNgm zvxGq%VbDAT)C)_O&7WFKH)4}dcxnnzl%_XxEv%p&$G%jfv&#N!)(YIvhe~18%o0>a zWvJP*P~gjWkV99vGy&Vu%|3K@^dI)eU1eC};U;5z1WdO(6DR}HMy(kEZr%BVIQ&EEP6q*AprsOySXRlI9l~EZr=Dx1S#$c`JzG3oN zI7`e+pvwK1pEu1l^&AjwWB+OkAJ++~9t@jnHcHL=wT_nQvuhgmOQ;z7G9yWhhShs} z?+kms+`tyAY2IXp#hp7ju{rr-0fMs;UoAoc4v$xXa;|XSo?!L+Ywx-(Ykat}E7|#x zTO7AlBa&4uezxYY{PxG4DBpw8Ev#Hp?xrwYOB7;}DoLw8 zPAhFnvqV~jH0z11RaVdaHkAe=&9u&s@@}s_8RyJ&8I{Z&mcY(X1v~!bgteTA(!oH`HBUfvVo6E|rhnc=`buwV)_a&cLJn>=cVkW0WG@ z&0l6-Qns8HkuPJA{4yAI^VMx@8@B5j8j`2a;O^onvP18R)<{am;X=@F%UV!za`bgp zKe*@0|30mtLo#bDWM_*3JrE|r#)NROR|F+Ff=&${#U;M+81c#TgN=jy^Mouys&gLc zP_UcK$pW9rUwQ>85DXnx<5;07whcgS)22ySjn@n4NL1ZFwEG>iy^#tV1~1&lA0AKd zZZ&<-4b*W`)Uj6(ZZm0BS%@iF1P~Enu)x6W6*-lsK6*YeO5^74jx%6RRlA)BcPw@k zx;r59veaoeJV`ny6whZN30rE`o=&<|%jd2;6#9LIkHlLV0qR70~mK zL?$#5t&RXDDPWOKppLOl&a);5Em5E0t`Gg@HFix#+26(fw#$2p6I&8|JseB`si^EM z_8|Eug*S4XQ zfa_bkSAt5U(wD6?Z3JJ79Dq-6H9HFV+4YhokMW@rqX5 zWckGd0>S6FFRv0Uo7W$9mQfqdpOy^s-z@5qqM^$N|O?V~h1I7~L#Rr2Bgy7`f(tpZOL^Hdl_Ea(bg3P?5e^9>U^vgow>}0DzqI!1J z^`zaymLO2J1kki?+qP}nw(Ux*(zb2ewr$(Cv+K>?plAITcij`QqsDsajL)n%_R01H z%Y&J4r+G)sgJ8A7;+9ONZO~>_`AFY5p&8|SGFlMBkwrh02|w?<1SqI^NdJI$+`E%T zja_&sU4FMbLB|O$&K^U`%OI=`<-yN4B9TJixYNowW5p?ikuOK}dxe0f(Dus`4O}L0 z9rRJ8y#=Gj<{=$v^w-{pUvh)Z3xo4t{pIog_)3%aD`2PE$h_lxm-(vHPG);u6Wx|yl-cv^uf052k zNf=|qHR%`YW)YvN0Q9F&+~Gqh8Fy`%^XFqih?h^QMT#?a8UzzUYrN)%_;@hL?Qqfs z1Xz{`IlW5rrKo5zhbS1I1e(Q>O5FSn`2C$Lx?*{ZV+qsyeCiCuR_435jCe(-U%&x0 zfoH$VJ(H8WFoy*AAfEOkIKn!mhA>8Z|6RvgH!D;>td4vr2|nOaiorVtGAfR#Ib!t%voJJ+M|>Le6e}%Z@_RmV-%l1Nw!9y=26On zEC84E&y|g31n>MEzAJ1#FF>8#_hTT*2VEyRtg?^71_VK(GxBZ3q2?kX7 za5NQ2haY%vTqQDAH(-f_&9Z>&tizQUn!^a^ugKyOo5WUw&$*$^PC%7}!K^tAm>3|J zT=9Vn6f$IvL=mPvlzojEge2^oglfVLG}4qbI?rzBcaE(zc?cnb7?0rTT+t6g(`QT3 zuFRiP&bmK8T@)i!UL9!?T`h<>!^5$S~AYcNZd32{9xT!BdhB{U5VV`VGH{) zf3Xe+>TNTSO4SL~v^Q{A|5c2Rn+P$+eBV-sQ|!EJ%%5A&EVxL%?9MHaEgA)Q>qXc& zJ{zdHI{zr&76_qOW9Np*oR##J; zd&F9Clp+@6TY%YME zg5FfjPI+mfWw4p$QHOK4k=XQp`Snss-H5c)7W7s=U=rf;=ilXl73@BQ1L zG3;MGSfi=SyZ&h`E*rarq{jeACqxvJO%&qvmf#?S2h*Lx+lmAS%zQ)m$TLiADW1D~ z*_*{v)|&_yo5v!5spFiSfcU0T=uMLkYMr}|$vEGmjS{6EfDH0d(6Q(34so5cM)*Lt z2xULm4nNnK?IyE$5YrMmAp~xkWVUC%i$*@Y0P)vM{toRU1J&lk948OF9)C3xb zUK~lW^qvz8c>2Snk{xZF@+B>Y0=vs!5gwTI(SPoY+)`Zr%Ftr}aID~TxwX8S^>el+ zSvK4~;usF*B?9X(Y?^)0@c0^PuXZ-5!C;Beq7|-pH8y8YPt1$MuT-5YjV`E&I5yGC z%P44icp&b}!&D&csmf*;K{pH6^TeV}4}(FUl7UPx3+!3SOPYxE6HEt{p6j~4b(ulM zt3}Li3u>%lwtp-R(SGnL3@_g4;=h!R%J@F01O5fXII9q!y~_TK#hm6RR#ZGC&UXNU z@33_>YaGQ-tX0Hll^Wm1OJOJt_lN+yA2*cGv*`)!ilDPW}td;Q}}V zQiYrfo0)0!#hc_hF^_O@@e1XRHHHoByM?PE9%-lHH@lfkX0Zb6KM^BV(+aV;=3oUU zqV97od5n!gjsCNRN4Sh2P3Q87JeEZ#|8*RUapp>N%B;`5InfvlZ1Q#&NQuES52*_G5(X&z?^R&XdKDwE(6!bt{|0rHmIDF4mm^v zpZomvmN)^QkJ2IZ6Q`Q_G%9)U;<=2oBdQ)T)=*y6oZh4a-#BZQ1UyKf8bhAqpKi z)j%V{cJ(+MG#@IVlO!Q{wlNXbk{}SsBt zicp+m1iwnBYw52|+FP`?9{u&Z;K~+ql$6g|(vwWQo?Y zBJuqaJg8`^^d$=WCHw{BsX#6S2i;aD0-1le1aoyHx1l{caYKD4O*Zbbn5f5>=bZca_?Kn!X+vJMOX9YTnz$DImdAE96Tyd9`^g zYVD5qkp@VIRf*<1es}y_z$&Q+8=?-STg&iYAL@1O0u6P-y_mO?cx+o6+3@lwh#gp%xM7?f0NzX(^DlnVV0KxN6<&qz z!5Q)qLmg@4ya#c`u$puoJ|C!VWF2T3seRtn*X#wa7X7{qJs%PoTf;tOrEcfyYRal% zp@o_O-pv4N6jT-v6}mec$sh=vq0(3c)1MO!W~MPBQ$uN-yGp*?e?Onj<$Qhk_}B5} zc=w!2szdECo^7TSvBc}p3OHl7U?u{OeETinw79W?QzklSl}{&h?L6oQeT5Tg-3}pZ zo<_{gwr#fM^lJT1KECLzm$^Rv4XUe~E@cjtNo^6_CBr~T)22Gs<<;5Qz`~ldrK#6` z6R?$0b?1IS_Cma#4H8o5c~Yj+a)i7ItlkDp4V3uX3>9`V5bHaiUo$dqyERF(=jc0+ zOL1ARiAc`a~GSY)@?JvHb~>b5V?F252Hy%~nNa zUT1eut*=SYj~br(=~7$WYJUH!@p^X`m*glU#U0Q@IJ(HtPC}^^PjmylG(984Ne$$# z|K?C^Fkrta#1n`j$-GhgYJ0~CMIeLw0kD}Qs|A6(RBY?rSv+`TJ>jmAERhb$`nRk_ z$OO<~!pI5I=lpq1w(5<#TOm>N>9noY5NUg0+dwR>Md>3V8?YG5&h)vsp5NWyhqyUO zzA`6$(DC(^DOt)u2ik1E!FPj-`qyu&-A(8V)3Q=~+V4*Tr+4&iV3JF+qzxmSp7c7=Ceg7^*ujg>v^TiBs(7e^{p25 z=6Ns{fFVJ;}|4_AMsQ;+YK8vAm{ zX*0tlcWoMbd1bZN>;>H-XUxo4pdJYQ>tQ5;~rn5j__ZW3NJp;7b?=ycN#ffbVg~rdF zWB^F`fX`IUC@Y`Em>dzHF)jOpNOKk?qZ%fiHIN9A=V@C5QEQ~R-T77-!z4f65tZND zurNUPjvnhYBI4qm4tcIuW}c8gBMCBbXvnrs{bzKPbUOa`ujKb3Aa)Lr;v>0*n@5DF z$rGNt(_=>UUw9vi{xyKjp8M(xhcHX_M^qWHghlpZ`fJW`0^)`ZmnGb0#@e z)BZJ+g#FeCv{^C8{Zl&-9B^eeub4l06&|Dmm=Vm{l?@Sgy=(cv}oq zMaIxMJWZwz6ZRVdIA!)PM5A$qv;(c|R{S)WW86UyOb*Rus-1K`K^`nFU*NRpnJ@S#uk7Y` z9nBFoKgiXJ%V$qmhj6~#w@o#opT^&LS)n@JsUX1dm7boK{2u5IU#_rz0k_? zoPc&6z#8I~8|4=Txs1KiSy7t0dTPpbow{Qe%q2vAIC(a=$z!i?qx;g?sh!xbmnLJQ zLA7f!iWYyJnoS?)n;^ql9vyilN_X==63UYuc7zRyuoSrAsr3uju!4nJxdaZ9+q6_% z19ZbiIACWhGoAgFoMBlVpf1a}R6QV*^F&H^{Svl?)ias@2D7fc9A*cArbGbtw}Rt< zIZhBsH;ysJ^zk~!0YPsxf#F0Mzg5R-j}f3>vHMLPn9zqK2GBm|`D!V${So}SQ9$?M z@x_*<5OPpq390c`^oeg1$vyrNT+<*&cQ}j%wryvPXB=EqfSKSpX4#W>dL2^Yo`zU=`Z#j7o zOTd$XcEIreFcMDJQ`)Gtn7$lb>^)j$(B38&3SYX4}g!*Sbu=bS5m7uTcD*boKq)0_L*w&f7j zG)wG{fD|AMM-IH82V9|69F{uv{d%?>IAZm<`Na`t|HnXAU!UvoSVE8r*>SNR;_a5J z^Q+z%iz|DT$!N&pcg_1^I--?|mhN}%?srfBJcRU=Jsg2J<1UOHdL{tZ+=9n>zO)5f zZnL9#!REdmas2<;B59vxm18_p&GHd^urjcOx{kZu(y9bD=RLM3oDhKQbu)KimW>Ug znD#*=Omag2USMix`E~Jx+m!vJ%iD)wenx^4s_!C|KqTw(eH5G3lQ2y?HAyB7q6@9G zI@cd}ZyLPDy;i#qcrA-iB)_sL@U=Z|v)P9PBxyvnhmoVBu-tuw)k+SlfmHL%X~kzE zwxuj=ET)C~uKMe-6O)h`$4%QZry)VAaH*|XUYKMZ-f@cmfC=n0;+K+d7*K4z;z^TH zE(m)k1qrZ|4q}O+#}U8XxZ(iXr)-v-AfgVQ80a>oSk*jYn{XIYW^JjzvW^k)C{qCj z_y#BbGbN*lK-)z&P$(8PM_{M5Nx7ZmJh<2Vvbd^3ulT3Z+KQvvrxu#Pl@{;ENkLhC z&3A}D8!f)|)Ja4r@nA=+Efvv?7i+Op;xy%5T_T!2}zdx=eht31+E3`-mr06RUaG7+00f%V>V|hpC;K z)j{13FCr*A`k79k1$#)mHfW)TQ((P-YO>Ho(8a zlC85PB_B!Ys`0X4ykpMBi+I+0Gc-#I$ zb3D~Mt=N#~+?4%Raub6)qq6GjTvh2;3&|FF2tl%F?ncUj@f$a|$Hi{Ej)fj*VcksO zDiS&OD^JL^>(c%5D#lS(r$KR-nYR5MRAoH7^=wCcS&mxEJ_^lDnq6^AMLmP~ld6fi z7Va)KV{}uoT4h|p7>-FRVG%^c(cBM-nv6RZfl{Q05HlA2lnJZR*M^ooDUC1#kK|9NJHxISC4Bjb7-#cPC%M|Kjp{0cHJbviT za*6JB=qdty9MD;=oQ~TNUW@!r>`Q!-FBU12$lU+{cor9BJ9OwLApJxjn#pCpg!T~( zrj6{Omi6>kbXzv?I(*#=Rku3lfN2zneXfdhll5_X}%mwDGbeRWK`T!Hh=;WSo1kg{0w62Fp+wq1y@ zZG#!+I~$Zu_$LO2z)a+@toUd%DWCUJizkZv1V#Vh2SbpjSbKn`WWJ4a)&&utpz0@x zNoAtHyC+l2mjps-iGcfrf_iz-ZD zDn>{WFp?kQpk&q6Tc7auA<>RvZ-kz(u}OAuz&3MqXO@Ewm}B%9e>ScVyi>7}D?LVm z`OHSA-DvzsJcE2m84W-f+6+pCq9@*y;ey+^v258V>5dn;`Q{kth%nQKfFcXRLmY`o=jEL(KoIYQwh&omHJkPTQA5lFWEi+*I}x1g0`>XWZ9hpi|T+g zxhYLI(Nyr{Y#^jm6{*xd&KuODw29;g{Y-ev3%Y&h?!1yl!mWXX)Au2K{Y9U@ zDD8@9S~o#UF0P#_EE05m&iDZv0%SwNXy<|-_Pj}n(G#RPg6n;ib!hEG4p--b{ZUV5 z9l46=)+DrbXTN@N1!qw+IM3A8x^`AjcSkDY{|t;a0i2!5?lXz=?w-A{#)3i!U{{V% zyo(JSr6N*PubO00EhZ}}WBaEglMTfZ2=`o}`fACPiClf^sKs~16|k$t*&9?eG*$T7 zt}dkaI357w>baP;touq#Kclo!WitJ%4&a+DYFW@MW4H3|T-7aTFcCpKt{&kAGUo6l z#_fp8D;dpA{gTOGpeBADfK(j*aCAtTepm|7myE7w(@c3rj`Dn*iUV~~_@f`5XYsOO zEkw!8N|?41R*Y>{f-pReNVO%xTkG1~#+O*St19+(XQ7B{2jajb@bg@A_mvZ9^^s;) zwwSA|Mah$dr{m;f(_o@T@Q0Gi{uwznRc#nd5p0tSTbTiM?XQvYmF^`V(5xKg0p-X) z(xArv|d zRkB-)7fa5mx#B#m^nyKDur`RN2}oQYf=wkEiT{jJCOS&P(h zj4)o%ytmf`cKuZ!5QNIwo@V(x*dsJJ!X@!f=!3da;Br~kqZchml6G*xZtxCD=IY__ z_TOuq107Q4y3ZP9WBF(l7pBiUL}|9rA{Rm{H-KLs1x!a)Nr{f3M&I@++KWmiBQ_`X z$4f?95FMmAj+H`38qfrRPw2cAQ?tViarjt1!d#ChQt2E?PFi3rQN`O{@PsEwru$$`)hZ)O%>aWE9}Mf8@*K%Vafs2gSu_`m; zK29;V+7n~~;l)k0%4Uqi(Y{r$QS+KTKTv``-{slqPYkwtoBGn1XBqP@O48sB`_;66enF1kv@KPDEJK ziUJCVbjbZ5p5j=#Vg=*(HY_@o8IUs3bQ?i13k( zKWi~VxdpcNwHna9PyRqN4&w_Dt1)7>0i}#nxU*C!_GZM66O>hcyb`Cx9-xZ5`qK+O z%cyLy?i`BEJWEG1TQhZ`Q)!XNl=++`qZbo{%$r^{ka%e`kUg5@M2T!xb|-I&I!DND z0!~F)C2FWVmp5k~%|rwGLYt+cT~fT*@M%ZZW=4>BzB8PPzH~uK=j-tXK;|l_*PR0+ z3ugW!i|h3V;d(>~eTuOTq9C(=?k;`<_|F(wn5jYfJ4tvw*V4Os9fs6ia}`{2X>*v~ zGckkmC$CvK*Ue17GM?(*oNhGLa%wzxEK< zzoK&DEVM4z-Bbz5!0z^g9OF)SAYb-HF{>-s$^Mpp3fk5-!SQrXM>$+1v~Zv-LlJ)x z-6i_lUgu`iVyV^s&qY(8&hsPKd8KKi9D(X7T3l^JrFJDV2PcvM}_ZRAzzoDtV4ZY{WF9p~)@!bSW(_sYF?Q}*HF?@Q~d2q_^X z_mzpH)C>GF?)7d{%h@qDQc>c>2OeJ8$zVq-95$^TH#Qlc#f9 z)K5@xkd#Ncos!l0_vxHtIOz-*pz8!uH)GAvR1i664qZQL?CVm=FvV1kJI+uTl}2y z+S4lY7=4ajTkKL;%>j}NkZFYc2{t_&=|3~K_9~2tz*!0e?%neeP2G*9O*{uZ?1Ib? z*jP{29JAcqBBTg%#x|I7&IoJ*#&S3l#@Ls-W)O7>&3DLoVQcMLwd-XJ3HNA;%piVA zcupC@hMp#SPfv2uu(N{V`G*>faP|I-faiYAd7^FQRuO;$Siz| zR1i%>dec?xCNmi)Ci@wRd_XmaRXI@?G>y)i`4l8>3pT!4KHaz8XC17UWyg-+#Q6@UEsGxkrWeJakW?g%D*F%{{~#rY!R2-eBx{J6cX|FZTPD0ONf zLQA!eM__eksfsxFmSet{9rDYx4wr)+)dQUkvwmt*7*9w}ir@@A2_WnB-xe9kR6B1D z$#DDy;yq5BHT(R#?WaHBFY}bke^yP4OBHgZul*+VR<|O^s?nnE$uao-0dl;~-wy(J4`OMn? z>k2yeVLK3dN}y1U~vJ0=&1En_TNcsDClEB^Wyf*)QJxdH0sFXb;N&f!z8KxW^CoeD}{q8f;YSx(SywAc5$mN}0GRlLIona8q zDYH%@Gjx_XI5NglI660knT7?00vS6xi5!T_o04LytbK-H)z$3@O+Lx?3Wwmk$dK3rjv^^OQ_(}yTT2DR$T$#&lon6#ZmW1 z;6;i^GxKz2fpP-0<;;W1{x#QJbON9EB?zQ%*0E)}?O8+%_NwURffeV49o8_r%XGi1 z=+$RYSC5i)PRKG)WVV@mR8&K+Z)gIZqq?N8X~`gsO1rC$OQR!ElV+(N7hsOCc%`hP z7P~)tR+{#hgD;LJt;1z7MfP>*VcP2hPyVY18oMAW&yM8WIfgPA6sO%~rWJC&=sr5e zwm=Z?^J(VT`x>^{7mi5anqn;uEj<20dLb0tMd`2JBKYv^Vvm6ghZuypn>0%-O(x7l zBPdAcBr@!k=$d+mnXse?4*4-X_xyWlVMd+HA5#(%Z8EcopZb=#Db*pUyF40}g2Fw3 z(FIF*pgB8^oSv2XiGXaS!b>}xP3T@!l;X8Ko_gXO`}&_d_R1Clzi&?0gQ{_4I5IU@C237I6@LNhN7p zG{(kya25cU;H5@ZVh}bM$}OqojNMI@FxzTc#Q;G_^g+3D%VX-D6?nP@aFBpBQDeT( zxe+7~X{H4CswXkjM(y zl3)I=WMjvq=&wR@ijl@shXykI*y&7?{EILV=BwYV7xdSVY$8jy{M0Ka$ljs_A1~&D zq-CZz@6Z$?q;5zY9so^pf(^P!wgPttOpp!(4cea#$#m^*vpFY%T(8c?TiIqtM}o$2 z8ttnMm|cC0#UDnWg`osnWm3nluwgvaVM<#K$SwS0YChWG>aYS==@A01@1QO8BCc~k zRUPx+;lKQ(`v?{*uGlPqMSekxa|LAS(CY#m_n84TTY@*DjIQ|&IV&TRo+-A{IRK}B z;rKBK!2XC6h)*rfmj}p#umKB?Xa)0?osR~=xsLKpPDp7y8S%SfMn=@ z@C%ow7DzDc_~qtR0em2B>564Gh1c|N+=IMv@9h!5oGe_Z8)^4`qSU|+m-LsijW`c~ z0m9G=BDkm#HZcf^wx+$7cAI#g47#f0*)jSrVV6abqH=ht`6x~*LyAVOYz;#~5@AQa zj?-o6Aeh&vmtsbbGwsCG@`+-u220#RcHy+fXLd7oOT}y_oyguIzy;-QM_*s%Ar4*A zl#>~@bNm`jD9ZGv=A@W%vSF$W*Dzw9RL&Be>K-(c7`JFehq~+g*2Sv4To-ts2rH*i zhVwr|CTxH4DmU`q($p;qV7`UVSnOGi+>V|n7>I`Y7D|=(IyZ4kHO4`RhA~7)M`M`X zw;Wwp&|s1af-_!-3IJa>bq%MLr)PuOTYq6pE(Av~T7eurE?)Fb-~)hRz-_<*axTJEi1 zg@BHr#`VVi9=JH5T=n5xpwb_4U*y;Q`ZI>`RYxrzpXWO-oX27opVA}QQ_e)aj1mFg zv$JvDyUAyUYR@yBSkibM_eup4J<}@*8hu3&V83Uvz12!uIwuf49U_&XTLofJTpbh_ zDDd78szg`y&RzOI_niaETxUEX=0%5)r}%9G+i#NG8_k5l ztu=A-DG*AWko#PG^dNr2OT>b!ZjH!c4n+O!3uIP*OOB$*7n^;D?kND%D&>^&*QJN6 zM;5a7K_n}XU!)NP;TwdDk#X)uZySQhpuN`HfJ_Mr#5HEjkP{lw=RsZ%uFadJyu^$#Y z%lzDLMncB?m|fZUz)1@|ttsW%hKvt6^kt3IZh6$#L_oXy*3@zDtwo~@rtWjA8mwYW zmrAL;I&xqip_GAXmd&9?0AWF~5!~Ri$72Zh>c;ZyH4`pkt@YseoM?BO%4trSRL+jXIZLg!8jg5jq%Hxo3RfRNVrBU#y@GvmHXJR(F;D=?uy7pk%O zTrMq2Ea3r;hZuUmHZ&nm8rmAZd@cc{w@E>$+x6X7bSb?y?Pp^rT4J3l<_YSQ-wUfHlcm1is3V{kKh@-chp7-O&^|{<#2CQb;ju z`}7IUk?Y}H9X;9gmWfpUKH+sz0~UDjE_MWZzm_Do^YuZ^Ns4^-+V-;!_#&w{z|x;+ zZrj&qKN7#}hmj`bF0iSca=SQ3{X;&+x)dcxWZO<1a^0BsR~e1 zRc+`}y8=0}DdISinzzNbmB;IonXV|NQ`^p$LWcY<>kXAPlgVpP3)1UO3eu5BNwq(xuts^U{>*bDC=C;_*+1UEU8y_P^e5r@@ zcNm_&ei#rGfvEbHfr)Q3w*HLN57iJ8>@z5LA%X#w zO5JmNTj1Ceq@)*#5RW9C9S<p=@=J#S9@&49KD@W#AnbU@2Z%a?-}_+gm5Pf zYn8`_8~D|#X+lXk4C&gl_0$b93yg&8zSoaEw@!sb^e6pkA+&v*7f(ft8@cJS9VaL` z2KV1IqMd)#hEoG^6IGS4IL`$web@th0>$+xyl`u*q?~ow*K z0(cX}hW%84T_$?c6jyFvuXq=Q*$t;jrUu=o`oUychoR`4lqJ5IQAPdp4X>?%`%`DJ zuhr{@q+s=kF#4<_7gKZa-*qvMhyFamv{KcD_;)9oVuxiTntIle6#P~ z&3tiq-Rw&Qq7?ueF{BXUC6Qx;NsBrpResIx&* z7P+A2R&nT_0A@U{2#-?6-F=bH*~k5tQWNxaHnxv=%%&^<9-6?&^RxBwJ%)SsFg18& zGM~VSNy;Uemk8|~k6)b2h)Ky94%p}$+3RQHT@r9ez8B?&e90?}RfzEQV;S;f5eo9* z$W3^)0Micq8VNN&M>n8(Buq_A&qMCLpDNg;X|99y|^L90^h1lUq;w?i;=BkX~2te{Hm zMN{C4F#2&PDf-=D&a#6H6Z%_07?ddhI%j7Ml=lj7m~TM8G-zIn4wQC%$>bVdP8hjr z*XSL8G1z>N+sN4ahkdp|-f_pG1Z6rsw=rvR++p>nlwYGLrUcDY(UP5~6txaEZxwvB zmKuL}=0ZJu`%Jg|g8MOOo3LglkF&{3^D?VP!z!Oy)DCF7y$K8%n#4AwEy>2q6*)oi zb($6h#s*8g6*MF4<@kc$EHAN10IiQNBZ89BP$cXFlGH zb4Nljrw==X;QRbt4Q=|zf6Nb>wRRwOBwooQeRbLITu8d{W&TTJykeg=m5uL#HD+yi zpd^^6Su@@_O35rZ>zIYt}ABA)EgSUl0xIFsiMf;D!LHxlR#~)lu zeDk6|eDmz`gEy4lzkvMWE%o1@!?e@Hhsg7R$Mk;c9cE?&;+zX!Mt2_fxm~4p(BG!2W=>_g^Y{T z7N7ZxyaR)O)geQiPcAWNGtg3mna|+%S-VH{7q8;~)1BN1M;kGn6j^qMc;i?nLb<0Xn7?`p{E0 zw4GwtrvZK-|XCz!h_aXdxIqe&^aX?*~O6TZs&-8sE2zZeT4mC zoj-WY(p+gEBXXPQySB4clnGErPMuF(&Va}1E1Y!+62I{B7(cx1cI!Ir!{9-!o9adG?_B+Ta*dejJH4R2PX1Dv& zPrr)m)1BMl+JJUS* z7!Bsfrvb5Z*Y%j+S1<_*cnfVI2Y^2N%a0GFC+p2>)=mZA8?B@_3*HNn_~oXv3xWe@ zuSOlavxX7g$H`B5?As7gKO{?QG_VDrGD_Z6@L-FMY=Y-e_m9gWfx3l%+bubshg@0- zF1B3lQB4wY+xaE9h*x~B7u(5VRNG^zCHLx~hg9AVs6sT{RkANLTQs*1cEk$$2I+H4 z2u+#u4PDIAJQA=cI-eHM$l>9fomMI8h{GOZ`1O=)(!^jeV2XrQEQbKjeApwh#OssF zHq;s31(3mSOh;Qu66J0Su?Wy`&!9bTojeii1 zlbtYe8Rgw^HBKk8#2@uCWqa&)w)kLCW{JfSb*cAAXxz!XKlaYZ>vpKkd;z*UrvSLP z_NfOpGcjrDM*;Yb0=4{6HAvd-AP4O2Suy)nA4+a8Q%p4{2D<-RixQ!S_+yxp*_u`< zut(m7VctYbPq^#Q;Uy`JDAsiZnaI1C)+i+ll@w`TcB&3nF2i!?OjWsq``0nHV|4>&#X;Ja8A-oR>>aiRQ_OV^aSJs38O~m zj7|_2a(Uz#W3zNBEY18$>!^cUHTL>YR}5w?=8J@5p|!byRzU z!C0FA4TdL2?p<*@)rCVq5Ahl*>J_D?SltVnWa`N;pFR;+Hj#>=W=>Y^zCMf8^(Cu} z8jM;8Y;7d7{;p!ow{IV2+Zf+H)lo(RE^;+CW~HtW0(U@F(;Dq{S_{k#EF}5$K>(FK zC(VNh*ij~w`!SpTut;m5mHqlh_Db4&!_A!X+=&x;8j`F6&8vKmaL75FRTRS|kb!PP zTN+vum;{U)f}id2N*dq?Sd{-X&5@*s$pnvD7yJAm%$I{&;zkusc}6_7JZZb=#43ph z3cw6O`g%dx^IYdlLyyv-TKjcoPI#GJ-6eaZgd#=ALou#>xsXyQ_)C#JfK`df65`!n zxqgh!ri?bFRbfUiE7JYD`+7& zN5FSPYg;#Iwsd-ki42dZnkwA%$)wkLzg7`YV@`Wsdy-NsPMRdX-^kbU#EL=h!V z0L1ADLz*BfQ#PPsu~S(m>%sFzbk`amT2S}_k35d$HbgPJg%TdV652FZ<=@5aBci-p zjD9*FiV_xE^ZjtnOQhAAJ|!Ke1>m6EGN&ZaQz-UoCL+?9pLppM!;YoVt2hc8uvfkM z5eao0db9%Wc)#7qBER$(5ZRn#kK?NxXbBvo0L80NdX2}ud{ymlNUTBPi1_e(-;h_U zXt2e*I`N)`s<6|o|2FtC^*rm;d}xHt02ZAtJQ(OD(BXs*W!_gbdfU^W7E?9in8hEdfxXDU;(~I_*r|DgHnnl}<&ywibhi4t2@WDrvDg%MN?h_r4 z!V-embqPS*=_S>_!)a@kC=Qe*fuHYGa%H&-%k=F)kCWN4q3)O50!|6^8j}ftA zYH}v5_4*kSPL0N13__;^r*Q+d=u$iHL1#MdPa}a-8P$N_O6yT|(|-VQK#sr7J*i#G z&a}b`+(ReMkW>h=P4t(_k?!zD@lJ}_P@Sby`dPFF)C^ILi=(>@Y9-BA*57$vH8)>S*l)_NQR4+AW;n5hT z%5vfLQ2BUQs?NgoltL7(EB2dAZsK!qxNPn^+=LZgvr;>i?xdW5E7~Uz=mT7^UDH(x zPbDsV-gn#mnP4RTa3H>2_pl6PF^{!=vx})94#Y>U-CW`P9Pvjp&@?(H_kVTg@q!E? zIBs}y?8B_KrHe)~QVW3P1Pncy$K)%3>u6O^u7|{Ck_Ucx^xf0>#aA&{80<|lLyFi! zV&UVY(6_w48i#Q|1c4xXwEaRWQ z&|n!)pLN&Ogp{XF3w{LF4ymXFzVG=uz4+*(86hBNIE(WemcSLymCnhNKWKyMPt#>X zr_T|U@K_H3Z5m7F+rcNDW5*6NF;Ddy;*cO*4W0xLA}nDkSMH?qQu3!;hnthcksk4n zF`TRs)7($l`ybcQ5tw8){D5mXR_-5MY+^804Q6^Cu2W>|*pX`D3Xq=>?j)Aypu6n#2GE=m#npnC0L2LpL`}|jG*GSZ&ZU+{ltz0zQ><< z%`>G{QbNaf*!In*P84iXST|fozM;AI6`#i^lAb`U9sOuiilDpx$ke}bh>YGS9K=op zl+uSP=Ft4Ysw(*J_Iqoq){2DLRsSB`x4mJJyo|_^1=*GZjyZ!WQ+D|8vGhPUvy-Sc zebUq!=CVBxC1fNVbD6*15g_*3yTf@oirdB-uf zslfLeSlwThn8!?VT0M#h1UH3|t>rs3(X3E|rGnjvFLFqsym<7z^ETLI6WulZixcJj z&P(&zoU-i#ZeKLjE^AXz!>jy?vMvHz@+9L4Kzu=std6>Y!yI3qg!e_73^pV?idyAi zf)f4jvnj0l7YLw$*iE;m)2qr^QuO#Psn*HFM?1v@e%EZ*{U*9orV4y>jc?sdIc8$} z&*+5UkmXJF6ICl-S3t*d0=0htreh>vamD_&%X^9wTKduq)ekyLG!|8MkGPIeu@y5XqMDIh~~645{YG(f6;z%VqKR zb?9W9O(z^fs2mF5K{j4z1|dYten16F$A*NSDL(6A96rgg(_KKp1%(kbA6UUmZjkYf zECpI~V{ibBiw6pkilUnt8NN1KUFje2qz+@-aOf!%l-u((q|)vr3VH8{L3QvKDtRfn z%ZpUepAq_rV*;rZ(Wy7lV>33wZ#KO!Rvh`b@Rif7ye?x|&l_VL8NU}qDF;G527I?t&+PBh`dPL`C#<;pU&H`J!^E>fzkKWxwafQLLT>k4 z%<_*=35BYXgB1S@hT~b%iodVT#<>KeKw_Kfpbi?$Vr0GIs%c)Jjo&-Kc!1!Z^qa~s zZ-g&+ft6Q$=u#7QZ6+q#bjR_~o;eW)vDRNAqb|Y1dF!HswdsrqfJ}e1FB|}o&(+vw z9RC2}E>4URYezuJXbk|Y^?#`uDFUjj=ek(e6bZ-R+v+GgxCh-Vv-5_^@(ZkPurU=B zh~^@73;?HwD9_F+dS@I0Ne(7L(z$F4zr)8y&0G+3$Qng+a}xJhST96^$@5_)3yFSl z?A$>)!KjLN;rfqJ9-9stBO8x9$)u~?Oj24$3w+grx0ogqL!w3DCYjJrqzO!Ux`aPT@QyjLf^U+0Ocew7_v>^_G^lZeQcnf zEa45+t|*MZrfQ*PMqnL0xQh@9CbZ#8U4hFZK5pbm6p6nmu@<({gKz(IStDylJwWL@ zO8N@uv9rkr$O^j2X_UI^e$uq8MSH!N>#G6P&ft~kpguY32Cs3%z3jRy1=qeANIWw- zt^?XLc|ycxy?>;+E!MRYD)3`xuS4Z-XQz5f}x9lChg3CUEOeJ{orrLs)*nEOl}mZ%Z93 zHe;2&e+L|@(kdFAzaC7V#-TVQkS^IRn6nxO2xECe6BIeL0OYZ-5_TwL=+MKFT-#R& zU26-dtUNIpfR+Snr7^-s_(Bc&L3j0x1J*tD94*PNDh=5PSTn!l~!gKaM5T5v#^3zud}^fx$P0;~2^I z^9)!U(rA$V8$oHvh*l$l(a)o$;Qp>2t))(foYQDw*}DogeKKMmv!SEl+!oHf3y-n$ zy~}G>9=*neDCPJyh5~D~=BOBdx)ft+?Kj`K7GMaNNh~(>P8g>pP`Lr_(F;6H{ z`=7>#t&C(#_)fr6NFR)_>nLw6!_t-%rw(@dH&i@IdRXna8XWv`&${mGPxqXudm!k? zAssl9mrz3V8kzcV^hBQ1GD?L^`q~tHBJMT_qO?qo`$!t9Q7U7Ra9G#qezjm$69}DhWWxg=E{DFZ zMTGh*YjT3%{1;UR7t--^o2D3()BkaczItavzf}hL7jbU_XxK&_q7W$z89qUWcq1Sd zioP`}PTUvSVN&>Q;5!fD-Dvxjqr)4XC2^8rwW_*0>-{0G$*zuzQo?0v58O?h(~i5S zALlmu<9?Xbasvv>lb!=`3+Wb?ea898MdQ4L7O6$xOt-^(t^TGPDF&fi=t^)TgGg%T zs<+I9eV!GPe2yBRyKi#NtMPtT7;2(VYFBBsBSlA6CzlC|J;+?w36O)bf<9kemD5GQ zs!3IJRyQhiqjcoL%+b?eh^#*tvsd5#%m&HI7OfNX+HdL4+$g5ACdg>>NzRn868A$j73IV{70xH);&XotjEqQ-lpd23i+^2zfN$_&pb&S(M4=v zC%~QD$0y&5j>J>8MRCRlzlCH4+IUST0aYdyybv}L_iFD^sFpn7O)f-N$S~nl|2l=e!rdR(C zIvG_Zas209Hr%L=8rD(VK-`rolRAiweQQro?)KO}yd=hyQpmQ0opaYt-8qWK=8cg5 zc5sqv$EbxJ4~B~{%L?uH1nC#YLxa6TM)j>R3CH`L1RmBh!m&~6etoOvbK(nlmXRj0dRO#wT>beGAYpe+e&gC&;z#=Hxxv_(C zo|j1>Vhrn8nG3Z%iKigMH1;vhDuW86W-G3$l4lCAT0uKln83s16=26OXFtqg=Ej@V z3f*U3*+7;NR1Fp`xyCI_Q!_4_R=U$ca_K!-w)IgdnAjXY4)#4Lr*R}MvxEFqt_K)P z8B*}h$vQ$UglC&O)P>q;jpSo`PZd0zmRnZrSr1Ui5rCLJ47OF)rlX|*V&*5t>)tqp{l5$9Oav}xJiJ+3mRW)=JY zh8y6W0X#xi=}ergt<@X_L`L5wwB&;VPCIq>UXx2$R?@VnUv^pIX_Cvv-T-vMlj@;Q zMWx`Toi^6yqXg+OUSrWAX4!bbq?ZST0B|_Zclq4a%YB8y@~bk+LJP1_y>&xtFOsb3 zKU>lY>HwXF+ zh6o492{H2N5&3_it}d^OYSSH^`G7&AEsMJ9Jo&+SZZH__5}*DzMDGNhAF%Uwh%+bn z&YD^ej6%Y*jg65>tq4jRW1hZ2U-#mDOSzAb5X>l4_?ULtle~rDNJ^_0!A`=X(B5|C z8Qoj8ZS_~tp)@WKEyQ9n$@{J$TXr*`kb+zyga05zF|EgbRFmTMO9<(uz^dLFJ?^+E z7XNX9saCC6!1~^$2gj*QA*vN{h|xSrG7bt@aWwO7+kOhVxA=#-rW!dfCJCYee65-| z-H$D=IjB;!kjLOMjmrtjkGd_GJMZ`KqHpJKRzvq%n!3v@lPAonOBw>xXw1gNeFpdVf zxr*b;h{0$T$_v-jMw?84z`4NKkQlK?<^?6s>S1V3iFiMMTh zf|Qu|49q=x6CLP1mm;8p(^4f~?Sc7x%2XaYBfX}Qb4^k-Ec5c6->NS@8rpoOxbUx7 zf<7^FUG2fcgf2JJl*<4XEi{^F1Xq1o!W5U0-ZCkTcR$xocHvc@@)iIUC|WT$%)7Cicg|TiJJ4hG-lA>b{ zks_D*rj60|2A%sR3_`XFJ=r{+_rQXR#5z*gt1<1xKmULG(l-9dCA3rdb6STK$hp*i zZs^%`S!6jsw%=vFeVnARP`C;%dER>_=Y@!FiLb60C5(J$WJ7(?6O1}{YOO1-Fyvt2G71mC%B);8IPQ` z-rauYO4p%nI&%S%fz&&5IH0TiSxDT_^nuf+e;|1CmXnqNE*gm@i}JAPc%qRXIiA%| zqK+fzBBHphA>$@9c5L|k2GX=Y=32#)@HmHPdN?U+#Ny-1)PvRMi9Bs>TGI4g^Kp?X z;J36W$1o_DO{_OeGU_VFN^L`56AlqLK7@S#Dj5X@18*h+>9~QT_BVUG2YyD9sl$8P?Z2Ofeu!y$_@-)S8t#Ef1*cZ&Tlr5^n~QEO{N5IfWhFgNtV=2{-xxBpNsH zooq?c?yX6eqhplvQhICSTGtq%JEN2#~Ve@_vrRR$d z7@)EZ!u&Ltg*>>V0J>%jsH-_O=tpe#A<)$4D-k0gEPtbqF^_3NZY~pX6OfZ+$9oc} z71rJvU>Sot6<%BTq_1-Tedu+8qKy}p?>Nlt-wd@a5KJPzHSO3LVaBp30Ncg!N){1~ zgIt1}oi?=E%^f1D^m75y2|fqk3k-9jKE>!d9)h{JW4gSaZ*v2g9bw|Es`*W|Ax^qM z@uHt5{Dp}gK6C#!vVnVHwjLPw-9FEeSxTZ0qmX^65JrHSeBaS1WPXYi}4iAMCHsJG`~bQVlf zCY?8qru4tssa(Jd1tewfrp!3c*gFx~^9LHKTG+k7R!=kfR96~Luay?{`qCcdVJJE$ zGvA?q<3y=U>voAW!#Qh-cB_=lb*gbpHkn_^fqE>_w7Z~@p)j1{x}FkL{w z^S9fm(+}8;HwiiJ1#R|Wzgs-g1;~t;pg1E6cwdLgpr3AC|7@6;4p9R`u=BB=)QMM_ zdZX*x*eq*q)dY{pWtwBRO}kVHx?1$PE;rlELK1XAI-oZ&MopT%$BU|WW69eByc~#B z9BYTs#8eOxZ5g6wmGXZ!S}LBEKl_B=rRPxD(x=kt3|VFS}spx>w#Ex_QK z!=q!y4OI=u3(NP6@9<|Na|!oP4ltzsMSo+C0o!E}X-&gFs<<84gYhAkaX6_B&Rees z`K&JQXk_^~uoM3XbsPIuk%yWZ{OYtD%DLLz#()2P$gE~=)`??G$LzxwW&zB`_@H-SFjB4cGBL`Z=d;kPnW{arBm zK3UK$k6kz#d4XUn&8z#Sh0b6>k^z#^*=Icq#gPt@k>TM5C1~|AtHW;`dcw$apz&tk zPPA3UHyFejW~Wy+59(TRYg6OAm4P6WW|AJ+`KqvQB?rV4=rm(9wn7vHPp?Ory&p&h zfl+M{3gdm}KP@`7F_=)GRM`5V@D0c|9bY=W%IR9J9f)LGZNubQldKp2eYcd3$k>tM z5LTu*(ob?iF>1F|2DI2PTm1I(kyI4UMp;Uv&yftGh`d;ybxgJZc@y7bX#^&EPpXKc z%R@PM_?Rr3m`5@)NN>18T32n*RJjOVzvxB00Y$CI^a=0lLoZDg+*w^d?@Gzte;vD8 zk_j#XV0#6of8JIy9z}+kK?FD6H9i+EU&iGNJL?d+SGcB!8*w9v#+|Y;8JtIND6sL_ zX26HKp)))s|6KIc4Z>U^WK{ZRYRXI=uR`LWh(J$MX0W$+Au5O%Rjb>1tmK4QPKPTL zmZQ*8Y~`;SeANZ%u#X)-|9>wedy}qTLO$~K!bVdfbHo75`ig@?D0&8ay`)!pn+%m| z69Xi=Du;p(7A5p{!aJhaehIFB75eH*7v;)2#^@SRrC#%6QSjBMY+)?}hu6ms{KzgE z!Du;(S%@#d=3Z+49eQD3jAq%bMxAeiFs{%(s1LP(%L8Dc)pM|?tUji`HUU!YelLD6 z>2uAq@ivWEEDg29%G>h8;gE`@9#^D^_}XEU&WH&gL2u4Fl3p+1QlBZ6*~;m;;n)Xo zU~peAKd`xuxkcst1-saj*SQCT>`nA;({&O#bTxfmySt~c0pWP{nV*&55|C=4D2U|u zf;Z1ga4u|ZvLwQ^%b6FJcotNE%ZHuPY{JFGOf5a{n_MT}=ka)WNuS)YP$%hKz1~gy zynjQzwE;B7zfujC-+7KC7S^n*{gUEaQ<$EEls*ZrMU3Ap^XXcP6Qs~Gt5_f1)^-lw zmKXwf04bV|@un6{KfXOzlB+u`F(K$>-@Sh9!#uRjHSq^Q^?Oj5H+9H*P=ePG=q%r> zRlS%HB5mkYud{wbzkTDi1|HR>$b4N?uYu4kTpuzMRr3B3c z4|!=&`r4zy1{c^qMFC9~uo+z7a5x||m?-rW(S7U&(Z7yPY&i+EnN$Kd4ldC0qBZOX`gpySW)=>b**M z`|%PUO#z=6YrygW&Dy@^{=TI;V^_I-_R~`w%2^>#%K<5~>5nJOq%oZcMYCrsUYWk(3 zw6mM($^9MX@tRYFwEQ|p{|>Kxi)}PWc?wRGR(#A zZ)({Kd>X%`-zc5SR|K#JGfW18K}7VLkp>0ERl(zrFJ*+62;v^KKnxSwpu=e>b~_6t zYGRQ&lI2Jr7mtO>4!@Y18n0;KBet-7m^?@}4-ny_0!O!e0Nol80aK(`!?Fbw8EPFgVVkzu{|ZV;#?wFMe<-)%G+8se0b=B91E*qMxz|j?P0%R=!hKZ*_6{GoWui%MQ?2Wz1-PZc&~764UUn!QmPfRD_?W8Oz8 z-kY)KBi-~8x{9bIOOEw+g#v)2x&aL zva%V)x~b4?UK7Zb08G|Ati5=!Q6`qk&2*J%m63%^{{ctBp)xL z)TdkX)Y*{?!=Z_tSA@dc%sdd*%h4~Y)_TMx$u9>ik3!DUcbYTGU&Z`{rz9mj5MTc0k*Dv}d6;N`uk^j(l~@2yah*uKH0d4xLLV9X(1P`r zI;n)Jf$)*b=xHX_8j@41A~rz4bg&h*zVCK@pXZ(baT2`WL0C6qDv=i%>P$_|GpB#w zuwWiDS>8UnrC*18IQr3`OKA{@1Z z%wR)nG+i?*_-lj|NIDs6}6DQ$i-iPEr?=U!jcU~&G7d?`Lt z3@KwxsuFdBdQ0^!$nVzyk~Lf6SN)dLGTB1K%zrkNfi`&_(zw@#A*IUsS58ocWx#M@ zPMmDtZXzlI=V_X$NpCA3{t)0Vg8^SNeg-l$V@LxSLP5su*7*A97(Hhx>W&%wTm5sN zgx%#=?|^lmDH8Urmn$tG5AX-NkV%p=ccC9bW#3wRJRXcQ0iqG*cARqpZA)bt* zsQwpw8wwLs+fxCu>y<)I6bnKxe_8jw2BpD0ED{6$u&Y&;wAzX}JeI(n(a0i%XfBzaH3Z^_W1eUarXWfRq1% zy3VG{%Bjeix84WZ6&}veUhFbiFUx*K;Cf5F$@*&&{C`~TYuCUn+*bc@b2PS*-52BC zByEc#tG7OrS+Wy%knu}33EYpfN>8tGiZ~j+EU)}}SZnbghhac2cRM7AoIi4_Hr+pG zXk$rh;sutC1vQuVIUdRHMxA zuMz<-xv@UO3~kx}QFO{JW~hNeLl=)bubaE13bn3S^DcMain9+Ps+(;O+DWbY!LhV5 zrEfE)r>-(`(X)GKI5rS>de{m|kfA;+9|X7mNXVFYQ}N))>jP$I7v7T_`jrMA$i8%l|TDb+l#rk#U5;xUlCvGRpEkbx1q` z5cwli^+E-6>vduM0l!^@Xn{yw?Hpy+03P=sSx_8>(5Q+TSr6lFRg9SV4>;z1e31>MYUf70?Snmm-mq!fxHZPR;8^Bwmd{mRUb3WmGbhGcn>FpBxhSgrLB z52DuVra(5|zWYL_jDJZYR_>i+DZiJv7r%;UD3$A%@1uY5HNbmE-*>M+G zxzA~t5-iX0dr1c3L7j!E?-!}>?lsSm%exMawBYhb$% z(}tNGjf|+2=8~i3JFmxpV5@q4yeX-uD!j})2K&B`iSR#T43zoUb_|Msom9QWg0aJDiQF(Y^4F2Mz1KG-8 z?7$A9w12h7Pn-r5Hz>5b-xq^>R%^O>8|8@u0zt6>pe z8;N6~GM}y*Y;NWsMAwUkiAJrup|LWJnU} z8^XkZ9TX(k*mQgoqXlY7Ox--apZw?J zk+|FuWKo_wzT}QM-j+G6aacz0b`d>8E{6(+AcmS`RzRQjiLjEY?9>cMQLv*+FO(S6 z53bGzfYAP}j|?x2HPoC$lr2(P>_X}?JLTlZV|RVLTFOSHYTy>3-=^!s+1XciuUwZ2 z1d*Bc@40B8HlI?CC4M@1E@&h+_5W1eGwGdZ$Axap&V0asCnVv23^SlbvoJ%bzVQNx zqm?WAAh$Cf<9>}?$l);5Gh~4AAwjS|U}ee3>+LNKBhBJb(?uSot1<_2!yH8L@VPH0 zo>++%qyfk=#4s|Bl5L6!FC4lL_EHW$CAP#aHSeKVKIpI%`|<;U{qqe(ObElZn`lKw zai*{|of>#LVNbM)SRiT?bO%#7%clZAv zhA;sGe>R4hB)R_1-8X_XQ~6ZfPenINcvDLJMJ2?0T(EFj)dRn05JlRJzXT>;MiVL8 zH_m+hw-z0g#dw$0)=ZgcW%QXpG6aVPEg>jQD?P-&%?Xi2yR8(+kXT8U>JAas?3tvZ z;x!T-m2d^*N~SsdpKD!r0%wGem_&_zpaBdzyI{A`|8K)~YAvCy5d0r62f}@CtVGQU zT%aW!k0;$~WE!N{2IRH{zBT%5>_eTf6CQ{ehe>D(YfGb=9ylfc28$iyJ@w{h^49@yO;$%hE<44)r0+HEmDCV1_Wie zpXZ|+F*VkUQ@7`*lybM-j+|?s$ZP!~r{bFoN$9!VRv%I8bTGUwYA69Ea%ZXgb`Ybz z(qs;fpW-pYFWi!)Ma07;jBI>OSn#T|F3AO?|WrNrM>UVn8|7z4srzi=d9X`w`(U_UE5%2lsHHPdM6EzA+or7=6Dr5{*Rf7+j)^9Tx>2>E>u(xn^l$|>(ScsRdq)O9hS12?P;Rnre zH%S4Eo12QILg$GfpwX`(^$@wzckn4tt->(ls`Yjb(xMp{5ULPr*ItrXiKR0evN8Su zO^WT5qZhMiP7NRxJ+mb>{|#vlkr%Xj6n(;tG219MZGVQuZ_}bw%vwrrcW^C~~N(1mwDY=oI}c&%XWK zs3~7BqOX&yF`R%7bI&`CpBpC!3b7KrRIaxRA|T9S)Hr?rOVr$JwFN2DYKVgTX8$(dRO>@1 z^t;U4Y6c#LYB&WYr+o|eLIuDO+;Bh#&(d#5iN{=fcq~RY%99#vv25k;1~q>AnR|-( zb#6Xn=_~H-ddv?q+2ub*4v)5fLjN*Fo7`0JxL&gWQ#Ml9u->8p@U3a!>;0l)0vnIV(q_NzRSZIeze|$wCc*9^xCWK> zC(g@A{%y8meFr_zSN5^12uCq<#gDW;651FspFn@O{z8-u#?d`yJ3WC!QjvM5@i7ix zgY@X`_-;PB3qa9$>>K>Cx;yHEv7u8JcmUlRLLctL(gNaM!*SPA>uv0 zE?CieNS(x#pp}st98xd(?r&UF_1M&!sWtSu@LiIU?=dWON}uSyvKx5{)n1 z#e`YK6{RhRcgcLNMiW@}I-t!w@`D0rvv{?pgFV7s2X6cq({{FKSYbf6+1tVpkK-=t zkSFO(;&SUkioG6M9sA)zj7;Vr)pJF2G3HieNqBSiFF5I>hg9kHT8ybTDKXtF{x4D< zu$lxz1{`;-{p={F9u3A>GTzhu zU`aVWyPk}fZR;07ci>Ia0qc#d4;tKhE*?Nv+WhUye4MadgQLPT%{ZjReh0ZzsWL=QKY0Ly#FrDtFVpB z8r-Ft+{Z_+D>~$*T--v?5lPj97}-I1;CAQy)FO|J)~={&>P;t?Le+pX#mU08g7(He zzi-d9)eY6xB5-6L(lUshW${d2pb%R&<&YBzqy|MZ1V?9ofM`V8&ad&bz<}Zyy+Tr$ z_hJ!8IQE}Kmz*AhP)?eOS*lSa$pF;!+ces5!$$tMj_MFyL!jro$UV**eTV|96%TZd z{5vxlPRwbGGXg-ac1mJ?na*cv4&)E33pQ*B4v;nr3#N&#V>*#BVkLAXdcy_veGT3$ z7qMpPP^w@laLT#ix*FUBYJK$di3b6Ua(Kk!SpLu9#ToM~^K+d=-e=Km{)ybbNrd{n zTKvwDd^M(c6tnF0lINKcB!7ynz+w$(+aa8ku!%N^v6Rij6HapP=?8l?7RKGagF!_G z{4JSNWIT1YyxYwTKm`MF#}Sdfxgrv-LNiMoX>PR%>aQQx*u%4S_hN(X>E^QzYjikW zDdHZ3gO^HtCX@%-I>t*6>fN&Lg@Krr)ZfSlU>iuI2TGg=j6x*8eRh$n&K7(5`b!nS z6fQfDI@1Lmxi%c$cdv7R0ZsR+4w{E0c`!DI^-79elxX)|b5%;nIj>gH9ort0*#{Tq z>ZgK-#A>!Gj*?{bmQ{0u9d;t3(-zJl90p) z>K$lUl)9y3fS9;~lrhD3tY+MiHAG`{&m7Gd%_%IIiKTGNMSKD4^e|l$47K(?f@)Ge zEGDMr-O1->tnv>{jC9z?D!O?rlMM;_SML;S2XR7qYH4K52tnt%P5>a%h0!zQSvum^ zC`{}!&ISVF*SNSaJ+olT;b!UWEY;2dQw7DsB-752Q5yN_{O!rHU|KcYGL zcZ59)75FL)5^kR z8Dpemgm%?`Go)=5j-69!;B$f!573D8jo@>YV zhM4rW+!)f3pH;^>zIBqLGK=p?F=anV(Ra&Dhc(J7+iu}%+qJ(;oYOB)fom7W_Ld`} zH!k060%moEcc2Vq0yEICk4gk42g=Yo2a*E2B}%A;<0Q2!>!nMW3!o2gTO}xg4pkx7 z%PT2SG$s^+6*(ZPBEMK0RpuqY82&5G8wW6RDvc_`f5aT@Q3io(`$5YUXQSKOuf8PsJ(UG zQg|Zqdo612P`M!w^zBi&xci*V`}Az0_+BXK$AN%r$+Y!koSUx4FP?a4q%~h4BsQm{ zDGBkegpvDje=&p4oy zi;K%B`d%}tzD?zB$ik5UJwZ_Qh)1M#*h*c>$YY*P=u;1Vg;bk za5+>8@Y#?v+h8xGE!8haxee7ov*3SX%=Ob8^lU`uLlQw};_?`^q!w@|h$?8W@B8;E zbXb%%Lbe=_+Ushl1DY8w%&L#6iq)4P&krVYc0?QVeE5oY9sgJ&-z+v5gv>kcUR1Rv zgzfm7-=bL6J#t2yN10fcY}Sku@FKger~D+xM|D6SPE>FP-r2&oJreu5A{_6b`T&!XGqi z3D8tFisUpABItx$kMBTJ{H67ftSt_WOLdr9XK-Qwu+(TTftZ#2LsEbT^38@W@yc^+ae51DYkgd8Q;C9R z(+gG5Zrp%nGEdb6*=j+Cyj#r;up3NG>i{ixGh3ZpPL^k!h6r~*2VK}d_6viPj=Uu&p2&vBQ-C(aRM*VjfFJv~ zKbafR8|PFeGwG zpCL`;BG1Wd$=nez5m!<(ct-ld1(nsfFa1bPG^429P6%Ojsx zt_@oEW146F;6L-h-`J3q8mMUnhQN8jVNq*;F_i)hL_Rz=mE-l^4242+*RorKpg%Uy zx5P=zFiZIM)?Zjh>z~docY6O5HFHxx`M**r`{^JHHU4BGA?%q}7iQ4Ck@wEJ_=GjX3)OCBvcQcd&%>n+s8<1tAv@7;w1@N8?T=5=&nN8} zH0wPu9W0s~f^2_oDN6_^dW;Wj!1r==_myi`RQV%W)WZCpt~OV-M^#mn`xg6iC`2tn z$fCO0wR>Fz6d0MM$bJSc7GFDZGrFZhb-c*AyQ1&A|4NNHUmBJmN!?p~siA-K!u+_NqMOvXp^kSIM>1)N+W%UfW8z5%CIy2O0L7bH_jz10D)U(iAg8dID?mDM^wxh zM)npYw^yK-C^e#Gk#^sj5x$T(lubDV6DD@L9a#B{Yt5*!c(9K;zzX{Rf{5IR@{$Dj zSsQia+6kVwILOXT`_S|6lsHGCjrh_cBEWv1eBJK?jTF)pU9}eP3(axRVEW_VnAB4P zPcdbeCZf!=I1_7*vXu$glXCGa4l>A7_!`o=c%pK$ zN>5HAw#$B8$i>&G?fD4OG6iv0Yzs25_+8bF{0`M`&+tZeHTg^v+9_i@BfnROo+8V? z=m+c4HZ*yRYO&xiv`Am#Q+pDsog?Uw?&@kTn*s^i_jxFl>7}hKvk^g2{fn@xTwlw& z2qO+pL`~+>i80niYau^+L6-o3bV5E!kH$l9B55tG!Uo6k*3-qbNp8Lc87H-K;Hu0q zbc*5LpfS9eseDHAcH>5zjX@&uQc!T9PG1PA-oK9eYktunAFl}0!@K&OqifD-%0IVj z${X9P3m*-x*&Wezrr1k(_IT}$r+SWTrXTnD*K-sQRABK8z|!?I5Dx3*+$&oZ=RHel zJp|iCP(LdY&+gu|717&gfu2cGfdkM5hv(Eg{B72Ih%tk?#e=g29c3>twYxLkB8IC&MO;AX##IHHf9zulA)pm%pyj4pA+k(= zlr9f1Wr@=0)HzIvJQGPE95E@=dg@`ME!#VjcT!u~4Fr6IkYOHEDZsa<6;dE+kKmu@ z7rq#RwoTJ4hGxmNeDV?_y#?}SY!Wi%{NK5|DKB&)?R62NF%by?oy~^IaTu|3vWc&# z&S%QLakk@QoaLPaxKr@`3{MhTSSlpp$bLreh;{ZpG2+XPAXp>FPU$>X zeFw1METhvrpGaCcb#X;$9P3NLF(Wu0d>C71PNnB5 zYxFFD1*&x)Lf^UnG%DVL7iixsb37Q&#o6Sven~!&6zsLxNMa>S1mZ{j`|Zz76_&Dc zHm9n6GE=X@-Gmmv+cAfsTrzB44CoGCLPX0{alii1eVd}z)W0&&qcfnARytKJHx|*y ziZ#|bHaK^aX#Ekp>OyqGP)U8tT|**+KIkB^&U**QaInKfpXZd{_fELSy~xwq3GF7> zCE4>rUPSIHCcc%Ddv{^c5?K%h_~GF!eqS6Kk+P=8s3itT6T5~7KpHaH#pT;g3}rTv zD;_!0CEWv~H!{j1aN~W`_2@%GdlT&Q+=?+kJ-}Iq#bGo)<^NwW|9a=bOYV&^6MbY4 z)|>e5uDLpGbzJb&+8F4n@Q0&C5QO_IuvvE?J>&BB*Nqj7$M@kaY4?+-qub_V=T=Mx?Lh_O<^8hF@)IeYb{RYA5i&ueO!G+gB0umHrm2`kLp}Re&0zv55aUg;{K- zA0olo;qaYSMFokJ$wR2Ey7|6uo96kxZ=2@%zHgj`raMbu0?$bHdRXwmyg#~W19ao` zd-{H#tFZw~LNEkLGO->CcCx++7+L`lbWuA+tS@1H7vX*v;X0-Ouh?8yuj1RpT>y@C z>xEYnQMBu$WCjVLx)6gD0Dt+}jOWb?y-gx;!RRtq;Ybbb0l>ju51jqWY#bQkPn!G! zWV?}S5((S@V@Y2>iQtR7i5Rjvz0+OPNv5>iRARsTGZbD+*j5Aqvgbb}u9)U>MF!w= z={YXn+(+mTHqA+YOdQFlI zowq9IV=PQ)lz(3;D$8R>io@h9O3vJtXi>BygkHmHLgUTJ~5-L3)mISeOK{d=67T77^lr;20b!3W81}Z3if1*DK)B*8sGX%FEDm1|FP{5XJO` zRGd$)d%_t*{3ZzLwTk4j1m)AVxq|H^Ns|9jGyx*1Z7k{*%)o#>0$7 z6Cr1GI%=@{?Y}T(h`@FoE*6S5wXHRZ{sVw-km;8QdJa)$xAQkCn0LQ|n>#{MJtU+> zzhsrY1(v(0f|BUr=U^b*V6x?_?I1DHHpu8(B2fh7P2K!n=U3J{~3GiKe#KtQ=RY8nw;~v z{N88JIRg53SN5h!2r1G zoO64iBx};x$oe(Asv5HbLIh1<%OAvti9^!yaE%f69-5Y741gF}y zlLXN(p>-Z4Fpyw#1o%S*b={g!td-nW+cJ6agW%XeW+Hij-fUDX?R6V%@0AXI1jW49 zYn=dkJa4%V5Mm4>m4_04BJNXukJ;G~_K+8%mu>r}`1E9-f&*&YgMnNA?P5Ph(SrQ% zp@z~2ouWVW;%QlCn6PMK2~u3)<+>;5!*zOh0+-XqpZWW=j*`D$TXBE0BzTs}t%XJN z-@viR+w<8*NE6H>ww_ng1D_KHiO_Cpxj?@yi#&~W=?%Cf&H{t|J(xfyuxo`S^ywGX z3FB7_c;Su*73}WHFr|#g#>L->q3-r->8S_Cv#wG{)k#1{uJmN@B_t8wXM5mL3#INb zh0*rjgfPBDtV}kj=D`1=7{Ab;<2ufVgqH?9f*M6w$oRVng&%nPjQID@50s2vn8jgO z!x2^T;@#ZI?@Ht|y0~-2{dBp#0+|{YJ;4adA)q@kfm6mxT*1mCP4vyRPzck|@I7uC zq`yFiSg;b%@LcbdL_dVrwYM3hp>B|>1FL-+elX<@x*9>NS`G%X`Mh+)sCa4-QkXm1 zP=|Ywt7FFjbm1>MqvA^d5&A!T_bl$I+w{D=w0`jd@Y`#&udImQJ@NeDUjG^{yS_ZL z_t}@_-#l^m?u`84*6XX6p50vHr{CP}y}a)1*_-tZ&4Yb(rhfPM?AaH>zPxnw*^BbG z*UNtTO8>3y%in6&Z*TVBx}^W~vOV;x8|zcI$@%D<_`#j}smtS6*IL^;2mInz+-ony z{wGZN!7E<6d?M?u_5SY3*|RTS{^*owWCq~>SeP$}0j7bL&(9(Cs04UHuED68#VFT) zC)=fYaz8p5PM=~QH;h(oQ>E5nJu+8Sowe;_ONW54yjM_W7kXiPtkw*L_ zRc9zlAxXJPfxQR`>+2mQM1r(8SAc`K#aI7nZxod@1VqA!34P;+H)^{&mjjDn;sqws zO$9PIqdj8FHy5Ma1vdd4b4|F!_oOhOtm|+VKU-u|s%`nqN{M+A!m2=gbU>F5;N6pZ z8~$$89J(Xm%=FF*i%qy3Y4+-Qt~%jG*nF<}J2K%W zY*qFcyuJ4vV;q{e^J{)4@;uW>N}Cl8C=`Aa-lV5eS9kcy;Sz?~=L81wFEDOI<#F-s zAk$Wmj7mL!ppD>Rx2E>5j00ON!)(mAbXc8)>l7ZwQDAHcLIuCEHUAnC2y@kTZX##I zcaptFZbP{p7Nh)(vc$aOBlB7raHK$m-8WufsrN`rlFOlWJc*9>bhxG7JxKke0tdP)_2au zkaATPM7pmsqfCnOTa@UHrGKeH<4aytuUD5UgIGg$L+Nrv2;lG}#lX?%;kAqJl@KuDe#DFtGz822>q1 z;ta?e_VTt;&Ep`H91r3pWJ6FAejF+7#*ZCBBnh4HxsF&0S1II3y)l&!kj=7`7oZTz z7C`WbY@Ezu$cA|bcfrpwa<~dCL@KBqX$Ter(X53;#SRc*2LfAa^8ReJ-eVxaz#Vc7 zJ$kiO0@&~!S6mrVqO0nDJK~3{YEX7fDSC+1F6cVLssFuRR*lhfW3gU%Avj*s!f5hm zeIb`a!Rfnv9sbK(C6pV8#}F1|Hu{n9iXV!`HDKGCS$3^sn-I;;Td_prL)w9=2^vP{ zM5(sRWD8Q*1he9^0fw3wKTlB2!t{LvZ#{d-Adq6tSCPBN&$3lF{3M-ZZ-iY%dEUM` z8Bja~V^_LdE@PQiI58#q5Ffk|h=o%s0CU8EbJ9fIfnO9k%!*+!pMHSu>m=qhRUSm!?;O7ZHO4AQbH`GrXAoMoJIVB4-%)!|XzTqI^8Sz!HE`B^X z>DrgaJ4r<^p;MCd0glVTz1;Ke^pMZ)?hn|NhTkx3nv+`zo-bwzhV)a{Em5$&XF#{b z4s{{d3UN}+N_m0uXVD$U?jDI=4o)q4q`4D(#0@u3x8;p#SP)OH{T6OnY49RGZJu#O zCFP)w1Lw$VAnLoE!&T6g+@e~Wv=yvJC&?uhsSAlr0##H*Uy;+Sk?p5#54~z&DbknM zt^!@BG&Zenr9@}Ja|p?o9%Ll_ezgdTbFJ+W^t-gE5NhDj>aY4vD;lWb`$MXAy;`K_ z&m_A_2GwCmRF#b~zXdTX7;$f zGr2-J47Z%BpyOW)iiBfe5DFh=>_d|;ISH|Yw@4KJ?KH4yVWOz5N^%Bv8J#fgGn42x zqycVgMv!Ee)7usO7hkn6h#klU*=x>-c}~AZI_nVbCi`xZoQbLVn>oYeh3vJ}cWc>t zNa!;XBg%|_QPhie&vCj9wnAUmsifR4^BD9ql1W21_4A{&{r4Vr7GbF># zj@I81d@t~=Q93RQDf7=}3Px_VGKqd*G>^qaxe~z?xE!@B(NC4^86q*%$ozCP!b_NW zbdkf^DA0o~Whd_4l9Yj-Iy<)@Tfg>%{UNk>%3Af z!JND*E;}ZV%^H#AsBM-oln*wq?YK;|9Fx@h0_$LlB1OfU1~^^eL!x$zBsIsvkIj^R z{)GyqTf_1yqaUX%@jCF0!ezJD>j(-`wTp=ltS<1_0LXu-l59F8y?z4$QL%j047Xd6 zqxqh@440+O7Q-or>lAl2$q?bYg&Bs%Jp1U`KCsSfFP?=Cf#me(r>GnVX`FBk*$$Xd z$v99=`0j#GE_Qt}mW{tIPl8d6$A=-C$4v0-p0k24@&5e2%A0iCQbu>WJBF4_w}4C; zfE%g!G1Bp_hy&bgap9!F$JQi|Lc8djY856N)1v-vBr4Pbt3rRx&t_vQxli*Iw%y4! zhk+P*21{{a0cS<$HNKu90lX0W+|&NJVETr8SSjd69=T!P+a^yFXfjq_bl_ejZh5h}eHHSJccIk0 zA8t=OJu&`H1&mRxfhgdGHwp!zUOmW;S%|@4o4_De@BnBz6xSe@U2Q}VYbr}kEcVP- zN(r=lE7w}x#`n4je5l{wSXD&v(0y|__T)VsZ8f{8pg9*{h07f zp$OD)HDCwSUL3G5IvEQiHg6n}8h)W4!c)CCz`@g|H@>&+C`A`Fw+w30DJ{*T1Xy!) zUkzsc^ygwhipV6*f8G(YpdeVW?3wBtF>5_(-jWC?{q1()>xX1TON9b>-Jp8?50#(z z+(;q~_b~kVSIr1!If!HM^}MpQT^^lXtVO*-a*&=7M}Lzpady#cZCGBb@9iIwnAp#< zSsBNXwbjh?s+W(@TLZ~3OxX4HSBKLi=4Lw=XOSyPs2_pBiD7WrjO?!wc!79F<&2S2MiI+)i5Zv zLfLfW>#2HnPVQImKG+mvDQl4=Wddam=~c9xX4oGuma6+NB!sdt_DryKaO=_I9bvyx zttGC=@ASgY2YDGk59bOg*2hzsh=@dA zBd9?W09(pSUhKwV@8TdqpWB41$8?;n-m0hyuH-yNv=`P=fgLaDjtFKrC^AAB$KIj! zv+rgJgmdQfvii;S%vm?JTSiZjQO~ z70t3*sM0ReSphs4J&u-S+!Fv&tjxn-7pPfj$!KCD19%%d!_f|etueRc}uy#=J zuvhOc3e0be3FNFT)h^AGdeC**@Q~cl05jZ}Gy(+fJz1N^licKw)H<0TkwhXW|8e;9O z7DM9~ycb4;5%$QbAi|-rhJ|tcfP$22=G_PFLKqkKT*U%KtJT+z2EH_*Pv**bRoF%o z|9f3SQ_P@aPIJ$24U%J5`rkDpQt$do3&(-?&l$9Lwt%r1kVB1S#x?2R_Rr)kpO|z` z-Qe=y*Z#+w&s$p1xsgKTr}xYXRyH}Y44+BGqsCfz^1_F-3L9#O;0!Af8{%%eS6me| zGX|-u`!$fv)?a4uX>Jlb{DgMR?ca#&LxsLuN5Ke~WPNr-|^$O(b-0bs+V6m+ck z8V0n`Xnov!PsTYI4kSmK8mv%c|uOtr9%f<*%94=j{H*Vqd z45Ee{2n8rlI+{4gEHxR+`WX3xBEk>1BSrng9_K*0OZ-BYL(2oumsp>%ESqNdH#0gO zER7!|C9TS^#e`I1z8Xcw*XhmXPw>{-#)cRD6R_uTW3v=x%R{F_Vd>;zjO3C>v(Ke- z+4I#^R3D|_MOEotSqR-BCVvssIHyBuy7C{$g&CFoqF}HJR6x>}SbTq8gHNT!yRH+v z=16DaEr!tSz%eOibXBFG z0Rh6DG(;{H`79mmfT=s`#E5o6u205dprLYPB>Wjv)au&`;TA$Dq}ao>iv^rIXn&Gq zU=x0^dWG6~nxYyWS(Bngr|Jf&!tPt7qM}zD4`6@yktP;dvWuG^4XX+a(Zbm6jziP& z16=-^&LJ~iiMkx5p}3xXL;sA=Se>`@diw>JD_1fueY>_D*VCK#TtWh#&AZNaHc5oX zMJdb3sG)!e1yiVD(tjY%q&Ak)Sa^$RX0L>l0_@lY*Rk){@_r(2U^WqYQ7DP-)H(HN zCIYE2it&Ke7W!j=l;-5z|FLjWbfY=de+KhZBc@U@oB^^Pv_}4Z}sqZ|#&^1H@UccKpQ+;TVaTq0= z_d0u90T(V>SCR!G89$l@Ia^NyyyXYiTA{P6u&7cxf&NkKVnbaUfd2&@`~`-diFd7W zmr9Xb1N9&uLrT0q3{qsY=$ARjr)`PMjt^4>{3f{Nmm)btYY06v;=%TvXyvFD0YpXi zTvn6d9*fkQTvTMSe3#LrI!F<{D@VFc-lp6gh>pBY!AB(u`F%SMg=NA zaqW{Mw^+QK)7N;#b}R#43kXbAl$_vcqrxkj$`j=3 zh|y-vwnd{dbVyTLi<_&oG*(`wND7)an%{t<&dbu*3_c3A=ejZ%-aJv?(9(*F;%Gb? ztUy8A_H(^jJdeBZICFH)#h%A}(^Pk@Sw$=UQs&B*A$hiQ)UnNO+*mPX)27gQQdjMy zyp^#L{s$91K581dqSmV&rJw4W*j8IjZfS_cv9_wa>@xdbz-O)|U{K1|nh~7){1a7? zJHkM|+JW2@SB$NN3=D9l00d7N^x$*1zl8&59V|amOE?GkQ51OkVuQ?ge}+q8rp)?@ z;6zoL7XfGYnp2vc(EPBqOD$-+mqLXTM)ve=G8bHwbxm+nmyoFR<65RkfhY_M#B8x? zVqILcS%uLH5~+d_&Y~)lIJ+mDXHi*3CW3m6vjqj-S@aCGT$gYzL2?6IQbZp%_DBap zhxxqwhU0(TfLBH~V#d3`42<4WZAw}&i`&@JCsm^imkZgDeqd{N5DGp6fs7liWJ8?k z0}hqd>aYRf8M#Uy>N|qcWV?X(1@tFg5YG~{)>XvJ=2x!lhgC;@Po z6+Z)zp1ITB>x=DP(|cxDk<)RZp->lDfQsbZXjCl>pPMpMF{A!#M@HJmO5SP1B~~~IkNgBo_mxJEWfNQwZg}Lwo&6)R z^*T<_FIUSW$~Xzf$J?)7J@aE{&67h*GiTxd$kWZalPGHPaT8u6{^q%jDilp*?M?z4H!3*To1X*ZE==Jn3DEgZijUIn^<#+Uz2`9Hgfd$WTt+UueU@kre!) z^VrO#2|%K4U&v@H4isr$w^lbbayzCb_IebUQqFe#j zP%oRwsw(EZ(SY?qjUs25!_~8L%8L^F5bo315$@MVgd7O>2rzQozXXzt=u+l&^Y!aX z`%wR9HETiQBOOJ;I1rKC6uaD5HZ-+6;FFp$Qe*Ut#b*GnVPeWrV#=a)W~0F9mLbbY zmd&f?>PF(?sk=vX$Gq^S+Kg}_hl&rx+F#c>t6^S0s4Z!9=j9ZV33c=yrEn|rZtiiu z%AZdfdtyRU`we-GEsjm>P%kKNm_x|ttzWZilxcvWdolWyyv0cU8={4#rM$L)>4%2N zDq&Z*g0W2mMxIV!5#a$`b;HqARCW3rw(1xy+m_f>+cs>o z5)efjGFwB`&A_8WcQ|O?3lAuh+a>cyRI((mdPV#a zz$}~|RA*C4PnH!F;yl~dv3|wvwgwPa+4dUAV|9!9zAb4$BBY6i&h4nt2=nviJ`?bt zWdffw8fa&*Sr(bunY6Qm3G*C;uc%z5$Db|%5$rTgOk>8xF|e^>Mjs(HVa_y69LfXT z&yAdZOJ^o~bn2MRy=HemTK>;a->LOwfP5AdYhC32`uLJZxCgX=Dd zkUtXO#p++E!t0+lh0Lw;kq>DMx6rd4A$_$r3sW;tNl)a`Qp`jZfzH)#eB&jO=|29H z3!rBW<)hd&!6xg_US;DkiV9C9=k!*&3ofye#s(?S2p16C$rg7Z<}_bdt8hRr`E&78 z0);1BxJaJ!$o?Ci<*&{Qih4gnF~j{(#l#vCCB~g1oxfl?1d@C+&3PNc=2?-u=!r7s zXueGg)xV)aoCu@3+K8=0{Jjz6TIlh}Ro7w*4zjK*LO6BZm{$wR&FPuNzr7Oo(;LL&X0+A10W?TK zw>LG{%(XqAHkw1_G(?vmPC?^P7(wE`nHcnrbTUCyx(tEThk@pN>dO?3Sivm`pL)1Jp2UNT{_FHp;rRV1hE@=msE-l(_~5o6n3~v z1@HEZR;!sgRIv~E$r%W3bL}d8)tXnCvKj2aAwA6fu=w^0&_Yy(@6@QW8b^5pjSpTsDy=64SzEYq30f*@nG0`Gw z1&PY+t}*OWW>V9#ytm+%1+LTDfUh`_dFGK6y&X2SY=6>t4pN*5)^|}Wr)YW%x5Qsm zD?MZj-k_&4aGY;OIdfs3a*$IyJKLrJBfosN{nZDFqZALocXoPy{U7t`StyN;P=-nc z>QW55mczG;({v&H48=lFS7qiQMjdA~upCq#@_KI=I(T8<7WO6;`i2!b(|8x%fH}KX zj~DY*Di=xWW;N6Y(vIrpJ+WF-E{ozl*oA*|3}YqVZ0-h~M8DZ*M`WWY8!}xoKc@So z!MiLRGH<)O3o_vILy#%Qj%qLn-m)t?yLqFw{*41dLZfzOF2qAey?&~)*|F&KWuN{8 zyhS-pyzfR~th24MovNtYn*3GqiJFKvE;838$J*922oPOzEEQSTB9IR$Lb4QqIiVCF znM(S`CFjzNUp9Pj)(R1_*B)>rly zW_oVM2;l^DJ91K;SlB+&^elGa1!dNn+TCfUQuKRqu++BisW#@s69;k60ceQ#_QJz) zdm^!D<00NXFOyx?h=m_f(9??f*cuSy^l}X_lt59~ujXydy9ykIeFoVSLk`7KO!a>u zkU{;UZn!Pqz`5BhC~jMV8-yb*C!Ggx9sEFu|0BfW58d!bRZJIEGjy7|yOqxcn5KPM z*q0vIF35knrw#=;&WY>P+}hx)vVU}u#dCp+pGC$zOyAupI|U}n!XrT9fxMIoJ9C#s z8v1SkZT;FUPa1YgYPAX@FOs-i4sl<;$i`uVE+pUN<_;!)ULUP5>q5+;Ho`$5-L6{+ zX$H%O`(>QHBlwizw#RNJ!jqv|$)9v~pt_|(u|OJ$1E(p%3l#gDHh>uQlL^EL!yGB$ zba2{CSQEa<$L9}osd@N?1D9`DqqBVLk%gVL+n;Tvn%SjvpjVB~Wr<|e>;KEb^DV8~D_(}plW%$!3JW}f8C2v!Boiec z^a&uuDf)65^bOOmabdI%9i_6tTvp6GO+tiD*H=t(=WHQj)|lxKNW6V8cX$3$?HYyz zo2&9#xY|S$3%K;9bCqMz-y?NZN{HhGqMY=h=h_1k{Y4a!pr;QA0%L}5QVGL+uSdkN zov@ZnoAMoosS_}z-7**Mr%-`GM^Io$*{gVX?H)ro2u_OIiSkDD_@` zd4Y_GoV$cRz~<>%CV*E-Uv!=!Rm4m4W?{YmRLTumN$yO-Q1Kf!_D8p6RG`Jd>uzy* z6?b7;tbOj!@B2q(x-4z&{_OIGCG8~1&iLgzZ~0_21ziS`_SENq1H z)qMv_r5ni8Nh776MuoMSuUgKY$O)CtQX^79y#jg@cH;S`$H5l>H*H8_lXY=XKzV(5 z+(Oa$z9f6KKPX@-n%>ttsxYB;y`Ba)OO)C{Gr7Z;ivIiL%_lIYy+(A3*e1(e=b7f{ zBwIKB!cG0ED_S5}0_*t9bq1=cqLHu?Q3qGB5gOi=cUPnw0o#*i=yQH3Tnb6;L6%LV zXc{jUk0NlbiCMh6XH=cWlixXkdJ;N`ERoB@Gh(& zd~;AQ1Hn5n&(?>7p05;Ccn#QP8)nP^>}U;1$W-9gV|ZuGjU^INh%KRW@I=}?ov^J6 zkOT#>lX1;VS-1#LzVOm zLsqFr|N7tnF%SSO&8`75d@StHM@fKl@{toWdR_A8Kef>0d4aiVs+~F}?%VBAc3z2y zL(;t4vTemO>6hE;Tyb3$f$YBtRsd)+ZiI3fw=wIO;X)_l>iU@nH7JC>wN2h!T|Lq6Y;t;|E+_ie0NX zWORA?YxUX)8s{4G0#rT*Yf}lbNci25hsB*|de;A0{ub=?80iich&(S2il%Ih1iO(=TlWTVtq5 zetj=Wt`^cTy%QqF$?R+{)UTCSM8fY*CRox%TM19D>e{E?4YE6B9?V*-spVqfB&B3i z*NUElSW8c+DT-DEY^eBnLQ+ldB{(j%?dLdR7X=rI$O}+x13xWGG|eUN24Adif6~h1 z``b`S;D2RqLW#;2?GI!0m8J@S60PnKZiDeW$}?<2N5EI;Mx*S~0Vp?G6-`cv6AQJbyv{~XP#)5m)OFNMwAC~A6e3kFqceia@VyVrIF8w>R6o4S?{vcDi0-D{F z(sf0TugVK>C=UQ&%U0aReHJa~*tWm1Z_(wKAe2wvEXKyR)V1$dTNZ5~AFt zew4?dvHIJtIT$JLjxJ>`0zkVr(I6H|c~L3not%L2=oQy%9agEJv8zVDnZF(P#sl!_ zNH_Iu#uuJ)x&LHJh<~6GZTZ zrK=^m2*X4(M;Cu3=h$yq&1`)6*LZWsub{dA30`QfVC9a=CWPS-@2$A}meb)Y9Ut@* zDE-x}1wgQi#75!xr~~%fKXako#+387q0?i@qdbqg=Ar2!l1sp%0O9gQEvLG*0y&D` z0ETYO4Jpbruuc%6AaG9H>TP&Xh9YvL5R)Xy9#V$?Y+&N|TigsDZ4wht zlJHVqXNLGFo^GQfJM0)=(T$vZ(XRz!`c);}W`MO&aAxBo%3FKObShbcklh1JjN(>b z>r{|F`}>U3bzz;!996lJ%yo^kBe)F!EatZV?NH^(MMWl4jS z&xjD+nb3VYL1k3J{vdO8^5F0URzh@yxPHAZ3$>%p#@k!BKb~w}?OnA%2EnkyJ8n%o z{O;aL;(E9*wpB!K@B1L+@VF-WtId^H$Y&|}FvjQeJ?&CAA<*N7l%I&)7y#VT8Oz>J z3&opoNgafl5|Gq8#;au%oV0H1HxP?w4sCxxnI9+H=-{c5WaPq(mzv7XYJIqIbq#*L z`R43cW%Dp=mDEAs+6dCQQK|{M-PN_7;Llxr5Z(LEiL~uKLIJmvo@|P_wtbff%|MZq zuGpy)zQ@~(k(f2Lh zk;=|wW8lKnri*p&cJ>f?V?K&jaTzA!s84~xrwc8XmW!lA97Jira2fY_>Hv*}fMJ41al2TQV!_9yNor4thl180J0cGTyXw5)TQ~jxZQQ-H*UC>S z!%Ywjrp)KH!V!LXy|1jAlC7IzU2^stOP-sjBbE14z2eZ)6z8KfF{^2Pzm$)ZTSvlP zgcs$00=+dNOV+FJi_H05genn3Z_j&rdU|{!6Kd6)5UfM&{&-seCfI8Kv0w*MI>mJD zH?s-Bf<%$DExNewD5S_ugxHZ1B6$I}Sn!qW;>SM5HJF*K;e8n-c6;5>{0uIi?j-n2 ztdnY)ft>(w?)*!}i;Mrst?ngF8-|ZlD-s>L_j2|~C zZQzjY~mv_{|{WaAFRR^8A?n3j*BV;N8hF9#n)G0 z!T7?0dYycFl7U|UVo}uZL2-TAXkN=8TglujZY~9IdTYu&5WkD`$!(y#2}45> z<40Ka#ZsEm1n32@k_`7{DS}NE(dlqc>(JYgK!*k-li5sJN46|V*z~LBBJMSo>_p$j$tjxk3loX-;tb0P)(93aXCOjSF?4$PU%BPJ-WCBhKlL+Bj<6bCGNi)2@m zsAk&y)nqU#!f#!^b(l-@Q9dtdD3S`9r9pQfZtTG(krnEsKZP9yyMPmlvjln02^rDl z!PyCR92hux*{&A4WEK|ohY15{Z9@71P@J(RwfD_A!lzub%{(4T58HX5r$Xi5TWtR_jPG9c6c)r;4OU2r%{_rod<(uc z+ero7iPH4o8y+Is?{xl{_Tm_;<{4z*su0$E&)zw3dp6%(mqD8(5*)^0q1jew$BG2p z&88YB8p;vbJtxt5u{fDl+v5c#0jkC}$i+k@c@~6qmT>zv*NA(tH!YlYN7=8p@QFVM2rU+O zJen4y>5VFAn;5vKrL6DE`dIxOZ6)#7)ru1&qCeqz6u&JkP__c&|O<9g2e%(`98p7U*M1nxsuP}8W3IP83c%Xj}5 z{S@XD5d=$o`W_D?21tdcFY1~2c7|B&3o$h#~ z_LD#5ZOs4mx5T96Fw&~_2F%9_ke>UvBwv40j+vhvGSJwVO+AQ*;%IOuyDY=x(t(O9 zISBDk`bF&g9K!sCZQ7H7j+QV0gR`5PO$0G_rx&Qxg>Wn0 z;YU5p_AUN8+A|k4iZkZn-meO-ozdRobKG3x2k+@!0q>J;lQ$}10LDEKREI|1pP~1e z_b*?-W8&uNigzpEta+PNg*NV9J&8aQhd^Mt=X*db&D_t$uUh8D9KpUbiD0GKSJxqw5tVEO z{R@`!ur3ktxHd;c7-k?_bi*`^=cE~_*PQh^_Pp?5NrON^U@|xXdzQrV+7t$2V`IJ! zF_Sn7W(o;nI~p+`#e^!q?1AZdVn1j@Fo@jMJ%L`>z&n*oZC5vTU3b3LgZn;xYo@T& z`3IS9KWn#nueIUk)wqUw@$G~h3V8t@TgH5zCdzcr(3uNrA?w=Y2%%ARUaY5A{pW9s z?qqo;(5Ecvbcb=52()%zybHU&z7SQ@=le>TB2g7#%~&AV3)Dy_&SPk z?>x+mZy#!;pR7WcIAalqU%Idm_wj*-3cE8X*2vpPcY%vU$XlpIrcA;)rb-*dt?lR| ztX(+gHNjdg473H=W#M6i4Aar4Ku~w#$4sc!y{qGq33})tsnho-uqaFVd4m4S_3>85 z*;XQ)#R&3M`Fx>RMZZk=aCl>?U!onKVuI8lPgv=O8z1gcj1x-11xhZ24S&%vz@Git zt!#XdpLspl=8KtbD^~X$D)b{`!HWL;ZiWV(ry>=`_3T{vYo_??~AkWZDl=s3qmofNtex1W;Y>yyreA>Mbp zs?#i0Y6%)GFQWzIFQOwiA%4hosMLgrF!%|`(mK14fFr!sO9cw1 zbf?JqpN)`+z8_9p5uBRH6^MAO8FwbMl)5)QRFo7pC^MfNQJXzzg01;d)y6pxzkAn! z1vBcn>;vH0K%^Q@u7kYAK3S%byh33rdIH8VtBFvr^J_nWS5~hJhz3=a=p>Rn4QZ-5 z=%cawxU=_L6T)Ta)vU@2J~;f8$wv7{6K#GkRC(9!7zoHo5JK^r9U8SrhLP%m z(W`2DdfLG|SRk||3C09DYGnD@z4c3I2@#-F!}Zg_t5me`AsOQq#we~Z!uu8VqVEYI zZ9abax8+zqn#%GlPr6#9(TR^zhtvTa{k!4JVGPFMrPYH%C0}r1n!)wZ7)8t82@gKd z=(?f#9c#^w3ETn_do7YULLN|dqSO1-{xIHMbp=o(YcQ)#Pg$X;IyYw}_V6>gvoH`p zhDM3V*oM;HeM1I#-Dn`km~r2NG=R31B^S4G@K(e%r6fwZM&7peh`shI?aLNYo;kXo zC4J3o>Yfw|Rjv`;^?K4Ok(7AwAv*jun3-=W__ls^vIRXycSF=c@3f4Q(uhj*XPnuS zfKk2cq5zcC_LFeAW?M|A7KtogZsei*L~4Xi)#+ZIPT7+X_^+B;AbuESQ-Ol?O@ps$ zKNIPZgeW#a!(cR|4!)Obq*&6Lo23BLM_y_%#+`XY$AwYuqRS~N$18j?1Y`PCE)iQy zc&_~`DnU8-rVB<4Z(6lo$~U|ILsGpx49_rud)im4tu%Y&I~`5PQE1{CaX#3TELib&z9@YTHd>C+=LNy)OQ;altJ}Gx~B?1l0ve;%^pxA>nly z8)?Xn_nNcU_Y*ZPI7nuiqs~L5F!71 zr|7Ia#w;q24+i;*?@U1D8U99MH(hR(SmSY`7_-U};4Bc8+oY1b#dBH}CHbbRi~5Iz zZ^Q7a{(~pr*HmRAi6qc;q2Ry~WnYIqNJ?=6X;c?D`f!0?B;ObR)`A1)f^b$r=Kqr*l$_xxF-1>=v+`;Qlw(F0!m45MYnw<`5V)Jsh~>#Z<~y#GJ(h^M?HDE% zfd9Y9<^2Vgn&q`@Ee5xJ9eT~v0z#=9G+0PEGR~}TqCB!8H?ew1!>bfeu~3!0&>x|u z-0oJbdQy$adnv0#yxh-+7~+#|aZl84c_8bhv$)+JZtVmPj1*~!3Fu21?YsYTE#P^I zR+ZZBJD-}VpN^BW;z3EbkYpg$MR`-X<)dy$dm z9CkIS17f~Y%~DT*eZ)nao01aEc!-VfV~7ddsCV7>M@ZLxdft(Y69Wk;&#cpu(2_N< zLw|N<`v1;M0SKW`@+F%9r?05k#}8Rp-p?DGpUW96LdA#;vz zyj5ww_IU{?yMB+ZAa~J;ddK+v$jf_hYMFN1ECl??InS|tb+Rm;USYH0I#C?9V7I?< zq&IFJn%@sH+V=%qna$)-KIw*3m@nFwnO_~hGn zJ(SdpD~j<>#mT6iifRWbT3D?0dHr71^_J26mT@me9znjJ65acsgGe$JdMI7~DZ5Q5 zWSu*YK8UO0XQh?(k_gIyIO>EHCL%x@x>W3 z38h_?AmFQOM-qat!cTr>7RB4|4R)_g>(&^Kim6rENj6*|?WIJe zBu3DByK=}`0!NY@Y{Eq(<4lrn1wwchFxsc|F|IW~uRDPO++c)@E83>7?T?lF6D+7A zXPksAS5<`Bjx>T6X0d!UX^Nu?#fTAHq^LMz;DdcZT_%Z1zc+feZD$JvKtPf^z)K1g z{yHw&8LSlS_1;!ulc|>oPcTu5%A(R+qH&4??dFZVWGFQ zu(+#>ldeTsgu4v9oWSL7eEaLSRU~6b#_J(V?*G`Q6ednGQ)+ZJ)Ov+I_Jzaq2eWgO zFnK()ps-kLTv0i?$yWbdiL9J0WWn`G9&cDqOmQqsIe;UMEPc{=71Jc z9Cghwq>l;YJ+ju9*eUJ{nBd6Fx0Y!U6qMe%mJPaD`mHZVu7|k5CZruu?c;qCts3 zu;Ba&`$4>`X`?W8KFZP_SW_ijMUKUVs?*2Q8JbnvGN~UY26dzz^e=PC$4}7|)qlR7>6v#6JdS;6e7uH zgEC9zmgfo96$glyq5(PTRVvo9|8)EM`sxbYXnuOaJ5>XaQbdrvX^aitq73ChSrwMf zg&;Hj9fx4ZgG=*mNt^gbE%Gl*h#z)=jAraC@E^nf?h+9K0Dv(r39n4O94>A3e*2M1 zN9r2M**n`@7^)7XlGR5)L+uC2hnriLPyeUyNOHQvWM4C@05d$A( zw5ZnNjpK!sP^yod3n)^l;Evq4+RPyh(LHoZGpux#hib9%Ce5yCI)T9(%)tNC5kT!1 z=)?t(eGkaolVm8nZ`l?P!>9IjbY=!S-Cxfv9=mEBbikjRjIUGpW$*=f8967vju8pr z_nwdYFh?^6lwp2<-M0~5kR2aLiDm~69F6+UTB(+?Yc48ujTci11L^vk?i_quX4Hm( z7xfps3IzVo>Hq@*BYJQGOUV{Z-v&F78p_2BPa}COweW{w_))RO=aYx2&y@{wG5;pm z#PcN9Ckt=DWKcpndJ!$lSW`E3VL2&7{?BY_2?oBzRO>xo*VU+ zl+D4*W4YMyo@gp<+C3NRl}B~`rfG`~I-Irvw#qBLuS(RmN|aP1DP=pb-<*g2r)>#za%t$FtG43RTTwo>R00B52}?yNmVzDZ4XE~OI?70wq9~)^WD6w zN-whX;{S`nLL2E4!%D@K`H|KYCs(kxN<)RYVf*{|Yc994Ap92P12i_-eO?LEf&tJ1 zn(X?JA3c-`AXe!ST%Cve3I-W5$e$KWKYbER=!mJm90gm2z*<3y?B zL+8mN)**1h-1F2=9(q7cr@rm3y4 zuRba))DQDji0B@V+DqO3xhNVUiMOjz-KU||P4Cfzk|B^ZLf$?{;DiAPw+5+Frl+5F zdpJw_uR4#;3%R|iXGEiEdxisovA(%je^Ow)c)%t)<6xP-KhTH8`|!b~ye0nU+{v`= z3vLfXoWcPdGEAv1EG~YW^mvdFJXBcly+K1noXA9smNjoWiJvz=D9B5SV-x|&kDr}Sw4YB2A^Iz$&I@?Zb(DG z&}>YD?ZT;zk7?#5xkv?esqa!4Y&a;XlZjpBs{A8gU(7 z>-ey*vrE4De~TO~+U-a<)+6vod_bFk@_0dk#hb1>S%$dP1*wQ|U1a^TrvJ_EWzvUV zp$)&U6y*D&i~D2QL8&8Gm{5lpRf`gQ!?9?p(=*sx8zq=QA6Lnk{%`s;&5Y#M1YQ;3 zuSRMe3_geP)nh~ca#aIkd?-QeX-*k+KE`%$0)A#FU>ElEPh~6?nI`~gn-?2G9V?Q1WHqF=bHsvFFnC^r2^K8vmE=E6 z*jEj@6C$zq2$eE&5nw0&js>4X*I#DAOoir{_Xvetpsd$6lkvi_*bZK6{7lP=;r)11 z1QGN8+=4%u_!5R+>%p-3j^m?0fF#h)qfR&um42roQlWK`u!B=nAH!*%!>~2-* zg>I7K)sMU8D2a1A6lJ=}PHby91A*h5z64G6UXv~b_1Qc-C>qp#Tq(R5?gboghY{-A25jWa+P_reXqgHfNxl6f>s>;A;Qq#pJ1f+$)SAWirDwk=E_5@3Kppp8t8@zQ`?kwBDmUEB*HEggd*|*9bxDl z__h=>ppf?nBU^!F*DK#sFK*`1U>=v|GR?C^3&1BT4Nn?xz4+ zr)KEE{*yotz;1bmYqU@yWD$gSpU0M3fkl_NK8Z#|CqCB4mE?HExR8(HV*vy~jRe(2 zFQ@7ap>vMeKEoW!w!|aD!e317+R)2+6#ftp)SF2-4e&F_OA2Vj<2yGQj$T5qQa_Vx zdq^9RvJV|7vkUr2n*`^Dvupa%N*dyWdlu7k;wT_*2@(!!1iU77+SC<>*vb)SB zK7D0KcdCh7#ULj!L9%#|g`EVPxP=P&M>0R-L$M3wkJ_-rUHVc@&Y9gMS1D6+$4$)& zPOX>#ukPHH*mjv~PT~_joYMYLsB?3S`;sbmIIrsa6_5aH`OL zJm_@N@_~bU7~j~r-w6sVg;C#9U2--oX;ZPA4yiw$%*8~VCWb!RH;)bv zp=eiY1Tg;Wd$t9Kp#EPCam;B)c=#Rd%5u$^kW{~IM-SM? zu5>aO@l5o;LaA z!VNb+#RT>`kNJ~wS;=%^mud1_5`tsGJT%bktU{oj%8nEUb)&+wJJbpml|k=Zh3=jEHdTiJ1S;xU!!cR>=oml#tVjvYWl)ls zqGh)FcEnXg(P0}kNAwf z-jlp18VmiADSIe|g#KmxbpsTLhfbIU!ygUuN!h~hz5sn@8V6>pulF6abzRZX@XI>9 zL68={5rJ)B_qow#nWMwVRO9_i&;D{sNuHZljF1Mg%LpF)w4gDM`6tZckeia!%~c*o zlM=lcty<)#RLT7jiqs_{&ylqlpYP8o$W-+4I9X22nUp%P0OAnl8zL_0SG7!di-Wr( zH1R)1LKcy*#8{d4LJSpFziEn@d}fatr=*#Q=Yh&iQI@A>{>#^%qaW|C%iRf+`otlj z3C|%X4y-=Ev~ZF6qhe0t>|d2nuA8lh!U` z>Y`NAzDam?=m{UM$k;oj=oPL5c1Wr^4{c5GCF-Pj`JiBHpb}y9Alve2xT(;y!Yh;^lMCJTD&@uoU~_D#tj6`>e_Q_C*BZv66ZQY zg^1(l{At3nCs35E*_p#p6h6hsNupj530&u_B9=GL9h(BcztzRo+p|1dfFlV*jxU6!x7+Q`&JH_6ZT`+7hGMeB z+@@?Ks3TBjoEpxE7l2ge*M_E^*5s9kPcg)*Bo7lQL#UI5#dk?uP;+^+I=z@ZI9C$| zFvS>bRqC{!N+L@S?6@M*cSM%Lz^V~{d{-S6Q86OcOWf&B@4g&0L8m1Z+Xeo{B;`*t zA!pCM`cLA=!t$7MFQXZ8elcardGpVE?tv1D$@PUUq1`lh3^Em38O{8<)-9LAaZXxG z>S{YC2glo91KDtghyNl4J<>t}`e0=02kLFtG~n0fNzuCo#!_v9kV>-he=#5kfTxOw zoYaXDxb}Pc2Y|P>>CJyIwg7f8n0RHhm0}9*#Vm@IY}!3unzaH{=lla@m~bNXXwch( zI>>Q7yGnYu!SkL`B=sA?r**K5AhF_=GTY%~+tRrK9xiBy=d83rgeH-#nEyE}xTa}6 zg;rpPnoQW)Uoi3;_H3r2BKk6LvKM!a-i5{~c^bU4$3RB+?o=NwuugxHJ#5#Fin7Yj z2roM&%Kl8LbTgazi`clpRi(XQ0BWcRw)$}{2ZO#dn%i>(|)%EgNEXzjf1UHWN>z?lhge< zM|u3Z9fcO+ma%#NTpnKoXnzL35WE{iO(ae^)8$Vs^9t#foa0My5k>;qEwt0S~yvmZ^1EDW@Kq~DmS z4$8a~t0lh)LnuG$=T5nwmoK>!&?fEW_w0d8Y3D>b z(>2*E7V3hTv5Z4Etz?!$meKCi8ZBl5LDqLaQddM_w!wdBX`B?oER~zF$t02F-XAXwG4sC!l@4IxtVD}=a+k{%ld+8iiG)30Uo|Gut4D(`MtWm9}?2_ z(!PnPXFhGVh3@~?I#s|9WE|mR|6J~VsX*!rjeCR6mF7Ey{{%&Ftb!Cbn9&%xk)l@2 zEyrqV5KvMHR;lZUzAo-S&z{O&dvvJsOj&d)Lh5G?Cr63Ku-2bSspC&5afDtfUEiip zi+oj@Ae15!3R4GvYoV51I9OW>`)r36G^^+cLCgYdU;ngoha4I;0;2HSJ>PstFZjLb zRJ*L-4ISOT_GHLRG^0^evzVvPWW8^hP$Xj-ThfOWEAJ+eU4sk4n~sHHGwB& zytn_gm(ut-SL=3nAB+Y18+ppuc0n`ub8O>LY+v4GScf@AkF`-#Grf7cDfiDP8iUQ2 zS%CsD8Q{^*N#^AajqY2=41~I8opH>KP;dVE48L&49Kf}u9+6Q%M1-*O&al=-dOT9u zT!)u2*B^EGj`Q5P!W6iZy6Sd`qkDD~cR3WXMss=u-U!K&pQ2g>R9}}k0jr0@cE?~O z9KAYT)F|Y27D?^$vxTX6n`U`2!N<9ULdVxn_Bx{ItY^ZyRp?|o{WzD3rXfbhQL5NZ zTgZ%^pc6m$TTxH*V$jDn+8*`4<*+FhwA*-sFdc+@vt=G!_d{^up&m7QVlTUsr&p*| z9gCRjbah60cP{ZFT@bk5Gki5`BZ(rjw`EpyU#(fG4I>C5C*b7m_ThX`j0Sz(rzUT& zC2#vXb{f02&?zMY;64>LtN38a0dQQ2(%8DJ2>dr8tO%q>aLvkwQH3uQqF@J6RH+Rb3o=Yaq6DX8 z%O*P>lgaF~L>&ihqy;aTxq1wq=^|GFAR*pu7I8u6unR)urHaZ;&bUB^rKl3brB@xp z55JNM)>GNf+~JI3=&?Rhm!1r@E&pYC6DI2C0H2dL*)?#*kH%nD&hISb+88PgCLgR4 zEg?D)>%r&CNNXeE)U~}>NRl6pBe*8#1tJ>?Zr?!;Q+gBw0&UN|7C)-3)Y3+jt=Q>} z5Tp!6$|ia5+RBN1i;Fb^u?0CO5HX*O70o`D1RLNd!y`bCI9pn&=bGxFf>5)i@O7*@ z`&w^o(-DFZIHG`|4mETfxV#WjTYda9JcY*p-QXc_1A_A+7~^;+o3+ML=kQHY=dUC= zj9&pl>ZoNo`weB)>D$N+lsQf_P|L@~yZY5Ray}p%N|Sc&LGL$Rg%uV()2%7IbgV7^ zWiuQ;sXHRt_?GqT0n%rOe8{Sjfg6 z`5Ug&y#OhXTZJ{t0rhTYWAV~-o9LvDdyw1sj%U!2tyS-Ox1$4klk$KG>S>pMi|iS2DYo^+`Gk(@=eY00!6FQdlvo^6gmb1RO7PuVEu#;?yB=_LcbV^>hfsX^R z`G_`#f)ZQ-im0}Cjj6#H(izB8)DU~kmGPmi7p!ndA*BL{2az|ch%`7X^XjEk~-I;4ZT#08PIs18;`3zHq}3xqhUoCv5i$oAQ0 ze^WRtxYs|4lKLMhmDi^Q0r)8yWE`Fs`6oeLt7mrt>t24HDLP}Kb{ntw38fNW-9ycPB)?nNF(dC4iX&mXbg=ml@_#{#?G~feCQ)`jb*0_zZqr|ERH7c-=PlLt z$5HCywV!$e_`E6rJ7S$Aqjmq0!PvAjG&%@o?B_sBNs^1 z^B|AYlhxJ``nn`ET-L7sg10CUN!aNb9)ZeeF7&{T=)8Ap=+SXOTqUr96`LbiaYpj% z_BhfC@Hd@t^K)@?SC=!x?e}Zq&zRtlqq~au-6(|wKi;@wZOIG#QJ>-jgDovmTI8TV zU-_6||176WI${6<&%zmJC0NU+%#~)1KRWH2jE}(NP4fI)B%0HNtY&ARQZ4pOAL%;%CefQ#AH1gj5+ZK(!4I9{^d=% zq-~A9w-G>A`lRVSZY*gj|F&w5I8K>EW2020=gEK-RfzH%NQ$8{LUuU4hNvI>!q_F< zp2}+qlo<82QJrHFoZZVv?D+x%XQ=}1mh?deHa16!9Fj_b7FRI{LdDZB zh$VTNkmcw{$7r-V?&Vh3HALS>GAgiBFjt&)RHG&=jatVMV5k^+5+v~X zDjj8tL`$QAr0eOysGOhqGW0?W$D$R<&C3o($;5E;3HJ)?40(C!N{;=J_S$sg(>4I` z3YQ%azR@H89Vrh(ATc*{uHH-*p_o_%0eIdxFT>_2JBn11I4QU27Rn zF%eFhuqckl{)#ur_HaHsjVU`qB#Xf1#!?0QXdW9E`%=4lCgJAdmY z0b3Q@&K{Q++`!xfqQomhM~z*6uq6(+>JlWIzk$zXwWQNKaxXdU_kxD&k}`i}A!e^z zq6ukMGnZ@!HDcaGwHb0PP=vVv!m zb$NF9^$roKMXdW9qtRy_xwA!s+WSNxuPI3IDPWoSEV#7q9hZ(M=s&eQ>9WMjEd?{A z+(ZdM_J!-J$(g>W%h@AVkY^6tb&s4ejIVaNuT@6oM|IbNl(|wE=jCUUhV@P=#;EcgC4p0&4w6PZ?$eHLD$5c3|8!0GS*xy z`WdCf*ToQ1+Xm3oJA~E5XOS{9g}Zpv2RQfb#SwtJE^}P`ZWKTy2)cf@PHyUeg-3Ba zYt(h@bpvo1Uka&Bv4fhBE(wBa&P60HOZDVl2h4PJK8Itx^?8P_`UO5g&40VWgrg)* z^ec-T;nfv7*TJB0wNLp{_w{y%EN;1Zl2wt)MWBLG4@!;F%2RG9Z&pUFU23S?!4gHt zNIo_E%=mmRpvtLx$%KHfv!>Z>8#^EX2U5TLX$iBOOjNRKHbCKJfy;kL@x~kRRxEDz z4&x!FAZnLR?B_NI_>@GD-5|UYw%KI#)*_}Dp}}kl&G|>r7R9$CmXLuAA_At4%4#c| z-?i(r4dPT&DVQgqH-TEM6k~c6>0n>mpWq0q`)>}q-IP+!U8ALt_Htu;qCI2;{L@+nKzF(zKZ^jYNMq!q)eSCvDF+fN2U$-uaRTo_zWr-b7wn40h7V_(U4UjtPa@ z0A*&Cz9(n}X(*J^s~xg~97QiZ$WB$BaioUu=Px}+dznyWO0~F@x|iKtQ|Dz!2~w_5 zT|NVIydM#a7@Imh4|M~O^#AA`TJ7oElHN;;$|E4RDpgU3j&xf(Lp;gK=NUp|z&7a7tg| zoqGo#_b+TNxy`x*K!uG>HRcd4Ufid`IQ=BhwUaph(%`Sv7XOGAu-I5Gn_5*MNt<7@ z&eiAE=a0SRUZf(E{Ss5Ld@ux}YYx4Ko`tWk<1C`f25Cj6gy#|cSfX|Z5*rq!fB7m7 zc*gD+P)ijXpi2?o4z>r92toc^0jvq3Dye=Qa79vS&oXW3v<{5Ai(@rfkYe3IKu+Po zju_uhwmZe@6%X}Ev5bUvaD`6@(%5|h7lo8i2ZO8^wuy|GwMJ%DZ z1E~Gip;+blKr(4+s5MXOr5^miGgV4PAk*~(Y40JKA(>W1{o#v~~2V~dV!MF860t3HgZd-1zD>F8M184_LHFPnzCLvVd zd=~rz(tV>-xeQH6%sHgp6(OX;Q$(gwLMCAd*+bc!P1ce`ai@Cx=+?GeRKY-Y{>-dAB6?Fbzvm@6oVdDQ<%-M>Ob@X6ys_R4)t0)= z_}5zw-ZT;Q5n+qWMK9^htF+5NMvd;WH<0K8kd{QKCioN;Z=0&DA|&;E2Ky5j4O(|d zUmDWAH8ZA5j={{6BGbed!mNF)(fv%*x(@F1~?dvZq@$5oO#ZU%rPT?&>}1G z**0}k4=1R8%CAI$kA>-kGd&RWidZi=Wo1zzw9fTSl(MTHspu4PzWu2iYavHyB^s-~ z%*z=(%uKyE!Ixg*VucKO)?nV-xn(oNrUmy6`1+DG2jpW)2P#)a3cg2;evZb3Yr8PL z#nh5I{xd%sFxtG?|3>ifoRz<0jho$G`@7;^yb<-A!eO5%B(0inzuq!id=6Lue*LcR z)9E1__F|JEEbUuDlO(3Xh8I3vLMX|L?E%LFGZ&CbV?=OYf1vg2n8MR(T3~=Bz>Jr$ zjVkB!icvHyI&V6c9vtq`Yg{@zR^@-h2@yM#G91U>j*H0b@u{a}!)fY_+U<^md&lm; z)NkfV$aNXfOa-cXRg9qm9-{k3NpM--{Z`{3yRs2Vaj|=KxoEv?Y3|WLhE2I~#*9Xj zPm2HDhAzBo?*?|CZyBp9ZP%ae=aY9}Cw+NOU`PVad1B08Ge%5Z9Dx@7wfj*$>eMaJA*GGa&8w*{!EP!3LQksjE--!R{9K;*=*g!tGDV=hp0pF>K*L_x z8S&ca)*Grt0|G0-!OVs*G7Gc7@^97Au=Z+MLD0FCWuPVkL?kTkzax-g%5<|B#?77F zjl}T1+@T{-&$_i=cuCqvW7S#?(kTS#4!Wus67w=fvtE~Z*&o!{Y|bfPjRY%l65sqjDU}nkoI_s`f*p*^GJ#ecQsic!8C;TSH(Dh* zY&4q-VVSM7{sm4cz^tbw5j@C*e-dtOp}DtOe)r3+CoChzZsr)Ul~Z~~Xv{WyxARyt z0WUxGradr(e7Gyc-{^)9w(`HZFE(?1AwxEZD^|nU;X<{8=#qY1_25&V6a?4 zKut;hlQQDqC(Mk*x*&{cx8(fp$D&)47~#8u9vqBav~Tw>&w}VWTP1d2g|s*a3++3A z-K-gB9sBwl54PBav3-62=5I)C<5asjT1chntNpHsS z>ts&ZCH=ZsZWa|J%GmVFOen2$Q%;%aotOLKeuDLu0ECN2D`;cz^%mg|>By7k4QB*AZEFZGymHD%20?qDKpe zS3HUye04X7xI!Zh!RNDgihgj3Bd`q z?zMBPEfG&9p+8P*s5h05(a1*R>D`0uv5>Iwelr+TEtk5$?L09QK-a1R5Q1{u=jH0_ zPC?}@L&K@i84|*l!rlJh*-=^JG?ZY|TcjGdC-%dYoJpS4n#i~ebLD7rt{G)y13)}l zHX#c4aEks{<9VNFm;%vH;IFjHbryR2_FB(R-A>d%FQ@WHMi&biLw0*Wrdk%MEf6&T z*{(T!Ny|3~^9@XbFoZ{DV_ zlTf-(uSGc&NKBi)sVX-%NemXS%*IoOd&mOJ5WU8QOq)aq@J0-zU6Jp#(qpIf%YVG4v|H1Q^$p^61A0# zIGMYb%xP);aa8M|jRXc0wtiqtZtJR9E;_;-D1J}rV41Pf#+NiDGIHod2`y0EI*wEC zCs<;wt)LfHc^D>x8?dVHCM679HW}W2(fSXKXoOSX%BLsN%p8qdz}C-q{r)b)gXyfD z@zE<{*QmAfzB-~&c+7rX%ZEUc(k?A><@HYE-jxT%S0)8n`nX=csgw1d7Rz_2VUa{u zOj=~Hg{#p%?zd4;ZFzxB$u=Yw6^9%KOELS{L*~~ve{|hA!FVPOE^Lv)*>k5BS%1Br z1ohU-naqjUHU>-N&u|Z$HH46hc3E~31|vUCO5KUfG*SoUT-d|W`8df{5A6askJSQe zYy*ltfq`GmwjQLEwrGjw1~%-(cJBZz!AHVV<|4%3_MWRYGw|`oV{buNXnq37)bj9&-i!+fpeb8Yj6)Mzl=<;t zbw1RuRaGS&Wcxl1>+e+%S}1;Y{1v#M-)FV6=u(MC`>r*v;MA!N9ruM^Q(~#mUy&b- zz0J|;xxtJirAZl<;(gl-nk`yV!+;C?e3xHHF_Dq8#$gLHoP+SZGeqAulE?F-`V?{m zPJUE~{f5rGS8y{flJ?)st>tImg-l$;nPTx9i_=38PY!L8dWA1T9Vncy>-80j%4#7< z^*bJhTfl^Y=C#Ig1Ch1Fc76bM1X`k4i9pd%D_MVa|RE6uHgny$B+#XWw8=DZOTe0__m9 zf#3dN_Fpy#IWCU}d)oaKDK$G|9ETw)HIf)7o+mE8Vpk~gf13A`vNGG;5sOzbF--Ev z#Tfx}c<_{(p)V6i3c{~gkKa-d!iZE>{qYHQ>%P9E-}R=_JdO3@ZVlp{qWbsixWLO| z_x8*U<`G)GqSMN#H8XRj_;6tM=g3_XgeY2fYfl8%)K&ks8b$o*%OObNzetz5DT1zX z2zWQ4u)5BPW57vo)az|Ih}4|+eoF(n91aB{@vc0@W=!Crqx-WwT2zOj z^z!(rxdF9YaBqXxJ1|2#?eU37EhJLq1b-xh@lk(6pz@s}#7QL+5abWI%Hek-^Gx0z z;^6-C;Dbr7ZIHf;B`+nv#EVVdRKu5e`*!;NEm*!$nx$T(>#sQ_ft}{5yuT({$!y-N zMCUqaAGayiP}kG2VWS9ssH`?8RH<=#4H)`w<^%+j07^{c{Hogpepnq&xXOox&EG-> zP=Hq+kP8xCT(m$5GwvPAIqr;enz^-q(xwo+j7a|xxs7(_^bS=lE^pIGI>Na8P=TK9 z`{xu859%AkH&!_U`_O9$egIe0owyIyRXh|h=PKH?7~V(NK@VIxWZiO^r;=jJ`cRiy zB|)0bh$pkM4p6Vk>ez=z6_$_tXfjH)jU#mPV6WxYBehn&$P7Dz10@qE8MNPOx$Z3I8YP z#;tL0i9$E>YpHR?1%Zd@lHcO1ie-|`;!{9N1Z!vohHY*OsBT4u@4zwtxbE;@iJbC@ zf%Vv&IVxZa5%fwjZW?!M@!?_9kId4U;pA@H5z=Jn&D?``_+Rqe1$<{OC6^E~w4`k} zJ!Y%?;yF;MRVq3|xVjKu0W^sEWWQPm4c87?WAG?%FAt}N>=rddmA`wfb;8T${<0D> zdKEHL3Qf6&f|w#M^a92!5>4^ATjUlS-|LGZX{{MvF7uhlUTOtDYL{f#otZ0tRliG9 z@elXz|3JLlO$w*QX50~NB@Nn3C7BM&xBm#O)8%xV>*ceun~6_NX6Qf;RWpD=L0@VJ zXy3&b^Jdrr+2hT7VGs#Gc+bx#8(}b$g4$e*UF2=~53t!GGwx+Kx8aRn!u0Q6-7EhY z;#e|ipCf^90|W&MOCbPo0yE3RJU=a}#N{w#u4+R) z0StaM4<7zPNI&g^&#uIw?S55XL8#)y!eA@9KI-?);~=P|G;Yc@Ai1s1*8L+Noe{9K zX~2+)y6sNuwdA#`$6?qBvf)y6qcpx4L*W8UhICO8jV`o;Bi~vV`E&eMNuSc>=cO>c zMN`*H)L}*qEcbVpFp5uUtkxJrbQXQ0&EmVeR=SW*TH_Ie?Mnh?^|L5 zgfA7VN+)(k*6PUBR}I*=tf_GUuLz49X+^g4?PFZ>+11&bS#c)(~%r!asicwSAq%E%W7d8&gvsWYs0mmutzmRoYSF@gYUns6FO;^jFYkT0uH zNTD_QaJS!7P3u6jl`?VS?0aTFRXI$HjW5>AZXn5*=>u}pGnf=OGMlb#ddmUMs0g3< z7mLUBxkm;tGz~h&UaQwfg~X$*R?$(G(Ncp10Zh1B*6)p_0;F=8&P@QQ-5!Krep}(a zTjtfIvsDvVd=b6e!oyqx9JZ@OZuZ8O@OY5F8fcx=@gFghiyMYjH6Rp>SySwvnYG%A zJ50D@L6Vv)0+PA!8y1lI8gA#ieBTM8cOF_Q)lY@U+mLr@c_Msk*vj9rmrB6^ptUY7YOINEFJiu$Nv%%5>U3wJrV-FCU0-m-no9 z>E6SC(T%DZzsGRn%XxA3EQ*7qUwAj@g0ahhxW)=w<$|$@WWqP+NL}ZV=)SXY6hVu7Hnx@m6=~0j+oo4J5wf9)?=vl zRI4Dbb|!eB42r1(=5F59BayUC1sk-A)C3FLoW9MihRJ?TnE}U>Jwmi-u3N#XxbUR- zjI#J_pM$gbN|m|YbsK>tqzY_BMMRgiUE9JR-(awVTG0Bey^PHK1jm~ww9oxRVt)EX z%OR|&HY*=(OFQaZLtmoF`gFNliX{)sPwf!pWR?sbN+PW2Qh~*wFD@p4vE)<2TkMs@ zY~i6NDXO1h@o#uybsFutU_I^zP(!wWVIKh{^B|o}=EXg>^EkgpmJom#OTlbqo5i9b zB_@q>+Yt*Zc=)e3Pz%J&I?zctLqrLIQS7YgNU-_JoA*!3Sa{ecXzVGu#)BrIq}O7m z8a;Ka8R_WMcJDt)``cy655CohXGA5mqCM=drvNHV^z);(ru)6@DB)~=jniAF9;9Q) zw$gOuN?Qp1#zc;woF+G)4s13M#f9Kv6Kx{lR&q;`m53}D4We&JuH9ap1~CKR17l3^ z=A_#9?x!V=c{xP~a7m%jU|9+N0pF#5?GvGopf*LeGan06bYE?=q0$#_FksWt)->il z>%r0dQ{0=uD$aA(jNuxdU+}#Cw)yH)4`GWD%hi01KvI(lsT+PN%0tTxhR7s_6p8as z@voL@b=6ZbJuRW7ts;v1vG11i8&NkdFY899A29It#u6UiM2(EvkXeWf^zgeFI~WZWRPkK&Tno{6Kee~X?`njh_w9!eDG@QW3AoiGj;vL|=4 zEZu_M_>;*Cla~pGuM$7Z!lhN_%lITnMn)aPeG*hqVSW>Iy}EZkg@Y~WpA@ZC3oJI3 zN986=%D@4tXpo>*u2_@edhY#s8TNGg*=MU}AOv97tQSq#)WMG{kfs=FMPrN@qF%WX zer5HniGlzwDvLpnqNhUR7}UN|mJiCP)1bZrjfrOcBwyB*-k+k zq8XXV;+=MIkR0RV#a*Q1R_G=WqC-2VG?Mk$uK~PEbQnFxL9uS0lJ7SCv4n)lvgiDHq|u*q0xiywK>E>#Fl-rR7wygG{I`ygZFphYJ+n+K zx`lzjh7){o#$&6T_s(#NQ_%-nFo5oQ-9P(=#Ow3oS1Vx^v+rJ4 zG1Wt|(G9xgpnC)G&<6$zH}S;%?Q5Wr?Flkqth{nsuZin|GZ<}pm1Je%^Ae{Zh~@n! zonz3t#vPng5LHpqxzT$@rF3xlTMJHo<7n(|@|$2W5UIeG7s_tdG!LOi>ZqBhYo23! zG>l9;`xkn*)2|(hbXU<}lz5|p&&FKnOFeP3bSE~?J(@A4D~@;)(Y-|1!j z(uXhY?uO49Y0+o~zvVvpVZHrKHcgc$Pv0B2Ao^DHGKwu4!3EY!^PzVnMH|#B??QZ>vp`*%wf5u2~ zMr@*g2e1V*Z4;w7?(~dih-HBz68yVSb4P-u!!K~{kK9rR0CT*g;0Xy+nJ8FPS zFd-M%V9^MX4oOQ`O!*Ap;+V0Oi9McuQ;x`emU^?2Z_F3)rAzdP;7_U?+p1+Oh`W}8 z`(H>X>Tfg8V)cKxWM0tS`2tNAeRF{X^fA6Ps>DzJ-JUOd%*KL}_@7(`#t}EUc36$l ze{m`yG-?o8q7Aj*9qO5DAzemtEsci-8sa8Lr?8vi#(|f>1btg>g+_4N)U~H)9Y(Z) zX-QCm8@?towCDvVO%K`U;4dxr z0d28Uj|{8bKR_X|X!W)CgfS16QK>g4S<>@g_FM9|R~puC3lR;|VGK$D-9=cqi#Mb; zy87H5vL1`Dhff=~ z6*dzzTAo)m5|c8?UW{9_^MG~v(YkxwUMv3OzMe{?NTDp$twD~`jKuzh@CG@D0~4ef z#?rn|uEK{P>tR*WT)LIxy>gYN(KeJ^ z?iRvucM5CC>sB~ADBN_zq54{oC3L*D3dd#JJqklKIoP(>waA?dh^|5JLDnfqf6|0p z#W79J2TcKNRGv~@`iU^N zp`W(d2YR>RH0e>S`Q|I92kefG+$RvC!K(d6L)!vJ&q{+vOE;rKr;~#ky>#eZ4*D9T z`fGAD1W1oT&u^j&5dj~f3`o)c6tg3IwkoAu`JzFIV=9t&R;ScCtx#QPw1pV915tE5 zy;!175HyRmbhx+1$v<~`x%ekn?%4zuiv>9ELK&amC9rL~zO)Q9EoP756&JrBC4Oyx zC$wHZ<^j&ac2P6ZQpq?nJ@K`6Guo{>o67cT~S#_no>#-tRNaQjk)xH9vHEY#4gZx&5t(LH4 zh}s0Q?O$ymcF_;%&>;l$8V|LWtVE!ON&?8LcJE|7Uza*0fD?pT~A*tM<`ltvRSt7=rY}T@gY12ia zuzkWNPiy(fNd;=`GHj9-CJ+f~S%nTbAK%}|X*<;UweuAp9M*Z)%DAT3xBV~6uI$MajQh+4;CN#XUNQBW=PZLZ&WWI{joW}z3eA(9T zfGb$QtkF^lpkrFQC`>{=B4`ti-vFBpgYm1FxbXa{F&p-?_{VpQZp&Gy(cOOt@&$-p2;$RsT>k=afaqa^t}p&jv34lYdm zMN^dBEWDfag=VCH-j^#cxp9IyfdW~fN74oU2eW7q)B~vOEIQ_(tB}u) zq8Z684$z_>0qZ$7Eg{EsTy`j&tm=zuN#NF&6I)<&KAOBK46@moM=#Dxp|9YadNK6^wQ%cUF;S_uJMx) zs5MK8O_!HvZ)Xc<%q>Bh^_|uYCKUOMI}O9dYvo!ph!b;&S$KbjJ{%C8WKJOJKx{%! z?D`J`6IgCgkoix(znf~#y{t+ci*Y#m(>w(^&@k`kTr-4wje1)j%I*rcF~)z5xW#CM zE=sUY1tT+_T#|@FRMRRzbXr>^6;Io_R2Gp^4|TEinAoeW&;a00NZAl{xCAiX%K_+) zwEKE{jo62HoY=K@spvvaSy>KJ+f;Rfantx{B^Z|b>(KD$TFf9e!1Q&sC$Z`QE_)Q< zf3{Yt1-A2XA?^55c?P^4Z(#MB8ITTO3F(8$u@@&shUzwr&@LxR7Fe#-%Gak8U*&77 znY)hV>ctz53DvGdtjET54p}GI)6P3^G&T-+#<}RkmCx+TM0q$L7|>wv7{twZ;)_+V zf6~b3cRdxmFne5@hw9g?XPl?l<{Mt|f*%Do7~o~zj0y=$>D{MIu(KIDULPHwsBmrV zB>i&{JT3)n7h>7iF!$XsAA zh~NVwA9uBH1ik`4Km+h_;DE>m`FdQo^7o^feG--utj1*MhhvNY@c|O@O0lH8naTII z#e*B9a3G}BMA^;1=R6XosbCnbJsP-o%(+3QW8wz)1^x0MJ50kFH(*D)NBr$6Oe zDXWv@<(;l+0dR7Lb<_V2-5 zUBz7)hhHN8*i-}&>cEs@&O6AaAmMA29zXFc@y@+wV+6nEr~TuC)MtKNjBrp6#QFW_ zw{Hv8_B#>&vi6!TMJK;4l8yJJVX?}TJsRVYNh zb`HRFj|&I5q_#~gQO_v6Wc<$BVb1U!dPlud`V_@q)otfdwS@^i7X+Z}29~aG#PG3H zecfnZyX4l*VtE|aBE|LEOA`56Dn(S?r65N0C$^6be?1^z&lP5D!IXp*L+`%ly6rHX z35;&lTOcfCns|pWEpl!tnmUb$YB~erc%f zlLFp~5BCH%sT&0y)u(-pc$V1x5JN*pCY+H zX9oX(A`=|wl;>h=9YNT9=K_(gmACGk9o8?_=JVtJbD{pqSSd|y?IDaOp`{vv_HNk} z`etcE^Q@Dl7!B2#|1*gd_e1e|X%XBpGi_n;P_>9`Nt%M4c}9?o3f=yp7Bcw*F5}K! zlaI-=su#~8MN>atDl71uO$}$EWqA53^#_D^JO~3u5D%8!exT<%SsaGFDXzLyD;SwrAuMw0KCnXSMAIABoNqL+MW^crnbGgYO!mxjUCSNNJF z`!wn4>POHx;RiDDYbcF^cAg#5n2}gmxisJ~SyI7Y$7}62>`|f}2(dM_CJv;=of8_w z%Mv~|<8avCoyhRB{aQ3wL|#XDo@Y(5+L?cMRV&(UuEcunGpKXspJ~{Lj+ajR(nOvk znRN=c+#YVVe4{#9E5iPdZ)1mxY-T?zfJw9ZlGS`9puVAo+c3CbqSYsEofqXDL0*hm z&=d3^6qv9FArTPm=qm-;@ab(@sS4y9&p7ctnb5Woo``H8^&}i4hv)S+*@d={z=!FKP(8DFRKFi`7=hvI35o7FKKSM)zq)urCCZ(d-ug8 zmEyh;4q3+r^t*AN?=;rr4qjkOCSY(6^CH<#3pn7g0j3i>E5k{TeZ%F<=N_z^INXv3 zad|QrI17gwX?}M{&BJ1p3RgYbh5~emrlzA)(`)f_LIPF37TJ_fI4GoM^HI=tNzz6e z)ncJstqiM7edNguG|tg_Ca_N9qRcV_SyP16xL{Xv>=0GVX7Bo`FsP z4)CE_ON1&t=HetGMk{m~N3!!1(%qSBQv7YLIM0da57F={a8z{lGQ zy(y9iE;p;a$nJBXK1rw@(I0d;x_2OJc5f2WXpMk#E=&>Pt}Zuv`xUAlbeFndNM^=I zfdI3DzzA4Wn;xHDOP%yd)!@oc2A_jcYd(-TME*EQ0>ADCHj;C9z)R>bqpd0m|3wMO zR7}_k<@U$mv))YG(4(q~ov^DLTQ{YcDbm$F96ZWj7{O3N6A-<(A!*fL>fZ8*76#w#!Z&qJodWK~BF$kvVdKE3V$y8h=A^8CVMs}oGSTjJ?k z9EMh1k%GzB<-TMawSN_e)Cbo9#vmVB4jbcJK9@hWj+Vw%<#HoORMGY%YaTV{%ca=^~{c>vk=)J zJGctsp487Mo>IC81d4~7qo7r&`Wj+md6&y`%x5svX2LF3f8u`-0R>g#GjLrW$%}G^F z&$huH?trspEf5YwR&WAO-|s6X(WL$X%^D6(=N;uIM$2;vS2(-1YJx6jU?ef+J*Il? zN~O-rDeC*T6D*~+<&V-$PDS?LF;4MJHDV70O*ijWy;602C5>XeVPBpJVW??X4VRI6 zX|Cnaxv_kn6H@gPm%_E5)>FvuB!Z`$ScM6oLg75MYe0!*SkQPlyJT*3i?_HRJ|Pzq zBYIp#+xYb})l+8`H7)XMHwf}ph^`R}`&&7^ZPTn4+5LR$Q&G2gRVIz7JH-MS!E{|c zZnZ>};ID2s=*^iX*UCQWPe_!?jVY-*s+=@k&-idTzcp}bb}_8j7&QRPi@bklXAH&r+Ni7Gb?$ey*=+bk&81g3z5#-1UwIAp}- zx+4{;&$=a98nP(|AuaLG*>`2ADz`o#oy}!e5u;Y2_~^Sq(yrG$ETe-1O0L2Os~~w4 z!?!1$=&zj6GSj>FZ>pi4A0SMz80F`kX_AgnJlnpY&GX)C-Yt4)5#0RmM+{+T`wpT> zgvY#FUnXovm$1{315J{L>Dzn*ggjXqTZ8q@E;T^e@^D zTZv83RGN=sjodC@zetJi@61p!0uAAPl^j#oF8#Li&q@G>j*jpKpW#0j>w(cy8L#Kp zt~9}gzSHl22+ulM_SIft$3re{R9LFjb%)-VPZ2)Y**56R_fa^o$umqz|tYJr!sfv0cD_e?g*_*6V)avv=u*S zUR01|pXqQ_w7eiA(O%YA%)k}cT`RYE$)?#jJZD<2#94vke@89LtsaCP7emR}6KrX2 z*!xO@km6!`M=K%Up-HFAVI+aQi1(LdcmVP?a3(4MD(`pE%ERi~GFNFz~p`no7RMdS*m*%qgBZ4`eM@J5v8 zCxeykwUvETRxTB-aDx>np)xxy{A8PuSy#AT_f7kW){>*y!w&(Ck!9wi{S_M1Z8is= zbb)q?yDnUSr62(jLTHr0Vx+UBd+scpX=xUAD}#vzz>YteJ04>fyb8hnshqJYtnTO` z^4z4TpfiDr+@lHBRyxia!5f0-0oM%eSo}zD=t5@95OxEi)CPL|%5Z;Vl5b;V$u|{Q zI?G@|giGhtSr-~}GC-mGEe+U-josHo5R+%NGreWg4Qd(3B^<@jo}OO%!BB6;#F1J5 zM=AineE(;P%zZGx@_!O6gtrQF+@(s&6S&vuEbLt&Wf0wu+tK5U9|0_jUGoM1d1W!|NI|<$7p_hZ@S|T&)jT~Q zoVt@m6_n3mZ@K;t+{MQaCi>arxlDkz0BhtVL#eHkY`$&p-b zP@ayj1ns3%sh~G{L6~6QN`R}AQJDh*{d(r{a*$J!(;%&?EG>w=%U!;?W1p)Oj+qgs&PNc$N7~W; zo#l(Aq9?SA+?g&Ch?Kn24;NzTyknfDF$nvm<3KPNoO z%yj-&;Gh8v_f{ReX zEs~?)#@!?Fj$44dKl{0iSBYsVb#Y7NsS372@sQvb_Fv!B2X(&X#}XduqM5&;6?(R9xkB0uI(~G>yDD-nN=zuJJ5vp!CLWg(<^H4U zhix8IVvdg!8$sOs9n(Y}edjr{z1Qi3%;Wl~iEY3|W|>v*>7j;fj=*9a z!O-f*@pB*aUby}W=&s93yqIuE?AAmAIwc=NzR(yGxnDQXNv?S>_>14=CxPwZY!n8YfxJ&h@ zQPHEz_twEv+{YBA4l{~8_zPsO`Q_j)RLKI~{%~~?&9J5QVuFyy$K^5IqH1n(Dm^8g zy}D|Rdc-u=H*7j*w~$IS3baE8M5WX|XiHA>=*^xTLthAIi9nUbP3(KyK%=peAyV(M zyS^|@YX^J&GLd^`cMkSczZ?EB%z!z!a6#_Y&(lBD32`=R<7_FXDH4@U)qHe--xg6* zU)agD>wWE@uDiC1zTj22q;k_@>#eZdZb#mbMB5IOqz-lsALtca@d{w*+7dt&CA~Eq zt;*k9*yF+bcGdxjZ^m>=R*RY4<9G+W(JNR^=>htucqO~iJHV2+3|o3BfbH9bjug=7 zFW76RQ3s^a2SDl^9zZ9bX@Wel-R9uYlS3>0d1h}8A^6rKkYVc}eevD*s?pVlFgbex zv?=8*h(D0RLb@@gU-2K_d)w3oB>?O~{Yh9z4Y(hq0Fn1_9)k0q#cZU0oI~rosnZwe zn6Vohq^aJ;E6s$-$N72bW4!_{f9pk{5)TIotff*oA?oOu){|cBPe+xl@VUYxi<}si zrm4Bk2pDwxY80N1MZtE+ZB@Xjc*ZLNAy)ri1vXS?UDDSt~KJ#9UQ?eB}S~qP7 z2bt7ZxK)-E&sFd`V_5;*|4EXjM=T`rdE`CJSWW1v&|&?5T3){k`U^GNiU6#Sh4m2` zP1h^ILB=pWBbHQFHP-=-ajPc3tR!N^2_~H#`IH;q= zqk>z-GJpG!Al?XNWms{4G$YDBHb=|(2;<)eMxq@1Z_mrIe}0eGf&UhiP_2kt(7KM; zY}nM-V1VP+7f(_5-Nd7eQ#d1ND*G=8PssJq8J=dByj)57^@G6CN=}7qd6@XM04Y*; zVGdFf*k$h$3aKt39>vmsbiR+q$>&%OFJ}wv8udJ>s ze+EiGk;hH%%|`7+XgL;kWM7;HO~iloF15NKZqART1WMY#09?DE^@oVu1uk<(0ZwS*P0+%9Sqj z;7x1fp0isga>Y*}!b&fVgcHx@r=O6n2?JB?Q}U)Fk$*AN7Z|1fE^AFD2aYAvm~OEi zg#Ul`wg$>ZReW=%#gl@iUXU!OG<75qQ1+xE1^2Y9l5;@MA}8rUM(eo%-A^{TQ@~Ij zr*;dK$$mI4Ri)_VCcXxW9y?q8l7e^Je|6PX73}4*7Xd_SCM0ohi=%$x)2RK-c{aq? zk`lO?7AyTR_^xWtk@Mu>tE^k0R2;RZ9?;`iwW+AsbFbqnNXWb@Z)d@t9Ax8^dV>EP z^KTeE0vz8TPA0lVrSzl@ZZEb#>?V>pas`9ilDgY`1s2WToyoG`TOXVLdHl#awnbnz zR~7^jsnu9zB7ORYkFo*KLy2f=KfD3X!NE9TO(xjG1Wp;y`%GY*xczF$wp1D}BHRxx z<4cEj+IBO%{SI^=RTVyJ0+6>JFhms+)yY)ybHW)vviJ9T9xAOnfxl&N5ij&n7~*M&TaOoR4vRi!Vx+D zZ+WmDk^?@LGwkSTcpgtooj4?14C?yKw@i+tzN&%8EvQ?3;e?Xr?!b1K&fkw{0LWFc zXA~S(PQ%$c*)Xe*mYJEy5ukjNFKs67wKW=NdLxr8IyuOfaI%Zj<|CsgYxV3{9rWi+ z>TCnQLFxhY`8Jj4kjT9XR;9uDCqTM=mRp`#ERg(R^bTOVaWLxdSd*>UtH%C)q zUFsi5f~OPpzQnHYywOoQAMDb9ogA5YQ}7s=^sJ zuL9~zxeb!V151gifj0jdzlB42m5B;r?}@(+GPi}?RAQKM`-j{ygVz*VjH-mOgSPAz zsAbx0@J-`N)&i(Q-L)1Y!ogrhrwWM3K{y^ht%tQHXFVNgX;oz-d$>XRLKk9_<%@t2 z*MncvqK?3q_zyvUV0a($9SH>P3-gu>US;cY0`d~6i+yICrKu3}?Ot2>6dme!-_Cub z*IaV`mAktJLkTmvGIKqJ zfG=|#BY0w4_;votTuvN6%OiF@9AQ&Y zg({5nRH$wRu%GnA?ug67jyg*3YSqBlxjS5^9XOcI-tJzL^Ij-c=hC-(y= zgQwrJY++71&%Q(Ad`(=imjv%fWhw1Fu%httIP8R8GpfY7yar;lW8=WlFJMagDN5~3 z0P_bPXmbMyb`T3w?gAPp>i%*=wqG^7vN4cguN1oSmaOBRm%gx z85jl8(r?emHF?RDq(*wZPxaeW$HwY}2Q1s1;ro5f92Va;9shQ04dAHg?ldkXDGFUy^6R#^!!Zc6P%wu(YPB#eNGBErMO1T^^>@D17rn zcyEi+Fv=G>IvMbc{0V$XsN%fkzmb5Fd`g*m)+G*@N-`Pg`Vl<$g;Ep};hUN<2rQM5 z)U0Sa-Mr`AJBWk9)@-q*$0O{NLHVh~s0NgTb=EB-hO`V}N?n>Yr^AwDc=FGn1?*tP z&!~7tYk{9w4!m-;Z-Ad#wZG&FMjOm^zTG3oJ2_NX*XBDF0%b6KgqBjO2%c$2@_6G7 zj*tnj0pnN;`hQLfj~Hq}O+DvQO;~6ES=wtTGyXnF1-nUlIWwDy8O`v{Ae~oa3e?Il z&#a1P`aCtT&jntm-)#4RtWm7*{|a+{Z#{g_bR3oLfFSqgRTtQI zjKnup3~f6~ZBq2@T3E;B#83^dv48e=+~La6;|*%$4(rc9Noy`VJ5eWkM4V1|%3fZ9 zgry&pdlm^~;J~lNLKhNc*w@r7{Yy0uV|-+| zzN?Lh&4So|aJ3bIifA%&%jsI?0^dxNOtB6^K>tTH1F|+x_HNooZ()`~PJLF)8bUd( zOKgd|j3ULQyApx=mF?rN2YU)PyVe9d|0q6L42XUf#b9xoj$qK#&2f#}NgTB_e{_n5 zm370yxQoidT4QmFb+AW(h>Gc5!Wt-~C9HYZp%SY7p3IFQhwSeT#{310z>))K06ovO z%!yX0a9QTRs_v`q7iZ<$ZLQ2g=7GdFVrK7ktyFCl_)(q)ew)tI1pEytFJJHFRx41O zRAW(tF47LA4l+rcxy(x>OHqL;zwe%wpOeh(VuR$Dreo*$lt|(!8v9q#Kb_Wj(_*gN zktOipN$bJrJ#_`^QnyAT38G^ z8$N@Jx2w^$h%w(RlMOavv(qBXTKV)m$PK%32e>I`HK2b~cLl7-@ z`*vp^=r25%C+OkMb|9|rRz4d#jueYh*U$?$5+vr!{Z)%^zzfarN zhwbeP_Ra~q6Mepfm)qSA{k;zU-nu{G>s5Weh<~?azi)4o>h%+!S4n`eGXOm#B2S2ryb88x z`;4$c#gT603l8!4A@Rnq?>`3G9zyeFQbUs?a$d(n{&N%&?Yy>(K}Omq!w}GThpE5p zHwP8aeO;f9f_Ov@@daA~Jz^)H(Cw-dYBl(K`zJaJ>)(h<1%#GFSd9W}1*D=311r$M z>f@m}lmihKbo@)T2fpuLI8%SB*u%vIC`iF!i- z;bgKGyg4oZhXtPdZFyBKle9gS=L}lf;RVz$?ct`)WNyG{N+_~-e>BuY32!66{B^GJvU9DVZDfJ6fkgb+`0I-Hko2^CT%Qi3TQLhW z%25qIHPER?h+7)J9I(w}m@b8yZfo@ih(6M+OwoJ4P>L0^f@nSKnT0gay z&M31okD8aU1ao^81bE}RvqDSgKj>!{-`qMbyPajgb_Xq2dP(bAW#l4s{oe`YXxZA} z&%OmFVTSgFHfXl$HJ-skh7m-2xEcIZt+<>-3$8{{u|NQ}HG(9xDy!wJ4P_rcE5D>Ff||z%on8bbhFKm5M#*V}k<)wpjK#42e=ko2l7j zyz)Z+l>)}DM)In1$X}S^*PCZ@oi*E_%z=U}1Qc>-Q|b+&(xZViC6d8~J^Siurwv34 zzDd5fKm@|BV)WRvSTuBmUnEP}0PsP)d|Z*b3-w&{W|V*f1sacEors1yzq%er0M2Sf zJxUJQbFDCfAytx^aIpY#dGHpw?X=OBZWYc#67O_j$(lgm&aT5o#8$>l@DE-HGs^lf zm2k_OlQ07I#+-cS?DEv;J{OixXV=etE+aIL0^Vy^seOGy^p8Zr1)U`nDUf!QlE{Qu z$Jz?Z78IVoLvAlL{r?K)soE`fgsmA-xrQ-?uB6WCC38l+Jv@TyYlA9Lmydsf3wIrZ zzwA|U?sXLBAov33DcUt9mOyZI#MXC>py48P9PInSFM4dvAnB^>^y5>jwAh^rmA2L`Q?3&N&{XnE<8~ew(k1@+^?OgIwpq1^`M)^As;c1yF({ z*Rqn*m)V{;QZPLkfX>E?v!o#{e9omulP80L#u1}eh1b|aPFm zTG}Mv0y#lLQPHool|z(SJPvD=Ite_9tdc464-ltWg3RT4y_FJof)(SmK!F^&Cqg^5 zdeb_P!QeZExc5p^Qn@I#wXQIoDiorSMoB~A5@jzib~0M8#nl!sX_T1eZfjY6aEhdK z>TYY~jh-i%n}G$^r8~`lP zkEX$mF@;r=E;toBy_!IyDu-6!<7t-5hoi&|e~f|>BF+{tprPA71Cs8CqANFdrrVi~ zM=su4N|#TiRe3Zw-G4YKYQ{rv2ScpMVpf)Wy=WI=*zqyBG% zuPgk*!-_Dtk0|*Z$!0iykt@e?RJ`89#S^)wU}gKO`p&bAdK{$owFC+~SO5ls3o(L$ z(v?1eMkfm8acG0(-U@Hu6F<#RDO%=?6hLu@UP=;|SBr_w@JP;=Y zkU?{XbFqs$i0=6m?3K9Bf*uE{qsRi#e}ukZI|@JygL!s5POAlb_=)@&<>^XpyWg|< zaPJu`?KQ-s8xrBox+Mu#=G+g19zj+09GYwIWuBS^=?^A8X_|HDHTI#)!#A=|+o=^( zN50;yVD#3dv5o2Ezv8!UA3RyvPmCNCP5q}sHP=1@>wa=;GCrp-CJF-&t~|#r3xMca3fPSi^brW%}II#@@$;o zOw5zxXhFa73dr8ZDDkP8<#B)IesxEU5eCu6fl6dEc3RUv zhE!u(rUdALw!}83z}>c3_4E#JmGVcJ60+^HYD&`KHR<@X)~WOv78~HH+cyS8@k3#s z+dVb&B~`>PPlx&x%@Wobdps4J)fV0!xgl4d z)mm?V7qUm*O8-xJ%gY=2z?7~L@di#qMLxW(;KIKD)y z1&Z?)76dylbsS#`hue*$=WJLcr`@C;3E|#JnX~@+bn9;rFKhfvFUmq9-e|-Q6*f zcRYgPaBn7ZZAKVaZdxWg0%Zcg&46B9tdsR=ceApY+IGc&_g{?)Y6t^jPvo!lsGm#S zjKS^@IIAViEPoKxsG6#DOwke54ItRy(UCI3EbVac4NAd|+5=+cqj@DDAnCSuoAe!| zra8;PR-A1$s?Gm?ah_Wwl(u_PUsY55rjnK6KFH14cuk$J{}obyu;V`+#OY@Gk^dL! z_JP%jzT`+>O{^qv6verXrnFwqJj2u3R$eaNfV-LV@W*p(A?khOB(d=;en?k=!!S|K z4qjxfuQ-nPPjx8<<+5t`yZzh=#vyLsPRdd0tMuP_@SgYue_0cdQN|*PeM-YEryzwc z_1N%84Tjyu;c})@l8oBl>cZRGcBPOk^n;UBlAz3MrE**u<-0skIocSH7M17?E`sBh zX4sS9X*Z`nNW{3PB_6cz%LA()sei%9z`}KVdjX6#h+7b0#nY1p*8U!iVDfGly62LA z@jtCywn2jgKBWTt|`;mB`l7^k2Q=MwH)5gMJb-TlpQ4)Nrqz1 zKM)2083=334vU)yg31R0+h2Dy&Gp?pDqB+tUI4EEGMVs~Ysv%PT+QDZy{Hkya>0y8 z-*l1s;k+Xw^uzL>6kxjKa$h(SBQy~Qh9a|!%^`mL-IRpY{sCy-p3Wy~rWGm2`nQxn zpk=uJNX7-4bEfcCUWm3s)_*tjW~aKnxp*U0(*^U?7GzXF-&qG?Uc;#ANzUz450Y6@ z1G$$eZ=@)g@mil?9Cq;)x&oi8a(f63N;b_3?-Al;Cuyy00}>^gCBc@Q>Y))`9{(5d43?`{yNJ|wF%jP zJnD-wEj4*KfSndCZsn~!&Bq6>sVJDQq>bbYruf?LcK8w6B3Ko;3A`teNDexv7olm5|pP#rN@;Z_rK)O!-|InKG+Iro5i@ zXB6=>R#HZCV~CQv&nNqaL^_PP!LXSVnaqkaL289lrmY4BZelUo(?wkwog{(-1zMCu zt~paFG4)Hqf{*ft{ic;bcB^P1>-BRH$|qjT>7oBxZVSh&&&@Nm0Fpe%xR-NGIPo4_ z0dfW~|06#jhC4U2wQd02%w3LS8kB@)uk=!|WG$rs5G|FRpuYAsd4UY5tsYpQh^MBdg=-lcynT6+DM zK_FX3J9$z$CghL@ymHsJf6H_db3y*JJ~z+O(Q-4C0i$6W_JEtwJ?jnUwKO^<8%1HJ zeVX=vUr$c2ahQfCs3sCB?jPOM;v~pL;#fN&UuFPE-#^-yzuu^g!jU>vDC)sZUXADy zyMIF%G(~ZH4lD;~5)cK37j2$!8e$b=RdTU|UGE)Q-u%~|T`$2m9RcwH4D=)*(2zxl z>92kqtiIdRakO}dnarx<2-u8korabTnfHwVgO*6JZcrA9{o>T;!z$9ah=C9o7zlYp zX@=nSAI}i>a~CUb&XRfnNE3}P;u6lcX10i7084qo-8zxcLrB@Rms9bfAmB&V98a7} zH@J=tFTs)bwq>l_^=iw?X|pI1X{zs$H?U{6c_5vvE07QD-H>L))uFpiH8j4L9;!It zW-TnbzQ6mN)hjK-Zt8<}H*Yn_uw_i8iu+5LU;h+$P1Pi6ogr;|0wTDsN+17yCadaR z_H+vF#Eqbr@!RzUUcrF_roeyz1+1JY3uPpo#@6iT4VY>w4x#=L`&4wOU+z~``b&V( zKG<{a1L#tTxoqKj9 z;c!uHfyrYzkW8@X!wEt!jy1|vkrKY^T*F+$qSP2IOTXO%gx626fb=X6*(3MB@WBfm zml;8w&e@FbP?WcgEl2Yn&iT-C3QNl|!kGpPSL-Yes^$JL9JQ~C7GJX#59T3)kLuq8 z_?>4wpO>Lxb2z{YRJq(yupOgoA6?TON{E&*0>LU$$LHh1$oG+$rkgN$lGk(Kw9~`{`d0xm6jL{xW9Hlc}zV>O2Gq13*b|;W9B>VFD-PM znMrN;Tm9k68tH;G8)_0j3&4Lg>PWX!acm%Uh!O(zTdL(*5LSdAL3FH}zwBcHm4kSp z;Me`|oqLS&Vmsjoq_$U+3eeC)C{vQR=zdP4KI)jhYh;V_1H?RT1!=lMZ9NI7jYi-o z>{h?|-nBycIoC)n1zR6za@ESrJUY0>$^N9H0Bo(`Y3`Z(3R3HSb%uv}M1mA3dTyh* z0{>U)Q3)5^#IXzVf>iE=7#z3x`PJRLK4P~;?6CeFNAw|RUP#3rt8Y*_w)O=r*zpXh zUaQ})OH}%EpXvH}{AwWoAy#Wt7doLB1UB+QdLfpLk6ObqRXdps{6`uGW=%WyFu~tj zz5e1nAMiGInHQq);zazxU6LpLelCaulzY3%lPFevzWFuRTgNi_ek1*cbGt=3-%!2C zP$vu1d_|a|kLqJ(hSn^sQMi;kHGmD+Q=dpPfIXY`PE{PXUi7d_f_KLOQrhZ zX8P>#lR7RHhg(@}T-&>e2=z}*tn~D<{-B8e8ms$=bB;x$br0H-8 z%;mkR^!SyewVyRf;x{*Bq|9#}|7$43-WJH3)QT~{S=JqM#WC()7>)2WBx9;9za<>k zv?4>Fwfjh3Xm`3Uo88qS5Y8yDpfC}yyj(|IRh^C@D1$IioE&Xh?9cTfcak|R8)XdkNamc3(o%aM zoz0@Yhid(@!OjL1c1%nfr;tdqIut+o%cACnP=cJ_SB+jQEogqEJDTSuucV`jcwO5 zBL)Tky{3`)+Rnx#RPuDSN(pb0THa zeBnvW8Wb_zq$p0w*Qc8>u4&2zO^Loq;=Ew3#D4h%m}R3sp7dq2&uU|E7!%?GCKyr2 zK(_{8C!2zI6CuR)^AcVJ-DQIs#3cMMren~)r!PoL;E(A+#A4UayFZ;7( zEMtg9`WIH|u1WG;GA%#9;azr@XY39vj_1r}EqnQLl1yGmsi2n{Y>(t%x+UDzgHLLB zh%b$LllRj81N=;ZVydrAj#lY6z}Oh4v%Z0_>-0iIn+})u^6M#-jBe-K}PQhbg*=f zepLqgqw=r(XT{NHa4JTgcN<48){aYQMyc0@u-zezc3DHf;lVc|U0}#ZQsUnDyP*!b zj1R}HR;`D~KOt+(Bzr5KRP}ksdNLN9QO2ty*cymAR>hqzLR#Ls`+;f2%>p+f1auT#`uH z6ZSO(#hQLJrG0#T*dH-u1?Rab^JMU8atmtH-RU;=J@ z4*3CQZOWV7Uwlm&!A}TV^!P7dn9`4%6t^w+MEnnM3i2_EUzJ#hE=h zp#wY#^hguVZBL6N%r24ry?2Q6j~5esJ{48ER~W|ApCChr>2@)Kb3G$s&a} z&1u!lH>!0Z&maXLR!`d3?r-PTE2=5k3jJ9uo5E~~9hV}gm7a(ZSM)Xf{fDyKZ421Y ze`P}e;m4N!JS)fKn>O#R##kg08izX}Qs7gFtVPTG+B`(j3-dMEmB@RjrH0|s82K7) zBubj<`n7?2`XFkVAw4wlhm*rDmx;}$1X?JZt(x|!d^hc-Rfvy8ulm*XYqri*u6JO4 zXK>O?P5eiUnuf?+6+5CimH2RdT7IMX;hZ`=DP4L>;~>XY0c*IC(TlH{?hBmWoA=cf zTwMCO55h4?=L`?0>6_EPm-?GK6glE%keoPqOq*rgz60`?Y87=_a(4y2AM!pk15F3m zdOPM`pCdbrq{(>{zA$by)<(YpiWsv~FGi9z@94YdP>Rs+R>_`O&;^v#UAG<4<&PDs zFM#msN-0FnWS;I~%RbKATCSq1r7wAdeMD5~b@BTvCJE8!>cs|%rKNYOY)ih$R$U80 zUL(A2eSC2zkp<~)X1I;3R8wr4uf12F%bhi+qV!*eguKcxXJQK+ZLS)!w;4&WP^PXF zGF=0jMbS{0M|L=~O}OPsE^M!MjSqr&pYL@=NQN808OSQrN?nZcABn5`TgVuQkhp0? z&c+p#z6H@(|A5$cX03ld;XcziO|gi<_PVx&t zJnvy8u>*bwh6BG0qO0Enx%ZhxnI1khWOt@F*|Mtn@#2Csq1CeZZ}jS5;bMuh1HV@p zWkiJk@W2Me4Kz=rSMiL(;;TefwXHlzE+KaM=%I_b`<-usRCx#ATY)(>Jal=U z0x-WB#}ozGTzt+3@sbVor9I)~WcPiYH|o zFu@X;vSx2g;`bDMfiDnMJ*OWuA@JKSHeq%OuqwcCq~MBG{|w(LSjA{aUFG;w+1J^y zNo^tI@0#jZp{68nVS!IK&pg}2nS`aan2T@ttN3ztY(j#kC;ah@jeo-0v49qzg@23~ zkqPwb+aOe6v53u{cIPe@QwJ8*BKtT>JSb#*wTIJAKZ-HkwG6r(O`<)L%9uc0>$>C$ z@dgpYyIvE|%Nr$kH)^e|zyU(PvZpilg}Ht$*6iJL<&E}_(ACV}3NuJz#45_YMQo1n zM;;-;OcR2kIVn10QcJzRWIwQt=8DYpGKH~ zt+##Yb-7?F4Wb1;Viv`=uFHWG%1rvfQh2$(Wkx{V)=uInB#w<*LxN~QMhMMi&XZ52 z`ca;1PaU>{O5WyxylDshNcm2$G}_;EE_WfG??}y^JHslp>JMnZY+f+H#v(9LBlx?j zT%|EV%@z+mFZh_p_|MJi5N)tTs3zICb18hQC{-WI-&o=o`ik5f)QI!67LJqJLvGJC zQ#(KW>-_G7QtyzN)~MThRYvyqrHi#VeXGKjOKKcjDITmc9t*9v&d^EtrY1zRq zy|FKldc2*5vT&EyK)j&M_hHYLvz?fuJ*SZ}>nZT$z4_pQQ&&8|Q9Zjd9l-_V_r?1A zN8i8pkBQy3WRW)(ToiEKSjN7`Y&1RkeQcM#V*%b<8oC{Hieeq(piAVaNdw9Gl6h3| zK>HJ9NTrDPi&JI=p53mo&W7U9t>J?+anF{9?4``}B$owv#>gOz z%N$ObE&ihPKW{C#aY>`-DB|Z`lZn-+g_0pvTu6MQE(MM0EO%=Bs8x3vz^;{Uu8@=! zk5pss4#Xlv)P_GDQk&;BIM)Hv%fe9UMazk zZyi26Aotuhekf^JSN`>_6WyrFL#QkO$z9}uSg7|RW~4B$36S=6ka(~ncAbKofUg!E zPfnsP4Dy3&-UgX;13?_1P}Z+56KIVt6IOQ;#@+zaE6=^0KobA8fG|rJASPtZ5<84e zHM_9uL`Tp`t5Vjtn37kIX0wo78EU2g^qMQeQ@^h$U?irj7F&4_7?6hi#Pz~G^Ku*Wj?ri|gR~RJzOR=@Z*3&D z3$yC*Wy9Lduos5yQ+1`b52CHN(peVRSF_z(t&jfngYN^;rrOCK4uQ8dYkF5LiNWh; zRZidsMRUX9bhi7Y+*V8D>1RhvtLw9p-}(@b^F+BtV*O_2nb>LtWB2+SPoj%U;x!Z~ zlswjw5N|s%(n0ROFIvn9pU+rYw;gT0sb2w&el*i5=y^L@6{>l zxH(0c)ENzh+oEf37#tH%71aUN7w1~18k`E7!WpJo>90yZ1w8?8+=%n}T)W*&7BYvy zEQoAH)zbJj{M?2l!q>N?&bD8o=6kr9%jj&~5DiSH0X@QUcxte9UIXEKFX=#^am7wv z!qQ-tf;Qo;msdyfyY}AhBXI7Vr<35R@S-oq)&RAPwp>?p{fBsz=Qftc`Msft#X9_9 zd%U|a>^$1$U*OPY#a){eXXEvXL2FsjALj-S0=T=ga_{)h4g>x}FAAI&>fVsa(1teW z2hsso9p|V8n?4AZv^Q(3ZLTY{?}aIz*5Yu1{jl`QxCkQ-DkN0c$8wf+IhW@{zjgXV z67fcEhnM;^L+pM_&g7Wc6tpjSCDrOc90=gt9~cj5oW41HUW-`WO62Y&fJsC@p+TuF zOtqP_AgKzJr8XwPpRxhJq-RDVFWIjrqEOHu6uK@J-(R~!XC#Mcy-erM{>48GCju3`8nf|phL#60mp+WRcYl)L3?B0&}E zp^wYhz0LI=KVD(f%OYr^+Xiycu=)vu{=zi7&nxr<7u^wG5PdSncRX1S4eYaOX*^|E zjQ1#=6;hITL1>wDwv`>A!QH*|NH&YRAh{Y?mFk5duCI}tk{Z(6w2o;q)Z+KymkBgo zS!+X88tj3Un*W$oDr3`~LB)-j(mR1Vsezn+TQ6v+xGKKbpgYB|6_Y`38+)ATjD}>i zJl^4bHUBH2IN3(aRnQL5AshI+K=(cW^H*Q71YQ`_MMvq zu|cBLNol`i;>PnR6yXpIsx->gAX0Z5uR&u@R{3)@br7QqLwx>dN8cE{<^cmLni(dC zFoDvk`RL?kt3m^45C_UbJddXth-*OwnU&!XY^9HAe>Wo7!s0#Gp_CDAHz(dLY>BO* zeHB)|$BeOX<|CG{H{5}wRv^b3OS#tm_J#PC!QL07CFB7N-@QW*z*0B-3b(;qmS=>6 zQ0pLYGR*%wuoiy-*vkvLJr=3$J^E36`tb7!3Z)INq+5)IZS+0MEcvaA&rkjo*vSRCebF{{WB515s6=jjioZKE_!`tsK3$Rr(AU*{PU zH8jKgBL<2ssaKcbkdqIvRkEuv_4Pv8kf;?`a*E8+y#^Puo=L2Yljg{&HnB@qv50`U zUZ5)r4R_xRR_b2XQgQ0b!cW=Dl4lvlN;f;&##GZh;};oVa|Oz7+KQ~_aZ}GWO{jAq z)QUBmb{>!fsUyGP=K6s>paz(=FS+(Wn*{-h&*XvrVUNC@!0oAMKG0xJeX6wOy~B1> z?mcXv#C^9YI$Wrjs6CiBm|MSk(>T`ip477HTV}6>q(lWB+XUwCVWuVnNeB#UrZ7Cf zXh)2os-CV0Etg!5lPFX2ZopflMYvr%ahRzX+`Wa)O_^?;)#%+%LZoP+RzkHIR*s^6 zUSW;B@Z-blMD}+l11|7f^T}}!7OCVnUY-CP+_RpxmK*)3`pdL+i&S*tblMN|&s2#R zmF^AAi;gC!<~00hBKc_4JjQ*R3&pS!hUbDOR6YOhW{9CO%gbturuUQ{RNXQ}V(v2; zT)2C=Y=&`z<2 zEFnoR0G?+sc8UBrB`SM(t;*4PHJb@F8q)lvs3ztu8=>oS`AE0Fb{1YM8< z8bO{&M&l4OAa;58u1w6v{5w#*Xh>-xJF5+&+s8W0f;o!CALa?o%t5Gw@c0w86o0Io zDlrGEgjDWzk~*1886Ti=%It#P0SxIhPwK3tq=(IBBUBV8(y-t_nd1R|t+N1H97ou= zBc@71^|b@IU@V>wea|Zkw12xQKAO~@aLr_$c;wiCgKEt(b2{bvrAj5%9<*wQ&iCWf zp)$eHuD$^a_2ZUzM$#Vie6>X0)|hZp`Gl`)S(hdk^H5aS!(Tm-b7b*#=S?3b%erXs_8?<`rr!!D2|OY&Z93JzC(|tyxP~!K zPH;ilxrk>DRnv(D0n==|^W_W%Xo^2TtJbGiy)Kb+)ZapLCHfAU3gIMAEocdrr{?pM z;nH;X$t|l}5{%&iLpPi5-?lM80Ss6z;!qX`%mr%9<{hU-BM1y0o}NGnt5q9hpnc6# zcMro?nKwRbo1t=Z|DaE7WA(Gdp_o-`wNtQl=y$`bl*kB%EAiPr8Rjn}N+i%2D`spC zj2(02!AI{I^O#%r3?2!trY2Od@cZB1i9I|t5KQ)>M9|VqF6pE};4B1*3N_W#@Y3@| zwOIF&SFoD~e<*0&x>}8dg0nkYgrY+s?4yGp34a+%HG-+f4n0+kvZ7euV2Bu*1i6Xg zQ%zd1;;!vwSgkCi`L91})IEXvU87lO8L_w&j-Jf=%QUGTrCkVOJY>5XDVp)JgAyy? zEpfoyx-6>1vtN3Ta<69eCpAxJ3;zvlA1}^*DK9d$<;`(kYlUdLxR#u3X|}bW7Sek# zh4%!>$F6Tkqv*DPz9%}k^4<%FfMTqJhnck^p4I_Bw>kjkq6+p`LjDEeHuJTIOSLq% zlUgW?20K%Tn11r5_V`)KE`sX zI%qW_Ff^(~%C+$@A=0jYTh%+=IzV_QO*g}nN*Yk1CQ{5WRYQ3g{wzg9maS?3tQ_1` z<2RbD3XPRegtIBzx)Zu&cs2g46YBQFR}psOh9uei?ik5Iw)*I@yI5v>SIs<{a&E}{ zq2=AIEg^_fh&ZbJDNcSl7bO2)5ztE@tBY&ffC9TWg#FbW#wjk}9FW;8)8cEhgtxFX zoY1vp)FMXNe%ze|UaE^l$%tZ$1CQ5IZt@*ksN8Od-c-j|eAQsRk*vs6vOxsLydHHI>Znh&O^@pQYU(j#Iy96n6RJdDsV@71!dYwcKJ9fY661}L6p#sqoE zdncg32eeu3%N!xii~4K5<&*_gr}u-~ojI9#1iZZdO8XO@ZI1Z+_E!BwvJHIv`Uar- zO-U|K#!1|?C2%s;|EvJrD*%pH5ejqZ^fg%dE*OPqG~IG7%d#mTP3%xt$@RBZV&mn6w#9oU7dKyx{+&frVmK)^kNpKgppT7WPx=`7@c=B3;OUqnb$rULbw9@h zCR=F1ZUi*842@T7Mk)?&FS0<^ttB4h3)!ex)`$z43w!t3#pTy~YGs6y(a(S5Jx{esA5?sAX3{c6f!98zzM)lZ){4giCa&_&#w_)j8p7>5?qgs$Mw&89!Y@ zB7ZLmxYaIAe={6}u-Kf;_R)Q++L80D?ZSHQ?`1%Y15&ewhDC3S^}d~j3U?ZgteVR_ zoa?U#L_bO!=z1D49^mE_X^Ov+^aC>+2wThpX0POoFiwt_M84^jAG9;Eb@q;yD@Qnt zVz)~V|&_vl6nsj-9>UGHA+F-1`^1zv{PZ1{gIcn z-)ZQ*B6jm8gck`AdO)|Y+zQY_sF|x}EIoh%`noNWvjfGy$evfK`vgJ4wOk6j<1A8EdGFtC6QHO zH0vQb8MtuO-NH#U-FbA?Fg}EpJd4+PXi#juUJW^0Eln?u=UQ(4YPTO#XU7FO9F(l7 zxgl78k;1C19j{2{b~|tN;fEFsyG79XVJijL(dkk^+4Opis|%)65q&zklYozSt-|fc ztk;&rw<+39ix$ZnFz{4tx0g~gwij8Oxqi|FLX@Tv{A3XvDJ=l{!QpXfnm%S^MP4N- z#LIQ1=rny;O>p(*kNOY08jy1iF~zsHuUH3Ru^ns>ByJ$T7C$V3>;q;^FFKc#L|eRF zX{5OI9^r;;)#<2K$2lV47@X&X@{!GLz75j2Wcu?vozNOmNR%v)?hTAitr7~io|}ZT zwMG{!O`bjIp8*y4xTt*7U9(lC1}l<7ypb=HlOhozL<3a3m*rn?=-BW&ZQqIPo%4f{ zsNt4=M24Xbbk0v@`quA~tY;pq@`se-fSHh9b~;Za`)j!qpM(Rjw<R>&ciT@$S?WNpaGcO3%uO<4lTU+sID%@PV)?$l z;S0V!F`-?>k8Z`h+yKhP^o04<!v3C|nC9l19JEFwBA#YR_mA@H`Wq!z&)fYy zn=z#^#SCx%MytFfx&f3-zC5k4xZ~8A7g84XqB?%2O@{~pf1v#BU!k*M&V74AVm_g3 zoL0alj+Q+*J4nLO%_U5`K`9_z#mv zQ~5pd)tvHKy%X%^gJd{u(SS>OGyKOuFP*NhqgWfzw^XJ3o%rphHvouvc4?)D5$HE| z;{{6p#BtN(?Ui(~gp8NH+qN-??k)%62kr{oSyR9NMw$l-xjl#T^a)f0suGna`KPwT zb8t7_j`Bg%U_sp2zQ>BzpONr|r_rwr@gq=e6oEswng^r@Ty=$LVYg1LJu;nPOg3&9 z#9BN-R&ArEJJk7V2y~CbgjZ-9!$&NE5-!k#S854((#iLa7Ir*6feGOD?p28o#N;3L z-t}uKLolk%L2rLWAyBzmgzO;B0qWam$IWj&m?hO{x@#9dU}OT9DPWfZtWf^_sC4Cp zMw&Ud+JRC9sOT#;@I?d$E^l)k)$5QVd^$~ zT_46C%7PCxF-dD2Al(Fm$e=OsXMDWeCNg}!D3b!CUvNaY^$4tQ^Z&qZU5x}wI!Xn4 z3VN3c8f%Dp-^6;PVd%l!GSYSRYzPbAu=M>u&VM5)!>qfbe|)?G!`b(aGq;8Ft~;w% zfAqn8l)BXFsI_GEp`dPt>}Yx%)S@6z4pJ*g4iKXxjH;K&-LewzF&<7=0Zm7p=4ihp z%R=rN<{a(1&~%Nxjwq1_4ZW$Hq#$K7b*1~-nRJ~(&8MWHS$|AE=^L@8dHNny1%qXS ze>vhO=n4GM6e<5I?hXeP8XUneEYTs0-7wwB$)A{;=Q#U=i#xoJ6q zJ#u^I&WMVM!@>W8X5quEAcM7dVJ`=Plc0{n*X#gXzI3I>UHrEB!|~t~xOjt^*c#>^ zGuZ<681&E|0mu@d4Qxt+qaJKv-0k^|4?q>|r$MwkIV|N&D#v6RGVIwaHbv?KWgbAu$01z3+>yZR%r*Dj zJ_B^>i!%$-8%YzdqlNPMcqTXpDU0JO*kfCqoIJFkA6X#y6<4mKh+zf(QQrQ*zn(Qd zoA-IF5wbxu21al&(j=l8h?@@N=rH*Z3EN4okE-rsB|H=(Vz0tjJ|x!vT-dt*$8<-X z>=BTCK`o?_$#hEeh85F?&6CY0{MBI`A9PV{`3mUZrER$Qn?ki0NP)+vs`2cmECt-r z^li$>>wIq8>te$K*Z`&xOrEHhXW7t@tcG?crLtT>(#$K0AwUuk;zo*xsv2TwIF9?M z@k46$me4X;lPo_z88m;oq>djG%K3MJNFPLo>nB&X*i#KvT+L?Y3mNdEpQnA;{Hv#W zH3ylwC-%SwedgB5xn8ZFw}nC8g*vU9e_TXYB!2DLp(`1loDev`Bp9x=-%;z4*58JI zn>&oJZNGt7QI1mxfJZ^8y>JM1sviJ1G>zWQTg5G%{%P2 zD^a(+m%#pa!f95uK65v+_-Y#Vt`db^E`Dro=ikB)#Z@&kfBhc{k*AT)rXP>tMr-@{ zwiLU(YX`8y24T?0rXuG58J9Lsx?6*Qk{r3#CWv&XqK&Yodrdd$^7LG~LuydRI9Do$ zRc3k4c}}uRu+N4`Cw3|dQC{oG+he=98*AMzq2mW3@CNK=wJ$2voyp4`wllwsWNBZn zApn(Kr#n%-ZhDndM@B~SDK+zDpK|Y%c=`%9u00QXsO0$j!0u_AzKSoRbC-X6{9tuR z_mxH|l}TaBP%BUJ~U403Q?rvR=&9QuYE~2{gY&3Q~sQQzI^(F-w!6? zpj5Y>a-oNFYO%_1q^!Bfx=9Bc787CHxZw2OQH$+6=Au8TRVNEn$JUhTlj3bs zZE_=xErJGK^O zDp8q(rJr2MHhz}r)70tM4=Taf#5$(`m4$sUrm!f`_k-~(gqqr4n;J~Q-+#&(z4NQt zKjwegDtwOt{l^z3hn_&JO#vAJcz_i^nMdEc_eg)r2{NEcS28<%?b`F+v=}%L_~hN{ zk&+t`(S@#A2+!M9KoyECjg%WDF05&O=1`yg`t5SmF#3RBkSl_UGtiO{n`T)N8*$IU z^B;kLOiVvzsB52IM-&la&hn~Q7)IJ@zyyggu5kDgiQ01ia+AHR(3>BuFHh zB8wDLewHdwpG}A5jMA>7RzMEJc-7Z?b>3v8b$Oh?+ZXgtyY zuyyU2+7&JSKBe@%wLun z8Op*VXJ;}q$7vpjqXu_Y9to@%Iq@J575YirXc~C;eVq#Y+GM>Xb?7&vIcsk~ANZ47 z1LniJi|erU*R{+JD8ANtkBzA+fMQG`bWBSq@$ZJ%+7p^H)s^XlfYvLVY&A8edOaVxrjK1u>J^H?J#1 zo11H~^6sR|?>s%cHl7EM(OsPC{T4b7C3758Ax1XF+`83^7`Evpm>R3|s);(X0xAOp zZ=tuw{&?|R@A58nji4Ds%8>5_g_*<0W@*4PJMcLmiZYw%SM-6!u{4n#Y03BQS1WD9nBR3crf zQ%C%q7>D_kp%B&;;Y^k7Hy&Ls(g|nxYdilkGH3b4#p8GLaU{XYJD&-E4$3p6} z?rW125~_6ICVdd zky2%_X@#64G%%{mR2FZwEP=sUtw<0?RsN+K4!MnPPF2cztU@F;h-OkL*S2$*2Dt|0 z^Ee$K^k3l#t_%L`KBwhGF9q;2K;;D0H+>_h>Jr#G~{nA8#sirYFt34aL zft*pK|LT2_w7SWLYowY!lEi+SPK8^GD{L|zBV=7U5TN`*$)5#TDl;D@6xFTSiR-Xt z9Q^?sCUIpA#Km*{y6!-pn*k~Py*BmnSRiTSbd^d91q!{OV)tJu!4;>Z#sa2oa? zSYiH1MXRakSNp-ZlmaJonfsJBJSLA}#oy~AIH2NjFd|9Lo`Shu%$NxPXq1`Ma}7qw z7h4+D=iWVKJcv#H9F)&TRRUyJ_%0|JSGK>dDVK}cR>W}ocP{2iBu5n;4e-&O}n(BRH=c2GT)ashK*tO9*q`oO8{FB zo;(lv{PtMmOGFH6bp*Q3Y}f`W*HL-@RYQ_HB!C zm4y0%J~SKY=kOIjD9m*CMx-cg*}BI{4u{gK4}m~8* zXH9PWa8Sk=u%5g-~8qf=#|V0hqc!AP+!|T`>{|eEl!RR)q2YQGHLt; zz!vyaMi@z4uHZ>#L=R*-@#f$ixBvC`dmn<0y#3^36>m=99sv1ElnIJHow#xP=$(}; zBj>l^u{WB1^a456e>JmCHi!tqN0vLQi1ke>6UB5t5k{0hQ|3j z&OMU0&!lOSeYgZkFUaNkC*i+hBQ0*qMgcYH55<>LSP#$6|HVLZ64K}gMHwDkB>I!F z8r>5rnlq-R0!m+erXygT{JIGH_fc8C+74nRCW$(9Jq5b9A&kQ63SVT*ia(rg?v&W^ zSH{x3EIqeyMBEsK9wJ7+LCfAh?UdM_)hiey7SzL@{MW|q*a0SR&cJWK$ZmH~*)V^@ z`Wr1F_gISeNA$WMuiwgHdXeO8)FU-ge&9YNeTQJ4O)M4l%>Goq#~j)GC0P~px(np+@>>%T_~$-1nOom;eTkHdPw;K@Zy+yQDh*7lpJ2EMT=-Cz$P(&tm%u~E6`ve9?@AA&qC;K?aem9Q_f!s7W&0;C{Rln4HIwy;DcWKf)#*^#T& z!TkRzGYF=z9`X4&|40c(>Jq5P;NpRjyKlzpJ@+i_`ExrO)Eg;lwy7G*S==yJuBoX5 zhsWWZUvaF4&%>T93J$d#?yeAg^Jvdu>ASW^-#-B6WN1xC!3c}XnnJT$`c>n%AAfiL z!$XA3-h25GO)alN@6e74O#FcSoA{gFqX!jZ5Lu+WeXd}Fy88{C0?()`&+Kd8NMnO) zzC9sNaPE`QV*(He;58fsroEQ_BQ-Ey(gFd3l1tHsf4yckg!-8xk=)%fzEV;-RB{CI z57?#6tsZ*RJpsQ>lX z@J4-k>nF~PQG7FlKVG{y6%Wk7h!uUDp@>Vbnt?d%PV|h_jOCd@%X(+jaec%eB0M*i zn&PEYueX%kYWve(rN9+je(bUz(yfjv7s|iCeogYXY{S#3v(Ai=*3@>cZMqtQ=q^0Y zi69=&us|+Z%dpA@A?`H(6Ed}N7tnAtsTBK%QvY~71Jx?)(Lz9X!_B_ia@xNhvB~`3 zFqI?z0u~P-X|pS`b*C3)0K%_M!v%&^Tvt)dY`k&TcI((imrLQ0WH_j7-7$5~3j5KsSQ#>iO7{Z%6lcv!cV zSyljM0KguL$#b2tW2OUS2drc7HOU{;G{KAlMu)|tvg_|&C)BiNW806!iRu^@XM?3@))3*aIOf&((_Jy)Lo$heK_HX=PF zINUS!jQ#@0VTv<%*A3-BXl7wx_*TxR&)U>EBYnk_<9)O!IERg+)jdA-6#RqBfn@ta^_YTdx5b<_?b;dGGnCAkx1hSkiAVm&fsa70H zO;cGcYiSgu2hj{%v)8DwS2?wltXu@NFZ$LY5UCg*;}v!nLG}r=7?j$vHxfO6VCRJC z*`j~sPH6-51Br-Qg0)CsOuZ;{PWwkX@0L({^F?!YL62J$3bxs`^ih z+zToth|apN7;yOp^x;$;-}b3eC$(ah|nf()5{Wa7w=2SoY<0D>acR%Ab{mt!?| zQ9f%;uX5wAzuW5@*>p!-4+l1a#^to!BOsBAOFvVa$Sp*t_%uf(Hl9EKpmZ_ZQSBAc z4!B}InWcy7sVr3!5i}n9B8%jfW_XEJX@+f+-b z-SlGMYegN?M|w>jcvpx%YLh@~aeJLmKd0RyIO$pV_Lj+Pg!;F&oj1xTJtpJy-Sbc%9KmN^GjWgIE)Ij`-2Dz9 zjgYEk-`|V#z37BPH`C{ma^#^a(3&N4i)K-2xHt@_~8S zi@ocGm%sYEELL&9TCWIN*w}+f-PNbcInnpfC4vSr5&LC{9w#`Nk#nk(2D%`8X~`uO zg{4(Mim?fl!PG5U5^CpVOzQyjOKJ=$71nLh86(w?L8b+@^qRpZ`{20v9j zuCY0KiueLw#aID{sJYhIp)L2SN}x@{U<6Neor+fE)EN2VDWjsp0xs$H1G=tqi0poo zAM|b}cJpDX$o!(>J>HKKZAwk&i`H1ssWIIf_WKSP8ySpa*CjE(YOgPo2+?6(v5j+6 z{jOdtS1E08&rwa<(%TCVwDvSnGgS5m295wifUQ>mGOlGY0zrOhli9{F)aMv{Uwa|| z`{GTGRmXabcrrX=3!=`#m;8cV@Q`K*A|M}GMEj&{WpU9B$eh!r$LO;%__i}^w5_ei zJ4SxRY@c0G|H-S4;%VxxcoR~}XdFK5O=9n0fF5g z8K-Wy73vzmZ%T&1LiU(LgmPbW{lSrtY79?5R#rC@NcGtBmX7UJhf@X8gxrhi=;x}C zMrKC9b#X^^ypXBC^o74aoT>qwR^Y21)Q1t4rXT4+!eaLEqU?#+1zFO#THCM@jBGIb zrT%Wc;rqvbwZJ41)g-OE+&dfF@(=#&u$U*=jz%;cVJ`F5pyVp;+oR6RFKwr4_bGJ; zYq)tLIk4T( zT)U>IEq`%z6DZifOM;h^r~cg#1QEFqEycZA9FSRdsC8I#>(a?UF)quXm=#nfwU6T7 ziN=@8#~vR;48w9aSV54FrxfyaGOt6GW@Og*s}nRD0r#GFj#--i6%g7I$**5jZI6e@O(qWPzfXB@VQbr{^q}B+Z%!TQ>n2zIs+}n#d`_%M+)<<`kGKk;H<`H(^1*ub^K83RA?5I`Z9n< zLZNcUCh+lizVwH%O>SOlZx-MQ{1{J^yU4T0DImm zUhC1OTti%}Hw%f(s2Qr;%lc;1%`_5d@{T3%!gk>XfesruNPA|SOG!!jU}pz}1Gc>3 zc1z_6k0@uI-lysSy}CsT`Q zC1Vtml^#zcwkTGfmDhHGFpyG^{1o^81ZY|$>cT>`#-_e-Xn3@KoijLkI`U74%o>Kt z@fVAV$i&%m!zbDd-_ilRiAMhx+(WpSjDlYIKh)VlZThTC=Nzp8)7{oDUe7(%I| zrpv$t*LgP9LABo&dwYa+PTw|II7FyBU|=7=QDoU}7SF7$5q7|@Yo4Y(D2jwy7qxnhxxY8Y5IDGU6gFqccViC;~v zCBiW?!iw*Dt{K(EC$G4xZD!2J1Ol{Qul8twd{*=Dpk#-el@8QdV`Dv{ww^GU(_p!! z8KJ#-6np`4ri#0Cn7epj`N-~gwp`QQkmk#srdgUH+_ZK!{GiZE{Bpx-{&Oe1R%g(xwmToB)`puhd=AhB#; zu6y<#YSK8vV5W-(zSmz>h23Rld`gD*W{Md$iXkODw8immF({S0{F`rWePJq*B=!08 z&hMOEb$kk3oyBO=rZXrKbnB_M+x(E14B405eNnDPEN)jF-uXSUFP%gv9pnCg^2B}I zE~h5m@zz$zm#8UZ=iiyBYVa$>)|PmGfN7jXg=37NC5fyUktRG?qS?)t4l~^MEv9&t zhnm>^!3Rokl9*2D-I-UY+RL!Di}ezc?8|54EX8V3E^H|WQyK6{BFVz6=&*u`{MQCE z7_JDBYs@C|fKRp+L-hW!se1W)hbf+jPUI3vzw$96$t*Win`N#Rg+J_O#t?a(s%&lA zz+2nQRY~2RGm%b&xC`fP*A}u|-evEP@lZAqu{+Qm|8`|{4AJL~gHeErcOkV{gr0Pf zday#AIJzzzXY^W&27uS|pgZ4@M~Vi^biu^q&+3fb(CSCKjy*OLfQhQQ_g%CJT&L5e zE<6H>jw0`O7a@x*Y`jA*Uv%yWvPTKx&?1G!2Q6!8hDPF~UnJv7IQwp#iI#la^mq#{ zZm^M(Wvlke%+63*|L${@%#aNpg;wrpl}SvqNtfh5djUbb?%L{>HxA`~V|6C?f5uu3 zD}=VW^&}LMR<~EL&THX&Daf!8ix!@eFPm(COr~(u5qBB1mleKbrOnfQ|L4}tui0(t z94Sf3;2{T($%GLkD_5JfV|{f2=CXC&YwaX`NcYKXW^8wC#-$!5e)2cvpVy5)5ha?{ zfu_t(xo;JzHeF88fza>|#v#T_IZ_*A46~Q7Q%(&5Os69dk>|B6UQcPrxxH-~3jw^~B~Reo4xO3` zu+!?k@2tyP4`OsKI&~npn2ZM1%&NFk$3?+E_KTOWukpX{ubhSrW0X2Pj~WC0LWxB= zhi7k&n%{`54q4CewWj&!{8aWTkYvFZh37QWXB24V%8(Q721CH0H}I?g{YVZJa2yAl zflxy5vZ~p?5BpwxW*a~;95!6h6XjBwF=G%`g1C?RC`lOruPUqdpJvG8e0f7;Fn*kC z8)>UuFFBrczC}9WBm2IHO>lQvJ%TXE{{uNd#=l>Qb4Q56&9H42K}aa-(>x30OyBRM z)VEY*SW97rHjn6C>k3sWOQ8bd^ndm{IjWGuD^f~sRvJf3mREB2_*vTFq9tv8QhLg8JRg#CkUe>+vOb~B!5wu+!^H6 zE0~W8{ti@-lWN4o+ceGmD`5kbXRZzio$J*A&KeijRF|pN$vUdz6Xc}F%^xZn3#DJa zWJ;&wAFmezK<6K1Mdt&OV6#Weycb?C6=d)<%yBy#%H?*d5!Bds>9=E8Ch~TZye1!( z>bMH@{EC7}d!aIL&7Ox#irA_%;4M3K9YvKiA`yaw*0Qavufla2g`k^%?6Im-aZ(pV#(5>3$3^_Sf0kdQ@+yvhH^iM3JaQZjvgW$A z{9WK~d}=aKp@c!N;#N@XhmvB#zQ2eT`OM@=DvrX_82lC{R@4?;OzOZ2M!R4}O)|bd ze-w4Y62h|vVKQ8gz>FEzK9Ompti11(+U3HD!CLyabhLC2>Cw?OzTxw`H;9$|4WB7T zzTzTo=HO}te9IZ11T>UUtHws}99!jaA3_t_@HF6XZ6BM=jB(nJnjF8`j0xFDiBt4n zjT}ACxoSjdct#{n;*O0NR*OMb>O>_a{FzLBfBEKO%&9qgZZL;(pR2w;Uy$VIyvvY@ z89kjTmEe}Q5k^tMZ4|G81_c!@69n&4M3J1MpOXD-&;|W2B6!jP4JCXPuh^Dx1ZtmU zMv$5?L%Wv9Ek$4#;I|qiI3@(J(K7*Cpw*M!0unXmkc+PH4LiKHqkR0J$`#Q3rf0;uUw5jIuhTG&F4Lk4`Cd+!HYe?(^=K!W29%n1rT^ zYb|&(!4_k@fV}U1Ygix`cy{7Wy|tbLnlFWy5*!Dz#?$iVke!G($vL;{r;Dgv>mq5~ zDqdMZNJ(QSwhtqlOq)K92faGDrWRq{vLLV|mn3njvhU=7P%-==Z^|q~ZR=KhIUaAT zeS3j*xQy~5oxdd*7!Gi}9#t9uTzC>Ik5!5>xjtxn#z(!re$VpPK4GB%YL+1g0z#aS z-7zqsf+{k6S3-v9e{0V|Dw$m;I3fnWv_Pq3)dRVB@LjjZpoH)hBOQ@BQPe}_ zg=0U$={y{BT_q2%PoTu`QjC%YeW=^NfI`lK{S;@@+hXk=`ALcFL+Rztw=7U(Fg*}} zkj~w*o@otqx|$OVI_MzbpMPC3RJ&*%tu7DTH`6d|?Gxeg#h$m{iDzBaQKl}d_AqFG zctc@BBqOwHv`^Q8B$xS23;p*q*IdU;Cp2G)?gY1epiK!KEE^e=v z8n(Lv#r>KhjZ$2RPY|(PnZSiYD?(v5 zAw$;E$qJl|Q*X6hJIV5uU~Mi`4z%_JDhNd9sX1Mjfu-h5eE)LE7U!D9zB5OKNhBku z&I%jMHftHkx7wv6ak{xSFD|~CKZ={Dq;Hn(m+ujzf%!Vaq{Ro0yRodQUrU`}Vff{p zJiQlz3xw;?ho13^g&qeJ+!L;(NY#yaBVieddkxM6#WTo(w#7{m}RIdn1><1J6uj7yyk9vmfNdn&82$W zi1THO} z&Q3~%<)u*j4YQkUN4v3C{}Vpj#w3}LLtKv03b z6QNwsL}V#`q4q>XLu=IK=PPVZU(vT9AjGWoQaL;>PLka2$-);p92i$QWGIO$!fpuP+}GOWLn(XFeTH;dzFCW!iwT@ z`EgLCG<9geRP`zIV|s`NxW}WA(+nD z6*qugCYkg+NYKbpgSp1xcW%<}E4gMS%bXJoRZ;Y8Ox1Yu^=B+EWj)xfokf0CY7MQp zVkHjR@T7s|rk(Y~+9Z>g1)|&)xcFvUXyoKZp zu-L1~b#OvTH10KFZOsmp>TvED5=XwfKAk++E>t@6?{)@N&~qQno9XYVJ~$CgySVeD zm<8=QLDQ4mdYx<=wcnU}uAD8Ya8xN&;%738_Lr4%##P=218|V`TuMr-L28aN&YAaV z*!B$PS4t`ix?HnEOvcZ0is-3p?_x0kCK67iJTR)_Mulhi-%?-^ptwh-rhnb|RA4F| zxMqn}3mU5iv+*X;Ev3f-#%ni^;(|f7wn|89C~=d6a9UQe!lIXxM~AB>7$~&vbU;}Y z?{0hnNTmLU!!_J_yZMBCw%$J#ZLoWPomO+g3HjEamq()ic2m+`ZA}WTsryA-!jDpM z;BZ6yRxq5a&HpLJkrmx;oX~K?AA+P^>b!SpWR`$$2?Me{>$}{OBd0Ieu~|`dIh)Fo z=2&4rF1xyUOFoj#v9D6Z&h7kkJGix~&Yi*x>R?Cc%#Gz-4x(&h)AcxC+XA+)2>ECtt(R~V#>CY^&mfd9d!X${}q`e8~LZT!`R{Vi30gKH~4~b2Mc9AT3dC6BQ z_;pl>pz!{z@8?}I;Rr|m8b2B0=?a#%k|FJbsN|y4IP@KCKR$qi-*~Q_kM3b4hSvEj zWrj-uen>f}wyj7B4py*y3c$&i@0dX;{WE)EBMXMCB#2zha4 zyE?`@U0UC(B-zhPa&tP6ZSf>X3JjO2!s_t;RgsZ7i+@yAWb_6dhz1(9Ed%uy7Ipdy z+)g$aN&-63g2M#wwunEnJ^HXmIu=)U+!irl?}0(6{8Ys?6!m!ER%}TBeT>?#!`=Z* z|0qeMffO$;@l_XjM-pcZz06N-v9@YmLC34AJ~#h+W?9k@2=;F5yyG0P3sjvOK9^1F zTfeA+)Nr--Jxr^e_lLDd0^H&W!RW}fZJs*fxWiX?jx2?(q&J8@33I2jOJqj#0)0MD ze;}k=t#;a!oM&dS8eydFQIc-N`a)QuQ8=?LW$4k&okJkV4A@htXvsl{Sy}`UDtoGg z8R^}gVMML*Pp>&W{E!Qh6JbZ~C;w_L6mql71s|lP`C8g;(7wgzf=8YH7d;~x2;8l} z)Wo(ZJ7)FdWm?YT>;8 zdjr3d z2+68yD$;<6jThtM&0+`078YEE3>2j*7G6((Dx;ryzMgYr8bLCa5l=zq{@z?d$2Kuz zMLZwVI>vT28lZ0l(DSQ~T*=Bejmg(h=v^9pMIV8N~nCgCn^&xq( z79F!*<1y;6OpI;4ZpTI1H|a z4TgB0X1+wC)Fa;YLQkrYzbQ&@F{<>s4!p1?-14Fd-PF7(r544F0)>zpfKa7%GNPB9 z>mwO<>jb*z06wf6Ar}wKA~~$Xw;?mZKJEv+XobL{X->jD_rC*!icTokhOa4`!e3$V zMx9hg%;8T~2W!8x=?eWF*Nnk`1LUnl3>w~!eMVf)&n*vq6j1hbDbr9ByAmbgY``<< zN^(2X@FG8tP>+d^gjR(sMhKDH4c}Ur8&*$oR*plYf7e3Y-uk55kQn z1e<=Pl-okl_{2eF-pBzsCvH!pWTx=<;(Ip&@*XtgkKASq(@kk))@~_o7{RU(or2Fv z*ND`c$^-N#hVePOvD+#3+i$gMYkfYi+DLy}>bVS?yU`(le0JmM1e2!VnadukG;}d9 z@r5lFdtu46e0uxS{lG2W*b8v8kkR*tOI0+0gWNB!=3W zk36{1mkOeS74W5DPJt~e@Vh#0MYdP=bsax}IjZBC_1dj}$rtktNuC_c1vP;?d%L}I z$PRz>3@m9}p%Yvx*6hEsI=D23h1_$3ODVEM)>VJ=ca7B&kIH%-x4~%Ve-NTVte1|c z#E<9n&p&=o1T_X|jE_?48W1MR)sMh_s|G3f)pLg@-04pkjsikr(rDUvYy2%oH_xSDZ;WUH-twaxFXE!(}H5G$jU9K5-%__%G*-ydY^?m#y+RRR> zrj1qyMVgbgozP0Q>f+NEXBc19@isf*FV<+Z~!8@AmbRTAW0oz5rR^um5p?1(wu?g?Ut|l(0ecXaOLa#y*=zYJaw2*4o$PT?9oY z8ZkoJ{?w1X8&TD#HL|8SCNLBKmU3uf*RGyYsUC415^&neIwy;=Z6Bx)l@K! z`I=I|7hDnD)p`AAjphV58Vl?x^)Pi|oO)+DaQwo6&VhGqtjDD`bFOkI;M5eJW2J2C z!Idv45tR+Bw&Wdn#cVuAX7ysDbdpjLt?=>~#g-}kv_4kuR@9W;<^NM2y5r%(7pV_w7<5EcbCxmjS{rI~OBpGwaYlzc{z4%g=Vn}w z$_^ekMq>7Jwfn48rPXJwZayFEIfe02Y>Vi9uAY==Vq`Ij!3Q3u=wWiiEHh2N`JTzF zuorZx8o!+Z48(--HeDPJXs)}+O#;9GVf^uyJ{xT((eGZbwZwK9>uk_(I5|r0a-`}e zN#8oQZ^C3VNOD;wCgz)T`|V!8i$lPVXRi}lGvkrL%2~4vwOJpNhzkFSNd_x4^K1}8 zJ!d@@_@X;XTy<#tsNsJKtRl{dgHr#8g;GJ_j&Ts+>mV6)oAhW}wl^}vgSN-Filt%0 z*zC<5oOh3YnsYJk^|ruPG#adZDAp$)w8ZBPdsRnrre_a9TSENeQnx@Hg@U_E9Ilf@ zyFCKu4QMUjkz8|_&BMIe$N~6ve;8a}Dm!5qH}vRDeQ@kFSmf%~mm^OBLhrdbCtL08 z81&LlR^EY5`eFbkOF(sN##EF~Z-d1GFopeqIC8E%O4J=-7$*N?z*2;ZKt(H7i3Ioh z61!mnYAMs>BV2uFk6H6Y0JZgiUE@mKk`?H$`_-fs!taF(M)d$fQSB_7O)~jg%#QA_ zsvgVemow|QFD+AUyB#N2;yCwq(l>JBOaDU0AvNFYyLweH(as9QOC}?vG z6Tw~<%{+W`Cgded{}HfmLH-209+MIO1vdUJn%TX;8mfeMt5{&>1?eMQtm@>S=y^LY{A(^U4Av`;8Mk5V~p56n_QnJ zhztiA-mr=f~=A@;;kpV zoY%2>vyc@QH_lc%BHMNPeuYvtlOA6lz9I!Q^QLFcRe{w!eXf4!Sn5hiOu1fO%)*C2h7ZZ!tYz^0!Q1 z4`hWdUYT@xA+_w|_Y1qRn{mJa;zoNoe#>C}mSa2~U(yf{sUw`Dyxt0*8;DL@C`G*Q zHCGAC3c9y@){vXa?ydN?$PmO|s#o_{J1&en!RZ;IjprIK9*UfgBLU6ETQiqlX+U%e zfO5|cc_WC=8D!q!eou#Nqe8BRsUeR59c=!kSuIKr5r=*3%=pk{tCDlnqOW0k5rk1y z=``sWpXsMbGtbR5<_RE?<2$ljeBS-=(_x+O@hYtE1xN#(OR)bE{Ld6am{NU&>Os3K z!Zd+Jsrf=^V(^-2wBbrDvqX8|8jHUZnz~ab{WXNLi{$QvcZ*4 zA+l36(OoU5625aHJXeQ*r1mQTGm5r6bl`{i3|PU4#yuk(u(gQN@SMx_boZUQ!-ne~ zH~5$bspG%qK2UOo=!Q|wV;>?S6*`H5F6KUefNzb(`f5fOke?O3Nx;+vFMch|EFA3_ z$BOXG6t@}WbKOaY6Fsjxy{c#L&w{4|DI1XbjoYKsInw_Rc0{!>?~{DsOcMG!r#&{^ z1qRgSuwV9sJcI7juD`kru{8`u;6x=a2|l+-MO1va&{n1kUSixqdeUvt3%LlobEEqZ zD(epp<4xWqIYc`;;$Dek&|N7AhY0!@z8I82ieQ)NiV1vTv%wT-bi25)<=i(z?5@fV zNGwYyk3I;G7<35ELeNx|5xQB{A80n4d#1etHSh8oXM6chGPD1E zgrOv@0JqYFg6Sem{=;;k8PQ+ZS>h;nQ@yUltxqBq5N~(; zz}dN{d-)0qs_r5pP5)x=bQTJuq}s^6ESmM)ET#I{No)t<=wtmRyJ-sgQ_Y5*Tif#9 zVn$Me1!CN#nms+H9ae92-pjbqNPpn(--n&phK&Ra_H3Av;r|5W-A8vK%hmHdF5D0v z8W!*?$9DchNL%1yYR2)_m$&Kn^e#XP4Mpy)sk5Q;XtmYMDS~jmGMGz*E1P*M>fpPG zJW+K_A1Oo=ci;WV<<=b2TZ|VK{!1MBW|kH;>p08DHT^h`VLoqYgwnwD(VDjLZgg5m z)n=z!80@B8th0O_q`^!=Uq94v(2j|V@h8H2J#JL$Z|-w%FH}Vp^D=IYBZ!GAjd3~w z=KR=n#a_5JyopWKBG7;#)tXs5`|(%Zih^~jzue@7^M_%oiE(N>$XwSaT|Qb}_WwF* zKdTfJ;SkZ=(Gy!JI1sJ-5h%8kcWXrwt&)RNpb7@f+^9-WK)4`#!cMn`)bqnOR$HCk zr?X3TD@!xPMdAW9bsP9p@1IBhDdByg?3Et{%PsHpq$&&61Qf?J0FJ|}*GIIcQn$?K zFXhG*;La4~3_}@1VB5hOo||eQ;5`(uo^YfE!VGPU^J)AMIE;nsV3>OPL3sph&H$ z=;7XNb>&;U&`^w1eGgQxqDV$Nb$1(#oV+@?+$qdi~y0-U|{v;DC~y7x9s(gSCsZITt+gO?$>-vqWP(e*mQ1I5NGR?|m(MEIET zH?q5e5M)(y-M>uIg2wSJu1F}8vLbh*-cF1wJ$NogS>lWv1S8Q(z&QEM^1|D=xJS!Y zpo#omt-jGQMftg4Bhgd z+;5^H?Qa_-7gYQccPV%C08y5AfLosJ13_JsJPM((e;HsLb!T=k#1XNpydIkBAKGQj zkf$>&QGOU67RT3-dBkS+>NM@rO?2Nm`c|RTlu>FLBerJkQLgF}pt*Jl<>4g!TZrG| zV?ExZ#nKc?DaLXR+}fbdv05sL>1O985#W$DKvz@o42-3WN7pUF{H=$$2Qb-n7G@Zg za2v$UwBtIzq|y23sY)#l;2uxll|MFlALag7v3NB>GuM>QTP`Yp@|i;bIiAaq z|9xMO%AJ#D;ydEfv&W*1LHt4b|2A`d{W_JA`rme?@+RKruh8Ljie2y*>Xh{~GGrsb zao3>0&G>!;7NF+hL>1T+qdL*`SBzHDK;xZ=m?af2#&80!hu6Afph&aJ8O#*<%4O(J z1Qh1;BdrXHa~>{B=d)VSI(UZcS?a>zgzy^bDdFXv)G3#FjQDXpZ*=2FER< zR!dVU{%=}A?}35}Y^9>}h%`GwL5}KVJ(p#71Il`ZQ>PJG0g`tOG(!UH@awBhg~T@E%#SBA7L;kdN$ZAYBvg88u5E!6pWVL(o#mlI`Cn*eu^h`Y?-#MK~{jQe6HiCGe%Gok(03%uxWOdto*T1@|b@^W8)<@jakkPNz zq!A~)_nJygQ|KSNHwRpbHBQett}(_UoHJ}cRi3v}Fcc^;u4)nY3k3#o9nFBcY@bkG z#Ov=uGc@k-I)VcmPwIyrAYe7!?-@(P`@Fe=lQYD`>m&-2pOBzwlTAtYyS9*N3Nn{9 z6Dv^epo;z*=T3+;`h>mbQxq;j|a z9@=9#ifck$P))u!hc1&Ztyf|lw-EO%_M$IvK3`yR%D{)9CSa(9uTHAbi&d*tIO)6$ z`HtX!PyG`5PFkJjIhh~>iegRC-0Cw7MH%+;q|X;BD%qjD5nQt9Ah%-yN-=qG;djh! zmRLS$Y!psy(nTChb3ufoU7E3}it?o6a_4W4T3FK!?oq-aGcgp3dbJ&}9Ua*Q5BjJU z0S}sl&$?LX8XXMfM{+|1^Qx}$A%DltpTIOB6WsqgQHr2}V2AoaLeW6PdpT=z0%{8Z zM`AQ!S$(iOH5j{b7WkbBu=rl~n=1*jPo!<2jv_I+8YRVCF(GdEXNswCfV>l;Fm6ta zUL9t}@ow9DIgA74QBlNGm($w9=wCC8v3=GM$%rNw6xxnLd+w^Fh@G<1`9R-Gqb|u% z+1~Y#ikBOHK!ij>rF9Jxu=vZ;pUae!j28~pZbB<~H=CeZl=9Qy>94C(QbR+8On*j2 zuyluWef4uD*4#0N#4WuQ|IRz)<3GD$c^MmO)X|L@SAL>kmLdcxUz-g1PO}{<6RUEgQ z&)e{hVMn|GNZudm-dC_HIcso}&M8T|KJ{$LdGGK4c#$$Wm-e6PQ{h`*OP`yl;N zX|hoAZPensn_+cJq);ocbuXUbZdUz$TEAh9F1d$>UrA6 zyE-fGkh)9dNYb*1MN|_t7s#!M7gYv9mHXgv8u=e#O)vTrr#N2hl+r-0-gZ*U8{|h? zX$&XKPc+(nqGi~p`})45P3Ri`ARbD)>N9kycx|)a$|&T#x_NcKpm~yg|5Rk+p5MB9 zEQn(a>{OhZP7XSx9=2MSkX*pEi9CRt#86K`NR`o~A@jt_Xi-E!)L%1La&Aq&h;=|? zli3KU32xROmitONEM%-rgMhq7M?p`8wn&NfMnOo9r%xaR5?0Ds@9(T%D~>C|4)-UV zKa&?=I`ND6HCwDYc5L5AmfC>l7F%4>GNdaZ;GHbw@|Z-E7Ae2ZmK66a6tk^9&YEwj zc}FWLIoU5smf9phsAY#4jB9qzu70Z4Pv#kN^HrcPHCDhdzxx(QRuzZjYzyi_p@rgv z`l;^FND0|^e14_3!OXfV2W8R$%>BqRu}h!CnJpJcU>@JNop8h*tnn}1*3h&wI2t#1 zx9C@WUDB=Ii-`IQeh0TWXI{s9{b`8H{XuxV71!fJQ1-8ZN7Hf1`iOiUb+Fa9=9k_7RW`Aba|N480GkxyeO zHf+af{C z9!*R7ucvoFTzKXtU?)PP6wK>Bm))|1PG%{4Jn=<>T=d z{01J1w)&!Q(U=hS-V&MraPz%h&9aFSid!i{Hl7I|AfP^1r!`%Q>!({OVb} zOuwG-7g zg>O#wj{R1eLRs#dOL}^xFLpG-;-oEeT? z4eBgJh00vjQ>&jhP0#HG=J+U87$5)wUij=#=vGVS?yeP8=K-Ib{56ghx!}Cr+Od6U zF>JeoTN<7@!*_rKi3}!W7@P)Fd8LS^9JB~1;&SXkjsqRfB(sqe_zglP10w(yAO$0f zz?dH=5A)@WjC1vZlu`L)wF z$r%v#sOVKWX0hwUG#1Cjkk?7^=2oX`L56Qlj+AO_R?^xLJGT6Kt_t3n8afbQvmyb9goH$B7y0%S}j zuXiHHsZ4!)&QbY+P1M?x%`chnp#t@Jqj$oGO#r(Ue6QOt0jAbsRv)U57aM|UWI9ti z6L^dod)&6WxxohK%1~buU-v`}`n09a;D@zM57owOy>nB?C2vYz!gUN6Zg;5#bN3;`iZ{~6N(@*>v zT%fVryAM!M%L*ca(1dRsooS5ZK^N90gHPd260BQaI&8$dl|eLYMgheH4!>T)tbqJ? zAOVt#T5q8w`0RK76BUA~dv~#>emz<2c>2^xzK)WfG1auP-3!0p<5=pB2cML|0Ddu{ zw`v3+znLiTE+*im6u7YGszde?b*tYAYrjSb#ev9Tu2P5jzrP42l=w2LP)>-Pc9<$Ow1;2v0`ngBXPppA&T zSy~TN;ZAr?VtxuR(Ni3V@S1ya!e;n3P+6zAxgwd9e%iT&Z%fAq-2+5i-^c4ebuUwK zq-om#dVGYccW4h*opdGwI<{udyW!^p)O^-p>Xg8f|3{&arI%#2nq|ihwh4Y1hyQ7c zf;1E3TzAN_09rRl->hW8ikT@5($78h%hy>|`^G$F6*JRnH9D8uCxj&unPD%`c4MuA{hh6=$Hr^kCvNPQ2Q6`po)W}^s(rFbv%JHEmTe>O@ES-UAv8;Y zaa_!xT++v+t_pdXKAOw z=3k9estKsF!8z)ldiStt;9*Nz{l5>a5a0ZGu|gOJMM2-a9`o~?ti3vrAzF8P95+9= zpWWYvjY->@QIXy$fnerJ(OtG`MhsdvVaT(7OUYoSvTRtguOmQR^blKw>oyLi6ka@^ z4JU0UpKlAvse#~NBJ%Ja-&ty&I$eEu%K1>;5Qw8-?(oYqQ49;*7z_({>WCacF(#;Z zmv6c=;+cO2K)%n+UAhhLs1-GoaZ`+qo6|6_%OuTl7VsFBo0;lJELl@o+a|bc=O)>; zG(z!YFrE{o$d;J{T)$8=qkQmB%ItXv4tf23Eu#=&Q!}Rk`$>omIEK>4PsHGLAXH)z zSwD@9(@)}(O9Z{f&{@YBH#<>cFTUMu_b7gz&jOyLh&S052}$Wp^msTi`12m_E#r=r z-f4Naq`_gXhSrusQ^)?7IvYS6S8ayKcC4bq1|>#kwgKRa-J+QGPjU?aR0^KUY#6*~ zzqzU!f6Y&hmcEl*2ani5c1GvST{$0tzOupGh%mos%5~d);!-iDQSZ4bgRyJWoXnoo z^6{+ZXkjEud(dua`bY@E8B!1az2Iud<3;kjX92WY%jU)SCFGj4kfG6m3i#M#IP2V# zo81x*eO1U$q-#arH%U-h2*PC92jLkJXBigSNwA;QQ0t?OfMm77QQ7UNH>RUk)&kyr zpOb7abA&g)MBigxlklNgON1&t=HetGMk{m~N3!!1(%qSBQrH>XLcdO!+&q_E1mn>Y zZ28VJwNb_3B>4vCcJOA*%qGaAC+7coI~)^)y_II~2d+`D;G_mB+SN|Go;U~DxviUc z*E{g1{1$mR%j*m9y7_chi;<$v@Ns1R%L`Y^hNgSkNZ_KhHTCvUKfWodeEMYnC*Rfg zJLA+S-XemO{U;@ZaIWHakU;q4^;Sk4%$5swUQ6;8n&gN05p@?@XSktwqVyPfS9OO% zl5CuhG3#ukqyfEe9r_}oCg!~ zj66MGj2--|%MA{9;Nm&GXxvxFm2$sb^mmukmj20W zXiY>aP>9Tg!K&)Z;cXzLvty0hHJzXnG5Z6<*egO=h!!QKz-=H~tO9aQ=rr zgwvji>C4MhXqZL_&r%d8lAHN;H}_|2p^z-fF_dIBPQTTl!RBM4!a)3t2gk#!Ak^*E4K@mJGk*Gr$D;>K|A&QR3oA|t zexn+vw|UEU0Bb@^qYE754hd7^Ln*JqI#iu5T4zxC$30=xkJ2MYbi!fEBTdvMDxs|Q zxe!MXU}su7h{>~RXLE;yqxoz70&8lVbPwgsl4M^;x(<#`;t^MuOWa7X-tTUKK_J&N z5?-gEz%c8+UaqiAAy7`=UUt4r{Z;#!yDg+zt{%-?nXOdRK4P=5LIs3Mf}&%tjxSMD*~t9U3U$*G~sAN)Ho(0%Nk`HgNmYKL z1rOrZrv7oPq!P*!J~Pk|(8@*-eBxz{l93EyG#&3k5&Xhq%cp3JQUTDE-!bi*CdLBl znsf~dqSMqTi1e@xb*1`~k0nKb#xLVUP7X$7%dyb*v*xhsW1M1tT1;YGPtI-WZEKgZ zV_bGrSK0U($6kCkWHhEk&o7l8qEw)1w;Ua^wtiw;5hz|drM7O(Nv`a~S(VDf#Y@^n zO(%9L27AORp#0)75Zz7DB`5RYq(Xpe#SvDnEcW5_yl^MmHUq|b=v9YZ(b?Q#^IB>l zehwh74`?FeT!<1uiZZ^1>!NC?wD=@0iTJqh2IRA@24gqXUBm5upvza{ZmGnor^W=` zUrS#2!xrgmy!&G~QSrSYvl84KmnxvZZS=&KETYG3m4Xl{ChfN+FE_KE0_C@sO5MT^ zRzT`cf`Z=~rjiF53z%7^^zVHoZRh3B0XO#_IFOsb`v^_uP6bV%g4Aq6A%7l%4mG4; z96DRz?^nR%EhVpTKSQ6i_|=YCzH5be4v<7@cCgMKXzwd@26q{?2Ln2t5-Em@b%v9r zos@fUE|`yc*2X5%>hSJQ-kU+ge2;IosSIscw;k6B?Dlt7c+m>vrpH`zUwwp!k1`3T zKn;`{tP8d3B)hBa5XjlD7OH*t&qYeEpF~AqoNoxT`s8Ml7{@n-rcHy-QM)@z=)ICZ z2QW_ z7LT-DLI%D(W4G$Hu-q}sWU@1R$fGVLNH@@}r>Ua#6=KSw<8*Fyd4WGtHwLvFpQreI zP`_zB^B#c(642u3?mxqszzTlxe*Qqj^<@u+s|Mp<|50?L@6?|Cd|S0wW8j+TA||rL z2I#p_JzmEE{B%pk3LUMxuewA`7-?X>KASej9Z#FOTqiqFPhpgb51$jnMp2Fash}*g z;D6#LVc2I=;qi-Hb*Z6A1MM1Rg9ij z{prq7I_<74#OM|;M_nU$E2P7f(+}jYOa3a#)AnHv(zy^1Cr?>;bD_M!t8vhO08+fB1 z7gaQ@_fPw$Xk!>+to+R!DqDWU9(h--4j7J-i@DT;p0R;`_iidJ9nh>?<#4mI%^h7# zDUde%dW@q8eZ8c}^%hHqEC#VzF`B_T6iX#q0k?0oc_GiJzBA;}9~UuHj;37sWi0Q| zkpkX@W`n!eQ1sRJ!?8gu#So!F77{+NV>OAdsL#1Ym{){(N#w)p8)?%D1hR~D4nn0e zlrLh}f0+Lc+Yck!Rhf9d%O96`mn0bcJo0V1V{bic&DBu`Q=UE2Uwy4mr61QgqJ-Di zZi&HcH0hDX-97QcGT61HltjrG+_Dd)aH3ck7F;_jByAwd6#$*|nU$ro~PHoB?^3Id2^6txxc8TKT8eJ>IN6 zf#2qjMY^MyVuMEG`^b{iXxhCHbZHfvn~mDfnGU#v8W8zO0X_}RcP%7;7lr4z2o44C zc7japq>A-lfXz=bl%q2VvQHmy(bY_@;qnu-0Wm4O3iG`lrt6ZC!ewtH4X!t=ZUBJq zGB5!!GG%$=TUdu&rrM9EAH;JJn}=kjhU1pf+n|pLbaPl@aN|gg!j%F=FB05xlaX@n z_$mVsQCREwj0t03Mg$!%x%mvk^M{J+4%9J|t4(%z%7(WPL{wxR8Eym1#8?PYdTZ?3F-bK;^3 z@imZLaprH8+XSt&1kqYV@KB@ye!fB4d6#b-p!s09c!^}NTB)Jix|RvPSUThi9icB9 z+$)@l^tA<-V;qPEB(LbijFv2&gi<8ZF}GF5>J0NxT6=TuQM9ULsx`#{$(IPEy=Sen zgB!`qf@X~VwAri{k3S5?{E+;- z)kvTbo>l~{3@{O<(du5tI5nq#+HbO+RY&qeK}k23iqR`wQ5m)AU_^d64phs^&*8rZ;m9o3C+0LW|)D2TV%gVW>>j zMWJ>g*<{l`%)|o^tR4P!hsLTr{%=Du*#{)*LA|`>HGwJ~a9f-5*>xW%ep1}iMM%G+ zU2iG!>29(F%_!a3DR~SFm~Z}p?q+?N^&Hr-&a;lkU9JzPqygTq{K;BH%*MgTlWyLE zF*xEiEjYBjuwB+wZTiBpUIIv5h+KnSeDy9xg(Tz{xU?PaZJ}xCZZDYyio4q|<@fb* z^0k?zXarI1;fmnMWt=(x}*!f*mlabJ2 zeq^Ch=}f#m&+$k;rL+2=FIA~Y4912H{)00*hM)@3;e|)tC-%QRI$TGZp4zEhY(I(C z>)_JC#Po(^@8s>}IS#>CO8R)8fv>xc$HLfH)=_89vomPDF@)&|3Krk-Qei(I%59>1 zugsurOoYG%cV9h~u&~m){8xa66_wT$B~y_BDFFNgE7*J$%f(esuirtix`2w=o|pcT z|8AiG&KL&8UkhqI!|Oz%;(sh?_7@XmNznAK)byN{GjRY)8TR`K#2&6z;=8IiAQ39{ zy`phmE9h20@P9>ha%-0}`r#|nwhS>yr+ouYI&X`rI+zLo zi*rlq9NB$XRGp`y!@ee_I;5mU$g%Yc`J~Eb%9Esj|9`}}^BB=CP}9?G91Pr@B4I=+ z+M66d_FU-YnMY!iGe)u4NH&xL*yX?iVwDQ+i`A#u(ppF$+hI&Rb_5Y96y_Y%kTl(h z7GI0efAtV?D;cY2>$b=zT8Al+5{ufMMuUUv+=Z|lj-sFykcm8$KU#N#k8&yRQzs3e zW)R^gjFqzyw9qdO* zD9yPIv$ED8kbvf4#>SaB+yQrZXy|9x3I~QlvSP!x;YPRR>L`CBN#|v74#Ew5Mbm?t znIGLfPaVq&&XW7<D*EJP{#2ozi0!^riT~rjuExr+97KjzRVDX;3 ztV_2-6~8});BiF-g5ShhLk%R@5^6#-m`1f1`(mmPGk!Ou0uaCTv6KPWH!-=|OKjqi z;ZM2XkC)(ku3*argI)&Z7s6BwKY-z-31VE8IkE_lB|LZj1COn<3Akrit=7a_+@HHn zN&RUrRb2e#fOIBBPq@^4KxqzssdiTZY^sFLm`R9(+xgc@{nny6&(9hEH==CPOv!PA zjA;RQhq<;m1|m_~KaywsttY8~{?zA9o)Q&wP}v|`@7ApC!{9&=^FTkkXR_ri57l)r z8ogG4u0=CiRX^_R7EoC|08c*#*2!A_i7U#wA*9~6bk9&>ATJpk#E<>czy$%9mQ|Io zo=XgU7_V#b*!GR;db@$v#-)^`Qe}%I!L`@5cZa-zcO&zQcbL;n!p8qTUoKsnGQMjx zha7}B0N&8i#-cW;l<{Rj?Lc2SinkV3S;VC(D0*JeZc38Qy}O$)2!KysNACL*pVn)M zUDd-)vJG6QztvS@U03?`m9A zbE|qHDJb8w;*Ome&1hTQ9%~KQtUYR~IwF1UWMXK&phsdBy%qRVNsL>At(F$-OMR0m zTSxanjA{rF#&_86Ch+|sMV>2#JM4)si-KgcLX=L6E5QgO_=uJ67=N6~MN|=hWYfPW zSVjy@M8N=n`oz|4KrU<`?levO_fvXIDt$&hLx~J>h2wc}a|!HkfWL+KD*kC`6EAh+ z*zZaiFk2#q>r|igUvWVUN8~PrVI#oG^^8X)+0(RWLu}7jt3B|1D58<}vtSDn@amSFq2eQBwYttRyvw%b~);EyJ#?_Bnt_-3zf&Vleii$M~HHggvenNJExj7N`3g13Cze3YoH zS0nkoZ^OUm9(c$2s)GD1h-9L)Ry5x%2o5|V(@<)Z(Q(rNrZ%a5R+CXs@CLZ|Y^&!R z$Ia*ZeNxnOK+1^7taLxi-d%BaBG$1CTCs!6g3A_jx0mQffh5laj-;49n}I>q8LX%y z0x@vnZM51p6lb~0dmPhbFz_?C! zb*P=|DaS5<*;8??vc&tf*Z17WVK3$f8IaPp2c;ARxv~qhOjWsw(&}W~+P%bjjIx=U7_u-nctI zxuS4i)w({?*&2TZk1s!6ddgX)2)P(=_tHRwMC4GmfJ^vDJZU%Fj<}0?ld8~aPJLxO zhX{J@JsX+q(&>$gv%bh?pgF29=fKuF_F!RY;#hISF**#`1|bs&nWsC)GKJt*6LUigEDXJ=`iqQavXcWO%+NXZ_#vL z%add4Zez`Bo`TETL<1~(&rj0ubhHFUP)zT+4|Xi$156IprESXye$Gn-ql8zTNsFYYxT%_2uty2l8@ zS~s%~>0}QUn1*{^H@PB2e<)@O9sGoIGfhtqipO*rH4cX_2;g+kxU;GDV}BEG*X*Qc zjeWNvNUe2MrAH{aB^iPOO)qok31qKby7!6pYI&{Gv)XPcgMs=)Yqz^ z3m-Gf2!d@G{z%8X^|XGSk;(-Wm26W!PHMXeAF)pC(!(5H+$6t>+aw3p&48BTqFtcZm z5(p0&Zx-Omh)%KXjW;WE`YXrU2{j$6g&RCjFq6HIxY9CTXswOY33IqE&Dxoc3{?EZ zB8Yqab4eRc=@nVFGpJ-2p#&Ne+m=nr!S~3io;_fQUhyM+D*Xy=LV@OnkTLLlFcd1t z{KUwoC#VxQ@7N3xy&}kdsaXS~=~b6^Mnte95Kd+@g$&7D=?`bWVArbUH8e1-%4?4K z8~3`Z=(KMVw*qCOI&`+Kz@DUK>__xdb_}Tj>G|V@jeF7ncPCkv+Fp~E9R1~Wg1rAx zC=b#P9<6bN6XOwOf5$CO+g#DQhzh0Mgc9-=ZvREuw=FB&tTV}!UyNkfoZQ8VgYx7o zv^AN5Y4d%(?cZ;7o%Z(8{5=MLhhn$e*lqUuJAV&DPxy6FeZ6jX+v?l=JsNMfpfBOx zmG=5!UvE~tzi)c~Tj)pMZ)+dJ&;$1MyZd`!zTZWQ?d}V{-i@!fvab7mYp=J^GW&N= z_;+#qJx1@hr=Bm{+6V3KH@@Dke}{4ZTh!0(>`wcA3I9X6UHf_^{k?W?+v`pHeGb2F z(Ei@$zi+3_^>m=IGXOr{0FN6tf=b6?0S1!Itq$;Ay|4mWXh#S2vOH8tbz(%eS#Ot& z{zp-%HJ;7DE1Hl|8YwSWt`Z(py)jo%mWy&lJ1Cr%%h0Qfs8N(5sB=0Xx(yf12!%u| zN3hv89G8A6)fGp);{bbzYRP4c=RB+*APzRC6!zID5KHf>evTwyg-(xtspUx+#!(q( z#nu@v|+i1m=a)~!XT&qu%TGz{2UeUIT6F(c zvY7G>Y0QFNu^7l1U=mC|lK{?byY{i43lahpvJuj2-uPXE&~mlr&_$;ig75q6dXKu7 zrVda36f4r(_ct#u|0+|o`$Q}Lzi~m(dz?(THE%t2g6Xpcq*;<*zpikQ5~4c1aGeP} z`j{MN<)xmh=Y)l;(Gv7yHV2rK=)(w7(-IdsD{nh*cCQ<%eUyM!Ed6*lO+MO$9Yvb` z*M{|?XwJWL@3nNd1bSkPn8PEc>xueSioVO%9@@(if(+trsJ}rUzc#E(fSRj^DJNk! z^)VDxoWZ~(&#M>2Z_ID%MIs9vI&WszZ;o7hZXTr-s|qIr;V(236rc`jdKzPy8in*Q zvtBm1Gw3-FovrbyI^a-JXWy-@+XA-|;D@oo7&@hN9ak&w6@ijuNF6;7B{1iG6>E#{ z25xbttx)?ZqDO%A>WEM~H*QO;J0-pu`~t#v`R>X(^*q{@MqH)kbFo{1C>8Pl1GpV+ zpOQ4i&6Hp$Y!qhlEE{*PrO;cm$=x;*;pU}|g11UVZcS)z_{D85{$6wHTn($Kld;8NU8=huZQQPkn+_lEeMJt_|0A<4M)N=P;HP#!m1w_)$+zVC?A2Ne81|h* zDqR9p+A#9F7s~8RrHbQd8>BMC2%@UQ#|Q`lT)8P%Fi(ap?_w{&h$#L(X>kKYlj?Kl)+r!wV$1`&J_K6ZnD$c$i8@|6HcW!Uc!;XKfKRjKEsg6(wh-mTiW!Hd{Pb32n$@lo)3u6!g znMh7Z$vOu&rO1|~h}&jD@l;1dHlq*X^H^dK={#iE)T$}j9>(@;MOW4Z$&jhr7kYOs zAjYFf)q%&S-R@ivohdrliEfs21o^Cn;ESuR1$g;Rnglyx(t zz|6W_m+}nHl;Z zsdmuec0;kRE+a{XD(E3Ff8S!S_!1X*1DDra6PrqV9H*8Sy4#UT?LBQZw4F`Qo6sNJ zqiY*p;cG6d2%F4>^N7R8>F`K&fiw3vd;&f%aN67OACv0V{^Q*X-x4~0qR4|Pe@M!=ERSEpshWc=Le5ra;rvJVdS{4uaDMm{k=o1r%$z?;a9XHp&}ql zbcg$ySWo4Bp4ZWV6@lH=G)~?lVL|=`NvADHVcX*8$QhBK8bIzQ1!RN(&bu^h%&Kii zXOddMmbQ*T{$`XQaCwLz+`u25iVy!_Is8#Y!tuu5f+vV1z=C`&X1=db5v&3z|bzZdvgKSU~?R5>#{*NKzfS*Z00YxNVli;L_4a5jWXM(^B&& zm#EB>`f1XfV-->HNJoakQ~9!}xr+dXmP6Maq&^vD8E;zS5u+neEB1SveKT!{Y&eBS zyDz%qh0{1#-{Yx$DRNX?Hy_#I_uDSpP-H9zRaX`|tGtH!e9LV7=5;+w%SqGHV&GX9 z@iJhjh&c|{h}!Kcu$?D!D#uzvt6z(mRIcQ4m6q-R+X+e&;eVK!`vWwiM8r8&5JRvj z05p~U$!0=IZI;1Q+?*W@-a_@;gB}s9kfR3EGaPk8J=1_b8{YBU#wt{?66dZ31pi3x z)*8;Kl=mYR!+|^XY&J{r?Kg_rH7d4#Mg5~qPLp&n#}a(OA&uw5j&^&C$vd6({XL<$ zsGWV;!0YyTmy7DfIHdCvLjNNH?9L;4)$NEUJwd;Fb9#LUj8;<2tR|X0m6*hH(}WLw z8c3kA?nu|*t_XEn#(ZVD2w`9A`IsKA${U>{5MmdS8WVy_`7$>_KYy~=urgd+EdC@LOU%0{m% zQn%VR=P@_`HqumP<7rz(pXLwGH#OAf8Zs_bc@8o{R64}-Y|`uBrIH7TRDZTNam|6TrSW47#j z#}&0}j9M9e*L7vVjjWYg^1w-IFsoVa#VFVH7fk3T?AsnKqHu^?zx^iM6UJm9;VF+u zyu4>AisHm;0^!?0anOgsy(J^ip|aTF*xq7z$iDcn&mamY#^MwigcsJiX`3eVQ1~#Y zv(F+F#MVrAO~wfIlUAN{#25Kg>j3*N0PEq@&T)u?Q=CER$Z~PcYnHhi=qixRVWg8M zvq=F!2-zVA7Mz$v-kCTNOO^>0xln6FFI7KbZgE}^+PeOcu6pq#(kQr35{X0!@`t^qrySkqD;qrQdQ0MB3XcE$YDd@8+=Bh%0-@oR9>Y0^lPXh^MNqz3dy zgS3z<`Pvxxe<&ZpwIhIV;{$)j<@y%;doT`rS0Fli(%SEtntaOY zc$KBqp$WBVA~<*EDjl{z+$vtjgyTljn)jG)Kw-a=gD2%1oP9L3n-P}gkq_&=F6&tB zMGFlqlDfNt1erI@G1U?A`%~jOkXBuUx8Og=rYEtFp@?r6n9_j3ot#-Puql!@!G-R5 zC$-y)mDLy&&huTG1q9yTemf2YtPw{49vb%f{%;(Cp@qCi`(5{b*3YU&_w$2l?IT!A zNW0!G{f?a`>Djgpv*HY2vls?2>$Y!gA~EQA3t*!|GUU&rg@!4Dx{Q6iYkNIHws;H2 z+YW%;d}F;|=7e0In2ECc_$`WUdY*X#+wxjba+Lbb20(tDbL$fIP;OSmzV3I#AtlU< zE)6_h$cpLidFPfrB|;BQe;t3aZDpGg>(rfkHsnxqX0Owa@*EqFmQ68+gLvuxC@W;4 zBNrfQp};m$@u1HNsuD>BdMQ(tSg8A}SI_8%S0_ohoAP!Zkt%cagutfEaW8DvtCxL~ z7m{}o&`~pZjO?TRd&V&lbb(#E+bp1^DW9C1#u@UL+L_OsU;SYfEJ+YQ%ki;J5j*Sv zvuxk^55!n7iG|=l{{npWtTmW{5J+4EWWxrk7RtjKsV@_|tslDufIt9KnTd`pxsVU& zCbEJ)A=oe5gCH*RN-`Rc;fzNAK+7J^C-xw@mz7dx0$g_0aX39>wW=!zi6{7eE;EtU z_l=krZ$0SU-X@eUX+0RKc2t|Ujfuz|j-$6hdZ;l(JY0$W+{W08TddO(l2s``L&oh_ z`u!C77wo}(M0T8{tI7)pU@w$>+;4Nq!n3|oI=%m-Zqxe_-|n=#&uN^S?;Cn z-H~lfgyKFtvnQs~!HTZbJTj^d{KJ^VH~W{l4peVdKDF5?qy#6G6kgn4XK878@)slb zN~vpS;wAre@s#_)Y0wfB0qeHbm2R|S#D2Vu^og}jWv~T=w^O(HPkvaUthr7#{w&*; zF#uSrU`me?qN@yj%CwsX)pwJsVqx9Pk9jkG6Ty@^(1jl8Hz*&XO>^szU>J`Yrneb` zooFpaK%{`fa?Y|(a~0dg0Q?fI^KSG?pn;V3Y7SZs8o+pY#W{*yNi4ig`whY)TR`}S zn9R+B9v9%Qa{`{R30Feg#!KVE)Ou*tplmIJ`2@vGGoG^Rk5N9pY z{vphDwlFtBoSWu-#ahPPP~My+z(8x{4EPp^++|qEQ8=NO7&!KGbO$4Z*8+*qB0Hz% z%gJm{4{j5Z28Y)`qk-cBa`$lY`G$9NxVZmrh{YIn6Iz8# zdh9fmZPxe4N+cY>C78D~-Xf+MYq+qq>QY?7*zkr)aj|=+BWzhZs)EEl6w1l1guDLK zbF2GLnKfeS&9<^&y6<76cpmW89n#2;e7J`dZ4FHJ?0_%kVa(0v@aPMmUSDX0>!#`!&{7ay!}NMX8;sk2c>O zBbd{g;`E6qrFW)FJL^I#;w{2KX15gq0EuQNAR3?l22qBnHnD5;|9*vU^)SmjE zxnW$M3sQyce;g4)>l2IJMYTjUXa-clFBm>h~E)fqD5{k z6WWD5%QI+C(Pf)$ct8E3Yw?kN_E``fCuU%1MVr0nyPnZKamp zTeGVf{lXgUk%zXC4=7WxMd4$s?#pP>9E_K1MH}j(l-2FkwH9<$W{RMEz<6XA;Wo&j z6Tmrtu*O!{Do+)_EeB3Nkrv-Ldx9>(jQQtGoM4}=$TqPRUkbht)OK)f7UM$<~oCr0G#u!iIj zikUIev?-4Gb5>YWCm2MB@s$?YTHee>>!}JJOUM97AtO!P{2F@`cXrj?2`{7Qnx}F= z#rJk0mF&aLEZeu%+zjU2%)!KPn8}+dd}oVureK^4^y$xRH<9+YxkMg8dndA1gh5kW zeyK{{i4aK_@->Vu7UsZK2~}V3{ZbO?Y|(u9OxN)yZ2BQ;c-in*{GAPMWA z+&g=b*pVP0Efz!-Zz9vNGC+krX(=d`f{CPR!Rh34-Vhg5rNk}g-<)!FD(Pnze0st$ z-8J>P9v%4*iAlzpxomc-02?lZSE#+aD5Ihk!+5}xPa_w~$|o`0&g1rF1_|KEEOpLj zuSx=S;V`?sS9FI%kBJpA9tpDV_bf{1_Mvq{s+*={$2WDLRb9rFHoKF>-_|wx!DN$; z$lO=x#K>Ht)3B(*FzdQH-k30X8qmtU3_$qoT0U9ImPpsXIF9p5L`}5Np+me*L6o$2 z=8jN~Q6r{*kRt=I;Dy1ozM`02md=Wiw;rd2uBxO%2C|!#vA5RiLzE%rzm}4Cj4!H( zAfQLUdgkAG9UgAIloZxI|9VAuB8biV2@>@1M5;`DZlzgcP9Ckdu(9;x^%7{jm;zFg z_^-A~%q-s+2s4fG=T&?V0nq%bh|=j(6XHFZi?Dx}Doc9g82dh5d)!T88McniuW4s06P+cj z>iDB;$OUIXo@_AqBi(i87H4XwUVt<3AXx98^%PPLmC^m;3-Tg-xXeF(;+&~~Zv8EzY$qhpx9S9hKby5$o$IB6d7*xP$fY!QFpsy29M-srA#)u<3&>D#ughl^zHts&4 z+zb8%cftz;u$i)p3x6X;VBD82a1ylOt%`1|%d#EeG^&o6D*KbBvTc|7*W1dTegss% zuP!;12woXx;FEyF9BtfnK(XD?B|$SO%qpi7Ud!I;g6zsIRv@Y&nmx@)CE)lG&Vm0T z=JE@T>hD5CZK{$apAh*PVKRf?CFnxdY*lg6$r(iAQtcDyEM2j=6_hsw8v-FWq^5b8 zqpftJfV*t&`RtYh@ck39@UK_C6qXGgTRt~wqaoEYfizb(Nomg|0bUNC_W9JSF-Aru z^=-AOZ`aa_udg2s%X2M-lQ@S-Kh5%DBDrkM51h&W{2&r!W zL4*OY>~PTC*%e{i_E}11H%u^CV}^kjW^l0W;!)e$I@uL#l3QcK1v-gOFRshHi_gx3 z{}E>-lj9$v=tyM`U$aF!BQ=@IZ^iz6y7n)$dqJDbc=*Sy1@d5L>;r2%82DWw^wV2z zy~znQSA`&wl;+}ix6jFoEb?;^AROab6a8FoWPT#rwzYE}(6jNgx;D+*`C z-}XD+Md&*i_XEH1u3Jb@EP%&;525y&CJ4SVZUGE^z%LbQD z%vSvSi=$cCdBr8htAXK_2-FL)_lK>^?Z>2rf4DQADeWDhJ0J2gH>ho`?#3sqJ%Pl_ z(b@vcWZmvYV$=}?bD;%-Jj&;D0C)jUiwDG|{optWE3M+>mMw2K88IL8?`i)yy}vg+ z;C!LS_#|PzXHe7G4I-jTdk|y|tJE1EP|s;`ce%Oqe~lNYqs}oxVr>P&!gvDuwoQ4d z8@QuOi(&PV?KWu!;A4pb0yo-rOCs05uNBUCXuhDYbB2z4nxjhUKjkOQ9zq9;Ey?jr zl`dy18+N~cN1wDoYjkKfyo$C=K3dsbMX~!`JMMG8KnR8wI}W^bV;WnU6R|hmj28^$ z$KfA+yd5V-Xq;ioSDBI3%3+qkgLD{eZfN*JK&n?UQQf&|ufABZ{I7gIgH?|VWX~a@ z%|H#AN?1b90gvDtFiAH_5nI^a@G zAFJB?B3W1s0c}|ZMS7kFI5*W8D5iF+^@31ul%{9fmg6I07(~D_Oh)lf zKt0PCNq>?W-3m+#+;oB|qWcSii0=+K(;!hyO$q3%k;T@6gCFQd zJ!}Ci-MjZ4{#?5R76KIN(jyz@jlHwTks011S&LANSEpLCM-#Kh7$qJ99=M$~qQ+ZB z-hc)7fchlBx?>>tG622)tYzgVUlr+DbS{)&i6epnXt{%SQCI}9_%Wk81fzc3+jbK^ zOGmsAx}66zOTZy+<3hY(LLQoOpS14RcTBu{9Da#Rx04!n+88ICN|f(YCkWQ)7qM3> zUa{An+US&9LQjoL2*~#W(QkwZdfa0;G<~$Mokf!kXB>1_jA`rj^^ z*{qqI-Rp8`Po#!3qnNdcgX@fDwFWM`Bg4CkQ`ZoLT7?o#^#kJIl)2W^q#t?walbW; zeOaI0+Q+dMK;W^Zv3;D!a=U5prs?1|E%B~aI;6POz7|m=NuTsdv67zv58zzihImiY zLl0uM_AD?WLd9;TbYsHevi?;~n~fJQ=K0S-TEQ9swMwoSJg`}V9e^0GOAWbU>r^O6 z?b{QEOYJ#;gMl%3w_Va22AdvCxje6=aX8teAK%B@L?2`dCB;ga}p8CAh{W+G~ z-vMUNjgs#)iQw)!6T#Pibj+eskjpSII^_XTp@xSb+f;p?R*S~6%kCA1YJ@KdF+Ohy zdo9J}j3SUI29!z2VVe40o+(D}RbO-_KD!p4c?u^o=>s7~l#;Ry$G@Uio3080tVc&U z-||a8JxEVbWe~ky6;miP|A^|phFX0NTQu1|P;36Nucshv&?hFIo%lZRCqN*U1z_;2 zf&s;Ds!riM{r@Xtt7hHa&?FoC9H`HNy5N|j(4_JMf!zx|OitVTp52K51L94;`#OJ_ zWSwObG*QW^Id~CdNpr7U%!lNV2hB%r=>*Rsa;Mm3w6^c}_b3*qv*n;5PVD+*2p(ob zZCanc|9ToJXLpxv-CzGHBgNkwy~o<%G|hMP6>X@AjR9HXK~n-V^orkG|p&D!PQ|x7$Ob=wo=!e0zc|2h&?U^UrKqO9&5_&g*04 zyU+62=zpbfuS_kb$7(w;Iu{BwW4s}cC!IB6>MnU+pQluC{oyOVp^Ez9!LYFJt)(@- z%0z}S{GVrZ2uPLBdvZghEpZIDP96lnoeZx6d4D3_!7<=Fa)s$V{XJZl8?wOtYX{6a`2mW4qa9O1cG(IQG~H;pW@(QBqb zSi{!^;jWHVl|d}-#fg@JOG z-9Y`6pvr;GAq$lu=8N?ZP7bL;fu7C-UPpx`rEa7%$mk`nL_R1``Mb<6$+=pe)t|F& z(gu@*NKrqg0kD~Xq{Hqn6L%T8+u;OYAN=&21mhM7;Cit8Dex_oV7jWS$J<(8E}l$e z%rCf?I2_@mw;iVZoxaT6*(fw{B?mD}W%#0aO%Cj56e$ zJxSf&w*BdV(KoG`FxEH(XyOs}(aYt6rJ>$y*XbB*3{}0JR`)Z$0VuJ8+@nH-T_v`Y zbQ^av#|q66JZYp=oZuVUKjin2Uqmyw6v6pr?}iN-Td4hEG3VHCpHL3^OU=!j^GpL_ z<I#q+q8KZH3!j$ zx_E}l;BiM=I}JkEHi<9a0usTcfGJ()O{+A7O? zMW&FW(UTMVGG^zQfCn6gW!tAX+s`ln^t)Rr@RcSy9H2(Elj|q3V9*j3%*2- zsyUsrfPa-ISrSy0Gb`XG{!hlro92Lk!M!yZ2!y$#Tb?w)B%35yLOok3R{^wKh@FaF zrKPpBnbFFq0Xs@*Kb1JJFxGL|#Ph9hsua4+JQ}hnXks|^PfCH$z6Tr47=ge;S#TJE zJKsyy_acKsD}U_LjJC5vLExWuV!bz>}GwPq`XV{PR#&G2U5_#&PknO zc7WumlANr{KJmP9PbfsbN!l)H5^AsMHGpn(1^FF&qXf$0J@=Y};;VJZMD#Y08ud`O5bS1V?OKfMRz79bE5*iL07w zv>SAaDD|vmNvodVcaFP9BlDcAumq=PrE~p-d3pZ|<{URkXHJEWUQdtr3e@WcDHm)p zW`PdMOMvYMMOIGUxvzM6!-WJtpcR|R(G9H?!OP3i6y%4ZnC7|{I4DgnZH5f++ntr# zV1mlafA4fOl`xy(=Z_2#=oswXj5!H>;+y3StaeA$!0V$wRjRa_u1f6tu@hl51p*p0 zpmL;X#|hs#yb&2dv`-E{i2&FDtl!biB3-dR={4GwrcScMz!U#-!k;az^_RKom{ZGc zFi3SG?p|6#u-p%ZT|;6w$jq=rL&SWfbkHpM#LWoek?o!&p z|1`)KSd_bRF-b!@_S3iq2iiI<5Oc~h@C0KudKAH9_vksDF2aK(+k$%Z0>?aS%}9ry z->kOwwK_Hxi6h`HT&wd3#fb!cfoIeUK82@t5MXv#J)aY_mYEo@*+lIq9=5Goie-} zOaA)QOy7h#GsXO%=X%KW=I30iM&U_q7o;$lo~^B6%k$U%ZitM2o_uTR=hBHpMCbYR zmMXv)oTCs>CLs#6vR@2c{)x0K*$bRdq9ygHesbfj+{Wu(R&0|LF{7=*vrG;N zF#}E+f9Uoy4?dp$2EH5*2D?+IA+*CKrIi~U!JUeUm5t6ZQ%5NR-T7upn`M?yYwX6;BOzM99)}_3@Ofumwd>a+pXp#Pk7A#Z-HH8Wd{O<|KS1sMl+Tzm^iLex!j8LeTc>!7y%?5EB_1_N8Al3hnKV8>gKODq*aB39ImqdL~ie zm9%pKUn`^mXn%7_M4A!yv=VKzY6YmT z$xmKHgisg>$o<|gy|)yE@px;a&k=BZi>srqx?uuWN!?Usm~AYuKeElEOUuMNn!TiU zoc{zIYy3sYgIo0(H|@a;j<%&ni)eF>D?eQMC&sf7z19+H>DiL(5vw%@3%pJSMm&d@ z1J6NZTJ{oBOC*HX8H?Wf6wYSk&ZZvMu*fcaqtMA^bWO>l{f4boJYq_;%MPWz{wS51 z-$DS?EEiOLyW)ITiXO}~CveGCMPX}d`K$v-CRt9&-(3KQ$SepiFmoMK+zl-<#64T! zX-tequp1pFoNj}XB0gNTZ{5k_9EA~A$x^U z77Ofxfo%u(z2Harnjh>+5xZMB#??#JgNJNDpXu*#N14U&@R}7Q4^G}Iigkj>NFxFZ zLfrahMk8v$5`va1iiK#7oiDWGD0PNHG&wfhrc_u_0S>B;v_M(8XSG=>4+=g>n|tA0--uM^9Ub@l}GbVxbq)B?GQ#|u#nORO2tmKaq<67KAR;1S9+EKr%#E9 zu{)*9OVY2bhe9G2cH9o-c9H@Hi`qgxr2}QDiUg~pXq^}hJVT1Mg}UDz)?{8QkbLvg zBuX~|96sA$N1U8HBILRwVIR@mrxx>{@K1LOY6z8jg5114b%l^Nk%P1pOGImg=QGCF zcRgg>9!BfY3EOHH`ByNjHeZF)phkt)?oN9ONBJNhm5*t{rPmz!_Z@U=T#I4Rh_!Z7 zIl(orhvmZYG4|$tC!I@a1cV_lhgd!+KOt@HdI&Yw$x?ogwcB%0eYJKBnT^iK5@Ig& zBN}U3O8WCVxSaG3N3C@!^@xKPiqGWe7MPEIb}~M#-v~#Z^YReqbJF|O*=h|BCr4wr zS0G~>na05IOZ&@$YcG5+U|xll1Sx1|eKzEfgT?bU*vX!;ZHH>$Gy&tZZRm|l{k!aGmq>q`vV-s**fN2|&j)YSAu`7unGtx0*H~q0uVTm@La(4d*u?M;(#PyfY!s+5741i_a2%djk0@6#V)E`~?t!uVK>%~tWECU*B2mwx38BbsRg?iDz~e^e zK0$0xi2aYKFC|fltljD=xhxd)jC62|Of)Skn*0OR4&hKxT1udnW5+`9BpruxHgZNA zj&q1Okexs-S3}Wn7X(UhCaXQR4b?aU-W9`tm8EyBJte4)zYLxe!`-1N|4c6B=IpqrGP43h^C?_nfIr4~+c*$*Ii5&D_n^ zgBuolE?<*m?4@8)(D2$84}X6jB|IYJ*fOn+a9vagzlbB=!ZX_3M24?$*N!(VrexW3 zz2+xHB#qXx)v{Zr{USQhG(Y5>;>i*sp=0%Ntf2We=|Gr1=te|(Xlh8sigCd zVH=YmdCzZF0*m4G6?98KPnzM>Vl&aO%!ENP#D>M4T(aSLDP}OFc>UBjYe2(F0rI)E zGq|c$eS?xHJh0~2wr$(CZQHizjBOidY}>YN+xG0awRfxj%?or_(&CY;bJsIGf_2f4D+D%3VKeJHkzE*MqW=nj^aax1hym8TnC4L{2XVtU`YK_{ zNH6o1=|R=gBd)fuxb)G2S<6kSESAX%eiJo%mWDX(S&YNZSedFlRvGNfH*{ESoe*}nRs7g zSQD{VZV}B#&vNdmE&jGZ<&si}tPAqxOBSd`Y;dwpqm|?IDA9wp#gh$8ESmdFcVqq^ z0(0%=b-*YUNi61U-}lyIO{a^~%TrB*M)A_Kfy+~7_1QLvtv;ra0H((PtxK8XB7-;I zfv9FkP{iA44`G3uO`KGNJCLa%XS>yc%p4?Ul_KXqfn5L8mijBjAtY#$!9(X?H$tg6 z@A5BUbE ztz}&!-C<9*+q*KU=PGiLrrc&g4j!G*DtI;Go97*eFf9tLX-QCJuXD^^nyHYZaN&t< zR-9HJzdA6sRjnd&9ACE^Jz#i(=WNfYWx-@7+RxpJ(<_8ilWw>|Z~Y!*QBsV0zJ0P0 zss3VoCE+4~kd1W5y5z@)cdFz^sZ~=GSA|q$Q}3VOm(~T0AS=wM))*0aS7_Y`3MO}d)~&Brv2FTlPEqcStg`9V;vYI6zI^D zCqxc|Vcj@?!7yz?*{Ii8J`N)XtFND@W&uG$K&40SYrnV6yrK=Y`gxf)j&=u)&fz&s zazx~Ou{s@G*5R&#on{6asLl#r^f|dV*i~c@`W&8AQxCd>b4(eqG#@vgAWt9I-~ddk z8M42TQIjd=?mGm?l~BWexcF>YMQna<45y$gH)jm(4F_ckh470K$N;~U`*FqAe#o*OSUfDc6HdFe zK)C*OYAFjQeE@| ztr)0(mENli~&~0bYHEfiB!`r}*i7$m$%QCN&{A`Ur z29O6nt{8N0VsC5|#Gova?Ur311qdTyX{ z!LHnx4jGf#u+z4}O$xXJO(hWi09WmHTDA>AsgHB8^iWlo*&Nk+tKVVa2Kxup1PjKO zyTX%tn8+PuqXm@z5S^gJp6)UYwe_y_QpT9?@{I|_dglagPR5DU=#<4=GaCy)uUu}p zwHjP#Y6|qlnr29+Hlly)MkO_LL!DDEH5|Z ztNYjZs_GLoXfPn1ZYOMlxO6cNXG_eJ@!fR^eXdHR0ICX6f6e<@P~DR$r|lzk&#Ca| zDG_#|NPEj_-}c8?>~_(g^K}UbhUAIFL7$7f_V=B+0h?Ew;+<4{M=51LRP>gUjZIfj zl&i*mpKA=TED~XUXgPvPtdleOd;%Rw3&c22JDDDtyygrnOFnm!cb>s~CNLb$xy45+ zu&5em>;sxJ!-U8^y{w@?W;4`rg4eXY08z#(z}Y?mtA=%YrU&i^u_KGkCXfgBU@+Qe zzlRi+VWdxa%NyBJS>g%c!NZA2S<8d_FaR)1zpIWZlTl|hBu%qAf`IJ0nX@2rP&O^w z{%TigEw#UNZ{4nd$JttocA68&5AK3oX3L(E`xe9VO28Ju0n;4Hqk~)rAH)>Km9m|) z_&l@Uo)M)kiC7;LW@m&eXey*2Wa{%LPt?mYZK#P?L%QPZgkpVZXn`TzME`=6_p;E) zBaz2!li~1K3u8+A3P11oF$*x{ zDP)J%JeT;uNu&q0WwkBA)a>^*B%)(s0GNZav}3?QXIl{sQh7SoNMm@{m`}a+T&PuG-89?gM=?=~Fr6&BE!w>P!yWdGFb!~!M|B_y!vN=cxc@h0G z1)C31&q2VhLG01^!ita8Gvv*tSb^#BnMg0;zaqd-zJ1CE6P7QE-e0v4YAgcK*XHvO zRXy5qpSZ$AvY&3-&2eF@xrfL^=jgn)dVql3M_uZX742t$hROR`22rprvySeW&9K_E z*ihizh!UzNUU_u?V9YNbMmVCgsv9X@9xZBZwJnDqqOMt7o|2FD>qlcc z1DrfLl7dOcucG>HJN`4~rA&0~6JXi{sRfU?s9A76b*_`)rVm3ZXFt9SD6#fUHEUE( zU6KlxqVWeIW4uU~#?9K5>e5=pqy@>?Z`M|vgDvkAuG%4VNX*5^c{zcTbecRgkx$i? zZ31^)A1Xa5594yY#pzkKY2t><7%f6Evl)H?B+*YdXPAa(BKGnYN$A`QjsAkpwQDM z{{;D4QeR!#y87oGRj?&zenRAD=${UZLyIWDxL;>j0sC2^RJTaM6vV|da8Y2*>BH2u zuR8*J8qepMQ?>b1t1t$LRh`3m^*5I{5tbT^;z`4GdjZKyP##6KvO)HoH*qNW84@#a zPU8oHzLFUj-c%huVF8q+bv=hoQXALb1s}sFfmO-f0AAg=QSnUc8Oj2x*k`tnVrn}x z?A8>tD4#?02hW>opsMSy7YMEanqO!Q;)3lMo#UPOx@Bo(C_T_xsB*%Y5r!^uF&3w> zocqEFs!P%<((Iqa;Ty_P7pb-pI)MKyP}}_{iQ`)cG2dm7H7#y}Ayd2b5$XL(nQMHJ zd~Jn%DI54-(q$pIxI~=KI?XODg^xZ5!UT37i7RlBP;@~DeW+sZ?e zF5Q@oUeV*0DD;C~&m(~U>VYGl%gZU`$i!-D)OWyt_x7kAiODEZ<#12=;#hn{Q=@q- z&J_O|G3nNF-#~)vGN<~thrA@(M+oud%mhQ@fdZvz2{iscX@G$5&Z{ggs7G91k#bLG zV2!Nh@PTsI!9UgSQu)}FWQ;_4UisCN7CiuFMg8+hd2~Gk?6^J@D0Gwuy8nh>9{*1F zc|h0^1q+YPiHajoA;S@QLhZL*WM`l7N~*(k3%}j$4WQ#~iy=08$&DC{sVnmDO^6DP zN^-gG9#uvJ0wHwg(eh@ODLgd>{3#9SnwbqHqEs2tU$y(xO8-*)hnix#ok~0GNhOn@ z)iO!>S~3WBF#q*y)`=7(;4F_4c_>U|-ANjcAB++qYz68V_c*A^^2_3{;u%%>c4;+a zi2j?r(iziKyj6)Pm@VN2cnnG|EKWOIS=uzV`o5OVP-4isaT!Ag<$DzlhYcx*A4P5Q zBRZNcTV-zd=s%t*o5Wr1PNdCcdT@exOHUbuHB!`WP=~ln@L7O99FErLZwY!b2FxbO z52PMXEe6Z1Jb*^>1jb;QuP7FZaF{LH%8Ks>wn$X{itKp!4 z*6%+B<#132U^;zpFyL5~VCaSl8BVly&4}_>%rz9%$%M@8?;ZV<9WJgNjpMu9Q!0R1 zt=Sjsvy$~L{={je3Zop0qbgkm@iBtX;^xj({*dAdqm&8H*GhNE3y6v?y^c6IlK>8j z|BVeD2>L=-Z;i!y)kNLDYfY2%em0ozLJL!5MeU%EVU@(q7sXN}w;5`#FN4f_AYPg{ z5ZP0wn*8SX%?L2w{R@cuxVe|JKt80zMK2`{;0Hn{Lz6<9O-b>%R*L`>O@(hD0TM-r zjS(2&D^_-h2)JV2WKM;iH6>FZ(F*7O{XVe3)K$cI$5)t$fYsL_h?L7yjP%)-?@Z4D+UmywP-~H<8 zxfHgzMVk$}0TX(prKnqK_=HawxK4E`_^Yfp{mue(xx6~fpCyuYkDzd1@Nnm=FJwPo z2)^$)GV`-~U)fd%X@-kChW-)IULMrPk6PD0PTQ&AOYr~YL=E7jO2Zt(Ou@ntjoFNP zE+RppuE~i!USZP8Q**0db{HcP2$zr44sxg+?m%f)_`^-WQj%U?bT?W(+-MjoVM*)8 zUT9S(-aGDGebD|D0c^yj2=ChX5YIIzcrQyrZj5;M@pl9@vkql-RfAG%W>2TIJ|Z5g z9zd|7{$7e)%?0&SgFRR1B1l6(Lq+IoE8_-q26|z$<$@aGnRt>T&A=_`Jq3xGSmv#O zP+L(Mt4$R_p#F1?L|Odh{V>=l;bO_eOREBz7>7hp zh|zLL#EGlc1{)mc75~#P#6f#}(aR@>(uSvddT>i(@{>`V6e|<-*E(za`71=K7M7f$ zLq++q+io$64+sBtlDEzpNtdi{OMTJ8Z_KC!lu8aOkT5p|`jj0r4~>hfH+B@mHMlFe znX3M&kA^TSxaNxm5-^E>oP}kjF{n889ogWT?&23@qJaGpw)gl(gw6S--v}et|N2zn z|DFR>@|I|wpA|6%2n?rjgEluuQckK4RL5ujFE}tyE+z=u2AKyYu8w31vBP6BSj|wB zH8j>AbX+Hel&~qRvs$S*c_!C86cTmLuap`JI1Oqr9xRrn(PyafvBP#ibw@IV#y40N z0>|!8V~Gy-H}NUP8AcHRewdjFhCo9ZiRVy=IDL|_S8{)2rOGRq?GJZPY=t#P|Nm(b zrY7Mh%l$qMQl^N{M!nAISabbgx~oJG_0a$sP&&V+`Go^z0#}v$4Z(y^-^SSMMz1R8 zutuInq*+vUASFJkhEucUNvwU%XisNdOsfAR0RZt@AS-?bI*L=FeMnE zX8r}m2p4IqC~$la#B*@qMrs9f^S(kzQ>Ha>S+~C@_ zK_3hR1T69G9mo-kDwxC%5ezIER%WoZpW!sJT29F&GHBmp@czN7tq%dkcCR!bYy7p- zo6|K6tSCQacP}1z^xzC0Yly#R9sD)n{{d`uFQ%OgfO0R#$af@^`N@RpZ=3I0xU@{^ z`=1?yY85zAHui-I1yVzKsuk?JIUlKun4E6d{*IXS&pm6uq4W`PC5nrBwnQ2i+cjC+ z_~ddrpNlChF9|wp6BIj&SYuzw2t8O+r5cY$l}#1%km$tfmhdD5w3EjFy#H(ACI@a- z8s8SO)k6G{?xpw(63ha>J@gx5wmUB0l!E~&p>4XVMOF^q^!2 zB#n@_&k=}McfX-k;2wG9o_*~dVW?NlvnSvV<}rLbumPz~XmDE0dK^3Q$iwPV?-#~+ z%fGNdUz8jD3#y7BBLL)@z!ZK%>g=T~+HH@u{T|?4ppc=P;J)wC^mUS^0&A%WOQ^9h zbx+&w44Y`+PaX6*nA#R5hufGraW%quycvL{JSy`Np*fCa;7M1Yl$l_*&O~Spzot*_ zvG9y|qJWYOgcRI$ynQl?&h8A(2s+0rk6tUejiF@EEa6Mqy* zpB>Qo|E2^WBlPo7h!xp)^X;Ce3cRMuS1BfN<`^@aktU9CXJuu~=&d2$Axa>z!!z|Ki^WHTay3(4m$nvAEy6Zf*D>xeXuF!i?5H zbYb~Q;E6kLw)u|PuJ080;Q&rGyxpbbhO4fECOSt?4o+fTX-5Y(wxB>Yi1cSY_2Sdz z3XY{2T|tekbE-(Y6?6_fuH`wKTB4;X*}>L>u1qCnGQ1~Vl53u=l6;36g7IMs896U%%8Vw1a^&-3Rz#4y9@>morP>*+uJcy zYZQg2*+L62XOfmf3D{us^;D%?LSs~57z)&SUF9RFDGSXJf=29d^Cyi1-4|a)W3n5(%*k2_hti9u2_6OnRXz25J%_BDqXX~wyBtcmMTwS%b?%fc%Col2zQyNQbLf~L<9LbVni z>SHvKrYV2-^sh_yL=j?^u@0RZ=%6;6vTm5!2XGQkf%}sE6{+pt5bWrziCm~Ld);1#L1I-+c@d4h4Aa|etWg+wV z<$VJ%7m=Gf!b+Mg#Gqa%a2HZUGq+p97ARsyqm0?&`#y7>z|P@ z*2c}#*>gszkhm_Ts?J90u=ocw+kNp6VE-jS#BuSsp<3A&Uey)c;>=i(gWl~AiCw#a zA~4Q&7U$`!R_L1HGe+(i7E%3H`UnS&JZiz}yAlFIl0!Fqy1;7!$8x4 ztpFO9!AdgdPWysY1JWwTNBuNIXW->{W8JR2If+IFABcd&4rMtI;xJ-iFAqJR=Uo1h zz4_M$Bqd!FjK#E>=m(P%NI?nRmLk*`ow^~pD825ev zI+k4Hyt`ZR=rdeLr0R*)elAxSH{^}VS7adkDC3iab`FamxrFM1jJ3D&i*iWt4;Vlgh zs!Z{a@T=0}&<{Gbl@dR{`Xj4U`i^n`=?dsqZuuEJX2%>SEQ9FglFK>E!YAwI(0Q{TGUJ9GIY**j%t{J z_3+8*xlTvLYr9PQH7dSH5S$!0j~QE3IU?%(bBrM%j`i11xzodxZLJnP%Ar}L6WxPe z$s^NRT&IHROyX?RraU+Qdywqx{rAN04-I?sCcj+)tM0B~J?FPoB`bF*vA^3Pa;EvU zOJ#0Y;(TRWmiDCB5yJ{mgSVE*d>&`2PSf~g@yrsq;Rr5;67+&v4pd8xgmDn}yk6q6 zj*2;`^s%tvvg#N5=eOXeuMV13v*&Ia)a)3SvjH}}=;~dg6 zw4_Hhls~8@UkVY(*=PZR9?%8gP9lW!i6QIZ``i%l1iyvwXyv=yh$~~|uuv{laQcqp zbb#8wM`OJT!A260Ym|(w2!N~7cATODj)A-*&>l1Rbhz5Q`fV{vIaxPJ*}2OA|4>05eAM;r0?64ZR{enn>7_ecx}N-Oemw{ zuBS+BHqvi}Y4y-zw0RpxfHMLJIw*ogPlBeI@gq0}ngs}4guw`%QO@)=`KQn|Y6HwL zk?Ci!SlEMp_2hQ!nJ0HoAvhXEj@_h$26`Y)9_lXmeocE%`NwQPlj59w2kuF}!psRX#4YK`OvfA#mtXSxc?p_CziuY5c_kK5*yri?;lvl(1V0r9E3IasMZ$K-L z;2Lrq*63M+7N#UsVj+_^pbK-TN@_^~5W%(C4*oOM7l!A6aQ&<8h!y zI~s%q&3SsAoTb2zj<}8fNHj~2cbW`n^qFQFcTl%etGA7=nAP2hs$Be&(v-0iOb{uX zgBny%+g)V{`O+_*Y0w2Luyas$eR{jgwQA>MdWuqT{kEP?Kej`~9`CF8sk{W&2z-P|eJxRGv%zLK7b!TF9a?fvQu zHj5QewcJTp;We@2?{vvxrv*mxlJEEkB0WGzPPH>Kj3Ry_rhnYM=@oqu<>2d^-J74 zzq}Ta+v*W+b=-r?X*n@2;cp&xM=kid33{LQ!(^|$e0vOYc2kxGdvDF9Nd{;Pt>h*0 zSDw595NRnNpwFn84QW4XZ-pRqvML3tqPvERUpw6{>tfP{+7RHOnhV?bZ@}?w;Lx9X zT}U~EB=}xpFic{{{6hh1(SP8G@hhtP2M4GuQrm=J)r@X)rnRN}@8lM+_M7lU*faX75wzax$b8&7>g?rdytksB z965G=b+DzJ$Qh0M(aMX>^}Sw#II_c_h*)+O0i8#(;iM<(XC2|UXi4p?hW3KIrD7FD z*}uIEIX+G_t`lHzR^Jep(LH&D2LO~ZzTEpS`Ci!fT7@(_h4L-o#9dfA3xbe^@`-s9 zC+z}U6SMIU(-H{$>h3NmvboAOWEZc##k)=IiABvqe#tO~{_Mv0#Pn#hukNRbw3g)J z(Ygd0WpAGnlh0X5>;Y$T{Q=`$K`=IbL-7Z*`s5B_A|YF2G3yCwM5BWIL-kRU9vm2! zhQ0VBQkh7YbU@W#3L{Qc)(3UM@QiJHwC}-vFSE#Cwx}Ru_(u13#oCeJ;h;~If|*ij z)w^_F6lo>p}r4UaEw5bfCTp^YJ$6y4T?1D8BwKJ2W~n}eT+>wh*w1XB<9AB*(k z=}sYaU`m){2aI!#mpR)7s(tl`vwj1ldm1ZO&KC`bM=fazC4b6UCvr>JJvlziiywyu ze$~3T-_nTyk+ zXRZRw2q0h)=hXr!;})MKwq60u%Z5$$E#$j^$0WsAZ)<1ZjdN!dw4&6}soVRWM!7vx zzAjmRw!@GNH`V{IeOpGZ%(Z8_x-VTaJqvp+fU|>VADU9 z^@Sm&nj>2K!oOY}Qa5a0r~{t++|N(*dK%O#u^-vrqM_xPaO%?`(P2#DktjQ+iCQxm z_Rc1)3eEb%L-G*dX)qae=-n%?Z{#}P?W(J`d`bTOv|82$q4_C1N&9!{GEW>aq0vfz z&VXTnu_l$gj#zF{BIKLiV*zNiP**WJw&GQB*H7MWmh(oZFpehCVjVf#FZ{>~gZh_1 zl~r$>kc7s&Pxt`1m|Wja6wwMOr}Hg81SLlW`bxJ}45E5xNLa1*K@Ly4Ps7Xr@Z>v} zZu8slZ{SKQA0j~_`dF#7QT|5ISZ0yN{pYfKA_@(P@rA5wQ?1oY_9+vZg&^k5(VLSX znn-h`H)z`1paLlh^Bj84Tmt1EF3TWh(@M%n#AfuWv z_ce_nSl^|c3%frgyZLb7 zrwhYBZLAatZwVs^S$)aInyzL_r<=15xwZ`;#(#*`gAAY5e0;t1ve(7Z>=(|LI~>zs zc*!O(ZZck*;P4tOJ=KuXB5=t1!_L5&$Y1!1on%OVSI@0XQfc&Q=t_HUvM>82b%i)& zWZa4h)J!XaH^Q;=;aKVec|tEGC1|{X&|zwhN}{C~=P6~z5k|2n6ZDI=A)x+fJS@p= zulzJKy-i3&Q6ZuXaBoJnst{{KH>;g}6R4s-1y?dY z8RW6g#mZE5M`2f9Bn`OXhq>!dh}GuI2BL5{19Zu5R3f3EtGE$Vh0y|Kmm%X-_s~eK zQGYLp^QGKTmr$$It>GHZTvyxeg*07^cit2V)yT6{KcR*_of7;`(!_e61O_zw{-;~{ zU(F^}-Rg0R!#AE48Hn79@pe@+R5%9F2`N!KYn{~`jettK8yf02YXk!oVTwcms>&__ z3R_3EUwD}WLE<(6sEP?bHnB-%`N2LN{jWDz|NL}Sb;|Z7z`Yh<6I+C5its*QfnT3G zqE^c!phKW3^N2i$CyFZM`5jR8nZ@PZt)=6`UnHb&JX}fuU8T$xc-aW=@q> zQY2JIY{3W;4j}t8;Tl+)_AnT}zGvcClns6!-pGdZ12{L)KD=!oH(Sw&9&It}fqm6P% zU`WPSaYf#u1f2rUPnKdgI2~SMe{s&tB;1er#})DC@MTQS)8ckowyG(~F-$bb+1zya zgb=%+XQ+cn681kBPgtgvq=rpwX!FhkPyPsQ%Zv40Fz2XXxaD931==Fox#whgEpLrw z6S2{TO=}bCYql#el2yxox+DQ1)60pfGjX)h+j)%wCu>`fkSSEpQGU@2$kX6^7a`?d z1~WV5Z4}vL+@iHItitB?y~yD#4@kefS+U43orc-)6sM4#a_)8SmSpjcDPiG`P z#PPzde#tviB46eme{?#)LhLq7E@kRLT0Nwg?2si&y(btTDlL-Bkco{R3E@x{5O}n- z6D&Pb&bLldeFmR|NJ5GHJX*AU)p}NY4?DtfM(pJbs;I_uW+JSp2UStJ3xDrzGsE^F zVC_$#r9d}%FMSu+637pgQb&C!0`sR`_v_a>G$bvxSza_MQpcJV-%E*xSL9Bbr9*~u z-}9%%en=od~%O z?_GFVhfy|~m=PxO19=Cg7lmG!!vDNn099CVsIb>U_~&uHYxfCEWj)TyrF22XD@BwA zLkADxZ8&hu`Hp zAjJscrTDS8ZR$c7nx}rqC_NG;IMyi$bYXStGxbBPcsvT*_qp*2@LXWfSisEPd8at3 ze}#e};f&}+2?BDjHmu73nE0^G)hDbx>NVfN{h{Y!zi`1^j@>6^y37@Z9I=L-J8`sm ztFhq|=;BhTPnMHTJeU5*PFH}oAm+ry3bXjCJ4CboGmw8=rve#I=|1ED)B+VGb`Hwz zOQW*4^D~JQ%)o_ae6`DJBv7U8Bikg;o1+SfthU zR!)n8IeLo{{JK&6hG4JFQ|_5UJPX;TX4oN1&U&lTT$VJWP7#hkbgah+xmao9PffMX z(>l=v$pev0yLm&}bSOmr@^}*xtPKC{$e@O0tCcFdlRN6$xF5%{1ubkwm7_XgFzk0? z5F)blENTyRtSMl#8|RMqcXAGUxaK_%M?v$r1js4&-@Cq&V)nRWS7aX4j(%h(beZ#) z@!KIcJp|_=#6{Woag@#0IxIt@pszfy;-BWIFT{5eky(z*_=a(RS?B&u`eO#+Kse2I zBd%u)l$u;TC2J>=trmee1_CRlVNv-vBu>eQPmYoCwd`^Gg<=UJ?P!m6OAnKVT(wJS z!*mihyVdCa8Dd9oERRKVpiBh=Aa+-%!&#+Yq?^{{z z%<{yKA#O?m#I%Y?Ra`_=+jgqzzS8tniONwvR+`9^-uRg1!z+9q!jIhH;_EXmlFip* z2P~HvqKB^P4S8Q~=WTk%PMdKt1yvPsPTmTJo&Aex7mU3{XiLL7^;&J$qdKDGDM;Rx za|jC@J|1DBkT{%x!nwnjy1J%s@pO+MkG9LY!F<(dyf?+f*80g@@1Iuf0;k(kvrYdD z4Vys|2v&BF=hZu?y1xKzT_w$!+9#yTet3@fh#ANE*#wd0*=9FR!mdL|Jiokp6-`9v zwdb?yR#(1}YcM9~1eD?MPU0;j#RDu5^@@0+vqvtEj%;Si`H3LK`NMNxL%Wu|E$s!~ zANh1-uaSA(fO2KZ=*+*y(q;S>PuADGR@M4F?0Lg=6&I#Id{%2A`nO+bve7IQ z^3f86`XK68@dbi_23BVJ9CmkN#5eO2ef<|s1`=0+rIlec#C6SOl2_vp#}@JSP?%oe zMH#+BdB+mCnzc%2^;>+A*C4#qdv;@9EuG=X(L<6}_7kPVRYB^&;i0G}xYYh8LsU6S zmQv*l&MR_`X*!;HiY>hxd*dpAt43a@pq0izPi2~WX6ws%wY8fwc{c`~dRDUGNEX4J zC{SCF+RqN5JG}}rTj~K&o*084ZrGW9UEEQPPBbaE^YXfg#1X7QUMWl0x>ppJvk~ye z*N@G69rU1@z$Fq6wrU-YR!F5b$n2!>M0U7W2YZBOjy+_vL0cnH+XS#}f7+g44o|o(r%JTi5hB=Z%;MQ1%BJqgB?DG*_TF=RmgJwD-DNo^1D9pgW&$zd z6U)xKj7Nv3!+91v`#1FZ5V1|91D`l~Z(<<*O9q!ZF!AukrhiOp~ z-A4`>zCI!9WcPrSS-wDEa#LI)3rA#(p)O9cjsk^=b0oZTGYsiZqe#3!>b;XhlEl%< zJ3U!vMbBZ7fh_gp1kSIzrgQh*XZjoks@3QKE~PZ5$mpxUdM-+cZt5(L8sI1fV)V-{ zBYyo;VZb+`s`aZw7V73RQtzG1jZR$eGO-HT9sM_J`1X43GhWEXLU!jXzQVkQNo;lu z9-V`B7MW56%2D-+tTfrwl;VjJ-ARs-SY$Q6;qNW5Ova%o6G~L3F4396i{dBM#4*)p zH0}&qwxpCDa1fF@j&%PLBBA!YN0T07C6Tcz% zr5oU~r?`J&tdIZTPaA7m>`Vavef5%c+Ko1Imk%Ao(wbni*sS2`PA!^7+yD%|-)_NT ztFRxUWVLfohE0XFMiRI4xv z%qi$#%DakN;*XZqLR&NMH4m-zgR12zgj=te5VXPKI5bzOWSn z6DF@wWJFdZ*am`ckE2RwjssJ3tmT$Tp}8_ zYWWoPIvQFSqu}JgFIr|S{zBamF02*scgJ}KkVZ*B{sB2RMdZS*dHiH^Dx*lamebCD zAocC6zB3UA;Gn{hQ3*R-GIGqR%B5U?;uK)!C&zI0+0g4$?wa)lW$_Sl98#cH|F z{LC&cP?Ke%1DTRzO`cqggLdt$?$)2U<(qV0_;R?xY*ew(9Y|lLve9%!ec+p;Ip3+m z>vUB~0v7nELGHt2e}IkYSk!`AK1RsZHB%&k2-(n3@|pH6S8bu{H2;416uD=4^P}8L1f@l_NO;jkqQ| z3(j%!uY-QFW^DRSut_-31(ntp_>}AS612lptgzPBIiMe$iN(`sR}QM$lM-qBCyI{2%x+&z;r;JB4Pn zJk7nqrK1i=c!zf_R7* zbw>XP5+OjW2d+MEQ0NV_fF9JB79sZ&l#Z%8ke~WsTh3EyZ~|1+g9*m-)vzj7E6D+e z@h$ih;FAN$sb5RCsp7;Z%ar6?!k)RA)++LJ-B!nw4rK!G1i8kl(AicWs-Q$VY_JOD z^zhp8hRJODOIq7B_B@T7YGHH#W}9vL1*z6Mt%-k*W0|^RlEj0()r~U>14nr=8Et=6 zJnz8_v#Weq%NQYBEdJ0&#+QM4iIZ05vjf{tB*aP^$qq9zWhUx~@W(oyvl(3FR#_-- zPHk|_)MUv-@>Bja*5PK}OMN8twIGz)d4j|8qLnYOo4~#GkF@v(c;y;8g4r?I~dAr zbprkq%V`PhtL2b8*!d-%-P@+D)Voog3WYL$y@|7t1{|Xv2RHZ_p0NIrYr3I~QXDG1K!Oq$_879YawB26Lx(`l zis7i98Ygpnc}> z@ArJz81DzzWKM3Uz*jQ(gZzDChy>5`oI*kLL^2{~Ihjz9-jZVSKu&^0jwF@m`65Z3}aqii+Vd0En*4&D9`5aKOS^isIWGgn3IFhzr@x^Fo=}}i%OvUU7i4U zKu;4zmKx)VZv`n(?akd84zC5x48cErv|;l{{V?B04((5q;NHt{L&{oka)MeB76^84 zxL&(`7|;spYIR?DW?^+85hw_Xr_>Aq8gd87 zbx^YaTNkMTFDEWs-tu;Ce>1&Y@8bO;=FmO<1`ZC$=&_^3i#v)&8ROESxY~Qz0XJJt21=BK;$0O}$tS?>GY3-e zD<)3{o4*&6tq<--+6p_KH*LV5+1bCi)=uuJe_8WZz|OM4_UR_)gHaj#^IF*R#V3Yz zx?u5%i5zgYoAvJ#O|p-x@G6^{$(@iSgp% zps8XLG{r?7zL<)gt-yXxxvJmDyDIV zTWy{W@)p8by~FZiz6qD}*5Vc2+D=soW=_)Ch!QL)U>l6M%4`^fNC^_A)|!i&=)|6I zN4IGZQA8kZcVlg_;$QK61SLI9^sT zh;4xfzr-aD5uOGrKe2Aa$}cO7-c!xV5qe33AT?=CaqGa*!lI$J10hI&2(xUN(*xf_ z<%qby^fKH3v4!iC1US1y-fK9Meo?p`1vG4{Iih1rjHhQU#^I;HxP_l_Riu4BZR(lj zC;$Fj`P)DC#x(ReNy)Hxz+n4b$MdCwr$(CZQHhO+qQAXwryMcc7I`iA*o6#UFklS7Y45rHHWe0Yidg&P;fpe zYBoZy>DO54zlUs*@`loP0&lw09!KbDtb-SpP-$u)=5>!AU4)CiG~ej32Po^x_UxZs z>uAfdSbs7C@&Y?KR-Dy;k~-UKTq~LyHu;>7^xl05)U9Wqjdztp%16nXu9h=mq2=ZB>(DFPM|nbmZoU@Nu85%(#ANKt{y->^H1op{@B2S?i8}TW{Kx^bA8yp86f zRgvYOsqkPd2_Io}xOLWG%eZE;CDMDYWXZc55#k#uYy+MSOr&-!63m0AS9;UfQUPFb zS?y?{2tKmy1dwlsD?)EM#ImKZ0Xd$j=N7)Obm+RZVcAR53KW02&kQg#v`nX~@l_(M zWuO}3{1B~q(Jk#4oo!@I*YP=E$qMh3?V6RoIB1?p?peU+-|QMf%h`NXn!Nium1*-l zd#Yr$#;$S*fnt&!?D-D>a%s$#E$p!<#?~;M&JIf55LfrM>tLSb(r7mBAO>X12f$#w zHZbzB;qGm0mfKEcVRbx^WX@CSFr)VDlpY`w3|{_t9x;;sCN1$b8*$F^8^EX0;Bh?! z-^R|e&^U=N-6E;3wcj>?Y154VkMm%F1H1;FLzA+DFu@8yvAn!YXwQDY!s3-wdt4hf znngsIRSRtfik4FaOoJ>;kY%8fE1C3>-6+l8qKkUFft3|N9$xyF{m4j_v^^39rwUhx z9|6IB4G3f+00PEmfMAOpp@#eSlrx4HCpzX<@wVW=p*B94aLv4*4{AolBU?Rpl$4Zn zej5{1~WRpH4m0g2;d*fem&vHE<)(mgxS&jIaEb3_3)0KSE1X2iRZ; zqy#&!d}V&TUZ0!os8&EXs}&6TahxAg1%JQR^)L!Zw)~0LD|>)vB4fHHo(vd9O!qYB zfXMuI9R+VXa+<#Bhs+o)#Ol#?bD$ho1U*)n{H%gOgV^x|Katj*>HX zW#`=J;*fZ$FB!LWHnzcUTUK#VQJYoSz9_W+(mqb%kt|=Y&=wA=^AqJbHr39^cUEsv zjdKiK3-Sfr%#u-df@O)VY_Eg2um&c}R#^>i6qJlbhQe%99FS(N^crn7|H?lZ#LQ)^ zjdSRr7>J+>T?C#3Jt`iKZs5@ehzzqJjt#c&sA%fu9~(LDIvW`h@f>&eu4w1yZYpc1 zYWC5C_ug?>{X&TWwwX5A1pECCMzDdBVc|yS03bR0WL*1ip@He68(&IMwQ$9gU6M+m znz6)JDU;!Skmrl4ohmx+6lWNP^vzch8d#hbd&^Cdt{P#DU1B!kG1D?v;rgC%cYaO^ z(0d$%0z;uLV21LS-pn}N`}_XXmyk?oyFS4-n{*eC{UXt0L<@tZV}?1&NsLm%$on2f zw%;Td7!~?C$3JtkK^M==-~Y7b#FpleoCn8CXmlpDoy~`TP^GVFB-@FrL1_`V45T#3RVe_ns~~$^c#ZUgQ#-6aRhUOv$_heT2}`rf7%dfinYLpx#NW0VLBr_ zq&Z07xSj+z(6gfc1@!cjMIEvHP$q)GolE)u<-HGkR7cK+%D|Y+n+iHnCP&a}m8DBp z2|DO3KtU_m#PN$!-@eFFHSCYvVZLR=x>ROj+zxG-`xkY+68fO$^tBPk`p8iG93_DG zz_i)8%ZiXKvo|H-ZT2Yl?Vep?jf>9`arq<@zT!|P>uj();Pxkod%1-`9PY9D?6>0{ zZxa6-?6Lq9C$+2{K7XNOgFu~vA20FbLHc$Z>{FvNE-ZA$`4SaNASS=tP4*T(!_!+l zrI;r8K(2mIgrWoy#F5IkS%ii5e7+a}5Qz*ZvbOUkJ*nXgJ>-J;)9o&Lyw(66?5S5NZ{a~FQw*RT9wF5Qpe1nF@SMWGh zTnx4g<06FhW9JXNbw-V+Wh;SC$A}KWTn;ZgRTQ5Y4-t#SQe;c-v zI2=H=n6YQquob+0Z0?2sfUn(aN^TQ4k$)NmkQs2AtnSJwf@;S!xJFU&N7JHv z*y!u5UJEBl;M#yQk%edic^nPebTj`{Y`8R!6D34O(mL3!5%_K&I1pI!-q(1o<)M*2 z4JEWfUKJ7`J$KSWP8`yFNg2SBdGTz;}T1eCkk%2M{}fJ9PRs)M&q@o2N`cE-CNQUFSjg~`koN~EE5nUG2@bHMM~ zJ%DdewUMEiNbiGQOvPk%(aY^7lgy64mq-qRD_j-?MfwtXT=QTuDu;}UzrhO?iQiwl zZR4v_GF&cjwluK7JSp0kU0b9PUv74vtQLyX7}zf`SWw0>=1iRk-(%cX6&MfFV*J3x zA8CEPIfY$C+2}$0MK@s`$I*o7X%V^;2rdipYywi&sHI6Y=I$d&{~`MD4AzWsBmTt) z=EebpVvrhnct(Er+K#i+3_Uh6hVYB-VfWMlAiXFtN-nrX6jAHrt4UFRN+4|xl|W-B zDq(Ouc}^XiUw4Fv;338=SUwbnNsz81GtphlM)(;Dd&FG3JW?fxLyLY>7}ipuaf-S# zRYxd)R#FwFWTv4QX8Ak)wJA-<4GlRkZ)ZaI+gg_K~le?5=>$Rzeax4-y1QBBGdFTGg4dfwFxlx~7fW6i= zbxfLG>kWF&$vsU{ivX!s^vO9pa95aT@{ zXA8_;pb{K6>Z74HAlV1`DMPb+peFnoO?_tWf2In|XtH^qQFz8W{|?WCQW1xOVgs*l zQ&TN^V>PbumjIbmt)$%e7;VRd#wQlSlV62#@zWyeX;`YpxbS$w-NHX&;(USpX`4Eh z1W%V)lK!jf;QDhYDN&t=5CGw}or7~Tw)z!9=gq{436Q(5yq4+L$N_GarJ7;6Rf701 z9&;pBaX*Lw*RF+TmC5uqVbrPc-;&^UjA61I28`X(gyB{K2GMq8OI76lZgCI9rXXq% zhk}wEZ88havI*i?!OOg_{UDl&qUSCvOty9Cjz)ShL9t%V#-zEWc|P+ziGY`aolnU= zaC)4~vmenC1QaPoeF!#2Tl^t!i<~u^$zoaG7Io2uNaTg7@y)6Kw%kv@ukN579_qTj5NJ~buIE8F!YIU?fv9hJKNn0zeF_1nT zjiGQ6vqQn!{x1;lamf%UeSUyVIokgakfxASbT=S74t>MWEJYG8Hu8m<=T@s+YJv@f z@k|n6)Efv&YPwsR-y~X74VepBdwgGNI7!?fSV+&b8Feva9t7OxSw!S^xPC`z;v0x0 z_>z1siR-_=h%J)#(`FE4^${<7uXu9PThx`_aF#Oo#)HPiPQ_?j3FNzoynJrgn-{+g z4M?w3BSuL`H{|u$t^r&$E#(Eo5i0@RnHTHynS<}fXCj3yW|ksc{>ewut1!v@bP(|e zK_qtlblBb1MwWKoVPE5RFxjtTCV2tQd=#tm640H*t$r^?jf=JLrcf=;NotSA(M`+F z|C5h)gk#cvQ^nT)t+mj#Zla17^cRDyX2IMxt!9ls)o4k+(6vPIJ7k>d$W&?8RtFikNqkT?rhAO(z(;|4B_$NzYqi$gMDx#% zT~!Sko{|JV#SU_=@m_sY4qvj_mAQrcW0BK}Lgoc%r`^!KCbnnjgo=#F$h#^40&U0~ zJWP?Zeh6~7g4Nn9JRCw$rUmf33guUYg)I*e{njjzyNmqvDS_K($I}cC=_Ara%Pgdf zuQP-4?q|cHM3!+BEjQe|ofz-Dlr9>z;YB?8GX*yEB@sEPs~1yJI|KIV{WDVuzRJxi zpLy*HLy#V!eM-R)tuD-6pjLSCM+P>B1nf#3C%NGdlufVUdB@^{s71_cYXrzv!(>+C zFz?TVqDn)Z<&DffZZucbX8II>A(elWPV?U(LpyvV)I$Q+<&+}y2D;Irjvl}vfr+`; z0>bwBvvKCRiZr3${+Ia))*o?Uyvy}zdie7t8ElcYLKdsU8fd-V`=BfSxf!RFzgD#bw@OfiMRPA7r-^EE`)!Cwmldc>>+C$=zbF)7 z?T|c6X?Q(!>dgv(pS#mgKkjsYhr(L@aCRJ?FTV4Gk zVmPbDV{fXnr7>Bb**O^^bD?gpr~GnfjUjNj8NW;}92$=VGAX?29O;1KvhXM`%Xd*W z73`3y7&G9kL-z|NM)}OOK{?N6abmtj5GSA=JHNLX-Ag|G^xJ@9RooA_5on2iL9%Fz zln&*8-qZf`nLpCDQx>%gp;=iJUz1lHB1L<7;9v5Q1^Xzg)jTej9gqZn7>89sBv51A zZ0mIU?_db2S=!uC@|>)A_)j)jj?4^Mxi4c{s~{gA^?QEh%^lhK^*X%giGtx2OT*C0 zC73@`vv0={X>DIGGlO7f@g75B-$yCjAH(z75z7@6&+o?o9<9LVw_?tLF7Bjg54y+` zl8`nT`j*mi?%Jr1!s(?hk^q%Bp8DHKIE-2?p zx4|k+FY32pA>aB2jM9x&GiWnYMj6JCPh<9~6=ETtRLw1V)`^KJ#@k}Va?n&cje$Zs z=A~&dpWS&&2Js&-Oyofw#WF3qN)D?!gbSGlS%8{0WBsGb!vrZfkn8ILPD}-Dk?hen zc5ER|74iNC2}PNQIoyJcM9~&?mMHxIPWp3FEx*e)Q^MV?B zK?vC^5viEXojRfUCur46%C5dS#jN%=}saV$wQSM58PaJHVyCdpQ#DIHkjV#fncEbV|sj6zjv37 zpCUadapzLp-KNgnu1TM)4Vh190*Zf=>N-cb2{-2w0wTRp2EmzllyC zwa!J<%#AjCI=(e*U&?HY1~9oPE%x;vdg_anWLjmzXK6s5V*ubO zfFghRu=a9tY~5hs=rP$ymkS|T7RvavMf@2Be!;UW*|mn*&>>-ialb$DCsK8Xl`xL= z?1{aSvt0SS@H=9av%mm@h9UyB{s1x;nN{)GujFKV-IHJNE0C_(nik#y?mAtaR3Auf zL6rV~5&T2?sDb+X0$0tYTb8ohlI}g#?+WUN~(; zkKShkFn3eb0KXzn43v5j5s`!`nUEXfWHA zjKDyeBmg%G&m)odoNQ9=^&k$|AOPD3C+v93kD)ijoB$^FsCaX?H_)_S3`^}SVT!J^ zlFlX(6U#w~e0jsclc3obUt=B1lrH1{q*uw`WO{tZ0t*CDQmVhN&9d zkW6&M9fH$yKAlU|IhtVGMh_Ohtca(nf{E|=&@QUUuJtew*r;y@F#tvQf`{Da>asr= zyW!D)_GGfc4R>coJ{|SrveDPu;(ldJpCtqKZ=4rPihPpf=xBS!;nWm)Z;#Dj&#fF# zYae`XOT;A!7`M%1&%359KUR@r=!uVp{&{2c|E`<|($Y|!2h95twIYLFK16nvBZ`19 z=EeX^?E5$}m>S+nuh*Z!sE+4CAo10-9dBu*zpKB|Np2LBATieTs+4Y~L<%F|_=*f8;GvIVaV(2LmFN|v(SlT*yt0+rt@EFVQeM?wxg^3DG zT}Wg|sy7Gd(3~mw3iWrzxWj@DnQ)rfJUBE4qQ;KaiD%15`AyXXo~)v*iA}ewMKYNL z_+uH_ArXVR?L}L%Tv>nGfqSQaNBS3p|HqBhuC+jb-TQZRn^)b!GCQ$aYd(s*F zpDX$Ff(+0hw4)ksp^yFG0Q+(x?m9B0WiE|rO(6t;{j68Y92WkSx(#F;up;M=^;d3X zNeixY-Xo0s$3S=ZUD^!GCQgi^O?xQ-4I z)!+ZimaA$3o+#?NsN@JAtpJswQD&hO@3c%6e<$(&VpE&^<+mwcQz!jKtZ6;4j&^HQ z`&{D5L{BDLNqm}8jJ%u2j$d1kBSrYN_kQQ`i@JQSd=Rd*HXTPsjvkg~gPUP=^N22c zRXH#jCw}O3XI&&$%yBKRJPFV3^1azjtUehddeXNnXW^N*bJ^C39iVvR(}0q5>qck= zWm3xVhm;E@aXZ6~4yA#xO$zH(aaIxQT%=qP(twK9o~)}<{v%ZydKDXXcSD9Ty(l?q zMmK5^P5-z0elR^qT%qUT<`mvc%KWJRIa4G*i$ldNljYwp%^-e1uQEgkH~#xJm+?gv zc+t{-XIhD?8U&=$h{1eEiv}X47VEup+Gq;VyWBOd8Md2OOco%cvw)b7u#o_1aUA2%0$yO3(G*vOsNoXvmW zVy|O80vv0@&tW%}bm>;3PPoN!=N~a>V5|NDTu2_C$Rhxn#c&i`|3ft~yec>V=YwGu z#Lz=XrFQh&X~Fq_WNQ6w&lH97-zzbAQc_Cdi`n=e&-Z$LC(Z`AGR~3TZO#2PnjpV*X?ARQRmE!}GoHM2I2vkp3b-dgq z)HsfT6=>}Q;rNJ6f5ru1CV`~q^sDc!h)7>~cW;;Fp=v2VV=Ch>)Os&|-yxYmyDf1! zML`RBWfgGo%(A*(w-!Ca4JvpowDQrq;4z5leiBW+_?>wqVBL4QtdwIZCr@#?Z!%gS zTmi4~C2=B4G8_$ryOY}e+p9rnVsC~R4&!HJPs7)5)iXBwds&gLr@NBi{QCCM5k4jR z7VPD+io$$mi>G78Pw93B!I^7C!{Ui*zR<5#!$IwT9(lx9Z^b8Bd-N4m`Eu3qItR9~ z%~y@~@Mm=dp0!i12_gg9zkPHqt?wlPDKlv!xlMk z;rB1(0fl5g4OOII2Wy(6D<2zoRD8WNRkP&C5v`=H-1hl4y(UACFE3M%lHg;zoic;{ z{b@A}56+kZ{DDKIwlv1De;6s>J)ff03ov^Glwv-i#@f)LZOFSPwUEJ88)Z^Y4v=w( zLkZq{-Q;5AP4~B}q^xmioI1RdkPMS^Al_w-Ge5pdZ;H^jwyvkfT}1uk1;q-MV7mOM|)UajI=>l(TjZn6*0bKfcBAW=DzK}L+T}bCZMtm6UcEJO^mqiu0 zGyXWKhM1ZeGx_}t7pPZE6kCB`p}^A#g|!WnXnVc8*-Gl!GS;Poo2gi!li{WLlyL_m?59RSaTYvV`m5Kl7|!} zaQB6p(jip5r#H`mJ;)Lu-l|n{&6f{s)KI@bjl$$QVGOM)!da`vtQzq#K>6s}BVaN+ z^8otZQzg5%;jm0u`Z4yGU)^c|U(Yk-Hh59>vc}swC<*@4J|h|FfU|2CYW=#TDpqTS z0z$g!dx^A>(-U~~d%xAGUZ`x#;Y$#J#vbe*5`>bB(9!|)07RIeG21NKiIj@CwmaSa zF0&1C))Sr*f>WO?WZ_M`opGQLLJ~?-YYU>v{w<-9Y^d)MShEO|rxor)p!H1%3W9db zLCQPepWcUWfIgLmmcpOodMe`q4h8Sl$*)hD+_~;nI$RO+ZvGP2a#L-*^}mJIAe5RZ zyHt%Bm{ErEDU7=|+9{*ZRwR={H}@rliQPSq@@R^|KhwRei(0WB=60dSI~tN!yAX4y zW`KKnXfia|QeZ5u3~O-3e(7T~XKvlsCHYMGfRuW9 zD1x+u=^xjvFaZ|IO(i|q!`-#FlTUYauOQb-9XiI>ivhia?t2;Hn=;7wk&bBcV>IJp zNf$W@NCuG&Kb;lD@sR9nrVruq$ngpoZS`(JL#P2oV`m_DK}1PynA&eIMq#BH2IaV# zBSwH!BSC6{6t4nvt5M#98~@815tTzOYMGr6O^43{fW*zsMp2PTUFbC8ue$*#f;{dX zm|(cA#I~NG=ZieHZY>)eEm4_2#0nuUyvElCfP7&FteYGQzIXS?pnX=4(9Ck1ZMqKb zAJzLz=+s_nP{X|yV-chzxafBJ{0O64ATgWuf)}zCd1i17zcC%<)1qZ))5qO9I7lp| z+;>?;ywmvSmXmx(DBds)giGylOeigy&%L%Ro&_>dLBBZx{8OtxV@{?4%UusCg6Rnv z*tuHti%lQ)xJ}Ly+6~WB9twY;b<2E@()224hB5Q*&A@Xtp!FHSH&J@l9@IiARaj0^4)DNH z`G~xxjg8B%a15_9Tn{1JpCZ~w`-3pEam!}2`)9fP#I4I@BB%?9Dh(?I)05m;nj`>Sp#ACc zc6c6>5I7*IX&5Vw0^|6XY;ZhH&18|U_wIz_leX!`(W>sJ^2Fv15 z8>IfN;QZKHKrS^S#&=enx5({;r1slGY3P!}zmsne^uRg5Ko%w6#a4FznWj$ly57!? z%fdpyGIH<61`Y1wD+$l>^^y7cX#^Y}A#d_(jEMfmZlsz-+rTnlmqQ9b0yc-7gI#87 z91YuLgacWciwnzcI$c@Hr8$>}fdt`jvK1?91xwkVy_yYa-(tuP*hmu5GWR4L9m1Y^T zZJ`a0oMP(d1-IaNO8I{F=Q`o&bz_`+ckxu>n;Q-uYYp1t(u|n(zCK_$QangWrQKj^ zAV}A+Rfvb1v%z5#AVYHbosqDiArl93Kf`qb4hocK`q&>|&x_G!=aN`7xP*gXBye+j z-Sg!zgQ3Se`<*4-6|u?ME@bbU==L7LY(5?`(SX^pJhW3lBYFiH0)X4QXG;2w?`grp z<1>Mn(@Z_9HfdXZO)CTZAj}Q&vwmn(EHuaqP&oDk#L*r}YZGFN3a+fJ8=v*M<~66`JN2o}H^0rY6q}xmz&Hzde=uwajw9`5)+Kcn7N*-e{~Qn(X?JAJJUvS*7P~|?aBu0t=)ay~G5ka#$GH*t!rkc6 zXAM46h_Y8n#IVj|>>e3+>8o!i46vEtBzT6TgGPvzaQURkDL3)ivMt3eFz)&n}NTRrMLI!$J13JIM?x) z5u;P4C!y3P+is$c9m+)d<+pf|MYj|@t-%J|OwfZ}s;MRayc*rsYjJd3x0LlwrB?T- zyA>yf`poRyK`n0~A2U_WT;u2>3$2@0!(Np9oAA(UugYsRX2bc7%ncDRj=M;> z84}t5a?ThJ-v04Az!1U-nHZeX|LS> z!_7!}v{kY)d6|ULzDI$+C4xV=A!qm8dooJGgvT7c!tTmP5K^DqA&6dY8o3jHNr?{6 zx>qnEQ5$zmWpCMSWZlPtnTm!2K`elQi3M<=ESww0-ofpj<3N%0w!|Y7q|p)n<+oph zsQ>X2*ONq^Vh$l?I;lg@g~e50*z}_|GY03S<{(sNFywrxAzl6DToHEPS|2>nTAY!g z-YioE>{`Fj9HgE{ecMpkOP-vn=P_|{l>0OAgH3jB|EejOrNIZ#Lz4;s5L{fu9=%}| zq=)Q~;J5k{kA8ko!VVJ5)qW`v4hN~%4Yj7lEo2D8amtLaUUn92y8sK# z19I=p7?Uj7lukby;sd8N+bA}S6yGj9!jmmk0tGIUb^fh2fY9fVO zj9)wFBbaVN_$|YyY`i-R-WKUkT=jRA5PI~nzV1(LVt|U@SaPZGI6p?-VD*QbXZnoU z8iaQrB}ckb6MkZbGQVUR1L!X6uW_vm074gv^1=-U0o#-J@4pre6hIGkNlR>Yh4zWgHfv=7LUyxshBO1F-8wkqK=%IVWtK*YSAi6dwkY>S@J2J2sOi(Z2O zW=+8|i--^srG8qLKv~a4g{FvLF%o5`Lv`E7qaVY>oD*Ze+g#P=gDAXt4uci5A8z*WPboXB%Qql zwPIvPy~}>v!}j14KVsp38Uk5|I14?(C;_#tBgnuO#hQ$WM>c>tdrMy8O~{;P=E2R^ z8KmH?#HDD78;83n)!KF!3{y9aeR~o7${S3ymITsoXEof41jN2Lie$(e^mAZ-9mwCN zHNSor$qlX68dH%7Vr$BAG)Y_t{o)K|I=?}BJO#Xv!Y5(#d0i2FXQ(Y1nH9?-h(fN? zr33WoKjhzM@1ZSZkK7L=vF!Df?PCboWw8QM^WB{)l9F3+k1EYA=F2pctksu%@`6=8-#Sbjt2%Se(juDUpA z2x`{hLxfW-qK>2`D~cm=m%DY^=B6U31K3vv^4qEdDasyERe2to5eyh(U8EljYk^tQ%r~kFz$K0jSk&ty zog`o?NCKNzVr*QQ7dF&CHHU&QHvrwE+jK4n61~oU<*)!-TlaV$!u*sz z0Zz-}cCD3up$FyjMWzhNt-TP;G-#BkDC{#k7wE2zXBh-DDfm)dt9t(cnqmVC7;}Mu zV2@t$x;2_>?GN1$B|=cQ_5ec7prm;dzqky1Q^0%cKv;~dz2i^>UaU*PWEkZb`BwD^ zLDsyR=NQBqSS^bv>Lws!-;9AXcU?VX#Mq6-3Xm|>z(2GI8{a#Bv?ORNWGG0i0r?Td zIiZ3PTzS~x-PCm2sm`;nkT~A(M1^;LmXp(Lv`~fa0nGz1iAsYv*WoZYKuXz^0bNvm zd0*VDoY?sH0;{cGdiEEK)5z>6RvbYd&odVv48Ouf9|_EzOENYLS&9X%#(zgU*b($E zETJaY@;_e^*gvUJQvT2SB@7}oM0Lk02h89>oGedlel0sOXDRh-{Tm_i+r-yRi2) z!khx}gW5|tzWm|?x8!W&5Bf34pLZN;mi2zV;)HV9+1~u$nMY<-%y`JKx#KwKd&<8( z^;Qr+#Z1zVj%147O`v0Hb|N=^>s79(ob2AH$f2$IIf9${eXIe(zjm=ph!J)Ys`(0> z#*B%V#m!1GNlyrYgwJJRu?RjpOh-MI>%FOY;z(kcz(c8J;cW7%aT#1?jIh%Q&A$w*;io^9w%IKWU+?v`aC*Ur%J#M@@Y_2YtCNgND z3@x>2olU1O0~2$zQ0V%n|7IRsDhA*9DZ8puRpn=gqK| zDfwI-e#?%BaPjPtby+iQyqU;8M1aiz%m}kf4%birZZmdBI{hp;41a+LAmpy}5g zyOKpF&Cu8t?`{%dXBb{(HEqy(VkE5No^kKc-?!bdR-oUVW4W(Cx(kC+w~{E@ z%Z5%4B~AJ11lzWpY1zVHqL{lUgZ`^5>!U7+LO-A|DFA;0|Rf$NWVno2~gA}!G4AFogjzuMlQ`a6gg<(fNw-0%x9 zfzn&mh}E?OF~KXlg$Y)f)V0qkLWg-F7_ztE4tctMsbI-#w(s{JC-(dK1m#cUIbahX zps@L;uXQ!!mv-2#5gM45VDat9U(D={9@&fF!zp<&2-n5qh3YY2t)(KAtj>MxW)K=5 zUk0F$KIigTy#+m$;i5Y18|NKO@r%M)1)p_T=*309c~B+;%15#q5oeeEI*&B!#1EwQl*6nYaqdlK(jk8E5)3t+#qu6pwrrfG2)Ynn+4=T5u6Bphzg} zNQ)42HlXi-b>u~^#U+<|(Y^s1jT|=Q9rwKfmMtcC^+$hunA!Q9N>yFELmpEW!1@mx zW%8;Ct3p*m;CMjNi`q=j(5Rd_1bW8>3}x$OAq{nNsJ$O4+QzB%7t@rDgC@h(=4dA% zL`PJ`X^R$Ko;Q-6U=y4FM>IU*f%)L~dP)SLn7{UAjN zgdz6hC5A-2wCogv#sy5nriKqz+laocoi>FB#1Xx{uLRyVw}SAEPh!sJi1@Rl>^N7= zip2b)*``*%Te+G&kAMfOj>;sl{O~+lm=dIk?9{C`HzP6GcOJ4ho1L8IyAIx@*+dHwKb5U5r!yrt0?EO^Nq!^mUfuVThd*)!NZW8t? zS6)Z{fEnQz&BREUr2-i@mg7E+HW`O&oK)F|T$9 zeA=MEJGg`YFO{4aXD)Y5{#!T_X~_WV9`9Ekc9jqfE+kAqf0G&FUN*%9s*c|T+_&r$J~}9n)8`&K6k!w!P6BtlXfQcTrJ`K&Bw^6rO8}GQCDGuEGP5cZry}0>XO=n zI!7YmY6>KHTN;L^lhAu)%c%#1-;RY%+&kz`Xug8E6C^(WpR7pmhLQxUWRwaQ3;PO* zv-M>VUQ2##VBqN#O|ey%o*%SvI=z9%Ww5ie;#MBg`Hq+QhIebfQmNsXj;ob$>MxN% z;i86VM5$SkQ~hX_`Qk5>xBEEMs9{x^xhBN{zE z;jdKqsSYKe;vW@qi${T^NHoh@tX0K_vVf1ywpReS>JlW*_gdXrSpCciZ?W%&P{Xg$ z1rIy>Y7g7Y)NvXlh?_PZReSLQICpmaAH!sGVh9CkZ8b9oz(&$%O2b*B@K>NmQx?GeK_G=iU#tK_f{7OC?vZ~UThhUngETQGTEE@FaAE%&{AX=haANl&3FfL1S2y=pfZF|n{U{8-E9>6g04)}`C3 z|B&y(L>DR%ILcN7hkQk6^x&Usuc@Cl4F~3tRcZiGd2X(X=8uf;8lFxIP2h&4uvkvc z)wZRHLq zq;9f}!eiK7Q6t`^r|8(WGv)3qt=e`^7M$H}2*(?Q3yv=a$+%YGBm4&fE!||bFdOSMIh9h7Ydnw=^Dps`gsie@gq? z^+Z0Nc^cdeeplkR3jN!~_-$!N{9L5JSw`fmVp780KYXtLsAluYW`5)b8o#^q|9qXz zv-=tMe_Q^8PGCrg&>I3aaVd8Ug*E{HnNdY3Mg==i>s6b)n-qlycp#Dy(Vpm;rYf45x+V_111dDM;tinG>CjJGCil zD8X_!U88c)PW|cX+R_6}L1r-CF65Zmq?BxjstK2m0f9q`uL;^>4%%@TF`_a%VNFS0 zbAT^)AfmGoK;aeB2@w!TV#n!vc08aKqlhY`ZT{l#Mx3KxGmpak2{uo_p*_H^U(SsH z-0KtCHMjq1+2VHm*H{=5Vy&u>MY``a4nsm>w3pRH7-1qppht^Xx!iQcBpFc>yn%P{ zF;n1srXxD0WJc=xlmqa+#gSD0KM5WoYsDmJU>Y1*zP{cA05BxTa4{QPhgt?@+r|<6 zYyFE-dB$=pF@?vwLcA0xUUHbg>wDr<6T(R)0u;4Pz%WQbtK;EVgX7zCbDOJAo)fKgF>t7N6E zC>f~rqBH3lIg5XIapJ~i?5PIpF|Bq_VcWMHzglO(_5hS{33k5|rP@rivW)ZY1rfeq zo-1>srfo|~z66pF$e($Y|3UYidar!vGpcL~=lJSeHB zd*YdvIxGo@d;Fogt$x8c4H+u@0l>WXVYwqyFnMto61+ zmgufTWc^TBA8{2D39K-Jn{cXpvCAZoksEXv^&W2IOa#W@yqG6Ccmp5cL#DvtaAXRh z2q%K8Xo{Ta_;~!GSL|b6VeaGX`(*iR>KTXGW?KvNcqS60qsgD=83&k~;8^JoDKCU~ zNa2y(66gWnXiTg+3*f3OKIo9rX7p=Lsf-y%-TbNXt`6Nm1dumvSiNSf&T0Zc1!??u z5_rWyC!5x(IvPv+7`)at+CQQV{LAq$AyMZK`Gy1HGGRw+70;C(6Gm`X7AmG2$6>e=Si=DsH{^(B`v*j2`aeIU7DN(k zKssw6DqEndS6DFHwzK;c1w#^_4f((nP*9`HGb3#E%M>(Ahir`=^aSIH*MG2Eh3V{L zM8IdgB0}PFgo#!Px8iQeHs8iRQ`p|eH$wu1YiAoiMV~)9d>6rxB)QYvvd`8E9(xmR zrkkKf;o_5x<1AWi)&P}}J|;Ca+DZV&aoJiN7H}LM5~k^}nWWH?i}tDp!j7L;d_mYT z+#I$&V3w*H-W>~UQd=v2oUr8mMw+U`cj+dJKOO5#spkLThvlEFXMU1;aSs51AwhCw z-x6SDZ!G^{IS**ebBe33No(WLv)~Azvr$!IWKVRlY(((|0StHyoNaD31J%CiY8|xo zuqy%t*dO!TFxsg^f27XJx?4I*bs<{bdUeXJ{in-BvJ+eYC+bDDyqef;|2>d_vRqE! z1!qVW=P3o5rpCTF$8EOAS6|EWk~6mEfgAq7+aw6CYUBjQWzdB3{zHsUW4t3)e2J`8 z^ST^w?zXJ|y1SKy;P3S<(6#90gP73o5nxOZgk(qv=w{EY7|O2{7$|pRUYi(c_37lT z&3G4YnWG^^uJ;nJ!vX$Ds4Xs%k|arzBuTt=Smntd)XJ)dQTq>TSO38<#_hqNuphbq ze*=*u(WBmwROf#JRSK&0i1jas8nQGGU8O3js;a80s;Z~1Xz8!{FvdH0Fi_x$-~a#s z3?w9xc&}k4T!{5Ah#Imq4_&1ys;a80s;a7|u4w6J2ciK+XWMmHy_v`?AQ`O;Mjk(# zc?pCZn?0Qms$xs_nXFQgW$_kt2R;6*M4x5JG5=l)W277E||`zAFuSoXx08cFAUh~7A+<6(SF+ctjo*YB>yI+T9 zoqVe#cG8KP{u`!7NX?1CIog(cA4kjDtn{a^ETF96TEn>dL) z4^!(uEHwDsE0bp5@0KBHZ;`L%#vPUXB4?ZJ`~NZ9h``s?+_ckl#@aOIoBtgRDB+V! zn%(QRD#UY@hHQSH_1MZ2ziG6u|5I1|%PIU`d#{R&Bq1IK?*B=9L@4<@xkGGlU&c?Y zQefs{xkPAvt9YA;j6?$l%e@>sZaVdUfay4Sw-B9sCoz$l^`lV2ef-*$A1EqE^}+fR zxTR;XKoFyUHnB$mKnOBAHN6iV7iy{|7{_c=>__Y5S#AmKfTl;!%)D% z(zyLbq8N!CKt9Xn&Gt}UTLHP^{3JPjy_&OBhB$+Uh@X-FNK4vh^2c@q?N1TiU2&k^ z+PNfMH7dHW=Mgh{lmQ)?K(tJ10-5cSU>*V%h^y}i1_s2llpTV>PKlPa+TYBwsD8)4;b>^NFLp(HWF>6Gi?g#rThAxi@c^IRv#H7bsFcE=srT4fZo zURav;25J+(?0z5^5U8NnJ+;gudVYY9i`W|X&YDsvNfQW*uD`}pJD22T&jhVd*pC$i)w97|z!*Pys zq7t=9{hw)o@F-oZQ@*&7lJk~AjxDE-Bt{mx4x`HVR-Ab&l@h4gWYIIGV1FzSOCp?D zggheucic=HR++Wn+5ql3pgyv(T@! zBAV`Fvk;fV^vc__A=pl4mUWR;=BIP@!TJ-jsM>)``C&P8i{D^%AAPMuk(x&CVH%GO zFD&;%8NKPU+m~aH53t+wY*lhj$5Q_o*R)FiDJH6Nl-=i^7lvnX^6QyxK)#0I1HQa> zj;eC|5(Mf;Ld0n%y-^L7tSR3&XA81eGMZx-b<%M%q^V#@wOEFM$e~njPCH z@^d`vHB!oLqzbDPLGwWnf3eAYK>OB3K;1=~ROlCAaF&i+N(I6H9CY;>jtv7d|9TJd zF^?v7?~~ZbT;`h%{5~4|h?N(W0ftqv{7H-WLBf~)`-~t`tf#G2^t+b#wxhUvla_er z@$jVT=k+&}3TAF!v$I6XnrC;=P5sb7_CMgA7|e`SOzWS;eBC!LqJ@wxVooMvIRsa} z523?RMU^=M-mjW3y>4?1dZjs-07-$y9!5^|H{e-cgl&I5Aux{a0EaI?lu%`ObvD@G z`w(Eut;4RvM%?>-;KHKjK~NI9OnFZvKR?zU_3;i07gxwH^^5G~dw7KN z&k_Ys0dk%w&=x=+Blw$5(T>VuVq zK*Yp}ixU%}2V-Iqec8Q8%p~@B4mNxVv}LQ>2+VtOA1>ny8-#a9y&X#C3=98yL^SLh z_(+KnG1DfLFdXpl1Cm{KoFRm~_Gw~GU#F*hY4UK_o+>2_I_NBdq!>DkrW2?TI}w*5 z!-@4eOXY@?A)6%qWu_{wSuR}Tv{D<#pgGQqx@j2?6_|{LV$$hcxo7;Zf=7Ea8C#{Z z5m+Rnxu9b1Sn1sq_tny91jmSKbvGe5ccFtus8Lm{3=9YQB^!wG8<))gdpLl9_3a~> z3&JvPu8P#SZ%1budL}w^Qq{sm6X?Z_|74cYGLWKV1E-5VnM6TclitT>^IIabC_YUZ?6V@dE7cIbSmLq++VXKA0~ib>ViQ?SE88* zmatLv+=#1P>fA)9dk7`~K zbu*w0^Luqg{WvEmrSAiG2<#D6-cA`M;%onA4Qd&^8!MEN{zJSm{I~MhVQU}kZH7}0 z^#=mKtySoP0KnNi?1a~LF0wid3 z|7)Fl&&$o@y(CcRwEfBVZSH-NN8Y_>t+V&q`|Oh<#cL`4nSwvBrO2A%)2wf#sDkr) z^^C_0#-vEm8_#|Ft@-$)6e&Jl^G1rSSZamM2DN^9YS3iH%E5N&HHQZU=-DfiW`}{p~bl8s1`dQB2um6wa9`UEV`gwH) zUR{Aiu7JiW37nIKy}gO&v{_%{`xCQ&;B`lq*zf4q9Q~gCbIv$I^G{M7fj{rn&#No& z|N07Oj1V)#_`hMPVeYVWu(am6D~^0VCG0KJ{(~cpzoYG6I8Vwnm+;8j$9eUm!k&wBOq>I(dAu0T@bo%ny_*L=l(PMO-ZAkKe+eFOU%_8II$*t@VS zunaJALmC))0?}a#@&nBUMxIFJz{nkmnLV%Yabg@}&lRt@0T=Am&;Q#ikQB!4U$^?t zoIhV{(!5Q{QspWwOPR`TIvx*u{q@&7!Ro&D+G|B&zv8*?VV}dY!Lq{s3wsAfUdUt` z`5`?_`w#RXXyglRM& zUpHru@$s*IUR{Bgb_JY`yQH4ijHKI+xat1IwaS3r-UX13F`rC4XXz( zs17R)D+0@n_veCThmjY)hS6WJa>Ivs`~fVhX__Y(U(lBzPtczrS7?6FK85xxv~S@o z264s<_VGk=d<9-7>-E#;j;=GGOU`}5+g|;=x&p}>E6Ev8*0HF^r#@<&^GK{)=RiIF zc~n01^jUK|4;eCKZ=O84!+y%Y%y#y@J=r1hht}03!G~{ zsEEg9VB~}X_^c8-DF@_*7&}aC+YiL3eZ=ZpF!cfPoH-}~SM zUfTvNZwJQ716yJ1Vas51VH5D0Cu|_BE36%?1&n-91ICzvd{7)#5LN)6mj^~(_ytB@ z_yP7U>`NGZhEHJRgAZXJz_P-uoM7)OIN}9+KSA?CayUU>D}C*(oRHl3!T$a{%RT0M zpLHDLSN_yL&-#7z`=9l?{n_4cKMsD+V=KmaZk?a3W0d2TW0+XC;+$C5`aCgDUzvCy zHe9~?`rBVFUAeZmef#!T3KuT?=+|61BwxW|abN#b;1pip`r2zLwgc}0rfD9aMz9Dr z12zu$_JH-rc{dz8!J5LFz{m&HU{zq{U?pKCU`6oR1!3d_#t%7RqVUNzPyOf`MsZ^$47FG_Sn|*lE%3stP|(NxE1Hr(TH^#F~B*J zn4k`qDSgH)9|!H;yWZD##hro$@;$E8xLsVgk;|pW*iCW27OQa%@9~Eb|2s_MIeB0M zZ1q3>@sFjz^E}uzJRS%0f(^jqKCmvZ*07eahOpXrttPB0&KWb5hn0en5Awlu-0&UF z$pv4*7&CkZ`xN#G>|}!UeOOl0G&ekpwqVZ-yk3rYL31eiRP(F$tMpNG1dsZD zS?{CuJUL^)`W|OvFDWe96ZQ5}9QhxO3^Z(}Gr~h{A_u;wqdgya~-I8;(Uhluc zxgICUIqEUS@n$tW4mH*}pX)i@igk^1Vx4m#@lKzau{GyRV&wy#kDD-g(Uq&$o{X8h z^~rSq`!O>E9!kH-0n&TCpA@Oo{4((BkM|M(fu`|X^8oe2Rlx8vZ~=XSIj~8vF|gry zzCWxVjBx^egEp|{u*R@DF!~3K7pN&RUMPb9i+%y)1ZoJ`Vbl=F2hh0u!tURyVutnQw5jhefF*A2W#?1|i zl8GCx1OHp4@1*Tgu3p#cZ~XJkK)g2q_*6%po3~WuzFJQ&H z#yNc?jc+TiHMV(ut$5bgUSpW|gJ!))yl478T8~yt@?76{>+y9wuT77C9{C)P&-26o zOaH$9%;))>#C}KpjsN}cf8YJ|(@($q_~Va%hUJFkh2=9XKaSc8;N0E{;<-QC3jNOi zQ~mx&AAMBFi5154$+aSQuePE%*H#SY%Ccq4RvcCWR?@UmIF|nK!w<{gSQf{!AAImZ zc~}KlMObCp`|rPB1y(I<)~q$(d+)vKg^QN#74|SX`bK!1EZOr!X6|?_b9Y6{m{k|0 z=eW(%f65N2)_h=?t9#lbZ@&4)(YM}u>xgNGaij&q4yH_*azAW$s#K{0Q>RWH;Ns%4 z3APco4z|+G&26c>yZih!Y0}J1moD9u^y$-2%#b0&n2Z@SdcuZg&YXE5tRJjbmMmF* zgLQiQ?YG;*+P(A6JFVV*_uXa~Q%zxwVGZ%O*N4?JtuDS#ZCEYSYT{TO-;-9&w5mAr zyYo6!GL6@T*N4}sJg!+eUN>Gt(|A2g<9hPi()6|Fb*J$j(2Cicd7t!s|g!G0tTam}&Va+VUgNy-MSU2?Z%B8k=L(ZR~8x? z8W|E25_$FN)yT`2FGpUycro(Ag$t4A&Yg=od-iP9>C>m9PM$m&b^Q48s3S*?L3 zM;$nDAZpK^-BCLNcRslk8Xg<*SS09dv`i0pEVBb2%N+OtUW-pi&#{}NA2^_8?@5w7 zZ-Kb%KYSOPBS#KpIdkTW)s`z)uGn9H{WUIc-n?-I3l@wmTC`|fnKEVKDpaTtTe))O z*cvrz#MZ7|JGN1yMzJkhwv6lCxpPeK-o0b`_3IZiY}l|EA0MBXapT6tOqw()diwO~ z(R1g{jb5-|LG-d^%VKeULCz*!-i--Kfma0+qOmT*s&ujFfcGGC@3gu_wL>J zE_^y9~Pz zi-bkN=x)ZmvgBQZxVX3&ZLzVjG1{V|qhq3?qGBQ=Bcq=@c@q8T(WB^x4+^ z_dFWoDH`J{8sje-V=)?IGa6$z8sj+z<2wdpJqF`G=Gd{LG3PGdio5$rv+$b2bUqHNsHIe&~J)^q31tt?NTJaIH)KY#xGafJ#M ziYr#ESX}ASrQ^z%FCSN}TD91kHEYJ!sZ&SczeS4{vF+Nmi|yI7XKere{bNUr7!eEn z$4;C$F?Qz6nX&Wc&5K>KWJ&Cb6)R%au3f8a^XAPl{{H?kzjR4cHe+zjlmTlo z5fKqFz>P9o(->Ua7+hmo^y9~mX)(C&y#DGw@Lte(U-XZ@SNi_(_y>&pSo|LkVYgwI zV25DafXD78{uzgm18j4(`TUDGDBqtS1itBn|93a+8VsXLjrTYl|JFaa$KuE^79=Ud zxDgu0kj*frL>tDMV9Y7QI20PjrC@vt#;XALf-x)@&w{Zn7~_I@N-*vP7!cq=fO~;Y zAi#?NHxe9tNRFR8Ct(jn?mmc>Z6~5EK0u_*@Q;u|Gj=OIu-~#~{sPBwRH&d7E?fv!MBocZsZyn+T)A>msZu4W zRjZcNuU}u9HESlVTep^u9Xkr*02w@Zuy}ZQ2*#@7fhkj_$gEkj1aW{YTC_+I2MFQ- z#RKpMWb4+giU)S>+9mM+1wMh|0r&(q9v}zs`;zkv4!9tfzyp`Th2+F*xGo{!$I#Fa zfnT8T&ugbS;6cPadHgU!o;P_%tiM&=ij?0VPSXVHgS)+ zi{qH{+Et9}%NXOF_s*ZcpfGR>csPFIgqkaZ4XSsYmT=@VuBVuL45zPCh z4nPi=w<}7noIWV-^(}O|0Li3kOP)ezRUizvhRP)B|rU~ zTM85?AkYKA1BDe2Kod|L06jqJ)vG6s8aI;W&6`Ub_yXO2`%U`67a#|CdXALQV@At_ z2@_;Ge1SQ0=E%JH^JV`01%lW>eBlqQTenU&z#o7nAlu;&1nk%WpCCy21pC1ShYkhH zVUr6^;5(nBPhfJvS>+d)J^{FZKEahM^a-xPUow1x5cmY!_MDKVn-0ku|D&=o@VIQ; zc|tY?os`YHPRZuor)A6TGs^t-oRO`2&%(~hmOW?X#JTI>fJljsdLj?$tC6p*z5zJl ziOCDfM=-~Q@)I!LqwGxi49OhNm_s7L1{(iIF+{ES*Z%*@&;ab`e;fXR-?*^5_b`6L z{}y%+*o(#-aN>NZRPW_0u7!M*xfS*ix5D1a+;Ma%I7;5i=Oxot?gwt~%k4Yx&l54e zr_sRG@@6g%aW6O$=BeyIxrfUSwWrF3EAUf+@oS;i*I0p{c4j%vp4(K~^n-r_sN>ZeJNB-gZzWmcYoBT6PHhB~F zx?47R|I_a!f4+Q@7ydy$9LWVm7!$x3s0crxa+NAly+%!`2VbC31$Mp5p1{C1b~pmx+@m%ap0pWX8-{GH32wnZID6EMBrymiw-hm8;jt z>b2`+-G+^_X^Wq1_1`Yrwj)M>Z$R6cuzxNc|VEcp`Gan06)Qm@Zq$yj8B{4>WuNu6h?xaA)%ZUtZkJzyhj=8kjsLZ0GM zV1#6=I8Hi^+9<&%FT+o}FZUlrsBs@=8UJ^~!QT=0Z5Dq2f!u@f81dj?0t?4u8lS)a z@KK@`@$gY3grYwk{)v5l=fAS?enmw-#yyLW$tw=XS2d@J8?gU&>G2jDhx2igrPMfa zE$A)1fs@CNAL9OFo(2adX7}$$NSTg{Bu$}F@^;D5Fnl)5z0gRh^4k)`o_FQ?&07+3 z{f3PLG!IiTCaJ5eI50|XK{Ri^zblK!zX|qYH zOrJ{5pMO=@*Z8L{P_PjEfa1kfJW!!RMX91Rfm%|hZe3~6upxW_azH!j)VYiF?9~g} zzyKLCY?$HzZ=caJe&QsVGQN zcx{{bg9Ek)?2sMs3&;Wc;O`tj%ySTWK|BW>SNK16@{|q#XK?L!4X<9k0)Hw*w(mJ6 zEr+a-_e+lw*Std|V^L4ZQqs$`Q8?1Pbb2ROpNl@F4_KVEl)|6XQ@&-vfJ4&cQ^|AwZh^nX+TOECZcmGK`A+($+l z_ixRPQ&OtqVo6hEw4^UK)`EM@0Vehfc}ufl>%iyG=D-(;;hwMEg)bj+^S0z|I!DsM zcQEj8Z~*b&VdVM*_$LPt`x^h8|1Vy?k^ujw;O}bu2Lt~HG5;C(--mf|x5EE6%$5E- zf+QsDiQK*)C)e-A%DxLxGSd@8Kj8jD_yFXA zk0i?nUjzGjBzJB@7v%h(H?QFX6jHH3aVb-#tds`_RH{@Nx?nA-3l3=9qzU{0#slr7 zL&r|iy+=>!3vFP~;34AS=_x+s0K^27rc9M-GiJ(k*lg1mAP+31R=8@ltbs2;9@qdL z*t*SMw(kg(o#4A&&%k~-dKsp9c z9Dq1rs^1+MJadoqhbE|a030xA#xAMe+EdN@ue-b_pMCu!@Sa!UKTjSbFF^b&9{_rQ z;sE*qrOQaU^5q3J0*D2m3Dm784I4F*Cdd!8Xwg#fK-X@DE--M=AQ?8?Lq>V~h|lOT zGGXFGnKEq}cwmOiMXrE6uwdaL_yWshg&7m9LR_$6<0kd}{I~B=zCa*+fuP-c5C;8wcQep2u~)7IF>R>UHqI326?0;Jq@K=MfKNEItzV$}tBR zJb?JHpqJu-Ze#r9FnIv;$s_oMR{Y!ZfED-F^Taqn`TsF7_Wu8?`9GPl2Yq|q7smJ3 zZrqXC>w=|p$0d@X_*ii-JX-nn3H|+e?58g_20p=P_{G2~IDqg88AIa24LpDTwzW1Gc``r&JC-|$% z4aVa?U%veDnqZM4QnXlcl@~yrkctIrKo4LnKn`e%{6H(@2impoAYHn4lit0d2_POA zHf*@k2Yrwy7z15k67mG(0LBD!=gl`b0J^}^W#9qC1S=5}tV8Z_6JrAS0@MW<4+I7= zCfKVu-~ee{@{Ve1DDHxOGER49+0`X zmxTww;fMtc!~HBZ8gmDAqEYfu`2geq?E~ne75mn6Cp-`d zz3m?I1=Rnw{`a@U|Fh2Lj^=p%9DOp*^*ru9b{YO{faGX6Tg~I3kcc|q0)6aNFJfd{7URPg{gphe%A@=Kol@=LBJV0L5z5xBD=XZQmj57h#wBU!@eOJlRvmwfFxNFY&R{ST$0jwvXPV_}J zjelqh$_D`c;X6+CCI0Uz{qHFa5IF#i|BEUI0R0*qz;SECKlED@Oc|C(H%L^S~2TBj~mCq`?8xcNx9_G=agh_DO>-Uh?bD zIh7AU{A&)-c>v7;j02bxC}DDd&I{B)osjYapb0i@+Dw|OSfDL*!OqeHx?rEarY7Jq zLUF+8F=Lf3z#PFe9bV%0ER!nn1DWj<^YZ- zZDS=9?ynpCfp^P}liK}O$@-wv%4dv5ZNUT76}=?<>m1;Vt^u^>|2o6>_*3eC%D;bP z`1iN&+>;Hv&Pbb)8|9l?Gn9YNxjs`#YWU9Qd%i|);GF+UlR234k0`%E$8+|$*N^RE z0OkVqbM*f?|9@3|syY8_{Et!iAHQU`+`pd?|H;t+Id-W59(II(jC<`Yy-2Xw*yGC=tP z$PbM4Qknqsg!BU#3(N!u7&!vu36URQJir`*@5g?oA$GT;K^!h`xYh4xm4f znLGf^H)Dx0Ql|3~nYaFkgxtD|`XJVPQh#!s7o;EX9P>Z%|FZM{e>(hA%cFnw5ZFI} zxXWX9kQ8pcP%;AR%;7PpRRgqY0G$7=8i39L9yB!o=EIl++>W{#;(sIX zzyIhNr2&LM10?>>T#uIZhv5S_z(4t5A@soJYwpT-9$Tfw*ljWly5KQv+hW~f?$dGn20fkn^- z7!RyM9pUOVYh}Hu4f>%5g1Ugp6Cf`_4$%6b&J&VDPo73@0DP)BfSRB^2k?G`fdj(A zZlhl4ICx;0(g5608;}|Czrh3A2jG$8&M@YT(?I)8Q`ARtHl8hmrU%NAv!RGfpP+8o z@CmHG|110_oeLn|m6mrsOy+MqA~pN0luxQmk+ksJIfo|}*K0m7?#pPU1>|frN4)0k zlFOmDaZetjwj*IKha6y!d;QoE?)7u5<+~DkOLD`XNT>Awg!u0|dXqeGfPbeN0Q4%2 ze_IRyotrT(YhjoJqYlm*fNk&rsE_T~wGTBw$N@qFxO6*4&fbWY{m2DcYl5wQz#`NN zm)IC5|C(@4+`BH4FZwK(kGe0El+7o|Tg}Ev_Nq;#WRXHj@8?`kE>L_>0N5w~VSH@F z0mY;^;sDkMsWk(r5n>!rt9EVF2sKbOLgav!En6u~utP^f7wpx$xAYq@Kn6n-@bvVO zk@N+|j6pmwQKn9tuCzh&0DXZ4s3Ts&S^;pta%zLn1(+vf-Ov_rfT|h5wP3E0*JjWD z{YoDU#x-LNm7xoqiq8+?8f!hk>IaYmcwg?`4U@Zf!c@&*oxaQE-O{KJgFlc-@c^)2 z$`KD3^&`}gUEvdCt2|j64qhj#0#3=TuyE9gvrd#;@F+ozki`#h7XQ;f*F6dT_VfQE z;GXmSkuxFEZz{CB2D8=Nofg`9=4ANy#6Eoj&h>nMsZL8|)|x}M`Q8!klbZj@0~+u4 zN7e^n<)##Gw-EI|@E4Is)Hwim)DhP1y<8qX2uJ#`Tr^Wn|T0g)yfBeA7I1) z$OD=&fT{z420$NxTDYMBY?aMhx675A55WOgxCss5?2Q;X5fY7hAmsiX_yELzr43Qy zzC2dkhi;d*I?R=n?PiH<>#35e8GNNiBcw$6N>UPYd+`#*r6`U?FxM9;Qd9~f?k6V{ zLQN3Y2~Y=MZ4f!2RB6NlN)N0m)oN77nt|FBQR=)c1jR@ z0qP_wCV+Mte8k8RXieZWYXxzAd9CRSXbzwsMh;+n7K$}R_wLg)Y7G@Ff)!#E}c4Bjo{Vj4i&v z|7-knzK?{SxBtXdX+LT+axIgSt>qE(#5y$q*7AOd`o6X!H;EtWx~Us5Kad#iHU6L1 z2YCGGfz<225_-SJzrg`%3y+e5E#?6GsPV`8KI(rO|5^he4=@JRIw1X0)&X#QvJS{r z2S6WybwDQmjTj)%rU9I~2rUbF0BV5efd5mW(GqkfN)`g^v^kc#;Kg`cZ9}BEuMo*F zH%#1qTPCiZ=8J2)nc~uNlKiLXIQgPp52;wTl=AI4w-+y7Lg{^67gQKLptu0|=Xybf ze{evFlE(YXmpAf))v8sO+O_K-4rpX@KnrQz2C)EY1v@ep=+;eo^z5l}goB0*QFQ{W z8)lA>et@nWrVlV@?mU@e<_T?nz-ps@cs=rjtR18_NL_&O06BoZfT|y4jsVw=egJbS zng@7|=?54bVB`jPFSH-PdvqP^itgRJt2EreL+7N;h;^zykaYnW$OD!*z>5F3!2xfV z882C2>A(RTm+zLHAVu3PmZ7tPzy+7#Z{Ek+A*?NeUJ!{zVllBs{?F9^cc31xH|Bop z{Qol60RLh7ALmgk)_6`_($7D2@j7a){N#g*lN9b*ucQ5VXGg{WjO(fAy;p9cl<&Gs zruZI|vzKowAA&go)&Sc7pUxg@f8Wv3e%`k}CjJLZ^?wTg8H*veSap&FpSq0oK6hWUm4~c%>l%Jd@V56r06`L)&aQ|z#0P}{-rLyFZiTQ13Z7z!2hAk(b6j*QhNI# z9|-&t_cZdsuWK;>FN>Cpt3=ZJ95(RZajv+wnI@?b2e>r$mO|AVfdk4&S*!spix{7A ze(@3|3@*SCJ;>F$@f>^ra0I!6c|q0*SFKh}SR0@?pg}`bA4ET(HP#Jr%|ORao#6*` zRrP{>`u3H<;DF&Hm>cjCZ*aia@kX7%6!-zG8Kf`3H3ZBNF2WjP<_YNsuwIDqfZ+#h zL%qN@#Q_0<1`m(}xQ2o;6zc|$9XE4?r;HpS`BwV@?_beQBTo2&O z@qYH3yj8xh!#eU&h)KN>kJ|YG3jYQNkO$r&7r;MIIuzui$i0jg5$-X*D>THaZj$0z7__`>`usm8>#uc%Wap_}m@4MdMfcCS+rPUO9 z3qC;B#)H-R|B4l$@1xeIJd9jW8ufprQ3p(apGM3x7f35fAAt3N$O%vnR5<~}0d-I# z$XI~20nJ;qkd~+sYTu!Q$_>&F_^o>n>4iE0T`x#oaFn;VT0_J-0lj{};DC8Df8jz? z7ht{6N|hT>wZogtcz}6Aa3ggR>H@nF572j_hGMH5gf>WiHMBwW9I(U!nge)$=(Evy z-)Q9WM_6Bc?(%gRyJ$D`=y~u3=zEUB_+?xW9|stIz&PODYUBcA?sA9EN=++UQyK^f@`^bS=<%9Rr*-YJusua@=wbW4>G00@Byzx;d_&yK*B!X#ms!&m#s1yoCAx zaIAd2E=E#=1Kcp*(>U)F{~5Kd6iGkzvbc6xWN<*+8RF7nqWq`HSjknVz0|B?toh;E zKj!*5x0i#DPcC480L2OL4N4&wNIsySVE6zPRE;ot390&^x>!4`YXo(DQ2P!YO%C`? zM@8&tl)G28k9##(338}R^ru*+s{&>9P94&Xhckp~`Q zo!~9xgI5QfkcLh4&0?+Y^J3lE?>XZD z`*~kK=J&c8c2^3toQL}k-_C-6<^+ngnhy>5j)Y;HX#Csz02=@FKXnX1AK*0b&p9eV zEr96*P~+nKMjep7HMSo-DIpPPAi+8S;D6(VXz@4^D~)$!j@R?O#=QN}H&)UN+hO3p z!yLo`&;sEDq-~5fjCC7IjcQeuKVKEKe&hjKrOE~$&@W)jPfjRPmV8iN&HpMVh;>5j zAw)lbaR7aQM$iLU8`Pp@OQi?U59o}wgTMXO9ezM}>4lnM!w(p)))IS-@-}tBiE0he zRMZKuR*0H_v37v#2w5*kO>m7`N3@3V063Ac05t*S2Y>^}1JngsH^{mwaw^vkC_exk zK%dL13Fvr$9BqpS-~(t5Ag71jy^D3?=h~q z)G@$S%>M~}0Q3aFc+@%|*SlGLfcRPfLkC>DX^X5nbw`43p?TD`SXpu@PR5;ymBGhi zCI6Nfafc6JoAaN-f1G5Pe^=bPFE=>A)B^tvAK;s&1Ef*CI#Q=jT{Vw0uCG!Bd;l(> z#>e%*m9Q3wH9*?Rm#?V$hgAgr`MS~qYNAFMwE>C)8XIc{TOudGb%Tlnx^$J!&;xoh zH;6R@tPvPIgf+vSW-KsHCZI+@#RA9;QWH=$!&pPOa1n9?@B>&Yv=Th93UvbOtTlt% zwRPg{ffbkS-th6r3wHM64vQ|LF1M~$@E2!fED+iFnc`wNUG;(|dazR`xNWXL) z)}FQX+#o;Hn}N9w>p>6;GEQK85YGoD{)+(rhyR}Vr*D2M?4ERhuFtyL#OiZ1W9}sG zSot(!{aL;|W9hdY8=6}Wk$O9n{*nO;@@)d91 zzLUTQuxbF>2T(D9)&U*(0J;`{F#t7g*1*xv^@9(v)PIjGzlwf9*W+YuNSw^M5GSKf z#!BadvGV?^Xh}!S&mQ~wF}1)k#|->iw7^u*0?Rb+AdTzSmAY8x!&-m#1FK%W2Iln| zI94}&1H}b~e?UIq`at>!^cBbxHK7BjHN&chP-AJ*q^ZgaDlGu(g_sxY3=Zhljj=#a zwT5^Aa)aamLlR4o&uT!s0q@@0eUSV^8@si0)x1I0Qo`af~vOw zIKZk2=ru$tM~Lfx5q*ZJ%V-X;`U2!|%>l$ejrF1I@AL?|FxQk^ymm{LZAC8vtS!ya zaF(hUBp0Mby-;S15BdP^#DDNrXY)VzC(!l3f2sMO*gbvehFXi~f>@aNcINLhf5+Dw z4q7XF5Kr@Wb!~2PasHzC!5;Ja|F?f$KV}@l+M=K8&y)S$R0wiTD3NF0-Y2G=s2Kv zpT4TU*x`f+on^0P}-d6Qmx%^+V(U_7-AZfOSG#JD}@?^co^wBXR)O4=_il zbwRElP<;g$518u*7<+LohF(8NUZ(N>kgG#1F@fd)?FU$E#91TC`GMp4%Jnd{UZ6L6 zvXyARNItGIN!1SiE01Snz`>{Hf3E){-v5&LkHYsofP8+=MzhtN?hN-z&qq$AIC7(` z>!-iKSV3d%e{oEVM>OW`kG#K~d^RKI^S0pM@BvsG;Im*CzSn)=KM^0m8UrNo0gN?4 zS_d$E0OkVid;qMITfKRkEII#3Hh}}yg98>K4j6qZR{9=^mG9QaB+dcU0;gXU*RG4r zIKa>X|J`_u6mHd5+O%k@a9bbwJoW&q3u9bRt5zMuFF@U2EyVe(|6}ftxMz<5@&d8X z=cxnMH){h`?-14p0{^Ysv_*{|YXg`U?52DGY5`m~L_dK2MA%D!dH~lBv1XVYz&atW zAte5Ff3bK?(5w;IxKa55Tr@hN(|-Q{%i^Ef z+<}u<-mRKKS+#iX-iH{KT;Mzv2dcKdndRH3ANtmv<1~ zF&}L80W!eX%h6z_Tnf1fUUTjPa9*JfVD$kw?wtDotOqpuf-x6By=x16fF*%PWM!zx zM#KTDuf@rX^Kmi~aX_=Zv6317eggep`~Ug~4oK?}1pLnvSEU6`Gqk{_-qNUD7irV7 zh3fgk`I|-zGo~jWC{DoqUI%dkV|~p98u#Q1zK8E`sD8Jp>KQ;CK;;Bb8`RFM4PZ`? zHG=FZra6GIfQPCP7$u{}a{ZuLCp2vu`VJd)LYf1XEr$=_yHc6zGib&FYArExCd{2u z7i0{@+<@YMVDuW)xdGD;pf<=_%v10M&cYwlc|!VX*oQFz2apGDLKD!j0FPQH)?-DF zS?XDw=cs9+=V)Y1O!Pfyf7dMf-(M2{!~tt?ay6dg6#E&W>$5Kh>-vvjtwapgNxTd# zUtjCw9;q>CbM`y>KH4AI3ykZ5E2Ew+4eGt(@o)M7&;h6o(+9X^=L2XBkjDf%U;-Z? zVJ_e)A7H1E3)tefRn~6UB#Vz@P1{W)4p@3QPR2tE9C|!f3i%~N3tSK`ZavW>Rn-N~ zQgwm?cA^Q1=;KxM-2m1jzx+59&H0r3d)=sXl{@16W59v>UMiVkyQ0tQBI6MP1PF1a zIDoaoRzHAwLUK0mTPW*>VT=W6&inw56Fs){Si>jaq}WL{9^1vaBz z7(B^qr8Plv0Q(6rKd9>kgO40heFk-JA^HJI6EOXNOIO%Sz^EC#rg^}u71El3)&#A7 zfOYJ0?Av+CI{m#0br`3K8hO z1l%{q(dZL`dO+a671suswZX(cKbt-P@y|GbJ^=R`VE+K_J2I3tLL*0F4VYpZ2k^q`f4GL{*tt-(PWS10zVXO+KnI5&z*=4U`}+I;uZ~unJ3CM8 zZ?ng~eoP-={mxTZlQb6nr4#A^tPM_!^`T=>H~K_rfrqArlN0QCP=eQ=K+&;fdzalimoA4CoqHq1li23adiO^|Db)Vd*X0DFn({-UfC zQoRKk3#j$OMqZF>2B-jIX~u{-dzRQs>cr4*h=C>Qm?2b>x!LJ}uw-59a}ASaz9b=lNL)Ieqk6eJA50D9Q!29LK3-_OSguO$oae$5mln#KoL}^Um0F@8CgnS@5 z0Ar5%z!MmUM~_=;g3u$>j05Nca7=I6yh)bqI4mo%qZ75jHHZVW7T9uMtYldcE$OxH z=jcc+&~uOJ0~qUrQldvdnx;O|q3;kG*srhB>Dlj>Yk4{Vw`~x^GnVIiU&i&sHIMZ3 z87J@$5v+>iISMI8`hf7SxAk2raSpUXXlx?`Q7v0k{Z^zGlz)B*;neFlbM?;)-k z;<`a*G9Kbz+j-LyWuWFz#FlxT6F#v0V zSQpG%ffMIL%(}p*V*x7%um+7i0*QZ>4}kx9@{AD&Xf0qr<|)ou9Jhf%SQ`W%fMa_7 z#?7+i*iBh))&wlOLM<>(X@SMIC8Y(%N`{5^p#`oqIG{cH1hkqg|7tW^vbP&4Lk10m zHs4pR%fnBiHxpa-I-AajF@7B5k?0ZY+Ggn9t`iK+Ua zP3$k~hgiT*j-)X-4(PxzXA`%nXQXn_~w#2YmMzXiw2$It@P zCRP^+A0XX?)6fDJfCHcfwl%cCw;KCM{obB3eDFY}%Tb?W-5zUqxJNL3fp*{l?hQn2 z8+?FsK1QrSt)IGq(*5E0^L73XvCp|*>HnDj`FWiC*+Z1|Kz&gU&>w4r4EzsOH36&( zVDA9d2XUVP_7PzmKs{jAEUp`d7JxlTx!(x&0Ge&j5#$9m4^R{2-YPmTp!5Lnsn!G! z95j4^_}qZebDVwv^;vSj1^8*!S|Rc{@lT`9LmyA)2el?}`|iES$4{Q9`QOBUfCc}~ z;{QL*{Liu8&I`3KcKGMK|3QUGvhVm6d5pL?DJ;FL*PUTIxz8u|cWJ!aAIWtOQ3J~T zgj}Kf*~bC8Hn@D3C8~!QIY8$FbR57jLqC!nK);e4!10x^CcxM$jAN2HLB_1~TU9=Q zIYGt&SQoo!-#J+eAAnlmO4J2TIcI2rwSu4p+OG+;#sVv#1>$ji{STi_rGWrOa>xE{UdLaD( z)(L7o&{#K!eTU3mLW%=$&9(=axj_{R7=D1N5rCh?{RnNjLBwHt-2i!jJ^*z=Yb@XyyY_U+%txQKPZ`I^sFb)NAYKwZ!2|x@$AAofMtPkRT zLsO@*ry$k~Va+i80QMARUQpEuLlb13FxL&Ux2VzsaJ{xc3(z$}S`$=y0DJ&yf~*nX zx*^sIGB2odgV?{7z8OvHf?5wC2hbPLJ^=kaZQy`N>kXH}=DeTE!y0P}(c zTFk@VvsctwQjLEe=>u4`0L1~O7Qk_K2020IrMO49Egt|Mz=#9(7+OGJp!jd~lVyRy z;tLH>Yk@P(y1+L3W91#+WN3k2`%NFf&;nhWkC(UVdr8Xd4dkPrYfIIJou$u!!QwF- zIePT-;adJa@Y(4Pa4sjNc~o4W{Q~&=oa^cLlM{5_PptN28k5!|{~zn)XFa$7JMDkp z{&nkP)_ATCI4SRy9k14f8+u^84?tgl{X>7KGaY>knHPw##RA#~AO|Fj1LAXnT(io& zG;@N?Q*mzM7`1T#@>+~(nd@45?1oJXTn;U8f~f_T^iM(y#QLBNiyw$vuhj+zKnrw% z7MQyESaGR5KwQ44CMiFwBJbs_BPDCLmCoJ!B0onS7!1tz2Y&mgIG$P_*Z6gVR@jBc zIzJq__K!S3j!=1jJVy>-k3br+&;0}R+F-5!G4`kb&zeAL0PGo}Gyv=?pw|d+e*yXc zx~DMpK%2^sE#Lv>QT%c3c8~)Y3+y%a9pd%nbtdPky+}KRIbtyf7I)JGKnE9W`2M->GKY8*b1g7*qFE20RpX>hq1EU7;r}Y2X zV~=~J7i{Ht{%0>vj(;m2Ugo(yF8|2KiG45bSHykBxHcdodO|wl0Okd_ztoIX`%xo! z4|9XDR*)P(vvI&R)&&@Q*)SG33qOFhsmuwop9S+&sy^7%0=U+VwSfWfX;=I2lBMVk z#Pva}3!GX58X27zgW9Pst+e|m>51@#A`1T>v}(zr2Y?Q-{T(q5BfoK zu{SAWfDG`Jv<6^3vQC)RYy4KkhqvV(`c6_WdWr*7eIWHf^bCftNe*ByJLaWqbpeP| zIZk)&fgXrHc&u^TzHO^4-FH^jAQzytK&%g%YHERXcg0Hjq_jZjfoTT&C(r^@Hyh*uWLw}Z)%Fmmj(}{{=Bke`=yQ)uhv#tckBfYz@EUvhG1XNe!z5Z71ta2 zea!Q~dygKyOx*W2j%Mv2b-%v-G5>QN0OEe)pL>W<|5G_Y)B;4zz0zK4YG&WT(f@=v;g)GVqbCA2-6QB{`ES6EvymLy#wG!@j6lmVE={v&;rP_ z)B}|t0PbZhX7m$6FA>H9xG#(asN0YO7z^0^0K|4?tQQ#(aX%c`57GD^IdWuQ%=yIp zTQF(>Jh$Hm@K2%taa{KpzYXj866XKdm-{{R@?gy$*Y!HX#*279DO^79>l*j^nAGPa z_Fc#U%#|$Od=&HiMAQLTYXjnQgjvu>ki7#dAUALr{br3l1QM1cA4KK$qtNI{a7oh5czyZ4u z2k^S;8bKWk82tl`eTV3i**M@5ast!?to?-IbAyrS6A%s!Fa&m7kpGAO-xqji-LF0V zbseyt|Md}XedZtjF6)U?px=0B{J+n&{~z(c0{w5&pudkb{^vQ@0(2X@^`+MRC56dn zeVrKAcz1M6>hlug0KUd`5`KHn8v7T5Gvn)n;yHlNQ5XEC##F=&hg1*xusdPO573&R z$_ZjlAqUvv0BQk@11x$V`&?PIfF0ZY#dl}0ti;@}wZK`>0=-VgN;_zQ?@|j)tR{fH zg1iqI_&0k6Q437n$XnchX#t#92M5#<_it+f|1kIi#@s!8h4t3oFr7gUi?^3;(c zl~EVi273YY>MMf~9}FHm2e?Vx7l51qeE_3A z(A;|z_*eZy%=$n>4>bCVtGuAD5rjWx;NR#Uz`UUL0gSp1TfJ~(I5v_Y{-FaB|AFTG zPyDBZIm!X(bpZc!{Bs@O1@t-kuFee9yCviRuJz@99>Lfr_htD2N#XHXUU!6d`{$DL zc~%avf4}B{L#M7n_n(jbr|oNm;(36*18D6$*CR(53Ldy)(*hVvY7Vfj5n%kOwSZu) z1!xX{77&Oyb{pb=B}cB?dIhaOO~6EGfy1yyxD5J+C)zKNTHq4&4ezto)B@QzoLV5_ zfNFz*{knz^06)O}8{nV*0JtE{w>4FK;Etog1y!IGR79*$S>DfCQ*st>Ak`b9CZKb7 zW1SD`3kQM=s2gzJXYP-FKe5mK#0}pcx<9bb`Ct40I{xP#0?Pk~@6UQ5l?OyUz-;ym zg6Ta)SQo^a0G$)$8lg37*Qq_m*gJ?l))@nk1DF@oy+d>#G2)+ng;hQPdLX&?7}jV} z58%CEU4ZH@%(?*h0JhoyQwz8ndOZ@op~8WE8~#zx&=x zEvR+DJ~(#ke(3|$8?YKZ(b%W^#p(d|*!n$>?Qx#iH*RJ zEJ6S9cn)A6QMUqK@=L>+vTVy?XaZPI25rEm2P!S#YJxc6H0GWYC$L69trg}T;)WKW zYJ)I;1tJbuw;fvG#Ykg)5VXMg&;or<#j3vHU#>+Qkc1DAe)0uqfeXO_tP5nXAZUTe z2e=jKEUsSy`{aS|>Kgd}u8zS0-;oDk-x&UY@(aGMiu^$(N%?67dFzvMlKz`2@@byB zQlLUJsoNC$h5csq1RgpRIwEjC+>HAG1HT6KVmzY@_#Pyw84+M`#-UI$IkA|R!gqr_>|9)l`u7vVd19~W-?tvE@3%hh=$yO>&c{A*53mpS z4#|wY3!m;GN**wBh3T+%kn4w9d#sh?XD>?xv_bZq(0RcGwL#DVPC*N>aR78`&Qa_a zzmX0rXm7y-rx=1mOoz3*5L_uNP(?(Etgg76@O8*HYtO??E1nwL(UH zAnSwCI|y8C_y8A_R!bj%F`KO};96+pox68YN^{?a{}CfbX#A&21piha0B?KdpBEDU zYhRarzuI_ibhrkP>w>aXnk<3A7nQGY=icvI3*ZP(&-0uWug`jJ{r{Z(uJt{R&Ux)v zL%|xU73dxEO|9w1y1@i%2I4s&a|!G*o8L>mtuaL=EZ&7Y@(s0jwT=bU8Ug6dx<;6F zX-{zg_7GvutDVScZ$+K!;?vPe8wbT;L8aa7SF={#9kuH>d~=u_E}O zGWei|e3rMK6sXur>a;*zQE#jT_VAQ(>tQI$LRxXRve)A6@d>x9gzDlQU~C= zOJki7_Zl?z9oG2(-8)3(1XUbht`oA<1wqSI_Jvb&(7f@iM@Kk%p68rlHo4D7 zJb0L>PN+X8slQEsf9itAu|G=H?#QJgj>=@K6NtyZ+ItrJ&!$I@u{79UqI8D^vNqr- z_7_1d*{z$ZC!O{Kn0rz=0dfFhQN5Qi{eZpuu}%ma5VSK;*6hX_VeA<|Es%Y~XI(I~ zz>eq}o^@r?y@Krb5ad3>E=_!7(4<+iXzon$88=bd^%yP%s&ez;E$YkwW> z@xe7hd9ZIE$NBTr1to>0;Qg$6Wi@ocSC%hhN(*no=^gV5PL35oDbJxZm0?-3@1jy2( zp$W7=_6;8nEwCcAz@+JRc*JXn0(LZbE44E@~7S`g;6z?$;q(e^+ zDO#h8e4M|TxFIe``DIN>^>rQi28<1AOIn)BAsTrD))bHflutmvKpD7!9079!Po(~` zx}^LZHHcr-5Le_J-utDI{7|yBRBYH&I`l%0&}$sy-yitb@jo>H zt_xVQO!p7xenQ4xB5HjAVgRKDG8cgPpQdtwi~*nn=zJji1?cqwIu}6hHP#0h`9S-+ zAmabFiU08M@Z;zKs^@>=Uf~~pfPD_Y5&ntiXZd;I_-8%fL#%tLk3GKF^WR`h@6njIKRSEA^?65s8*$I)HU4>IJP?f@Njq_UN@Hyh z*9voQNws#s$^pRt+px^oW6G_7r(~-zR=SN|k97p+sIVGmJifx2(_0`v{17T6oTg1*5Xf{AK@>>KW255a#n@RnwtGh_iU z$345)pK~TSKyks$=`wEOWa&9*lvHinTfQ&RTHemtNcjb+zN{rK@DJP>D0z`r_(njoWBfMwrt z=rN1~$jN$rz(vFXFol0$KLk49ar6KU^ziWL4L#5;rQqH<$A!2 zA-5%W(|J$T1ZqA=TLk-_LJyd+`jF~_%z4j>0Y~SK@bj$CS@D?EIX_#!#~$zYkM;YK zIy(D%`Fr-SMWSZ-4z8!?+#tz@z0k-5Z&M2d2iSZ8lLOv{50J6gC~+w;LcXXrUiwbl zEQe2=mzy_GCv^Fe@&Qy{5cO%QMwlD`UzPibvZj3(;(%2L&l?@kM<} z{S7|v>-v)F+lG=QR}1;JWG5-zpucqJJz7SNg>Nu_k*r>|Qr4{l=GS380Ahb?04f&< z4p23L$OSSFsPX{ludoC7XAEG|0idrc9{|^uH9>lf5bJ`pAHbOG9612F0M>3nvx!7L zCmh%hxqJ8SajXO03H%Q*@$Ukxs~iB1ItC#2|Frlgb{XIAJ$6ODs4-3T{?oX(9;pd( zPp~FK*2&3>*Nr~l*dyW3fPE{D9bw)6IX<@jj6L!hY%8 zV>BJ=0UUDx>My|q>5FmDT0%qFL&&TVhCizEf*7CdX~B7GH?)B5 zfkCqL^gWvv$iCs+LvRSRz~|jV@Qnteq(r}IvUKr6tjb)OX@jC7*FcCq&_n_uRHo~ z+$(}{)jX^r`nl08wYO|0LE-$kmFO=2kt=}upU~VFZK}BTHx>BLvXyLY&c2=PFWz!mn_Em`-Q6RhOsww zIK3uU=jD`d08W?zPM87RU?$>(S?~`gPMRVEhK-VzT?R?1y4~cPqAldzpBo@f03T34 z03W!J4>{X}an>2kD?v_G{F*X%9C<@*+r8kR0+ zJNc~mZ&IkvAZgQgqWDZ%C`*0UVhxqQ>v#)P$aoK*gR~q?)bp?*_@GQ?I2XprCNebtQy>9H!7^P!r5j!W%XUJTO9B@(q=b%6m)8 z!AoWR_Wg1Z9B}^pS@;21FN|?YpOt#xuAo3!dL%^FVSSMO9)eY{hhW-7`-USKg`GlY9HW~TB}J-R>uI)0wQnTycvPHKlIL> zJI8_loxnfi|5O_PI4b<(xqsqZ;UApvI?mPnk0a-D+Ozz;)cIe}-^4L%gMIhnC-|5sAKtKaA7 z=;(X#n(CM!`1CbtHDbN|w=C8QFb@EYP~)G+cn9Gj*zNdk^lDH zp|pUrXRu!A2zmy91NOp)-4?W0mR)$9ux~iDK<**<{rV*G0n`E=>>>D%`lF<|_Z(Tf za)s)}$-LV_^x~So5cxOs-Mz<$m) zz<*oAaGD; zQ~Yo+^i}2oxi%2^hY!GbORo=BJ^*up)B!G_UyzCaJ7HlO|AE7X4deVz{JX+=UG?d9!Xnj_|Ktjq=RzxxlwKmGkymib@fpV;r9u%F>qV4jw{3#=>f|C^-wrK`C9 z+)ddB#rjJjtbgk2f!-o3{S=p;I(b}996uJ{BLumZcnu(aZ6NZ2kvA~^qXr-pb{su` zb^`wcf&bLNcLMxl%GdKheE>E8(+$9Twf*Vl|M>Cx5Oddk@maJ-Y5&BxqZ$Ft0q?+1 z$P9ge>k4UK*P4NK1Z!l@y2Enf!ga*~^b4Lmd8}$^tr+2XVz_ejnlpSp%k#v&)spkO zj{X+==j^{5AM?7h_aJj8>@~P?*JwUow ztT~tiJ#pSVv~J4;m{1$?NIvP z`E#eys||Hn$N`>IYl47(hCzof$*nu# zDre64nE6M0{8%45;)dklG^zJkaj(yl`W@@**1yrwxjhGHUSrSL#0h8lj) zN$8L1e?ZoGI{ap4_@OH`n8G|4zmQ3lJZW z56A_q)n{CwbOOW$Dwn`|UNe6{yc7FcFW`EA_Wz}ZK;MDCI~P2%aN&GevV?rIK*mp* zCH*}oN&SwVlD9@*`J`wUNt2^BxS$nc|JKj|5Cg#fH|Brh-sg@DcSbtz5u0 zMqE?Gdcyyfogn$pf3ySEA1>Q^LQY@0p?rdvm`D{b+{YRlM|jfDSuyym=ll$fb!W$C z`Aq#cIXl{OfYlE$?gw}P*A?w#WW&yrQoGLzdABsy44^I`LoqMq0}%h312QQN7%G_x z4^ft(;6V5TeZ)0)FZr}oKWR5>w`|4E^jZs?d^S!zo^uaDSL`A9CiV{bqVptKz6yDD zJF06N_3aB zxjKl;kL|?urw)=PS7%8N|37~I2mbRB|GiE8lLPumrh@%p10+L%0g@(fe_YT0@xYhc|_+fIC2@t)W@eTQ_|V~qI)a>7@&rb`v;0S-odCnf+$$gxh&*pnH)}0;gIY3`?D+iDVxW^1R zAR^+f>_2uv`b^w{KEso-#&9@t1|ACkS&Dlg4lrYZ!tezO4VH{12e|&+UGjEZAnR|5 z+DrU}>>=0`bzjZL$c*J{W#jsFs{b}?ZE4(>gZ(&H_^wjtj1gFqPyaw`hRhoZJm_g?ic_jWd_Q@Fr{*AT2hRxAb%jjuKrD?CplDqbBd9QFEaiIqIOLs|^2Qfead;AZy;XgBctjvhDvJ@F6=?V-H z*Sv$}lM3F_df4*Fvlp+1-@ALqihr&D>G_|v0DAt{8ejtalLP)r^FPO|;jY3hOYcu(vxxkLE|`28%UCP@C43#9+l9qHy zW{4kZ>egb7J=fZEtu6h4RXA7pTfIg-RyqOv0s05@1&Gx}s3lsYbONs1K@I_Yz*vEp z*7*bK1;jghea^DX`^+(rEA+ZQ@`~~~z%l$k_&4zX=lVZuFQD~U1L(JPD|#pfs5*%N z)c-B=-6;Kh7D`#vIe%7Wu(n;pr20-FazfKw-eC_ z?0JE@0=a-(LcgGV*A+5z&0)EA^Pa@TMS**mM@ZTyaE9w=c|JM+kA6>L9PlhWz`Z7} zqnGe4^bQKY$6kUr&<{LVnhaPbpH_lKfEppU{6i!oeE{kKCI_U27VvRZA6bm`L0fJ< zy@%i@&$Wl(c=>ljZy7Y(SNy>NTw}vJd-mQW50DR5tv2fKxfXXB{BmM*Is5_g!7^ZV zF>tT-L*^0idJ}-ydO71=|=z#?+J^<$b%-~oX2OtmQ zmTySpx`3mv@DG2$I;ZiRLkz2$+-0U0v0`?H(8sSem zPLwqp&|e30E7#X-K))^a-zFcB1IP(H5|hfn0n`b}1w7J-ecL(aa{35LH>6LXVg;Q~ zV9k-?Bk*;a;ZH0xf8Ut<+5b!VA(;PHo1DbIXCwZ;Oj4Yn!nz4zX2S&~()<|_9hciEQif6kq` ztEchqN-M1aC)w|NdNZ@LtJ#@%&b#g1Te9B^@0GN=O_8&zaW|}0>aV)Nn*1Fq59~is z62ns-JXhqL@?hW23C98U9(#M=y=SHm1a$$gra(z;vCUgH%eJkX>5G<8Qw~l zUvU0c`Jb{sc!&5W_PsoSrE`I@zVf_brKhJ#8sdK@&sr?+ zb{#F({H2@JYV;BADR>n!mg)cw{`c?QosV^Z>8Js)5cYrS|3Udbs{QXo)c;N9cYAmI zM%&&8`yFisN1-Pa?AeDkavP-2=mf;VjhD;+++Xbllnoeb7zz6>57cYc1M&3T-CfsnYGM z8FJ^#{p9?nt>u&}{wcqH?hDioDU#`k37{>ovNZ(H`YU3WpY19G#!l55h0L{U9Kd>< z#2p*g-;)z2PWF5TT$jt*y_D0*1=JCUTlSO<=szG242LYB+uTQ^?SQx^<_-7MCCD3A z2S{aq#Xf6-dHA>W|I;Y{!v?_le>Ug;1*i=M{CjzTkOyQbd``>(kV zOHxXjd_H0he1HSx+@^@Zg#E7`^#73j9|r%h=~qXqiuRqh|G9QN7t671N5guw_c-SF zV*c~E!1MFvY}k%`dKt(^I8Cm6@=IS&pj<%vVaePdZo6(%8 z6VP{9I<_yK`#~HB_k;f{_!j@CPy`pt3attG(C;;q)O8XGV3+6{qE>PKEtm2>Yg7Jar3~En+O@Z9O&yrg#_aFa{ zVjo;Ib-FkIGY>GaKW8rHe_&tnzZf|HF#iYf&ouzL1_)~a(vTCJYcJU*%vvr_w;U!v zysM*}^81#B6Mg$%aQ=7kkL}aZBKrU0IsX@n6mv|)g5S!X8yvIc-8Z?{?ah98^hhD> zd%I-({7kHsnT8nR0l@aBkPVPa>kh^jM&<(A2hO{@m%Inx;kI3jAvpYvb3hRH?tT#S z!F~7s*|Raf0Of!X2W&xoLF5s(eB!K8VI08RwHe46vN~(EWM^ec0@et9HF=5F5S)tG zz+Iku2zV>)bYoO5X(p#7aTIF$>KlasOlu}?k0 zvlY;02)Ted0%QU9#J}3~s3WjJz9;V4Q$Eo5fqRwvaX!CC@$cIpP4-aR1n^JWANBv4 zxVE{-p-${u95%*C)_{aTr`I9dMk?vTRii_!}A?b${9 zKjr^LCjYzmKk1~C6#qDO-2H#Jj$+D#hl`58F%EFYYb@I??!#?Uk0%dMmd}H3-yb&r zyWSY3HbBnv^b3aNfxCK118_t&_zP}+aj48sS_@V@qW(e`hw_3!Ot7cIzpALA$ZnbyGa$; z07?y^!0~5j-w{OhEe`@XTDk zY~%ye6=+N3yzl7`;0nz9JfH6q|Eg=?yHw_&?1BGD+aIrq*ysFjIY7DoUvmIL_P6=J z1pi4|kCizo! z^{3YIQp3r^En;2KyK4pBLACwnV z?)Q9-p3UF4Jp%juUVeWl|1YjVb${Caq5sndU~~TxU;i(`zn25V%K?zC_mKF{^zomS zgZ00;d4*dxZz(_yAg=#&?SEB*^S|LA{mFRmB(&p#|5>wU6@yFU8x1IL5>6yO^-;eZUOm0s0cC<3IE+*CKU?9}#{72lwuFeZ&&gKn&8O zEhos@O*_>t5Gzijop0E;KH9nA-m~B4_QCsM_m|JjbOBcm2&V_|*{d9Yamze*9Lvc` zi84JYTgG7SXYCN$0xMrbu)9=wx-*```&e^>wY68jA!lFtI^yd8CD%OMMqX<*K)xC= zQEQkoPsv>6g4wDcsGR^ZfoG@l{Bmi-+JgV}OqwDO8{T@R{jy?X#%I2U&n}zl(i$o4&%|KlPQ&0dI&Ltg~K(`rvO2-WTHl!@tP^^apH)Er2q>hWyQ1pT?~vV0pwT$7&8?tpb$iF*&EyKo4*4*Wbpd-wg z3%P*418ZtG^!PyMZ{XMT1M@4Ip5WQ|r>Y%+_CDgD=hEh=vIRcNo^prz{;B&b{+avB zk^edWTmCQF|A~L*0I@t^ng^8fKjbN^4M_ZN+D!Rh=YQhg)&FY%-{gU_0QY0?PYOWO z*ncVk#|!_)1I?Q^S05*30glOd;NQk$tbK4?huaTgA<{m73+?<{ckGdOd(TtsGgi>W zy}M0);DUQmQ*vAqlalK8o ztKN{Rzkfq6xb0oJ{n^gay4zqGH)$Gj@&S|ckee4aM9KrxVXK=Co!x5pOqmKlB4h&M zopJ(ku061Bb_DLT-jDX!_C9NQKY@Sh{+Rz=-MtAnc9onOC6mUG*SgJ^G<1yCW z9n+DvgE)w`PaGX8Jfu7@Yek-}DW>hOA!Cfg=mhnfGoHA+{Hw7Td zK?aCV4Z%;PD%Jg8hwkD>pi?fU%)mS=Xt(o@2fn4&$8#-@5%nK{n7UC&;5#j z%=@gZO4*++r2qT)xA|Z50Q>boLii8O{~rE1|F>@4TG#(z-Z%fh;a}~4hJR9k;h%cI z@t*%p4q$)jrI%{_H1&alg@-H50X7z6>4W1r(!PsV+!GZ8z`UFrhs z;W&WYKz-qn_a`8C@otp`-0ul)mj~z3&Ix{BdG_shyZ6ZMwedAMz+?dP32=RY`2@r1 z0rY1xf9UeWRWdSrU&t01a}B|-cS!ZOMtOBa|2{yfB3Doq*aB-lh@1tO?>X;lo}HVp z9+@1Vy~hb$d&^@rzy(!)kM+2a8LoVwwY>V#02wlBGHi2mWznMf$^$ABsD411z_StX znEDckeR6`2ee#0&8yJtk#<`z9$vIf}W4=FPpSgc&_gCAW;(vwa|5Ev%`1kdH<^Xi- z0J!=;<$vG)m&W-&3jV{+|KtF+c;SEg^yzBGStSYKQ7TISk% zN7vcc#aer>z3Uxg4lc+Db-Jm*c1;2k&F2LH{JD=+!pY$C8nP9f+2ecDRpAI}j zF7R{&+6<^GAjSZ5zs>h52Y?$WKk!`Y{=~ld{b=*Iyx)xd&-SyY2?JpAU z-QzWC)|B^Jysy6G!Xrn^EeqHfjI|Gr<#7Au!9CB3h8xQDB1a@4BVTSt9)W7G{pp&+ zF!})Fihlh#>Y635)6cly8!NucbH4li<=HNa156JvIe_>tu?L2+1%&i~G|hQCHgkI^ zTVQ2s2v$W6!Ly#hI?d-km3#l*Q+|9q)*)O?J0R?V#5(fru+>AZt$OUq4L5)ns4Mu5 zwt}j#8P)_J{PzCV^2&$(Wd!1k7r}SH_#&?5r9YpzrfhFG=Uh*`dyW2n%Ke!4HRjJ- z^TW6w&iibO5c@~mvj(8ypEVUM{zBd ztTp)Y1N{(hn1x$-6mbTgKfgS9uE;rdeX;cJeX{!*lmW;ArUwLV0jyb(o$J*V^@<4rT^LPz#N0lr8As5~Do;>tg59#yOSdA-MvSbl#d5kZb4ZM4? zJ*ww}6S&Vtd!NSpVZF~>UGL}B{ad(riPrsM>-SNZ?Q(WAv2Cq)>=)LH)g=%WJnLb-V0 z!;e}CV}@)@MeA++#nQXuH_~>z@lSjqFSFVQxmLI?))CZ;U=v_og7c7H;HxRA$^i%9 zGpz`=WBE)h_z%hfcJKL|f--<{KqOlL$1vkrI7U*CJA7K&dM|G{Y=QBpA$Z2qsB!dC z51GDjv7{zrO-;fI88>CFyw`cS-1r1)9^TwS&c6C>sfio~9`;Qqu%3KC*}$_Gs7wGm z0XgE9Cpsc`|1g<~Ivz`wAdVL{J=*tF&qvQ~&ime4A6xJ1ulIxAulQdA4nSLqm|w2@ zVei!dWeosN_7CCTm;W{X5BR4om2nlsKj(kM{sis+ocANi|33UZ``_`vKgR;c#DPP| zqj}`0#!6py`DIZ#;5WbdjgBFXv1qur_iXIN(nrF)d%V2(j|De8mpOxqi;l>bunAlU z{~v9G;p~FshWgEWN{^8%)t_(x{=r!AT#<8Q`7N(jM9=rz`la=+UA7Afw@0i6F8`<(w#OVMoq=Kp7`gvS1)c{M<-27ovJYb`+E|6d~i zm&5+|jp3hIB|eW}Ex;DUi@)2Vh194~;}{;m1!?^B@tpg49WKrzZ67cE6Jx|CZ2~iw z=gBYsJOH%`!o>;nmiky%P#bFr+kd?jc0_LtVMTBq%V%Qc0OJAUU*lz04xkLc*dWxX z@a=)6Z2{&JRNq!=ip)(xT;PWI)et-zdBa&lu+O+zl7#u+IlAE0AE7QYv4zOOmK127V`MN`pTeD zQy>pOr$<|W_5EDuPk+A`@55XlT430IB?M^M5(*f8QwnZCr4CQ70g`-GBcBr8t0o0Llp*OLl&=-o~8U^L%$d+;$|) z2alJ<0kL2u+_|*#C$8Qk*ZDDnC2~M-$OK+%fHeg*5hL(^zXfuHaYE1`V!?Ao&kc9I z!DH_I;k~na&1c}^-~0iQ;sUaBVGHo}fHdg1sVS>uZ05F*EpQBS1<@AByx|qCA$ZCY zo#nUx?hnjC#)h3uCdD}l`87z zh$Rky3KZ2^o8qCJp$EXOqCg3{B|WZ9~88HId-8W#vXK=X!I zvWDRIu|Dvv0TU!8F#&Te@TPO^%8;*}m`eZ;tOEaJW~Rwfyx;xHNpk0lJ>{In??Wbd z8yJU8kaarPs2}+DLysG1BX|e;!`qMt-qh{epX(~Uzn%#D-V)dzmTR65!$0SHmHRd3 z*Sj8-{egehR82&Dpa9oGmH~hw2E|{?`DWe?mKuA@_aJ47wr(9&0p91 z`s@9d`|=1mh%x_cw(dvke`5aU{ju18wf%!PJ^XK>{VyE;BijFb4mfW1e?I@RFwFOf zp;+d9`%ZF4tJWXOOE3RTZAl0vRt_<&hwFn_5AKJb z=jSl5h>v~i&$$zM%)tS#cbx@40(}YJj4nW4cCR(K9lSrhr@Z%kPR0TB2Ob+A%ymMW zl>aSXh;jh@0qGd)iCFVCHfN7aLTs?v0()c?$;W9$a?8x4QX^U|@iE0xcW{AJ|MwU_ zE^wf*h9GT$_p}=Vy+29k|K*B*Z~!nDT5F>|H~QT?4Dxsi_$WPXmCRYVOg`!{My`A8 z6R8C|VKvnaAPc~c;N|Z3WdoBD(AR~oPz|yJb%@(v=q_K3oQ6D~E71}|c0bPh7V}Rz z#Mb{J*`vb^IHiW9bhZI;=ccvsNv&Av8Sa9Qp90 zk2KztJisv;#D5U$;r4@LInsU*1M%2r{6E(Lz4FN{tP}ne>joloz~4H}1ZQF`<#&(+ z?7oKM0Fwb|3osdg`2yVdVABIK(o$tUY=NWJd$vI43i>LqSU$-pl7A)^NkwW1(iV6I zY=J$;&4xcP$=A0H|6Z&ub#71Z_GE8vuLLI$pUJQfur4n#+IPq_x$C8#sMp!jw-Fc* z&|Zk`MyTVZwnL8->fP8vbNjQ#-$VcCD`Th3gRLVO^2bW=I=}(sA;rJS|Epm4$GTte zApN8C{Re%2w4Ipz@6Z2?{|&DHEfxRsUB-WTWd=N_lMhdkBtMU14NSn zOb>8EX8Q3TOwNW-GX?1TxO%%utKY*X%<&VCSxeJ#f&oBAK(?wPy=9mAAdcR&n zAnw1k?T`3J{qHSX3;g+CY`gg9Hf{X0_0#5G3%3978V5wu2i$vQ_m=o) z3+e%$KhTo_yfp%zPtf85Lbd?-0}@hc3p^xK{J6k))DZmMqaEaRdIorc7WqI_Bt3OZW10zuaoD{P>R6-~reHZ)hP6LL6XR;P*GT zgg!u<;yZHrAKOWXeiMNARmvx{LxlW(KL2q3x4A!v|C~JJ|3UtbUB}x06#t03)A`@< z4-U|^zrF%+LdpLeOSJj@@xJ>aV&6D`J>!X3x0#)GrGkG_$pN8;eH_yqCMpwnx&mc(^Bc^X zzf4|hGemxh8a}Myt+l*>cdw?e^{DNM+Fq=WRs;2ttE1ihw>~m)`a)Tq>E%ZW$swxy zBld^)0{VXt|CIj`TVeBmI_>{$+O*;NU(^3>{y!a<=lb6h7XP&KGd}Xv(@rfH{*4QM z_q*R|-DZwavk%z#v_3d~Bkdb@BlY}Tr1yfy4e#N4<}p8VArOsP+Tc1j2pS z@j4d#J;8fs_trSTRP#^)5MEs*)bDpW)8ow1%C@Cs~!$Qw>u z;2muSg>-Ni{~q>$bzm=*9H2d7l+&;V*FUD~m$A)rDGQkVKwBYg1Rf6zlONyJMyh}Z z>fBU<{RXJ*NBq}s^>I%BMm-C9GB)VurV4OyW#eOV`Z#BW*-;P0^e5WHu0Zw>#H1IPii1w^t1TD}m>Yo1RK z^Ij%m0v6I1xb7%o0*W9RM11HfQM=s!bW+Tv8UGf3qm{TFQWMyT@OvnQ-d@x9UfEp-OuR+W}Bi8gp zEicqZHT)|V;8^V&TWYPZyZ+W!rq5e0dAZs8{c8KenqQm$>HEv}Z2$Cqsr(Q9KPvu# zd&NKWfD;z~96uaO^!-r}U@ey4Uv*W?Jn-A!{!Z-!J9eWeEnfVt;MfhfZ{s{3eR=TD za~S*2H3QK&pdoyNHT)PMasYh;<^BG6ooDwXc;A8?U^0Nq0VV5%OV$UJiVLFb3%z?% z#+H)2;jjhv%_)}lhzopV5p01Yk4W87N2Ff3Mq40`*Zgp1DO=#FSOawF8+~C5gg*}W zcWr>gHF2kQNMK*%5)gl@$8g*@fO0u`!E}VuHbeLgGty=C>I|7QbBR3i=0L1D`T#ON z=6&S=$N+ex@;}%8k{{VT{Co00IDqoM+5c_+ zXZ+6@hJVig*hi-?)&H@tIlwSS#`0pmkni&YUAuPu2Vk@mEg$V5*Z=W3o8DJeAIrJK zKU)Fh?4y{(cVjN^_u1#dwZpiSnA$IX`AhW|&<4d9ZHvM2>+ahauWTPH=EwoG2ap45 zH|Y`9CP;rFa|pcKXMQNBV66Wq9_RDkxcAJ(zsUi|aDeX*jBX2LOaN?wc?Uzbz_`>9 zge~ype&b~|;(tvCpiJ-1|HL>OWq#(~P%Qesc<;FJ0b~SU9^mKsJth-mW@bp%>P-1! z^c-pQR1eJaEl?wsJn)gzAQN!EaVxF&$r`FJw;rxslbh$oNS4I?D)vM8r~ME358D4N z{>ShiR{tmd!3J^5{}U_z%i;(3eZ#-?9BW0uF6GJ2UAoARfBfT^IDnjS(M1<&`~dls zufrgg;5e=<=H1U5&UnnW%j!)IX#3?7T@w(_FF+2U->_xBg~}J-#Ja(7*X`n;+b### zvosD+TY#TCm@xtQn@i^nkFvT+cem zB|Okd@y{CSb#HDZRj&I$?s)lgS-LV+)~)k$DEe`~#J=RLS*!NHUAuOL@bB3FT>sw* zYyWfQ1H*l=CzhzwGw)1?j-BM<`ykkFT`!~0^e&jVDWdF0}TJv18j|u8yg%hE+_+7q(5$aHf%tCTwpwE z2%hwKC%Fy2c&9Kbw$l+U%12gnyHD^Om5jetBrKEO6PfpP(B zqUGgf%M9rF_rE?s>m=8@iS@lpuwTFN$6mc}^i{6=P=5F4?lN`WO3B~o)mJ1hQb$57 z+zR`@;s4N~L$3Xw_@^GA{(tmg=YP)qC+hqk_x9QHVm;D%<`?+zbI&&zE>2K60I>s~pxzMehqP}*`o80NoWI#* z0apg#Jth8)1490QPz{07^+YW$Nbv`I{GycAGE!}U@v0%%Lr!`86X`T^rmRkbZ(n2Y z5r0E_KQK%CyZYa4&d2=E*jqpUmTr4I;K>BMPSq1Avp`=kn?V})l^Z->$bu|Dd7%Ag zljY(&+oM)G^?}xkdk_B~O9SA)!L4n;5v}AWfA~a(Pgx?H^4F{GAHx5(Z3Vk`@6HDY zq&fIM3wRIB|G@VNJO7)^$UcboNc$Yug-OrSc9Z#N((c(TB1S-U1jrq551W-s)Ws@Eef;1d9k&`HR+b+XI^;U(^w7jF+d66+h9= zCkJrt;M<55_SOrO)&r;u)I$!Ti~iUb^W}P#1EPK3@p{khOVAF$=N04t^9e@M132d@ zt}q`bA}(-b?txIQpm@{}{6E+NpY1(PvJlTlpFVB*t_)C;cO$g+Cd31jQOF6D3CIVg zACLod8@=W2WBpE(DU26#bFuyr9PxUG(NY_Ff2~HXblx}YH@K~>G`OuDj(;rYHvWC0rdTIZ6IR;gZ2Q%5a0O15ZStOkLDmO>pP>p z&wEHc=&1hU^8Ml6Ay3*mZQ}vsYnKB;dH}fE^9h#d0m0bdOy~h~($x_0G0j0MTYsDJkJ)NOM6@{IKX+&xWKrJ{6G$~Mt&eK zu+IW7(8kwm$ZWar_73m?_#B|v_we7~w)WtFHu8hpJ3<#&hP+CffdB0UAOMyB4&ssaRL8NkO!h+tD^6b0~q`Fw@+p&2L$oY{c4Rr zl{dT3@nnF5<;y8dtQ)r((&-;vgL+2)&gbqu zd&-wzeWkUsnE#4v+bJs(-~3*C7U5(7p5v|$uo@!a;)2p3pO4SlrM5uE1y;6(AmajQ z3vBekmzeuK{F^V(!+)k?m6*;3&ei@-%=^vL$2~c~Z1g;jjriy1*k|YX`Hw^P1myFv zZS)&Atdnswm&>mn>?YNq_uJgh`Jeb_s|gPH(LG&c>ip!weS3BT{{=4oTVgDq32av< z{?RPfhRti9F_xU4`gG`LD~bu zeJ8K+D2dU3@xE}ZvX%w?DGxvVh%j#Iyz|bJ(@#G=ru%f(nP*DFh7ILss0shxdoAQ> zQBkSfO|C8Q*tnq5G5|3~JY^6sxX(*q3mg^C8iLRDl#>t}+-BGe$wKT7*ZQb0z_;6H z!k%Y1E{%J&#VbFmUY^JOHEN@Gxq|nUwgU2@eGhxD`FXvtzY%u+1O>N}Y5BDt0%-vA1f8X9?@Ne_K;U8F6{Cm(>>?{6Jr~8Dk{~M-b>4Ufr z?kf*q+!tZo)7JYQ@{?ZtqaVdIzKsK{XMWR0jc%5V3C-R%Uh_U0r2aNmMwrj84 zj5jS_6j0>EO`YK})c^0o4f~P;#MJ{@!mn>M31b?HK zhljR0jsJn|fwH^K%r02_7Gq+(12H1LS}e z$r*CXbDx0&T6=T9kNpNsI)Dc{$XVC6E}T3+t>8%E{(SfV(vBQCLiu0ke;@y65&ytE z*Z!VL?E9_M{7*Rm&p4LX2|fQC514J5J?A{WXe%5r@C&&Gvi+&2#m*RlC`%0~5edUX(tMxY) z?A@z5Ee3q?g*^52GjhchS0E2#)l%0U{4Md=@7b$&Dg5&}gyR6S1#X8vz#1E@sYeWv zAE+NNK7hEPFFgr*z^I)4AzNTPY6$+{V;$x3?qeVWq^Up9@^GXR|Go?m!Z0|1*r$!c zcwmjs0m==f_yKZ(=?i|(@A3EqHig{Mw#AJb*GqC*mNfb+IN(P31AN?jeMhNwW1GUM z3)4gR_x1l{@DC0M;U8G%3pAiQz<-tgZ@A}}N9 z|7V|lw%mEwUF!Q{-m}W!J=|wmH>eHQ3pMR6=lx-ToOka)ZRgznsn)r;wDF7b=wpw` zFOegGaSrkLO|g8(go%^%-cs(e`)$v}l>^KcxDV$wZroTfS*iZ;;DZlJw@5C)-{s8i}Cj-z9c&rQ%!ac5uwu-grd41N5 zA80>uWdYmvddL#c8%(cI*#Z0c`5R@$s!aL)UyuV1GJk`n-rV1yNk{CrEu6L}1Nc7> zb^iD9e~Jfr5C7x4lp^u@&%C(a`N)zf(tG<=DJx&p7w}WUVT-D3>_v($m20kHJi=crELE;+gdw&p-cs zVDv&c_uO-(PMtbZty(oX<&;y7`Tx~n7opvYdY0W+yU)hQx=@KxjjBL1gk#ZOTG_1;DaI4B^$Ot*B79%0I&nlZb$i@m^J(p>&#`a z-Y}1C?i&YKPaaS?A)q_h_ZT;r46%LNW*ItpxnjTWtsO%6pS~!w;OOD<a(wBE*Fq~6A<+nZmE`T^@lv}xN;uDk9!~k!P~U>}FGqo& zy7%ZQZ@%@Gy#4Qg1KYdR_UPVEyZ3xfCIgTIcz=0sWBHxe-1mgcgt?Wrz^9Kgs4c#I6-*#ap87;Z!OCkMbzLF{{NgYNVF5cVA%fx3bFo_(L4OW9!u zIG|~q6|4Ez7fbG$0-fV2lb{=^f~r*A*hty!$&*0YW(b?KiX{N?Z9d~S{VyeE7w z_`**K={&)EY4_jd0N&H)&2NwGcbt5Ryo30ex!`~)$Qc}u8iKS1{;Bgw$!2aY!~|0Q z3&jJ0|6CbBv1;?bkAHFi8}pspwsC^V0?HYVyuj}v{)u_hANZLK8@+b-YlEbEqjnzt z7q2cTI^y9UdZpq23_zRmzv2HhJbRV@33)W)m!0At+p)g>>+$c7VV4Uy_KEQkBS)#s z{_9`=TF$QGk-J_yMTCO`1_M%lV`i_Bkf zA*Wo`O7UNOw6HSxCyZl#ozU|?dBo-!8+YOQ;24c{pEw|vh$+5~U@|QaHtZaJruD}k zR~Y-Jop!2Jt9`DVTIc6d?PqsOovUBfc+zw4`Aljz|4dqZzKs6@*BJkJ1p5uqdiQ?X z{Wtvc8PNu4x&{3Lv@6j5dH3D-sNE5RHtfAQtx9$2wRDZl-sRZ><55G9>x16?dYa@Q zpVunH1KHYOTNjj_TVe-XyMDcA1LXXl@8O@Z%xua5?D5{lO&%v0&O`XeXI#81k8H%b zXgt1o%Vx>UT_->JWB0~lQ2 z`=sEvVHhSvmr{$8L{NxxO`0bq!%QU2ToQ9mC@v0%%05t?> zE=xj8fR`IMBm)F|0o32=2V4(*eWO3`6Z_0@W*ordp>5*@hc8T4uybtR`hwj%lo8jD!*}nB5o^U?5ZsVrHox2JO z4jed;zkmP!RN}uwhYl^ke^KXu&fosre?rXv90Rd78$XeH8)wmacU)FvJ6`y&?DtT< zXYNq01rEvp=YRv4Lxj0Rm{;iA;DF%0@NRWPX=@|iEOyRpZz~g4 z$gsq9G6^Z3xklKnA^1=wY6xEbug?*ep8>mnj^_`|L|kwLUm*4M5dQtSomk$4m{J$( z?za1$9S`;R4sLH!9@tz6y+0EEHTMs2u66&=pM+M*{_k@D=pg3TH>&@~ihZ7M<0n!d z4fpn5a9qZ^Us>G7>+{6)&Vqe%%ge(wXIPK}EO+ofKb@m;0A+#7{BPc;SU>OHTf6`E z?2hGtoq~MVvU~SVp&q<*=XTk)ZHsI{ob0Ae(7V@ToiMPuIx9<((y>N3&NT!X8+^~d zyU3IYHGhW!$0jqk+{LePNd$(ShU_9lNH&H6$8ifeAWt;(X8!AY_`2+xrfb?OzDqUwbZmejfh8 z>EQK)2eE!~|6a+=%a_?pGv(_^3DR}&0%_ZSwzMBGOFkR5P$te*HEOkg>rhKlp)EZFr&l@!or2dl(gp*YVc!xxN6;k7pb}J;3I2!#=U@wfI` zDPMk`iheKkfPoWNNu8!WBw|fL=m>v!bBwInv{P7nsJ#D6aewfz?AWwS^3(gt`ownV zKg0fV*d-2w2a3!5uHgOUv$5y3Z~s2^2lN`bTpoCPl>GX!&*hxkyGo5)I-(&T#?9@~ z7!%h<&S~0Ce)nj1d9n2fnLKB?QNL$K-#eYB3?DL4H^?TlOt(H!|- zh7KF<+5LfW(?zJ4sQ->O0M7q@uQGgS{@<>)3&lIRz;Co?*tT(j?fYC%xNqP7-Gzq^ zZH$Kh|J3>afB27$FLW&-asYiiaEYHc^w`?MlnZDRy!x3zvMddScVT5Livu{%@7kIo zS+gIOtgCRL7u#uKSth+kL0{&ZROREhsvy#nKA)&#N$;% zuU)53;7$E@8OO+?vOEK$A#?t)B`M*$F&0}{(V{AFipH`b2xyu4!$4c z0{=6G`}XhOjlAC*Ap?|}|1sYO*Zv(d|Kpth+5a%MY@7vqcPv(9yDZ#%8}D2G@D_a+ zXx%{O1`Fx}+-Dx4pFH@vj9-vNFGE=00ZaLzYHUP5&kOT7B%#H^cK=JKkJj4P1+|T{3+kIXze(>MJ=L+}lKd>7);5LE- zQYim->eQ(Pu7NUtdGL?ZD)fqI|HB%j;sXcZdxH#a@f(%xqs{Zd_sk|>a$}@Ei1l#$ z!LbnZdPP-h?q96&st`~CY5qGsZrt&%?R2F&-h zOEB-^-``$2sJ3KGzD4%#-HEzN(e(;G3%l2ahY!iB?9Fn?{jgg!ga6`AT+qolEfRx=bW1ANjSos=)y_5q^`(5~TO4X}Z z*Bk}(3A!PQ0 zy$?mie?rFQ;`U!HlKltv%K@~?V&Cu_#Cci!k#KLv!{I%6%*J3vdlxIQZ1exca(+e5 zB?r(Z*nLC-=G0zN7xei@agabwFkHyK)HWoYEN(84}=w_&VPM_r?L*+rHl${?Qjg z@86BQU+(;`_{aYz{%HrK?eAo?lh8`}|FB=tS48~JOI%-k+I1b|i4P}8*19c%*rZS# zS4Fv?EUbrn-*6tO4`MyszB>*pvK?)Ve;3{(mN~DFn3X9P--k5;hzAVE0gM~sdtHaE zKoKOy8F@PV{zJ%5FylVe^@CUsw@;e@Z2zR%BT$XZ7s{P#eu!!A<4 zSvUFFgT3YQNBYW7??>IE+psRaQG2QH=g4W;v^{iyw#b#&O78sI=Q3f&Vws$~M<&7+ zz_>uShT!dUj!KOY5$cGO14b7~-C_Hr`a9!24tQmtRDH1zWPm<$W|P0l_X4>}7@Iv2 zxzcH?^J0QMJ%F-6SQ)@@j``nmpcxO?zV*I*5Doul0^Ev!Z~)i-m74#te_ZDOg{w9e zSG%RNRJ*ye{OZxqWzdvWDhC{Z-{yb#k8b<=HaLM;rj3ueLx2C&*I{#rlN0DKsEe9n z11EX9z&_aY_wL;R+5bjK8d5*1>~CCNIAq{I+sF>SeU-Iaz)4 zYjd+?acZ`VienAIs>mBoIpCCwe_cujc=E}o)Ylh`33mMftaXIu>c(x8vdF`{1#P%=zwTz70HA^nX$g$lbU@?s{WXL=Iq0 zvGecgBh#1WfiqA?1-g93q}cGEIi(478|XLX!2gjWg>wJD$0BBlaf^okUQ)k#clp_$ z`bkFKW;u#6wP!ElKnivv9%vWr5xZm`+Q=D+a^YQFe!SvIRBsa`LQpKsss& zZjNgW!KyFxlC%HxzI^XwuRa269xPe5OydG-Mto51gC)AHx1Og@1BD!`o3;3G%>C9_T9_zee4%9lPc5;e#>R2g|~``@Y!=4Cj&h zAlAd}yI8Ksc15xHZGDcq_O3nqrXMr|FtD& z{xfCj|NQ-oS=_jFw_N$uSCBhc)1U7M%aj0QNQS4|?kVn)gR1fBz4TRrD26|EK(K z^S|e(p`E5Xo?$ns4j#Di`LAWzj8w=0`%sha0Bi$g=l^i=UEb|TxVPis@E$x?5zM=v zDKC!snev`{yw79oFY^Js)NzK!2!yi-Fix=fYa?XWz9QL{^@Xg2UC(6vXg&4+Ejgph zg@4vJP0HGgbxZxAd(bus{QDeG6aT-@=mecd$oE_W=&lXg!nqVW_l_Oj8UfnfY<&RZ z^R)gRa(c$QhTz$#Ay^AB0j+utl$7K|tO@XQ1c7rX0~iOGEuh2>SfT?2<$vOyzs)#6 zx4DnT`9Bu?1MDIAbGxG8_dgpp|IdPCPyctZ?{3p(QyY50AKn}(Qx_xm4D14j4uJ=v zpeOqy5U8wc*LY>vB_y-5nf<7>HQC2DZ+u9)F#ghR_ z)(27FrrgcBmlz}mFjpyS2#yQZ5X>%?Hfcq2=e%;*0*4=z8t+Z?ZGm5COd!_=G;2Qs z9Frw!$P>V|*(L*+9uQ6jpgoYkfqZ5hU~|5U`4Arf|JX+8-=W>`xrgTe&Ye3`|L1&f z^MABA9nSQ`Q*&F28Rz#hSi4LTW(X4$Z$h5BCL?TYtm^vW8$sX*Y7Vpp*ynkM^Ju-{JyP#txgy);8Ry^Sb2tZQuH7cT zdTgN7ZtC0MOb$m+f56Wl?k}r1?3F|NH%ji}S0rWBrGD&hZO8<5C1uQIvUb_qvZr7R zuwV8X0J|Uj-{JA!Glc)cj&Iq;KQW^IG3WtYBTOBevNrVqlL2T4;F{eGFekG|Bd4x^<9e(A3lKfS)1kgwo`PDuX!tCvov1Ivjw;u!0iT* z185r{4=`?E_KIAMAM)%2v9J9v5B?)z-aTFs%)6hlv3w%;^b1U1nk(ns?ajM34_jXi zvcX?In5el884t93N49KTGex#!50heIDLQQfore5F6298jpfh#_t(6v#J#mJdH~#hhl&3UKK|!9_&>c2{NwEkzaq;2 zM-cx@E@u4Sp@Rox(t_2}9(rf2es&LPRq{F0UKr2$-^F>P zZ5PWG*$$5B6Lp^)@X6rizCEx+4<`pu9joI6WX_2jIL6e9lAc0P}+| zPe^=f2zHmVpXw|_XD!oQL8b?o48Z?S+*8hf=;4P$*asqYo3&^A^zExT-E8h>Z~W$S z0pNdlcX0{+=XdGS#peHS760U8+V(kzGk0UJQ7h!Pj}K5^VBMxzvjL1pYSatJ7xmyX zxa%JyWZ3i+*}kg)wkE`%@84f89*8FQn|>b&_jbG@n0G&OqT)Sx-LwVLANNR$31Msj z9{z85eu(Tqj*)|L${kJ{WcKq z*#jKV4RcRdl_6RUSSaavTlBlQE}Ux(?fF|@+4;XBm=As?9ys_eeFx?HK4X%kW|L!W zft;6bcz&=i1C(ATSkd3WID{`IrD$7_v`#V+bF^bO#}IOr;<=lgX3sj33=qy1X#PO@ z0?ZbenVu#yGd6~D1;wj|;CZMaI4@z9WMr^*nCB0m9>6#N=5fB_iqdhzz*i|wU`^Gj z)23^VSC<2@?aBYe#UcFr^M5q_6Tg*xm8t)`xDReK{+l+!l3Ml{O*^ML<%@LA&DV*hmApMR_}K0ka#=v68F8z0a{ zNZhW<&X<2d9{AaVeZ$%Z4Et`+*a7mu1^4vS^#xNGXRD3y2z)|&U@LUTM7V8t{Dj+% z74O0G%QLo5#B(SEjGv#SF}cCG9L5Dc^YN5&;lHB4ozKedwfo%c8JqmaF-ARrSP9tz zeGVYzTwg#bTOjoG1!;MnEfBRsf;9v$s$dPlvyeCZi|LECc0elQf}n4hEs)nkx!|g+ zuP%jopsm#YuYUC_l^OWEeL2A9f7Acxp&s7}ga058&}aDJXY=IpKYy+~K)s)K02lYc z?FNh`f{lQDaQ7P{HMV%??mbcj+t5Dni;at5?~WbY<~g=)V=Pk7^CG>+;}sp>-}TSw z{EK<{w!aTQCMJM!`oBlKe*SjU?}l!EJbsIN&+S=RZ_hZI48Yi6#@kywfXM)+2c*Lm zm;qbh_{{CPM%eO(_eNY`yYwQte}0kFDr1dsy%B{{qvcd5F0ijKE|4_@SHaiFxIohb zCC90x1}&2moIC^*hy-LJ^lj^#77k8j(xS6UBTB+T3YPVafxUMzjbCdt$#xtceFdAzARlOL!% zTi%XX{!h4bH8y7zVsn^_3;utX1E>Q`Sdd*V9e}X`nm0HUCr~zD1)oE>XJ;J1@nkUp zt{%YJLB;{D9$+$n7Z;c+Gt$<}Xrw@$246rtYY5I-o-C^~OKgE!Lmc=g2a%&@&YmN6 z>(z@W2Vfze^zPG_enE}@bMf!%|0gv5z2^qMA#w$f2gv8+=Vi!4?~ax8?&_g>Kjr-( z_JjL%5NAN$;Hsy;M4qeVqC7yo05Jr?aTDARjH*k5NH`a}Ahkc+JIc1rLA~<$zKUTa4&o}%8 z$G#metB>|M&i%Xg>_<$*P>sEF@n_o&n6nE$Io9@KyxdQ~2lu==S~?+)k9ocM{p149 zG>KHOc}ubCwtES!)Ioc!oT}l85g*7TdL$Pd`Z&A{RV4=F3|NtDPt~| zoViaTm+(@5eekieK=2;B`}W+}6aSGoz+!^7BH!L_)DGH(JOMj)Y?ZBBH_PVD`C30H zCpQ=NIp7lhzy-_|w7#fR-tc(T5d3V)BJ~GYT%hRywl-iVWS4gBJ4BQL0Al&cPkvgs zY15|NC;=J7e>nU92_yd-?yWa{t^l#X^!sw%2YrX{_nn6vV*~tr@XQZc9v!~SG@fGW~JLcoPO?iGIWNhMFb7BQzaa=swHs@)|1=SI!bN)TOVZR$D zE&DByIao)xb?08y6PVkN_k;Wr?wUP&!%>X|e7@bZV|;+;;5zCeH_x1eHJU3l_#Mm@ zzBO;Wq>TKDt{3LoKrdf7a)fi8pym;{KsK%Z(ytXz`Z}UL0~h~64&XR)V}iJrK4;w~ z=`(VPbo_FjwE29NwCpua-tIa{UT!m5CeBWfTyg+pfQ-}>*aEkfvIX|>ZGjIhERxz$ zZGlIn-pIpJ^8@5BeSMHrdlhShkvII5Cy*QO?f#OOl7{)e#123^Aaww)QQ}%9$^lRR z6?p{%egJ2`aN0ChIgg6}6MFtP><9aUkOdeAxHf;gbQ`)%u7j>$llH%I*a~SQtc5x8 zj@L)X6!;CvC(JV)97EB@TUqY~$9_EbMUG(Y{cN3U4d1c!4UtcS>-iY7$J`oMKl8N^ z&tFYRl@%HJ$_JDSihbJw`G#{o@jnmsH1B(RtbRWDKkFhc@QS~DA?vp6R*rCgGiClA zo0rR~5f@9+;K=y`6Nfaw8sc+h+qxOY)Cwr;p1aR4k_1xDil#s&7zmuhbg^YwrM z$QRm6zV~Q*dGpJuvL#s@R@!2E&a0Qx1UpU^&W#GiF4>zuEu)Bk^y1 zKwbXm(L=Ij`);)r-uCyQ$U8`UNB0?=1G$xXq?k+Ynbwmv$2@g{{ooajqq4?YS?{}J zKc3sP;Za_1{#y7LoS;1}h|%Ca&#wc&o!Jg9_(LDL?zuto0M=i<+GVDE@YzCbfA2Iy zmLPLS8S%nBp>Gj8aJWY2!YpW~hSAYt-`Gfzwfcn z7ysOLIlyE|&Z(>)IBrg=)N1^RS2v&;?8?j=-n5g{Mh&qMQO7A;CQA|}%eMu(z5vq$7`K$P zDn%~(;Sa-cz%lrD{Qv(I_~&ya7cjP%@&Ns!BW9+_otWFX&WJKWc;BIyN4_Eaiq#u; zg*~CKbogos>g4WK8Q8{CdB$DvoOt3r_+4CwlfSJ%uELxh%8M6}|A|lP{p@K62x30k zK4k;r19F1fYUBHY`o4V^uZ1?p^t`Qz&n;a$lym>iEvsbJ@QWeupIa(FNcenV=W8C3 z?Hd<`YK8^xakzcM|3T=8j6Wy{!vWL-3J>j<7urqI+JRO#K$_V*)B$L$Dh1hJXLDPI70vp_1*}0@WWF$P+^UB**x; z@e_pk#2wk6+d=#X*Z(p8|37;EH#xw1+6s>%KAbh<#?Hx*d$3l7JfQMGDf@tjf6FIN zIeQ|3L73!)|4J=HnsP4Djm&1%EGXep_A?<&L z>sWfqeZ)U`U?1edQM1ye8Da>$_@J)7ov<|ijR$BKpuF(!UUSvI&f0S}w#wTF$A3Kc zSra2`{dVNw9dHc(IagDM_g+U;Pe{Xj{MF=C&FRZLKAvm<*%R}1thz!tp5XUz?N6t{ z%hZ>^8l%DAOZ;!n8d_$Ju+sRiBl&5)760YE@5DQ6gB;~h{3HLb*8hxvf0F?~195qM zWB$KbaBt_D-WTElf1UVOs6}$)^Mf=dpdQApVLsS%KC1>j^FM2I|K^D=!s5T%aMbAn4){N5E1)c{F+i~At=qgy#$tWW-=NFW=0~3Z zZGhy2pdUXR*4<;Y^;N|^x*R+-WJbDTKT=Eq>;7(AKRc@3&&58sX#=3Ifbjt3x#xUF z^YN_6aWxS)UY|K65c|;paiN!h3x0H8AGHm8F=WU|hxa}FgYO{&uzuK&?(HtiR%K(I z(B@EFU`Pg_9)Mhai7BuJ<{gwN%ohj_h`om3X4nFUJGQ{y^1VN|m%sO)ENhT2FxAf& z=-L7;Unp}0kq551=33)`ko_Muz!d*}{vYQ2GyUJz|NK|Y|1Jk855P{yH38dp?LkeM zW%A2M`XlEUYDfbELHz6fT|I#d`pSCg+{Up@?3HIM#uM+s@1pIG{<`a)9jtS>i%HJ^ z@AR4%!hi6yyneIgZ`rv=W+$wbRs$Bxov8W2JbqliXR$=&1nLu<->KVko9lk{ee}P6 z{0m_X;k^8v>KoV(z6t(6+uwf(bwUfU#%0`<<*F4#zaZlQ3U;kSosgqt-gC-mV!h);~EjRa~VU<^(1~P;ef7EwNXcD_1SEV2?)0~z-)nB8!$6< zEo^~auAq3-5Ipl4*jb+KD&rR>V11Am7Z}bKNIii5iq@`DR5Q2pQhfB(aONe+M@ z_T7E138I}pd;K^(pfo!vQ*mdU|stY>1D4I8pLZkD^H91)|FVHtvr{&pDko zz}sIx*8WfYKhk1+MEtwga}eJ_A0PdCw3V%1w_PSL%8`zPmdSH%r)nJ_#t&Zi>>!mD zZh|ay&p$_NtnsKhnX&=*f}bU)xZfLTn>xUTq>iuw)F{*b$9%!-5WF-EBZV`{Ni&Vv0Vx&+Q62Zj5th{sqj5I!2#|4jV4T6jMId;`RO)~p9$zbU}nU$*Uq{6l-^ zFaF$LbvDlTVL5=ub^b5Ge?7UqV$NCv!l=e3I_+0!mmgk~B_SL+Z%%2(VdwIR#jt7rXp6B{O>Koc3 z!~_OE7j2*GefI9%Eo+wk6TZL;G!}q*zBa#xP~PI#an4TJ$8*bRZ=w93v1Y40idqts z1DGd@d15>+ARnOlzMlqozc%E(>Zniq6Xc(2GjIXo>~={(K>>0F?$9-Owl>K00LvM! zwm{ecI5(#wE^rF+hL1N}AoPKLImOZ*YlI(LTqJei3#iMQ!Qt9y#08?&Y&FM^3HU;) z(iZq+7rEs1KC&!1RdWTY41oU^v;{IIh%yT70foDE?J7VGkbE?q{~i3h`M-(%|7!dj z_N{k0fVMsA1oL4lzWK$$Dhqgi?$R;ysn|5-@`bd{U|%WbMs2sklayn7rcP>ysS@ZkNCh9-g>|oux}hezX)X> zu6=wKdFOxk=G9M=-D<0(1$$RUV)vcNv|>aE+6Bgk1B z6iyGI9e}ZV>FMdR5-|ZIa}R`a1y#O=U~l<9eK>Qr{CYjzsUfcx8q?4{7%mQ7UNZxz7UQBJXxR+wzUG-1!ltT1s~Bd z`lQDP^*5{`Kx6``i4+0 zY385em|;vHo2?BZ2Y7Wuj?EL|$^f(l&Q3+HAjcLs5OIN>GK%EyD~ja2F^CI{788K_ zLA5$3dNROkUR>Z=s3BPGnJzMF?sCKhWon*~NVdTB`I`#CT?NR6n~&z=pK^c1`d@gj zvafFq|Az5kZ|l6-2j2_s8$TEixINbxGQT9(*8cF1@EO6@?dkxw-2l2I*VVn*eYVa! zv?qic=fQCwk9~~+##|RJ4kXbmnU2qq_FDF=A;HF2Ag=eaF~G55YlDJ$!b}f{lp`P=zPc3H0>@^R&Kuqf>w_v& zL+~Wn0&o8CYv>v91whX5^nmP8o)D7(U<)YRwR?BL{{8zmq7nZKI(F>X0^nxs&zXK7 zss0zb^b2?rra}yE>FAH7u?}LpO=HPJkfUtG| zUk}K@8sWt$nKEi^{A&od9WhgK;15hmGY-fqRYMSQL4`Ya?kd=~Z{J2V#eatm9o`43 ziT|_EEC#?hfVg!Vb}RJyM)2=G8$O>P-b2qx<+ZK4UW&F`#!0Z{!$uNn&T&3luB+hZ z$r(XT;JDzpVZ1;NV(sog4U+1JC80j)VvXBeM^p`Y2>uMe+xFew+JRWcdqvNsj5}dL zmM5R6&%$h6@YADq%9oRpP=6_uCsKbWf3G#ohf7`8xfk}H-4nYn#u0`e-W%f5uo_Pu z2;$xTr^{Ey-@LwXjqmfC-R6GY{Z4M%d;Bi`cHRfOKPCf^11wjV8xv%a$0jJL|00hYy=EEnba9`$9YzCj_}5$OCqb(SApy_YC{w7Z;Z{28^G0O>D;V zrTWbt_Zz>nw`=G3xX+f)*BXD9{T~}^`hPf&`1g-v%*_{C$`-gPRYv8+xrX3l9p$l4 zM@eStDvkMJjZozP_yqi10fpPPZG#1%Ab-!EJ!!}RxS)Oe_AP-=;{R;4Xg0vga6sJg zZ@9PT;64jJL&Lm>_l>d+G12xc7$?p9K|cwfi9Q$lX`IHj-1eS-@4%mH@g2yufbSv> zY~0(}vH6er2wB63^#*7|ZpipCjV~)L4|wZ`hDkQo=P>3r(m0RxxM7MtF`_ap<}F^= z-ER!n`Tm^S8PASuYmRj+jXTJ7Ph%a++Vxn+iu;7MEV;NxIeB^NOU}l<%EG;J8;@lp z=LOH zt~ZVk=Z7KIiD|}5Q#N7^g~Vi!CleBrBmps!+H>34=Q%u=Iu&CqxUPfWMVT%SG8M;? zCmWQEcaFgVkSg{^F-^IQ=FY;a; zIIvgV?=u^|?2f?wCs@PY32WFpNWELz%K6RP%apk*B`?p*WySl%`Gdcm>&cVR*t^?& zkLU1QjxpX}as)ZS?hR!HJ`eJ-$pyR?Vu;w~HM-Xv{5|3JU0yRR+8E&f@t!~AJ3Jd+ z2d~HFd(G3~^E;oV$M1}pVIGZjA#Ue)x!hhB{`vcq1AG~Pd=iuabR1#~rNT~^oSY!Ws|<{(u$fH5YN1-x9ijFbQ6p9g3jERmuL<=u5L6vVyFiMw{~ zkkK;}v2L}kG-%RZ8r+8d){mt@<5p7jx)#!@|0GFGOOd6^SNQj6vCPA7=Y8URDAjO` z@6BJh*uOUzZwV`vQ|;b(d1`aL{8av$G(IO{gx3^_2ROHf!#$4~_IVEZgJXhYf!|5I zZphD9+!OPhYspJ|{>Sh=#scHwgZ$3VklXcng1bx>;P>--Z4A0SkK1?*HxC&Hgk%74 z59g9qDXU}w>U@n_TPV}EcU9IFjM!h>h~50Xg0uja6qN-@80X+eJ+iAZwVVBtdWzzT418syeo#Bk?hKH7${g*dVUJGJNjmTeeI>({7XWyQ*svIrb72VB9% zI&8!V$B^DzaC%lwjyKkQ-GJkfx#P$IpJNUAxp#hwcp$Df@ng&!4&a(t#^Mdb+BnUf z6+`@-Vc%r^ATI14ZQ7JCixbl2r}us$bsBvr^&5XE^%}h|)vkG4PWshr^583<;=U}E zW%ymm7@OI-*|3Mg|Gjey^oSj}H@mcPd&e&7xf?OJ=4gC7KeGV$MEMf^63jomH{=3h zkX%eXfw~iU+IWD^lh4;=0saR2J?vc`u;cbSIrkHL#GGNDIvQ=gB#O*UT|Q*@5zlU8`}HjYpF`g7^>00Q?Tq1IR6=1CWze zCajQ&NqK4uNHhW&dgp{!Ip89FWBVRYnh}F#hdcyZ6|*p87X+H|q7guY6{d?Gydy z&Uu@7pND6hi08U~!&=0q764xeB=KMS_ZG|6{3Eh93+v>Qv92ZYkoId<7s{56u*>Z! zvQyC`j{Pv=+w<4!{p5J(7@};zIt+ZCw6{>6=-wV!{~q)Y zdU62sZ7@EkBVu*A4!1o1K^GImkYR#jmHLp%M8F)emzC|+s<`RNPvy)j-;%Sgd_!tp z`<7h)*eCL8yWz4RDN~B&VCeVa5@gr*qq1?$VaZ85B$>E3ygxaqhh#$@;-I%-Jr6TV z`>!H=cK4p`_&-@HXU$sxZ23G%`HOl2$G*!0#4Yj5=gVt19koWKs;&) zejhTxz3m3cilv_1nt<_%XHd9y?Ycc%HgDdvb?eqN!~iY)_~Va11jZ@*S4Rul0HVnN zIKAAj3gO?yKA$tk3CA?~nAjqx(yzfW#ahYad(JzYgJ#d2tGvg#y;u-HidVs|B1v4Z zUxxMCDjz+&R$gk7B~M(FArJf_RqniKl{9IXEVtE5mL?67<&Fzi$^AI~_|@t1;;pNt z;FM`Y%x-O{7QMtS4j9C`MpOnKxtY4V3lQl$BL zxHt8a(2}JYw(r5QhkueBf=_mmPlr#D1jqoC&sHWT73Srw*|RA>A8yJm z>4*VZ)Vg(R!+#B+*=zto9l&G&;@Dcb!S8>D^27I#Z}1-&9ZS*R{gi`|>VHSVKA!`* zG!OP>-beB%=N96Qy8PUE^VGh2@DOrTw3-Q-&fIV2j5Eg-7;D zCZ4J4#E?6v3!5BCy^nTgj?;Zur#3esTYmeO{+e$hXb+@KfO&SRBR3{%<|Sm(E?A_w zbj!lNJLhxE(k`Ln417e{ma;Bo!Nl|&DS%GG*P67$@_Cz0^1^MI;J{?LvD!+#H;rl} z$}P1L9`u7OiykEE0O~N@z!gG0l_}SYpNS5br$&_9nY?QPWtQo-9 zQQ&m5J|kYklI6?wnbDR?KLW3natiUq-#{E02N>R6{FCp9J#roA<@Fn>hvccuk2@vF z3lGRurzCj41J}iCJGS}1Gw|N^XRnmzKS+`dsO7cqK!NIZe17ZKZ`A)qzTy8h>{}ni zzbgZf-^@0}ajUYbkAIGnh0s4{Oq(WOFVB-nkOAUVLy&pHuXwAU%)vch1`Y<-6z1gQ z?%B9u1IhqwN=FR9qE@Y1eFRui_pj;qv;|l!FxLgRGJs*9eJn4YUvB&dv2XJk?;+>> z9DmLyz9@H7#-yB|l9~!R4zYN6<)j~!E^n=q2QOO%ye9y=3BdYFjNwFSR>^j29gdv@ zxv^@3+<)mR>G0Yb$xQP8?~$VifVoWN0qO;u@96Kz%FPLlbFOJi%3LpvUOg@k-pXX^EAbV-6w(hzIdoOWRTy?azYOPkWQ(IeWty)L54%}5i z0Rd(2y+a5gi?Byn2{U06AP_>v_5Zxjos--Nncu{={(s!B*UA0Ocb;>d>vOGho$Jsi zbRT^rPUwAYZ)Y+DJ;2^l*B-T3 zf4#|WK4*z_Ib*RfpDfHT`O4b$O_Sa4-1wbjd+`zZ*M)N6kT8zTg}(EAj(rL?x9`Z% z{?P-Lx^0);^Xz!(dexG4IRZXEpjZYF6QH)v120XpHEB`~(xud^_1K3OJr2M=zBM*0 zJ_z>qLd8147cr0H^5-WG-ete&xJtTuv1Hg{;q|Md4d30p;bQk&542fnWBTv3Y-bV_ z$TmrJ>ng0pfCKPA-|_3bKM=-QSO$dgk4`{#q-@w~Glt%42Y1c2)ky{JH!Gg^viYJ{ zvt+yTl4TD62M+FaHjC?<;yL(n-bV5G$4&{$0FQsi`%@{CaYFhZJr_6{MY}o33FpCIVmwmy6KQ&02z`2YqbU#_sT1;yefQS z`!`YZF#y&By$=w^e>euHw^aG6lK!s{`;zhA-Y32nmyqCe{<`(+#M=dybuiyPd1;Gm zw`A*FFVT86Uo6avzyD3`h8Byhi+=Z}vlrWY&!k!AK5FH_=bPNxL%iY=%s2BM$bfWL zKLYGN`L52!xq&mu!}Vy2WdQyFb@wj$@c?^cXuRqQ9dNbF59(ZsBJ4Z9l3tN)Bo2a3 z0sjXN9Z)~alICaH<2S6fF7*?wd!wX(8(lp8do)fGeG=@EZYwQjIyJylr%%2R>#_8e z{0sU{PEmQf7TkNe2hPAEnB0`MMQac%Z2bFI*umY=!hgOSr~mEh+^=tzWOrP$#NmH` z<{t4`Qn3#p9}fE@ESJE4SO$dgUu**e`vX1xUCb(kf8teBrcF21=5@R=YWX3Xxm&S7 z)eI&U`0<8(i}(z|U-!5FRh^JqUms>mlaq3^5BX49T3W`|ty@#H4jBLP%P+qs6l420 zRr4``unkb24^Uy4G-zas1t-v|4|jRM}*y0c_vV9zR;5Ou&F>bTrG)s2@=nke3e zzyC9>S96^$q<;Lg)iR5os=Pj3`?c=ANHN}6`FZF%zqSPLDj!?*D-PL*qZ8d(r6u9ih0WZ0fWvo86i+>sJe8Td&qSh%9|Onw4+wKbvYj znu3vYRNwP0b9YQ3{v#Xf_2Qp9E=hLy-=}p?<_x(_43)J@@Q-fy`ai7ud5(V3cuz6@ zVNI-McsbrI%g;|JeS`)O-wpb%3x95RL&vVgpo&e`x6O5AC2E zbPoLeU~LcV!`t|ci{j#x%X-LRYjEGKc72O@>(($%yq#b-{1-I((XCOub#D?cnv6|1|l4_4#6e%^7WJ@)6d7Nrm^>wy!u7ucs83+zqp4~qB{~V41je2Y=E#2P!kzYDgK#LXvjP>-+`?Z`1?iJ7jMVQ z?mwm)EZIj3?8QgdTBkD>TF+*h>oep2>)Q3r`>{ty!@OZd)M0m&KHmj zV9ewRZQ8!a9@gF=>ISi2_*~T!c1^X$&bj?#J4er*-CH%NZ+Xv}_Ik_ecX`#C^?1du zy6a85`O$Z6VbUsxrcjWOK*XB3){i&RrYC+Rf)G_BEQghU$IFzIHM|ag!n$ zAk48R6U@OI9&hMZ^eJ`%*j%}L11^kc+&!BvvfD3CD8&Cxm*0Wj z!Dhn_g)hB4f?qw)dcNhgBFuw(HTp^8v*>^DPfTWYA^xMIV^p7Wio^ebgS+jqUURH< zqw)4x+##DM_l>&YE*7{x-(IT88G`rswZDEbJ3C`bYR1Nmo7U~zxijH~7hZT>I7j!l zQ1dYW)&s)106q>FmH}XzR+X!K{O9Hta83d9>S^fgBu~x4d$i|L+m(Jy)^4to0p6~3F+t@Bc)mKEby(-k9kI`+ zEYrS#FI`QM%kCR!m)Y{ z9FKX9{0gwSbVZ6y9sP>*|3cyaxOJ)*Q^DBF>u;>}Xc}v`T@o+*|Cr0|m=_)G{E{`Q z;|XrDNx?Vr2Y&T-zQ;WC$a{Qq|LZx{tzEvF@Q)n9pCD(&>woY+Q~Lklp*_~;=6QDY zH%Hiy?}@YNav^8$EU;QUL$Dxf@v7Zh(>A4~r>7@8|NQgdAKibJn)d-%55NXs9S|G9 z=L437|EkDrkr>3KX;@{rmy>$P9^*-xT%KFt@_>KB8p?ZbnW zH!#4i`B^`^;$GDjz4L9mp!aLmtjk~R)&4UIv7aG4MO(Kfl8s*z`gCu)z&bUUZzDb? z7fCq@Temp9%i1M*@|)xvY?nTOH{cO4=WRsnC*t$TOPAZ6d9zi+XqT-@IAR^^mFADC z(SyF+dU34N|9kdk*u1C!|Jb0!?L7YJ2aSHvFP?{Q@ki(rc8P2HpLHwZ6vY0Eb62!J zEBO}2KmPw=f#5~))|7gy)`;}>*mxH7XTaJE5ZNX{lWS*G88&``AFP_^&#S;oWXot=R*GRZm>UB zydC|uYaQy&vq!H_vO@H=|q_c#F6>aahg-Kk zgRRS>1FXYCA6u*Y-mwdn$M?)T;~j2drth|H&7y>Tc>n7~pYBcPTjvJzY@+y`i(F;a zPKo!XtVj0->u=D<+t}z+p2tQck7lm0dNk*d@PAnNpBE7g=*O)W#|Zz~!gq$gGs@X( z9{=PTA*VA6{d%Y0g?q4$d|(Ze$Ji}2o=a0hCT!kXya%29$5onl@J|jo{{L*n|Fg6Q zSnep_b<_Mfq6+rFLY@qhc<-`*6i(f#1x`v9y1 z`29e&5eF<+|K}V(UO+6z%X4f!=!X9XZOH3k-wM}1pImKiPoFK@HtK8J+MYJsp1yCX za(P%6*zeZo(R1i9_{8fM@9Sa<9M!qj{68yhtv&nUOuOr;q1LNUKWls6TlU~z1}a}Y z&r((#w42VGZ(SPB``Xf`YvXy=z1cjApSRb|H~F!w4RXgu0#{%j82|>^>*w*0zc6=R zl*{X18Gpz+oG~XNI&^C?&w5`RT_*lnw?}_7N8Zlof3QuwGDZ82R9D0Go%2I^j(H^> zi(FuDLYmGBKTH6 z8F*|%>XyaN2lxm3XR9FxSO;Vs03U!DKy9oAo`8SOAW))sw$6npC~fz zagF(Gv$a2Crgd*V*Lt+5N}E&3`zm|AD!)@y_CUv2>rii2C1b9Bf2&68 z+W6jJJ~}m+ZMR=OU-`Gn!OzWh>u>DoD2fHhmUh0hi>2*w`W5|O68{IqE0NKnYm+&` zf0V=ju07KKk}2pRe0B5^YXD##TaCvqmq+u4t%^M2n)Kxs<$oNPEXZ{B3!f$L19`AX z-+`@1+y@;8{#mn`D*Vr#H&1(#4p|?E{}I-)*+^^OXqdHcF~UBMK44Kgxu-VI5d6!? z=pC=U==}fh3g_pjc^`l{pw9&;9S1DS2hiIqyvoGCWI(oRVHfNaZzE6flaPzf?pN+C z`}cC?E3aF5(0ZOd%Q`m}{#(rY+PAJvXIiJmGi=eEAnpx+;pe~)@CUq=E*3BjC+6XUD6qi0;S( z`o!Ma6{_dO{?H|h_FAhtQ>@=VQkWWtb>Gi+cc~j**0~}6Ih`fm z_?_g!J;J_jxXwFyazMe4`#kxHkc-~`o*HrhA0TW4g!csbIAC@2fy>0d>W^is?s7r; z&R`D*z7l>GG{NrYTveI2iia$+c6Fy}K4)0B=C$0K&9IIQr&+stQ^j}1t=$<@twa53 z)}_hxTK`SP!FT_-b(Cf9Kdv(*_d8#=h{y2*1(?)rj(?Q=mp)neFeYEINAt9BR<*W#z?-WUoQbW#QecN`WFnM zU%~$z;pgbFgSIk0)AcnHdUS3)-F|q{tRno+kxnMh9IU3SSFIPx8ydRYuT8PfW1n)o z6w0<=_KJ}#o-BPe!`83PwA{R7t`<+?V(r0~jR$`tGx#hrfqBIKn>JnekD9OjD_PS2 z!hfA%)}iTeHR=B*U)VLL4YU_OQ;vkTwGj)fjWY!6C@-kVJ|VEyD_M%(Pf}crYxHNE#0!(e+v2raY#kd; zao^ja;bgnFdLiECgZok9fL%H^oHaeTT z{**HMRsQdmO~>MXGfu|aTI0U)yczb8=N&HVPr$5ExEUUkr8&4Jf zX9W0{{Xcu|T(_qj?0WspJkSqflhlB~{^0uZUmHZTiTYjX5#67-D^@P9DfcJC?M(&i ztY;xhz&@W(O4eQ|+5gj~&ydcWZ^!eF2>-K$|1TZ(J2d^m+BY3)ZO#~IT`nDMBUG1S zuEHbJl^Z;4bAi3LF5jMAo^L;xnrDqGIy*!*K-2ykt^S`!$PO50^?yIe>gf!@y1)Lk z;FN26{k{zewa^!hr&E*3*8MwEY~z}PlKa_`dvU>9^IG!Y*1ijDc0(=@inJQA#E9#8(;HDj(4XHm#wYwLx-|1=(ZwZzkT@km3G%P zvm9NaZ&%T&#`C4`Xm$Dod-=D49$2R}03Rm-f8ZV(p);ThIGQtep40y;;t#lSl-&pQ zuL54`)OeEJe9=_l|A_K3g@485eBA*@>@{=anC zZ{Ku?wQKshU31DO_Q>xNY@YH0T`aJ$Zg@@35UkhdBdhnwyIcOZVT;QJVDvvefVTmN z17ZXCd_dL$eJ)UGA3!gad!2xP)gDaO8Eo0wgIIta2Y(}DiTAQ^j;sGaSKD=`jkPY# zC)P@9U3Z+_bm0^m@?MH+fdrT*-kO@WQTAi9i^U_0u|LQ&T%{VV)DJOJF$i5NllR#l ze-dkL>W#AwjU)$ZpuL*fA*fDSYXZ15bS65e%0Sj zzwxpDLN9SY;(){h$OZ6mKyL&1TmZi(h`j(+*$+@A{#Ac0TYC@-@cZF!XdEROxJP-{ z2lnOIt(Q%)b`8c_=Vs$;)|!sD>rNkKkN1wUEgOUNne>bu^10b}HO;0f_A+zUY$xl{ zA^3c(#m<;D%cdwUOJ3IU73{$e>RF5(w9&eKcY?L8U-Q0oY&>2(KF&7CS0t|)+aA7H zC7p(!h^~V+@Ev}pmyHAauCZ%R9TlR>xX|P3(W{-t(dEqXHlly9CXt(;t$5Z-M}w)N zkE4yFOO(wO56_XG4KAi@&nj`N81j8@EQwAQbx*N?L1x#el2?1et@ z?z^E$jBF%qXlzC3g6$1$z!bU~|9?gNe#xNG5z(VVqj7fQg_8pO@6K>z$HpfHfd4_G zo+LIZa)E2kgssx`rN_&BjME$`hvx~(-IzNAbe8xE89+S-8sE(xcJ}{KZ|WHb60xlMcqey5%lS5nMQyXoSIvIW3k z@mvjmyRE*X z*ZC7HZeFl4UvAOSnT$Qdc!QM>X^m2YcoB z3G!h_N(Mwu8^+eY(HQG-&UpDDsuP}->FRd!`}oiJ{m6tsA19W;zx(Y7Xv6Q_aNz{u zKg-#E(`U|f@(rw#vxyzVdObSgK;{9(Z*%OfmQ%$CWzEGH>(FG3wQD@e5~8U?cihPh zKI`mE(Tn>$`TgYoAX8XF2=G5hHowQdbiedI_;1sszg=<42lm$wQ!J`57C1rsgZvqS zx5eaH!?ER@BVKrRP_r@F*6@XC!Fs@xI!pXFpUMX4XLW!4!a~IY!9FnnY=E-jfPPO< zb=Cok@c;h%@Bc}IOw7s2Nk5X6mA!kZp@V4!q>v)LVm-~#FybG?8?>BDXZmw^Vr8|;B|1oS}^m3Ex@%TZl`rW zcZ{`dAU>}_AKNw@X}4TD(b+tE_U>~0fGoluLJn|8Hj)nq56z#t&8-jBKl$Y|(-;$-1D;nb{U6VmYy1z}6Plf+}*0SEetlb5l+m~yO8mUq<6bl@#Sm67L1wOYT z-!7k)XN@X~1x{8h@Y6KK0!J3c0zVi2KXG#4)Hb*MLAb~758D7fA26H?5UvXXzG>xM zC*WVUf4b_AW$)a%tDq?ECt6UqZ|(B^ihXG9v&l%813?}{B&}uLVfIkZ8QNEur+pQw zuPS>UTNe8Z9Rlz0+xU_A+ChF6HDrT^U4m?K@gB(tFiB1ly3WghX`@oDRXwfM)UdDD zp8lmxAA>KM>+}m)=l763=wEna>9S>d-RzlPED)}~jEE+lj`Wv)PZ_z%edh|vG{zOZ zV4?e6Z(~E7O=)S0L2`Z^d91-5T0tMqOhQgN86{le7eM2K2j$Nk%eRNFpCMT_tVVsM zpZ9l}=Ijdm?I_tn&;=QQ?nj0r%lU1O|LYV#_hYTjyageK1_V4i82(8}@wE=yXJ6hwzvEQF~1r7gl{KQGJGo$o+ zzQh03bp|;6w{1F5_(%7H{r=Xv$tP+b+m+vb#~yrCzL;zPVu7`Bh9I%PI=_5B{hQ6c zPb?7s-^T&{TAf3#wFjvH{z1P5{#1c|_SSOA)FF}qtbaz< z+BFsIY^bA@jRU!ptURqRIMYG9qbBXYW2UYXY0xU-idoa2QYpr6PMm#-o=@-*k@eC(N?TlqkJ{h=@xB*di|W?6Xd+I7dOa#4d}yKJhBrx z3KrvJAA`d=S~mkD>sId5JPfyX%44g3e}m7oZ>-NZ{?b+@Ymao!VdsxI+f8%9T!JU& z6&!*8-^u1ro)*4}{>vblK_OVT#GR@H(n+bb#%((H+zQp(KbIUZl>aPp*B)2SAj;OB z?}BC0J?J}feOWL4<;^n`{~WCNC+i>8Z>*bKb?WE#;md(fO{^L_4!sY*;5WjvtUIvA zPA)k)cDwhUc-}3qAq)JTB;pk49&`|MM-8itZCTdyyph(r0rA@E_m|JKsz20vUol4O z5;>APTith~Z|2M1ApS5*yvN#b?Cc$OZQaOe!*8{2FjO|~DD6d5Ep64xpFTrt&xJab z@vYyOs&&U8|G@9Jfi~<*@{RwY8S;SjTkHYiamWDb*-jTvQ`?^F-RG0+s#87}{v)Fe zeZKnC!8Yg}^k;sNOvF9{``nS0GiBd-{9ki=WHf5i@C)mC!3c-{bk*V+J$9V4*NM-K z9X~;`IFOmcK3Q#7pH}tXU>*PJ(>}K+e=^(AWUcZ&Mvocm?0#g*s8OSx%;d#By{Foh z!oTcoI8bN?0#&3a6X{-0m|AB zSSI-OWD)+~d+$Bx|Lgp5&LGNG9kK#239s)|ymfcR5xe1HuryRy0#DU%ts4xn8}%Lg z_GGJu%Wm0PQ=Pw$?#7k}`)*IUco!aqr+qC4Uthx4b>KDL6V6Y@Z@?zUo}eZH*ZVJu z$4?y;QGfjyuRG&&OI(oQazKf{VYAGYeK%= zL1K33YsSO4u!)e1;F>*I>_31O(8br^3Dd~S0puw5f?vnMmO$nn$ke&%n$unvFsFV^ ztNL?FJp5O;1@#R$Z-JN@K0nx}@2m|%r(*moejWkuv}rh0_#bXNB-6L2Z?)kgN7=Xu z6K$;YGYxsaW6L4ydH#rMj)z$OP|5B=Hf{6<$1^k4-x2!$v63;vhK-6;A1M~tsDfBv!|?^m z8OqD+_|}wc|7o2~wU47NHi%&c4G-3ew0X_~`);eHS@&Hc2zkL66 z)gQ}N9kPN%(hIR@c)VI{~+E^%+LG% z=>AsH{jC~*AR9pS{pu9Rr96|JopoE(*uDHh*7+G!lX&63q4&v=tS)eT<_NX ze$;ZbU8h{o>e9u(|7!K`sr!T9Am_BG(8#&Nw~xp-H&*cIn5^$IF29oxwRg z#7cEXb&cP#%f9`Z z-S?pEMa2QA6;$3Cg3Yx4S8rT_ojs|b;14mo^JmPRS1`2y2ln!dFW94xJYv0XzS%mq z?`T(Dd5xX-{R_D7Yu>!Yb;3XE0lq$PI0g{T1475s%DPU_|BC&k>kOi7okvm-%UV-m zzW+b|upr{OV;-)pJHYO3KUO)J`HEq!b3B23@wy+oA677FBpc)G!Pcgs z_$WdgU3toN@BTGkyAioT%#{7D#7dz% z_)puMYFUSmOMj2FmQHp>*8i(c{nTbpNVR z)ulJU{K6b0kKNQ3Q!)FL~pDTSoCf}4sZ#PWaYXhb& zC|HveV~LAYcW~ka`}m^|?U|?lY(M?UPpn6`?$)|hYr9%9;QaG0w97BQa^$za{cUtU zJ^*q6A0S*Cv?^-=MfiXB-FKb;pP8ANu5$>pwV$9tIw@I_Q|?u^&4^1=laZZ z--UjF$B7XU^Yi-6>wa(7dOoh`?s**9fefLh1N%PFKdcACcge9CcFh@z+sgikr0>_A z@tNIz?Fi+H>MYWY>lJ$%YZJ6CG)Dd*dA7+5ciPpb6BjP)`&lO{>-pm6T8r=6w-{&} z*Q%DT{BmLp#L>YjYk9=+yzYlKo*rS$hadabu@R7u!~)o_pL{JuK$(ywXCt=6A$?aTPxqmE#xk9ObhToqY(eQo&Q|ccU1OVs|EwD$N58?{!iPy z$@;$YZsEGT;+TJrx68k+x+|5?x#HIu`#{ZOe|wwob7j$Pa6ZM)*M*Q`f-*(uwp6%@n*zerOoP&om=Qy8}G zw4?Uyq-6VYV1FAiu%FGIIo*~oQ|+Po^K8WMFYS%jU$Z`s{!%pu?~)AYCLiDjcJ4Xf zx63cPD(C$3&c92TC+C>w)eQ?CLuG9gUz< zbsIb~{8MC!&a!kmBF3!`lS@mT7JOUy6g+!--}7i4}>g8|m#f`IpS=#1f!qsF+HGw~;4BXcLEiLc?m>VG$% zxmNP!&Q>GkN9a7?)Q!%DrvHqC`ZQn{+IYK&aeEq+clR`do_v<|64pwfw~Om4uEUv( zT<^WOs7C!~?0ugL;_t*Q=W30Vcs#y8xwEXJ9m!_>5Bx_)CpQP@4|e|lhK)MMu>Yqn zFMG|h-PW$zKx^GFvbileXMD#1+4!H>;17Z|l8IUaAQs3K;Qyl#{!7~bEuHU={U7N6 zKnC=+AAIW-`|T@>T`Z8CfC)RyK3bn^Lssv%7|HqJpZ&}F{p)@EeBfs`X5?^-TeQ%Y zBq!OF$rEhwpwF!D>#y3qcin02+P1apuDQ;BaLHwo0q5D3S6;oePMtdB1F|0AbAiKq z0m3l=z2$@p{PUh~c>NFlbp~O!_Pi93gUdNNIXQWDZwKXj)dl|nje`4UrT!1U<{t;| zA#M^iCC#ms5ueA8W=)DS;n_g|pKA_kFDR&i;spsTZwluv;_0p0P-gS?hp z8$EDUjrz~npM7+W>p%Ipv*(aU%bBnG4Xp>TMpumg$mrCn!DrI{pF91ZvVM)$q*pnA z?2(%%)Tr+#&dF!e6aB5*_XcV2pw?Hk-*xDrC8up?VVn?C(E)_Ut-%l;<# zlK1250vK1%-fur% z{D)PG_e$REvtf!2k1AXvF3=vt(Suft2Wwy($>x0QmZ=Uu#9PQgC3g;89pE3^Xn&=$ z(bLG^J#E7HXB`pzv$o*!$Fkpqxhk=YY|UQ6Eb$;j^==xl&LJs8COwHD@nug|Shoc-@|{#@RV_XGU;zw&=h z@B@eizOD7ZSM8@in{2V$_bQ&V)pE0s$j8Vx`P#Nv=YWrv-tQ-WzyBxyvhia_T72vR zi;r7qfCy?XYvE}gq*J)mX5x#xVZKx+bf&ph+YtA%-de`0{; z;U8UJ`YO`@@4WMl^Z!fYKSsKFvx^bk(?+!u>Z|r)voR61mUTvG|JOd9-F(2=gT#ix zzw>Xmm-KJT!@tLT_ztgo{5u=B6#ku_(Cj_beU|nuupc18{%g+@`_~Su4lMW|IeLuC zL1kaq$WPZPubn*+5q`Hm``g=zRVwbBkl^fRf2Vz;(JudgOPYe|R-ih7S=#5Ny`Bvs=gIQtYqW9) zM_X&v`JFu>z&>;Ho7N2IUgCngH4t>|znY-azsMuBrF2 z-E!qXTc$PjEt@xLjd73irjP5qoTH|5e{|0HEc@c~fj0b$LDv7H_hk=!VvAy9TpKlF zxV`ztKkey1{lOl6_#x}wwX6L9D+=TX= zPya>Nh1Q`##O>iMTmNU;%7lXv|4x@C zGgSX?wr$(2#ClyXKE6ctjZ|CF)twCUBefR8|406~V}qa;-2T1U?l+&fBf;9L-deX7 z(^N}EwcVQ2rUZ3GRGXzs^9k0uIeV+eNf)rcQgX!Yks0pt!f(5Jv@KhzJhu&NZM$p$ zYUk(Y=h?o!do5+{Dw{fKyuJR9m+b8~|LOdJDU&8xlIlqk6Pz$^w0-zqU%T(e_t;H0 z++f|56QEc?!NnK-Kyu*n<7a>O?4Jn##QgmlKvnhu73u%-@Xx+Kl>)bix-8OJ5Y%p^ z9&Gg+HG&t-+-t{jb)Md=S&9t@_CNZ_pWj%X|L^f$`X0tV_CNSfQ?461@8lG+hE6;! z>u{d^v~7%aZWvkryEmI--Oilt&h6WGV3*VXa*%jFSCGG!A)&D;fF`c?LnJxU!Q-01d<)ZDePK|`?=GEuJs*iED_venK`RDB6hkkB1 z-*}U?Yts%Jpx{E~1wQo9FV?hZ(SrRz#Q(zk04mxC1paxwMEuXk|M3y=pSk+<(%F#p zp-E&lrK$ToY3MdtZmN?ey~El9ddRJBi!aenW#PXx=EMJcJ?CTG;DohA;^yR@vM+?Q zVs>oFwwo`QXI-1ZFV*iqwWoi0;XK>1Ia_hcZ7v?nz7QI9VPmH4vG#Q%t7}bvIyIba z52{W$`|{SUPm%pR+pS-$TwQW)l-!@H;vekO{Q94ZjVOl@pMux|=jZsG`L~~06LDQ` z`p?|H^Vfh+i0hCe>G9A0ydeJX@gEtTn1ef1e>`PXrajVK=QpYzc_edLz0XMQH=V8e z&9hw{m}h>vRQR7E`$EqZ+8y1R&lL8N1+ocT^Y(z30pp!5fS+>5wG*W0)3i1n>*D+A zT1(o$pZcV^mU-ZSEnm9CCXOF#@AZAlK2;oG+^FF;S-6i~sJU7&-zJP7XCJ)xu08d~ zC++SZ{YY_u&i2EbZZ`Su1u>H6f6@h(b@_T=mE{1OfPc6CulygKLs+mdj`jcL4*!3= zXGx8Dr0<^t9$~LGduNJmT&@2V>bNkEhp}GzG1%uh@Bojq{zr}rH9GMth%bQu9N8NQ z^Y+!K27i}EbL`&hVl+p&T3_5~Bc1M_=wg7x16E3}b#%5-^<@kE*sbYYoe?rmH4TH> z)~x@Nzu^2M7atAI9}3GyuLh$S1tfK0dwalMlfWsz52>a_WR%bN-=>qwEt|DURNC){ zK7G&CS6_XJ@Q+USIRI7C|9<}u`F}cpI9ummxcp!0BXYg>T#E3I%$*le>sWt|J$8M9 zTeDfYYNgwwgN~+_&c;;L)-RRc>Up{}?)l$ipSz3wX?@Gb{@nVu^5)1v3D&oDM)N;6 zNIys3_f8Gw*u!1p-QLm_E0??dCY%e&9yIKM^ljSvd(nKI?-dzsplLhxFM7&u?ae*v z{3G-y>-5-1#5C}2{2HLQ0m_pB9{b3}qS$|7?9b;$A!FD_yKm2NyYsRII-3xTMAm<3 zJ9Bh~&`St)ppM5`W{Xc|%Y3u0pz>i_k&Yu8S#vFFJC%a%OQ z9*S8r?ESaja_a#@6%!mkcC;lXsQ#v60W+shvymgd)c%th%27^m*q<$XQP?lw{^5ro z4#NP@Rh1mzBK`02@ALoD($cj5NBg&B&rxS{^3d(VP-JH}aV}W*rcrjoxly)t<56LD zk8}e2e&dRKfX(s&$W!+`ULJ3Uu^-lNL4Ipt{y(C7e{pd9E!tjrBjuqI09pV*V2>(X)&O+liUVkF+GpV&k?gFxxI%7^g z7Os(a`h;ZwvX+L!Y}f15o~gDF!Htb7d3fT#AkB3^WK_A$$y#SVtd4P z$+<#y!8bIY|4W`E{(q$Bf7Qm>a!x)stKPf>zbm=eys#C?NN-bMi^vveP9rAa;{x~t zoc|Ei9B$uWyzoEOHf`K4-!#tUkRXGXFJEd~x3I=`NW7Zw&XQUtyKL&@i7pm^{wF7Z z7~s-m?W;^sjU~OFq&V?H+0oIO%enHmW@?XxY>EQe0>|&X^UmIZ2ypShYU%%?{eN4w zxc$HEbI;Bpck7_+F7bEs`NC0TtzDftvJ=xJPYT@H5bMv#820SI<9?3|JRaWX66S62 z&;Rguk#8&hPLq!Gx}Ury_U9c_&AXLJ2Q?>A)}vWjeTEmxdcNdy9nP3*1K!xAe&@TK z0``NbJ(o| zYMeet8u~DP=uo=x8rTnVBH0_MGaOVmg!R@SFM6hOqyxY1uMaGbu;0NryEcxpA6*gS z>)f$P9kF(8+QSaEkP4%#{ zT`WN7|608KfMH(@vHlCCQO*Hwhl(=k>qe|G#b9wgPhh!P52}TkT+GuHADDXV+JkcExkY z8JPFCkGEq-3zVy~UHj0d71h)vF2z3e(lP)Z_k8a48vlF#Cay(Xd;P|ZF7KbcE!6+; zdqV&5>s7K_YQ}T$Y^>HUsPh3Y6Q3i-HA6C&^=9Cl01wZ-GTg?y`PC zJ{>kY^}g8OM2<5$o%H}{Km+d{^Zbwe^m?BfO02OWV-uBIgkLmGd~!^+ZZ@vTvR>bv zFWanmj!NUEvj02PpJ%_iadDAf$J!;hr{50$(owaG|LXU(hjdn#M#@Q?xy#;qYOSG% zDjU~{ua}2^e5TSepx74(;(|J}s=*}Te}>}x8JdSg7dyg+oGa`uU6QQlH@R4#&fYQU zV8sbiloK%1t@|w#-cwU`HsQK;wtDqy*$k>ZnXLLM(mk=tqg@~$Yo222!~%r>0_*{u zAO4p@8vq%Aolh%$71{sc`d`%l*|B4XtN*o7bGck{hYK4xG9Aa91#$x{jUTkleIIc^ zSF1zgKsVv*qQ}|qfjl`gU?2WwuM9c!^X920k95%SyrZgDsCp1* ziNDL#{Z-?mZcU=>Hk~`YJ0nNGo8hnn=J9dib!1Jp?A{g5KEki4+V__AzRpfQ#<_fp zj9uw2zlh%fx8RqxQb!;80>lHL0W^XhG#=BS9dZ=C4{eC~u^x&)PA$PTIxB!TN`n^E9ujL~1PPaXqzev^GzwWt59*H^~YL4Ew|gMq!1uC<_BE+pO& zi5`ng2Cy!`c~*C~oT=QOotldz7dwJ?h?gWJCg!bQx7N~kY&Yp&ovoK=J2NsQ1J<;t(s+^rNIu>lr~FW9GyA3zLnj`$uw02@H}eeK(~Cl<(6E&RLse;FCB z{wJ71j-%to4%p^&Tusif@78#}-F0QG?a4UiYPNuX>NpY$LX~KTL_<@xCQ-12Kf3v z9{(Nd)oece?^gBRy=k=Fcy6?%rW|&*HF9d`d&+mH8%aOri&n+0=b4fRMNP8*%)kx^ zatv4>2y&0QwV1B+ho;y)EoaNu->tarV#SchIo%f}c`2IZtXQ!uk37KLyLal`N%?mB zwGUoRd-0tN*rI(Po3ys&Z2on=OV?VumQXu;A4TGeDcZA0RC0Se^LG4UAqcg zJ)Oe6^al^^vu&wbNB@3|{3m#3LBy?tX#d>9fla|VvdF>;WS2j9Y5))d#A;lDCw}(0>Smy?Q{8i--#$VieVmB2v zWC1du*dK6l!NNR&j*X_-y{+fiwrzVM5VT(#;*TI!|(+1}a* zsogu ze4U}Q<(Oh@dz{Zt{EXZ@bSnJK8V3FV`+%1%%5=ZmQ{xMGHL~CC(lA>22JzZ|c0{Ae zck+#T*T@?38u(r4!`x6W8LZP-E8sa~1pTA0=ofc}jr2qIu`Gwx-`ujqI-a39JS(#P zd75^pJKx@YCZIVqK}PYr^o>3+Kgh_E_^%=FRAWB7Hj1{NNIo6T%yTg;YI%^ii472~ zoUP81f5BW;hYlrWMNi3t;)Xm>thvM*0d~M_$ zyvlv=3fbP|vcTWS9&B-V9Q_LZ$urK+SDxez$-Hi{{9lt1Q`T0as`m|T|3b>XhWZKU=DbKHNE%Z%e>sW7r_WT63 zp{VDEUxa)H_t5TA#B}@!{&uRY`dr$N=PkZ-E@>p_q4I3n<6C(QNy1>uAeJSDx?6Wv+I_+^FCz z3E6WwDeKl968?AS+>LDox}=9yw}SIYwa%?LfO7J5&V=GSS_ATaKpC;X0&q`kaL}MZ z!=(GE3F2daRnq@je{kpja{f2*b?VP>?v~dxPL>Wym)wlDUT4PExLw~ORy?^tu`}iD zjM{0r#~r&WHn`u_+QPqy6TZm1L6@*UK1FfS{h9E!CRf+O8GG%~p4haad-H`g?i1tc zB>sNl_T`R$sDT21V}oF4<9ic-;Et|?@6orrc4ufmC%Hh2t!u-WTIol(MlnvFz5e?Y z7k^>Ax%o#F58W<1WVxdWIs_jdy}v~JhqkCb=5fX2xOS%>w|~8`(Qf>1taWKnJAGo# zx-?Q8WX^uYS#w-G6uSev2$@I>*UQGz_=m1FEYVGSp!pro-x03R}$5e{fc%nqFIrfv*F^cPJAEferiB;jx!PBy7b7a#W zQj9M{GGLW3zegfKI_;QS55NW}?FW>O1%i3(0mT8Q7Eifu*Hn+vtAVwQp{zb!!|`t9^mL`Oc*11BLwUY&3^;XhEI=&BfRy1E3A_m{=GG zoHk;o(~mXd^J3Y0ee+n!HewNR_R?c(EoQpTLlLgayY}tQ)t-j~_QsRz?T$;dS4Q%+ zXS3q(t7*SFoe^VC-5tbl!?F>~BNJ)J0OSF_n$!O-|1ZnQ)tdf(WuNa*XMtj|L0p17 z5PTqXEi#%$eIn!mXHaDB&r>Wk(YlHMD*N6OU-$U;_Cr_>U>{2#6|M)9e{i4hzia0q z#cWnO{L>fYqx46PZ2Cir@ny)qUn3nKFZ}1QF9;i;yjUQ905(9OEXaE6t+!tJ{`bGn z9zZVk{!}^t$J_t<-E`Ri*+;Xp3s_6`HUM$A1nFLUP4w;p#ZSQN^(_{OZnf0(U0oZ> zwiJI|e^#vhykoLG(R+nG|Ik`{;}7fY?~kmtKit03e$jb}-FWsQ@in*??Q63S8Vle1 z!o$H{C1Q%`er(yb;%P7+oJ*#4Hpxc*hu5(Uh~@6wsXX|6v)}e!Zk_9)Z)=G*`ko$5 zu}kDL)sL|sU6x>v_gZF8-@Dpg{p~t??f2{KFZZpsU)`|W?zt+_$wS`Ty$QIly*~78 zwn%&(YpatFxig!i@r#6UWFWX_{lTq4=rOjDuLlz3|APODXyRp5yT{7Rj#HuxzW&0yp^Nebqz{u^%JPg(W9s$~Czf7t-p*>b4KwPD>I8^Gn)X^z)BFW@f>s()cFg#z__#YY3Q?J)1U;FQZe{Q`nUguh8Yj5vS7t;>%@JpUe#(p~T z6OaMq<>2$eU+`9*;stA#9I{@LVLgRe|J}8D?;%_x`#fFzo#$#B^WlECKYf9H@V8(+ zEk!wM$UI_x#ECbirWM&m*hvomicgX=;r)O58UDW7evIzGF682@Wa1*!RKiCiR)ZWz zhv82m8#uRJrnddg$!qj^b@{4T29(46G-oG)|TH#bRG~jzrwXY{NBH? z{x1{{C2q(Bm;W2q|JsL;eN09+Ii)^_9D5i22OW_CM~)tLGWMRU6P@i|eY#bb&R_le zu_HQ)KgSPBhwRF;Lf*wXI~IO+`M1ikK_-%W#~m5qXe0iIx4;9jdalpk+U#N*|9_wj zV?ee%B%AE;!93OA%QW;CYmuxwqVvhoAoqxMBQF!-?|&Em>EEM0m%12IhH7iD7YP}_ z`Um?=iP_SY>oMy%oi)&nb z8-E&EB-<%x%a*MNrOT+3+=^6Q{1@7Nx(lt-r@UT=Wp9loR{o#ZP|Z-eT41<2k;f^61%k? zsWeXc$*bf~EVLW+{new-e-}M_G*N%Pv&i8D|4%uqP7mPYWA}skt&)W_pOcME6ySeu zk^b*oPqg?7_@q~hIK^xfr1ZDRBwpxA#r3Rq$7^>sU|n^7_puH&{p6 zv(@h(b5-0*#tDKLK{w?h{H%4nYJDj$eVy>H^%?Xi{3Y3xvvu3HgVO8ly`opzIa&!%5hAoKU(vjul;>J^cuJa^VIzZ|J3~|ul9FYw!hx))&@%GfBl4% zUqNZdNo(B zp!}{0g?7@iy9qgaR2Z3j? z(U>D_6#9k?z#kxPOa3|Mhwj{-V|V@_(RwtFMD~`4f9!*Wl1&Tj{t&?tpcgV8`>mK`lKNA15PJ(UV z{Q&qXui z<)VMd-y+YNYvjk<-FU0t$C7%iw9m&n?)_~O?ZEzHl1C{n_CVeUKBIh+94-7G)EXaW zZ?Dn1-=f!Ed+inB6MS>F5BMgg=WG1(yx034`(fQ*mDs<&qGJ5B*WT~BMc;WpV3Xp1 z_)&@4gUA?M9aGu5ul#D=e}N3B9&dsDJ1$MM)r!|~tE`1t5lkA3(9It0FA@mZ|oxd-vTuLl3`wqeC|i za`ukmdaITD6Z`t>ufHsOqT5-|<9xrczQ^7V`~1GAmKztA%JIMbP{`H+z zAA9j!mG_x$jpFTrc1tYlkZR}b-Q{r1It;jWFxx@cFUla=vub~rt{M*F?qobYA#P6(Mm1RHgpgIcr zefm)zU;oFR*L*Q=-NpZd-`+}>btb9Ksna?LwjTM<*woJFFO-cwCIY^kjVKy;JBaaP zj~`VHsZ|Np#ec>0=_UGgkv%nM{2ukP!1?lCeuHc8x52N>k8e&^OQs{Mk@3WMkW;J` z>fAeqto)A@#k8s;t9)OJ@sAuRf&X^NiZA78ugC@$b3k_%#wT(V_d6);rz`fiT5IsJ zZ@&5FOTs4@NAHKR@9q84*e537YyQ4*cR*JXXz`K{<;d#O?VBG>+2D!!<*<*4n=csMjpgJMnPqOQqRjn^2=aA17 zH{<~F^a1dnd0hE3o7^6*Pz)t!&z`-R!u}4${$P?=|;8&Q8~zkc0f2DSe6W6JnY-syd^j@DQ*2x)y`NQ@OhI+ z=m_se`ni(0fwz%Gw_cEBWBLc{`&r6iz*cnYM6v;~WdglV4Wi1(|6v*6V*vqO zhzFLG0pK8df$Dfj<{!;=+dLI(O1IwU1u~NUmE@s+@BckCKj7)9yVkfkns`9=ozA5q zN4?ljE%J%LKDwUqdTep$Igcysgk+Z!p}oh2`oFgD->bRk^W8*Sw>-<`_(#dlg7@&J zy>H^}e)`B9GDjunNp`H(E0JeD78s>`faQt-aSeD)>sn`&)?ZbbmtyRDIS}A~iRwEZ z7d}!Q&k_5>PSNk@DE61B+CMv#?+gAH_U+pj{DX0C^Lu^o<9t3I7{-28<9_;Dci|iO z^}kB-PduPJ8L%-m)s2O?7wekDhI8|dxm?(>pQhX0SEv?G-M9$lX;o-m_ItopSLvMY z;_v4kOi_&gsGD2zL&(+hJi{8lxB0;?G5s*#czfUDK79ADdEUYf4%P&UWdQsY&?Z*D ztG$%UFW^dBr}N+MPmz8|P|OM6r0j8gRk8v5gL%34s$?5KFhf?fP6W!$b~OisxOh;P z2`qyXbZTkrhjGQ&iGyJOxV2*0R?zv_apk?s{;wMTE2jZ{e*dp&>TTe=fm`giqL`m> z?d^Uqzwy;*^cPu$UBFnpt>*I;=g41S-z0fX542rs-5W)sgG$PP9*u)I#i4__@|`z} z?-sfBCFe^C|JrY#DeUi1eviZd+i$=9cVQE3dwbve`N)K@&i6Kd82eSx{rZ{`{(rgn z58D9c$^g-OQ!4AK!M+cCMRe;?_^S+vfe&|w9 zzN~Qd#BFPA(X61J&hgx%E*EURd~)Kx)YdGN!Hy3~>wS2|<2#J`@MHfv_mVO|v9Llr zfS4fmF#3u7DfWI*Q-J<(#m+onzrS_0 z^dntziQEVLp`_$w=bw6i-(%nV`(TLidR%$i8r<<-#*A-Ge3)}sR>gyVa8a|d^=OhP zd9~E`?mDiz5z0f1iE;Z3{8~SF_P$B!K9<#2<_JH4aWX&1E5Da(_Lx1&H&>q{;?-g~ z)dtWZ0mdM^O|q?`@V&- zUs3E&AE`S2%aZ}n6FpgE_sQ>Dr#gizrSGtLu?5h3nFqEfdwGxjd%b?~@i4v@JP;Fu$C!6$<73Vn)i2KB3(M>i1~QETib0efE+iMmECw*nau;D4E+|I<~U5}NomCdsRu%!3Crwby66 z+A0lfq0Zxcz7W%1?|a;16O@Ph(%7$6{8uCcprw-m@Vex$_Xqs?D!4<}9w`{eB&=bUY>66#_Td1jxy`K@RVKI(Ku zylnLO0cqN^g`a_)imt&1VT}--LqF*+*Y?yL7aw@?_BD#fEpg*xjL0{}-BaU-4mXG% zo;J`4y8*gEzpkRAqmAeaeIM$u!ruC0s*{P-apBs3V2|YU65*cn0F{p}zV!4#f1^vm zGX3}1FRfDu^8)y!gJJ(BF_BfbR;Quy2UHTx*wRX+2x^Qr$@V9dGSVjk6zLE&pHm zCpWw}Z+Z)BX*r7hWoq4jyK?;<{#B13{DbE(?!B!a*7dx{H}LM89#(qsUOxK`?f=NupOMVf2@_6@Gqy82RKAG>#f<_kY<+xD$GdpfXvkp*j) z9I?qmc4^Ogs{QunHTH{+E1Zqj`}}0Bp@^s9Yw_?+=Oj6t|E%?L>(gVEJ#*hW8}ydW zRGOKo{7t_nUi(v2uatTS=z8)LS!-FQdTI0p{1Kl<=fl?-;#Y7Q#yxmuOdi+OxqDti z2JoHmp`(j_%liZP(&#H}5Z0gL;uD=5z$RFwerF!qD|CvG>X{G({himLj(R1?9-IlWSh^vvr_fbD&i_V!M zhaVpw+{5qS2)P7iH;F#Z-bXe{pL;wp9GK=ZhZu=lne;%Y7z!{$bUQ&XwPkK12FYyQvZJ;nI_UQ@^`+c?`u=h+>@(wK+^Z4~yLi~P9owDmr?w6} zxNS#9PFB{DOksbA*8Uy-RgXW6|LWlWE60CnJ0N@}kJo$N7GQjglXK_c1LPjO1}+M~ilj$6B<#&>Y#%f8IgeD4zf z%HNcn!GGoZ`7M6a+p=CqIGtaLujxDd;jxZf^UXh3)q9xF!ZM(UF33mm8hq>IfP4aY znf>y}09Wf%{a>|umE_0H!g@aWZn%t_dTHsRL+aXWTQ9mmn+>b7MXwzB#knPFr>K&Q zWOt_KVUyc$hP^?&44Vjj5AM-<#LbaQVVzHZE5m;2v3i>a8gG>iz!=wWOqH*GP!dgN z+(`aVpNN>ivD`y0HkhsWAZvu-#+m`o6FcPGcxsI2YMzg(b{BQ&Jod|qOIC#aaDP3& z26`*-TbLJg1hrM+o!xu(xcaW}SkPznTYaK$^fBCL9+&PjW6dqln02<}KIKqyo>L&N zf;DEz*Br(EGKKvx{+}z<-speu@7MIp()Awm{$BXeN^cSAe{TnrmIHom0ND!ti}--L zWnK=*9w0`D56t{w{}L-CUw)BvAp5nzaN3qMmyfwWbC2rY?bjOQ0oSNQ4jwp@XuWE4 zknia*qtBz~`7Y!(-|g&J$sDK8B!l4V?K^_AGq6!h>xeM6J?_i8hu>GxW6x*Yy)6Ju zJg@n^GWZ1W12!yrXQ{^I_NYUbLRkI<<|}%x^ykC>hxyLa!^=ka4V>W<&`0`8pW$ox1Q`kM zB`;a##>iN))v;}&<_9_)9t7Jo9%Ii3n2B*T17pZo_!pVzY$4GIn-5g)e9KCA!Gn%4szRLGN(Bi`@i zd)Y7Me9yqX%2B9}a{=+hW*g!Zg5S9bXjpO-3KbW~g z_aO_26Tp+unK=f>=r86Rxye2J7;MmZuRkLI`eF;B$KW^iq?XoY@Mc&ycwEy@|F|ai zF!sZbJ->1HwgCJFO^}W7g0q)J6LbSuak2osIGO~t{lPDMMMHmWKU@tdArEd$zvouhO!>}_whMDHm6gCCqFjk#*YnIK%bCx%p-ar+^|h} zZH?Xo=8NXn$@IW>4q@m7d#I-uc^t)uA=|8mQ zbuVMR9`b$_-|PJ<=3l?5uzyab@_u)ymDc~n`hC1FjCmf{^ePMgJ7j;9$o~u5`xQL~ z`@V%`0Co-<74_TqnLGXF{Z1DZ(%fUel#P{hJU5qxYOqK0 zA@={@3c>C1ANKoeihKY0()g!{J$)cBFIOa=Khz~?Z!ntSC&--Mb>&ye4LFCRqBL3nV=OMYK*Y@gt#8dVk zIN(0Zc`1j4y~9~L=SI1T)J~;dl8#hxze`PZ`l8w||;^YZme_xqS|7RLXAGVt$pzsGKE+>5?bzxPLBo`XIqo_Iu!#E^V7qU<$q zGHkdsPS=F7g1o$3&N*;Rc~1p2(Y!#oEfCF%n)0Fxd<#F%`)K~NvZ35}3FG?iJoh{L z6a29L*2*|)gBE3dM^P^W8qt5o0eu)(j;BxIn8S4NwBhcbKYH|tqYd-?r6jJ zyG!&@cfK;(_+c^ce$E+JZf-E|;qmz~y8hDVqJH~%J?>}%-xSTSen8!?_5RcEFt7SP zmza~nd8xeLj8}aQ^JQ_r#gH6=cPwDaSzaNvo)2FI5|9+7G=e<}?9UY*pD6^XkNX$t>M(d~;jzPi|Ni}dr{A5YwnmN3DQo~` zGcOI<@8B8Aw2&1--!QjKK$^*H-7x_$A6XF zc|(osgJUYR&`T3OatM_O=?>`@3-XwD8zl_(wN?m9{@3=Bu(R z`0kU}NeY~#K;#q%$NZh09{E>Q_(?AV{O|eo#?p7c@95;ccn{V|TAZXsO?aR*#=O5D z*85*2_Pt>6GNAO`%Y>8n;(b;pX>pPkwao*iap(W{`20`YeIHKVi~XdNv^YtNuYw1_ zH{W~mI!S?(6gWwNuap8>WBBj6f|3{I_vhHT<-f)&$NsnC*Ym#7rNTGledCl$UjIgA zub*4d8}cOBD|)>^Q~g2(Z{YP(LpW9ucj&85zFq-=PSWQje^kI8fc+$XDj?8TO`jJk zq5sL(E9TFW^f^hN3gC;aQK5W0`T9xvoaCSKDR2@WC-G4}cQ{|}q+t>NqSEBSn7uPa(#p?#(0 zfEvB7V10$3uk3XR*a|-{2fSvlcd6v_mA!s$C7-Wf!`Dx#(YxI|VDe|SccuY>F&!_hJng0K>?*2X8 z`FyzP-;-Av6evNXbH8`~C5@Xj`>o#hL$%A*&Qfcl)=-VJYdO2R^!e4CY30weEq$IH zx%7Un{QQJ}lgIxrT&2g%|DGPu2ReQ8qKhv%_o0V>x%Bipbyn*CFaGD7-~9VGzxmCd ztKFpk+pArr2HY=D`@Y&aYRpk{wWey!RUwwpUrS4$~XD zLvQFnJ!Z~i;yl(mr<`)?8Lz(j+N7IqxwGKx^DkYi=b|+S)Bopx{%4Tp;8nFJ)E?0D zx2yG1>!fy_+7HyuSNopY*=pZWJ5#NhS{Of^{|Z*Z=Rnbqs>}&vsEv#9d!F;Z4ISW* zaP#NXMS5q9;pS+p=io``fINX$;oBB!-&Je&+zT)Do3doDy)q;@|I%wauhVmj)M7>V zS!$!z`sx4I)c&IOE481fA+I{CwNe8km#Kl13)RB&0#^7fKQA6DUbfLM`sd{tefDGU zbnrBSW;AH%u~g|C_2|)~<-&yvXUD|EB&j8a8voa*Es9;36dN1k+JXhqNzu{8jsIg7 zL@$nxp1*kR+_{Tq&z_w;W5$f+sZ*yenK*Ic(y?R5E*m!N%cToqVz;l}W%l{P9DC*S z1iRtRhq8Wf$%TinxZ;Y#*IaYWp|)+?9O%}q+rFD`zIoTZ_ujjsPoF+po__l2O)tIl z()u^wd^1IRo>zbN*=MT;4<5Yo%P+rNF=F`e6{ALtT0UmXm}TR~k6+4fO`be?DZe{& z=8PqCX3t8VH+N2Q)V#Ti=SR(REh;K%F>U_*`9))({|go@2sda4y<$Yq;a6RC74u!` zb>DsW^_0z)^;Oty1^QBotYrts@Rj%pUdz^zMmfPTK(5S{>i)QTbkv{QrMkgeRX=&1 zYJG0scihG-&b0wi2kq6tN%r8cpSN~x+gittovdrO?k3vX-FMw(k38~-{rTyq?XA9j zZQ#IxCK}s}88b~3u_Zc7Z>?(ArlzLawr$&VMsbF#BS_sr>hGzxnC;$o$hPlN?Yl#& z<##kk^)ZDoQQbUOx7F3*qZVKF+mWLxYgKUE zGWHy>zkW2|uK4L7yWp-*?dB&(+18A`u1;U3YA$AF9kvJmKFQAR{h|Htt?3&7fudUN zsjBU{Zv6&Vf0|m~)TrOS>wqoVm~Vq)k6Zs)yEF%hcGsgX*{O}sv-)RVWF0zow(i}# zTkqbz?I-u$XOBMim_7ZMXY93qzHT3V{ILxlGQ>t{E+$Wz>drynd|+yyFJHOR)f1+c zYue_`wkUb6{pOz&?Xr75vh(l!&@TINKWq8(f%cDq3vBa_JvtXnwKcPk>MRJ=EL0sy z--_z;s-B_hIZ_X6Wyg*k(L?UARBAE^w}$VI0tUpRdN>-<*W+W_#k&uNO^RU&VhF`~?riwhlRZl=&kL zA})#fJs5$-E$9l?1YO5G_YC~BIs_kmF%^$K^9mX^sEZo}?99wsNAa$zd2wqlJdu5Ma-8siLSVuw!{+74NV za?PiqSfj}((PSE4=)43IR`??|`!w;O*q2uf9)ycuE;(@V3vt3=uYVQ(%{2f2D*QFq zZ{E0S){J*Z3ci}TA2m8JH*hc3coxdHnui|a_n7`(y?Qw>KC|F|^6Yuk?dggK>rcZN z7hkRi2mBSASTULvhgfmN6?goQAM@{qhU4_Fz`p2fSm}3_IJkycTM{tSEew5!&qcku zZ-VO`esrrty?PS|U*Y?aBQbf( z6wI5y0Lxdbgxk9H*t%^y_IT`t@=F9V|BPbG3-7{zD(TS~;^6Gr)5JqGIt<^8%5Tph z9;Tx>@$j#?S^hak$Thc@yKnAS9>up zPQPf7;yWt_x8|hgrgCqjWSvD8X^?B^HLUi(f-!!#P<;7u6!~fms(wBN@3;OC?~)I< zY}E>_Sl;G!o3?H7X_qeO-Mf#$!SE3yF!86KFlXL8EM2x7YeS#-lwCub_Bz`*8;zX}y4Wn1N!AXQIN}^U$EL8%DYK!8;_Cyqk;^ zh$j92kHMcI8<5TFda3!u0-|;==z6LVd0=oRPtIOV0EiGxeO{)~|5 zbjlY#c=fXtDAR)LJ{K3oo6N-6<$)Id(mnL|yE*{*@9)wd!PJNJy>`zv2IhkM6K!2E zWS$R>#bq-F_HT@N&Ac3pz3dEg=W7dlTMI|q55%Hei#e#+at?MKiZwLIrYc+EMR z<5zG{j(6{aemIbM8E%M|{HeN!hVOvqA?>v0ey)Qod>ch=Xr1;`@;pKVc$f&YJDO!5Rk+cIPev`2wz9`IU3-4A$+7ME7yK@Y1Ktaew{k_;RL?fq%Vv_5R23 zzd_i$c}Ahydkay5@{HxFkx4m5785V9G{za;zjrMh{LDDl$4{N(oL!0Hq+eY|@1T72 ziz9!E^3&w50nuT}CPogW9ONDx2@mRG6Jl=T5w3v>TW_GUJ5XU^0*Ve?fg1fL;fp@K z(5-uS&iSr}_J{|3^697O+N~RU_3n)U0|(-pZ@(o!_yOZ4Odt+sVX?~+$2y>ly3>6Z zJctA3xpJ0|B$HWsq#O*2aj)3yR|$h_oKG2)uebWfVt+{5e+2%b-}}g0s=T`ZrJBvg zGwqjRgIBcSy@LDi!PxmXo2$Y3JK;b)eEf1hlpqbQ{PtYz_K!0-P+kee@i#e*NPi_4 zDnHR-=AHER@xktd)7Zg?ejNjDqRbY;fBQ{S%8vCy(SD23?yFJw=F5SEbstFn>Dlvh zbno5+J$m*u-}N6b0ACFrj3L8@;fGP94L>kASdxna(jv)5S_jIVCLBmtP@Ww1C7&J< z2hj8VyBBv-@a}5Z}k6U{_9<5FZ_&#{oD+Xt=?%FW%vv#v)BIge7A6St}PA( z|6i$tNnd!G>$W86W}Tj^k#YPq*Y^25T`ZmXyR9rFS`ZizfW3Z);gNF_i<58TMUR^( zCmfIuRB}Cq;)B=XkpXit{D)Eaj&pp#z(J%r{n58?KlEYg+wTkWo%DhsLx&le=*O|+ zFlq8+qa!RP4qVr)#iq?$uyfZgBcBQf0YQ#jn$k%mqgotjooc=a2g)y@as76MiUIlW z{{;RFhm2i*2`JlguAw<@dzm)k_O-t}=bd4%YYTtXb`7lE8-cRq1Et7U+7I7q@@!nV za6VrjBi+^7*Zfn?u0sb8V0T0+_K*$@K6cB{pi0{uI4D0O1P}CEf)?LS!;}eQF?!Sw zhSo?281&^=7&K^*sYQQ=a{h}3jrnmb*T5uWv#@f}TDNuByk#rgckhPb$yx{G2cm;U zPheg-%?st~usE=JLM~mV66}C~EeHGk>-<+dcjmfaBU`@o=?WaD?DKp4)fujZVP5#N z?zOlO?+~3EGS|nffznN98XRQioH8~ELx;$Vl|w5omktGs4juA`NBSkK&jOnHG_;N#mNL-=_FMsl5K{!g4V z2~(#{$E?|NjE*M#VC}kfMpxdodk^;Q_ck(^ax@AD%H5^@B&Fozz~(&q<=0Dv;V=90 zJ!PP~$v=YsyiFmLZ)Z{8TShu@mNNUjxqs%zQK3Hf54AFj>Z_`V&o4a4ec2?!=9F3M z%8?}fR5lyhc;S)Di$4jv<;06`q3n2Xlp5fIGEKii(P!GDSoMym^7B8}4=q}_ z04tU+#mW^cjGajOv1~=#wsQ^8W+!`*ad)rkwPiwB-=hhootgz~R+##0S@pkdpu@Y#Sd z7&UPg7A;+ljT<*$=Z0!(Y5nGWuwjLq-M_4w?_#fRLCh+$M~EGrCv7Tx`xbaE7hDcGzlVuXPmry=v!Y zrT3jV|1<532k|i1LJ8WuO3~)^Mz>{{?RJRv=oDmUrXeOe3IU|Yd&!G8k{7q~b>hXF zub|Y3?I_)EA*L-`gYBEvk*2J~q#5(k`%PM$h- zg?l-If4y9|Yd`WY!(Ve;dVp+c-^?aGCH!U6m;Gnh0$)f6)*87If4yv<@G6UhejcpDddI%XHv(Ds&? zaoX5{1TX0hqU9Ejh2iTAf4hI={GIc2y4L%&)?_bw>&x{h+k7U9HRPU1qo43xyIJTr zZap^bq;Jorj~6nSMJ9(?^Pl&d!k)jybk z4qv-q_NqMy3W?xeDfezNGYJ0^SAPBVm)JMod{e#{7XGU5%7gDk|D9>P_P8XIh9?|H z@1H#IDD9rbX?vD@TDtjMJkwz*+78=>X=@K7Fd~CI`d7+J%C~kkFJG=RjCE~cue$JW z`fTywSWgb!Uiry3?~lU!Uvtl))pQiCKLJJSjYXAa-W4XbINd)?FWV%q22 z&Y}3%Zg_amBCOlG6Ye{A8o7QGZ9U?x+6P$U=4R|a+NWIYx|(Z*I`F3T{Cy*quU?0V zq(7ezor}hwPQVi_zekxmL-F7nL-1g&;V4ya6rO7{4WEx$j~)9%3d8@O1p5Ol|0DQY zT*&SteJPB17|*@9#$T*MRodo+3+=zC*5?L&+>EU{Ffx;C=PGj?UN3+LXV^Pm7si9d zfocbT_=UDE;^yaz*c%Xoo@2M-U#(}MSiN!h+si}Ikvhad+Ng&|+(KpY;)>FXX`d@U zI|Bdcy$J0lxx&}WleUH3#>S)l7wNgOKW`v?5-z0sO72lD`G4!St?(f4-M``{{DPHR^73CX^L9sfcQLOHdDBWlRCNA^2a_jmP&42m7{72!RhX>N5+oUy` z=Ygcp(>d=o|DWlw3}v_;igH~PYb;zW!iPh)!QC&>tcB}0t`(T~&ak)Fcf*16da$`x zFI^&k`uRLUqmwb-#S?YA&PVBnV=!fZI^0hK?;f=FIpSi=Rg@m-j^ce5VBUIn_#N;u z`(Dy*CEH8K+Pa;%;NGys#d@v{U2DI1JJ*bC5gwkN%)jny_-}YvD0Q(AtlsL4o}<>{ z*)}t9-|M5%cf$57w{QG)_{+=U(r;JqD&JeU+uF)1(p|L=ExIqWQ&Ohv;n8Mau6m~x z8tmr=+Dw+%ZzR*JZQaJSQoDC_zTy9jeIkN3(=eOoO4*P`6%~o)*3vN;2!1ETpz3U zM3Fb#FgYSEd<)m|=P3N0K3hCkdL$gk?j?J?@}XyD(sv>*8vB#ZVkd3XpC7s9u#aw| zje6&8l>5n#HtI!q=Cf(|?3?+Rw_+=8J3a^?E_{7`jC?OWOMWVXxon`i7H;;?k4UyS z?G+y){AHK6I)L^l5|fhXSCr0t&&RLOpD2(1{No@0cz`(gL;BDCN>r*;i89#|{KmYZ z*O+UxFzofYFx>UonNI6kaB`SoZ{W;@Uoc^15FYu^1tm$F%2J;jJllu&Xy44i7c8u; zAB}Tuf3vkS4%8pneYF=NTb*>!gv11dM#W)o_I0dE2Ws!j<;B`7TXzwqhi*Wz?%YSO zI}By(4#mrDC*bq%mtdLOPSTbG2=G4y`r{dR@7cT8)biolOW1qS4(H2#89&;=r2`+~ z{Fje_wM$C|<^Jg%@Fx!bK^*+9{6jxPkYAY6oIV`#2e@(jwt=@Z9{w!+Eu0*9xP_po zEbb?)H#jIsy40F{A&Y)hS_^`$Gt3KLTiDxL@3uHMIACt*lgy1P{a<@j@o}*@l#p%o z;vXV!q3U+>;#@jZer_~MeYp(fI+J(S8;&BBV~Vm=Z1_FuemogNC%clzdKx^)*6l@H z$o{D!-BAjYIG!)wDrC1o|g+JT(*xKSi{n1{6 zc)nX#-P*S1z76lPFU0C8R$>D&p|Wy<&P)Ye^~mj;7>c8=#Tcs5)zZ|N`D;S z|9$uq2xjj?dvYa8lrVT0Hheg4+`3%=_LlZqJlJ_?YYTT6IpbCYqWoL) zQHu2F#ZOn^NUYLw|1l1P6X*9?9B6JBew@n-v@eoGI&>s16&@#UV{YPYJiFV87q7@h zkpU}EXUu9uMufrV&|yqiunkRn(jTDFD6WTLd26Cs_ZgVxvW0$-{s;}Fe#5;m`2z$W z2{rbIu!soyY2?D6HVD!GUkcIxKLdaHaET_1{~kUPf`Nm+G<;2Cv~yo$zrsA@*Kgi3 z>t8sqa93?%Zfj>)*=yxzmJH;wJsg$aT|^wr!!sRMz%QJN)1SbBeUGgz4$g}goV&oB z-40$LTTDFn3l5~7g*$n1zYy|b(jaS}D>pd+fB$?wzFoEx$>iAy@iByB6gKWUi0rYv9M|TX=|mE0z~mSP+N5_g;+F zQ`RFcCMp+}v1VUIHnr50M1+#RPG7zob-T=>t>Fh0dvhd8*B^ySzaJle{E1nUM1ffof{Eba4C}(zy2lnhwL2gg zj~X1z$5ZW>!$0EB(IWc}z2D+MeLj;b4{1+ObVxo6-Wk7QD|vA{eyAGb|xSaATf%PY8k=|q1oz|)3AqO3jJVbv|?$?CIr($o;Ei6d7jpxaW z#S7%OQgPJ@6dAY@FAQ5wU&nC5p776=C1ukWUD0Rlx2LBiBP=ooL#J=WLoI)z4lxEL z>W@XIp=%HmFa0Z1eu}hz{wnwz`zK*@|9=Ah8kdUTsz2?!$(O8j>C$<4kp4iw7y7m? zGc;KDi9TJvl5h91FfUwNTJ&&h2S2FZc_ku~Pf?e?k+)XtGncQ>&-6Oarf|`T52@T_ zL(&;_kNRolA=={|d&db0I7nW+i*#sU=q+QH$m7M+4&!e<=VS2Fok&ZJcgT{2z3lla zqAS9Kcx)1JnZ>=IUEW8~um}ADX@4zF`(M*OOA!{GC|}+GD*OxML4Ep;{v1`SRxN;s zhaY|z{rmTax34efrep08Oa?}RpM|A!EgWn;kbr9B$t5WlH5ssx{>1d}qn<4OZ~p{q z$@`)e{((V8AC*sn=BxZbv{ua;FuL(+BgdulEK?Nc=)UZ0SW7nF(A)B2?pxA_w8YR& zDBph}ea(Xn&k($mQ_~Fn5)IR}#f93*Bo5*dlF)PX29#vCUaK9vP z|0Q?+|DE;k>R*3!{=&lDxz;!}5A~Nt-E+oFo_Bfqn9(m*uNJq3fI|qp3)LZ0#uJ@^L zMjj$BpiJo48$W#tk=f_rNi*Ptm|J)Fkp833V!SJeM*T;`%V(6@0DBXAx9;h=OBj$Ngu()pFPf_LXqCfvN_-mXh7Ivb;vRBFHwL*n_ zUTxu2v0^222J04W8y5C@MsmB}bD4U`Ir`hb`^7>OeX%DhKK~YKzEKM$OP0)owSCv! z>PC$l8=5CxBmO0tq_ZzCzW9>yL0?I~5)azvWTWmF;Ow`;_3-09-2eGJ%-QaXwB$st zrzA7~B}YlG&Y=H=uJdq_mch@NIDIMyUrlw#eQ%7zSJOQR{~Ln8+5hF<%Rm1&;BWb# zU~2#5L%W=N-Z}?SxpL(^_{oPyv|IPyBpzf#(R_aP*=JF`dUZp~9xQTy-u=#Se(0fx z%=rqPbCjRjZVwM*%h&ljowbuapJ#v!?^mA$|Hh4*6nNJ&FV}>7#$_y{PT=fAT5 zk&%*Q{Eb9|G6`$ZF^xmT85hFG8TyEM21H}T{QY!-Ht?7Kk3g?IFv0$R9sU*ug7cMJ zJ|Va{^Iy$Xtq1wRI{ak6#JKU}v0=jo>Noy)vfW~od21>9&+<1Qzj2E)&OP|I+Ho#w zU-Ln0P4rQ7S$m6Jcpm1le?5VD-VxYJzlo+k`MkI?eMn2{JV(C;+_wus2G2x_?~2ao zY>xIC^2-TxEqpk>JAuIP)GIfxU$_2O|9=60XFNE=N7tIGq7nD{o3JXFXFs@a&~iS; zEDPXmH>qPd){cGG<*PUF8^vOk%RJO=zqjpby~&qGJ_Pca(LR1$bU5~hM!@R~FzGHn zr2p)@1dYb5!m*>F^fA}|&T&%&XZf5<&eB;J*!|M0r}59@HwYXRJI??t~` z{|DK{to_GX@3Pmv)0CAeo$hB)|U&H>V{ ztHzG1_gubui~GQYL;Nb7Oj=K0UY=zqK5QFmTVp$Q_97;(^rwIIX0#gUiq0c8VD1`k z_@@7iU0eg71UmC#`H=dcNS}pxxb8dn_NzfW0}^3u0GjL4DYW09eIMr{oLnUQZ{E5Y zZT&z0Ed13^o#oQ`bPIFm+QQtq{%^xyFucJtohQ!zg0}{5Ar9sly`a;mUEE8!1dfyb zxhD|6w;96LmO?m7l5Zz-zS4OeXrFDsG%q~RU>_CzNb34o&*6z&0Kk zT0vgy>_b|KKBQ%aY{UZ{MjPH#wOTcEPCzgg{8el3Q+kLgycRC#t8?Y{?c0AD{=*j> z#HRhxW{t|9%^CK(c80sX{@;MVaB%ac_HHiW(;xR3JDt|Td*5!ug-f?^HfcFwUlNg< z9%FeF;p@ubOvXM#iyY5h$KmK~o}*fbN<63BiauNertYDSJJ0AhnuNFMBf2{>8+$2_ z7VsezFP?c6rN3T-2kW#oIMBH=*_O;2AkDGx*LT9fDf$9f|9?CGNn#i>=kC5&&h)3< z@E!CSnTbKu_tB3*XY8q?I{Extm^;`1TksbguX7G-|7Pg?gN7Ff2R$bF6aKVQtSeOz z{+Zr+@RxOB#yXy5pdUu{PRq@C{M$EwL+0_5aNQLG#XFFFVPDo2Y@iQmOJ67Z9DPX3 zjNFL_=|}xalXnnt?5L4B#S3&ERd6?YHfw_e*1ujN!Q}ZD!avXd|Iff*`-1BRNxen%{REvd}ywV#v{;f%SN+aB@ z3{npDAnm_QK5XUNTNpx|6`_Av%Rw6{mvT>o_PKNC8TaJkIV01gP`5skn904ed>>Nl zw^Ct=!-urR??TmzBDb8r{m3DYmM$9U*WUo^L;J>X@|7uzwYUK>kR*E^wpHVxqo;X zI*#1VvoCWEJrW*jbY5nB!K0HIbAmFQd}1|*|6jmgIFL;=A>%BbB|RxlpX?4l?BS|# z#2LDjJMVR$3eSVWWk)#b_Fl&`98%O^!L3k`{Ra-@Z)tz|2n zy)l$DXfCSI&rv+~ZnkROuYGNv3pMdEjBhskto3Vb)a0ASMol|HGVKik$&7nKI&_B* z>6{4s?ehirY~EI6aj!+TIO&Uop(*tnG%)l3dT#s~!vEhs|E|8t&sP4nKA|b-K7Kd% zrxuYnIO0B3eQyCe|F9GLf|HH^q2jP;Towmb-Y;BR*xjoZeNV|gXZC^Rmm=G<=)H>Y zqCHFd>@~*9%3`dl=RaPFVq61r>1!uiqu&|2DqkO?exT5BXC&8I|E&|8)7jZs zwDTvzi*XJ%krx;CAuUB-T&&N0tluAm?2L3H^Yrf1HxCD&eA)%HD~kST{@eH)_oV%I zoBz&or|@u%u`+~^Plk3%7gK zf{*6A{IW{7n1{#PEa>)o1BTmVLEHim>-E5#&4y752^Ob z=tFw{7t8SG=+%hl{;>8GPMkVpd=l(BShQ%7g}79kfpUd4piERVtPadT#`nZvWq!+~P zuE+Q`r)e`PxF4$da_yQUUdyR7=TWccN|bLt9b*@H;&^5XdGUGdBrmo;q?K}gNXt$- zgunNkhacDNGrU-KxTNG%RDb??vkrRp=<#2Hzi^-;-AuAj&gl!7y&(jz=EfRREFhhU znY=p4!2TxT`DbA-_{%?TUvMh@vKElmxfnQ0ZmHL29sPtBQhuM!c}~A}*4BTnc+)xN zbMHx>w68HH7)e!D_++C8JM^)~a`iD!t^wLbDJN>4sV&7#3ZU=2{Dj9XJB0F#BU7T$ zRJgha;{^9q!sC(*FJ6`A@F6v^Cg{T}AJXFAtV6XgU2ur@qqNi%Lx&-v}IHzSICOmy4g;P>Xffj?=B z{C_IFvw-|`A;QdB0_DZa#y3koyrNAnby-C@U@6;Miu%OO%=ICL=8Jd8{&q6a&7lu( zd>9cMsvvUXLpXmb0++}K1pBL;^Rs!Dsyxp&KiqmD&nzrKdB(sg)@V9f4p>XOvLp6~ z{8j>Z#!Y@Jc|N4M=Q%3ShxG5g7NX;<&B#o3>=O$v(`U?NypNuR;s0;be`oq*;cjay zd&=irG$)4qvhyf+p4X;tCvl)NNxS?L4G#VY_WE7=ZG8F}JVCfhw{rD}=Cij9PmnyM zYw_S524RZheF>Ro@iXNb;W$rDGP37oqy>FtXi(JFXK*}bzOm6c=05o_d5mIJJYTB2rlj$FWn3KgTOZOxPI@tYNK4a)^ub>9u-Y>KS-Coa zY@=b};nx0R^{@Xb{4E|V|D8uU#4)xxoW~2W%ss-uNaOxJJm`1HiH*KkZ}_Qv)U7;Y zpHpayE1bW=l}aAm`F-9!r1P?gUOY>k>_TQP-O1-ix=*w*j%PvEd&OWm&-NcUnr7_N zJU3@-Xo92mwUUz(5fY!4=R^9;?wgMD9OA|FAvNbYMy@h=N-=u`S;qdx7;w(#-=%-u z^Y72nAIS-JKRe&%>af9{rY8);3S|`4YDaz1I+Pq^=XJsN@l34bPBH}HRC7f4%f7C6s>rq_ zebefbw40gt%8n+TOng=QUJf79a|L`zD;r+S*k|$~B`+SvGmzRVOC%1sk4;u&&c7S` zKLP)z^xwP5KNjZdllCB_r^_!RI+nhwu~B&?Ixfb@&C*G7PMqQyxl1NaUoFO%6b{NT zj?Jhg{vGG$FKbMoAE!<{;z0{jsI|0I&`o3uYRcCT8rv)M06x# z6C;q6#&~FnDTtx$5P38UQDMYMocv?x|C>r*RGx>H4s(G%3~?#PXOS!^pCF%em8oBki^+!s%aq0d>g z*&CEC{yh7u_t^Ie_QHz;_9t-i4EN7-(vX^wh!mFe>=fjjV*DP)`pnK@oXK1qs2^Hq z+Fy~KS#v6m^X)(k=@3PVAER&MA*b^k73Rl5ekXVrqYYsjjZXL?y3vv(>ejI683B-A{S(}2j_8J9yJNISV$;rvY zqLC-?eWx^x;X6&~l8%v`(-D4{{PuXd@qck%1EQ%~8}f0G?_oe-Aoe7l#dgX=`97pP zS0*3QG4vs&eQxFk57D8k=gysrt6jTxBf?&>2NVn)6}Q z@zSwrQ4T(PBNmNIg`!cZ5Hu?vik6j*qGe?k*3Bz~qDh%hG%OK{_Z|($}HF$g&BH$&sG8pljTFo*k?{Yqz}@E zwEA~TDE~xWJ#+e0e63ownh?^8O|Llhb`2B+K0oEd|BB&Dn)ezbT(FB#{M=DN;ncOA=knV&30f;%V_g?V8+TdTYiM*iUe(SeFy?EoVC^2L+ zDh^!CGYrwm^=iM-f^fIFX|xXRN`ox?72`qeg-3?-OhFd^BLXKu$&LPw`U_UE!N0$moIbQfc_uu2O{vz^z}U7nSjnO zbIp|AfA_X3`=C+z{idx(Y^!PIyR^kVHYl?n?>*^mCOR5J9V#g$4gFdi zMSb3rHx|BEecQhyjcYy3@DKT>CZ{n!Kql!veWjBqQ<`}Aod14-e(>`>#^<-7OM5@~ zFwVgQ`mGd*L%Jvd|Lp6652kParS@yDv?R=xCrG(xlrzJ|q2XozpM^i;X0fk4^FdlT zYj`Yb7I8<@DtpnSa(>b0dS!SPr1T!Nec=FlHV(vRbpz1qQ7_akz6*^idK7%d+|PS! z96)?bw&9bKDI{N~q$J|x$t(=%a1^zQ?JD?OZku&V?m~m|9_aY$A@pn#h)-(y@jg$y zSz;F&SMop;r~avL>O=o`f{k5LGP2HON5yD8By&C;X1{mmkEhaJ)GzA+ukGY>F~^J? zB|rbnoMgC6h{v}bLKqi0gZ}=E??brw^9*Fbp>VwY)Hb~O;0nxImxaBgLwAcqT58}j zl&Dd6FrlpZU!FxdOvH!p&;=~~HCG~H!g0huo#W!TE9^j{$~y~`H;QjXmwE@VYh4n; zjwbTlSsc$z#q*gI%pDbl#te2*uk7{$&*&Ym71@FrLusp`Tx9)<|nrFg_E=8-?He+`tW1ro{hqTmp>rnjD3F&_;Qv6xMTsdWg2l3%MXpn_} zN@^O{UOXm$9g0_rtV4rJn+ueh#p$=v`Vf6m)7ej+e+^N*_G3IVa+KGBq`Q%3E;X*Y z6?MyRD)4OGa+^4B+z}nYXTy*2xiHG`Dfr_3gV1{mxAl6l_4w(_P|}1{`oS{h<1xoM z^U$!PIC3$JTxZim*+;?u^X9(1e?xv-eD2LM zn+SiNQ#~4J@(iS=Ct+BZAi{S&YM0)K_OE+T_D^NJhl7l_ zA9TeAml%Xcha%vxKZ5;}(Xi@9)G6;);NI6uxZ?9xUZlIJ2xOcu#X(maqFHKBHi4kv zV1t7n`vu+6R^1A2s8eno4tXWxSVX8fC#@VZJ(_u;X2~`AZBcF!$T>L9K*Jq+YS6%J-q=n%Pe78h&L)P#OlEF*h1eS zXCKnC8?T_`D39AubRB)AWU10k33JgOl{B>5P5gc-aIc}2C?zL@rqx8x<-tj}PL)0mb!T<09tapjT zD@9inxJT{QEV&YUHph`Bgc!L`ap~2@&$L5JpOt~nr!)$f;z zEW^^N$0+-Rn{(6hZRp<21Fx5KYL9KzDzgfEwnW3-V=K>LC!p=C+flPr{ynwJtz!RN z;pdfz9lJL(ZVmO2r`Hkoi%_e=LcCaHIy!u~9o`p!DRJb*J8y#I6gBr=Mb}A7Z#8`L zO+5LpClAs0Tzpu$tRx4C2ju;=|LCJ;j8}P1v}fb82)y**61-VqMP8|0ZaL~zUX8#5 z@uUOfZPZf*Z|OJAMe?4`14$1WH{cLq;F9+az3eh4@UM;aSzfa{?pk`_M9=e6%Xrw=;4h_N!_q%ZJ<&Rz0FBf$|?{_>5zP-Hy(drpD zyk44fzx;gEEH@i9icG@f74h((Jk%xdIy$Tihhn5`S+g7+KWc{;o_~?&P%3^wfQ$B9 z9*_tBj|hL->&{8;SvozGFj>q#F3&49OE1NHH8vO?7)m-US|FHM*jv~LCniR1d<2%= z|2?BTp-~gK`YzyWCR0oI5(a;!D{h(ePW*V9|lXa1lKc z50|{)Iw$U~@9Fs30USBZ_`xBEFs@%1WuJm=y;*VxzWOkj@Q=-dzirFn+rnQmVO(M) zCJqe48-@C8-qXhM9P` z=R6U6^a5JfpbTDq8~OMa)UULW^oi?%bj^K3Ft;l%(-xA11#@S?opD3mcWgzEZe8(W zjTiCgqmPppo4*1wkl@d}XXjGV_vQScuUFWnXcg zle#{~Tm+K$WFwtBGT!KJdAbhY(~!Da&qiG9N10P6*cUUtz56}AnlPpmVSAKxO16-~ z@YiRZ6$__aE}k`{V+87zD%j_`CA~1J8*{jDeeCz~Ht`|*wLEr*{?+m!Ukq-0)Qr)& zuexqZ{Y*NfuBC(4FYSea?}wmG4eIrk_EC@UBrf-$A?4V5xCjLu*>2rxGv9yJxqfuF3G^^~(w)0DaGCuIw zm`1siXESMg7Y}rv|FStse^9QLMD7`V(droWQSSops8`aPx>h8@qmIEZ$Qx^Br=V`} zg7-A7Xi`D2%I|NTV*4?71kbN0 zgqyfA+B5iq@GsmaJ=3^6VO(gu>W987{C(r{)V+z9W>vh2A1@Z-pwe#YA&jZGmbRn= zo;Y`wxomjO(sku>?550g^k^vK#RcOq<0;LVGspLz|NN)sf8L*!f8xl?qQcYB=2_a> zDjwi?@=N_vJ{a1O_Q^EHIXe<;boKrnPJQ$!^A;RB#5{<8 zXxp}J&phDs!s=g|*Qpsv7}+I;?fMdCvQ;@s^D2kXu*^aBDFewFQIx+KFC;eB*ttY= zLc_vvG$M#+dT2YYO&fK&gL%*D9eTcLML+oO%jCVmjN8Gzq6GTY)jP&}g`fPknpHl8 zmeu@maL;i>Cn&}s=Xs1nmlckM|A<)5C*N$m_pd;ML+9s()3(nAQ`gI2jF=-1`QN}l zf;6yTTh7m!ezNW6w9EPKkxxS`*D;?deYaBb0mDIM=&<~Rqi9- zRzAs-l;0GqI+C!P5| zQ};J1?}zuF48~4Z`2!|U|2b;bO=N5+=Q3qICSGX!+!5l=&)~|rExm8*V4mA0KT~WT z(M0KAci2A**;&mPyZEkb_9ejJ#J1t6{Rn%NL!?IsxCTg{w^D!N zx{ip9yhL03S<3X_J`B&vXL8Suxx|QvJ$v>fHg4QlcG0{)JO8zRCtGbsb~=W%k3;Q6&(K$^^h? z$8q8;!kpt5{f>y{UM6F-eOfcjJm=hw+HG1PfcEPE+AX6ndswn*YtHaw^lueS+jao= zBmD2^U$cqiBV5D|LZ>>cUcVpv3kfq(y&(Ra4vG&Z%@u6iifO%`o!j7@pSkNfGd zn9wJQG>))vy0>N30DcbOy7hOg?Meq}$Dobw@FB?#zwumrICHq35e^s&S~$2iX3Usr zJdZA0oc{7`5ElMCw_#|jY_}01C-A`&A>6N^ZL?|++j10p-IQ~pehL4)(x^1oN@eOM zPWS2=eXjGMA90n!{Z`ttNV6or%Em6(NLKRR&U4G9+?GDK~3D_4%yZoJ{V+IpXQXGqyu$825!jv+==` z-18%Do$q(x!?89*8}{!^qFn7S9q0=4+a4V;V#JWsr%z{ldwcsb7OwW<^(Wo80ROe{ zm;bBwoduU9+Uk9FoFq)SM?~1Ps>c1Os(-!ID;j`r+r%S{d+6be6)t$F4aqRVgXp^S z4!7CaXi!r7Uc%X5Z%1pdF58;<f68E|~Kz$M6~LbxFU_^O`S5iI1eD4D_fM&2>un z7wS_>;-oqETz&VXQ3g3;;_p$P)%k11pBJoczH-@q?y!I6!Jp^Q<-1|@8^TFANG1=9 z3_Xcq9l3W}mho;l&K8x3hpI>LR<(kqRh1)zMKI^dVYGRQI`9m|Mo2fXSL_zi9A}%I z@Lgea@r(yq5ggRQ6}ZYOrwu z>^czKVyQ!BWThE8J-TZWI@E|@dz3Lt6CWjm2`j-O1n)e244*fMfy-FN*$HDDRN;>C z1ZA%iJe*-K`;m%pAbMoiy6lxHDe2g`>Ntk9OGKNe!;P`=ffp3X! zF&|7^Ci7uL7@CujnVkoFi(mO!DL%&6?s=rGD~vuLVF>a$fuz)UlmCVARQ@%4{>$2YEbM<5{uT$) zZ#9Pm8|jY;DfF?)Oy<6PCS~(%Y+sp$b@Q^YaZwiK!E6K{;vR1z_i)JjcS^kEM*B=I~+GeAk%m^Lm%wDSjZE zV0s$;Yl92EBm4+`(2mk)Izzr%>GHw6k_&%({>2&Y1z>--`7ihz9ORzYxxjg9<|zHL z1xhXo)Ow^xcVxSBW)#eZrZAg@H5qifj~lC3pTi z$HCa;%55rMp>f)09ks(>^d$SGcO-H@zVJJu=|L*X)F7OWWO!ehDW&K;XOkd0Teq%r;>3xNoSdBG z6DM*KS&|Bt6a1`_lf-*WIez?jQg(KBfuis9yW?5=%)ehd+B%+8xGmF8UR!_u{kHFS zs~zL}<98K&R^xSyS>HRoBZ}91=zmz__aX22^??T-xSuf$%d(WuEfrW7E*1IR|CKvo zP#8}7T#ls-?VRGf_oO?21~>nA&wm#Jze_lOa{6_Q^Yvrq!-2f6{SE(jEnxf~92`^P z|KM;Z9PV>kBlnFy;J88mov#}?eeZnznA7*A?{Qtk>3iqvf867Hr}zBLsjdGG?wqJc zfy?LayYIeNSh}(N?Y_tPpMOs|s{hh|=_1m@|M}dDweIi!<=97fEk1RxqI+d8v^oqg z|Ls4$L+`T1^n;&O->ZGu4OH~IYT0sRM=SnJ^%qL^`r_LQuheSPkMVt~vsB{u^0CtI zD!Ny;2l<>>8-#N^&h0y2tAFC#w(!#Y=~=y3encv=>y>7af3Gd_Gg6VioowB2Ss42@aYQesXue@H{ld)(vF&4^d=F<78OP4O+3?4kV z_fJ3l^vV4B^E>E0OO`BYm)k}=w%gX0RjXFDu|@r2cLitRTR1R(>Oc4CsarATHFXgc z?Jw9u9}1((NS~38W(#w%QtxpT?a8TVPeLV!`F9dd+{7}U^BBKmKR)l>&%})WmigH^ zKI~v#H0{}G@8~S|oAs>Tr8ZR5rfr`#FX~x0p>eRo!h>*dNB^x}EZsOW=QI{>In1+` zYw^%K^YGp`8?oL~ei!+1^(HJkfQG$R8e59=Tj}Sv$PX{$^fhb^A{YKz4_fzk?f>izK_;*5&^g{b8*q2AMk6&(KZO|30JopPHx`txbDi4ev^CQ0b=39KpSpDCR9LXHS)3M5Rjma^l z{4vVeqw(uqveo3AK4<2WQ~yQ3?&yC)`UyPx!9pz9a>VpoHWL-+{tNCF{;H?B`r*ko z3ve(rJ{Jd!W6J)^b`#C~AZLHQg#&Td3=S6VJAnbNQJ6GqKJ$Z5gz|)s8pB-SlO|#2 ztl5lbyvpQ3@nb%m@Q6s`^JQ&7T64>mEo;ku3;uWQ|2F1PRZij4%oSt%Ubz2u{@D6B z=j+EKx1f5*h1la4W%{l)AlS)Pm7H@K$)|2$)V`nbuE%v$T$+Yw#_z=3dGndud^#pi znaVPSaj2(b;i5$*x1{pC`EgBX4DuV)yR^Q!KmQT?E*g8+{>P=AF#X@^6MfJATU^-w z3m+NTC(viYEJusdmRa5y3k?x{D;SrVNmG-nDKYp2#{bI>6uLF%ibYs!x!)86y?!FxBJwi!~95|R3dKU8%9eF?1|4M7mqTKK;c>RNK@M!%$ zc%uI2sPn-9bnHD6-;bYzHS0E*xYhDWkw1;jSe`j^2I4J*z4ceWWB#lECmFA$|KvR; zPkV2|bdO&gPMpq@9>Sr``>QDL+{Q*jZRC@>!;m@{%7T!x+fmge~m}}W+9Qx1u|tf zp8Rkws<)kqu8b?OHS2a>|CLvu3~{hw(-y2=vxfA?6^a?Y&}BJ(n7j~OznO*F9me6I zW~1>&=b1Rgc)BxZ&b0m4xq;v9zg@qPiCI|4dGYZN+wcVAWIf->g>n1*$zL7&cy=AA zHhs_Szs}UF#4+~KOxFWw)MFMlXWT-g{rNa3KO+=v7wuwKrliMes77rvXe1+;r{y-(}ReoMak!~V-=ZO5Qmb3Y~X1pDrX=RR75raU`-jAzU3 znz&d0g%cI+XV2Yw5S}M)o1A-Q9&NpbGK}LkbI}^+li$RAw_D8G-N^O3gE=OZ_f9$8 zD}VEi3?S@TDJ z1D!H*{1n>}eat zlqpj_q5LZNO9r}Q{?He}@bJ6!-}c>(C+E~z^y2z{{KEy<>=m9TSD5+4z8CJl=&5k9 z%hw-!a&F^co|Cs@P#n?LU(d(0Ex$pHcSoS#_w%u7t2=4vev{8I^yo3u|4EZ3ssC1& zxTF78{!w3>Fb~Ce@<{PNeYX8}u1}vmubAw3?1TAc40&|*9{tyTrTh-39662Y@s9K5 z8iV2iR~oSeD_z&)%h3z)QtL5z_MMUVdctxXIOH$CGv>d^0_E*bIe#kW!e4rg`72C+ zv<{WesTA|qRAX%6vE#-W+0c$dW3WZveZRn)V_1Mq&Unb3KXwiY{wk6)z3D$Bdisnr zJss*{y#l+A$Q2+H=JZT#9vlXPLzZ!bF ziFrtrcR{%@cX|4tRUhWi`(U!^KV_y*nHNjC+n?&c?W6E6IpyV-9XZaPVJ?U-zUXiE z#sq&At?y5N*n}6}8)snk;{IQ+g7brAKPz>2j|a%%kU8+pqPwWtGcIshYl#|l(VW*wMWsp zb7$s^nPu|2D8I4t>k0nq|C1bx@{3h(+S<_5D!Fn|EqCu+sWe=BP=e%(4fL}$j+lZd`3B< z=0{vakKp{ZTw!rCUY)wv@U8;~51REZx+VTg`tRI-*&NJY;q`0#?A%Y=SNmPzTEA-! zWt}*Ux4&A4S3g~fz+=o=bdtWd2ZtawXnYi^WKQ$Hd6MSr9N+|mCt=N#}Z+<)iKqOq3FI*&p9x9dQ9 zS%(pusS_;bxx5Vsc6}6wS3ioZjA+Ujrx@pcJ>LI%Ek@1Xi=N*v$D||FgPiDTMc31) zNP4Qcamu9{Nf}!GpFVy1$G_MAdc7z^?L0;}I_P8J{^_%G|DCVx7=;6sY~tagVVlsn z593zs>xJk;!-xyTc0YxhU0pD3)j`8sv={H|A86?5)9(3ex$M||n6Wz;Lx&ABd0-`f z{!#x2&)A2@827DC&(&DG^$6wWb9c;n+h;ymcUYB>pZP z%a?Rb^t7%sJ)L?OA1&}e(V|67|0(}y{mSo8wz)gzzxra~nVNM9Kdtnm4P^;l`E)s4 z_Z-WcJGRgAMX}et7>8DIE1sro`sn+LWxo#He%yhl+AhEt7hhx~`a0IZnrF$2Qq35m z8A~)dJsX{eu0`8{3$W5FxF9{hdBC8s?WCn!kOk-ogNNboa_&p7b=<~lBe{poa4-adOX9}=)}{*W0J7li+=iX zJYPY3kL*`_{0=+lsg+Na6PoliEcH0Zzq;hopS$*7{ZPNvZVvSw`Gd%ACp)C{|Kzkx zyw6zGZ**lo#n{X|UnZ?tuYgF5p)RG|jI!0pmU))f8T64?oD1c*(HQh@(Gup-HAU}L zTN;n-Eg_VZr}8Y^#F%^>RCGOsN5<@w&B)CEyTM;JTfs^E^4DLkV#k&g4C-+NbspyV zoQHieuzN7JZ%$@h`rj~X?Lj>I(E_t3jJ(fySQpM;!q$!S=T{=X=aKy8dvp3eSX)!n;&Bbo-y{3N8g)8T6oBO{^UKL zV`G0BRpr^K+|sD(KGfxR{XXM4UFN}-eO702WS@Sc>P{1vL$z?B_!yc)ie;lSw)2l1 z$NG$XdivVVU(sus=SAjfll)VF|EmAO>R6 zcRTpo4)p8lLp)r@k+5i7yl@E}-{rYSey`ta^WK__gYtTn&8WredR4ZfPUWrW(`6rX zv7I;isrvs~WkbTg=@6(I<~hSt!iz-{(Xsg^WV!e0WE87=5IVB&a!oLlsS8~Wm6rc0UTeV9BNi=cZba?MYuI1v=0t5WevwJt@`+B!3$B;+=x&QIJ)?2@ByHKm*3cSv< z)n1;Amq%SgwmYpW<@nmSH;%t8=RM2O=QDTEho3cVffbkW`(>u@x=+4$DrszY@1AJ9 zT45<_R$Pi+pKdd9jQU@r;$qY)zZfsDsQ(+>Lhy2h#Vm`kZVk_pQeUi5Wh&3!M;KbL zW7`&N-?|xFwrnDfj>J0mf5CzUHNd}@Dk+A78(yun*1%nT7j03|wdl~Rl@$MBE$?+T zpHu?wr#c$@Id z-Mer4A|mXh!HaOE@w~_H8uOi^y<0cN(Vm}T=5sCLSZDOiIX#{uu1i|$cOV@{L$XNg z7*~nsSap`Q8OK(untR@!q1K#oqJ)Ko<@KL?rq79nUA}akJS&)e@gr>f*iT==&c~ES z?03^@g7rc22Yw6-^I0^Pf49_6>M}9hOOg%R{E7Z+ zzf$o$(wS#uP>&EadWh$@c-E{{HJ$kgGS`hB@>OADlGip4e#PghWh?}ge^G43leoI!?IlNo@ z0GbhAI{R0LW7WFnJo%h5=+o@9?1y>wpLOfjnf+hMWEz9;uybn~zU&!F+2N2WgL;Hg zrbr`?yJljqS=)o5HIzB@y?)o&>pXnW6kTuMoN91nav(?+r2M0~^~%GZ9K!+B;yAW$ zOrm{(wjr)H6~$ZGx-p*f#*2OT!7C5#H9Td;j2W_@YyYBv{ZsuHa$ASs@48RFsff;>XYL(+uXjln)V-Pu8iR_yQ(Jmo^Gf&W zvz}33Y!Q#pK0W&`*`dJxh5B!c)`ALs2C9oLiMFcfTHo25pScz*8Oxi8F~J^Kaj#lMjI?&nvmSYZmEd7gV+&lBD+Qs;e<`xYc0Err}a2XW0pKWtvtzsU;f3vHf`EizYy(j{-f$c zkCkuTq5IhTA9&yye%8K^=B{X;`JJ`BpO<(3efHUBf8)G2_wd|Rna3V`tn%ZJKVFG- zmB$`?wCdw5k3RZHHRc#*`rwBie&msdAFffO#v{)^|2#|ehZ(E$*`KD*|G8nSkB%^h z_K0G|ihazpi*NEBPw}2dUwrX}hnSmK&sQ_=d;GDgk3arcl_#EfqAJhyO4rC+XQ82? z9qB6*diKnjkY6sI3*nwzNK|}kNNi$S2<4Ly%A6sL@fk{eGc+S3Jv1#XEi^SX^=MQ= z)+JY;9P}JK;e6XRtxpadI3R^>_=A_r`yP` zj9DR@1;g-Q0navlH)a;Pb?t(|Uw=)1fyI!WTxW02&Cdo`BB&KuN}8hjB#5VelrgPzZs2@ zVDTS{gTqjYcEp& zq1~B$QgK5B1NA|2?##73vpb(9*TswH5f+<<5p!vKU|!bGCvL&_M}7rGYLs4m23;2I!aCR07(aC$1`L~m zkG}W`Z}pslcl*spKxo3vf&Kfw!}g1?ed8-Zf3@D4|H`*}>C!Jq%{+-M-jNtO*9-4{ zy#e3N-iH+0&M#iPVBjEo497tk;w)qx2?fDrMaXg2r~#?@``ycHx%gf7a z-mmQs^uN%%{^q~|kcS#y1qDgihtRDSTFXNBATHaC+5RyoDy?G-N6vEYDHHk@LXTOD zOMQ6-qN{J>m2CDw0@nhed5DfnLg2pBn7;A=Ht&yREZyYt_Ll4q9=Lxo{_ZfQ6Wq_@ zo&{zt^se8UIu(BY{>;6mNHMt3UAR!(fW_NVkgql%CHXw-qk^S6DS>r6>h1jhQtYhe zTp*|3kYkNl7eNlhqI_iLDlspBbpSagSHK$I`Mm%CVE=wDO>k3*{ueoIq3bQ`7jl@l z6Bw3{k2n4>?oB*D?$22E@e=s>$`P<`GcnQ%H2OB9bsu0}M^-$4en4NJ!0q zPFsQ^t&v<~4K81w)&FK+>_roNSOXZfco%!S#U5kfgT&ury_}eofYkI%%pzxRbcRXt zmDjFRaxApcC$pC4SS&u>asXeM`|vb*H3y%%ilyNh&=wYO{#Ov|y6Rf(?JevNVgF0d zKok4FyO)}GJ;d5L{|2yrVRHaa9(WghBlK%jraH_DNI-0A8mdxXLyI+D()^KaB)-2r z8le}f;6Zw^A?NPm>0muhq+BGQZ>7M7Ts}V1{Ci;ET50zECfa}v%=462xA30GQ^;{I z+9B)?9OCbrY-2Cm==}LdsLSf{en12+rGJ5&omEIy)*$fkMG248^0y25Wj;P0-HjQ+ z8ImS6*T^n~LLuhg1N+qL_I0#(OZfozjfvP8dWmzriEEDg8sG!=1#WGwX+=beNwVEd zf3$_??m2BhcLzC}Yq_u2(Z+Sz4PpsZNY`1hf-)wgy~VIFD{IQ!krZ>rwSL0}G5;Re zC$8wr3SrJAav(Kzu$HHUelL+4Q-ypE-Pauvn7+`JJ4DW~IcwwakDaObWM>*Y0#lHx zyo{;~vr(C|6dheZA~M~?+({gEhQuSub`!7Wunp(>F`o5>I-|+u@23#cIOqWKa>Se+Od;c`pzBh73C3@sF!L1`(3wSsEVE!k*WVP9~0+z{HX%rB~yZBS+q z7o#3PSYioQewhkgWfT42kJP|v#@W;KRW8E?^b8G>^ zgRDqn+_H2>BFDT9=c4RbGPMZf2W4RVLm61~Mgii_SeZL%MQ`6V1T52GxLYE*#cFY_ zn|$a^cI1{A@r~`iUw$?DCTp~IMbQ2utT&hJYc$2aSFZG6-WyqXZEzBN<`rRwuL%qO znvDs+iNm4YrPNq#L_!=f)8UDDdvr23tt`Q&70m67I!~N9ffzv*^|G#F%PKWqa665x zlsaM>?5ikpRfJ>Thxa}E%rn#32I-~K7t5nhT*f$ZX>Rw=XPqm5 zZ|6;NGc>^c^#r^g0326cfoo`RSr$UULhdt;)qn zw_xOE*bs3vZPxg@ST=+aaiy*t9&29E8^;ZY9S2_snLqSSGm{O~vxVU)wP8;T)Vg zV#Dozac`^#XFn(w7%+Kw4sx^Wv0_d!rV*1YD{H~c+a2iYY3DPYJS)VBJl-zA&#qns zt~O!vQ><%UlB&s!4SOU|Jc!oEhMmRl;jcs_Zp zUM@sjL<90KT*1z@<@n1`;+u;NXkvcuRJawd56*?RyB4|R7|2MZF2I};@||A94+ksJ z=jx<=v&5q~g`A9=m~*PKG{gP10`guJ;7^ZaAoF~a2(=`hDcXNm*w^W_zIX5SBlff% zv&R-=(jaP9y5*5iTLq6P8qSM0`r@mMbvyCJx(aeYt1!Xs5+?GTDZ`ZPa})8)Hul>! zY+P=}tNi@k(Ms}%5of2KRpfEzKZfRGoLi2Vn|HPPqF_%?Pw@rHjIM9wwpH}iMxp$EN#n21(< z=4nAn9Cft%h*ybt?P?c7_tatoKetr$!au<0JzzTzCMR>N}&<)*S?h0#=`DA5$ki;LBAa;5RTM6T5pDvYIt z{9L8rI#5nbUtU*>_&qn~<#mrg_UHs+1 zt(KX5_S<99OB|8siyY+qYD~VUZ?xCj8|oU{dcqQoSmov0^psK zZ-WniJN}h%gW6l0=O*S=;at9jwMf^c801u1DJ~RR(0i*3bOYE*y~C}6+Yk|PjNIZT zN$>f@>8MC?3vDe;vZ%~*=LOaf2(2oywi9c*umlYvQ#436U%}Y)e$3n+0l&4|v3q|w zxtS_)^2CXg;(Fo@GS=TXla*G4#%U3GL2xa1GS=uuzP1{NW0d$XuLomJ*>Uhl1onp< z!@(2j2>K@Yw79;y+A7m%wa$9-QHWSVQ_0%BGsUQ7O^H}<-{$vRqOW*43;5ff6t3lu zl5<&6`X5e~qJUS_KMqZLSB(Sd7Y9$wO&>?)R*oE^+r%`Gk_txI;F)6DbbI%xH ztE@ms=n?69yS-kPRb1_?;a&!zBmFbuVo{?ZVxlZ<6YG1!$j@Ow*x5pSS9%S@sks>( zpNX)LgHrn&9St&#xxpEJPERf13S4I_`4e?XkEfb&BHf5X(Ms6MBe|ZmBRX9TclBMo zuwRK?p9f0UGe0BiyVK)rV2(T@s)Bo<^k`>e7C4}rTz|VvA zd&_d+IXewU_p4wjGax=8en&%Njr*3BMpx)AD=wxsV&yCizWb^IYZe(`P`AKKrbXPz z8gkBS;65QA@&(0s^S7C>S-OZjbVNBEbuwE`N8i>@%bi|-H#l=r9nS8a>(21KR_F5R zI_Ke_DrZM$hjYuyQfKV(YNy50==7Uk>iqt2QtD!l`A;=Bw~TT$)_uTs$ZG4VWuiYE zj(S;BORcQhW_53Dw#n@FTA8)l;$CO3<~cmiUNe_#_eo;>2uySLp#SpVb>bRv?LPql CZy=fg literal 0 HcmV?d00001 diff --git a/images/mapper-icon/README.txt b/images/mapper-icon/README.txt new file mode 100644 index 0000000..023fe85 --- /dev/null +++ b/images/mapper-icon/README.txt @@ -0,0 +1,4 @@ +There are two Photoshop files for the icons, one for large sized icons and one for small sizes. +Due to having a few different effects on them the icons where made in PS. The main parts are vector and should be fine to scale up if needed (the large icon is 512px now). + +The Photoshop files are licensed under Creative Commons Attribution 4.0 International Licenses. \ No newline at end of file diff --git a/images/mapper-icon/mapper-help.xcf b/images/mapper-icon/mapper-help.xcf new file mode 100644 index 0000000000000000000000000000000000000000..ee192cb06c5a0714bff1693bbb57a4db459debe2 GIT binary patch literal 6244 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 yvINX%7PBw z8XZrKF-WB&wf~Mj8hN9%Pg>~rufABD4#-r(jxN4nfCqG-BgJpmgbws`MpX_SSWI%r z!1o)8@O9wDGT{D&>oczdixIuk+JJz-z@WeY=KynHP*9*#pi_`jpgt%#$lKWo3BlcQ z<4534A^@L{k6SK(jv#@!2X6jgqC1QP;S0d8VBTN|`XXmA4=m7sh)jTFSLlH&ce&Y- z`7@#e?U*)+CVfw%PtoU-(a+tSCi0n|q8*dI9a9|9&z_=}9llQ|(YBZD&tu*S{`6(r zpJ|JW18oK<7(B)JW@iGjpUrY*IkWXm8XOf3aGKc!wlizsVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i*Z0 z6b%iqfHFn^01NaJ6Ck)K{!7zgipfezX0TDG?8X~Dx ziCt1fYh&tKDJ*SmeI#u)Su0uAN@S%L->7Y@1`CQHNP!_l<)OeZVStf=fqCD1XYQT3 zuXFC%yMOSAlePN$^PIiU`R@Jw{eIu?PiU>lWHLC8!{ty)p|wUziIftp^_6!*2(;E$ z))Q_H5j^@$_AH-KEWJHmkzn@ch=+*7dGdjCXW0`r&R=&mh+Pt0-Bv6ti>^I4(*E zq}Igt{EpPl-Dm;9o99!sWFa{;b}&ffm0uIzbBOTr8z}tVt$2=$%4CRcco(;`o9e%R zh}Vd_hS}VRNG9q3 z#mkIr+=)AyKp-$){gCh--=gfvA7WRP5otS28(tDKu-D(EbzlX3x zIWZ0tr;DTS~tB3pKmKG#p(3y+e2-|gs}NBQ;5xIII}_8g+* z*hylW_h1x+seI-UtfqRD=TTWVh0`n6GW@}()UJ7e9FS8vtg<2!ZAZy`{S0m_i5E0b zc_jp@%1LC>^!4=-4u@-`)*8b!M54Qo0WX2Sp%$I<&|^vb#U*%COX!acprZ+*Jp;u3 z2I0C|bRvl!PmxzwOWbELbmlx3f^p^2mx+;g?=8d)#o2vFF&57x+ggKO(t>PmVDS6} zve~R29UVO&rIdJCNBb5u<5&T@KH7;&XRwQlh#osm&w(QtwbghH6&Q0Hi48~S{&)vg zVLn!20cW>;N^~fKylv0AHbnSO;;X=!Q6MKCtCwn3U%Ssvk@;Iuao8~7T#8+8L?efK>w(Y0; z@fS($I0zaBcKwcmya0{&{uvoBM*x!|Y^0l`{5wlA3j$<9A?B`ZA%A28!_KwDqT`u$ z>(>2z>CziLskO#346MnMnDhJ-RNr$lF^fs+h{`Upz+P!nsJTu`+2wjGxvAq~cNPSKrNW*D*{o z1d+5w9DFj%yxG$^G1`ImSr^5#Z^BR`OyU4~w1W##P3Y>!wLEs*0R+G$02;%xu>wJo z!+o6i@D+N$+RK#Y1zdmsQ<(k$xojHKA4EA>29E7#_rKhaK%$&9({5YE`tb&eEzP3R zAOT@>`uITxBBT7tpUy^he}b!^JyJ!ve-4&$R7?OMq)DRx97jKTgQ3nt6xB3w-2*?N zWZG@++qyEkA=DpL6zfJ3Y5P%1S8+^tN-n{Qy_3c$jQD!Hm$?(6f)xF^4nGLZ;1Y zWzm9GKHInJioBWtZYIU-@BIKr8we?JGs%m@T$0ku5-g>}Gz^diz3uz?@{MOHz2yO3 zRAn?r#<;#{GO@GA*!$8y(Q;!8!NT97(nFjY4rA5*1rOb}h;TTJ=Xp#302OOK7=}S2k>K-#hsg8U^nSda*5&sxyN+<@ zlsKHFX)&>ED|@{a(3)(S42*tTs<)1R#;r$XHw_-uibN{}2e z5mH{x0d0QM&4^2m=g^@;yz=U6bai#n(sB(yeE0`6Ha0S~vYIFFyPc!OQ7V={!ujC@ z=eoZp=Xn^WNp;OMUi{e_T1yT>=K=a+n&G_Vgo~?j#-g-ud5L+;{}O506DiPIV_7~z zd76KFVIA+kzlqy#U(S!8ewxb4a!k|0)soIn-=pKB*O>pn)0EAaOS4u;c`Kk8Pp0X+ z(9N`xQ8@c9Sx3>8nT1<=Ej{};Gu-((l{4otxoIxB?DmOC==b}1`|Y=w+uFk7#fw?7 zVg+@zwWQN&oSeed9-Z6Y;MD)V&$ahI#pLPNkQyJmLY)wT(L@TLj*}nX0oftW44Jf_ zv`HL#hy3C~8os@Xs;gQMh6NWrrkaogmSv%oVs2|Ij+-Nub&z&|RJ5Ptn>P^aKF;+I z|CGY2CfrQw8^+T_8zy6;=a}I?f$0A&iKN5HXgw5M!}Ph$gr`;#42ICk!!QkmVB!sA zG8tB`{66F3V`N;9RBV``(_b<8`vb(fk5g1Llf`S+;SZOP%cd{7TgwLml94`6w0%rj zMSwuTUQ}j?Gtn?k^?lS#o`cGzaI+3sl_Pz=gCcuA()3MiL>Fyy4mX?T#J0ESJ<^6R zT*Tz278-84i^9qVv{K}P19*u6jO;ie+hF?477Wu=6U*>Yw>$iw&yg94Fn{&a6jnEZ5GdEda~-r&Nb@31 zzu)>g=RV&~>CEexd*9`pKN!+dN_`o?#g?QzCN<2aqV-1Fp8s3E-n$7mo5u77E^2`^80qO`xbsU| zR<6OYY+N^s$T`@#BwoCq-mwA>$7XZp>>#n|AhvB&RaHq%O%0VbH5BHD7(B9@)KCwW zFEFtRq!5N74d&gwhQ6k`oZkIDJ)do*qID^?3zy*wPol5y3{z*cP+VD$lNkde-VbhiBl?PjT&G24`e{6iI7n)zbU zX!Nkw=z_-i%&BT79giTSK`QH!93CRSX@SU}v5?5%Ao}6}Dl9kIl87S8r?j+G**-sxxvIP z_&{IE8&OkHR(R-(&)03){n^`&a?WioRV^Bv9z5r;B4q#NoG7 zY`s_<1zPvdicE1eF)}tZHx)=ectpBFN|@K@3tyU?)0s1@j$hccn%s69hn`=ME}D|w zH1+6`eJfVizuLPlOKOse=cFg|H>RrXx4RWNb6;3C!}0R(?>~R*`NbD6`{vbpwq?S_ zW~1zb;|r z`XJ^id3tY$;Ds$7cEaxOdbV$4Uu}|+@afy3^18J2RPD|?wmRE%{Oz-oH;}Ah@xp4OG&G*xUQI7tY?&cIOm(E z=jpm`JqjLqSi^8Q9AJ#iZf$Kn`s@!o{x@q@-Gs`DN&rA_?;V^wclP7Qr=8d!%W{ut znvW@p@?TxolMlj`9%Qg_<3_(M%XOPKZ+;KJQ2-E}+JbYqE(bv1a2J9IYsd!c*RQ{Q z>C&Z4BodjP&1Ns{+_}>n4WE9=+@z(YW!d)a+cO2=f^Y$0nkELnJS@|K;27Te9XodP z8HV8sA^Laj+-d2$u09fhNF*`^fM{)Pefz%52)J$AFwHznGY{J`K?v^g{no8pZvhb5 zYXmjC~o0bX5^nd`cZ5yu3Ya7;d{22fsxC+kCmgwUf&?i?t=9?*6DMCnRtiIH{j;>9DSMxFZ_@3_=6R|wn?LeR3Z zGQznF(=?Gc^8f(V>qR<~$`Z=EO>bi9aU+x~#nE-0x3sh@E7lDk!%dquO?Dh77>!0x zmkun*(#E|tt_P=lK7X~}S0)ukb1sk=i054HcE!qLtNy*~XZC0qrE=dq&=A`50&IG$ zb6?|~(!Rx7`xHXiNmW*>6jhOg;K-yiSy#C4*k4pN9Xax}Q#xj@*6s~}J>O}pYy*Hu zB=TG|8vO?_!4 z&%D}CwEIo2Y3*`3mHr7v8Vb6?1s#8Rza( zwq<6sshi!MZH+JKy3R}OzVp*JJV_!Q5CRJbh^M%(5DT=hCB>bB5E2LkcpwlMM+rMs z-?Z;*_mD6`=RSY;SLd(3yM7En#C=xIsuN>(dt_-rFc>6)h!Fu_$T8%Z)g9Le~Wx6CBx?o)@2&^Z8@Na$Wd)|0M}8>9nkEMbpr|TyxKrWSqNiSKpV4iE zl766Jk;g*_06(w0_ZAV!0#i3LI6+|1xk526h$5WC>``jkzdG&KT_1@ zg2QnflMu9b8=;$3l)kz_RmT)8)~jmOSV~{1*V?}VL7JGvyp-I977!R2BzIdB0D61B zz@6A_#N)k)$9vJ=*9X%yK>!HBkxi#_1jOiUTlsSUR0V#qR#B7!$T~%lW#E-=BXpY! zKF@KS0W)tpgYyVjwvBWqjYKMeM4}(5R1&Fl0stUU%051S{tOoa?hvnMCOs8JNn2IF$>2qbnJ|kQm2UlO)7!6S#JZ^KSz{edFG#k_X4i{AJaY zNi;Fg|NAau)$dRu6ab>p=rLW_Ty2c=|6Jw#iLb9}`{m<{ z{?NRjY0nckT&MmD=d=4bKLel^0J=L?UgO-Io64kn)9FNM-9bhqqKz9jj?3rsckg=$ zix3U)84mqUB8kCegxn`!T697x(*`MaW1Zb zh~hXLE_NsYY~Q~9j;gAshj+mhok{LIYZ#%@Du*xD+NV&6_f=U9_6Dx~^y2wrv+n{Zx<#UB`~g563Hd z8z1F59`?)*KkmW%5B;S)eL7rb<#&WjLw$yk1P0Fv;tY zJ@wVeu?H8v^ya8VXlX$*A~>c`pUyCt(MCwaijHxN+s6sPWI_l}r_+K`21QYzDuZ+7 zaygXw13tk~ujDEY&ehtz#Tc5Vv2ZvXC@pkLYeEaar;$ixX*3!=INTw-bwc0`*L9Ih z4uDV^uBfaGDvAmMAe+q~-WR_GiY_of2<`?bSq@-#0ivd+rh+jx0l>M@cBhz#1OUy= z&13G@|ARIAue|!3bE0|64bi;ihWN>^KN3q{+<#~^ywYtimixX7bX|8eO=Cic+M)Ic zJ0Uwh+z5u@DX=WIP3*eZp>y^1&?pvCsov5j)k$3Fa zk)s4eQ4|bb2L!SrgV4JK!r^eO&*!^rn&vgbFz%_8ishjnr$a=hX0zGzm;0W+K5JIp zxYEbz%*n@o>-BmMT9!3F7K_Dp?b`Lgj|%pG XWIp=ogJwFy00000NkvXXu0mjf1u`(? literal 0 HcmV?d00001 diff --git a/images/move.png b/images/move.png new file mode 100644 index 0000000000000000000000000000000000000000..02fb73307516b1e1f0ec713b6d7f5b29bef3d5ad GIT binary patch literal 1840 zcmV-02haG4P)Px*>PbXFR9M5+mrrb5R~5#8_ulvBy%~FI*LEE{sT~qr6FD^Lkc5z=O-cz=Vl2cW zk&0L#MHL-&f!Jgbb&Xh5Vv!&;ijaT>QV|Fh$s^&=ruid7Vgi$PQ6Y>{wNX-MJTvy# zGxPr6%fjO{NkdEAu;8tp-aYr+@7;U8?|fIV&L@{I?iWHFF~%$dr^=;5Z9RF!Iv1Y+ z{R7AY+SUQld}si2`C&fBq$v@1`O@*HsKrUZYzz@5-vL~LJ_zI4bVWrtZN<|bJ5+evB zO|#j?5;lVan^Za@{P*<%=qpFRF5>kN_v*ILTc4{V@?I`sE-9 z!qD?*w>*5Wh1d3Id%j+2`mZcDR>YVmvY&y0iXP~Fw7D%LmBr#MQ)wsS=e+~E(;B<1&TT1@y`Dc%8 zf|$R)Jwv5>>n-5La;b3bHh_+{zdo{Y;}c(g>KU@#J-@^|)0^g(U`6wyS z2#nTb(k_mh#&y!TZkm*v!A+$}rPFk0y2+$dC?#n75tsjdmiIn*|2pu36ha)?Ha0f$ z^q#|HT>G}Xq9u-Pp_H{+?NXwYLLw1DqNE_v8lyEz3WShIIx%wX-NLag(5t0pj3MLN zJiX^I+s4L5gb+uhF=n~GRLApMca?^;6d;ijq>>0BFb1RoAwVN;mykvv#OjzaAcR1w z)wv)$xjrI^=eMXY)iK5_OW;(cTEB31<_wMHCPE0nptUARV&Yh%O-zy`Bsw9{dbJO% z{kfDFv-&O3dNn3V&}MaRlEi4O0fP{N#&VOhGiTO3#HqC+>;XyxeFF!c+4~$l*kt+WZ_NC*vC{Nk0isS=*rDMI z13mrLM>Nz_8?`kWzIlgu+1YOQM~4p_?THDv{I~N|s<)kiSVWJf{Eu|F3K%x^YWhcO!ZD3Hbh<{!!keTsMIi7iyL1*{kP37=Y|OO6a8QlyKcNj^mO}r%AhMEZY|Mk?kzP zFnjeJ3$@C0M}TgAVlOC{3iH6x+G6!gqtUw7QNX*@Gd9Mg+zhEy2FLBf&16uPZSKc}rP=n@YP%Sx53HvspaAE$&1S?E8lFj7yBO{+Cr<{9&7>1-t%; zZoBM??ke>U^dD4er7kL=hDJeZLn7jm5(v08gqI)OnbXC!k+JW?xKusTXl69mbH4MM z@xcEq@?UJK$`~hw1@dkN7mNd7!v<|zXR5g`LOT&)eSQ72TrT&mh?I;m7-R6>>yxk%nY!jFra=|`UYN$jJFw$o6!Q~hf9-+XQxm0cZ zJOI$ON<^+(?>q^>$`OG*B(MkM>^|0nAkF)!0Ktc~6Mw%ewATx-0 z2=>Fy#)6oaAmto-RYqh2Z%eTNabbvae=k5%Xre;9nY`Cxo3Q|K&2t~+0bbw5`9+*Bg4+i7q|>%JJZgfrw$XMK(PuKxMWP1) zmY0{W>pwdb1w8NJv=RMSizu_;qj``kQ{8>c()+Ea(0)p>xdkyNp>qyZjjXz5Nh%eb zJ_06CKaG(SAOf&5#fzQa`ScU_6UFA1NR%C^9_W{0asp;?Orsp*IYRUtB#$=>{POGf z$mGS7-aUt|f-L1XOD{mFpjwHh3d#hRje(RK4xrbrCP4qK*Ol9z4QaP$ zB0w01u}ZUm;sE+(Xb;3G&N>H11xP%I05&!@FlI1uh6OanWah@e?qL{S$7*F|CG}!T zQk>+WI7tmb|J9u4iTE=iTT(+XjMA*NEST6TNGiioz`DnJl8oemyOdkq1z9qR=SBkP zt&A)XRZRu!2Phk3PF1yBE|(FB-UUGr{Oh~`$mjECz&vmawE6)`KxJ=l@5|NI)h|Rk zFN(X9=I-w9SHJ>bfdjzz`fv=G1MUG+ow>a$ literal 0 HcmV?d00001 diff --git a/images/open-orienteering.png b/images/open-orienteering.png new file mode 100644 index 0000000000000000000000000000000000000000..e1db26415abf6a4650008ac7810f87279fde5aaf GIT binary patch literal 45070 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 RVTgc_!=~1RQWN)!{{s*86^#G@ literal 0 HcmV?d00001 diff --git a/images/open.png b/images/open.png new file mode 100644 index 0000000000000000000000000000000000000000..472484f11279c3e37a20ddcc8277c516ad246115 GIT binary patch literal 1176 zcmV;J1ZVq+P)1yxd$0C6kuV7bR+PdY00{*lQ*M=l z8i@j0IzB)oK0r!@M1^RfLrAAU7s-kOiqb{G#>6NV#`n5+-j|u38H#(!I(Lo}a#ASx zOOJMTcK3OH^E@-NyCMujUZ>=1js9N%@BwbzxZx|Mem2IuchLhvh}>HH`NM|~zg!tW znx z&Mh?nAq1P7oB6@P!AHRDl>lh1(=5xxrAr$aW3bkq30%yC5Q1v8T0cBI{9=21`_`;~ znVfUlTKn$b$Z5|!&XdlVufDtg z{_MP%0o`cSt6FQ+AChLZ%>LhFqBx(7Z0TsvBM}Qt0W>N;D$@u-XLfnM6&qu8zdxYc z?Gk5}dbNZ#lN;pJ!Lx=t&)ie96SLNlWQH`$!HM|>RxCkQsRXoIEixmh1tqLgcycwb zi*cG$Z~BaeIo7%-3jleZt0)@L?+?)C4XS~Ua}H0Qo97pkgHfu{xh3!=ajLB``OyM^ zF(xaQOH`X1tkudmKnk(E_%A5q9HUf|TZiWfg0jbHk|`m?$pV12HXDt`91j%h^-Jj7 zzS8J3ZHY4jPT)`dls(DlRON!Fldzb9)c1Y7AfQQ^OxX*AOy-!wy2(Ca?8H=Vm#s*g zgTW}ZxpRvE*4kt|9@824R9a=CM3d?1O9;q~MH`3CExDO)%P*OgL>Y$T#1qn6T7pyv zfeadyNs60>X%$s}D(4*iBtcj^SO93Z+hS*D$3(GWaO(K; zWQgp(;tse($HzmPB*O>0?RG5;!*~t=cu^Gn^vj|6Os(Vpw*Sf>yJzIU0$80M96$Ks z>*6&r`!#E~+s$BY?af-y+H^Q6gv?ojDIcPR55KRq$62A|r46qc=Z1 z+Wyip<*w4l7+;fWN4R2eBK$)#e#le_XtH!?-*5e(w7>yEWtqm~gC5mEkLvKtb(-=m zCvc)Z$MkGnb8EggjfB+-Sgo*g90Z2XlnHW2GQO0_g1=Iqui0HzexbOmRcrPKP^eNt zvAm3#NCZ}m#E1%Ua7zhftEFyJV|$TDk&cL#fTO7i*<)j%-J{}sK5?$+m(aqNiSV^X z?a}IrvQwock&Ggx+7b;Mf11iI$j$rQRI`TyBm})T zkQV$JBFlnyj|IF6;>f@Ngk-;H!XeP<$nNGNCWC@hBIL0^2>Y<)7hxazf}iw^oc`|e z%5*A%PKdzUjoHVmjOC_nx;%mpdBA$c5fNkzO|kN{zZY3Ey-4iu4u0zu~pBtC?BFOSKEuuqcZ zQ{Vg2h=g=br<&avN+!#^_8~3~|zxuliPcH(HFywfA~WL4JP0 zC)LK?T1f~YxHt@125wEWSXheO4)7Umn^6tdB%sx%_ck{kC?W|00ml$zk%!$G!T3Vh z%SmKgcfT{Z83f{H!CMVkZ`RiAt)wW52ro~954ymFB#heGh$u?mpX+0;#Xe8dw44gK z!C)ZnBk-b0Z73`*{>WJQght?25OP@{Nx<+F3)hM`VPlz-@tnC_uB5>AdVSmw5ZfsA zkH;Eniy%t~EZl@Fi?Gdem~w<&3@f%iXty&OjW8OGYtF^?aW(KxQ}(-cwbf->Ed|zX zfygmf@{6#|aX~?n&t4etTi08e*=$~ujL~SkGapw2+x7aBdusNpg@7CE@(ct~#>fl@ zub;OT?PJcMFEQ|Y$MLT3BhYq8{m_#Y6?qwjr4Yjb5b0`M`}sU39AO6|@XvL2Bv<8< zPQ!E}t?}VUN+{m%g2;M6C^Nu_l%Irny62C?YJ9x-KS&pM5zu6*UZ8R)LI^s+F3sU$ zPw%2-eD34U-~3&PuEuOO-vt&O$Lb>PvAm-LBo#h1M>|7>&2=T>#Y;PrZKw*W=gxBb4qR?msb)jn#(J%3R$011EyK%{Bf8x27$ z$Xc;Co6UDZKmfr0UC>^)K?0BlKo!TLgvb~ILeyxQwxy~8B>-Hv!M}Pxh<~{$H_K79 cxcu+hKe~1czV38>V*mgE07*qoM6N<$g8$T4I{*Lx literal 0 HcmV?d00001 diff --git a/images/paste.png b/images/paste.png new file mode 100644 index 0000000000000000000000000000000000000000..dd429ced625603273671b08c983a1c31c8207649 GIT binary patch literal 1027 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 Ef}!P{mjD0& literal 0 HcmV?d00001 diff --git a/images/print-mode-raster.png b/images/print-mode-raster.png new file mode 100644 index 0000000000000000000000000000000000000000..e6e976e1bcd33281121fe2504b770faaa35d3c9c GIT binary patch literal 1686 zcmV;H25I?;P)

    Z>`DU_RVMN#=dsuxRMXal zV@;8C*1o;dfJyzs8DZeCKJpHT2Xwm^jz7Q$+w<-5)vg4gcF-b`RR?lTQd?V_KDejn z8Gy*2E!3WJa2E@N5}(CN8z&xURYyUx_a{qWBVisS5`fXXlM{f^{(*tGu$3&&Jo+;f z4FRK^!I_y5&^(c`h8sYtBhY0BoPijmhN-2^rU%d(q2@$ig zDO5O)iH+3*ry?FVm>lYxn{F+kemk` z+!*?8VGW&~xlgFC%61sbg3t)B=5!5LWv2bW3LJPYN8nI?(ws^2>v9OHSRN zXsUUdtyNpK*#|cP@=+2JhPEso0{cZ;7(d~X!+<{bc?g3wG$0f!E32O9t>xwKwHVPg zB>>Cb%>MHAmc{&GG0E?;x15CW8XRxvvSkh%8gB0Hna?vr_@U@*WllZSTI*UX2@z9N z#0R+q6o`kMoGQVdU=#o)UB}D}=bM8p#OO*Y4+qCRZtfSa>FS1yE)2El)~r=8x@m%c ze)jX5?MV^`D2kLh_R0N0sT10_6s+Dpm6UO&fiR8ZwSFH;uUP1L>46NR?@B30tJ(QB zuZ0;IeUWtx;!4lhSO)l?eb+58sz`(MjKG<{q6OYRu&YGPnM&N%QuQ|XsTlpCd(|%) zNgYrgJb17?UdGlQ!J-nWL`2(C_SP8`9;7_^P_NkvT43gVr8h#XI_gypEFg9BSuh`$ z+o_ZMFu)CGG^zxZqSS0Ogl4kHiH%l>x4j#V1L#vX;<$)uZZ-^46#BrA;$o!ohm>MK z51LtAR9&umB*U)#v7@tdXLtAK_;{>AXUt9$iFQxAJbCT$VytGQ=jHjKdWi{Arntw; z8+CcU<;kB9EJ?%xbFDuE9|}T8G^doX#7C$1Ft3N$yh%dh1M>m0K%}oHS;8N*>S7ne zH~<*dP}NW;sfNHP0h>2FJ1Yhky@rPu;dhf(RyBVwMO+Rkx((m4{VWf?Y9Q&Mw~QLt zs!fLTa*MiJ(#8w)Fkv8(2H2o`uV%hvbA9D$GKgj{t?~8qy9cTOWD@)UTHg{Z3JiX* zmW+cQk+G`yn3$}C_pt1x^51-?YwDLpOJDbSnJ<^VO6e+MfxN9mZCZ*OTac)g*KV0W)4QNq&B4JP9Uo_!Gjnl0&xowPvqDwl#qAF~ z3}BIx<<;1Th-N4;19o4I<-O|aJZq<|L|R>U5QsTIUHpf+)4=*c=%RJ_jQchyx*JOY zuvX@)GFEsE3KCLeS{=&uhnfTZ4g_EVa`NnS_wAW0kT$Cu(z!V~pZ)#Cm5A;j#=C&> z?+dT1x9iu*zg6{6Zx;*}#9}_R%FC&mtHzP-h&qNd!K2_6)zBb=6h&eUk;^lca~F{{ zox(RwSX1tYui;3MV%A8pF(E8~&%&JtsM;-7O=$p9w_#!u6cmJPc(Bb2FE>GX$D!m8 zgY8$Taj`#sdxI?(g#8DwqupOiVIGt7vH%qv)u64%&&3u1ihXD~C0Kj5`xJy0NjNZM z7o}HBNHrbKgSKro*NlrtCHNa`GPVKj+>aiqwsbl!_q>B1gv1w_TuS!>{?U5?3m<}m z@&5^(ROD7i{RFzUEKzd z10Qm8r$LEbl~MUWXgyh1R~HJL+_O?MbyltF?+(o{FS!ogwnXjRd0y!y$d|?D;}IV} zqQ%#1y+3~Q4Z7I+co|q2VsvzLyrBEROl=ii00n6*fngnFkPn!+k&qdxI4Qp!0|-1X zH8f@y7Sg6N20{Ay@`Yf^^V0njKD7c2LjhF)k)~}qf!u2ZZm|Nj3=C6@R&1#Rv@(y! zbPw>?q?*754s;*vA_;2*WuWUI2SC6kT3`~0L?JL9qF35H$P(j5cB{2Y8xG!!sb#w&4 zMLA)Z13KLk=zq}yj;!DtW!l+y1205CK)^tfGQ?N={>2LDfY9kb0BZRX5%CECik6nv z>JIB#?dE@T`+f?cQ6%64kRB)Ci08$j-?qGt+07jM)5gy3L2qNOP0If_>nA(mW(mO+ zw!7nn5qQ4vo8`$B3Ou|4V>U(CB&8v>B(cx4{TF!D!g64VI0jqycR(`l`qNTVbu2Br z0V^MGH{xTLbV16&Qv$zX2zt^tSmflP@4jwy&Dy%ayREUgIcUs&O=$n*!-tDhB|Xn# zKUCR8%86pXTSVv*vBxi+k75q~@G!ZM;er=N-FlCAM%ZfWj~&nW+^OizFQ}=g@Wdl} zkMNn|kmuD=ZXnB_Lrj5oIypHJF@5o&uks?k^G~*2JiclnCdmAk@WUfEJ2$^3*x%L4 zJBYNuvIbojJ04EA1dcGZXE*}nnj5gK$7%X^??)jYMg7$e(wFC~w!|c$7s531f3*M0 zPm)=_ugrD-DEj}+qs87k|FaqVy9m6k`dPw}#Umd)G@?;SYCOEK7{dSk#7s=jzDw^} z*0}ys(C$-HF{0UT^#5&qsGLub{>=`L-XKZ1ALDy0lk#9=h0B6iVrB3 zlwRoAw~!CHLCnYLRe-5O1)M8_h-g7)o1jMm#(npVi;|@wj{}h;9Q4T6PdP2el7$i~%I0OLSr_Z{~*a0Q72;M@m76AiP z+l4k_&DXE_0C)pe7J&KL9k}`}77Z}A9g}#R5)u+3pGZVbeltL8&C2&gbn+nk*$o;= zBz*$VZq%P1=u#gKwG5u`gyCicbTUq(o;%zd@X!*{U0^hr74FvOd(YAb>9M|d?>$m2 zx{8VS1LzL2al^QmRKOvp3-bje_-Lg)GuR^xp-#&s3de)}1%OHm$QM@t6=NXBr@Yo; zhOc0SPk{Y{0py*+R>w+z#?Jo!izO(p!GL#G(Zpw8?90oAHs@a3;tYEPK1Oj^um<_%TVDopa8h%z*)?dxdH*eiaXHATY>j3Ku z7#-69&caNV5ZDE@PFpi^aOQ&5#DqN&MyoL2hHZU853Ird7`!TkFzJVRF$fq@v9a(& z8^{l~LRWx2fMe!;mE)Gp>Hs6ePUh>XM?iW$!d#t_^1N^05VrCMj1=1+LIAmI5r8Qj zyexm<83VXsU@#6T#zvT{B0Umt9ypLO@Nj}Pz##kAz}u5S7Jr~wPT&QTcx9Mq!y{3Fz%rqc z%)$3S$Rka{NG%5du6J6Re%?40Ik|s)JS8Z&9sB!ywu?Pgj+S6dLGglTAKOLO7j=VE7FfydxMdws0n+1CLbfLY9m-%jBI9nOSVB^$oA88D z2<_9O>B|cr$&@6ZlPJhk0CJzba&ow(2%?_TQoLii-3pxaEztW`_sp&$O%RY@Mee5# zNUa`4AyHV~#3W0B9h^Ce5Tec-CBu+c&)&7*!_-8%;EjZF(d^%e83a-0%Yu6-Fw_MZ z4&<Y!vlm|#Swuq#?Z}Z+S}U$E_ENW9Vn&dRSQ#7 zQ}9I$>P+?Yuy@~C1*8fNFQf<}nbH7u9LNt_D8hjLvsg3!J`D#hZUrH0B$fjY0!kRu z3qPnk@Qh<#f^^suV14{8l@Qt>x(qJCY#Ay6JYSO?u>~mtx@+Ua1cjrcBg|9>^`g?! z(oS&SW-6y4buLy`1(rrIszG4N1NsIi4R$yL?V_{(d!2fFW zFfYs~v&2)fo@a^yq=Z@ru}M^Zk=P6qHYFl-P*ZSlWH*2B-sJI;RwA;PC~rVULPbRd z8M|h!TQp25*`0UaLfCXdZvm&*BH#@`nzz){)Igr?fHDvTwFXKp<~M*p6V=Y4%xZbx z16RP}!mr&4PBf4%#Nc4d!6gkh3$~Y7B2vr;M962Whe+qy8YjB&u zMS8$yWv>M>{2fp-JY9=|%ykNi0M%?-P*EZ9U=Gv(L!1^VYWI)X+1XIkNRlZRn_63a zp`a9Nu)tW1Lf9<|#`CLayC{H=!Di(TE&#wu7@{z|utwuQ&BxSI|8?p_+nFvv4w4TdclQ4cO~TTpTDo@%-IzUZ8YPh+{+b zh3A`#$*hFCDo~!Fj;+S=!Xrb;5WhAyHbJ^}U-%|E zVBptzIVylC|K7I$&eIUX;C89*jmHF!f1{%-i7VN(|Ke`D)!z{t!D6EBvQe2fSv}R3 zQymd87H%;q*Yxbwvwkr&bWP>QxHld@mbguD<%+st*A{*(!WIo}VD0_ZA%ZV4ns3Wy z3c7aSyaiS#KY#zuS>ju_lwZ7{T{4fkuNesUG%BlU`wQehpa6i4KPqRY z0x^da0!={@X`;D){j2wWM~7TtVd3`fZZb4^P?eyO-^<6-P*+#d&|r#v^FmEcMN7-V zz;g=JxU;jf<*^cG5V7xYbBFi!Df3-`iA#xr2si~Y1ovEOMwv2HvloC27RsnvTR+6| zMimgKqAT&#%goCIKoov<=7s`&rv>I~^D>gQ>7#NgZ}jw_pw=64-IKYA<^3M|>(JOo z1k5F4Mt^nzzk#Xo*x$j; z`0*sp1Ov4*Cq6x>WRK@D&KDOGyY_GNfaG~#DI*{v z`URIkIuM~sz~4oH)T_?U&rQuE`c~|a-ZQYes^+|GfE**QRt%IBhy2%e-zSDs&;bFo z1H5i&%8bo9GeOr7>8+b3q0?DeOvjEA66j4$O@#*+8dz9ZzuVhW;COfWAdz8s33~lq zHa107-q$sK7{5^m2Fah($|R?!e}clF{EPu!VXBYcPS|;)%V_TkwNppfnF9a zbh&KDf3Q9(?%^Q}z@fRnKNQxqbGlVG2P%`2(hV@@8g%DocRJYdQ$pn;A|}>_zY=16 z7@V2v2?Q_%2ni3TJq*O7-o$X?SdR{J4_W5v6Wv0f*zWAMvmt)d1aE_Q zPH^j%BjpQF z>mZF>Iu4GI>x6VafCjj1^qilctCyOQ=GPpM;S23O`*27~OpLBs@alXq*79h4`r;D= zoS7;OgpS3N{9({S8Dn)dJK=PZlQc-259;OjWx~S4lloV{C4HZbEf`WB3mdx=?J%2X zFlPJUpm}_pJf-(Hz&3bD4bAgx2NPg%AQ?Sf1DA-%wA@|r&2)hl)A`6N>p9baiG2~t z*i4%iHAs9#(`w9&?d|OkZ{vW55M|sJD4AlGuce|Q?(ED5vV54D6~ry{l0q|!sQCEK zW5iPC_wS-<%DP-+3$2G%*Jm$ZzGE({EYi5JNmk@idxPvODA zK~Nh40VmmlS^^CLoQ`SVzInA!x#mpmY2@De(BRdeQ=Zou0U`o`;rl=b%cflXfBqC3 z6=vb#8GZnb+k(aQb^q=d9MlRFO`te`qXFD+&~qh&C|W@@q(i&fKuPTbUGhH2wxIeT zj}imt_8qpG7&BUYO8)!Ut7>|?pL)R-ghJ&sa|MDMrg2SRQ-ZqP97O34-`_2Wzyb9M zhx}0h@I>LW?M9dO3vk*Lf&=Ra#w^b`g*HhLkU*S8M1$mChN`QVHU#>Pw`SkUUB*8Z8BqiGR=5*_XQ z%=XvYMbohXGY)r-+c@xW;J0TC;7k0%eSj~xEt=~aD}%x3ynA0GHv-ZfWoBii6^;=3 z3Iy2lz6&g3dw(A=!u6D1qeU6H%;#^+&E-W!MRRQ+((cRLgT4)v0sJmkMS=Swr(&S< z14+qN+L$JSRssYWsjKO=1SKhFb{t4^8-{frFUl3Qe&X^4XN|!UUk5fBs3|>u6=J6ae=7N=((#<)VLr2fP6+Id6+d!6Nql z!w2g(_70jICzxCaBAQz-H3|6vzjL zi`8%9t23^{26kiENfx<^lEkF>+ABp#bQ+y2yk8*5l&z?qA!7 z*PMSU50|%hb>*eSTLeMMwg1cn&0EN2uYP!#BwSVks0DH)3w00arc%y?Zv~Ik*gE_5 zHmE@c@sCY1-?an}im6etC*7jRAaRl^& zU2d}e3;CvEMzSwL^Aq@7E@?1k`l6*E)+zVCRX@Mv)T$*d|32 z6DF9%>OuG1{Q#4-Krj{U{6Dn42{hMj+xM%<)j;z|l zo^)<>fxC^q5K3%Bi-sRiF9!sv`Jb!sb zZ>aCRd%X;j0Z^mOGykS;JTKC`pOmzhx~0S@b-gjFS0XK+7AFdC9)p6*Am_%@CucUV zIw7k5cFsWAytH6QU>aI_zr3Ctr)(&bcLn>C<ZvK?a+fpN1?WOap#Lm0z&5> z>#eh5#ny(}jf~l*N#9;;#aPcbIOpMUVDUvxR0_u&UESWy-ZsoewcUoAE^w7vVBX%`G6qm!pafC(0jK(Tp zW_QyuJ2{bQOR9@gA3qjM{Dq|>mxkwvBRD#Gf`ihgxnG@!Udq{ot6c+})qBmj{XcgW z54p*kY54y27;pnj2Sio$?j}c=#9eQ1beZvEu))pE?`Q&sBYgxnQQY!cS#T!TcSOZk zcNEIlxKIA>Q=f@<4yy_j^F`V#ftHhVK=3Pk^sV;x8}9G#z*xWZgc2%84Gj%^52M<( zYghaAjTdXKVDp19_gG(AYHA0))vOtkj}MyZ&%aGP!SHV{mM=MS%fBd6<`B!0H=fOa z4_%vvOMedQ1|aQyLVdvp>XZPp*`%zGd-jF9R;C)3oL(?6iKqP6Ux)O!R|(B9y^3dx z!sQ_crTX1xhpe`El>U%L{4la`AgjJol1$4_5KE*`qlSoHlLKdO(i(x9=&W2BlwJC* zzTQi9L|I+Klr4wGA-~~Zz{I9FwUCnwC=6CYloufN6IZS%;IZFoZGEa;NP|kw%NH+} zvShYzSCMpS<*Mf5>+9>ivYWee)$!v})z#I{H^rC{KGNtUr+&FEDvE;{Q^JKz^XH}8 zgu2f-HzUoJuH?n0m@jEhZX~tnBRw%WX^@wfcS&coSfI1>C_%CBJ0*sOO2+8BBGvz* zo|=>6L17RCKj`7o(H^|~pZ(0%uAL20ko@#%5xsU_S2gTZdcU@pW*y+^;KbRNX+Do6 z3(%50O+A)O(2Y$wDbEgkY}yleqOxP;*}{>{J-?5Un0Q`2zlZOxu-Lvk{Z8E6;G8(L z-HfmO)S?bG92s$I*7Nq?A3r%#^>^3&Pai62>CjJ|I)DDWU|oiZo3ZM}EaVDrSY&C~02J5`6qm%rB8dHt+Pj&ak;Yps)F@-Hnd$X|MNbl3KL z;dzI34fQWt4(v?i#T9g1BFSGvzE$U@b2^b3%nU0Wa^ZkeX|M3;RmGkDD%@ZH-_qVGb z`HyeDUvb>;pHBSAfBWyX#=k$c_}lp_RvrHHIK(e38WXL?VQA9Q2+E$^y=Q*7Ue1GE zLEbX~0hi49DaQzQOD<}`K!>&o#)WGPU6Srit&E?$Zryt=Bip2X?G#ogHr{-(g7W12 zGj%oc*MxV|)~#E27Nv%k*Dw_oPqAO8UE^aH^CLS%Lh@scR*gIXX_ELI+m2yVc)svHe@2g+{FR)7~~%uYC##DJt)&JCQCQ&I z4nF+M8*qvY=KT~G7dsx=3&G`8K zD_7>~m*AyKTBE4^(>NtHl{)=5--zCyy}xsHj2)05<=fqPgq+-q=-%d+I?HaF>5?>y zo&|KK?+{O}FN81(ST6F%zj(Q~ceJbnk6um`^povewO*~^YCOofj*FF(A@Y%;fp{3Sijx;3lSE$AV}QnU zIh5E-pn93(z}J_9HrUuqPz%abFt)Rs$lMXV=IyI(5)KXyH#X;W=e6qT^;>Xq;3J?K z`X?NSNCZcknwr`zZY``WF)c9I=b!29(x0p3xYFV)AZUHGiA`d-_R8usGS;-lLNc^Y9eE&P+l2 z^>XUnom+ktR@K(di_jI)nwzA#t?tlMbwcOzY(>y^e<%!$JVu(=6KKe0kmiMWIgM6c zKxQYWfA4FRP}f^1p30xuQW6E}$<`gX^UJj-BrzVK4RHdKk-$*g5s(9rkcqerS}Hn_R*H6II^tVH zJq!NeMU3p_2M1qA@%Q56ry;uAYG?O|Plhkq#@5#K(W9?V zKbQ$urkfm2Zk;H%1?_9>hq%dYt;nD3Id1g{Bqscbb&s3MSGT0_l!ab%irBWM^87+;rK=qH$j$hC35Bdy-xBl_*wUmPBQ~mkD zo)jkRRAuuoS_8#%k+B9=M>*^S7*dDMorh1J>@7YKkcVWKfeIFVv>0bZKQ7n_h+qNg zU}|>JJ0})f|LKlrP!e_T+jmuEolpbuKPVEF)lOCIa6_Gb070Al>{&6@>OCjlT0l$b zm#47=p#(Rx6m~~1vw696Mf(3pP1$|hdD96 zyTnQrivT1zefsp_9QLVGI{&}r3f@y1tbBlIk_!!Pn9rtN5ysWQgZE&(JD*!Fw6<^G zCZyML0-7)ZiwVk}Q|xw$FH9@&kH_jSsDK(-?dvd+3CVS|+7h<=93hJ=d(l~U#R{QR z6TlqOw~?P5AVtm6&m}_(^qpYR9!Q7TxgX;5?gIw6a`Lkg&0eRWiBO4)y!+#Qr|9d~ z1#HE?At3~*m=XBcUyr48*^VO?Y z*U*57w^rThKD_s$g%+C!kavPT$Zmi%JTZRHQy`=OL&cK_B0b)BeCs`cWi=>_QtG!6 zrGtp0FFrr8z1q*29};_vwlwK)tVz=uzB0-)rV5o6M&)Pk_$oB#HN_ni((@#T{<5dyK1+ks*0bw z?CzMfot)W0+1}r8oK@^#sp>e6Vd!Sd(jb+i>M4=o`r?{$o|YJ4lBLt3Meh_8SGf$L$P zKfnco7gs-_qCces6|W?<_W`UKqV)XDH_x6rH4EghyjO86f2V47y^&TO8Mjf++17qu zCIL1%4>p6dOkQ4|$4uisWB&a4cIk_koSQUz*|O8#lRFq@KE4(2 zUfsOunKn&Wc`&ZsyD;}qto6HnS4qgd3!87Mpxseqw4s-sn`qtehR|xn* z`#JNMKUr(E`PEf`lD^X&l_k(Avn`DMYw!0;Yl#Ursq5Ca?_s(E=U;CMO>8}VLoW20 zJNF%>Y6{~_@uyEUNRzJvIQ1VeVB_1{6Pctxp%x#isQB%+uXDS7vfZ{v7TkNiYLV=! zD7$^nm(Em`apiC2zWy;fb~5!SP4`7q)2?v;_4V2Vhd&BPNSLmtrzhs%_wU!S(=Eel zIuCi)auSW8u%*(;3E%>*jE~RfSG9TFsCz)d+HXAVvV0wdjy9ZFiNCKxlu! zV_p@zoqb5(ZsI?iaCX@-Me=e5t1BXf+Y~ZB4Gy*@q+2% zH-U$ZRxaOJHysk|G)xsE4Lafr6q{VTjJ*CE%O+hp#$lzsYSm%gaF6=sLK?vzCi`y{ z5I-HNOvykM2rFVc!Tb9uuG!kaTb*0h{?M5AM;uyNS|qR>5MwDZ}pUiEoI#-545; zyl&vmoxk)en!xErO=-M0K*>xB-&!|m^I?Q^F6>|C?H1m4acUIH~W|xW++csv^(xrQJ!?(P=&(cd!I4AOskVMl* zg1-`l;f@>_xoY9(De=a?yx$vr9iDY2U{w0or$J%UzTInMW|QxFWmWz$Wm(^!fq{Pu zwwDo$28lG9t5Ea~4uvLTYXVvt53%L4u&nHEC>?>S5TRK_NrNRKD5vjv8<@f;un;iS z%;$$67yoTe#10NwZ0ARC1d}fEX-euX_BqBe`sNR?m7#$CV8m?{5fx&Ez7KUJ8aia_ z=l*EtQvG$X@4%gL>p~E-(l>$kL)mruqe)L=1^l2nN>e#Iqv3N9%_BABA>x$1@&$Ux z|1QBz*Jqp+quRA=Ye6x9Xmg*v?PXXMTLq+F!)u*Dmq5J{0S!Jcj0rap&qF~kdS1bo z_n$tk{G>4((vleU`4PH3NF1eRc_W~;^Rk>MqZL$Uj?>(U5(E;Nr?!)GS~SvZ({E^k zRU72o?y!0sfxgaLx-O18m6PH+*u24Oswu}jDp{ItmLNJLkk*M)O{?y7+}Vk z?9@2+lXQz1*wGPrCp-Mp&?kj zX3bz2w|@m2`R{Jn7}p{Wb#R++6kl5mcMvVlp6!qHKa13IdGp*@5g0^>j)={A8EK@+ zzv8>O`;gd~8?!^D!Cg=|th2DtWU7ZorMJ_&n&{ZZVtuyJoxOEsMKt(lYa}jDg9w@_q+HU6~+F|qDa}U`-uPp>&et<8Q>;mc{sx_CA5oFELE<{xMstm%!rqk`Y!LJ0Pf`W%0rK-B}_0285Z5Kje zc*v={!E^<~DwQG$I77*O7O7`sGzPWEqD|;RDGkH&Ucc60_&s~Z-`}!#B@&aY^(Utf zFWojJnYJGNr(aJ228D*#qV{h*Tr7*_B%S&@lEo4W^J1{8EhT77h;@_vI)n@V^7NbAr zP=?P$O)-U}+5+ihG3d*+Fs6B-JCf7W*B4(Za+@}Jtf#xu#(@uT_WKX;X$i!oa8u({iO zfgF#ol%G;1bwO0FCZJOmBbY)zse_J{8|ZMm5B4~7=0!)QjMK#x6=OFl%lv-%h?djt z)9wwC?mua17O)Z&LoMM5IVnNW(c0ow)KU^sJ{?yJ;zQ}EU(8kqa>}AZAC{Dm`nA6^ zpN$4co7|EcRLD|ri7Ye|C09{u={MaNUrIv|jIj5GcLmZt#;sq$NT$_)egEptnJrrI z{-dyTvSKdZig!#feogpNM7`NuQ@$?%mOJ8W1s=NQQC_M{theFzOXIqN1%>l*tboS9fyCD zQ5?9VV_&KL5=IiM?=>y?`KDrtLw@y=pN0+|&&GE8mb-hUVXw^h8FQu0yVpN7TRnR0 zW8;fK_5scICXY|Yj_@?@QrBbC**nJ_YCcwpm$Y-{-FlgwA3i+X(XD+&O_e=D4ID!Y zmo7c!{dVVE=o_3txWFux`UJyu&-c>uW!zqV7#kt#j?o)FZWuO zEBW%V^-$SW?@}ZmBNd@lnpe`P`QmSmrR)9p^U1WyJ-T&E;ynWzK15QDYM|<+fz^?e zZtdIdyVE{f@@09PUSfgV`7qg4T9xhph%V-}Y2Ow;Bj1%N)Tc@FvLG@l7WeFU;LE}b zVfly4*TufC{I&Z{SqEw{+k5(f6c-4fP)#nb;+0i&cQ^iEbw;TlQ2!avCWlP z&(HjSNx>+uRG8PRecSpj3z`FC!)lEZJ?0JT*mhlGhyNRwq?%s&>$|q0;j3WfDSks| zCnfxw&?zcFW3nGgWS=89=nsKZfR*xJ{<`g+o7e{_7ufLoii(2G6=AWl zr_L9h8opSfyN}VEj=m1R*`tb~LNP03`1Fld4h1a@xp!Yt!v=H!3&UR-}e!d|{uNGqwgf-{Z1fsZvLi0%;F_8c;h&?VWGZiqU11M7~!5;$wAS=J6 z8;>G3n`D^GOZ#>n{hwTiQUO4$2v?y~0VeI)aehQlXy{`FE&4S&&0(Us#H^;Ef8&yF zB3-3WBJ${b{94$2Bor_tUDoyCD-Y0{quL-r?;TwA#7^Vn`sLO5pZ0U+w02BrK3F}P^3HGpK(4O)(FF-p!Tr_(3!9C*EaViBDkyU1n3%|Q z>a8#sGXv;BG`SfP*wy!Gg#0~fRPq%CUVjh`o!Pjiva(II`D)Z2r^p*O&VboC9vHZ{ z$XMvgev3qmce-MB3(PI=2WG_6G^=75z-#{z7`|N*Jg@`}vNh2aKd)Z>;Vhp|mnU#Z zmVv|jBO}Bl!m%2Ya+QyOOkl@H?+p?l&g;km5aS6XkA4+AK?60W~_ChWQOZ3mHw{BS}T7mlza0j_4oDdZii)q|x`)$r4 zbVuO5m*gK#J%l3+#K$wud$zfKgc-rXZ}EaC0bx*M0C3~mFoN^(O%N!+^$P*jl9^2pg|OHH%-NMaUd_{OWeq?= z8}g7BWNmA^p3o_Q!%=>}eEC8Kg@}%Fm62#Y+jiig;Udv$;nQAIpJ2pUs-yFe;DlA& zW7P4$oZdYJ)`P;WcpFBWzX23X(QXK8z`($OLO#jq$5p+_!p|xef{xCaMw^-2zw{LX zxcl)MA%U311i}X*g-~sb%rfgGLb5}vp2|9wKuQ@3Tm}(Qoo}0wT*W)sgR&3hyF|2# z%A`qs$BpCu0FmW~16%~fn9XU95tuW=vQ(8kXi*hrb=*$}oZaq%7ZxP9WDp)v!{2SaQunI} zpMZZT+3@(S8XaQZb>ZwmX86;d95*HdZZ;aC-dz~8rcU*RcZGGkbmazbUAL zMPUdF=^Cfsft~6S5wXSZ9S?->B*FAcn1?=RIB5_J2KH{Vc9U!EZPmg zP;w+ZSve>+_AFhTz5|t$JO*_LU%KK@<{}X~9CPC>@vwke-rjMI^>ol<&3-77 zJ^Ik+sCZ!XkcSVKlWWU#Mp_2aM_yUydmbKHJbJeL31aVre)L8qG`w&GkW%Xywb5}b zde_{JN`0jS#s{l9W!vY$7Dc@}wqwb194fUFggJB>k8_!xo;tWR=Z;P6A65bp_>tLf z=lQsr%}`P44R~=}ZK#_Z0xogXfrgt^4ct|`?0Q}!+vqdNHi$jvVi6!a7m+8)1?w1A z2sP`9pbVsl2mLp6snEy?_aH4J6Fp^ExCKxqCjVrUpFtBK5|QzFPBV*qi{8@pz}9(H0;h zWVy472k}DmWzVB=$Hgbo8Xzi!Hz)JdzMUQ2OZPFne0!HJGoC7COq+`xl~W=-+v&6A>@XctV=rC4Ox}~e z=VW##0`N1c%^GeGS)xqxu8m*pY-}!@_J~?)*y7xU5fwlc9LI%%yasD3j(nEFyc6vK=~h* z;MklCYgo#hRPQ6LJ~6N>ZE1+PD?fTP7;Us?vl@mO5l1M5g2oAs2=pgy|8h286&n!6 zuv>x4ensbU;ad400J>#zMJ*_}AW9Kf=H1*P%SJGz;7~Ng$7iJ6@#Wf+GOehbO=$D$ zEESoqZf%VvpNc{98UEfA=tJ>RfjYN(jux)yXo8c`*>wA|7ajKeHE@Rn3>Jm{2rfJ& zC3hOeb*83|#OgxT;h5mh$-&FivC4yLoj=gIajOuhw@*7&|&;vaySj{GD*IYPn1jdBVpFivSo5(D$DEG*# zb%^omz1(T}D_7$;#iJ}RrwUB~$i};(qQP&T7rIhYx{-eZ)WqKe(;|4^m}XliK1a*8pVJ4j(zjpDrb&{%OXJlS{tg{4n-iLsf(ZHmVr6bIGSHcBhq zQNwaZ=$*aA!6E+vTm%1Q3 z0J;O9=mBUj9`GgaX2E}#duy!i#Dx5ClDN4Nsa)}aKE~W#)h$FjS!i(Y?rsvf$-w&C zzPvh3S1Zz$I3tc6KYnK7PYK}&qviVDn>7b>qN-!<*Slfft&OCV`L`}}nf_KKI8J1< zO-x-qIQES<@)^OX(L$jt7HBs!-VU(Zz4H1$DJ`jH8;8&MQPhSyaR@OM^+l*9P@HP? z_FQnh?`+^7Z1fYvq(&H3kx=>)lYUxjYUEp;H>-p_4rPkqv>b3YiGP{RN8N6UKrn1cSn?^`?b+=P;9YEe7 z`|3=CJOH-xa3TYcdX35NBn+^||d+-~5bgurj9_=fBPm(~@nB9bjNX#V= zsHwhkxkReKMp)!s!YUH&^wS3Y1sxUa)gDCNv$oH_q#bscm@8M+0^wtX8W4&j%88U= zm5KfHqAWauR`FGmqBuwhj}ZTULa7A~_r5G? z-uuXrBaueWU&2fQ&IwKDkCdi`aF4)zO(K=;M*36S`;fY?YN@fjP+U`Z8!@@mP z8A}!g^bWN6Eir!XB;vnNVBRg+7eA^}h^0^by1hKNcm9BXVSUCgu08y>79f6>^`Ifj z<*k#NCN@h+wjn!b4RigiX;#^x`q_6c6fUkPj%oM5z`EEmmvk=cR}8Z11&Wfmz9qnB zMAVqB`CASfN&Z_~y|?Rsbk_gvzyG1X-goDy>~}L;2krj@u=mDtz^(-yog8=U|8a5R z-t!&0OdoIDw)Obtwt;^0jZ_Pd>{q-FLiy(dR4n^9=q7;3e<-K_+kfAW7hm}MZ^wpT z?YiQB{UZ0ru0rFZ$2$3*QM+g>H!{}i-#=KsFOZd>!O&!3>K5137Ne5!<10aQaZT82S7xlv;5exAHy#CBUbW< zuC)c#x~?9NsF6S;i=c{9nh1_`QugZf*8=U}sj$IJE=Qr{AGW^UY`WkOSai9A$M? zK`rPsx;1e{s-auJ>6%TO{tm0d5_FSs7%7C@wWvL=jOV!Fe(}M4Q`#OmsbgG%~pjhVQ2k z5FQ!%QQ#--+9iKNK}0%icumRf>Cko$SryM<{z^VDz2P4=j1Ea#<_OW(aI!gKfrs1Le4XC1+Qi-7mQz&X8(f#k#;ObAdH zupkF zH>$-_3bK_149Jqj)HTr656rq~(pr&bV*av%un!arT#LmuaULU_NtA+brHKOGxz{Tj zhD!k`Y-#0w?ZNwh>NkEE)Iz&`ns-qJv@+hNc%lE!iphY?59#54e45!Z%A)YZ!08&$ z)7JL(3xYC7lJ}7?QAMMP}I?58VzcG(2u<^w_7?ZO%NoZU)y=r$jF@`&fS{-B!u0dxvEY z9qt-u$UEGuar(G9MlS%$Du7)&y5VEi&-=l}5;h15!7GL~8P;3Zg z1{ZJ-ZUbP>4KG~^7RSFV3^NEvaMo%t7>KBjBs;`Y;5JH(v?y>Mb5nts^oNx<0vWV1 z^xNV+#cnkJz8K>^vAa;#jfVjPV*%!16Fj{aE&UBR=d_#K-=mBr;0(^+y!mq0cx5G} zp(JkMyLxy&a`j2{9aPgG7iG;fgjiyK@_esjJV8hx!1jmY;{A{lARjMYydb`0|C|f=($b{Kf5BkT z@>1`s#i%i3Jg$5I-pjh8&JZbzom-CK%;iA=C4whOQdmGV3~KTn4h{@YdpNhnCtgG> zciil_^3)NEic3>w0>P0jBIj?N!@6J8>?xRapeoKE5mbSfyob!sgve*k zly7k`)>gcI6C7x@e*I8AeUStYEyd$A6mcuGxV~|PJ4N6I3?~WQ*L5o|c?&OAO2{U7E1KTD zumR%cbYN5$-JeH~cBIako2;n*{{15d-$#BPw<(PWt3}!!2W%D8kH`)cdk=+eR-jFW zuj)yWWI^8~FhW$w&)9deyZXI#1$8B|^eIfYpns>_u964=Z&_wwa1134wF)G30v##$ zwn#-@zj4C^eIlx51w%7P!dAJTi4#qr$^KfE2LT`$~6~aeoBr#YfcaXp4Vsg7+fr z;pJszQu&i=Kk1BK;gWGFB4Yk<_lcVla?O>oo@gqybu>DkD2vprjef-UoBBQj1`L40 z7!FGqXDIHq5qt)xD4zRnzby&w%F7)w_lOEeznrNc?p=V?&8lRefODDndAUF4eLyBw zkmM5)B24fhy%`9K1X=~K>pRO zIC!&=j?Ng0zIBE07l43-*;dmc!DwbpYzTYQ;3=OlQ7GOV*CtTa zB|S%lXm4E8%ZeH%+&(4){^E2!HhY0x!VO~Bxnigb2;?v>8Yo*uzcZWsxfh3de7?oK zU#u!fpgOR&a18ye^0= z|Fb#v)M)wg2Y^GV6pIk=eF$Z*;+IWq`fecT_FVgsC_daO!%0aP?tX1Y)jId_glvj8 zFK*WpyIrWwMw!>8MqH?VajTRTO8h2G9mt6wlYn=`#D|ei3jrX+94B%%4PKC!$D9iS zl|qtn6ojJs#Vbk0Pl*9MK z87exXe=!*1)dBWw;FyHlysqvr2o4d~ivSfFpx%={vYrlFEjK5?wT&=z;w7ln^7vI~ zWac*UayQkJp=ah=T1F<NfI_wp3z2{^7q@EEKbAE58E-k&#& ztF1=0FvLBXY5#P?*rs2xqpB=#J!=^4C4wFy?xFCs4fy7d^YZi@?JZ*Tai|hTK8|JT z-Vlc>U>Tx*FE4RbD=24;(5ccK!~KDQ4*zSLkYk@YbN1X2_tX-#Ha8ENtt(qTv-fkv z2sX(ZH-8&p{wT$1`RI(g(Yj<{awhVImDmp`e&*=uc_(Q`MMj>$FD;q>)hOawLP%+!+{S(1cv$DGY)iV(62EnhQ53yQoZb4IxNA(0iXAJY-t z21huvk(4cmG23i#sZ0~ zX(WWosEq0L{mBrKU=}r@gQHFK)=AO2#+rhbfr?YHUKy4_uP1#a8eh!qlBRDFBd>dI zYRR2ClOzA}$w5o#yLsNM^)@t76fUvK#X-%X!_8)|pZa}SJap#FK@iZ%xAj-9T!27; zMI2}893HU6f(sEcrObCRnG!Ok@-xzWQSSup%bDiLIy@zZ(ABurCDMY3n`vPgci3bX7 z&R6aste+xua&JkKeo0b$dJ5Rn~AmWZyUdEi04Xs%+r}g z@ddAU1sVF<+u7Nvg)1nNX?DK2CM;lLtV&e5d|@cv=jlKUyfZ$YKNUmQl=M2|)n8J?Wt z#I7J(J}@#eedN7si5~HjzlMix`L_Lx-g@Pb{v4EM{TzmWv_a*DzuT z<^os<4b%zdJ&w!WE@~bIuFY?^eGTl0+fB0aAOaX%3?cQiSo?VA*2D}+cq7sy*uJVu zv#`8Gl^+eNmyf!8)PqTklZzrGE@O3%rg=jpCL-H#l}j*(6O1ETOF?mFWr?#Myq*c& zd(mT%9G^3v=w7)(1gp%P*|YZsKg)gj5*_m2IT)lb(p=E_iyN&0dYC(I*^Oz0q88%e zw0WO5?3ySeg#OH=9F4*Dt^ol7rwxuLehrvxD@pR(MwMi)u%L&>bJ(xVhjRt(!iN3T z1mt?*>ea5yw#c&Z;s}9|d2#)1(`N4t{_mJ6hm9Yf`FZ|?o!>We%S+bhDW7;^K{syb zM8_ZEZYD5BTGjkZ?E=S(x`Mq(yx!xUHgB*m)1?EJo!8rlUJ#3z8nG}T>5*}trzSKs z@6}Q_2v8+12C}xy({qEh_0{6+*FDva_Z8F;qn;$MQ)#J=kQza=2(iSg&*ufs!Eq!+ zQW}1q)O(PT&Ew;C?b;=mMa5NLBl2@yKrF&Gw^J$Ds zLd>wMR-Ns6sg~rg8|Dt_#NYmlV65SHPPw~3Kn5s1rMZC>dr+u};0qY4&i;|b0MJ!~ zZLHt0VMM!VmHji?3;bT_T*R!RS8J(j2`fOAOX3G1hr$$X2YogzxUW&43LVZgAvN9< z<3rmhW-8=0tGK+vrC#XEOaVnjZaK}wV6W7l1X<#+8b)Y3;60h`PkD{W*r`A{~MwWa2Brk;NP2bCiHcUZ9y#(|vvyYw92ln&4gr zh#FAioR-w3S^`n3rz9jUxxL*}ZLAkJqFMGd;6z&+t$>URxgyjG!!F6T%B4pU&kP%& zI!vTIGF+N$*f7*RSpa{RF17d27}jqU!>mwSVgpEQCbc2U3J;$U8ZyR!Z7u)}q~ipM z1OJR8mKn%KvQ4k$t&$u~Z-FY)0yE$OC?%{Ns;@c3E0yRH&Iv5UP`a}sbk|c+dVVed z<|CR0iBms-TmGDOgL6dk9snb2A}mpdU{)`$5kFt$jmHbWiSbf{c)FNM8{ECJQ|pAYe%6A;X6s6j!AQ z@ditp(-Hx_fNRpCB@~QqpCJQUltdAOsUJZ<5`a>8fIB|;OAWZe-5b>{8GyVt$60lG-Q)eey=(>#k%TWZf^Unz41k!!64 zV6PT&lw2N>Yb(^;&V+QJ9sv!xZ(x=1NSsgXxFd5eoTq0JnA20E5`MmWj~=ySa}6C- z4pj7KI(#sSjuBjX2gvoJAsi$lA`c?De#B${XE! zW?$thFnoa)*1J(B;x2xBbArX;au1ZjV`dV|i`la^$G5ny7e*>~&0FV$oEyN_Bvgl+97xZ|xI3!+vPQ_J7pe)9mE|3vrOlrEIFWE$we&B5qNhnX6O>9k(CGAt}S`~kfc zGDX>l;1KOF{rngrv$`M&q&$j0eD+L=bnN;?@=O1%1?X+xa>whG_m9<&bB4*AI*$iuUlKg~-lO>6# zvs0XJQd00k-%aE8O+7^I3jjWKu`+I7vAN>R_mu$okOK(O%&!f8{Bm$A;xa7JtrHyU zZjV}j^z#O*)xJ|~MtZxsy*CSa_vMSmt%}pCO^v|`ce}ln?;WxDuiN4Bu^a95pO-%0 z7}Xs0tNh&esD8cT7O7IY^WM9!Af^s0MRb2eQd632A-2C(6*@L~6 zWf@iJU{8;$O&EJDY_Q?pStD!gJ8)oaK0R(=N6P{9?b?dbrwj)F2*2L@Xnc|6sbmu^ z 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�yOu_jZY_0Q@EF6|V2>47$v804_C|UWH~- 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�xCBE%U^CEFY=QH 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 literal 0 HcmV?d00001 diff --git a/doc/manual/pages/images/color_editor_desktop.png b/doc/manual/pages/images/color_editor_desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..edd21f1ea99ec6ad3e909754c2bf96d32b4adc8f GIT binary patch literal 35336 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 literal 0 HcmV?d00001 diff --git a/doc/manual/pages/images/color_editor_professional.png b/doc/manual/pages/images/color_editor_professional.png new file mode 100644 index 0000000000000000000000000000000000000000..46da52e088a509e4e54c3fa5cca1acbb446ac909 GIT binary patch literal 51197 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