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.

394 lines
12 KiB

6 years ago
14 years ago
6 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2015-2016 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <confirm.h>
  27. #include <widgets/unit_binder.h>
  28. #include <pcb_edit_frame.h>
  29. #include <dialog_shim.h>
  30. #include <locale_io.h>
  31. #include <filter_reader.h>
  32. #include <dialogs/dialog_text_entry.h>
  33. #include <board.h>
  34. #include <footprint.h>
  35. #include <pcb_shape.h>
  36. #include <microwave/microwave_tool.h>
  37. #include <pad.h>
  38. #include <math/util.h> // for KiROUND
  39. #include <wx/button.h>
  40. #include <wx/dialog.h>
  41. #include <wx/filedlg.h>
  42. #include <wx/radiobox.h>
  43. #include <wx/sizer.h>
  44. #include <wx/statbox.h>
  45. static std::vector< wxRealPoint > g_PolyEdges;
  46. static double g_ShapeScaleX, g_ShapeScaleY;
  47. static wxSize g_ShapeSize;
  48. static int g_PolyShapeType;
  49. enum id_mw_cmd {
  50. ID_READ_SHAPE_FILE = 1000
  51. };
  52. class MWAVE_POLYGONAL_SHAPE_DLG : public DIALOG_SHIM
  53. {
  54. public:
  55. MWAVE_POLYGONAL_SHAPE_DLG( PCB_EDIT_FRAME* parent, const wxPoint& pos );
  56. ~MWAVE_POLYGONAL_SHAPE_DLG()
  57. {
  58. delete m_sizeX;
  59. delete m_sizeY;
  60. };
  61. bool TransferDataFromWindow() override;
  62. private:
  63. void OnCancelClick( wxCommandEvent& event );
  64. /**
  65. * Read a description shape file.
  66. *
  67. * File format is
  68. * Unit=MM
  69. * XScale=271.501
  70. * YScale=1.00133
  71. *
  72. * $COORD
  73. * 0 0.6112600148417837
  74. * 0.001851851851851852 0.6104800531118608
  75. * ....
  76. * $ENDCOORD
  77. *
  78. * Each line is the X Y coord (normalized units from 0 to 1)
  79. */
  80. void ReadDataShapeDescr( wxCommandEvent& event );
  81. DECLARE_EVENT_TABLE()
  82. PCB_EDIT_FRAME* m_frame;
  83. wxRadioBox* m_shapeOptionCtrl;
  84. UNIT_BINDER* m_sizeX;
  85. UNIT_BINDER* m_sizeY;
  86. };
  87. BEGIN_EVENT_TABLE( MWAVE_POLYGONAL_SHAPE_DLG, DIALOG_SHIM )
  88. EVT_BUTTON( wxID_CANCEL, MWAVE_POLYGONAL_SHAPE_DLG::OnCancelClick )
  89. EVT_BUTTON( ID_READ_SHAPE_FILE, MWAVE_POLYGONAL_SHAPE_DLG::ReadDataShapeDescr )
  90. END_EVENT_TABLE()
  91. MWAVE_POLYGONAL_SHAPE_DLG::MWAVE_POLYGONAL_SHAPE_DLG( PCB_EDIT_FRAME* parent,
  92. const wxPoint& framepos ) :
  93. DIALOG_SHIM( parent, -1, _( "Complex Shape" ), framepos, wxDefaultSize,
  94. wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
  95. m_sizeX(),
  96. m_sizeY()
  97. {
  98. m_frame = parent;
  99. g_PolyEdges.clear();
  100. wxBoxSizer* mainBoxSizer = new wxBoxSizer( wxVERTICAL );
  101. SetSizer( mainBoxSizer );
  102. // Controls
  103. wxBoxSizer* topBoxSizer = new wxBoxSizer( wxHORIZONTAL );
  104. mainBoxSizer->Add( topBoxSizer, 1, wxGROW | wxALL, 5 );
  105. wxString shapelist[] = { _( "Normal" ), _( "Symmetrical" ), _( "Mirrored" ) };
  106. m_shapeOptionCtrl = new wxRadioBox( this, -1, _( "Shape" ),
  107. wxDefaultPosition, wxDefaultSize, 3,
  108. shapelist, 1,
  109. wxRA_SPECIFY_COLS );
  110. topBoxSizer->Add( m_shapeOptionCtrl, 1, wxGROW | wxALL, 5 );
  111. auto sizeSizer = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _( "Size" ) ),
  112. wxVERTICAL );
  113. wxBoxSizer* xSizer = new wxBoxSizer( wxHORIZONTAL );
  114. wxBoxSizer* ySizer = new wxBoxSizer( wxHORIZONTAL );
  115. topBoxSizer->Add( sizeSizer, 1, wxGROW | wxALL, 5 );
  116. sizeSizer->Add( xSizer, 0, 0, 5 );
  117. sizeSizer->Add( ySizer, 0, 0, 5 );
  118. wxStaticText* xLabel = new wxStaticText( this, -1, _( "X:" ) );
  119. wxTextCtrl* xCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString );
  120. wxStaticText* xUnits = new wxStaticText( this, -1, _( "units" ) );
  121. xSizer->Add( xLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
  122. xSizer->Add( xCtrl, 1, wxALIGN_CENTER_VERTICAL, 5 );
  123. xSizer->Add( xUnits, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
  124. m_sizeX = new UNIT_BINDER( m_frame, xLabel, xCtrl, xUnits );
  125. wxStaticText* yLabel = new wxStaticText( this, -1, _( "Y:" ) );
  126. wxTextCtrl* yCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString );
  127. wxStaticText* yUnits = new wxStaticText( this, -1, _( "units" ) );
  128. ySizer->Add( yLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
  129. ySizer->Add( yCtrl, 1, wxALIGN_CENTER_VERTICAL, 5 );
  130. ySizer->Add( yUnits, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
  131. m_sizeY = new UNIT_BINDER( m_frame, yLabel, yCtrl, yUnits );
  132. // Buttons
  133. wxBoxSizer* buttonsBoxSizer = new wxBoxSizer( wxHORIZONTAL );
  134. mainBoxSizer->Add( buttonsBoxSizer, 0, wxALL, 5 );
  135. wxButton* btn = new wxButton( this, ID_READ_SHAPE_FILE, _( "Read Shape Description File..." ) );
  136. buttonsBoxSizer->Add( btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 10 );
  137. buttonsBoxSizer->AddStretchSpacer();
  138. wxStdDialogButtonSizer* sdbSizer = new wxStdDialogButtonSizer();
  139. buttonsBoxSizer->Add( sdbSizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
  140. wxButton* sdbSizerOK = new wxButton( this, wxID_OK );
  141. sdbSizer->AddButton( sdbSizerOK );
  142. wxButton* sdbSizerCancel = new wxButton( this, wxID_CANCEL );
  143. sdbSizer->AddButton( sdbSizerCancel );
  144. sdbSizer->Realize();
  145. GetSizer()->SetSizeHints( this );
  146. }
  147. void MWAVE_POLYGONAL_SHAPE_DLG::OnCancelClick( wxCommandEvent& event )
  148. {
  149. g_PolyEdges.clear();
  150. event.Skip();
  151. }
  152. bool MWAVE_POLYGONAL_SHAPE_DLG::TransferDataFromWindow()
  153. {
  154. if( !wxDialog::TransferDataFromWindow() )
  155. return false;
  156. g_ShapeSize.x = m_sizeX->GetValue();
  157. g_ShapeSize.y = m_sizeY->GetValue();
  158. g_PolyShapeType = m_shapeOptionCtrl->GetSelection();
  159. return true;
  160. }
  161. void MWAVE_POLYGONAL_SHAPE_DLG::ReadDataShapeDescr( wxCommandEvent& event )
  162. {
  163. static wxString s_lastpath; // To remember the last open path during a session
  164. wxString fullFileName;
  165. wxString mask = wxFileSelectorDefaultWildcardStr;
  166. fullFileName = wxFileSelector( _( "Shape Description File" ), s_lastpath,
  167. fullFileName, wxEmptyString, mask, wxFD_OPEN, this );
  168. if( fullFileName.IsEmpty() )
  169. return;
  170. wxFileName fn( fullFileName );
  171. s_lastpath = fn.GetPath();
  172. g_PolyEdges.clear();
  173. FILE* File = wxFopen( fullFileName, wxT( "rt" ) );
  174. if( File == nullptr )
  175. {
  176. DisplayError( this, _( "File not found" ) );
  177. return;
  178. }
  179. double unitconv = pcbIUScale.IU_PER_MM;
  180. g_ShapeScaleX = g_ShapeScaleY = 1.0;
  181. FILE_LINE_READER fileReader( File, fullFileName );
  182. FILTER_READER reader( fileReader );
  183. LOCALE_IO toggle;
  184. while( reader.ReadLine() )
  185. {
  186. char* Line = reader.Line();
  187. char* param1 = strtok( Line, " =\n\r" );
  188. char* param2 = strtok( nullptr, " \t\n\r" );
  189. if( strncasecmp( param1, "Unit", 4 ) == 0 )
  190. {
  191. if( strncasecmp( param2, "inch", 4 ) == 0 )
  192. unitconv = pcbIUScale.IU_PER_MILS*1000;
  193. if( strncasecmp( param2, "mm", 2 ) == 0 )
  194. unitconv = pcbIUScale.IU_PER_MM;
  195. }
  196. if( strncasecmp( param1, "$ENDCOORD", 8 ) == 0 )
  197. break;
  198. if( strncasecmp( param1, "$COORD", 6 ) == 0 )
  199. {
  200. while( reader.ReadLine() )
  201. {
  202. Line = reader.Line();
  203. param1 = strtok( Line, " \t\n\r" );
  204. param2 = strtok( nullptr, " \t\n\r" );
  205. if( strncasecmp( param1, "$ENDCOORD", 8 ) == 0 )
  206. break;
  207. wxRealPoint coord( atof( param1 ), atof( param2 ) );
  208. g_PolyEdges.push_back( coord );
  209. }
  210. }
  211. if( strncasecmp( Line, "XScale", 6 ) == 0 )
  212. g_ShapeScaleX = atof( param2 );
  213. if( strncasecmp( Line, "YScale", 6 ) == 0 )
  214. g_ShapeScaleY = atof( param2 );
  215. }
  216. g_ShapeScaleX *= unitconv;
  217. g_ShapeScaleY *= unitconv;
  218. m_sizeX->SetValue( (int) g_ShapeScaleX );
  219. m_sizeY->SetValue( (int) g_ShapeScaleY );
  220. }
  221. FOOTPRINT* MICROWAVE_TOOL::createPolygonShape()
  222. {
  223. PAD* pad1;
  224. PAD* pad2;
  225. FOOTPRINT* footprint;
  226. wxString cmp_name;
  227. int pad_count = 2;
  228. PCB_SHAPE* shape;
  229. PCB_EDIT_FRAME& editFrame = *getEditFrame<PCB_EDIT_FRAME>();
  230. MWAVE_POLYGONAL_SHAPE_DLG dlg( &editFrame, wxDefaultPosition );
  231. int ret = dlg.ShowModal();
  232. if( ret != wxID_OK )
  233. {
  234. g_PolyEdges.clear();
  235. return nullptr;
  236. }
  237. if( g_PolyShapeType == 2 ) // mirrored
  238. g_ShapeScaleY = -g_ShapeScaleY;
  239. g_ShapeSize.x = KiROUND( g_ShapeScaleX );
  240. g_ShapeSize.y = KiROUND( g_ShapeScaleY );
  241. if(( g_ShapeSize.x ) == 0 || ( g_ShapeSize.y == 0 ) )
  242. {
  243. editFrame.ShowInfoBarError( _( "Shape has a null size." ) );
  244. return nullptr;
  245. }
  246. if( g_PolyEdges.size() == 0 )
  247. {
  248. editFrame.ShowInfoBarError( _( "Shape has no points." ) );
  249. return nullptr;
  250. }
  251. cmp_name = wxT( "muwave_polygon" );
  252. // Create a footprint with 2 pads, orientation = 0, pos 0
  253. footprint = createBaseFootprint( cmp_name, 0, pad_count );
  254. // We try to place the footprint anchor to the middle of the shape len
  255. VECTOR2I offset;
  256. offset.x = -g_ShapeSize.x / 2;
  257. auto it = footprint->Pads().begin();
  258. pad1 = *it;
  259. pad1->SetX( offset.x );
  260. pad2 = *( ++it );
  261. pad2->SetX( offset.x + g_ShapeSize.x );
  262. // Add a polygonal edge (corners will be added later) on copper layer
  263. shape = new PCB_SHAPE( footprint, SHAPE_T::POLY );
  264. shape->SetFilled( true );
  265. shape->SetLayer( F_Cu );
  266. footprint->Add( shape, ADD_MODE::INSERT );
  267. // Get the corner buffer of the polygonal edge
  268. std::vector<VECTOR2I> polyPoints;
  269. polyPoints.reserve( g_PolyEdges.size() + 2 );
  270. // Init start point coord:
  271. polyPoints.emplace_back( VECTOR2I( offset.x, 0 ) );
  272. VECTOR2I last_coordinate;
  273. for( wxRealPoint& pt: g_PolyEdges ) // Copy points
  274. {
  275. last_coordinate.x = KiROUND( pt.x * g_ShapeScaleX );
  276. last_coordinate.y = -KiROUND( pt.y * g_ShapeScaleY );
  277. last_coordinate += offset;
  278. polyPoints.push_back( last_coordinate );
  279. }
  280. // finish the polygonal shape
  281. if( last_coordinate.y != 0 )
  282. polyPoints.emplace_back( VECTOR2I( last_coordinate.x, 0 ) );
  283. switch( g_PolyShapeType )
  284. {
  285. case 0: // shape from file
  286. case 2: // shape from file, mirrored (the mirror is already done)
  287. break;
  288. case 1: // Symmetric shape: add the symmetric (mirrored) shape
  289. for( int ndx = (int) polyPoints.size() - 1; ndx >= 0; --ndx )
  290. {
  291. VECTOR2I pt = polyPoints[ndx];
  292. pt.y = -pt.y; // mirror about X axis
  293. polyPoints.push_back( pt );
  294. }
  295. break;
  296. }
  297. shape->SetPolyPoints( polyPoints );
  298. // Set the polygon outline thickness to 0, only the polygonal shape is filled
  299. // without extra thickness.
  300. shape->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
  301. g_PolyEdges.clear();
  302. editFrame.OnModify();
  303. return footprint;
  304. }