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  aBuffer.back();
248 
249  // Rotate point
250  angle += 900;
251 
252  for( unsigned jj = 0; jj < aBuffer.size(); jj++ )
253  {
254  RotatePoint( &aBuffer[jj].x, &aBuffer[jj].y, aStartPoint.x, aStartPoint.y, 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  wxTextEntryDialog dlg( nullptr, wxEmptyString, _( "Length of Trace:" ), 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  wxTextEntryDialog cmpdlg( nullptr, wxEmptyString, _( "Component Value:" ), 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 
347  // here the module is already in the BOARD, CreateNewModule() does that.
348  module->SetFPID( LIB_ID( std::string( "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->GraphicalItems().PushBack( PtSegm );
366  }
367 
368  // Place a pad on each end of coil.
369  pad = new D_PAD( module );
370 
371  module->Pads().PushFront( pad );
372 
373  pad->SetPadName( "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->Pads().Insert( newpad, pad->Next() );
386 
387  pad = newpad;
388  pad->SetPadName( "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:104
void SetEnd0(const wxPoint &aPoint)
TEXTE_MODULE & Reference()
Definition: class_module.h:456
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:144
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:743
#define abs(a)
Definition: auxiliary.h:84
DLIST< BOARD_ITEM > & GraphicalItems()
Definition: class_module.h:137
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:455
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:297
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:404
void SetEnd(const wxPoint &aEnd)
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: base_struct.h:254
DLIST< D_PAD > & Pads()
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:153
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:186
#define min(a, b)
Definition: auxiliary.h:85
int sign(T val)
Definition: math_util.h:44
void SetWidth(int aWidth)