KiCad PCB EDA Suite
microwave_polygon.cpp
Go to the documentation of this file.
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 <[email protected]>
6  * Copyright (C) 2015-2016 Wayne Stambaugh <[email protected]>
7  * Copyright (C) 1992-2021 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 
27 #include <confirm.h>
28 #include <widgets/unit_binder.h>
29 #include <pcb_edit_frame.h>
30 #include <dialog_shim.h>
31 #include <locale_io.h>
32 #include <filter_reader.h>
34 #include <board.h>
35 #include <footprint.h>
36 #include <fp_shape.h>
38 #include <pad.h>
39 #include <pcbnew.h>
40 #include <math/util.h> // for KiROUND
41 
42 #include <wx/button.h>
43 #include <wx/dialog.h>
44 #include <wx/filedlg.h>
45 #include <wx/radiobox.h>
46 #include <wx/sizer.h>
47 #include <wx/statbox.h>
48 
49 static std::vector< wxRealPoint > g_PolyEdges;
51 static wxSize g_ShapeSize;
52 static int g_PolyShapeType;
53 
54 
55 enum id_mw_cmd {
57 };
58 
59 
61 {
62 public:
63  MWAVE_POLYGONAL_SHAPE_DLG( PCB_EDIT_FRAME* parent, const wxPoint& pos );
64 
66  {
67  delete m_sizeX;
68  delete m_sizeY;
69  };
70 
71  bool TransferDataFromWindow() override;
72 
73 private:
74  void OnCancelClick( wxCommandEvent& event );
75 
92  void ReadDataShapeDescr( wxCommandEvent& event );
93 
95 
97  wxRadioBox* m_shapeOptionCtrl;
100 };
101 
102 
103 BEGIN_EVENT_TABLE( MWAVE_POLYGONAL_SHAPE_DLG, wxDialog )
104  EVT_BUTTON( wxID_CANCEL, MWAVE_POLYGONAL_SHAPE_DLG::OnCancelClick )
106 END_EVENT_TABLE()
107 
108 
110  const wxPoint& framepos ) :
111  DIALOG_SHIM( parent, -1, _( "Complex Shape" ), framepos, wxDefaultSize,
112  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
113  m_sizeX(),
114  m_sizeY()
115 {
116  m_frame = parent;
117 
118  g_PolyEdges.clear();
119 
120  wxBoxSizer* mainBoxSizer = new wxBoxSizer( wxVERTICAL );
121  SetSizer( mainBoxSizer );
122 
123  // Controls
124 
125  wxBoxSizer* topBoxSizer = new wxBoxSizer( wxHORIZONTAL );
126  mainBoxSizer->Add( topBoxSizer, 1, wxGROW | wxALL, 5 );
127 
128  wxString shapelist[] = { _( "Normal" ), _( "Symmetrical" ), _( "Mirrored" ) };
129 
130  m_shapeOptionCtrl = new wxRadioBox( this, -1, _( "Shape" ),
131  wxDefaultPosition, wxDefaultSize, 3,
132  shapelist, 1,
133  wxRA_SPECIFY_COLS );
134  topBoxSizer->Add( m_shapeOptionCtrl, 1, wxGROW | wxALL, 5 );
135 
136  auto sizeSizer = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _( "Size" ) ),
137  wxVERTICAL );
138  wxBoxSizer* xSizer = new wxBoxSizer( wxHORIZONTAL );
139  wxBoxSizer* ySizer = new wxBoxSizer( wxHORIZONTAL );
140 
141  topBoxSizer->Add( sizeSizer, 1, wxGROW | wxALL, 5 );
142  sizeSizer->Add( xSizer, 0, 0, 5 );
143  sizeSizer->Add( ySizer, 0, 0, 5 );
144 
145  wxStaticText* xLabel = new wxStaticText( this, -1, _( "X:" ) );
146  wxTextCtrl* xCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString );
147  wxStaticText* xUnits = new wxStaticText( this, -1, _( "units" ) );
148 
149  xSizer->Add( xLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
150  xSizer->Add( xCtrl, 1, wxALIGN_CENTER_VERTICAL, 5 );
151  xSizer->Add( xUnits, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
152  m_sizeX = new UNIT_BINDER( m_frame, xLabel, xCtrl, xUnits );
153 
154  wxStaticText* yLabel = new wxStaticText( this, -1, _( "Y:" ) );
155  wxTextCtrl* yCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString );
156  wxStaticText* yUnits = new wxStaticText( this, -1, _( "units" ) );
157 
158  ySizer->Add( yLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
159  ySizer->Add( yCtrl, 1, wxALIGN_CENTER_VERTICAL, 5 );
160  ySizer->Add( yUnits, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
161  m_sizeY = new UNIT_BINDER( m_frame, yLabel, yCtrl, yUnits );
162 
163  // Buttons
164 
165  wxBoxSizer* buttonsBoxSizer = new wxBoxSizer( wxHORIZONTAL );
166  mainBoxSizer->Add( buttonsBoxSizer, 0, wxALL, 5 );
167 
168  wxButton* btn = new wxButton( this, ID_READ_SHAPE_FILE, _( "Read Shape Description File..." ) );
169  buttonsBoxSizer->Add( btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 10 );
170  buttonsBoxSizer->AddStretchSpacer();
171 
172  wxStdDialogButtonSizer* sdbSizer = new wxStdDialogButtonSizer();
173  buttonsBoxSizer->Add( sdbSizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
174  wxButton* sdbSizerOK = new wxButton( this, wxID_OK );
175  sdbSizer->AddButton( sdbSizerOK );
176  wxButton* sdbSizerCancel = new wxButton( this, wxID_CANCEL );
177  sdbSizer->AddButton( sdbSizerCancel );
178  sdbSizer->Realize();
179 
180  GetSizer()->SetSizeHints( this );
181 }
182 
183 
184 void MWAVE_POLYGONAL_SHAPE_DLG::OnCancelClick( wxCommandEvent& event )
185 {
186  g_PolyEdges.clear();
187  event.Skip();
188 }
189 
190 
192 {
193  if( !wxDialog::TransferDataFromWindow() )
194  return false;
195 
198  g_PolyShapeType = m_shapeOptionCtrl->GetSelection();
199 
200  return true;
201 }
202 
203 
205 {
206  static wxString s_lastpath; // To remember the last open path during a session
207  wxString fullFileName;
208  wxString mask = wxFileSelectorDefaultWildcardStr;
209 
210  fullFileName = wxFileSelector( _( "Shape Description File" ), s_lastpath,
211  fullFileName, wxEmptyString, mask, wxFD_OPEN, this );
212 
213  if( fullFileName.IsEmpty() )
214  return;
215 
216  wxFileName fn( fullFileName );
217  s_lastpath = fn.GetPath();
218  g_PolyEdges.clear();
219 
220  FILE* File = wxFopen( fullFileName, wxT( "rt" ) );
221 
222  if( File == nullptr )
223  {
224  DisplayError( this, _( "File not found" ) );
225  return;
226  }
227 
228  double unitconv = IU_PER_MM;
230 
231  FILE_LINE_READER fileReader( File, fullFileName );
232  FILTER_READER reader( fileReader );
233 
234  LOCALE_IO toggle;
235 
236  while( reader.ReadLine() )
237  {
238  char* Line = reader.Line();
239  char* param1 = strtok( Line, " =\n\r" );
240  char* param2 = strtok( nullptr, " \t\n\r" );
241 
242  if( strncasecmp( param1, "Unit", 4 ) == 0 )
243  {
244  if( strncasecmp( param2, "inch", 4 ) == 0 )
245  unitconv = IU_PER_MILS*1000;
246 
247  if( strncasecmp( param2, "mm", 2 ) == 0 )
248  unitconv = IU_PER_MM;
249  }
250 
251  if( strncasecmp( param1, "$ENDCOORD", 8 ) == 0 )
252  break;
253 
254  if( strncasecmp( param1, "$COORD", 6 ) == 0 )
255  {
256  while( reader.ReadLine() )
257  {
258  Line = reader.Line();
259  param1 = strtok( Line, " \t\n\r" );
260  param2 = strtok( nullptr, " \t\n\r" );
261 
262  if( strncasecmp( param1, "$ENDCOORD", 8 ) == 0 )
263  break;
264 
265  wxRealPoint coord( atof( param1 ), atof( param2 ) );
266  g_PolyEdges.push_back( coord );
267  }
268  }
269 
270  if( strncasecmp( Line, "XScale", 6 ) == 0 )
271  g_ShapeScaleX = atof( param2 );
272 
273  if( strncasecmp( Line, "YScale", 6 ) == 0 )
274  g_ShapeScaleY = atof( param2 );
275  }
276 
277  g_ShapeScaleX *= unitconv;
278  g_ShapeScaleY *= unitconv;
279 
280  m_sizeX->SetValue( (int) g_ShapeScaleX );
281  m_sizeY->SetValue( (int) g_ShapeScaleY );
282 }
283 
284 
286 {
287  PAD* pad1;
288  PAD* pad2;
289  FOOTPRINT* footprint;
290  wxString cmp_name;
291  int pad_count = 2;
292  FP_SHAPE* shape;
293 
294  PCB_EDIT_FRAME& editFrame = *getEditFrame<PCB_EDIT_FRAME>();
295 
296  MWAVE_POLYGONAL_SHAPE_DLG dlg( &editFrame, wxDefaultPosition );
297 
298  int ret = dlg.ShowModal();
299 
300  if( ret != wxID_OK )
301  {
302  g_PolyEdges.clear();
303  return nullptr;
304  }
305 
306  if( g_PolyShapeType == 2 ) // mirrored
308 
311 
312  if(( g_ShapeSize.x ) == 0 || ( g_ShapeSize.y == 0 ) )
313  {
314  editFrame.ShowInfoBarError( _( "Shape has a null size." ) );
315  return nullptr;
316  }
317 
318  if( g_PolyEdges.size() == 0 )
319  {
320  editFrame.ShowInfoBarError( _( "Shape has no points." ) );
321  return nullptr;
322  }
323 
324  cmp_name = wxT( "muwave_polygon" );
325 
326  // Create a footprint with 2 pads, orientation = 0, pos 0
327  footprint = createBaseFootprint( cmp_name, 0, pad_count );
328 
329  // We try to place the footprint anchor to the middle of the shape len
330  wxPoint offset;
331  offset.x = -g_ShapeSize.x / 2;
332 
333  auto it = footprint->Pads().begin();
334 
335  pad1 = *it;
336  pad1->SetX0( offset.x );
337  pad1->SetX( pad1->GetPos0().x );
338 
339  pad2 = *( ++it );
340  pad2->SetX0( offset.x + g_ShapeSize.x );
341  pad2->SetX( pad2->GetPos0().x );
342 
343  // Add a polygonal edge (corners will be added later) on copper layer
344  shape = new FP_SHAPE( footprint, SHAPE_T::POLY );
345  shape->SetFilled( true );
346  shape->SetLayer( F_Cu );
347 
348  footprint->Add( shape, ADD_MODE::INSERT );
349 
350  // Get the corner buffer of the polygonal edge
351  std::vector<wxPoint> polyPoints;
352  polyPoints.reserve( g_PolyEdges.size() + 2 );
353 
354  // Init start point coord:
355  polyPoints.emplace_back( wxPoint( offset.x, 0 ) );
356 
357  wxPoint last_coordinate;
358 
359  for( wxRealPoint& pt: g_PolyEdges ) // Copy points
360  {
361  last_coordinate.x = KiROUND( pt.x * g_ShapeScaleX );
362  last_coordinate.y = -KiROUND( pt.y * g_ShapeScaleY );
363  last_coordinate += offset;
364  polyPoints.push_back( last_coordinate );
365  }
366 
367  // finish the polygonal shape
368  if( last_coordinate.y != 0 )
369  polyPoints.emplace_back( wxPoint( last_coordinate.x, 0 ) );
370 
371  switch( g_PolyShapeType )
372  {
373  case 0: // shape from file
374  case 2: // shape from file, mirrored (the mirror is already done)
375  break;
376 
377  case 1: // Symmetric shape: add the symmetric (mirrored) shape
378  for( int ndx = (int) polyPoints.size() - 1; ndx >= 0; --ndx )
379  {
380  wxPoint pt = polyPoints[ndx];
381  pt.y = -pt.y; // mirror about X axis
382  polyPoints.push_back( pt );
383  }
384 
385  break;
386  }
387 
388  shape->SetPolyPoints( polyPoints );
389 
390  // Set the polygon outline thickness to 0, only the polygonal shape is filled
391  // without extra thickness.
392  shape->SetWidth( 0 );
393  g_PolyEdges.clear();
394 
395  editFrame.OnModify();
396  return footprint;
397 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:279
FOOTPRINT * createPolygonShape()
MWAVE_POLYGONAL_SHAPE_DLG(PCB_EDIT_FRAME *parent, const wxPoint &pos)
void OnModify() override
Must be called after a board change to set the modified flag.
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:117
void SetFilled(bool aFlag)
Definition: eda_shape.h:92
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:164
This file is part of the common library.
static constexpr double IU_PER_MM
Mock up a conversion function.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:82
PADS & Pads()
Definition: footprint.h:169
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
A LINE_READER that reads from an open file.
Definition: richio.h:172
static int g_PolyShapeType
static wxSize g_ShapeSize
void ReadDataShapeDescr(wxCommandEvent &event)
Read a description shape file.
void OnCancelClick(wxCommandEvent &event)
#define _(s)
void SetX0(int x)
Definition: pad.h:230
void SetWidth(int aWidth)
Definition: eda_shape.h:97
const wxPoint & GetPos0() const
Definition: pad.h:227
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
Definition: eda_shape.cpp:1085
static double g_ShapeScaleX
bool TransferDataFromWindow() override
Definition: layer_ids.h:71
static std::vector< wxRealPoint > g_PolyEdges
The main frame for Pcbnew.
#define IU_PER_MILS
Definition: plotter.cpp:130
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
virtual void SetValue(long long int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
virtual long long int GetValue()
Return the current value in Internal Units.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Removes an item from the container.
Definition: footprint.cpp:513
Definition: pad.h:57
Read lines of text from another LINE_READER but only returns non-comment lines and non-blank lines fr...
Definition: filter_reader.h:36
static double g_ShapeScaleY
void SetX(int x)
Definition: pad.h:224