ADDED: Polygon boolean operations in PCB editor
This allows common operations like merging a pin courtyard into the body courtyard in the fooprint editor, taking a "bite" out of a polygon and so on, For now, this only supports polygons made of straight lines. There are some wierd cases when the operations result in nothing (e.g. wen a big polygon is substracted from a smaller one that it contains entirely). I have tried to do something senisble in these cases, but there may be more optimal ways to handle it. Relates-To: https://gitlab.com/kicad/code/kicad/-/issues/13025
|
@ -351,6 +351,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::import].emplace_back( BITMAPS::import, wxT( "import_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::import].emplace_back( BITMAPS::import, wxT( "import_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::info].emplace_back( BITMAPS::info, wxT( "info_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::info].emplace_back( BITMAPS::info, wxT( "info_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::insert_module_board].emplace_back( BITMAPS::insert_module_board, wxT( "insert_module_board_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::insert_module_board].emplace_back( BITMAPS::insert_module_board, wxT( "insert_module_board_24.png" ), 24, wxT( "light" ) );
|
||||||
|
aBitmapInfoCache[BITMAPS::intersect_polygons].emplace_back( BITMAPS::intersect_polygons, wxT( "intersect_polygons_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::language].emplace_back( BITMAPS::language, wxT( "language_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::language].emplace_back( BITMAPS::language, wxT( "language_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::layers_manager].emplace_back( BITMAPS::layers_manager, wxT( "layers_manager_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::layers_manager].emplace_back( BITMAPS::layers_manager, wxT( "layers_manager_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::leave_sheet].emplace_back( BITMAPS::leave_sheet, wxT( "leave_sheet_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::leave_sheet].emplace_back( BITMAPS::leave_sheet, wxT( "leave_sheet_24.png" ), 24, wxT( "light" ) );
|
||||||
|
@ -375,7 +376,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::marker_next].emplace_back( BITMAPS::marker_next, wxT( "marker_next_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::marker_next].emplace_back( BITMAPS::marker_next, wxT( "marker_next_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::marker_previous].emplace_back( BITMAPS::marker_previous, wxT( "marker_previous_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::marker_previous].emplace_back( BITMAPS::marker_previous, wxT( "marker_previous_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::measurement].emplace_back( BITMAPS::measurement, wxT( "measurement_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::measurement].emplace_back( BITMAPS::measurement, wxT( "measurement_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pcb_target].emplace_back( BITMAPS::pcb_target, wxT( "pcb_target_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::merge_polygons].emplace_back( BITMAPS::merge_polygons, wxT( "merge_polygons_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::mirror_h].emplace_back( BITMAPS::mirror_h, wxT( "mirror_h_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::mirror_h].emplace_back( BITMAPS::mirror_h, wxT( "mirror_h_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::mirror_v].emplace_back( BITMAPS::mirror_v, wxT( "mirror_v_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::mirror_v].emplace_back( BITMAPS::mirror_v, wxT( "mirror_v_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::mode_module].emplace_back( BITMAPS::mode_module, wxT( "mode_module_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::mode_module].emplace_back( BITMAPS::mode_module, wxT( "mode_module_24.png" ), 24, wxT( "light" ) );
|
||||||
|
@ -429,6 +430,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::part_properties].emplace_back( BITMAPS::part_properties, wxT( "part_properties_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::part_properties].emplace_back( BITMAPS::part_properties, wxT( "part_properties_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::paste].emplace_back( BITMAPS::paste, wxT( "paste_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::paste].emplace_back( BITMAPS::paste, wxT( "paste_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::paste_special].emplace_back( BITMAPS::paste_special, wxT( "paste_special_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::paste_special].emplace_back( BITMAPS::paste_special, wxT( "paste_special_24.png" ), 24, wxT( "light" ) );
|
||||||
|
aBitmapInfoCache[BITMAPS::pcb_target].emplace_back( BITMAPS::pcb_target, wxT( "pcb_target_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pin2pin].emplace_back( BITMAPS::pin2pin, wxT( "pin2pin_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::pin2pin].emplace_back( BITMAPS::pin2pin, wxT( "pin2pin_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pin_size_to].emplace_back( BITMAPS::pin_size_to, wxT( "pin_size_to_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::pin_size_to].emplace_back( BITMAPS::pin_size_to, wxT( "pin_size_to_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pin_show_etype].emplace_back( BITMAPS::pin_show_etype, wxT( "pin_show_etype_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::pin_show_etype].emplace_back( BITMAPS::pin_show_etype, wxT( "pin_show_etype_24.png" ), 24, wxT( "light" ) );
|
||||||
|
@ -520,6 +522,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::show_back_assembly_layers].emplace_back( BITMAPS::show_back_assembly_layers, wxT( "show_back_assembly_layers_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::show_back_assembly_layers].emplace_back( BITMAPS::show_back_assembly_layers, wxT( "show_back_assembly_layers_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::special_tools].emplace_back( BITMAPS::special_tools, wxT( "special_tools_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::special_tools].emplace_back( BITMAPS::special_tools, wxT( "special_tools_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::spreadsheet].emplace_back( BITMAPS::spreadsheet, wxT( "spreadsheet_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::spreadsheet].emplace_back( BITMAPS::spreadsheet, wxT( "spreadsheet_24.png" ), 24, wxT( "light" ) );
|
||||||
|
aBitmapInfoCache[BITMAPS::subtract_polygons].emplace_back( BITMAPS::subtract_polygons, wxT( "subtract_polygons_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::swap].emplace_back( BITMAPS::swap, wxT( "swap_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::swap].emplace_back( BITMAPS::swap, wxT( "swap_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::swap_layer].emplace_back( BITMAPS::swap_layer, wxT( "swap_layer_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::swap_layer].emplace_back( BITMAPS::swap_layer, wxT( "swap_layer_24.png" ), 24, wxT( "light" ) );
|
||||||
aBitmapInfoCache[BITMAPS::switch_corner_rounding_shape].emplace_back( BITMAPS::switch_corner_rounding_shape, wxT( "switch_corner_rounding_shape_24.png" ), 24, wxT( "light" ) );
|
aBitmapInfoCache[BITMAPS::switch_corner_rounding_shape].emplace_back( BITMAPS::switch_corner_rounding_shape, wxT( "switch_corner_rounding_shape_24.png" ), 24, wxT( "light" ) );
|
||||||
|
@ -740,6 +743,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::import].emplace_back( BITMAPS::import, wxT( "import_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::import].emplace_back( BITMAPS::import, wxT( "import_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::info].emplace_back( BITMAPS::info, wxT( "info_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::info].emplace_back( BITMAPS::info, wxT( "info_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::insert_module_board].emplace_back( BITMAPS::insert_module_board, wxT( "insert_module_board_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::insert_module_board].emplace_back( BITMAPS::insert_module_board, wxT( "insert_module_board_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
aBitmapInfoCache[BITMAPS::intersect_polygons].emplace_back( BITMAPS::intersect_polygons, wxT( "intersect_polygons_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::language].emplace_back( BITMAPS::language, wxT( "language_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::language].emplace_back( BITMAPS::language, wxT( "language_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::layers_manager].emplace_back( BITMAPS::layers_manager, wxT( "layers_manager_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::layers_manager].emplace_back( BITMAPS::layers_manager, wxT( "layers_manager_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::leave_sheet].emplace_back( BITMAPS::leave_sheet, wxT( "leave_sheet_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::leave_sheet].emplace_back( BITMAPS::leave_sheet, wxT( "leave_sheet_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
@ -764,7 +768,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::marker_next].emplace_back( BITMAPS::marker_next, wxT( "marker_next_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::marker_next].emplace_back( BITMAPS::marker_next, wxT( "marker_next_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::marker_previous].emplace_back( BITMAPS::marker_previous, wxT( "marker_previous_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::marker_previous].emplace_back( BITMAPS::marker_previous, wxT( "marker_previous_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::measurement].emplace_back( BITMAPS::measurement, wxT( "measurement_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::measurement].emplace_back( BITMAPS::measurement, wxT( "measurement_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pcb_target].emplace_back( BITMAPS::pcb_target, wxT( "pcb_target_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::merge_polygons].emplace_back( BITMAPS::merge_polygons, wxT( "merge_polygons_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::mirror_h].emplace_back( BITMAPS::mirror_h, wxT( "mirror_h_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::mirror_h].emplace_back( BITMAPS::mirror_h, wxT( "mirror_h_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::mirror_v].emplace_back( BITMAPS::mirror_v, wxT( "mirror_v_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::mirror_v].emplace_back( BITMAPS::mirror_v, wxT( "mirror_v_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::mode_module].emplace_back( BITMAPS::mode_module, wxT( "mode_module_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::mode_module].emplace_back( BITMAPS::mode_module, wxT( "mode_module_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
@ -818,6 +822,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::part_properties].emplace_back( BITMAPS::part_properties, wxT( "part_properties_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::part_properties].emplace_back( BITMAPS::part_properties, wxT( "part_properties_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::paste].emplace_back( BITMAPS::paste, wxT( "paste_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::paste].emplace_back( BITMAPS::paste, wxT( "paste_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::paste_special].emplace_back( BITMAPS::paste_special, wxT( "paste_special_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::paste_special].emplace_back( BITMAPS::paste_special, wxT( "paste_special_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
aBitmapInfoCache[BITMAPS::pcb_target].emplace_back( BITMAPS::pcb_target, wxT( "pcb_target_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pin2pin].emplace_back( BITMAPS::pin2pin, wxT( "pin2pin_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::pin2pin].emplace_back( BITMAPS::pin2pin, wxT( "pin2pin_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pin_size_to].emplace_back( BITMAPS::pin_size_to, wxT( "pin_size_to_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::pin_size_to].emplace_back( BITMAPS::pin_size_to, wxT( "pin_size_to_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::pin_show_etype].emplace_back( BITMAPS::pin_show_etype, wxT( "pin_show_etype_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::pin_show_etype].emplace_back( BITMAPS::pin_show_etype, wxT( "pin_show_etype_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
@ -907,6 +912,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
|
||||||
aBitmapInfoCache[BITMAPS::show_back_assembly_layers].emplace_back( BITMAPS::show_back_assembly_layers, wxT( "show_back_assembly_layers_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::show_back_assembly_layers].emplace_back( BITMAPS::show_back_assembly_layers, wxT( "show_back_assembly_layers_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::special_tools].emplace_back( BITMAPS::special_tools, wxT( "special_tools_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::special_tools].emplace_back( BITMAPS::special_tools, wxT( "special_tools_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::spreadsheet].emplace_back( BITMAPS::spreadsheet, wxT( "spreadsheet_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::spreadsheet].emplace_back( BITMAPS::spreadsheet, wxT( "spreadsheet_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
aBitmapInfoCache[BITMAPS::subtract_polygons].emplace_back( BITMAPS::subtract_polygons, wxT( "subtract_polygons_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::swap].emplace_back( BITMAPS::swap, wxT( "swap_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::swap].emplace_back( BITMAPS::swap, wxT( "swap_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::swap_layer].emplace_back( BITMAPS::swap_layer, wxT( "swap_layer_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::swap_layer].emplace_back( BITMAPS::swap_layer, wxT( "swap_layer_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
aBitmapInfoCache[BITMAPS::switch_corner_rounding_shape].emplace_back( BITMAPS::switch_corner_rounding_shape, wxT( "switch_corner_rounding_shape_dark_24.png" ), 24, wxT( "dark" ) );
|
aBitmapInfoCache[BITMAPS::switch_corner_rounding_shape].emplace_back( BITMAPS::switch_corner_rounding_shape, wxT( "switch_corner_rounding_shape_dark_24.png" ), 24, wxT( "dark" ) );
|
||||||
|
|
|
@ -297,6 +297,7 @@ enum class BITMAPS : unsigned int
|
||||||
import_vector,
|
import_vector,
|
||||||
info,
|
info,
|
||||||
insert_module_board,
|
insert_module_board,
|
||||||
|
intersect_polygons,
|
||||||
invisible_text,
|
invisible_text,
|
||||||
kicad_icon_small,
|
kicad_icon_small,
|
||||||
label_align_left,
|
label_align_left,
|
||||||
|
@ -328,6 +329,7 @@ enum class BITMAPS : unsigned int
|
||||||
marker_next,
|
marker_next,
|
||||||
marker_previous,
|
marker_previous,
|
||||||
measurement,
|
measurement,
|
||||||
|
merge_polygons,
|
||||||
microstrip,
|
microstrip,
|
||||||
microstrip_zodd_zeven,
|
microstrip_zodd_zeven,
|
||||||
minus,
|
minus,
|
||||||
|
@ -557,6 +559,7 @@ enum class BITMAPS : unsigned int
|
||||||
stroke_dashdotdot,
|
stroke_dashdotdot,
|
||||||
stroke_dot,
|
stroke_dot,
|
||||||
stroke_solid,
|
stroke_solid,
|
||||||
|
subtract_polygons,
|
||||||
swap,
|
swap,
|
||||||
swap_layer,
|
swap_layer,
|
||||||
switch_corner_rounding_shape,
|
switch_corner_rounding_shape,
|
||||||
|
|
|
@ -116,11 +116,14 @@ static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERAC
|
||||||
|
|
||||||
menu->SetTitle( _( "Shape Modification" ) );
|
menu->SetTitle( _( "Shape Modification" ) );
|
||||||
|
|
||||||
static std::vector<KICAD_T> filletChamferTypes = { PCB_SHAPE_LOCATE_POLY_T,
|
static const std::vector<KICAD_T> filletChamferTypes = { PCB_SHAPE_LOCATE_POLY_T,
|
||||||
PCB_SHAPE_LOCATE_RECT_T,
|
PCB_SHAPE_LOCATE_RECT_T,
|
||||||
PCB_SHAPE_LOCATE_SEGMENT_T };
|
PCB_SHAPE_LOCATE_SEGMENT_T };
|
||||||
|
|
||||||
static std::vector<KICAD_T> lineExtendTypes = { PCB_SHAPE_LOCATE_SEGMENT_T };
|
static const std::vector<KICAD_T> lineExtendTypes = { PCB_SHAPE_LOCATE_SEGMENT_T };
|
||||||
|
|
||||||
|
static const std::vector<KICAD_T> polygonBooleanTypes = { PCB_SHAPE_LOCATE_RECT_T,
|
||||||
|
PCB_SHAPE_LOCATE_POLY_T };
|
||||||
|
|
||||||
auto hasCornerCondition =
|
auto hasCornerCondition =
|
||||||
[aTool]( const SELECTION& aSelection )
|
[aTool]( const SELECTION& aSelection )
|
||||||
|
@ -145,6 +148,15 @@ static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERAC
|
||||||
&& SELECTION_CONDITIONS::Count( 2 ) );
|
&& SELECTION_CONDITIONS::Count( 2 ) );
|
||||||
menu->AddItem( PCB_ACTIONS::pointEditorMoveCorner, hasCornerCondition );
|
menu->AddItem( PCB_ACTIONS::pointEditorMoveCorner, hasCornerCondition );
|
||||||
menu->AddItem( PCB_ACTIONS::pointEditorMoveMidpoint, hasMidpointCondition );
|
menu->AddItem( PCB_ACTIONS::pointEditorMoveMidpoint, hasMidpointCondition );
|
||||||
|
|
||||||
|
menu->AddSeparator();
|
||||||
|
|
||||||
|
menu->AddItem( PCB_ACTIONS::mergePolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
|
||||||
|
&& SELECTION_CONDITIONS::MoreThan( 1 ) );
|
||||||
|
menu->AddItem( PCB_ACTIONS::subtractPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
|
||||||
|
&& SELECTION_CONDITIONS::MoreThan( 1 ) );
|
||||||
|
menu->AddItem( PCB_ACTIONS::intersectPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
|
||||||
|
&& SELECTION_CONDITIONS::MoreThan( 1 ) );
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
|
@ -1394,6 +1406,117 @@ int EDIT_TOOL::ModifyLines( const TOOL_EVENT& aEvent )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int EDIT_TOOL::BooleanPolygons( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
PCB_SELECTION& selection = m_selectionTool->RequestSelection(
|
||||||
|
[]( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
|
||||||
|
{
|
||||||
|
// Iterate from the back so we don't have to worry about removals.
|
||||||
|
for( int i = aCollector.GetCount() - 1; i >= 0; --i )
|
||||||
|
{
|
||||||
|
BOARD_ITEM* item = aCollector[i];
|
||||||
|
|
||||||
|
if( !item->IsType( {
|
||||||
|
PCB_SHAPE_LOCATE_POLY_T,
|
||||||
|
PCB_SHAPE_LOCATE_RECT_T,
|
||||||
|
} ) )
|
||||||
|
{
|
||||||
|
aCollector.Remove( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true /* prompt user regarding locked items */ );
|
||||||
|
|
||||||
|
const EDA_ITEM* const last_item = selection.GetLastAddedItem();
|
||||||
|
|
||||||
|
// Gather or construct polygon source shapes to merge
|
||||||
|
std::vector<PCB_SHAPE*> items_to_process;
|
||||||
|
for( EDA_ITEM* item : selection )
|
||||||
|
{
|
||||||
|
items_to_process.push_back( static_cast<PCB_SHAPE*>( item ) );
|
||||||
|
|
||||||
|
// put the last one in the selection at the front of the vector
|
||||||
|
// so it can be used as the property donor and as the basis for the
|
||||||
|
// boolean operation
|
||||||
|
if( item == last_item )
|
||||||
|
{
|
||||||
|
std::swap( items_to_process.back(), items_to_process.front() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOARD_COMMIT commit{ this };
|
||||||
|
|
||||||
|
// Handle modifications to existing items by the routine
|
||||||
|
const auto item_modification_handler = [&]( PCB_SHAPE& aItem )
|
||||||
|
{
|
||||||
|
commit.Modify( &aItem );
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<PCB_SHAPE*> items_to_select_on_success;
|
||||||
|
const auto item_creation_handler = [&]( std::unique_ptr<PCB_SHAPE> aItem )
|
||||||
|
{
|
||||||
|
items_to_select_on_success.push_back( aItem.get() );
|
||||||
|
commit.Add( aItem.release() );
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto item_removal_handler = [&]( PCB_SHAPE& aItem )
|
||||||
|
{
|
||||||
|
commit.Remove( &aItem );
|
||||||
|
};
|
||||||
|
|
||||||
|
// Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
|
||||||
|
ITEM_MODIFICATION_ROUTINE::CALLABLE_BASED_HANDLER change_handler(
|
||||||
|
item_creation_handler, item_modification_handler, item_removal_handler );
|
||||||
|
|
||||||
|
// Construct an appropriate routine
|
||||||
|
std::unique_ptr<POLYGON_BOOLEAN_ROUTINE> boolean_routine;
|
||||||
|
if( aEvent.IsAction( &PCB_ACTIONS::mergePolygons ) )
|
||||||
|
{
|
||||||
|
boolean_routine =
|
||||||
|
std::make_unique<POLYGON_MERGE_ROUTINE>( frame()->GetModel(), change_handler );
|
||||||
|
}
|
||||||
|
else if( aEvent.IsAction( &PCB_ACTIONS::subtractPolygons ) )
|
||||||
|
{
|
||||||
|
boolean_routine =
|
||||||
|
std::make_unique<POLYGON_SUBTRACT_ROUTINE>( frame()->GetModel(), change_handler );
|
||||||
|
}
|
||||||
|
else if( aEvent.IsAction( &PCB_ACTIONS::intersectPolygons ) )
|
||||||
|
{
|
||||||
|
boolean_routine =
|
||||||
|
std::make_unique<POLYGON_INTERSECT_ROUTINE>( frame()->GetModel(), change_handler );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxASSERT_MSG( false, "Could not find a polygon routine for this action" );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the operation on each polygon
|
||||||
|
for( PCB_SHAPE* shape : items_to_process )
|
||||||
|
{
|
||||||
|
boolean_routine->ProcessShape( *shape );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select new items
|
||||||
|
for( PCB_SHAPE* item : items_to_select_on_success )
|
||||||
|
{
|
||||||
|
m_selectionTool->AddItemToSel( item, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify other tools of the changes
|
||||||
|
m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
|
||||||
|
|
||||||
|
commit.Push( boolean_routine->GetCommitDescription() );
|
||||||
|
|
||||||
|
if( const std::optional<wxString> msg = boolean_routine->GetStatusMessage() )
|
||||||
|
{
|
||||||
|
frame()->ShowInfoBarMsg( *msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
|
int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
||||||
|
@ -2642,6 +2765,9 @@ void EDIT_TOOL::setTransitions()
|
||||||
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::filletLines.MakeEvent() );
|
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::filletLines.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::chamferLines.MakeEvent() );
|
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::chamferLines.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::extendLines.MakeEvent() );
|
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::extendLines.MakeEvent() );
|
||||||
|
Go( &EDIT_TOOL::BooleanPolygons, PCB_ACTIONS::mergePolygons.MakeEvent() );
|
||||||
|
Go( &EDIT_TOOL::BooleanPolygons, PCB_ACTIONS::subtractPolygons.MakeEvent() );
|
||||||
|
Go( &EDIT_TOOL::BooleanPolygons, PCB_ACTIONS::intersectPolygons.MakeEvent() );
|
||||||
|
|
||||||
Go( &EDIT_TOOL::copyToClipboard, ACTIONS::copy.MakeEvent() );
|
Go( &EDIT_TOOL::copyToClipboard, ACTIONS::copy.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::copyToClipboard, PCB_ACTIONS::copyWithReference.MakeEvent() );
|
Go( &EDIT_TOOL::copyToClipboard, PCB_ACTIONS::copyWithReference.MakeEvent() );
|
||||||
|
|
|
@ -126,6 +126,12 @@ public:
|
||||||
*/
|
*/
|
||||||
int ModifyLines( const TOOL_EVENT& aEvent );
|
int ModifyLines( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify selected polygons into a single polygon using boolean operations
|
||||||
|
* such as merge (union) or subtract (difference)
|
||||||
|
*/
|
||||||
|
int BooleanPolygons( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete currently selected items.
|
* Delete currently selected items.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -301,3 +301,194 @@ void LINE_EXTENSION_ROUTINE::ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLin
|
||||||
|
|
||||||
AddSuccess();
|
AddSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void POLYGON_BOOLEAN_ROUTINE::ProcessShape( PCB_SHAPE& aPcbShape )
|
||||||
|
{
|
||||||
|
std::unique_ptr<SHAPE_POLY_SET> poly;
|
||||||
|
|
||||||
|
switch( aPcbShape.GetShape() )
|
||||||
|
{
|
||||||
|
case SHAPE_T::POLY:
|
||||||
|
{
|
||||||
|
poly = std::make_unique<SHAPE_POLY_SET>( aPcbShape.GetPolyShape() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SHAPE_T::RECTANGLE:
|
||||||
|
{
|
||||||
|
SHAPE_POLY_SET rect_poly;
|
||||||
|
|
||||||
|
const std::vector<VECTOR2I> rect_pts = aPcbShape.GetRectCorners();
|
||||||
|
|
||||||
|
rect_poly.NewOutline();
|
||||||
|
|
||||||
|
for( const VECTOR2I& pt : rect_pts )
|
||||||
|
{
|
||||||
|
rect_poly.Append( pt );
|
||||||
|
}
|
||||||
|
|
||||||
|
poly = std::make_unique<SHAPE_POLY_SET>( std::move( rect_poly ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !poly )
|
||||||
|
{
|
||||||
|
// Not a polygon or rectangle, nothing to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !m_workingPolygon )
|
||||||
|
{
|
||||||
|
auto initial = std::make_unique<PCB_SHAPE>( GetBoard(), SHAPE_T::POLY );
|
||||||
|
initial->SetPolyShape( *poly );
|
||||||
|
|
||||||
|
// Copy properties
|
||||||
|
initial->SetLayer( aPcbShape.GetLayer() );
|
||||||
|
initial->SetWidth( aPcbShape.GetWidth() );
|
||||||
|
|
||||||
|
// Keep the pointer
|
||||||
|
m_workingPolygon = initial.get();
|
||||||
|
// Hand over ownership
|
||||||
|
GetHandler().AddNewItem( std::move( initial ) );
|
||||||
|
|
||||||
|
// And remove the shape
|
||||||
|
GetHandler().DeleteItem( aPcbShape );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( ProcessSubsequentPolygon( *poly ) )
|
||||||
|
{
|
||||||
|
// If we could process the polygon, delete the source
|
||||||
|
GetHandler().DeleteItem( aPcbShape );
|
||||||
|
AddSuccess();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddFailure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wxString POLYGON_MERGE_ROUTINE::GetCommitDescription() const
|
||||||
|
{
|
||||||
|
return _( "Merge polygons." );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::optional<wxString> POLYGON_MERGE_ROUTINE::GetStatusMessage() const
|
||||||
|
{
|
||||||
|
if( GetSuccesses() == 0 )
|
||||||
|
{
|
||||||
|
return _( "Unable to merge the selected polygons." );
|
||||||
|
}
|
||||||
|
else if( GetFailures() > 0 )
|
||||||
|
{
|
||||||
|
return _( "Some of the polygons could not be merged." );
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool POLYGON_MERGE_ROUTINE::ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon )
|
||||||
|
{
|
||||||
|
const SHAPE_POLY_SET::POLYGON_MODE poly_mode = SHAPE_POLY_SET::POLYGON_MODE::PM_FAST;
|
||||||
|
|
||||||
|
SHAPE_POLY_SET working_copy = GetWorkingPolygon()->GetPolyShape();
|
||||||
|
working_copy.BooleanAdd( aPolygon, poly_mode );
|
||||||
|
|
||||||
|
// Check it's not disjoint - this doesn't work well in the UI
|
||||||
|
if( working_copy.OutlineCount() != 1 )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetWorkingPolygon()->SetPolyShape( working_copy );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wxString POLYGON_SUBTRACT_ROUTINE::GetCommitDescription() const
|
||||||
|
{
|
||||||
|
return _( "Subtract polygons." );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::optional<wxString> POLYGON_SUBTRACT_ROUTINE::GetStatusMessage() const
|
||||||
|
{
|
||||||
|
if( GetSuccesses() == 0 )
|
||||||
|
{
|
||||||
|
return _( "Unable to subtract the selected polygons." );
|
||||||
|
}
|
||||||
|
else if( GetFailures() > 0 )
|
||||||
|
{
|
||||||
|
return _( "Some of the polygons could not be subtracted." );
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool POLYGON_SUBTRACT_ROUTINE::ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon )
|
||||||
|
{
|
||||||
|
const SHAPE_POLY_SET::POLYGON_MODE poly_mode = SHAPE_POLY_SET::POLYGON_MODE::PM_FAST;
|
||||||
|
|
||||||
|
SHAPE_POLY_SET working_copy = GetWorkingPolygon()->GetPolyShape();
|
||||||
|
working_copy.BooleanSubtract( aPolygon, poly_mode );
|
||||||
|
|
||||||
|
// Subtraction can create holes or delete the polygon
|
||||||
|
// In theory we can allow holes as the EDA_SHAPE will fracture for us, but that's
|
||||||
|
// probably not what the user has in mind (?)
|
||||||
|
if( working_copy.OutlineCount() != 1 || working_copy.HoleCount( 0 ) > 0
|
||||||
|
|| working_copy.VertexCount( 0 ) == 0 )
|
||||||
|
{
|
||||||
|
// If that happens, just skip the operation
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetWorkingPolygon()->SetPolyShape( working_copy );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString POLYGON_INTERSECT_ROUTINE::GetCommitDescription() const
|
||||||
|
{
|
||||||
|
return _( "Intersect polygons." );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::optional<wxString> POLYGON_INTERSECT_ROUTINE::GetStatusMessage() const
|
||||||
|
{
|
||||||
|
if( GetSuccesses() == 0 )
|
||||||
|
{
|
||||||
|
return _( "Unable to intersect the selected polygons." );
|
||||||
|
}
|
||||||
|
else if( GetFailures() > 0 )
|
||||||
|
{
|
||||||
|
return _( "Some of the polygons could not be intersected." );
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool POLYGON_INTERSECT_ROUTINE::ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon )
|
||||||
|
{
|
||||||
|
const SHAPE_POLY_SET::POLYGON_MODE poly_mode = SHAPE_POLY_SET::POLYGON_MODE::PM_FAST;
|
||||||
|
|
||||||
|
SHAPE_POLY_SET working_copy = GetWorkingPolygon()->GetPolyShape();
|
||||||
|
working_copy.BooleanIntersection( aPolygon, poly_mode );
|
||||||
|
|
||||||
|
// Is there anything left?
|
||||||
|
if( working_copy.OutlineCount() == 0 )
|
||||||
|
{
|
||||||
|
// There was no intersection. Rather than deleting the working polygon, we'll skip
|
||||||
|
// and report a failure.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetWorkingPolygon()->SetPolyShape( working_copy );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -297,4 +297,74 @@ public:
|
||||||
void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) override;
|
void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A routine that modifies polygons using boolean operations
|
||||||
|
*/
|
||||||
|
class POLYGON_BOOLEAN_ROUTINE : public ITEM_MODIFICATION_ROUTINE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
POLYGON_BOOLEAN_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) :
|
||||||
|
ITEM_MODIFICATION_ROUTINE( aBoard, aHandler ), m_workingPolygon( nullptr )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessShape( PCB_SHAPE& aPcbShape );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PCB_SHAPE* GetWorkingPolygon() const { return m_workingPolygon; }
|
||||||
|
|
||||||
|
virtual bool ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PCB_SHAPE* m_workingPolygon;
|
||||||
|
};
|
||||||
|
|
||||||
|
class POLYGON_MERGE_ROUTINE : public POLYGON_BOOLEAN_ROUTINE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
POLYGON_MERGE_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) :
|
||||||
|
POLYGON_BOOLEAN_ROUTINE( aBoard, aHandler )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString GetCommitDescription() const override;
|
||||||
|
std::optional<wxString> GetStatusMessage() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class POLYGON_SUBTRACT_ROUTINE : public POLYGON_BOOLEAN_ROUTINE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
POLYGON_SUBTRACT_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) :
|
||||||
|
POLYGON_BOOLEAN_ROUTINE( aBoard, aHandler )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString GetCommitDescription() const override;
|
||||||
|
std::optional<wxString> GetStatusMessage() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class POLYGON_INTERSECT_ROUTINE : public POLYGON_BOOLEAN_ROUTINE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
POLYGON_INTERSECT_ROUTINE( BOARD_ITEM* aBoard, CHANGE_HANDLER& aHandler ) :
|
||||||
|
POLYGON_BOOLEAN_ROUTINE( aBoard, aHandler )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString GetCommitDescription() const override;
|
||||||
|
std::optional<wxString> GetStatusMessage() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* ITEM_MODIFICATION_ROUTINE_H_ */
|
#endif /* ITEM_MODIFICATION_ROUTINE_H_ */
|
||||||
|
|
|
@ -585,6 +585,27 @@ TOOL_ACTION PCB_ACTIONS::extendLines( TOOL_ACTION_ARGS()
|
||||||
.MenuText( _( "Extend Lines to Meet" ) )
|
.MenuText( _( "Extend Lines to Meet" ) )
|
||||||
.Tooltip( _( "Extend lines to meet each other" ) ) );
|
.Tooltip( _( "Extend lines to meet each other" ) ) );
|
||||||
|
|
||||||
|
TOOL_ACTION PCB_ACTIONS::mergePolygons( TOOL_ACTION_ARGS()
|
||||||
|
.Name( "pcbnew.InteractiveEdit.mergePolygons" )
|
||||||
|
.Scope( AS_GLOBAL )
|
||||||
|
.MenuText( _( "Merge Polygons" ) )
|
||||||
|
.Tooltip( _( "Merge selected polygons into a single polygon" ) )
|
||||||
|
.Icon( BITMAPS::merge_polygons ) );
|
||||||
|
|
||||||
|
TOOL_ACTION PCB_ACTIONS::subtractPolygons( TOOL_ACTION_ARGS()
|
||||||
|
.Name( "pcbnew.InteractiveEdit.subtractPolygons" )
|
||||||
|
.Scope( AS_GLOBAL )
|
||||||
|
.MenuText( _( "Subtract Polygons" ) )
|
||||||
|
.Tooltip( _( "Subtract selected polygons from the last one selected" ) )
|
||||||
|
.Icon( BITMAPS::subtract_polygons ) );
|
||||||
|
|
||||||
|
TOOL_ACTION PCB_ACTIONS::intersectPolygons( TOOL_ACTION_ARGS()
|
||||||
|
.Name( "pcbnew.InteractiveEdit.intersectPolygons" )
|
||||||
|
.Scope( AS_GLOBAL )
|
||||||
|
.MenuText( _( "Intersect Polygons" ) )
|
||||||
|
.Tooltip( _( "Create the intersection of the selected polygons" ) )
|
||||||
|
.Icon( BITMAPS::intersect_polygons ) );
|
||||||
|
|
||||||
TOOL_ACTION PCB_ACTIONS::deleteFull( TOOL_ACTION_ARGS()
|
TOOL_ACTION PCB_ACTIONS::deleteFull( TOOL_ACTION_ARGS()
|
||||||
.Name( "pcbnew.InteractiveEdit.deleteFull" )
|
.Name( "pcbnew.InteractiveEdit.deleteFull" )
|
||||||
.Scope( AS_GLOBAL )
|
.Scope( AS_GLOBAL )
|
||||||
|
|
|
@ -160,6 +160,13 @@ public:
|
||||||
/// Extend selected lines to meet at a point
|
/// Extend selected lines to meet at a point
|
||||||
static TOOL_ACTION extendLines;
|
static TOOL_ACTION extendLines;
|
||||||
|
|
||||||
|
/// Merge multiple polygons into a single polygon
|
||||||
|
static TOOL_ACTION mergePolygons;
|
||||||
|
/// Subtract polygons from other polygons
|
||||||
|
static TOOL_ACTION subtractPolygons;
|
||||||
|
/// Intersection of multiple polygons
|
||||||
|
static TOOL_ACTION intersectPolygons;
|
||||||
|
|
||||||
/// Activation of the edit tool
|
/// Activation of the edit tool
|
||||||
static TOOL_ACTION properties;
|
static TOOL_ACTION properties;
|
||||||
|
|
||||||
|
|
|
@ -327,6 +327,7 @@ set( BMAPS_MID
|
||||||
import
|
import
|
||||||
info
|
info
|
||||||
insert_module_board
|
insert_module_board
|
||||||
|
intersect_polygons
|
||||||
language
|
language
|
||||||
layers_manager
|
layers_manager
|
||||||
leave_sheet
|
leave_sheet
|
||||||
|
@ -351,7 +352,7 @@ set( BMAPS_MID
|
||||||
marker_next
|
marker_next
|
||||||
marker_previous
|
marker_previous
|
||||||
measurement
|
measurement
|
||||||
pcb_target
|
merge_polygons
|
||||||
mirror_h
|
mirror_h
|
||||||
mirror_v
|
mirror_v
|
||||||
mode_module
|
mode_module
|
||||||
|
@ -405,6 +406,7 @@ set( BMAPS_MID
|
||||||
part_properties
|
part_properties
|
||||||
paste
|
paste
|
||||||
paste_special
|
paste_special
|
||||||
|
pcb_target
|
||||||
pin2pin
|
pin2pin
|
||||||
pin_size_to
|
pin_size_to
|
||||||
pin_show_etype
|
pin_show_etype
|
||||||
|
@ -496,6 +498,7 @@ set( BMAPS_MID
|
||||||
show_back_assembly_layers
|
show_back_assembly_layers
|
||||||
special_tools
|
special_tools
|
||||||
spreadsheet
|
spreadsheet
|
||||||
|
subtract_polygons
|
||||||
swap
|
swap
|
||||||
swap_layer
|
swap_layer
|
||||||
switch_corner_rounding_shape
|
switch_corner_rounding_shape
|
||||||
|
|
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 156 B |
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 156 B |
After Width: | Height: | Size: 144 B |
After Width: | Height: | Size: 162 B |
|
@ -0,0 +1,91 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
sodipodi:docname="intersect_polygons.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2554"
|
||||||
|
inkscape:window-height="1400"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="11.313708"
|
||||||
|
inkscape:cx="41.100582"
|
||||||
|
inkscape:cy="9.3249707"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="18"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid2997"
|
||||||
|
empspacing="2"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true"
|
||||||
|
spacingx="0.5"
|
||||||
|
spacingy="0.5" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
id="rect4835"
|
||||||
|
style="fill:none;fill-opacity:0.336982;stroke:#e0e0e0;stroke-width:2;stroke-linejoin:round;stroke-opacity:1"
|
||||||
|
d="M 3,15 V 3 h 12 v 6 h 6 V 21 H 9 v -6 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ded3dd;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 15,15 V 9 H 9 v 6 z"
|
||||||
|
id="path6559"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,91 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
sodipodi:docname="merge_polygons.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2554"
|
||||||
|
inkscape:window-height="1400"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="11.313708"
|
||||||
|
inkscape:cx="48.260038"
|
||||||
|
inkscape:cy="9.148194"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="18"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid2997"
|
||||||
|
empspacing="2"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true"
|
||||||
|
spacingx="0.5"
|
||||||
|
spacingy="0.5" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 15,15 V 9 H 9 v 6 z"
|
||||||
|
id="path6559"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
id="rect4835"
|
||||||
|
style="fill:none;fill-opacity:0.336982;stroke:#ded3dd;stroke-width:2;stroke-linejoin:round"
|
||||||
|
d="M 3,15 V 3 h 12 v 6 h 6 V 21 H 9 v -6 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,91 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
sodipodi:docname="subtract_polygons.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2554"
|
||||||
|
inkscape:window-height="1400"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="8"
|
||||||
|
inkscape:cy="3.5"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="18"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid2997"
|
||||||
|
empspacing="2"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true"
|
||||||
|
spacingx="0.5"
|
||||||
|
spacingy="0.5" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 21,21 V 9 H 9 v 12 z"
|
||||||
|
id="path6559-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
id="rect4835-5"
|
||||||
|
style="fill:none;fill-opacity:0.336982;stroke:#ded3dd;stroke-width:2;stroke-linejoin:round;stroke-opacity:1"
|
||||||
|
d="M 3,15 V 3 H 15 V 9 H 9 v 6 z"
|
||||||
|
sodipodi:nodetypes="ccccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,100 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
sodipodi:docname="merge_polygons.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2554"
|
||||||
|
inkscape:window-height="1400"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="22.124583"
|
||||||
|
inkscape:cx="10.983258"
|
||||||
|
inkscape:cy="9.9662896"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="18"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showguides="true">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid2997"
|
||||||
|
empspacing="2"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true"
|
||||||
|
spacingx="0.5"
|
||||||
|
spacingy="0.5" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="9,34"
|
||||||
|
orientation="0,-1"
|
||||||
|
id="guide38552" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="9,34"
|
||||||
|
orientation="0,-1"
|
||||||
|
id="guide38554" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#545454;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 15,15 V 9 H 9 v 6 z"
|
||||||
|
id="path6559"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
id="rect4835"
|
||||||
|
style="fill:none;fill-opacity:0.336982;stroke:#b9b9b9;stroke-width:2;stroke-linejoin:round;stroke-opacity:1"
|
||||||
|
d="M 3,15 V 3 h 12 v 6 h 6 V 21 H 9 v -6 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,91 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
sodipodi:docname="merge_polygons.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2554"
|
||||||
|
inkscape:window-height="1400"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="7.8222213"
|
||||||
|
inkscape:cx="-5.113637"
|
||||||
|
inkscape:cy="18.217332"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="18"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid2997"
|
||||||
|
empspacing="2"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true"
|
||||||
|
spacingx="0.5"
|
||||||
|
spacingy="0.5" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#b9b9b9;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 15,15 V 9 H 9 v 6 z"
|
||||||
|
id="path6559"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
id="rect4835"
|
||||||
|
style="fill:none;fill-opacity:0.336982;stroke:#545454;stroke-width:2;stroke-linejoin:round;stroke-opacity:1"
|
||||||
|
d="M 3,15 V 3 h 12 v 6 h 6 V 21 H 9 v -6 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,91 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
sodipodi:docname="subtract_polygons.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2554"
|
||||||
|
inkscape:window-height="1400"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="11.062291"
|
||||||
|
inkscape:cx="14.101961"
|
||||||
|
inkscape:cy="23.819658"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="18"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid2997"
|
||||||
|
empspacing="2"
|
||||||
|
visible="true"
|
||||||
|
enabled="true"
|
||||||
|
snapvisiblegridlinesonly="true"
|
||||||
|
spacingx="0.5"
|
||||||
|
spacingy="0.5" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#b9b9b9;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 21,21 V 9 H 9 v 12 z"
|
||||||
|
id="path6559"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
id="rect4835"
|
||||||
|
style="fill:none;fill-opacity:0.336982;stroke:#545454;stroke-width:2;stroke-linejoin:round;stroke-opacity:1"
|
||||||
|
d="M 3,15 V 3 H 15 V 9 H 9 v 6 z"
|
||||||
|
sodipodi:nodetypes="ccccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.0 KiB |