You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
8.6 KiB

5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Created on: 11 Mar 2016, author John Beard
  5. * Copyright (C) 2016-2024 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include "array_creator.h"
  25. #include <array_pad_number_provider.h>
  26. #include <board_commit.h>
  27. #include <pcb_group.h>
  28. #include <pad.h>
  29. #include <dialogs/dialog_create_array.h>
  30. #include <tool/tool_manager.h>
  31. #include <tools/board_reannotate_tool.h>
  32. #include <tools/pcb_selection_tool.h>
  33. /**
  34. * Transform a #BOARD_ITEM from the given #ARRAY_OPTIONS and an index into the array.
  35. *
  36. * @param aArrOpts The array options that describe the array
  37. * @param aIndex The index in the array of this item
  38. * @param aItem The item to transform
  39. */
  40. static void TransformItem( const ARRAY_OPTIONS& aArrOpts, int aIndex, BOARD_ITEM& aItem )
  41. {
  42. const ARRAY_OPTIONS::TRANSFORM transform = aArrOpts.GetTransform( aIndex, aItem.GetPosition() );
  43. aItem.Move( transform.m_offset );
  44. aItem.Rotate( aItem.GetPosition(), transform.m_rotation );
  45. }
  46. void ARRAY_CREATOR::Invoke()
  47. {
  48. // bail out if no items
  49. if( m_selection.Size() == 0 )
  50. return;
  51. FOOTPRINT* const fp = m_isFootprintEditor ? m_parent.GetBoard()->GetFirstFootprint() : nullptr;
  52. const bool enableArrayNumbering = m_isFootprintEditor;
  53. VECTOR2I origin;
  54. if( m_selection.Size() == 1 )
  55. origin = m_selection.Items()[0]->GetPosition();
  56. else
  57. origin = m_selection.GetCenter();
  58. std::unique_ptr<ARRAY_OPTIONS> array_opts;
  59. DIALOG_CREATE_ARRAY dialog( &m_parent, array_opts, enableArrayNumbering, origin );
  60. int ret = dialog.ShowModal();
  61. if( ret != wxID_OK || array_opts == nullptr )
  62. return;
  63. BOARD_COMMIT commit( &m_parent );
  64. ARRAY_PAD_NUMBER_PROVIDER pad_number_provider( fp, *array_opts );
  65. EDA_ITEMS all_added_items;
  66. // The first item in list is the original item. We do not modify it
  67. for( int ptN = 0; ptN < array_opts->GetArraySize(); ptN++ )
  68. {
  69. PCB_SELECTION items_for_this_block;
  70. std::set<FOOTPRINT*> fpDeDupe;
  71. for ( int i = 0; i < m_selection.Size(); ++i )
  72. {
  73. BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( m_selection[ i ] );
  74. if( !item )
  75. continue;
  76. FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( item->GetParentFootprint() );
  77. // If it is not the footprint editor, then duplicate the parent footprint instead.
  78. // This check assumes that the footprint child objects are correctly parented, if
  79. // they are not, this will segfault.
  80. if( !m_isFootprintEditor && parentFootprint )
  81. {
  82. // It is possible to select multiple footprint child objects in the board editor.
  83. // Do not create multiple copies of the same footprint when this occurs.
  84. if( fpDeDupe.count( parentFootprint ) == 0 )
  85. {
  86. fpDeDupe.emplace( parentFootprint );
  87. item = parentFootprint;
  88. }
  89. else
  90. {
  91. continue;
  92. }
  93. }
  94. BOARD_ITEM* this_item = nullptr;
  95. if( ptN == 0 )
  96. {
  97. // the first point: we don't own this or add it, but
  98. // we might still modify it (position or label)
  99. this_item = item;
  100. commit.Modify( this_item );
  101. TransformItem( *array_opts, ptN, *this_item );
  102. }
  103. else
  104. {
  105. if( m_isFootprintEditor )
  106. {
  107. // Fields cannot be duplicated, especially mandatory fields.
  108. // A given field is unique for the footprint
  109. if( item->Type() == PCB_FIELD_T )
  110. continue;
  111. // Don't bother incrementing pads: the footprint won't update until commit,
  112. // so we can only do this once
  113. this_item = fp->DuplicateItem( item );
  114. }
  115. else
  116. {
  117. switch( item->Type() )
  118. {
  119. case PCB_FOOTPRINT_T:
  120. case PCB_SHAPE_T:
  121. case PCB_REFERENCE_IMAGE_T:
  122. case PCB_GENERATOR_T:
  123. case PCB_TEXT_T:
  124. case PCB_TEXTBOX_T:
  125. case PCB_TABLE_T:
  126. case PCB_TRACE_T:
  127. case PCB_ARC_T:
  128. case PCB_VIA_T:
  129. case PCB_DIM_ALIGNED_T:
  130. case PCB_DIM_CENTER_T:
  131. case PCB_DIM_RADIAL_T:
  132. case PCB_DIM_ORTHOGONAL_T:
  133. case PCB_DIM_LEADER_T:
  134. case PCB_TARGET_T:
  135. case PCB_ZONE_T:
  136. this_item = item->Duplicate();
  137. break;
  138. case PCB_GROUP_T:
  139. this_item = static_cast<PCB_GROUP*>( item )->DeepDuplicate();
  140. break;
  141. default:
  142. // Silently drop other items (such as footprint texts) from duplication
  143. break;
  144. }
  145. }
  146. // Add new items to selection (footprints in the selection will be reannotated)
  147. items_for_this_block.Add( this_item );
  148. if( this_item )
  149. {
  150. // Because aItem is/can be created from a selected item, and inherits from
  151. // it this state, reset the selected stated of aItem:
  152. this_item->ClearSelected();
  153. this_item->RunOnDescendants(
  154. []( BOARD_ITEM* aItem )
  155. {
  156. aItem->ClearSelected();
  157. } );
  158. TransformItem( *array_opts, ptN, *this_item );
  159. // If a group is duplicated, add also created members to the board
  160. if( this_item->Type() == PCB_GROUP_T )
  161. {
  162. this_item->RunOnDescendants(
  163. [&]( BOARD_ITEM* aItem )
  164. {
  165. commit.Add( aItem );
  166. } );
  167. }
  168. commit.Add( this_item );
  169. }
  170. }
  171. // attempt to renumber items if the array parameters define
  172. // a complete numbering scheme to number by (as opposed to
  173. // implicit numbering by incrementing the items during creation
  174. if( this_item && array_opts->ShouldNumberItems() )
  175. {
  176. // Renumber non-aperture pads.
  177. if( this_item->Type() == PCB_PAD_T )
  178. {
  179. PAD& pad = static_cast<PAD&>( *this_item );
  180. if( pad.CanHaveNumber() )
  181. {
  182. wxString newNumber = pad_number_provider.GetNextPadNumber();
  183. pad.SetNumber( newNumber );
  184. }
  185. }
  186. }
  187. }
  188. if( !m_isFootprintEditor && array_opts->ShouldReannotateFootprints() )
  189. {
  190. m_toolMgr->GetTool<BOARD_REANNOTATE_TOOL>()->ReannotateDuplicates( items_for_this_block,
  191. all_added_items );
  192. }
  193. for( EDA_ITEM* item : items_for_this_block )
  194. all_added_items.push_back( item );
  195. }
  196. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
  197. m_toolMgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &all_added_items );
  198. commit.Push( _( "Create Array" ) );
  199. }