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-2023 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
49static std::vector< wxRealPoint > g_PolyEdges;
51static wxSize g_ShapeSize;
52static int g_PolyShapeType;
53
54
57};
58
59
61{
62public:
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
73private:
74 void OnCancelClick( wxCommandEvent& event );
75
92 void ReadDataShapeDescr( wxCommandEvent& event );
93
95
97 wxRadioBox* m_shapeOptionCtrl;
100};
101
102
103BEGIN_EVENT_TABLE( MWAVE_POLYGONAL_SHAPE_DLG, DIALOG_SHIM )
104 EVT_BUTTON( wxID_CANCEL, MWAVE_POLYGONAL_SHAPE_DLG::OnCancelClick )
106END_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
184void 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 = pcbIUScale.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 = pcbIUScale.IU_PER_MILS*1000;
246
247 if( strncasecmp( param2, "mm", 2 ) == 0 )
248 unitconv = pcbIUScale.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
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 VECTOR2I 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<VECTOR2I> polyPoints;
352 polyPoints.reserve( g_PolyEdges.size() + 2 );
353
354 // Init start point coord:
355 polyPoints.emplace_back( VECTOR2I( offset.x, 0 ) );
356
357 VECTOR2I 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( VECTOR2I( 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 VECTOR2I 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.
393 g_PolyEdges.clear();
394
395 editFrame.OnModify();
396 return footprint;
397}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:226
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
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...
void SetFilled(bool aFlag)
Definition: eda_shape.h:95
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1121
A LINE_READER that reads from an open file.
Definition: richio.h:173
Read lines of text from another LINE_READER but only returns non-comment lines and non-blank lines fr...
Definition: filter_reader.h:37
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
PADS & Pads()
Definition: footprint.h:170
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:568
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:117
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
FOOTPRINT * createPolygonShape()
void OnCancelClick(wxCommandEvent &event)
MWAVE_POLYGONAL_SHAPE_DLG(PCB_EDIT_FRAME *parent, const wxPoint &pos)
bool TransferDataFromWindow() override
void ReadDataShapeDescr(wxCommandEvent &event)
Read a description shape file.
Definition: pad.h:60
void SetX0(int x)
Definition: pad.h:255
void SetX(int x)
Definition: pad.h:249
const VECTOR2I & GetPos0() const
Definition: pad.h:252
The main frame for Pcbnew.
void OnModify() override
Must be called after a board change to set the modified flag.
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:72
Simple container to manage line stroke parameters.
Definition: stroke_params.h:88
virtual long long int GetValue()
Return the current value in Internal Units.
virtual void SetValue(long long int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:300
This file is part of the common library.
#define _(s)
@ F_Cu
Definition: layer_ids.h:64
static double g_ShapeScaleX
static double g_ShapeScaleY
static std::vector< wxRealPoint > g_PolyEdges
static int g_PolyShapeType
static wxSize g_ShapeSize
@ ID_READ_SHAPE_FILE
const double IU_PER_MM
Definition: base_units.h:77
const double IU_PER_MILS
Definition: base_units.h:78
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:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:590