KiCad PCB EDA Suite
microwave_inductor.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) 2017 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include "microwave_inductor.h"
25 
26 #include <wx/wx.h>
27 
28 #include <base_units.h>
29 #include <validators.h>
30 #include <dialog_text_entry.h>
31 #include <pcb_edit_frame.h>
32 
33 #include <class_pad.h>
34 #include <class_edge_mod.h>
35 #include <class_module.h>
36 
37 
38 using namespace MWAVE;
39 
50 static void gen_arc( std::vector <wxPoint>& aBuffer,
51  const wxPoint& aStartPoint,
52  const wxPoint& aCenter,
53  int a_ArcAngle )
54 {
55  const int SEGM_COUNT_PER_360DEG = 16;
56  auto first_point = aStartPoint - aCenter;
57  int seg_count = ( ( abs( a_ArcAngle ) ) * SEGM_COUNT_PER_360DEG ) / 3600;
58 
59  if( seg_count == 0 )
60  seg_count = 1;
61 
62  double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count;
63 
64  // Creates nb_seg point to approximate arc by segments:
65  for( int ii = 1; ii <= seg_count; ii++ )
66  {
67  double rot_angle = increment_angle * ii;
68  double fcos = cos( rot_angle );
69  double fsin = sin( rot_angle );
70  wxPoint currpt;
71 
72  // Rotate current point:
73  currpt.x = KiROUND( ( first_point.x * fcos + first_point.y * fsin ) );
74  currpt.y = KiROUND( ( first_point.y * fcos - first_point.x * fsin ) );
75 
76  auto corner = aCenter + currpt;
77  aBuffer.push_back( corner );
78  }
79 }
80 
81 
91 static int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
92  const wxPoint& aStartPoint,
93  const wxPoint& aEndPoint,
94  int aLength, int aWidth )
95 {
96 /* We must determine:
97  * segm_count = number of segments perpendicular to the direction
98  * segm_len = length of a strand
99  * radius = radius of rounded parts of the coil
100  * stubs_len = length of the 2 stubs( segments parallel to the direction)
101  * connecting the start point to the start point of the S shape
102  * and the ending point to the end point of the S shape
103  * The equations are (assuming the area size of the entire shape is Size:
104  * Size.x = 2 * radius + segm_len
105  * Size.y = (segm_count + 2 ) * 2 * radius + 2 * stubs_len
106  * inductorPattern.m_length = 2 * delta // connections to the coil
107  * + (segm_count-2) * segm_len // length of the strands except 1st and last
108  * + (segm_count) * (PI * radius) // length of rounded
109  * segm_len + / 2 - radius * 2) // length of 1st and last bit
110  *
111  * The constraints are:
112  * segm_count >= 2
113  * radius < m_Size.x
114  * Size.y = (radius * 4) + (2 * stubs_len)
115  * segm_len > radius * 2
116  *
117  * The calculation is conducted in the following way:
118  * first:
119  * segm_count = 2
120  * radius = 4 * Size.x (arbitrarily fixed value)
121  * Then:
122  * Increasing the number of segments to the desired length
123  * (radius decreases if necessary)
124  */
125  wxPoint size;
126 
127  // This scale factor adjusts the arc length to handle
128  // the arc to segment approximation.
129  // because we use SEGM_COUNT_PER_360DEG segment to approximate a circle,
130  // the trace len must be corrected when calculated using arcs
131  // this factor adjust calculations and must be changed if SEGM_COUNT_PER_360DEG is modified
132  // because trace using segment is shorter the corresponding arc
133  // ADJUST_SIZE is the ratio between tline len and the arc len for an arc
134  // of 360/ADJUST_SIZE angle
135  #define ADJUST_SIZE 0.988
136 
137  auto pt = aEndPoint - aStartPoint;
138  double angle = -ArcTangente( pt.y, pt.x );
139  int min_len = KiROUND( EuclideanNorm( pt ) );
140  int segm_len = 0; // length of segments
141  int full_len; // full len of shape (sum of length of all segments + arcs)
142 
143 
144  /* Note: calculations are made for a vertical coil (more easy calculations)
145  * and after points are rotated to their actual position
146  * So the main direction is the Y axis.
147  * the 2 stubs are on the Y axis
148  * the others segments are parallel to the X axis.
149  */
150 
151  // Calculate the size of area (for a vertical shape)
152  size.x = min_len / 2;
153  size.y = min_len;
154 
155  // Choose a reasonable starting value for the radius of the arcs.
156  int radius = std::min( aWidth * 5, size.x / 4 );
157 
158  int segm_count; // number of full len segments
159  // the half size segments (first and last segment) are not counted here
160  int stubs_len = 0; // length of first or last segment (half size of others segments)
161 
162  for( segm_count = 0; ; segm_count++ )
163  {
164  stubs_len = ( size.y - ( radius * 2 * (segm_count + 2 ) ) ) / 2;
165 
166  if( stubs_len < size.y / 10 ) // Reduce radius.
167  {
168  stubs_len = size.y / 10;
169  radius = ( size.y - (2 * stubs_len) ) / ( 2 * (segm_count + 2) );
170 
171  if( radius < aWidth ) // Radius too small.
172  {
173  // Unable to create line: Requested length value is too large for room
174  return 0;
175  }
176  }
177 
178  segm_len = size.x - ( radius * 2 );
179  full_len = 2 * stubs_len; // Length of coil connections.
180  full_len += segm_len * segm_count; // Length of full length segments.
181  full_len += KiROUND( ( segm_count + 2 ) * M_PI * ADJUST_SIZE * radius ); // Ard arcs len
182  full_len += segm_len - (2 * radius); // Length of first and last segments
183  // (half size segments len = segm_len/2 - radius).
184 
185  if( full_len >= aLength )
186  break;
187  }
188 
189  // Adjust len by adjusting segm_len:
190  int delta_size = full_len - aLength;
191 
192  // reduce len of the segm_count segments + 2 half size segments (= 1 full size segment)
193  segm_len -= delta_size / (segm_count + 1);
194 
195  // Generate first line (the first stub) and first arc (90 deg arc)
196  pt = aStartPoint;
197  aBuffer.push_back( pt );
198  pt.y += stubs_len;
199  aBuffer.push_back( pt );
200 
201  auto centre = pt;
202  centre.x -= radius;
203  gen_arc( aBuffer, pt, centre, -900 );
204  pt = aBuffer.back();
205 
206  int half_size_seg_len = segm_len / 2 - radius;
207 
208  if( half_size_seg_len )
209  {
210  pt.x -= half_size_seg_len;
211  aBuffer.push_back( pt );
212  }
213 
214  // Create shape.
215  int ii;
216  int sign = 1;
217  segm_count += 1; // increase segm_count to create the last half_size segment
218 
219  for( ii = 0; ii < segm_count; ii++ )
220  {
221  int arc_angle;
222 
223  if( ii & 1 ) // odd order arcs are greater than 0
224  sign = -1;
225  else
226  sign = 1;
227 
228  arc_angle = 1800 * sign;
229  centre = pt;
230  centre.y += radius;
231  gen_arc( aBuffer, pt, centre, arc_angle );
232  pt = aBuffer.back();
233  pt.x += segm_len * sign;
234  aBuffer.push_back( pt );
235  }
236 
237  // The last point is false:
238  // it is the end of a full size segment, but must be
239  // the end of the second half_size segment. Change it.
240  sign *= -1;
241  aBuffer.back().x = aStartPoint.x + radius * sign;
242 
243  // create last arc
244  pt = aBuffer.back();
245  centre = pt;
246  centre.y += radius;
247  gen_arc( aBuffer, pt, centre, 900 * sign );
248 
249  // Rotate point
250  angle += 900;
251 
252  for( unsigned jj = 0; jj < aBuffer.size(); jj++ )
253  {
254  RotatePoint( &aBuffer[jj], aStartPoint, angle );
255  }
256 
257  // push last point (end point)
258  aBuffer.push_back( aEndPoint );
259 
260  return 1;
261 }
262 
263 
265  PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage )
266 {
267  /* Build a microwave inductor footprint.
268  * - Length Mself.lng
269  * - Extremities Mself.m_Start and Mself.m_End
270  * We must determine:
271  * Mself.nbrin = number of segments perpendicular to the direction
272  * (The coil nbrin will demicercles + 1 + 2 1 / 4 circle)
273  * Mself.lbrin = length of a strand
274  * Mself.radius = radius of rounded parts of the coil
275  * Mself.delta = segments extremities connection between him and the coil even
276  *
277  * The equations are
278  * Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin
279  * Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius
280  * Mself.lng = 2 * Mself.delta / / connections to the coil
281  + (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last
282  + (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded
283  * Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit
284  *
285  * The constraints are:
286  * Nbrin >= 2
287  * Mself.radius < Mself.m_Size.x
288  * Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord
289  * Mself.lbrin> Mself.radius * 2
290  *
291  * The calculation is conducted in the following way:
292  * Initially:
293  * Nbrin = 2
294  * Radius = 4 * m_Size.x (arbitrarily fixed value)
295  * Then:
296  * Increasing the number of segments to the desired length
297  * (Radius decreases if necessary)
298  */
299 
300  D_PAD* pad;
301  int ll;
302  wxString msg;
303 
304  auto pt = inductorPattern.m_End - inductorPattern.m_Start;
305  int min_len = KiROUND( EuclideanNorm( pt ) );
306  inductorPattern.m_length = min_len;
307 
308  // Enter the desired length.
309  msg = StringFromValue( g_UserUnit, inductorPattern.m_length );
310  WX_TEXT_ENTRY_DIALOG dlg( nullptr, _( "Length of Trace:" ), wxEmptyString, msg );
311 
312  if( dlg.ShowModal() != wxID_OK )
313  return nullptr; // canceled by user
314 
315  msg = dlg.GetValue();
316  inductorPattern.m_length = ValueFromString( g_UserUnit, msg );
317 
318  // Control values (ii = minimum length)
319  if( inductorPattern.m_length < min_len )
320  {
321  aErrorMessage = _( "Requested length < minimum length" );
322  return nullptr;
323  }
324 
325  // Calculate the elements.
326  std::vector <wxPoint> buffer;
327  ll = BuildCornersList_S_Shape( buffer, inductorPattern.m_Start,
328  inductorPattern.m_End, inductorPattern.m_length,
329  inductorPattern.m_Width );
330 
331  if( !ll )
332  {
333  aErrorMessage = _( "Requested length too large" );
334  return nullptr;
335  }
336 
337  // Generate footprint. the value is also used as footprint name.
338  msg = "L";
339  WX_TEXT_ENTRY_DIALOG cmpdlg( nullptr, _( "Component Value:" ), wxEmptyString, msg );
340  cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );
341 
342  if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
343  return nullptr; // Aborted by user
344 
345  MODULE* module = aPcbFrame->CreateNewModule( msg );
346  aPcbFrame->AddModuleToBoard( module );
347 
348  module->SetFPID( LIB_ID( wxString( "mw_inductor" ) ) );
349  module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
350  module->ClearFlags();
351  module->SetPosition( inductorPattern.m_End );
352 
353  // Generate segments
354  for( unsigned jj = 1; jj < buffer.size(); jj++ )
355  {
356  EDGE_MODULE* PtSegm;
357  PtSegm = new EDGE_MODULE( module );
358  PtSegm->SetStart( buffer[jj - 1] );
359  PtSegm->SetEnd( buffer[jj] );
360  PtSegm->SetWidth( inductorPattern.m_Width );
361  PtSegm->SetLayer( module->GetLayer() );
362  PtSegm->SetShape( S_SEGMENT );
363  PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
364  PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
365  module->GraphicalItemsList().PushBack( PtSegm );
366  }
367 
368  // Place a pad on each end of coil.
369  pad = new D_PAD( module );
370 
371  module->PadsList().PushFront( pad );
372 
373  pad->SetName( "1" );
374  pad->SetPosition( inductorPattern.m_End );
375  pad->SetPos0( pad->GetPosition() - module->GetPosition() );
376 
377  pad->SetSize( wxSize( inductorPattern.m_Width, inductorPattern.m_Width ) );
378 
379  pad->SetLayerSet( LSET( module->GetLayer() ) );
381  pad->SetShape( PAD_SHAPE_CIRCLE );
382 
383  D_PAD* newpad = new D_PAD( *pad );
384 
385  module->PadsList().Insert( newpad, pad->Next() );
386 
387  pad = newpad;
388  pad->SetName( "2" );
389  pad->SetPosition( inductorPattern.m_Start );
390  pad->SetPos0( pad->GetPosition() - module->GetPosition() );
391 
392  // Modify text positions.
393  wxPoint refPos( ( inductorPattern.m_Start.x + inductorPattern.m_End.x ) / 2,
394  ( inductorPattern.m_Start.y + inductorPattern.m_End.y ) / 2 );
395 
396  wxPoint valPos = refPos;
397 
398  refPos.y -= module->Reference().GetTextSize().y;
399  module->Reference().SetPosition( refPos );
400  valPos.y += module->Value().GetTextSize().y;
401  module->Value().SetPosition( valPos );
402 
403  module->CalculateBoundingBox();
404  return module;
405 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:112
void SetEnd0(const wxPoint &aPoint)
TEXTE_MODULE & Reference()
Definition: class_module.h:502
void SetShape(STROKE_T aShape)
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:107
void PushFront(T *aNewElement)
Function PushFront puts aNewElement at front of list sequence.
Definition: dlist.h:240
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
Implementation of conversion functions that require both schematic and board internal units...
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
void CalculateBoundingBox()
Function CalculateBoundingBox calculates the bounding box in board coordinates.
wxString StringFromValue(EDA_UNITS_T aUnit, int aValue, bool aAddUnitSymbol)
Function StringFromValue returns the string from aValue according to units (inch, mm ...
Definition: base_units.cpp:205
Parameters for construction of a microwave inductor.
void SetTextValidator(wxTextValidatorStyle style)
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:61
Set for modules listed in the automatic insertion list (usually SMD footprints)
Definition: class_module.h:77
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:219
usual segment : line with rounded ends
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:216
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
MODULE * CreateNewModule(const wxString &aModuleName)
Function CreateNewModule Creates a new module or footprint, at position 0,0 The new module contains o...
#define abs(a)
Definition: auxiliary.h:84
Class FILE_NAME_CHAR_VALIDATOR.
Definition: validators.h:43
void PushBack(T *aNewElement)
Function PushBack puts aNewElement at the end of the list sequence.
Definition: dlist.h:250
Class LSET is a set of PCB_LAYER_IDs.
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:170
void SetName(const wxString &aName)
Set the pad name (sometimes called pad number, although it can be an array reference like AA12)...
Definition: class_pad.h:182
TEXTE_MODULE & Value()
read/write accessors:
Definition: class_module.h:501
void SetPos0(const wxPoint &aPos)
Definition: class_pad.h:262
static void gen_arc(std::vector< wxPoint > &aBuffer, const wxPoint &aStartPoint, const wxPoint &aCenter, int a_ArcAngle)
Function gen_arc generates an arc using arc approximation by lines: Center aCenter Angle "angle" (in ...
int ValueFromString(EDA_UNITS_T aUnits, const wxString &aTextValue)
Function ValueFromString converts aTextValue in aUnits to internal units used by the application...
Definition: base_units.cpp:370
D_PAD * Next() const
Definition: class_pad.h:160
void SetSize(const wxSize &aSize)
Definition: class_pad.h:268
static int BuildCornersList_S_Shape(std::vector< wxPoint > &aBuffer, const wxPoint &aStartPoint, const wxPoint &aEndPoint, int aLength, int aWidth)
Function BuildCornersList_S_Shape Create a path like a S-shaped coil.
virtual void SetPosition(const wxPoint &aPos) override
void SetPosition(const wxPoint &aPos) override
void AddModuleToBoard(MODULE *module, wxDC *aDC=nullptr)
Adds the given module to the board.
void SetAttribute(PAD_ATTR_T aAttribute)
Definition: class_pad.cpp:392
Pad object description.
void SetStart(const wxPoint &aStart)
EDA_UNITS_T g_UserUnit
Global variables definitions.
Definition: common.cpp:57
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:401
wxString GetValue()
MODULE * CreateMicrowaveInductor(INDUCTOR_PATTERN &aPattern, PCB_EDIT_FRAME *aPcbFrame, wxString &aErrorMessage)
Creates an S-shaped coil footprint for microwave applications.
#define ADJUST_SIZE
Virtual component: when created by copper shapes on board (Like edge card connectors, mounting hole...)
Definition: class_module.h:79
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void SetStart0(const wxPoint &aPoint)
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:217
void SetEnd(const wxPoint &aEnd)
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: base_struct.h:265
DLIST< BOARD_ITEM > & GraphicalItemsList()
Definition: class_module.h:164
DLIST< D_PAD > & PadsList()
Definition: class_module.h:161
Module description (excepted pads)
const wxSize & GetTextSize() const
Definition: eda_text.h:213
const wxPoint GetPosition() const override
Definition: class_pad.h:220
EDGE_MODULE class definition.
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
const wxPoint GetPosition() const override
Definition: class_module.h:182
void SetFPID(const LIB_ID &aFPID)
Definition: class_module.h:192
Custom text control validator definitions.
void Insert(T *aNewElement, T *aElementAfterMe)
Function Insert puts aNewElement just in front of aElementAfterMe in the list sequence.
Definition: dlist.h:200
void SetAttributes(int aAttributes)
Definition: class_module.h:225
#define min(a, b)
Definition: auxiliary.h:85
int sign(T val)
Definition: math_util.h:44
void SetWidth(int aWidth)