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 <wxPcbStruct.h>
31 
32 #include <class_pad.h>
33 #include <class_edge_mod.h>
34 #include <class_module.h>
35 
36 
37 using namespace MWAVE;
38 
49 static void gen_arc( std::vector <wxPoint>& aBuffer,
50  const wxPoint& aStartPoint,
51  const wxPoint& aCenter,
52  int a_ArcAngle )
53 {
54  const int SEGM_COUNT_PER_360DEG = 16;
55  auto first_point = aStartPoint - aCenter;
56  int seg_count = ( ( abs( a_ArcAngle ) ) * SEGM_COUNT_PER_360DEG ) / 3600;
57 
58  if( seg_count == 0 )
59  seg_count = 1;
60 
61  double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count;
62 
63  // Creates nb_seg point to approximate arc by segments:
64  for( int ii = 1; ii <= seg_count; ii++ )
65  {
66  double rot_angle = increment_angle * ii;
67  double fcos = cos( rot_angle );
68  double fsin = sin( rot_angle );
69  wxPoint currpt;
70 
71  // Rotate current point:
72  currpt.x = KiROUND( ( first_point.x * fcos + first_point.y * fsin ) );
73  currpt.y = KiROUND( ( first_point.y * fcos - first_point.x * fsin ) );
74 
75  auto corner = aCenter + currpt;
76  aBuffer.push_back( corner );
77  }
78 }
79 
80 
90 static int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
91  const wxPoint& aStartPoint,
92  const wxPoint& aEndPoint,
93  int aLength, int aWidth )
94 {
95 /* We must determine:
96  * segm_count = number of segments perpendicular to the direction
97  * segm_len = length of a strand
98  * radius = radius of rounded parts of the coil
99  * stubs_len = length of the 2 stubs( segments parallel to the direction)
100  * connecting the start point to the start point of the S shape
101  * and the ending point to the end point of the S shape
102  * The equations are (assuming the area size of the entire shape is Size:
103  * Size.x = 2 * radius + segm_len
104  * Size.y = (segm_count + 2 ) * 2 * radius + 2 * stubs_len
105  * inductorPattern.m_length = 2 * delta // connections to the coil
106  * + (segm_count-2) * segm_len // length of the strands except 1st and last
107  * + (segm_count) * (PI * radius) // length of rounded
108  * segm_len + / 2 - radius * 2) // length of 1st and last bit
109  *
110  * The constraints are:
111  * segm_count >= 2
112  * radius < m_Size.x
113  * Size.y = (radius * 4) + (2 * stubs_len)
114  * segm_len > radius * 2
115  *
116  * The calculation is conducted in the following way:
117  * first:
118  * segm_count = 2
119  * radius = 4 * Size.x (arbitrarily fixed value)
120  * Then:
121  * Increasing the number of segments to the desired length
122  * (radius decreases if necessary)
123  */
124  wxPoint size;
125 
126  // This scale factor adjusts the arc length to handle
127  // the arc to segment approximation.
128  // because we use SEGM_COUNT_PER_360DEG segment to approximate a circle,
129  // the trace len must be corrected when calculated using arcs
130  // this factor adjust calculations and must be changed if SEGM_COUNT_PER_360DEG is modified
131  // because trace using segment is shorter the corresponding arc
132  // ADJUST_SIZE is the ratio between tline len and the arc len for an arc
133  // of 360/ADJUST_SIZE angle
134  #define ADJUST_SIZE 0.988
135 
136  auto pt = aEndPoint - aStartPoint;
137  double angle = -ArcTangente( pt.y, pt.x );
138  int min_len = KiROUND( EuclideanNorm( pt ) );
139  int segm_len = 0; // length of segments
140  int full_len; // full len of shape (sum of length of all segments + arcs)
141 
142 
143  /* Note: calculations are made for a vertical coil (more easy calculations)
144  * and after points are rotated to their actual position
145  * So the main direction is the Y axis.
146  * the 2 stubs are on the Y axis
147  * the others segments are parallel to the X axis.
148  */
149 
150  // Calculate the size of area (for a vertical shape)
151  size.x = min_len / 2;
152  size.y = min_len;
153 
154  // Choose a reasonable starting value for the radius of the arcs.
155  int radius = std::min( aWidth * 5, size.x / 4 );
156 
157  int segm_count; // number of full len segments
158  // the half size segments (first and last segment) are not counted here
159  int stubs_len = 0; // length of first or last segment (half size of others segments)
160 
161  for( segm_count = 0; ; segm_count++ )
162  {
163  stubs_len = ( size.y - ( radius * 2 * (segm_count + 2 ) ) ) / 2;
164 
165  if( stubs_len < size.y / 10 ) // Reduce radius.
166  {
167  stubs_len = size.y / 10;
168  radius = ( size.y - (2 * stubs_len) ) / ( 2 * (segm_count + 2) );
169 
170  if( radius < aWidth ) // Radius too small.
171  {
172  // Unable to create line: Requested length value is too large for room
173  return 0;
174  }
175  }
176 
177  segm_len = size.x - ( radius * 2 );
178  full_len = 2 * stubs_len; // Length of coil connections.
179  full_len += segm_len * segm_count; // Length of full length segments.
180  full_len += KiROUND( ( segm_count + 2 ) * M_PI * ADJUST_SIZE * radius ); // Ard arcs len
181  full_len += segm_len - (2 * radius); // Length of first and last segments
182  // (half size segments len = segm_len/2 - radius).
183 
184  if( full_len >= aLength )
185  break;
186  }
187 
188  // Adjust len by adjusting segm_len:
189  int delta_size = full_len - aLength;
190 
191  // reduce len of the segm_count segments + 2 half size segments (= 1 full size segment)
192  segm_len -= delta_size / (segm_count + 1);
193 
194  // Generate first line (the first stub) and first arc (90 deg arc)
195  pt = aStartPoint;
196  aBuffer.push_back( pt );
197  pt.y += stubs_len;
198  aBuffer.push_back( pt );
199 
200  auto centre = pt;
201  centre.x -= radius;
202  gen_arc( aBuffer, pt, centre, -900 );
203  pt = aBuffer.back();
204 
205  int half_size_seg_len = segm_len / 2 - radius;
206 
207  if( half_size_seg_len )
208  {
209  pt.x -= half_size_seg_len;
210  aBuffer.push_back( pt );
211  }
212 
213  // Create shape.
214  int ii;
215  int sign = 1;
216  segm_count += 1; // increase segm_count to create the last half_size segment
217 
218  for( ii = 0; ii < segm_count; ii++ )
219  {
220  int arc_angle;
221 
222  if( ii & 1 ) // odd order arcs are greater than 0
223  sign = -1;
224  else
225  sign = 1;
226 
227  arc_angle = 1800 * sign;
228  centre = pt;
229  centre.y += radius;
230  gen_arc( aBuffer, pt, centre, arc_angle );
231  pt = aBuffer.back();
232  pt.x += segm_len * sign;
233  aBuffer.push_back( pt );
234  }
235 
236  // The last point is false:
237  // it is the end of a full size segment, but must be
238  // the end of the second half_size segment. Change it.
239  sign *= -1;
240  aBuffer.back().x = aStartPoint.x + radius * sign;
241 
242  // create last arc
243  pt = aBuffer.back();
244  centre = pt;
245  centre.y += radius;
246  gen_arc( aBuffer, pt, centre, 900 * sign );
247 
248  // Rotate point
249  angle += 900;
250 
251  for( unsigned jj = 0; jj < aBuffer.size(); jj++ )
252  {
253  RotatePoint( &aBuffer[jj], aStartPoint, angle );
254  }
255 
256  // push last point (end point)
257  aBuffer.push_back( aEndPoint );
258 
259  return 1;
260 }
261 
262 
264  PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage )
265 {
266  /* Build a microwave inductor footprint.
267  * - Length Mself.lng
268  * - Extremities Mself.m_Start and Mself.m_End
269  * We must determine:
270  * Mself.nbrin = number of segments perpendicular to the direction
271  * (The coil nbrin will demicercles + 1 + 2 1 / 4 circle)
272  * Mself.lbrin = length of a strand
273  * Mself.radius = radius of rounded parts of the coil
274  * Mself.delta = segments extremities connection between him and the coil even
275  *
276  * The equations are
277  * Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin
278  * Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius
279  * Mself.lng = 2 * Mself.delta / / connections to the coil
280  + (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last
281  + (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded
282  * Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit
283  *
284  * The constraints are:
285  * Nbrin >= 2
286  * Mself.radius < Mself.m_Size.x
287  * Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord
288  * Mself.lbrin> Mself.radius * 2
289  *
290  * The calculation is conducted in the following way:
291  * Initially:
292  * Nbrin = 2
293  * Radius = 4 * m_Size.x (arbitrarily fixed value)
294  * Then:
295  * Increasing the number of segments to the desired length
296  * (Radius decreases if necessary)
297  */
298 
299  D_PAD* pad;
300  int ll;
301  wxString msg;
302 
303  auto pt = inductorPattern.m_End - inductorPattern.m_Start;
304  int min_len = KiROUND( EuclideanNorm( pt ) );
305  inductorPattern.m_length = min_len;
306 
307  // Enter the desired length.
308  msg = StringFromValue( g_UserUnit, inductorPattern.m_length );
309  wxTextEntryDialog dlg( nullptr, wxEmptyString, _( "Length of Trace:" ), msg );
310 
311  if( dlg.ShowModal() != wxID_OK )
312  return nullptr; // canceled by user
313 
314  msg = dlg.GetValue();
315  inductorPattern.m_length = ValueFromString( g_UserUnit, msg );
316 
317  // Control values (ii = minimum length)
318  if( inductorPattern.m_length < min_len )
319  {
320  aErrorMessage = _( "Requested length < minimum length" );
321  return nullptr;
322  }
323 
324  // Calculate the elements.
325  std::vector <wxPoint> buffer;
326  ll = BuildCornersList_S_Shape( buffer, inductorPattern.m_Start,
327  inductorPattern.m_End, inductorPattern.m_length,
328  inductorPattern.m_Width );
329 
330  if( !ll )
331  {
332  aErrorMessage = _( "Requested length too large" );
333  return nullptr;
334  }
335 
336  // Generate footprint. the value is also used as footprint name.
337  msg = "L";
338  wxTextEntryDialog cmpdlg( nullptr, wxEmptyString, _( "Component Value:" ), msg );
339  cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );
340 
341  if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
342  return nullptr; // Aborted by user
343 
344  MODULE* module = aPcbFrame->CreateNewModule( msg );
345 
346  // here the module is already in the BOARD, CreateNewModule() does that.
347  module->SetFPID( LIB_ID( wxString( "mw_inductor" ) ) );
348  module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
349  module->ClearFlags();
350  module->SetPosition( inductorPattern.m_End );
351 
352  // Generate segments
353  for( unsigned jj = 1; jj < buffer.size(); jj++ )
354  {
355  EDGE_MODULE* PtSegm;
356  PtSegm = new EDGE_MODULE( module );
357  PtSegm->SetStart( buffer[jj - 1] );
358  PtSegm->SetEnd( buffer[jj] );
359  PtSegm->SetWidth( inductorPattern.m_Width );
360  PtSegm->SetLayer( module->GetLayer() );
361  PtSegm->SetShape( S_SEGMENT );
362  PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
363  PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
364  module->GraphicalItemsList().PushBack( PtSegm );
365  }
366 
367  // Place a pad on each end of coil.
368  pad = new D_PAD( module );
369 
370  module->PadsList().PushFront( pad );
371 
372  pad->SetPadName( "1" );
373  pad->SetPosition( inductorPattern.m_End );
374  pad->SetPos0( pad->GetPosition() - module->GetPosition() );
375 
376  pad->SetSize( wxSize( inductorPattern.m_Width, inductorPattern.m_Width ) );
377 
378  pad->SetLayerSet( LSET( module->GetLayer() ) );
380  pad->SetShape( PAD_SHAPE_CIRCLE );
381 
382  D_PAD* newpad = new D_PAD( *pad );
383 
384  module->PadsList().Insert( newpad, pad->Next() );
385 
386  pad = newpad;
387  pad->SetPadName( "2" );
388  pad->SetPosition( inductorPattern.m_Start );
389  pad->SetPos0( pad->GetPosition() - module->GetPosition() );
390 
391  // Modify text positions.
392  wxPoint refPos( ( inductorPattern.m_Start.x + inductorPattern.m_End.x ) / 2,
393  ( inductorPattern.m_Start.y + inductorPattern.m_End.y ) / 2 );
394 
395  wxPoint valPos = refPos;
396 
397  refPos.y -= module->Reference().GetTextSize().y;
398  module->Reference().SetPosition( refPos );
399  valPos.y += module->Value().GetTextSize().y;
400  module->Value().SetPosition( valPos );
401 
402  module->CalculateBoundingBox();
403  return module;
404 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:104
void SetEnd0(const wxPoint &aPoint)
TEXTE_MODULE & Reference()
Definition: class_module.h:463
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...
void CalculateBoundingBox()
Function CalculateBoundingBox calculates the bounding box in board coordinates.
const wxPoint & GetPosition() const override
Definition: class_module.h:155
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:203
Parameters for construction of a microwave inductor.
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:59
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:169
usual segment : line with rounded ends
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:317
Class LIB_ID.
Definition: lib_id.h:56
MODULE * CreateNewModule(const wxString &aModuleName)
Function CreateNewModule Creates a new module or footprint, at position 0,0 The new module contains o...
Definition: librairi.cpp:747
#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:271
TEXTE_MODULE & Value()
read/write accessors:
Definition: class_module.h:462
void SetPos0(const wxPoint &aPos)
Definition: class_pad.h:175
const wxPoint & GetPosition() const override
Definition: class_pad.h:170
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:368
D_PAD * Next() const
Definition: class_pad.h:106
void SetSize(const wxSize &aSize)
Definition: class_pad.h:181
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 SetAttribute(PAD_ATTR_T aAttribute)
Definition: class_pad.cpp:348
Pad object description.
void SetStart(const wxPoint &aStart)
EDA_UNITS_T g_UserUnit
Global variables definitions.
Definition: common.cpp:56
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:234
PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
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:167
void SetPadName(const wxString &name)
Set the pad name (sometimes called pad number, although it can be an array ref like AA12 the pad name...
Definition: class_pad.cpp:455
void SetEnd(const wxPoint &aEnd)
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: base_struct.h:254
DLIST< BOARD_ITEM > & GraphicalItemsList()
Definition: class_module.h:137
DLIST< D_PAD > & PadsList()
Definition: class_module.h:134
Module description (excepted pads)
const wxSize & GetTextSize() const
Definition: eda_text.h:215
EDGE_MODULE class definition.
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
void SetFPID(const LIB_ID &aFPID)
Definition: class_module.h:165
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:198
#define min(a, b)
Definition: auxiliary.h:85
int sign(T val)
Definition: math_util.h:44
void SetWidth(int aWidth)