From 1dbe78c68bb47171ab10649d8fe4a77de72b9b74 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Sat, 20 Jan 2024 18:35:29 -0500 Subject: [PATCH] Add QA tests and expand serialization for API --- api/CMakeLists.txt | 23 +- api/enums/enum_exporter.cpp | 91 - api/proto/board/board.proto | 116 + api/proto/board/board_commands.proto | 151 + api/proto/board/board_types.proto | 449 +- api/proto/common/commands/base_commands.proto | 5 + .../common/commands/editor_commands.proto | 160 +- .../common/commands/project_commands.proto | 34 + api/proto/common/envelope.proto | 4 +- api/proto/common/types/base_types.proto | 193 +- api/proto/common/types/enums.proto | 119 + api/proto/common/types/project_settings.proto | 35 + api/proto/schematic/schematic_commands.proto | 24 + api/proto/schematic/schematic_types.proto | 75 + common/CMakeLists.txt | 16 +- common/api/api_enums.cpp | 436 ++ common/api/api_handler.cpp | 21 +- common/api/api_handler_common.cpp | 32 +- common/api/api_handler_editor.cpp | 353 ++ common/api/api_plugin.cpp | 3 +- common/api/api_plugin_manager.cpp | 48 +- common/api/api_server.cpp | 26 +- common/api/api_utils.cpp | 80 + common/dialogs/panel_plugin_settings.cpp | 4 + common/eda_draw_frame.cpp | 44 + common/eda_shape.cpp | 9 +- common/pgm_base.cpp | 5 + common/tool/actions.cpp | 9 + eeschema/CMakeLists.txt | 9 + eeschema/api/api_handler_sch.cpp | 305 ++ eeschema/api/api_handler_sch.h | 74 + eeschema/api/api_sch_utils.cpp | 107 + eeschema/api/api_sch_utils.h | 31 + eeschema/lib_shape.h | 2 +- eeschema/menubar.cpp | 5 + eeschema/sch_commit.h | 1 + eeschema/sch_edit_frame.cpp | 21 + eeschema/sch_edit_frame.h | 10 + eeschema/sch_field.h | 3 +- eeschema/sch_label.cpp | 67 + eeschema/sch_label.h | 12 + eeschema/sch_line.cpp | 46 + eeschema/sch_line.h | 3 + eeschema/sch_shape.h | 3 +- eeschema/sch_table.h | 2 +- eeschema/toolbars_sch_editor.cpp | 21 +- eeschema/tools/sch_editor_control.cpp | 14 + eeschema/tools/sch_editor_control.h | 1 + eeschema/tools/symbol_editor_move_tool.cpp | 4 + include/api/api_enums.h | 29 + include/api/api_handler.h | 30 +- include/api/api_handler_common.h | 17 +- include/api/api_handler_editor.h | 115 + include/api/api_plugin.h | 2 +- include/api/api_plugin_manager.h | 3 +- include/api/api_server.h | 8 +- include/api/api_utils.h | 48 + include/api/serializable.h | 60 + include/eda_base_frame.h | 7 + include/eda_draw_frame.h | 13 + include/eda_item.h | 7 +- include/eda_shape.h | 1 + include/font/text_attributes.h | 2 + include/template_fieldnames.h | 1 + include/tool/actions.h | 3 + include/tool/tool_manager.h | 8 + pcbnew/api/api_handler_pcb.cpp | 735 ++-- pcbnew/api/api_handler_pcb.h | 75 +- pcbnew/api/api_pcb_enums.cpp | 105 + pcbnew/api/api_pcb_utils.cpp | 100 + pcbnew/api/api_pcb_utils.h | 45 + pcbnew/board.cpp | 29 +- .../board_stackup_manager/board_stackup.cpp | 41 + pcbnew/board_stackup_manager/board_stackup.h | 7 +- pcbnew/cross-probing.cpp | 2 +- pcbnew/footprint.cpp | 249 ++ pcbnew/footprint.h | 3 + pcbnew/footprint_edit_frame.cpp | 4 +- pcbnew/footprint_wizard_frame.cpp | 4 +- pcbnew/menubar_pcb_editor.cpp | 2 +- pcbnew/pad.cpp | 158 + pcbnew/pad.h | 3 + pcbnew/pcb_edit_frame.cpp | 43 +- pcbnew/pcb_edit_frame.h | 10 +- pcbnew/pcb_field.cpp | 45 + pcbnew/pcb_field.h | 5 + pcbnew/pcb_painter.cpp | 3 + pcbnew/pcb_shape.cpp | 271 ++ pcbnew/pcb_shape.h | 3 + pcbnew/pcb_text.cpp | 108 + pcbnew/pcb_text.h | 3 + pcbnew/pcb_track.cpp | 67 +- .../python/scripting/pcb_scripting_tool.cpp | 9 +- pcbnew/toolbars_pcb_editor.cpp | 35 +- pcbnew/tools/pcb_actions.cpp | 6 - pcbnew/tools/pcb_actions.h | 1 - qa/data/pcbnew/api_kitchen_sink.kicad_pcb | 3878 +++++++++++++++++ qa/data/pcbnew/api_kitchen_sink.kicad_pro | 272 ++ qa/tests/CMakeLists.txt | 1 + {api/enums => qa/tests/api}/CMakeLists.txt | 48 +- qa/tests/api/test_api_enums.cpp | 163 + qa/tests/api/test_api_module.cpp | 50 + qa/tests/api/test_api_proto.cpp | 121 + scripting/CMakeLists.txt | 1 - scripting/python_manager.cpp | 10 +- scripting/python_manager.h | 4 +- 106 files changed, 9643 insertions(+), 736 deletions(-) delete mode 100644 api/enums/enum_exporter.cpp create mode 100644 api/proto/board/board.proto create mode 100644 api/proto/board/board_commands.proto create mode 100644 api/proto/common/commands/project_commands.proto create mode 100644 api/proto/common/types/enums.proto create mode 100644 api/proto/common/types/project_settings.proto create mode 100644 api/proto/schematic/schematic_commands.proto create mode 100644 api/proto/schematic/schematic_types.proto create mode 100644 common/api/api_enums.cpp create mode 100644 common/api/api_handler_editor.cpp create mode 100644 common/api/api_utils.cpp create mode 100644 eeschema/api/api_handler_sch.cpp create mode 100644 eeschema/api/api_handler_sch.h create mode 100644 eeschema/api/api_sch_utils.cpp create mode 100644 eeschema/api/api_sch_utils.h create mode 100644 include/api/api_enums.h create mode 100644 include/api/api_handler_editor.h create mode 100644 include/api/api_utils.h create mode 100644 include/api/serializable.h create mode 100644 pcbnew/api/api_pcb_enums.cpp create mode 100644 pcbnew/api/api_pcb_utils.cpp create mode 100644 pcbnew/api/api_pcb_utils.h create mode 100644 qa/data/pcbnew/api_kitchen_sink.kicad_pcb create mode 100644 qa/data/pcbnew/api_kitchen_sink.kicad_pro rename {api/enums => qa/tests/api}/CMakeLists.txt (50%) create mode 100644 qa/tests/api/test_api_enums.cpp create mode 100644 qa/tests/api/test_api_module.cpp create mode 100644 qa/tests/api/test_api_proto.cpp diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 2138c8de34..893b3cc7e2 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -22,11 +22,19 @@ set( KIAPI_PROTO_SRCS common/envelope.proto common/types/base_types.proto + common/types/enums.proto + common/types/project_settings.proto common/commands/base_commands.proto common/commands/editor_commands.proto + common/commands/project_commands.proto + board/board.proto + board/board_commands.proto board/board_types.proto + + schematic/schematic_types.proto + schematic/schematic_commands.proto ) # Generated C++ code must be in the build dir; it is dependent on the version of protoc installed @@ -101,18 +109,3 @@ target_include_directories( kiapi INTERFACE # Because when building internally, the generated files do not include the "api" base path target_include_directories( kiapi PUBLIC ${KIAPI_CPP_BASEPATH} ) - -option( KICAD_BUILD_ENUM_EXPORTER - "Build the enum exporter used as part of generating the IPC APIs" - OFF ) - -if( KICAD_BUILD_ENUM_EXPORTER ) - add_subdirectory( enums ) - - add_custom_target( enum_definitions - COMMAND $ ${CMAKE_CURRENT_BINARY_DIR}/enums.json - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Generating API definitions from KiCad enums..." - DEPENDS enum_exporter - ) -endif() diff --git a/api/enums/enum_exporter.cpp b/api/enums/enum_exporter.cpp deleted file mode 100644 index c9accd65bb..0000000000 --- a/api/enums/enum_exporter.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. - * - * 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 . - */ - -#include -#include -#include - -#include -#include -#include - -#define MAGIC_ENUM_RANGE_MAX 1024 -#include - -#include -#include -#include - - -template -nlohmann::json FormatEnum() -{ - nlohmann::json js; - - js["type"] = magic_enum::enum_type_name(); - js["values"] = nlohmann::json::array(); - - for( const std::pair& entry : magic_enum::enum_entries() ) - { - js["values"].emplace_back( nlohmann::json( { - { "key", entry.second }, - { "value", static_cast( entry.first ) } - } ) ); - } - - return js; -} - - -int main( int argc, char* argv[] ) -{ - argparse::ArgumentParser args( "enum_exporter" ); - - args.add_argument( "output_dir" ).default_value( std::string() ); - - try - { - args.parse_args( argc, argv ); - } - catch( const std::runtime_error& err ) - { - std::cerr << err.what() << std::endl; - std::cerr << args; - std::exit( 1 ); - } - - std::filesystem::path path( args.get( "output_dir" ) ); - std::ofstream outfile; - - if( !path.empty() ) - { - path = std::filesystem::absolute( path ); - outfile.open( path ); - } - - std::ostream& out = outfile.is_open() ? outfile : std::cout; - - nlohmann::json js = nlohmann::json::array(); - - js += FormatEnum(); - js += FormatEnum(); - js += FormatEnum(); - - out << js.dump( 4 ) << std::endl; -} diff --git a/api/proto/board/board.proto b/api/proto/board/board.proto new file mode 100644 index 0000000000..75f7f22cdd --- /dev/null +++ b/api/proto/board/board.proto @@ -0,0 +1,116 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +syntax = "proto3"; + +package kiapi.board; + +import "common/types/base_types.proto"; +import "board/board_types.proto"; + +message BoardFinish +{ + string type_name = 1; +} + +message BoardImpedanceControl +{ + bool is_controlled = 1; +} + +message BoardEdgeConnector +{ +} + +message Castellation +{ + bool has_castellated_pads = 1; +} + +message EdgePlating +{ + bool has_edge_plating = 1; +} + +message BoardEdgeSettings +{ + BoardEdgeConnector connector = 1; + Castellation castellation = 2; + EdgePlating plating = 3; +} + +message BoardStackupCopperLayer +{ + +} + +message BoardStackupLayer +{ + kiapi.common.types.Distance thickness = 1; + kiapi.board.types.BoardLayer layer = 2; + bool enabled = 3; + oneof item { + BoardStackupCopperLayer copper = 4; + } +} + +message BoardStackup +{ + BoardFinish finish = 1; + BoardImpedanceControl impedance = 2; + // NOTE: m_HasThicknessConstrains appears to be unused + BoardEdgeSettings edge = 3; + repeated BoardStackupLayer layers = 4; +} + +// LAYER_CLASS_* in BOARD_DESIGN_SETTINGS -- needs to become an enum class +enum BoardLayerClass +{ + BLC_UNKNOWN = 0; + BLC_SILKSCREEN = 1; + BLC_COPPER = 2; + BLC_EDGES = 3; + BLC_COURTYARD = 4; + BLC_FABRICATION = 5; + BLC_OTHER = 6; +} + +message BoardLayerGraphicsDefaults +{ + BoardLayerClass layer = 1; + kiapi.common.types.TextAttributes text = 2; + kiapi.common.types.Distance line_thickness = 3; +} + +message GraphicsDefaults +{ + repeated BoardLayerGraphicsDefaults layers = 1; +} + +// Anything that isn't stackup or design rules +message BoardSettings +{ + GraphicsDefaults graphics_defaults = 1; + // Dimension default settings +} + +message BoardDesignRules +{ +} diff --git a/api/proto/board/board_commands.proto b/api/proto/board/board_commands.proto new file mode 100644 index 0000000000..a0b313b6ce --- /dev/null +++ b/api/proto/board/board_commands.proto @@ -0,0 +1,151 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +syntax = "proto3"; + +package kiapi.board.commands; + +import "common/types/base_types.proto"; +import "common/types/enums.proto"; +import "board/board.proto"; +import "board/board_types.proto"; + +/* + * Board stackup and properties + */ + +message GetBoardStackup +{ + kiapi.common.types.DocumentSpecifier board = 1; +} + +message BoardStackupResponse +{ + kiapi.board.BoardStackup stackup = 1; +} + +message UpdateBoardStackup +{ + kiapi.common.types.DocumentSpecifier board = 1; + kiapi.board.BoardStackup stackup = 2; +} + +message GetGraphicsDefaults +{ + kiapi.common.types.DocumentSpecifier board = 1; +} + +message GraphicsDefaultsResponse +{ + kiapi.board.GraphicsDefaults defaults = 1; +} + +/* + * Net management + */ + +message GetNets +{ + kiapi.common.types.DocumentSpecifier board = 1; + + // If provided, will only return nets that belong to the given netclass. + // If more than one netclass_filter is given, nets belonging to any of the given classes will + // be returned. + repeated string netclass_filter = 2; +} + +message NetsResponse +{ + repeated kiapi.board.types.Net nets = 1; +} + +// Retrieve all the copper items belonging to a certain net or set of nets +// returns kiapi.common.commands.GetItemsResponse +message GetItemsByNet +{ + // Specifies which document to query, which fields to return, etc. + kiapi.common.types.ItemHeader header = 1; + + // List of one or more types of items to retreive + repeated kiapi.common.types.KiCadObjectType types = 2; + + // A list of net codes to filter items by + repeated kiapi.board.types.NetCode net_codes = 3; +} + +// Retrieve all the copper items belonging to a certain net class or set of net classes +// returns kiapi.common.commands.GetItemsResponse +message GetItemsByNetClass +{ + // Specifies which document to query, which fields to return, etc. + kiapi.common.types.ItemHeader header = 1; + + // List of one or more types of items to retreive + repeated kiapi.common.types.KiCadObjectType types = 2; + + // A list of net class names to filter items by + repeated string net_classes = 3; +} + +/* + * Blocking operations + */ + +// Refills some or all zones on the board. +// This is a blocking operation; it will return Empty immediately, but KiCad will return +// ApiStatusCode.AS_BUSY to all future API requests until the zone fill has completed. +message RefillZones +{ + kiapi.common.types.DocumentSpecifier board = 1; + + // A list of zones to refill. If empty, all zones are refilled. + repeated kiapi.common.types.KIID zones = 2; +} + +/* + * Utilities + */ + +// returns kiapi.common.commands.BoundingBoxResponse +message GetTextExtents +{ + // A temporary text item to calculate the bounding box for + kiapi.board.types.Text text = 1; +} + +//// Interactive commands //// +// These commands begin an interactive operation in the editor. +// They return a response immediately, but the editor will become busy +// and will not reply to further API commands until the user has finished +// the operation. +// These commands will return an error if received in a non-interactive context. + +// Selects and begins an interactive move of the given item(s). +// NOTE: Takes ownership of the active commit, if one exists: +// the move tool will push the commit when the user confirms the move, +// or roll back the commit if the user cancels the move. Keep this in +// mind if using this command in combination with commands that create +// or modify items using an explicit commit. +message InteractiveMoveItems +{ + kiapi.common.types.DocumentSpecifier board = 1; + + repeated kiapi.common.types.KIID items = 2; +} diff --git a/api/proto/board/board_types.proto b/api/proto/board/board_types.proto index d02ef2da4e..27fae3172f 100644 --- a/api/proto/board/board_types.proto +++ b/api/proto/board/board_types.proto @@ -21,90 +21,467 @@ syntax = "proto3"; package kiapi.board.types; +import "google/protobuf/any.proto"; import "common/types/base_types.proto"; -/// Represents a track segment on a board +enum BoardLayer +{ + BL_UNKNOWN = 0; + BL_UNDEFINED = 1; + BL_UNSELECTED = 2; + BL_F_Cu = 3; + BL_In1_Cu = 4; + BL_In2_Cu = 5; + BL_In3_Cu = 6; + BL_In4_Cu = 7; + BL_In5_Cu = 8; + BL_In6_Cu = 9; + BL_In7_Cu = 10; + BL_In8_Cu = 11; + BL_In9_Cu = 12; + BL_In10_Cu = 13; + BL_In11_Cu = 14; + BL_In12_Cu = 15; + BL_In13_Cu = 16; + BL_In14_Cu = 17; + BL_In15_Cu = 18; + BL_In16_Cu = 19; + BL_In17_Cu = 20; + BL_In18_Cu = 21; + BL_In19_Cu = 22; + BL_In20_Cu = 23; + BL_In21_Cu = 24; + BL_In22_Cu = 25; + BL_In23_Cu = 26; + BL_In24_Cu = 27; + BL_In25_Cu = 28; + BL_In26_Cu = 29; + BL_In27_Cu = 30; + BL_In28_Cu = 31; + BL_In29_Cu = 32; + BL_In30_Cu = 33; + BL_B_Cu = 34; + BL_B_Adhes = 35; + BL_F_Adhes = 36; + BL_B_Paste = 37; + BL_F_Paste = 38; + BL_B_SilkS = 39; + BL_F_SilkS = 40; + BL_B_Mask = 41; + BL_F_Mask = 42; + BL_Dwgs_User = 43; + BL_Cmts_User = 44; + BL_Eco1_User = 45; + BL_Eco2_User = 46; + BL_Edge_Cuts = 47; + BL_Margin = 48; + BL_B_CrtYd = 49; + BL_F_CrtYd = 50; + BL_B_Fab = 51; + BL_F_Fab = 52; + BL_User_1 = 53; + BL_User_2 = 54; + BL_User_3 = 55; + BL_User_4 = 56; + BL_User_5 = 57; + BL_User_6 = 58; + BL_User_7 = 59; + BL_User_8 = 60; + BL_User_9 = 61; +} + +message NetCode +{ + int32 value = 1; +} + +// Describes a copper item's net +message Net +{ + // A unique code representing this net + NetCode code = 1; + + // Human-readable net name + string name = 2; +} + +// Represents a track segment on a board message Track { kiapi.common.types.KIID id = 1; - kiapi.common.types.Point2D start = 2; - kiapi.common.types.Point2D end = 3; + kiapi.common.types.Vector2 start = 2; + kiapi.common.types.Vector2 end = 3; kiapi.common.types.Distance width = 4; kiapi.common.types.LockedState locked = 5; - kiapi.common.types.BoardLayer layer = 6; - kiapi.common.types.Net net = 7; + BoardLayer layer = 6; + Net net = 7; } -/// Represents an arc track (not a PCB_SHAPE in arc shape) -/// Arc tracks in KiCad store start, midpoint, and end. -/// All other values (center point, angles, etc) are inferred. +// Represents an arc track (not a PCB_SHAPE in arc shape) +// Arc tracks in KiCad store start, midpoint, and end. +// All other values (center point, angles, etc) are inferred. message Arc { kiapi.common.types.KIID id = 1; - kiapi.common.types.Point2D start = 2; - kiapi.common.types.Point2D mid = 3; /// Arc midpoint - kiapi.common.types.Point2D end = 4; + kiapi.common.types.Vector2 start = 2; + kiapi.common.types.Vector2 mid = 3; // Arc midpoint + kiapi.common.types.Vector2 end = 4; kiapi.common.types.Distance width = 5; kiapi.common.types.LockedState locked = 6; - kiapi.common.types.BoardLayer layer = 7; - kiapi.common.types.Net net = 8; + BoardLayer layer = 7; + Net net = 8; } enum PadStackType { PST_UNKNOWN = 0; - PST_THROUGH = 1; /// Through all layers; same shape on all layers - PST_BLIND_BURIED = 2; /// From a start layer to end layer (inclusive); same shape on all included layers + PST_THROUGH = 1; // Through all layers; same shape on all layers + PST_BLIND_BURIED = 2; // From a start layer to end layer (inclusive); same shape on all included layers } enum UnconnectedLayerRemoval { ULR_UNKNOWN = 0; - /// Keep annular rings on all layers + // Keep annular rings on all layers ULR_KEEP = 1; - /// Remove annular rings on unconnected layers, including start and end layers. + // Remove annular rings on unconnected layers, including start and end layers. ULR_REMOVE = 2; - /// Remove annular rings on unconnected layers, but preserve start and end layers even if unconnected. + // Remove annular rings on unconnected layers, but preserve start and end layers even if unconnected. ULR_REMOVE_EXCEPT_START_AND_END = 3; } -/// A pad stack definition for a multilayer pad or via. +// The shape of a pad on a given layer +enum PadStackShape +{ + PSS_UNKNOWN = 0; + PSS_CIRCLE = 1; + PSS_RECTANGLE = 2; + PSS_OVAL = 3; + PSS_TRAPEZOID = 4; + PSS_ROUNDRECT = 5; + PSS_CHAMFEREDRECT = 6; + PSS_CUSTOM = 7; +} + +// Which corners are chamfered in a PSS_CHAMFEREDRECT +message ChamferedRectCorners +{ + bool top_left = 1; + bool top_right = 2; + bool bottom_left = 3; + bool bottom_right = 4; +} + +// The defintion of a padstack on a single layer +message PadStackLayer +{ + // The board layers of this padstack entry + repeated BoardLayer layers = 1; + + // The shape of the pad on this layer + PadStackShape shape = 2; + + // The size (x and y) of the shape on this layer + kiapi.common.types.Vector2 size = 3; + + // How much to round the corners of the shape by, as a fraction of min(size.x, size.y) + // Only used for PSS_ROUNDRECT or PSS_CHAMFEREDRECT + float corner_rounding_ratio = 4; + + // How much to round the corners of the shape by, as a fraction of min(size.x, size.y) + // Only used for PSS_CHAMFEREDRECT + float chamfer_ratio = 5; + + ChamferedRectCorners chamfered_corners = 6; + + repeated GraphicShape custom_shapes = 7; +} + +// A pad stack definition for a multilayer pad or via. message PadStack { - /// What type of pad stack this represents. + // What type of pad stack this represents. PadStackType type = 1; - /// Lowest (closest to F_Cu) layer this stack exists on. Ignored if type == PST_THROUGH. - kiapi.common.types.BoardLayer start_layer = 2; + // Lowest (closest to F_Cu) layer this stack exists on. Ignored if type == PST_THROUGH. + BoardLayer start_layer = 2; - /// Highest (closest to B_Cu) layer this stack exists on. Ignored if type == PST_THROUGH. - kiapi.common.types.BoardLayer end_layer = 3; + // Highest (closest to B_Cu) layer this stack exists on. Ignored if type == PST_THROUGH. + BoardLayer end_layer = 3; - /// How to treat annular rings on unconnected layers. + // How to treat pad shapes on unconnected layers. UnconnectedLayerRemoval unconnected_layer_removal = 4; + + // The diameter, in x and y, of the pad's drilled hole, if this pad has a hole. + // x and y will be the same value if the hole is round. + kiapi.common.types.Vector2 drill_diameter = 5; + + repeated PadStackLayer layers = 6; + + // The overall rotation of this padstack (affects all layers) + kiapi.common.types.Angle angle = 7; } -/// Represents a via +// Represents a via message Via { - /// The unique identifier of the via + // The unique identifier of the via kiapi.common.types.KIID id = 1; - /// The location of the via's center point - kiapi.common.types.Point2D position = 2; + // The location of the via's center point + kiapi.common.types.Vector2 position = 2; - /// The diameter of the via's circular copper pad - kiapi.common.types.Distance pad_diameter = 4; + // The pad stack definition for this via. The via's VIATYPE (blind/buried/normal) is inferred from this. + PadStack pad_stack = 3; - /// The diameter of the via's drilled hole - kiapi.common.types.Distance drill_diameter = 5; + kiapi.common.types.LockedState locked = 4; - /// The pad stack definition for this via. The via's VIATYPE (blind/buried/normal) is inferred from this. + Net net = 5; +} + +message GraphicSegmentAttributes +{ + kiapi.common.types.Vector2 start = 1; + kiapi.common.types.Vector2 end = 2; +} + +message GraphicRectangleAttributes +{ + kiapi.common.types.Vector2 top_left = 1; + kiapi.common.types.Vector2 bottom_right = 2; +} + +message GraphicArcAttributes +{ + kiapi.common.types.Vector2 start = 1; + kiapi.common.types.Vector2 mid = 2; + kiapi.common.types.Vector2 end = 3; +} + +message GraphicCircleAttributes +{ + kiapi.common.types.Vector2 center = 1; + + // A point on the radius of the circle. This is stored instead of just a radius so that the point + // by which the user can adjust the circle radius is persisted. + kiapi.common.types.Vector2 radius_point = 2; +} + +message GraphicBezierAttributes +{ + kiapi.common.types.Vector2 start = 1; + kiapi.common.types.Vector2 control1 = 2; + kiapi.common.types.Vector2 control2 = 3; + kiapi.common.types.Vector2 end = 4; +} + +message GraphicShape +{ + kiapi.common.types.KIID id = 1; + kiapi.common.types.LockedState locked = 2; + BoardLayer layer = 3; + Net net = 4; + kiapi.common.types.GraphicAttributes attributes = 5; + + oneof geometry { + GraphicSegmentAttributes segment = 6; + GraphicRectangleAttributes rectangle = 7; + GraphicArcAttributes arc = 8; + GraphicCircleAttributes circle = 9; + kiapi.common.types.PolySet polygon = 10; + GraphicBezierAttributes bezier = 11; + } +} + +// A board-specific text object, existing on a board layer +message Text +{ + kiapi.common.types.Text text = 1; + BoardLayer layer = 2; +} + +// A board-specific textbox, existing on a board layer +message TextBox +{ + kiapi.common.types.TextBox textbox = 1; + BoardLayer layer = 2; +} + +// NOTE: There has been some discussion about what to do with pad attributes and properties. +// This may be considered somewhat unstable until we decide what to do with the KiCad side. +// It is not clear what the set of mutually-exclusive pad types will be at the end of the day, +// versus what will be non-exclusive attributes/properties. +// For now, this maps to PAD_ATTRIB in KiCad. +enum PadType +{ + PT_UNKNOWN = 0; + PT_PTH = 1; + PT_SMD = 2; + PT_EDGE_CONNECTOR = 3; + PT_NPTH = 4; +} + +enum CustomPadShapeZoneFillStrategy +{ + CPSZ_UNKNOWN = 0; + CPSZ_OUTLINE = 1; + CPSZ_CONVEXHULL = 2; +} + +message ThermalSpokeSettings +{ + int64 width = 1; + kiapi.common.types.Angle angle = 2; + int64 gap = 3; +} + +message Pad +{ + kiapi.common.types.KIID id = 1; + kiapi.common.types.LockedState locked = 2; + string number = 3; + Net net = 4; + PadType type = 5; PadStack pad_stack = 6; - kiapi.common.types.LockedState locked = 7; - kiapi.common.types.Net net = 8; + // A pad's position is always relative to the parent footprint's origin + kiapi.common.types.Vector2 position = 7; + + DesignRuleOverrides overrides = 8; + + ThermalSpokeSettings thermal_spokes = 9; +} + +// Copper zone, non-copper zone, or rule area +message Zone +{ + // TODO +} + +message Dimension +{ + // TODO +} + +message ReferenceImage +{ + // TODO +} + +message Group +{ + // TODO +} + +message FieldId +{ + int32 id = 1; +} + +message Field +{ + FieldId id = 1; + string name = 2; + Text text = 3; +} + +message Model3D +{ + // TODO +} + + +enum FootprintMountingStyle +{ + FMS_UNKNOWN = 0; + FMS_THROUGH_HOLE = 1; + FMS_SMD = 2; + FMS_UNSPECIFIED = 3; +} + + +message FootprintAttributes +{ + string description = 1; + string keywords = 2; + bool not_in_schematic = 3; + bool exclude_from_position_files = 4; + bool exclude_from_bill_of_materials = 5; + bool exempt_from_courtyard_requirement = 6; + bool do_not_populate = 7; + FootprintMountingStyle mounting_style = 8; +} + +// enum class ZONE_CONNECTION +enum ZoneConnectionStyle +{ + ZCS_UNKNOWN = 0; + ZCS_INHERITED = 1; + ZCS_NONE = 2; + ZCS_THERMAL = 3; + ZCS_FULL = 4; + ZCS_PTH_THERMAL = 5; // Thermal reliefs for plated through holes, solid for SMD pads +} + +message DesignRuleOverrides +{ + // Copper-to-copper clearance override + kiapi.common.types.Distance clearance = 1; + + // Solder mask expansion/contraction + kiapi.common.types.Distance solder_mask_margin = 2; + + // Solder paste expansion/contraction + kiapi.common.types.Distance solder_paste_margin = 3; + + // Solder paste expansion/contraction ratio + kiapi.common.types.Ratio solder_paste_margin_ratio = 4; + + ZoneConnectionStyle zone_connection = 5; +} + +message NetTieDefinition +{ + repeated string pad_number = 1; +} + +// A footprint definition (i.e. what would be in a library) +message Footprint +{ + kiapi.common.types.LibraryIdentifier id = 1; + kiapi.common.types.Vector2 anchor = 2; + FootprintAttributes attributes = 3; + DesignRuleOverrides overrides = 4; + repeated NetTieDefinition net_ties = 5; + repeated BoardLayer private_layers = 6; + + Field reference_field = 7; + Field value_field = 8; + Field datasheet_field = 9; + Field description_field = 10; + + // All footprint items except for mandatory fields + repeated google.protobuf.Any items = 11; +} + +// An instance of a footprint on a board +message FootprintInstance +{ + kiapi.common.types.KIID id = 1; + kiapi.common.types.Vector2 position = 2; + kiapi.common.types.Angle orientation = 3; + BoardLayer layer = 4; + kiapi.common.types.LockedState locked = 5; + Footprint definition = 6; + + Field reference_field = 7; + Field value_field = 8; + Field datasheet_field = 9; + Field description_field = 10; + + FootprintAttributes attributes = 11; + DesignRuleOverrides overrides = 12; } diff --git a/api/proto/common/commands/base_commands.proto b/api/proto/common/commands/base_commands.proto index d2c69e1b4c..583a99d6b2 100644 --- a/api/proto/common/commands/base_commands.proto +++ b/api/proto/common/commands/base_commands.proto @@ -31,3 +31,8 @@ message GetVersionResponse { kiapi.common.types.KiCadVersion version = 1; } + +// A command to check if the connection to KiCad is OK +message Ping +{ +} diff --git a/api/proto/common/commands/editor_commands.proto b/api/proto/common/commands/editor_commands.proto index 6299faaa8f..c48dcb0e9a 100644 --- a/api/proto/common/commands/editor_commands.proto +++ b/api/proto/common/commands/editor_commands.proto @@ -27,17 +27,18 @@ package kiapi.common.commands; import "google/protobuf/any.proto"; import "common/types/base_types.proto"; +import "common/types/enums.proto"; -/// Refreshes the given frame, if that frame is open +// Refreshes the given frame, if that frame is open message RefreshEditor { kiapi.common.types.FrameType frame = 1; } -/// Retrieves a list of open documents of the given type +// Retrieves a list of open documents of the given type message GetOpenDocuments { - /// Which type of documents to query + // Which type of documents to query kiapi.common.types.DocumentType type = 1; } @@ -86,140 +87,150 @@ message BeginCommit message BeginCommitResponse { + // Opaque identifier tracking a commit + kiapi.common.types.KIID id = 1; } -enum CommitResult +enum CommitAction { - CR_UNKNOWN = 0; - CR_OK = 1; // Commit was pushed successfully - CR_NO_COMMIT = 2; // There was no commit started + CMA_UNKNOWN = 0; + CMA_COMMIT = 1; // Commit the changes to the design + CMA_DROP = 2; // Cancel this commit } message EndCommit { + // The ID that was given by BeginCommit + kiapi.common.types.KIID id = 1; + + // What to do with this commit + CommitAction action = 2; + // Optional message describing this changeset - string message = 1; + string message = 3; } message EndCommitResponse { - CommitResult result = 1; } -/// Creates new items on a given document +// Creates new items on a given document message CreateItems { - /// Specifies which document to create on, which fields are included, etc. + // Specifies which document to create on, which fields are included, etc. kiapi.common.types.ItemHeader header = 1; - /// List of items to create + // List of items to create repeated google.protobuf.Any items = 2; - /// Items may be created on a top-level document (sheet, board, etc) or inside a container - /// (symbol, footprint). If this field is not empty, it holds the ID of a symbol or footprint - /// that the items should be added to. This ID must be an existing symbol (for schematic - /// documents) or footprint (for board documents). If the given container does not exist or is - /// not the correct item type, the CreateItems call will fail. + // Items may be created on a top-level document (sheet, board, etc) or inside a container + // (symbol, footprint). If this field is not empty, it holds the ID of a symbol or footprint + // that the items should be added to. This ID must be an existing symbol (for schematic + // documents) or footprint (for board documents). If the given container does not exist or is + // not the correct item type, the CreateItems call will fail. kiapi.common.types.KIID container = 3; } -enum ItemCreationStatus +enum ItemStatusCode { - ICS_UNKNOWN = 0; - ICS_OK = 1; /// The item was created - ICS_INVALID_TYPE = 2; /// The item's type is not valid for the given document - ICS_EXISTING = 3; /// The item had a specified KIID and that KIID was already in use + ISC_UNKNOWN = 0; + ISC_OK = 1; // The item was created or updated + ISC_INVALID_TYPE = 2; // The item's type is not valid for the given document + ISC_EXISTING = 3; // The item to be created had a specified KIID and that KIID was already in use + ISC_NONEXISTENT = 4; // The item to be updated did not exist in the given document + ISC_IMMUTABLE = 5; // The item to be updated is not allowed to be modified by the API + ISC_INVALID_DATA = 7; // The item to be created does not have valid data for the given document +} + +// Per-item status feedback for creation and update calls +message ItemStatus +{ + ItemStatusCode code = 1; + + string error_message = 2; } message ItemCreationResult { - ItemCreationStatus status = 1; + ItemStatus status = 1; - /// The created version of the item, including an updated KIID as applicable + // The created version of the item, including an updated KIID as applicable google.protobuf.Any item = 2; } message CreateItemsResponse { - /// Specifies which document was modified, which fields are included in created_items, etc. + // Specifies which document was modified, which fields are included in created_items, etc. kiapi.common.types.ItemHeader header = 1; - /// Status of the overall request; may return IRS_OK even if no items were created + // Status of the overall request; may return IRS_OK even if no items were created kiapi.common.types.ItemRequestStatus status = 2; - /// Status of each item to be created + // Status of each item to be created repeated ItemCreationResult created_items = 3; } message GetItems { - /// Specifies which document to query, which fields to return, etc. + // Specifies which document to query, which fields to return, etc. kiapi.common.types.ItemHeader header = 1; - /// List of one or more types of items to retreive - repeated kiapi.common.types.ItemType types = 2; + // List of one or more types of items to retreive + repeated kiapi.common.types.KiCadObjectType types = 2; } message GetItemsResponse { - /// Specifies which document was modified, which fields are included in items, etc. + // Specifies which document was modified, which fields are included in items, etc. kiapi.common.types.ItemHeader header = 1; - /// Status of the overall request; may return IRS_OK even if no items were retrieved + // Status of the overall request; may return IRS_OK even if no items were retrieved kiapi.common.types.ItemRequestStatus status = 2; repeated google.protobuf.Any items = 3; } -/// Updates items in a given document +// Updates items in a given document message UpdateItems { - /// Specifies which document to modify, which fields are included, etc. + // Specifies which document to modify, which fields are included, etc. kiapi.common.types.ItemHeader header = 1; - /// List of items to modify + // List of items to modify repeated google.protobuf.Any items = 2; } -enum ItemUpdateStatus -{ - IUS_UNKNOWN = 0; - IUS_OK = 1; /// The item was updated - IUS_INVALID_TYPE = 2; /// The item's type is not valid for the given document - IUS_NONEXISTENT = 3; /// The item did not exist in the given document - IUS_IMMUTABLE = 4; /// The item is not allowed to be modified by the API -} message ItemUpdateResult { - ItemUpdateStatus status = 1; + ItemStatus status = 1; - /// The update version of the item + // The update version of the item google.protobuf.Any item = 2; } message UpdateItemsResponse { - /// Specifies which document was modified, which fields are included in updated_items, etc. + // Specifies which document was modified, which fields are included in updated_items, etc. kiapi.common.types.ItemHeader header = 1; - /// Status of the overall request; may return IRS_OK even if no items were modified + // Status of the overall request; may return IRS_OK even if no items were modified kiapi.common.types.ItemRequestStatus status = 2; - /// Status of each item to be created + // Status of each item to be created repeated ItemUpdateResult updated_items = 3; } -/// Deletes items in a given document +// Deletes items in a given document message DeleteItems { - /// Specifies which document to modify + // Specifies which document to modify kiapi.common.types.ItemHeader header = 1; - /// List of item KIIDs to delete + // List of item KIIDs to delete repeated kiapi.common.types.KIID item_ids = 2; } @@ -227,8 +238,8 @@ enum ItemDeletionStatus { IDS_UNKNOWN = 0; IDS_OK = 1; - IDS_NONEXISTENT = 2; /// The item did not exist in the given document - IDS_IMMUTABLE = 3; /// The item is not allowed to be modified by the API + IDS_NONEXISTENT = 2; // The item did not exist in the given document + IDS_IMMUTABLE = 3; // The item is not allowed to be modified by the API } message ItemDeletionResult @@ -240,12 +251,49 @@ message ItemDeletionResult message DeleteItemsResponse { - /// Specifies which document was modified, etc. + // Specifies which document was modified, etc. kiapi.common.types.ItemHeader header = 1; - /// Status of the overall request; may return IRS_OK even if no items were deleted + // Status of the overall request; may return IRS_OK even if no items were deleted kiapi.common.types.ItemRequestStatus status = 2; - /// Status of each item requested to be deleted + // Status of each item requested to be deleted repeated ItemDeletionResult deleted_items = 3; } + +message GetItemBoundingBox +{ + kiapi.common.types.ItemHeader header = 1; + + kiapi.common.types.KIID id = 2; +} + +message BoundingBoxResponse +{ + kiapi.common.types.Vector2 position = 1; + kiapi.common.types.Vector2 size = 2; +} + +// Tests if a certain point falls within tolerance of an item's geometry +message HitTest +{ + kiapi.common.types.ItemHeader header = 1; + + kiapi.common.types.KIID id = 2; + + kiapi.common.types.Vector2 position = 3; + + int32 tolerance = 4; +} + +enum HitTestResult +{ + HTR_UNKNOWN = 0; + HTR_NO_HIT = 1; + HTR_HIT = 2; +} + +message HitTestResponse +{ + HitTestResult result = 1; +} diff --git a/api/proto/common/commands/project_commands.proto b/api/proto/common/commands/project_commands.proto new file mode 100644 index 0000000000..979039aa74 --- /dev/null +++ b/api/proto/common/commands/project_commands.proto @@ -0,0 +1,34 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +syntax = "proto3"; + +package kiapi.common.commands; + +import "common/types/project_settings.proto"; + + +message GetNetClasses +{ +} + +message NetClassesResponse +{ + repeated kiapi.common.project.NetClass net_classes = 1; +} diff --git a/api/proto/common/envelope.proto b/api/proto/common/envelope.proto index 8ae918d423..81e2a62b58 100644 --- a/api/proto/common/envelope.proto +++ b/api/proto/common/envelope.proto @@ -29,9 +29,11 @@ enum ApiStatusCode AS_OK = 1; // Request succeeded AS_TIMEOUT = 2; // Request timed out AS_BAD_REQUEST = 3; // The request had invalid parameters or otherwise was illegal - AS_NOT_READY = 4; // KiCad was not (yet) in a state where it could handle API requests + AS_NOT_READY = 4; // KiCad has recently started and cannot handle API requests yet AS_UNHANDLED = 5; // The request was not handled by KiCad AS_TOKEN_MISMATCH = 6; // The kicad_token in the request didn't match this KiCad's token + AS_BUSY = 7; // KiCad is busy performing an operation and can't accept API commands + AS_UNIMPLEMENTED = 8; // The requested API call has not yet been implemented } /* diff --git a/api/proto/common/types/base_types.proto b/api/proto/common/types/base_types.proto index b9fa3fc9cd..aa47eed6e7 100644 --- a/api/proto/common/types/base_types.proto +++ b/api/proto/common/types/base_types.proto @@ -27,6 +27,7 @@ syntax = "proto3"; package kiapi.common.types; import "google/protobuf/field_mask.proto"; +import "common/types/enums.proto"; enum CommandStatus { @@ -95,10 +96,10 @@ enum DocumentType */ message LibraryIdentifier { - /// The library portion of the LIB_ID + // The library portion of the LIB_ID string library_nickname = 1; - /// The symbol or footprint name + // The symbol or footprint name string entry_name = 2; } @@ -107,13 +108,25 @@ message LibraryIdentifier */ message SheetPath { - /// The canonical path to the sheet. The first KIID will be the root sheet, etc. + // The canonical path to the sheet. The first KIID will be the root sheet, etc. repeated KIID path = 1; - /// The path converted to a human readable form such as "/", "/child", or "/child/grandchild" + // The path converted to a human readable form such as "/", "/child", or "/child/grandchild" string path_human_readable = 2; } +/** + * Describes a KiCad project + */ +message ProjectSpecifier +{ + // The name of the project (without the kicad_pro extension) + string name = 1; + + // The path to the project directory + string path = 2; +} + /** * Describes a document that will be the target of a request */ @@ -123,24 +136,17 @@ message DocumentSpecifier oneof identifier { - /// If type == DT_SYMBOL or DT_FOOTPRINT, identifies a certain library entry + // If type == DT_SYMBOL or DT_FOOTPRINT, identifies a certain library entry LibraryIdentifier lib_id = 2; - /// If type == DT_SCHEMATIC, identifies a sheet with a given path + // If type == DT_SCHEMATIC, identifies a sheet with a given path SheetPath sheet_path = 3; - /// If type == DT_PCB, identifies a PCB with a given filename, e.g. "board.kicad_pcb" + // If type == DT_PCB, identifies a PCB with a given filename, e.g. "board.kicad_pcb" string board_filename = 4; } -} -/** - * Describes the type of a KiCad item (wrapper for KICAD_T) - */ -message ItemType -{ - /// Must be a valid value in the KICAD_T C++ enum (see typeinfo.h) - int32 type = 1; + ProjectSpecifier project = 5; } /** @@ -148,11 +154,15 @@ message ItemType */ message ItemHeader { - /// Which document is this request targeting? + // Which document is this request targeting? DocumentSpecifier document = 1; - /// Which fields on the item(s) are included with this request or response - google.protobuf.FieldMask field_mask = 2; + // Which container within the document is this request targeting? + // If container is omitted or empty, the document is used as the container. + KIID container = 2; + + // Which fields on the item(s) are included with this request or response + google.protobuf.FieldMask field_mask = 3; } /** @@ -162,32 +172,93 @@ enum ItemRequestStatus { IRS_UNKNOWN = 0; IRS_OK = 1; - IRS_DOCUMENT_NOT_FOUND = 2; /// The given document is not open in KiCad - IRS_FIELD_MASK_INVALID = 3; /// The given field_mask contains invalid specifiers + IRS_DOCUMENT_NOT_FOUND = 2; // The given document is not open in KiCad + IRS_FIELD_MASK_INVALID = 3; // The given field_mask contains invalid specifiers } -/// Describes a point in 2D space. All coordinates are in nanometers. -message Point2D +// Describes a point or distance in 2D space. All coordinates are in nanometers. +message Vector2 { int64 x_nm = 1; int64 y_nm = 2; } -/// Describes a point in 3D space. All coordinates are in nanometers. -message Point3D +// Describes a point or distance in 3D space. All coordinates are in nanometers. +message Vector3 { int64 x_nm = 1; int64 y_nm = 2; int64 z_nm = 3; } -/// Describes a quantity of distance (size, length, etc). All coordinates are in nanometers. +// Describes a quantity of distance (size, length, etc). All coordinates are in nanometers. message Distance { int64 value_nm = 1; } -/// Describes whether or not an item is locked for editing or movement +// Corresponds to EDA_ANGLE, where the underlying storage is degrees +message Angle +{ + double value_degrees = 1; +} + +// Represents a value from 0.0 to 1.0 +message Ratio +{ + double value = 1; +} + +// Corresponds to COLOR4D. Each color channel is a double from 0.0 to 1.0. +message Color +{ + double r = 1; + double g = 2; + double b = 3; + double a = 4; +} + +// The formulation of arc that is used in KiCad core geometry code. +// Start, midpoint (on the arc) and end are stored. Angle, center point, etc are calculated. +message ArcStartMidEnd +{ + Vector2 start = 1; + Vector2 mid = 2; + Vector2 end = 3; +} + +message PolyLineNode +{ + oneof geometry { + Vector2 point = 1; + ArcStartMidEnd arc = 2; + } +} + +// Corresponds to class SHAPE_LINE_CHAIN: A closed or open polyline that can include arcs. +// For non-arc cases, each node is a point along the line. An implicit line segment exists +// between the last and first node if closed is true. When arcs are present, the arc start and +// end points are not duplicated by point nodes (meaning, for example, a rectangle with rounded +// corners could be represented with four arc nodes and no point nodes). +message PolyLine +{ + repeated PolyLineNode nodes = 1; + bool closed = 2; +} + +message PolygonWithHoles +{ + PolyLine outline = 1; + repeated PolyLine holes = 2; +} + +// Corresponds to SHAPE_POLY_SET: a set of polygons or polylines +message PolySet +{ + repeated PolygonWithHoles polygons = 1; +} + +// Describes whether or not an item is locked for editing or movement enum LockedState { LS_UNKNOWN = 0; @@ -195,23 +266,69 @@ enum LockedState LS_LOCKED = 2; } -message BoardLayer +message TextAttributes { - int32 layer_id = 1; /// From PCB_LAYER_T + string font_name = 1; + HorizontalAlignment horizontal_alignment = 2; + VerticalAlignment vertical_alignment = 3; + kiapi.common.types.Angle angle = 4; + double line_spacing = 5; + kiapi.common.types.Distance stroke_width = 6; + bool italic = 7; + bool bold = 8; + bool underlined = 9; + bool visible = 10; + bool mirrored = 11; + bool multiline = 12; + bool keep_upright = 13; + kiapi.common.types.Vector2 size = 14; } -/// Describes a copper item's net -message Net +message Text { - /// A unique code representing this net - int32 code = 1; - - /// Human-readable net name - string name = 2; + kiapi.common.types.KIID id = 1; + kiapi.common.types.Vector2 position = 2; + kiapi.common.types.TextAttributes attributes = 3; + kiapi.common.types.LockedState locked = 4; + string text = 5; + string hyperlink = 6; + bool knockout = 7; } -/// Describes a net class (a grouping of nets) -message NetClass +message TextBox { - string name = 1; + kiapi.common.types.KIID id = 1; + kiapi.common.types.Vector2 top_left = 2; + kiapi.common.types.Vector2 bottom_right = 3; + kiapi.common.types.TextAttributes attributes = 4; + kiapi.common.types.LockedState locked = 5; + string text = 6; +} + +message StrokeAttributes +{ + Distance width = 1; + StrokeLineStyle style = 2; + Color color = 3; +} + +enum GraphicFillType +{ + GFT_UNKNOWN = 0; + GFT_UNFILLED = 1; + GFT_FILLED = 2; +} + +message GraphicFillAttributes +{ + GraphicFillType fill_type = 1; + + // Color of the fill (not used in board and footprints) + Color color = 2; +} + +message GraphicAttributes +{ + StrokeAttributes stroke = 1; + GraphicFillAttributes fill = 2; } diff --git a/api/proto/common/types/enums.proto b/api/proto/common/types/enums.proto new file mode 100644 index 0000000000..97811512ec --- /dev/null +++ b/api/proto/common/types/enums.proto @@ -0,0 +1,119 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +/* + * enums.proto + * Includes protobuf versions of common KiCad enums + */ + +syntax = "proto3"; + +package kiapi.common.types; + +// The set of object types (from KICAD_T) that are exposed to the API. +enum KiCadObjectType +{ + KOT_UNKNOWN = 0; + + KOT_PCB_FOOTPRINT = 1; + KOT_PCB_PAD = 2; + KOT_PCB_SHAPE = 3; + KOT_PCB_REFERENCE_IMAGE = 4; + KOT_PCB_FIELD = 5; + KOT_PCB_GENERATOR = 6; + KOT_PCB_TEXT = 7; + KOT_PCB_TEXTBOX = 8; + KOT_PCB_TABLE = 9; + KOT_PCB_TABLECELL = 10; + KOT_PCB_TRACE = 11; + KOT_PCB_VIA = 12; + KOT_PCB_ARC = 13; + KOT_PCB_MARKER = 14; + KOT_PCB_DIMENSION = 15; + KOT_PCB_ZONE = 16; + KOT_PCB_GROUP = 17; + + KOT_SCH_MARKER = 18; + KOT_SCH_JUNCTION = 19; + KOT_SCH_NO_CONNECT = 20; + KOT_SCH_BUS_WIRE_ENTRY = 21; + KOT_SCH_BUS_BUS_ENTRY = 22; + KOT_SCH_LINE = 23; + KOT_SCH_SHAPE = 24; + KOT_SCH_BITMAP = 25; + KOT_SCH_TEXTBOX = 26; + KOT_SCH_TEXT = 27; + KOT_SCH_TABLE = 28; + KOT_SCH_TABLECELL = 29; + KOT_SCH_LABEL = 30; + KOT_SCH_GLOBAL_LABEL = 31; + KOT_SCH_HIER_LABEL = 32; + KOT_SCH_DIRECTIVE_LABEL = 33; + KOT_SCH_FIELD = 34; + KOT_SCH_SYMBOL = 35; + KOT_SCH_SHEET_PIN = 36; + KOT_SCH_SHEET = 37; + KOT_SCH_PIN = 38; + + KOT_LIB_SYMBOL = 39; + KOT_LIB_SHAPE = 40; + KOT_LIB_TEXT = 41; + KOT_LIB_TEXTBOX = 42; + KOT_LIB_PIN = 43; + KOT_LIB_FIELD = 44; + + KOT_WSG_LINE = 45; + KOT_WSG_RECT = 46; + KOT_WSG_POLY = 47; + KOT_WSG_TEXT = 48; + KOT_WSG_BITMAP = 49; + KOT_WSG_PAGE = 50; +} + +// Mapped to GR_TEXT_H_ALIGN_T +enum HorizontalAlignment +{ + HA_UNKNOWN = 0; + HA_LEFT = 1; + HA_CENTER = 2; + HA_RIGHT = 3; + HA_INDETERMINATE = 4; +} + +// Mapped to GR_TEXT_V_ALIGN_T +enum VerticalAlignment +{ + VA_UNKNOWN = 0; + VA_TOP = 1; + VA_CENTER = 2; + VA_BOTTOM = 3; + VA_INDETERMINATE = 4; +} + +// Mapped to LINE_STYLE +enum StrokeLineStyle +{ + SLS_UNKNOWN = 0; + SLS_DEFAULT = 1; + SLS_SOLID = 2; + SLS_DASH = 3; + SLS_DOT = 4; + SLS_DASHDOT = 5; + SLS_DASHDOTDOT = 6; +} diff --git a/api/proto/common/types/project_settings.proto b/api/proto/common/types/project_settings.proto new file mode 100644 index 0000000000..9dd92b3291 --- /dev/null +++ b/api/proto/common/types/project_settings.proto @@ -0,0 +1,35 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +/* + * project_settings.proto + * Messages that describes project settings shared between schematics and boards + */ + +syntax = "proto3"; + +package kiapi.common.project; + + +message NetClass +{ + // The name of the netclass (the literal string "Default" for the default netclass) + string name = 1; + +} diff --git a/api/proto/schematic/schematic_commands.proto b/api/proto/schematic/schematic_commands.proto new file mode 100644 index 0000000000..9de8e7ec99 --- /dev/null +++ b/api/proto/schematic/schematic_commands.proto @@ -0,0 +1,24 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +syntax = "proto3"; + +package kiapi.schematic.types; + + diff --git a/api/proto/schematic/schematic_types.proto b/api/proto/schematic/schematic_types.proto new file mode 100644 index 0000000000..3f872dcb2c --- /dev/null +++ b/api/proto/schematic/schematic_types.proto @@ -0,0 +1,75 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +syntax = "proto3"; + +package kiapi.schematic.types; + +import "common/types/base_types.proto"; + +enum SchematicLayer +{ + SL_UNKNOWN = 0; +} + +/// Represents a schematic line segment, which may be a wire, bus, or graphical line +message Line +{ + kiapi.common.types.KIID id = 1; + kiapi.common.types.Vector2 start = 2; + kiapi.common.types.Vector2 end = 3; + + /** + * One of: LAYER_BUS, LAYER_WIRE, LAYER_NOTES + */ + SchematicLayer layer = 4; +} + +message Text +{ + kiapi.common.types.Text text = 1; +} + +message LocalLabel +{ + kiapi.common.types.KIID id = 1; + kiapi.common.types.Vector2 position = 2; + Text text = 3; +} + +message GlobalLabel +{ + kiapi.common.types.KIID id = 1; + kiapi.common.types.Vector2 position = 2; + Text text = 3; +} + +message HierarchicalLabel +{ + kiapi.common.types.KIID id = 1; + kiapi.common.types.Vector2 position = 2; + Text text = 3; +} + +message DirectiveLabel +{ + kiapi.common.types.KIID id = 1; + kiapi.common.types.Vector2 position = 2; + Text text = 3; +} diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c41bb9369b..6bc36acb42 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -41,8 +41,7 @@ add_custom_target( -DSRC_PATH=${PROJECT_SOURCE_DIR} -DKICAD_CMAKE_MODULE_PATH=${KICAD_CMAKE_MODULE_PATH} -P ${KICAD_CMAKE_MODULE_PATH}/BuildSteps/WriteVersionHeader.cmake - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - BYPRODUCTS ${CMAKE_BINARY_DIR}/kicad_build_version.h + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} BYPRODUCTS ${CMAKE_BINARY_DIR}/kicad_build_version.h DEPENDS ${KICAD_CMAKE_MODULE_PATH}/BuildSteps/WriteVersionHeader.cmake COMMENT "Generating version string header" ) @@ -632,14 +631,14 @@ set( COMMON_SRCS http_lib/http_lib_connection.cpp http_lib/http_lib_settings.cpp + api/api_enums.cpp + api/api_utils.cpp ) if( KICAD_IPC_API ) set( COMMON_SRCS ${COMMON_SRCS} - api/api_server.cpp - api/api_handler.cpp - api/api_handler_common.cpp + api/api_handler_editor.cpp ) endif() @@ -693,6 +692,7 @@ target_include_directories( common . ${CMAKE_BINARY_DIR} $ + $ ) # text markup support @@ -794,6 +794,12 @@ set( PCB_COMMON_SRCS widgets/net_selector.cpp ) +set( PCB_COMMON_SRCS + ${PCB_COMMON_SRCS} + ${CMAKE_SOURCE_DIR}/pcbnew/api/api_pcb_enums.cpp + ${CMAKE_SOURCE_DIR}/pcbnew/api/api_pcb_utils.cpp +) + # add -DPCBNEW to compilation of these PCBNEW sources set_source_files_properties( ${PCB_COMMON_SRCS} PROPERTIES COMPILE_DEFINITIONS "PCBNEW" diff --git a/common/api/api_enums.cpp b/common/api/api_enums.cpp new file mode 100644 index 0000000000..5021ef814a --- /dev/null +++ b/common/api/api_enums.cpp @@ -0,0 +1,436 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace kiapi; +using namespace kiapi::common; + +template<> +KICAD_T FromProtoEnum( types::KiCadObjectType aValue ) +{ + switch( aValue ) + { + case types::KiCadObjectType::KOT_PCB_FOOTPRINT: return PCB_FOOTPRINT_T; + case types::KiCadObjectType::KOT_PCB_PAD: return PCB_PAD_T; + case types::KiCadObjectType::KOT_PCB_SHAPE: return PCB_SHAPE_T; + case types::KiCadObjectType::KOT_PCB_REFERENCE_IMAGE: return PCB_REFERENCE_IMAGE_T; + case types::KiCadObjectType::KOT_PCB_FIELD: return PCB_FIELD_T; + case types::KiCadObjectType::KOT_PCB_GENERATOR: return PCB_GENERATOR_T; + case types::KiCadObjectType::KOT_PCB_TEXT: return PCB_TEXT_T; + case types::KiCadObjectType::KOT_PCB_TEXTBOX: return PCB_TEXTBOX_T; + case types::KiCadObjectType::KOT_PCB_TABLE: return PCB_TABLE_T; + case types::KiCadObjectType::KOT_PCB_TABLECELL: return PCB_TABLECELL_T; + case types::KiCadObjectType::KOT_PCB_TRACE: return PCB_TRACE_T; + case types::KiCadObjectType::KOT_PCB_VIA: return PCB_VIA_T; + case types::KiCadObjectType::KOT_PCB_ARC: return PCB_ARC_T; + case types::KiCadObjectType::KOT_PCB_MARKER: return PCB_MARKER_T; + case types::KiCadObjectType::KOT_PCB_DIMENSION: return PCB_DIMENSION_T; + case types::KiCadObjectType::KOT_PCB_ZONE: return PCB_ZONE_T; + case types::KiCadObjectType::KOT_PCB_GROUP: return PCB_GROUP_T; + case types::KiCadObjectType::KOT_SCH_MARKER: return SCH_MARKER_T; + case types::KiCadObjectType::KOT_SCH_JUNCTION: return SCH_JUNCTION_T; + case types::KiCadObjectType::KOT_SCH_NO_CONNECT: return SCH_NO_CONNECT_T; + case types::KiCadObjectType::KOT_SCH_BUS_WIRE_ENTRY: return SCH_BUS_WIRE_ENTRY_T; + case types::KiCadObjectType::KOT_SCH_BUS_BUS_ENTRY: return SCH_BUS_BUS_ENTRY_T; + case types::KiCadObjectType::KOT_SCH_LINE: return SCH_LINE_T; + case types::KiCadObjectType::KOT_SCH_SHAPE: return SCH_SHAPE_T; + case types::KiCadObjectType::KOT_SCH_BITMAP: return SCH_BITMAP_T; + case types::KiCadObjectType::KOT_SCH_TEXTBOX: return SCH_TEXTBOX_T; + case types::KiCadObjectType::KOT_SCH_TEXT: return SCH_TEXT_T; + case types::KiCadObjectType::KOT_SCH_TABLE: return SCH_TABLE_T; + case types::KiCadObjectType::KOT_SCH_TABLECELL: return SCH_TABLECELL_T; + case types::KiCadObjectType::KOT_SCH_LABEL: return SCH_LABEL_T; + case types::KiCadObjectType::KOT_SCH_GLOBAL_LABEL: return SCH_GLOBAL_LABEL_T; + case types::KiCadObjectType::KOT_SCH_HIER_LABEL: return SCH_HIER_LABEL_T; + case types::KiCadObjectType::KOT_SCH_DIRECTIVE_LABEL: return SCH_DIRECTIVE_LABEL_T; + case types::KiCadObjectType::KOT_SCH_FIELD: return SCH_FIELD_T; + case types::KiCadObjectType::KOT_SCH_SYMBOL: return SCH_SYMBOL_T; + case types::KiCadObjectType::KOT_SCH_SHEET_PIN: return SCH_SHEET_PIN_T; + case types::KiCadObjectType::KOT_SCH_SHEET: return SCH_SHEET_T; + case types::KiCadObjectType::KOT_SCH_PIN: return SCH_PIN_T; + case types::KiCadObjectType::KOT_LIB_SYMBOL: return LIB_SYMBOL_T; + case types::KiCadObjectType::KOT_LIB_SHAPE: return LIB_SHAPE_T; + case types::KiCadObjectType::KOT_LIB_TEXT: return LIB_TEXT_T; + case types::KiCadObjectType::KOT_LIB_TEXTBOX: return LIB_TEXTBOX_T; + case types::KiCadObjectType::KOT_LIB_PIN: return LIB_PIN_T; + case types::KiCadObjectType::KOT_LIB_FIELD: return LIB_FIELD_T; + case types::KiCadObjectType::KOT_WSG_LINE: return WSG_LINE_T; + case types::KiCadObjectType::KOT_WSG_RECT: return WSG_RECT_T; + case types::KiCadObjectType::KOT_WSG_POLY: return WSG_POLY_T; + case types::KiCadObjectType::KOT_WSG_TEXT: return WSG_TEXT_T; + case types::KiCadObjectType::KOT_WSG_BITMAP: return WSG_BITMAP_T; + case types::KiCadObjectType::KOT_WSG_PAGE: return WSG_PAGE_T; + + case types::KiCadObjectType::KOT_UNKNOWN: return TYPE_NOT_INIT; + default: + wxCHECK_MSG( false, TYPE_NOT_INIT, + "Unhandled case in FromProtoEnum" ); + } +} + + +template<> +types::KiCadObjectType ToProtoEnum( KICAD_T aValue ) +{ + switch( aValue ) + { + case PCB_FOOTPRINT_T: return types::KiCadObjectType::KOT_PCB_FOOTPRINT; + case PCB_PAD_T: return types::KiCadObjectType::KOT_PCB_PAD; + case PCB_SHAPE_T: return types::KiCadObjectType::KOT_PCB_SHAPE; + case PCB_REFERENCE_IMAGE_T: return types::KiCadObjectType::KOT_PCB_REFERENCE_IMAGE; + case PCB_FIELD_T: return types::KiCadObjectType::KOT_PCB_FIELD; + case PCB_GENERATOR_T: return types::KiCadObjectType::KOT_PCB_GENERATOR; + case PCB_TEXT_T: return types::KiCadObjectType::KOT_PCB_TEXT; + case PCB_TEXTBOX_T: return types::KiCadObjectType::KOT_PCB_TEXTBOX; + case PCB_TABLE_T: return types::KiCadObjectType::KOT_PCB_TABLE; + case PCB_TABLECELL_T: return types::KiCadObjectType::KOT_PCB_TABLECELL; + case PCB_TRACE_T: return types::KiCadObjectType::KOT_PCB_TRACE; + case PCB_VIA_T: return types::KiCadObjectType::KOT_PCB_VIA; + case PCB_ARC_T: return types::KiCadObjectType::KOT_PCB_ARC; + case PCB_MARKER_T: return types::KiCadObjectType::KOT_PCB_MARKER; + case PCB_DIMENSION_T: return types::KiCadObjectType::KOT_PCB_DIMENSION; + case PCB_ZONE_T: return types::KiCadObjectType::KOT_PCB_ZONE; + case PCB_GROUP_T: return types::KiCadObjectType::KOT_PCB_GROUP; + case SCH_MARKER_T: return types::KiCadObjectType::KOT_SCH_MARKER; + case SCH_JUNCTION_T: return types::KiCadObjectType::KOT_SCH_JUNCTION; + case SCH_NO_CONNECT_T: return types::KiCadObjectType::KOT_SCH_NO_CONNECT; + case SCH_BUS_WIRE_ENTRY_T: return types::KiCadObjectType::KOT_SCH_BUS_WIRE_ENTRY; + case SCH_BUS_BUS_ENTRY_T: return types::KiCadObjectType::KOT_SCH_BUS_BUS_ENTRY; + case SCH_LINE_T: return types::KiCadObjectType::KOT_SCH_LINE; + case SCH_SHAPE_T: return types::KiCadObjectType::KOT_SCH_SHAPE; + case SCH_BITMAP_T: return types::KiCadObjectType::KOT_SCH_BITMAP; + case SCH_TEXTBOX_T: return types::KiCadObjectType::KOT_SCH_TEXTBOX; + case SCH_TEXT_T: return types::KiCadObjectType::KOT_SCH_TEXT; + case SCH_TABLE_T: return types::KiCadObjectType::KOT_SCH_TABLE; + case SCH_TABLECELL_T: return types::KiCadObjectType::KOT_SCH_TABLECELL; + case SCH_LABEL_T: return types::KiCadObjectType::KOT_SCH_LABEL; + case SCH_GLOBAL_LABEL_T: return types::KiCadObjectType::KOT_SCH_GLOBAL_LABEL; + case SCH_HIER_LABEL_T: return types::KiCadObjectType::KOT_SCH_HIER_LABEL; + case SCH_DIRECTIVE_LABEL_T: return types::KiCadObjectType::KOT_SCH_DIRECTIVE_LABEL; + case SCH_FIELD_T: return types::KiCadObjectType::KOT_SCH_FIELD; + case SCH_SYMBOL_T: return types::KiCadObjectType::KOT_SCH_SYMBOL; + case SCH_SHEET_PIN_T: return types::KiCadObjectType::KOT_SCH_SHEET_PIN; + case SCH_SHEET_T: return types::KiCadObjectType::KOT_SCH_SHEET; + case SCH_PIN_T: return types::KiCadObjectType::KOT_SCH_PIN; + case LIB_SYMBOL_T: return types::KiCadObjectType::KOT_LIB_SYMBOL; + case LIB_SHAPE_T: return types::KiCadObjectType::KOT_LIB_SHAPE; + case LIB_TEXT_T: return types::KiCadObjectType::KOT_LIB_TEXT; + case LIB_TEXTBOX_T: return types::KiCadObjectType::KOT_LIB_TEXTBOX; + case LIB_PIN_T: return types::KiCadObjectType::KOT_LIB_PIN; + case LIB_FIELD_T: return types::KiCadObjectType::KOT_LIB_FIELD; + case WSG_LINE_T: return types::KiCadObjectType::KOT_WSG_LINE; + case WSG_RECT_T: return types::KiCadObjectType::KOT_WSG_RECT; + case WSG_POLY_T: return types::KiCadObjectType::KOT_WSG_POLY; + case WSG_TEXT_T: return types::KiCadObjectType::KOT_WSG_TEXT; + case WSG_BITMAP_T: return types::KiCadObjectType::KOT_WSG_BITMAP; + case WSG_PAGE_T: return types::KiCadObjectType::KOT_WSG_PAGE; + default: + wxCHECK_MSG( false, types::KiCadObjectType::KOT_UNKNOWN, + "Unhandled case in ToProtoEnum"); + } +} + + +template<> +PCB_LAYER_ID FromProtoEnum( board::types::BoardLayer aValue ) +{ + switch( aValue ) + { + case board::types::BoardLayer::BL_UNDEFINED: return UNDEFINED_LAYER; + case board::types::BoardLayer::BL_UNSELECTED: return UNSELECTED_LAYER; + case board::types::BoardLayer::BL_F_Cu: return F_Cu; + case board::types::BoardLayer::BL_In1_Cu: return In1_Cu; + case board::types::BoardLayer::BL_In2_Cu: return In2_Cu; + case board::types::BoardLayer::BL_In3_Cu: return In3_Cu; + case board::types::BoardLayer::BL_In4_Cu: return In4_Cu; + case board::types::BoardLayer::BL_In5_Cu: return In5_Cu; + case board::types::BoardLayer::BL_In6_Cu: return In6_Cu; + case board::types::BoardLayer::BL_In7_Cu: return In7_Cu; + case board::types::BoardLayer::BL_In8_Cu: return In8_Cu; + case board::types::BoardLayer::BL_In9_Cu: return In9_Cu; + case board::types::BoardLayer::BL_In10_Cu: return In10_Cu; + case board::types::BoardLayer::BL_In11_Cu: return In11_Cu; + case board::types::BoardLayer::BL_In12_Cu: return In12_Cu; + case board::types::BoardLayer::BL_In13_Cu: return In13_Cu; + case board::types::BoardLayer::BL_In14_Cu: return In14_Cu; + case board::types::BoardLayer::BL_In15_Cu: return In15_Cu; + case board::types::BoardLayer::BL_In16_Cu: return In16_Cu; + case board::types::BoardLayer::BL_In17_Cu: return In17_Cu; + case board::types::BoardLayer::BL_In18_Cu: return In18_Cu; + case board::types::BoardLayer::BL_In19_Cu: return In19_Cu; + case board::types::BoardLayer::BL_In20_Cu: return In20_Cu; + case board::types::BoardLayer::BL_In21_Cu: return In21_Cu; + case board::types::BoardLayer::BL_In22_Cu: return In22_Cu; + case board::types::BoardLayer::BL_In23_Cu: return In23_Cu; + case board::types::BoardLayer::BL_In24_Cu: return In24_Cu; + case board::types::BoardLayer::BL_In25_Cu: return In25_Cu; + case board::types::BoardLayer::BL_In26_Cu: return In26_Cu; + case board::types::BoardLayer::BL_In27_Cu: return In27_Cu; + case board::types::BoardLayer::BL_In28_Cu: return In28_Cu; + case board::types::BoardLayer::BL_In29_Cu: return In29_Cu; + case board::types::BoardLayer::BL_In30_Cu: return In30_Cu; + case board::types::BoardLayer::BL_B_Cu: return B_Cu; + case board::types::BoardLayer::BL_B_Adhes: return B_Adhes; + case board::types::BoardLayer::BL_F_Adhes: return F_Adhes; + case board::types::BoardLayer::BL_B_Paste: return B_Paste; + case board::types::BoardLayer::BL_F_Paste: return F_Paste; + case board::types::BoardLayer::BL_B_SilkS: return B_SilkS; + case board::types::BoardLayer::BL_F_SilkS: return F_SilkS; + case board::types::BoardLayer::BL_B_Mask: return B_Mask; + case board::types::BoardLayer::BL_F_Mask: return F_Mask; + case board::types::BoardLayer::BL_Dwgs_User: return Dwgs_User; + case board::types::BoardLayer::BL_Cmts_User: return Cmts_User; + case board::types::BoardLayer::BL_Eco1_User: return Eco1_User; + case board::types::BoardLayer::BL_Eco2_User: return Eco2_User; + case board::types::BoardLayer::BL_Edge_Cuts: return Edge_Cuts; + case board::types::BoardLayer::BL_Margin: return Margin; + case board::types::BoardLayer::BL_B_CrtYd: return B_CrtYd; + case board::types::BoardLayer::BL_F_CrtYd: return F_CrtYd; + case board::types::BoardLayer::BL_B_Fab: return B_Fab; + case board::types::BoardLayer::BL_F_Fab: return F_Fab; + case board::types::BoardLayer::BL_User_1: return User_1; + case board::types::BoardLayer::BL_User_2: return User_2; + case board::types::BoardLayer::BL_User_3: return User_3; + case board::types::BoardLayer::BL_User_4: return User_4; + case board::types::BoardLayer::BL_User_5: return User_5; + case board::types::BoardLayer::BL_User_6: return User_6; + case board::types::BoardLayer::BL_User_7: return User_7; + case board::types::BoardLayer::BL_User_8: return User_8; + case board::types::BoardLayer::BL_User_9: return User_9; + + case board::types::BoardLayer::BL_UNKNOWN: return UNDEFINED_LAYER; + default: + wxCHECK_MSG( false, UNDEFINED_LAYER, + "Unhandled case in FromProtoEnum" ); + } +} + + +template<> +board::types::BoardLayer ToProtoEnum( PCB_LAYER_ID aValue ) +{ + switch( aValue ) + { + case UNDEFINED_LAYER: return board::types::BoardLayer::BL_UNDEFINED; + case UNSELECTED_LAYER: return board::types::BoardLayer::BL_UNSELECTED; + case F_Cu: return board::types::BoardLayer::BL_F_Cu; + case In1_Cu: return board::types::BoardLayer::BL_In1_Cu; + case In2_Cu: return board::types::BoardLayer::BL_In2_Cu; + case In3_Cu: return board::types::BoardLayer::BL_In3_Cu; + case In4_Cu: return board::types::BoardLayer::BL_In4_Cu; + case In5_Cu: return board::types::BoardLayer::BL_In5_Cu; + case In6_Cu: return board::types::BoardLayer::BL_In6_Cu; + case In7_Cu: return board::types::BoardLayer::BL_In7_Cu; + case In8_Cu: return board::types::BoardLayer::BL_In8_Cu; + case In9_Cu: return board::types::BoardLayer::BL_In9_Cu; + case In10_Cu: return board::types::BoardLayer::BL_In10_Cu; + case In11_Cu: return board::types::BoardLayer::BL_In11_Cu; + case In12_Cu: return board::types::BoardLayer::BL_In12_Cu; + case In13_Cu: return board::types::BoardLayer::BL_In13_Cu; + case In14_Cu: return board::types::BoardLayer::BL_In14_Cu; + case In15_Cu: return board::types::BoardLayer::BL_In15_Cu; + case In16_Cu: return board::types::BoardLayer::BL_In16_Cu; + case In17_Cu: return board::types::BoardLayer::BL_In17_Cu; + case In18_Cu: return board::types::BoardLayer::BL_In18_Cu; + case In19_Cu: return board::types::BoardLayer::BL_In19_Cu; + case In20_Cu: return board::types::BoardLayer::BL_In20_Cu; + case In21_Cu: return board::types::BoardLayer::BL_In21_Cu; + case In22_Cu: return board::types::BoardLayer::BL_In22_Cu; + case In23_Cu: return board::types::BoardLayer::BL_In23_Cu; + case In24_Cu: return board::types::BoardLayer::BL_In24_Cu; + case In25_Cu: return board::types::BoardLayer::BL_In25_Cu; + case In26_Cu: return board::types::BoardLayer::BL_In26_Cu; + case In27_Cu: return board::types::BoardLayer::BL_In27_Cu; + case In28_Cu: return board::types::BoardLayer::BL_In28_Cu; + case In29_Cu: return board::types::BoardLayer::BL_In29_Cu; + case In30_Cu: return board::types::BoardLayer::BL_In30_Cu; + case B_Cu: return board::types::BoardLayer::BL_B_Cu; + case B_Adhes: return board::types::BoardLayer::BL_B_Adhes; + case F_Adhes: return board::types::BoardLayer::BL_F_Adhes; + case B_Paste: return board::types::BoardLayer::BL_B_Paste; + case F_Paste: return board::types::BoardLayer::BL_F_Paste; + case B_SilkS: return board::types::BoardLayer::BL_B_SilkS; + case F_SilkS: return board::types::BoardLayer::BL_F_SilkS; + case B_Mask: return board::types::BoardLayer::BL_B_Mask; + case F_Mask: return board::types::BoardLayer::BL_F_Mask; + case Dwgs_User: return board::types::BoardLayer::BL_Dwgs_User; + case Cmts_User: return board::types::BoardLayer::BL_Cmts_User; + case Eco1_User: return board::types::BoardLayer::BL_Eco1_User; + case Eco2_User: return board::types::BoardLayer::BL_Eco2_User; + case Edge_Cuts: return board::types::BoardLayer::BL_Edge_Cuts; + case Margin: return board::types::BoardLayer::BL_Margin; + case B_CrtYd: return board::types::BoardLayer::BL_B_CrtYd; + case F_CrtYd: return board::types::BoardLayer::BL_F_CrtYd; + case B_Fab: return board::types::BoardLayer::BL_B_Fab; + case F_Fab: return board::types::BoardLayer::BL_F_Fab; + case User_1: return board::types::BoardLayer::BL_User_1; + case User_2: return board::types::BoardLayer::BL_User_2; + case User_3: return board::types::BoardLayer::BL_User_3; + case User_4: return board::types::BoardLayer::BL_User_4; + case User_5: return board::types::BoardLayer::BL_User_5; + case User_6: return board::types::BoardLayer::BL_User_6; + case User_7: return board::types::BoardLayer::BL_User_7; + case User_8: return board::types::BoardLayer::BL_User_8; + case User_9: return board::types::BoardLayer::BL_User_9; + default: + wxCHECK_MSG( false, board::types::BoardLayer::BL_UNKNOWN, + "Unhandled case in ToProtoEnum"); + } +} + + +template<> +SCH_LAYER_ID FromProtoEnum( schematic::types::SchematicLayer aValue ) +{ + switch( aValue ) + { + + default: + wxCHECK_MSG( false, SCH_LAYER_ID_START, + "Unhandled case in FromProtoEnum" ); + } +} + + +template<> +schematic::types::SchematicLayer ToProtoEnum( SCH_LAYER_ID aValue ) +{ + switch( aValue ) + { + + default: + wxCHECK_MSG( false, schematic::types::SchematicLayer::SL_UNKNOWN, + "Unhandled case in ToProtoEnum"); + } +} + + +template<> +GR_TEXT_H_ALIGN_T FromProtoEnum( types::HorizontalAlignment aValue ) +{ + switch( aValue ) + { + case types::HorizontalAlignment::HA_LEFT: return GR_TEXT_H_ALIGN_LEFT; + case types::HorizontalAlignment::HA_CENTER: return GR_TEXT_H_ALIGN_CENTER; + case types::HorizontalAlignment::HA_RIGHT: return GR_TEXT_H_ALIGN_RIGHT; + case types::HorizontalAlignment::HA_INDETERMINATE: return GR_TEXT_H_ALIGN_INDETERMINATE; + + case types::HorizontalAlignment::HA_UNKNOWN: return GR_TEXT_H_ALIGN_CENTER; + default: + wxCHECK_MSG( false, GR_TEXT_H_ALIGN_CENTER, + "Unhandled case in FromProtoEnum" ); + } +} + + +template<> +types::HorizontalAlignment ToProtoEnum( GR_TEXT_H_ALIGN_T aValue ) +{ + switch( aValue ) + { + case GR_TEXT_H_ALIGN_LEFT: return types::HorizontalAlignment::HA_LEFT; + case GR_TEXT_H_ALIGN_CENTER: return types::HorizontalAlignment::HA_CENTER; + case GR_TEXT_H_ALIGN_RIGHT: return types::HorizontalAlignment::HA_RIGHT; + case GR_TEXT_H_ALIGN_INDETERMINATE: return types::HorizontalAlignment::HA_INDETERMINATE; + default: + wxCHECK_MSG( false, types::HorizontalAlignment::HA_UNKNOWN, + "Unhandled case in ToProtoEnum"); + } +} + + +template<> +GR_TEXT_V_ALIGN_T FromProtoEnum( types::VerticalAlignment aValue ) +{ + switch( aValue ) + { + case types::VerticalAlignment::VA_TOP: return GR_TEXT_V_ALIGN_TOP; + case types::VerticalAlignment::VA_CENTER: return GR_TEXT_V_ALIGN_CENTER; + case types::VerticalAlignment::VA_BOTTOM: return GR_TEXT_V_ALIGN_BOTTOM; + case types::VerticalAlignment::VA_INDETERMINATE: return GR_TEXT_V_ALIGN_INDETERMINATE; + + case types::VerticalAlignment::VA_UNKNOWN: return GR_TEXT_V_ALIGN_CENTER; + default: + wxCHECK_MSG( false, GR_TEXT_V_ALIGN_CENTER, + "Unhandled case in FromProtoEnum" ); + } +} + + +template<> +types::VerticalAlignment ToProtoEnum( GR_TEXT_V_ALIGN_T aValue ) +{ + switch( aValue ) + { + case GR_TEXT_V_ALIGN_TOP: return types::VerticalAlignment::VA_TOP; + case GR_TEXT_V_ALIGN_CENTER: return types::VerticalAlignment::VA_CENTER; + case GR_TEXT_V_ALIGN_BOTTOM: return types::VerticalAlignment::VA_BOTTOM; + case GR_TEXT_V_ALIGN_INDETERMINATE: return types::VerticalAlignment::VA_INDETERMINATE; + default: + wxCHECK_MSG( false, types::VerticalAlignment::VA_UNKNOWN, + "Unhandled case in ToProtoEnum"); + } +} + + +template<> +LINE_STYLE FromProtoEnum( types::StrokeLineStyle aValue ) +{ + switch( aValue ) + { + case types::StrokeLineStyle::SLS_DEFAULT: return LINE_STYLE::DEFAULT; + case types::StrokeLineStyle::SLS_SOLID: return LINE_STYLE::SOLID; + case types::StrokeLineStyle::SLS_DASH: return LINE_STYLE::DASH; + case types::StrokeLineStyle::SLS_DOT: return LINE_STYLE::DOT; + case types::StrokeLineStyle::SLS_DASHDOT: return LINE_STYLE::DASHDOT; + case types::StrokeLineStyle::SLS_DASHDOTDOT: return LINE_STYLE::DASHDOTDOT; + case types::StrokeLineStyle::SLS_UNKNOWN: + default: + wxCHECK_MSG( false, LINE_STYLE::DEFAULT, + "Unhandled case in FromProtoEnum" ); + } +} + + +template<> +types::StrokeLineStyle ToProtoEnum( LINE_STYLE aValue ) +{ + switch( aValue ) + { + case LINE_STYLE::DEFAULT: return types::StrokeLineStyle::SLS_DEFAULT; + case LINE_STYLE::SOLID: return types::StrokeLineStyle::SLS_SOLID; + case LINE_STYLE::DASH: return types::StrokeLineStyle::SLS_DASH; + case LINE_STYLE::DOT: return types::StrokeLineStyle::SLS_DOT; + case LINE_STYLE::DASHDOT: return types::StrokeLineStyle::SLS_DASHDOT; + case LINE_STYLE::DASHDOTDOT: return types::StrokeLineStyle::SLS_DASHDOTDOT; + default: + wxCHECK_MSG( false, types::StrokeLineStyle::SLS_UNKNOWN, + "Unhandled case in ToProtoEnum"); + } +} diff --git a/common/api/api_handler.cpp b/common/api/api_handler.cpp index f1f1620a95..c772370759 100644 --- a/common/api/api_handler.cpp +++ b/common/api/api_handler.cpp @@ -19,10 +19,14 @@ */ #include +#include using kiapi::common::ApiRequest, kiapi::common::ApiResponse, kiapi::common::ApiResponseStatus; +const wxString API_HANDLER::m_defaultCommitMessage = _( "Modification from API" ); + + API_RESULT API_HANDLER::Handle( ApiRequest& aMsg ) { ApiResponseStatus status; @@ -55,20 +59,3 @@ API_RESULT API_HANDLER::Handle( ApiRequest& aMsg ) // This response is used internally; no need for an error message return tl::unexpected( status ); } - - -std::optional API_HANDLER::TypeNameFromAny( const google::protobuf::Any& aMessage ) -{ - static const std::map s_types = { - { "type.googleapis.com/kiapi.board.types.Track", PCB_TRACE_T }, - { "type.googleapis.com/kiapi.board.types.Arc", PCB_ARC_T }, - { "type.googleapis.com/kiapi.board.types.Via", PCB_VIA_T }, - }; - - auto it = s_types.find( aMessage.type_url() ); - - if( it != s_types.end() ) - return it->second; - - return std::nullopt; -} diff --git a/common/api/api_handler_common.cpp b/common/api/api_handler_common.cpp index 8ff39c6f58..58c157f187 100644 --- a/common/api/api_handler_common.cpp +++ b/common/api/api_handler_common.cpp @@ -22,7 +22,11 @@ #include #include +#include #include +#include +#include +#include #include using namespace kiapi::common::commands; @@ -34,10 +38,13 @@ API_HANDLER_COMMON::API_HANDLER_COMMON() : API_HANDLER() { registerHandler( &API_HANDLER_COMMON::handleGetVersion ); + registerHandler( &API_HANDLER_COMMON::handleGetNetClasses ); + registerHandler( &API_HANDLER_COMMON::handlePing ); } -HANDLER_RESULT API_HANDLER_COMMON::handleGetVersion( GetVersion& aMsg ) +HANDLER_RESULT API_HANDLER_COMMON::handleGetVersion( GetVersion&, + const HANDLER_CONTEXT& ) { GetVersionResponse reply; @@ -50,3 +57,26 @@ HANDLER_RESULT API_HANDLER_COMMON::handleGetVersion( GetVers return reply; } + + +HANDLER_RESULT API_HANDLER_COMMON::handleGetNetClasses( GetNetClasses& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + NetClassesResponse reply; + + std::shared_ptr& netSettings = + Pgm().GetSettingsManager().Prj().GetProjectFile().m_NetSettings; + + for( const auto& [name, netClass] : netSettings->m_NetClasses ) + { + reply.add_net_classes()->set_name( name.ToStdString() ); + } + + return reply; +} + + +HANDLER_RESULT API_HANDLER_COMMON::handlePing( Ping& aMsg, const HANDLER_CONTEXT& aCtx ) +{ + return Empty(); +} diff --git a/common/api/api_handler_editor.cpp b/common/api/api_handler_editor.cpp new file mode 100644 index 0000000000..58d5f64912 --- /dev/null +++ b/common/api/api_handler_editor.cpp @@ -0,0 +1,353 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include +#include +#include + +using namespace kiapi::common::commands; + + +API_HANDLER_EDITOR::API_HANDLER_EDITOR( EDA_BASE_FRAME* aFrame ) : + API_HANDLER(), + m_frame( aFrame ) +{ + registerHandler( &API_HANDLER_EDITOR::handleBeginCommit ); + registerHandler( &API_HANDLER_EDITOR::handleEndCommit ); + registerHandler( &API_HANDLER_EDITOR::handleCreateItems ); + registerHandler( &API_HANDLER_EDITOR::handleUpdateItems ); + registerHandler( &API_HANDLER_EDITOR::handleDeleteItems ); + registerHandler( &API_HANDLER_EDITOR::handleHitTest ); +} + + +HANDLER_RESULT API_HANDLER_EDITOR::handleBeginCommit( BeginCommit& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + if( m_commits.count( aCtx.ClientName ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "the client {} already has a commit in progress", + aCtx.ClientName ) ); + return tl::unexpected( e ); + } + + wxASSERT( !m_activeClients.count( aCtx.ClientName ) ); + + BeginCommitResponse response; + + KIID id; + m_commits[aCtx.ClientName] = std::make_pair( id, createCommit() ); + response.mutable_id()->set_value( id.AsStdString() ); + + m_activeClients.insert( aCtx.ClientName ); + + return response; +} + + +HANDLER_RESULT API_HANDLER_EDITOR::handleEndCommit( EndCommit& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + if( !m_commits.count( aCtx.ClientName ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "the client {} does not has a commit in progress", + aCtx.ClientName ) ); + return tl::unexpected( e ); + } + + wxASSERT( m_activeClients.count( aCtx.ClientName ) ); + + const std::pair>& pair = m_commits.at( aCtx.ClientName ); + const KIID& id = pair.first; + const std::unique_ptr& commit = pair.second; + + EndCommitResponse response; + + // Do not check IDs with drop; it is a safety net in case the id was lost on the client side + switch( aMsg.action() ) + { + case kiapi::common::commands::CMA_DROP: + { + commit->Revert(); + m_commits.erase( aCtx.ClientName ); + m_activeClients.erase( aCtx.ClientName ); + break; + } + + case kiapi::common::commands::CMA_COMMIT: + { + if( aMsg.id().value().compare( id.AsStdString() ) != 0 ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "the id {} does not match the commit in progress", + aMsg.id().value() ) ); + return tl::unexpected( e ); + } + + pushCurrentCommit( aCtx, wxString( aMsg.message().c_str(), wxConvUTF8 ) ); + break; + } + + default: + break; + } + + return response; +} + + +COMMIT* API_HANDLER_EDITOR::getCurrentCommit( const HANDLER_CONTEXT& aCtx ) +{ + if( !m_commits.count( aCtx.ClientName ) ) + { + KIID id; + m_commits[aCtx.ClientName] = std::make_pair( id, createCommit() ); + } + + return m_commits.at( aCtx.ClientName ).second.get(); +} + + +void API_HANDLER_EDITOR::pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage ) +{ + auto it = m_commits.find( aCtx.ClientName ); + + if( it == m_commits.end() ) + return; + + it->second.second->Push( aMessage.IsEmpty() ? m_defaultCommitMessage : aMessage ); + m_commits.erase( it ); + m_activeClients.erase( aCtx.ClientName ); +} + + +HANDLER_RESULT API_HANDLER_EDITOR::validateDocument( + const kiapi::common::types::DocumentSpecifier& aDocument ) +{ + if( !validateDocumentInternal( aDocument ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "the requested document {} is not open", + aDocument.board_filename() ) ); + return tl::unexpected( e ); + } + + return true; +} + + +HANDLER_RESULT> API_HANDLER_EDITOR::validateItemHeaderDocument( + const kiapi::common::types::ItemHeader& aHeader ) +{ + if( !aHeader.has_document() || aHeader.document().type() != thisDocumentType() ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_UNHANDLED ); + // No error message, this is a flag that the server should try a different handler + return tl::unexpected( e ); + } + + HANDLER_RESULT documentValidation = validateDocument( aHeader.document() ); + + if( !documentValidation ) + return tl::unexpected( documentValidation.error() ); + + if( !validateDocumentInternal( aHeader.document() ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "the requested document {} is not open", + aHeader.document().board_filename() ) ); + return tl::unexpected( e ); + } + + if( aHeader.has_container() ) + { + return KIID( aHeader.container().value() ); + } + + // Valid header, but no container provided + return std::nullopt; +} + + +std::optional API_HANDLER_EDITOR::checkForBusy() +{ + if( !m_frame->CanAcceptApiCommands() ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BUSY ); + e.set_error_message( "KiCad is busy and cannot respond to API requests right now" ); + return e; + } + + return std::nullopt; +} + + +HANDLER_RESULT API_HANDLER_EDITOR::handleCreateItems( CreateItems& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + CreateItemsResponse response; + + HANDLER_RESULT result = handleCreateUpdateItemsInternal( true, aCtx, + aMsg.header(), aMsg.items(), + [&]( const ItemStatus& aStatus, const google::protobuf::Any& aItem ) + { + ItemCreationResult itemResult; + itemResult.mutable_status()->CopyFrom( aStatus ); + itemResult.mutable_item()->CopyFrom( aItem ); + response.mutable_created_items()->Add( std::move( itemResult ) ); + } ); + + if( !result.has_value() ) + return tl::unexpected( result.error() ); + + response.set_status( *result ); + return response; +} + + +HANDLER_RESULT API_HANDLER_EDITOR::handleUpdateItems( UpdateItems& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + UpdateItemsResponse response; + + HANDLER_RESULT result = handleCreateUpdateItemsInternal( false, aCtx, + aMsg.header(), aMsg.items(), + [&]( const ItemStatus& aStatus, const google::protobuf::Any& aItem ) + { + ItemUpdateResult itemResult; + itemResult.mutable_status()->CopyFrom( aStatus ); + itemResult.mutable_item()->CopyFrom( aItem ); + response.mutable_updated_items()->Add( std::move( itemResult ) ); + } ); + + if( !result.has_value() ) + return tl::unexpected( result.error() ); + + response.set_status( *result ); + return response; +} + + +HANDLER_RESULT API_HANDLER_EDITOR::handleDeleteItems( DeleteItems& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + if( !validateItemHeaderDocument( aMsg.header() ) ) + { + ApiResponseStatus e; + // No message needed for AS_UNHANDLED; this is an internal flag for the API server + e.set_status( ApiStatusCode::AS_UNHANDLED ); + return tl::unexpected( e ); + } + + std::map itemsToDelete; + + for( const kiapi::common::types::KIID& kiidBuf : aMsg.item_ids() ) + { + if( !kiidBuf.value().empty() ) + { + KIID kiid( kiidBuf.value() ); + itemsToDelete[kiid] = ItemDeletionStatus::IDS_NONEXISTENT; + } + } + + if( itemsToDelete.empty() ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( "no valid items to delete were given" ); + return tl::unexpected( e ); + } + + deleteItemsInternal( itemsToDelete, aCtx ); + + DeleteItemsResponse response; + + for( const auto& [id, status] : itemsToDelete ) + { + ItemDeletionResult result; + result.mutable_id()->set_value( id.AsStdString() ); + result.set_status( status ); + } + + response.set_status( kiapi::common::types::ItemRequestStatus::IRS_OK ); + return response; +} + + +HANDLER_RESULT API_HANDLER_EDITOR::handleHitTest( HitTest& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + if( !validateItemHeaderDocument( aMsg.header() ) ) + { + ApiResponseStatus e; + // No message needed for AS_UNHANDLED; this is an internal flag for the API server + e.set_status( ApiStatusCode::AS_UNHANDLED ); + return tl::unexpected( e ); + } + + HitTestResponse response; + + std::optional item = getItemFromDocument( aMsg.header().document(), + KIID( aMsg.id().value() ) ); + + if( !item ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( "the requested item ID is not present in the given document" ); + return tl::unexpected( e ); + } + + if( ( *item )->HitTest( kiapi::common::UnpackVector2( aMsg.position() ), aMsg.tolerance() ) ) + response.set_result( HitTestResult::HTR_HIT ); + else + response.set_result( HitTestResult::HTR_NO_HIT ); + + return response; +} diff --git a/common/api/api_plugin.cpp b/common/api/api_plugin.cpp index a18f0a844b..6b4853acba 100644 --- a/common/api/api_plugin.cpp +++ b/common/api/api_plugin.cpp @@ -312,7 +312,8 @@ std::optional API_PLUGIN::createActionFromJson( const nlohmann::j } wxBitmap bmp; - bmp.LoadFile( iconFile.GetFullPath() ); + // TODO: If necessary; support types other than PNG + bmp.LoadFile( iconFile.GetFullPath(), wxBITMAP_TYPE_PNG ); if( bmp.IsOk() ) bitmaps.push_back( bmp ); diff --git a/common/api/api_plugin_manager.cpp b/common/api/api_plugin_manager.cpp index 4fd573ca67..14f71efcc9 100644 --- a/common/api/api_plugin_manager.cpp +++ b/common/api/api_plugin_manager.cpp @@ -303,10 +303,15 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent ) return; } + wxLogTrace( traceApi, wxString::Format( "Manager: begin processing; %zu jobs left in queue", + m_jobs.size() ) ); + JOB& job = m_jobs.front(); if( job.type == JOB_TYPE::CREATE_ENV ) { + wxLogTrace( traceApi, "Manager: Python exe '%s'", + Pgm().GetCommonSettings()->m_Api.python_interpreter ); wxLogTrace( traceApi, wxString::Format( "Manager: creating Python env at %s", job.env_path ) ); PYTHON_MANAGER manager( Pgm().GetCommonSettings()->m_Api.python_interpreter ); @@ -330,6 +335,7 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent ) wxLogTrace( traceApi, wxString::Format( "Manager: installing dependencies for %s", job.plugin_path ) ); + std::optional pythonHome = PYTHON_MANAGER::GetPythonEnvironment( job.identifier ); std::optional python = PYTHON_MANAGER::GetVirtualPython( job.identifier ); wxFileName reqs = wxFileName( job.plugin_path, "requirements.txt" ); @@ -337,28 +343,46 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent ) { wxLogTrace( traceApi, wxString::Format( "Manager: error: python not found at %s", job.env_path ) ); - wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, - wxID_ANY ); - QueueEvent( evt ); } else if( !reqs.IsFileReadable() ) { wxLogTrace( traceApi, wxString::Format( "Manager: error: requirements.txt not found at %s", job.plugin_path ) ); - wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, - wxID_ANY ); - QueueEvent( evt ); } else { - PYTHON_MANAGER manager( *python ); + wxLogTrace( traceApi, "Manager: Python exe '%s'", *python ); - wxString cmd = wxString::Format( + PYTHON_MANAGER manager( *python ); + wxExecuteEnv env; + + if( pythonHome ) + env.env[wxS( "VIRTUAL_ENV" )] = *pythonHome; + + wxString cmd = wxS( "-m ensurepip" ); + wxLogTrace( traceApi, "Manager: calling python `%s`", cmd ); + + manager.Execute( cmd, + [=]( int aRetVal, const wxString& aOutput, const wxString& aError ) + { + wxLogTrace( traceApi, wxString::Format( "Manager: ensurepip (%d): %s", + aRetVal, aOutput ) ); + + if( !aError.IsEmpty() ) + { + wxLogTrace( traceApi, + wxString::Format( "Manager: ensurepip err: %s", aError ) ); + } + }, &env ); + + cmd = wxString::Format( wxS( "-m pip install --no-input --isolated --require-virtualenv " "--exists-action i -r '%s'" ), reqs.GetFullPath() ); + wxLogTrace( traceApi, "Manager: calling python `%s`", cmd ); + manager.Execute( cmd, [=]( int aRetVal, const wxString& aOutput, const wxString& aError ) { @@ -382,10 +406,14 @@ void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent ) wxID_ANY ); QueueEvent( evt ); - } ); + }, &env ); } + + wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY ); + QueueEvent( evt ); } m_jobs.pop_front(); - wxLogTrace( traceApi, wxString::Format( "Manager: %zu jobs left in queue", m_jobs.size() ) ); + wxLogTrace( traceApi, wxString::Format( "Manager: done processing; %zu jobs left in queue", + m_jobs.size() ) ); } diff --git a/common/api/api_server.cpp b/common/api/api_server.cpp index d45437cbc5..9c7318f82e 100644 --- a/common/api/api_server.cpp +++ b/common/api/api_server.cpp @@ -95,9 +95,11 @@ KICAD_API_SERVER::KICAD_API_SERVER() : m_logFilePath.SetName( s_logFileName ); if( ADVANCED_CFG::GetCfg().m_EnableAPILogging ) + { PATHS::EnsurePathExists( PATHS::GetLogsPath() ); + log( fmt::format( "--- KiCad API server started at {} ---\n", SocketPath() ) ); + } - log( fmt::format( "--- KiCad API server started at {} ---\n", SocketPath() ) ); wxLogTrace( traceApi, wxString::Format( "Server: listening at %s", SocketPath() ) ); Bind( API_REQUEST_EVENT, &KICAD_API_SERVER::handleApiEvent, this ); @@ -168,10 +170,13 @@ void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent ) error.mutable_status()->set_status( ApiStatusCode::AS_BAD_REQUEST ); error.mutable_status()->set_error_message( "request could not be parsed" ); m_server->Reply( error.SerializeAsString() ); - log( "Response (ERROR): " + error.Utf8DebugString() ); + + if( ADVANCED_CFG::GetCfg().m_EnableAPILogging ) + log( "Response (ERROR): " + error.Utf8DebugString() ); } - log( "Request: " + request.Utf8DebugString() ); + if( ADVANCED_CFG::GetCfg().m_EnableAPILogging ) + log( "Request: " + request.Utf8DebugString() ); if( !request.header().kicad_token().empty() && request.header().kicad_token().compare( m_token ) != 0 ) @@ -182,7 +187,9 @@ void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent ) error.mutable_status()->set_error_message( "the provided kicad_token did not match this KiCad instance's token" ); m_server->Reply( error.SerializeAsString() ); - log( "Response (ERROR): " + error.Utf8DebugString() ); + + if( ADVANCED_CFG::GetCfg().m_EnableAPILogging ) + log( "Response (ERROR): " + error.Utf8DebugString() ); } API_RESULT result; @@ -203,7 +210,9 @@ void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent ) { result->mutable_header()->set_kicad_token( m_token ); m_server->Reply( result->SerializeAsString() ); - log( "Response: " + result->Utf8DebugString() ); + + if( ADVANCED_CFG::GetCfg().m_EnableAPILogging ) + log( "Response: " + result->Utf8DebugString() ); } else { @@ -220,16 +229,15 @@ void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent ) } m_server->Reply( error.SerializeAsString() ); - log( "Response (ERROR): " + error.Utf8DebugString() ); + + if( ADVANCED_CFG::GetCfg().m_EnableAPILogging ) + log( "Response (ERROR): " + error.Utf8DebugString() ); } } void KICAD_API_SERVER::log( const std::string& aOutput ) { - if( !ADVANCED_CFG::GetCfg().m_EnableAPILogging ) - return; - FILE* fp = wxFopen( m_logFilePath.GetFullPath(), wxT( "a" ) ); if( !fp ) diff --git a/common/api/api_utils.cpp b/common/api/api_utils.cpp new file mode 100644 index 0000000000..e25d575cab --- /dev/null +++ b/common/api/api_utils.cpp @@ -0,0 +1,80 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 Jon Evans + * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include + +namespace kiapi::common +{ + +std::optional TypeNameFromAny( const google::protobuf::Any& aMessage ) +{ + static const std::map s_types = { + { "type.googleapis.com/kiapi.board.types.Track", PCB_TRACE_T }, + { "type.googleapis.com/kiapi.board.types.Arc", PCB_ARC_T }, + { "type.googleapis.com/kiapi.board.types.Via", PCB_VIA_T }, + { "type.googleapis.com/kiapi.board.types.Text", PCB_TEXT_T }, + { "type.googleapis.com/kiapi.board.types.TextBox", PCB_TEXTBOX_T }, + { "type.googleapis.com/kiapi.board.types.GraphicShape", PCB_SHAPE_T }, + { "type.googleapis.com/kiapi.board.types.Pad", PCB_PAD_T }, + { "type.googleapis.com/kiapi.board.types.Zone", PCB_ZONE_T }, + { "type.googleapis.com/kiapi.board.types.Dimension", PCB_DIMENSION_T }, + { "type.googleapis.com/kiapi.board.types.ReferenceImage", PCB_REFERENCE_IMAGE_T }, + { "type.googleapis.com/kiapi.board.types.Group", PCB_GROUP_T }, + { "type.googleapis.com/kiapi.board.types.Field", PCB_FIELD_T }, + { "type.googleapis.com/kiapi.board.types.FootprintInstance", PCB_FOOTPRINT_T }, + }; + + auto it = s_types.find( aMessage.type_url() ); + + if( it != s_types.end() ) + return it->second; + + return std::nullopt; +} + + +LIB_ID LibIdFromProto( const types::LibraryIdentifier& aId ) +{ + return LIB_ID( aId.library_nickname(), aId.entry_name() ); +} + + +types::LibraryIdentifier LibIdToProto( const LIB_ID& aId ) +{ + types::LibraryIdentifier msg; + msg.set_library_nickname( aId.GetLibNickname() ); + msg.set_entry_name( aId.GetLibItemName() ); + return msg; +} + + +void PackVector2( kiapi::common::types::Vector2& aOutput, const VECTOR2I aInput ) +{ + aOutput.set_x_nm( aInput.x ); + aOutput.set_y_nm( aInput.y ); +} + +VECTOR2I UnpackVector2( const types::Vector2& aInput ) +{ + return VECTOR2I( aInput.x_nm(), aInput.y_nm() ); +} + +} // namespace kiapi::common diff --git a/common/dialogs/panel_plugin_settings.cpp b/common/dialogs/panel_plugin_settings.cpp index 137a2f7329..6a6179f7cb 100644 --- a/common/dialogs/panel_plugin_settings.cpp +++ b/common/dialogs/panel_plugin_settings.cpp @@ -97,6 +97,7 @@ void PANEL_PLUGIN_SETTINGS::OnEnableApiChecked( wxCommandEvent& aEvent ) void PANEL_PLUGIN_SETTINGS::updateApiStatusText() { +#ifdef KICAD_IPC_API if( m_cbEnableApi->GetValue() && Pgm().GetApiServer().Running() ) { m_stApiStatus->SetLabel( wxString::Format( _( "Listening at %s" ), @@ -106,6 +107,9 @@ void PANEL_PLUGIN_SETTINGS::updateApiStatusText() { m_stApiStatus->SetLabel( wxEmptyString ); } +#else + m_stApiStatus->SetLabel( _( "This installation of KiCad does not have API support enabled." ) ); +#endif } diff --git a/common/eda_draw_frame.cpp b/common/eda_draw_frame.cpp index a840a6714e..c9a63ceff9 100644 --- a/common/eda_draw_frame.cpp +++ b/common/eda_draw_frame.cpp @@ -23,6 +23,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -1343,3 +1345,45 @@ bool EDA_DRAW_FRAME::SaveCanvasImageToFile( const wxString& aFileName, image.Destroy(); return retv; } + + +void EDA_DRAW_FRAME::addApiPluginTools() +{ +#ifdef KICAD_IPC_API + // TODO: Add user control over visibility and order + API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager(); + + mgr.ButtonBindings().clear(); + + std::vector actions = mgr.GetActionsForScope( PluginActionScope() ); + + for( auto& action : actions ) + { + if( !action->show_button ) + continue; + + const wxBitmapBundle& icon = KIPLATFORM::UI::IsDarkTheme() && action->icon_dark.IsOk() + ? action->icon_dark + : action->icon_light; + + wxAuiToolBarItem* button = m_mainToolBar->AddTool( wxID_ANY, wxEmptyString, icon, + action->name ); + + Connect( button->GetId(), wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler( EDA_DRAW_FRAME::OnApiPluginInvoke ) ); + + mgr.ButtonBindings().insert( { button->GetId(), action->identifier } ); + } +#endif +} + + +void EDA_DRAW_FRAME::OnApiPluginInvoke( wxCommandEvent& aEvent ) +{ +#ifdef KICAD_IPC_API + API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager(); + + if( mgr.ButtonBindings().count( aEvent.GetId() ) ) + mgr.InvokeAction( mgr.ButtonBindings().at( aEvent.GetId() ) ); +#endif +} diff --git a/common/eda_shape.cpp b/common/eda_shape.cpp index 8aae5bad4e..784c515556 100644 --- a/common/eda_shape.cpp +++ b/common/eda_shape.cpp @@ -94,6 +94,7 @@ wxString EDA_SHAPE::SHAPE_T_asString() const case SHAPE_T::CIRCLE: return wxS( "S_CIRCLE" ); case SHAPE_T::POLY: return wxS( "S_POLYGON" ); case SHAPE_T::BEZIER: return wxS( "S_CURVE" ); + case SHAPE_T::UNDEFINED: return wxS( "UNDEFINED" ); } return wxEmptyString; // Just to quiet GCC. @@ -243,12 +244,14 @@ void EDA_SHAPE::move( const VECTOR2I& aMoveVector ) switch ( m_shape ) { case SHAPE_T::ARC: + m_arcCenter += aMoveVector; + KI_FALLTHROUGH; + case SHAPE_T::SEGMENT: case SHAPE_T::RECTANGLE: case SHAPE_T::CIRCLE: m_start += aMoveVector; m_end += aMoveVector; - m_arcCenter += aMoveVector; break; case SHAPE_T::POLY: @@ -284,11 +287,13 @@ void EDA_SHAPE::scale( double aScale ) switch( m_shape ) { case SHAPE_T::ARC: + scalePt( m_arcCenter ); + KI_FALLTHROUGH; + case SHAPE_T::SEGMENT: case SHAPE_T::RECTANGLE: scalePt( m_start ); scalePt( m_end ); - scalePt( m_arcCenter ); break; case SHAPE_T::CIRCLE: // ring or circle diff --git a/common/pgm_base.cpp b/common/pgm_base.cpp index 5347753b47..1282dfc860 100644 --- a/common/pgm_base.cpp +++ b/common/pgm_base.cpp @@ -558,7 +558,10 @@ bool PGM_BASE::InitPgm( bool aHeadless, bool aSkipPyInit, bool aIsUnitTest ) m_settings_manager = std::make_unique( aHeadless ); m_background_jobs_monitor = std::make_unique(); m_notifications_manager = std::make_unique(); + +#ifdef KICAD_IPC_API m_plugin_manager = std::make_unique( &App() ); +#endif // Our unit test mocks break if we continue // A bug caused InitPgm to terminate early in unit tests and the mocks are...simplistic @@ -613,7 +616,9 @@ bool PGM_BASE::InitPgm( bool aHeadless, bool aSkipPyInit, bool aIsUnitTest ) // Need to create a project early for now (it can have an empty path for the moment) GetSettingsManager().LoadProject( "" ); +#ifdef KICAD_IPC_API m_plugin_manager->ReloadPlugins(); +#endif // This sets the maximum tooltip display duration to 10s (up from 5) but only affects // Windows as other platforms display tooltips while the mouse is not moving diff --git a/common/tool/actions.cpp b/common/tool/actions.cpp index bbb32aca68..a74717c9f3 100644 --- a/common/tool/actions.cpp +++ b/common/tool/actions.cpp @@ -1143,6 +1143,15 @@ TOOL_ACTION ACTIONS::ddAddLibrary( TOOL_ACTION_ARGS() .Name( "common.Control.ddaddLibrary" ) .Scope( AS_GLOBAL ) ); +// API + +TOOL_ACTION ACTIONS::pluginsReload( TOOL_ACTION_ARGS() + .Name( "common.API.pluginsReload" ) + .Scope( AS_GLOBAL ) + .FriendlyName( _( "Refresh Plugins" ) ) + .Tooltip( _( "Reload all python plugins and refresh plugin menus" ) ) + .Icon( BITMAPS::reload ) ); + // System-wide selection Events const TOOL_EVENT EVENTS::PointSelectedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.pointSelected" ); diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 0cbc750d36..b61db91bc7 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -422,6 +422,8 @@ set( EESCHEMA_SRCS toolbars_sch_editor.cpp toolbars_symbol_viewer.cpp + api/api_sch_utils.cpp + netlist_exporters/netlist_exporter_allegro.cpp netlist_exporters/netlist_exporter_base.cpp netlist_exporters/netlist_exporter_cadstar.cpp @@ -457,6 +459,13 @@ set( EESCHEMA_SRCS tools/symbol_editor_pin_tool.cpp ) +if( KICAD_IPC_API ) + set( EESCHEMA_SRCS + ${EESCHEMA_SRCS} + api/api_handler_sch.cpp + ) +endif() + if( WIN32 ) if( MINGW ) # EESCHEMA_RESOURCES variable is set by the macro. diff --git a/eeschema/api/api_handler_sch.cpp b/eeschema/api/api_handler_sch.cpp new file mode 100644 index 0000000000..c4a436dcaa --- /dev/null +++ b/eeschema/api/api_handler_sch.cpp @@ -0,0 +1,305 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace kiapi::common::commands; +using kiapi::common::types::CommandStatus; +using kiapi::common::types::DocumentType; +using kiapi::common::types::ItemRequestStatus; + + +API_HANDLER_SCH::API_HANDLER_SCH( SCH_EDIT_FRAME* aFrame ) : + API_HANDLER_EDITOR(), + m_frame( aFrame ) +{ + registerHandler( + &API_HANDLER_SCH::handleGetOpenDocuments ); +} + + +std::unique_ptr API_HANDLER_SCH::createCommit() +{ + return std::make_unique( m_frame ); +} + + +bool API_HANDLER_SCH::validateDocumentInternal( const DocumentSpecifier& aDocument ) const +{ + if( aDocument.type() != DocumentType::DOCTYPE_SCHEMATIC ) + return false; + + // TODO(JE) need serdes for SCH_SHEET_PATH <> SheetPath + return true; + //wxString currentPath = m_frame->GetCurrentSheet().PathAsString(); + //return 0 == aDocument.sheet_path().compare( currentPath.ToStdString() ); +} + + +HANDLER_RESULT API_HANDLER_SCH::handleGetOpenDocuments( + GetOpenDocuments& aMsg, const HANDLER_CONTEXT& ) +{ + if( aMsg.type() != DocumentType::DOCTYPE_SCHEMATIC ) + { + ApiResponseStatus e; + // No message needed for AS_UNHANDLED; this is an internal flag for the API server + e.set_status( ApiStatusCode::AS_UNHANDLED ); + return tl::unexpected( e ); + } + + GetOpenDocumentsResponse response; + common::types::DocumentSpecifier doc; + + wxFileName fn( m_frame->GetCurrentFileName() ); + + doc.set_type( DocumentType::DOCTYPE_SCHEMATIC ); + doc.set_board_filename( fn.GetFullName() ); + + response.mutable_documents()->Add( std::move( doc ) ); + return response; +} + + +HANDLER_RESULT> API_HANDLER_SCH::createItemForType( KICAD_T aType, + EDA_ITEM* aContainer ) +{ + if( !aContainer ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( "Tried to create an item in a null container" ); + return tl::unexpected( e ); + } + + if( aType == SCH_PIN_T && !dynamic_cast( aContainer ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "Tried to create a pin in {}, which is not a symbol", + aContainer->GetFriendlyName().ToStdString() ) ); + return tl::unexpected( e ); + } + else if( aType == SCH_SYMBOL_T && !dynamic_cast( aContainer ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "Tried to create a symbol in {}, which is not a schematic", + aContainer->GetFriendlyName().ToStdString() ) ); + return tl::unexpected( e ); + } + + std::unique_ptr created = CreateItemForType( aType, aContainer ); + + if( !created ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled", + magic_enum::enum_name( aType ) ) ); + return tl::unexpected( e ); + } + + return created; +} + + +HANDLER_RESULT API_HANDLER_SCH::handleCreateUpdateItemsInternal( bool aCreate, + const HANDLER_CONTEXT& aCtx, + const types::ItemHeader &aHeader, + const google::protobuf::RepeatedPtrField& aItems, + std::function aItemHandler ) +{ + ApiResponseStatus e; + + auto containerResult = validateItemHeaderDocument( aHeader ); + + if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED ) + { + // No message needed for AS_UNHANDLED; this is an internal flag for the API server + e.set_status( ApiStatusCode::AS_UNHANDLED ); + return tl::unexpected( e ); + } + else if( !containerResult ) + { + e.CopyFrom( containerResult.error() ); + return tl::unexpected( e ); + } + + SCH_SCREEN* screen = m_frame->GetScreen(); + EE_RTREE& screenItems = screen->Items(); + + std::map itemUuidMap; + + std::for_each( screenItems.begin(), screenItems.end(), + [&]( EDA_ITEM* aItem ) + { + itemUuidMap[aItem->m_Uuid] = aItem; + } ); + + EDA_ITEM* container = nullptr; + + if( containerResult->has_value() ) + { + const KIID& containerId = **containerResult; + + if( itemUuidMap.count( containerId ) ) + { + container = itemUuidMap.at( containerId ); + + if( !container ) + { + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( + "The requested container {} is not a valid schematic item container", + containerId.AsStdString() ) ); + return tl::unexpected( e ); + } + } + else + { + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( + "The requested container {} does not exist in this document", + containerId.AsStdString() ) ); + return tl::unexpected( e ); + } + } + + COMMIT* commit = getCurrentCommit( aCtx ); + + for( const google::protobuf::Any& anyItem : aItems ) + { + ItemStatus status; + std::optional type = TypeNameFromAny( anyItem ); + + if( !type ) + { + status.set_code( ItemStatusCode::ISC_INVALID_TYPE ); + status.set_error_message( fmt::format( "Could not decode a valid type from {}", + anyItem.type_url() ) ); + aItemHandler( status, anyItem ); + continue; + } + + HANDLER_RESULT> creationResult = + createItemForType( *type, container ); + + if( !creationResult ) + { + status.set_code( ItemStatusCode::ISC_INVALID_TYPE ); + status.set_error_message( creationResult.error().error_message() ); + aItemHandler( status, anyItem ); + continue; + } + + std::unique_ptr item( std::move( *creationResult ) ); + + if( !item->Deserialize( anyItem ) ) + { + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "could not unpack {} from request", + item->GetClass().ToStdString() ) ); + return tl::unexpected( e ); + } + + if( aCreate && itemUuidMap.count( item->m_Uuid ) ) + { + status.set_code( ItemStatusCode::ISC_EXISTING ); + status.set_error_message( fmt::format( "an item with UUID {} already exists", + item->m_Uuid.AsStdString() ) ); + aItemHandler( status, anyItem ); + continue; + } + else if( !aCreate && !itemUuidMap.count( item->m_Uuid ) ) + { + status.set_code( ItemStatusCode::ISC_NONEXISTENT ); + status.set_error_message( fmt::format( "an item with UUID {} does not exist", + item->m_Uuid.AsStdString() ) ); + aItemHandler( status, anyItem ); + continue; + } + + status.set_code( ItemStatusCode::ISC_OK ); + google::protobuf::Any newItem; + + if( aCreate ) + { + item->Serialize( newItem ); + commit->Add( item.release() ); + + if( !m_activeClients.count( aCtx.ClientName ) ) + pushCurrentCommit( aCtx, _( "Added items via API" ) ); + } + else + { + EDA_ITEM* edaItem = itemUuidMap[item->m_Uuid]; + + if( SCH_ITEM* schItem = dynamic_cast( edaItem ) ) + { + schItem->SwapData( static_cast( item.get() ) ); + schItem->Serialize( newItem ); + commit->Modify( schItem ); + } + else if( LIB_ITEM* libItem = dynamic_cast( edaItem ) ) + { + // TODO: there is not currently a way to do this, haha + } + else + { + wxASSERT( false ); + } + + if( !m_activeClients.count( aCtx.ClientName ) ) + pushCurrentCommit( aCtx, _( "Created items via API" ) ); + } + + aItemHandler( status, newItem ); + } + + + return ItemRequestStatus::IRS_OK; +} + + +void API_HANDLER_SCH::deleteItemsInternal( std::map& aItemsToDelete, + const HANDLER_CONTEXT& aCtx ) +{ + // TODO +} + + +std::optional API_HANDLER_SCH::getItemFromDocument( const DocumentSpecifier& aDocument, + const KIID& aId ) +{ + if( !validateDocument( aDocument ) ) + return std::nullopt; + + // TODO + + return std::nullopt; +} diff --git a/eeschema/api/api_handler_sch.h b/eeschema/api/api_handler_sch.h new file mode 100644 index 0000000000..63e79c5a6f --- /dev/null +++ b/eeschema/api/api_handler_sch.h @@ -0,0 +1,74 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#ifndef KICAD_API_HANDLER_SCH_H +#define KICAD_API_HANDLER_SCH_H + +#include +#include +#include + +using namespace kiapi; +using namespace kiapi::common; + +class SCH_EDIT_FRAME; +class SCH_ITEM; + + +class API_HANDLER_SCH : public API_HANDLER_EDITOR +{ +public: + API_HANDLER_SCH( SCH_EDIT_FRAME* aFrame ); + +protected: + std::unique_ptr createCommit() override; + + kiapi::common::types::DocumentType thisDocumentType() const override + { + return kiapi::common::types::DOCTYPE_SCHEMATIC; + } + + bool validateDocumentInternal( const DocumentSpecifier& aDocument ) const override; + + HANDLER_RESULT> createItemForType( KICAD_T aType, + EDA_ITEM* aContainer ); + + HANDLER_RESULT handleCreateUpdateItemsInternal( bool aCreate, + const HANDLER_CONTEXT& aCtx, + const types::ItemHeader &aHeader, + const google::protobuf::RepeatedPtrField& aItems, + std::function aItemHandler ) + override; + + void deleteItemsInternal( std::map& aItemsToDelete, + const HANDLER_CONTEXT& aCtx ) override; + + std::optional getItemFromDocument( const DocumentSpecifier& aDocument, + const KIID& aId ) override; + +private: + HANDLER_RESULT handleGetOpenDocuments( + commands::GetOpenDocuments& aMsg, const HANDLER_CONTEXT& aCtx ); + + SCH_EDIT_FRAME* m_frame; +}; + + +#endif //KICAD_API_HANDLER_SCH_H diff --git a/eeschema/api/api_sch_utils.cpp b/eeschema/api/api_sch_utils.cpp new file mode 100644 index 0000000000..6907adcd60 --- /dev/null +++ b/eeschema/api/api_sch_utils.cpp @@ -0,0 +1,107 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api_sch_utils.h" + + +std::unique_ptr CreateItemForType( KICAD_T aType, EDA_ITEM* aContainer ) +{ + SCH_ITEM* parentSchItem = dynamic_cast( aContainer ); + LIB_SYMBOL* parentLibSymbol = nullptr; + + if( aContainer && aContainer->Type() == LIB_SYMBOL_T ) + parentLibSymbol = static_cast( aContainer ); + + switch( aType ) + { + case SCH_JUNCTION_T: return std::make_unique(); + case SCH_NO_CONNECT_T: return std::make_unique(); + case SCH_BUS_WIRE_ENTRY_T: return std::make_unique(); + case SCH_BUS_BUS_ENTRY_T: return std::make_unique(); + case SCH_LINE_T: return std::make_unique(); + case SCH_SHAPE_T: return std::make_unique(); + case SCH_BITMAP_T: return std::make_unique(); + case SCH_TEXTBOX_T: return std::make_unique(); + case SCH_TEXT_T: return std::make_unique(); + case SCH_TABLE_T: return std::make_unique(); + case SCH_TABLECELL_T: return std::make_unique(); + case SCH_LABEL_T: return std::make_unique(); + case SCH_GLOBAL_LABEL_T: return std::make_unique(); + case SCH_HIER_LABEL_T: return std::make_unique(); + case SCH_DIRECTIVE_LABEL_T: return std::make_unique(); + case SCH_FIELD_T: return std::make_unique( parentSchItem ); + + case SCH_SYMBOL_T: + { + // TODO: constructing currently requires more than just a "container" LIB_SYMBOL + return nullptr; + } + + case SCH_SHEET_PIN_T: + { + if( aContainer && aContainer->Type() == SCH_SHEET_T ) + return std::make_unique( static_cast( aContainer ) ); + + return nullptr; + } + + case SCH_SHEET_T: return std::make_unique(); + + case SCH_PIN_T: + { + // TODO: constructing currently requires LIB_PIN and SCH_SYMBOL ptr, + // or SCH_SYMBOL and number+alt. Need to determine ideal default ctor. + return nullptr; + } + + case LIB_SYMBOL_T: return nullptr; // TODO: ctor currently requires non-null name + case LIB_SHAPE_T: return std::make_unique( parentLibSymbol ); + case LIB_TEXT_T: return std::make_unique( parentLibSymbol ); + case LIB_TEXTBOX_T: return std::make_unique( parentLibSymbol ); + case LIB_PIN_T: return std::make_unique( parentLibSymbol ); + case LIB_FIELD_T: return std::make_unique( parentLibSymbol ); + + default: + return nullptr; + } +} diff --git a/eeschema/api/api_sch_utils.h b/eeschema/api/api_sch_utils.h new file mode 100644 index 0000000000..b1efbba257 --- /dev/null +++ b/eeschema/api/api_sch_utils.h @@ -0,0 +1,31 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#ifndef KICAD_API_SCH_UTILS_H +#define KICAD_API_SCH_UTILS_H + +#include +#include + +class EDA_ITEM; + +std::unique_ptr CreateItemForType( KICAD_T aType, EDA_ITEM* aContainer ); + +#endif //KICAD_API_SCH_UTILS_H diff --git a/eeschema/lib_shape.h b/eeschema/lib_shape.h index d7b38889ac..d8001333a8 100644 --- a/eeschema/lib_shape.h +++ b/eeschema/lib_shape.h @@ -32,7 +32,7 @@ class LIB_SHAPE : public LIB_ITEM, public EDA_SHAPE { public: - LIB_SHAPE( LIB_SYMBOL* aParent, SHAPE_T aShape, int aLineWidth = 0, + LIB_SHAPE( LIB_SYMBOL* aParent, SHAPE_T aShape = SHAPE_T::UNDEFINED, int aLineWidth = 0, FILL_T aFillType = FILL_T::NO_FILL, KICAD_T aType = LIB_SHAPE_T ); // Do not create a copy constructor. The one generated by the compiler is adequate. diff --git a/eeschema/menubar.cpp b/eeschema/menubar.cpp index abade27b4f..554d08d5a8 100644 --- a/eeschema/menubar.cpp +++ b/eeschema/menubar.cpp @@ -326,6 +326,11 @@ void SCH_EDIT_FRAME::doReCreateMenuBar() update = toolsMenu->Add( ACTIONS::updateSchematicFromPcb ); update->Enable( !Kiface().IsSingle() ); +#ifdef KICAD_IPC_API + toolsMenu->AppendSeparator(); + toolsMenu->Add( ACTIONS::pluginsReload ); +#endif + //-- Preferences menu ----------------------------------------------- // ACTION_MENU* prefsMenu = new ACTION_MENU( false, selTool ); diff --git a/eeschema/sch_commit.h b/eeschema/sch_commit.h index 2065f0ed33..3aff7f5686 100644 --- a/eeschema/sch_commit.h +++ b/eeschema/sch_commit.h @@ -31,6 +31,7 @@ class PICKED_ITEMS_LIST; class TOOL_MANAGER; class SCH_EDIT_FRAME; +class SCH_BASE_FRAME; class EDA_DRAW_FRAME; class TOOL_BASE; diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index b01a6879b2..192f631cc9 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -22,6 +22,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include #include #include #include @@ -97,6 +99,10 @@ #include #include +#ifdef KICAD_IPC_API +#include +#endif + #define DIFF_SYMBOLS_DIALOG_NAME wxT( "DiffSymbolsDialog" ) @@ -177,6 +183,16 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : ReCreateVToolbar(); ReCreateOptToolbar(); +#ifdef KICAD_IPC_API + wxTheApp->Bind( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED, + [&]( wxCommandEvent& aEvt ) + { + wxLogTrace( traceApi, "SCH frame: EDA_EVT_PLUGIN_AVAILABILITY_CHANGED" ); + ReCreateHToolbar(); + aEvt.Skip(); + } ); +#endif + m_hierarchy = new HIERARCHY_PANE( this ); // Initialize common print setup dialog settings. @@ -380,6 +396,11 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : updateTitle(); m_toolManager->GetTool()->ResetHistory(); +#ifdef KICAD_IPC_API + m_apiHandler = std::make_unique( this ); + Pgm().GetApiServer().RegisterHandler( m_apiHandler.get() ); +#endif + // Default shutdown reason until a file is loaded KIPLATFORM::APP::SetShutdownBlockReason( this, _( "New schematic file is unsaved" ) ); diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index 5217b56413..723a3909a0 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -62,6 +62,7 @@ class DIALOG_SYMBOL_FIELDS_TABLE; class DIALOG_SCH_FIND; class RESCUER; class HIERARCHY_PANE; +class API_HANDLER_SCH; /// Schematic search type used by the socket link with Pcbnew @@ -872,6 +873,11 @@ public: void ToggleNetNavigator(); + PLUGIN_ACTION_SCOPE PluginActionScope() const override + { + return PLUGIN_ACTION_SCOPE::SCHEMATIC; + } + DECLARE_EVENT_TABLE() protected: @@ -1016,6 +1022,10 @@ private: bool m_highlightedConnChanged; std::vector m_schematicChangeListeners; + +#ifdef KICAD_IPC_API + std::unique_ptr m_apiHandler; +#endif }; diff --git a/eeschema/sch_field.h b/eeschema/sch_field.h index 967c836713..e2f6b95727 100644 --- a/eeschema/sch_field.h +++ b/eeschema/sch_field.h @@ -54,7 +54,8 @@ public: SCH_FIELD( const VECTOR2I& aPos, int aFieldId, SCH_ITEM* aParent, const wxString& aName = wxEmptyString ); - SCH_FIELD( SCH_ITEM* aParent, int aFieldId, const wxString& aName = wxEmptyString ); + SCH_FIELD( SCH_ITEM* aParent, int aFieldId = INVALID_FIELD, + const wxString& aName = wxEmptyString ); SCH_FIELD( const SCH_FIELD& aText ); diff --git a/eeschema/sch_label.cpp b/eeschema/sch_label.cpp index b69718a1fe..1fd893352a 100644 --- a/eeschema/sch_label.cpp +++ b/eeschema/sch_label.cpp @@ -43,6 +43,9 @@ #include #include #include +#include +#include +#include using KIGFX::SCH_RENDER_SETTINGS; @@ -1470,6 +1473,31 @@ SCH_LABEL::SCH_LABEL( const VECTOR2I& pos, const wxString& text ) : } +void SCH_LABEL::Serialize( google::protobuf::Any &aContainer ) const +{ + kiapi::schematic::types::LocalLabel label; + + label.mutable_id()->set_value( m_Uuid.AsStdString() ); + kiapi::common::PackVector2( *label.mutable_position(), GetPosition() ); + + aContainer.PackFrom( label ); +} + + +bool SCH_LABEL::Deserialize( const google::protobuf::Any &aContainer ) +{ + kiapi::schematic::types::LocalLabel label; + + if( !aContainer.UnpackTo( &label ) ) + return false; + + const_cast( m_Uuid ) = KIID( label.id().value() ); + SetPosition( kiapi::common::UnpackVector2( label.position() ) ); + + return true; +} + + const BOX2I SCH_LABEL::GetBodyBoundingBox() const { BOX2I rect = GetTextBox(); @@ -1542,6 +1570,19 @@ SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const SCH_DIRECTIVE_LABEL& aClassLabel } +void SCH_DIRECTIVE_LABEL::Serialize( google::protobuf::Any &aContainer ) const +{ + // TODO +} + + +bool SCH_DIRECTIVE_LABEL::Deserialize( const google::protobuf::Any &aContainer ) +{ + // TODO + return false; +} + + int SCH_DIRECTIVE_LABEL::GetPenWidth() const { int pen = 0; @@ -1791,6 +1832,19 @@ SCH_GLOBALLABEL::SCH_GLOBALLABEL( const SCH_GLOBALLABEL& aGlobalLabel ) : } +void SCH_GLOBALLABEL::Serialize( google::protobuf::Any &aContainer ) const +{ + // TODO +} + + +bool SCH_GLOBALLABEL::Deserialize( const google::protobuf::Any &aContainer ) +{ + // TODO + return false; +} + + VECTOR2I SCH_GLOBALLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const { int horiz = GetLabelBoxExpansion( aSettings ); @@ -1991,6 +2045,19 @@ SCH_HIERLABEL::SCH_HIERLABEL( const VECTOR2I& pos, const wxString& text, KICAD_T } +void SCH_HIERLABEL::Serialize( google::protobuf::Any &aContainer ) const +{ + // TODO +} + + +bool SCH_HIERLABEL::Deserialize( const google::protobuf::Any &aContainer ) +{ + // TODO + return false; +} + + void SCH_HIERLABEL::SetSpinStyle( SPIN_STYLE aSpinStyle ) { SCH_LABEL_BASE::SetSpinStyle( aSpinStyle ); diff --git a/eeschema/sch_label.h b/eeschema/sch_label.h index 9bd052f1f3..14cca3783c 100644 --- a/eeschema/sch_label.h +++ b/eeschema/sch_label.h @@ -377,6 +377,9 @@ public: ~SCH_LABEL() { } + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + static inline bool ClassOf( const EDA_ITEM* aItem ) { return aItem && SCH_LABEL_T == aItem->Type(); @@ -426,6 +429,9 @@ public: ~SCH_DIRECTIVE_LABEL() { } + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + static inline bool ClassOf( const EDA_ITEM* aItem ) { return aItem && SCH_DIRECTIVE_LABEL_T == aItem->Type(); @@ -483,6 +489,9 @@ public: ~SCH_GLOBALLABEL() { } + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + static inline bool ClassOf( const EDA_ITEM* aItem ) { return aItem && SCH_GLOBAL_LABEL_T == aItem->Type(); @@ -542,6 +551,9 @@ public: ~SCH_HIERLABEL() { } + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + static inline bool ClassOf( const EDA_ITEM* aItem ) { return aItem && SCH_HIER_LABEL_T == aItem->Type(); diff --git a/eeschema/sch_line.cpp b/eeschema/sch_line.cpp index efdd9fb777..e5c884c732 100644 --- a/eeschema/sch_line.cpp +++ b/eeschema/sch_line.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include SCH_LINE::SCH_LINE( const VECTOR2I& pos, int layer ) : @@ -90,6 +93,49 @@ SCH_LINE::SCH_LINE( const SCH_LINE& aLine ) : } +void SCH_LINE::Serialize( google::protobuf::Any &aContainer ) const +{ + kiapi::schematic::types::Line line; + + line.mutable_id()->set_value( m_Uuid.AsStdString() ); + kiapi::common::PackVector2( *line.mutable_start(), GetStartPoint() ); + kiapi::common::PackVector2( *line.mutable_end(), GetEndPoint() ); + line.set_layer( + ToProtoEnum( GetLayer() ) ); + + aContainer.PackFrom( line ); +} + + +bool SCH_LINE::Deserialize( const google::protobuf::Any &aContainer ) +{ + kiapi::schematic::types::Line line; + + if( !aContainer.UnpackTo( &line ) ) + return false; + + const_cast( m_Uuid ) = KIID( line.id().value() ); + SetStartPoint( kiapi::common::UnpackVector2( line.start() ) ); + SetEndPoint( kiapi::common::UnpackVector2( line.end() ) ); + SCH_LAYER_ID layer = + FromProtoEnum( line.layer() ); + + switch( layer ) + { + case LAYER_WIRE: + case LAYER_BUS: + case LAYER_NOTES: + SetLayer( layer ); + break; + + default: + break; + } + + return true; +} + + wxString SCH_LINE::GetFriendlyName() const { switch( GetLayer() ) diff --git a/eeschema/sch_line.h b/eeschema/sch_line.h index 8c44cf46be..ba1573a1a1 100644 --- a/eeschema/sch_line.h +++ b/eeschema/sch_line.h @@ -51,6 +51,9 @@ public: ~SCH_LINE() { } + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + static inline bool ClassOf( const EDA_ITEM* aItem ) { return aItem && SCH_LINE_T == aItem->Type(); diff --git a/eeschema/sch_shape.h b/eeschema/sch_shape.h index 92e313a9c8..2e33ca769d 100644 --- a/eeschema/sch_shape.h +++ b/eeschema/sch_shape.h @@ -32,7 +32,8 @@ class SCH_SHAPE : public SCH_ITEM, public EDA_SHAPE { public: - SCH_SHAPE( SHAPE_T aShape, int aLineWidth = 0, FILL_T aFillType = FILL_T::NO_FILL, + SCH_SHAPE( SHAPE_T aShape = SHAPE_T::UNDEFINED, int aLineWidth = 0, + FILL_T aFillType = FILL_T::NO_FILL, KICAD_T aType = SCH_SHAPE_T ); // Do not create a copy constructor. The one generated by the compiler is adequate. diff --git a/eeschema/sch_table.h b/eeschema/sch_table.h index 00ebac8472..89cf0e2507 100644 --- a/eeschema/sch_table.h +++ b/eeschema/sch_table.h @@ -32,7 +32,7 @@ class SCH_TABLE : public SCH_ITEM { public: - SCH_TABLE( int aLineWidth ); + SCH_TABLE( int aLineWidth = 0 ); SCH_TABLE( const SCH_TABLE& aTable ); diff --git a/eeschema/toolbars_sch_editor.cpp b/eeschema/toolbars_sch_editor.cpp index 2045f766bc..e5df50add3 100644 --- a/eeschema/toolbars_sch_editor.cpp +++ b/eeschema/toolbars_sch_editor.cpp @@ -25,11 +25,13 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include @@ -119,11 +121,24 @@ void SCH_EDIT_FRAME::ReCreateHToolbar() m_mainToolBar->AddScaledSeparator( this ); m_mainToolBar->Add( EE_ACTIONS::showPcbNew ); - // Access to the scripting console - if( SCRIPTING::IsWxAvailable() ) + // Add scripting console and API plugins + bool scriptingAvailable = SCRIPTING::IsWxAvailable(); +#ifdef KICAD_IPC_API + bool haveApiPlugins = Pgm().GetCommonSettings()->m_Api.enable_server && + !Pgm().GetPluginManager().GetActionsForScope( PLUGIN_ACTION_SCOPE::SCHEMATIC ).empty(); +#else + bool haveApiPlugins = false; +#endif + + if( scriptingAvailable || haveApiPlugins ) { m_mainToolBar->AddScaledSeparator( this ); - m_mainToolBar->Add( EE_ACTIONS::showPythonConsole, ACTION_TOOLBAR::TOGGLE ); + + if( scriptingAvailable ) + m_mainToolBar->Add( EE_ACTIONS::showPythonConsole, ACTION_TOOLBAR::TOGGLE ); + + if( haveApiPlugins ) + addApiPluginTools(); } // after adding the tools to the toolbar, must call Realize() to reflect the changes diff --git a/eeschema/tools/sch_editor_control.cpp b/eeschema/tools/sch_editor_control.cpp index c22b87efca..b659802a20 100644 --- a/eeschema/tools/sch_editor_control.cpp +++ b/eeschema/tools/sch_editor_control.cpp @@ -80,6 +80,10 @@ #include #include "sch_edit_table_tool.h" +#ifdef KICAD_IPC_API +#include +#endif + /** * Flag to enable schematic paste debugging output. @@ -2537,6 +2541,15 @@ int SCH_EDITOR_CONTROL::TogglePythonConsole( const TOOL_EVENT& aEvent ) } +int SCH_EDITOR_CONTROL::ReloadPlugins( const TOOL_EVENT& aEvent ) +{ +#ifdef KICAD_IPC_API + Pgm().GetPluginManager().ReloadPlugins(); +#endif + return 0; +} + + int SCH_EDITOR_CONTROL::RepairSchematic( const TOOL_EVENT& aEvent ) { int errors = 0; @@ -2731,6 +2744,7 @@ void SCH_EDITOR_CONTROL::setTransitions() Go( &SCH_EDITOR_CONTROL::ToggleAnnotateAuto, EE_ACTIONS::toggleAnnotateAuto.MakeEvent() ); Go( &SCH_EDITOR_CONTROL::TogglePythonConsole, EE_ACTIONS::showPythonConsole.MakeEvent() ); + Go( &SCH_EDITOR_CONTROL::ReloadPlugins, ACTIONS::pluginsReload.MakeEvent() ); Go( &SCH_EDITOR_CONTROL::RepairSchematic, EE_ACTIONS::repairSchematic.MakeEvent() ); diff --git a/eeschema/tools/sch_editor_control.h b/eeschema/tools/sch_editor_control.h index b444c74813..41190a8bf4 100644 --- a/eeschema/tools/sch_editor_control.h +++ b/eeschema/tools/sch_editor_control.h @@ -143,6 +143,7 @@ public: int ToggleAnnotateAuto( const TOOL_EVENT& aEvent ); int ToggleAnnotateRecursive( const TOOL_EVENT& aEvent ); int TogglePythonConsole( const TOOL_EVENT& aEvent ); + int ReloadPlugins( const TOOL_EVENT& aEvent ); int GridFeedback( const TOOL_EVENT& aEvent ); diff --git a/eeschema/tools/symbol_editor_move_tool.cpp b/eeschema/tools/symbol_editor_move_tool.cpp index ef144eeac5..1df710c126 100644 --- a/eeschema/tools/symbol_editor_move_tool.cpp +++ b/eeschema/tools/symbol_editor_move_tool.cpp @@ -467,6 +467,10 @@ int SYMBOL_EDITOR_MOVE_TOOL::AlignElements( const TOOL_EVENT& aEvent ) doMoveItem( shape, newStart - shape->GetStart() ); break; + + case SHAPE_T::UNDEFINED: + wxASSERT_MSG( false, wxT( "Undefined shape in AlignElements" ) ); + break; } } else diff --git a/include/api/api_enums.h b/include/api/api_enums.h new file mode 100644 index 0000000000..42fbc4f076 --- /dev/null +++ b/include/api/api_enums.h @@ -0,0 +1,29 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#ifndef KICAD_API_ENUMS_H +#define KICAD_API_ENUMS_H + +template +KiCadEnum FromProtoEnum( ProtoEnum aEnumValue ); + +template +ProtoEnum ToProtoEnum( KiCadEnum aEnumValue ); + +#endif //KICAD_API_ENUMS_H diff --git a/include/api/api_handler.h b/include/api/api_handler.h index 1dc76ad79a..daf668f5a4 100644 --- a/include/api/api_handler.h +++ b/include/api/api_handler.h @@ -32,7 +32,7 @@ #include -#include +#include #include #include @@ -44,11 +44,20 @@ typedef tl::expected API_RESULT; template using HANDLER_RESULT = tl::expected; -class API_HANDLER + +struct HANDLER_CONTEXT +{ + std::string ClientName; +}; + + +class KICOMMON_API API_HANDLER { public: API_HANDLER() {} + virtual ~API_HANDLER() {} + /** * Attempt to handle the given API request, if a handler exists in this class for the message. * @param aMsg is a request to attempt to handle @@ -56,8 +65,6 @@ public: */ API_RESULT Handle( ApiRequest& aMsg ); - static std::optional TypeNameFromAny( const google::protobuf::Any& aMessage ); - protected: /** @@ -81,7 +88,9 @@ protected: * @param aHandler is the handler function for the given request and response types */ template - void registerHandler( HANDLER_RESULT( HandlerType::* aHandler )( RequestType& ) ) + void registerHandler( + HANDLER_RESULT( HandlerType::* aHandler )( RequestType&, + const HANDLER_CONTEXT& ) ) { std::string typeName = RequestType().GetTypeName(); @@ -91,14 +100,17 @@ protected: m_handlers[typeName] = [=]( ApiRequest& aRequest ) -> API_RESULT { - RequestType command; + RequestType cmd; ApiResponse envelope; - if( !tryUnpack( aRequest, envelope, command ) ) + if( !tryUnpack( aRequest, envelope, cmd ) ) return envelope; + HANDLER_CONTEXT ctx; + ctx.ClientName = aRequest.header().client_name(); + HANDLER_RESULT response = - std::invoke( aHandler, static_cast( this ), command ); + std::invoke( aHandler, static_cast( this ), cmd, ctx ); if( response.has_value() ) { @@ -116,6 +128,8 @@ protected: /// Maps type name (without the URL prefix) to a handler method std::map m_handlers; + static const wxString m_defaultCommitMessage; + private: template diff --git a/include/api/api_handler_common.h b/include/api/api_handler_common.h index c950d4c535..57d2d34950 100644 --- a/include/api/api_handler_common.h +++ b/include/api/api_handler_common.h @@ -21,22 +21,29 @@ #ifndef KICAD_API_HANDLER_COMMON_H #define KICAD_API_HANDLER_COMMON_H +#include + #include #include -#include - -#include +#include using namespace kiapi; using namespace kiapi::common; +using google::protobuf::Empty; -class API_HANDLER_COMMON : public API_HANDLER +class KICOMMON_API API_HANDLER_COMMON : public API_HANDLER { public: API_HANDLER_COMMON(); private: - HANDLER_RESULT handleGetVersion( commands::GetVersion& aMsg ); + HANDLER_RESULT handleGetVersion( commands::GetVersion& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleGetNetClasses( commands::GetNetClasses& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handlePing( commands::Ping& aMsg, const HANDLER_CONTEXT& aCtx ); }; #endif //KICAD_API_HANDLER_COMMON_H diff --git a/include/api/api_handler_editor.h b/include/api/api_handler_editor.h new file mode 100644 index 0000000000..f6e47630c5 --- /dev/null +++ b/include/api/api_handler_editor.h @@ -0,0 +1,115 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#ifndef KICAD_API_HANDLER_EDITOR_H +#define KICAD_API_HANDLER_EDITOR_H + +#include +#include +#include +#include + +using namespace kiapi::common; +using kiapi::common::types::DocumentSpecifier; +using kiapi::common::types::ItemRequestStatus; +using kiapi::common::commands::ItemDeletionStatus; + +class EDA_BASE_FRAME; + +/** + * Base class for API handlers related to editor frames + */ +class API_HANDLER_EDITOR : public API_HANDLER +{ +public: + API_HANDLER_EDITOR( EDA_BASE_FRAME* aFrame = nullptr ); + +protected: + /// If the header is valid, returns the item container + HANDLER_RESULT> validateItemHeaderDocument( + const kiapi::common::types::ItemHeader& aHeader ); + + HANDLER_RESULT validateDocument( const DocumentSpecifier& aDocument ); + + /** + * Checks if the editor can accept commands + * @return an error status if busy, std::nullopt if not busy + */ + virtual std::optional checkForBusy(); + + HANDLER_RESULT handleBeginCommit( commands::BeginCommit& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleEndCommit( commands::EndCommit& aMsg, + const HANDLER_CONTEXT& aCtx ); + + COMMIT* getCurrentCommit( const HANDLER_CONTEXT& aCtx ); + + virtual void pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage ); + + HANDLER_RESULT handleCreateItems( commands::CreateItems& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleUpdateItems( commands::UpdateItems& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleDeleteItems( commands::DeleteItems& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleHitTest( commands::HitTest& aMsg, + const HANDLER_CONTEXT& aCtx ); + + /** + * Override this to create an appropriate COMMIT subclass for the frame in question + * @return a new COMMIT, bound to the editor frame + */ + virtual std::unique_ptr createCommit() = 0; + + /** + * Override this to specify which document type this editor handles + */ + virtual kiapi::common::types::DocumentType thisDocumentType() const = 0; + + /** + * @return true if the given document is valid for this editor and is currently open + */ + virtual bool validateDocumentInternal( const DocumentSpecifier& aDocument ) const = 0; + + virtual HANDLER_RESULT handleCreateUpdateItemsInternal( bool aCreate, + const HANDLER_CONTEXT& aCtx, + const types::ItemHeader &aHeader, + const google::protobuf::RepeatedPtrField& aItems, + std::function aItemHandler ) = 0; + + virtual void deleteItemsInternal( std::map& aItemsToDelete, + const HANDLER_CONTEXT& aCtx ) = 0; + + virtual std::optional getItemFromDocument( const DocumentSpecifier& aDocument, + const KIID& aId ) = 0; + +protected: + std::map>> m_commits; + + std::set m_activeClients; + + EDA_BASE_FRAME* m_frame; +}; + +#endif //KICAD_API_HANDLER_EDITOR_H diff --git a/include/api/api_plugin.h b/include/api/api_plugin.h index 47bdf38aec..9aba69032f 100644 --- a/include/api/api_plugin.h +++ b/include/api/api_plugin.h @@ -58,7 +58,7 @@ enum class PLUGIN_ACTION_SCOPE SCHEMATIC, FOOTPRINT, SYMBOL, - KICAD + PROJECT_MANAGER }; diff --git a/include/api/api_plugin_manager.h b/include/api/api_plugin_manager.h index 4e4f604efe..1194f1b294 100644 --- a/include/api/api_plugin_manager.h +++ b/include/api/api_plugin_manager.h @@ -32,7 +32,7 @@ * * Use "KICAD_API" to enable. * - * @ingroup traceApi + * @ingroup trace_env_vars */ extern const KICOMMON_API wxChar* const traceApi; @@ -41,6 +41,7 @@ wxDECLARE_EVENT( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxCommandEvent ); /// Notifies other parts of KiCad when plugin availability changes extern const KICOMMON_API wxEventTypeTag EDA_EVT_PLUGIN_AVAILABILITY_CHANGED; + /** * Responsible for loading plugin definitions for API-based plugins (ones that do not run inside * KiCad itself, but instead are launched as external processes by KiCad) diff --git a/include/api/api_server.h b/include/api/api_server.h index c19ca3744e..11bec3a14a 100644 --- a/include/api/api_server.h +++ b/include/api/api_server.h @@ -1,8 +1,8 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2023 Jon Evans - * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. * * 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 @@ -28,6 +28,8 @@ #include #include +#include + class API_HANDLER; class API_HANDLER_COMMON; class KINNG_REQUEST_SERVER; @@ -37,7 +39,7 @@ class wxEvtHandler; wxDECLARE_EVENT( API_REQUEST_EVENT, wxCommandEvent ); -class KICAD_API_SERVER : public wxEvtHandler +class KICOMMON_API KICAD_API_SERVER : public wxEvtHandler { public: KICAD_API_SERVER(); diff --git a/include/api/api_utils.h b/include/api/api_utils.h new file mode 100644 index 0000000000..38404a7d24 --- /dev/null +++ b/include/api/api_utils.h @@ -0,0 +1,48 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#ifndef KICAD_API_UTILS_H +#define KICAD_API_UTILS_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace kiapi::common +{ + +std::optional TypeNameFromAny( const google::protobuf::Any& aMessage ); + +LIB_ID LibIdFromProto( const types::LibraryIdentifier& aId ); + +types::LibraryIdentifier LibIdToProto( const LIB_ID& aId ); + +void PackVector2( kiapi::common::types::Vector2& aOutput, const VECTOR2I aInput ); + +VECTOR2I UnpackVector2( const types::Vector2& aInput ); + +} // namespace kiapi::common + +#endif //KICAD_API_UTILS_H diff --git a/include/api/serializable.h b/include/api/serializable.h new file mode 100644 index 0000000000..b38ff5d828 --- /dev/null +++ b/include/api/serializable.h @@ -0,0 +1,60 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#ifndef KICAD_SERIALIZABLE_H +#define KICAD_SERIALIZABLE_H + +#include + +namespace google { + namespace protobuf { + class Any; + } +} + +/** + * Interface for objects that can be serialized to Protobuf messages + */ +class SERIALIZABLE +{ +public: + /** + * Serializes this object to the given Any message. + * The Any message's concrete type will be specific to the object in question. + * @param aContainer will be filled with a message describing this object + */ + virtual void Serialize( google::protobuf::Any &aContainer ) const + { + wxASSERT_MSG( false, wxS( "Serialize called on an object that doesn't implement it!" ) ); + } + + /** + * Deserializes the given protobuf message into this object. + * @param aContainer is an Any which should have a concrete type matching this object + * @return true if unpacking and deserialization succeeded + */ + virtual bool Deserialize( const google::protobuf::Any &aContainer ) + { + wxASSERT_MSG( false, wxS( "Deserialize called on an object that doesn't implement it!" ) ); + return false; + } +}; + +#endif //KICAD_SERIALIZABLE_H diff --git a/include/eda_base_frame.h b/include/eda_base_frame.h index 1c90f6fc8e..e92a95592b 100644 --- a/include/eda_base_frame.h +++ b/include/eda_base_frame.h @@ -600,6 +600,13 @@ public: */ virtual void HandleSystemColorChange(); + /** + * Checks if this frame is ready to accept API commands. + * A frame might not accept commands if a long-running process is underway, a dialog is open, + * the user is interacting with a tool, etc. + */ + virtual bool CanAcceptApiCommands() { return IsEnabled(); } + protected: ///< Default style flags used for wxAUI toolbars. static constexpr int KICAD_AUI_TB_STYLE = wxAUI_TB_DEFAULT_STYLE | wxAUI_TB_PLAIN_BACKGROUND; diff --git a/include/eda_draw_frame.h b/include/eda_draw_frame.h index ed6f9dcce3..e0747db5b5 100644 --- a/include/eda_draw_frame.h +++ b/include/eda_draw_frame.h @@ -26,6 +26,7 @@ #ifndef DRAW_FRAME_H_ #define DRAW_FRAME_H_ +#include #include #include #include @@ -490,6 +491,13 @@ public: */ bool SaveCanvasImageToFile( const wxString& aFileName, BITMAP_TYPE aBitmapType ); + /** + * Handler for activating an API plugin (via toolbar or menu) + */ + virtual void OnApiPluginInvoke( wxCommandEvent& aEvent ); + + virtual PLUGIN_ACTION_SCOPE PluginActionScope() const { return PLUGIN_ACTION_SCOPE::INVALID; } + DECLARE_EVENT_TABLE() @@ -525,6 +533,11 @@ protected: virtual void handleActivateEvent( wxActivateEvent& aEvent ); void onActivate( wxActivateEvent& aEvent ); + /** + * Append actions from API plugins to the main toolbar + */ + virtual void addApiPluginTools(); + wxSocketServer* m_socketServer; diff --git a/include/eda_item.h b/include/eda_item.h index 89ff04b830..38796083ce 100644 --- a/include/eda_item.h +++ b/include/eda_item.h @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -83,7 +84,7 @@ typedef const INSPECTOR_FUNC& INSPECTOR; /** * A base class for most all the KiCad significant classes used in schematics and boards. */ -class EDA_ITEM : public KIGFX::VIEW_ITEM +class EDA_ITEM : public KIGFX::VIEW_ITEM, public SERIALIZABLE { public: virtual ~EDA_ITEM() { }; @@ -438,10 +439,6 @@ public: virtual void ViewGetLayers( int aLayers[], int& aCount ) const override; - virtual void Serialize( google::protobuf::Any &aContainer ) const {} - - virtual bool Deserialize( const google::protobuf::Any &aContainer ) { return false; } - #if defined(DEBUG) /** diff --git a/include/eda_shape.h b/include/eda_shape.h index ff07a81689..c8da3983b2 100644 --- a/include/eda_shape.h +++ b/include/eda_shape.h @@ -40,6 +40,7 @@ using KIGFX::COLOR4D; enum class SHAPE_T : int { + UNDEFINED = -1, SEGMENT = 0, RECTANGLE, /// use RECTANGLE instead of RECT to avoid collision in a Windows header ARC, diff --git a/include/font/text_attributes.h b/include/font/text_attributes.h index a036fdaf0a..34b8bef2f6 100644 --- a/include/font/text_attributes.h +++ b/include/font/text_attributes.h @@ -38,6 +38,7 @@ class FONT; // NB: values -1,0,1 are used in computations, do not change them // +// This is API surface mapped to common.types.HorizontalAlignment enum GR_TEXT_H_ALIGN_T { GR_TEXT_H_ALIGN_LEFT = -1, @@ -46,6 +47,7 @@ enum GR_TEXT_H_ALIGN_T GR_TEXT_H_ALIGN_INDETERMINATE }; +// This is API surface mapped to common.types.VertialAlignment enum GR_TEXT_V_ALIGN_T { GR_TEXT_V_ALIGN_TOP = -1, diff --git a/include/template_fieldnames.h b/include/template_fieldnames.h index fa44364738..939f6a639d 100644 --- a/include/template_fieldnames.h +++ b/include/template_fieldnames.h @@ -40,6 +40,7 @@ class TEMPLATE_FIELDNAMES_LEXER; * an unlimited number of user defined fields, only some of which have indices defined here. */ enum MANDATORY_FIELD_T { + INVALID_FIELD = -1, ///< The field ID hasn't been set yet; field is invalid REFERENCE_FIELD = 0, ///< Field Reference of part, i.e. "IC21" VALUE_FIELD, ///< Field Value of part, i.e. "3.3K" FOOTPRINT_FIELD, ///< Field Name Module PCB, i.e. "16DIP300" diff --git a/include/tool/actions.h b/include/tool/actions.h index d131e916df..b60a143a2a 100644 --- a/include/tool/actions.h +++ b/include/tool/actions.h @@ -220,6 +220,9 @@ public: static TOOL_ACTION getInvolved; static TOOL_ACTION reportBug; + // API + static TOOL_ACTION pluginsReload; + ///< Cursor control event types enum CURSOR_EVENT_TYPE { diff --git a/include/tool/tool_manager.h b/include/tool/tool_manager.h index e260ade1c2..b58e963d82 100644 --- a/include/tool/tool_manager.h +++ b/include/tool/tool_manager.h @@ -270,6 +270,14 @@ public: doRunAction( aAction, false, a, nullptr ); } + bool PostAction( const TOOL_ACTION& aAction, COMMIT* aCommit ) + { + // Default initialize the parameter argument to an empty std::any + std::any a; + + return doRunAction( aAction, false, a, aCommit ); + } + /** * Send a cancel event to the tool currently at the top of the tool stack. */ diff --git a/pcbnew/api/api_handler_pcb.cpp b/pcbnew/api/api_handler_pcb.cpp index 609dbe2899..f52c5e9361 100644 --- a/pcbnew/api/api_handler_pcb.cpp +++ b/pcbnew/api/api_handler_pcb.cpp @@ -21,10 +21,26 @@ #include #include +#include +#include +#include #include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include +#include #include +#include +#include +#include #include @@ -33,34 +49,44 @@ using kiapi::common::types::CommandStatus; using kiapi::common::types::DocumentType; using kiapi::common::types::ItemRequestStatus; -static const wxString s_defaultCommitMessage = wxS( "Modification from API" ); - API_HANDLER_PCB::API_HANDLER_PCB( PCB_EDIT_FRAME* aFrame ) : - API_HANDLER(), - m_frame( aFrame ), - m_transactionInProgress( false ) + API_HANDLER_EDITOR( aFrame ) { registerHandler( &API_HANDLER_PCB::handleRunAction ); registerHandler( &API_HANDLER_PCB::handleGetOpenDocuments ); - registerHandler( &API_HANDLER_PCB::handleBeginCommit ); - registerHandler( &API_HANDLER_PCB::handleEndCommit ); - registerHandler( &API_HANDLER_PCB::handleCreateItems ); registerHandler( &API_HANDLER_PCB::handleGetItems ); - registerHandler( &API_HANDLER_PCB::handleUpdateItems ); - registerHandler( &API_HANDLER_PCB::handleDeleteItems ); + registerHandler( &API_HANDLER_PCB::handleGetStackup ); + registerHandler( + &API_HANDLER_PCB::handleGetGraphicsDefaults ); + registerHandler( + &API_HANDLER_PCB::handleGetTextExtents ); + + registerHandler( &API_HANDLER_PCB::handleInteractiveMoveItems ); + registerHandler( &API_HANDLER_PCB::handleGetNets ); + registerHandler( &API_HANDLER_PCB::handleRefillZones ); } -HANDLER_RESULT API_HANDLER_PCB::handleRunAction( RunAction& aRequest ) +PCB_EDIT_FRAME* API_HANDLER_PCB::frame() const { + return static_cast( m_frame ); +} + + +HANDLER_RESULT API_HANDLER_PCB::handleRunAction( RunAction& aRequest, + const HANDLER_CONTEXT& ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + RunActionResponse response; - if( m_frame->GetToolManager()->RunAction( aRequest.action(), true ) ) + if( frame()->GetToolManager()->RunAction( aRequest.action(), true ) ) response.set_status( RunActionStatus::RAS_OK ); else response.set_status( RunActionStatus::RAS_INVALID ); @@ -70,7 +96,7 @@ HANDLER_RESULT API_HANDLER_PCB::handleRunAction( RunAction& a HANDLER_RESULT API_HANDLER_PCB::handleGetOpenDocuments( - GetOpenDocuments& aMsg ) + GetOpenDocuments& aMsg, const HANDLER_CONTEXT& ) { if( aMsg.type() != DocumentType::DOCTYPE_PCB ) { @@ -83,166 +109,178 @@ HANDLER_RESULT API_HANDLER_PCB::handleGetOpenDocuments GetOpenDocumentsResponse response; common::types::DocumentSpecifier doc; - wxFileName fn( m_frame->GetCurrentFileName() ); + wxFileName fn( frame()->GetCurrentFileName() ); doc.set_type( DocumentType::DOCTYPE_PCB ); doc.set_board_filename( fn.GetFullName() ); + doc.mutable_project()->set_name( frame()->Prj().GetProjectName().ToStdString() ); + doc.mutable_project()->set_path( frame()->Prj().GetProjectDirectory().ToStdString() ); + response.mutable_documents()->Add( std::move( doc ) ); return response; } -HANDLER_RESULT API_HANDLER_PCB::handleBeginCommit( BeginCommit& aMsg ) +void API_HANDLER_PCB::pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage ) { - BeginCommitResponse response; - - if( m_commit ) - { - // TODO: right now there is no way for m_transactionInProgress to be true here, but - // we should still check it as a safety measure and return a specific error - //if( !m_transactionInProgress ) - - m_commit->Revert(); - } - - m_commit.reset( new BOARD_COMMIT( m_frame ) ); - - // TODO: return an opaque ID for this new commit to make this more robust - m_transactionInProgress = true; - - return response; + API_HANDLER_EDITOR::pushCurrentCommit( aCtx, aMessage ); + frame()->Refresh(); } -HANDLER_RESULT API_HANDLER_PCB::handleEndCommit( EndCommit& aMsg ) +std::unique_ptr API_HANDLER_PCB::createCommit() { - EndCommitResponse response; - - // TODO: return more specific error if m_transactionInProgress is false - if( !m_transactionInProgress ) - { - // Make sure we don't get stuck with a commit we can never push - m_commit.reset(); - response.set_result( CommitResult::CR_NO_COMMIT ); - return response; - } - - if( !m_commit ) - { - response.set_result( CommitResult::CR_NO_COMMIT ); - return response; - } - - pushCurrentCommit( aMsg.message() ); - m_transactionInProgress = false; - - response.set_result( CommitResult::CR_OK ); - return response; + return std::make_unique( frame() ); } -BOARD_COMMIT* API_HANDLER_PCB::getCurrentCommit() +std::optional API_HANDLER_PCB::getItemById( const KIID& aId ) const { - if( !m_commit ) - m_commit.reset( new BOARD_COMMIT( m_frame ) ); + BOARD_ITEM* item = frame()->GetBoard()->GetItem( aId ); - return m_commit.get(); + if( item == DELETED_BOARD_ITEM::GetInstance() ) + return std::nullopt; + + return item; } -void API_HANDLER_PCB::pushCurrentCommit( const std::string& aMessage ) +bool API_HANDLER_PCB::validateDocumentInternal( const DocumentSpecifier& aDocument ) const { - wxCHECK( m_commit, /* void */ ); - - wxString msg( aMessage.c_str(), wxConvUTF8 ); - - if( msg.IsEmpty() ) - msg = s_defaultCommitMessage; - - m_commit->Push( msg ); - m_commit.reset(); - - m_frame->Refresh(); -} - - -bool API_HANDLER_PCB::validateItemHeaderDocument( const common::types::ItemHeader& aHeader ) -{ - // TODO: this should return a more complex error type. - // We should provide detailed feedback when a header fails validation, and distinguish between - // "skip this handler" and "this is the right handler, but the request is invalid" - if( !aHeader.has_document() || aHeader.document().type() != DocumentType::DOCTYPE_PCB ) + if( aDocument.type() != DocumentType::DOCTYPE_PCB ) return false; - wxFileName fn( m_frame->GetCurrentFileName() ); - - return aHeader.document().board_filename().compare( fn.GetFullName() ) == 0; + wxFileName fn( frame()->GetCurrentFileName() ); + return 0 == aDocument.board_filename().compare( fn.GetFullName() ); } -std::unique_ptr API_HANDLER_PCB::createItemForType( KICAD_T aType, - BOARD_ITEM_CONTAINER* aContainer ) +HANDLER_RESULT> API_HANDLER_PCB::createItemForType( KICAD_T aType, + BOARD_ITEM_CONTAINER* aContainer ) { - switch( aType ) + if( !aContainer ) { - case PCB_TRACE_T: return std::make_unique( aContainer ); - case PCB_ARC_T: return std::make_unique( aContainer ); - case PCB_VIA_T: return std::make_unique( aContainer ); - default: return nullptr; + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( "Tried to create an item in a null container" ); + return tl::unexpected( e ); } + + if( aType == PCB_PAD_T && !dynamic_cast( aContainer ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "Tried to create a pad in {}, which is not a footprint", + aContainer->GetFriendlyName().ToStdString() ) ); + return tl::unexpected( e ); + } + else if( aType == PCB_FOOTPRINT_T && !dynamic_cast( aContainer ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "Tried to create a footprint in {}, which is not a board", + aContainer->GetFriendlyName().ToStdString() ) ); + return tl::unexpected( e ); + } + + std::unique_ptr created = CreateItemForType( aType, aContainer ); + + if( !created ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "Tried to create an item of type {}, which is unhandled", + magic_enum::enum_name( aType ) ) ); + return tl::unexpected( e ); + } + + return created; } -HANDLER_RESULT API_HANDLER_PCB::handleCreateItems( CreateItems& aMsg ) +HANDLER_RESULT API_HANDLER_PCB::handleCreateUpdateItemsInternal( bool aCreate, + const HANDLER_CONTEXT& aCtx, + const types::ItemHeader &aHeader, + const google::protobuf::RepeatedPtrField& aItems, + std::function aItemHandler ) { ApiResponseStatus e; - if( !validateItemHeaderDocument( aMsg.header() ) ) + auto containerResult = validateItemHeaderDocument( aHeader ); + + if( !containerResult && containerResult.error().status() == ApiStatusCode::AS_UNHANDLED ) { // No message needed for AS_UNHANDLED; this is an internal flag for the API server e.set_status( ApiStatusCode::AS_UNHANDLED ); return tl::unexpected( e ); } - - BOARD* board = m_frame->GetBoard(); - BOARD_ITEM_SET boardItems = board->GetItemSet(); - - std::map itemUuidMap; - - std::for_each( boardItems.begin(), boardItems.end(), - [&]( BOARD_ITEM* aItem ) - { - itemUuidMap[aItem->m_Uuid] = aItem; - } ); - - BOARD_COMMIT* commit = getCurrentCommit(); - - CreateItemsResponse response; - - for( const google::protobuf::Any& anyItem : aMsg.items() ) + else if( !containerResult ) { - ItemCreationResult itemResult; + e.CopyFrom( containerResult.error() ); + return tl::unexpected( e ); + } + + BOARD* board = frame()->GetBoard(); + BOARD_ITEM_CONTAINER* container = board; + + if( containerResult->has_value() ) + { + const KIID& containerId = **containerResult; + std::optional optItem = getItemById( containerId ); + + if( optItem ) + { + container = dynamic_cast( *optItem ); + + if( !container ) + { + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( + "The requested container {} is not a valid board item container", + containerId.AsStdString() ) ); + return tl::unexpected( e ); + } + } + else + { + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( + "The requested container {} does not exist in this document", + containerId.AsStdString() ) ); + return tl::unexpected( e ); + } + } + + BOARD_COMMIT* commit = static_cast( getCurrentCommit( aCtx ) ); + + for( const google::protobuf::Any& anyItem : aItems ) + { + ItemStatus status; std::optional type = TypeNameFromAny( anyItem ); if( !type ) { - itemResult.set_status( ItemCreationStatus::ICS_INVALID_TYPE ); - response.mutable_created_items()->Add( std::move( itemResult ) ); + status.set_code( ItemStatusCode::ISC_INVALID_TYPE ); + status.set_error_message( fmt::format( "Could not decode a valid type from {}", + anyItem.type_url() ) ); + aItemHandler( status, anyItem ); continue; } - std::unique_ptr item = createItemForType( *type, board ); + HANDLER_RESULT> creationResult = + createItemForType( *type, container ); - if( !item ) + if( !creationResult ) { - itemResult.set_status( ItemCreationStatus::ICS_INVALID_TYPE ); - e.set_error_message( fmt::format( "item type {} not supported for board", - magic_enum::enum_name( *type ) ) ); - response.mutable_created_items()->Add( std::move( itemResult ) ); + status.set_code( ItemStatusCode::ISC_INVALID_TYPE ); + status.set_error_message( creationResult.error().error_message() ); + aItemHandler( status, anyItem ); continue; } + std::unique_ptr item( std::move( *creationResult ) ); + if( !item->Deserialize( anyItem ) ) { e.set_status( ApiStatusCode::AS_BAD_REQUEST ); @@ -251,28 +289,70 @@ HANDLER_RESULT API_HANDLER_PCB::handleCreateItems( CreateIt return tl::unexpected( e ); } - if( itemUuidMap.count( item->m_Uuid ) ) + std::optional optItem = getItemById( item->m_Uuid ); + + if( aCreate && optItem ) { - itemResult.set_status( ItemCreationStatus::ICS_EXISTING ); - response.mutable_created_items()->Add( std::move( itemResult ) ); + status.set_code( ItemStatusCode::ISC_EXISTING ); + status.set_error_message( fmt::format( "an item with UUID {} already exists", + item->m_Uuid.AsStdString() ) ); + aItemHandler( status, anyItem ); + continue; + } + else if( !aCreate && !optItem ) + { + status.set_code( ItemStatusCode::ISC_NONEXISTENT ); + status.set_error_message( fmt::format( "an item with UUID {} does not exist", + item->m_Uuid.AsStdString() ) ); + aItemHandler( status, anyItem ); continue; } - itemResult.set_status( ItemCreationStatus::ICS_OK ); - item->Serialize( *itemResult.mutable_item() ); - commit->Add( item.release() ); + if( aCreate && !board->GetEnabledLayers().Contains( item->GetLayer() ) ) + { + status.set_code( ItemStatusCode::ISC_INVALID_DATA ); + status.set_error_message( fmt::format( "attempted to add item on disabled layer {}", + LayerName( item->GetLayer() ).ToStdString() ) ); + aItemHandler( status, anyItem ); + continue; + } - response.mutable_created_items()->Add( std::move( itemResult ) ); + status.set_code( ItemStatusCode::ISC_OK ); + google::protobuf::Any newItem; + + if( aCreate ) + { + item->Serialize( newItem ); + commit->Add( item.release() ); + } + else + { + BOARD_ITEM* boardItem = *optItem; + commit->Modify( boardItem ); + boardItem->SwapItemData( item.get() ); + boardItem->Serialize( newItem ); + } + + aItemHandler( status, newItem ); } - pushCurrentCommit( "Added items via API" ); - response.set_status( ItemRequestStatus::IRS_OK ); - return response; + if( !m_activeClients.count( aCtx.ClientName ) ) + { + pushCurrentCommit( aCtx, aCreate ? _( "Created items via API" ) + : _( "Added items via API" ) ); + } + + + return ItemRequestStatus::IRS_OK; } -HANDLER_RESULT API_HANDLER_PCB::handleGetItems( GetItems& aMsg ) +HANDLER_RESULT API_HANDLER_PCB::handleGetItems( GetItems& aMsg, + const HANDLER_CONTEXT& ) { + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + if( !validateItemHeaderDocument( aMsg.header() ) ) { ApiResponseStatus e; @@ -283,18 +363,17 @@ HANDLER_RESULT API_HANDLER_PCB::handleGetItems( GetItems& aMsg GetItemsResponse response; - BOARD* board = m_frame->GetBoard(); + BOARD* board = frame()->GetBoard(); std::vector items; std::set typesRequested, typesInserted; bool handledAnything = false; - for( const common::types::ItemType& typeMessage : aMsg.types() ) + for( int typeRaw : aMsg.types() ) { - KICAD_T type; + auto typeMessage = static_cast( typeRaw ); + KICAD_T type = FromProtoEnum( typeMessage ); - if( std::optional opt_type = magic_enum::enum_cast( typeMessage.type() ) ) - type = *opt_type; - else + if( type == TYPE_NOT_INIT ) continue; typesRequested.emplace( type ); @@ -313,6 +392,31 @@ HANDLER_RESULT API_HANDLER_PCB::handleGetItems( GetItems& aMsg typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ); break; + case PCB_PAD_T: + { + handledAnything = true; + + for( FOOTPRINT* fp : board->Footprints() ) + { + std::copy( fp->Pads().begin(), fp->Pads().end(), + std::back_inserter( items ) ); + } + + typesInserted.insert( PCB_PAD_T ); + break; + } + + case PCB_FOOTPRINT_T: + { + handledAnything = true; + + std::copy( board->Footprints().begin(), board->Footprints().end(), + std::back_inserter( items ) ); + + typesInserted.insert( PCB_FOOTPRINT_T ); + break; + } + default: break; } @@ -341,154 +445,259 @@ HANDLER_RESULT API_HANDLER_PCB::handleGetItems( GetItems& aMsg } -HANDLER_RESULT API_HANDLER_PCB::handleUpdateItems( UpdateItems& aMsg ) +void API_HANDLER_PCB::deleteItemsInternal( std::map& aItemsToDelete, + const HANDLER_CONTEXT& aCtx ) { - ApiResponseStatus e; - - if( !validateItemHeaderDocument( aMsg.header() ) ) - { - // No message needed for AS_UNHANDLED; this is an internal flag for the API server - e.set_status( ApiStatusCode::AS_UNHANDLED ); - return tl::unexpected( e ); - } - - BOARD* board = m_frame->GetBoard(); - BOARD_ITEM_SET boardItems = board->GetItemSet(); - - std::map itemUuidMap; - - std::for_each( boardItems.begin(), boardItems.end(), - [&]( BOARD_ITEM* aItem ) - { - itemUuidMap[aItem->m_Uuid] = aItem; - } ); - - BOARD_COMMIT* commit = getCurrentCommit(); - - UpdateItemsResponse response; - - for( const google::protobuf::Any& anyItem : aMsg.items() ) - { - ItemUpdateResult itemResult; - std::optional type = TypeNameFromAny( anyItem ); - - if( !type ) - { - itemResult.set_status( ItemUpdateStatus::IUS_INVALID_TYPE ); - response.mutable_updated_items()->Add( std::move( itemResult ) ); - continue; - } - - std::unique_ptr temporaryItem = createItemForType( *type, board ); - - if( !temporaryItem ) - { - itemResult.set_status( ItemUpdateStatus::IUS_INVALID_TYPE ); - response.mutable_updated_items()->Add( std::move( itemResult ) ); - continue; - } - - if( !temporaryItem->Deserialize( anyItem ) ) - { - e.set_status( ApiStatusCode::AS_BAD_REQUEST ); - e.set_error_message( fmt::format( "could not unpack {} from request", - magic_enum::enum_name( *type ) ) ); - return tl::unexpected( e ); - } - - if( !itemUuidMap.count( temporaryItem->m_Uuid ) ) - { - itemResult.set_status( ItemUpdateStatus::IUS_NONEXISTENT ); - response.mutable_updated_items()->Add( std::move( itemResult ) ); - continue; - } - - BOARD_ITEM* boardItem = itemUuidMap[temporaryItem->m_Uuid]; - - boardItem->SwapItemData( temporaryItem.get() ); - - itemResult.set_status( ItemUpdateStatus::IUS_OK ); - boardItem->Serialize( *itemResult.mutable_item() ); - commit->Modify( boardItem ); - - itemResult.set_status( ItemUpdateStatus::IUS_OK ); - response.mutable_updated_items()->Add( std::move( itemResult ) ); - } - - response.set_status( ItemRequestStatus::IRS_OK ); - pushCurrentCommit( "Updated items via API" ); - return response; -} - - -HANDLER_RESULT API_HANDLER_PCB::handleDeleteItems( DeleteItems& aMsg ) -{ - if( !validateItemHeaderDocument( aMsg.header() ) ) - { - ApiResponseStatus e; - // No message needed for AS_UNHANDLED; this is an internal flag for the API server - e.set_status( ApiStatusCode::AS_UNHANDLED ); - return tl::unexpected( e ); - } - - std::map itemsToDelete; - - for( const common::types::KIID& kiidBuf : aMsg.item_ids() ) - { - if( !kiidBuf.value().empty() ) - { - KIID kiid( kiidBuf.value() ); - itemsToDelete[kiid] = ItemDeletionStatus::IDS_NONEXISTENT; - } - } - - if( itemsToDelete.empty() ) - { - ApiResponseStatus e; - e.set_status( ApiStatusCode::AS_BAD_REQUEST ); - e.set_error_message( "no valid items to delete were given" ); - return tl::unexpected( e ); - } - - BOARD* board = m_frame->GetBoard(); - - // This is somewhat inefficient on paper, but the total number of items on a board is - // not computationally-speaking very high even on what we'd consider a large design. - // If this ends up not being the case, we should consider doing something like refactoring - // BOARD to contain all items in a contiguous memory arena and constructing views over it - // when we want to filter to just tracks, etc. - BOARD_ITEM_SET items = board->GetItemSet(); + BOARD* board = frame()->GetBoard(); std::vector validatedItems; - for( BOARD_ITEM* item : items ) + for( std::pair pair : aItemsToDelete ) { - if( itemsToDelete.count( item->m_Uuid ) ) + if( BOARD_ITEM* item = board->GetItem( pair.first ) ) { validatedItems.push_back( item ); - itemsToDelete[item->m_Uuid] = ItemDeletionStatus::IDS_OK; + aItemsToDelete[pair.first] = ItemDeletionStatus::IDS_OK; } // Note: we don't currently support locking items from API modification, but here is where // to add it in the future (and return IDS_IMMUTABLE) } - BOARD_COMMIT* commit = getCurrentCommit(); + COMMIT* commit = getCurrentCommit( aCtx ); for( BOARD_ITEM* item : validatedItems ) commit->Remove( item ); - if( !m_transactionInProgress ) - pushCurrentCommit( "Deleted items via API" ); + if( !m_activeClients.count( aCtx.ClientName ) ) + pushCurrentCommit( aCtx, _( "Deleted items via API" ) ); +} - DeleteItemsResponse response; - for( const auto& [id, status] : itemsToDelete ) +std::optional API_HANDLER_PCB::getItemFromDocument( const DocumentSpecifier& aDocument, + const KIID& aId ) +{ + if( !validateDocument( aDocument ) ) + return std::nullopt; + + return getItemById( aId ); +} + + +HANDLER_RESULT API_HANDLER_PCB::handleGetStackup( GetBoardStackup& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + HANDLER_RESULT documentValidation = validateDocument( aMsg.board() ); + + if( !documentValidation ) + return tl::unexpected( documentValidation.error() ); + + const BOARD* board = frame()->GetBoard(); + BoardStackupResponse response; + google::protobuf::Any any; + const BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); + + if( frame()->GetBoard()->GetDesignSettings().m_HasStackup ) { - ItemDeletionResult result; - result.mutable_id()->set_value( id.AsStdString() ); - result.set_status( status ); + const BOARD_STACKUP& stackup = bds.GetStackupDescriptor(); + stackup.Serialize( any ); + } + else + { + BOARD_STACKUP stackup; + stackup.BuildDefaultStackupList( &bds, board->GetCopperLayerCount() ); + stackup.Serialize( any ); } - response.set_status( ItemRequestStatus::IRS_OK ); + any.UnpackTo( response.mutable_stackup() ); + return response; } + + +HANDLER_RESULT API_HANDLER_PCB::handleGetGraphicsDefaults( + GetGraphicsDefaults& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + HANDLER_RESULT documentValidation = validateDocument( aMsg.board() ); + + if( !documentValidation ) + return tl::unexpected( documentValidation.error() ); + + const BOARD_DESIGN_SETTINGS& bds = frame()->GetBoard()->GetDesignSettings(); + GraphicsDefaultsResponse response; + + // TODO: This should change to be an enum class + constexpr std::array classOrder = { + kiapi::board::BLC_SILKSCREEN, + kiapi::board::BLC_COPPER, + kiapi::board::BLC_EDGES, + kiapi::board::BLC_COURTYARD, + kiapi::board::BLC_FABRICATION, + kiapi::board::BLC_OTHER + }; + + for( int i = 0; i < LAYER_CLASS_COUNT; ++i ) + { + kiapi::board::BoardLayerGraphicsDefaults* l = response.mutable_defaults()->add_layers(); + + l->set_layer( classOrder[i] ); + l->mutable_line_thickness()->set_value_nm( bds.m_LineThickness[i] ); + + kiapi::common::types::TextAttributes* text = l->mutable_text(); + text->mutable_size()->set_x_nm( bds.m_TextSize[i].x ); + text->mutable_size()->set_y_nm( bds.m_TextSize[i].y ); + text->mutable_stroke_width()->set_value_nm( bds.m_TextThickness[i] ); + text->set_italic( bds.m_TextItalic[i] ); + text->set_keep_upright( bds.m_TextUpright[i] ); + } + + return response; +} + + +HANDLER_RESULT API_HANDLER_PCB::handleGetTextExtents( + GetTextExtents& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + PCB_TEXT text( frame()->GetBoard() ); + + google::protobuf::Any any; + any.PackFrom( aMsg.text() ); + + if( !text.Deserialize( any ) ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( "Could not decode text in GetTextExtents message" ); + return tl::unexpected( e ); + } + + commands::BoundingBoxResponse response; + + BOX2I bbox = text.GetTextBox(); + EDA_ANGLE angle = text.GetTextAngle(); + + if( !angle.IsZero() ) + bbox = bbox.GetBoundingBoxRotated( text.GetTextPos(), text.GetTextAngle() ); + + response.mutable_position()->set_x_nm( bbox.GetPosition().x ); + response.mutable_position()->set_y_nm( bbox.GetPosition().y ); + response.mutable_size()->set_x_nm( bbox.GetSize().x ); + response.mutable_size()->set_y_nm( bbox.GetSize().y ); + + return response; +} + + +HANDLER_RESULT API_HANDLER_PCB::handleInteractiveMoveItems( InteractiveMoveItems& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + HANDLER_RESULT documentValidation = validateDocument( aMsg.board() ); + + if( !documentValidation ) + return tl::unexpected( documentValidation.error() ); + + TOOL_MANAGER* mgr = frame()->GetToolManager(); + std::vector toSelect; + + for( const kiapi::common::types::KIID& id : aMsg.items() ) + { + if( std::optional item = getItemById( KIID( id.value() ) ) ) + toSelect.emplace_back( static_cast( *item ) ); + } + + if( toSelect.empty() ) + { + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_BAD_REQUEST ); + e.set_error_message( fmt::format( "None of the given items exist on the board", + aMsg.board().board_filename() ) ); + return tl::unexpected( e ); + } + + PCB_SELECTION_TOOL* selectionTool = mgr->GetTool(); + selectionTool->GetSelection().SetReferencePoint( toSelect[0]->GetPosition() ); + + mgr->RunAction( PCB_ACTIONS::selectionClear ); + mgr->RunAction( PCB_ACTIONS::selectItems, &toSelect ); + + COMMIT* commit = getCurrentCommit( aCtx ); + mgr->PostAction( PCB_ACTIONS::move, commit ); + + return Empty(); +} + + +HANDLER_RESULT API_HANDLER_PCB::handleGetNets( GetNets& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + HANDLER_RESULT documentValidation = validateDocument( aMsg.board() ); + + if( !documentValidation ) + return tl::unexpected( documentValidation.error() ); + + NetsResponse response; + BOARD* board = frame()->GetBoard(); + + std::set netclassFilter; + + for( const std::string& nc : aMsg.netclass_filter() ) + netclassFilter.insert( wxString( nc.c_str(), wxConvUTF8 ) ); + + for( NETINFO_ITEM* net : board->GetNetInfo() ) + { + NETCLASS* nc = net->GetNetClass(); + + if( !netclassFilter.empty() && nc && !netclassFilter.count( nc->GetName() ) ) + continue; + + board::types::Net* netProto = response.add_nets(); + netProto->set_name( net->GetNetname() ); + netProto->mutable_code()->set_value( net->GetNetCode() ); + } + + return response; +} + + +HANDLER_RESULT API_HANDLER_PCB::handleRefillZones( RefillZones& aMsg, + const HANDLER_CONTEXT& aCtx ) +{ + if( std::optional busy = checkForBusy() ) + return tl::unexpected( *busy ); + + HANDLER_RESULT documentValidation = validateDocument( aMsg.board() ); + + if( !documentValidation ) + return tl::unexpected( documentValidation.error() ); + + if( aMsg.zones().empty() ) + { + TOOL_MANAGER* mgr = frame()->GetToolManager(); + frame()->CallAfter( [mgr]() + { + mgr->RunAction( PCB_ACTIONS::zoneFillAll ); + } ); + } + else + { + // TODO + ApiResponseStatus e; + e.set_status( ApiStatusCode::AS_UNIMPLEMENTED ); + return tl::unexpected( e ); + } + + return Empty(); +} diff --git a/pcbnew/api/api_handler_pcb.h b/pcbnew/api/api_handler_pcb.h index 13b6e32719..55d836f35d 100644 --- a/pcbnew/api/api_handler_pcb.h +++ b/pcbnew/api/api_handler_pcb.h @@ -23,14 +23,16 @@ #include -#include - +#include +#include +#include #include - +#include #include using namespace kiapi; using namespace kiapi::common; +using namespace kiapi::board::commands; using google::protobuf::Empty; @@ -44,7 +46,7 @@ class PCB_TRACK; class PROPERTY_BASE; -class API_HANDLER_PCB : public API_HANDLER +class API_HANDLER_PCB : public API_HANDLER_EDITOR { public: API_HANDLER_PCB( PCB_EDIT_FRAME* aFrame ); @@ -52,35 +54,64 @@ public: private: typedef std::map PROTO_PROPERTY_MAP; - static std::unique_ptr createItemForType( KICAD_T aType, + static HANDLER_RESULT> createItemForType( KICAD_T aType, BOARD_ITEM_CONTAINER* aContainer ); - HANDLER_RESULT handleRunAction( commands::RunAction& aMsg ); + HANDLER_RESULT handleRunAction( commands::RunAction& aMsg, + const HANDLER_CONTEXT& aCtx ); HANDLER_RESULT handleGetOpenDocuments( - commands::GetOpenDocuments& aMsg ); + commands::GetOpenDocuments& aMsg, const HANDLER_CONTEXT& aCtx ); - HANDLER_RESULT handleBeginCommit( commands::BeginCommit& aMsg ); - HANDLER_RESULT handleEndCommit( commands::EndCommit& aMsg ); + HANDLER_RESULT handleGetItems( commands::GetItems& aMsg, + const HANDLER_CONTEXT& aCtx ); - HANDLER_RESULT handleCreateItems( commands::CreateItems& aMsg ); - HANDLER_RESULT handleGetItems( commands::GetItems& aMsg ); - HANDLER_RESULT handleUpdateItems( commands::UpdateItems& aMsg ); - HANDLER_RESULT handleDeleteItems( commands::DeleteItems& aMsg ); + HANDLER_RESULT handleGetStackup( GetBoardStackup& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleGetGraphicsDefaults( GetGraphicsDefaults& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleGetTextExtents( GetTextExtents& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleInteractiveMoveItems( InteractiveMoveItems& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleGetNets( GetNets& aMsg, + const HANDLER_CONTEXT& aCtx ); + + HANDLER_RESULT handleRefillZones( RefillZones& aMsg, const HANDLER_CONTEXT& aCtx ); + +protected: + std::unique_ptr createCommit() override; + + kiapi::common::types::DocumentType thisDocumentType() const override + { + return kiapi::common::types::DOCTYPE_PCB; + } + + bool validateDocumentInternal( const DocumentSpecifier& aDocument ) const override; + + void deleteItemsInternal( std::map& aItemsToDelete, + const HANDLER_CONTEXT& aCtx ) override; + + std::optional getItemFromDocument( const DocumentSpecifier& aDocument, + const KIID& aId ) override; private: + PCB_EDIT_FRAME* frame() const; - bool validateItemHeaderDocument( const common::types::ItemHeader& aHeader ); + void pushCurrentCommit( const HANDLER_CONTEXT& aCtx, const wxString& aMessage ) override; - BOARD_COMMIT* getCurrentCommit(); + std::optional getItemById( const KIID& aId ) const; - void pushCurrentCommit( const std::string& aMessage ); - - PCB_EDIT_FRAME* m_frame; - - std::unique_ptr m_commit; - - bool m_transactionInProgress; + HANDLER_RESULT handleCreateUpdateItemsInternal( bool aCreate, + const HANDLER_CONTEXT& aCtx, + const types::ItemHeader &aHeader, + const google::protobuf::RepeatedPtrField& aItems, + std::function aItemHandler ) + override; }; #endif //KICAD_API_HANDLER_PCB_H diff --git a/pcbnew/api/api_pcb_enums.cpp b/pcbnew/api/api_pcb_enums.cpp new file mode 100644 index 0000000000..5cf5182880 --- /dev/null +++ b/pcbnew/api/api_pcb_enums.cpp @@ -0,0 +1,105 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include +#include + +#include +#include + +using namespace kiapi::board; + +template<> +types::PadStackShape ToProtoEnum( PAD_SHAPE aValue ) +{ + switch( aValue ) + { + case PAD_SHAPE::CIRCLE: return types::PadStackShape::PSS_CIRCLE; + case PAD_SHAPE::RECTANGLE: return types::PadStackShape::PSS_RECTANGLE; + case PAD_SHAPE::OVAL: return types::PadStackShape::PSS_OVAL; + case PAD_SHAPE::TRAPEZOID: return types::PadStackShape::PSS_TRAPEZOID; + case PAD_SHAPE::ROUNDRECT: return types::PadStackShape::PSS_ROUNDRECT; + case PAD_SHAPE::CHAMFERED_RECT: return types::PadStackShape::PSS_CHAMFEREDRECT; + case PAD_SHAPE::CUSTOM: return types::PadStackShape::PSS_CUSTOM; + + default: + wxCHECK_MSG( false, types::PadStackShape::PSS_UNKNOWN, + "Unhandled case in ToProtoEnum"); + } +} + + +template<> +PAD_SHAPE FromProtoEnum( types::PadStackShape aValue ) +{ + switch( aValue ) + { + case types::PadStackShape::PSS_CIRCLE: return PAD_SHAPE::CIRCLE; + case types::PadStackShape::PSS_RECTANGLE: return PAD_SHAPE::RECTANGLE; + case types::PadStackShape::PSS_OVAL: return PAD_SHAPE::OVAL; + case types::PadStackShape::PSS_TRAPEZOID: return PAD_SHAPE::TRAPEZOID; + case types::PadStackShape::PSS_ROUNDRECT: return PAD_SHAPE::ROUNDRECT; + case types::PadStackShape::PSS_CHAMFEREDRECT: return PAD_SHAPE::CHAMFERED_RECT; + case types::PadStackShape::PSS_CUSTOM: return PAD_SHAPE::CUSTOM; + + default: + wxCHECK_MSG( false, PAD_SHAPE::CIRCLE, + "Unhandled case in FromProtoEnum" ); + } +} + + +template<> +types::ZoneConnectionStyle ToProtoEnum( ZONE_CONNECTION aValue ) +{ + switch( aValue ) + { + case ZONE_CONNECTION::INHERITED: return types::ZoneConnectionStyle::ZCS_INHERITED; + case ZONE_CONNECTION::NONE: return types::ZoneConnectionStyle::ZCS_NONE; + case ZONE_CONNECTION::THERMAL: return types::ZoneConnectionStyle::ZCS_THERMAL; + case ZONE_CONNECTION::FULL: return types::ZoneConnectionStyle::ZCS_FULL; + case ZONE_CONNECTION::THT_THERMAL: return types::ZoneConnectionStyle::ZCS_PTH_THERMAL; + + default: + wxCHECK_MSG( false, types::ZoneConnectionStyle::ZCS_UNKNOWN, + "Unhandled case in ToProtoEnum"); + } +} + + +template<> +ZONE_CONNECTION FromProtoEnum( types::ZoneConnectionStyle aValue ) +{ + switch( aValue ) + { + case types::ZoneConnectionStyle::ZCS_UNKNOWN: return ZONE_CONNECTION::INHERITED; + case types::ZoneConnectionStyle::ZCS_INHERITED: return ZONE_CONNECTION::INHERITED; + case types::ZoneConnectionStyle::ZCS_NONE: return ZONE_CONNECTION::NONE; + case types::ZoneConnectionStyle::ZCS_THERMAL: return ZONE_CONNECTION::THERMAL; + case types::ZoneConnectionStyle::ZCS_FULL: return ZONE_CONNECTION::FULL; + case types::ZoneConnectionStyle::ZCS_PTH_THERMAL: return ZONE_CONNECTION::THT_THERMAL; + + default: + wxCHECK_MSG( false, ZONE_CONNECTION::INHERITED, + "Unhandled case in FromProtoEnum" ); + } +} diff --git a/pcbnew/api/api_pcb_utils.cpp b/pcbnew/api/api_pcb_utils.cpp new file mode 100644 index 0000000000..e6da507630 --- /dev/null +++ b/pcbnew/api/api_pcb_utils.cpp @@ -0,0 +1,100 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 Jon Evans + * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +std::unique_ptr CreateItemForType( KICAD_T aType, BOARD_ITEM_CONTAINER* aContainer ) +{ + switch( aType ) + { + case PCB_TRACE_T: return std::make_unique( aContainer ); + case PCB_ARC_T: return std::make_unique( aContainer ); + case PCB_VIA_T: return std::make_unique( aContainer ); + case PCB_TEXT_T: return std::make_unique( aContainer ); + case PCB_TEXTBOX_T: return std::make_unique( aContainer ); + case PCB_SHAPE_T: return std::make_unique( aContainer ); + case PCB_ZONE_T: return std::make_unique( aContainer ); + case PCB_GROUP_T: return std::make_unique( aContainer ); + case PCB_REFERENCE_IMAGE_T: return std::make_unique( aContainer ); + + case PCB_PAD_T: + { + FOOTPRINT* footprint = dynamic_cast( aContainer ); + + if( !footprint ) + return nullptr; + + return std::make_unique( footprint ); + } + + case PCB_FOOTPRINT_T: + { + BOARD* board = dynamic_cast( aContainer ); + + if( !board ) + return nullptr; + + return std::make_unique( board ); + } + + default: + return nullptr; + } +} + +namespace kiapi::board +{ + +void PackLayerSet( google::protobuf::RepeatedField& aOutput, const LSET& aLayerSet ) +{ + for( const PCB_LAYER_ID& layer : aLayerSet.Seq() ) + aOutput.Add( ToProtoEnum( layer ) ); +} + + +LSET UnpackLayerSet( const google::protobuf::RepeatedField& aProtoLayerSet ) +{ + LSET set; + + for( int layer : aProtoLayerSet ) + { + wxCHECK2( layer >= F_Cu && layer < PCB_LAYER_ID_COUNT, continue ); + PCB_LAYER_ID boardLayer = + FromProtoEnum( static_cast( layer ) ); + set.set( boardLayer ); + } + + return set; +} + +} // namespace kiapi::board diff --git a/pcbnew/api/api_pcb_utils.h b/pcbnew/api/api_pcb_utils.h new file mode 100644 index 0000000000..953dcac420 --- /dev/null +++ b/pcbnew/api/api_pcb_utils.h @@ -0,0 +1,45 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 Jon Evans + * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#ifndef KICAD_API_PCB_UTLIS_H +#define KICAD_API_PCB_UTLIS_H + +#include +#include +#include +#include +#include +#include + +class BOARD_ITEM; +class BOARD_ITEM_CONTAINER; + +std::unique_ptr CreateItemForType( KICAD_T aType, BOARD_ITEM_CONTAINER* aContainer ); + +namespace kiapi::board +{ + +void PackLayerSet( google::protobuf::RepeatedField& aOutput, const LSET& aLayerSet ); + +LSET UnpackLayerSet( const google::protobuf::RepeatedField& aInput ); + +} // namespace kiapi::board + +#endif //KICAD_API_PCB_UTLIS_H diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp index c35c2505cf..19ae065bb2 100644 --- a/pcbnew/board.cpp +++ b/pcbnew/board.cpp @@ -154,37 +154,26 @@ BOARD::~BOARD() // Clean up the owned elements DeleteMARKERs(); - for( ZONE* zone : m_zones ) - delete zone; - - m_zones.clear(); - delete m_SolderMaskBridges; - for( FOOTPRINT* footprint : m_footprints ) - delete footprint; + BOARD_ITEM_SET ownedItems = GetItemSet(); + m_zones.clear(); m_footprints.clear(); - - for( PCB_TRACK* t : m_tracks ) - delete t; - m_tracks.clear(); - - for( BOARD_ITEM* d : m_drawings ) - delete d; - m_drawings.clear(); - - for( PCB_GROUP* g : m_groups ) - delete g; - m_groups.clear(); + // Generators not currently returned by GetItemSet for( PCB_GENERATOR* g : m_generators ) - delete g; + ownedItems.insert( g ); m_generators.clear(); + + // Delete the owned items after clearing the containers, because some item dtors + // cause call chains that query the containers + for( BOARD_ITEM* item : ownedItems ) + delete item; } diff --git a/pcbnew/board_stackup_manager/board_stackup.cpp b/pcbnew/board_stackup_manager/board_stackup.cpp index 567da8b3d4..e27f920678 100644 --- a/pcbnew/board_stackup_manager/board_stackup.cpp +++ b/pcbnew/board_stackup_manager/board_stackup.cpp @@ -27,6 +27,9 @@ #include #include // For _HKI definition #include "stackup_predefined_prms.h" +#include +#include +#include bool DIELECTRIC_PRMS::operator==( const DIELECTRIC_PRMS& aOther ) const @@ -418,6 +421,44 @@ bool BOARD_STACKUP::operator==( const BOARD_STACKUP& aOther ) const } +void BOARD_STACKUP::Serialize( google::protobuf::Any& aContainer ) const +{ + kiapi::board::BoardStackup stackup; + + for( const BOARD_STACKUP_ITEM* item : m_list ) + { + kiapi::board::BoardStackupLayer* layer = stackup.mutable_layers()->Add(); + + // TODO dielectric sub-layers + layer->mutable_thickness()->set_value_nm( item->GetThickness() ); + layer->set_layer( ToProtoEnum( + item->GetBrdLayerId() ) ); + + switch( item->GetType() ) + { + case BOARD_STACKUP_ITEM_TYPE::BS_ITEM_TYPE_COPPER: + { + layer->mutable_copper()->New(); + // (no copper params yet...) + break; + } + + default: + // TODO + break; + } + } + + aContainer.PackFrom( stackup ); +} + + +bool BOARD_STACKUP::Deserialize( const google::protobuf::Any& aContainer ) +{ + return true; +} + + void BOARD_STACKUP::RemoveAll() { for( BOARD_STACKUP_ITEM* item : m_list ) diff --git a/pcbnew/board_stackup_manager/board_stackup.h b/pcbnew/board_stackup_manager/board_stackup.h index d3ab92892f..dd062b1cfb 100644 --- a/pcbnew/board_stackup_manager/board_stackup.h +++ b/pcbnew/board_stackup_manager/board_stackup.h @@ -29,6 +29,7 @@ #include #include #include +#include class BOARD; class BOARD_DESIGN_SETTINGS; @@ -213,7 +214,7 @@ private: * @note There are a few other parameters related to the physical stackup like finish type, * impedance control and a few others. */ -class BOARD_STACKUP +class BOARD_STACKUP : public SERIALIZABLE { public: BOARD_STACKUP(); @@ -225,6 +226,10 @@ public: ~BOARD_STACKUP() { RemoveAll(); } + void Serialize( google::protobuf::Any &aContainer ) const override; + + bool Deserialize( const google::protobuf::Any &aContainer ) override; + const std::vector& GetList() const { return m_list; } /// @return a reference to the layer aIndex, or nullptr if not exists diff --git a/pcbnew/cross-probing.cpp b/pcbnew/cross-probing.cpp index 7802b8b6ff..196079fd74 100644 --- a/pcbnew/cross-probing.cpp +++ b/pcbnew/cross-probing.cpp @@ -719,7 +719,7 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail ) } case MAIL_RELOAD_PLUGINS: - GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload ); + GetToolManager()->RunAction( ACTIONS::pluginsReload ); break; // many many others. diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index 9871dafcd7..2558a5d9de 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -23,6 +23,7 @@ * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #include @@ -53,6 +54,12 @@ #include #include "convert_basic_shapes_to_polygon.h" +#include +#include +#include +#include +#include + FOOTPRINT::FOOTPRINT( BOARD* parent ) : BOARD_ITEM_CONTAINER((BOARD_ITEM*) parent, PCB_FOOTPRINT_T ), @@ -253,6 +260,248 @@ FOOTPRINT::~FOOTPRINT() } +void FOOTPRINT::Serialize( google::protobuf::Any &aContainer ) const +{ + kiapi::board::types::FootprintInstance footprint; + + footprint.mutable_id()->set_value( m_Uuid.AsStdString() ); + footprint.mutable_position()->set_x_nm( GetPosition().x ); + footprint.mutable_position()->set_y_nm( GetPosition().y ); + footprint.mutable_orientation()->set_value_degrees( GetOrientationDegrees() ); + footprint.set_layer( ToProtoEnum( GetLayer() ) ); + footprint.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED + : kiapi::common::types::LockedState::LS_UNLOCKED ); + + google::protobuf::Any buf; + GetField( REFERENCE_FIELD )->Serialize( buf ); + buf.UnpackTo( footprint.mutable_reference_field() ); + GetField( VALUE_FIELD )->Serialize( buf ); + buf.UnpackTo( footprint.mutable_value_field() ); + GetField( DATASHEET_FIELD )->Serialize( buf ); + buf.UnpackTo( footprint.mutable_datasheet_field() ); + GetField( DESCRIPTION_FIELD )->Serialize( buf ); + buf.UnpackTo( footprint.mutable_description_field() ); + + kiapi::board::types::FootprintAttributes* attrs = footprint.mutable_attributes(); + + attrs->set_not_in_schematic( IsBoardOnly() ); + attrs->set_exclude_from_position_files( IsExcludedFromPosFiles() ); + attrs->set_exclude_from_bill_of_materials( IsExcludedFromBOM() ); + attrs->set_exempt_from_courtyard_requirement( AllowMissingCourtyard() ); + attrs->set_do_not_populate( IsDNP() ); + + kiapi::board::types::Footprint* def = footprint.mutable_definition(); + + def->mutable_id()->CopyFrom( kiapi::common::LibIdToProto( GetFPID() ) ); + // anchor? + def->mutable_attributes()->set_description( GetLibDescription().ToStdString() ); + def->mutable_attributes()->set_keywords( GetKeywords().ToStdString() ); + + // TODO: serialize library mandatory fields + + kiapi::board::types::DesignRuleOverrides* overrides = def->mutable_overrides(); + + if( GetLocalClearance().has_value() ) + overrides->mutable_clearance()->set_value_nm( *GetLocalClearance() ); + + if( GetLocalSolderMaskMargin().has_value() ) + overrides->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() ); + + if( GetLocalSolderPasteMargin().has_value() ) + overrides->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() ); + + if( GetLocalSolderPasteMarginRatio().has_value() ) + overrides->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() ); + + overrides->set_zone_connection( + ToProtoEnum( GetLocalZoneConnection() ) ); + + for( const wxString& group : GetNetTiePadGroups() ) + { + kiapi::board::types::NetTieDefinition* netTie = def->add_net_ties(); + wxStringTokenizer tokenizer( group, " " ); + + while( tokenizer.HasMoreTokens() ) + netTie->add_pad_number( tokenizer.GetNextToken().ToStdString() ); + } + + for( PCB_LAYER_ID layer : GetPrivateLayers().Seq() ) + { + def->add_private_layers( + ToProtoEnum( layer ) ); + } + + for( const PCB_FIELD* item : Fields() ) + { + if( item->GetId() < MANDATORY_FIELDS ) + continue; + + google::protobuf::Any* itemMsg = def->add_items(); + item->Serialize( *itemMsg ); + } + + for( const PAD* item : Pads() ) + { + google::protobuf::Any* itemMsg = def->add_items(); + item->Serialize( *itemMsg ); + } + + for( const BOARD_ITEM* item : GraphicalItems() ) + { + google::protobuf::Any* itemMsg = def->add_items(); + item->Serialize( *itemMsg ); + } + + for( const ZONE* item : Zones() ) + { + google::protobuf::Any* itemMsg = def->add_items(); + item->Serialize( *itemMsg ); + } + + // TODO: 3D models + + aContainer.PackFrom( footprint ); +} + + +bool FOOTPRINT::Deserialize( const google::protobuf::Any &aContainer ) +{ + kiapi::board::types::FootprintInstance footprint; + + if( !aContainer.UnpackTo( &footprint ) ) + return false; + + const_cast( m_Uuid ) = KIID( footprint.id().value() ); + SetPosition( VECTOR2I( footprint.position().x_nm(), footprint.position().y_nm() ) ); + SetOrientationDegrees( footprint.orientation().value_degrees() ); + SetLayer( FromProtoEnum( footprint.layer() ) ); + SetLocked( footprint.locked() == kiapi::common::types::LockedState::LS_LOCKED ); + + google::protobuf::Any buf; + kiapi::board::types::Field mandatoryField; + + if( footprint.has_reference_field() ) + { + mandatoryField = footprint.reference_field(); + mandatoryField.mutable_id()->set_id( REFERENCE_FIELD ); + buf.PackFrom( mandatoryField ); + GetField( REFERENCE_FIELD )->Deserialize( buf ); + } + + if( footprint.has_value_field() ) + { + mandatoryField = footprint.value_field(); + mandatoryField.mutable_id()->set_id( VALUE_FIELD ); + buf.PackFrom( mandatoryField ); + GetField( VALUE_FIELD )->Deserialize( buf ); + } + + if( footprint.has_datasheet_field() ) + { + mandatoryField = footprint.datasheet_field(); + mandatoryField.mutable_id()->set_id( DATASHEET_FIELD ); + buf.PackFrom( mandatoryField ); + GetField( DATASHEET_FIELD )->Deserialize( buf ); + } + + if( footprint.has_description_field() ) + { + mandatoryField = footprint.description_field(); + mandatoryField.mutable_id()->set_id( DESCRIPTION_FIELD ); + buf.PackFrom( mandatoryField ); + GetField( DESCRIPTION_FIELD )->Deserialize( buf ); + } + + SetBoardOnly( footprint.attributes().not_in_schematic() ); + SetExcludedFromBOM( footprint.attributes().exclude_from_bill_of_materials() ); + SetExcludedFromPosFiles( footprint.attributes().exclude_from_position_files() ); + SetAllowMissingCourtyard( footprint.attributes().exempt_from_courtyard_requirement() ); + SetDNP( footprint.attributes().do_not_populate() ); + + // Definition + SetFPID( kiapi::common::LibIdFromProto( footprint.definition().id() ) ); + // TODO: how should anchor be handled? + SetLibDescription( footprint.definition().attributes().description() ); + SetKeywords( footprint.definition().attributes().keywords() ); + + const kiapi::board::types::DesignRuleOverrides& overrides = footprint.overrides(); + + if( overrides.has_clearance() ) + SetLocalClearance( overrides.clearance().value_nm() ); + else + SetLocalClearance( std::nullopt ); + + if( overrides.has_solder_mask_margin() ) + SetLocalSolderMaskMargin( overrides.solder_mask_margin().value_nm() ); + else + SetLocalSolderMaskMargin( std::nullopt ); + + if( overrides.has_solder_paste_margin() ) + SetLocalSolderPasteMargin( overrides.solder_paste_margin().value_nm() ); + else + SetLocalSolderPasteMargin( std::nullopt ); + + if( overrides.has_solder_paste_margin_ratio() ) + SetLocalSolderPasteMarginRatio( overrides.solder_paste_margin_ratio().value() ); + else + SetLocalSolderPasteMarginRatio( std::nullopt ); + + SetLocalZoneConnection( FromProtoEnum( overrides.zone_connection() ) ); + + for( const kiapi::board::types::NetTieDefinition& netTieMsg : footprint.definition().net_ties() ) + { + wxString group; + + for( const std::string& pad : netTieMsg.pad_number() ) + group.Append( wxString::Format( wxT( "%s " ), pad ) ); + + group.Trim(); + AddNetTiePadGroup( group ); + } + + LSET privateLayers; + + for( int layerMsg : footprint.definition().private_layers() ) + { + auto layer = static_cast( layerMsg ); + privateLayers.set( FromProtoEnum( layer ) ); + } + + SetPrivateLayers( privateLayers ); + + // Footprint items + for( PCB_FIELD* field : Fields() ) + { + if( field->GetId() >= MANDATORY_FIELDS ) + Remove( field ); + } + + Pads().clear(); + GraphicalItems().clear(); + Zones().clear(); + Groups().clear(); + Models().clear(); + + for( const google::protobuf::Any& itemMsg : footprint.definition().items() ) + { + std::optional type = kiapi::common::TypeNameFromAny( itemMsg ); + + if( !type ) + continue; + + std::unique_ptr item = CreateItemForType( *type, this ); + + if( item && item->Deserialize( itemMsg ) ) + Add( item.release(), ADD_MODE::APPEND ); + } + + // TODO: 3D models + + return true; +} + + PCB_FIELD* FOOTPRINT::GetField( MANDATORY_FIELD_T aFieldType ) { return m_fields[aFieldType]; diff --git a/pcbnew/footprint.h b/pcbnew/footprint.h index 46e187bf32..65829b739a 100644 --- a/pcbnew/footprint.h +++ b/pcbnew/footprint.h @@ -117,6 +117,9 @@ public: FOOTPRINT& operator=( const FOOTPRINT& aOther ); FOOTPRINT& operator=( FOOTPRINT&& aOther ); + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + static inline bool ClassOf( const EDA_ITEM* aItem ) { return aItem && aItem->Type() == PCB_FOOTPRINT_T; diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp index e26e4e902b..ce326be7bb 100644 --- a/pcbnew/footprint_edit_frame.cpp +++ b/pcbnew/footprint_edit_frame.cpp @@ -1151,9 +1151,9 @@ void FOOTPRINT_EDIT_FRAME::setupTools() PCB_EDIT_FRAME* pcbframe = static_cast( Kiway().Player( FRAME_PCB_EDITOR, false ) ); if( pcbframe ) - pcbframe->GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload ); + pcbframe->GetToolManager()->RunAction( ACTIONS::pluginsReload ); else - m_toolManager->RunAction( PCB_ACTIONS::pluginsReload ); + m_toolManager->RunAction( ACTIONS::pluginsReload ); } diff --git a/pcbnew/footprint_wizard_frame.cpp b/pcbnew/footprint_wizard_frame.cpp index 753cf22d19..c67e201086 100644 --- a/pcbnew/footprint_wizard_frame.cpp +++ b/pcbnew/footprint_wizard_frame.cpp @@ -643,7 +643,7 @@ void FOOTPRINT_WIZARD_FRAME::PythonPluginsReload() PCB_EDIT_FRAME* pcbframe = static_cast( Kiway().Player( FRAME_PCB_EDITOR, false ) ); if( pcbframe ) - pcbframe->GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload ); + pcbframe->GetToolManager()->RunAction( ACTIONS::pluginsReload ); else - GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload ); + GetToolManager()->RunAction( ACTIONS::pluginsReload ); } diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp index 73b3a2db95..e8c59ce423 100644 --- a/pcbnew/menubar_pcb_editor.cpp +++ b/pcbnew/menubar_pcb_editor.cpp @@ -436,7 +436,7 @@ void PCB_EDIT_FRAME::doReCreateMenuBar() submenuActionPlugins->SetTitle( _( "External Plugins" ) ); submenuActionPlugins->SetIcon( BITMAPS::puzzle_piece ); - submenuActionPlugins->Add( PCB_ACTIONS::pluginsReload ); + submenuActionPlugins->Add( ACTIONS::pluginsReload ); submenuActionPlugins->Add( PCB_ACTIONS::pluginsShowFolder ); // Populate the Action Plugin sub-menu: Must be done before Add diff --git a/pcbnew/pad.cpp b/pcbnew/pad.cpp index 3198983b45..8cbc7ba3ab 100644 --- a/pcbnew/pad.cpp +++ b/pcbnew/pad.cpp @@ -50,6 +50,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -135,6 +139,160 @@ PAD& PAD::operator=( const PAD &aOther ) } +void PAD::Serialize( google::protobuf::Any &aContainer ) const +{ + kiapi::board::types::Pad pad; + + pad.mutable_id()->set_value( m_Uuid.AsStdString() ); + kiapi::common::PackVector2( *pad.mutable_position(), GetPosition() ); + pad.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED + : kiapi::common::types::LockedState::LS_UNLOCKED ); + pad.mutable_net()->mutable_code()->set_value( GetNetCode() ); + pad.mutable_net()->set_name( GetNetname() ); + + kiapi::board::types::PadStack* padstack = pad.mutable_pad_stack(); + padstack->set_type( kiapi::board::types::PadStackType::PST_THROUGH ); + padstack->set_start_layer( + ToProtoEnum( m_layer ) ); + padstack->set_end_layer( + ToProtoEnum( FlipLayer( m_layer ) ) ); + kiapi::common::PackVector2( *padstack->mutable_drill_diameter(), + { GetDrillSizeX(), GetDrillSizeY() } ); + padstack->mutable_angle()->set_value_degrees( GetOrientationDegrees() ); + + kiapi::board::types::PadStackLayer* stackLayer = padstack->add_layers(); + kiapi::board::PackLayerSet( *stackLayer->mutable_layers(), GetLayerSet() ); + kiapi::common::PackVector2( *stackLayer->mutable_size(), + { GetSizeX(), GetSizeY() } ); + stackLayer->set_shape( + ToProtoEnum( GetShape() ) ); + + kiapi::board::types::UnconnectedLayerRemoval ulr; + + if( m_removeUnconnectedLayer ) + { + if( m_keepTopBottomLayer ) + ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END; + else + ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE; + } + else + { + ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP; + } + + padstack->set_unconnected_layer_removal( ulr ); + + kiapi::board::types::DesignRuleOverrides* overrides = pad.mutable_overrides(); + + if( GetLocalClearance().has_value() ) + overrides->mutable_clearance()->set_value_nm( *GetLocalClearance() ); + + if( GetLocalSolderMaskMargin().has_value() ) + overrides->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() ); + + if( GetLocalSolderPasteMargin().has_value() ) + overrides->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() ); + + if( GetLocalSolderPasteMarginRatio().has_value() ) + overrides->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() ); + + overrides->set_zone_connection( + ToProtoEnum( GetLocalZoneConnection() ) ); + + kiapi::board::types::ThermalSpokeSettings* thermals = pad.mutable_thermal_spokes(); + + thermals->set_width( GetThermalSpokeWidth() ); + thermals->set_gap( GetThermalGap() ); + thermals->mutable_angle()->set_value_degrees( GetThermalSpokeAngleDegrees() ); + + aContainer.PackFrom( pad ); +} + + +bool PAD::Deserialize( const google::protobuf::Any &aContainer ) +{ + kiapi::board::types::Pad pad; + + if( !aContainer.UnpackTo( &pad ) ) + return false; + + const_cast( m_Uuid ) = KIID( pad.id().value() ); + SetPosition( kiapi::common::UnpackVector2( pad.position() ) ); + SetNetCode( pad.net().code().value() ); + SetLocked( pad.locked() == kiapi::common::types::LockedState::LS_LOCKED ); + + const kiapi::board::types::PadStack& padstack = pad.pad_stack(); + + SetLayer( FromProtoEnum( + padstack.start_layer() ) ); + + SetDrillSize( kiapi::common::UnpackVector2( padstack.drill_diameter() ) ); + SetOrientationDegrees( padstack.angle().value_degrees() ); + + // We don't yet support complex padstacks + if( padstack.layers_size() == 1 ) + { + const kiapi::board::types::PadStackLayer& layer = padstack.layers( 0 ); + SetSize( kiapi::common::UnpackVector2( layer.size() ) ); + SetLayerSet( kiapi::board::UnpackLayerSet( layer.layers() ) ); + SetShape( FromProtoEnum( layer.shape() ) ); + } + + switch( padstack.unconnected_layer_removal() ) + { + case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE: + m_removeUnconnectedLayer = true; + m_keepTopBottomLayer = false; + break; + + case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END: + m_removeUnconnectedLayer = true; + m_keepTopBottomLayer = true; + break; + + default: + case kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP: + m_removeUnconnectedLayer = false; + m_keepTopBottomLayer = false; + break; + } + + const kiapi::board::types::DesignRuleOverrides& overrides = pad.overrides(); + + if( overrides.has_clearance() ) + SetLocalClearance( overrides.clearance().value_nm() ); + else + SetLocalClearance( std::nullopt ); + + if( overrides.has_solder_mask_margin() ) + SetLocalSolderMaskMargin( overrides.solder_mask_margin().value_nm() ); + else + SetLocalSolderMaskMargin( std::nullopt ); + + if( overrides.has_solder_paste_margin() ) + SetLocalSolderPasteMargin( overrides.solder_paste_margin().value_nm() ); + else + SetLocalSolderPasteMargin( std::nullopt ); + + if( overrides.has_solder_paste_margin_ratio() ) + SetLocalSolderPasteMarginRatio( overrides.solder_paste_margin_ratio().value() ); + else + SetLocalSolderPasteMarginRatio( std::nullopt ); + + SetLocalZoneConnection( FromProtoEnum( overrides.zone_connection() ) ); + + const kiapi::board::types::ThermalSpokeSettings& thermals = pad.thermal_spokes(); + + SetThermalGap( thermals.gap() ); + SetThermalSpokeWidth( thermals.width() ); + SetThermalSpokeAngleDegrees( thermals.angle().value_degrees() ); + + return true; +} + + bool PAD::CanHaveNumber() const { // Aperture pads don't get a number diff --git a/pcbnew/pad.h b/pcbnew/pad.h index 9377082301..b811030c76 100644 --- a/pcbnew/pad.h +++ b/pcbnew/pad.h @@ -65,6 +65,9 @@ public: PAD( const PAD& aPad ); PAD& operator=( const PAD &aOther ); + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + /* * Default layers used for pads, according to the pad type. * diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 60648d29e7..652aecffbe 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -266,6 +266,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : ReCreateVToolbar(); ReCreateOptToolbar(); +#ifdef KICAD_IPC_API wxTheApp->Bind( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED, [&]( wxCommandEvent& aEvt ) { @@ -273,7 +274,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : ReCreateHToolbar(); aEvt.Skip(); } ); - +#endif m_propertiesPanel = new PCB_PROPERTIES_PANEL( this, this ); @@ -438,7 +439,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : PythonSyncProjectName(); // Sync action plugins in case they changed since the last time the frame opened - GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload ); + GetToolManager()->RunAction( ACTIONS::pluginsReload ); #ifdef KICAD_IPC_API m_apiHandler = std::make_unique( this ); @@ -2062,24 +2063,6 @@ void PCB_EDIT_FRAME::PythonSyncProjectName() } -void PCB_EDIT_FRAME::OnApiPluginMenu( wxCommandEvent& aEvent ) -{ - API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager(); - - if( mgr.MenuBindings().count( aEvent.GetId() ) ) - mgr.InvokeAction( mgr.MenuBindings().at( aEvent.GetId() ) ); -} - - -void PCB_EDIT_FRAME::OnApiPluginButton( wxCommandEvent& aEvent ) -{ - API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager(); - - if( mgr.ButtonBindings().count( aEvent.GetId() ) ) - mgr.InvokeAction( mgr.ButtonBindings().at( aEvent.GetId() ) ); -} - - void PCB_EDIT_FRAME::ShowFootprintPropertiesDialog( FOOTPRINT* aFootprint ) { if( aFootprint == nullptr ) @@ -2496,6 +2479,26 @@ void PCB_EDIT_FRAME::ProjectChanged() } +bool PCB_EDIT_FRAME::CanAcceptApiCommands() +{ + // For now, be conservative: Don't allow any API use while the user is changing things + if( GetToolManager()->GetCurrentTool() != GetToolManager()->GetTool() ) + return false; + + ZONE_FILLER_TOOL* zoneFillerTool = m_toolManager->GetTool(); + + if( zoneFillerTool->IsBusy() ) + return false; + + ROUTER_TOOL* routerTool = m_toolManager->GetTool(); + + if( routerTool->RoutingInProgress() ) + return false; + + return EDA_BASE_FRAME::CanAcceptApiCommands(); +} + + bool ExportBoardToHyperlynx( BOARD* aBoard, const wxFileName& aPath ); diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index 79f97ecf12..aa0358dd79 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -699,6 +699,8 @@ public: void ProjectChanged() override; + bool CanAcceptApiCommands() override; + wxString GetCurrentFileName() const override; SELECTION& GetCurrentSelection() override; @@ -760,11 +762,6 @@ protected: */ void AddActionPluginTools(); - /** - * Append actions from API plugins to the main toolbar - */ - void AddApiPluginTools(); - /** * Execute action plugin's Run() method and updates undo buffer. * @@ -786,8 +783,7 @@ protected: */ void OnActionPluginButton( wxCommandEvent& aEvent ); - void OnApiPluginMenu( wxCommandEvent& aEvent ); - void OnApiPluginButton( wxCommandEvent& aEvent ); + PLUGIN_ACTION_SCOPE PluginActionScope() const override { return PLUGIN_ACTION_SCOPE::PCB; } /** * Update the state of the GUI after a new board is loaded or created. diff --git a/pcbnew/pcb_field.cpp b/pcbnew/pcb_field.cpp index fb1859cf60..6d8d734400 100644 --- a/pcbnew/pcb_field.cpp +++ b/pcbnew/pcb_field.cpp @@ -27,6 +27,8 @@ #include #include #include +#include + PCB_FIELD::PCB_FIELD( FOOTPRINT* aParent, int aFieldId, const wxString& aName ) : PCB_TEXT( aParent, PCB_FIELD_T ), @@ -44,6 +46,49 @@ PCB_FIELD::PCB_FIELD( const PCB_TEXT& aText, int aFieldId, const wxString& aName } +void PCB_FIELD::Serialize( google::protobuf::Any &aContainer ) const +{ + kiapi::board::types::Field field; + + google::protobuf::Any anyText; + PCB_TEXT::Serialize( anyText ); + anyText.UnpackTo( field.mutable_text() ); + + field.set_name( GetCanonicalName().ToStdString() ); + field.mutable_id()->set_id( GetId() ); + + aContainer.PackFrom( field ); +} + + +bool PCB_FIELD::Deserialize( const google::protobuf::Any &aContainer ) +{ + kiapi::board::types::Field field; + + if( !aContainer.UnpackTo( &field ) ) + return false; + + if( field.has_id() ) + setId( field.id().id() ); + + // Mandatory fields have a blank Name in the KiCad object + if( m_id >= MANDATORY_FIELDS ) + SetName( wxString( field.name().c_str(), wxConvUTF8 ) ); + + if( field.has_text() ) + { + google::protobuf::Any anyText; + anyText.PackFrom( field.text() ); + PCB_TEXT::Deserialize( anyText ); + } + + if( field.text().layer() == kiapi::board::types::BoardLayer::BL_UNKNOWN ) + SetLayer( F_SilkS ); + + return true; +} + + wxString PCB_FIELD::GetName( bool aUseDefaultName ) const { if( m_parent && m_parent->Type() == PCB_FOOTPRINT_T ) diff --git a/pcbnew/pcb_field.h b/pcbnew/pcb_field.h index 22db3b2a70..9beba7f9c0 100644 --- a/pcbnew/pcb_field.h +++ b/pcbnew/pcb_field.h @@ -37,6 +37,9 @@ public: PCB_FIELD( const PCB_TEXT& aText, int aFieldId, const wxString& aName = wxEmptyString ); + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + static inline bool ClassOf( const EDA_ITEM* aItem ) { return aItem && PCB_FIELD_T == aItem->Type(); @@ -114,6 +117,8 @@ protected: void swapData( BOARD_ITEM* aImage ) override; private: + void setId( int aId ) { m_id = aId; } + int m_id; ///< Field index, @see enum MANDATORY_FIELD_T wxString m_name; diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 7dc2e97f34..2a071a6482 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -1944,6 +1944,9 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer ) } break; + + case SHAPE_T::UNDEFINED: + break; } } else diff --git a/pcbnew/pcb_shape.cpp b/pcbnew/pcb_shape.cpp index 57a283e617..bb88535cb9 100644 --- a/pcbnew/pcb_shape.cpp +++ b/pcbnew/pcb_shape.cpp @@ -24,6 +24,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include + #include #include #include @@ -35,6 +38,10 @@ #include #include #include +#include +#include +#include + PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType ) : BOARD_CONNECTED_ITEM( aParent, aItemType ), @@ -54,6 +61,270 @@ PCB_SHAPE::~PCB_SHAPE() { } +// TODO: lift out +kiapi::common::types::PolyLine lineChainToProto( const SHAPE_LINE_CHAIN& aSlc ) +{ + kiapi::common::types::PolyLine msg; + + for( int vertex = 0; vertex < aSlc.PointCount(); vertex = aSlc.NextShape( vertex ) ) + { + kiapi::common::types::PolyLineNode* node = msg.mutable_nodes()->Add(); + + if( aSlc.IsPtOnArc( vertex ) ) + { + const SHAPE_ARC& arc = aSlc.Arc( aSlc.ArcIndex( vertex ) ); + node->mutable_arc()->mutable_start()->set_x_nm( arc.GetP0().x ); + node->mutable_arc()->mutable_start()->set_y_nm( arc.GetP0().y ); + node->mutable_arc()->mutable_mid()->set_x_nm( arc.GetArcMid().x ); + node->mutable_arc()->mutable_mid()->set_y_nm( arc.GetArcMid().y ); + node->mutable_arc()->mutable_end()->set_x_nm( arc.GetP1().x ); + node->mutable_arc()->mutable_end()->set_y_nm( arc.GetP1().y ); + } + else + { + node->mutable_point()->set_x_nm( aSlc.CPoint( vertex ).x ); + node->mutable_point()->set_y_nm( aSlc.CPoint( vertex ).y ); + } + } + + msg.set_closed( aSlc.IsClosed() ); + + return msg; +} + + +void PCB_SHAPE::Serialize( google::protobuf::Any &aContainer ) const +{ + kiapi::board::types::GraphicShape msg; + + msg.mutable_id()->set_value( m_Uuid.AsStdString() ); + msg.set_layer( ToProtoEnum( GetLayer() ) ); + msg.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED + : kiapi::common::types::LockedState::LS_UNLOCKED ); + msg.mutable_net()->mutable_code()->set_value( GetNetCode() ); + msg.mutable_net()->set_name( GetNetname() ); + + kiapi::common::types::StrokeAttributes* stroke + = msg.mutable_attributes()->mutable_stroke(); + kiapi::common::types::GraphicFillAttributes* fill = msg.mutable_attributes()->mutable_fill(); + + stroke->mutable_width()->set_value_nm( GetWidth() ); + + switch( GetLineStyle() ) + { + case LINE_STYLE::DEFAULT: stroke->set_style( kiapi::common::types::SLS_DEFAULT ); break; + case LINE_STYLE::SOLID: stroke->set_style( kiapi::common::types::SLS_SOLID ); break; + case LINE_STYLE::DASH: stroke->set_style( kiapi::common::types::SLS_DASH ); break; + case LINE_STYLE::DOT: stroke->set_style( kiapi::common::types::SLS_DOT ); break; + case LINE_STYLE::DASHDOT: stroke->set_style( kiapi::common::types::SLS_DASHDOT ); break; + case LINE_STYLE::DASHDOTDOT: stroke->set_style( kiapi::common::types::SLS_DASHDOTDOT ); break; + default: break; + } + + switch( GetFillMode() ) + { + case FILL_T::FILLED_SHAPE: fill->set_fill_type( kiapi::common::types::GFT_FILLED ); break; + default: fill->set_fill_type( kiapi::common::types::GFT_UNFILLED ); break; + } + + switch( GetShape() ) + { + case SHAPE_T::SEGMENT: + { + kiapi::board::types::GraphicSegmentAttributes* segment = msg.mutable_segment(); + kiapi::common::PackVector2( *segment->mutable_start(), GetStart() ); + kiapi::common::PackVector2( *segment->mutable_end(), GetEnd() ); + break; + } + + case SHAPE_T::RECTANGLE: + { + kiapi::board::types::GraphicRectangleAttributes* rectangle = msg.mutable_rectangle(); + kiapi::common::PackVector2( *rectangle->mutable_top_left(), GetStart() ); + kiapi::common::PackVector2( *rectangle->mutable_bottom_right(), GetEnd() ); + break; + } + + case SHAPE_T::ARC: + { + kiapi::board::types::GraphicArcAttributes* arc = msg.mutable_arc(); + kiapi::common::PackVector2( *arc->mutable_start(), GetStart() ); + kiapi::common::PackVector2( *arc->mutable_mid(), GetArcMid() ); + kiapi::common::PackVector2( *arc->mutable_end(), GetEnd() ); + break; + } + + case SHAPE_T::CIRCLE: + { + kiapi::board::types::GraphicCircleAttributes* circle = msg.mutable_circle(); + kiapi::common::PackVector2( *circle->mutable_center(), GetStart() ); + kiapi::common::PackVector2( *circle->mutable_radius_point(), GetEnd() ); + break; + } + + case SHAPE_T::POLY: + { + kiapi::common::types::PolySet* polyset = msg.mutable_polygon(); + + for( int idx = 0; idx < GetPolyShape().OutlineCount(); ++idx ) + { + const SHAPE_POLY_SET::POLYGON& poly = GetPolyShape().Polygon( idx ); + + if( poly.empty() ) + continue; + + kiapi::common::types::PolygonWithHoles* polyMsg = polyset->mutable_polygons()->Add(); + polyMsg->mutable_outline()->CopyFrom( lineChainToProto( poly.front() ) ); + + if( poly.size() > 1 ) + { + for( size_t hole = 1; hole < poly.size(); ++hole ) + polyMsg->mutable_holes()->Add( lineChainToProto( poly[hole] ) ); + } + } + break; + } + + case SHAPE_T::BEZIER: + { + kiapi::board::types::GraphicBezierAttributes* bezier = msg.mutable_bezier(); + kiapi::common::PackVector2( *bezier->mutable_start(), GetStart() ); + kiapi::common::PackVector2( *bezier->mutable_control1(), GetBezierC1() ); + kiapi::common::PackVector2( *bezier->mutable_control2(), GetBezierC2() ); + kiapi::common::PackVector2( *bezier->mutable_end(), GetEnd() ); + break; + } + + default: + wxASSERT_MSG( false, "Unhandled shape in PCB_SHAPE::Serialize" ); + } + + aContainer.PackFrom( msg ); +} + + +// TODO(JE) lift out +SHAPE_LINE_CHAIN lineChainFromProto( const kiapi::common::types::PolyLine& aProto ) +{ + SHAPE_LINE_CHAIN slc; + + for( const kiapi::common::types::PolyLineNode& node : aProto.nodes() ) + { + if( node.has_point() ) + { + slc.Append( VECTOR2I( node.point().x_nm(), node.point().y_nm() ) ); + } + else if( node.has_arc() ) + { + slc.Append( SHAPE_ARC( VECTOR2I( node.arc().start().x_nm(), node.arc().start().y_nm() ), + VECTOR2I( node.arc().mid().x_nm(), node.arc().mid().y_nm() ), + VECTOR2I( node.arc().end().x_nm(), node.arc().end().y_nm() ), + 0 /* don't care about width here */ ) ); + } + } + + slc.SetClosed( aProto.closed() ); + + return slc; +} + + +bool PCB_SHAPE::Deserialize( const google::protobuf::Any &aContainer ) +{ + kiapi::board::types::GraphicShape msg; + + if( !aContainer.UnpackTo( &msg ) ) + return false; + + // Initialize everything to a known state that doesn't get touched by every + // codepath below, to make sure the equality operator is consistent + m_start = {}; + m_end = {}; + m_arcCenter = {}; + m_arcMidData = {}; + m_bezierC1 = {}; + m_bezierC2 = {}; + m_editState = 0; + m_proxyItem = false; + m_endsSwapped = false; + + const_cast( m_Uuid ) = KIID( msg.id().value() ); + SetLocked( msg.locked() == kiapi::common::types::LS_LOCKED ); + SetLayer( FromProtoEnum( msg.layer() ) ); + SetNetCode( msg.net().code().value() ); + + SetFilled( msg.attributes().fill().fill_type() == kiapi::common::types::GFT_FILLED ); + SetWidth( msg.attributes().stroke().width().value_nm() ); + + switch( msg.attributes().stroke().style() ) + { + case kiapi::common::types::SLS_DEFAULT: SetLineStyle( LINE_STYLE::DEFAULT ); break; + case kiapi::common::types::SLS_SOLID: SetLineStyle( LINE_STYLE::SOLID ); break; + case kiapi::common::types::SLS_DASH: SetLineStyle( LINE_STYLE::DASH ); break; + case kiapi::common::types::SLS_DOT: SetLineStyle( LINE_STYLE::DOT ); break; + case kiapi::common::types::SLS_DASHDOT: SetLineStyle( LINE_STYLE::DASHDOT ); break; + case kiapi::common::types::SLS_DASHDOTDOT: SetLineStyle( LINE_STYLE::DASHDOTDOT ); break; + default: break; + } + + if( msg.has_segment() ) + { + SetShape( SHAPE_T::SEGMENT ); + SetStart( kiapi::common::UnpackVector2( msg.segment().start() ) ); + SetEnd( kiapi::common::UnpackVector2( msg.segment().end() ) ); + } + else if( msg.has_rectangle() ) + { + SetShape( SHAPE_T::RECTANGLE ); + SetStart( kiapi::common::UnpackVector2( msg.rectangle().top_left() ) ); + SetEnd( kiapi::common::UnpackVector2( msg.rectangle().bottom_right() ) ); + } + else if( msg.has_arc() ) + { + SetShape( SHAPE_T::ARC ); + SetArcGeometry( kiapi::common::UnpackVector2( msg.arc().start() ), + kiapi::common::UnpackVector2( msg.arc().mid() ), + kiapi::common::UnpackVector2( msg.arc().end() ) ); + } + else if( msg.has_circle() ) + { + SetShape( SHAPE_T::CIRCLE ); + SetStart( kiapi::common::UnpackVector2( msg.circle().center() ) ); + SetEnd( kiapi::common::UnpackVector2( msg.circle().radius_point() ) ); + } + else if( msg.has_polygon() ) + { + SetShape( SHAPE_T::POLY ); + const auto& polyMsg = msg.polygon().polygons(); + + SHAPE_POLY_SET sps; + + for( const kiapi::common::types::PolygonWithHoles& polygonWithHoles : polyMsg ) + { + SHAPE_POLY_SET::POLYGON polygon; + + polygon.emplace_back( lineChainFromProto( polygonWithHoles.outline() ) ); + + for( const kiapi::common::types::PolyLine& holeMsg : polygonWithHoles.holes() ) + polygon.emplace_back( lineChainFromProto( holeMsg ) ); + + sps.AddPolygon( polygon ); + } + + SetPolyShape( sps ); + } + else if( msg.has_bezier() ) + { + SetShape( SHAPE_T::BEZIER ); + SetStart( kiapi::common::UnpackVector2( msg.bezier().start() ) ); + SetBezierC1( kiapi::common::UnpackVector2( msg.bezier().control1() ) ); + SetBezierC2( kiapi::common::UnpackVector2( msg.bezier().control2() ) ); + SetEnd( kiapi::common::UnpackVector2( msg.bezier().end() ) ); + } + + return true; +} + bool PCB_SHAPE::IsType( const std::vector& aScanTypes ) const { diff --git a/pcbnew/pcb_shape.h b/pcbnew/pcb_shape.h index 3705cb9e1c..f22d0bf786 100644 --- a/pcbnew/pcb_shape.h +++ b/pcbnew/pcb_shape.h @@ -57,6 +57,9 @@ public: return wxT( "PCB_SHAPE" ); } + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + bool IsConnected() const override; wxString GetFriendlyName() const override { return EDA_SHAPE::GetFriendlyName(); } diff --git a/pcbnew/pcb_text.cpp b/pcbnew/pcb_text.cpp index f4b641610b..e777e2e8f9 100644 --- a/pcbnew/pcb_text.cpp +++ b/pcbnew/pcb_text.cpp @@ -23,6 +23,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include + #include #include #include @@ -37,6 +39,12 @@ #include #include #include +#include +#include +#include + + +using namespace kiapi::common; PCB_TEXT::PCB_TEXT( BOARD_ITEM* parent, KICAD_T idtype ) : @@ -75,6 +83,106 @@ PCB_TEXT::~PCB_TEXT() } +void PCB_TEXT::Serialize( google::protobuf::Any &aContainer ) const +{ + kiapi::board::types::Text boardText; + boardText.set_layer( ToProtoEnum( GetLayer() ) ); + + kiapi::common::types::Text& text = *boardText.mutable_text(); + + text.mutable_id()->set_value( m_Uuid.AsStdString() ); + text.mutable_position()->set_x_nm( GetPosition().x ); + text.mutable_position()->set_y_nm( GetPosition().y ); + text.set_text( GetText().ToStdString() ); + text.set_hyperlink( GetHyperlink().ToStdString() ); + text.set_locked( IsLocked() ? types::LockedState::LS_LOCKED + : types::LockedState::LS_UNLOCKED ); + + kiapi::common::types::TextAttributes* attrs = text.mutable_attributes(); + + if( GetFont() ) + attrs->set_font_name( GetFont()->GetName().ToStdString() ); + + attrs->set_horizontal_alignment( + ToProtoEnum( GetHorizJustify() ) ); + + attrs->set_vertical_alignment( + ToProtoEnum( GetVertJustify() ) ); + + attrs->mutable_angle()->set_value_degrees( GetTextAngleDegrees() ); + attrs->set_line_spacing( GetLineSpacing() ); + attrs->mutable_stroke_width()->set_value_nm( GetTextThickness() ); + attrs->set_italic( IsItalic() ); + attrs->set_bold( IsBold() ); + attrs->set_underlined( GetAttributes().m_Underlined ); + attrs->set_visible( IsVisible() ); + attrs->set_mirrored( IsMirrored() ); + attrs->set_multiline( IsMultilineAllowed() ); + attrs->set_keep_upright( IsKeepUpright() ); + attrs->mutable_size()->set_x_nm( GetTextSize().x ); + attrs->mutable_size()->set_y_nm( GetTextSize().y ); + + text.set_knockout( IsKnockout() ); + + aContainer.PackFrom( boardText ); +} + + +bool PCB_TEXT::Deserialize( const google::protobuf::Any &aContainer ) +{ + kiapi::board::types::Text textWrapper; + + if( !aContainer.UnpackTo( &textWrapper ) ) + return false; + + SetLayer( FromProtoEnum( textWrapper.layer() ) ); + + const kiapi::common::types::Text& text = textWrapper.text(); + + const_cast( m_Uuid ) = KIID( text.id().value() ); + SetPosition( VECTOR2I( text.position().x_nm(), text.position().y_nm() ) ); + SetLocked( text.locked() == kiapi::common::types::LockedState::LS_LOCKED ); + SetText( wxString( text.text().c_str(), wxConvUTF8 ) ); + SetHyperlink( wxString( text.hyperlink().c_str(), wxConvUTF8 ) ); + SetIsKnockout( text.knockout() ); + + if( text.has_attributes() ) + { + TEXT_ATTRIBUTES attrs = GetAttributes(); + + attrs.m_Bold = text.attributes().bold(); + attrs.m_Italic = text.attributes().italic(); + attrs.m_Underlined = text.attributes().underlined(); + attrs.m_Visible = text.attributes().visible(); + attrs.m_Mirrored = text.attributes().mirrored(); + attrs.m_Multiline = text.attributes().multiline(); + attrs.m_KeepUpright = text.attributes().keep_upright(); + attrs.m_Size = VECTOR2I( text.attributes().size().x_nm(), text.attributes().size().y_nm() ); + + if( !text.attributes().font_name().empty() ) + { + attrs.m_Font = KIFONT::FONT::GetFont( + wxString( text.attributes().font_name().c_str(), wxConvUTF8 ), attrs.m_Bold, + attrs.m_Italic ); + } + + attrs.m_Angle = EDA_ANGLE( text.attributes().angle().value_degrees(), DEGREES_T ); + attrs.m_LineSpacing = text.attributes().line_spacing(); + SetTextThickness( text.attributes().stroke_width().value_nm() ); + + attrs.m_Halign = FromProtoEnum( + text.attributes().horizontal_alignment() ); + + attrs.m_Valign = FromProtoEnum( + text.attributes().vertical_alignment() ); + + SetAttributes( attrs ); + } + + return true; +} + + wxString PCB_TEXT::GetShownText( bool aAllowExtraText, int aDepth ) const { const FOOTPRINT* parentFootprint = GetParentFootprint(); diff --git a/pcbnew/pcb_text.h b/pcbnew/pcb_text.h index 9f10c1893c..e6c63ecaff 100644 --- a/pcbnew/pcb_text.h +++ b/pcbnew/pcb_text.h @@ -65,6 +65,9 @@ public: return false; } + void Serialize( google::protobuf::Any &aContainer ) const override; + bool Deserialize( const google::protobuf::Any &aContainer ) override; + void StyleFromSettings( const BOARD_DESIGN_SETTINGS& settings ) override; /** diff --git a/pcbnew/pcb_track.cpp b/pcbnew/pcb_track.cpp index c10a74d65b..faac0c209d 100644 --- a/pcbnew/pcb_track.cpp +++ b/pcbnew/pcb_track.cpp @@ -23,8 +23,6 @@ * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include - #include #include #include @@ -46,6 +44,9 @@ #include #include +#include +#include +#include #include using KIGFX::PCB_PAINTER; @@ -296,15 +297,15 @@ void PCB_TRACK::Serialize( google::protobuf::Any &aContainer ) const kiapi::board::types::Track track; track.mutable_id()->set_value( m_Uuid.AsStdString() ); - track.mutable_start()->set_x_nm( GetPosition().x ); - track.mutable_start()->set_y_nm( GetPosition().y ); + track.mutable_start()->set_x_nm( GetStart().x ); + track.mutable_start()->set_y_nm( GetStart().y ); track.mutable_end()->set_x_nm( GetEnd().x ); track.mutable_end()->set_y_nm( GetEnd().y ); track.mutable_width()->set_value_nm( GetWidth() ); - track.mutable_layer()->set_layer_id( GetLayer() ); + track.set_layer( ToProtoEnum( GetLayer() ) ); track.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED : kiapi::common::types::LockedState::LS_UNLOCKED ); - track.mutable_net()->set_code( GetNetCode() ); + track.mutable_net()->mutable_code()->set_value( GetNetCode() ); track.mutable_net()->set_name( GetNetname() ); aContainer.PackFrom( track ); @@ -322,8 +323,8 @@ bool PCB_TRACK::Deserialize( const google::protobuf::Any &aContainer ) SetStart( VECTOR2I( track.start().x_nm(), track.start().y_nm() ) ); SetEnd( VECTOR2I( track.end().x_nm(), track.end().y_nm() ) ); SetWidth( track.width().value_nm() ); - SetLayer( magic_enum::enum_cast( track.layer().layer_id() ).value_or( F_Cu ) ); - SetNetCode( track.net().code() ); + SetLayer( FromProtoEnum( track.layer() ) ); + SetNetCode( track.net().code().value() ); SetLocked( track.locked() == kiapi::common::types::LockedState::LS_LOCKED ); return true; @@ -335,17 +336,17 @@ void PCB_ARC::Serialize( google::protobuf::Any &aContainer ) const kiapi::board::types::Arc arc; arc.mutable_id()->set_value( m_Uuid.AsStdString() ); - arc.mutable_start()->set_x_nm( GetPosition().x ); - arc.mutable_start()->set_y_nm( GetPosition().y ); + arc.mutable_start()->set_x_nm( GetStart().x ); + arc.mutable_start()->set_y_nm( GetStart().y ); arc.mutable_mid()->set_x_nm( GetMid().x ); arc.mutable_mid()->set_y_nm( GetMid().y ); arc.mutable_end()->set_x_nm( GetEnd().x ); arc.mutable_end()->set_y_nm( GetEnd().y ); arc.mutable_width()->set_value_nm( GetWidth() ); - arc.mutable_layer()->set_layer_id( GetLayer() ); + arc.set_layer( ToProtoEnum( GetLayer() ) ); arc.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED : kiapi::common::types::LockedState::LS_UNLOCKED ); - arc.mutable_net()->set_code( GetNetCode() ); + arc.mutable_net()->mutable_code()->set_value( GetNetCode() ); arc.mutable_net()->set_name( GetNetname() ); aContainer.PackFrom( arc ); @@ -364,8 +365,8 @@ bool PCB_ARC::Deserialize( const google::protobuf::Any &aContainer ) SetMid( VECTOR2I( arc.mid().x_nm(), arc.mid().y_nm() ) ); SetEnd( VECTOR2I( arc.end().x_nm(), arc.end().y_nm() ) ); SetWidth( arc.width().value_nm() ); - SetLayer( magic_enum::enum_cast( arc.layer().layer_id() ).value_or( F_Cu ) ); - SetNetCode( arc.net().code() ); + SetLayer( FromProtoEnum( arc.layer() ) ); + SetNetCode( arc.net().code().value() ); SetLocked( arc.locked() == kiapi::common::types::LockedState::LS_LOCKED ); return true; @@ -379,15 +380,22 @@ void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const via.mutable_id()->set_value( m_Uuid.AsStdString() ); via.mutable_position()->set_x_nm( GetPosition().x ); via.mutable_position()->set_y_nm( GetPosition().y ); - via.mutable_pad_diameter()->set_value_nm( GetWidth() ); - via.mutable_drill_diameter()->set_value_nm( GetDrillValue() ); kiapi::board::types::PadStack* padstack = via.mutable_pad_stack(); padstack->set_type( GetViaType() == VIATYPE::BLIND_BURIED ? kiapi::board::types::PadStackType::PST_BLIND_BURIED : kiapi::board::types::PadStackType::PST_THROUGH ); - padstack->mutable_start_layer()->set_layer_id( m_layer ); - padstack->mutable_end_layer()->set_layer_id( m_bottomLayer ); + padstack->set_start_layer( + ToProtoEnum( m_layer ) ); + padstack->set_end_layer( + ToProtoEnum( m_bottomLayer ) ); + kiapi::common::PackVector2( *padstack->mutable_drill_diameter(), + { GetDrillValue(), GetDrillValue() } ); + + kiapi::board::types::PadStackLayer* stackLayer = padstack->add_layers(); + kiapi::board::PackLayerSet( *stackLayer->mutable_layers(), GetLayerSet() ); + kiapi::common::PackVector2( *stackLayer->mutable_size(), + { GetWidth(), GetWidth() } ); kiapi::board::types::UnconnectedLayerRemoval ulr; @@ -409,7 +417,7 @@ void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const via.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED : kiapi::common::types::LockedState::LS_UNLOCKED ); - via.mutable_net()->set_code( GetNetCode() ); + via.mutable_net()->mutable_code()->set_value( GetNetCode() ); via.mutable_net()->set_name( GetNetname() ); aContainer.PackFrom( via ); @@ -426,11 +434,17 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer ) const_cast( m_Uuid ) = KIID( via.id().value() ); SetStart( VECTOR2I( via.position().x_nm(), via.position().y_nm() ) ); SetEnd( GetStart() ); - SetWidth( via.pad_diameter().value_nm() ); - SetDrill( via.drill_diameter().value_nm() ); + SetDrill( via.pad_stack().drill_diameter().x_nm() ); const kiapi::board::types::PadStack& padstack = via.pad_stack(); + // We don't yet support complex padstacks for vias + if( padstack.layers_size() == 1 ) + { + const kiapi::board::types::PadStackLayer& layer = padstack.layers( 0 ); + SetWidth( layer.size().x_nm() ); + } + switch( padstack.type() ) { case kiapi::board::types::PadStackType::PST_BLIND_BURIED: @@ -444,10 +458,11 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer ) if( GetViaType() != VIATYPE::THROUGH ) { - m_layer = magic_enum::enum_cast( padstack.start_layer().layer_id() ) - .value_or( F_Cu ); - m_bottomLayer = magic_enum::enum_cast( padstack.end_layer().layer_id() ) - .value_or( B_Cu ); + m_layer = FromProtoEnum( + padstack.start_layer() ); + + m_bottomLayer = FromProtoEnum( + padstack.end_layer() ); } else { @@ -474,7 +489,7 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer ) break; } - SetNetCode( via.net().code() ); + SetNetCode( via.net().code().value() ); SetLocked( via.locked() == kiapi::common::types::LockedState::LS_LOCKED ); return true; diff --git a/pcbnew/python/scripting/pcb_scripting_tool.cpp b/pcbnew/python/scripting/pcb_scripting_tool.cpp index d6d9127c31..b8455b39ea 100644 --- a/pcbnew/python/scripting/pcb_scripting_tool.cpp +++ b/pcbnew/python/scripting/pcb_scripting_tool.cpp @@ -24,7 +24,6 @@ #include "pcb_scripting_tool.h" #include -#include #include #include #include @@ -38,6 +37,10 @@ #include #include +#ifdef KICAD_IPC_API +#include +#endif + using initfunc = PyObject* (*)(void); SCRIPTING_TOOL::SCRIPTING_TOOL() : @@ -108,8 +111,10 @@ int SCRIPTING_TOOL::reloadPlugins( const TOOL_EVENT& aEvent ) return -1; } +#ifdef KICAD_IPC_API // TODO move this elsewhere when SWIG plugins are removed Pgm().GetPluginManager().ReloadPlugins(); +#endif if( !m_isFootprintEditor ) { @@ -161,6 +166,6 @@ int SCRIPTING_TOOL::showPluginFolder( const TOOL_EVENT& aEvent ) void SCRIPTING_TOOL::setTransitions() { - Go( &SCRIPTING_TOOL::reloadPlugins, PCB_ACTIONS::pluginsReload.MakeEvent() ); + Go( &SCRIPTING_TOOL::reloadPlugins, ACTIONS::pluginsReload.MakeEvent() ); Go( &SCRIPTING_TOOL::showPluginFolder, PCB_ACTIONS::pluginsShowFolder.MakeEvent() ); } diff --git a/pcbnew/toolbars_pcb_editor.cpp b/pcbnew/toolbars_pcb_editor.cpp index 9e1def8378..5294ed9f6a 100644 --- a/pcbnew/toolbars_pcb_editor.cpp +++ b/pcbnew/toolbars_pcb_editor.cpp @@ -284,8 +284,12 @@ void PCB_EDIT_FRAME::ReCreateHToolbar() // Add SWIG and API plugins bool scriptingAvailable = SCRIPTING::IsWxAvailable(); +#ifdef KICAD_IPC_API bool haveApiPlugins = Pgm().GetCommonSettings()->m_Api.enable_server && !Pgm().GetPluginManager().GetActionsForScope( PLUGIN_ACTION_SCOPE::PCB ).empty(); +#else + bool haveApiPlugins = false; +#endif if( scriptingAvailable || haveApiPlugins ) { @@ -298,7 +302,7 @@ void PCB_EDIT_FRAME::ReCreateHToolbar() } if( haveApiPlugins ) - AddApiPluginTools(); + addApiPluginTools(); } // after adding the buttons to the toolbar, must call Realize() to reflect the changes @@ -306,35 +310,6 @@ void PCB_EDIT_FRAME::ReCreateHToolbar() } -void PCB_EDIT_FRAME::AddApiPluginTools() -{ - // TODO: Add user control over visibility and order - API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager(); - - mgr.ButtonBindings().clear(); - - std::vector actions = mgr.GetActionsForScope( PLUGIN_ACTION_SCOPE::PCB ); - - for( auto& action : actions ) - { - if( !action->show_button ) - continue; - - const wxBitmapBundle& icon = KIPLATFORM::UI::IsDarkTheme() && action->icon_dark.IsOk() - ? action->icon_dark - : action->icon_light; - - wxAuiToolBarItem* button = m_mainToolBar->AddTool( wxID_ANY, wxEmptyString, icon, - action->name ); - - Connect( button->GetId(), wxEVT_COMMAND_MENU_SELECTED, - wxCommandEventHandler( PCB_EDIT_FRAME::OnApiPluginButton ) ); - - mgr.ButtonBindings().insert( { button->GetId(), action->identifier } ); - } -} - - void PCB_EDIT_FRAME::ReCreateOptToolbar() { // Note: diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index e154ee5be5..2a79d313e7 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -985,12 +985,6 @@ TOOL_ACTION PCB_ACTIONS::defaultPadProperties( TOOL_ACTION_ARGS() // SCRIPTING TOOL // -TOOL_ACTION PCB_ACTIONS::pluginsReload( TOOL_ACTION_ARGS() - .Name( "pcbnew.ScriptingTool.pluginsReload" ) - .Scope( AS_GLOBAL ) - .FriendlyName( _( "Refresh Plugins" ) ) - .Tooltip( _( "Reload all python plugins and refresh plugin menus" ) ) - .Icon( BITMAPS::reload ) ); TOOL_ACTION PCB_ACTIONS::pluginsShowFolder( TOOL_ACTION_ARGS() .Name( "pcbnew.ScriptingTool.pluginsShowFolder" ) diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index 6ce5dc13cd..43dcc0f678 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -399,7 +399,6 @@ public: static TOOL_ACTION zoneDuplicate; /// Scripting Actions - static TOOL_ACTION pluginsReload; static TOOL_ACTION pluginsShowFolder; // Global edit tool diff --git a/qa/data/pcbnew/api_kitchen_sink.kicad_pcb b/qa/data/pcbnew/api_kitchen_sink.kicad_pcb new file mode 100644 index 0000000000..2483a6dbc5 --- /dev/null +++ b/qa/data/pcbnew/api_kitchen_sink.kicad_pcb @@ -0,0 +1,3878 @@ +(kicad_pcb + (version 20240225) + (generator "pcbnew") + (generator_version "8.99") + (general + (thickness 1.6) + (legacy_teardrops no) + ) + (paper "A4") + (layers + (0 "F.Cu" signal) + (31 "B.Cu" signal) + (32 "B.Adhes" user "B.Adhesive") + (33 "F.Adhes" user "F.Adhesive") + (34 "B.Paste" user) + (35 "F.Paste" user) + (36 "B.SilkS" user "B.Silkscreen") + (37 "F.SilkS" user "F.Silkscreen") + (38 "B.Mask" user) + (39 "F.Mask" user) + (40 "Dwgs.User" user "User.Drawings") + (41 "Cmts.User" user "User.Comments") + (42 "Eco1.User" user "User.Eco1") + (43 "Eco2.User" user "User.Eco2") + (44 "Edge.Cuts" user) + (45 "Margin" user) + (46 "B.CrtYd" user "B.Courtyard") + (47 "F.CrtYd" user "F.Courtyard") + (48 "B.Fab" user) + (49 "F.Fab" user) + (50 "User.1" user) + (51 "User.2" user) + (52 "User.3" user) + (53 "User.4" user) + (54 "User.5" user) + (55 "User.6" user) + (56 "User.7" user) + (57 "User.8" user) + (58 "User.9" user) + ) + (setup + (pad_to_mask_clearance 0) + (allow_soldermask_bridges_in_footprints no) + (pcbplotparams + (layerselection 0x00010fc_ffffffff) + (plot_on_all_layers_selection 0x0000000_00000000) + (disableapertmacros no) + (usegerberextensions no) + (usegerberattributes yes) + (usegerberadvancedattributes yes) + (creategerberjobfile yes) + (dashed_line_dash_ratio 12.000000) + (dashed_line_gap_ratio 3.000000) + (svgprecision 4) + (plotframeref no) + (viasonmask no) + (mode 1) + (useauxorigin no) + (hpglpennumber 1) + (hpglpenspeed 20) + (hpglpendiameter 15.000000) + (pdf_front_fp_property_popups yes) + (pdf_back_fp_property_popups yes) + (pdf_metadata yes) + (dxfpolygonmode yes) + (dxfimperialunits yes) + (dxfusepcbnewfont yes) + (psnegative no) + (psa4output no) + (plotreference yes) + (plotvalue yes) + (plotfptext yes) + (plotinvisibletext no) + (sketchpadsonfab no) + (subtractmaskfromsilk no) + (outputformat 1) + (mirror no) + (drillshape 1) + (scaleselection 1) + (outputdirectory "") + ) + ) + (net 0 "") + (net 1 "A") + (footprint "D5" + (layer "F.Cu") + (uuid "15a9bad1-3969-483f-a7df-b8dcfd5c5be8") + (at 105.85 65.35) + (descr "Diode 5 pas") + (tags "DIODE DEV") + (property "Reference" "D1" + (at 0 0 0) + (layer "F.SilkS") + (uuid "08beb1ba-64ac-45e6-9c0e-57c8d2e1a3d2") + (effects + (font + (size 1.524 1.016) + (thickness 0.254) + ) + ) + ) + (property "Value" "1N4007" + (at -0.254 0 0) + (layer "F.SilkS") + (hide yes) + (uuid "551d7001-ab50-4196-bf85-eed0ce4c930f") + (effects + (font + (size 1.524 1.016) + (thickness 0.254) + ) + ) + ) + (property "Footprint" "D5" + (at 0 0 0) + (unlocked yes) + (layer "F.Fab") + (hide yes) + (uuid "9fa72c1a-74e4-4181-a5c3-f6dc09430202") + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Datasheet" "" + (at 0 0 0) + (unlocked yes) + (layer "F.Fab") + (hide yes) + (uuid "3694faef-303c-4f8c-8654-ba2e8c26cb0c") + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Description" "" + (at 0 0 0) + (unlocked yes) + (layer "F.Fab") + (hide yes) + (uuid "5e1f6e7a-690d-49e0-8e9d-6a2197f59583") + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (attr through_hole) + (fp_line + (start -5.08 -1.27) + (end -5.08 0) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "ac24e0bf-67c8-43f4-bd54-7dc8c2779b8f") + ) + (fp_line + (start -5.08 0) + (end -6.35 0) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "0d4b3ce6-e5ed-40ad-9a1a-fd481ae644b6") + ) + (fp_line + (start -5.08 0) + (end -5.08 1.27) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "a031fc22-e4d8-4697-ad42-9f1c9acf67b3") + ) + (fp_line + (start -5.08 1.27) + (end 5.08 1.27) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "47fd7bdd-22ba-47df-865f-dfc849031c2c") + ) + (fp_line + (start 3.81 -1.27) + (end 3.81 1.27) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "9ee7ff54-48de-49af-b710-a297a943932f") + ) + (fp_line + (start 4.064 -1.27) + (end 4.064 1.27) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "d73ef625-203f-49f8-9883-1cd8fe86f331") + ) + (fp_line + (start 5.08 -1.27) + (end -5.08 -1.27) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "b5aebf80-fe83-4fd9-a64b-1b1f424400c7") + ) + (fp_line + (start 5.08 0) + (end 5.08 -1.27) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "93e83871-234f-43f8-90a2-fe0c11639810") + ) + (fp_line + (start 5.08 1.27) + (end 5.08 0) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "96268265-710d-4688-83a5-cfdcf05d418e") + ) + (fp_line + (start 6.35 0) + (end 5.08 0) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "0437f17e-1aa3-4fef-920e-ad29d374514c") + ) + (pad "1" thru_hole circle + (at -6.35 0) + (size 2.032 2.032) + (drill 1.143) + (layers "*.Cu" "*.Mask" "F.SilkS") + (remove_unused_layers no) + (net 1 "A") + (uuid "af83e208-80d8-4838-9ffe-eaca56d969d0") + ) + (pad "2" thru_hole rect + (at 6.35 0) + (size 2.032 2.032) + (drill 1.143) + (layers "*.Cu" "*.Mask" "F.SilkS") + (remove_unused_layers no) + (solder_mask_margin 0.3) + (solder_paste_margin 0.3) + (solder_paste_margin_ratio 0.23) + (clearance 1) + (zone_connect 1) + (thermal_bridge_width 20) + (thermal_bridge_angle 47) + (thermal_gap 10) + (teardrops + (best_length_ratio 0.45) + (max_length 2) + (best_width_ratio 0.95) + (max_width 3) + (curve_points 0) + (filter_ratio 0.8) + (enabled yes) + (allow_two_segments no) + (prefer_zone_connections no) + ) + (uuid "b38f2485-6695-4057-bc99-fba8492caea0") + ) + (model "discret/diode.wrl" + (offset + (xyz 0 0 0) + ) + (scale + (xyz 0.5 0.5 0.5) + ) + (rotate + (xyz 0 0 0) + ) + ) + ) + (footprint "bornier2" + (layer "F.Cu") + (uuid "8dc9231d-b479-4199-ad9c-1b70e93c7791") + (at 88.3 68.9 -90) + (descr "Bornier d'alimentation 2 pins") + (tags "DEV") + (property "Reference" "P2" + (at 0 -5.08 -90) + (layer "F.SilkS") + (uuid "ba42dcaf-4533-4d88-b1fa-fdb993574d72") + (effects + (font + (size 1.524 1.524) + (thickness 0.3048) + ) + ) + ) + (property "Value" "CONN_2" + (at 0 5.08 -90) + (layer "F.SilkS") + (uuid "66aeb929-e503-4d95-8f48-82e9d57c9ad0") + (effects + (font + (size 1.524 1.524) + (thickness 0.3048) + ) + ) + ) + (property "Footprint" "bornier2" + (at 0 0 -90) + (unlocked yes) + (layer "F.Fab") + (hide yes) + (uuid "f4c1f82d-5e51-4256-ba8d-645c67392576") + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Datasheet" "" + (at 0 0 -90) + (unlocked yes) + (layer "F.Fab") + (hide yes) + (uuid "c1f23185-60d5-47ee-bdf1-b8db1ebb9dc3") + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (property "Description" "" + (at 0 0 -90) + (unlocked yes) + (layer "F.Fab") + (hide yes) + (uuid "46864a55-3772-4b10-b98e-af0b7b9d7d57") + (effects + (font + (size 1.27 1.27) + ) + ) + ) + (attr through_hole) + (fp_line + (start -5.08 3.81) + (end 5.08 3.81) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "029a691e-8348-4bb7-b8ce-c09a01955b42") + ) + (fp_line + (start 5.08 3.81) + (end 5.08 -3.81) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "ef80f3bc-cc20-4a9a-975f-ea015f9e9335") + ) + (fp_line + (start 5.08 2.54) + (end -5.08 2.54) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "30f2ddd8-04bc-4177-bae7-a2617cff1ae8") + ) + (fp_line + (start -5.08 -3.81) + (end -5.08 3.81) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "b6565f58-3eeb-4abb-bc10-02800e9d4dfd") + ) + (fp_line + (start 5.08 -3.81) + (end -5.08 -3.81) + (stroke + (width 0.3048) + (type solid) + ) + (layer "F.SilkS") + (uuid "a7881ec6-e1b3-403e-9451-8f4b412254cd") + ) + (pad "1" thru_hole rect + (at -2.54 0 270) + (size 2.54 2.54) + (drill 1.524) + (layers "*.Cu" "*.Mask" "F.SilkS") + (remove_unused_layers no) + (net 1 "A") + (uuid "afe67c4a-2148-455d-81b1-6bcc2a745c4d") + ) + (pad "2" thru_hole circle + (at 2.54 0 270) + (size 2.54 2.54) + (drill 1.524) + (layers "*.Cu" "*.Mask" "F.SilkS") + (remove_unused_layers no) + (uuid "d615d510-9f7d-4da4-907d-b271da602bbf") + ) + (model "device/bornier_2.wrl" + (offset + (xyz 0 0 0) + ) + (scale + (xyz 1 1 1) + ) + (rotate + (xyz 0 0 0) + ) + ) + ) + (gr_arc + (start 111.5 55) + (mid 110.556284 51.236651) + (end 113.164101 54.1094) + (stroke + (width 0.1) + (type default) + ) + (layer "B.Paste") + (uuid "744755ec-7832-4a1f-b739-5fbf3418cd46") + ) + (gr_poly + (pts + (xy 105.25 114.4) (xy 105.25 136.8) (xy 128.7 145.65) (xy 143.05 131.35) (xy 121.65 127.7) (xy 122.15 109.95) + ) + (stroke + (width 0.05) + (type solid) + ) + (fill none) + (layer "Edge.Cuts") + (uuid "052086a7-98a9-4ea1-bdee-ad673fb3a26c") + ) + (gr_poly + (pts + (xy 215.5 49.5) (xy 106.25 29.4) (xy 35.65 100.85) (xy 76.55 170.7) (xy 187.85 168.1) (xy 258.75 96.9) + (xy 222 79.2) + ) + (stroke + (width 0.05) + (type solid) + ) + (fill none) + (layer "Edge.Cuts") + (uuid "98ab137c-f925-4075-af50-4ac8fe13c14d") + ) + (gr_circle + (center 224.6 97.7) + (end 228.25 99.55) + (stroke + (width 0.05) + (type default) + ) + (fill none) + (layer "Edge.Cuts") + (uuid "b27945e5-32fe-454b-ad80-b75cae1194e1") + ) + (gr_circle + (center 197.75 63.3) + (end 199.05 65.4) + (stroke + (width 0.05) + (type default) + ) + (fill none) + (layer "Edge.Cuts") + (uuid "e1699138-73b7-4cfd-aec1-5bee869795d9") + ) + (gr_rect + (start 93.5 47.25) + (end 102 54.75) + (stroke + (width 1.87) + (type solid) + ) + (fill none) + (layer "B.Fab") + (uuid "01315513-10cc-4984-96ac-98682589967a") + ) + (gr_rect + (start 95 49.5) + (end 100 50.5) + (stroke + (width 0.1) + (type dash_dot_dot) + ) + (fill none) + (layer "B.Fab") + (uuid "2e68cc14-52eb-4276-b39c-c502b042bc91") + ) + (gr_arc + (start 105.222971 54.2804) + (mid 106.709486 54.262003) + (end 108 55) + (stroke + (width 0.1) + (type default) + ) + (layer "B.Fab") + (uuid "6542d6b2-94a8-4546-a80d-e4122c6f29e1") + ) + (gr_circle + (center 106.5 48.5) + (end 108.5 49) + (locked yes) + (stroke + (width 0.177) + (type default) + ) + (fill none) + (layer "B.Fab") + (uuid "b5d27e62-c3ee-43b4-a859-5e626272bf9c") + ) + (gr_rect + (start 95 51) + (end 100 52) + (stroke + (width 0) + (type solid) + ) + (fill solid) + (layer "B.Fab") + (uuid "cb2af193-349d-4651-a0c1-5d028759a9f3") + ) + (gr_line + (start 113.5 47) + (end 115 47) + (stroke + (width 0.1) + (type default) + ) + (layer "User.4") + (uuid "22cd6520-1cf2-46ca-a9d9-28e8d8c4863c") + ) + (gr_line + (start 111 46.5) + (end 115.5 54) + (stroke + (width 0.1) + (type default) + ) + (layer "User.4") + (uuid "5cc814d5-0815-4cbb-b9cb-07dfd7225722") + ) + (gr_text "Text!" + (at 120.45 49.35 0) + (layer "B.Fab") + (uuid "2c37f972-a39a-4270-ad71-bec4a2358dd8") + (effects + (font + (size 1 1) + (thickness 0.15) + ) + (justify left bottom mirror) + ) + ) + (gr_text_box "Box\no\nCharacters" + (start 116.9 49.9) + (end 127.3 55.45) + (margins 1.0025 1.0025 1.0025 1.0025) + (layer "F.SilkS") + (uuid "e767597a-10fe-4c42-aa00-6a6954af3954") + (effects + (font + (size 0.9 0.9) + (thickness 0.17) + (bold yes) + ) + (justify top) + ) + (border yes) + (stroke + (width 0.12) + (type dot) + ) + ) + (segment + (start 88.3 63.2) + (end 90.65 60.85) + (width 0.2) + (layer "B.Cu") + (net 1) + (uuid "314404ac-770d-48bd-95c0-61764c6aa3ed") + ) + (segment + (start 90.65 60.85) + (end 95 60.85) + (width 0.2) + (layer "B.Cu") + (net 1) + (uuid "50e69c17-3b82-46ec-8e12-83615bba445c") + ) + (segment + (start 88.3 66.36) + (end 88.3 63.2) + (width 0.2) + (layer "B.Cu") + (net 1) + (uuid "68c5bb53-edda-4916-9e13-61e1c3937aec") + ) + (segment + (start 95 60.85) + (end 99.5 65.35) + (width 0.2) + (layer "B.Cu") + (net 1) + (uuid "fabfab7b-4ea2-487a-a340-e24bbc4c982e") + ) + (zone + (net 0) + (net_name "") + (layer "B.Cu") + (uuid "6f75f268-7b12-4df8-b032-ef2b7e95c515") + (hatch edge 0.508) + (connect_pads + (clearance 0.508) + ) + (min_thickness 0.254) + (filled_areas_thickness no) + (fill yes + (thermal_gap 0.762) + (thermal_bridge_width 0.508) + ) + (polygon + (pts + (xy 232.833 116.922) (xy 232.833 38.436) (xy 133.9 38.436) (xy 133.9 116.922) + ) + ) + (filled_polygon + (layer "B.Cu") + (island) + (pts + (xy 232.775121 38.456002) (xy 232.821614 38.509658) (xy 232.833 38.562) (xy 232.833 116.168359) + (xy 232.812998 116.23648) (xy 232.759342 116.282973) (xy 232.693829 116.293669) (xy 232.625623 116.2865) + (xy 232.62562 116.2865) (xy 232.53238 116.2865) (xy 232.532376 116.2865) (xy 232.464172 116.293669) + (xy 232.393044 116.301145) (xy 232.393042 116.301145) (xy 232.393038 116.301146) (xy 232.21522 116.358922) + (xy 232.215208 116.358928) (xy 232.05329 116.45241) (xy 232.053289 116.452411) (xy 231.991401 116.508136) + (xy 231.927393 116.538853) (xy 231.90709 116.5405) (xy 231.13335 116.5405) (xy 231.072803 116.547009) + (xy 231.072795 116.547011) (xy 230.935797 116.59811) (xy 230.935792 116.598112) (xy 230.818738 116.685738) + (xy 230.731112 116.802792) (xy 230.73111 116.802796) (xy 230.717222 116.840033) (xy 230.674676 116.896869) + (xy 230.608155 116.921679) (xy 230.599167 116.922) (xy 189.514073 116.922) (xy 189.445952 116.901998) + (xy 189.399459 116.848342) (xy 189.389355 116.778068) (xy 189.418849 116.713488) (xy 189.454104 116.685186) + (xy 189.565558 116.624869) (xy 189.602727 116.595938) (xy 189.602728 116.595937) (xy 188.950824 115.944033) + (xy 188.916798 115.881721) (xy 188.921863 115.810906) (xy 188.96441 115.75407) (xy 188.982712 115.742674) + (xy 189.005854 115.730883) (xy 189.096883 115.639854) (xy 189.108671 115.616717) (xy 189.157416 115.565103) + (xy 189.226331 115.548034) (xy 189.293533 115.570934) (xy 189.310033 115.584824) (xy 189.96113 116.235921) + (xy 189.961131 116.235921) (xy 190.052874 116.095499) (xy 190.150098 115.873852) (xy 190.150101 115.873845) + (xy 190.209515 115.639221) (xy 190.229504 115.398) (xy 190.209515 115.156778) (xy 190.150101 114.922154) + (xy 190.150098 114.922147) (xy 190.052874 114.7005) (xy 189.961131 114.560077) (xy 189.310033 115.211176) + (xy 189.247721 115.245201) (xy 189.176905 115.240136) (xy 189.12007 115.197589) (xy 189.108672 115.179284) + (xy 189.096883 115.156146) (xy 189.096881 115.156144) (xy 189.09688 115.156142) (xy 189.005855 115.065117) + (xy 188.982714 115.053326) (xy 188.931099 115.004578) (xy 188.914034 114.935662) (xy 188.936935 114.868461) + (xy 188.950823 114.851964) (xy 189.602728 114.20006) (xy 189.602023 114.188704) (xy 189.617766 114.119475) + (xy 189.668438 114.069747) (xy 189.737951 114.055308) (xy 189.73872 114.055373) (xy 189.907 114.070096) + (xy 190.139532 114.049752) (xy 190.364998 113.989339) (xy 190.405711 113.970353) (xy 190.475899 113.959691) + (xy 190.514569 113.974212) (xy 190.515656 113.972048) (xy 190.520237 113.974348) (xy 190.527857 113.979202) + (xy 190.528395 113.979404) (xy 190.528554 113.979509) (xy 190.52849 113.979605) (xy 190.53381 113.982994) + (xy 190.533873 113.982894) (xy 190.540115 113.986743) (xy 190.540118 113.986746) (xy 190.540121 113.986747) + (xy 190.540122 113.986748) (xy 190.607309 114.018077) (xy 190.673551 114.051346) (xy 190.680455 114.053859) + (xy 190.680414 114.053971) (xy 190.686589 114.056117) (xy 190.686627 114.056004) (xy 190.69359 114.058311) + (xy 190.693591 114.058311) (xy 190.693593 114.058312) (xy 190.740651 114.068028) (xy 190.766185 114.073301) + (xy 190.789792 114.078896) (xy 190.83833 114.0904) (xy 190.838336 114.0904) (xy 190.845619 114.091252) + (xy 190.845605 114.091369) (xy 190.85211 114.092033) (xy 190.852121 114.091916) (xy 190.859428 114.092554) + (xy 190.859435 114.092556) (xy 190.93352 114.0904) (xy 194.671054 114.0904) (xy 194.689313 114.091729) + (xy 194.711788 114.095022) (xy 194.743545 114.092243) (xy 194.761881 114.09064) (xy 194.767374 114.0904) + (xy 194.775189 114.0904) (xy 194.790919 114.088561) (xy 194.806651 114.086722) (xy 194.880483 114.080263) + (xy 194.880495 114.080258) (xy 194.887665 114.078779) (xy 194.887689 114.078896) (xy 194.894082 114.077479) + (xy 194.894055 114.077364) (xy 194.901189 114.075672) (xy 194.901195 114.075672) (xy 194.970871 114.050311) + (xy 195.041229 114.026998) (xy 195.04124 114.02699) (xy 195.047884 114.023894) (xy 195.047935 114.024003) + (xy 195.053809 114.021159) (xy 195.053756 114.021053) (xy 195.060319 114.017756) (xy 195.06032 114.017754) + (xy 195.060323 114.017754) (xy 195.103525 113.989339) (xy 195.122257 113.977019) (xy 195.141717 113.965015) + (xy 195.185357 113.938099) (xy 195.185362 113.938093) (xy 195.191115 113.933546) (xy 195.191189 113.933639) + (xy 195.196252 113.929515) (xy 195.196176 113.929424) (xy 195.201798 113.924705) (xy 195.201798 113.924703) + (xy 195.201805 113.9247) (xy 195.252668 113.870787) (xy 196.471432 112.652022) (xy 196.485265 112.640068) + (xy 196.503497 112.626496) (xy 196.535846 112.587942) (xy 196.539515 112.58394) (xy 196.545059 112.578397) + (xy 196.564697 112.55356) (xy 196.612347 112.496774) (xy 196.612348 112.49677) (xy 196.612351 112.496768) + (xy 196.616385 112.490636) (xy 196.616484 112.490701) (xy 196.619993 112.485193) (xy 196.619892 112.485131) + (xy 196.623743 112.478887) (xy 196.623744 112.478884) (xy 196.623746 112.478882) (xy 196.65508 112.411684) + (xy 196.688347 112.345446) (xy 196.688347 112.345444) (xy 196.688349 112.345441) (xy 196.69086 112.338543) + (xy 196.690972 112.338584) (xy 196.693116 112.332416) (xy 196.693002 112.332379) (xy 196.69531 112.325413) + (xy 196.69531 112.325411) (xy 196.695312 112.325408) (xy 196.710303 112.252806) (xy 196.7274 112.18067) + (xy 196.7274 112.180664) (xy 196.728252 112.173381) (xy 196.728369 112.173394) (xy 196.729033 112.16689) + (xy 196.728916 112.16688) (xy 196.729554 112.159573) (xy 196.729556 112.159566) (xy 196.7274 112.08548) + (xy 196.7274 111.857835) (xy 196.747402 111.789715) (xy 196.777875 111.758009) (xy 196.777599 111.75768) + (xy 196.780667 111.755105) (xy 196.781132 111.754621) (xy 196.781809 111.754148) (xy 196.931148 111.604809) + (xy 197.052286 111.431806) (xy 197.141542 111.240395) (xy 197.196204 111.036394) (xy 197.214611 110.826) + (xy 197.196204 110.615606) (xy 197.141542 110.411605) (xy 197.052286 110.220195) (xy 197.052285 110.220194) + (xy 197.052284 110.220191) (xy 196.931152 110.047195) (xy 196.931149 110.047192) (xy 196.866718 109.982761) + (xy 196.781809 109.897852) (xy 196.756946 109.880443) (xy 196.608806 109.776714) (xy 196.515479 109.733195) + (xy 196.462194 109.686278) (xy 196.442733 109.618001) (xy 196.463275 109.550041) (xy 196.517298 109.503975) + (xy 196.568729 109.493) (xy 196.750132 109.493) (xy 196.750138 109.493) (xy 196.750145 109.492999) + (xy 196.750149 109.492999) (xy 196.810696 109.48649) (xy 196.810699 109.486489) (xy 196.810701 109.486489) + (xy 196.815919 109.484543) (xy 196.884036 109.459136) (xy 196.947704 109.435389) (xy 196.987848 109.405338) + (xy 197.064761 109.347761) (xy 197.152387 109.230707) (xy 197.152389 109.230703) (xy 197.15239 109.230702) + (xy 197.183703 109.14675) (xy 197.203987 109.092367) (xy 197.246534 109.035531) (xy 197.313054 109.010721) + (xy 197.322042 109.0104) (xy 200.095754 109.0104) (xy 200.163875 109.030402) (xy 200.184849 109.047305) + (xy 200.36267 109.225126) (xy 200.396696 109.287438) (xy 200.399096 109.325202) (xy 200.382666 109.513002) + (xy 200.378904 109.556) (xy 200.380131 109.570022) (xy 200.399248 109.788535) (xy 200.457756 110.006889) + (xy 200.459661 110.013998) (xy 200.480175 110.05799) (xy 200.558308 110.225546) (xy 200.558309 110.225548) + (xy 200.692186 110.416745) (xy 200.69219 110.41675) (xy 200.692193 110.416754) (xy 200.857246 110.581807) + (xy 200.939871 110.639662) (xy 200.984199 110.695117) (xy 200.9936 110.742874) (xy 200.9936 111.399052) + (xy 200.99227 111.417311) (xy 200.988978 111.439784) (xy 200.988978 111.439789) (xy 200.99336 111.489881) + (xy 200.9936 111.495374) (xy 200.9936 111.503191) (xy 200.997277 111.534651) (xy 201.003736 111.60848) + (xy 201.00522 111.615666) (xy 201.005104 111.615689) (xy 201.006521 111.622079) (xy 201.006635 111.622052) + (xy 201.008329 111.629199) (xy 201.033688 111.698871) (xy 201.057003 111.769231) (xy 201.060103 111.775879) + (xy 201.059996 111.775928) (xy 201.062848 111.781818) (xy 201.062953 111.781766) (xy 201.066244 111.78832) + (xy 201.10698 111.850256) (xy 201.145897 111.913352) (xy 201.150454 111.919115) (xy 201.150361 111.919188) + (xy 201.154493 111.92426) (xy 201.154584 111.924184) (xy 201.159299 111.929804) (xy 201.1593 111.929805) + (xy 201.161096 111.931499) (xy 201.197289 111.965646) (xy 201.213212 111.980668) (xy 201.796971 112.564427) + (xy 201.808938 112.578275) (xy 201.822502 112.596496) (xy 201.861028 112.628824) (xy 201.865082 112.632538) + (xy 201.8706 112.638057) (xy 201.895444 112.657701) (xy 201.952226 112.705348) (xy 201.958357 112.70938) + (xy 201.958291 112.709478) (xy 201.96381 112.712994) (xy 201.963873 112.712894) (xy 201.970115 112.716743) + (xy 201.970118 112.716746) (xy 201.970121 112.716747) (xy 201.970122 112.716748) (xy 202.037309 112.748077) + (xy 202.103551 112.781346) (xy 202.110455 112.783859) (xy 202.110414 112.783971) (xy 202.116589 112.786117) + (xy 202.116627 112.786004) (xy 202.12359 112.788311) (xy 202.123591 112.788311) (xy 202.123593 112.788312) + (xy 202.170651 112.798028) (xy 202.196185 112.803301) (xy 202.219792 112.808896) (xy 202.26833 112.8204) + (xy 202.268336 112.8204) (xy 202.275619 112.821252) (xy 202.275605 112.821369) (xy 202.28211 112.822033) + (xy 202.282121 112.821916) (xy 202.289428 112.822554) (xy 202.289435 112.822556) (xy 202.36352 112.8204) + (xy 208.006054 112.8204) (xy 208.024313 112.821729) (xy 208.046788 112.825022) (xy 208.078545 112.822243) + (xy 208.096881 112.82064) (xy 208.102374 112.8204) (xy 208.110189 112.8204) (xy 208.125919 112.818561) + (xy 208.141651 112.816722) (xy 208.215483 112.810263) (xy 208.215495 112.810258) (xy 208.222665 112.808779) + (xy 208.222689 112.808896) (xy 208.229082 112.807479) (xy 208.229055 112.807364) (xy 208.236189 112.805672) + (xy 208.236195 112.805672) (xy 208.305871 112.780311) (xy 208.376229 112.756998) (xy 208.37624 112.75699) + (xy 208.382884 112.753894) (xy 208.382935 112.754003) (xy 208.388809 112.751159) (xy 208.388756 112.751053) + (xy 208.395319 112.747756) (xy 208.39532 112.747754) (xy 208.395323 112.747754) (xy 208.44199 112.71706) + (xy 208.457257 112.707019) (xy 208.482545 112.691421) (xy 208.520357 112.668099) (xy 208.520362 112.668093) + (xy 208.526115 112.663546) (xy 208.526189 112.663639) (xy 208.531252 112.659515) (xy 208.531176 112.659424) + (xy 208.536798 112.654705) (xy 208.536798 112.654703) (xy 208.536805 112.6547) (xy 208.587668 112.600787) + (xy 209.181219 112.007235) (xy 209.186947 112.002166) (xy 209.215768 111.979645) (xy 209.241152 111.947913) + (xy 209.243289 111.945382) (xy 209.24504 111.943415) (xy 209.24506 111.943396) (xy 209.259168 111.925553) + (xy 209.26996 111.911905) (xy 210.40499 110.493117) (xy 211.066649 109.666042) (xy 211.124821 109.625346) + (xy 211.176019 109.619236) (xy 211.186569 109.620158) (xy 211.243 109.625096) (xy 211.475532 109.604752) + (xy 211.700998 109.544339) (xy 211.912548 109.445691) (xy 212.103754 109.311807) (xy 212.268807 109.146754) + (xy 212.402691 108.955548) (xy 212.501339 108.743998) (xy 212.561752 108.518532) (xy 212.582096 108.286) + (xy 212.582096 108.285999) (xy 217.523904 108.285999) (xy 217.544248 108.518535) (xy 217.604569 108.743655) + (xy 217.604661 108.743998) (xy 217.637708 108.814868) (xy 217.703308 108.955546) (xy 217.703309 108.955548) + (xy 217.837186 109.146745) (xy 217.83719 109.14675) (xy 217.837193 109.146754) (xy 218.002246 109.311807) + (xy 218.00225 109.31181) (xy 218.002254 109.311813) (xy 218.078927 109.3655) (xy 218.193452 109.445691) + (xy 218.405002 109.544339) (xy 218.603829 109.597614) (xy 218.630083 109.604649) (xy 218.630468 109.604752) + (xy 218.863 109.625096) (xy 219.095532 109.604752) (xy 219.320998 109.544339) (xy 219.532548 109.445691) + (xy 219.723754 109.311807) (xy 219.888807 109.146754) (xy 219.946661 109.064129) (xy 220.002119 109.019801) + (xy 220.049875 109.0104) (xy 225.8485 109.0104) (xy 225.916621 109.030402) (xy 225.963114 109.084058) + (xy 225.9745 109.1364) (xy 225.9745 109.604649) (xy 225.981009 109.665196) (xy 225.981011 109.665204) + (xy 226.03211 109.802202) (xy 226.032112 109.802207) (xy 226.119738 109.919261) (xy 226.236792 110.006887) + (xy 226.236794 110.006888) (xy 226.236796 110.006889) (xy 226.295875 110.028924) (xy 226.373795 110.057988) + (xy 226.373803 110.05799) (xy 226.43435 110.064499) (xy 226.434355 110.064499) (xy 226.434362 110.0645) + (xy 226.434368 110.0645) (xy 229.071632 110.0645) (xy 229.071638 110.0645) (xy 229.071645 110.064499) + (xy 229.071649 110.064499) (xy 229.132196 110.05799) (xy 229.132199 110.057989) (xy 229.132201 110.057989) + (xy 229.269204 110.006889) (xy 229.284655 109.995323) (xy 229.386261 109.919261) (xy 229.473887 109.802207) + (xy 229.473887 109.802206) (xy 229.473889 109.802204) (xy 229.51861 109.682304) (xy 229.524988 109.665204) + (xy 229.52499 109.665196) (xy 229.531499 109.604649) (xy 229.5315 109.604632) (xy 229.5315 106.967367) + (xy 229.531499 106.96735) (xy 229.52499 106.906803) (xy 229.524988 106.906795) (xy 229.489165 106.810752) + (xy 229.473889 106.769796) (xy 229.473888 106.769794) (xy 229.473887 106.769792) (xy 229.386261 106.652738) + (xy 229.269207 106.565112) (xy 229.269202 106.56511) (xy 229.132204 106.514011) (xy 229.132196 106.514009) + (xy 229.071649 106.5075) (xy 229.071638 106.5075) (xy 226.434362 106.5075) (xy 226.43435 106.5075) + (xy 226.373803 106.514009) (xy 226.373795 106.514011) (xy 226.236797 106.56511) (xy 226.236792 106.565112) + (xy 226.119738 106.652738) (xy 226.032112 106.769792) (xy 226.03211 106.769797) (xy 225.981011 106.906795) + (xy 225.981009 106.906803) (xy 225.9745 106.96735) (xy 225.9745 107.4356) (xy 225.954498 107.503721) + (xy 225.900842 107.550214) (xy 225.8485 107.5616) (xy 220.049875 107.5616) (xy 219.981754 107.541598) + (xy 219.946662 107.507871) (xy 219.888809 107.425249) (xy 219.888807 107.425246) (xy 219.723754 107.260193) + (xy 219.641128 107.202337) (xy 219.5968 107.14688) (xy 219.5874 107.099125) (xy 219.5874 105.662874) + (xy 219.607402 105.594753) (xy 219.641127 105.559662) (xy 219.723754 105.501807) (xy 219.888807 105.336754) + (xy 220.022691 105.145548) (xy 220.121339 104.933998) (xy 220.181752 104.708532) (xy 220.202096 104.476) + (xy 220.181752 104.243468) (xy 220.121339 104.018002) (xy 220.022691 103.806452) (xy 219.946662 103.697871) + (xy 219.888813 103.615254) (xy 219.888809 103.615249) (xy 219.888807 103.615246) (xy 219.723754 103.450193) + (xy 219.72375 103.45019) (xy 219.723745 103.450186) (xy 219.532548 103.316309) (xy 219.532546 103.316308) + (xy 219.321 103.217662) (xy 219.320995 103.21766) (xy 219.277494 103.206004) (xy 225.716252 103.206004) + (xy 225.73522 103.483323) (xy 225.735221 103.483329) (xy 225.791778 103.755501) (xy 225.79178 103.755509) + (xy 225.884872 104.017443) (xy 226.012763 104.264261) (xy 226.012764 104.264262) (xy 226.146319 104.453468) + (xy 226.95204 103.647747) (xy 227.014353 103.613722) (xy 227.085168 103.618786) (xy 227.142004 103.661333) + (xy 227.142121 103.661489) (xy 227.194968 103.732476) (xy 227.30211 103.822378) (xy 227.341436 103.881486) + (xy 227.342564 103.952473) (xy 227.310214 104.007994) (xy 226.506655 104.811552) (xy 226.506655 104.811554) + (xy 226.57844 104.869956) (xy 226.815965 105.014399) (xy 227.070933 105.125147) (xy 227.070943 105.12515) + (xy 227.338603 105.200146) (xy 227.338611 105.200148) (xy 227.613993 105.237999) (xy 227.614007 105.238) + (xy 227.891993 105.238) (xy 227.892006 105.237999) (xy 228.167388 105.200148) (xy 228.167396 105.200146) + (xy 228.435056 105.12515) (xy 228.435066 105.125147) (xy 228.690034 105.014399) (xy 228.927558 104.869956) + (xy 228.999343 104.811554) (xy 228.999343 104.811553) (xy 228.195862 104.008073) (xy 228.161837 103.94576) + (xy 228.166901 103.874945) (xy 228.209448 103.818109) (xy 228.215706 103.813713) (xy 228.246138 103.793699) + (xy 228.368378 103.664132) (xy 228.368378 103.664131) (xy 228.373414 103.658794) (xy 228.374633 103.659944) + (xy 228.424205 103.622618) (xy 228.495006 103.617354) (xy 228.557414 103.651204) (xy 228.557665 103.651454) + (xy 229.359679 104.453468) (xy 229.493231 104.26427) (xy 229.493236 104.264261) (xy 229.621127 104.017443) + (xy 229.714219 103.755509) (xy 229.714221 103.755501) (xy 229.770778 103.483329) (xy 229.770779 103.483323) + (xy 229.789748 103.206004) (xy 229.789748 103.205995) (xy 229.770779 102.928676) (xy 229.770778 102.92867) + (xy 229.714221 102.656498) (xy 229.714219 102.65649) (xy 229.621127 102.394556) (xy 229.493236 102.147738) + (xy 229.493236 102.147737) (xy 229.359679 101.95853) (xy 228.553957 102.764252) (xy 228.491645 102.798277) + (xy 228.420829 102.793212) (xy 228.363994 102.750665) (xy 228.363794 102.750397) (xy 228.311032 102.679524) + (xy 228.311029 102.679522) (xy 228.311029 102.679521) (xy 228.311027 102.679519) (xy 228.203889 102.589621) + (xy 228.164562 102.530512) (xy 228.163435 102.459525) (xy 228.195784 102.404003) (xy 228.999343 101.600445) + (xy 228.927554 101.54204) (xy 228.690034 101.3976) (xy 228.435066 101.286852) (xy 228.435056 101.286849) + (xy 228.167396 101.211853) (xy 228.167388 101.211851) (xy 227.892006 101.174) (xy 227.613993 101.174) + (xy 227.338611 101.211851) (xy 227.338603 101.211853) (xy 227.070943 101.286849) (xy 227.070933 101.286852) + (xy 226.815962 101.397602) (xy 226.578449 101.542036) (xy 226.506655 101.600444) (xy 227.310137 102.403926) + (xy 227.344162 102.466239) (xy 227.339098 102.537054) (xy 227.296551 102.59389) (xy 227.290282 102.598293) + (xy 227.259861 102.618301) (xy 227.132586 102.753206) (xy 227.13137 102.752058) (xy 227.081766 102.789392) + (xy 227.010963 102.794639) (xy 226.948564 102.760774) (xy 226.948334 102.760545) (xy 226.146319 101.958529) + (xy 226.146318 101.958529) (xy 226.012768 102.147728) (xy 225.884872 102.394556) (xy 225.79178 102.65649) + (xy 225.791778 102.656498) (xy 225.735221 102.92867) (xy 225.73522 102.928676) (xy 225.716252 103.205995) + (xy 225.716252 103.206004) (xy 219.277494 103.206004) (xy 219.095535 103.157248) (xy 218.920116 103.141901) + (xy 218.863 103.136904) (xy 218.862999 103.136904) (xy 218.630464 103.157248) (xy 218.405004 103.21766) + (xy 218.404999 103.217662) (xy 218.193453 103.316308) (xy 218.193451 103.316309) (xy 218.002254 103.450186) + (xy 218.002243 103.450195) (xy 217.837195 103.615243) (xy 217.837186 103.615254) (xy 217.703309 103.806451) + (xy 217.703308 103.806453) (xy 217.604662 104.017999) (xy 217.60466 104.018004) (xy 217.544248 104.243464) + (xy 217.523904 104.475999) (xy 217.544248 104.708535) (xy 217.600559 104.91869) (xy 217.604661 104.933998) + (xy 217.637198 105.003774) (xy 217.703308 105.145546) (xy 217.703309 105.145548) (xy 217.837186 105.336745) + (xy 217.83719 105.33675) (xy 217.837193 105.336754) (xy 218.002246 105.501807) (xy 218.084871 105.559662) + (xy 218.129199 105.615117) (xy 218.1386 105.662874) (xy 218.1386 107.099125) (xy 218.118598 107.167246) + (xy 218.084871 107.202337) (xy 218.025678 107.243786) (xy 218.002245 107.260194) (xy 218.002243 107.260195) + (xy 217.83719 107.425249) (xy 217.837186 107.425254) (xy 217.703309 107.616451) (xy 217.703308 107.616453) + (xy 217.604662 107.827999) (xy 217.60466 107.828004) (xy 217.544248 108.053464) (xy 217.523904 108.285999) + (xy 212.582096 108.285999) (xy 212.561752 108.053468) (xy 212.501339 107.828002) (xy 212.402691 107.616452) + (xy 212.323613 107.503517) (xy 212.268813 107.425254) (xy 212.268809 107.425249) (xy 212.268807 107.425246) + (xy 212.103754 107.260193) (xy 212.10375 107.26019) (xy 212.103745 107.260186) (xy 211.912548 107.126309) + (xy 211.912546 107.126308) (xy 211.701 107.027662) (xy 211.700995 107.02766) (xy 211.475535 106.967248) + (xy 211.243 106.946904) (xy 211.010464 106.967248) (xy 210.785004 107.02766) (xy 210.784999 107.027662) + (xy 210.573453 107.126308) (xy 210.573451 107.126309) (xy 210.382254 107.260186) (xy 210.382243 107.260195) + (xy 210.217195 107.425243) (xy 210.217186 107.425254) (xy 210.083309 107.616451) (xy 210.083308 107.616453) + (xy 209.984662 107.827999) (xy 209.98466 107.828004) (xy 209.924248 108.053464) (xy 209.905907 108.263101) + (xy 209.903904 108.286) (xy 209.924248 108.518532) (xy 209.924249 108.518537) (xy 209.924248 108.518537) + (xy 209.959197 108.648971) (xy 209.957507 108.719948) (xy 209.935879 108.760293) (xy 208.164577 110.97442) + (xy 208.159926 110.979616) (xy 207.804846 111.334696) (xy 207.742536 111.36872) (xy 207.715753 111.3716) + (xy 202.705247 111.3716) (xy 202.637126 111.351598) (xy 202.616151 111.334695) (xy 202.479304 111.197847) + (xy 202.445279 111.135535) (xy 202.4424 111.108752) (xy 202.4424 110.742874) (xy 202.462402 110.674753) + (xy 202.496127 110.639662) (xy 202.578754 110.581807) (xy 202.743807 110.416754) (xy 202.877691 110.225548) + (xy 202.976339 110.013998) (xy 203.036752 109.788532) (xy 203.057096 109.556) (xy 203.036752 109.323468) + (xy 203.033627 109.311807) (xy 203.011896 109.230703) (xy 202.976339 109.098002) (xy 202.877691 108.886452) + (xy 202.813442 108.794695) (xy 202.743813 108.695254) (xy 202.743809 108.695249) (xy 202.743807 108.695246) + (xy 202.578754 108.530193) (xy 202.57875 108.53019) (xy 202.578745 108.530186) (xy 202.387548 108.396309) + (xy 202.387546 108.396308) (xy 202.260176 108.336914) (xy 202.175998 108.297661) (xy 202.175996 108.29766) + (xy 202.175995 108.29766) (xy 201.950535 108.237248) (xy 201.776133 108.22199) (xy 201.718 108.216904) + (xy 201.717999 108.216904) (xy 201.487203 108.237096) (xy 201.417599 108.223107) (xy 201.387127 108.20067) + (xy 201.004032 107.817575) (xy 200.99206 107.803723) (xy 200.984762 107.79392) (xy 200.978496 107.785503) + (xy 200.978494 107.785501) (xy 200.939976 107.75318) (xy 200.935923 107.749466) (xy 200.9304 107.743942) + (xy 200.905544 107.724289) (xy 200.905544 107.724288) (xy 200.848774 107.676653) (xy 200.84877 107.676651) + (xy 200.848769 107.67665) (xy 200.84264 107.672618) (xy 200.842703 107.67252) (xy 200.837189 107.669006) + (xy 200.837128 107.669106) (xy 200.830882 107.665254) (xy 200.76369 107.633922) (xy 200.697451 107.600655) + (xy 200.690553 107.598144) (xy 200.690592 107.598034) (xy 200.684406 107.595884) (xy 200.68437 107.595995) + (xy 200.67741 107.593688) (xy 200.604814 107.578698) (xy 200.532668 107.561599) (xy 200.52538 107.560748) + (xy 200.525393 107.56063) (xy 200.518889 107.559966) (xy 200.518879 107.560084) (xy 200.511566 107.559444) + (xy 200.511565 107.559444) (xy 200.43748 107.5616) (xy 197.322042 107.5616) (xy 197.253921 107.541598) + (xy 197.207428 107.487942) (xy 197.203987 107.479633) (xy 197.152389 107.341296) (xy 197.152387 107.341292) + (xy 197.064761 107.224238) (xy 196.947707 107.136612) (xy 196.947702 107.13661) (xy 196.810704 107.085511) + (xy 196.810696 107.085509) (xy 196.750149 107.079) (xy 196.750138 107.079) (xy 195.255862 107.079) + (xy 195.25585 107.079) (xy 195.195303 107.085509) (xy 195.195295 107.085511) (xy 195.058297 107.13661) + (xy 195.058292 107.136612) (xy 194.941238 107.224238) (xy 194.853612 107.341292) (xy 194.85361 107.341297) + (xy 194.802511 107.478295) (xy 194.802509 107.478303) (xy 194.796 107.53885) (xy 194.796 109.033149) + (xy 194.802509 109.093696) (xy 194.802511 109.093704) (xy 194.85361 109.230702) (xy 194.853612 109.230707) + (xy 194.941238 109.347761) (xy 195.058292 109.435387) (xy 195.058294 109.435388) (xy 195.058296 109.435389) + (xy 195.117375 109.457424) (xy 195.195295 109.486488) (xy 195.195303 109.48649) (xy 195.25585 109.492999) + (xy 195.255855 109.492999) (xy 195.255862 109.493) (xy 195.255868 109.493) (xy 195.437272 109.493) + (xy 195.505393 109.513002) (xy 195.551886 109.566658) (xy 195.56199 109.636932) (xy 195.532496 109.701512) + (xy 195.490522 109.733194) (xy 195.479174 109.738486) (xy 195.397191 109.776715) (xy 195.224195 109.897847) + (xy 195.224185 109.897856) (xy 195.074851 110.04719) (xy 194.982907 110.178501) (xy 194.92745 110.222829) + (xy 194.85683 110.230138) (xy 194.79347 110.198107) (xy 194.764306 110.156843) (xy 194.751872 110.128497) + (xy 194.660131 109.988077) (xy 194.009033 110.639176) (xy 193.946721 110.673201) (xy 193.875905 110.668136) + (xy 193.81907 110.625589) (xy 193.807672 110.607284) (xy 193.795883 110.584146) (xy 193.795881 110.584144) + (xy 193.79588 110.584142) (xy 193.704855 110.493117) (xy 193.681714 110.481326) (xy 193.630099 110.432578) + (xy 193.613034 110.363662) (xy 193.635935 110.296461) (xy 193.649823 110.279964) (xy 194.301727 109.62806) + (xy 194.264555 109.599128) (xy 194.264554 109.599127) (xy 194.051686 109.48393) (xy 194.051684 109.483928) + (xy 193.822764 109.40534) (xy 193.822757 109.405338) (xy 193.584016 109.3655) (xy 193.341984 109.3655) + (xy 193.103242 109.405338) (xy 193.103235 109.40534) (xy 192.874315 109.483928) (xy 192.874313 109.48393) + (xy 192.661447 109.599126) (xy 192.624271 109.628061) (xy 192.62427 109.628061) (xy 193.276175 110.279966) + (xy 193.310201 110.342278) (xy 193.305136 110.413093) (xy 193.262589 110.469929) (xy 193.244284 110.481327) + (xy 193.221144 110.493117) (xy 193.130117 110.584144) (xy 193.118327 110.607284) (xy 193.069578 110.658899) + (xy 193.000663 110.675964) (xy 192.933462 110.653062) (xy 192.916966 110.639175) (xy 192.265867 109.988076) + (xy 192.265866 109.988077) (xy 192.174131 110.128488) (xy 192.174124 110.128502) (xy 192.076901 110.350147) + (xy 192.076898 110.350154) (xy 192.017484 110.584778) (xy 191.997495 110.826) (xy 192.017484 111.067221) + (xy 192.076898 111.301845) (xy 192.076901 111.301852) (xy 192.174126 111.523502) (xy 192.265866 111.663921) + (xy 192.916964 111.012823) (xy 192.979277 110.978798) (xy 193.050092 110.983862) (xy 193.106928 111.026409) + (xy 193.118326 111.044714) (xy 193.130117 111.067855) (xy 193.221142 111.15888) (xy 193.221144 111.158881) + (xy 193.221146 111.158883) (xy 193.244282 111.170671) (xy 193.295897 111.219418) (xy 193.312964 111.288332) + (xy 193.290064 111.355534) (xy 193.276175 111.372033) (xy 192.62427 112.023937) (xy 192.624271 112.023938) + (xy 192.661437 112.052866) (xy 192.661447 112.052872) (xy 192.874313 112.168069) (xy 192.874315 112.168071) + (xy 193.103235 112.246659) (xy 193.103242 112.246661) (xy 193.341984 112.2865) (xy 193.584016 112.2865) + (xy 193.822757 112.246661) (xy 193.822764 112.246659) (xy 194.051684 112.168071) (xy 194.051686 112.168069) + (xy 194.264558 112.052869) (xy 194.301727 112.023938) (xy 194.301728 112.023937) (xy 193.649824 111.372033) + (xy 193.615798 111.309721) (xy 193.620863 111.238906) (xy 193.66341 111.18207) (xy 193.681712 111.170674) + (xy 193.704854 111.158883) (xy 193.795883 111.067854) (xy 193.807671 111.044717) (xy 193.856416 110.993103) + (xy 193.925331 110.976034) (xy 193.992533 110.998934) (xy 194.009033 111.012824) (xy 194.66013 111.663921) + (xy 194.660131 111.663921) (xy 194.751875 111.523498) (xy 194.764306 111.495158) (xy 194.809986 111.440808) + (xy 194.877798 111.419783) (xy 194.946212 111.438758) (xy 194.982907 111.473498) (xy 195.074376 111.60413) + (xy 195.074852 111.604809) (xy 195.133964 111.663921) (xy 195.183197 111.713154) (xy 195.217222 111.775466) + (xy 195.212156 111.846282) (xy 195.183197 111.891344) (xy 194.469848 112.604695) (xy 194.407535 112.63872) + (xy 194.380752 112.6416) (xy 191.353732 112.6416) (xy 191.285611 112.621598) (xy 191.239118 112.567942) + (xy 191.228211 112.526581) (xy 191.225752 112.498468) (xy 191.225296 112.496768) (xy 191.208557 112.434294) + (xy 191.165339 112.273002) (xy 191.066691 112.061452) (xy 190.973523 111.928394) (xy 190.932813 111.870254) + (xy 190.93281 111.87025) (xy 190.932807 111.870246) (xy 190.767754 111.705193) (xy 190.76775 111.70519) + (xy 190.767745 111.705186) (xy 190.576548 111.571309) (xy 190.576546 111.571308) (xy 190.413243 111.495158) + (xy 190.364998 111.472661) (xy 190.364996 111.47266) (xy 190.364995 111.47266) (xy 190.139535 111.412248) + (xy 189.965132 111.39699) (xy 189.907 111.391904) (xy 189.906999 111.391904) (xy 189.676203 111.412096) + (xy 189.606599 111.398107) (xy 189.576127 111.37567) (xy 187.721905 109.521448) (xy 187.687879 109.459136) + (xy 187.685 109.432353) (xy 187.685 108.173867) (xy 187.684999 108.17385) (xy 187.67849 108.113303) + (xy 187.678488 108.113295) (xy 187.628025 107.978001) (xy 187.627389 107.976296) (xy 187.627388 107.976294) + (xy 187.627387 107.976292) (xy 187.539761 107.859238) (xy 187.422707 107.771612) (xy 187.422702 107.77161) + (xy 187.285704 107.720511) (xy 187.285696 107.720509) (xy 187.225149 107.714) (xy 187.225138 107.714) + (xy 185.730862 107.714) (xy 185.73085 107.714) (xy 185.670303 107.720509) (xy 185.670295 107.720511) + (xy 185.533297 107.77161) (xy 185.533292 107.771612) (xy 185.416238 107.859238) (xy 185.328612 107.976292) + (xy 185.32861 107.976296) (xy 185.277013 108.114633) (xy 185.234466 108.171469) (xy 185.167946 108.196279) + (xy 185.158958 108.1966) (xy 183.999947 108.1966) (xy 183.981687 108.19527) (xy 183.959213 108.191978) + (xy 183.909116 108.19636) (xy 183.903624 108.1966) (xy 183.895808 108.1966) (xy 183.870655 108.19954) + (xy 183.864366 108.200275) (xy 183.849596 108.201567) (xy 183.790513 108.206736) (xy 183.783329 108.20822) + (xy 183.783305 108.208105) (xy 183.77692 108.20952) (xy 183.776948 108.209635) (xy 183.7698 108.211329) + (xy 183.700153 108.236679) (xy 183.629769 108.260001) (xy 183.623123 108.263101) (xy 183.623074 108.262996) + (xy 183.617177 108.265851) (xy 183.617229 108.265955) (xy 183.610673 108.269247) (xy 183.548747 108.309976) + (xy 183.485642 108.3489) (xy 183.479886 108.353452) (xy 183.479814 108.353361) (xy 183.47474 108.357495) + (xy 183.474815 108.357584) (xy 183.469193 108.362301) (xy 183.42122 108.41315) (xy 183.418332 108.416211) + (xy 183.039846 108.794696) (xy 182.977536 108.82872) (xy 182.950753 108.8316) (xy 180.524837 108.8316) + (xy 180.456716 108.811598) (xy 180.425008 108.781125) (xy 180.42468 108.781401) (xy 180.422115 108.778344) + (xy 180.421628 108.777876) (xy 180.42115 108.777194) (xy 180.421146 108.777189) (xy 180.346923 108.702966) + (xy 180.271809 108.627852) (xy 180.250128 108.612671) (xy 180.098806 108.506714) (xy 179.907398 108.417459) + (xy 179.907393 108.417457) (xy 179.812715 108.392088) (xy 179.703394 108.362796) (xy 179.493 108.344389) + (xy 179.492999 108.344389) (xy 179.282606 108.362796) (xy 179.10601 108.410114) (xy 179.035034 108.408424) + (xy 178.976238 108.368629) (xy 178.948291 108.303365) (xy 178.9474 108.288407) (xy 178.9474 107.241246) + (xy 178.967402 107.173125) (xy 178.9843 107.152155) (xy 179.373624 106.76283) (xy 179.435935 106.728807) + (xy 179.50675 106.733871) (xy 179.515953 106.737725) (xy 179.543002 106.750339) (xy 179.768468 106.810752) + (xy 180.001 106.831096) (xy 180.233532 106.810752) (xy 180.458998 106.750339) (xy 180.670548 106.651691) + (xy 180.861754 106.517807) (xy 181.026807 106.352754) (xy 181.160691 106.161548) (xy 181.259339 105.949998) + (xy 181.319752 105.724532) (xy 181.340096 105.492) (xy 181.319752 105.259468) (xy 181.259339 105.034002) + (xy 181.160691 104.822452) (xy 181.087451 104.717854) (xy 181.026813 104.631254) (xy 181.02681 104.63125) + (xy 181.026807 104.631246) (xy 180.861754 104.466193) (xy 180.86175 104.46619) (xy 180.861745 104.466186) + (xy 180.670548 104.332309) (xy 180.670546 104.332308) (xy 180.524622 104.264262) (xy 180.458998 104.233661) + (xy 180.458996 104.23366) (xy 180.458987 104.233657) (xy 180.40622 104.219518) (xy 180.345597 104.182566) + (xy 180.314576 104.118705) (xy 180.323006 104.048211) (xy 180.349734 104.00872) (xy 184.20115 100.157305) + (xy 184.263463 100.123279) (xy 184.290246 100.1204) (xy 185.660109 100.1204) (xy 185.72823 100.140402) + (xy 185.774723 100.194058) (xy 185.784827 100.264332) (xy 185.755333 100.328912) (xy 185.73238 100.349612) + (xy 185.720729 100.357771) (xy 185.699188 100.372854) (xy 185.699185 100.372856) (xy 185.549856 100.522185) + (xy 185.549847 100.522195) (xy 185.428715 100.695191) (xy 185.339459 100.886601) (xy 185.339457 100.886606) + (xy 185.305476 101.013428) (xy 185.284796 101.090606) (xy 185.266389 101.301) (xy 185.284796 101.511394) + (xy 185.308657 101.600444) (xy 185.339457 101.715393) (xy 185.339459 101.715398) (xy 185.428714 101.906806) + (xy 185.533608 102.056611) (xy 185.549852 102.079809) (xy 185.699191 102.229148) (xy 185.699865 102.22962) + (xy 185.700092 102.229904) (xy 185.703401 102.23268) (xy 185.702843 102.233344) (xy 185.744196 102.285071) + (xy 185.7536 102.332836) (xy 185.7536 103.779052) (xy 185.75227 103.797311) (xy 185.748978 103.819784) + (xy 185.748978 103.819789) (xy 185.75336 103.869881) (xy 185.7536 103.875374) (xy 185.7536 103.883191) + (xy 185.757277 103.914651) (xy 185.763736 103.98848) (xy 185.76522 103.995666) (xy 185.765104 103.995689) + (xy 185.766521 104.002079) (xy 185.766635 104.002052) (xy 185.768329 104.009199) (xy 185.787945 104.063093) + (xy 185.793688 104.078871) (xy 185.806887 104.118705) (xy 185.817003 104.149231) (xy 185.820103 104.155879) + (xy 185.819996 104.155928) (xy 185.822848 104.161818) (xy 185.822953 104.161766) (xy 185.826244 104.16832) + (xy 185.86698 104.230256) (xy 185.905897 104.293352) (xy 185.910454 104.299115) (xy 185.910361 104.299188) + (xy 185.914493 104.30426) (xy 185.914584 104.304184) (xy 185.919299 104.309804) (xy 185.973213 104.360669) + (xy 189.263841 107.651297) (xy 189.297867 107.713609) (xy 189.292802 107.784424) (xy 189.250256 107.841259) + (xy 189.226239 107.859238) (xy 189.138612 107.976292) (xy 189.13861 107.976297) (xy 189.087511 108.113295) + (xy 189.087509 108.113303) (xy 189.081 108.17385) (xy 189.081 109.668149) (xy 189.087509 109.728696) + (xy 189.087511 109.728704) (xy 189.13861 109.865702) (xy 189.138612 109.865707) (xy 189.226238 109.982761) + (xy 189.343292 110.070387) (xy 189.343294 110.070388) (xy 189.343296 110.070389) (xy 189.402375 110.092424) + (xy 189.480295 110.121488) (xy 189.480303 110.12149) (xy 189.54085 110.127999) (xy 189.540855 110.127999) + (xy 189.540862 110.128) (xy 189.540868 110.128) (xy 191.035132 110.128) (xy 191.035138 110.128) + (xy 191.035145 110.127999) (xy 191.035149 110.127999) (xy 191.095696 110.12149) (xy 191.095699 110.121489) + (xy 191.095701 110.121489) (xy 191.100651 110.119643) (xy 191.12967 110.108819) (xy 191.232704 110.070389) + (xy 191.249269 110.057989) (xy 191.349761 109.982761) (xy 191.437387 109.865707) (xy 191.437387 109.865706) + (xy 191.437389 109.865704) (xy 191.486813 109.733195) (xy 191.488488 109.728704) (xy 191.48849 109.728696) + (xy 191.494999 109.668149) (xy 191.495 109.668132) (xy 191.495 108.173867) (xy 191.494999 108.17385) + (xy 191.48849 108.113303) (xy 191.488488 108.113295) (xy 191.438025 107.978001) (xy 191.437389 107.976296) + (xy 191.437388 107.976294) (xy 191.437387 107.976292) (xy 191.349761 107.859238) (xy 191.232707 107.771612) + (xy 191.232702 107.77161) (xy 191.094177 107.719942) (xy 191.037341 107.677395) (xy 191.013107 107.612422) + (xy 191.012826 107.612455) (xy 191.012691 107.611305) (xy 191.012531 107.610875) (xy 191.012422 107.609193) + (xy 191.0124 107.608823) (xy 191.0124 107.608812) (xy 191.008724 107.577366) (xy 191.002264 107.503517) + (xy 191.002261 107.503509) (xy 191.000779 107.496327) (xy 191.000896 107.496302) (xy 190.99948 107.489917) + (xy 190.999364 107.489945) (xy 190.997672 107.482807) (xy 190.997672 107.482805) (xy 190.97232 107.413153) + (xy 190.948999 107.342772) (xy 190.948996 107.342768) (xy 190.945896 107.336118) (xy 190.946003 107.336068) + (xy 190.943152 107.330179) (xy 190.943046 107.330233) (xy 190.939756 107.323684) (xy 190.939754 107.323677) + (xy 190.917788 107.290279) (xy 190.899023 107.261747) (xy 190.8601 107.198645) (xy 190.860099 107.198643) + (xy 190.860096 107.19864) (xy 190.855546 107.192885) (xy 190.855638 107.192811) (xy 190.851506 107.187739) + (xy 190.851416 107.187815) (xy 190.846702 107.182197) (xy 190.82641 107.163053) (xy 190.792787 107.131331) + (xy 189.035048 105.373592) (xy 187.239305 103.577848) (xy 187.205279 103.515536) (xy 187.2024 103.488753) + (xy 187.2024 102.332836) (xy 187.222402 102.264715) (xy 187.252875 102.233009) (xy 187.252599 102.23268) + (xy 187.255667 102.230105) (xy 187.256132 102.229621) (xy 187.256809 102.229148) (xy 187.406148 102.079809) + (xy 187.429387 102.04662) (xy 187.484843 102.002292) (xy 187.555463 101.994983) (xy 187.618823 102.027013) + (xy 187.654809 102.088214) (xy 187.6586 102.118891) (xy 187.6586 102.509052) (xy 187.65727 102.527311) + (xy 187.653978 102.549784) (xy 187.653978 102.549789) (xy 187.65836 102.599881) (xy 187.6586 102.605374) + (xy 187.6586 102.613191) (xy 187.662277 102.644651) (xy 187.668736 102.71848) (xy 187.67022 102.725666) + (xy 187.670104 102.725689) (xy 187.671521 102.732079) (xy 187.671635 102.732052) (xy 187.673329 102.739199) + (xy 187.698688 102.808871) (xy 187.722003 102.879231) (xy 187.725103 102.885879) (xy 187.724996 102.885928) + (xy 187.727848 102.891818) (xy 187.727953 102.891766) (xy 187.731244 102.89832) (xy 187.77198 102.960256) + (xy 187.810897 103.023352) (xy 187.815454 103.029115) (xy 187.815361 103.029188) (xy 187.819493 103.03426) + (xy 187.819584 103.034184) (xy 187.824299 103.039804) (xy 187.878213 103.090669) (xy 191.001968 106.214425) + (xy 191.013941 106.228279) (xy 191.020375 106.236922) (xy 191.027504 106.246497) (xy 191.066022 106.278817) + (xy 191.070069 106.282527) (xy 191.075598 106.288056) (xy 191.075597 106.288056) (xy 191.100455 106.30771) + (xy 191.157226 106.355348) (xy 191.163357 106.35938) (xy 191.163291 106.359478) (xy 191.16881 106.362994) + (xy 191.168873 106.362894) (xy 191.175115 106.366743) (xy 191.175118 106.366746) (xy 191.175121 106.366747) + (xy 191.175122 106.366748) (xy 191.242309 106.398077) (xy 191.308551 106.431346) (xy 191.315455 106.433859) + (xy 191.315414 106.433971) (xy 191.321589 106.436117) (xy 191.321627 106.436004) (xy 191.32859 106.438311) + (xy 191.328591 106.438311) (xy 191.328593 106.438312) (xy 191.375651 106.448028) (xy 191.401185 106.453301) + (xy 191.424792 106.458896) (xy 191.47333 106.4704) (xy 191.473336 106.4704) (xy 191.480619 106.471252) + (xy 191.480605 106.471369) (xy 191.48711 106.472033) (xy 191.487121 106.471916) (xy 191.494428 106.472554) + (xy 191.494435 106.472556) (xy 191.56852 106.4704) (xy 197.846054 106.4704) (xy 197.864313 106.471729) + (xy 197.886788 106.475022) (xy 197.918545 106.472243) (xy 197.936881 106.47064) (xy 197.942374 106.4704) + (xy 197.950189 106.4704) (xy 197.965919 106.468561) (xy 197.981651 106.466722) (xy 198.055483 106.460263) + (xy 198.055495 106.460258) (xy 198.062665 106.458779) (xy 198.062689 106.458896) (xy 198.069082 106.457479) + (xy 198.069055 106.457364) (xy 198.076189 106.455672) (xy 198.076195 106.455672) (xy 198.145871 106.430311) + (xy 198.216229 106.406998) (xy 198.21624 106.40699) (xy 198.222884 106.403894) (xy 198.222935 106.404003) + (xy 198.228809 106.401159) (xy 198.228756 106.401053) (xy 198.235319 106.397756) (xy 198.23532 106.397754) + (xy 198.235323 106.397754) (xy 198.265643 106.377811) (xy 198.297257 106.357019) (xy 198.316843 106.344938) + (xy 198.360357 106.318099) (xy 198.360362 106.318093) (xy 198.366115 106.313546) (xy 198.366189 106.313639) + (xy 198.371252 106.309515) (xy 198.371176 106.309424) (xy 198.376798 106.304705) (xy 198.376798 106.304703) + (xy 198.376805 106.3047) (xy 198.427668 106.250787) (xy 201.387127 103.291327) (xy 201.449437 103.257304) + (xy 201.487197 103.254903) (xy 201.718 103.275096) (xy 201.950532 103.254752) (xy 202.175998 103.194339) + (xy 202.387548 103.095691) (xy 202.578754 102.961807) (xy 202.743807 102.796754) (xy 202.793567 102.725689) + (xy 202.801662 102.714129) (xy 202.857119 102.669801) (xy 202.904875 102.6604) (xy 204.341125 102.6604) + (xy 204.409246 102.680402) (xy 204.444338 102.714129) (xy 204.50219 102.79675) (xy 204.502193 102.796754) + (xy 204.667246 102.961807) (xy 204.66725 102.96181) (xy 204.667254 102.961813) (xy 204.776346 103.0382) + (xy 204.858452 103.095691) (xy 205.070002 103.194339) (xy 205.295468 103.254752) (xy 205.528 103.275096) + (xy 205.760532 103.254752) (xy 205.985998 103.194339) (xy 206.197548 103.095691) (xy 206.388754 102.961807) + (xy 206.553807 102.796754) (xy 206.687691 102.605548) (xy 206.786339 102.393998) (xy 206.846752 102.168532) + (xy 206.867096 101.936) (xy 206.846752 101.703468) (xy 206.843627 101.691807) (xy 206.819147 101.600444) + (xy 206.786339 101.478002) (xy 206.687691 101.266452) (xy 206.608613 101.153517) (xy 206.553813 101.075254) + (xy 206.553809 101.075249) (xy 206.553807 101.075246) (xy 206.388754 100.910193) (xy 206.38875 100.91019) + (xy 206.388745 100.910186) (xy 206.197548 100.776309) (xy 206.197546 100.776308) (xy 206.028178 100.69733) + (xy 205.985998 100.677661) (xy 205.985996 100.67766) (xy 205.985995 100.67766) (xy 205.760535 100.617248) + (xy 205.586133 100.60199) (xy 205.528 100.596904) (xy 205.527999 100.596904) (xy 205.295464 100.617248) + (xy 205.070004 100.67766) (xy 205.069999 100.677662) (xy 204.858453 100.776308) (xy 204.858451 100.776309) + (xy 204.667254 100.910186) (xy 204.667243 100.910195) (xy 204.502195 101.075243) (xy 204.50219 101.075249) + (xy 204.444338 101.157871) (xy 204.388881 101.202199) (xy 204.341125 101.2116) (xy 202.904875 101.2116) + (xy 202.836754 101.191598) (xy 202.801662 101.157871) (xy 202.743809 101.075249) (xy 202.743804 101.075243) + (xy 202.578756 100.910195) (xy 202.578745 100.910186) (xy 202.387548 100.776309) (xy 202.387546 100.776308) + (xy 202.218178 100.69733) (xy 202.175998 100.677661) (xy 202.175996 100.67766) (xy 202.175995 100.67766) + (xy 201.950533 100.617248) (xy 201.950526 100.617247) (xy 201.824926 100.606258) (xy 201.758808 100.580395) + (xy 201.717168 100.522891) (xy 201.713228 100.452004) (xy 201.746813 100.391642) (xy 201.780454 100.358002) + (xy 201.981151 100.157304) (xy 202.043464 100.123279) (xy 202.070247 100.1204) (xy 207.34833 100.1204) + (xy 208.985754 100.1204) (xy 209.053875 100.140402) (xy 209.074849 100.157305) (xy 210.481695 101.564151) + (xy 210.515721 101.626463) (xy 210.5186 101.653246) (xy 210.5186 103.289125) (xy 210.498598 103.357246) + (xy 210.464871 103.392337) (xy 210.408283 103.431962) (xy 210.382245 103.450194) (xy 210.382243 103.450195) + (xy 210.21719 103.615249) (xy 210.159338 103.697871) (xy 210.103881 103.742199) (xy 210.056125 103.7516) + (xy 207.494947 103.7516) (xy 207.476687 103.75027) (xy 207.454213 103.746978) (xy 207.404116 103.75136) + (xy 207.398624 103.7516) (xy 207.390808 103.7516) (xy 207.365655 103.75454) (xy 207.359366 103.755275) + (xy 207.344596 103.756567) (xy 207.285513 103.761736) (xy 207.278329 103.76322) (xy 207.278305 103.763105) + (xy 207.27192 103.76452) (xy 207.271948 103.764635) (xy 207.2648 103.766329) (xy 207.195153 103.791679) + (xy 207.124769 103.815001) (xy 207.118123 103.818101) (xy 207.118074 103.817996) (xy 207.112177 103.820851) + (xy 207.112229 103.820955) (xy 207.105673 103.824247) (xy 207.043747 103.864976) (xy 206.980642 103.9039) + (xy 206.974886 103.908452) (xy 206.974814 103.908361) (xy 206.96974 103.912495) (xy 206.969815 103.912584) + (xy 206.964193 103.917301) (xy 206.913331 103.971212) (xy 205.059573 105.824968) (xy 205.045724 105.836938) + (xy 205.027502 105.850504) (xy 205.027499 105.850507) (xy 204.995181 105.889022) (xy 204.99147 105.893071) + (xy 204.985942 105.898599) (xy 204.985939 105.898603) (xy 204.966289 105.923455) (xy 204.918653 105.980225) + (xy 204.914623 105.986353) (xy 204.914526 105.986289) (xy 204.911009 105.991808) (xy 204.911108 105.991869) + (xy 204.907255 105.998116) (xy 204.875922 106.065309) (xy 204.842654 106.131551) (xy 204.840143 106.138451) + (xy 204.840033 106.138411) (xy 204.837886 106.14459) (xy 204.837996 106.144627) (xy 204.835688 106.15159) + (xy 204.820698 106.224185) (xy 204.803599 106.296331) (xy 204.802748 106.30362) (xy 204.802631 106.303606) + (xy 204.801966 106.310113) (xy 204.802084 106.310124) (xy 204.801444 106.317435) (xy 204.8036 106.391519) + (xy 204.8036 108.369125) (xy 204.783598 108.437246) (xy 204.749871 108.472337) (xy 204.670587 108.527854) + (xy 204.667245 108.530194) (xy 204.667243 108.530195) (xy 204.50219 108.695249) (xy 204.502186 108.695254) + (xy 204.368309 108.886451) (xy 204.368308 108.886453) (xy 204.269662 109.097999) (xy 204.26966 109.098004) + (xy 204.209248 109.323464) (xy 204.188904 109.556) (xy 204.209248 109.788535) (xy 204.267756 110.006889) + (xy 204.269661 110.013998) (xy 204.290175 110.05799) (xy 204.368308 110.225546) (xy 204.368309 110.225548) + (xy 204.502186 110.416745) (xy 204.50219 110.41675) (xy 204.502193 110.416754) (xy 204.667246 110.581807) + (xy 204.66725 110.58181) (xy 204.667254 110.581813) (xy 204.762929 110.648805) (xy 204.858452 110.715691) + (xy 205.070002 110.814339) (xy 205.295468 110.874752) (xy 205.528 110.895096) (xy 205.760532 110.874752) + (xy 205.985998 110.814339) (xy 206.197548 110.715691) (xy 206.388754 110.581807) (xy 206.553807 110.416754) + (xy 206.687691 110.225548) (xy 206.786339 110.013998) (xy 206.846752 109.788532) (xy 206.867096 109.556) + (xy 206.846752 109.323468) (xy 206.843627 109.311807) (xy 206.821896 109.230703) (xy 206.786339 109.098002) + (xy 206.687691 108.886452) (xy 206.623442 108.794695) (xy 206.553813 108.695254) (xy 206.553809 108.695249) + (xy 206.553807 108.695246) (xy 206.388754 108.530193) (xy 206.306128 108.472337) (xy 206.2618 108.41688) + (xy 206.2524 108.369125) (xy 206.2524 106.733246) (xy 206.272402 106.665125) (xy 206.289305 106.644151) + (xy 207.696152 105.237305) (xy 207.758464 105.203279) (xy 207.785247 105.2004) (xy 210.056125 105.2004) + (xy 210.124246 105.220402) (xy 210.159338 105.254129) (xy 210.21719 105.33675) (xy 210.217193 105.336754) + (xy 210.382246 105.501807) (xy 210.38225 105.50181) (xy 210.382254 105.501813) (xy 210.491346 105.5782) + (xy 210.573452 105.635691) (xy 210.785002 105.734339) (xy 211.010468 105.794752) (xy 211.243 105.815096) + (xy 211.475532 105.794752) (xy 211.700998 105.734339) (xy 211.912548 105.635691) (xy 212.103754 105.501807) + (xy 212.268807 105.336754) (xy 212.402691 105.145548) (xy 212.501339 104.933998) (xy 212.561752 104.708532) + (xy 212.582096 104.476) (xy 212.561752 104.243468) (xy 212.501339 104.018002) (xy 212.402691 103.806452) + (xy 212.326662 103.697871) (xy 212.268813 103.615254) (xy 212.268809 103.615249) (xy 212.268807 103.615246) + (xy 212.103754 103.450193) (xy 212.021128 103.392337) (xy 211.9768 103.33688) (xy 211.9674 103.289125) + (xy 211.9674 101.362947) (xy 211.96873 101.344687) (xy 211.969043 101.342543) (xy 211.972022 101.322213) + (xy 211.968929 101.286852) (xy 211.96764 101.272115) (xy 211.9674 101.266623) (xy 211.9674 101.25881) + (xy 211.963724 101.227366) (xy 211.960411 101.189493) (xy 211.957264 101.153517) (xy 211.957261 101.153509) + (xy 211.955779 101.146327) (xy 211.955896 101.146302) (xy 211.95448 101.139917) (xy 211.954364 101.139945) + (xy 211.952672 101.132807) (xy 211.952672 101.132805) (xy 211.92732 101.063153) (xy 211.903999 100.992772) + (xy 211.903996 100.992768) (xy 211.900896 100.986118) (xy 211.901003 100.986068) (xy 211.898152 100.980179) + (xy 211.898046 100.980233) (xy 211.894756 100.973684) (xy 211.894754 100.973677) (xy 211.866011 100.929975) + (xy 211.854023 100.911747) (xy 211.8151 100.848645) (xy 211.815099 100.848643) (xy 211.815096 100.84864) + (xy 211.810546 100.842885) (xy 211.810638 100.842811) (xy 211.806506 100.837739) (xy 211.806416 100.837815) + (xy 211.801702 100.832197) (xy 211.753383 100.786611) (xy 211.747787 100.781331) (xy 209.894032 98.927575) + (xy 209.88206 98.913723) (xy 209.875424 98.904809) (xy 209.868496 98.895503) (xy 209.868494 98.895501) + (xy 209.829976 98.86318) (xy 209.825923 98.859466) (xy 209.8204 98.853942) (xy 209.795544 98.834289) + (xy 209.788479 98.828361) (xy 209.738774 98.786653) (xy 209.73877 98.786651) (xy 209.738769 98.78665) + (xy 209.73264 98.782618) (xy 209.732703 98.78252) (xy 209.727189 98.779006) (xy 209.727128 98.779106) + (xy 209.720882 98.775254) (xy 209.65369 98.743922) (xy 209.587451 98.710655) (xy 209.580553 98.708144) + (xy 209.580592 98.708034) (xy 209.574406 98.705884) (xy 209.57437 98.705995) (xy 209.56741 98.703688) + (xy 209.494814 98.688698) (xy 209.422668 98.671599) (xy 209.41538 98.670748) (xy 209.415393 98.67063) + (xy 209.408889 98.669966) (xy 209.408879 98.670084) (xy 209.401566 98.669444) (xy 209.401565 98.669444) + (xy 209.32748 98.6716) (xy 201.779947 98.6716) (xy 201.761687 98.67027) (xy 201.739213 98.666978) + (xy 201.689116 98.67136) (xy 201.683624 98.6716) (xy 201.675808 98.6716) (xy 201.650655 98.67454) + (xy 201.644366 98.675275) (xy 201.629596 98.676567) (xy 201.570513 98.681736) (xy 201.563329 98.68322) + (xy 201.563305 98.683105) (xy 201.55692 98.68452) (xy 201.556948 98.684635) (xy 201.5498 98.686329) + (xy 201.480153 98.711679) (xy 201.409769 98.735001) (xy 201.403123 98.738101) (xy 201.403074 98.737996) + (xy 201.397177 98.740851) (xy 201.397229 98.740955) (xy 201.390673 98.744247) (xy 201.328747 98.784976) + (xy 201.265642 98.8239) (xy 201.259886 98.828452) (xy 201.259814 98.828361) (xy 201.25474 98.832495) + (xy 201.254815 98.832584) (xy 201.249193 98.837301) (xy 201.198331 98.891212) (xy 197.644848 102.444695) + (xy 197.582536 102.478721) (xy 197.555753 102.4816) (xy 196.052042 102.4816) (xy 195.983921 102.461598) + (xy 195.937428 102.407942) (xy 195.933987 102.399633) (xy 195.882389 102.261296) (xy 195.882387 102.261292) + (xy 195.794761 102.144238) (xy 195.677707 102.056612) (xy 195.677702 102.05661) (xy 195.540704 102.005511) + (xy 195.540696 102.005509) (xy 195.480149 101.999) (xy 195.480138 101.999) (xy 195.298729 101.999) + (xy 195.230608 101.978998) (xy 195.184115 101.925342) (xy 195.174011 101.855068) (xy 195.203505 101.790488) + (xy 195.245479 101.758805) (xy 195.254545 101.754576) (xy 195.338806 101.715286) (xy 195.511809 101.594148) + (xy 195.661148 101.444809) (xy 195.782286 101.271806) (xy 195.871542 101.080395) (xy 195.881293 101.044002) + (xy 195.918244 100.98338) (xy 195.982104 100.952358) (xy 196.052599 100.960786) (xy 196.107346 101.005988) + (xy 196.124706 101.044001) (xy 196.12523 101.045956) (xy 196.134456 101.08039) (xy 196.134459 101.080398) + (xy 196.223714 101.271806) (xy 196.336355 101.432675) (xy 196.344852 101.444809) (xy 196.494191 101.594148) + (xy 196.667194 101.715286) (xy 196.858605 101.804542) (xy 197.062606 101.859204) (xy 197.273 101.877611) + (xy 197.483394 101.859204) (xy 197.687395 101.804542) (xy 197.878806 101.715286) (xy 198.051809 101.594148) + (xy 198.201148 101.444809) (xy 198.322286 101.271806) (xy 198.411542 101.080395) (xy 198.466204 100.876394) + (xy 198.484611 100.666) (xy 198.466204 100.455606) (xy 198.411542 100.251605) (xy 198.322286 100.060195) + (xy 198.322285 100.060194) (xy 198.322284 100.060191) (xy 198.201152 99.887195) (xy 198.201143 99.887185) + (xy 198.123205 99.809247) (xy 198.089179 99.746935) (xy 198.0863 99.720152) (xy 198.0863 99.399884) + (xy 198.086577 99.309396) (xy 198.086576 99.309395) (xy 198.086577 99.309391) (xy 198.076967 99.266687) + (xy 198.075825 99.259903) (xy 198.070927 99.216425) (xy 198.070926 99.216422) (xy 198.070926 99.216419) + (xy 198.060144 99.185607) (xy 198.057356 99.177639) (xy 198.055356 99.170656) (xy 198.046342 99.130596) + (xy 198.046341 99.130594) (xy 198.04634 99.130588) (xy 198.027466 99.091089) (xy 198.02485 99.084745) + (xy 198.010394 99.04343) (xy 198.007227 99.038389) (xy 197.98854 99.008649) (xy 197.985035 99.002286) + (xy 197.967327 98.965222) (xy 197.964332 98.961443) (xy 197.940132 98.930906) (xy 197.936178 98.925316) + (xy 197.912887 98.888247) (xy 197.912885 98.888245) (xy 197.912884 98.888243) (xy 197.848952 98.824312) + (xy 197.055222 98.025712) (xy 197.052941 98.023293) (xy 197.052187 98.022449) (xy 197.052183 98.022444) + (xy 197.020859 97.991139) (xy 196.98961 97.959698) (xy 196.989609 97.959697) (xy 196.989606 97.959694) + (xy 196.988744 97.958919) (xy 196.986349 97.956649) (xy 194.1165 95.088515) (xy 193.558507 94.530855) + (xy 193.524464 94.468553) (xy 193.522056 94.43075) (xy 193.526987 94.374389) (xy 193.532096 94.316) + (xy 193.511752 94.083468) (xy 193.451339 93.858002) (xy 193.352691 93.646452) (xy 193.284832 93.54954) + (xy 193.218813 93.455254) (xy 193.21881 93.45525) (xy 193.218807 93.455246) (xy 193.053754 93.290193) + (xy 193.05375 93.29019) (xy 193.053745 93.290186) (xy 192.862548 93.156309) (xy 192.862546 93.156308) + (xy 192.700075 93.080546) (xy 192.650998 93.057661) (xy 192.650996 93.05766) (xy 192.650995 93.05766) + (xy 192.425535 92.997248) (xy 192.251132 92.98199) (xy 192.193 92.976904) (xy 192.192999 92.976904) + (xy 191.960464 92.997248) (xy 191.735004 93.05766) (xy 191.734999 93.057662) (xy 191.523453 93.156308) + (xy 191.523451 93.156309) (xy 191.332254 93.290186) (xy 191.332249 93.29019) (xy 191.167192 93.455246) + (xy 191.165144 93.457688) (xy 191.163969 93.458469) (xy 191.163303 93.459136) (xy 191.163169 93.459002) + (xy 191.106036 93.497016) (xy 191.06862 93.5027) (xy 189.28007 93.5027) (xy 189.211949 93.482698) + (xy 189.190975 93.465795) (xy 186.911519 91.186339) (xy 186.911515 91.186333) (xy 186.861292 91.136112) + (xy 186.825372 91.113542) (xy 186.819606 91.109451) (xy 186.786441 91.083002) (xy 186.748212 91.064591) + (xy 186.74203 91.061175) (xy 186.70611 91.038606) (xy 186.706109 91.038605) (xy 186.706108 91.038605) + (xy 186.666069 91.024594) (xy 186.659538 91.021889) (xy 186.650982 91.017769) (xy 186.621316 91.003483) + (xy 186.579951 90.994041) (xy 186.573166 90.992086) (xy 186.556937 90.986407) (xy 186.533126 90.978075) + (xy 186.533121 90.978074) (xy 186.533119 90.978073) (xy 186.533115 90.978073) (xy 186.490956 90.973321) + (xy 186.483989 90.972137) (xy 186.44264 90.9627) (xy 186.442637 90.9627) (xy 186.396672 90.9627) + (xy 186.269672 90.9627) (xy 181.708847 90.9627) (xy 181.640726 90.942698) (xy 181.619752 90.925795) + (xy 181.541811 90.847854) (xy 181.541805 90.847849) (xy 181.368806 90.726714) (xy 181.177398 90.637459) + (xy 181.177393 90.637457) (xy 181.082716 90.612088) (xy 180.973394 90.582796) (xy 180.763 90.564389) + (xy 180.762999 90.564389) (xy 180.657802 90.573592) (xy 180.552606 90.582796) (xy 180.497944 90.597442) + (xy 180.348606 90.637457) (xy 180.348601 90.637459) (xy 180.157191 90.726715) (xy 179.984195 90.847847) + (xy 179.984185 90.847856) (xy 179.834856 90.997185) (xy 179.834847 90.997195) (xy 179.713715 91.170191) + (xy 179.624459 91.361601) (xy 179.624457 91.361606) (xy 179.591971 91.482847) (xy 179.569796 91.565606) + (xy 179.569142 91.573077) (xy 179.553664 91.750002) (xy 179.551389 91.776) (xy 179.569796 91.986394) + (xy 179.578152 92.017578) (xy 179.624457 92.190393) (xy 179.624459 92.190398) (xy 179.713714 92.381805) + (xy 179.713714 92.381806) (xy 179.834852 92.554809) (xy 179.984191 92.704148) (xy 180.157194 92.825286) + (xy 180.348605 92.914542) (xy 180.384997 92.924293) (xy 180.44562 92.961245) (xy 180.476641 93.025105) + (xy 180.468213 93.0956) (xy 180.42301 93.150347) (xy 180.384998 93.167706) (xy 180.376462 93.169993) + (xy 180.348606 93.177457) (xy 180.348601 93.177459) (xy 180.157191 93.266715) (xy 179.984195 93.387847) + (xy 179.984185 93.387856) (xy 179.834858 93.537183) (xy 179.834849 93.537194) (xy 179.834372 93.537876) + (xy 179.834085 93.538105) (xy 179.83132 93.541401) (xy 179.830657 93.540844) (xy 179.778913 93.582202) + (xy 179.731163 93.5916) (xy 177.236667 93.5916) (xy 177.168546 93.571598) (xy 177.147572 93.554695) + (xy 174.900452 91.307575) (xy 174.88848 91.293723) (xy 174.874916 91.275503) (xy 174.874914 91.275501) + (xy 174.836396 91.24318) (xy 174.832343 91.239466) (xy 174.82682 91.233942) (xy 174.801964 91.214289) + (xy 174.745194 91.166653) (xy 174.74519 91.166651) (xy 174.745189 91.16665) (xy 174.73906 91.162618) + (xy 174.739123 91.16252) (xy 174.733609 91.159006) (xy 174.733548 91.159106) (xy 174.727302 91.155254) + (xy 174.66011 91.123922) (xy 174.593871 91.090655) (xy 174.586973 91.088144) (xy 174.587012 91.088034) + (xy 174.580826 91.085884) (xy 174.58079 91.085995) (xy 174.57383 91.083688) (xy 174.501233 91.068698) + (xy 174.477431 91.063057) (xy 174.434749 91.052941) (xy 174.37308 91.017769) (xy 174.345754 90.974371) + (xy 174.292389 90.831296) (xy 174.292387 90.831292) (xy 174.204761 90.714238) (xy 174.087707 90.626612) + (xy 174.087702 90.62661) (xy 173.950704 90.575511) (xy 173.950696 90.575509) (xy 173.890149 90.569) + (xy 173.890138 90.569) (xy 172.395862 90.569) (xy 172.39585 90.569) (xy 172.335303 90.575509) (xy 172.335295 90.575511) + (xy 172.198297 90.62661) (xy 172.198292 90.626612) (xy 172.081238 90.714238) (xy 171.993612 90.831292) + (xy 171.99361 90.831297) (xy 171.942511 90.968295) (xy 171.942509 90.968303) (xy 171.936 91.02885) + (xy 171.936 92.523149) (xy 171.942509 92.583696) (xy 171.942511 92.583704) (xy 171.99361 92.720702) + (xy 171.993612 92.720707) (xy 172.081238 92.837761) (xy 172.198292 92.925387) (xy 172.198294 92.925387) + (xy 172.198296 92.925389) (xy 172.205146 92.927944) (xy 172.205149 92.927945) (xy 172.261984 92.970493) + (xy 172.286794 93.037013) (xy 172.271702 93.106387) (xy 172.221499 93.156588) (xy 172.205149 93.164055) + (xy 172.198295 93.166611) (xy 172.198292 93.166612) (xy 172.081238 93.254238) (xy 171.993612 93.371292) + (xy 171.99361 93.371297) (xy 171.942511 93.508295) (xy 171.942509 93.508303) (xy 171.936 93.56885) + (xy 171.936 95.063149) (xy 171.942509 95.123696) (xy 171.942511 95.123704) (xy 171.99361 95.260702) + (xy 171.993612 95.260707) (xy 172.086639 95.384975) (xy 172.085403 95.385899) (xy 172.11465 95.439459) + (xy 172.109585 95.510274) (xy 172.080624 95.555337) (xy 168.330809 99.305152) (xy 168.268497 99.339178) + (xy 168.230733 99.341578) (xy 168.063 99.326904) (xy 167.830464 99.347248) (xy 167.605004 99.40766) + (xy 167.604999 99.407662) (xy 167.393453 99.506308) (xy 167.393451 99.506309) (xy 167.202254 99.640186) + (xy 167.202243 99.640195) (xy 167.037195 99.805243) (xy 167.037186 99.805254) (xy 166.903309 99.996451) + (xy 166.903308 99.996453) (xy 166.804662 100.207999) (xy 166.80466 100.208004) (xy 166.744248 100.433464) + (xy 166.723904 100.666) (xy 166.744248 100.898535) (xy 166.799474 101.104641) (xy 166.804661 101.123998) + (xy 166.852863 101.227366) (xy 166.903308 101.335546) (xy 166.903309 101.335548) (xy 167.037186 101.526745) + (xy 167.03719 101.52675) (xy 167.037193 101.526754) (xy 167.202246 101.691807) (xy 167.20225 101.69181) + (xy 167.202254 101.691813) (xy 167.235776 101.715285) (xy 167.393452 101.825691) (xy 167.605002 101.924339) + (xy 167.830468 101.984752) (xy 168.063 102.005096) (xy 168.295532 101.984752) (xy 168.520998 101.924339) + (xy 168.732548 101.825691) (xy 168.923754 101.691807) (xy 169.088807 101.526754) (xy 169.222691 101.335548) + (xy 169.321339 101.123998) (xy 169.381752 100.898532) (xy 169.402096 100.666) (xy 169.381752 100.433468) + (xy 169.373035 100.400935) (xy 169.374723 100.32996) (xy 169.405644 100.27923) (xy 173.053908 96.630967) + (xy 173.116216 96.596944) (xy 173.187031 96.602009) (xy 173.232094 96.63097) (xy 174.897489 98.296365) + (xy 174.931513 98.358675) (xy 174.926449 98.42949) (xy 174.897489 98.474553) (xy 174.754847 98.617195) + (xy 174.633715 98.790191) (xy 174.544459 98.981601) (xy 174.544457 98.981606) (xy 174.516011 99.08777) + (xy 174.493009 99.173617) (xy 174.489796 99.185607) (xy 174.477833 99.32235) (xy 174.471389 99.396) + (xy 174.489796 99.606394) (xy 174.503932 99.659151) (xy 174.544457 99.810393) (xy 174.544459 99.810398) + (xy 174.633714 100.001806) (xy 174.754378 100.174133) (xy 174.754852 100.174809) (xy 174.904191 100.324148) + (xy 174.904194 100.32415) (xy 174.947018 100.354136) (xy 174.991346 100.409593) (xy 174.998655 100.480213) + (xy 174.997351 100.486406) (xy 174.983412 100.545219) (xy 174.982464 100.548759) (xy 174.964749 100.607931) + (xy 174.964748 100.607933) (xy 174.964749 100.607933) (xy 174.964207 100.617248) (xy 174.964152 100.618185) + (xy 174.96097 100.639909) (xy 174.959943 100.644243) (xy 174.924773 100.705916) (xy 174.881372 100.733245) + (xy 174.738295 100.786611) (xy 174.738292 100.786612) (xy 174.621238 100.874238) (xy 174.533612 100.991292) + (xy 174.53361 100.991297) (xy 174.482511 101.128295) (xy 174.482509 101.128303) (xy 174.476 101.18885) + (xy 174.476 102.683149) (xy 174.482509 102.743696) (xy 174.482511 102.743704) (xy 174.53361 102.880702) + (xy 174.533612 102.880707) (xy 174.621238 102.997761) (xy 174.738292 103.085387) (xy 174.738294 103.085388) + (xy 174.738296 103.085389) (xy 174.785451 103.102977) (xy 174.875295 103.136488) (xy 174.875303 103.13649) + (xy 174.93585 103.142999) (xy 174.935855 103.142999) (xy 174.935862 103.143) (xy 174.935868 103.143) + (xy 176.322352 103.143) (xy 176.390473 103.163002) (xy 176.436966 103.216658) (xy 176.44707 103.286932) + (xy 176.417576 103.351512) (xy 176.411447 103.358095) (xy 173.905887 105.863655) (xy 173.843575 105.897681) + (xy 173.77276 105.892616) (xy 173.715924 105.850069) (xy 173.691113 105.783549) (xy 173.695086 105.741948) + (xy 173.69975 105.72454) (xy 173.69975 105.724539) (xy 173.699752 105.724532) (xy 173.720096 105.492) + (xy 173.699752 105.259468) (xy 173.639339 105.034002) (xy 173.540691 104.822452) (xy 173.467451 104.717854) + (xy 173.406813 104.631254) (xy 173.40681 104.63125) (xy 173.406807 104.631246) (xy 173.241754 104.466193) + (xy 173.24175 104.46619) (xy 173.241745 104.466186) (xy 173.050548 104.332309) (xy 173.050546 104.332308) + (xy 172.904622 104.264262) (xy 172.838998 104.233661) (xy 172.838996 104.23366) (xy 172.838995 104.23366) + (xy 172.613535 104.173248) (xy 172.415004 104.155879) (xy 172.381 104.152904) (xy 172.380999 104.152904) + (xy 172.148464 104.173248) (xy 171.923004 104.23366) (xy 171.922999 104.233662) (xy 171.711453 104.332308) + (xy 171.711451 104.332309) (xy 171.520254 104.466186) (xy 171.520243 104.466195) (xy 171.355195 104.631243) + (xy 171.355186 104.631254) (xy 171.221309 104.822451) (xy 171.221308 104.822453) (xy 171.122662 105.033999) + (xy 171.12266 105.034004) (xy 171.062248 105.259464) (xy 171.041904 105.492) (xy 171.062248 105.724535) + (xy 171.12266 105.949995) (xy 171.122662 105.95) (xy 171.221308 106.161546) (xy 171.221309 106.161548) + (xy 171.355186 106.352745) (xy 171.35519 106.35275) (xy 171.355193 106.352754) (xy 171.520246 106.517807) + (xy 171.52025 106.51781) (xy 171.520254 106.517813) (xy 171.593975 106.569433) (xy 171.638304 106.62489) + (xy 171.645613 106.695509) (xy 171.613583 106.758869) (xy 171.6108 106.761741) (xy 169.860082 108.512458) + (xy 169.79777 108.546484) (xy 169.726954 108.541419) (xy 169.670119 108.498872) (xy 169.645308 108.432352) + (xy 169.645375 108.413476) (xy 169.655408 108.285999) (xy 169.635803 108.036892) (xy 169.577471 107.79392) + (xy 169.481845 107.563057) (xy 169.353914 107.354295) (xy 169.353913 107.354294) (xy 168.609033 108.099175) + (xy 168.546721 108.133201) (xy 168.475906 108.128136) (xy 168.41907 108.085589) (xy 168.407672 108.067284) + (xy 168.395883 108.044146) (xy 168.395881 108.044144) (xy 168.39588 108.044142) (xy 168.304855 107.953117) + (xy 168.281714 107.941326) (xy 168.230099 107.892578) (xy 168.213034 107.823662) (xy 168.235935 107.756461) + (xy 168.249823 107.739964) (xy 168.994703 106.995085) (xy 168.994703 106.995084) (xy 168.785942 106.867154) + (xy 168.555079 106.771528) (xy 168.312107 106.713196) (xy 168.062999 106.693591) (xy 167.813892 106.713196) + (xy 167.57092 106.771528) (xy 167.340062 106.867152) (xy 167.131295 106.995085) (xy 167.131295 106.995086) + (xy 167.876175 107.739966) (xy 167.910201 107.802278) (xy 167.905136 107.873093) (xy 167.862589 107.929929) + (xy 167.844284 107.941327) (xy 167.821144 107.953117) (xy 167.730117 108.044144) (xy 167.718327 108.067284) + (xy 167.669578 108.118899) (xy 167.600663 108.135964) (xy 167.533462 108.113062) (xy 167.516966 108.099175) + (xy 166.772086 107.354295) (xy 166.772085 107.354295) (xy 166.644152 107.563062) (xy 166.548528 107.79392) + (xy 166.490196 108.036892) (xy 166.470591 108.285999) (xy 166.490196 108.535107) (xy 166.548528 108.778079) + (xy 166.644154 109.008942) (xy 166.772084 109.217703) (xy 166.772085 109.217703) (xy 167.516964 108.472823) + (xy 167.579277 108.438798) (xy 167.650092 108.443862) (xy 167.706928 108.486409) (xy 167.718326 108.504714) + (xy 167.730117 108.527855) (xy 167.821142 108.61888) (xy 167.821144 108.618881) (xy 167.821146 108.618883) + (xy 167.844282 108.630671) (xy 167.895897 108.679418) (xy 167.912964 108.748332) (xy 167.890064 108.815534) + (xy 167.876175 108.832033) (xy 167.131294 109.576913) (xy 167.131635 109.579792) (xy 167.119777 109.649791) + (xy 167.071958 109.702268) (xy 167.006508 109.7206) (xy 162.917946 109.7206) (xy 162.899686 109.71927) + (xy 162.898239 109.719058) (xy 162.877212 109.715978) (xy 162.877211 109.715978) (xy 162.827118 109.72036) + (xy 162.821625 109.7206) (xy 162.813808 109.7206) (xy 162.782349 109.724277) (xy 162.708516 109.730736) + (xy 162.701331 109.73222) (xy 162.701307 109.732107) (xy 162.694909 109.733526) (xy 162.694936 109.733638) + (xy 162.687799 109.735329) (xy 162.634515 109.754723) (xy 162.618132 109.760686) (xy 162.594678 109.768458) + (xy 162.547769 109.784002) (xy 162.54112 109.787103) (xy 162.541071 109.786998) (xy 162.535183 109.789848) + (xy 162.535235 109.789952) (xy 162.528678 109.793245) (xy 162.46675 109.833974) (xy 162.403641 109.872901) + (xy 162.397885 109.877452) (xy 162.397813 109.877362) (xy 162.392745 109.88149) (xy 162.39282 109.881579) + (xy 162.387194 109.886299) (xy 162.336347 109.940195) (xy 161.904706 110.371835) (xy 161.842394 110.405861) + (xy 161.771579 110.400796) (xy 161.714743 110.358249) (xy 161.689932 110.291729) (xy 161.693903 110.250138) + (xy 161.724087 110.13749) (xy 161.7389 109.968178) (xy 161.7389 109.143822) (xy 161.724087 108.97451) + (xy 161.665399 108.755483) (xy 161.569569 108.549975) (xy 161.439509 108.36423) (xy 161.27917 108.203891) + (xy 161.268757 108.1966) (xy 161.093425 108.073831) (xy 160.88792 107.978002) (xy 160.887915 107.978) + (xy 160.786263 107.950763) (xy 160.66889 107.919313) (xy 160.443 107.89955) (xy 160.21711 107.919313) + (xy 160.158423 107.935038) (xy 159.998084 107.978) (xy 159.998079 107.978002) (xy 159.792574 108.073831) + (xy 159.606833 108.203888) (xy 159.606827 108.203893) (xy 159.446493 108.364227) (xy 159.446488 108.364233) + (xy 159.316431 108.549974) (xy 159.287195 108.612671) (xy 159.240277 108.665956) (xy 159.172 108.685417) + (xy 159.10404 108.664875) (xy 159.058805 108.612671) (xy 159.034555 108.560668) (xy 159.029569 108.549975) + (xy 158.899509 108.36423) (xy 158.73917 108.203891) (xy 158.734006 108.200275) (xy 158.681128 108.163249) + (xy 158.6368 108.107792) (xy 158.6274 108.060037) (xy 158.6274 107.3584) (xy 158.647402 107.290279) + (xy 158.701058 107.243786) (xy 158.7534 107.2324) (xy 162.159054 107.2324) (xy 162.177313 107.233729) + (xy 162.199788 107.237022) (xy 162.231545 107.234243) (xy 162.249881 107.23264) (xy 162.255374 107.2324) + (xy 162.263189 107.2324) (xy 162.278919 107.230561) (xy 162.294651 107.228722) (xy 162.368483 107.222263) + (xy 162.368495 107.222258) (xy 162.375665 107.220779) (xy 162.375689 107.220896) (xy 162.382082 107.219479) + (xy 162.382055 107.219364) (xy 162.389189 107.217672) (xy 162.389195 107.217672) (xy 162.458871 107.192311) + (xy 162.529229 107.168998) (xy 162.52924 107.16899) (xy 162.535884 107.165894) (xy 162.535935 107.166003) + (xy 162.541809 107.163159) (xy 162.541756 107.163053) (xy 162.548319 107.159756) (xy 162.54832 107.159754) + (xy 162.548323 107.159754) (xy 162.583511 107.13661) (xy 162.610257 107.119019) (xy 162.629717 107.107015) + (xy 162.673357 107.080099) (xy 162.673362 107.080093) (xy 162.679115 107.075546) (xy 162.679189 107.075639) + (xy 162.684252 107.071515) (xy 162.684176 107.071424) (xy 162.689798 107.066705) (xy 162.689798 107.066703) + (xy 162.689805 107.0667) (xy 162.740668 107.012787) (xy 164.59443 105.159025) (xy 164.608269 105.147065) + (xy 164.626497 105.133496) (xy 164.658827 105.094965) (xy 164.662511 105.090944) (xy 164.66806 105.085397) + (xy 164.680526 105.069629) (xy 164.68771 105.060545) (xy 164.709982 105.034002) (xy 164.735347 105.003774) + (xy 164.735348 105.00377) (xy 164.735351 105.003768) (xy 164.739385 104.997636) (xy 164.739484 104.997701) + (xy 164.742996 104.992188) (xy 164.742894 104.992126) (xy 164.746741 104.985887) (xy 164.746746 104.985882) + (xy 164.778077 104.91869) (xy 164.811347 104.852446) (xy 164.811347 104.852443) (xy 164.813858 104.845547) + (xy 164.813969 104.845587) (xy 164.816116 104.839413) (xy 164.816003 104.839376) (xy 164.818312 104.832407) + (xy 164.833301 104.759813) (xy 164.843245 104.717856) (xy 164.8504 104.68767) (xy 164.8504 104.687663) + (xy 164.851252 104.680382) (xy 164.851368 104.680395) (xy 164.852033 104.673889) (xy 164.851916 104.673879) + (xy 164.852554 104.666572) (xy 164.852556 104.666565) (xy 164.8504 104.59248) (xy 164.850399 101.108946) + (xy 164.85173 101.090681) (xy 164.852446 101.085795) (xy 164.855022 101.068213) (xy 164.852463 101.038964) + (xy 164.85064 101.018115) (xy 164.8504 101.012623) (xy 164.8504 101.00481) (xy 164.846724 100.973366) + (xy 164.846724 100.973365) (xy 164.840264 100.899517) (xy 164.840261 100.899509) (xy 164.838779 100.892327) + (xy 164.838896 100.892302) (xy 164.83748 100.885917) (xy 164.837364 100.885945) (xy 164.835672 100.878807) + (xy 164.835672 100.878805) (xy 164.81032 100.809153) (xy 164.786999 100.738772) (xy 164.786996 100.738768) + (xy 164.783896 100.732118) (xy 164.784003 100.732068) (xy 164.781152 100.726179) (xy 164.781046 100.726233) + (xy 164.777756 100.719684) (xy 164.777754 100.719677) (xy 164.757268 100.688529) (xy 164.737023 100.657747) + (xy 164.702723 100.60214) (xy 164.698099 100.594643) (xy 164.698096 100.59464) (xy 164.693546 100.588885) + (xy 164.693638 100.588811) (xy 164.689506 100.583739) (xy 164.689416 100.583815) (xy 164.684702 100.578197) + (xy 164.65739 100.55243) (xy 164.630787 100.527331) (xy 164.26076 100.157304) (xy 163.879535 99.776078) + (xy 163.845509 99.713766) (xy 163.850574 99.642951) (xy 163.893121 99.586115) (xy 163.959641 99.561304) + (xy 163.98937 99.562702) (xy 164.131979 99.5865) (xy 164.374016 99.5865) (xy 164.612757 99.546661) + (xy 164.612764 99.546659) (xy 164.841684 99.468071) (xy 164.841686 99.468069) (xy 165.054558 99.352869) + (xy 165.091727 99.323938) (xy 165.091728 99.323937) (xy 164.439824 98.672033) (xy 164.405798 98.609721) + (xy 164.410863 98.538906) (xy 164.45341 98.48207) (xy 164.471712 98.470674) (xy 164.494854 98.458883) + (xy 164.585883 98.367854) (xy 164.597671 98.344717) (xy 164.646416 98.293103) (xy 164.715331 98.276034) + (xy 164.782533 98.298934) (xy 164.799033 98.312824) (xy 165.45013 98.963921) (xy 165.450131 98.963921) + (xy 165.541874 98.823499) (xy 165.639098 98.601852) (xy 165.639101 98.601845) (xy 165.698515 98.367221) + (xy 165.718504 98.126) (xy 165.698515 97.884778) (xy 165.639101 97.650154) (xy 165.639098 97.650147) + (xy 165.541874 97.4285) (xy 165.450131 97.288077) (xy 164.799033 97.939176) (xy 164.736721 97.973201) + (xy 164.665905 97.968136) (xy 164.60907 97.925589) (xy 164.597674 97.907287) (xy 164.585883 97.884146) + (xy 164.585881 97.884144) (xy 164.58588 97.884142) (xy 164.494855 97.793117) (xy 164.471714 97.781326) + (xy 164.420099 97.732578) (xy 164.403034 97.663662) (xy 164.425935 97.596461) (xy 164.439823 97.579964) + (xy 165.091727 96.92806) (xy 165.054555 96.899128) (xy 165.054554 96.899127) (xy 164.910951 96.821414) + (xy 164.86056 96.771401) (xy 164.845208 96.702084) (xy 164.869769 96.635471) (xy 164.89865 96.607387) + (xy 165.0318 96.514154) (xy 165.031809 96.514148) (xy 165.181148 96.364809) (xy 165.302286 96.191806) + (xy 165.391542 96.000395) (xy 165.446204 95.796394) (xy 165.464611 95.586) (xy 165.446204 95.375606) + (xy 165.391542 95.171605) (xy 165.302286 94.980195) (xy 165.302285 94.980194) (xy 165.302284 94.980191) + (xy 165.181152 94.807195) (xy 165.181149 94.807192) (xy 165.147952 94.773995) (xy 165.031809 94.657852) + (xy 165.025005 94.653088) (xy 164.858806 94.536714) (xy 164.667398 94.447459) (xy 164.667393 94.447457) + (xy 164.572716 94.422088) (xy 164.463394 94.392796) (xy 164.253 94.374389) (xy 164.252999 94.374389) + (xy 164.147803 94.383592) (xy 164.042606 94.392796) (xy 163.987944 94.407442) (xy 163.838606 94.447457) + (xy 163.838601 94.447459) (xy 163.647191 94.536715) (xy 163.474195 94.657847) (xy 163.474192 94.65785) + (xy 163.324849 94.807194) (xy 163.324846 94.807197) (xy 163.284309 94.86509) (xy 163.228851 94.909418) + (xy 163.220733 94.912421) (xy 163.182773 94.925) (xy 163.17612 94.928103) (xy 163.176071 94.927998) + (xy 163.170183 94.930848) (xy 163.170235 94.930952) (xy 163.163678 94.934245) (xy 163.10175 94.974974) + (xy 163.038641 95.013901) (xy 163.032885 95.018452) (xy 163.032813 95.018362) (xy 163.027745 95.02249) + (xy 163.02782 95.022579) (xy 163.022194 95.027299) (xy 162.971347 95.081195) (xy 161.752573 96.299968) + (xy 161.738724 96.311938) (xy 161.720502 96.325504) (xy 161.720499 96.325507) (xy 161.688181 96.364022) + (xy 161.68447 96.368071) (xy 161.678942 96.373599) (xy 161.678939 96.373603) (xy 161.659289 96.398455) + (xy 161.611653 96.455225) (xy 161.607623 96.461353) (xy 161.607526 96.461289) (xy 161.604009 96.466808) + (xy 161.604108 96.466869) (xy 161.600255 96.473116) (xy 161.568922 96.540309) (xy 161.535654 96.606551) + (xy 161.533143 96.613451) (xy 161.533033 96.613411) (xy 161.530886 96.61959) (xy 161.530996 96.619627) + (xy 161.528688 96.62659) (xy 161.513698 96.699185) (xy 161.496599 96.771331) (xy 161.495748 96.77862) + (xy 161.495631 96.778606) (xy 161.494966 96.785113) (xy 161.495084 96.785124) (xy 161.494444 96.792435) + (xy 161.4966 96.866519) (xy 161.4966 99.080052) (xy 161.49527 99.098311) (xy 161.491978 99.120784) + (xy 161.491978 99.120789) (xy 161.49636 99.170881) (xy 161.4966 99.176374) (xy 161.4966 99.184191) + (xy 161.500277 99.215651) (xy 161.506736 99.28948) (xy 161.50822 99.296666) (xy 161.508104 99.296689) + (xy 161.509521 99.303079) (xy 161.509635 99.303052) (xy 161.511329 99.310199) (xy 161.521877 99.339178) + (xy 161.536688 99.379871) (xy 161.559517 99.448766) (xy 161.560003 99.450231) (xy 161.563103 99.456879) + (xy 161.562996 99.456928) (xy 161.565848 99.462818) (xy 161.565953 99.462766) (xy 161.569244 99.46932) + (xy 161.60998 99.531256) (xy 161.648897 99.594352) (xy 161.653454 99.600115) (xy 161.653361 99.600188) + (xy 161.657493 99.60526) (xy 161.657584 99.605184) (xy 161.662299 99.610804) (xy 161.716213 99.661669) + (xy 163.364695 101.310151) (xy 163.39872 101.372463) (xy 163.4016 101.399246) (xy 163.4016 104.250753) + (xy 163.381598 104.318874) (xy 163.364695 104.339848) (xy 161.957848 105.746695) (xy 161.895536 105.780721) + (xy 161.868753 105.7836) (xy 158.7534 105.7836) (xy 158.685279 105.763598) (xy 158.638786 105.709942) + (xy 158.6274 105.6576) (xy 158.6274 103.431962) (xy 158.647402 103.363841) (xy 158.681127 103.32875) + (xy 158.73917 103.288109) (xy 158.899509 103.12777) (xy 159.029569 102.942025) (xy 159.058805 102.879327) + (xy 159.105722 102.826043) (xy 159.173999 102.806582) (xy 159.241959 102.827124) (xy 159.287194 102.879327) + (xy 159.296051 102.89832) (xy 159.316431 102.942025) (xy 159.420512 103.090669) (xy 159.446491 103.12777) + (xy 159.60683 103.288109) (xy 159.792575 103.418169) (xy 159.998083 103.513999) (xy 160.21711 103.572687) + (xy 160.443 103.59245) (xy 160.66889 103.572687) (xy 160.887917 103.513999) (xy 161.093425 103.418169) + (xy 161.27917 103.288109) (xy 161.439509 103.12777) (xy 161.569569 102.942025) (xy 161.665399 102.736517) + (xy 161.724087 102.51749) (xy 161.7389 102.348178) (xy 161.7389 101.523822) (xy 161.724087 101.35451) + (xy 161.665399 101.135483) (xy 161.569569 100.929975) (xy 161.439509 100.74423) (xy 161.27917 100.583891) + (xy 161.271035 100.578195) (xy 161.221128 100.543249) (xy 161.1768 100.487792) (xy 161.1674 100.440037) + (xy 161.1674 99.457946) (xy 161.16873 99.439686) (xy 161.172022 99.417212) (xy 161.168755 99.379871) + (xy 161.16764 99.367117) (xy 161.1674 99.361624) (xy 161.1674 99.353811) (xy 161.163722 99.32235) + (xy 161.162588 99.30939) (xy 161.157263 99.248516) (xy 161.157262 99.248513) (xy 161.157262 99.248511) + (xy 161.155779 99.241328) (xy 161.155894 99.241304) (xy 161.154479 99.23492) (xy 161.154365 99.234948) + (xy 161.152672 99.227809) (xy 161.152672 99.227805) (xy 161.127313 99.158132) (xy 161.103998 99.087771) + (xy 161.103995 99.087767) (xy 161.100896 99.081119) (xy 161.101004 99.081068) (xy 161.098154 99.075182) + (xy 161.098047 99.075236) (xy 161.094757 99.068687) (xy 161.094754 99.068677) (xy 161.073587 99.036495) + (xy 161.054025 99.00675) (xy 161.0151 98.943643) (xy 161.010546 98.937884) (xy 161.010638 98.93781) + (xy 161.006507 98.932738) (xy 161.006416 98.932815) (xy 161.001699 98.927193) (xy 160.960413 98.888243) + (xy 160.947804 98.876347) (xy 160.4111 98.339643) (xy 160.377074 98.277331) (xy 160.374675 98.239566) + (xy 160.384611 98.126005) (xy 160.384161 98.12086) (xy 160.366204 97.915606) (xy 160.311542 97.711605) + (xy 160.222286 97.520195) (xy 160.222285 97.520194) (xy 160.222284 97.520191) (xy 160.101152 97.347195) + (xy 160.101149 97.347192) (xy 160.042034 97.288077) (xy 159.951809 97.197852) (xy 159.812337 97.100193) + (xy 159.778806 97.076714) (xy 159.587398 96.987459) (xy 159.58739 96.987456) (xy 159.560229 96.980178) + (xy 159.551002 96.977706) (xy 159.49038 96.940756) (xy 159.459358 96.876896) (xy 159.467786 96.806401) + (xy 159.512988 96.751654) (xy 159.551001 96.734293) (xy 159.587395 96.724542) (xy 159.778806 96.635286) + (xy 159.951809 96.514148) (xy 160.101148 96.364809) (xy 160.222286 96.191806) (xy 160.311542 96.000395) + (xy 160.366204 95.796394) (xy 160.384611 95.586) (xy 160.366204 95.375606) (xy 160.311542 95.171605) + (xy 160.222286 94.980195) (xy 160.222285 94.980194) (xy 160.222284 94.980191) (xy 160.101152 94.807195) + (xy 160.101149 94.807192) (xy 160.067952 94.773995) (xy 159.951809 94.657852) (xy 159.945005 94.653088) + (xy 159.778806 94.536714) (xy 159.587398 94.447459) (xy 159.587393 94.447457) (xy 159.492715 94.422088) + (xy 159.383394 94.392796) (xy 159.173 94.374389) (xy 158.962606 94.392796) (xy 158.907944 94.407442) + (xy 158.758606 94.447457) (xy 158.758601 94.447459) (xy 158.567191 94.536715) (xy 158.394195 94.657847) + (xy 158.394185 94.657856) (xy 158.244858 94.807183) (xy 158.244849 94.807194) (xy 158.244372 94.807876) + (xy 158.244085 94.808105) (xy 158.24132 94.811401) (xy 158.240657 94.810844) (xy 158.188913 94.852202) + (xy 158.141163 94.8616) (xy 156.549875 94.8616) (xy 156.481754 94.841598) (xy 156.446662 94.807871) + (xy 156.388809 94.725249) (xy 156.388804 94.725243) (xy 156.223756 94.560195) (xy 156.223745 94.560186) + (xy 156.032548 94.426309) (xy 156.032546 94.426308) (xy 155.821 94.327662) (xy 155.820995 94.32766) + (xy 155.595535 94.267248) (xy 155.363 94.246904) (xy 155.130464 94.267248) (xy 154.905004 94.32766) + (xy 154.904999 94.327662) (xy 154.693453 94.426308) (xy 154.693451 94.426309) (xy 154.502254 94.560186) + (xy 154.502243 94.560195) (xy 154.337195 94.725243) (xy 154.33719 94.725249) (xy 154.279338 94.807871) + (xy 154.223881 94.852199) (xy 154.176125 94.8616) (xy 150.558046 94.8616) (xy 150.489925 94.841598) + (xy 150.443432 94.787942) (xy 150.433328 94.717668) (xy 150.462822 94.653088) (xy 150.468951 94.646504) + (xy 150.689147 94.426309) (xy 151.943152 93.172304) (xy 152.005465 93.138279) (xy 152.032248 93.1354) + (xy 154.176125 93.1354) (xy 154.244246 93.155402) (xy 154.279338 93.189129) (xy 154.33719 93.27175) + (xy 154.337193 93.271754) (xy 154.502246 93.436807) (xy 154.50225 93.43681) (xy 154.502254 93.436813) + (xy 154.588233 93.497016) (xy 154.693452 93.570691) (xy 154.905002 93.669339) (xy 155.130468 93.729752) + (xy 155.363 93.750096) (xy 155.595532 93.729752) (xy 155.820998 93.669339) (xy 156.032548 93.570691) + (xy 156.223754 93.436807) (xy 156.388807 93.271754) (xy 156.446184 93.189811) (xy 156.446662 93.189129) + (xy 156.502119 93.144801) (xy 156.549875 93.1354) (xy 158.141164 93.1354) (xy 158.209285 93.155402) + (xy 158.240991 93.185876) (xy 158.241321 93.1856) (xy 158.24389 93.188662) (xy 158.244375 93.189129) + (xy 158.244852 93.189809) (xy 158.394191 93.339148) (xy 158.567194 93.460286) (xy 158.758605 93.549542) + (xy 158.962606 93.604204) (xy 159.173 93.622611) (xy 159.383394 93.604204) (xy 159.587395 93.549542) + (xy 159.778806 93.460286) (xy 159.951809 93.339148) (xy 160.101148 93.189809) (xy 160.222286 93.016806) + (xy 160.311542 92.825395) (xy 160.366204 92.621394) (xy 160.384611 92.411) (xy 160.384611 92.410999) + (xy 162.787495 92.410999) (xy 162.807484 92.652221) (xy 162.866898 92.886845) (xy 162.866901 92.886852) + (xy 162.964126 93.108502) (xy 163.055866 93.248921) (xy 163.706964 92.597823) (xy 163.769277 92.563798) + (xy 163.840092 92.568862) (xy 163.896928 92.611409) (xy 163.908326 92.629714) (xy 163.920117 92.652855) + (xy 164.011142 92.74388) (xy 164.011144 92.743881) (xy 164.011146 92.743883) (xy 164.034282 92.755671) + (xy 164.085897 92.804418) (xy 164.102964 92.873332) (xy 164.080064 92.940534) (xy 164.066175 92.957033) + (xy 163.41427 93.608937) (xy 163.414271 93.608938) (xy 163.451437 93.637866) (xy 163.451447 93.637872) + (xy 163.664313 93.753069) (xy 163.664315 93.753071) (xy 163.893235 93.831659) (xy 163.893242 93.831661) + (xy 164.131984 93.8715) (xy 164.374016 93.8715) (xy 164.612757 93.831661) (xy 164.612764 93.831659) + (xy 164.841684 93.753071) (xy 164.841686 93.753069) (xy 165.054558 93.637869) (xy 165.091727 93.608938) + (xy 165.091728 93.608937) (xy 164.439824 92.957033) (xy 164.405798 92.894721) (xy 164.410863 92.823906) + (xy 164.45341 92.76707) (xy 164.471712 92.755674) (xy 164.494854 92.743883) (xy 164.585883 92.652854) + (xy 164.597671 92.629717) (xy 164.646416 92.578103) (xy 164.715331 92.561034) (xy 164.782533 92.583934) + (xy 164.799033 92.597824) (xy 165.45013 93.248921) (xy 165.450131 93.248921) (xy 165.541874 93.108499) + (xy 165.639098 92.886852) (xy 165.639101 92.886845) (xy 165.698515 92.652221) (xy 165.718504 92.410999) + (xy 165.698515 92.169778) (xy 165.639101 91.935154) (xy 165.639098 91.935147) (xy 165.541874 91.7135) + (xy 165.450131 91.573077) (xy 164.799033 92.224176) (xy 164.736721 92.258201) (xy 164.665905 92.253136) + (xy 164.60907 92.210589) (xy 164.597672 92.192284) (xy 164.585883 92.169146) (xy 164.585881 92.169144) + (xy 164.58588 92.169142) (xy 164.494855 92.078117) (xy 164.471714 92.066326) (xy 164.420099 92.017578) + (xy 164.403034 91.948662) (xy 164.425935 91.881461) (xy 164.439823 91.864964) (xy 165.091727 91.21306) + (xy 165.054555 91.184128) (xy 165.054554 91.184127) (xy 164.841686 91.06893) (xy 164.841684 91.068928) + (xy 164.612764 90.99034) (xy 164.612757 90.990338) (xy 164.374016 90.9505) (xy 164.131984 90.9505) + (xy 163.893242 90.990338) (xy 163.893235 90.99034) (xy 163.664315 91.068928) (xy 163.664313 91.06893) + (xy 163.451447 91.184126) (xy 163.414271 91.213061) (xy 163.41427 91.213061) (xy 164.066175 91.864966) + (xy 164.100201 91.927278) (xy 164.095136 91.998093) (xy 164.052589 92.054929) (xy 164.034284 92.066327) + (xy 164.011144 92.078117) (xy 163.920117 92.169144) (xy 163.908327 92.192284) (xy 163.859578 92.243899) + (xy 163.790663 92.260964) (xy 163.723462 92.238062) (xy 163.706966 92.224175) (xy 163.055867 91.573076) + (xy 163.055866 91.573077) (xy 162.964131 91.713488) (xy 162.964124 91.713502) (xy 162.866901 91.935147) + (xy 162.866898 91.935154) (xy 162.807484 92.169778) (xy 162.787495 92.410999) (xy 160.384611 92.410999) + (xy 160.366204 92.200606) (xy 160.311542 91.996605) (xy 160.222286 91.805195) (xy 160.222285 91.805194) + (xy 160.222284 91.805191) (xy 160.101152 91.632195) (xy 160.101149 91.632192) (xy 160.034563 91.565606) + (xy 159.951809 91.482852) (xy 159.812337 91.385193) (xy 159.778806 91.361714) (xy 159.587398 91.272459) + (xy 159.587393 91.272457) (xy 159.478128 91.24318) (xy 159.383394 91.217796) (xy 159.173 91.199389) + (xy 159.172999 91.199389) (xy 159.067803 91.208592) (xy 158.962606 91.217796) (xy 158.907944 91.232442) + (xy 158.758606 91.272457) (xy 158.758601 91.272459) (xy 158.567191 91.361715) (xy 158.394195 91.482847) + (xy 158.394185 91.482856) (xy 158.244858 91.632183) (xy 158.244849 91.632194) (xy 158.244372 91.632876) + (xy 158.244085 91.633105) (xy 158.24132 91.636401) (xy 158.240657 91.635844) (xy 158.188913 91.677202) + (xy 158.141163 91.6866) (xy 156.549875 91.6866) (xy 156.481754 91.666598) (xy 156.446662 91.632871) + (xy 156.388809 91.550249) (xy 156.388804 91.550243) (xy 156.223756 91.385195) (xy 156.223745 91.385186) + (xy 156.032548 91.251309) (xy 156.032546 91.251308) (xy 155.888474 91.184126) (xy 155.820998 91.152661) + (xy 155.820996 91.15266) (xy 155.820995 91.15266) (xy 155.595535 91.092248) (xy 155.363 91.071904) + (xy 155.130464 91.092248) (xy 154.905004 91.15266) (xy 154.904999 91.152662) (xy 154.693453 91.251308) + (xy 154.693451 91.251309) (xy 154.502254 91.385186) (xy 154.502243 91.385195) (xy 154.337195 91.550243) + (xy 154.33719 91.550249) (xy 154.279338 91.632871) (xy 154.223881 91.677199) (xy 154.176125 91.6866) + (xy 151.741946 91.6866) (xy 151.723686 91.68527) (xy 151.701212 91.681978) (xy 151.651119 91.68636) + (xy 151.645626 91.6866) (xy 151.637807 91.6866) (xy 151.606348 91.690277) (xy 151.532517 91.696736) + (xy 151.525332 91.69822) (xy 151.525308 91.698107) (xy 151.518924 91.699522) (xy 151.518951 91.699634) + (xy 151.511807 91.701327) (xy 151.442127 91.726688) (xy 151.371773 91.75) (xy 151.365128 91.7531) + (xy 151.365078 91.752994) (xy 151.359179 91.75585) (xy 151.359232 91.755954) (xy 151.352675 91.759246) + (xy 151.290743 91.79998) (xy 151.227641 91.838901) (xy 151.221886 91.843452) (xy 151.221814 91.843361) + (xy 151.21674 91.847495) (xy 151.216815 91.847584) (xy 151.211193 91.852301) (xy 151.160331 91.906212) + (xy 148.621224 94.445317) (xy 148.558912 94.479343) (xy 148.488096 94.474278) (xy 148.459859 94.459436) + (xy 148.412547 94.426308) (xy 148.201 94.327662) (xy 148.200995 94.32766) (xy 147.975535 94.267248) + (xy 147.743 94.246904) (xy 147.510464 94.267248) (xy 147.285004 94.32766) (xy 147.284999 94.327662) + (xy 147.073453 94.426308) (xy 147.073451 94.426309) (xy 146.882254 94.560186) (xy 146.882243 94.560195) + (xy 146.717195 94.725243) (xy 146.717186 94.725254) (xy 146.583309 94.916451) (xy 146.583308 94.916453) + (xy 146.484662 95.127999) (xy 146.48466 95.128004) (xy 146.424248 95.353464) (xy 146.403904 95.586) + (xy 146.424248 95.818535) (xy 146.48466 96.043995) (xy 146.484662 96.044) (xy 146.583308 96.255546) + (xy 146.583309 96.255548) (xy 146.717186 96.446745) (xy 146.71719 96.44675) (xy 146.717193 96.446754) + (xy 146.882246 96.611807) (xy 146.88225 96.61181) (xy 146.882254 96.611813) (xy 146.977604 96.678578) + (xy 147.073452 96.745691) (xy 147.073453 96.745691) (xy 147.073454 96.745692) (xy 147.075526 96.746889) + (xy 147.076254 96.747653) (xy 147.077958 96.748846) (xy 147.077718 96.749188) (xy 147.124513 96.798278) + (xy 147.13794 96.867993) (xy 147.111545 96.933901) (xy 147.077892 96.96306) (xy 147.077958 96.963154) + (xy 147.077054 96.963786) (xy 147.075526 96.965111) (xy 147.073454 96.966307) (xy 146.882254 97.100186) + (xy 146.882243 97.100195) (xy 146.717195 97.265243) (xy 146.717186 97.265254) (xy 146.583309 97.456451) + (xy 146.583308 97.456453) (xy 146.484662 97.667999) (xy 146.48466 97.668004) (xy 146.424248 97.893464) + (xy 146.403904 98.126) (xy 146.424248 98.358535) (xy 146.482756 98.576889) (xy 146.484661 98.583998) + (xy 146.505175 98.62799) (xy 146.583308 98.795546) (xy 146.583309 98.795548) (xy 146.717186 98.986745) + (xy 146.71719 98.98675) (xy 146.717193 98.986754) (xy 146.882246 99.151807) (xy 146.88225 99.15181) + (xy 146.882254 99.151813) (xy 146.928495 99.184191) (xy 147.073452 99.285691) (xy 147.285002 99.384339) + (xy 147.510468 99.444752) (xy 147.743 99.465096) (xy 147.975532 99.444752) (xy 148.200998 99.384339) + (xy 148.412548 99.285691) (xy 148.603754 99.151807) (xy 148.768807 98.986754) (xy 148.902691 98.795548) + (xy 149.001339 98.583998) (xy 149.061752 98.358532) (xy 149.082096 98.126) (xy 149.061903 97.895199) + (xy 149.075892 97.825597) (xy 149.098323 97.795131) (xy 150.546153 96.347302) (xy 150.608463 96.313279) + (xy 150.635246 96.3104) (xy 154.176125 96.3104) (xy 154.244246 96.330402) (xy 154.279338 96.364129) + (xy 154.33719 96.44675) (xy 154.337193 96.446754) (xy 154.502246 96.611807) (xy 154.50225 96.61181) + (xy 154.502254 96.611813) (xy 154.597604 96.678578) (xy 154.693452 96.745691) (xy 154.693453 96.745691) + (xy 154.693454 96.745692) (xy 154.695526 96.746889) (xy 154.696254 96.747653) (xy 154.697958 96.748846) + (xy 154.697718 96.749188) (xy 154.744513 96.798278) (xy 154.75794 96.867993) (xy 154.731545 96.933901) + (xy 154.697892 96.96306) (xy 154.697958 96.963154) (xy 154.697054 96.963786) (xy 154.695526 96.965111) + (xy 154.693454 96.966307) (xy 154.502254 97.100186) (xy 154.502243 97.100195) (xy 154.337195 97.265243) + (xy 154.337186 97.265254) (xy 154.203309 97.456451) (xy 154.203308 97.456453) (xy 154.104662 97.667999) + (xy 154.10466 97.668004) (xy 154.044248 97.893464) (xy 154.023904 98.126) (xy 154.044248 98.358535) + (xy 154.102756 98.576889) (xy 154.104661 98.583998) (xy 154.125175 98.62799) (xy 154.203308 98.795546) + (xy 154.203309 98.795548) (xy 154.337186 98.986745) (xy 154.33719 98.98675) (xy 154.337193 98.986754) + (xy 154.502246 99.151807) (xy 154.50225 99.15181) (xy 154.502254 99.151813) (xy 154.548495 99.184191) + (xy 154.693452 99.285691) (xy 154.905002 99.384339) (xy 155.130468 99.444752) (xy 155.363 99.465096) + (xy 155.595532 99.444752) (xy 155.820998 99.384339) (xy 156.032548 99.285691) (xy 156.223754 99.151807) + (xy 156.388807 98.986754) (xy 156.446183 98.904813) (xy 156.446662 98.904129) (xy 156.502119 98.859801) + (xy 156.549875 98.8504) (xy 158.141164 98.8504) (xy 158.209285 98.870402) (xy 158.240991 98.900876) + (xy 158.241321 98.9006) (xy 158.24389 98.903662) (xy 158.244375 98.904129) (xy 158.244852 98.904809) + (xy 158.394191 99.054148) (xy 158.567194 99.175286) (xy 158.758605 99.264542) (xy 158.962606 99.319204) + (xy 159.173 99.337611) (xy 159.273497 99.328818) (xy 159.286567 99.327675) (xy 159.356172 99.341663) + (xy 159.386644 99.3641) (xy 159.681695 99.659151) (xy 159.715721 99.721463) (xy 159.7186 99.748246) + (xy 159.7186 100.440037) (xy 159.698598 100.508158) (xy 159.664872 100.543249) (xy 159.606829 100.583891) + (xy 159.606827 100.583893) (xy 159.446488 100.744233) (xy 159.316431 100.929974) (xy 159.287195 100.992671) + (xy 159.240277 101.045956) (xy 159.172 101.065417) (xy 159.10404 101.044875) (xy 159.058805 100.992671) + (xy 159.040006 100.952358) (xy 159.029569 100.929975) (xy 158.899509 100.74423) (xy 158.73917 100.583891) + (xy 158.739061 100.583815) (xy 158.553425 100.453831) (xy 158.34792 100.358002) (xy 158.347915 100.358) + (xy 158.239355 100.328912) (xy 158.12889 100.299313) (xy 157.903 100.27955) (xy 157.902999 100.27955) + (xy 157.806451 100.287997) (xy 157.67711 100.299313) (xy 157.618423 100.315038) (xy 157.458084 100.358) + (xy 157.458079 100.358002) (xy 157.252574 100.453831) (xy 157.066833 100.583888) (xy 157.066827 100.583893) + (xy 156.906493 100.744227) (xy 156.906488 100.744233) (xy 156.776431 100.929974) (xy 156.747195 100.992671) + (xy 156.700277 101.045956) (xy 156.632 101.065417) (xy 156.56404 101.044875) (xy 156.518805 100.992671) + (xy 156.500006 100.952358) (xy 156.489569 100.929975) (xy 156.359509 100.74423) (xy 156.19917 100.583891) + (xy 156.199061 100.583815) (xy 156.013425 100.453831) (xy 155.80792 100.358002) (xy 155.807915 100.358) + (xy 155.699355 100.328912) (xy 155.58889 100.299313) (xy 155.363 100.27955) (xy 155.13711 100.299313) + (xy 155.078422 100.315038) (xy 154.918084 100.358) (xy 154.918079 100.358002) (xy 154.712574 100.453831) + (xy 154.526833 100.583888) (xy 154.526827 100.583893) (xy 154.366493 100.744227) (xy 154.366488 100.744233) + (xy 154.236431 100.929974) (xy 154.207195 100.992671) (xy 154.160277 101.045956) (xy 154.092 101.065417) + (xy 154.02404 101.044875) (xy 153.978805 100.992671) (xy 153.960006 100.952358) (xy 153.949569 100.929975) + (xy 153.819509 100.74423) (xy 153.65917 100.583891) (xy 153.659061 100.583815) (xy 153.473425 100.453831) + (xy 153.26792 100.358002) (xy 153.267915 100.358) (xy 153.159355 100.328912) (xy 153.04889 100.299313) + (xy 152.823 100.27955) (xy 152.59711 100.299313) (xy 152.538422 100.315038) (xy 152.378084 100.358) + (xy 152.378079 100.358002) (xy 152.172574 100.453831) (xy 151.986833 100.583888) (xy 151.986827 100.583893) + (xy 151.826493 100.744227) (xy 151.826488 100.744233) (xy 151.696431 100.929974) (xy 151.640486 101.04995) + (xy 151.593569 101.103235) (xy 151.526291 101.1227) (xy 143.43815 101.1227) (xy 143.370029 101.102698) + (xy 143.349055 101.085795) (xy 141.112855 98.849595) (xy 141.078829 98.787283) (xy 141.083894 98.716468) + (xy 141.126441 98.659632) (xy 141.192961 98.634821) (xy 141.20195 98.6345) (xy 141.441632 98.6345) + (xy 141.441638 98.6345) (xy 141.441645 98.634499) (xy 141.441649 98.634499) (xy 141.502196 98.62799) + (xy 141.502199 98.627989) (xy 141.502201 98.627989) (xy 141.639204 98.576889) (xy 141.642376 98.574515) + (xy 141.756261 98.489261) (xy 141.843887 98.372207) (xy 141.843887 98.372206) (xy 141.843889 98.372204) + (xy 141.894989 98.235201) (xy 141.895934 98.226416) (xy 141.901499 98.174649) (xy 141.9015 98.174632) + (xy 141.9015 95.537367) (xy 141.901499 95.53735) (xy 141.89499 95.476803) (xy 141.894988 95.476795) + (xy 141.857246 95.375607) (xy 141.843889 95.339796) (xy 141.843888 95.339794) (xy 141.843887 95.339792) + (xy 141.756261 95.222738) (xy 141.639207 95.135112) (xy 141.639202 95.13511) (xy 141.502204 95.084011) + (xy 141.502196 95.084009) (xy 141.441649 95.0775) (xy 141.441638 95.0775) (xy 140.9734 95.0775) + (xy 140.905279 95.057498) (xy 140.858786 95.003842) (xy 140.8474 94.9515) (xy 140.8474 94.43075) + (xy 140.8474 93.906243) (xy 140.867401 93.838126) (xy 140.884299 93.817156) (xy 141.529152 93.172304) + (xy 141.591465 93.138279) (xy 141.618248 93.1354) (xy 146.556125 93.1354) (xy 146.624246 93.155402) + (xy 146.659338 93.189129) (xy 146.71719 93.27175) (xy 146.717193 93.271754) (xy 146.882246 93.436807) + (xy 146.88225 93.43681) (xy 146.882254 93.436813) (xy 146.968233 93.497016) (xy 147.073452 93.570691) + (xy 147.285002 93.669339) (xy 147.510468 93.729752) (xy 147.743 93.750096) (xy 147.975532 93.729752) + (xy 148.200998 93.669339) (xy 148.412548 93.570691) (xy 148.603754 93.436807) (xy 148.768807 93.271754) + (xy 148.902691 93.080548) (xy 149.001339 92.868998) (xy 149.061752 92.643532) (xy 149.082096 92.411) + (xy 149.061752 92.178468) (xy 149.001339 91.953002) (xy 148.902691 91.741452) (xy 148.8452 91.659346) + (xy 148.768813 91.550254) (xy 148.768809 91.550249) (xy 148.768807 91.550246) (xy 148.603754 91.385193) + (xy 148.60375 91.38519) (xy 148.603745 91.385186) (xy 148.412548 91.251309) (xy 148.412546 91.251308) + (xy 148.268474 91.184126) (xy 148.200998 91.152661) (xy 148.200996 91.15266) (xy 148.200995 91.15266) + (xy 147.975535 91.092248) (xy 147.801132 91.07699) (xy 147.743 91.071904) (xy 147.742999 91.071904) + (xy 147.510464 91.092248) (xy 147.285004 91.15266) (xy 147.284999 91.152662) (xy 147.073453 91.251308) + (xy 147.073451 91.251309) (xy 146.882254 91.385186) (xy 146.882243 91.385195) (xy 146.717195 91.550243) + (xy 146.71719 91.550249) (xy 146.659338 91.632871) (xy 146.603881 91.677199) (xy 146.556125 91.6866) + (xy 141.327946 91.6866) (xy 141.309686 91.68527) (xy 141.287212 91.681978) (xy 141.237119 91.68636) + (xy 141.231626 91.6866) (xy 141.223807 91.6866) (xy 141.192348 91.690277) (xy 141.118517 91.696736) + (xy 141.111332 91.69822) (xy 141.111308 91.698107) (xy 141.104924 91.699522) (xy 141.104951 91.699634) + (xy 141.097807 91.701327) (xy 141.028127 91.726688) (xy 140.957773 91.75) (xy 140.951128 91.7531) + (xy 140.951078 91.752994) (xy 140.945179 91.75585) (xy 140.945232 91.755954) (xy 140.938675 91.759246) + (xy 140.876743 91.79998) (xy 140.813641 91.838901) (xy 140.807886 91.843452) (xy 140.807814 91.843361) + (xy 140.802744 91.84749) (xy 140.802819 91.84758) (xy 140.797194 91.8523) (xy 140.746331 91.906211) + (xy 139.654573 92.997968) (xy 139.640724 93.009938) (xy 139.622502 93.023504) (xy 139.622499 93.023507) + (xy 139.590181 93.062022) (xy 139.58647 93.066071) (xy 139.580942 93.071599) (xy 139.580939 93.071603) + (xy 139.561289 93.096455) (xy 139.513653 93.153225) (xy 139.509623 93.159353) (xy 139.509526 93.159289) + (xy 139.506009 93.164808) (xy 139.506108 93.164869) (xy 139.502255 93.171116) (xy 139.470922 93.238309) + (xy 139.437654 93.304551) (xy 139.435143 93.311451) (xy 139.435033 93.311411) (xy 139.432886 93.31759) + (xy 139.432996 93.317627) (xy 139.430688 93.32459) (xy 139.415698 93.397185) (xy 139.398599 93.469331) + (xy 139.397748 93.47662) (xy 139.397631 93.476606) (xy 139.396966 93.483113) (xy 139.397084 93.483124) + (xy 139.396444 93.490435) (xy 139.3986 93.564519) (xy 139.3986 94.9515) (xy 139.378598 95.019621) + (xy 139.324942 95.066114) (xy 139.2726 95.0775) (xy 138.80435 95.0775) (xy 138.743803 95.084009) + (xy 138.743795 95.084011) (xy 138.606797 95.13511) (xy 138.606792 95.135112) (xy 138.489738 95.222738) + (xy 138.402112 95.339792) (xy 138.40211 95.339797) (xy 138.351011 95.476795) (xy 138.351009 95.476803) + (xy 138.3445 95.53735) (xy 138.344499 95.537367) (xy 138.3445 97.81667) (xy 138.324498 97.884791) + (xy 138.270842 97.931284) (xy 138.200568 97.941388) (xy 138.135988 97.911894) (xy 138.129405 97.905765) + (xy 138.026805 97.803165) (xy 137.992779 97.740853) (xy 137.9899 97.71407) (xy 137.9899 86.614688) + (xy 138.009902 86.546567) (xy 138.063558 86.500074) (xy 138.133832 86.48997) (xy 138.188168 86.511473) + (xy 138.215323 86.530488) (xy 138.416804 86.62444) (xy 138.631537 86.681978) (xy 138.853 86.701353) + (xy 138.853002 86.701352) (xy 138.853005 86.701353) (xy 138.932952 86.694358) (xy 139.025182 86.686289) + (xy 139.094786 86.700277) (xy 139.125258 86.722714) (xy 140.836965 88.434421) (xy 140.848938 88.448275) + (xy 140.862502 88.466496) (xy 140.901028 88.498824) (xy 140.905082 88.502538) (xy 140.910596 88.508053) + (xy 140.9106 88.508056) (xy 140.910603 88.508059) (xy 140.935434 88.527693) (xy 140.992226 88.575347) + (xy 140.992227 88.575347) (xy 140.992229 88.575349) (xy 140.99836 88.579382) (xy 140.998296 88.579478) + (xy 141.003813 88.582993) (xy 141.003875 88.582895) (xy 141.010115 88.586744) (xy 141.076617 88.617754) + (xy 141.077299 88.618072) (xy 141.143554 88.651347) (xy 141.143556 88.651347) (xy 141.150453 88.653858) + (xy 141.150412 88.65397) (xy 141.156585 88.656115) (xy 141.156623 88.656003) (xy 141.163589 88.65831) + (xy 141.163592 88.658312) (xy 141.236178 88.673298) (xy 141.236179 88.673299) (xy 141.308329 88.6904) + (xy 141.315618 88.691252) (xy 141.315604 88.691368) (xy 141.322111 88.692033) (xy 141.322122 88.691915) + (xy 141.329426 88.692553) (xy 141.329434 88.692555) (xy 141.4035 88.6904) (xy 146.556125 88.6904) + (xy 146.624246 88.710402) (xy 146.659338 88.744129) (xy 146.71719 88.82675) (xy 146.717193 88.826754) + (xy 146.882246 88.991807) (xy 146.88225 88.99181) (xy 146.882254 88.991813) (xy 146.933452 89.027662) + (xy 147.073452 89.125691) (xy 147.285002 89.224339) (xy 147.510468 89.284752) (xy 147.743 89.305096) + (xy 147.975532 89.284752) (xy 148.200998 89.224339) (xy 148.412548 89.125691) (xy 148.603754 88.991807) + (xy 148.768807 88.826754) (xy 148.902691 88.635548) (xy 149.001339 88.423998) (xy 149.061752 88.198532) + (xy 149.082096 87.966) (xy 149.061752 87.733468) (xy 149.058627 87.721807) (xy 149.033413 87.627704) + (xy 149.001339 87.508002) (xy 148.902691 87.296452) (xy 148.826662 87.187871) (xy 148.768813 87.105254) + (xy 148.768809 87.105249) (xy 148.768807 87.105246) (xy 148.603754 86.940193) (xy 148.60375 86.94019) + (xy 148.603745 86.940186) (xy 148.412548 86.806309) (xy 148.412546 86.806308) (xy 148.306772 86.756984) + (xy 148.200998 86.707661) (xy 148.200996 86.70766) (xy 148.200995 86.70766) (xy 147.975535 86.647248) + (xy 147.801132 86.63199) (xy 147.743 86.626904) (xy 147.742999 86.626904) (xy 147.510464 86.647248) + (xy 147.285004 86.70766) (xy 147.284999 86.707662) (xy 147.073453 86.806308) (xy 147.073451 86.806309) + (xy 146.882254 86.940186) (xy 146.882243 86.940195) (xy 146.717195 87.105243) (xy 146.71719 87.105249) + (xy 146.659338 87.187871) (xy 146.603881 87.232199) (xy 146.556125 87.2416) (xy 141.745247 87.2416) + (xy 141.677126 87.221598) (xy 141.656152 87.204695) (xy 140.149715 85.698258) (xy 140.115689 85.635946) + (xy 140.113289 85.598182) (xy 140.114429 85.585154) (xy 140.128353 85.426) (xy 140.108978 85.204537) + (xy 140.05144 84.989804) (xy 139.957488 84.788324) (xy 139.829977 84.606219) (xy 139.672781 84.449023) + (xy 139.672777 84.44902) (xy 139.672772 84.449016) (xy 139.490677 84.321512) (xy 139.490675 84.321511) + (xy 139.289199 84.227561) (xy 139.289193 84.227559) (xy 139.247073 84.216273) (xy 139.074463 84.170022) + (xy 138.853 84.150647) (xy 138.631537 84.170022) (xy 138.523398 84.198998) (xy 138.416806 84.227559) + (xy 138.416801 84.227561) (xy 138.215321 84.321513) (xy 138.188169 84.340525) (xy 138.120895 84.363212) + (xy 138.052035 84.345926) (xy 138.003452 84.294156) (xy 137.9899 84.237311) (xy 137.9899 73.5383) + (xy 138.009902 73.470179) (xy 138.063558 73.423686) (xy 138.1159 73.4123) (xy 140.097137 73.4123) + (xy 140.165258 73.432302) (xy 140.211751 73.485958) (xy 140.221855 73.556232) (xy 140.192361 73.620812) + (xy 140.176653 73.63604) (xy 140.146655 73.660444) (xy 140.950137 74.463926) (xy 140.984162 74.526239) + (xy 140.979098 74.597054) (xy 140.936551 74.65389) (xy 140.930282 74.658293) (xy 140.899861 74.678301) + (xy 140.772586 74.813206) (xy 140.77137 74.812058) (xy 140.721766 74.849392) (xy 140.650963 74.854639) + (xy 140.588564 74.820774) (xy 140.588334 74.820545) (xy 139.786319 74.018529) (xy 139.786318 74.018529) + (xy 139.652768 74.207728) (xy 139.524872 74.454556) (xy 139.43178 74.71649) (xy 139.431778 74.716498) + (xy 139.375221 74.98867) (xy 139.37522 74.988676) (xy 139.356252 75.265995) (xy 139.356252 75.266004) + (xy 139.37522 75.543323) (xy 139.375221 75.543329) (xy 139.431778 75.815501) (xy 139.43178 75.815509) + (xy 139.524872 76.077443) (xy 139.652763 76.324261) (xy 139.652764 76.324262) (xy 139.786319 76.513468) + (xy 140.59204 75.707747) (xy 140.654353 75.673722) (xy 140.725168 75.678786) (xy 140.782004 75.721333) + (xy 140.782121 75.721489) (xy 140.834968 75.792476) (xy 140.94211 75.882378) (xy 140.981436 75.941486) + (xy 140.982564 76.012473) (xy 140.950214 76.067994) (xy 140.146655 76.871553) (xy 140.146655 76.871554) + (xy 140.21844 76.929956) (xy 140.455965 77.074399) (xy 140.710933 77.185147) (xy 140.710943 77.18515) + (xy 140.978603 77.260146) (xy 140.978611 77.260148) (xy 141.253993 77.297999) (xy 141.254007 77.298) + (xy 141.531993 77.298) (xy 141.532006 77.297999) (xy 141.807388 77.260148) (xy 141.807396 77.260146) + (xy 142.075056 77.18515) (xy 142.075066 77.185147) (xy 142.330034 77.074399) (xy 142.567558 76.929956) + (xy 142.639343 76.871554) (xy 142.639343 76.871553) (xy 141.835862 76.068073) (xy 141.801837 76.00576) + (xy 141.806901 75.934945) (xy 141.849448 75.878109) (xy 141.855706 75.873713) (xy 141.886138 75.853699) + (xy 142.008378 75.724132) (xy 142.008378 75.724131) (xy 142.013414 75.718794) (xy 142.014633 75.719944) + (xy 142.064205 75.682618) (xy 142.135006 75.677354) (xy 142.197414 75.711204) (xy 142.197665 75.711454) + (xy 142.999679 76.513468) (xy 143.133231 76.32427) (xy 143.133236 76.324261) (xy 143.261127 76.077443) + (xy 143.354219 75.815509) (xy 143.354221 75.815501) (xy 143.410778 75.543329) (xy 143.410779 75.543323) + (xy 143.429748 75.266004) (xy 143.429748 75.265995) (xy 143.410779 74.988676) (xy 143.410778 74.98867) + (xy 143.354221 74.716498) (xy 143.354219 74.71649) (xy 143.261127 74.454556) (xy 143.133236 74.207738) + (xy 143.133236 74.207737) (xy 142.999679 74.01853) (xy 142.193957 74.824252) (xy 142.131645 74.858277) + (xy 142.060829 74.853212) (xy 142.003994 74.810665) (xy 142.003794 74.810397) (xy 141.951032 74.739524) + (xy 141.951029 74.739522) (xy 141.951029 74.739521) (xy 141.951027 74.739519) (xy 141.843889 74.649621) + (xy 141.804562 74.590512) (xy 141.803435 74.519525) (xy 141.835784 74.464003) (xy 142.639343 73.660444) + (xy 142.609346 73.63604) (xy 142.569127 73.577534) (xy 142.566923 73.506572) (xy 142.603434 73.445683) + (xy 142.667069 73.414199) (xy 142.688863 73.4123) (xy 143.41693 73.4123) (xy 143.485051 73.432302) + (xy 143.506025 73.449205) (xy 144.563112 74.506292) (xy 144.563113 74.506293) (xy 144.637237 74.580417) + (xy 144.692707 74.635887) (xy 144.728621 74.658453) (xy 144.734386 74.662544) (xy 144.767559 74.688998) + (xy 144.782247 74.696071) (xy 144.805794 74.70741) (xy 144.811968 74.710823) (xy 144.84789 74.733394) + (xy 144.870684 74.74137) (xy 144.887926 74.747404) (xy 144.894458 74.750109) (xy 144.924139 74.764402) + (xy 144.932684 74.768517) (xy 144.974052 74.777958) (xy 144.98082 74.779908) (xy 145.020879 74.793926) + (xy 145.063041 74.798676) (xy 145.069988 74.799856) (xy 145.111363 74.8093) (xy 145.157328 74.8093) + (xy 151.526291 74.8093) (xy 151.594412 74.829302) (xy 151.640485 74.882049) (xy 151.660117 74.92415) + (xy 151.696431 75.002025) (xy 151.790046 75.135722) (xy 151.826491 75.18777) (xy 151.98683 75.348109) + (xy 152.172575 75.478169) (xy 152.378083 75.573999) (xy 152.59711 75.632687) (xy 152.823 75.65245) + (xy 153.04889 75.632687) (xy 153.267917 75.573999) (xy 153.473425 75.478169) (xy 153.65917 75.348109) + (xy 153.819509 75.18777) (xy 153.949569 75.002025) (xy 153.978805 74.939327) (xy 154.025722 74.886043) + (xy 154.093999 74.866582) (xy 154.161959 74.887124) (xy 154.207195 74.939328) (xy 154.236431 75.002025) + (xy 154.330046 75.135722) (xy 154.366491 75.18777) (xy 154.52683 75.348109) (xy 154.712575 75.478169) + (xy 154.918083 75.573999) (xy 155.13711 75.632687) (xy 155.363 75.65245) (xy 155.58889 75.632687) + (xy 155.807917 75.573999) (xy 156.013425 75.478169) (xy 156.19917 75.348109) (xy 156.359509 75.18777) + (xy 156.489569 75.002025) (xy 156.518805 74.939327) (xy 156.565722 74.886043) (xy 156.633999 74.866582) + (xy 156.701959 74.887124) (xy 156.747195 74.939328) (xy 156.776431 75.002025) (xy 156.870046 75.135722) + (xy 156.906491 75.18777) (xy 157.06683 75.348109) (xy 157.124871 75.38875) (xy 157.169199 75.444205) + (xy 157.1786 75.491962) (xy 157.1786 80.120037) (xy 157.158598 80.188158) (xy 157.124872 80.223249) + (xy 157.066829 80.263891) (xy 157.066827 80.263893) (xy 156.906488 80.424233) (xy 156.776431 80.609974) + (xy 156.747195 80.672671) (xy 156.700277 80.725956) (xy 156.632 80.745417) (xy 156.56404 80.724875) + (xy 156.518805 80.672671) (xy 156.495793 80.623323) (xy 156.489569 80.609975) (xy 156.359509 80.42423) + (xy 156.19917 80.263891) (xy 156.146195 80.226797) (xy 156.141128 80.223249) (xy 156.0968 80.167792) + (xy 156.0874 80.120037) (xy 156.0874 79.010947) (xy 156.08873 78.992687) (xy 156.089043 78.990543) + (xy 156.092022 78.970213) (xy 156.088627 78.931402) (xy 156.08764 78.920115) (xy 156.0874 78.914623) + (xy 156.0874 78.90681) (xy 156.083724 78.875366) (xy 156.081207 78.846593) (xy 156.077264 78.801517) + (xy 156.077261 78.801509) (xy 156.075779 78.794327) (xy 156.075896 78.794302) (xy 156.07448 78.787917) + (xy 156.074364 78.787945) (xy 156.072672 78.780807) (xy 156.072672 78.780805) (xy 156.04732 78.711153) + (xy 156.023999 78.640772) (xy 156.023996 78.640768) (xy 156.020896 78.634118) (xy 156.021003 78.634068) + (xy 156.018152 78.628179) (xy 156.018046 78.628233) (xy 156.014756 78.621684) (xy 156.014754 78.621677) + (xy 155.9982 78.596508) (xy 155.974023 78.559747) (xy 155.94247 78.508594) (xy 155.935099 78.496643) + (xy 155.935096 78.49664) (xy 155.930546 78.490885) (xy 155.930638 78.490811) (xy 155.926506 78.485739) + (xy 155.926416 78.485815) (xy 155.921702 78.480197) (xy 155.867788 78.429332) (xy 155.157028 77.718571) + (xy 155.145057 77.704719) (xy 155.134597 77.690669) (xy 155.131496 77.686503) (xy 155.131494 77.686501) + (xy 155.092976 77.65418) (xy 155.088923 77.650466) (xy 155.0834 77.644942) (xy 155.058544 77.625289) + (xy 155.056406 77.623495) (xy 155.001774 77.577653) (xy 155.00177 77.577651) (xy 155.001769 77.57765) + (xy 154.99564 77.573618) (xy 154.995703 77.57352) (xy 154.990189 77.570006) (xy 154.990128 77.570106) + (xy 154.983882 77.566254) (xy 154.91669 77.534922) (xy 154.850451 77.501655) (xy 154.843553 77.499144) + (xy 154.843592 77.499034) (xy 154.837406 77.496884) (xy 154.83737 77.496995) (xy 154.83041 77.494688) + (xy 154.757814 77.479698) (xy 154.685668 77.462599) (xy 154.67838 77.461748) (xy 154.678393 77.46163) + (xy 154.671889 77.460966) (xy 154.671879 77.461084) (xy 154.664566 77.460444) (xy 154.664565 77.460444) + (xy 154.59048 77.4626) (xy 151.741947 77.4626) (xy 151.723687 77.46127) (xy 151.701213 77.457978) + (xy 151.651116 77.46236) (xy 151.645624 77.4626) (xy 151.637808 77.4626) (xy 151.612655 77.46554) + (xy 151.606366 77.466275) (xy 151.591596 77.467567) (xy 151.532513 77.472736) (xy 151.525329 77.47422) + (xy 151.525305 77.474105) (xy 151.51892 77.47552) (xy 151.518948 77.475635) (xy 151.5118 77.477329) + (xy 151.442153 77.502679) (xy 151.371769 77.526001) (xy 151.365123 77.529101) (xy 151.365074 77.528996) + (xy 151.359177 77.531851) (xy 151.359229 77.531955) (xy 151.352673 77.535247) (xy 151.290747 77.575976) + (xy 151.227642 77.6149) (xy 151.221886 77.619452) (xy 151.221814 77.619361) (xy 151.21674 77.623495) + (xy 151.216815 77.623584) (xy 151.211193 77.628301) (xy 151.160332 77.682211) (xy 150.322569 78.519972) + (xy 150.30872 78.531941) (xy 150.290502 78.545504) (xy 150.290499 78.545507) (xy 150.258181 78.584022) + (xy 150.25447 78.588071) (xy 150.248942 78.593599) (xy 150.248939 78.593603) (xy 150.229289 78.618455) + (xy 150.181653 78.675225) (xy 150.177623 78.681353) (xy 150.177526 78.681289) (xy 150.174009 78.686808) + (xy 150.174108 78.686869) (xy 150.170255 78.693116) (xy 150.138922 78.760309) (xy 150.105654 78.826551) + (xy 150.103143 78.833451) (xy 150.103033 78.833411) (xy 150.100886 78.83959) (xy 150.100996 78.839627) + (xy 150.098688 78.84659) (xy 150.083698 78.919185) (xy 150.066599 78.991331) (xy 150.065748 78.99862) + (xy 150.065631 78.998606) (xy 150.064966 79.005113) (xy 150.065084 79.005124) (xy 150.064444 79.012435) + (xy 150.0666 79.086519) (xy 150.066599 81.644752) (xy 150.046597 81.712873) (xy 150.029695 81.733847) + (xy 149.638848 82.124695) (xy 149.576535 82.15872) (xy 149.549752 82.1616) (xy 148.045357 82.1616) + (xy 147.977236 82.141598) (xy 147.930743 82.087942) (xy 147.920639 82.017668) (xy 147.950133 81.953088) + (xy 148.009859 81.914704) (xy 148.015943 81.913081) (xy 148.235079 81.860471) (xy 148.465942 81.764845) + (xy 148.674703 81.636914) (xy 148.674704 81.636913) (xy 147.929824 80.892033) (xy 147.895798 80.829721) + (xy 147.900863 80.758906) (xy 147.94341 80.70207) (xy 147.961712 80.690674) (xy 147.984854 80.678883) + (xy 148.075883 80.587854) (xy 148.087671 80.564717) (xy 148.136416 80.513103) (xy 148.205331 80.496034) + (xy 148.272533 80.518934) (xy 148.289033 80.532824) (xy 149.033913 81.277704) (xy 149.033914 81.277703) + (xy 149.161845 81.068942) (xy 149.257471 80.838079) (xy 149.315803 80.595107) (xy 149.335408 80.346) + (xy 149.315803 80.096892) (xy 149.257471 79.85392) (xy 149.161845 79.623057) (xy 149.033914 79.414295) + (xy 149.033913 79.414294) (xy 148.289033 80.159175) (xy 148.226721 80.193201) (xy 148.155906 80.188136) + (xy 148.09907 80.145589) (xy 148.087672 80.127284) (xy 148.075883 80.104146) (xy 148.075881 80.104144) + (xy 148.07588 80.104142) (xy 147.984855 80.013117) (xy 147.961714 80.001326) (xy 147.910099 79.952578) + (xy 147.893034 79.883662) (xy 147.915935 79.816461) (xy 147.929823 79.799964) (xy 148.674703 79.055085) + (xy 148.674703 79.055084) (xy 148.465942 78.927154) (xy 148.235079 78.831528) (xy 147.992107 78.773196) + (xy 147.742999 78.753591) (xy 147.493892 78.773196) (xy 147.25092 78.831528) (xy 147.020062 78.927152) + (xy 146.811295 79.055085) (xy 146.811295 79.055086) (xy 147.556175 79.799966) (xy 147.590201 79.862278) + (xy 147.585136 79.933093) (xy 147.542589 79.989929) (xy 147.524284 80.001327) (xy 147.501144 80.013117) + (xy 147.410117 80.104144) (xy 147.398327 80.127284) (xy 147.349578 80.178899) (xy 147.280663 80.195964) + (xy 147.213462 80.173062) (xy 147.196966 80.159175) (xy 146.452086 79.414295) (xy 146.452085 79.414295) + (xy 146.324152 79.623062) (xy 146.228528 79.85392) (xy 146.170196 80.096892) (xy 146.150591 80.346) + (xy 146.170196 80.595107) (xy 146.228528 80.838079) (xy 146.324154 81.068942) (xy 146.452084 81.277703) + (xy 146.452085 81.277703) (xy 147.196964 80.532823) (xy 147.259277 80.498798) (xy 147.330092 80.503862) + (xy 147.386928 80.546409) (xy 147.398326 80.564714) (xy 147.410117 80.587855) (xy 147.501142 80.67888) + (xy 147.501144 80.678881) (xy 147.501146 80.678883) (xy 147.524282 80.690671) (xy 147.575897 80.739418) + (xy 147.592964 80.808332) (xy 147.570064 80.875534) (xy 147.556175 80.892033) (xy 146.811294 81.636913) + (xy 146.811295 81.636914) (xy 147.020057 81.764845) (xy 147.25092 81.860471) (xy 147.470057 81.913081) + (xy 147.531626 81.948433) (xy 147.564309 82.01146) (xy 147.557729 82.082151) (xy 147.513974 82.138062) + (xy 147.446938 82.161443) (xy 147.440643 82.1616) (xy 142.502355 82.1616) (xy 142.434234 82.141598) + (xy 142.399142 82.10787) (xy 142.369981 82.066223) (xy 142.369978 82.06622) (xy 142.311277 82.007519) + (xy 142.212781 81.909023) (xy 142.212777 81.90902) (xy 142.212772 81.909016) (xy 142.030677 81.781512) + (xy 142.030675 81.781511) (xy 141.829199 81.687561) (xy 141.829193 81.687559) (xy 141.787074 81.676273) + (xy 141.614463 81.630022) (xy 141.393 81.610647) (xy 141.171537 81.630022) (xy 141.056463 81.660856) + (xy 140.956806 81.687559) (xy 140.956801 81.687561) (xy 140.755323 81.781512) (xy 140.573222 81.90902) + (xy 140.573216 81.909025) (xy 140.416025 82.066216) (xy 140.41602 82.066222) (xy 140.288512 82.248323) + (xy 140.194561 82.449801) (xy 140.194559 82.449806) (xy 140.179201 82.507124) (xy 140.137022 82.664537) + (xy 140.117647 82.886) (xy 140.137022 83.107463) (xy 140.181982 83.275256) (xy 140.194559 83.322193) + (xy 140.194561 83.322199) (xy 140.288511 83.523675) (xy 140.288512 83.523677) (xy 140.416016 83.705772) + (xy 140.416019 83.705776) (xy 140.416023 83.705781) (xy 140.573219 83.862977) (xy 140.573222 83.862979) + (xy 140.573227 83.862983) (xy 140.642018 83.911151) (xy 140.755323 83.990488) (xy 140.956804 84.08444) + (xy 141.171537 84.141978) (xy 141.393 84.161353) (xy 141.614463 84.141978) (xy 141.829196 84.08444) + (xy 142.030677 83.990488) (xy 142.212781 83.862977) (xy 142.369977 83.705781) (xy 142.398114 83.665598) + (xy 142.399142 83.66413) (xy 142.454599 83.619801) (xy 142.502355 83.6104) (xy 149.840054 83.6104) + (xy 149.858313 83.611729) (xy 149.880788 83.615022) (xy 149.912545 83.612243) (xy 149.930881 83.61064) + (xy 149.936374 83.6104) (xy 149.944189 83.6104) (xy 149.959919 83.608561) (xy 149.975651 83.606722) + (xy 150.049483 83.600263) (xy 150.049495 83.600258) (xy 150.056665 83.598779) (xy 150.056689 83.598896) + (xy 150.063082 83.597479) (xy 150.063055 83.597364) (xy 150.070189 83.595672) (xy 150.070195 83.595672) + (xy 150.139871 83.570311) (xy 150.210229 83.546998) (xy 150.21024 83.54699) (xy 150.216884 83.543894) + (xy 150.216935 83.544003) (xy 150.222809 83.541159) (xy 150.222756 83.541053) (xy 150.229319 83.537756) + (xy 150.22932 83.537754) (xy 150.229323 83.537754) (xy 150.259643 83.517811) (xy 150.291257 83.497019) + (xy 150.310716 83.485015) (xy 150.354357 83.458099) (xy 150.354362 83.458093) (xy 150.360115 83.453546) + (xy 150.360189 83.453639) (xy 150.365252 83.449515) (xy 150.365176 83.449424) (xy 150.370798 83.444705) + (xy 150.370798 83.444703) (xy 150.370805 83.4447) (xy 150.421667 83.390788) (xy 151.259426 82.553029) + (xy 151.273272 82.541062) (xy 151.291497 82.527496) (xy 151.304577 82.511907) (xy 151.363685 82.47258) + (xy 151.434672 82.471452) (xy 151.495001 82.508882) (xy 151.525517 82.572985) (xy 151.5271 82.592897) + (xy 151.5271 82.807649) (xy 151.533609 82.868196) (xy 151.533611 82.868204) (xy 151.58471 83.005202) + (xy 151.584712 83.005207) (xy 151.672338 83.122261) (xy 151.789392 83.209887) (xy 151.789394 83.209888) + (xy 151.789396 83.209889) (xy 151.832559 83.225988) (xy 151.926395 83.260988) (xy 151.926403 83.26099) + (xy 151.98695 83.267499) (xy 151.986955 83.267499) (xy 151.986962 83.2675) (xy 152.136873 83.2675) + (xy 152.204994 83.287502) (xy 152.244114 83.327353) (xy 152.250901 83.338357) (xy 152.255454 83.344115) + (xy 152.255361 83.344188) (xy 152.259493 83.34926) (xy 152.259584 83.349184) (xy 152.264299 83.354804) + (xy 152.318213 83.405669) (xy 153.790965 84.878421) (xy 153.802938 84.892275) (xy 153.816502 84.910496) + (xy 153.855028 84.942824) (xy 153.859082 84.946538) (xy 153.864596 84.952053) (xy 153.8646 84.952056) + (xy 153.864603 84.952059) (xy 153.889434 84.971693) (xy 153.946226 85.019347) (xy 153.946227 85.019347) + (xy 153.946229 85.019349) (xy 153.95236 85.023382) (xy 153.952296 85.023478) (xy 153.957813 85.026993) + (xy 153.957875 85.026895) (xy 153.964115 85.030744) (xy 154.031299 85.062072) (xy 154.097554 85.095347) + (xy 154.097556 85.095347) (xy 154.104453 85.097858) (xy 154.104412 85.09797) (xy 154.110585 85.100115) + (xy 154.110623 85.100003) (xy 154.117589 85.10231) (xy 154.117592 85.102312) (xy 154.150464 85.109099) + (xy 154.190179 85.117299) (xy 154.262329 85.1344) (xy 154.269618 85.135252) (xy 154.269604 85.135368) + (xy 154.276111 85.136033) (xy 154.276122 85.135915) (xy 154.283426 85.136553) (xy 154.283434 85.136555) + (xy 154.3575 85.1344) (xy 159.46933 85.1344) (xy 160.167953 85.1344) (xy 160.236074 85.154402) (xy 160.282567 85.208058) + (xy 160.292671 85.278332) (xy 160.263177 85.342912) (xy 160.257048 85.349495) (xy 158.973348 86.633195) + (xy 158.911036 86.667221) (xy 158.884253 86.6701) (xy 151.587746 86.6701) (xy 151.519625 86.650098) + (xy 151.498651 86.633195) (xy 150.636571 85.771115) (xy 149.762072 84.896615) (xy 149.7501 84.882763) + (xy 149.736536 84.864543) (xy 149.722781 84.853001) (xy 149.698016 84.83222) (xy 149.693963 84.828506) + (xy 149.68844 84.822982) (xy 149.663584 84.803329) (xy 149.661593 84.801658) (xy 149.606814 84.755693) + (xy 149.60681 84.755691) (xy 149.606809 84.75569) (xy 149.60068 84.751658) (xy 149.600743 84.75156) + (xy 149.595229 84.748046) (xy 149.595168 84.748146) (xy 149.588922 84.744294) (xy 149.52173 84.712962) + (xy 149.455491 84.679695) (xy 149.448593 84.677184) (xy 149.448632 84.677074) (xy 149.442446 84.674924) + (xy 149.44241 84.675035) (xy 149.43545 84.672728) (xy 149.362854 84.657738) (xy 149.290708 84.640639) + (xy 149.28342 84.639788) (xy 149.283433 84.63967) (xy 149.276929 84.639006) (xy 149.276919 84.639124) + (xy 149.269606 84.638484) (xy 149.269605 84.638484) (xy 149.19552 84.64064) (xy 144.996589 84.64064) + (xy 144.928468 84.620638) (xy 144.907494 84.603736) (xy 144.84264 84.538882) (xy 144.752781 84.449023) + (xy 144.752777 84.44902) (xy 144.752772 84.449016) (xy 144.570677 84.321512) (xy 144.570675 84.321511) + (xy 144.369199 84.227561) (xy 144.369193 84.227559) (xy 144.327074 84.216273) (xy 144.154463 84.170022) + (xy 143.933 84.150647) (xy 143.932999 84.150647) (xy 143.859178 84.157105) (xy 143.711537 84.170022) + (xy 143.603398 84.198998) (xy 143.496806 84.227559) (xy 143.496801 84.227561) (xy 143.295323 84.321512) + (xy 143.113222 84.44902) (xy 143.113216 84.449025) (xy 142.956025 84.606216) (xy 142.95602 84.606222) + (xy 142.828512 84.788323) (xy 142.734561 84.989801) (xy 142.734559 84.989806) (xy 142.725537 85.023478) + (xy 142.677022 85.204537) (xy 142.657647 85.426) (xy 142.677022 85.647463) (xy 142.710155 85.771115) + (xy 142.734559 85.862193) (xy 142.734561 85.862199) (xy 142.828511 86.063675) (xy 142.828512 86.063677) + (xy 142.956016 86.245772) (xy 142.95602 86.245777) (xy 142.956023 86.245781) (xy 143.113219 86.402977) + (xy 143.113223 86.40298) (xy 143.113227 86.402983) (xy 143.199603 86.463464) (xy 143.295323 86.530488) + (xy 143.496804 86.62444) (xy 143.711537 86.681978) (xy 143.933 86.701353) (xy 144.154463 86.681978) + (xy 144.369196 86.62444) (xy 144.570677 86.530488) (xy 144.752781 86.402977) (xy 144.909977 86.245781) + (xy 144.962758 86.170402) (xy 144.981827 86.143169) (xy 145.037284 86.098841) (xy 145.08504 86.08944) + (xy 148.853794 86.08944) (xy 148.921915 86.109442) (xy 148.942888 86.126344) (xy 149.845423 87.02888) + (xy 150.679468 87.862925) (xy 150.691441 87.876779) (xy 150.705004 87.894997) (xy 150.743522 87.927317) + (xy 150.747569 87.931027) (xy 150.753098 87.936556) (xy 150.753097 87.936556) (xy 150.777955 87.95621) + (xy 150.834726 88.003848) (xy 150.840857 88.00788) (xy 150.840791 88.007978) (xy 150.84631 88.011494) + (xy 150.846373 88.011394) (xy 150.852615 88.015243) (xy 150.852618 88.015246) (xy 150.852621 88.015247) + (xy 150.852622 88.015248) (xy 150.919809 88.046577) (xy 150.986051 88.079846) (xy 150.992955 88.082359) + (xy 150.992914 88.082471) (xy 150.999089 88.084617) (xy 150.999127 88.084504) (xy 151.00609 88.086811) + (xy 151.006091 88.086811) (xy 151.006093 88.086812) (xy 151.053151 88.096528) (xy 151.078685 88.101801) + (xy 151.102292 88.107396) (xy 151.15083 88.1189) (xy 151.150836 88.1189) (xy 151.158119 88.119752) + (xy 151.158105 88.119869) (xy 151.16461 88.120533) (xy 151.164621 88.120416) (xy 151.171928 88.121054) + (xy 151.171935 88.121056) (xy 151.24602 88.1189) (xy 159.174554 88.1189) (xy 159.192813 88.120229) + (xy 159.215288 88.123522) (xy 159.247045 88.120743) (xy 159.265381 88.11914) (xy 159.270874 88.1189) + (xy 159.278689 88.1189) (xy 159.294419 88.117061) (xy 159.310151 88.115222) (xy 159.383983 88.108763) + (xy 159.383995 88.108758) (xy 159.391165 88.107279) (xy 159.391189 88.107396) (xy 159.397582 88.105979) + (xy 159.397555 88.105864) (xy 159.404689 88.104172) (xy 159.404695 88.104172) (xy 159.474371 88.078811) + (xy 159.544729 88.055498) (xy 159.54474 88.05549) (xy 159.551384 88.052394) (xy 159.551435 88.052503) + (xy 159.557309 88.049659) (xy 159.557256 88.049553) (xy 159.563819 88.046256) (xy 159.56382 88.046254) + (xy 159.563823 88.046254) (xy 159.603391 88.020229) (xy 159.625757 88.005519) (xy 159.65592 87.986914) + (xy 159.688857 87.966599) (xy 159.688862 87.966593) (xy 159.694615 87.962046) (xy 159.694689 87.962139) + (xy 159.699752 87.958015) (xy 159.699676 87.957924) (xy 159.705298 87.953205) (xy 159.705298 87.953203) + (xy 159.705305 87.9532) (xy 159.756168 87.899287) (xy 161.384064 86.271392) (xy 161.936239 85.719218) + (xy 161.998551 85.685192) (xy 162.069367 85.690257) (xy 162.126202 85.732804) (xy 162.151013 85.799324) + (xy 162.135922 85.868698) (xy 162.132767 85.874147) (xy 162.072154 85.973058) (xy 161.976528 86.20392) + (xy 161.918196 86.446892) (xy 161.898591 86.695999) (xy 161.918196 86.945107) (xy 161.976528 87.188079) + (xy 162.072154 87.418942) (xy 162.200084 87.627703) (xy 162.200085 87.627703) (xy 162.944964 86.882823) + (xy 163.007277 86.848798) (xy 163.078092 86.853862) (xy 163.134928 86.896409) (xy 163.146326 86.914714) + (xy 163.158117 86.937855) (xy 163.249142 87.02888) (xy 163.249144 87.028881) (xy 163.249146 87.028883) + (xy 163.272282 87.040671) (xy 163.323897 87.089418) (xy 163.340964 87.158332) (xy 163.318064 87.225534) + (xy 163.304175 87.242033) (xy 162.559294 87.986913) (xy 162.559295 87.986914) (xy 162.768057 88.114845) + (xy 162.99892 88.210471) (xy 163.241892 88.268803) (xy 163.490999 88.288408) (xy 163.740107 88.268803) + (xy 163.983079 88.210471) (xy 164.213942 88.114845) (xy 164.422703 87.986914) (xy 164.422704 87.986913) + (xy 163.677824 87.242033) (xy 163.643798 87.179721) (xy 163.648863 87.108906) (xy 163.69141 87.05207) + (xy 163.709712 87.040674) (xy 163.732854 87.028883) (xy 163.823883 86.937854) (xy 163.835671 86.914717) + (xy 163.884416 86.863103) (xy 163.953331 86.846034) (xy 164.020533 86.868934) (xy 164.037033 86.882824) + (xy 164.781913 87.627704) (xy 164.781914 87.627703) (xy 164.909845 87.418942) (xy 165.005471 87.188079) + (xy 165.063803 86.945107) (xy 165.083408 86.695999) (xy 165.063803 86.446892) (xy 165.005471 86.20392) + (xy 164.909845 85.973057) (xy 164.781914 85.764295) (xy 164.781913 85.764294) (xy 164.037033 86.509175) + (xy 163.974721 86.543201) (xy 163.903906 86.538136) (xy 163.84707 86.495589) (xy 163.835674 86.477287) + (xy 163.823883 86.454146) (xy 163.823881 86.454144) (xy 163.82388 86.454142) (xy 163.732855 86.363117) + (xy 163.709714 86.351326) (xy 163.658099 86.302578) (xy 163.641034 86.233662) (xy 163.663935 86.166461) + (xy 163.677823 86.149964) (xy 164.422703 85.405085) (xy 164.422363 85.402211) (xy 164.43422 85.332211) + (xy 164.482038 85.279733) (xy 164.54749 85.2614) (xy 165.334054 85.2614) (xy 165.352313 85.262729) + (xy 165.374788 85.266022) (xy 165.406545 85.263243) (xy 165.424881 85.26164) (xy 165.430374 85.2614) + (xy 165.438189 85.2614) (xy 165.453919 85.259561) (xy 165.469651 85.257722) (xy 165.543483 85.251263) + (xy 165.543495 85.251258) (xy 165.550665 85.249779) (xy 165.550689 85.249896) (xy 165.557082 85.248479) + (xy 165.557055 85.248364) (xy 165.564189 85.246672) (xy 165.564195 85.246672) (xy 165.633871 85.221311) + (xy 165.704229 85.197998) (xy 165.70424 85.19799) (xy 165.710884 85.194894) (xy 165.710935 85.195003) + (xy 165.716809 85.192159) (xy 165.716756 85.192053) (xy 165.723319 85.188756) (xy 165.72332 85.188754) + (xy 165.723323 85.188754) (xy 165.753643 85.168811) (xy 165.785257 85.148019) (xy 165.809854 85.132847) + (xy 165.848357 85.109099) (xy 165.848362 85.109093) (xy 165.854115 85.104546) (xy 165.854189 85.104639) + (xy 165.859252 85.100515) (xy 165.859176 85.100424) (xy 165.864798 85.095705) (xy 165.864798 85.095703) + (xy 165.864805 85.0957) (xy 165.915668 85.041787) (xy 166.499432 84.458022) (xy 166.513265 84.446068) + (xy 166.531497 84.432496) (xy 166.563846 84.393942) (xy 166.567515 84.38994) (xy 166.57306 84.384396) + (xy 166.5927 84.359556) (xy 166.640347 84.302774) (xy 166.640348 84.30277) (xy 166.640351 84.302768) + (xy 166.644385 84.296636) (xy 166.644484 84.296701) (xy 166.647996 84.291188) (xy 166.647894 84.291126) + (xy 166.651741 84.284887) (xy 166.651746 84.284882) (xy 166.683077 84.21769) (xy 166.716347 84.151446) + (xy 166.716347 84.151443) (xy 166.718858 84.144547) (xy 166.718969 84.144587) (xy 166.721116 84.138413) + (xy 166.721003 84.138376) (xy 166.723312 84.131407) (xy 166.738301 84.058813) (xy 166.7383 84.058813) + (xy 166.7554 83.98667) (xy 166.7554 83.986663) (xy 166.756252 83.979382) (xy 166.756368 83.979395) + (xy 166.757033 83.972889) (xy 166.756916 83.972879) (xy 166.757554 83.965572) (xy 166.757556 83.965565) + (xy 166.7554 83.891479) (xy 166.7554 81.587246) (xy 166.775402 81.519125) (xy 166.792305 81.498151) + (xy 169.469152 78.821305) (xy 169.531464 78.787279) (xy 169.558247 78.7844) (xy 171.335722 78.7844) + (xy 171.403843 78.804402) (xy 171.450336 78.858058) (xy 171.46044 78.928332) (xy 171.430946 78.992912) + (xy 171.37122 79.031296) (xy 171.368333 79.032107) (xy 171.161004 79.08766) (xy 171.160999 79.087662) + (xy 170.949453 79.186308) (xy 170.949451 79.186309) (xy 170.758254 79.320186) (xy 170.758243 79.320195) + (xy 170.593195 79.485243) (xy 170.59319 79.485249) (xy 170.535338 79.567871) (xy 170.479881 79.612199) + (xy 170.432125 79.6216) (xy 170.410947 79.6216) (xy 170.392687 79.62027) (xy 170.370213 79.616978) + (xy 170.320116 79.62136) (xy 170.314624 79.6216) (xy 170.306808 79.6216) (xy 170.281655 79.62454) + (xy 170.275366 79.625275) (xy 170.260596 79.626567) (xy 170.201513 79.631736) (xy 170.194329 79.63322) + (xy 170.194305 79.633105) (xy 170.18792 79.63452) (xy 170.187948 79.634635) (xy 170.1808 79.636329) + (xy 170.111153 79.661679) (xy 170.040769 79.685001) (xy 170.034123 79.688101) (xy 170.034074 79.687996) + (xy 170.028177 79.690851) (xy 170.028229 79.690955) (xy 170.021673 79.694247) (xy 169.959747 79.734976) + (xy 169.896642 79.7739) (xy 169.890886 79.778452) (xy 169.890814 79.778361) (xy 169.88574 79.782495) + (xy 169.885815 79.782584) (xy 169.880193 79.787301) (xy 169.829331 79.841212) (xy 168.229573 81.440968) + (xy 168.215724 81.452938) (xy 168.197502 81.466504) (xy 168.197499 81.466507) (xy 168.165181 81.505022) + (xy 168.16147 81.509071) (xy 168.155942 81.514599) (xy 168.155939 81.514603) (xy 168.136289 81.539455) + (xy 168.088653 81.596225) (xy 168.084623 81.602353) (xy 168.084526 81.602289) (xy 168.081009 81.607808) + (xy 168.081108 81.607869) (xy 168.077255 81.614116) (xy 168.045922 81.681309) (xy 168.012654 81.747551) + (xy 168.010143 81.754451) (xy 168.010033 81.754411) (xy 168.007886 81.76059) (xy 168.007996 81.760627) + (xy 168.005688 81.76759) (xy 167.990698 81.840185) (xy 167.973599 81.912331) (xy 167.972748 81.91962) + (xy 167.972631 81.919606) (xy 167.971966 81.926113) (xy 167.972084 81.926124) (xy 167.971444 81.933435) + (xy 167.9736 82.007519) (xy 167.9736 87.650052) (xy 167.97227 87.668311) (xy 167.968978 87.690784) + (xy 167.968978 87.690789) (xy 167.97336 87.740881) (xy 167.9736 87.746374) (xy 167.9736 87.754191) + (xy 167.977277 87.785651) (xy 167.983736 87.85948) (xy 167.98522 87.866666) (xy 167.985104 87.866689) + (xy 167.986521 87.873079) (xy 167.986635 87.873052) (xy 167.988327 87.880194) (xy 167.988328 87.880195) + (xy 168.013688 87.949871) (xy 168.035351 88.015248) (xy 168.037003 88.020231) (xy 168.040103 88.026879) + (xy 168.039996 88.026928) (xy 168.042848 88.032818) (xy 168.042953 88.032766) (xy 168.046244 88.03932) + (xy 168.08698 88.101256) (xy 168.125897 88.164352) (xy 168.130454 88.170115) (xy 168.130361 88.170188) + (xy 168.134493 88.17526) (xy 168.134584 88.175184) (xy 168.139299 88.180804) (xy 168.193213 88.231669) + (xy 169.411965 89.450421) (xy 169.423938 89.464275) (xy 169.437502 89.482496) (xy 169.476028 89.514824) + (xy 169.480082 89.518538) (xy 169.485599 89.524056) (xy 169.485601 89.524057) (xy 169.485603 89.524059) + (xy 169.494198 89.530855) (xy 169.510438 89.543697) (xy 169.536347 89.565437) (xy 169.567226 89.591347) + (xy 169.567232 89.59135) (xy 169.573361 89.595382) (xy 169.573295 89.595481) (xy 169.578814 89.598996) + (xy 169.578877 89.598896) (xy 169.58512 89.602747) (xy 169.652314 89.63408) (xy 169.698157 89.657103) + (xy 169.718554 89.667347) (xy 169.718556 89.667347) (xy 169.725453 89.669858) (xy 169.725412 89.66997) + (xy 169.731587 89.672116) (xy 169.731625 89.672004) (xy 169.738588 89.674311) (xy 169.738591 89.674311) + (xy 169.738592 89.674312) (xy 169.750753 89.676823) (xy 169.811192 89.689303) (xy 169.821188 89.691672) + (xy 169.88333 89.7064) (xy 169.883333 89.7064) (xy 169.890619 89.707252) (xy 169.890605 89.707369) + (xy 169.897109 89.708033) (xy 169.89712 89.707916) (xy 169.904427 89.708554) (xy 169.904434 89.708556) + (xy 169.978519 89.7064) (xy 172.065054 89.7064) (xy 172.083313 89.707729) (xy 172.105788 89.711022) + (xy 172.137545 89.708243) (xy 172.155881 89.70664) (xy 172.161374 89.7064) (xy 172.169189 89.7064) + (xy 172.184919 89.704561) (xy 172.200651 89.702722) (xy 172.274483 89.696263) (xy 172.274495 89.696258) + (xy 172.281665 89.694779) (xy 172.281689 89.694896) (xy 172.288082 89.693479) (xy 172.288055 89.693364) + (xy 172.295189 89.691672) (xy 172.295195 89.691672) (xy 172.364871 89.666311) (xy 172.435229 89.642998) + (xy 172.43524 89.64299) (xy 172.441884 89.639894) (xy 172.441935 89.640003) (xy 172.447809 89.637159) + (xy 172.447756 89.637053) (xy 172.454319 89.633756) (xy 172.45432 89.633754) (xy 172.454323 89.633754) + (xy 172.501466 89.602747) (xy 172.516257 89.593019) (xy 172.535717 89.581015) (xy 172.579357 89.554099) + (xy 172.579362 89.554093) (xy 172.585115 89.549546) (xy 172.585189 89.549639) (xy 172.590252 89.545515) + (xy 172.590176 89.545424) (xy 172.595798 89.540705) (xy 172.595798 89.540703) (xy 172.595805 89.5407) + (xy 172.646668 89.486787) (xy 174.054644 88.078811) (xy 175.463551 86.669905) (xy 175.525863 86.635879) + (xy 175.552646 86.633) (xy 176.430132 86.633) (xy 176.430138 86.633) (xy 176.430145 86.632999) (xy 176.430149 86.632999) + (xy 176.490696 86.62649) (xy 176.490699 86.626489) (xy 176.490701 86.626489) (xy 176.627704 86.575389) + (xy 176.744761 86.487761) (xy 176.808232 86.402974) (xy 176.832387 86.370707) (xy 176.832387 86.370706) + (xy 176.832389 86.370704) (xy 176.883489 86.233701) (xy 176.883494 86.233662) (xy 176.889999 86.173149) + (xy 176.89 86.173132) (xy 176.89 84.678867) (xy 176.889999 84.67885) (xy 176.88349 84.618303) (xy 176.883488 84.618295) + (xy 176.832389 84.481297) (xy 176.832387 84.481292) (xy 176.744761 84.364238) (xy 176.627707 84.276612) + (xy 176.627702 84.27661) (xy 176.490704 84.225511) (xy 176.490696 84.225509) (xy 176.430149 84.219) + (xy 176.430138 84.219) (xy 176.248729 84.219) (xy 176.180608 84.198998) (xy 176.134115 84.145342) + (xy 176.124011 84.075068) (xy 176.153505 84.010488) (xy 176.195479 83.978805) (xy 176.223872 83.965565) + (xy 176.288806 83.935286) (xy 176.461809 83.814148) (xy 176.611148 83.664809) (xy 176.732286 83.491806) + (xy 176.821542 83.300395) (xy 176.831293 83.264002) (xy 176.868244 83.20338) (xy 176.932104 83.172358) + (xy 177.002599 83.180786) (xy 177.057346 83.225988) (xy 177.074707 83.264003) (xy 177.084456 83.30039) + (xy 177.084459 83.300398) (xy 177.173714 83.491806) (xy 177.294378 83.664133) (xy 177.294852 83.664809) + (xy 177.444191 83.814148) (xy 177.617194 83.935286) (xy 177.808605 84.024542) (xy 178.012606 84.079204) + (xy 178.223 84.097611) (xy 178.323497 84.088818) (xy 178.336567 84.087675) (xy 178.406172 84.101663) + (xy 178.436644 84.1241) (xy 179.464187 85.151643) (xy 179.498213 85.213955) (xy 179.493148 85.28477) + (xy 179.450601 85.341606) (xy 179.386074 85.366258) (xy 179.260473 85.377247) (xy 179.260466 85.377248) + (xy 179.035004 85.43766) (xy 179.034999 85.437662) (xy 178.823453 85.536308) (xy 178.823451 85.536309) + (xy 178.632254 85.670186) (xy 178.632243 85.670195) (xy 178.467195 85.835243) (xy 178.467186 85.835254) + (xy 178.333309 86.026451) (xy 178.333308 86.026453) (xy 178.234662 86.237999) (xy 178.23466 86.238004) + (xy 178.174248 86.463464) (xy 178.153904 86.696) (xy 178.174248 86.928535) (xy 178.234544 87.153562) + (xy 178.234661 87.153998) (xy 178.255175 87.19799) (xy 178.333308 87.365546) (xy 178.333309 87.365548) + (xy 178.467186 87.556745) (xy 178.46719 87.55675) (xy 178.467193 87.556754) (xy 178.632246 87.721807) + (xy 178.63225 87.72181) (xy 178.632254 87.721813) (xy 178.678495 87.754191) (xy 178.823452 87.855691) + (xy 179.035002 87.954339) (xy 179.22601 88.005519) (xy 179.24536 88.010704) (xy 179.260468 88.014752) + (xy 179.493 88.035096) (xy 179.725532 88.014752) (xy 179.950998 87.954339) (xy 180.162548 87.855691) + (xy 180.353754 87.721807) (xy 180.46996 87.6056) (xy 180.532268 87.571578) (xy 180.603084 87.576642) + (xy 180.648146 87.605602) (xy 181.490818 88.448275) (xy 182.318898 89.276355) (xy 182.352924 89.338667) + (xy 182.355324 89.37643) (xy 182.346045 89.482496) (xy 182.345389 89.49) (xy 182.363796 89.700394) + (xy 182.393088 89.809716) (xy 182.418457 89.904393) (xy 182.418459 89.904398) (xy 182.507713 90.095805) + (xy 182.507714 90.095806) (xy 182.628852 90.268809) (xy 182.778191 90.418148) (xy 182.951194 90.539286) + (xy 183.142605 90.628542) (xy 183.346606 90.683204) (xy 183.557 90.701611) (xy 183.767394 90.683204) + (xy 183.971395 90.628542) (xy 184.162806 90.539286) (xy 184.335809 90.418148) (xy 184.485148 90.268809) + (xy 184.606286 90.095806) (xy 184.695542 89.904395) (xy 184.750204 89.700394) (xy 184.768611 89.49) + (xy 187.171495 89.49) (xy 187.191484 89.731221) (xy 187.250898 89.965845) (xy 187.250901 89.965852) + (xy 187.348126 90.187502) (xy 187.439866 90.327921) (xy 188.090964 89.676823) (xy 188.153277 89.642798) + (xy 188.224092 89.647862) (xy 188.280928 89.690409) (xy 188.292326 89.708714) (xy 188.304117 89.731855) + (xy 188.395142 89.82288) (xy 188.395144 89.822881) (xy 188.395146 89.822883) (xy 188.418282 89.834671) + (xy 188.469897 89.883418) (xy 188.486964 89.952332) (xy 188.464064 90.019534) (xy 188.450175 90.036033) + (xy 187.79827 90.687937) (xy 187.798271 90.687938) (xy 187.835437 90.716866) (xy 187.835447 90.716872) + (xy 188.048313 90.832069) (xy 188.048315 90.832071) (xy 188.277235 90.910659) (xy 188.277242 90.910661) + (xy 188.515984 90.9505) (xy 188.758016 90.9505) (xy 188.996757 90.910661) (xy 188.996764 90.910659) + (xy 189.225684 90.832071) (xy 189.225686 90.832069) (xy 189.438558 90.716869) (xy 189.475727 90.687938) + (xy 189.475728 90.687937) (xy 188.823824 90.036033) (xy 188.789798 89.973721) (xy 188.794863 89.902906) + (xy 188.83741 89.84607) (xy 188.855712 89.834674) (xy 188.878854 89.822883) (xy 188.969883 89.731854) + (xy 188.981671 89.708717) (xy 189.030416 89.657103) (xy 189.099331 89.640034) (xy 189.166533 89.662934) + (xy 189.183033 89.676824) (xy 189.83413 90.327921) (xy 189.834131 90.327921) (xy 189.925874 90.187499) + (xy 190.023098 89.965852) (xy 190.023101 89.965845) (xy 190.082515 89.731221) (xy 190.102504 89.49) + (xy 190.082515 89.248778) (xy 190.023101 89.014154) (xy 190.023098 89.014147) (xy 189.925874 88.7925) + (xy 189.834131 88.652077) (xy 189.183033 89.303176) (xy 189.120721 89.337201) (xy 189.049905 89.332136) + (xy 188.99307 89.289589) (xy 188.981672 89.271284) (xy 188.969883 89.248146) (xy 188.969881 89.248144) + (xy 188.96988 89.248142) (xy 188.878855 89.157117) (xy 188.855714 89.145326) (xy 188.804099 89.096578) + (xy 188.787034 89.027662) (xy 188.809935 88.960461) (xy 188.823823 88.943964) (xy 189.475727 88.29206) + (xy 189.438555 88.263128) (xy 189.438554 88.263127) (xy 189.225686 88.14793) (xy 189.225684 88.147928) + (xy 188.996764 88.06934) (xy 188.996757 88.069338) (xy 188.758016 88.0295) (xy 188.515984 88.0295) + (xy 188.277242 88.069338) (xy 188.277235 88.06934) (xy 188.048315 88.147928) (xy 188.048313 88.14793) + (xy 187.835447 88.263126) (xy 187.798271 88.292061) (xy 187.79827 88.292061) (xy 188.450175 88.943966) + (xy 188.484201 89.006278) (xy 188.479136 89.077093) (xy 188.436589 89.133929) (xy 188.418284 89.145327) + (xy 188.395144 89.157117) (xy 188.304117 89.248144) (xy 188.292327 89.271284) (xy 188.243578 89.322899) + (xy 188.174663 89.339964) (xy 188.107462 89.317062) (xy 188.090966 89.303175) (xy 187.439867 88.652076) + (xy 187.439866 88.652077) (xy 187.348131 88.792488) (xy 187.348124 88.792502) (xy 187.250901 89.014147) + (xy 187.250898 89.014154) (xy 187.191484 89.248778) (xy 187.171495 89.49) (xy 184.768611 89.49) + (xy 184.750204 89.279606) (xy 184.695542 89.075605) (xy 184.606286 88.884195) (xy 184.606285 88.884194) + (xy 184.606284 88.884191) (xy 184.485152 88.711195) (xy 184.485149 88.711192) (xy 184.424268 88.650311) + (xy 184.335809 88.561852) (xy 184.301883 88.538097) (xy 184.162806 88.440714) (xy 183.971398 88.351459) + (xy 183.971393 88.351457) (xy 183.876716 88.326088) (xy 183.767394 88.296796) (xy 183.557 88.278389) + (xy 183.556998 88.278389) (xy 183.443429 88.288324) (xy 183.373824 88.274334) (xy 183.343354 88.251898) + (xy 182.343491 87.252035) (xy 181.319032 86.227575) (xy 181.30706 86.213723) (xy 181.299762 86.20392) + (xy 181.293496 86.195503) (xy 181.293494 86.195501) (xy 181.254976 86.16318) (xy 181.250923 86.159466) + (xy 181.2454 86.153942) (xy 181.220544 86.134289) (xy 181.198886 86.116116) (xy 181.163774 86.086653) + (xy 181.16377 86.086651) (xy 181.163769 86.08665) (xy 181.15764 86.082618) (xy 181.157703 86.08252) + (xy 181.152189 86.079006) (xy 181.152128 86.079106) (xy 181.145882 86.075254) (xy 181.07869 86.043922) + (xy 181.012451 86.010655) (xy 181.005553 86.008144) (xy 181.005592 86.008034) (xy 180.999406 86.005884) + (xy 180.99937 86.005995) (xy 180.99241 86.003688) (xy 180.919814 85.988698) (xy 180.847668 85.971599) + (xy 180.84038 85.970748) (xy 180.840393 85.97063) (xy 180.833889 85.969966) (xy 180.833879 85.970084) + (xy 180.826566 85.969444) (xy 180.826565 85.969444) (xy 180.75248 85.9716) (xy 180.679875 85.9716) + (xy 180.611754 85.951598) (xy 180.576662 85.917871) (xy 180.518809 85.835249) (xy 180.518804 85.835243) + (xy 180.353756 85.670195) (xy 180.353745 85.670186) (xy 180.336703 85.658253) (xy 180.292374 85.602796) + (xy 180.285065 85.532177) (xy 180.317095 85.468816) (xy 180.378297 85.432831) (xy 180.408973 85.42904) + (xy 185.300994 85.42904) (xy 185.319253 85.430369) (xy 185.341728 85.433662) (xy 185.373485 85.430883) + (xy 185.391821 85.42928) (xy 185.397314 85.42904) (xy 185.405129 85.42904) (xy 185.431133 85.426) + (xy 185.436591 85.425362) (xy 185.510423 85.418903) (xy 185.510435 85.418898) (xy 185.517605 85.417419) + (xy 185.517629 85.417536) (xy 185.524022 85.416119) (xy 185.523995 85.416004) (xy 185.531129 85.414312) + (xy 185.531135 85.414312) (xy 185.600811 85.388951) (xy 185.671169 85.365638) (xy 185.67118 85.36563) + (xy 185.677824 85.362534) (xy 185.677875 85.362643) (xy 185.683749 85.359799) (xy 185.683696 85.359693) + (xy 185.690259 85.356396) (xy 185.69026 85.356394) (xy 185.690263 85.356394) (xy 185.747462 85.318773) + (xy 185.752197 85.315659) (xy 185.771656 85.303655) (xy 185.815297 85.276739) (xy 185.815302 85.276733) + (xy 185.821055 85.272186) (xy 185.821129 85.272279) (xy 185.826192 85.268155) (xy 185.826116 85.268064) + (xy 185.831738 85.263345) (xy 185.831738 85.263343) (xy 185.831745 85.26334) (xy 185.882608 85.209427) + (xy 185.996689 85.095346) (xy 186.173505 84.918531) (xy 186.235817 84.884505) (xy 186.306632 84.88957) + (xy 186.363468 84.932117) (xy 186.388279 84.998637) (xy 186.3886 85.007626) (xy 186.3886 85.509125) + (xy 186.368598 85.577246) (xy 186.334872 85.612337) (xy 186.317539 85.624474) (xy 186.284708 85.647463) + (xy 186.252245 85.670194) (xy 186.252243 85.670195) (xy 186.08719 85.835249) (xy 186.087186 85.835254) + (xy 185.953309 86.026451) (xy 185.953308 86.026453) (xy 185.854662 86.237999) (xy 185.85466 86.238004) + (xy 185.794248 86.463464) (xy 185.773904 86.696) (xy 185.794248 86.928535) (xy 185.854544 87.153562) + (xy 185.854661 87.153998) (xy 185.875175 87.19799) (xy 185.953308 87.365546) (xy 185.953309 87.365548) + (xy 186.087186 87.556745) (xy 186.08719 87.55675) (xy 186.087193 87.556754) (xy 186.252246 87.721807) + (xy 186.25225 87.72181) (xy 186.252254 87.721813) (xy 186.298495 87.754191) (xy 186.443452 87.855691) + (xy 186.655002 87.954339) (xy 186.84601 88.005519) (xy 186.86536 88.010704) (xy 186.880468 88.014752) + (xy 187.113 88.035096) (xy 187.345532 88.014752) (xy 187.570998 87.954339) (xy 187.782548 87.855691) + (xy 187.973754 87.721807) (xy 188.138807 87.556754) (xy 188.196661 87.474129) (xy 188.252119 87.429801) + (xy 188.299875 87.4204) (xy 189.300754 87.4204) (xy 189.368875 87.440402) (xy 189.389849 87.457305) + (xy 190.366965 88.434421) (xy 190.378938 88.448275) (xy 190.392502 88.466496) (xy 190.431028 88.498824) + (xy 190.435082 88.502538) (xy 190.440599 88.508056) (xy 190.440601 88.508057) (xy 190.440603 88.508059) + (xy 190.449198 88.514855) (xy 190.465438 88.527697) (xy 190.491347 88.549437) (xy 190.522226 88.575347) + (xy 190.522232 88.57535) (xy 190.528361 88.579382) (xy 190.528295 88.579481) (xy 190.533814 88.582996) + (xy 190.533877 88.582896) (xy 190.54012 88.586747) (xy 190.607314 88.61808) (xy 190.642092 88.635546) + (xy 190.673554 88.651347) (xy 190.673556 88.651347) (xy 190.680453 88.653858) (xy 190.680412 88.65397) + (xy 190.686587 88.656116) (xy 190.686625 88.656004) (xy 190.693588 88.658311) (xy 190.693591 88.658311) + (xy 190.693592 88.658312) (xy 190.726796 88.665168) (xy 190.766192 88.673303) (xy 190.776188 88.675672) + (xy 190.83833 88.6904) (xy 190.838333 88.6904) (xy 190.845619 88.691252) (xy 190.845605 88.691369) + (xy 190.852109 88.692033) (xy 190.85212 88.691916) (xy 190.859427 88.692554) (xy 190.859434 88.692556) + (xy 190.933519 88.6904) (xy 194.671054 88.6904) (xy 194.689313 88.691729) (xy 194.711788 88.695022) + (xy 194.743545 88.692243) (xy 194.761881 88.69064) (xy 194.767374 88.6904) (xy 194.775189 88.6904) + (xy 194.790919 88.688561) (xy 194.806651 88.686722) (xy 194.880483 88.680263) (xy 194.880495 88.680258) + (xy 194.887665 88.678779) (xy 194.887689 88.678896) (xy 194.894082 88.677479) (xy 194.894055 88.677364) + (xy 194.901189 88.675672) (xy 194.901195 88.675672) (xy 194.970871 88.650311) (xy 195.041229 88.626998) + (xy 195.04124 88.62699) (xy 195.047884 88.623894) (xy 195.047935 88.624003) (xy 195.053809 88.621159) + (xy 195.053756 88.621053) (xy 195.060319 88.617756) (xy 195.06032 88.617754) (xy 195.060323 88.617754) + (xy 195.107466 88.586747) (xy 195.122257 88.577019) (xy 195.146851 88.561849) (xy 195.185357 88.538099) + (xy 195.185362 88.538093) (xy 195.191115 88.533546) (xy 195.191189 88.533639) (xy 195.196252 88.529515) + (xy 195.196176 88.529424) (xy 195.201798 88.524705) (xy 195.201798 88.524703) (xy 195.201805 88.5247) + (xy 195.252668 88.470787) (xy 196.471432 87.252022) (xy 196.485265 87.240068) (xy 196.503497 87.226496) + (xy 196.535846 87.187942) (xy 196.539515 87.18394) (xy 196.545059 87.178397) (xy 196.560924 87.158332) + (xy 196.564696 87.153562) (xy 196.570296 87.146887) (xy 196.579399 87.136037) (xy 196.605056 87.114526) + (xy 196.604298 87.113443) (xy 196.708228 87.04067) (xy 196.781809 86.989148) (xy 196.931148 86.839809) + (xy 197.052286 86.666806) (xy 197.141542 86.475395) (xy 197.196204 86.271394) (xy 197.214611 86.061) + (xy 197.196204 85.850606) (xy 197.141542 85.646605) (xy 197.052286 85.455195) (xy 197.052285 85.455194) + (xy 197.052284 85.455191) (xy 196.931152 85.282195) (xy 196.931149 85.282192) (xy 196.857015 85.208058) + (xy 196.781809 85.132852) (xy 196.747883 85.109097) (xy 196.608806 85.011714) (xy 196.515479 84.968195) + (xy 196.462194 84.921278) (xy 196.442733 84.853001) (xy 196.463275 84.785041) (xy 196.517298 84.738975) + (xy 196.568729 84.728) (xy 196.750132 84.728) (xy 196.750138 84.728) (xy 196.750145 84.727999) (xy 196.750149 84.727999) + (xy 196.810696 84.72149) (xy 196.810699 84.721489) (xy 196.810701 84.721489) (xy 196.947704 84.670389) + (xy 196.955311 84.664695) (xy 197.064761 84.582761) (xy 197.152387 84.465707) (xy 197.152389 84.465703) + (xy 197.15239 84.465702) (xy 197.191981 84.359556) (xy 197.203987 84.327367) (xy 197.246534 84.270531) + (xy 197.313054 84.245721) (xy 197.322042 84.2454) (xy 201.526423 84.2454) (xy 201.594544 84.265402) + (xy 201.641037 84.319058) (xy 201.644823 84.328303) (xy 201.668688 84.393871) (xy 201.686963 84.449023) + (xy 201.692003 84.464231) (xy 201.695103 84.470879) (xy 201.694996 84.470928) (xy 201.697848 84.476818) + (xy 201.697953 84.476766) (xy 201.701244 84.48332) (xy 201.74198 84.545256) (xy 201.780897 84.608352) + (xy 201.785454 84.614115) (xy 201.785361 84.614188) (xy 201.789493 84.61926) (xy 201.789584 84.619184) + (xy 201.794299 84.624804) (xy 201.848213 84.675669) (xy 203.066965 85.894421) (xy 203.078938 85.908275) + (xy 203.092502 85.926496) (xy 203.131028 85.958824) (xy 203.135082 85.962538) (xy 203.140599 85.968056) + (xy 203.140601 85.968057) (xy 203.140603 85.968059) (xy 203.146924 85.973057) (xy 203.165438 85.987697) + (xy 203.184496 86.003688) (xy 203.222226 86.035347) (xy 203.222232 86.03535) (xy 203.228361 86.039382) + (xy 203.228295 86.039481) (xy 203.233814 86.042996) (xy 203.233877 86.042896) (xy 203.24012 86.046747) + (xy 203.307314 86.07808) (xy 203.364375 86.106737) (xy 203.373554 86.111347) (xy 203.373556 86.111347) + (xy 203.380453 86.113858) (xy 203.380412 86.11397) (xy 203.386587 86.116116) (xy 203.386625 86.116004) + (xy 203.393588 86.118311) (xy 203.393591 86.118311) (xy 203.393592 86.118312) (xy 203.426796 86.125168) + (xy 203.466192 86.133303) (xy 203.476188 86.135672) (xy 203.53833 86.1504) (xy 203.538333 86.1504) + (xy 203.545619 86.151252) (xy 203.545605 86.151369) (xy 203.552109 86.152033) (xy 203.55212 86.151916) + (xy 203.559427 86.152554) (xy 203.559434 86.152556) (xy 203.633519 86.1504) (xy 207.34833 86.1504) + (xy 209.911054 86.1504) (xy 209.929313 86.151729) (xy 209.951788 86.155022) (xy 209.983545 86.152243) + (xy 210.001881 86.15064) (xy 210.007374 86.1504) (xy 210.015189 86.1504) (xy 210.030919 86.148561) + (xy 210.046651 86.146722) (xy 210.120483 86.140263) (xy 210.120495 86.140258) (xy 210.127665 86.138779) + (xy 210.127689 86.138896) (xy 210.134082 86.137479) (xy 210.134055 86.137364) (xy 210.141189 86.135672) + (xy 210.141195 86.135672) (xy 210.210871 86.110311) (xy 210.281229 86.086998) (xy 210.28124 86.08699) + (xy 210.287884 86.083894) (xy 210.287935 86.084003) (xy 210.293809 86.081159) (xy 210.293756 86.081053) + (xy 210.300319 86.077756) (xy 210.30032 86.077754) (xy 210.300323 86.077754) (xy 210.347466 86.046747) + (xy 210.362257 86.037019) (xy 210.387161 86.021658) (xy 210.425357 85.998099) (xy 210.425362 85.998093) + (xy 210.431115 85.993546) (xy 210.431189 85.993639) (xy 210.436252 85.989515) (xy 210.436176 85.989424) + (xy 210.441798 85.984705) (xy 210.441798 85.984703) (xy 210.441805 85.9847) (xy 210.492668 85.930787) + (xy 211.711432 84.712022) (xy 211.725265 84.700068) (xy 211.743497 84.686496) (xy 211.775846 84.647942) + (xy 211.779515 84.64394) (xy 211.785059 84.638397) (xy 211.804697 84.61356) (xy 211.852347 84.556774) + (xy 211.852348 84.55677) (xy 211.852351 84.556768) (xy 211.856385 84.550636) (xy 211.856484 84.550701) + (xy 211.859993 84.545193) (xy 211.859892 84.545131) (xy 211.863743 84.538887) (xy 211.863744 84.538884) + (xy 211.863746 84.538882) (xy 211.89508 84.471684) (xy 211.928347 84.405446) (xy 211.928347 84.405444) + (xy 211.928349 84.405441) (xy 211.93086 84.398543) (xy 211.930972 84.398584) (xy 211.933116 84.392416) + (xy 211.933002 84.392379) (xy 211.93531 84.385413) (xy 211.93531 84.385411) (xy 211.935312 84.385408) + (xy 211.950303 84.312806) (xy 211.9674 84.24067) (xy 211.9674 84.240664) (xy 211.968252 84.233381) + (xy 211.968369 84.233394) (xy 211.969033 84.22689) (xy 211.968916 84.22688) (xy 211.969554 84.219573) + (xy 211.969556 84.219566) (xy 211.9674 84.14548) (xy 211.9674 84.072874) (xy 211.987402 84.004753) + (xy 212.021127 83.969662) (xy 212.103754 83.911807) (xy 212.268807 83.746754) (xy 212.402691 83.555548) + (xy 212.501339 83.343998) (xy 212.561752 83.118532) (xy 212.582096 82.886) (xy 217.523904 82.886) + (xy 217.544248 83.118535) (xy 217.603955 83.341364) (xy 217.604661 83.343998) (xy 217.63618 83.41159) + (xy 217.703308 83.555546) (xy 217.703309 83.555548) (xy 217.837186 83.746745) (xy 217.83719 83.74675) + (xy 217.837193 83.746754) (xy 218.002246 83.911807) (xy 218.084871 83.969662) (xy 218.129199 84.025117) + (xy 218.1386 84.072874) (xy 218.1386 85.403093) (xy 218.138493 85.406757) (xy 218.134902 85.4684) + (xy 218.134903 85.468407) (xy 218.145625 85.52922) (xy 218.146156 85.532845) (xy 218.153328 85.594196) + (xy 218.156844 85.603858) (xy 218.162524 85.625055) (xy 218.164308 85.63517) (xy 218.16431 85.635177) + (xy 218.188768 85.691876) (xy 218.190112 85.695259) (xy 218.197781 85.716327) (xy 218.211245 85.753321) + (xy 218.211246 85.753323) (xy 218.216888 85.761901) (xy 218.227314 85.781236) (xy 218.231381 85.790665) + (xy 218.268264 85.84021) (xy 218.270366 85.843211) (xy 218.304298 85.894803) (xy 218.304297 85.894803) + (xy 218.311769 85.901852) (xy 218.326367 85.918254) (xy 218.332504 85.926497) (xy 218.37103 85.958824) + (xy 218.37982 85.9662) (xy 218.382553 85.968633) (xy 218.387243 85.973057) (xy 218.427475 86.011014) + (xy 218.436363 86.016145) (xy 218.454358 86.028744) (xy 218.462226 86.035347) (xy 218.517439 86.063075) + (xy 218.520632 86.064797) (xy 218.574127 86.095683) (xy 218.583965 86.098628) (xy 218.604374 86.106737) + (xy 218.613547 86.111344) (xy 218.61355 86.111345) (xy 218.613554 86.111347) (xy 218.673661 86.125592) + (xy 218.677173 86.126532) (xy 218.736353 86.144251) (xy 218.745724 86.144796) (xy 218.746594 86.144847) + (xy 218.768335 86.14803) (xy 218.77833 86.1504) (xy 218.840094 86.1504) (xy 218.843758 86.150507) + (xy 218.905402 86.154097) (xy 218.905403 86.154096) (xy 218.905407 86.154097) (xy 218.915518 86.152314) + (xy 218.937398 86.1504) (xy 225.8485 86.1504) (xy 225.916621 86.170402) (xy 225.963114 86.224058) + (xy 225.9745 86.2764) (xy 225.9745 86.744649) (xy 225.981009 86.805196) (xy 225.981011 86.805204) + (xy 226.03211 86.942202) (xy 226.032112 86.942207) (xy 226.119738 87.059261) (xy 226.236792 87.146887) + (xy 226.236794 87.146888) (xy 226.236796 87.146889) (xy 226.267476 87.158332) (xy 226.373795 87.197988) + (xy 226.373803 87.19799) (xy 226.43435 87.204499) (xy 226.434355 87.204499) (xy 226.434362 87.2045) + (xy 226.434368 87.2045) (xy 229.071632 87.2045) (xy 229.071638 87.2045) (xy 229.071645 87.204499) + (xy 229.071649 87.204499) (xy 229.132196 87.19799) (xy 229.132199 87.197989) (xy 229.132201 87.197989) + (xy 229.269204 87.146889) (xy 229.324829 87.105249) (xy 229.386261 87.059261) (xy 229.473887 86.942207) + (xy 229.473887 86.942206) (xy 229.473889 86.942204) (xy 229.524989 86.805201) (xy 229.5315 86.744638) + (xy 229.5315 84.107362) (xy 229.531499 84.10735) (xy 229.52499 84.046803) (xy 229.524988 84.046795) + (xy 229.487003 83.944955) (xy 229.473889 83.909796) (xy 229.473888 83.909794) (xy 229.473887 83.909792) + (xy 229.386261 83.792738) (xy 229.269207 83.705112) (xy 229.269202 83.70511) (xy 229.132204 83.654011) + (xy 229.132196 83.654009) (xy 229.071649 83.6475) (xy 229.071638 83.6475) (xy 226.434362 83.6475) + (xy 226.43435 83.6475) (xy 226.373803 83.654009) (xy 226.373795 83.654011) (xy 226.236797 83.70511) + (xy 226.236792 83.705112) (xy 226.119738 83.792738) (xy 226.032112 83.909792) (xy 226.03211 83.909797) + (xy 225.981011 84.046795) (xy 225.981009 84.046803) (xy 225.9745 84.10735) (xy 225.9745 84.5756) + (xy 225.954498 84.643721) (xy 225.900842 84.690214) (xy 225.8485 84.7016) (xy 219.7134 84.7016) + (xy 219.645279 84.681598) (xy 219.598786 84.627942) (xy 219.5874 84.5756) (xy 219.5874 84.072874) + (xy 219.607402 84.004753) (xy 219.641127 83.969662) (xy 219.723754 83.911807) (xy 219.888807 83.746754) + (xy 220.022691 83.555548) (xy 220.121339 83.343998) (xy 220.181752 83.118532) (xy 220.202096 82.886) + (xy 220.181752 82.653468) (xy 220.121339 82.428002) (xy 220.022691 82.216452) (xy 219.951547 82.114848) + (xy 219.888813 82.025254) (xy 219.888809 82.025249) (xy 219.888807 82.025246) (xy 219.723754 81.860193) + (xy 219.641128 81.802337) (xy 219.5968 81.74688) (xy 219.5874 81.699125) (xy 219.5874 80.346004) + (xy 225.716252 80.346004) (xy 225.73522 80.623323) (xy 225.735221 80.623329) (xy 225.791778 80.895501) + (xy 225.79178 80.895509) (xy 225.884872 81.157443) (xy 226.012763 81.404261) (xy 226.012764 81.404262) + (xy 226.146319 81.593468) (xy 226.95204 80.787747) (xy 227.014353 80.753722) (xy 227.085168 80.758786) + (xy 227.142004 80.801333) (xy 227.142121 80.801489) (xy 227.194968 80.872476) (xy 227.30211 80.962378) + (xy 227.341436 81.021486) (xy 227.342564 81.092473) (xy 227.310214 81.147994) (xy 226.506655 81.951553) + (xy 226.506655 81.951554) (xy 226.57844 82.009956) (xy 226.815965 82.154399) (xy 227.070933 82.265147) + (xy 227.070943 82.26515) (xy 227.338603 82.340146) (xy 227.338611 82.340148) (xy 227.613993 82.377999) + (xy 227.614007 82.378) (xy 227.891993 82.378) (xy 227.892006 82.377999) (xy 228.167388 82.340148) + (xy 228.167396 82.340146) (xy 228.435056 82.26515) (xy 228.435066 82.265147) (xy 228.690034 82.154399) + (xy 228.927558 82.009956) (xy 228.999343 81.951554) (xy 228.999343 81.951553) (xy 228.195862 81.148073) + (xy 228.161837 81.08576) (xy 228.166901 81.014945) (xy 228.209448 80.958109) (xy 228.215706 80.953713) + (xy 228.246138 80.933699) (xy 228.368378 80.804132) (xy 228.368378 80.804131) (xy 228.373414 80.798794) + (xy 228.374633 80.799944) (xy 228.424205 80.762618) (xy 228.495006 80.757354) (xy 228.557414 80.791204) + (xy 228.557665 80.791454) (xy 229.359679 81.593468) (xy 229.493231 81.40427) (xy 229.493236 81.404261) + (xy 229.621127 81.157443) (xy 229.714219 80.895509) (xy 229.714221 80.895501) (xy 229.770778 80.623329) + (xy 229.770779 80.623323) (xy 229.789748 80.346004) (xy 229.789748 80.345995) (xy 229.770779 80.068676) + (xy 229.770778 80.06867) (xy 229.714221 79.796498) (xy 229.714219 79.79649) (xy 229.621127 79.534556) + (xy 229.493236 79.287738) (xy 229.493236 79.287737) (xy 229.359679 79.09853) (xy 228.553957 79.904252) + (xy 228.491645 79.938277) (xy 228.420829 79.933212) (xy 228.363994 79.890665) (xy 228.363794 79.890397) + (xy 228.311032 79.819524) (xy 228.311029 79.819522) (xy 228.311029 79.819521) (xy 228.311027 79.819519) + (xy 228.203889 79.729621) (xy 228.164562 79.670512) (xy 228.163435 79.599525) (xy 228.195784 79.544003) + (xy 228.999343 78.740445) (xy 228.927554 78.68204) (xy 228.690034 78.5376) (xy 228.435066 78.426852) + (xy 228.435056 78.426849) (xy 228.167396 78.351853) (xy 228.167388 78.351851) (xy 227.892006 78.314) + (xy 227.613993 78.314) (xy 227.338611 78.351851) (xy 227.338603 78.351853) (xy 227.070943 78.426849) + (xy 227.070933 78.426852) (xy 226.815962 78.537602) (xy 226.578449 78.682036) (xy 226.506655 78.740444) + (xy 227.310137 79.543926) (xy 227.344162 79.606239) (xy 227.339098 79.677054) (xy 227.296551 79.73389) + (xy 227.290282 79.738293) (xy 227.259861 79.758301) (xy 227.132586 79.893206) (xy 227.13137 79.892058) + (xy 227.081766 79.929392) (xy 227.010963 79.934639) (xy 226.948564 79.900774) (xy 226.948334 79.900545) + (xy 226.146319 79.098529) (xy 226.146318 79.098529) (xy 226.012768 79.287728) (xy 225.884872 79.534556) + (xy 225.79178 79.79649) (xy 225.791778 79.796498) (xy 225.735221 80.06867) (xy 225.73522 80.068676) + (xy 225.716252 80.345995) (xy 225.716252 80.346004) (xy 219.5874 80.346004) (xy 219.5874 77.722874) + (xy 219.607402 77.654753) (xy 219.641127 77.619662) (xy 219.723754 77.561807) (xy 219.888807 77.396754) + (xy 220.022691 77.205548) (xy 220.121339 76.993998) (xy 220.181752 76.768532) (xy 220.202096 76.536) + (xy 220.181752 76.303468) (xy 220.178627 76.291807) (xy 220.15947 76.220309) (xy 220.121339 76.078002) + (xy 220.022691 75.866452) (xy 219.945263 75.755873) (xy 219.888813 75.675254) (xy 219.888809 75.675249) + (xy 219.888807 75.675246) (xy 219.723754 75.510193) (xy 219.72375 75.51019) (xy 219.723745 75.510186) + (xy 219.532548 75.376309) (xy 219.532546 75.376308) (xy 219.321 75.277662) (xy 219.320995 75.27766) + (xy 219.095535 75.217248) (xy 218.921132 75.20199) (xy 218.863 75.196904) (xy 218.862999 75.196904) + (xy 218.630464 75.217248) (xy 218.405004 75.27766) (xy 218.404999 75.277662) (xy 218.193453 75.376308) + (xy 218.193451 75.376309) (xy 218.002254 75.510186) (xy 218.002243 75.510195) (xy 217.837195 75.675243) + (xy 217.837186 75.675254) (xy 217.703309 75.866451) (xy 217.703308 75.866453) (xy 217.604662 76.077999) + (xy 217.60466 76.078004) (xy 217.544248 76.303464) (xy 217.523904 76.535999) (xy 217.544248 76.768535) + (xy 217.604313 76.9927) (xy 217.604661 76.993998) (xy 217.642153 77.074399) (xy 217.703308 77.205546) + (xy 217.703309 77.205548) (xy 217.837186 77.396745) (xy 217.83719 77.39675) (xy 217.837193 77.396754) + (xy 218.002246 77.561807) (xy 218.084871 77.619662) (xy 218.129199 77.675117) (xy 218.1386 77.722874) + (xy 218.1386 81.699125) (xy 218.118598 81.767246) (xy 218.084872 81.802337) (xy 218.002245 81.860194) + (xy 218.002243 81.860195) (xy 217.83719 82.025249) (xy 217.837186 82.025254) (xy 217.703309 82.216451) + (xy 217.703308 82.216453) (xy 217.604662 82.427999) (xy 217.60466 82.428004) (xy 217.544248 82.653464) + (xy 217.523904 82.886) (xy 212.582096 82.886) (xy 212.561752 82.653468) (xy 212.501339 82.428002) + (xy 212.402691 82.216452) (xy 212.331547 82.114848) (xy 212.268813 82.025254) (xy 212.268809 82.025249) + (xy 212.268807 82.025246) (xy 212.103754 81.860193) (xy 212.10375 81.86019) (xy 212.103745 81.860186) + (xy 211.912548 81.726309) (xy 211.912546 81.726308) (xy 211.780537 81.664751) (xy 211.700998 81.627661) + (xy 211.700996 81.62766) (xy 211.700995 81.62766) (xy 211.475535 81.567248) (xy 211.243 81.546904) + (xy 211.010464 81.567248) (xy 210.785004 81.62766) (xy 210.784999 81.627662) (xy 210.573453 81.726308) + (xy 210.573451 81.726309) (xy 210.382254 81.860186) (xy 210.382243 81.860195) (xy 210.217195 82.025243) + (xy 210.217186 82.025254) (xy 210.083309 82.216451) (xy 210.083308 82.216453) (xy 209.984662 82.427999) + (xy 209.98466 82.428004) (xy 209.924248 82.653464) (xy 209.903904 82.886) (xy 209.924248 83.118535) + (xy 209.983955 83.341364) (xy 209.984661 83.343998) (xy 210.01618 83.41159) (xy 210.083308 83.555546) + (xy 210.083309 83.555548) (xy 210.217186 83.746745) (xy 210.21719 83.74675) (xy 210.217193 83.746754) + (xy 210.280159 83.80972) (xy 210.333395 83.862956) (xy 210.36742 83.925268) (xy 210.362354 83.996084) + (xy 210.333395 84.041146) (xy 209.709848 84.664695) (xy 209.647535 84.69872) (xy 209.620752 84.7016) + (xy 203.975247 84.7016) (xy 203.907126 84.681598) (xy 203.886152 84.664695) (xy 203.262604 84.041147) + (xy 203.228578 83.978835) (xy 203.233643 83.90802) (xy 203.262604 83.862957) (xy 203.315841 83.80972) + (xy 203.378807 83.746754) (xy 203.512691 83.555548) (xy 203.611339 83.343998) (xy 203.671752 83.118532) + (xy 203.692096 82.886) (xy 203.671752 82.653468) (xy 203.611339 82.428002) (xy 203.512691 82.216452) + (xy 203.441547 82.114848) (xy 203.378813 82.025254) (xy 203.378809 82.025249) (xy 203.378807 82.025246) + (xy 203.213754 81.860193) (xy 203.21375 81.86019) (xy 203.213745 81.860186) (xy 203.022548 81.726309) + (xy 203.022546 81.726308) (xy 202.890537 81.664751) (xy 202.810998 81.627661) (xy 202.810996 81.62766) + (xy 202.810995 81.62766) (xy 202.585535 81.567248) (xy 202.411133 81.55199) (xy 202.353 81.546904) + (xy 202.352999 81.546904) (xy 202.120464 81.567248) (xy 201.895004 81.62766) (xy 201.894999 81.627662) + (xy 201.683453 81.726308) (xy 201.683451 81.726309) (xy 201.492254 81.860186) (xy 201.492243 81.860195) + (xy 201.327195 82.025243) (xy 201.327186 82.025254) (xy 201.193309 82.216451) (xy 201.193308 82.216453) + (xy 201.094662 82.427999) (xy 201.09466 82.428004) (xy 201.034248 82.653464) (xy 201.034248 82.653468) + (xy 201.032312 82.675606) (xy 201.031789 82.681581) (xy 201.005926 82.7477) (xy 200.948422 82.789339) + (xy 200.906268 82.7966) (xy 197.322042 82.7966) (xy 197.253921 82.776598) (xy 197.207428 82.722942) + (xy 197.203987 82.714633) (xy 197.152389 82.576296) (xy 197.152387 82.576292) (xy 197.064761 82.459238) + (xy 196.947707 82.371612) (xy 196.947702 82.37161) (xy 196.810704 82.320511) (xy 196.810696 82.320509) + (xy 196.750149 82.314) (xy 196.750138 82.314) (xy 195.255862 82.314) (xy 195.25585 82.314) (xy 195.195303 82.320509) + (xy 195.195295 82.320511) (xy 195.058297 82.37161) (xy 195.058292 82.371612) (xy 194.941238 82.459238) + (xy 194.853612 82.576292) (xy 194.85361 82.576297) (xy 194.802511 82.713295) (xy 194.802509 82.713303) + (xy 194.796 82.77385) (xy 194.796 84.268149) (xy 194.802509 84.328696) (xy 194.802511 84.328704) + (xy 194.85361 84.465702) (xy 194.853612 84.465707) (xy 194.941238 84.582761) (xy 195.058292 84.670387) + (xy 195.058294 84.670388) (xy 195.058296 84.670389) (xy 195.101475 84.686494) (xy 195.195295 84.721488) + (xy 195.195303 84.72149) (xy 195.25585 84.727999) (xy 195.255855 84.727999) (xy 195.255862 84.728) + (xy 195.255868 84.728) (xy 195.437272 84.728) (xy 195.505393 84.748002) (xy 195.551886 84.801658) + (xy 195.56199 84.871932) (xy 195.532496 84.936512) (xy 195.490522 84.968195) (xy 195.397191 85.011715) + (xy 195.224195 85.132847) (xy 195.224185 85.132856) (xy 195.074851 85.28219) (xy 194.982907 85.413501) + (xy 194.92745 85.457829) (xy 194.85683 85.465138) (xy 194.79347 85.433107) (xy 194.764306 85.391843) + (xy 194.751872 85.363497) (xy 194.660131 85.223077) (xy 194.009033 85.874176) (xy 193.946721 85.908201) + (xy 193.875905 85.903136) (xy 193.81907 85.860589) (xy 193.807672 85.842284) (xy 193.795883 85.819146) + (xy 193.795881 85.819144) (xy 193.79588 85.819142) (xy 193.704855 85.728117) (xy 193.681714 85.716326) + (xy 193.630099 85.667578) (xy 193.613034 85.598662) (xy 193.635935 85.531461) (xy 193.649823 85.514964) + (xy 194.301727 84.86306) (xy 194.264555 84.834128) (xy 194.264554 84.834127) (xy 194.051686 84.71893) + (xy 194.051684 84.718928) (xy 193.822764 84.64034) (xy 193.822757 84.640338) (xy 193.584016 84.6005) + (xy 193.341984 84.6005) (xy 193.103242 84.640338) (xy 193.103235 84.64034) (xy 192.874315 84.718928) + (xy 192.874313 84.71893) (xy 192.661447 84.834126) (xy 192.624271 84.863061) (xy 192.62427 84.863061) + (xy 193.276175 85.514966) (xy 193.310201 85.577278) (xy 193.305136 85.648093) (xy 193.262589 85.704929) + (xy 193.244284 85.716327) (xy 193.221144 85.728117) (xy 193.130117 85.819144) (xy 193.118327 85.842284) + (xy 193.069578 85.893899) (xy 193.000663 85.910964) (xy 192.933462 85.888062) (xy 192.916966 85.874175) + (xy 192.265867 85.223076) (xy 192.265866 85.223077) (xy 192.174131 85.363488) (xy 192.174124 85.363502) + (xy 192.076901 85.585147) (xy 192.076898 85.585154) (xy 192.017484 85.819778) (xy 191.997495 86.060999) + (xy 192.017484 86.302221) (xy 192.076898 86.536845) (xy 192.076901 86.536852) (xy 192.174126 86.758502) + (xy 192.265866 86.898921) (xy 192.916964 86.247823) (xy 192.979277 86.213798) (xy 193.050092 86.218862) + (xy 193.106928 86.261409) (xy 193.118326 86.279714) (xy 193.130117 86.302855) (xy 193.221143 86.393881) + (xy 193.221145 86.393882) (xy 193.221146 86.393883) (xy 193.244282 86.405671) (xy 193.295897 86.454419) + (xy 193.312963 86.523334) (xy 193.290062 86.590536) (xy 193.276175 86.607033) (xy 192.678515 87.204695) + (xy 192.616202 87.23872) (xy 192.589419 87.2416) (xy 191.275247 87.2416) (xy 191.207126 87.221598) + (xy 191.186152 87.204695) (xy 190.209032 86.227575) (xy 190.19706 86.213723) (xy 190.189762 86.20392) + (xy 190.183496 86.195503) (xy 190.183494 86.195501) (xy 190.144976 86.16318) (xy 190.140923 86.159466) + (xy 190.1354 86.153942) (xy 190.110544 86.134289) (xy 190.088886 86.116116) (xy 190.053774 86.086653) + (xy 190.05377 86.086651) (xy 190.053769 86.08665) (xy 190.04764 86.082618) (xy 190.047703 86.08252) + (xy 190.042189 86.079006) (xy 190.042128 86.079106) (xy 190.035882 86.075254) (xy 189.96869 86.043922) + (xy 189.902451 86.010655) (xy 189.895553 86.008144) (xy 189.895592 86.008034) (xy 189.889406 86.005884) + (xy 189.88937 86.005995) (xy 189.88241 86.003688) (xy 189.809814 85.988698) (xy 189.737668 85.971599) + (xy 189.73038 85.970748) (xy 189.730393 85.97063) (xy 189.723889 85.969966) (xy 189.723879 85.970084) + (xy 189.716566 85.969444) (xy 189.716565 85.969444) (xy 189.64248 85.9716) (xy 188.299875 85.9716) + (xy 188.231754 85.951598) (xy 188.196662 85.917871) (xy 188.138809 85.835249) (xy 188.138807 85.835246) + (xy 187.973754 85.670193) (xy 187.899031 85.617871) (xy 187.891128 85.612337) (xy 187.8468 85.55688) + (xy 187.8374 85.509125) (xy 187.8374 84.205042) (xy 187.857402 84.136921) (xy 187.911058 84.090428) + (xy 187.919367 84.086987) (xy 188.027102 84.046803) (xy 188.057704 84.035389) (xy 188.132521 83.979382) + (xy 188.174761 83.947761) (xy 188.262387 83.830707) (xy 188.262387 83.830706) (xy 188.262389 83.830704) + (xy 188.313489 83.693701) (xy 188.316181 83.668668) (xy 188.319999 83.633149) (xy 188.32 83.633132) + (xy 188.32 82.138867) (xy 188.319999 82.13885) (xy 188.31349 82.078303) (xy 188.313488 82.078295) + (xy 188.279242 81.98648) (xy 188.262389 81.941296) (xy 188.262388 81.941294) (xy 188.262387 81.941292) + (xy 188.174761 81.824238) (xy 188.057707 81.736612) (xy 188.057702 81.73661) (xy 187.920704 81.685511) + (xy 187.920696 81.685509) (xy 187.860149 81.679) (xy 187.860138 81.679) (xy 186.365862 81.679) (xy 186.36585 81.679) + (xy 186.305303 81.685509) (xy 186.305295 81.685511) (xy 186.168297 81.73661) (xy 186.168292 81.736612) + (xy 186.051238 81.824238) (xy 185.963612 81.941292) (xy 185.96361 81.941297) (xy 185.912511 82.078295) + (xy 185.912509 82.078303) (xy 185.906 82.13885) (xy 185.906 83.084933) (xy 185.885998 83.153054) + (xy 185.869095 83.174028) (xy 185.099788 83.943335) (xy 185.037476 83.977361) (xy 185.010693 83.98024) + (xy 180.393887 83.98024) (xy 180.325766 83.960238) (xy 180.304792 83.943335) (xy 179.4611 83.099643) + (xy 179.427074 83.037331) (xy 179.424675 82.999566) (xy 179.434611 82.886005) (xy 179.434611 82.886) + (xy 179.416204 82.675606) (xy 179.361542 82.471605) (xy 179.272286 82.280195) (xy 179.272285 82.280194) + (xy 179.272284 82.280191) (xy 179.151152 82.107195) (xy 179.151149 82.107192) (xy 179.104522 82.060565) + (xy 179.001809 81.957852) (xy 178.995005 81.953088) (xy 178.828806 81.836714) (xy 178.637398 81.747459) + (xy 178.637393 81.747457) (xy 178.508322 81.712873) (xy 178.433394 81.692796) (xy 178.223 81.674389) + (xy 178.012606 81.692796) (xy 177.988986 81.699125) (xy 177.808606 81.747457) (xy 177.808601 81.747459) + (xy 177.617191 81.836715) (xy 177.444195 81.957847) (xy 177.444185 81.957856) (xy 177.294856 82.107185) + (xy 177.294847 82.107195) (xy 177.173715 82.280191) (xy 177.084459 82.471601) (xy 177.084457 82.471606) + (xy 177.08237 82.479397) (xy 177.075231 82.506043) (xy 177.074707 82.507997) (xy 177.037755 82.56862) + (xy 176.973895 82.599641) (xy 176.9034 82.591213) (xy 176.848653 82.54601) (xy 176.831293 82.507997) + (xy 176.831059 82.507124) (xy 176.821542 82.471605) (xy 176.732286 82.280195) (xy 176.732285 82.280194) + (xy 176.732284 82.280191) (xy 176.611152 82.107195) (xy 176.611149 82.107192) (xy 176.564522 82.060565) + (xy 176.461809 81.957852) (xy 176.455005 81.953088) (xy 176.288806 81.836714) (xy 176.097398 81.747459) + (xy 176.097393 81.747457) (xy 175.968322 81.712873) (xy 175.893394 81.692796) (xy 175.683 81.674389) + (xy 175.682999 81.674389) (xy 175.600909 81.681571) (xy 175.472606 81.692796) (xy 175.448986 81.699125) + (xy 175.268606 81.747457) (xy 175.268601 81.747459) (xy 175.077191 81.836715) (xy 174.904195 81.957847) + (xy 174.904185 81.957856) (xy 174.754858 82.107183) (xy 174.754849 82.107194) (xy 174.754372 82.107876) + (xy 174.754085 82.108105) (xy 174.75132 82.111401) (xy 174.750657 82.110844) (xy 174.698913 82.152202) + (xy 174.651163 82.1616) (xy 171.934947 82.1616) (xy 171.916687 82.16027) (xy 171.894213 82.156978) + (xy 171.844116 82.16136) (xy 171.838624 82.1616) (xy 171.830808 82.1616) (xy 171.805655 82.16454) + (xy 171.799366 82.165275) (xy 171.784596 82.166567) (xy 171.725513 82.171736) (xy 171.718329 82.17322) + (xy 171.718305 82.173105) (xy 171.71192 82.17452) (xy 171.711948 82.174635) (xy 171.7048 82.176329) + (xy 171.635153 82.201679) (xy 171.564769 82.225001) (xy 171.558123 82.228101) (xy 171.558074 82.227996) + (xy 171.552177 82.230851) (xy 171.552229 82.230955) (xy 171.545673 82.234247) (xy 171.483747 82.274976) + (xy 171.420642 82.3139) (xy 171.414886 82.318452) (xy 171.414814 82.318361) (xy 171.40974 82.322495) + (xy 171.409815 82.322584) (xy 171.404193 82.327301) (xy 171.353332 82.381211) (xy 170.642569 83.091972) + (xy 170.62872 83.103941) (xy 170.610502 83.117504) (xy 170.610499 83.117507) (xy 170.578181 83.156022) + (xy 170.57447 83.160071) (xy 170.568942 83.165599) (xy 170.568939 83.165603) (xy 170.549289 83.190455) + (xy 170.501653 83.247225) (xy 170.497623 83.253353) (xy 170.497526 83.253289) (xy 170.494009 83.258808) + (xy 170.494108 83.258869) (xy 170.490255 83.265116) (xy 170.490254 83.265117) (xy 170.490254 83.265118) + (xy 170.463639 83.322193) (xy 170.458922 83.332309) (xy 170.425654 83.398551) (xy 170.423143 83.405451) + (xy 170.423033 83.405411) (xy 170.420886 83.41159) (xy 170.420996 83.411627) (xy 170.418688 83.41859) + (xy 170.403698 83.491185) (xy 170.386599 83.563331) (xy 170.385748 83.57062) (xy 170.385631 83.570606) + (xy 170.384966 83.577113) (xy 170.385084 83.577124) (xy 170.384444 83.584435) (xy 170.3866 83.658519) + (xy 170.3866 85.509125) (xy 170.366598 85.577246) (xy 170.332872 85.612337) (xy 170.315539 85.624474) + (xy 170.282708 85.647463) (xy 170.250245 85.670194) (xy 170.250243 85.670195) (xy 170.08519 85.835249) + (xy 170.085186 85.835254) (xy 169.951309 86.026451) (xy 169.951308 86.026453) (xy 169.852662 86.237999) + (xy 169.85266 86.238004) (xy 169.792248 86.463464) (xy 169.771904 86.696) (xy 169.792248 86.928535) + (xy 169.852544 87.153562) (xy 169.852661 87.153998) (xy 169.873175 87.19799) (xy 169.951308 87.365546) + (xy 169.951309 87.365548) (xy 170.085186 87.556745) (xy 170.08519 87.55675) (xy 170.085193 87.556754) + (xy 170.250246 87.721807) (xy 170.25025 87.72181) (xy 170.250254 87.721813) (xy 170.296495 87.754191) + (xy 170.441452 87.855691) (xy 170.653002 87.954339) (xy 170.799254 87.993526) (xy 170.860333 88.009893) + (xy 170.920956 88.046845) (xy 170.951977 88.110705) (xy 170.943549 88.1812) (xy 170.898346 88.235947) + (xy 170.830721 88.257564) (xy 170.827722 88.2576) (xy 170.320247 88.2576) (xy 170.252126 88.237598) + (xy 170.231152 88.220695) (xy 169.459305 87.448848) (xy 169.425279 87.386536) (xy 169.4224 87.359753) + (xy 169.4224 84.86306) (xy 169.4224 82.349241) (xy 169.442401 82.281124) (xy 169.459295 82.26016) + (xy 170.463853 81.255601) (xy 170.526163 81.221577) (xy 170.596978 81.226642) (xy 170.642041 81.255601) + (xy 170.758246 81.371807) (xy 170.758249 81.371809) (xy 170.758254 81.371813) (xy 170.857018 81.440968) + (xy 170.949452 81.505691) (xy 171.161002 81.604339) (xy 171.386468 81.664752) (xy 171.619 81.685096) + (xy 171.851532 81.664752) (xy 172.076998 81.604339) (xy 172.288548 81.505691) (xy 172.479754 81.371807) + (xy 172.644807 81.206754) (xy 172.778691 81.015548) (xy 172.877339 80.803998) (xy 172.937752 80.578532) + (xy 172.958096 80.346) (xy 172.937752 80.113468) (xy 172.877339 79.888002) (xy 172.778691 79.676452) + (xy 172.644807 79.485246) (xy 172.479754 79.320193) (xy 172.47975 79.32019) (xy 172.479745 79.320186) + (xy 172.288548 79.186309) (xy 172.288546 79.186308) (xy 172.182773 79.136984) (xy 172.076998 79.087661) + (xy 172.076996 79.08766) (xy 172.076995 79.08766) (xy 171.869667 79.032107) (xy 171.809044 78.995155) + (xy 171.778023 78.931295) (xy 171.786451 78.8608) (xy 171.831654 78.806053) (xy 171.899279 78.784436) + (xy 171.902278 78.7844) (xy 181.375094 78.7844) (xy 181.378758 78.784507) (xy 181.440402 78.788097) + (xy 181.440402 78.788096) (xy 181.440407 78.788097) (xy 181.495439 78.778392) (xy 181.501229 78.777372) + (xy 181.504857 78.776841) (xy 181.505151 78.776806) (xy 181.566195 78.769672) (xy 181.575853 78.766156) + (xy 181.597056 78.760474) (xy 181.607174 78.758691) (xy 181.663885 78.734227) (xy 181.667247 78.73289) + (xy 181.725323 78.711754) (xy 181.733907 78.706107) (xy 181.753233 78.695686) (xy 181.762665 78.691619) + (xy 181.812227 78.65472) (xy 181.815171 78.652659) (xy 181.866805 78.6187) (xy 181.87385 78.611231) + (xy 181.890261 78.596627) (xy 181.894323 78.593603) (xy 181.898497 78.590496) (xy 181.938213 78.543162) + (xy 181.940605 78.540474) (xy 181.983013 78.495526) (xy 181.988146 78.486632) (xy 182.000746 78.468639) + (xy 182.007347 78.460774) (xy 182.035082 78.405546) (xy 182.036781 78.402394) (xy 182.067683 78.348873) + (xy 182.070627 78.339035) (xy 182.078733 78.318632) (xy 182.083347 78.309446) (xy 182.097593 78.24933) + (xy 182.09853 78.245831) (xy 182.116251 78.186647) (xy 182.116846 78.176406) (xy 182.120033 78.154654) + (xy 182.1224 78.14467) (xy 182.1224 78.082905) (xy 182.122507 78.079241) (xy 182.126097 78.017593) + (xy 182.126096 78.017588) (xy 182.124314 78.007479) (xy 182.1224 77.985602) (xy 182.1224 76.888246) + (xy 182.142402 76.820125) (xy 182.159305 76.799151) (xy 184.836152 74.122305) (xy 184.898464 74.088279) + (xy 184.925247 74.0854) (xy 186.295109 74.0854) (xy 186.36323 74.105402) (xy 186.409723 74.159058) + (xy 186.419827 74.229332) (xy 186.390333 74.293912) (xy 186.36738 74.314612) (xy 186.355729 74.322771) + (xy 186.334188 74.337854) (xy 186.334185 74.337856) (xy 186.184856 74.487185) (xy 186.184847 74.487195) + (xy 186.063715 74.660191) (xy 185.974459 74.851601) (xy 185.974457 74.851606) (xy 185.949419 74.945051) + (xy 185.919796 75.055606) (xy 185.901389 75.266) (xy 185.919796 75.476394) (xy 185.928853 75.510194) + (xy 185.974457 75.680393) (xy 185.974459 75.680398) (xy 186.063714 75.871806) (xy 186.161613 76.011621) + (xy 186.184852 76.044809) (xy 186.334191 76.194148) (xy 186.334865 76.19462) (xy 186.335092 76.194904) + (xy 186.338401 76.19768) (xy 186.337843 76.198344) (xy 186.379196 76.250071) (xy 186.3886 76.297836) + (xy 186.3886 77.109052) (xy 186.38727 77.127311) (xy 186.383978 77.149784) (xy 186.383978 77.149789) + (xy 186.38836 77.199881) (xy 186.3886 77.205374) (xy 186.3886 77.213191) (xy 186.392277 77.244651) + (xy 186.398736 77.31848) (xy 186.40022 77.325666) (xy 186.400104 77.325689) (xy 186.401521 77.332079) + (xy 186.401635 77.332052) (xy 186.403329 77.339199) (xy 186.40738 77.350328) (xy 186.428688 77.408871) + (xy 186.451372 77.477329) (xy 186.452003 77.479231) (xy 186.455103 77.485879) (xy 186.454996 77.485928) + (xy 186.457848 77.491818) (xy 186.457953 77.491766) (xy 186.461244 77.49832) (xy 186.50198 77.560256) + (xy 186.540897 77.623352) (xy 186.545454 77.629115) (xy 186.545361 77.629188) (xy 186.549493 77.63426) + (xy 186.549584 77.634184) (xy 186.554299 77.639804) (xy 186.608213 77.690669) (xy 190.161695 81.244151) + (xy 190.195721 81.306463) (xy 190.1986 81.333246) (xy 190.1986 81.566957) (xy 190.178598 81.635078) + (xy 190.124942 81.681571) (xy 190.116634 81.685012) (xy 189.978295 81.736611) (xy 189.978292 81.736612) + (xy 189.861238 81.824238) (xy 189.773612 81.941292) (xy 189.77361 81.941297) (xy 189.722511 82.078295) + (xy 189.722509 82.078303) (xy 189.716 82.13885) (xy 189.716 83.633149) (xy 189.722509 83.693696) + (xy 189.722511 83.693704) (xy 189.77361 83.830702) (xy 189.773612 83.830707) (xy 189.861238 83.947761) + (xy 189.978292 84.035387) (xy 189.978294 84.035388) (xy 189.978296 84.035389) (xy 190.008898 84.046803) + (xy 190.115295 84.086488) (xy 190.115303 84.08649) (xy 190.17585 84.092999) (xy 190.175855 84.092999) + (xy 190.175862 84.093) (xy 190.175868 84.093) (xy 191.670132 84.093) (xy 191.670138 84.093) (xy 191.670145 84.092999) + (xy 191.670149 84.092999) (xy 191.730696 84.08649) (xy 191.730699 84.086489) (xy 191.730701 84.086489) + (xy 191.867704 84.035389) (xy 191.942521 83.979382) (xy 191.984761 83.947761) (xy 192.072387 83.830707) + (xy 192.072387 83.830706) (xy 192.072389 83.830704) (xy 192.123489 83.693701) (xy 192.126181 83.668668) + (xy 192.129999 83.633149) (xy 192.13 83.633132) (xy 192.13 82.138867) (xy 192.129999 82.13885) (xy 192.12349 82.078303) + (xy 192.123488 82.078295) (xy 192.089242 81.98648) (xy 192.072389 81.941296) (xy 192.072388 81.941294) + (xy 192.072387 81.941292) (xy 191.984761 81.824238) (xy 191.867707 81.736612) (xy 191.867704 81.736611) + (xy 191.729366 81.685012) (xy 191.672531 81.642465) (xy 191.647721 81.575944) (xy 191.6474 81.566957) + (xy 191.6474 81.088649) (xy 191.667402 81.020528) (xy 191.721058 80.974035) (xy 191.791332 80.963931) + (xy 191.826645 80.974452) (xy 191.872705 80.99593) (xy 191.877309 80.998077) (xy 191.943551 81.031346) + (xy 191.950455 81.033859) (xy 191.950414 81.033971) (xy 191.956589 81.036117) (xy 191.956627 81.036004) + (xy 191.96359 81.038311) (xy 191.963591 81.038311) (xy 191.963593 81.038312) (xy 192.010651 81.048028) + (xy 192.036185 81.053301) (xy 192.059792 81.058896) (xy 192.10833 81.0704) (xy 192.108336 81.0704) + (xy 192.115619 81.071252) (xy 192.115605 81.071369) (xy 192.12211 81.072033) (xy 192.122121 81.071916) + (xy 192.129428 81.072554) (xy 192.129435 81.072556) (xy 192.20352 81.0704) (xy 199.751054 81.0704) + (xy 199.769313 81.071729) (xy 199.791788 81.075022) (xy 199.823545 81.072243) (xy 199.841881 81.07064) + (xy 199.847374 81.0704) (xy 199.855189 81.0704) (xy 199.870919 81.068561) (xy 199.886651 81.066722) + (xy 199.960483 81.060263) (xy 199.960495 81.060258) (xy 199.967665 81.058779) (xy 199.967689 81.058896) + (xy 199.974082 81.057479) (xy 199.974055 81.057364) (xy 199.981189 81.055672) (xy 199.981195 81.055672) + (xy 200.050871 81.030311) (xy 200.121229 81.006998) (xy 200.12124 81.00699) (xy 200.127884 81.003894) + (xy 200.127935 81.004003) (xy 200.133809 81.001159) (xy 200.133756 81.001053) (xy 200.140319 80.997756) + (xy 200.14032 80.997754) (xy 200.140323 80.997754) (xy 200.176385 80.974035) (xy 200.202257 80.957019) + (xy 200.221716 80.945015) (xy 200.265357 80.918099) (xy 200.265362 80.918093) (xy 200.271115 80.913546) + (xy 200.271189 80.913639) (xy 200.276252 80.909515) (xy 200.276176 80.909424) (xy 200.281798 80.904705) + (xy 200.281798 80.904703) (xy 200.281805 80.9047) (xy 200.332668 80.850787) (xy 202.821426 78.362028) + (xy 202.835265 78.350068) (xy 202.853497 78.336496) (xy 202.885846 78.297942) (xy 202.889515 78.29394) + (xy 202.895059 78.288397) (xy 202.914693 78.263565) (xy 202.962347 78.206774) (xy 202.962348 78.206771) + (xy 202.96638 78.200643) (xy 202.966479 78.200708) (xy 202.969993 78.19519) (xy 202.969892 78.195128) + (xy 202.973739 78.188889) (xy 202.973745 78.188882) (xy 203.005072 78.1217) (xy 203.038347 78.055446) + (xy 203.038347 78.055444) (xy 203.038349 78.055441) (xy 203.04086 78.048543) (xy 203.040972 78.048584) + (xy 203.043119 78.042408) (xy 203.043004 78.04237) (xy 203.045309 78.035412) (xy 203.045312 78.035408) + (xy 203.060299 77.96282) (xy 203.0774 77.89067) (xy 203.078252 77.883382) (xy 203.078368 77.883395) + (xy 203.079033 77.876891) (xy 203.078915 77.876881) (xy 203.079553 77.869573) (xy 203.079555 77.869566) + (xy 203.0774 77.7955) (xy 203.0774 76.452874) (xy 203.097402 76.384753) (xy 203.131127 76.349662) + (xy 203.213754 76.291807) (xy 203.378807 76.126754) (xy 203.433218 76.049047) (xy 203.436662 76.044129) + (xy 203.492119 75.999801) (xy 203.539875 75.9904) (xy 205.611125 75.9904) (xy 205.679246 76.010402) + (xy 205.714338 76.044129) (xy 205.77219 76.12675) (xy 205.772193 76.126754) (xy 205.937246 76.291807) + (xy 205.93725 76.29181) (xy 205.937254 76.291813) (xy 206.046346 76.3682) (xy 206.128452 76.425691) + (xy 206.340002 76.524339) (xy 206.565468 76.584752) (xy 206.798 76.605096) (xy 207.030532 76.584752) + (xy 207.255998 76.524339) (xy 207.467548 76.425691) (xy 207.658754 76.291807) (xy 207.823807 76.126754) + (xy 207.957691 75.935548) (xy 208.056339 75.723998) (xy 208.116752 75.498532) (xy 208.137096 75.266) + (xy 208.116752 75.033468) (xy 208.056339 74.808002) (xy 207.957691 74.596452) (xy 207.873579 74.476328) + (xy 207.823813 74.405254) (xy 207.823809 74.405249) (xy 207.823807 74.405246) (xy 207.658754 74.240193) + (xy 207.65875 74.24019) (xy 207.658745 74.240186) (xy 207.467548 74.106309) (xy 207.467546 74.106308) + (xy 207.3232 74.038998) (xy 207.255998 74.007661) (xy 207.255996 74.00766) (xy 207.255995 74.00766) + (xy 207.030535 73.947248) (xy 206.798 73.926904) (xy 206.565464 73.947248) (xy 206.340004 74.00766) + (xy 206.339999 74.007662) (xy 206.128453 74.106308) (xy 206.128451 74.106309) (xy 205.937254 74.240186) + (xy 205.937243 74.240195) (xy 205.772195 74.405243) (xy 205.77219 74.405249) (xy 205.714338 74.487871) + (xy 205.658881 74.532199) (xy 205.611125 74.5416) (xy 203.539875 74.5416) (xy 203.471754 74.521598) + (xy 203.436662 74.487871) (xy 203.378809 74.405249) (xy 203.378804 74.405243) (xy 203.213756 74.240195) + (xy 203.213745 74.240186) (xy 203.022548 74.106309) (xy 203.022546 74.106308) (xy 202.8782 74.038998) + (xy 202.810998 74.007661) (xy 202.810996 74.00766) (xy 202.810995 74.00766) (xy 202.585535 73.947248) + (xy 202.353 73.926904) (xy 202.120464 73.947248) (xy 201.895004 74.00766) (xy 201.894999 74.007662) + (xy 201.683453 74.106308) (xy 201.683451 74.106309) (xy 201.492254 74.240186) (xy 201.492243 74.240195) + (xy 201.327195 74.405243) (xy 201.327186 74.405254) (xy 201.193309 74.596451) (xy 201.193308 74.596453) + (xy 201.094662 74.807999) (xy 201.09466 74.808004) (xy 201.034248 75.033464) (xy 201.013904 75.266) + (xy 201.034248 75.498535) (xy 201.09466 75.723995) (xy 201.094661 75.723998) (xy 201.097289 75.729633) + (xy 201.193308 75.935546) (xy 201.193309 75.935548) (xy 201.327186 76.126745) (xy 201.32719 76.12675) + (xy 201.327193 76.126754) (xy 201.492246 76.291807) (xy 201.574871 76.349662) (xy 201.619199 76.405117) + (xy 201.6286 76.452874) (xy 201.6286 77.453753) (xy 201.608598 77.521874) (xy 201.591695 77.542848) + (xy 199.549848 79.584695) (xy 199.487536 79.618721) (xy 199.460753 79.6216) (xy 192.545246 79.6216) + (xy 192.477125 79.601598) (xy 192.456151 79.584695) (xy 189.779305 76.907848) (xy 189.745279 76.845536) + (xy 189.7424 76.818753) (xy 189.7424 76.083891) (xy 189.762402 76.01577) (xy 189.816058 75.969277) + (xy 189.886332 75.959173) (xy 189.950912 75.988667) (xy 189.971611 76.011618) (xy 189.994852 76.044809) + (xy 190.144191 76.194148) (xy 190.317194 76.315286) (xy 190.508605 76.404542) (xy 190.712606 76.459204) + (xy 190.923 76.477611) (xy 191.133394 76.459204) (xy 191.337395 76.404542) (xy 191.528806 76.315286) + (xy 191.701809 76.194148) (xy 191.851148 76.044809) (xy 191.972286 75.871806) (xy 192.061542 75.680395) + (xy 192.116204 75.476394) (xy 192.134611 75.266) (xy 192.116204 75.055606) (xy 192.068885 74.879009) + (xy 192.070575 74.808034) (xy 192.110369 74.749239) (xy 192.175634 74.721291) (xy 192.190592 74.7204) + (xy 193.701164 74.7204) (xy 193.769285 74.740402) (xy 193.800991 74.770876) (xy 193.801321 74.7706) + (xy 193.80389 74.773662) (xy 193.804375 74.774129) (xy 193.804852 74.774809) (xy 193.954191 74.924148) + (xy 194.127194 75.045286) (xy 194.149328 75.055607) (xy 194.220521 75.088805) (xy 194.273806 75.135722) + (xy 194.293267 75.203999) (xy 194.272725 75.271959) (xy 194.218702 75.318025) (xy 194.167271 75.329) + (xy 193.98585 75.329) (xy 193.925303 75.335509) (xy 193.925295 75.335511) (xy 193.788297 75.38661) + (xy 193.788292 75.386612) (xy 193.671238 75.474238) (xy 193.583612 75.591292) (xy 193.58361 75.591297) + (xy 193.532511 75.728295) (xy 193.532509 75.728303) (xy 193.526 75.78885) (xy 193.526 77.283149) + (xy 193.532509 77.343696) (xy 193.532511 77.343704) (xy 193.58361 77.480702) (xy 193.583612 77.480707) + (xy 193.671238 77.597761) (xy 193.788292 77.685387) (xy 193.788294 77.685388) (xy 193.788296 77.685389) + (xy 193.840121 77.704719) (xy 193.925295 77.736488) (xy 193.925303 77.73649) (xy 193.98585 77.742999) + (xy 193.985855 77.742999) (xy 193.985862 77.743) (xy 193.985868 77.743) (xy 195.480132 77.743) (xy 195.480138 77.743) + (xy 195.480145 77.742999) (xy 195.480149 77.742999) (xy 195.540696 77.73649) (xy 195.540699 77.736489) + (xy 195.540701 77.736489) (xy 195.677704 77.685389) (xy 195.68195 77.682211) (xy 195.794761 77.597761) + (xy 195.882387 77.480707) (xy 195.882389 77.480703) (xy 195.88239 77.480702) (xy 195.913701 77.396754) + (xy 195.933987 77.342367) (xy 195.976534 77.285531) (xy 196.043054 77.260721) (xy 196.052042 77.2604) + (xy 198.481054 77.2604) (xy 198.499313 77.261729) (xy 198.521788 77.265022) (xy 198.553545 77.262243) + (xy 198.571881 77.26064) (xy 198.577374 77.2604) (xy 198.585189 77.2604) (xy 198.600919 77.258561) + (xy 198.616651 77.256722) (xy 198.690483 77.250263) (xy 198.690495 77.250258) (xy 198.697665 77.248779) + (xy 198.697689 77.248896) (xy 198.704082 77.247479) (xy 198.704055 77.247364) (xy 198.711189 77.245672) + (xy 198.711195 77.245672) (xy 198.780871 77.220311) (xy 198.851229 77.196998) (xy 198.85124 77.19699) + (xy 198.857884 77.193894) (xy 198.857935 77.194003) (xy 198.863809 77.191159) (xy 198.863756 77.191053) + (xy 198.870319 77.187756) (xy 198.87032 77.187754) (xy 198.870323 77.187754) (xy 198.928045 77.149789) + (xy 198.932257 77.147019) (xy 198.951717 77.135015) (xy 198.995357 77.108099) (xy 198.995362 77.108093) + (xy 199.001115 77.103546) (xy 199.001189 77.103639) (xy 199.006252 77.099515) (xy 199.006176 77.099424) + (xy 199.011798 77.094705) (xy 199.011798 77.094703) (xy 199.011805 77.0947) (xy 199.062668 77.040787) + (xy 200.281432 75.822022) (xy 200.295265 75.810068) (xy 200.313497 75.796496) (xy 200.345846 75.757942) + (xy 200.349515 75.75394) (xy 200.355059 75.748397) (xy 200.374697 75.72356) (xy 200.422347 75.666774) + (xy 200.422348 75.66677) (xy 200.422351 75.666768) (xy 200.426385 75.660636) (xy 200.426484 75.660701) + (xy 200.429993 75.655193) (xy 200.429892 75.655131) (xy 200.433743 75.648887) (xy 200.433744 75.648884) + (xy 200.433746 75.648882) (xy 200.46508 75.581684) (xy 200.498347 75.515446) (xy 200.498347 75.515444) + (xy 200.498349 75.515441) (xy 200.50086 75.508543) (xy 200.500972 75.508584) (xy 200.503116 75.502416) + (xy 200.503002 75.502379) (xy 200.50531 75.495413) (xy 200.50531 75.495411) (xy 200.505312 75.495408) + (xy 200.520303 75.422806) (xy 200.5374 75.35067) (xy 200.5374 75.350664) (xy 200.538252 75.343381) + (xy 200.538369 75.343394) (xy 200.539033 75.33689) (xy 200.538916 75.33688) (xy 200.539554 75.329573) + (xy 200.539556 75.329566) (xy 200.5374 75.25548) (xy 200.5374 73.078245) (xy 200.557402 73.010124) + (xy 200.574296 72.989159) (xy 201.346153 72.217302) (xy 201.408463 72.183279) (xy 201.435246 72.1804) + (xy 206.07833 72.1804) (xy 208.985754 72.1804) (xy 209.053875 72.200402) (xy 209.074849 72.217305) + (xy 210.481695 73.624151) (xy 210.515721 73.686463) (xy 210.5186 73.713246) (xy 210.5186 75.349125) + (xy 210.498598 75.417246) (xy 210.464871 75.452337) (xy 210.384603 75.508543) (xy 210.382245 75.510194) + (xy 210.382243 75.510195) (xy 210.21719 75.675249) (xy 210.217186 75.675254) (xy 210.083309 75.866451) + (xy 210.083308 75.866453) (xy 209.984662 76.077999) (xy 209.98466 76.078004) (xy 209.924248 76.303464) + (xy 209.903904 76.536) (xy 209.924096 76.766795) (xy 209.910107 76.836399) (xy 209.88767 76.866871) + (xy 206.329573 80.424968) (xy 206.315724 80.436938) (xy 206.297502 80.450504) (xy 206.297499 80.450507) + (xy 206.265181 80.489022) (xy 206.26147 80.493071) (xy 206.255942 80.498599) (xy 206.255939 80.498603) + (xy 206.236289 80.523455) (xy 206.188653 80.580225) (xy 206.184623 80.586353) (xy 206.184526 80.586289) + (xy 206.181009 80.591808) (xy 206.181108 80.591869) (xy 206.177255 80.598116) (xy 206.145922 80.665309) + (xy 206.112654 80.731551) (xy 206.110143 80.738451) (xy 206.110033 80.738411) (xy 206.107886 80.74459) + (xy 206.107996 80.744627) (xy 206.105688 80.75159) (xy 206.090698 80.824185) (xy 206.073599 80.896331) + (xy 206.072748 80.90362) (xy 206.072631 80.903606) (xy 206.071966 80.910113) (xy 206.072084 80.910124) + (xy 206.071444 80.917435) (xy 206.0736 80.991519) (xy 206.0736 81.699125) (xy 206.053598 81.767246) + (xy 206.019872 81.802337) (xy 205.937245 81.860194) (xy 205.937243 81.860195) (xy 205.77219 82.025249) + (xy 205.772186 82.025254) (xy 205.638309 82.216451) (xy 205.638308 82.216453) (xy 205.539662 82.427999) + (xy 205.53966 82.428004) (xy 205.479248 82.653464) (xy 205.458904 82.886) (xy 205.479248 83.118535) + (xy 205.538955 83.341364) (xy 205.539661 83.343998) (xy 205.57118 83.41159) (xy 205.638308 83.555546) + (xy 205.638309 83.555548) (xy 205.772186 83.746745) (xy 205.77219 83.74675) (xy 205.772193 83.746754) + (xy 205.937246 83.911807) (xy 205.93725 83.91181) (xy 205.937254 83.911813) (xy 206.024466 83.972879) + (xy 206.128452 84.045691) (xy 206.340002 84.144339) (xy 206.565468 84.204752) (xy 206.798 84.225096) + (xy 207.030532 84.204752) (xy 207.255998 84.144339) (xy 207.467548 84.045691) (xy 207.658754 83.911807) + (xy 207.823807 83.746754) (xy 207.957691 83.555548) (xy 208.056339 83.343998) (xy 208.116752 83.118532) + (xy 208.137096 82.886) (xy 208.116752 82.653468) (xy 208.056339 82.428002) (xy 207.957691 82.216452) + (xy 207.886547 82.114848) (xy 207.823813 82.025254) (xy 207.823809 82.025249) (xy 207.823807 82.025246) + (xy 207.658754 81.860193) (xy 207.576128 81.802337) (xy 207.5318 81.74688) (xy 207.5224 81.699125) + (xy 207.5224 81.333245) (xy 207.542402 81.265124) (xy 207.5593 81.244155) (xy 210.912127 77.891327) + (xy 210.974437 77.857303) (xy 211.012197 77.854903) (xy 211.243 77.875096) (xy 211.475532 77.854752) + (xy 211.700998 77.794339) (xy 211.912548 77.695691) (xy 212.103754 77.561807) (xy 212.268807 77.396754) + (xy 212.402691 77.205548) (xy 212.501339 76.993998) (xy 212.561752 76.768532) (xy 212.582096 76.536) + (xy 212.561752 76.303468) (xy 212.558627 76.291807) (xy 212.53947 76.220309) (xy 212.501339 76.078002) + (xy 212.402691 75.866452) (xy 212.325263 75.755873) (xy 212.268813 75.675254) (xy 212.268809 75.675249) + (xy 212.268807 75.675246) (xy 212.103754 75.510193) (xy 212.021128 75.452337) (xy 211.9768 75.39688) + (xy 211.9674 75.349125) (xy 211.9674 73.422947) (xy 211.96873 73.404687) (xy 211.969043 73.402543) + (xy 211.972022 73.382213) (xy 211.969571 73.354199) (xy 211.96764 73.332115) (xy 211.9674 73.326623) + (xy 211.9674 73.31881) (xy 211.963724 73.287366) (xy 211.962345 73.2716) (xy 211.957264 73.213517) + (xy 211.957261 73.213509) (xy 211.955779 73.206327) (xy 211.955896 73.206302) (xy 211.95448 73.199917) + (xy 211.954364 73.199945) (xy 211.952672 73.192807) (xy 211.952672 73.192805) (xy 211.92732 73.123153) + (xy 211.903999 73.052772) (xy 211.903996 73.052768) (xy 211.900896 73.046118) (xy 211.901003 73.046068) + (xy 211.898152 73.040179) (xy 211.898046 73.040233) (xy 211.894756 73.033684) (xy 211.894754 73.033677) + (xy 211.874246 73.002496) (xy 211.854023 72.971747) (xy 211.8151 72.908645) (xy 211.815099 72.908643) + (xy 211.815096 72.90864) (xy 211.810546 72.902885) (xy 211.810638 72.902811) (xy 211.806506 72.897739) + (xy 211.806416 72.897815) (xy 211.801702 72.892197) (xy 211.795219 72.886081) (xy 211.747787 72.841331) + (xy 210.829259 71.922803) (xy 209.894032 70.987575) (xy 209.88206 70.973723) (xy 209.868496 70.955503) + (xy 209.868494 70.955501) (xy 209.829976 70.92318) (xy 209.825923 70.919466) (xy 209.8204 70.913942) + (xy 209.795544 70.894289) (xy 209.788588 70.888452) (xy 209.738774 70.846653) (xy 209.73877 70.846651) + (xy 209.738769 70.84665) (xy 209.73264 70.842618) (xy 209.732703 70.84252) (xy 209.727189 70.839006) + (xy 209.727128 70.839106) (xy 209.720882 70.835254) (xy 209.65369 70.803922) (xy 209.587451 70.770655) + (xy 209.580553 70.768144) (xy 209.580592 70.768034) (xy 209.574406 70.765884) (xy 209.57437 70.765995) + (xy 209.56741 70.763688) (xy 209.494814 70.748698) (xy 209.422668 70.731599) (xy 209.41538 70.730748) + (xy 209.415393 70.73063) (xy 209.408889 70.729966) (xy 209.408879 70.730084) (xy 209.401566 70.729444) + (xy 209.401565 70.729444) (xy 209.32748 70.7316) (xy 201.144946 70.7316) (xy 201.126686 70.73027) + (xy 201.121047 70.729444) (xy 201.104212 70.726978) (xy 201.104211 70.726978) (xy 201.054118 70.73136) + (xy 201.048625 70.7316) (xy 201.040808 70.7316) (xy 201.009349 70.735277) (xy 200.935516 70.741736) + (xy 200.928331 70.74322) (xy 200.928307 70.743107) (xy 200.921909 70.744526) (xy 200.921936 70.744638) + (xy 200.914799 70.746329) (xy 200.867107 70.763688) (xy 200.845132 70.771686) (xy 200.821678 70.779458) + (xy 200.774769 70.795002) (xy 200.76812 70.798103) (xy 200.768071 70.797998) (xy 200.762183 70.800848) + (xy 200.762235 70.800952) (xy 200.755678 70.804245) (xy 200.69375 70.844974) (xy 200.630641 70.883901) + (xy 200.624885 70.888452) (xy 200.624813 70.888362) (xy 200.619745 70.89249) (xy 200.61982 70.892579) + (xy 200.614194 70.897299) (xy 200.563347 70.951195) (xy 199.344573 72.169968) (xy 199.330724 72.181938) + (xy 199.312502 72.195504) (xy 199.312499 72.195507) (xy 199.280181 72.234022) (xy 199.27647 72.238071) + (xy 199.270942 72.243599) (xy 199.270939 72.243603) (xy 199.251289 72.268455) (xy 199.203653 72.325225) + (xy 199.199623 72.331353) (xy 199.199526 72.331289) (xy 199.196009 72.336808) (xy 199.196108 72.336869) + (xy 199.192255 72.343116) (xy 199.160922 72.410309) (xy 199.127654 72.476551) (xy 199.125143 72.483451) + (xy 199.125033 72.483411) (xy 199.122886 72.48959) (xy 199.122996 72.489627) (xy 199.120688 72.49659) + (xy 199.105698 72.569185) (xy 199.088599 72.641331) (xy 199.087748 72.64862) (xy 199.087631 72.648606) + (xy 199.086966 72.655113) (xy 199.087084 72.655124) (xy 199.086444 72.662435) (xy 199.0886 72.736519) + (xy 199.0886 74.913753) (xy 199.068598 74.981874) (xy 199.051695 75.002848) (xy 198.279848 75.774695) + (xy 198.217536 75.808721) (xy 198.190753 75.8116) (xy 196.052042 75.8116) (xy 195.983921 75.791598) + (xy 195.937428 75.737942) (xy 195.933987 75.729633) (xy 195.882389 75.591296) (xy 195.882387 75.591292) + (xy 195.794761 75.474238) (xy 195.677707 75.386612) (xy 195.677702 75.38661) (xy 195.540704 75.335511) + (xy 195.540696 75.335509) (xy 195.480149 75.329) (xy 195.480138 75.329) (xy 195.298729 75.329) (xy 195.230608 75.308998) + (xy 195.184115 75.255342) (xy 195.174011 75.185068) (xy 195.203505 75.120488) (xy 195.245479 75.088805) + (xy 195.254545 75.084576) (xy 195.338806 75.045286) (xy 195.511809 74.924148) (xy 195.661148 74.774809) + (xy 195.782286 74.601806) (xy 195.871542 74.410395) (xy 195.881293 74.374002) (xy 195.918244 74.31338) + (xy 195.982104 74.282358) (xy 196.052599 74.290786) (xy 196.107346 74.335988) (xy 196.124707 74.374003) + (xy 196.134456 74.41039) (xy 196.134459 74.410398) (xy 196.223714 74.601806) (xy 196.344378 74.774133) + (xy 196.344852 74.774809) (xy 196.494191 74.924148) (xy 196.667194 75.045286) (xy 196.858605 75.134542) + (xy 197.062606 75.189204) (xy 197.273 75.207611) (xy 197.483394 75.189204) (xy 197.687395 75.134542) + (xy 197.878806 75.045286) (xy 198.051809 74.924148) (xy 198.201148 74.774809) (xy 198.322286 74.601806) + (xy 198.411542 74.410395) (xy 198.466204 74.206394) (xy 198.484611 73.996) (xy 198.466204 73.785606) + (xy 198.411542 73.581605) (xy 198.322286 73.390195) (xy 198.322285 73.390194) (xy 198.322284 73.390191) + (xy 198.201152 73.217195) (xy 198.201149 73.217192) (xy 198.129752 73.145795) (xy 198.051809 73.067852) + (xy 198.030128 73.052671) (xy 197.878806 72.946714) (xy 197.687398 72.857459) (xy 197.687393 72.857457) + (xy 197.592715 72.832088) (xy 197.483394 72.802796) (xy 197.273 72.784389) (xy 197.272996 72.784389) + (xy 197.272994 72.784389) (xy 197.267499 72.784389) (xy 197.267499 72.782702) (xy 197.205405 72.770204) + (xy 197.174964 72.747784) (xy 195.972275 71.545095) (xy 195.938249 71.482783) (xy 195.943314 71.411968) + (xy 195.972275 71.366905) (xy 198.207975 69.131205) (xy 198.270287 69.097179) (xy 198.29707 69.0943) + (xy 204.984634 69.0943) (xy 204.984637 69.0943) (xy 205.025993 69.08486) (xy 205.032956 69.083676) + (xy 205.075121 69.078926) (xy 205.115169 69.064911) (xy 205.121955 69.062956) (xy 205.163316 69.053517) + (xy 205.201547 69.035105) (xy 205.208051 69.03241) (xy 205.24811 69.018394) (xy 205.284041 68.995816) + (xy 205.290194 68.992415) (xy 205.32844 68.973998) (xy 205.361618 68.947537) (xy 205.367351 68.943469) + (xy 205.403293 68.920887) (xy 205.532887 68.791293) (xy 205.550607 68.773573) (xy 205.550608 68.77357) + (xy 210.580385 63.743795) (xy 210.580385 63.743794) (xy 210.598101 63.726079) (xy 210.598105 63.726074) + (xy 210.612887 63.711293) (xy 210.635464 63.67536) (xy 210.639537 63.669618) (xy 210.665998 63.63644) + (xy 210.684415 63.598194) (xy 210.687816 63.592041) (xy 210.710394 63.55611) (xy 210.72441 63.516051) + (xy 210.727105 63.509547) (xy 210.745517 63.471316) (xy 210.754956 63.429955) (xy 210.756912 63.423168) + (xy 210.757862 63.420453) (xy 210.770926 63.383121) (xy 210.775676 63.340956) (xy 210.77686 63.333995) + (xy 210.781618 63.313149) (xy 210.7863 63.292637) (xy 210.7863 63.109363) (xy 210.7863 63.109362) + (xy 210.7863 46.645328) (xy 210.7863 46.645327) (xy 210.7863 46.599363) (xy 210.776856 46.557988) + (xy 210.775676 46.551041) (xy 210.770926 46.508879) (xy 210.756908 46.46882) (xy 210.754958 46.462052) + (xy 210.745517 46.420684) (xy 210.741369 46.412071) (xy 210.727109 46.382458) (xy 210.724404 46.375926) + (xy 210.71837 46.358684) (xy 210.710394 46.33589) (xy 210.687823 46.299968) (xy 210.68441 46.293794) + (xy 210.671858 46.267728) (xy 210.665998 46.255559) (xy 210.639544 46.222386) (xy 210.635453 46.216621) + (xy 210.612887 46.180707) (xy 210.612887 46.180706) (xy 210.483293 46.051113) (xy 209.374745 44.942565) + (xy 212.424456 44.942565) (xy 212.424456 54.859563) (xy 230.818962 54.859563) (xy 230.818962 44.942565) + (xy 212.424456 44.942565) (xy 209.374745 44.942565) (xy 207.996812 43.564632) (xy 206.774433 42.342252) + (xy 206.759656 42.327475) (xy 206.723732 42.304902) (xy 206.717966 42.300811) (xy 206.684801 42.274362) + (xy 206.646572 42.255951) (xy 206.64039 42.252535) (xy 206.60447 42.229966) (xy 206.604469 42.229965) + (xy 206.604468 42.229965) (xy 206.564429 42.215954) (xy 206.557898 42.213249) (xy 206.51968 42.194845) + (xy 206.519676 42.194843) (xy 206.478311 42.185401) (xy 206.471526 42.183446) (xy 206.455297 42.177767) + (xy 206.431486 42.169435) (xy 206.431481 42.169434) (xy 206.431479 42.169433) (xy 206.431475 42.169433) + (xy 206.389316 42.164681) (xy 206.382349 42.163497) (xy 206.341 42.15406) (xy 206.340997 42.15406) + (xy 206.295032 42.15406) (xy 170.453337 42.15406) (xy 170.270064 42.15406) (xy 170.270059 42.15406) + (xy 170.22871 42.163498) (xy 170.221743 42.164681) (xy 170.179577 42.169434) (xy 170.179574 42.169434) + (xy 170.139533 42.183445) (xy 170.132743 42.185402) (xy 170.091387 42.194842) (xy 170.09138 42.194844) + (xy 170.053156 42.21325) (xy 170.04663 42.215954) (xy 170.006591 42.229965) (xy 169.970664 42.252537) + (xy 169.96448 42.255955) (xy 169.926263 42.27436) (xy 169.89309 42.300812) (xy 169.887328 42.3049) + (xy 169.851406 42.327473) (xy 169.75939 42.419486) (xy 169.759373 42.419506) (xy 167.557285 44.621595) + (xy 167.494973 44.65562) (xy 167.46819 44.6585) (xy 167.12535 44.6585) (xy 167.064803 44.665009) + (xy 167.064795 44.665011) (xy 166.927797 44.71611) (xy 166.927792 44.716112) (xy 166.810738 44.803738) + (xy 166.71771 44.92801) (xy 166.716532 44.927128) (xy 166.673207 44.970455) (xy 166.603833 44.985548) + (xy 166.537312 44.960739) (xy 166.523724 44.948964) (xy 165.139392 43.564632) (xy 163.213375 41.638615) + (xy 163.180873 41.606113) (xy 163.144952 41.583542) (xy 163.139186 41.579451) (xy 163.106021 41.553002) + (xy 163.067792 41.534591) (xy 163.06161 41.531175) (xy 163.02569 41.508606) (xy 163.025689 41.508605) + (xy 163.025688 41.508605) (xy 162.985649 41.494594) (xy 162.979118 41.491889) (xy 162.9409 41.473485) + (xy 162.940896 41.473483) (xy 162.899531 41.464041) (xy 162.892746 41.462086) (xy 162.876517 41.456407) + (xy 162.852706 41.448075) (xy 162.852701 41.448074) (xy 162.852699 41.448073) (xy 162.852695 41.448073) + (xy 162.810536 41.443321) (xy 162.803569 41.442137) (xy 162.76222 41.4327) (xy 162.762217 41.4327) + (xy 162.716252 41.4327) (xy 143.2975 41.4327) (xy 143.229379 41.412698) (xy 143.182886 41.359042) + (xy 143.1715 41.3067) (xy 143.1715 40.927367) (xy 143.171499 40.92735) (xy 143.16499 40.866803) + (xy 143.164988 40.866795) (xy 143.113889 40.729797) (xy 143.113887 40.729792) (xy 143.026261 40.612738) + (xy 142.909207 40.525112) (xy 142.909202 40.52511) (xy 142.772204 40.474011) (xy 142.772196 40.474009) + (xy 142.711649 40.4675) (xy 142.711638 40.4675) (xy 140.074362 40.4675) (xy 140.07435 40.4675) (xy 140.013803 40.474009) + (xy 140.013795 40.474011) (xy 139.876797 40.52511) (xy 139.876792 40.525112) (xy 139.759738 40.612738) + (xy 139.672112 40.729792) (xy 139.67211 40.729797) (xy 139.621011 40.866795) (xy 139.621009 40.866803) + (xy 139.6145 40.92735) (xy 139.6145 43.564649) (xy 139.621009 43.625196) (xy 139.621011 43.625204) + (xy 139.67211 43.762202) (xy 139.672112 43.762207) (xy 139.759738 43.879261) (xy 139.876792 43.966887) + (xy 139.876794 43.966888) (xy 139.876796 43.966889) (xy 139.935875 43.988924) (xy 140.013795 44.017988) + (xy 140.013803 44.01799) (xy 140.07435 44.024499) (xy 140.074355 44.024499) (xy 140.074362 44.0245) + (xy 140.074368 44.0245) (xy 142.711632 44.0245) (xy 142.711638 44.0245) (xy 142.711645 44.024499) + (xy 142.711649 44.024499) (xy 142.772196 44.01799) (xy 142.772199 44.017989) (xy 142.772201 44.017989) + (xy 142.909204 43.966889) (xy 143.026261 43.879261) (xy 143.113889 43.762204) (xy 143.164989 43.625201) + (xy 143.1715 43.564638) (xy 143.1715 43.1853) (xy 143.191502 43.117179) (xy 143.245158 43.070686) + (xy 143.2975 43.0593) (xy 162.28151 43.0593) (xy 162.349631 43.079302) (xy 162.370605 43.096205) + (xy 165.822968 46.548567) (xy 165.822973 46.548574) (xy 165.970284 46.695885) (xy 165.970286 46.695886) + (xy 165.970287 46.695887) (xy 165.992145 46.709621) (xy 166.006201 46.718453) (xy 166.011966 46.722544) + (xy 166.045137 46.748996) (xy 166.04514 46.748998) (xy 166.072332 46.762093) (xy 166.083365 46.767406) + (xy 166.089538 46.770816) (xy 166.115119 46.78689) (xy 166.125466 46.793392) (xy 166.125467 46.793392) + (xy 166.12547 46.793394) (xy 166.165516 46.807407) (xy 166.172041 46.81011) (xy 166.183674 46.815712) + (xy 166.210265 46.828517) (xy 166.251626 46.837957) (xy 166.258402 46.839909) (xy 166.298459 46.853926) + (xy 166.298463 46.853926) (xy 166.298465 46.853927) (xy 166.320495 46.856408) (xy 166.340617 46.858675) + (xy 166.347572 46.859856) (xy 166.388943 46.8693) (xy 166.434908 46.8693) (xy 166.5395 46.8693) + (xy 166.607621 46.889302) (xy 166.654114 46.942958) (xy 166.664416 46.99032) (xy 166.66532 46.990272) + (xy 166.6655 46.993649) (xy 166.672009 47.054196) (xy 166.672011 47.054204) (xy 166.72311 47.191202) + (xy 166.723112 47.191207) (xy 166.810738 47.308261) (xy 166.927792 47.395887) (xy 166.927794 47.395888) + (xy 166.927796 47.395889) (xy 166.986875 47.417924) (xy 167.064795 47.446988) (xy 167.064803 47.44699) + (xy 167.12535 47.453499) (xy 167.125355 47.453499) (xy 167.125362 47.4535) (xy 167.125368 47.4535) + (xy 169.000632 47.4535) (xy 169.000638 47.4535) (xy 169.000645 47.453499) (xy 169.000649 47.453499) + (xy 169.061196 47.44699) (xy 169.061199 47.446989) (xy 169.061201 47.446989) (xy 169.198204 47.395889) + (xy 169.198305 47.395814) (xy 169.315261 47.308261) (xy 169.402887 47.191207) (xy 169.402887 47.191206) + (xy 169.402889 47.191204) (xy 169.453989 47.054201) (xy 169.453997 47.054132) (xy 169.460499 46.993649) + (xy 169.4605 46.993632) (xy 169.4605 46.056) (xy 170.026796 46.056) (xy 170.045587 46.318738) (xy 170.101579 46.576133) + (xy 170.193634 46.822942) (xy 170.319874 47.054132) (xy 170.403689 47.166096) (xy 171.326964 46.242822) + (xy 171.389277 46.208797) (xy 171.460092 46.213861) (xy 171.516928 46.256408) (xy 171.528325 46.274711) + (xy 171.538043 46.293784) (xy 171.540118 46.297856) (xy 171.631142 46.38888) (xy 171.631144 46.388881) + (xy 171.631146 46.388883) (xy 171.654282 46.400671) (xy 171.705897 46.449418) (xy 171.722964 46.518332) + (xy 171.700064 46.585534) (xy 171.686176 46.602033) (xy 170.762901 47.525308) (xy 170.762901 47.525309) + (xy 170.874863 47.609123) (xy 171.106057 47.735365) (xy 171.352866 47.82742) (xy 171.610261 47.883412) + (xy 171.873 47.902203) (xy 172.135738 47.883412) (xy 172.393133 47.82742) (xy 172.639942 47.735365) + (xy 172.871132 47.609126) (xy 172.983097 47.525308) (xy 172.059823 46.602035) (xy 172.025798 46.539722) + (xy 172.030862 46.468907) (xy 172.073409 46.412071) (xy 172.09171 46.400675) (xy 172.114854 46.388883) + (xy 172.205883 46.297854) (xy 172.217672 46.274715) (xy 172.266419 46.223101) (xy 172.335333 46.206034) + (xy 172.402535 46.228934) (xy 172.419035 46.242823) (xy 173.342308 47.166096) (xy 173.426126 47.054132) + (xy 173.552365 46.822942) (xy 173.64442 46.576133) (xy 173.700412 46.318738) (xy 173.719203 46.055999) + (xy 173.700412 45.793261) (xy 173.64442 45.535866) (xy 173.552365 45.289057) (xy 173.426123 45.057863) + (xy 173.342309 44.945901) (xy 173.342308 44.945901) (xy 172.419033 45.869176) (xy 172.356721 45.903201) + (xy 172.285905 45.898136) (xy 172.22907 45.855589) (xy 172.217673 45.837286) (xy 172.205883 45.814146) + (xy 172.205881 45.814144) (xy 172.20588 45.814142) (xy 172.114856 45.723118) (xy 172.109608 45.720444) + (xy 172.091713 45.711326) (xy 172.040099 45.662579) (xy 172.023033 45.593664) (xy 172.045934 45.526462) + (xy 172.059822 45.509964) (xy 172.983097 44.58669) (xy 172.871132 44.502874) (xy 172.639942 44.376634) + (xy 172.393133 44.284579) (xy 172.135738 44.228587) (xy 171.872999 44.209796) (xy 171.610261 44.228587) + (xy 171.352866 44.284579) (xy 171.106057 44.376634) (xy 170.874868 44.502873) (xy 170.762901 44.586689) + (xy 171.686176 45.509964) (xy 171.720201 45.572277) (xy 171.715137 45.643092) (xy 171.67259 45.699928) + (xy 171.654286 45.711325) (xy 171.631147 45.723115) (xy 171.631144 45.723117) (xy 171.540117 45.814144) + (xy 171.540115 45.814147) (xy 171.528325 45.837286) (xy 171.479576 45.888901) (xy 171.410661 45.905965) + (xy 171.34346 45.883063) (xy 171.326964 45.869176) (xy 170.403689 44.945901) (xy 170.319873 45.057868) + (xy 170.193634 45.289057) (xy 170.101579 45.535866) (xy 170.045587 45.793261) (xy 170.026796 46.056) + (xy 169.4605 46.056) (xy 169.4605 45.118367) (xy 169.460499 45.118352) (xy 169.457273 45.088346) + (xy 169.469878 45.018478) (xy 169.49345 44.985788) (xy 170.661677 43.817562) (xy 170.723987 43.783539) + (xy 170.75077 43.78066) (xy 205.86029 43.78066) (xy 205.928411 43.800662) (xy 205.949385 43.817565) + (xy 209.122795 46.990975) (xy 209.156821 47.053287) (xy 209.1597 47.08007) (xy 209.1597 62.811929) + (xy 209.139698 62.88005) (xy 209.122795 62.901024) (xy 207.177161 64.846658) (xy 207.114849 64.880684) + (xy 207.044034 64.875619) (xy 206.987198 64.833072) (xy 206.962387 64.766552) (xy 206.968462 64.71793) + (xy 206.977836 64.689641) (xy 206.977837 64.689633) (xy 206.9885 64.585278) (xy 206.9885 64.09) + (xy 206.046763 64.09) (xy 205.978642 64.069998) (xy 205.932149 64.016342) (xy 205.922045 63.946068) + (xy 205.922314 63.944289) (xy 205.922913 63.940507) (xy 205.939466 63.836) (xy 205.922314 63.727709) + (xy 205.931413 63.6573) (xy 205.977135 63.602986) (xy 206.044963 63.582013) (xy 206.046763 63.582) + (xy 206.9885 63.582) (xy 206.9885 63.086721) (xy 206.977837 62.982366) (xy 206.977837 62.982363) + (xy 206.921807 62.813273) (xy 206.921806 62.813271) (xy 206.828293 62.661662) (xy 206.828288 62.661656) + (xy 206.702343 62.535711) (xy 206.702337 62.535706) (xy 206.550728 62.442193) (xy 206.550726 62.442192) + (xy 206.381634 62.386162) (xy 206.277278 62.3755) (xy 205.782 62.3755) (xy 205.782 63.317237) (xy 205.761998 63.385358) + (xy 205.708342 63.431851) (xy 205.638068 63.441955) (xy 205.636338 63.441693) (xy 205.559985 63.4296) + (xy 205.496015 63.4296) (xy 205.419709 63.441685) (xy 205.3493 63.432586) (xy 205.294986 63.386865) + (xy 205.274013 63.319036) (xy 205.274 63.317237) (xy 205.274 62.3755) (xy 204.778722 62.3755) (xy 204.674366 62.386162) + (xy 204.674363 62.386162) (xy 204.505273 62.442192) (xy 204.505271 62.442193) (xy 204.353662 62.535706) + (xy 204.353656 62.535711) (xy 204.227711 62.661656) (xy 204.227706 62.661662) (xy 204.134193 62.813271) + (xy 204.134192 62.813273) (xy 204.078162 62.982363) (xy 204.078162 62.982366) (xy 204.0675 63.086721) + (xy 204.0675 63.582) (xy 205.009237 63.582) (xy 205.077358 63.602002) (xy 205.123851 63.655658) + (xy 205.133955 63.725932) (xy 205.133693 63.727662) (xy 205.116534 63.836) (xy 205.131294 63.929191) + (xy 205.133686 63.944289) (xy 205.124587 64.0147) (xy 205.078865 64.069014) (xy 205.011037 64.089987) + (xy 205.009237 64.09) (xy 204.0675 64.09) (xy 204.0675 64.585278) (xy 204.067499 64.585278) (xy 204.078162 64.689633) + (xy 204.078162 64.689636) (xy 204.134192 64.858726) (xy 204.134193 64.858728) (xy 204.227706 65.010337) + (xy 204.227711 65.010343) (xy 204.353656 65.136288) (xy 204.353662 65.136293) (xy 204.505271 65.229806) + (xy 204.505273 65.229807) (xy 204.674365 65.285837) (xy 204.778722 65.2965) (xy 205.274 65.2965) + (xy 205.274 64.354762) (xy 205.294002 64.286641) (xy 205.347658 64.240148) (xy 205.417932 64.230044) + (xy 205.419635 64.230302) (xy 205.466976 64.2378) (xy 205.496013 64.2424) (xy 205.496015 64.2424) + (xy 205.559987 64.2424) (xy 205.589023 64.2378) (xy 205.636289 64.230314) (xy 205.706699 64.239412) + (xy 205.761013 64.285133) (xy 205.781987 64.352961) (xy 205.782 64.354762) (xy 205.782 65.2965) + (xy 206.277278 65.2965) (xy 206.381633 65.285837) (xy 206.381641 65.285836) (xy 206.40993 65.276462) + (xy 206.480884 65.274021) (xy 206.541895 65.310329) (xy 206.57359 65.373857) (xy 206.565908 65.444437) + (xy 206.538658 65.485161) (xy 204.593025 67.430795) (xy 204.530713 67.46482) (xy 204.50393 67.4677) + (xy 197.816361 67.4677) (xy 197.785167 67.474819) (xy 197.774998 67.47714) (xy 197.76804 67.478322) + (xy 197.751307 67.480208) (xy 197.725882 67.483073) (xy 197.685834 67.497085) (xy 197.679043 67.499042) + (xy 197.637687 67.508482) (xy 197.637682 67.508483) (xy 197.599454 67.526891) (xy 197.592925 67.529595) + (xy 197.552894 67.543603) (xy 197.552888 67.543606) (xy 197.516972 67.566173) (xy 197.510787 67.569591) + (xy 197.472562 67.588) (xy 197.472557 67.588003) (xy 197.439392 67.614451) (xy 197.433633 67.618538) + (xy 197.419141 67.627644) (xy 197.397706 67.641113) (xy 197.347482 67.691336) (xy 197.34748 67.691339) + (xy 194.433025 70.605795) (xy 194.370713 70.639821) (xy 194.34393 70.6427) (xy 192.04738 70.6427) + (xy 191.979259 70.622698) (xy 191.950856 70.597688) (xy 191.948807 70.595246) (xy 191.870434 70.516873) + (xy 191.783754 70.430193) (xy 191.78375 70.43019) (xy 191.783745 70.430186) (xy 191.592548 70.296309) + (xy 191.592546 70.296308) (xy 191.381 70.197662) (xy 191.380995 70.19766) (xy 191.155535 70.137248) + (xy 191.016012 70.125041) (xy 190.923 70.116904) (xy 190.922998 70.116904) (xy 190.80781 70.126981) + (xy 190.738206 70.112991) (xy 190.707735 70.090555) (xy 189.435184 68.818004) (xy 188.161255 67.544075) + (xy 188.161254 67.544074) (xy 188.128753 67.511573) (xy 188.092832 67.489002) (xy 188.087066 67.484911) + (xy 188.086361 67.484349) (xy 188.077322 67.47714) (xy 188.053901 67.458462) (xy 188.015672 67.440051) + (xy 188.00949 67.436635) (xy 187.97357 67.414066) (xy 187.973569 67.414065) (xy 187.973568 67.414065) + (xy 187.933529 67.400054) (xy 187.926998 67.397349) (xy 187.88878 67.378945) (xy 187.888776 67.378943) + (xy 187.888772 67.378942) (xy 187.84741 67.369501) (xy 187.840626 67.367546) (xy 187.824397 67.361867) + (xy 187.800586 67.353535) (xy 187.800581 67.353534) (xy 187.800579 67.353533) (xy 187.800575 67.353533) + (xy 187.758416 67.348781) (xy 187.751449 67.347597) (xy 187.7101 67.33816) (xy 187.710097 67.33816) + (xy 187.664132 67.33816) (xy 184.499845 67.33816) (xy 184.431724 67.318158) (xy 184.385231 67.264502) + (xy 184.375127 67.194228) (xy 184.396631 67.139891) (xy 184.462691 67.045548) (xy 184.561339 66.833998) + (xy 184.621752 66.608532) (xy 184.642096 66.376) (xy 189.330591 66.376) (xy 189.350196 66.625107) + (xy 189.408528 66.868079) (xy 189.504154 67.098942) (xy 189.632084 67.307703) (xy 189.632085 67.307703) + (xy 190.376964 66.562823) (xy 190.439277 66.528798) (xy 190.510092 66.533862) (xy 190.566928 66.576409) + (xy 190.578326 66.594714) (xy 190.590117 66.617855) (xy 190.681142 66.70888) (xy 190.681144 66.708881) + (xy 190.681146 66.708883) (xy 190.704282 66.720671) (xy 190.755897 66.769418) (xy 190.772964 66.838332) + (xy 190.750064 66.905534) (xy 190.736175 66.922033) (xy 189.991294 67.666913) (xy 189.991295 67.666914) + (xy 190.200057 67.794845) (xy 190.43092 67.890471) (xy 190.673892 67.948803) (xy 190.923 67.968408) + (xy 191.172107 67.948803) (xy 191.415079 67.890471) (xy 191.645942 67.794845) (xy 191.854703 67.666914) + (xy 191.854704 67.666913) (xy 191.109824 66.922033) (xy 191.075798 66.859721) (xy 191.080863 66.788906) + (xy 191.12341 66.73207) (xy 191.141712 66.720674) (xy 191.164854 66.708883) (xy 191.255883 66.617854) + (xy 191.267671 66.594717) (xy 191.316416 66.543103) (xy 191.385331 66.526034) (xy 191.452533 66.548934) + (xy 191.469033 66.562824) (xy 192.213913 67.307704) (xy 192.213914 67.307703) (xy 192.341845 67.098942) + (xy 192.437471 66.868079) (xy 192.495803 66.625107) (xy 192.515408 66.376) (xy 192.495803 66.126892) + (xy 192.437471 65.88392) (xy 192.341845 65.653057) (xy 192.213914 65.444295) (xy 192.213913 65.444294) + (xy 191.469033 66.189175) (xy 191.406721 66.223201) (xy 191.335906 66.218136) (xy 191.27907 66.175589) + (xy 191.267672 66.157284) (xy 191.255883 66.134146) (xy 191.255881 66.134144) (xy 191.25588 66.134142) + (xy 191.164855 66.043117) (xy 191.141714 66.031326) (xy 191.090099 65.982578) (xy 191.073034 65.913662) + (xy 191.095935 65.846461) (xy 191.109823 65.829964) (xy 191.854703 65.085085) (xy 191.854703 65.085084) + (xy 191.645942 64.957154) (xy 191.415079 64.861528) (xy 191.172107 64.803196) (xy 190.923 64.783591) + (xy 190.673892 64.803196) (xy 190.43092 64.861528) (xy 190.200062 64.957152) (xy 189.991295 65.085085) + (xy 189.991295 65.085086) (xy 190.736175 65.829966) (xy 190.770201 65.892278) (xy 190.765136 65.963093) + (xy 190.722589 66.019929) (xy 190.704284 66.031327) (xy 190.681144 66.043117) (xy 190.590117 66.134144) + (xy 190.578327 66.157284) (xy 190.529578 66.208899) (xy 190.460663 66.225964) (xy 190.393462 66.203062) + (xy 190.376966 66.189175) (xy 189.632086 65.444295) (xy 189.632085 65.444295) (xy 189.504152 65.653062) + (xy 189.408528 65.88392) (xy 189.350196 66.126892) (xy 189.330591 66.376) (xy 184.642096 66.376) + (xy 184.621752 66.143468) (xy 184.561339 65.918002) (xy 184.462691 65.706452) (xy 184.4052 65.624346) + (xy 184.328813 65.515254) (xy 184.328809 65.515249) (xy 184.328807 65.515246) (xy 184.163754 65.350193) + (xy 184.081128 65.292337) (xy 184.0368 65.23688) (xy 184.0274 65.189125) (xy 184.0274 65.167947) + (xy 184.02873 65.149687) (xy 184.029083 65.147275) (xy 184.032022 65.127213) (xy 184.029571 65.099199) + (xy 184.02764 65.077115) (xy 184.0274 65.071623) (xy 184.0274 65.06381) (xy 184.023724 65.032366) + (xy 184.022751 65.021245) (xy 184.017264 64.958517) (xy 184.017261 64.958509) (xy 184.015779 64.951327) + (xy 184.015896 64.951302) (xy 184.01448 64.944917) (xy 184.014364 64.944945) (xy 184.012672 64.937807) + (xy 184.012672 64.937805) (xy 183.98732 64.868153) (xy 183.963999 64.797772) (xy 183.963996 64.797768) + (xy 183.960896 64.791118) (xy 183.961003 64.791068) (xy 183.958152 64.785179) (xy 183.958046 64.785233) + (xy 183.954756 64.778684) (xy 183.954754 64.778677) (xy 183.932254 64.744467) (xy 183.914023 64.716747) + (xy 183.8751 64.653645) (xy 183.875099 64.653643) (xy 183.875096 64.65364) (xy 183.870546 64.647885) + (xy 183.870638 64.647811) (xy 183.866506 64.642739) (xy 183.866416 64.642815) (xy 183.861702 64.637197) + (xy 183.807787 64.586331) (xy 182.534606 63.313149) (xy 184.001 63.313149) (xy 184.007509 63.373696) + (xy 184.007511 63.373704) (xy 184.05861 63.510702) (xy 184.058612 63.510707) (xy 184.146238 63.627761) + (xy 184.263292 63.715387) (xy 184.263294 63.715388) (xy 184.263296 63.715389) (xy 184.319765 63.736451) + (xy 184.400295 63.766488) (xy 184.400303 63.76649) (xy 184.46085 63.772999) (xy 184.460855 63.772999) + (xy 184.460862 63.773) (xy 184.460868 63.773) (xy 185.955132 63.773) (xy 185.955138 63.773) (xy 185.955145 63.772999) + (xy 185.955149 63.772999) (xy 186.015696 63.76649) (xy 186.015699 63.766489) (xy 186.015701 63.766489) + (xy 186.152704 63.715389) (xy 186.154711 63.713887) (xy 186.269761 63.627761) (xy 186.357387 63.510707) + (xy 186.357387 63.510706) (xy 186.357389 63.510704) (xy 186.404975 63.383123) (xy 186.408488 63.373704) + (xy 186.40849 63.373696) (xy 186.414999 63.313149) (xy 186.415 63.313132) (xy 186.415 61.818867) + (xy 186.414999 61.81885) (xy 186.40849 61.758303) (xy 186.408488 61.758295) (xy 186.361215 61.631554) + (xy 186.357389 61.621296) (xy 186.357388 61.621294) (xy 186.357387 61.621292) (xy 186.269761 61.504238) + (xy 186.152707 61.416612) (xy 186.152704 61.416611) (xy 186.014366 61.365012) (xy 185.957531 61.322465) + (xy 185.932721 61.255944) (xy 185.9324 61.246957) (xy 185.9324 57.203245) (xy 185.952402 57.135124) + (xy 185.969305 57.11415) (xy 188.011151 55.072305) (xy 188.073463 55.038279) (xy 188.100246 55.0354) + (xy 191.840754 55.0354) (xy 191.908875 55.055402) (xy 191.929849 55.072305) (xy 192.701695 55.844151) + (xy 192.735721 55.906463) (xy 192.7386 55.933246) (xy 192.7386 55.990037) (xy 192.718598 56.058158) + (xy 192.684872 56.093249) (xy 192.626829 56.133891) (xy 192.626827 56.133893) (xy 192.466488 56.294233) + (xy 192.466483 56.294239) (xy 192.444438 56.325722) (xy 192.38898 56.370049) (xy 192.318361 56.377357) + (xy 192.255001 56.345324) (xy 192.219017 56.284122) (xy 192.215949 56.266914) (xy 192.21239 56.233804) + (xy 192.212388 56.233795) (xy 192.183324 56.155875) (xy 192.161289 56.096796) (xy 192.161288 56.096794) + (xy 192.161287 56.096792) (xy 192.073661 55.979738) (xy 191.956607 55.892112) (xy 191.956602 55.89211) + (xy 191.819604 55.841011) (xy 191.819596 55.841009) (xy 191.759049 55.8345) (xy 191.759038 55.8345) + (xy 190.086962 55.8345) (xy 190.08695 55.8345) (xy 190.026403 55.841009) (xy 190.026395 55.841011) + (xy 189.889397 55.89211) (xy 189.889392 55.892112) (xy 189.772338 55.979738) (xy 189.684712 56.096792) + (xy 189.68471 56.096797) (xy 189.633611 56.233795) (xy 189.633609 56.233803) (xy 189.6271 56.29435) + (xy 189.6271 58.677649) (xy 189.633609 58.738196) (xy 189.633611 58.738204) (xy 189.68471 58.875202) + (xy 189.684712 58.875207) (xy 189.772338 58.992261) (xy 189.889392 59.079887) (xy 189.889394 59.079888) + (xy 189.889396 59.079889) (xy 189.948475 59.101924) (xy 190.026395 59.130988) (xy 190.026403 59.13099) + (xy 190.08695 59.137499) (xy 190.086955 59.137499) (xy 190.086962 59.1375) (xy 190.086968 59.1375) + (xy 191.759032 59.1375) (xy 191.759038 59.1375) (xy 191.759045 59.137499) (xy 191.759049 59.137499) + (xy 191.819596 59.13099) (xy 191.819599 59.130989) (xy 191.819601 59.130989) (xy 191.956604 59.079889) + (xy 191.977831 59.063999) (xy 192.073661 58.992261) (xy 192.161287 58.875207) (xy 192.161287 58.875206) + (xy 192.161289 58.875204) (xy 192.212389 58.738201) (xy 192.215949 58.705084) (xy 192.243116 58.639492) + (xy 192.301432 58.598999) (xy 192.372384 58.596463) (xy 192.433443 58.632688) (xy 192.444439 58.646278) + (xy 192.466406 58.677649) (xy 192.466491 58.67777) (xy 192.62683 58.838109) (xy 192.812575 58.968169) + (xy 193.018083 59.063999) (xy 193.23711 59.122687) (xy 193.463 59.14245) (xy 193.68889 59.122687) + (xy 193.907917 59.063999) (xy 194.113425 58.968169) (xy 194.29917 58.838109) (xy 194.459509 58.67777) + (xy 194.476567 58.653408) (xy 194.532021 58.60908) (xy 194.60264 58.601769) (xy 194.666001 58.633798) + (xy 194.686274 58.658335) (xy 194.760315 58.775421) (xy 194.760316 58.775422) (xy 194.926195 58.962661) + (xy 194.926205 58.96267) (xy 195.119972 59.120876) (xy 195.11998 59.120882) (xy 195.336614 59.245955) + (xy 195.33662 59.245958) (xy 195.570524 59.334668) (xy 195.748999 59.371102) (xy 195.749 59.371102) + (xy 195.749 58.004762) (xy 195.769002 57.936641) (xy 195.822658 57.890148) (xy 195.892932 57.880044) + (xy 195.894635 57.880302) (xy 195.941976 57.8878) (xy 195.971013 57.8924) (xy 195.971015 57.8924) + (xy 196.034987 57.8924) (xy 196.064023 57.8878) (xy 196.111289 57.880314) (xy 196.181699 57.889412) + (xy 196.236013 57.935133) (xy 196.256987 58.002961) (xy 196.257 58.004762) (xy 196.257 59.371538) + (xy 196.313928 59.364626) (xy 196.313931 59.364626) (xy 196.554201 59.295031) (xy 196.554205 59.29503) + (xy 196.780219 59.187785) (xy 196.780221 59.187783) (xy 196.986084 59.045688) (xy 196.986107 59.04567) + (xy 197.16651 58.87239) (xy 197.166512 58.872388) (xy 197.316797 58.672395) (xy 197.317451 58.67115) + (xy 197.317841 58.670746) (xy 197.319515 58.6681) (xy 197.320064 58.668447) (xy 197.366812 58.62012) + (xy 197.435926 58.603878) (xy 197.502849 58.62758) (xy 197.53224 58.657418) (xy 197.546406 58.677649) + (xy 197.546491 58.67777) (xy 197.70683 58.838109) (xy 197.764871 58.87875) (xy 197.809199 58.934205) + (xy 197.8186 58.981962) (xy 197.8186 60.599052) (xy 197.81727 60.617311) (xy 197.813978 60.639784) + (xy 197.813978 60.639789) (xy 197.81836 60.689881) (xy 197.8186 60.695374) (xy 197.8186 60.703191) + (xy 197.822277 60.734651) (xy 197.828736 60.80848) (xy 197.83022 60.815666) (xy 197.830104 60.815689) + (xy 197.831521 60.822079) (xy 197.831635 60.822052) (xy 197.833329 60.829199) (xy 197.858688 60.898871) + (xy 197.882003 60.969231) (xy 197.885103 60.975879) (xy 197.884996 60.975928) (xy 197.887848 60.981818) + (xy 197.887953 60.981766) (xy 197.891244 60.98832) (xy 197.93198 61.050256) (xy 197.970897 61.113352) + (xy 197.975454 61.119115) (xy 197.975361 61.119188) (xy 197.979493 61.12426) (xy 197.979584 61.124184) + (xy 197.984299 61.129804) (xy 198.038213 61.180669) (xy 199.209898 62.352355) (xy 199.243924 62.414667) + (xy 199.246324 62.45243) (xy 199.237883 62.548922) (xy 199.236389 62.566) (xy 199.254796 62.776394) + (xy 199.278302 62.86412) (xy 199.309457 62.980393) (xy 199.309459 62.980398) (xy 199.398714 63.171806) + (xy 199.506476 63.325707) (xy 199.519852 63.344809) (xy 199.669191 63.494148) (xy 199.842194 63.615286) + (xy 200.033605 63.704542) (xy 200.237606 63.759204) (xy 200.448 63.777611) (xy 200.658394 63.759204) + (xy 200.862395 63.704542) (xy 201.053806 63.615286) (xy 201.226809 63.494148) (xy 201.376148 63.344809) + (xy 201.497286 63.171806) (xy 201.586542 62.980395) (xy 201.641204 62.776394) (xy 201.659611 62.566) + (xy 201.641204 62.355606) (xy 201.586542 62.151605) (xy 201.497286 61.960195) (xy 201.497285 61.960194) + (xy 201.497284 61.960191) (xy 201.376152 61.787195) (xy 201.376149 61.787192) (xy 201.347252 61.758295) + (xy 201.226809 61.637852) (xy 201.199888 61.619002) (xy 201.053806 61.516714) (xy 200.862398 61.427459) + (xy 200.862393 61.427457) (xy 200.767716 61.402088) (xy 200.658394 61.372796) (xy 200.448 61.354389) + (xy 200.447998 61.354389) (xy 200.334429 61.364324) (xy 200.264824 61.350334) (xy 200.234356 61.3279) + (xy 199.304302 60.397845) (xy 199.270279 60.335536) (xy 199.2674 60.308753) (xy 199.2674 58.981962) + (xy 199.287402 58.913841) (xy 199.321127 58.87875) (xy 199.37917 58.838109) (xy 199.539509 58.67777) + (xy 199.669569 58.492025) (xy 199.765399 58.286517) (xy 199.824087 58.06749) (xy 199.8389 57.898178) + (xy 199.8389 57.073822) (xy 199.824087 56.90451) (xy 199.765399 56.685483) (xy 199.669569 56.479975) + (xy 199.539509 56.29423) (xy 199.37917 56.133891) (xy 199.33924 56.105932) (xy 199.193425 56.003831) + (xy 198.98792 55.908002) (xy 198.987915 55.908) (xy 198.881262 55.879423) (xy 198.76889 55.849313) + (xy 198.543 55.82955) (xy 198.31711 55.849313) (xy 198.29951 55.854029) (xy 198.098084 55.908) (xy 198.098079 55.908002) + (xy 197.892574 56.003831) (xy 197.706833 56.133888) (xy 197.706827 56.133893) (xy 197.546493 56.294227) + (xy 197.546486 56.294236) (xy 197.529431 56.318593) (xy 197.473974 56.362921) (xy 197.403354 56.370229) + (xy 197.339994 56.338198) (xy 197.319725 56.313664) (xy 197.245684 56.196578) (xy 197.245683 56.196577) + (xy 197.079804 56.009338) (xy 197.079794 56.009329) (xy 196.886027 55.851123) (xy 196.886019 55.851117) + (xy 196.669385 55.726044) (xy 196.669379 55.726041) (xy 196.435476 55.637332) (xy 196.257 55.600896) + (xy 196.257 56.967237) (xy 196.236998 57.035358) (xy 196.183342 57.081851) (xy 196.113068 57.091955) + (xy 196.111338 57.091693) (xy 196.034985 57.0796) (xy 195.971015 57.0796) (xy 195.894709 57.091685) + (xy 195.8243 57.082586) (xy 195.769986 57.036865) (xy 195.749013 56.969036) (xy 195.749 56.967237) + (xy 195.749 55.60046) (xy 195.748999 55.60046) (xy 195.692068 55.607373) (xy 195.451798 55.676968) + (xy 195.451794 55.676969) (xy 195.22578 55.784214) (xy 195.225778 55.784216) (xy 195.019915 55.926311) + (xy 195.019892 55.926329) (xy 194.839489 56.099609) (xy 194.839487 56.099611) (xy 194.6892 56.299607) + (xy 194.688536 56.300873) (xy 194.688141 56.30128) (xy 194.686485 56.3039) (xy 194.68594 56.303555) + (xy 194.639164 56.351892) (xy 194.570047 56.36812) (xy 194.503128 56.344404) (xy 194.473759 56.31458) + (xy 194.459515 56.294238) (xy 194.459511 56.294233) (xy 194.459509 56.29423) (xy 194.29917 56.133891) + (xy 194.299166 56.133888) (xy 194.241128 56.093249) (xy 194.1968 56.037792) (xy 194.1874 55.990037) + (xy 194.1874 55.642946) (xy 194.18873 55.624686) (xy 194.190508 55.612545) (xy 194.192022 55.602212) + (xy 194.188523 55.562215) (xy 194.18764 55.552117) (xy 194.1874 55.546624) (xy 194.1874 55.538811) + (xy 194.183722 55.50735) (xy 194.181072 55.477057) (xy 194.177263 55.433516) (xy 194.177262 55.433513) + (xy 194.177262 55.433511) (xy 194.175779 55.426328) (xy 194.175894 55.426304) (xy 194.174479 55.41992) + (xy 194.174365 55.419948) (xy 194.172672 55.412809) (xy 194.172672 55.412805) (xy 194.147313 55.343132) + (xy 194.123998 55.272771) (xy 194.123995 55.272767) (xy 194.120896 55.266119) (xy 194.121004 55.266068) + (xy 194.118154 55.260182) (xy 194.118047 55.260236) (xy 194.114757 55.253687) (xy 194.114754 55.253677) + (xy 194.093587 55.221495) (xy 194.074025 55.19175) (xy 194.0351 55.128643) (xy 194.030546 55.122884) + (xy 194.030638 55.12281) (xy 194.026507 55.117738) (xy 194.026416 55.117815) (xy 194.021699 55.112193) + (xy 193.99401 55.086071) (xy 193.967804 55.061347) (xy 192.749031 53.842575) (xy 192.73706 53.828723) + (xy 192.723496 53.810503) (xy 192.699348 53.79024) (xy 192.684976 53.77818) (xy 192.680923 53.774466) + (xy 192.6754 53.768942) (xy 192.650544 53.749289) (xy 192.643588 53.743452) (xy 192.593774 53.701653) + (xy 192.59377 53.701651) (xy 192.593769 53.70165) (xy 192.58764 53.697618) (xy 192.587703 53.69752) + (xy 192.582189 53.694006) (xy 192.582128 53.694106) (xy 192.575882 53.690254) (xy 192.50869 53.658922) + (xy 192.442451 53.625655) (xy 192.435553 53.623144) (xy 192.435592 53.623034) (xy 192.429406 53.620884) + (xy 192.42937 53.620995) (xy 192.42241 53.618688) (xy 192.349814 53.603698) (xy 192.277668 53.586599) + (xy 192.27038 53.585748) (xy 192.270393 53.58563) (xy 192.263889 53.584966) (xy 192.263879 53.585084) + (xy 192.256566 53.584444) (xy 192.256565 53.584444) (xy 192.18248 53.5866) (xy 187.809946 53.5866) + (xy 187.791686 53.58527) (xy 187.786047 53.584444) (xy 187.769212 53.581978) (xy 187.769211 53.581978) + (xy 187.719118 53.58636) (xy 187.713625 53.5866) (xy 187.705808 53.5866) (xy 187.674349 53.590277) + (xy 187.600516 53.596736) (xy 187.593331 53.59822) (xy 187.593307 53.598107) (xy 187.586909 53.599526) + (xy 187.586936 53.599638) (xy 187.579799 53.601329) (xy 187.532107 53.618688) (xy 187.510132 53.626686) + (xy 187.486678 53.634458) (xy 187.439769 53.650002) (xy 187.43312 53.653103) (xy 187.433071 53.652998) + (xy 187.427183 53.655848) (xy 187.427235 53.655952) (xy 187.420678 53.659245) (xy 187.35875 53.699974) + (xy 187.295641 53.738901) (xy 187.289885 53.743452) (xy 187.289813 53.743362) (xy 187.284745 53.74749) + (xy 187.28482 53.747579) (xy 187.279194 53.752299) (xy 187.228347 53.806195) (xy 184.739573 56.294968) + (xy 184.725724 56.306938) (xy 184.707502 56.320504) (xy 184.707499 56.320507) (xy 184.675181 56.359022) + (xy 184.67147 56.363071) (xy 184.665942 56.368599) (xy 184.665939 56.368603) (xy 184.646289 56.393455) + (xy 184.598653 56.450225) (xy 184.594623 56.456353) (xy 184.594526 56.456289) (xy 184.591009 56.461808) + (xy 184.591108 56.461869) (xy 184.587255 56.468116) (xy 184.587254 56.468117) (xy 184.587254 56.468118) + (xy 184.586376 56.47) (xy 184.555922 56.535309) (xy 184.522654 56.601551) (xy 184.520143 56.608451) + (xy 184.520033 56.608411) (xy 184.517886 56.61459) (xy 184.517996 56.614627) (xy 184.515688 56.62159) + (xy 184.500698 56.694185) (xy 184.483599 56.766331) (xy 184.482748 56.77362) (xy 184.482631 56.773606) + (xy 184.481966 56.780113) (xy 184.482084 56.780124) (xy 184.481444 56.787435) (xy 184.4836 56.861519) + (xy 184.4836 61.246957) (xy 184.463598 61.315078) (xy 184.409942 61.361571) (xy 184.401634 61.365012) + (xy 184.263295 61.416611) (xy 184.263292 61.416612) (xy 184.146238 61.504238) (xy 184.058612 61.621292) + (xy 184.05861 61.621297) (xy 184.007511 61.758295) (xy 184.007509 61.758303) (xy 184.001 61.81885) + (xy 184.001 63.313149) (xy 182.534606 63.313149) (xy 181.954032 62.732575) (xy 181.94206 62.718723) + (xy 181.928496 62.700503) (xy 181.928494 62.700501) (xy 181.889976 62.66818) (xy 181.885923 62.664466) + (xy 181.8804 62.658942) (xy 181.855544 62.639289) (xy 181.798774 62.591653) (xy 181.79877 62.591651) + (xy 181.798769 62.59165) (xy 181.79264 62.587618) (xy 181.792703 62.58752) (xy 181.787189 62.584006) + (xy 181.787128 62.584106) (xy 181.780882 62.580254) (xy 181.71369 62.548922) (xy 181.647451 62.515655) + (xy 181.640553 62.513144) (xy 181.640592 62.513034) (xy 181.634406 62.510884) (xy 181.63437 62.510995) + (xy 181.62741 62.508688) (xy 181.554814 62.493698) (xy 181.482668 62.476599) (xy 181.47538 62.475748) + (xy 181.475393 62.47563) (xy 181.468889 62.474966) (xy 181.468879 62.475084) (xy 181.461566 62.474444) + (xy 181.461565 62.474444) (xy 181.38748 62.4766) (xy 177.861769 62.4766) (xy 177.793648 62.456598) + (xy 177.747155 62.402942) (xy 177.737051 62.332668) (xy 177.766545 62.268088) (xy 177.772674 62.261505) + (xy 182.215297 57.818883) (xy 186.777974 53.256205) (xy 186.840287 53.222179) (xy 186.86707 53.2193) + (xy 197.999634 53.2193) (xy 197.999637 53.2193) (xy 198.040993 53.20986) (xy 198.047956 53.208676) + (xy 198.090121 53.203926) (xy 198.130169 53.189911) (xy 198.136955 53.187956) (xy 198.178316 53.178517) + (xy 198.216547 53.160105) (xy 198.223051 53.15741) (xy 198.26311 53.143394) (xy 198.299041 53.120816) + (xy 198.305194 53.117415) (xy 198.34344 53.098998) (xy 198.376618 53.072537) (xy 198.382351 53.068469) + (xy 198.418293 53.045887) (xy 198.547887 52.916293) (xy 198.607412 52.856768) (xy 198.607414 52.856764) + (xy 199.150385 52.313795) (xy 199.150385 52.313794) (xy 199.168108 52.296072) (xy 199.168108 52.29607) + (xy 199.182887 52.281293) (xy 199.205464 52.24536) (xy 199.209537 52.239618) (xy 199.235998 52.20644) + (xy 199.254408 52.168208) (xy 199.257819 52.162037) (xy 199.280394 52.12611) (xy 199.294411 52.086048) + (xy 199.297099 52.079559) (xy 199.315517 52.041315) (xy 199.324957 51.999954) (xy 199.326906 51.993183) + (xy 199.340926 51.953121) (xy 199.345675 51.910961) (xy 199.34686 51.903993) (xy 199.3563 51.862636) + (xy 199.3563 51.293169) (xy 199.376302 51.225048) (xy 199.393205 51.204074) (xy 199.393205 51.204073) + (xy 199.539509 51.05777) (xy 199.669569 50.872025) (xy 199.725514 50.752049) (xy 199.772431 50.698765) + (xy 199.839709 50.6793) (xy 204.349634 50.6793) (xy 204.349637 50.6793) (xy 204.390993 50.66986) + (xy 204.397956 50.668676) (xy 204.440121 50.663926) (xy 204.480169 50.649911) (xy 204.486955 50.647956) + (xy 204.528316 50.638517) (xy 204.566547 50.620105) (xy 204.573051 50.61741) (xy 204.61311 50.603394) + (xy 204.649041 50.580816) (xy 204.655194 50.577415) (xy 204.69344 50.558998) (xy 204.726618 50.532537) + (xy 204.732351 50.528469) (xy 204.768293 50.505887) (xy 204.897887 50.376293) (xy 204.957391 50.316789) + (xy 204.957404 50.316774) (xy 205.429965 49.844213) (xy 205.492275 49.810189) (xy 205.522497 49.808269) + (xy 205.522497 49.807611) (xy 205.527994 49.807611) (xy 205.527997 49.80761) (xy 205.528 49.807611) + (xy 205.738394 49.789204) (xy 205.942395 49.734542) (xy 206.133806 49.645286) (xy 206.306809 49.524148) + (xy 206.456148 49.374809) (xy 206.577286 49.201806) (xy 206.666542 49.010395) (xy 206.721204 48.806394) + (xy 206.739611 48.596) (xy 206.721204 48.385606) (xy 206.666542 48.181605) (xy 206.577286 47.990195) + (xy 206.577285 47.990194) (xy 206.577284 47.990191) (xy 206.456152 47.817195) (xy 206.456149 47.817192) + (xy 206.381575 47.742618) (xy 206.306809 47.667852) (xy 206.272743 47.643999) (xy 206.133806 47.546714) + (xy 205.942398 47.457459) (xy 205.942393 47.457457) (xy 205.847715 47.432088) (xy 205.738394 47.402796) + (xy 205.528 47.384389) (xy 205.527999 47.384389) (xy 205.435153 47.392512) (xy 205.317606 47.402796) + (xy 205.262944 47.417442) (xy 205.113606 47.457457) (xy 205.113601 47.457459) (xy 204.922191 47.546715) + (xy 204.749195 47.667847) (xy 204.749185 47.667856) (xy 204.599856 47.817185) (xy 204.599847 47.817195) + (xy 204.478715 47.990191) (xy 204.389459 48.181601) (xy 204.389457 48.181606) (xy 204.361693 48.285224) + (xy 204.334796 48.385606) (xy 204.318361 48.573468) (xy 204.316389 48.596005) (xy 204.316389 48.601501) + (xy 204.314699 48.601501) (xy 204.302209 48.663584) (xy 204.279785 48.694033) (xy 203.958025 49.015795) + (xy 203.895713 49.04982) (xy 203.868929 49.0527) (xy 199.839709 49.0527) (xy 199.771588 49.032698) + (xy 199.725514 48.97995) (xy 199.723652 48.975956) (xy 199.669569 48.859975) (xy 199.539509 48.67423) + (xy 199.37917 48.513891) (xy 199.347242 48.491535) (xy 199.193425 48.383831) (xy 198.98792 48.288002) + (xy 198.987915 48.288) (xy 198.841299 48.248715) (xy 198.76889 48.229313) (xy 198.543 48.20955) + (xy 198.31711 48.229313) (xy 198.258423 48.245038) (xy 198.098084 48.288) (xy 198.098079 48.288002) + (xy 197.892574 48.383831) (xy 197.706833 48.513888) (xy 197.706827 48.513893) (xy 197.546493 48.674227) + (xy 197.546488 48.674233) (xy 197.416431 48.859974) (xy 197.387195 48.922671) (xy 197.340277 48.975956) + (xy 197.272 48.995417) (xy 197.20404 48.974875) (xy 197.158805 48.922671) (xy 197.147006 48.897369) + (xy 197.129569 48.859975) (xy 196.999509 48.67423) (xy 196.83917 48.513891) (xy 196.807242 48.491535) + (xy 196.653425 48.383831) (xy 196.44792 48.288002) (xy 196.447915 48.288) (xy 196.301299 48.248715) + (xy 196.22889 48.229313) (xy 196.003 48.20955) (xy 195.77711 48.229313) (xy 195.718423 48.245038) + (xy 195.558084 48.288) (xy 195.558079 48.288002) (xy 195.352574 48.383831) (xy 195.166833 48.513888) + (xy 195.166827 48.513893) (xy 195.006493 48.674227) (xy 195.006488 48.674233) (xy 194.876431 48.859974) + (xy 194.847195 48.922671) (xy 194.800277 48.975956) (xy 194.732 48.995417) (xy 194.66404 48.974875) + (xy 194.618805 48.922671) (xy 194.607006 48.897369) (xy 194.589569 48.859975) (xy 194.459509 48.67423) + (xy 194.29917 48.513891) (xy 194.267242 48.491535) (xy 194.113425 48.383831) (xy 193.90792 48.288002) + (xy 193.907915 48.288) (xy 193.761299 48.248715) (xy 193.68889 48.229313) (xy 193.463 48.20955) + (xy 193.23711 48.229313) (xy 193.178423 48.245038) (xy 193.018084 48.288) (xy 193.018079 48.288002) + (xy 192.812574 48.383831) (xy 192.626833 48.513888) (xy 192.626827 48.513893) (xy 192.466493 48.674227) + (xy 192.466488 48.674233) (xy 192.336431 48.859974) (xy 192.307195 48.922671) (xy 192.260277 48.975956) + (xy 192.192 48.995417) (xy 192.12404 48.974875) (xy 192.078805 48.922671) (xy 192.067006 48.897369) + (xy 192.049569 48.859975) (xy 191.919509 48.67423) (xy 191.75917 48.513891) (xy 191.727242 48.491535) + (xy 191.573425 48.383831) (xy 191.36792 48.288002) (xy 191.367915 48.288) (xy 191.221299 48.248715) + (xy 191.14889 48.229313) (xy 190.923 48.20955) (xy 190.69711 48.229313) (xy 190.638423 48.245038) + (xy 190.478084 48.288) (xy 190.478079 48.288002) (xy 190.272574 48.383831) (xy 190.086833 48.513888) + (xy 190.086827 48.513893) (xy 189.926493 48.674227) (xy 189.926488 48.674233) (xy 189.796431 48.859974) + (xy 189.740486 48.97995) (xy 189.693569 49.033235) (xy 189.626291 49.0527) (xy 185.116361 49.0527) + (xy 185.085167 49.059819) (xy 185.074998 49.06214) (xy 185.06804 49.063322) (xy 185.051307 49.065208) + (xy 185.025882 49.068073) (xy 184.985834 49.082085) (xy 184.979043 49.084042) (xy 184.937687 49.093482) + (xy 184.937682 49.093483) (xy 184.899454 49.111891) (xy 184.892925 49.114595) (xy 184.852894 49.128603) + (xy 184.852888 49.128606) (xy 184.816972 49.151173) (xy 184.810787 49.154591) (xy 184.772562 49.173) + (xy 184.772557 49.173003) (xy 184.739392 49.199451) (xy 184.733633 49.203538) (xy 184.719141 49.212644) + (xy 184.697706 49.226113) (xy 184.647482 49.276336) (xy 184.64748 49.276339) (xy 178.558025 55.365795) + (xy 178.495713 55.399821) (xy 178.46893 55.4027) (xy 172.818847 55.4027) (xy 172.750726 55.382698) + (xy 172.729752 55.365795) (xy 172.651811 55.287854) (xy 172.651805 55.287849) (xy 172.478806 55.166714) + (xy 172.287398 55.077459) (xy 172.287393 55.077457) (xy 172.192716 55.052088) (xy 172.083394 55.022796) + (xy 171.873 55.004389) (xy 171.662606 55.022796) (xy 171.615567 55.0354) (xy 171.458606 55.077457) + (xy 171.458601 55.077459) (xy 171.267191 55.166715) (xy 171.094195 55.287847) (xy 171.094185 55.287856) + (xy 170.944856 55.437185) (xy 170.944851 55.437191) (xy 170.893465 55.510578) (xy 170.838007 55.554906) + (xy 170.767388 55.562215) (xy 170.704028 55.530184) (xy 170.668043 55.468982) (xy 170.664905 55.451114) + (xy 170.655837 55.362366) (xy 170.655837 55.362363) (xy 170.599807 55.193273) (xy 170.599806 55.193271) + (xy 170.506293 55.041662) (xy 170.506288 55.041656) (xy 170.380343 54.915711) (xy 170.380337 54.915706) + (xy 170.228728 54.822193) (xy 170.228726 54.822192) (xy 170.171041 54.803078) (xy 170.112669 54.762664) + (xy 170.085413 54.697108) (xy 170.097926 54.627223) (xy 170.121572 54.594384) (xy 170.261148 54.454809) + (xy 170.382286 54.281806) (xy 170.471542 54.090395) (xy 170.526204 53.886394) (xy 170.544611 53.676) + (xy 170.526204 53.465606) (xy 170.471542 53.261605) (xy 170.382286 53.070195) (xy 170.382285 53.070194) + (xy 170.382284 53.070191) (xy 170.261152 52.897195) (xy 170.261143 52.897185) (xy 170.183205 52.819247) + (xy 170.149179 52.756935) (xy 170.1463 52.730152) (xy 170.1463 50.03822) (xy 170.166302 49.970099) + (xy 170.219958 49.923606) (xy 170.2723 49.91222) (xy 182.388794 49.91222) (xy 182.388797 49.91222) + (xy 182.430153 49.90278) (xy 182.437116 49.901596) (xy 182.479281 49.896846) (xy 182.519329 49.882831) + (xy 182.526115 49.880876) (xy 182.567476 49.871437) (xy 182.605707 49.853025) (xy 182.612211 49.85033) + (xy 182.65227 49.836314) (xy 182.688201 49.813736) (xy 182.694354 49.810335) (xy 182.7326 49.791918) + (xy 182.765778 49.765457) (xy 182.771511 49.761389) (xy 182.807453 49.738807) (xy 182.937047 49.609213) + (xy 185.330409 47.215849) (xy 185.392719 47.181826) (xy 185.463534 47.18689) (xy 185.52037 47.229437) + (xy 185.537557 47.260913) (xy 185.58261 47.381702) (xy 185.582612 47.381707) (xy 185.670238 47.498761) + (xy 185.787292 47.586387) (xy 185.787294 47.586388) (xy 185.787296 47.586389) (xy 185.846375 47.608424) + (xy 185.924295 47.637488) (xy 185.924303 47.63749) (xy 185.98485 47.643999) (xy 185.984855 47.643999) + (xy 185.984862 47.644) (xy 185.984868 47.644) (xy 188.241132 47.644) (xy 188.241138 47.644) (xy 188.241145 47.643999) + (xy 188.241149 47.643999) (xy 188.301696 47.63749) (xy 188.301699 47.637489) (xy 188.301701 47.637489) + (xy 188.438704 47.586389) (xy 188.491704 47.546714) (xy 188.555761 47.498761) (xy 188.643387 47.381707) + (xy 188.643387 47.381706) (xy 188.643389 47.381704) (xy 188.694489 47.244701) (xy 188.700241 47.191204) + (xy 188.700999 47.184149) (xy 188.701 47.184132) (xy 188.701 44.927867) (xy 188.700999 44.92785) + (xy 188.69449 44.867303) (xy 188.694488 44.867295) (xy 188.643389 44.730297) (xy 188.643387 44.730292) + (xy 188.555761 44.613238) (xy 188.438707 44.525612) (xy 188.438702 44.52561) (xy 188.301704 44.474511) + (xy 188.301696 44.474509) (xy 188.241149 44.468) (xy 188.241138 44.468) (xy 185.984862 44.468) (xy 185.98485 44.468) + (xy 185.924303 44.474509) (xy 185.924295 44.474511) (xy 185.787297 44.52561) (xy 185.787292 44.525612) + (xy 185.670238 44.613238) (xy 185.582612 44.730292) (xy 185.58261 44.730297) (xy 185.531511 44.867295) + (xy 185.531509 44.867303) (xy 185.525 44.92785) (xy 185.525 45.1167) (xy 185.504998 45.184821) (xy 185.451342 45.231314) + (xy 185.399 45.2427) (xy 185.24844 45.2427) (xy 185.20709 45.252138) (xy 185.200123 45.253321) (xy 185.157958 45.258073) + (xy 185.117909 45.272086) (xy 185.111119 45.274043) (xy 185.069767 45.283482) (xy 185.069759 45.283485) + (xy 185.031536 45.30189) (xy 185.02501 45.304594) (xy 184.984971 45.318605) (xy 184.949044 45.341177) + (xy 184.94286 45.344595) (xy 184.904643 45.363) (xy 184.87147 45.389452) (xy 184.865708 45.39354) + (xy 184.829786 45.416113) (xy 184.779562 45.466336) (xy 184.77956 45.466339) (xy 181.997185 48.248715) + (xy 181.934873 48.282741) (xy 181.90809 48.28562) (xy 169.382215 48.28562) (xy 169.375158 48.285224) + (xy 169.333 48.280474) (xy 169.290841 48.285224) (xy 169.283785 48.28562) (xy 167.90786 48.28562) + (xy 167.86651 48.295058) (xy 167.859543 48.296241) (xy 167.817378 48.300993) (xy 167.777329 48.315006) + (xy 167.770539 48.316963) (xy 167.729187 48.326402) (xy 167.729179 48.326405) (xy 167.690956 48.34481) + (xy 167.68443 48.347514) (xy 167.644391 48.361525) (xy 167.608464 48.384097) (xy 167.60228 48.387515) + (xy 167.564063 48.40592) (xy 167.53089 48.432372) (xy 167.525128 48.43646) (xy 167.489207 48.459033) + (xy 167.489205 48.459034) (xy 167.419984 48.528252) (xy 167.419799 48.52844) (xy 163.414841 52.533398) + (xy 163.352529 52.567424) (xy 163.281714 52.562359) (xy 163.224878 52.519812) (xy 163.207692 52.48834) + (xy 163.179889 52.413796) (xy 163.179887 52.413793) (xy 163.179887 52.413792) (xy 163.092261 52.296738) + (xy 162.975207 52.209112) (xy 162.975202 52.20911) (xy 162.838204 52.158011) (xy 162.838196 52.158009) + (xy 162.777649 52.1515) (xy 162.777638 52.1515) (xy 160.648362 52.1515) (xy 160.64835 52.1515) (xy 160.587803 52.158009) + (xy 160.587795 52.158011) (xy 160.450797 52.20911) (xy 160.450792 52.209112) (xy 160.333738 52.296738) + (xy 160.246112 52.413792) (xy 160.24611 52.413797) (xy 160.195011 52.550795) (xy 160.195009 52.550803) + (xy 160.1885 52.61135) (xy 160.1885 54.740649) (xy 160.195009 54.801196) (xy 160.195011 54.801204) + (xy 160.24611 54.938202) (xy 160.246112 54.938207) (xy 160.333738 55.055261) (xy 160.450792 55.142887) + (xy 160.450794 55.142887) (xy 160.450796 55.142889) (xy 160.525337 55.170691) (xy 160.582171 55.213237) + (xy 160.606982 55.279757) (xy 160.591891 55.349131) (xy 160.570398 55.377841) (xy 154.486445 61.461795) + (xy 154.424133 61.495821) (xy 154.39735 61.4987) (xy 148.90868 61.4987) (xy 148.840559 61.478698) + (xy 148.819585 61.461796) (xy 147.929823 60.572035) (xy 147.895798 60.509722) (xy 147.900862 60.438907) + (xy 147.943409 60.382071) (xy 147.96171 60.370675) (xy 147.984854 60.358883) (xy 148.075883 60.267854) + (xy 148.087672 60.244715) (xy 148.136419 60.193101) (xy 148.205333 60.176034) (xy 148.272535 60.198934) + (xy 148.289035 60.212823) (xy 149.212308 61.136096) (xy 149.296126 61.024132) (xy 149.422365 60.792942) + (xy 149.51442 60.546133) (xy 149.570412 60.288738) (xy 149.589203 60.026) (xy 149.570412 59.763261) + (xy 149.51442 59.505866) (xy 149.422365 59.259057) (xy 149.296123 59.027863) (xy 149.212309 58.915901) + (xy 149.212308 58.915901) (xy 148.289033 59.839176) (xy 148.226721 59.873201) (xy 148.155905 59.868136) + (xy 148.09907 59.825589) (xy 148.087673 59.807286) (xy 148.075883 59.784146) (xy 148.075881 59.784144) + (xy 148.07588 59.784142) (xy 147.984856 59.693118) (xy 147.983459 59.692406) (xy 147.961713 59.681326) + (xy 147.910099 59.632579) (xy 147.893033 59.563664) (xy 147.915934 59.496462) (xy 147.929822 59.479964) + (xy 148.853097 58.55669) (xy 148.741132 58.472874) (xy 148.509942 58.346634) (xy 148.263133 58.254579) + (xy 148.005738 58.198587) (xy 147.743 58.179796) (xy 147.480261 58.198587) (xy 147.222866 58.254579) + (xy 146.976057 58.346634) (xy 146.744868 58.472873) (xy 146.632901 58.556689) (xy 147.556176 59.479964) + (xy 147.590201 59.542277) (xy 147.585137 59.613092) (xy 147.54259 59.669928) (xy 147.524286 59.681325) + (xy 147.501147 59.693115) (xy 147.501144 59.693117) (xy 147.410117 59.784144) (xy 147.410115 59.784147) + (xy 147.398325 59.807286) (xy 147.349576 59.858901) (xy 147.280661 59.875965) (xy 147.21346 59.853063) + (xy 147.196964 59.839176) (xy 146.273689 58.915901) (xy 146.189873 59.027868) (xy 146.063634 59.259057) + (xy 145.971579 59.505866) (xy 145.915587 59.763261) (xy 145.896796 60.026) (xy 145.915587 60.288738) + (xy 145.971579 60.546133) (xy 146.063634 60.792942) (xy 146.189874 61.024132) (xy 146.273689 61.136096) + (xy 147.196964 60.212822) (xy 147.259277 60.178797) (xy 147.330092 60.183861) (xy 147.386928 60.226408) + (xy 147.398325 60.244711) (xy 147.409406 60.266459) (xy 147.410118 60.267856) (xy 147.501142 60.35888) + (xy 147.501144 60.358881) (xy 147.501146 60.358883) (xy 147.524282 60.370671) (xy 147.575897 60.419418) + (xy 147.592964 60.488332) (xy 147.570064 60.555534) (xy 147.556176 60.572033) (xy 146.666415 61.461795) + (xy 146.604102 61.49582) (xy 146.577319 61.4987) (xy 146.254361 61.4987) (xy 146.230097 61.504238) + (xy 146.212998 61.50814) (xy 146.20604 61.509322) (xy 146.189307 61.511208) (xy 146.163882 61.514073) + (xy 146.123834 61.528085) (xy 146.117043 61.530042) (xy 146.075687 61.539482) (xy 146.075682 61.539483) + (xy 146.037454 61.557891) (xy 146.030925 61.560595) (xy 145.990894 61.574603) (xy 145.990888 61.574606) + (xy 145.954972 61.597173) (xy 145.948787 61.600591) (xy 145.910562 61.619) (xy 145.910557 61.619003) + (xy 145.877392 61.645451) (xy 145.871633 61.649538) (xy 145.857141 61.658644) (xy 145.835706 61.672113) + (xy 145.7437 61.764116) (xy 145.743674 61.764146) (xy 144.776023 62.731796) (xy 144.713713 62.76582) + (xy 144.68693 62.7687) (xy 140.666361 62.7687) (xy 140.635167 62.775819) (xy 140.624998 62.77814) + (xy 140.61804 62.779322) (xy 140.601347 62.781204) (xy 140.575882 62.784073) (xy 140.535834 62.798085) + (xy 140.529043 62.800042) (xy 140.487687 62.809482) (xy 140.487682 62.809483) (xy 140.449454 62.827891) + (xy 140.442925 62.830595) (xy 140.402894 62.844603) (xy 140.402888 62.844606) (xy 140.366972 62.867173) + (xy 140.360787 62.870591) (xy 140.322562 62.889) (xy 140.322553 62.889005) (xy 140.289391 62.915451) + (xy 140.283628 62.91954) (xy 140.24771 62.94211) (xy 140.247707 62.942112) (xy 140.247707 62.942113) + (xy 140.215205 62.974615) (xy 139.60782 63.582) (xy 136.648584 66.541234) (xy 136.648582 66.541237) + (xy 136.536714 66.653104) (xy 136.53671 66.653109) (xy 136.51414 66.689028) (xy 136.510051 66.694791) + (xy 136.483605 66.727953) (xy 136.4836 66.727962) (xy 136.465191 66.766187) (xy 136.461773 66.772372) + (xy 136.439206 66.808288) (xy 136.439203 66.808294) (xy 136.425195 66.848325) (xy 136.422491 66.854854) + (xy 136.404083 66.893082) (xy 136.404082 66.893087) (xy 136.394642 66.934443) (xy 136.392685 66.941234) + (xy 136.378673 66.981282) (xy 136.373924 67.023433) (xy 136.37274 67.030401) (xy 136.3633 67.071761) + (xy 136.3633 72.549784) (xy 136.362904 72.55684) (xy 136.358154 72.599) (xy 136.362363 72.63636) + (xy 136.362904 72.641156) (xy 136.3633 72.648214) (xy 136.3633 98.011503) (xy 136.3633 98.194777) + (xy 136.3633 98.194779) (xy 136.363299 98.194779) (xy 136.372737 98.236129) (xy 136.373921 98.243096) + (xy 136.378673 98.285255) (xy 136.378675 98.285266) (xy 136.383165 98.298097) (xy 136.392686 98.325306) + (xy 136.394641 98.33209) (xy 136.400677 98.358535) (xy 136.404083 98.373456) (xy 136.41164 98.38915) + (xy 136.422489 98.411678) (xy 136.425194 98.418209) (xy 136.43683 98.451462) (xy 136.439206 98.45825) + (xy 136.449449 98.474553) (xy 136.461773 98.494166) (xy 136.465191 98.500352) (xy 136.483602 98.538581) + (xy 136.510051 98.571746) (xy 136.514142 98.577512) (xy 136.536713 98.613433) (xy 136.569215 98.645935) + (xy 137.038711 99.11543) (xy 137.499429 99.576149) (xy 137.49943 99.576151) (xy 137.646744 99.723465) + (xy 137.646746 99.723466) (xy 137.646747 99.723467) (xy 137.669636 99.737849) (xy 137.682661 99.746033) + (xy 137.688426 99.750124) (xy 137.716288 99.772342) (xy 137.7216 99.776578) (xy 137.748792 99.789673) + (xy 137.759825 99.794986) (xy 137.765998 99.798396) (xy 137.791579 99.81447) (xy 137.801926 99.820972) + (xy 137.801928 99.820973) (xy 137.80193 99.820974) (xy 137.841976 99.834987) (xy 137.84849 99.837684) + (xy 137.886725 99.856097) (xy 137.92764 99.865435) (xy 137.928083 99.865537) (xy 137.934868 99.867491) + (xy 137.974919 99.881506) (xy 137.974924 99.881506) (xy 137.974926 99.881507) (xy 137.992192 99.883452) + (xy 138.017078 99.886255) (xy 138.024033 99.887436) (xy 138.065404 99.89688) (xy 138.111368 99.89688) + (xy 139.11567 99.89688) (xy 139.183791 99.916882) (xy 139.230284 99.970538) (xy 139.240388 100.040812) + (xy 139.210894 100.105392) (xy 139.181137 100.130537) (xy 138.948449 100.272036) (xy 138.876655 100.330444) + (xy 139.680137 101.133926) (xy 139.714162 101.196239) (xy 139.709098 101.267054) (xy 139.666551 101.32389) + (xy 139.660282 101.328293) (xy 139.629861 101.348301) (xy 139.502586 101.483206) (xy 139.50137 101.482058) + (xy 139.451766 101.519392) (xy 139.380963 101.524639) (xy 139.318564 101.490774) (xy 139.318334 101.490545) + (xy 138.516319 100.688529) (xy 138.516318 100.688529) (xy 138.382768 100.877728) (xy 138.254872 101.124556) + (xy 138.16178 101.38649) (xy 138.161778 101.386498) (xy 138.105221 101.65867) (xy 138.10522 101.658676) + (xy 138.086252 101.935995) (xy 138.086252 101.936004) (xy 138.10522 102.213323) (xy 138.105221 102.213329) + (xy 138.161778 102.485501) (xy 138.16178 102.485509) (xy 138.254872 102.747443) (xy 138.382763 102.994261) + (xy 138.382764 102.994262) (xy 138.516319 103.183468) (xy 139.32204 102.377747) (xy 139.384353 102.343722) + (xy 139.455168 102.348786) (xy 139.512004 102.391333) (xy 139.512121 102.391489) (xy 139.564968 102.462476) + (xy 139.67211 102.552378) (xy 139.711436 102.611486) (xy 139.712564 102.682473) (xy 139.680214 102.737994) + (xy 138.876655 103.541552) (xy 138.876655 103.541554) (xy 138.94844 103.599956) (xy 139.185965 103.744399) + (xy 139.440933 103.855147) (xy 139.440943 103.85515) (xy 139.708603 103.930146) (xy 139.708611 103.930148) + (xy 139.983993 103.967999) (xy 139.984007 103.968) (xy 140.261993 103.968) (xy 140.262006 103.967999) + (xy 140.537388 103.930148) (xy 140.537396 103.930146) (xy 140.805056 103.85515) (xy 140.805066 103.855147) + (xy 141.060034 103.744399) (xy 141.297558 103.599956) (xy 141.369343 103.541554) (xy 141.369343 103.541553) + (xy 140.565862 102.738073) (xy 140.531837 102.67576) (xy 140.536901 102.604945) (xy 140.579448 102.548109) + (xy 140.585706 102.543713) (xy 140.616138 102.523699) (xy 140.738378 102.394132) (xy 140.738378 102.394131) + (xy 140.743414 102.388794) (xy 140.744633 102.389944) (xy 140.794205 102.352618) (xy 140.865006 102.347354) + (xy 140.927414 102.381204) (xy 140.927665 102.381454) (xy 141.729679 103.183468) (xy 141.863231 102.99427) + (xy 141.863236 102.994261) (xy 141.991127 102.747443) (xy 142.084219 102.485509) (xy 142.084221 102.485501) + (xy 142.099406 102.412427) (xy 142.132849 102.3498) (xy 142.194842 102.315196) (xy 142.265702 102.319601) + (xy 142.311864 102.348965) (xy 142.409192 102.446293) (xy 142.426912 102.464013) (xy 142.426915 102.464015) + (xy 142.483317 102.520417) (xy 142.538787 102.575887) (xy 142.574701 102.598453) (xy 142.580466 102.602544) + (xy 142.613639 102.628998) (xy 142.628327 102.636071) (xy 142.651874 102.64741) (xy 142.658048 102.650823) + (xy 142.69397 102.673394) (xy 142.716764 102.68137) (xy 142.734006 102.687404) (xy 142.740538 102.690109) + (xy 142.770219 102.704402) (xy 142.778764 102.708517) (xy 142.820132 102.717958) (xy 142.8269 102.719908) + (xy 142.866959 102.733926) (xy 142.909121 102.738676) (xy 142.916068 102.739856) (xy 142.957443 102.7493) + (xy 143.003408 102.7493) (xy 147.764841 102.7493) (xy 147.832962 102.769302) (xy 147.879455 102.822958) + (xy 147.889559 102.893232) (xy 147.860065 102.957812) (xy 147.813059 102.991709) (xy 147.655062 103.057152) + (xy 147.446295 103.185085) (xy 147.446295 103.185086) (xy 148.191175 103.929966) (xy 148.225201 103.992278) + (xy 148.220136 104.063093) (xy 148.177589 104.119929) (xy 148.159284 104.131327) (xy 148.136144 104.143117) + (xy 148.045117 104.234144) (xy 148.033327 104.257284) (xy 147.984578 104.308899) (xy 147.915663 104.325964) + (xy 147.848462 104.303062) (xy 147.831966 104.289175) (xy 147.087086 103.544295) (xy 147.087085 103.544295) + (xy 146.959152 103.753062) (xy 146.863528 103.98392) (xy 146.805196 104.226892) (xy 146.785591 104.475999) + (xy 146.805196 104.725107) (xy 146.863528 104.968079) (xy 146.959154 105.198942) (xy 147.087084 105.407703) + (xy 147.087085 105.407703) (xy 147.831964 104.662823) (xy 147.894277 104.628798) (xy 147.965092 104.633862) + (xy 148.021928 104.676409) (xy 148.033326 104.694714) (xy 148.045117 104.717855) (xy 148.136142 104.80888) + (xy 148.136144 104.808881) (xy 148.136146 104.808883) (xy 148.159282 104.820671) (xy 148.210897 104.869418) + (xy 148.227964 104.938332) (xy 148.205064 105.005534) (xy 148.191175 105.022033) (xy 147.446294 105.766913) + (xy 147.446295 105.766914) (xy 147.655057 105.894845) (xy 147.88592 105.990471) (xy 148.128892 106.048803) + (xy 148.377999 106.068408) (xy 148.529262 106.056503) (xy 148.598743 106.071099) (xy 148.649302 106.120941) + (xy 148.664889 106.190205) (xy 148.640554 106.256901) (xy 148.605299 106.289354) (xy 148.56064 106.316901) + (xy 148.554885 106.321452) (xy 148.554813 106.321362) (xy 148.549745 106.32549) (xy 148.54982 106.325579) + (xy 148.544194 106.330299) (xy 148.493347 106.384194) (xy 148.493347 106.384195) (xy 147.352846 107.524696) + (xy 147.290536 107.55872) (xy 147.263753 107.5616) (xy 142.502355 107.5616) (xy 142.434234 107.541598) + (xy 142.399142 107.50787) (xy 142.369981 107.466223) (xy 142.369978 107.46622) (xy 142.316911 107.413153) + (xy 142.212781 107.309023) (xy 142.212777 107.30902) (xy 142.212772 107.309016) (xy 142.030677 107.181512) + (xy 142.030675 107.181511) (xy 141.829199 107.087561) (xy 141.829193 107.087559) (xy 141.784707 107.075639) + (xy 141.614463 107.030022) (xy 141.393 107.010647) (xy 141.171537 107.030022) (xy 141.056463 107.060856) + (xy 140.956806 107.087559) (xy 140.956801 107.087561) (xy 140.755323 107.181512) (xy 140.573222 107.30902) + (xy 140.573216 107.309025) (xy 140.416025 107.466216) (xy 140.41602 107.466222) (xy 140.288512 107.648323) + (xy 140.194561 107.849801) (xy 140.194559 107.849806) (xy 140.177318 107.914152) (xy 140.137022 108.064537) + (xy 140.117647 108.286) (xy 140.137022 108.507463) (xy 140.174939 108.648971) (xy 140.194559 108.722193) + (xy 140.194561 108.722199) (xy 140.288511 108.923675) (xy 140.288512 108.923677) (xy 140.416016 109.105772) + (xy 140.416019 109.105776) (xy 140.416023 109.105781) (xy 140.573219 109.262977) (xy 140.573222 109.262979) + (xy 140.573227 109.262983) (xy 140.642951 109.311804) (xy 140.755323 109.390488) (xy 140.956804 109.48444) + (xy 141.171537 109.541978) (xy 141.393 109.561353) (xy 141.614463 109.541978) (xy 141.829196 109.48444) + (xy 142.030677 109.390488) (xy 142.212781 109.262977) (xy 142.369977 109.105781) (xy 142.399141 109.06413) + (xy 142.454599 109.019801) (xy 142.502355 109.0104) (xy 145.689953 109.0104) (xy 145.758074 109.030402) + (xy 145.804567 109.084058) (xy 145.814671 109.154332) (xy 145.785177 109.218912) (xy 145.779064 109.225478) + (xy 145.043243 109.961299) (xy 144.980933 109.995323) (xy 144.910117 109.990258) (xy 144.865055 109.961297) + (xy 144.752783 109.849025) (xy 144.752772 109.849016) (xy 144.570677 109.721512) (xy 144.570675 109.721511) + (xy 144.369199 109.627561) (xy 144.369193 109.627559) (xy 144.327074 109.616273) (xy 144.154463 109.570022) + (xy 143.933 109.550647) (xy 143.932999 109.550647) (xy 143.871825 109.555999) (xy 143.711537 109.570022) + (xy 143.60292 109.599126) (xy 143.496806 109.627559) (xy 143.496801 109.627561) (xy 143.295323 109.721512) + (xy 143.113222 109.84902) (xy 143.113216 109.849025) (xy 142.956025 110.006216) (xy 142.95602 110.006222) + (xy 142.828512 110.188323) (xy 142.734561 110.389801) (xy 142.734559 110.389806) (xy 142.719201 110.447124) + (xy 142.677022 110.604537) (xy 142.657647 110.826) (xy 142.677022 111.047463) (xy 142.721982 111.215256) + (xy 142.734559 111.262193) (xy 142.734561 111.262199) (xy 142.828511 111.463675) (xy 142.828512 111.463677) + (xy 142.956016 111.645772) (xy 142.956019 111.645776) (xy 142.956023 111.645781) (xy 143.113219 111.802977) + (xy 143.113222 111.802979) (xy 143.113227 111.802983) (xy 143.199603 111.863464) (xy 143.295323 111.930488) + (xy 143.496804 112.02444) (xy 143.711537 112.081978) (xy 143.933 112.101353) (xy 144.154463 112.081978) + (xy 144.369196 112.02444) (xy 144.570677 111.930488) (xy 144.752781 111.802977) (xy 144.909977 111.645781) + (xy 144.936096 111.60848) (xy 144.939142 111.60413) (xy 144.994599 111.559801) (xy 145.042355 111.5504) + (xy 145.141054 111.5504) (xy 145.159313 111.551729) (xy 145.181788 111.555022) (xy 145.213545 111.552243) + (xy 145.231881 111.55064) (xy 145.237374 111.5504) (xy 145.245189 111.5504) (xy 145.260919 111.548561) + (xy 145.276651 111.546722) (xy 145.350483 111.540263) (xy 145.350495 111.540258) (xy 145.357665 111.538779) + (xy 145.357689 111.538896) (xy 145.364082 111.537479) (xy 145.364055 111.537364) (xy 145.371189 111.535672) + (xy 145.371195 111.535672) (xy 145.440871 111.510311) (xy 145.511229 111.486998) (xy 145.51124 111.48699) + (xy 145.517884 111.483894) (xy 145.517935 111.484003) (xy 145.523809 111.481159) (xy 145.523756 111.481053) + (xy 145.530319 111.477756) (xy 145.53032 111.477754) (xy 145.530323 111.477754) (xy 145.560643 111.457811) + (xy 145.592257 111.437019) (xy 145.611716 111.425015) (xy 145.655357 111.398099) (xy 145.655362 111.398093) + (xy 145.661115 111.393546) (xy 145.661189 111.393639) (xy 145.666252 111.389515) (xy 145.666176 111.389424) + (xy 145.671798 111.384705) (xy 145.671798 111.384703) (xy 145.671805 111.3847) (xy 145.722668 111.330787) + (xy 146.289456 110.763999) (xy 146.863151 110.190305) (xy 146.925463 110.156279) (xy 146.952246 110.1534) + (xy 149.041754 110.1534) (xy 149.109875 110.173402) (xy 149.130849 110.190305) (xy 149.775695 110.835151) + (xy 149.809721 110.897463) (xy 149.8126 110.924246) (xy 149.8126 111.447645) (xy 149.792598 111.515766) + (xy 149.738942 111.562259) (xy 149.668668 111.572363) (xy 149.604088 111.542869) (xy 149.572406 111.500896) + (xy 149.543429 111.438758) (xy 149.537691 111.426452) (xy 149.461729 111.317967) (xy 149.403813 111.235254) + (xy 149.40381 111.23525) (xy 149.403807 111.235246) (xy 149.238754 111.070193) (xy 149.23875 111.07019) + (xy 149.238745 111.070186) (xy 149.047548 110.936309) (xy 149.047546 110.936308) (xy 148.836 110.837662) + (xy 148.835995 110.83766) (xy 148.610535 110.777248) (xy 148.436133 110.76199) (xy 148.378 110.756904) + (xy 148.377999 110.756904) (xy 148.145464 110.777248) (xy 147.920004 110.83766) (xy 147.919999 110.837662) + (xy 147.708453 110.936308) (xy 147.708451 110.936309) (xy 147.517254 111.070186) (xy 147.517243 111.070195) + (xy 147.352195 111.235243) (xy 147.352186 111.235254) (xy 147.218309 111.426451) (xy 147.218308 111.426453) + (xy 147.119662 111.637999) (xy 147.11966 111.638004) (xy 147.059248 111.863464) (xy 147.043169 112.047248) + (xy 147.038904 112.096) (xy 147.054389 112.272999) (xy 147.059096 112.326794) (xy 147.045107 112.396398) + (xy 147.022671 112.42687) (xy 146.844848 112.604695) (xy 146.782536 112.63872) (xy 146.755752 112.6416) + (xy 141.745247 112.6416) (xy 141.677126 112.621598) (xy 141.656152 112.604695) (xy 140.149715 111.098258) + (xy 140.115689 111.035946) (xy 140.113289 110.998182) (xy 140.128353 110.826) (xy 140.108978 110.604537) + (xy 140.05144 110.389804) (xy 139.957488 110.188324) (xy 139.829977 110.006219) (xy 139.672781 109.849023) + (xy 139.672777 109.84902) (xy 139.672772 109.849016) (xy 139.490677 109.721512) (xy 139.490675 109.721511) + (xy 139.289199 109.627561) (xy 139.289193 109.627559) (xy 139.247074 109.616273) (xy 139.074463 109.570022) + (xy 138.853 109.550647) (xy 138.631537 109.570022) (xy 138.52292 109.599126) (xy 138.416806 109.627559) + (xy 138.416801 109.627561) (xy 138.215323 109.721512) (xy 138.033222 109.84902) (xy 138.033216 109.849025) + (xy 137.876025 110.006216) (xy 137.87602 110.006222) (xy 137.748512 110.188323) (xy 137.654561 110.389801) + (xy 137.654559 110.389806) (xy 137.639201 110.447124) (xy 137.597022 110.604537) (xy 137.577647 110.826) + (xy 137.597022 111.047463) (xy 137.641982 111.215256) (xy 137.654559 111.262193) (xy 137.654561 111.262199) + (xy 137.748511 111.463675) (xy 137.748512 111.463677) (xy 137.876016 111.645772) (xy 137.876019 111.645776) + (xy 137.876023 111.645781) (xy 138.033219 111.802977) (xy 138.033222 111.802979) (xy 138.033227 111.802983) + (xy 138.119603 111.863464) (xy 138.215323 111.930488) (xy 138.416804 112.02444) (xy 138.631537 112.081978) + (xy 138.853 112.101353) (xy 138.853002 112.101352) (xy 138.853005 112.101353) (xy 138.932952 112.094358) + (xy 139.025182 112.086289) (xy 139.094786 112.100277) (xy 139.125258 112.122714) (xy 140.836965 113.834421) + (xy 140.848938 113.848275) (xy 140.862502 113.866496) (xy 140.901028 113.898824) (xy 140.905082 113.902538) + (xy 140.910596 113.908053) (xy 140.9106 113.908056) (xy 140.910603 113.908059) (xy 140.935434 113.927693) + (xy 140.992226 113.975347) (xy 140.992227 113.975347) (xy 140.992229 113.975349) (xy 140.99836 113.979382) + (xy 140.998296 113.979478) (xy 141.003813 113.982993) (xy 141.003875 113.982895) (xy 141.010115 113.986744) + (xy 141.076617 114.017754) (xy 141.077299 114.018072) (xy 141.143554 114.051347) (xy 141.143556 114.051347) + (xy 141.150453 114.053858) (xy 141.150412 114.05397) (xy 141.156585 114.056115) (xy 141.156623 114.056003) + (xy 141.163589 114.05831) (xy 141.163592 114.058312) (xy 141.220661 114.070095) (xy 141.236179 114.073299) + (xy 141.308329 114.0904) (xy 141.315618 114.091252) (xy 141.315604 114.091368) (xy 141.322111 114.092033) + (xy 141.322122 114.091915) (xy 141.329426 114.092553) (xy 141.329434 114.092555) (xy 141.4035 114.0904) + (xy 147.046054 114.0904) (xy 147.064313 114.091729) (xy 147.086788 114.095022) (xy 147.118545 114.092243) + (xy 147.136881 114.09064) (xy 147.142374 114.0904) (xy 147.150189 114.0904) (xy 147.165919 114.088561) + (xy 147.181651 114.086722) (xy 147.255483 114.080263) (xy 147.255495 114.080258) (xy 147.262665 114.078779) + (xy 147.262689 114.078896) (xy 147.269082 114.077479) (xy 147.269055 114.077364) (xy 147.276189 114.075672) + (xy 147.276195 114.075672) (xy 147.345871 114.050311) (xy 147.416229 114.026998) (xy 147.41624 114.02699) + (xy 147.422884 114.023894) (xy 147.422935 114.024003) (xy 147.428809 114.021159) (xy 147.428756 114.021053) + (xy 147.435319 114.017756) (xy 147.43532 114.017754) (xy 147.435323 114.017754) (xy 147.478525 113.989339) + (xy 147.497257 113.977019) (xy 147.516716 113.965015) (xy 147.560357 113.938099) (xy 147.560362 113.938093) + (xy 147.566115 113.933546) (xy 147.566189 113.933639) (xy 147.571252 113.929515) (xy 147.571176 113.929424) + (xy 147.576798 113.924705) (xy 147.576798 113.924703) (xy 147.576805 113.9247) (xy 147.627668 113.870787) + (xy 148.047128 113.451326) (xy 148.109436 113.417304) (xy 148.147197 113.414903) (xy 148.378 113.435096) + (xy 148.610532 113.414752) (xy 148.835998 113.354339) (xy 149.047548 113.255691) (xy 149.238754 113.121807) + (xy 149.403807 112.956754) (xy 149.537691 112.765548) (xy 149.54089 112.758689) (xy 149.553801 112.731) + (xy 149.58001 112.674794) (xy 149.626926 112.62151) (xy 149.695203 112.602049) (xy 149.763163 112.62259) + (xy 149.809229 112.676613) (xy 149.819724 112.71706) (xy 149.822736 112.75148) (xy 149.82422 112.758666) + (xy 149.824104 112.758689) (xy 149.825521 112.765079) (xy 149.825635 112.765052) (xy 149.827329 112.772199) + (xy 149.851969 112.839895) (xy 149.852688 112.841871) (xy 149.874715 112.908346) (xy 149.876003 112.912231) + (xy 149.879103 112.918879) (xy 149.878996 112.918928) (xy 149.881848 112.924818) (xy 149.881953 112.924766) + (xy 149.885244 112.93132) (xy 149.92598 112.993256) (xy 149.964897 113.056352) (xy 149.969454 113.062115) + (xy 149.969361 113.062188) (xy 149.973493 113.06726) (xy 149.973584 113.067184) (xy 149.978299 113.072804) + (xy 150.032213 113.123669) (xy 151.504965 114.596421) (xy 151.516938 114.610275) (xy 151.530502 114.628496) + (xy 151.569028 114.660824) (xy 151.573082 114.664538) (xy 151.578596 114.670053) (xy 151.5786 114.670056) + (xy 151.578603 114.670059) (xy 151.603434 114.689693) (xy 151.660226 114.737347) (xy 151.660227 114.737347) + (xy 151.660229 114.737349) (xy 151.66636 114.741382) (xy 151.666296 114.741478) (xy 151.671813 114.744993) + (xy 151.671875 114.744895) (xy 151.678115 114.748744) (xy 151.744617 114.779754) (xy 151.745299 114.780072) + (xy 151.811554 114.813347) (xy 151.811556 114.813347) (xy 151.818453 114.815858) (xy 151.818412 114.81597) + (xy 151.824585 114.818115) (xy 151.824623 114.818003) (xy 151.831589 114.82031) (xy 151.831592 114.820312) + (xy 151.904178 114.835298) (xy 151.904179 114.835299) (xy 151.976329 114.8524) (xy 151.983618 114.853252) + (xy 151.983604 114.853368) (xy 151.990111 114.854033) (xy 151.990122 114.853915) (xy 151.997426 114.854553) + (xy 151.997434 114.854555) (xy 152.0715 114.8524) (xy 160.635054 114.8524) (xy 160.653313 114.853729) + (xy 160.675788 114.857022) (xy 160.707545 114.854243) (xy 160.725881 114.85264) (xy 160.731374 114.8524) + (xy 160.739189 114.8524) (xy 160.754919 114.850561) (xy 160.770651 114.848722) (xy 160.844483 114.842263) + (xy 160.844495 114.842258) (xy 160.851665 114.840779) (xy 160.851689 114.840896) (xy 160.858082 114.839479) + (xy 160.858055 114.839364) (xy 160.865189 114.837672) (xy 160.865195 114.837672) (xy 160.934871 114.812311) + (xy 161.005229 114.788998) (xy 161.00524 114.78899) (xy 161.011884 114.785894) (xy 161.011935 114.786003) + (xy 161.017809 114.783159) (xy 161.017756 114.783053) (xy 161.024319 114.779756) (xy 161.02432 114.779754) + (xy 161.024323 114.779754) (xy 161.071469 114.748745) (xy 161.086257 114.739019) (xy 161.105716 114.727015) + (xy 161.149357 114.700099) (xy 161.149362 114.700093) (xy 161.155115 114.695546) (xy 161.155189 114.695639) + (xy 161.160252 114.691515) (xy 161.160176 114.691424) (xy 161.165798 114.686705) (xy 161.165798 114.686703) + (xy 161.165805 114.6867) (xy 161.216668 114.632787) (xy 162.45441 113.395046) (xy 162.516716 113.361025) + (xy 162.587531 113.366089) (xy 162.644367 113.408636) (xy 162.669111 113.474259) (xy 162.680196 113.615107) + (xy 162.738528 113.858079) (xy 162.834154 114.088942) (xy 162.962084 114.297703) (xy 162.962085 114.297703) + (xy 163.706964 113.552823) (xy 163.769277 113.518798) (xy 163.840092 113.523862) (xy 163.896928 113.566409) + (xy 163.908326 113.584714) (xy 163.920117 113.607855) (xy 164.011142 113.69888) (xy 164.011144 113.698881) + (xy 164.011146 113.698883) (xy 164.034282 113.710671) (xy 164.085897 113.759418) (xy 164.102964 113.828332) + (xy 164.080064 113.895534) (xy 164.066175 113.912033) (xy 163.321294 114.656913) (xy 163.321295 114.656914) + (xy 163.530057 114.784845) (xy 163.76092 114.880471) (xy 164.003892 114.938803) (xy 164.252999 114.958408) + (xy 164.502107 114.938803) (xy 164.745079 114.880471) (xy 164.975942 114.784845) (xy 165.184703 114.656914) + (xy 165.184704 114.656913) (xy 164.439824 113.912033) (xy 164.405798 113.849721) (xy 164.410863 113.778906) + (xy 164.45341 113.72207) (xy 164.471712 113.710674) (xy 164.494854 113.698883) (xy 164.585883 113.607854) + (xy 164.597671 113.584717) (xy 164.646416 113.533103) (xy 164.715331 113.516034) (xy 164.782533 113.538934) + (xy 164.799033 113.552824) (xy 165.543913 114.297704) (xy 165.543914 114.297703) (xy 165.671845 114.088942) + (xy 165.767471 113.858079) (xy 165.825803 113.615107) (xy 165.845408 113.366) (xy 165.825803 113.116892) + (xy 165.767471 112.87392) (xy 165.671845 112.643057) (xy 165.543914 112.434295) (xy 165.543913 112.434294) + (xy 164.799033 113.179175) (xy 164.736721 113.213201) (xy 164.665906 113.208136) (xy 164.60907 113.165589) + (xy 164.597674 113.147287) (xy 164.585883 113.124146) (xy 164.585881 113.124144) (xy 164.58588 113.124142) + (xy 164.494855 113.033117) (xy 164.471714 113.021326) (xy 164.420099 112.972578) (xy 164.403034 112.903662) + (xy 164.425935 112.836461) (xy 164.439823 112.819964) (xy 165.227919 112.03187) (xy 165.231388 112.035339) + (xy 165.251252 112.013418) (xy 165.317006 111.9949) (xy 169.461554 111.9949) (xy 169.479813 111.996229) + (xy 169.502288 111.999522) (xy 169.534045 111.996743) (xy 169.552381 111.99514) (xy 169.557874 111.9949) + (xy 169.565689 111.9949) (xy 169.581419 111.993061) (xy 169.597151 111.991222) (xy 169.670983 111.984763) + (xy 169.670995 111.984758) (xy 169.678165 111.983279) (xy 169.678189 111.983396) (xy 169.684582 111.981979) + (xy 169.684555 111.981864) (xy 169.691689 111.980172) (xy 169.691695 111.980172) (xy 169.761371 111.954811) + (xy 169.831729 111.931498) (xy 169.83174 111.93149) (xy 169.838384 111.928394) (xy 169.838435 111.928503) + (xy 169.844309 111.925659) (xy 169.844256 111.925553) (xy 169.850819 111.922256) (xy 169.85082 111.922254) + (xy 169.850823 111.922254) (xy 169.888421 111.897525) (xy 169.912757 111.881519) (xy 169.942022 111.863468) + (xy 169.975857 111.842599) (xy 169.975862 111.842593) (xy 169.981615 111.838046) (xy 169.981689 111.838139) + (xy 169.986752 111.834015) (xy 169.986676 111.833924) (xy 169.992298 111.829205) (xy 169.992298 111.829203) + (xy 169.992305 111.8292) (xy 170.043168 111.775287) (xy 175.51485 106.303606) (xy 177.343152 104.475305) + (xy 177.405464 104.441279) (xy 177.432247 104.4384) (xy 178.77333 104.4384) (xy 178.868181 104.4384) + (xy 178.936302 104.458402) (xy 178.982795 104.512058) (xy 178.992899 104.582332) (xy 178.971394 104.63667) + (xy 178.841309 104.822451) (xy 178.841308 104.822453) (xy 178.742662 105.033999) (xy 178.74266 105.034003) + (xy 178.682247 105.259472) (xy 178.682246 105.259477) (xy 178.672262 105.373592) (xy 178.646399 105.43971) + (xy 178.635837 105.451704) (xy 177.754573 106.332968) (xy 177.740724 106.344938) (xy 177.722502 106.358504) + (xy 177.722499 106.358507) (xy 177.690181 106.397022) (xy 177.68647 106.401071) (xy 177.680942 106.406599) + (xy 177.680939 106.406603) (xy 177.661289 106.431455) (xy 177.613653 106.488225) (xy 177.609623 106.494353) + (xy 177.609526 106.494289) (xy 177.606009 106.499808) (xy 177.606108 106.499869) (xy 177.602255 106.506116) + (xy 177.602254 106.506117) (xy 177.602254 106.506118) (xy 177.576887 106.560517) (xy 177.570922 106.573309) + (xy 177.537654 106.639551) (xy 177.535143 106.646451) (xy 177.535033 106.646411) (xy 177.532886 106.65259) + (xy 177.532996 106.652627) (xy 177.530688 106.65959) (xy 177.515698 106.732185) (xy 177.498599 106.804331) + (xy 177.497748 106.81162) (xy 177.497631 106.811606) (xy 177.496966 106.818113) (xy 177.497084 106.818124) + (xy 177.496444 106.825435) (xy 177.4986 106.899519) (xy 177.4986 108.288407) (xy 177.478598 108.356528) + (xy 177.424942 108.403021) (xy 177.354668 108.413125) (xy 177.33999 108.410114) (xy 177.232206 108.381234) + (xy 177.163394 108.362796) (xy 177.163395 108.362796) (xy 177.055551 108.353361) (xy 176.953 108.344389) + (xy 176.952999 108.344389) (xy 176.901427 108.348901) (xy 176.742606 108.362796) (xy 176.718986 108.369125) + (xy 176.538606 108.417457) (xy 176.538601 108.417459) (xy 176.347191 108.506715) (xy 176.174195 108.627847) + (xy 176.174185 108.627856) (xy 176.024858 108.777183) (xy 176.024849 108.777194) (xy 176.024372 108.777876) + (xy 176.024085 108.778105) (xy 176.02132 108.781401) (xy 176.020657 108.780844) (xy 175.968913 108.822202) + (xy 175.921163 108.8316) (xy 175.744947 108.8316) (xy 175.726687 108.83027) (xy 175.704213 108.826978) + (xy 175.654116 108.83136) (xy 175.648624 108.8316) (xy 175.640808 108.8316) (xy 175.615655 108.83454) + (xy 175.609366 108.835275) (xy 175.595684 108.836472) (xy 175.535513 108.841736) (xy 175.528329 108.84322) + (xy 175.528305 108.843105) (xy 175.52192 108.84452) (xy 175.521948 108.844635) (xy 175.5148 108.846329) + (xy 175.445153 108.871679) (xy 175.374769 108.895001) (xy 175.368123 108.898101) (xy 175.368074 108.897996) + (xy 175.362177 108.900851) (xy 175.362229 108.900955) (xy 175.355673 108.904247) (xy 175.293747 108.944976) + (xy 175.230642 108.9839) (xy 175.224886 108.988452) (xy 175.224814 108.988361) (xy 175.21974 108.992495) + (xy 175.219815 108.992584) (xy 175.214193 108.997301) (xy 175.163331 109.051212) (xy 172.203872 112.01067) + (xy 172.14156 112.044696) (xy 172.103796 112.047096) (xy 171.873 112.026904) (xy 171.640464 112.047248) + (xy 171.415004 112.10766) (xy 171.414999 112.107662) (xy 171.203453 112.206308) (xy 171.203451 112.206309) + (xy 171.012254 112.340186) (xy 171.012243 112.340195) (xy 170.847195 112.505243) (xy 170.847186 112.505254) + (xy 170.713309 112.696451) (xy 170.713308 112.696453) (xy 170.614662 112.907999) (xy 170.61466 112.908004) + (xy 170.554248 113.133464) (xy 170.533904 113.366) (xy 170.554248 113.598535) (xy 170.61466 113.823995) + (xy 170.614662 113.824) (xy 170.713308 114.035546) (xy 170.713309 114.035548) (xy 170.847186 114.226745) + (xy 170.84719 114.22675) (xy 170.847193 114.226754) (xy 171.012246 114.391807) (xy 171.01225 114.39181) + (xy 171.012254 114.391813) (xy 171.121346 114.4682) (xy 171.203452 114.525691) (xy 171.415002 114.624339) + (xy 171.640468 114.684752) (xy 171.873 114.705096) (xy 172.105532 114.684752) (xy 172.330998 114.624339) + (xy 172.542548 114.525691) (xy 172.733754 114.391807) (xy 172.898807 114.226754) (xy 173.032691 114.035548) + (xy 173.131339 113.823998) (xy 173.191752 113.598532) (xy 173.212096 113.366) (xy 173.191903 113.135199) + (xy 173.205892 113.065597) (xy 173.228326 113.035128) (xy 175.887656 110.375799) (xy 175.949966 110.341775) + (xy 176.020782 110.34684) (xy 176.065844 110.375801) (xy 176.174191 110.484148) (xy 176.347194 110.605286) + (xy 176.407399 110.63336) (xy 176.440521 110.648805) (xy 176.493806 110.695722) (xy 176.513267 110.763999) + (xy 176.492725 110.831959) (xy 176.438702 110.878025) (xy 176.387271 110.889) (xy 176.20585 110.889) + (xy 176.145303 110.895509) (xy 176.145295 110.895511) (xy 176.008297 110.94661) (xy 176.008292 110.946612) + (xy 175.891238 111.034238) (xy 175.803612 111.151292) (xy 175.80361 111.151297) (xy 175.752511 111.288295) + (xy 175.752509 111.288303) (xy 175.746 111.34885) (xy 175.746 112.843149) (xy 175.752509 112.903696) + (xy 175.752511 112.903704) (xy 175.80361 113.040702) (xy 175.803612 113.040707) (xy 175.891238 113.157761) + (xy 176.008292 113.245387) (xy 176.008294 113.245388) (xy 176.008296 113.245389) (xy 176.067375 113.267424) + (xy 176.145295 113.296488) (xy 176.145303 113.29649) (xy 176.20585 113.302999) (xy 176.205855 113.302999) + (xy 176.205862 113.303) (xy 176.205868 113.303) (xy 177.700132 113.303) (xy 177.700138 113.303) + (xy 177.700145 113.302999) (xy 177.700149 113.302999) (xy 177.760696 113.29649) (xy 177.760699 113.296489) + (xy 177.760701 113.296489) (xy 177.897704 113.245389) (xy 178.014761 113.157761) (xy 178.022606 113.147281) + (xy 178.102387 113.040707) (xy 178.102387 113.040706) (xy 178.102389 113.040704) (xy 178.150019 112.913004) + (xy 178.153488 112.903704) (xy 178.15349 112.903696) (xy 178.159999 112.843149) (xy 178.16 112.843132) + (xy 178.16 112.731) (xy 180.947904 112.731) (xy 180.968248 112.963535) (xy 181.02866 113.188995) + (xy 181.028662 113.189) (xy 181.127308 113.400546) (xy 181.127309 113.400548) (xy 181.261186 113.591745) + (xy 181.26119 113.59175) (xy 181.261193 113.591754) (xy 181.426246 113.756807) (xy 181.508871 113.814662) + (xy 181.553199 113.870117) (xy 181.5626 113.917874) (xy 181.5626 113.939052) (xy 181.56127 113.957311) + (xy 181.557978 113.979784) (xy 181.557978 113.979789) (xy 181.56236 114.029881) (xy 181.5626 114.035374) + (xy 181.5626 114.043191) (xy 181.566277 114.074651) (xy 181.572736 114.14848) (xy 181.57422 114.155666) + (xy 181.574104 114.155689) (xy 181.575521 114.162079) (xy 181.575635 114.162052) (xy 181.577329 114.169199) + (xy 181.598274 114.226745) (xy 181.602688 114.238871) (xy 181.609509 114.259457) (xy 181.626003 114.309231) + (xy 181.629103 114.315879) (xy 181.628996 114.315928) (xy 181.631848 114.321818) (xy 181.631953 114.321766) + (xy 181.635244 114.32832) (xy 181.67598 114.390256) (xy 181.714897 114.453352) (xy 181.719454 114.459115) + (xy 181.719361 114.459188) (xy 181.723493 114.46426) (xy 181.723584 114.464184) (xy 181.728299 114.469804) + (xy 181.7283 114.469805) (xy 181.782212 114.520668) (xy 182.445899 115.184355) (xy 182.479923 115.246666) + (xy 182.482324 115.284429) (xy 182.474347 115.375614) (xy 182.472389 115.398) (xy 182.490796 115.608394) + (xy 182.520088 115.717715) (xy 182.545457 115.812393) (xy 182.545459 115.812398) (xy 182.634714 116.003806) + (xy 182.701509 116.0992) (xy 182.755852 116.176809) (xy 182.905191 116.326148) (xy 183.078194 116.447286) + (xy 183.269605 116.536542) (xy 183.473606 116.591204) (xy 183.684 116.609611) (xy 183.894394 116.591204) + (xy 184.098395 116.536542) (xy 184.289806 116.447286) (xy 184.462809 116.326148) (xy 184.612148 116.176809) + (xy 184.733286 116.003806) (xy 184.822542 115.812395) (xy 184.877204 115.608394) (xy 184.895611 115.398) + (xy 184.877204 115.187606) (xy 184.822542 114.983605) (xy 184.733286 114.792195) (xy 184.733285 114.792194) + (xy 184.733284 114.792191) (xy 184.612152 114.619195) (xy 184.612149 114.619192) (xy 184.553033 114.560076) + (xy 184.462809 114.469852) (xy 184.46274 114.469804) (xy 184.289806 114.348714) (xy 184.098398 114.259459) + (xy 184.098393 114.259457) (xy 184.003715 114.234088) (xy 183.894394 114.204796) (xy 183.684 114.186389) + (xy 183.683998 114.186389) (xy 183.57043 114.196324) (xy 183.500825 114.182334) (xy 183.470355 114.159898) + (xy 183.196604 113.886147) (xy 183.162578 113.823835) (xy 183.167643 113.75302) (xy 183.196604 113.707957) + (xy 183.236692 113.667869) (xy 183.312807 113.591754) (xy 183.446691 113.400548) (xy 183.545339 113.188998) + (xy 183.605752 112.963532) (xy 183.626096 112.731) (xy 183.605752 112.498468) (xy 183.605296 112.496768) + (xy 183.588557 112.434294) (xy 183.545339 112.273002) (xy 183.446691 112.061452) (xy 183.353523 111.928394) + (xy 183.312813 111.870254) (xy 183.31281 111.87025) (xy 183.312807 111.870246) (xy 183.147754 111.705193) + (xy 183.14775 111.70519) (xy 183.147745 111.705186) (xy 182.956548 111.571309) (xy 182.956546 111.571308) + (xy 182.793243 111.495158) (xy 182.744998 111.472661) (xy 182.744996 111.47266) (xy 182.744995 111.47266) + (xy 182.519535 111.412248) (xy 182.345132 111.39699) (xy 182.287 111.391904) (xy 182.286999 111.391904) + (xy 182.054464 111.412248) (xy 181.829004 111.47266) (xy 181.828999 111.472662) (xy 181.617453 111.571308) + (xy 181.617451 111.571309) (xy 181.426254 111.705186) (xy 181.426243 111.705195) (xy 181.261195 111.870243) + (xy 181.261186 111.870254) (xy 181.127309 112.061451) (xy 181.127308 112.061453) (xy 181.028662 112.272999) + (xy 181.02866 112.273004) (xy 180.968248 112.498464) (xy 180.947904 112.731) (xy 178.16 112.731) + (xy 178.16 111.965646) (xy 178.180002 111.897525) (xy 178.1969 111.876555) (xy 178.691429 111.382025) + (xy 178.705268 111.370065) (xy 178.723497 111.356496) (xy 178.755846 111.317942) (xy 178.759515 111.31394) + (xy 178.765059 111.308397) (xy 178.784697 111.28356) (xy 178.832347 111.226774) (xy 178.832348 111.22677) + (xy 178.832351 111.226768) (xy 178.836385 111.220636) (xy 178.836484 111.220701) (xy 178.839993 111.215193) + (xy 178.839892 111.215131) (xy 178.843743 111.208887) (xy 178.843744 111.208884) (xy 178.843746 111.208882) + (xy 178.87508 111.141684) (xy 178.908347 111.075446) (xy 178.908347 111.075444) (xy 178.908349 111.075441) + (xy 178.91086 111.068543) (xy 178.910972 111.068584) (xy 178.913116 111.062416) (xy 178.913002 111.062379) + (xy 178.91531 111.055413) (xy 178.91531 111.055411) (xy 178.915312 111.055408) (xy 178.930303 110.982806) + (xy 178.9474 110.91067) (xy 178.9474 110.910664) (xy 178.948252 110.903381) (xy 178.948369 110.903394) + (xy 178.949033 110.89689) (xy 178.948916 110.89688) (xy 178.949554 110.889572) (xy 178.949556 110.889566) + (xy 178.947744 110.827332) (xy 178.965756 110.758662) (xy 179.018036 110.710628) (xy 179.087986 110.698484) + (xy 179.106296 110.701962) (xy 179.282606 110.749204) (xy 179.493 110.767611) (xy 179.703394 110.749204) + (xy 179.907395 110.694542) (xy 180.098806 110.605286) (xy 180.271809 110.484148) (xy 180.421148 110.334809) + (xy 180.421623 110.33413) (xy 180.421907 110.333902) (xy 180.424679 110.3306) (xy 180.425342 110.331157) + (xy 180.477076 110.289803) (xy 180.524836 110.2804) (xy 183.241054 110.2804) (xy 183.259313 110.281729) + (xy 183.281788 110.285022) (xy 183.313545 110.282243) (xy 183.331881 110.28064) (xy 183.337374 110.2804) + (xy 183.345189 110.2804) (xy 183.360919 110.278561) (xy 183.376651 110.276722) (xy 183.450483 110.270263) + (xy 183.450495 110.270258) (xy 183.457665 110.268779) (xy 183.457689 110.268896) (xy 183.464082 110.267479) + (xy 183.464055 110.267364) (xy 183.471189 110.265672) (xy 183.471195 110.265672) (xy 183.540871 110.240311) + (xy 183.611229 110.216998) (xy 183.61124 110.21699) (xy 183.617884 110.213894) (xy 183.617935 110.214003) + (xy 183.623809 110.211159) (xy 183.623756 110.211053) (xy 183.630319 110.207756) (xy 183.63032 110.207754) + (xy 183.630323 110.207754) (xy 183.660643 110.187811) (xy 183.692257 110.167019) (xy 183.723088 110.148002) + (xy 183.755357 110.128099) (xy 183.755362 110.128093) (xy 183.761115 110.123546) (xy 183.761189 110.123639) + (xy 183.766252 110.119515) (xy 183.766176 110.119424) (xy 183.771798 110.114705) (xy 183.771798 110.114703) + (xy 183.771805 110.1147) (xy 183.822668 110.060787) (xy 184.010554 109.872901) (xy 184.201152 109.682304) + (xy 184.263464 109.648279) (xy 184.290247 109.6454) (xy 185.158958 109.6454) (xy 185.227079 109.665402) + (xy 185.273572 109.719058) (xy 185.277013 109.727367) (xy 185.32861 109.865703) (xy 185.328612 109.865707) + (xy 185.416238 109.982761) (xy 185.533292 110.070387) (xy 185.533294 110.070388) (xy 185.533296 110.070389) + (xy 185.592375 110.092424) (xy 185.670295 110.121488) (xy 185.670303 110.12149) (xy 185.73085 110.127999) + (xy 185.730855 110.127999) (xy 185.730862 110.128) (xy 186.227354 110.128) (xy 186.295475 110.148002) + (xy 186.316449 110.164905) (xy 188.55167 112.400126) (xy 188.585696 112.462438) (xy 188.588096 112.500202) + (xy 188.56856 112.723497) (xy 188.567904 112.731) (xy 188.570401 112.759538) (xy 188.588248 112.963535) + (xy 188.64866 113.188995) (xy 188.648662 113.189) (xy 188.747308 113.400546) (xy 188.747309 113.400548) + (xy 188.881186 113.591745) (xy 188.881195 113.591756) (xy 189.017309 113.72787) (xy 189.051335 113.790182) + (xy 189.04627 113.860997) (xy 189.003723 113.917833) (xy 188.937203 113.942644) (xy 188.907479 113.941247) + (xy 188.885021 113.9375) (xy 188.642984 113.9375) (xy 188.404242 113.977338) (xy 188.404235 113.97734) + (xy 188.175315 114.055928) (xy 188.175313 114.05593) (xy 187.962447 114.171126) (xy 187.925271 114.200061) + (xy 187.92527 114.200061) (xy 188.577175 114.851966) (xy 188.611201 114.914278) (xy 188.606136 114.985093) + (xy 188.563589 115.041929) (xy 188.545284 115.053327) (xy 188.522144 115.065117) (xy 188.431117 115.156144) + (xy 188.419327 115.179284) (xy 188.370578 115.230899) (xy 188.301663 115.247964) (xy 188.234462 115.225062) + (xy 188.217966 115.211175) (xy 187.566867 114.560076) (xy 187.566866 114.560077) (xy 187.475131 114.700488) + (xy 187.475124 114.700502) (xy 187.377901 114.922147) (xy 187.377898 114.922154) (xy 187.318484 115.156778) + (xy 187.298495 115.398) (xy 187.318484 115.639221) (xy 187.377898 115.873845) (xy 187.377901 115.873852) + (xy 187.475126 116.095502) (xy 187.566866 116.235921) (xy 188.217964 115.584823) (xy 188.280277 115.550798) + (xy 188.351092 115.555862) (xy 188.407928 115.598409) (xy 188.419326 115.616714) (xy 188.431117 115.639855) + (xy 188.522142 115.73088) (xy 188.522144 115.730881) (xy 188.522146 115.730883) (xy 188.545282 115.742671) + (xy 188.596897 115.791418) (xy 188.613964 115.860332) (xy 188.591064 115.927534) (xy 188.577175 115.944033) + (xy 187.92527 116.595937) (xy 187.925271 116.595938) (xy 187.962437 116.624866) (xy 187.962447 116.624872) + (xy 188.073896 116.685186) (xy 188.124287 116.735199) (xy 188.139639 116.804516) (xy 188.115078 116.871129) + (xy 188.058403 116.913889) (xy 188.013927 116.922) (xy 137.276833 116.922) (xy 137.208712 116.901998) + (xy 137.162219 116.848342) (xy 137.158778 116.840033) (xy 137.144889 116.802796) (xy 137.144887 116.802792) + (xy 137.057261 116.685738) (xy 136.940207 116.598112) (xy 136.940202 116.59811) (xy 136.803204 116.547011) + (xy 136.803196 116.547009) (xy 136.742649 116.5405) (xy 136.742638 116.5405) (xy 135.96891 116.5405) + (xy 135.900789 116.520498) (xy 135.884599 116.508136) (xy 135.822715 116.452415) (xy 135.822713 116.452414) + (xy 135.82271 116.452411) (xy 135.822709 116.45241) (xy 135.660791 116.358928) (xy 135.660779 116.358922) + (xy 135.482961 116.301146) (xy 135.482958 116.301145) (xy 135.482956 116.301145) (xy 135.399252 116.292347) + (xy 135.343623 116.2865) (xy 135.34362 116.2865) (xy 135.25038 116.2865) (xy 135.250376 116.2865) + (xy 135.182172 116.293669) (xy 135.111044 116.301145) (xy 135.111042 116.301145) (xy 135.111038 116.301146) + (xy 134.93322 116.358922) (xy 134.933208 116.358928) (xy 134.77129 116.45241) (xy 134.771289 116.452411) + (xy 134.709401 116.508136) (xy 134.645393 116.538853) (xy 134.62509 116.5405) (xy 134.5345 116.5405) + (xy 134.466379 116.520498) (xy 134.419886 116.466842) (xy 134.4085 116.4145) (xy 134.4085 116.331232) + (xy 134.428502 116.263111) (xy 134.433972 116.255947) (xy 134.433787 116.255813) (xy 134.54757 116.099204) + (xy 134.547569 116.099204) (xy 134.547573 116.0992) (xy 134.623625 115.928385) (xy 134.6625 115.74549) + (xy 134.6625 115.55851) (xy 134.623625 115.375615) (xy 134.547573 115.2048) (xy 134.547571 115.204797) + (xy 134.54757 115.204795) (xy 134.433788 115.048188) (xy 134.435873 115.046672) (xy 134.410149 114.993093) + (xy 134.4085 114.972779) (xy 134.4085 114.206362) (xy 134.408332 114.204796) (xy 134.40199 114.145803) + (xy 134.401988 114.145795) (xy 134.368469 114.05593) (xy 134.350889 114.008796) (xy 134.350888 114.008794) + (xy 134.350887 114.008792) (xy 134.263261 113.891738) (xy 134.146207 113.804112) (xy 134.146202 113.80411) + (xy 134.009204 113.753011) (xy 134.001526 113.751197) (xy 134.002039 113.749024) (xy 133.946929 113.726191) + (xy 133.906444 113.667869) (xy 133.9 113.62809) (xy 133.9 60.026004) (xy 139.356252 60.026004) (xy 139.37522 60.303323) + (xy 139.375221 60.303329) (xy 139.431778 60.575501) (xy 139.43178 60.575509) (xy 139.524872 60.837443) + (xy 139.652763 61.084261) (xy 139.652764 61.084262) (xy 139.786319 61.273468) (xy 140.59204 60.467747) + (xy 140.654353 60.433722) (xy 140.725168 60.438786) (xy 140.782004 60.481333) (xy 140.782121 60.481489) + (xy 140.834968 60.552476) (xy 140.94211 60.642378) (xy 140.981436 60.701486) (xy 140.982564 60.772473) + (xy 140.950214 60.827994) (xy 140.146655 61.631553) (xy 140.146655 61.631554) (xy 140.21844 61.689956) + (xy 140.455965 61.834399) (xy 140.710933 61.945147) (xy 140.710943 61.94515) (xy 140.978603 62.020146) + (xy 140.978611 62.020148) (xy 141.253993 62.057999) (xy 141.254007 62.058) (xy 141.531993 62.058) + (xy 141.532006 62.057999) (xy 141.807388 62.020148) (xy 141.807396 62.020146) (xy 142.075056 61.94515) + (xy 142.075066 61.945147) (xy 142.330034 61.834399) (xy 142.567558 61.689956) (xy 142.639343 61.631554) + (xy 142.639343 61.631553) (xy 141.835862 60.828073) (xy 141.801837 60.76576) (xy 141.806901 60.694945) + (xy 141.849448 60.638109) (xy 141.855706 60.633713) (xy 141.886138 60.613699) (xy 142.008378 60.484132) + (xy 142.008378 60.484131) (xy 142.013414 60.478794) (xy 142.014633 60.479944) (xy 142.064205 60.442618) + (xy 142.135006 60.437354) (xy 142.197414 60.471204) (xy 142.197665 60.471454) (xy 142.999679 61.273468) + (xy 143.133231 61.08427) (xy 143.133236 61.084261) (xy 143.261127 60.837443) (xy 143.354219 60.575509) + (xy 143.354221 60.575501) (xy 143.410778 60.303329) (xy 143.410779 60.303323) (xy 143.429748 60.026004) + (xy 143.429748 60.025995) (xy 143.410779 59.748676) (xy 143.410778 59.74867) (xy 143.354221 59.476498) + (xy 143.354219 59.47649) (xy 143.261127 59.214556) (xy 143.133236 58.967738) (xy 143.133236 58.967737) + (xy 142.999679 58.77853) (xy 142.193957 59.584252) (xy 142.131645 59.618277) (xy 142.060829 59.613212) + (xy 142.003994 59.570665) (xy 142.003794 59.570397) (xy 141.951032 59.499524) (xy 141.951029 59.499522) + (xy 141.951029 59.499521) (xy 141.951027 59.499519) (xy 141.843889 59.409621) (xy 141.804562 59.350512) + (xy 141.803435 59.279525) (xy 141.835784 59.224003) (xy 142.639343 58.420445) (xy 142.567554 58.36204) + (xy 142.330034 58.2176) (xy 142.075066 58.106852) (xy 142.075056 58.106849) (xy 141.807396 58.031853) + (xy 141.807388 58.031851) (xy 141.532006 57.994) (xy 141.253993 57.994) (xy 140.978611 58.031851) + (xy 140.978603 58.031853) (xy 140.710943 58.106849) (xy 140.710933 58.106852) (xy 140.455962 58.217602) + (xy 140.218449 58.362036) (xy 140.146655 58.420444) (xy 140.950137 59.223926) (xy 140.984162 59.286239) + (xy 140.979098 59.357054) (xy 140.936551 59.41389) (xy 140.930282 59.418293) (xy 140.899861 59.438301) + (xy 140.772586 59.573206) (xy 140.77137 59.572058) (xy 140.721766 59.609392) (xy 140.650963 59.614639) + (xy 140.588564 59.580774) (xy 140.588334 59.580545) (xy 139.786319 58.778529) (xy 139.786318 58.778529) + (xy 139.652768 58.967728) (xy 139.524872 59.214556) (xy 139.43178 59.47649) (xy 139.431778 59.476498) + (xy 139.375221 59.74867) (xy 139.37522 59.748676) (xy 139.356252 60.025995) (xy 139.356252 60.026004) + (xy 133.9 60.026004) (xy 133.9 56.264649) (xy 139.6145 56.264649) (xy 139.621009 56.325196) (xy 139.621011 56.325204) + (xy 139.67211 56.462202) (xy 139.672112 56.462207) (xy 139.759738 56.579261) (xy 139.876792 56.666887) + (xy 139.876794 56.666888) (xy 139.876796 56.666889) (xy 139.926637 56.685479) (xy 140.013795 56.717988) + (xy 140.013803 56.71799) (xy 140.07435 56.724499) (xy 140.074355 56.724499) (xy 140.074362 56.7245) + (xy 140.074368 56.7245) (xy 142.711632 56.7245) (xy 142.711638 56.7245) (xy 142.711645 56.724499) + (xy 142.711649 56.724499) (xy 142.772196 56.71799) (xy 142.772199 56.717989) (xy 142.772201 56.717989) + (xy 142.909204 56.666889) (xy 142.909377 56.66676) (xy 143.026261 56.579261) (xy 143.113887 56.462207) + (xy 143.113887 56.462206) (xy 143.113889 56.462204) (xy 143.164989 56.325201) (xy 143.165088 56.324289) + (xy 143.171499 56.264649) (xy 143.1715 56.264632) (xy 143.1715 55.879423) (xy 143.191502 55.811302) + (xy 143.245158 55.764809) (xy 143.269456 55.756584) (xy 143.298927 55.749856) (xy 143.305879 55.748676) + (xy 143.348041 55.743926) (xy 143.388089 55.729911) (xy 143.394875 55.727956) (xy 143.436236 55.718517) + (xy 143.474467 55.700105) (xy 143.480971 55.69741) (xy 143.52103 55.683394) (xy 143.556961 55.660816) + (xy 143.563114 55.657415) (xy 143.60136 55.638998) (xy 143.634538 55.612537) (xy 143.640271 55.608469) + (xy 143.676213 55.585887) (xy 143.805807 55.456293) (xy 143.823517 55.438583) (xy 143.823523 55.438575) + (xy 144.735895 54.526204) (xy 144.798208 54.492179) (xy 144.824991 54.4893) (xy 147.652852 54.4893) + (xy 147.720973 54.509302) (xy 147.760283 54.549463) (xy 147.77584 54.574849) (xy 147.931682 54.757318) + (xy 148.114151 54.91316) (xy 148.318751 55.03854) (xy 148.540447 55.130369) (xy 148.773778 55.186387) + (xy 149.013 55.205214) (xy 149.252222 55.186387) (xy 149.485553 55.130369) (xy 149.707249 55.03854) + (xy 149.911849 54.91316) (xy 150.094318 54.757318) (xy 150.25016 54.574849) (xy 150.37554 54.370249) + (xy 150.467369 54.148553) (xy 150.523387 53.915222) (xy 150.542214 53.676) (xy 150.523387 53.436778) + (xy 150.467369 53.203447) (xy 150.37554 52.981751) (xy 150.25016 52.777151) (xy 150.094318 52.594682) + (xy 149.911849 52.43884) (xy 149.707249 52.31346) (xy 149.666879 52.296738) (xy 149.485551 52.22163) + (xy 149.325936 52.18331) (xy 149.252222 52.165613) (xy 149.013 52.146786) (xy 148.773778 52.165613) + (xy 148.540448 52.22163) (xy 148.318752 52.313459) (xy 148.114152 52.438839) (xy 147.931682 52.594682) + (xy 147.775839 52.777152) (xy 147.760285 52.802535) (xy 147.707637 52.850166) (xy 147.652852 52.8627) + (xy 144.344281 52.8627) (xy 144.313087 52.869819) (xy 144.302918 52.87214) (xy 144.29596 52.873322) + (xy 144.279227 52.875208) (xy 144.253802 52.878073) (xy 144.213754 52.892085) (xy 144.206963 52.894042) + (xy 144.165607 52.903482) (xy 144.165602 52.903483) (xy 144.127374 52.921891) (xy 144.120845 52.924595) + (xy 144.080814 52.938603) (xy 144.080808 52.938606) (xy 144.044892 52.961173) (xy 144.038707 52.964591) + (xy 144.000482 52.983) (xy 144.000477 52.983003) (xy 143.967312 53.009451) (xy 143.961553 53.013538) + (xy 143.947061 53.022644) (xy 143.925626 53.036113) (xy 143.925625 53.036114) (xy 143.856404 53.105332) + (xy 143.856219 53.10552) (xy 143.366092 53.595647) (xy 143.30378 53.629673) (xy 143.232965 53.624608) + (xy 143.176129 53.582061) (xy 143.158942 53.550585) (xy 143.127246 53.465607) (xy 143.113889 53.429796) + (xy 143.113888 53.429794) (xy 143.113887 53.429792) (xy 143.026261 53.312738) (xy 142.909207 53.225112) + (xy 142.909202 53.22511) (xy 142.772204 53.174011) (xy 142.772196 53.174009) (xy 142.711649 53.1675) + (xy 142.711638 53.1675) (xy 140.074362 53.1675) (xy 140.07435 53.1675) (xy 140.013803 53.174009) + (xy 140.013795 53.174011) (xy 139.876797 53.22511) (xy 139.876792 53.225112) (xy 139.759738 53.312738) + (xy 139.672112 53.429792) (xy 139.67211 53.429797) (xy 139.621011 53.566795) (xy 139.621009 53.566803) + (xy 139.6145 53.62735) (xy 139.6145 56.264649) (xy 133.9 56.264649) (xy 133.9 47.326004) (xy 139.356252 47.326004) + (xy 139.37522 47.603323) (xy 139.375221 47.603329) (xy 139.431778 47.875501) (xy 139.43178 47.875509) + (xy 139.524872 48.137443) (xy 139.652763 48.384261) (xy 139.652764 48.384262) (xy 139.786319 48.573468) + (xy 140.59204 47.767747) (xy 140.654353 47.733722) (xy 140.725168 47.738786) (xy 140.782004 47.781333) + (xy 140.782121 47.781489) (xy 140.834968 47.852476) (xy 140.94211 47.942378) (xy 140.981436 48.001486) + (xy 140.982564 48.072473) (xy 140.950214 48.127994) (xy 140.146655 48.931553) (xy 140.146655 48.931554) + (xy 140.21844 48.989956) (xy 140.455965 49.134399) (xy 140.710933 49.245147) (xy 140.710943 49.24515) + (xy 140.978603 49.320146) (xy 140.978611 49.320148) (xy 141.253993 49.357999) (xy 141.254007 49.358) + (xy 141.531993 49.358) (xy 141.532006 49.357999) (xy 141.807388 49.320148) (xy 141.807396 49.320146) + (xy 142.075056 49.24515) (xy 142.075066 49.245147) (xy 142.330034 49.134399) (xy 142.567558 48.989956) + (xy 142.639343 48.931554) (xy 142.639343 48.931553) (xy 141.835862 48.128073) (xy 141.801837 48.06576) + (xy 141.806901 47.994945) (xy 141.849448 47.938109) (xy 141.855706 47.933713) (xy 141.886138 47.913699) + (xy 142.008378 47.784132) (xy 142.008378 47.784131) (xy 142.013414 47.778794) (xy 142.014633 47.779944) + (xy 142.064205 47.742618) (xy 142.135006 47.737354) (xy 142.197414 47.771204) (xy 142.197665 47.771454) + (xy 142.999679 48.573468) (xy 143.133231 48.38427) (xy 143.133236 48.384261) (xy 143.261127 48.137443) + (xy 143.354219 47.875509) (xy 143.354221 47.875501) (xy 143.410778 47.603329) (xy 143.410779 47.603323) + (xy 143.429748 47.326004) (xy 143.429748 47.325995) (xy 143.410779 47.048676) (xy 143.410778 47.04867) + (xy 143.354221 46.776498) (xy 143.354219 46.77649) (xy 143.261127 46.514556) (xy 143.133236 46.267738) + (xy 143.133236 46.267737) (xy 142.999679 46.07853) (xy 142.193957 46.884252) (xy 142.131645 46.918277) + (xy 142.060829 46.913212) (xy 142.003994 46.870665) (xy 142.003794 46.870397) (xy 141.951032 46.799524) + (xy 141.951029 46.799522) (xy 141.951029 46.799521) (xy 141.951027 46.799519) (xy 141.843889 46.709621) + (xy 141.804562 46.650512) (xy 141.803435 46.579525) (xy 141.835784 46.524003) (xy 142.303789 46.055999) + (xy 146.086895 46.055999) (xy 146.107284 46.315072) (xy 146.16795 46.567762) (xy 146.267399 46.807854) + (xy 146.403186 47.029437) (xy 146.406486 47.033301) (xy 147.196964 46.242822) (xy 147.259277 46.208797) + (xy 147.330092 46.213861) (xy 147.386928 46.256408) (xy 147.398325 46.274711) (xy 147.408043 46.293784) + (xy 147.410118 46.297856) (xy 147.501142 46.38888) (xy 147.501144 46.388881) (xy 147.501146 46.388883) + (xy 147.524282 46.400671) (xy 147.575897 46.449418) (xy 147.592964 46.518332) (xy 147.570064 46.585534) + (xy 147.556176 46.602033) (xy 146.765697 47.392512) (xy 146.765697 47.392513) (xy 146.769561 47.395813) + (xy 146.991145 47.5316) (xy 147.231237 47.631049) (xy 147.483928 47.691715) (xy 147.483926 47.691715) + (xy 147.742999 47.712104) (xy 148.002072 47.691715) (xy 148.254762 47.631049) (xy 148.494854 47.5316) + (xy 148.716437 47.395814) (xy 148.720301 47.392513) (xy 147.929823 46.602035) (xy 147.895798 46.539722) + (xy 147.900862 46.468907) (xy 147.943409 46.412071) (xy 147.96171 46.400675) (xy 147.984854 46.388883) + (xy 148.075883 46.297854) (xy 148.087672 46.274715) (xy 148.136419 46.223101) (xy 148.205333 46.206034) + (xy 148.272535 46.228934) (xy 148.289035 46.242823) (xy 149.079513 47.033301) (xy 149.082814 47.029437) + (xy 149.2186 46.807854) (xy 149.318049 46.567762) (xy 149.378715 46.315072) (xy 149.399104 46.056) + (xy 149.378715 45.796927) (xy 149.318049 45.544237) (xy 149.2186 45.304145) (xy 149.082813 45.082561) + (xy 149.082813 45.08256) (xy 149.079513 45.078697) (xy 149.079512 45.078697) (xy 148.289033 45.869176) + (xy 148.226721 45.903201) (xy 148.155905 45.898136) (xy 148.09907 45.855589) (xy 148.087673 45.837286) + (xy 148.075883 45.814146) (xy 148.075881 45.814144) (xy 148.07588 45.814142) (xy 147.984856 45.723118) + (xy 147.979608 45.720444) (xy 147.961713 45.711326) (xy 147.910099 45.662579) (xy 147.893033 45.593664) + (xy 147.915934 45.526462) (xy 147.929822 45.509964) (xy 148.720301 44.719486) (xy 148.720301 44.719485) + (xy 148.716437 44.716186) (xy 148.494854 44.580399) (xy 148.254762 44.48095) (xy 148.002071 44.420284) + (xy 148.002073 44.420284) (xy 147.743 44.399895) (xy 147.483927 44.420284) (xy 147.231237 44.48095) + (xy 146.991145 44.580399) (xy 146.769565 44.716183) (xy 146.765697 44.719485) (xy 147.556176 45.509964) + (xy 147.590201 45.572277) (xy 147.585137 45.643092) (xy 147.54259 45.699928) (xy 147.524286 45.711325) + (xy 147.501147 45.723115) (xy 147.501144 45.723117) (xy 147.410117 45.814144) (xy 147.410115 45.814147) + (xy 147.398325 45.837286) (xy 147.349576 45.888901) (xy 147.280661 45.905965) (xy 147.21346 45.883063) + (xy 147.196964 45.869176) (xy 146.406485 45.078697) (xy 146.403183 45.082565) (xy 146.267399 45.304145) + (xy 146.16795 45.544237) (xy 146.107284 45.796927) (xy 146.086895 46.055999) (xy 142.303789 46.055999) + (xy 142.639343 45.720445) (xy 142.567554 45.66204) (xy 142.330034 45.5176) (xy 142.075066 45.406852) + (xy 142.075056 45.406849) (xy 141.807396 45.331853) (xy 141.807388 45.331851) (xy 141.532006 45.294) + (xy 141.253993 45.294) (xy 140.978611 45.331851) (xy 140.978603 45.331853) (xy 140.710943 45.406849) + (xy 140.710933 45.406852) (xy 140.455962 45.517602) (xy 140.218449 45.662036) (xy 140.146654 45.720444) + (xy 140.950137 46.523926) (xy 140.984162 46.586239) (xy 140.979098 46.657054) (xy 140.936551 46.71389) + (xy 140.930282 46.718293) (xy 140.899861 46.738301) (xy 140.772586 46.873206) (xy 140.77137 46.872058) + (xy 140.721766 46.909392) (xy 140.650963 46.914639) (xy 140.588564 46.880774) (xy 140.588334 46.880545) + (xy 139.786319 46.078529) (xy 139.786318 46.078529) (xy 139.652768 46.267728) (xy 139.524872 46.514556) + (xy 139.43178 46.77649) (xy 139.431778 46.776498) (xy 139.375221 47.04867) (xy 139.37522 47.048676) + (xy 139.356252 47.325995) (xy 139.356252 47.326004) (xy 133.9 47.326004) (xy 133.9 40.713909) (xy 133.920002 40.645788) + (xy 133.973658 40.599295) (xy 134.001714 40.591599) (xy 134.001526 40.590803) (xy 134.009198 40.588989) + (xy 134.009201 40.588989) (xy 134.146204 40.537889) (xy 134.163274 40.525111) (xy 134.263261 40.450261) + (xy 134.350887 40.333207) (xy 134.350887 40.333206) (xy 134.350889 40.333204) (xy 134.401989 40.196201) + (xy 134.4085 40.135638) (xy 134.4085 39.369232) (xy 134.428502 39.301111) (xy 134.433972 39.293947) + (xy 134.433787 39.293813) (xy 134.54757 39.137204) (xy 134.547569 39.137204) (xy 134.547573 39.1372) + (xy 134.623625 38.966385) (xy 134.6625 38.78349) (xy 134.6625 38.59651) (xy 134.6625 38.596509) + (xy 134.6625 38.596506) (xy 134.660734 38.588198) (xy 134.666135 38.517407) (xy 134.708951 38.460774) + (xy 134.775589 38.43628) (xy 134.78398 38.436) (xy 232.707 38.436) + ) + ) + (filled_polygon + (layer "B.Cu") + (island) + (pts + (xy 151.594412 102.769302) (xy 151.640485 102.822049) (xy 151.677829 102.902133) (xy 151.696431 102.942025) + (xy 151.800512 103.090669) (xy 151.826491 103.12777) (xy 151.98683 103.288109) (xy 152.172575 103.418169) + (xy 152.378083 103.513999) (xy 152.59711 103.572687) (xy 152.823 103.59245) (xy 153.04889 103.572687) + (xy 153.267917 103.513999) (xy 153.473425 103.418169) (xy 153.65917 103.288109) (xy 153.819509 103.12777) + (xy 153.949569 102.942025) (xy 153.978805 102.879327) (xy 154.025722 102.826043) (xy 154.093999 102.806582) + (xy 154.161959 102.827124) (xy 154.207194 102.879327) (xy 154.216051 102.89832) (xy 154.236431 102.942025) + (xy 154.340512 103.090669) (xy 154.366491 103.12777) (xy 154.52683 103.288109) (xy 154.712575 103.418169) + (xy 154.918083 103.513999) (xy 155.13711 103.572687) (xy 155.363 103.59245) (xy 155.58889 103.572687) + (xy 155.807917 103.513999) (xy 156.013425 103.418169) (xy 156.19917 103.288109) (xy 156.359509 103.12777) + (xy 156.489569 102.942025) (xy 156.518805 102.879327) (xy 156.565722 102.826043) (xy 156.633999 102.806582) + (xy 156.701959 102.827124) (xy 156.747194 102.879327) (xy 156.756051 102.89832) (xy 156.776431 102.942025) + (xy 156.880512 103.090669) (xy 156.906491 103.12777) (xy 157.06683 103.288109) (xy 157.124871 103.32875) + (xy 157.169199 103.384205) (xy 157.1786 103.431962) (xy 157.1786 106.485093) (xy 157.178493 106.488757) + (xy 157.174902 106.550401) (xy 157.174903 106.550406) (xy 157.176686 106.560517) (xy 157.1786 106.582397) + (xy 157.1786 108.060037) (xy 157.158598 108.128158) (xy 157.124872 108.163249) (xy 157.066829 108.203891) + (xy 157.066827 108.203893) (xy 156.906488 108.364233) (xy 156.776431 108.549974) (xy 156.747195 108.612671) + (xy 156.700277 108.665956) (xy 156.632 108.685417) (xy 156.56404 108.664875) (xy 156.518805 108.612671) + (xy 156.494555 108.560668) (xy 156.489569 108.549975) (xy 156.359509 108.36423) (xy 156.19917 108.203891) + (xy 156.194006 108.200275) (xy 156.141128 108.163249) (xy 156.0968 108.107792) (xy 156.0874 108.060037) + (xy 156.0874 107.712947) (xy 156.08873 107.694687) (xy 156.089043 107.692543) (xy 156.092022 107.672213) + (xy 156.089571 107.644199) (xy 156.08764 107.622115) (xy 156.0874 107.616623) (xy 156.0874 107.60881) + (xy 156.083724 107.577366) (xy 156.082093 107.55872) (xy 156.077264 107.503517) (xy 156.077261 107.503509) + (xy 156.075779 107.496327) (xy 156.075896 107.496302) (xy 156.07448 107.489917) (xy 156.074364 107.489945) + (xy 156.072672 107.482807) (xy 156.072672 107.482805) (xy 156.04732 107.413153) (xy 156.023999 107.342772) + (xy 156.023996 107.342768) (xy 156.020896 107.336118) (xy 156.021003 107.336068) (xy 156.018152 107.330179) + (xy 156.018046 107.330233) (xy 156.014756 107.323684) (xy 156.014754 107.323677) (xy 155.992788 107.290279) + (xy 155.974023 107.261747) (xy 155.9351 107.198645) (xy 155.935099 107.198643) (xy 155.935096 107.19864) + (xy 155.930546 107.192885) (xy 155.930638 107.192811) (xy 155.926506 107.187739) (xy 155.926416 107.187815) + (xy 155.921702 107.182197) (xy 155.90141 107.163053) (xy 155.867788 107.131332) (xy 155.157028 106.420571) + (xy 155.145057 106.406719) (xy 155.139404 106.399126) (xy 155.131496 106.388503) (xy 155.10557 106.366748) + (xy 155.092976 106.35618) (xy 155.088923 106.352466) (xy 155.0834 106.346942) (xy 155.058544 106.327289) + (xy 155.051588 106.321452) (xy 155.001774 106.279653) (xy 155.00177 106.279651) (xy 155.001769 106.27965) + (xy 154.99564 106.275618) (xy 154.995703 106.27552) (xy 154.990189 106.272006) (xy 154.990128 106.272106) + (xy 154.983882 106.268254) (xy 154.91669 106.236922) (xy 154.850451 106.203655) (xy 154.843553 106.201144) + (xy 154.843592 106.201034) (xy 154.837406 106.198884) (xy 154.83737 106.198995) (xy 154.83041 106.196688) + (xy 154.757814 106.181698) (xy 154.685668 106.164599) (xy 154.67838 106.163748) (xy 154.678393 106.16363) + (xy 154.671889 106.162966) (xy 154.671879 106.163084) (xy 154.664566 106.162444) (xy 154.664565 106.162444) + (xy 154.59048 106.1646) (xy 149.083141 106.1646) (xy 149.01502 106.144598) (xy 148.968527 106.090942) + (xy 148.958423 106.020668) (xy 148.987917 105.956088) (xy 149.034923 105.922191) (xy 149.100942 105.894845) + (xy 149.309703 105.766914) (xy 149.309704 105.766913) (xy 148.564824 105.022033) (xy 148.530798 104.959721) + (xy 148.535863 104.888906) (xy 148.57841 104.83207) (xy 148.596712 104.820674) (xy 148.619854 104.808883) + (xy 148.710883 104.717854) (xy 148.722671 104.694717) (xy 148.771416 104.643103) (xy 148.840331 104.626034) + (xy 148.907533 104.648934) (xy 148.924033 104.662824) (xy 149.668913 105.407704) (xy 149.668914 105.407703) + (xy 149.796845 105.198942) (xy 149.892471 104.968079) (xy 149.950803 104.725107) (xy 149.970408 104.475999) + (xy 149.950803 104.226892) (xy 149.892471 103.98392) (xy 149.796845 103.753057) (xy 149.668914 103.544295) + (xy 149.668913 103.544294) (xy 148.924033 104.289175) (xy 148.861721 104.323201) (xy 148.790906 104.318136) + (xy 148.73407 104.275589) (xy 148.722672 104.257284) (xy 148.710883 104.234146) (xy 148.710881 104.234144) + (xy 148.71088 104.234142) (xy 148.619855 104.143117) (xy 148.596714 104.131326) (xy 148.545099 104.082578) + (xy 148.528034 104.013662) (xy 148.550935 103.946461) (xy 148.564823 103.929964) (xy 149.309703 103.185085) + (xy 149.309703 103.185084) (xy 149.100942 103.057154) (xy 148.942941 102.991709) (xy 148.88766 102.947161) + (xy 148.865239 102.879798) (xy 148.882797 102.811006) (xy 148.934759 102.762628) (xy 148.991159 102.7493) + (xy 151.526291 102.7493) + ) + ) + (filled_polygon + (layer "B.Cu") + (island) + (pts + (xy 168.461821 49.932222) (xy 168.508314 49.985878) (xy 168.5197 50.03822) (xy 168.5197 52.730151) + (xy 168.499698 52.798272) (xy 168.482796 52.819246) (xy 168.404847 52.897195) (xy 168.283715 53.070191) + (xy 168.194459 53.261601) (xy 168.194457 53.261606) (xy 168.154442 53.410944) (xy 168.139796 53.465606) + (xy 168.121389 53.676) (xy 168.139796 53.886394) (xy 168.169088 53.995716) (xy 168.194457 54.090393) + (xy 168.194459 54.090398) (xy 168.283714 54.281806) (xy 168.404849 54.454805) (xy 168.404854 54.454811) + (xy 168.494985 54.544942) (xy 168.529011 54.607254) (xy 168.523946 54.678069) (xy 168.481399 54.734905) + (xy 168.418699 54.759384) (xy 168.352365 54.766162) (xy 168.352363 54.766162) (xy 168.183273 54.822192) + (xy 168.183271 54.822193) (xy 168.031662 54.915706) (xy 168.031656 54.915711) (xy 167.905711 55.041656) + (xy 167.905706 55.041662) (xy 167.812193 55.193271) (xy 167.812192 55.193273) (xy 167.756162 55.362363) + (xy 167.756162 55.362366) (xy 167.7455 55.466721) (xy 167.7455 55.962) (xy 168.687237 55.962) (xy 168.755358 55.982002) + (xy 168.801851 56.035658) (xy 168.811955 56.105932) (xy 168.811693 56.107662) (xy 168.794534 56.216) + (xy 168.810783 56.318593) (xy 168.811686 56.324289) (xy 168.802587 56.3947) (xy 168.756865 56.449014) + (xy 168.689037 56.469987) (xy 168.687237 56.47) (xy 167.7455 56.47) (xy 167.7455 56.965278) (xy 167.745499 56.965278) + (xy 167.756162 57.069633) (xy 167.756162 57.069636) (xy 167.812192 57.238726) (xy 167.812193 57.238728) + (xy 167.905706 57.390337) (xy 167.905711 57.390343) (xy 168.031656 57.516288) (xy 168.031662 57.516293) + (xy 168.183271 57.609806) (xy 168.183273 57.609807) (xy 168.352365 57.665837) (xy 168.456722 57.6765) + (xy 168.952 57.6765) (xy 168.951999 56.734762) (xy 168.972001 56.666641) (xy 169.025657 56.620148) + (xy 169.095931 56.610044) (xy 169.097657 56.610305) (xy 169.138153 56.616719) (xy 169.174013 56.6224) + (xy 169.174015 56.6224) (xy 169.237986 56.6224) (xy 169.25685 56.619412) (xy 169.314288 56.610314) + (xy 169.384698 56.619412) (xy 169.439012 56.665133) (xy 169.459986 56.732961) (xy 169.459999 56.734762) + (xy 169.46 57.6765) (xy 169.955278 57.6765) (xy 170.059633 57.665837) (xy 170.059636 57.665837) + (xy 170.228726 57.609807) (xy 170.228728 57.609806) (xy 170.380337 57.516293) (xy 170.380343 57.516288) + (xy 170.506288 57.390343) (xy 170.506293 57.390337) (xy 170.599806 57.238728) (xy 170.599807 57.238726) + (xy 170.655837 57.069636) (xy 170.655837 57.069633) (xy 170.664905 56.980885) (xy 170.691727 56.91515) + (xy 170.74983 56.874352) (xy 170.820767 56.871443) (xy 170.882016 56.907347) (xy 170.893465 56.921421) + (xy 170.944851 56.994809) (xy 171.022794 57.072752) (xy 171.05682 57.135064) (xy 171.059699 57.161847) + (xy 171.059699 59.00193) (xy 171.039697 59.070051) (xy 171.022795 59.091024) (xy 170.938023 59.175796) + (xy 170.875714 59.20982) (xy 170.84893 59.2127) (xy 164.697 59.2127) (xy 164.628879 59.192698) (xy 164.582386 59.139042) + (xy 164.571 59.0867) (xy 164.571 58.897867) (xy 164.570999 58.89785) (xy 164.56449 58.837303) (xy 164.564488 58.837295) + (xy 164.513389 58.700297) (xy 164.513387 58.700292) (xy 164.425761 58.583238) (xy 164.308707 58.495612) + (xy 164.308702 58.49561) (xy 164.171704 58.444511) (xy 164.171696 58.444509) (xy 164.111149 58.438) + (xy 164.111138 58.438) (xy 161.854862 58.438) (xy 161.85485 58.438) (xy 161.794303 58.444509) (xy 161.794295 58.444511) + (xy 161.657297 58.49561) (xy 161.657292 58.495612) (xy 161.540238 58.583238) (xy 161.452612 58.700292) + (xy 161.45261 58.700297) (xy 161.401511 58.837295) (xy 161.401509 58.837303) (xy 161.395 58.89785) + (xy 161.395 61.154149) (xy 161.401509 61.214696) (xy 161.401511 61.214704) (xy 161.45261 61.351702) + (xy 161.452612 61.351707) (xy 161.540238 61.468761) (xy 161.657292 61.556387) (xy 161.657294 61.556388) + (xy 161.657296 61.556389) (xy 161.668573 61.560595) (xy 161.794295 61.607488) (xy 161.794303 61.60749) + (xy 161.85485 61.613999) (xy 161.854855 61.613999) (xy 161.854862 61.614) (xy 161.854868 61.614) + (xy 164.111132 61.614) (xy 164.111138 61.614) (xy 164.111145 61.613999) (xy 164.111149 61.613999) + (xy 164.171696 61.60749) (xy 164.171699 61.607489) (xy 164.171701 61.607489) (xy 164.308704 61.556389) + (xy 164.3439 61.530042) (xy 164.425761 61.468761) (xy 164.513387 61.351707) (xy 164.513387 61.351706) + (xy 164.513389 61.351704) (xy 164.564489 61.214701) (xy 164.566758 61.193603) (xy 164.570999 61.154149) + (xy 164.571 61.154132) (xy 164.571 60.9653) (xy 164.591002 60.897179) (xy 164.644658 60.850686) + (xy 164.697 60.8393) (xy 171.329634 60.8393) (xy 171.329637 60.8393) (xy 171.370993 60.82986) (xy 171.377956 60.828676) + (xy 171.420121 60.823926) (xy 171.460169 60.809911) (xy 171.466955 60.807956) (xy 171.508316 60.798517) + (xy 171.546547 60.780105) (xy 171.553051 60.77741) (xy 171.59311 60.763394) (xy 171.629041 60.740816) + (xy 171.635194 60.737415) (xy 171.67344 60.718998) (xy 171.706618 60.692537) (xy 171.712351 60.688469) + (xy 171.748293 60.665887) (xy 171.877887 60.536293) (xy 171.937412 60.476768) (xy 171.937414 60.476764) + (xy 172.480385 59.933795) (xy 172.480385 59.933794) (xy 172.498108 59.916072) (xy 172.498108 59.91607) + (xy 172.512887 59.901293) (xy 172.535464 59.86536) (xy 172.539537 59.859618) (xy 172.565998 59.82644) + (xy 172.584408 59.788208) (xy 172.587819 59.782037) (xy 172.610394 59.74611) (xy 172.624411 59.706048) + (xy 172.627099 59.699559) (xy 172.645517 59.661315) (xy 172.654957 59.619954) (xy 172.656906 59.613183) + (xy 172.670926 59.573121) (xy 172.675675 59.530961) (xy 172.67686 59.523993) (xy 172.682446 59.499521) + (xy 172.6863 59.482637) (xy 172.6863 59.299363) (xy 172.6863 59.299362) (xy 172.6863 57.161843) + (xy 172.706301 57.093726) (xy 172.723201 57.072755) (xy 172.72976 57.066196) (xy 172.792073 57.032177) + (xy 172.818847 57.0293) (xy 178.949634 57.0293) (xy 178.949637 57.0293) (xy 178.990993 57.01986) + (xy 178.997956 57.018676) (xy 179.040121 57.013926) (xy 179.080169 56.999911) (xy 179.086955 56.997956) + (xy 179.128316 56.988517) (xy 179.166547 56.970105) (xy 179.173051 56.96741) (xy 179.21311 56.953394) + (xy 179.249041 56.930816) (xy 179.255194 56.927415) (xy 179.29344 56.908998) (xy 179.326618 56.882537) + (xy 179.332351 56.878469) (xy 179.368293 56.855887) (xy 179.497887 56.726293) (xy 179.557391 56.666789) + (xy 179.557404 56.666774) (xy 185.507974 50.716205) (xy 185.570287 50.682179) (xy 185.59707 50.6793) + (xy 189.626291 50.6793) (xy 189.694412 50.699302) (xy 189.740485 50.752049) (xy 189.796431 50.872025) + (xy 189.926491 51.05777) (xy 190.08683 51.218109) (xy 190.272575 51.348169) (xy 190.278469 51.350917) + (xy 190.281873 51.352505) (xy 190.335158 51.399421) (xy 190.35462 51.467698) (xy 190.334079 51.535658) + (xy 190.280057 51.581725) (xy 190.228624 51.5927) (xy 186.386361 51.5927) (xy 186.355167 51.599819) + (xy 186.344998 51.60214) (xy 186.33804 51.603322) (xy 186.321307 51.605208) (xy 186.295882 51.608073) + (xy 186.255834 51.622085) (xy 186.249043 51.624042) (xy 186.207687 51.633482) (xy 186.207682 51.633483) + (xy 186.169454 51.651891) (xy 186.162925 51.654595) (xy 186.122894 51.668603) (xy 186.122888 51.668606) + (xy 186.086972 51.691173) (xy 186.080787 51.694591) (xy 186.042562 51.713) (xy 186.042557 51.713003) + (xy 186.009392 51.739451) (xy 186.003633 51.743538) (xy 185.989141 51.752644) (xy 185.967706 51.766113) + (xy 185.87569 51.858126) (xy 185.875673 51.858146) (xy 176.653025 61.080795) (xy 176.590713 61.114821) + (xy 176.56393 61.1177) (xy 173.559361 61.1177) (xy 173.530952 61.124184) (xy 173.517998 61.12714) + (xy 173.51104 61.128322) (xy 173.494307 61.130208) (xy 173.468882 61.133073) (xy 173.428834 61.147085) + (xy 173.422043 61.149042) (xy 173.380687 61.158482) (xy 173.380682 61.158483) (xy 173.342454 61.176891) + (xy 173.335925 61.179595) (xy 173.295894 61.193603) (xy 173.295888 61.193606) (xy 173.259972 61.216173) + (xy 173.253787 61.219591) (xy 173.215562 61.238) (xy 173.215553 61.238005) (xy 173.182391 61.264451) + (xy 173.176628 61.26854) (xy 173.14071 61.29111) (xy 173.140707 61.291112) (xy 173.140707 61.291113) + (xy 173.108205 61.323615) (xy 172.644629 61.787191) (xy 172.208025 62.223795) (xy 172.145713 62.257821) + (xy 172.11893 62.2607) (xy 168.73336 62.2607) (xy 168.69201 62.270138) (xy 168.685043 62.271321) + (xy 168.642878 62.276073) (xy 168.602829 62.290086) (xy 168.596039 62.292043) (xy 168.554687 62.301482) + (xy 168.554679 62.301485) (xy 168.516456 62.31989) (xy 168.50993 62.322594) (xy 168.469891 62.336605) + (xy 168.433964 62.359177) (xy 168.42778 62.362595) (xy 168.389563 62.381) (xy 168.35639 62.407452) + (xy 168.350628 62.41154) (xy 168.314706 62.434113) (xy 168.264482 62.484336) (xy 168.264481 62.484337) + (xy 167.493178 63.255639) (xy 167.493174 63.255645) (xy 167.423114 63.325704) (xy 167.42311 63.325709) + (xy 167.40054 63.361628) (xy 167.396451 63.367391) (xy 167.370005 63.400553) (xy 167.37 63.400562) + (xy 167.351591 63.438787) (xy 167.348173 63.444972) (xy 167.325606 63.480888) (xy 167.325603 63.480894) + (xy 167.311595 63.520925) (xy 167.308891 63.527454) (xy 167.290483 63.565682) (xy 167.290482 63.565687) + (xy 167.281042 63.607043) (xy 167.279085 63.613834) (xy 167.265073 63.653882) (xy 167.260324 63.696033) + (xy 167.25914 63.703001) (xy 167.2497 63.744361) (xy 167.2497 63.790328) (xy 167.249699 74.87693) + (xy 167.229697 74.945051) (xy 167.212794 74.966025) (xy 165.223025 76.955795) (xy 165.160713 76.98982) + (xy 165.13393 76.9927) (xy 163.018359 76.9927) (xy 162.97701 77.002138) (xy 162.970043 77.003321) + (xy 162.927877 77.008074) (xy 162.927874 77.008074) (xy 162.887833 77.022085) (xy 162.881043 77.024042) + (xy 162.839687 77.033482) (xy 162.83968 77.033484) (xy 162.801456 77.05189) (xy 162.79493 77.054594) + (xy 162.754891 77.068605) (xy 162.718964 77.091177) (xy 162.71278 77.094595) (xy 162.674561 77.113001) + (xy 162.674559 77.113002) (xy 162.668 77.118233) (xy 162.643756 77.137567) (xy 162.641392 77.139452) + (xy 162.635628 77.143541) (xy 162.599708 77.166111) (xy 162.583456 77.182362) (xy 162.567205 77.198615) + (xy 161.239358 78.526461) (xy 159.914984 79.850834) (xy 159.914982 79.850837) (xy 159.803114 79.962704) + (xy 159.80311 79.962709) (xy 159.78054 79.998628) (xy 159.776451 80.004391) (xy 159.750005 80.037553) + (xy 159.75 80.037562) (xy 159.731591 80.075787) (xy 159.728173 80.081972) (xy 159.705606 80.117888) + (xy 159.705603 80.117894) (xy 159.691595 80.157926) (xy 159.688891 80.164456) (xy 159.672679 80.198121) + (xy 159.631429 80.246665) (xy 159.606834 80.263887) (xy 159.60683 80.26389) (xy 159.446488 80.424233) + (xy 159.316431 80.609974) (xy 159.287195 80.672671) (xy 159.240277 80.725956) (xy 159.172 80.745417) + (xy 159.10404 80.724875) (xy 159.058805 80.672671) (xy 159.035793 80.623323) (xy 159.029569 80.609975) + (xy 158.899509 80.42423) (xy 158.73917 80.263891) (xy 158.686195 80.226797) (xy 158.681128 80.223249) + (xy 158.6368 80.167792) (xy 158.6274 80.120037) (xy 158.6274 76.049047) (xy 158.647402 75.980926) + (xy 158.701058 75.934433) (xy 158.771332 75.924329) (xy 158.835912 75.953823) (xy 158.842491 75.959948) + (xy 159.112258 76.229715) (xy 159.378972 76.496429) (xy 159.390944 76.510283) (xy 159.404504 76.528497) + (xy 159.443022 76.560817) (xy 159.447069 76.564527) (xy 159.452598 76.570056) (xy 159.452597 76.570056) + (xy 159.477455 76.58971) (xy 159.534226 76.637348) (xy 159.540357 76.64138) (xy 159.540291 76.641478) + (xy 159.54581 76.644994) (xy 159.545873 76.644894) (xy 159.552115 76.648743) (xy 159.552118 76.648746) + (xy 159.552121 76.648747) (xy 159.552122 76.648748) (xy 159.619309 76.680077) (xy 159.685551 76.713346) + (xy 159.692455 76.715859) (xy 159.692414 76.715971) (xy 159.698589 76.718117) (xy 159.698627 76.718004) + (xy 159.70559 76.720311) (xy 159.705591 76.720311) (xy 159.705593 76.720312) (xy 159.752651 76.730028) + (xy 159.778185 76.735301) (xy 159.801792 76.740896) (xy 159.85033 76.7524) (xy 159.850336 76.7524) + (xy 159.857619 76.753252) (xy 159.857605 76.753369) (xy 159.86411 76.754033) (xy 159.864121 76.753916) + (xy 159.871428 76.754554) (xy 159.871435 76.754556) (xy 159.94552 76.7524) (xy 161.524054 76.7524) + (xy 161.542313 76.753729) (xy 161.564788 76.757022) (xy 161.596545 76.754243) (xy 161.614881 76.75264) + (xy 161.620374 76.7524) (xy 161.628189 76.7524) (xy 161.643919 76.750561) (xy 161.659651 76.748722) + (xy 161.733483 76.742263) (xy 161.733495 76.742258) (xy 161.740665 76.740779) (xy 161.740689 76.740896) + (xy 161.747082 76.739479) (xy 161.747055 76.739364) (xy 161.754189 76.737672) (xy 161.754195 76.737672) + (xy 161.823871 76.712311) (xy 161.894229 76.688998) (xy 161.89424 76.68899) (xy 161.900884 76.685894) + (xy 161.900935 76.686003) (xy 161.906809 76.683159) (xy 161.906756 76.683053) (xy 161.913319 76.679756) + (xy 161.91332 76.679754) (xy 161.913323 76.679754) (xy 161.943643 76.659811) (xy 161.975257 76.639019) + (xy 161.994716 76.627015) (xy 162.038357 76.600099) (xy 162.038362 76.600093) (xy 162.044115 76.595546) + (xy 162.044189 76.595639) (xy 162.049252 76.591515) (xy 162.049176 76.591424) (xy 162.054798 76.586705) + (xy 162.054798 76.586703) (xy 162.054805 76.5867) (xy 162.105667 76.532788) (xy 162.816426 75.822029) + (xy 162.830272 75.810062) (xy 162.848497 75.796496) (xy 162.880827 75.757965) (xy 162.884511 75.753944) + (xy 162.89006 75.748397) (xy 162.904896 75.729633) (xy 162.90971 75.723545) (xy 162.931043 75.69812) + (xy 162.957347 75.666774) (xy 162.957348 75.66677) (xy 162.957351 75.666768) (xy 162.961385 75.660636) + (xy 162.961484 75.660701) (xy 162.964996 75.655188) (xy 162.964894 75.655126) (xy 162.968741 75.648887) + (xy 162.968746 75.648882) (xy 163.000077 75.58169) (xy 163.033347 75.515446) (xy 163.033348 75.515441) + (xy 163.035858 75.508547) (xy 163.035969 75.508587) (xy 163.038116 75.502413) (xy 163.038003 75.502376) + (xy 163.040312 75.495407) (xy 163.055301 75.422813) (xy 163.060128 75.402447) (xy 163.0724 75.35067) + (xy 163.0724 75.350663) (xy 163.073252 75.343382) (xy 163.073368 75.343395) (xy 163.074033 75.336889) + (xy 163.073916 75.336879) (xy 163.074554 75.329572) (xy 163.074556 75.329565) (xy 163.0724 75.25548) + (xy 163.0724 71.05958) (xy 163.092402 70.991459) (xy 163.109305 70.970485) (xy 163.706966 70.372824) + (xy 163.769278 70.338798) (xy 163.840093 70.343863) (xy 163.896929 70.38641) (xy 163.908326 70.404713) + (xy 163.914483 70.416796) (xy 163.920118 70.427856) (xy 164.011142 70.51888) (xy 164.011144 70.518881) + (xy 164.011146 70.518883) (xy 164.034282 70.530671) (xy 164.085897 70.579418) (xy 164.102964 70.648332) + (xy 164.080064 70.715534) (xy 164.066175 70.732033) (xy 163.41427 71.383937) (xy 163.414271 71.383938) + (xy 163.451437 71.412866) (xy 163.451447 71.412872) (xy 163.664313 71.528069) (xy 163.664315 71.528071) + (xy 163.893235 71.606659) (xy 163.893242 71.606661) (xy 164.131984 71.6465) (xy 164.374016 71.6465) + (xy 164.612757 71.606661) (xy 164.612764 71.606659) (xy 164.841684 71.528071) (xy 164.841686 71.528069) + (xy 165.054558 71.412869) (xy 165.091727 71.383938) (xy 165.091728 71.383937) (xy 164.439824 70.732033) + (xy 164.405798 70.669721) (xy 164.410863 70.598906) (xy 164.45341 70.54207) (xy 164.471712 70.530674) + (xy 164.494854 70.518883) (xy 164.585883 70.427854) (xy 164.597671 70.404717) (xy 164.646416 70.353103) + (xy 164.715331 70.336034) (xy 164.782533 70.358934) (xy 164.799033 70.372824) (xy 165.45013 71.023921) + (xy 165.450131 71.023921) (xy 165.541874 70.883499) (xy 165.639098 70.661852) (xy 165.639101 70.661845) + (xy 165.698515 70.427221) (xy 165.718504 70.185999) (xy 165.698515 69.944778) (xy 165.639101 69.710154) + (xy 165.639098 69.710147) (xy 165.541874 69.4885) (xy 165.450131 69.348077) (xy 164.799033 69.999176) + (xy 164.736721 70.033201) (xy 164.665905 70.028136) (xy 164.60907 69.985589) (xy 164.597674 69.967287) + (xy 164.585883 69.944146) (xy 164.585881 69.944144) (xy 164.58588 69.944142) (xy 164.494855 69.853117) + (xy 164.471714 69.841326) (xy 164.420099 69.792578) (xy 164.403034 69.723662) (xy 164.425935 69.656461) + (xy 164.439823 69.639964) (xy 165.091727 68.98806) (xy 165.054555 68.959128) (xy 165.054554 68.959127) + (xy 164.841686 68.84393) (xy 164.841684 68.843928) (xy 164.612764 68.76534) (xy 164.612757 68.765338) + (xy 164.374016 68.7255) (xy 164.131984 68.7255) (xy 163.893242 68.765338) (xy 163.893231 68.765341) + (xy 163.844741 68.781988) (xy 163.773817 68.785188) (xy 163.712421 68.749535) (xy 163.680047 68.68635) + (xy 163.686974 68.615692) (xy 163.714733 68.573722) (xy 164.72143 67.567025) (xy 164.735269 67.555065) + (xy 164.753497 67.541496) (xy 164.785827 67.502965) (xy 164.789511 67.498944) (xy 164.79506 67.493397) + (xy 164.814708 67.468547) (xy 164.819211 67.46318) (xy 164.829397 67.45104) (xy 164.855062 67.429535) + (xy 164.854298 67.428443) (xy 164.961278 67.353534) (xy 165.031809 67.304148) (xy 165.181148 67.154809) + (xy 165.302286 66.981806) (xy 165.391542 66.790395) (xy 165.446204 66.586394) (xy 165.464611 66.376) + (xy 165.446204 66.165606) (xy 165.391542 65.961605) (xy 165.302286 65.770195) (xy 165.302285 65.770194) + (xy 165.302284 65.770191) (xy 165.181152 65.597195) (xy 165.181149 65.597192) (xy 165.142309 65.558352) + (xy 165.031809 65.447852) (xy 165.026728 65.444294) (xy 164.898649 65.354612) (xy 164.854321 65.299155) + (xy 164.847012 65.228535) (xy 164.879043 65.165175) (xy 164.910951 65.140585) (xy 165.054558 65.062869) + (xy 165.091727 65.033938) (xy 165.091728 65.033937) (xy 164.439824 64.382033) (xy 164.405798 64.319721) + (xy 164.410863 64.248906) (xy 164.45341 64.19207) (xy 164.471712 64.180674) (xy 164.494854 64.168883) + (xy 164.585883 64.077854) (xy 164.597671 64.054717) (xy 164.646416 64.003103) (xy 164.715331 63.986034) + (xy 164.782533 64.008934) (xy 164.799033 64.022824) (xy 165.45013 64.673921) (xy 165.450131 64.673921) + (xy 165.541874 64.533499) (xy 165.639098 64.311852) (xy 165.639101 64.311845) (xy 165.698515 64.077221) + (xy 165.718504 63.836) (xy 165.698515 63.594778) (xy 165.639101 63.360154) (xy 165.639098 63.360147) + (xy 165.541874 63.1385) (xy 165.450131 62.998077) (xy 164.799033 63.649176) (xy 164.736721 63.683201) + (xy 164.665905 63.678136) (xy 164.60907 63.635589) (xy 164.597672 63.617284) (xy 164.585883 63.594146) + (xy 164.585881 63.594144) (xy 164.58588 63.594142) (xy 164.494855 63.503117) (xy 164.471714 63.491326) + (xy 164.420099 63.442578) (xy 164.403034 63.373662) (xy 164.425935 63.306461) (xy 164.439823 63.289964) + (xy 165.091727 62.63806) (xy 165.054555 62.609128) (xy 165.054554 62.609127) (xy 164.841686 62.49393) + (xy 164.841684 62.493928) (xy 164.612764 62.41534) (xy 164.612757 62.415338) (xy 164.374016 62.3755) + (xy 164.131984 62.3755) (xy 163.893242 62.415338) (xy 163.893235 62.41534) (xy 163.664315 62.493928) + (xy 163.664313 62.49393) (xy 163.451447 62.609126) (xy 163.414271 62.638061) (xy 163.41427 62.638061) + (xy 164.066175 63.289966) (xy 164.100201 63.352278) (xy 164.095136 63.423093) (xy 164.052589 63.479929) + (xy 164.034284 63.491327) (xy 164.011144 63.503117) (xy 163.920117 63.594144) (xy 163.908327 63.617284) + (xy 163.859578 63.668899) (xy 163.790663 63.685964) (xy 163.723462 63.663062) (xy 163.706966 63.649175) + (xy 163.055867 62.998076) (xy 163.055866 62.998077) (xy 162.964131 63.138488) (xy 162.964124 63.138502) + (xy 162.866901 63.360147) (xy 162.866898 63.360154) (xy 162.807484 63.594778) (xy 162.787495 63.836) + (xy 162.807484 64.077221) (xy 162.866898 64.311845) (xy 162.866901 64.311852) (xy 162.964126 64.533502) + (xy 163.055866 64.673921) (xy 163.706964 64.022823) (xy 163.769277 63.988798) (xy 163.840092 63.993862) + (xy 163.896928 64.036409) (xy 163.908326 64.054714) (xy 163.920117 64.077855) (xy 164.011142 64.16888) + (xy 164.011144 64.168881) (xy 164.011146 64.168883) (xy 164.034282 64.180671) (xy 164.085897 64.229418) + (xy 164.102964 64.298332) (xy 164.080064 64.365534) (xy 164.066175 64.382033) (xy 163.41427 65.033937) + (xy 163.414271 65.033938) (xy 163.451437 65.062866) (xy 163.451446 65.062872) (xy 163.595048 65.140585) + (xy 163.645439 65.190598) (xy 163.660791 65.259915) (xy 163.636231 65.326528) (xy 163.60735 65.354612) + (xy 163.474191 65.447851) (xy 163.474185 65.447856) (xy 163.324856 65.597185) (xy 163.324847 65.597195) + (xy 163.203715 65.770191) (xy 163.114459 65.961601) (xy 163.114457 65.961606) (xy 163.09883 66.019929) + (xy 163.059796 66.165606) (xy 163.041389 66.376) (xy 163.059796 66.586394) (xy 163.068853 66.620195) + (xy 163.114457 66.790393) (xy 163.114459 66.790398) (xy 163.162342 66.893084) (xy 163.181628 66.934443) + (xy 163.183608 66.938688) (xy 163.194269 67.00888) (xy 163.165289 67.073693) (xy 163.158508 67.081033) + (xy 161.879573 68.359968) (xy 161.865724 68.371938) (xy 161.847502 68.385504) (xy 161.847499 68.385507) + (xy 161.815181 68.424022) (xy 161.81147 68.428071) (xy 161.805942 68.433599) (xy 161.805939 68.433603) + (xy 161.786289 68.458455) (xy 161.738653 68.515225) (xy 161.734623 68.521353) (xy 161.734526 68.521289) + (xy 161.731009 68.526808) (xy 161.731108 68.526869) (xy 161.727255 68.533116) (xy 161.727254 68.533117) + (xy 161.727254 68.533118) (xy 161.708321 68.57372) (xy 161.695922 68.600309) (xy 161.662654 68.666551) + (xy 161.660143 68.673451) (xy 161.660033 68.673411) (xy 161.657886 68.67959) (xy 161.657996 68.679627) + (xy 161.655688 68.68659) (xy 161.640698 68.759185) (xy 161.623599 68.831331) (xy 161.622748 68.83862) + (xy 161.622631 68.838606) (xy 161.621966 68.845113) (xy 161.622084 68.845124) (xy 161.621444 68.852435) + (xy 161.6236 68.926519) (xy 161.6236 72.68413) (xy 161.603598 72.752251) (xy 161.549942 72.798744) + (xy 161.479668 72.808848) (xy 161.415088 72.779354) (xy 161.408505 72.773225) (xy 161.279178 72.643898) + (xy 161.27917 72.643891) (xy 161.221128 72.603249) (xy 161.1768 72.547792) (xy 161.1674 72.500037) + (xy 161.1674 72.152946) (xy 161.16873 72.134686) (xy 161.172022 72.112212) (xy 161.169803 72.086859) + (xy 161.16764 72.062117) (xy 161.1674 72.056624) (xy 161.1674 72.048811) (xy 161.163722 72.01735) + (xy 161.161471 71.991615) (xy 161.157263 71.943516) (xy 161.157262 71.943513) (xy 161.157262 71.943511) + (xy 161.155779 71.936328) (xy 161.155894 71.936304) (xy 161.154479 71.92992) (xy 161.154365 71.929948) + (xy 161.152672 71.922809) (xy 161.152672 71.922805) (xy 161.127313 71.853132) (xy 161.103998 71.782771) + (xy 161.103995 71.782767) (xy 161.100896 71.776119) (xy 161.101004 71.776068) (xy 161.098154 71.770182) + (xy 161.098047 71.770236) (xy 161.094757 71.763687) (xy 161.094754 71.763677) (xy 161.073587 71.731495) + (xy 161.054025 71.70175) (xy 161.0151 71.638643) (xy 161.010546 71.632884) (xy 161.010638 71.63281) + (xy 161.006507 71.627738) (xy 161.006416 71.627815) (xy 161.001699 71.622193) (xy 160.97401 71.596071) + (xy 160.947804 71.571347) (xy 160.26749 70.891033) (xy 160.233465 70.828722) (xy 160.23853 70.757907) + (xy 160.242387 70.748698) (xy 160.311542 70.600395) (xy 160.366204 70.396394) (xy 160.384611 70.186) + (xy 160.366204 69.975606) (xy 160.311542 69.771605) (xy 160.222286 69.580195) (xy 160.222285 69.580194) + (xy 160.222284 69.580191) (xy 160.101152 69.407195) (xy 160.101149 69.407192) (xy 160.042034 69.348077) + (xy 159.951809 69.257852) (xy 159.812337 69.160193) (xy 159.778806 69.136714) (xy 159.587398 69.047459) + (xy 159.587393 69.047457) (xy 159.478919 69.018392) (xy 159.383394 68.992796) (xy 159.173 68.974389) + (xy 159.172999 68.974389) (xy 159.067802 68.983592) (xy 158.962606 68.992796) (xy 158.938986 68.999125) + (xy 158.758606 69.047457) (xy 158.758601 69.047459) (xy 158.567191 69.136715) (xy 158.394195 69.257847) + (xy 158.394185 69.257856) (xy 158.244856 69.407185) (xy 158.244851 69.407191) (xy 158.126034 69.576879) + (xy 158.08898 69.611841) (xy 158.08565 69.613894) (xy 158.07989 69.61845) (xy 158.079818 69.618359) + (xy 158.074741 69.622495) (xy 158.074815 69.622584) (xy 158.069193 69.627301) (xy 158.018331 69.681212) + (xy 156.74724 70.952302) (xy 156.684928 70.986328) (xy 156.614113 70.981263) (xy 156.557277 70.938716) + (xy 156.532466 70.872196) (xy 156.543949 70.809959) (xy 156.621339 70.643998) (xy 156.681752 70.418532) + (xy 156.702096 70.186) (xy 156.681752 69.953468) (xy 156.621339 69.728002) (xy 156.522691 69.516452) + (xy 156.437079 69.394185) (xy 156.388813 69.325254) (xy 156.388809 69.325249) (xy 156.388807 69.325246) + (xy 156.223754 69.160193) (xy 156.141128 69.102337) (xy 156.0968 69.04688) (xy 156.0874 68.999125) + (xy 156.0874 68.832874) (xy 156.107402 68.764753) (xy 156.141127 68.729662) (xy 156.223754 68.671807) + (xy 156.388807 68.506754) (xy 156.522691 68.315548) (xy 156.621339 68.103998) (xy 156.681752 67.878532) + (xy 156.702096 67.646) (xy 156.681752 67.413468) (xy 156.664102 67.347597) (xy 156.640366 67.259011) + (xy 156.642056 67.188034) (xy 156.68185 67.129239) (xy 156.747115 67.101291) (xy 156.762073 67.1004) + (xy 158.141164 67.1004) (xy 158.209285 67.120402) (xy 158.240991 67.150876) (xy 158.241321 67.1506) + (xy 158.24389 67.153662) (xy 158.244375 67.154129) (xy 158.244852 67.154809) (xy 158.394191 67.304148) + (xy 158.567194 67.425286) (xy 158.758605 67.514542) (xy 158.962606 67.569204) (xy 159.173 67.587611) + (xy 159.383394 67.569204) (xy 159.587395 67.514542) (xy 159.778806 67.425286) (xy 159.951809 67.304148) + (xy 160.101148 67.154809) (xy 160.222286 66.981806) (xy 160.311542 66.790395) (xy 160.366204 66.586394) + (xy 160.384611 66.376) (xy 160.366204 66.165606) (xy 160.311542 65.961605) (xy 160.222286 65.770195) + (xy 160.222285 65.770194) (xy 160.222284 65.770191) (xy 160.101152 65.597195) (xy 160.101149 65.597192) + (xy 160.062309 65.558352) (xy 159.951809 65.447852) (xy 159.945005 65.443088) (xy 159.778806 65.326714) + (xy 159.587398 65.237459) (xy 159.58739 65.237456) (xy 159.558837 65.229806) (xy 159.551002 65.227706) + (xy 159.49038 65.190756) (xy 159.459358 65.126896) (xy 159.467786 65.056401) (xy 159.512988 65.001654) + (xy 159.551001 64.984293) (xy 159.587395 64.974542) (xy 159.778806 64.885286) (xy 159.951809 64.764148) + (xy 160.101148 64.614809) (xy 160.222286 64.441806) (xy 160.311542 64.250395) (xy 160.366204 64.046394) + (xy 160.384611 63.836) (xy 160.366204 63.625606) (xy 160.311542 63.421605) (xy 160.222286 63.230195) + (xy 160.222285 63.230194) (xy 160.222284 63.230191) (xy 160.101152 63.057195) (xy 160.101149 63.057192) + (xy 160.042033 62.998076) (xy 159.951809 62.907852) (xy 159.924888 62.889002) (xy 159.778806 62.786714) + (xy 159.587398 62.697459) (xy 159.587393 62.697457) (xy 159.478128 62.66818) (xy 159.383394 62.642796) + (xy 159.173 62.624389) (xy 158.962606 62.642796) (xy 158.907944 62.657442) (xy 158.758606 62.697457) + (xy 158.758601 62.697459) (xy 158.567191 62.786715) (xy 158.394195 62.907847) (xy 158.394185 62.907856) + (xy 158.244856 63.057185) (xy 158.244847 63.057195) (xy 158.123717 63.230188) (xy 158.123716 63.230189) + (xy 158.111543 63.256296) (xy 158.094599 63.292634) (xy 158.094498 63.29285) (xy 158.047581 63.346135) + (xy 157.980303 63.3656) (xy 157.057875 63.3656) (xy 156.989754 63.345598) (xy 156.954662 63.311871) + (xy 156.896809 63.229249) (xy 156.896804 63.229243) (xy 156.731756 63.064195) (xy 156.731745 63.064186) + (xy 156.540548 62.930309) (xy 156.540546 62.930308) (xy 156.329 62.831662) (xy 156.328995 62.83166) + (xy 156.103535 62.771248) (xy 155.929133 62.75599) (xy 155.871 62.750904) (xy 155.870999 62.750904) + (xy 155.853265 62.752455) (xy 155.806765 62.756523) (xy 155.737162 62.742535) (xy 155.686169 62.693135) + (xy 155.669979 62.624009) (xy 155.693732 62.557104) (xy 155.706683 62.541915) (xy 162.320385 55.928215) + (xy 162.357219 55.891381) (xy 162.359812 55.88469) (xy 162.375467 55.859775) (xy 162.379537 55.854038) + (xy 162.405998 55.82086) (xy 162.424415 55.782614) (xy 162.427816 55.776461) (xy 162.450394 55.74053) + (xy 162.46441 55.700471) (xy 162.467105 55.693967) (xy 162.485517 55.655736) (xy 162.494956 55.614375) + (xy 162.496912 55.607588) (xy 162.510925 55.567543) (xy 162.510926 55.567541) (xy 162.515676 55.525376) + (xy 162.51686 55.518415) (xy 162.518649 55.510578) (xy 162.5263 55.477057) (xy 162.5263 55.3265) + (xy 162.546302 55.258379) (xy 162.599958 55.211886) (xy 162.6523 55.2005) (xy 162.777632 55.2005) + (xy 162.777638 55.2005) (xy 162.777645 55.200499) (xy 162.777649 55.200499) (xy 162.838196 55.19399) + (xy 162.838199 55.193989) (xy 162.838201 55.193989) (xy 162.975204 55.142889) (xy 162.994235 55.128643) + (xy 163.092261 55.055261) (xy 163.179887 54.938207) (xy 163.179887 54.938206) (xy 163.179889 54.938204) + (xy 163.230989 54.801201) (xy 163.2375 54.740638) (xy 163.237499 54.615299) (xy 163.257502 54.547179) + (xy 163.311158 54.500686) (xy 163.3635 54.4893) (xy 163.514054 54.4893) (xy 163.514057 54.4893) + (xy 163.555413 54.47986) (xy 163.562376 54.478676) (xy 163.604541 54.473926) (xy 163.644589 54.459911) + (xy 163.651375 54.457956) (xy 163.692736 54.448517) (xy 163.730967 54.430105) (xy 163.737471 54.42741) + (xy 163.77753 54.413394) (xy 163.813461 54.390816) (xy 163.819614 54.387415) (xy 163.85786 54.368998) + (xy 163.891038 54.342537) (xy 163.896771 54.338469) (xy 163.932713 54.315887) (xy 164.062307 54.186293) + (xy 168.299474 49.949125) (xy 168.361787 49.915099) (xy 168.38857 49.91222) (xy 168.3937 49.91222) + ) + ) + ) +) diff --git a/qa/data/pcbnew/api_kitchen_sink.kicad_pro b/qa/data/pcbnew/api_kitchen_sink.kicad_pro new file mode 100644 index 0000000000..40248ec724 --- /dev/null +++ b/qa/data/pcbnew/api_kitchen_sink.kicad_pro @@ -0,0 +1,272 @@ +{ + "board": { + "3dviewports": [], + "design_settings": { + "defaults": { + "apply_defaults_to_fp_fields": false, + "apply_defaults_to_fp_shapes": false, + "apply_defaults_to_fp_text": false, + "board_outline_line_width": 0.05, + "copper_line_width": 0.2, + "copper_text_italic": false, + "copper_text_size_h": 1.5, + "copper_text_size_v": 1.5, + "copper_text_thickness": 0.3, + "copper_text_upright": false, + "courtyard_line_width": 0.05, + "dimension_precision": 4, + "dimension_units": 3, + "dimensions": { + "arrow_length": 1270000, + "extension_offset": 500000, + "keep_text_aligned": true, + "suppress_zeroes": false, + "text_position": 0, + "units_format": 1 + }, + "fab_line_width": 0.1, + "fab_text_italic": false, + "fab_text_size_h": 1.0, + "fab_text_size_v": 1.0, + "fab_text_thickness": 0.15, + "fab_text_upright": false, + "other_line_width": 0.1, + "other_text_italic": false, + "other_text_size_h": 1.0, + "other_text_size_v": 1.0, + "other_text_thickness": 0.15, + "other_text_upright": false, + "pads": { + "drill": 1.143, + "height": 2.032, + "width": 2.032 + }, + "silk_line_width": 0.1, + "silk_text_italic": false, + "silk_text_size_h": 1.0, + "silk_text_size_v": 1.0, + "silk_text_thickness": 0.1, + "silk_text_upright": false, + "zones": { + "min_clearance": 0.5 + } + }, + "diff_pair_dimensions": [], + "drc_exclusions": [], + "meta": { + "version": 2 + }, + "rule_severities": { + "annular_width": "error", + "clearance": "error", + "connection_width": "warning", + "copper_edge_clearance": "error", + "copper_sliver": "warning", + "courtyards_overlap": "error", + "diff_pair_gap_out_of_range": "error", + "diff_pair_uncoupled_length_too_long": "error", + "drill_out_of_range": "error", + "duplicate_footprints": "warning", + "extra_footprint": "warning", + "footprint": "error", + "footprint_symbol_mismatch": "warning", + "footprint_type_mismatch": "ignore", + "hole_clearance": "error", + "hole_near_hole": "error", + "invalid_outline": "error", + "isolated_copper": "warning", + "item_on_disabled_layer": "error", + "items_not_allowed": "error", + "length_out_of_range": "error", + "lib_footprint_issues": "warning", + "lib_footprint_mismatch": "warning", + "malformed_courtyard": "error", + "microvia_drill_out_of_range": "error", + "missing_courtyard": "ignore", + "missing_footprint": "warning", + "net_conflict": "warning", + "npth_inside_courtyard": "ignore", + "padstack": "warning", + "pth_inside_courtyard": "ignore", + "shorting_items": "error", + "silk_edge_clearance": "warning", + "silk_over_copper": "warning", + "silk_overlap": "warning", + "skew_out_of_range": "error", + "solder_mask_bridge": "error", + "starved_thermal": "error", + "text_height": "warning", + "text_thickness": "warning", + "through_hole_pad_without_hole": "error", + "too_many_vias": "error", + "track_dangling": "warning", + "track_width": "error", + "tracks_crossing": "error", + "unconnected_items": "error", + "unresolved_variable": "error", + "via_dangling": "warning", + "zones_intersect": "error" + }, + "rules": { + "max_error": 0.005, + "min_clearance": 0.0, + "min_connection": 0.0, + "min_copper_edge_clearance": 0.5, + "min_hole_clearance": 0.25, + "min_hole_to_hole": 0.25, + "min_microvia_diameter": 0.2, + "min_microvia_drill": 0.1, + "min_resolved_spokes": 2, + "min_silk_clearance": 0.0, + "min_text_height": 0.8, + "min_text_thickness": 0.08, + "min_through_hole_diameter": 0.3, + "min_track_width": 0.0, + "min_via_annular_width": 0.1, + "min_via_diameter": 0.5, + "solder_mask_to_copper_clearance": 0.0, + "use_height_for_length_calcs": true + }, + "teardrop_options": [ + { + "td_onpadsmd": true, + "td_onroundshapesonly": false, + "td_ontrackend": false, + "td_onviapad": true + } + ], + "teardrop_parameters": [ + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_round_shape", + "td_width_to_size_filter_ratio": 0.9 + }, + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_rect_shape", + "td_width_to_size_filter_ratio": 0.9 + }, + { + "td_allow_use_two_tracks": true, + "td_curve_segcount": 0, + "td_height_ratio": 1.0, + "td_length_ratio": 0.5, + "td_maxheight": 2.0, + "td_maxlen": 1.0, + "td_on_pad_in_zone": false, + "td_target_name": "td_track_end", + "td_width_to_size_filter_ratio": 0.9 + } + ], + "track_widths": [], + "tuning_pattern_settings": { + "diff_pair_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 1.0 + }, + "diff_pair_skew_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 0.6 + }, + "single_track_defaults": { + "corner_radius_percentage": 80, + "corner_style": 1, + "max_amplitude": 1.0, + "min_amplitude": 0.2, + "single_sided": false, + "spacing": 0.6 + } + }, + "via_dimensions": [], + "zones_allow_external_fillets": false + }, + "ipc2581": { + "dist": "", + "distpn": "", + "internal_id": "", + "mfg": "", + "mpn": "" + }, + "layer_presets": [], + "viewports": [] + }, + "boards": [], + "cvpcb": { + "equivalence_files": [] + }, + "libraries": { + "pinned_footprint_libs": [], + "pinned_symbol_libs": [] + }, + "meta": { + "filename": "api_kitchen_sink.kicad_pro", + "version": 1 + }, + "net_settings": { + "classes": [ + { + "bus_width": 12, + "clearance": 0.2, + "diff_pair_gap": 0.25, + "diff_pair_via_gap": 0.25, + "diff_pair_width": 0.2, + "line_style": 0, + "microvia_diameter": 0.3, + "microvia_drill": 0.1, + "name": "Default", + "pcb_color": "rgba(0, 0, 0, 0.000)", + "schematic_color": "rgba(0, 0, 0, 0.000)", + "track_width": 0.2, + "via_diameter": 0.6, + "via_drill": 0.3, + "wire_width": 6 + } + ], + "meta": { + "version": 3 + }, + "net_colors": null, + "netclass_assignments": null, + "netclass_patterns": [] + }, + "pcbnew": { + "last_paths": { + "gencad": "", + "idf": "", + "netlist": "", + "plot": "", + "pos_files": "", + "specctra_dsn": "", + "step": "", + "svg": "", + "vrml": "" + }, + "page_layout_descr_file": "" + }, + "schematic": { + "legacy_lib_dir": "", + "legacy_lib_list": [] + }, + "sheets": [], + "text_variables": {} +} diff --git a/qa/tests/CMakeLists.txt b/qa/tests/CMakeLists.txt index 885d5e03db..d4c5b8556a 100644 --- a/qa/tests/CMakeLists.txt +++ b/qa/tests/CMakeLists.txt @@ -20,6 +20,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ## Unit tests +add_subdirectory( api ) add_subdirectory( common ) add_subdirectory( gerbview ) add_subdirectory( eeschema ) diff --git a/api/enums/CMakeLists.txt b/qa/tests/api/CMakeLists.txt similarity index 50% rename from api/enums/CMakeLists.txt rename to qa/tests/api/CMakeLists.txt index 3c5efd20e4..841193a2b5 100644 --- a/api/enums/CMakeLists.txt +++ b/qa/tests/api/CMakeLists.txt @@ -15,29 +15,45 @@ # You should have received a copy of the GNU General Public License along # with this program. If not, see . -include_directories( BEFORE ${INC_BEFORE} ) -include_directories( - ${INC_AFTER} +set( QA_API_SRCS + test_api_module.cpp + test_api_enums.cpp + test_api_proto.cpp ) -add_executable( enum_exporter WIN32 - enum_exporter.cpp +add_executable( qa_api + ${QA_API_SRCS} ) -target_link_libraries( enum_exporter +add_dependencies( qa_api pcbnew ) + +target_link_libraries( qa_api + kiapi + qa_utils + qa_pcbnew_utils + pcbnew_kiface_objects + 3d-viewer + connectivity + pcbcommon + pnsrouter + gal common + idf3 + markdown_lib + ${PCBNEW_IO_LIBRARIES} + ${wxWidgets_LIBRARIES} + Boost::headers + Boost::unit_test_framework + ${PCBNEW_EXTRA_LIBS} # -lrt must follow Boost ) -target_include_directories( enum_exporter PRIVATE +target_include_directories( qa_api PRIVATE + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/common + ${CMAKE_SOURCE_DIR}/qa/mocks/include + ${CMAKE_SOURCE_DIR}/pcbnew + ${CMAKE_CURRENT_SOURCE_DIR} $ ) -if( MSVC ) - # The cli needs subsystem:console or else we can't link wmain/main - set_target_properties(enum_exporter PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE") - set_target_properties(enum_exporter PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_CONSOLE") - set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") - set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE") - set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") - set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE") -endif() +kicad_add_boost_test( qa_api qa_api ) diff --git a/qa/tests/api/test_api_enums.cpp b/qa/tests/api/test_api_enums.cpp new file mode 100644 index 0000000000..fbf630cf30 --- /dev/null +++ b/qa/tests/api/test_api_enums.cpp @@ -0,0 +1,163 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include +#include +#include + +// Common +#include +#include +#include +#include +#include +#include +#include + +// Board-specific +#include +#include + +using namespace kiapi::common; + +BOOST_AUTO_TEST_SUITE( ApiEnums ) + +/** + * Checks if a KiCad enum has been properly mapped to a Protobuf enum + * @tparam KiCadEnum is an enum type + * @tparam ProtoEnum is a Protobuf enum type + * @param aPartiallyMapped is true if only some of the KiCad enum values are exposed to the API + */ +template +void testEnums( bool aPartiallyMapped = false ) +{ + boost::bimap protoToKiCadSeen; + std::set seenProtos; + + for( ProtoEnum value : magic_enum::enum_values() ) + { + BOOST_TEST_CONTEXT( magic_enum::enum_type_name() << "::" + << magic_enum::enum_name( value ) ) + { + std::string name( magic_enum::enum_name( value ) ); + auto splitPos = name.find_first_of( '_' ); + + // Protobuf enum names should be formatted as PREFIX_KEY + BOOST_REQUIRE_MESSAGE( splitPos != std::string::npos, + "Proto enum name doesn't have a prefix" ); + + std::string suffix = name.substr( splitPos ); + + // Protobuf enum with the value 0 should not map to anything + if( static_cast( value ) == 0 ) + { + BOOST_REQUIRE_MESSAGE( suffix.compare( "_UNKNOWN" ) == 0, + "Proto enum with value 0 must be named _UNKNOWN" ); + continue; + } + + KiCadEnum result; + // Every non-unknown Proto value should map to a valid KiCad value + BOOST_REQUIRE_NO_THROW( result = ( FromProtoEnum( value ) ) ); + + // There should be a 1:1 mapping + BOOST_REQUIRE( !protoToKiCadSeen.left.count( value ) ); + protoToKiCadSeen.left.insert( { value, result } ); + } + } + + for( KiCadEnum value : magic_enum::enum_values() ) + { + BOOST_TEST_CONTEXT( magic_enum::enum_type_name() << "::" + << magic_enum::enum_name( value ) ) + { + ProtoEnum result; + + if( aPartiallyMapped ) + { + try + { + result = ToProtoEnum( value ); + } + catch( KI_TEST::WX_ASSERT_ERROR ) + { + // If it wasn't mapped from KiCad to Proto, it shouldn't be mapped the other way + BOOST_REQUIRE_MESSAGE( !protoToKiCadSeen.right.count( value ), + "Proto enum is mapped to this KiCad enum, but not vice versa" ); + continue; + } + } + else + { + // Every KiCad enum value should map to a non-unknown Protobuf value + BOOST_REQUIRE_NO_THROW( result = ( ToProtoEnum( value ) ) ); + } + + // Protobuf "unknown" should always be zero value by convention + BOOST_REQUIRE( result != static_cast( 0 ) ); + + // There should be a 1:1 mapping + BOOST_REQUIRE( !seenProtos.count( result ) ); + seenProtos.insert( result ); + + // Round-tripping should work + KiCadEnum roundTrip = FromProtoEnum( result ); + BOOST_REQUIRE( roundTrip == value ); + } + } +} + +BOOST_AUTO_TEST_CASE( HorizontalAlignment ) +{ + testEnums(); +} + +BOOST_AUTO_TEST_CASE( VerticalAlignment ) +{ + testEnums(); +} + +BOOST_AUTO_TEST_CASE( StrokeLineStyle ) +{ + testEnums(); +} + +BOOST_AUTO_TEST_CASE( KiCadObjectType ) +{ + testEnums( true ); +} + +BOOST_AUTO_TEST_CASE( BoardLayer ) +{ + testEnums( true ); +} + +BOOST_AUTO_TEST_CASE( PadStackShape ) +{ + testEnums(); +} + +BOOST_AUTO_TEST_CASE( ZoneConnectionStyle ) +{ + testEnums(); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/qa/tests/api/test_api_module.cpp b/qa/tests/api/test_api_module.cpp new file mode 100644 index 0000000000..bc995a851b --- /dev/null +++ b/qa/tests/api/test_api_module.cpp @@ -0,0 +1,50 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include + + +void wxAssertThrower( const wxString& aFile, int aLine, const wxString& aFunc, + const wxString& aCond, const wxString& aMsg ) +{ + throw KI_TEST::WX_ASSERT_ERROR( aFile, aLine, aFunc, aCond, aMsg ); +} + + +bool init_unit_test() +{ + boost::unit_test::framework::master_test_suite().p_name.value = "IPC API tests"; + + wxApp::SetInstance( new wxAppConsole ); + + bool ok = wxInitialize( boost::unit_test::framework::master_test_suite().argc, + boost::unit_test::framework::master_test_suite().argv ); + + wxSetAssertHandler( &wxAssertThrower ); + + return ok; +} + + +int main( int argc, char* argv[] ) +{ + return boost::unit_test::unit_test_main( &init_unit_test, argc, argv ); +} diff --git a/qa/tests/api/test_api_proto.cpp b/qa/tests/api/test_api_proto.cpp new file mode 100644 index 0000000000..5028f94907 --- /dev/null +++ b/qa/tests/api/test_api_proto.cpp @@ -0,0 +1,121 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 Jon Evans + * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + + +BOOST_AUTO_TEST_SUITE( ApiProto ) + +struct PROTO_TEST_FIXTURE +{ + PROTO_TEST_FIXTURE() : + m_settingsManager( true /* headless */ ) + { } + + SETTINGS_MANAGER m_settingsManager; + std::unique_ptr m_board; +}; + + +template +void testProtoFromKiCadObject( KiCadClass* aInput ) +{ + BOOST_TEST_CONTEXT( aInput->GetFriendlyName() << ": " << aInput->m_Uuid.AsStdString() ) + { + google::protobuf::Any any; + BOOST_REQUIRE_NO_THROW( aInput->Serialize( any ) ); + + ProtoClass proto; + BOOST_REQUIRE_MESSAGE( any.UnpackTo( &proto ), + "Any message did not unpack into the requested type" ); + + KiCadClass output( *static_cast( aInput->Clone() ) ); + + bool deserializeResult = false; + BOOST_REQUIRE_NO_THROW( deserializeResult = output.Deserialize( any ) ); + BOOST_REQUIRE_MESSAGE( deserializeResult, "Deserialize failed" ); + + // This round-trip checks that we can create an equivalent protobuf + google::protobuf::Any outputAny; + BOOST_REQUIRE_NO_THROW( output.Serialize( outputAny ) ); + if( !( outputAny.SerializeAsString() == any.SerializeAsString() ) ) + { + BOOST_TEST_MESSAGE( "Input: " << any.Utf8DebugString() ); + BOOST_TEST_MESSAGE( "Output: " << outputAny.Utf8DebugString() ); + BOOST_TEST_FAIL( "Round-tripped protobuf does not match" ); + } + + // This round-trip checks that we can create an equivalent KiCad object + if( !( output == *aInput ) ) + { + if( ( output == *aInput ) ) + BOOST_TEST_MESSAGE("ha"); + BOOST_TEST_FAIL( "Round-tripped object does not match" ); + } + } +} + + +BOOST_FIXTURE_TEST_CASE( BoardTypes, PROTO_TEST_FIXTURE ) +{ + KI_TEST::LoadBoard( m_settingsManager, "api_kitchen_sink", m_board ); + + for( PCB_TRACK* track : m_board->Tracks() ) + { + switch( track->Type() ) + { + case PCB_TRACE_T: + testProtoFromKiCadObject( track ); + break; + + case PCB_ARC_T: + testProtoFromKiCadObject( track ); + break; + + case PCB_VIA_T: + testProtoFromKiCadObject( track ); + break; + + default: + wxFAIL; + } + } + + for( FOOTPRINT* footprint : m_board->Footprints() ) + testProtoFromKiCadObject( footprint ); + + // Not yet +// for( ZONE* zone : m_board->Zones() ) +// testProtoFromKiCadObject( zone ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/scripting/CMakeLists.txt b/scripting/CMakeLists.txt index 719208225a..5ec2face6b 100644 --- a/scripting/CMakeLists.txt +++ b/scripting/CMakeLists.txt @@ -1,6 +1,5 @@ set( KIPYTHON_SRCS kipython_settings.cpp - python_manager.cpp ) add_library( scripting STATIC diff --git a/scripting/python_manager.cpp b/scripting/python_manager.cpp index 8209d985b0..576ca0988d 100644 --- a/scripting/python_manager.cpp +++ b/scripting/python_manager.cpp @@ -46,7 +46,7 @@ public: { char buffer[4096]; buffer[ processOut->Read( buffer, sizeof( buffer ) - 1 ).LastRead() ] = '\0'; - output.append( buffer, sizeof( buffer ) ); + output.append( buffer, processOut->LastRead() ); bytesRead += processOut->LastRead(); } @@ -57,7 +57,7 @@ public: { char buffer[4096]; buffer[ processOut->Read( buffer, sizeof( buffer ) - 1 ).LastRead() ] = '\0'; - error.append( buffer, sizeof( buffer ) ); + error.append( buffer, processOut->LastRead() ); bytesRead += processOut->LastRead(); } @@ -95,6 +95,12 @@ wxString PYTHON_MANAGER::FindPythonInterpreter() #else wxArrayString output; + if( 0 == wxExecute( wxS( "which -a python3" ), output, wxEXEC_SYNC ) ) + { + if( !output.IsEmpty() ) + return output[0]; + } + if( 0 == wxExecute( wxS( "which -a python" ), output, wxEXEC_SYNC ) ) { if( !output.IsEmpty() ) diff --git a/scripting/python_manager.h b/scripting/python_manager.h index 16948c38bb..b076ad1351 100644 --- a/scripting/python_manager.h +++ b/scripting/python_manager.h @@ -26,8 +26,10 @@ #include +#include -class PYTHON_MANAGER + +class KICOMMON_API PYTHON_MANAGER { public: PYTHON_MANAGER( const wxString& aInterpreterPath ) :