Allow calling of MW inductor function externally

This makes it possible to access the creation functions
for the microwave inductor externally, so that it can be
used from GAL.

Unlike the Gap/Stub tools, the inductor tool is more interactive, and
doesn't just return a MODULE*.

Introduces a new directory in pcbnew called 'microwave' to better
isolate generic (i.e. canvas-agnostic) functions, and a namespace MWAVE
to avoid polluting the global namespace.
This commit is contained in:
John Beard 2017-02-23 16:00:37 +08:00 committed by Tomasz Włostowski
parent 796e5fdc45
commit 3994e9a268
4 changed files with 484 additions and 406 deletions

View File

@ -194,6 +194,10 @@ set( PCBNEW_AUTOROUTER_SRCS
autorouter/work.cpp
)
set( PCBNEW_MICROWAVE_SRCS
microwave/microwave_inductor.cpp
)
set( PCBNEW_CLASS_SRCS
board_commit.cpp
tool_modview.cpp
@ -315,8 +319,12 @@ set( PCBNEW_CLASS_SRCS
footprint_preview_panel.cpp
)
set( PCBNEW_SRCS ${PCBNEW_AUTOROUTER_SRCS} ${PCBNEW_CLASS_SRCS} ${PCBNEW_DIALOGS} )
set( PCBNEW_SRCS
${PCBNEW_AUTOROUTER_SRCS}
${PCBNEW_MICROWAVE_SRCS}
${PCBNEW_CLASS_SRCS}
${PCBNEW_DIALOGS}
)
# extra sources from common
set( PCBNEW_COMMON_SRCS

View File

@ -0,0 +1,405 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "microwave_inductor.h"
#include <wx/wx.h>
#include <base_units.h>
#include <validators.h>
#include <wxPcbStruct.h>
#include <class_pad.h>
#include <class_edge_mod.h>
#include <class_module.h>
using namespace MWAVE;
/**
* Function gen_arc
* generates an arc using arc approximation by lines:
* Center aCenter
* Angle "angle" (in 0.1 deg)
* @param aBuffer = a buffer to store points.
* @param aStartPoint = starting point of arc.
* @param aCenter = arc centre.
* @param a_ArcAngle = arc length in 0.1 degrees.
*/
static void gen_arc( std::vector <wxPoint>& aBuffer,
const wxPoint& aStartPoint,
const wxPoint& aCenter,
int a_ArcAngle )
{
const int SEGM_COUNT_PER_360DEG = 16;
auto first_point = aStartPoint - aCenter;
int seg_count = ( ( abs( a_ArcAngle ) ) * SEGM_COUNT_PER_360DEG ) / 3600;
if( seg_count == 0 )
seg_count = 1;
double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count;
// Creates nb_seg point to approximate arc by segments:
for( int ii = 1; ii <= seg_count; ii++ )
{
double rot_angle = increment_angle * ii;
double fcos = cos( rot_angle );
double fsin = sin( rot_angle );
wxPoint currpt;
// Rotate current point:
currpt.x = KiROUND( ( first_point.x * fcos + first_point.y * fsin ) );
currpt.y = KiROUND( ( first_point.y * fcos - first_point.x * fsin ) );
auto corner = aCenter + currpt;
aBuffer.push_back( corner );
}
}
/**
* Function BuildCornersList_S_Shape
* Create a path like a S-shaped coil
* @param aBuffer = a buffer where to store points (ends of segments)
* @param aStartPoint = starting point of the path
* @param aEndPoint = ending point of the path
* @param aLength = full length of the path
* @param aWidth = segment width
*/
static int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
const wxPoint& aStartPoint,
const wxPoint& aEndPoint,
int aLength, int aWidth )
{
/* We must determine:
* segm_count = number of segments perpendicular to the direction
* segm_len = length of a strand
* radius = radius of rounded parts of the coil
* stubs_len = length of the 2 stubs( segments parallel to the direction)
* connecting the start point to the start point of the S shape
* and the ending point to the end point of the S shape
* The equations are (assuming the area size of the entire shape is Size:
* Size.x = 2 * radius + segm_len
* Size.y = (segm_count + 2 ) * 2 * radius + 2 * stubs_len
* inductorPattern.m_length = 2 * delta // connections to the coil
* + (segm_count-2) * segm_len // length of the strands except 1st and last
* + (segm_count) * (PI * radius) // length of rounded
* segm_len + / 2 - radius * 2) // length of 1st and last bit
*
* The constraints are:
* segm_count >= 2
* radius < m_Size.x
* Size.y = (radius * 4) + (2 * stubs_len)
* segm_len > radius * 2
*
* The calculation is conducted in the following way:
* first:
* segm_count = 2
* radius = 4 * Size.x (arbitrarily fixed value)
* Then:
* Increasing the number of segments to the desired length
* (radius decreases if necessary)
*/
wxPoint size;
// This scale factor adjusts the arc length to handle
// the arc to segment approximation.
// because we use SEGM_COUNT_PER_360DEG segment to approximate a circle,
// the trace len must be corrected when calculated using arcs
// this factor adjust calculations and must be changed if SEGM_COUNT_PER_360DEG is modified
// because trace using segment is shorter the corresponding arc
// ADJUST_SIZE is the ratio between tline len and the arc len for an arc
// of 360/ADJUST_SIZE angle
#define ADJUST_SIZE 0.988
auto pt = aEndPoint - aStartPoint;
double angle = -ArcTangente( pt.y, pt.x );
int min_len = KiROUND( EuclideanNorm( pt ) );
int segm_len = 0; // length of segments
int full_len; // full len of shape (sum of length of all segments + arcs)
/* Note: calculations are made for a vertical coil (more easy calculations)
* and after points are rotated to their actual position
* So the main direction is the Y axis.
* the 2 stubs are on the Y axis
* the others segments are parallel to the X axis.
*/
// Calculate the size of area (for a vertical shape)
size.x = min_len / 2;
size.y = min_len;
// Choose a reasonable starting value for the radius of the arcs.
int radius = std::min( aWidth * 5, size.x / 4 );
int segm_count; // number of full len segments
// the half size segments (first and last segment) are not counted here
int stubs_len = 0; // length of first or last segment (half size of others segments)
for( segm_count = 0; ; segm_count++ )
{
stubs_len = ( size.y - ( radius * 2 * (segm_count + 2 ) ) ) / 2;
if( stubs_len < size.y / 10 ) // Reduce radius.
{
stubs_len = size.y / 10;
radius = ( size.y - (2 * stubs_len) ) / ( 2 * (segm_count + 2) );
if( radius < aWidth ) // Radius too small.
{
// Unable to create line: Requested length value is too large for room
return 0;
}
}
segm_len = size.x - ( radius * 2 );
full_len = 2 * stubs_len; // Length of coil connections.
full_len += segm_len * segm_count; // Length of full length segments.
full_len += KiROUND( ( segm_count + 2 ) * M_PI * ADJUST_SIZE * radius ); // Ard arcs len
full_len += segm_len - (2 * radius); // Length of first and last segments
// (half size segments len = segm_len/2 - radius).
if( full_len >= aLength )
break;
}
// Adjust len by adjusting segm_len:
int delta_size = full_len - aLength;
// reduce len of the segm_count segments + 2 half size segments (= 1 full size segment)
segm_len -= delta_size / (segm_count + 1);
// Generate first line (the first stub) and first arc (90 deg arc)
pt = aStartPoint;
aBuffer.push_back( pt );
pt.y += stubs_len;
aBuffer.push_back( pt );
auto centre = pt;
centre.x -= radius;
gen_arc( aBuffer, pt, centre, -900 );
pt = aBuffer.back();
int half_size_seg_len = segm_len / 2 - radius;
if( half_size_seg_len )
{
pt.x -= half_size_seg_len;
aBuffer.push_back( pt );
}
// Create shape.
int ii;
int sign = 1;
segm_count += 1; // increase segm_count to create the last half_size segment
for( ii = 0; ii < segm_count; ii++ )
{
int arc_angle;
if( ii & 1 ) // odd order arcs are greater than 0
sign = -1;
else
sign = 1;
arc_angle = 1800 * sign;
centre = pt;
centre.y += radius;
gen_arc( aBuffer, pt, centre, arc_angle );
pt = aBuffer.back();
pt.x += segm_len * sign;
aBuffer.push_back( pt );
}
// The last point is false:
// it is the end of a full size segment, but must be
// the end of the second half_size segment. Change it.
sign *= -1;
aBuffer.back().x = aStartPoint.x + radius * sign;
// create last arc
pt = aBuffer.back();
centre = pt;
centre.y += radius;
gen_arc( aBuffer, pt, centre, 900 * sign );
aBuffer.back();
// Rotate point
angle += 900;
for( unsigned jj = 0; jj < aBuffer.size(); jj++ )
{
RotatePoint( &aBuffer[jj].x, &aBuffer[jj].y, aStartPoint.x, aStartPoint.y, angle );
}
// push last point (end point)
aBuffer.push_back( aEndPoint );
return 1;
}
MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern,
PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage )
{
/* Build a microwave inductor footprint.
* - Length Mself.lng
* - Extremities Mself.m_Start and Mself.m_End
* We must determine:
* Mself.nbrin = number of segments perpendicular to the direction
* (The coil nbrin will demicercles + 1 + 2 1 / 4 circle)
* Mself.lbrin = length of a strand
* Mself.radius = radius of rounded parts of the coil
* Mself.delta = segments extremities connection between him and the coil even
*
* The equations are
* Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin
* Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius
* Mself.lng = 2 * Mself.delta / / connections to the coil
+ (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last
+ (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded
* Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit
*
* The constraints are:
* Nbrin >= 2
* Mself.radius < Mself.m_Size.x
* Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord
* Mself.lbrin> Mself.radius * 2
*
* The calculation is conducted in the following way:
* Initially:
* Nbrin = 2
* Radius = 4 * m_Size.x (arbitrarily fixed value)
* Then:
* Increasing the number of segments to the desired length
* (Radius decreases if necessary)
*/
D_PAD* pad;
int ll;
wxString msg;
auto pt = inductorPattern.m_End - inductorPattern.m_Start;
int min_len = KiROUND( EuclideanNorm( pt ) );
inductorPattern.m_length = min_len;
// Enter the desired length.
msg = StringFromValue( g_UserUnit, inductorPattern.m_length );
wxTextEntryDialog dlg( NULL, wxEmptyString, _( "Length of Trace:" ), msg );
if( dlg.ShowModal() != wxID_OK )
return NULL; // canceled by user
msg = dlg.GetValue();
inductorPattern.m_length = ValueFromString( g_UserUnit, msg );
// Control values (ii = minimum length)
if( inductorPattern.m_length < min_len )
{
aErrorMessage = _( "Requested length < minimum length" );
return NULL;
}
// Calculate the elements.
std::vector <wxPoint> buffer;
ll = BuildCornersList_S_Shape( buffer, inductorPattern.m_Start,
inductorPattern.m_End, inductorPattern.m_length,
inductorPattern.m_Width );
if( !ll )
{
aErrorMessage = _( "Requested length too large" );
return NULL;
}
// Generate footprint. the value is also used as footprint name.
msg.Empty();
wxTextEntryDialog cmpdlg( NULL, wxEmptyString, _( "Component Value:" ), msg );
cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );
if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
return NULL; // Aborted by user
MODULE* module = aPcbFrame->CreateNewModule( msg );
// here the module is already in the BOARD, CreateNewModule() does that.
module->SetFPID( LIB_ID( std::string( "mw_inductor" ) ) );
module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
module->ClearFlags();
module->SetPosition( inductorPattern.m_End );
// Generate segments
for( unsigned jj = 1; jj < buffer.size(); jj++ )
{
EDGE_MODULE* PtSegm;
PtSegm = new EDGE_MODULE( module );
PtSegm->SetStart( buffer[jj - 1] );
PtSegm->SetEnd( buffer[jj] );
PtSegm->SetWidth( inductorPattern.m_Width );
PtSegm->SetLayer( module->GetLayer() );
PtSegm->SetShape( S_SEGMENT );
PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
module->GraphicalItems().PushBack( PtSegm );
}
// Place a pad on each end of coil.
pad = new D_PAD( module );
module->Pads().PushFront( pad );
pad->SetPadName( "1" );
pad->SetPosition( inductorPattern.m_End );
pad->SetPos0( pad->GetPosition() - module->GetPosition() );
pad->SetSize( wxSize( inductorPattern.m_Width, inductorPattern.m_Width ) );
pad->SetLayerSet( LSET( module->GetLayer() ) );
pad->SetAttribute( PAD_ATTRIB_SMD );
pad->SetShape( PAD_SHAPE_CIRCLE );
D_PAD* newpad = new D_PAD( *pad );
module->Pads().Insert( newpad, pad->Next() );
pad = newpad;
pad->SetPadName( "2" );
pad->SetPosition( inductorPattern.m_Start );
pad->SetPos0( pad->GetPosition() - module->GetPosition() );
// Modify text positions.
wxPoint refPos( ( inductorPattern.m_Start.x + inductorPattern.m_End.x ) / 2,
( inductorPattern.m_Start.y + inductorPattern.m_End.y ) / 2 );
wxPoint valPos = refPos;
refPos.y -= module->Reference().GetTextSize().y;
module->Reference().SetPosition( refPos );
valPos.y += module->Value().GetTextSize().y;
module->Value().SetPosition( valPos );
module->CalculateBoundingBox();
return module;
}

View File

@ -0,0 +1,55 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef MICROWAVE_INDUCTOR__H_
#define MICROWAVE_INDUCTOR__H_
#include <wx/wx.h> // for wxPoint
class MODULE;
class PCB_EDIT_FRAME;
namespace MWAVE
{
/**
* Parameters for construction of a microwave inductor
*/
struct INDUCTOR_PATTERN
{
public:
wxPoint m_Start;
wxPoint m_End;
int m_length; // full length trace.
int m_Width; // Trace width.
};
/**
* Creates an S-shaped coil footprint for microwave applications.
*/
MODULE* CreateMicrowaveInductor( INDUCTOR_PATTERN& aPattern,
PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage );
}
#endif // MICROWAVE_INDUCTOR__H_

View File

@ -42,12 +42,14 @@
#include <gr_basic.h>
#include <macros.h>
#include <base_units.h>
#include <validators.h>
#include <validators.h> //
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <microwave/microwave_inductor.h>
#include <pcbnew.h>
static std::vector< wxRealPoint > PolyEdges;
@ -57,10 +59,6 @@ static int PolyShapeType;
static void Exit_Self( EDA_DRAW_PANEL* aPanel, wxDC* aDC );
static void gen_arc( std::vector <wxPoint>& aBuffer,
wxPoint aStartPoint,
wxPoint aCenter,
int a_ArcAngle );
static void ShowBoundingBoxMicroWaveInductor( EDA_DRAW_PANEL* aPanel,
wxDC* aDC,
@ -68,30 +66,11 @@ static void ShowBoundingBoxMicroWaveInductor( EDA_DRAW_PANEL* aPanel,
bool aErase );
static int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
wxPoint aStartPoint, wxPoint aEndPoint,
int aLength, int aWidth );
///> An inductor pattern temporarily used during mu-wave inductor creation
static MWAVE::INDUCTOR_PATTERN s_inductor_pattern;
/**
* Creates a self-shaped coil for microwave applications.
*/
static MODULE* CreateMicrowaveInductor( PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage );
class MUWAVE_INDUCTOR
{
public:
wxPoint m_Start;
wxPoint m_End;
wxSize m_Size;
int m_length; // full length trace.
int m_Width; // Trace width.
// A flag set to true when mu-wave inductor is being created
bool m_Flag;
};
// An instance of MUWAVE_INDUCTOR temporary used during mu-wave inductor creation
static MUWAVE_INDUCTOR s_inductor_pattern;
///> A flag set to true when mu-wave inductor is being created
static bool s_inductorInProgress = false;
/* This function shows on screen the bounding box of the inductor that will be
@ -152,14 +131,14 @@ void Exit_Self( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
if( aPanel->IsMouseCaptured() )
aPanel->CallMouseCapture( aDC, wxDefaultPosition, false );
s_inductor_pattern.m_Flag = false;
s_inductorInProgress = false;
aPanel->SetMouseCapture( NULL, NULL );
}
void PCB_EDIT_FRAME::Begin_Self( wxDC* DC )
{
if( s_inductor_pattern.m_Flag )
if( s_inductorInProgress )
{
m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
m_canvas->SetMouseCapture( NULL, NULL );
@ -172,7 +151,10 @@ void PCB_EDIT_FRAME::Begin_Self( wxDC* DC )
s_inductor_pattern.m_Width = GetDesignSettings().GetCurrentTrackWidth();
s_inductor_pattern.m_End = GetCrossHairPosition();
MODULE* footprint = CreateMicrowaveInductor( this, errorMessage );
wxASSERT( s_inductorInProgress );
s_inductorInProgress = false;
MODULE* footprint = MWAVE::CreateMicrowaveInductor( s_inductor_pattern, this, errorMessage );
if( footprint )
{
@ -189,7 +171,7 @@ void PCB_EDIT_FRAME::Begin_Self( wxDC* DC )
s_inductor_pattern.m_Start = GetCrossHairPosition();
s_inductor_pattern.m_End = s_inductor_pattern.m_Start;
s_inductor_pattern.m_Flag = true;
s_inductorInProgress = true;
// Update the initial coordinates.
GetScreen()->m_O_Curseur = GetCrossHairPosition();
@ -200,378 +182,6 @@ void PCB_EDIT_FRAME::Begin_Self( wxDC* DC )
}
MODULE* CreateMicrowaveInductor( PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage )
{
/* Build a microwave inductor footprint.
* - Length Mself.lng
* - Extremities Mself.m_Start and Mself.m_End
* We must determine:
* Mself.nbrin = number of segments perpendicular to the direction
* (The coil nbrin will demicercles + 1 + 2 1 / 4 circle)
* Mself.lbrin = length of a strand
* Mself.radius = radius of rounded parts of the coil
* Mself.delta = segments extremities connection between him and the coil even
*
* The equations are
* Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin
* Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius
* Mself.lng = 2 * Mself.delta / / connections to the coil
+ (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last
+ (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded
* Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit
*
* The constraints are:
* Nbrin >= 2
* Mself.radius < Mself.m_Size.x
* Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord
* Mself.lbrin> Mself.radius * 2
*
* The calculation is conducted in the following way:
* Initially:
* Nbrin = 2
* Radius = 4 * m_Size.x (arbitrarily fixed value)
* Then:
* Increasing the number of segments to the desired length
* (Radius decreases if necessary)
*/
D_PAD* pad;
int ll;
wxString msg;
wxASSERT( s_inductor_pattern.m_Flag );
s_inductor_pattern.m_Flag = false;
wxPoint pt = s_inductor_pattern.m_End - s_inductor_pattern.m_Start;
int min_len = KiROUND( EuclideanNorm( pt ) );
s_inductor_pattern.m_length = min_len;
// Enter the desired length.
msg = StringFromValue( g_UserUnit, s_inductor_pattern.m_length );
wxTextEntryDialog dlg( NULL, wxEmptyString, _( "Length of Trace:" ), msg );
if( dlg.ShowModal() != wxID_OK )
return NULL; // canceled by user
msg = dlg.GetValue();
s_inductor_pattern.m_length = ValueFromString( g_UserUnit, msg );
// Control values (ii = minimum length)
if( s_inductor_pattern.m_length < min_len )
{
aErrorMessage = _( "Requested length < minimum length" );
return NULL;
}
// Calculate the elements.
std::vector <wxPoint> buffer;
ll = BuildCornersList_S_Shape( buffer, s_inductor_pattern.m_Start,
s_inductor_pattern.m_End, s_inductor_pattern.m_length,
s_inductor_pattern.m_Width );
if( !ll )
{
aErrorMessage = _( "Requested length too large" );
return NULL;
}
// Generate footprint. the value is also used as footprint name.
msg.Empty();
wxTextEntryDialog cmpdlg( NULL, wxEmptyString, _( "Component Value:" ), msg );
cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );
if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
return NULL; // Aborted by user
MODULE* module = aPcbFrame->CreateNewModule( msg );
// here the module is already in the BOARD, CreateNewModule() does that.
module->SetFPID( LIB_ID( std::string( "mw_inductor" ) ) );
module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
module->ClearFlags();
module->SetPosition( s_inductor_pattern.m_End );
// Generate segments
for( unsigned jj = 1; jj < buffer.size(); jj++ )
{
EDGE_MODULE* PtSegm;
PtSegm = new EDGE_MODULE( module );
PtSegm->SetStart( buffer[jj - 1] );
PtSegm->SetEnd( buffer[jj] );
PtSegm->SetWidth( s_inductor_pattern.m_Width );
PtSegm->SetLayer( module->GetLayer() );
PtSegm->SetShape( S_SEGMENT );
PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
module->GraphicalItems().PushBack( PtSegm );
}
// Place a pad on each end of coil.
pad = new D_PAD( module );
module->Pads().PushFront( pad );
pad->SetPadName( wxT( "1" ) );
pad->SetPosition( s_inductor_pattern.m_End );
pad->SetPos0( pad->GetPosition() - module->GetPosition() );
pad->SetSize( wxSize( s_inductor_pattern.m_Width, s_inductor_pattern.m_Width ) );
pad->SetLayerSet( LSET( module->GetLayer() ) );
pad->SetAttribute( PAD_ATTRIB_SMD );
pad->SetShape( PAD_SHAPE_CIRCLE );
D_PAD* newpad = new D_PAD( *pad );
module->Pads().Insert( newpad, pad->Next() );
pad = newpad;
pad->SetPadName( wxT( "2" ) );
pad->SetPosition( s_inductor_pattern.m_Start );
pad->SetPos0( pad->GetPosition() - module->GetPosition() );
// Modify text positions.
wxPoint refPos( ( s_inductor_pattern.m_Start.x + s_inductor_pattern.m_End.x ) / 2,
( s_inductor_pattern.m_Start.y + s_inductor_pattern.m_End.y ) / 2 );
wxPoint valPos = refPos;
refPos.y -= module->Reference().GetTextSize().y;
module->Reference().SetPosition( refPos );
valPos.y += module->Value().GetTextSize().y;
module->Value().SetPosition( valPos );
module->CalculateBoundingBox();
return module;
}
/**
* Function gen_arc
* generates an arc using arc approximation by lines:
* Center aCenter
* Angle "angle" (in 0.1 deg)
* @param aBuffer = a buffer to store points.
* @param aStartPoint = starting point of arc.
* @param aCenter = arc centre.
* @param a_ArcAngle = arc length in 0.1 degrees.
*/
static void gen_arc( std::vector <wxPoint>& aBuffer,
wxPoint aStartPoint,
wxPoint aCenter,
int a_ArcAngle )
{
#define SEGM_COUNT_PER_360DEG 16
wxPoint first_point = aStartPoint - aCenter;
int seg_count = ( ( abs( a_ArcAngle ) ) * SEGM_COUNT_PER_360DEG ) / 3600;
if( seg_count == 0 )
seg_count = 1;
double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count;
// Creates nb_seg point to approximate arc by segments:
for( int ii = 1; ii <= seg_count; ii++ )
{
double rot_angle = increment_angle * ii;
double fcos = cos( rot_angle );
double fsin = sin( rot_angle );
wxPoint currpt;
// Rotate current point:
currpt.x = KiROUND( ( first_point.x * fcos + first_point.y * fsin ) );
currpt.y = KiROUND( ( first_point.y * fcos - first_point.x * fsin ) );
wxPoint corner = aCenter + currpt;
aBuffer.push_back( corner );
}
}
/**
* Function BuildCornersList_S_Shape
* Create a path like a S-shaped coil
* @param aBuffer = a buffer where to store points (ends of segments)
* @param aStartPoint = starting point of the path
* @param aEndPoint = ending point of the path
* @param aLength = full length of the path
* @param aWidth = segment width
*/
int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
wxPoint aStartPoint, wxPoint aEndPoint,
int aLength, int aWidth )
{
/* We must determine:
* segm_count = number of segments perpendicular to the direction
* segm_len = length of a strand
* radius = radius of rounded parts of the coil
* stubs_len = length of the 2 stubs( segments parallel to the direction)
* connecting the start point to the start point of the S shape
* and the ending point to the end point of the S shape
* The equations are (assuming the area size of the entire shape is Size:
* Size.x = 2 * radius + segm_len
* Size.y = (segm_count + 2 ) * 2 * radius + 2 * stubs_len
* s_inductor_pattern.m_length = 2 * delta // connections to the coil
* + (segm_count-2) * segm_len // length of the strands except 1st and last
* + (segm_count) * (PI * radius) // length of rounded
* segm_len + / 2 - radius * 2) // length of 1st and last bit
*
* The constraints are:
* segm_count >= 2
* radius < m_Size.x
* Size.y = (radius * 4) + (2 * stubs_len)
* segm_len > radius * 2
*
* The calculation is conducted in the following way:
* first:
* segm_count = 2
* radius = 4 * Size.x (arbitrarily fixed value)
* Then:
* Increasing the number of segments to the desired length
* (radius decreases if necessary)
*/
wxSize size;
// This scale factor adjusts the arc length to handle
// the arc to segment approximation.
// because we use SEGM_COUNT_PER_360DEG segment to approximate a circle,
// the trace len must be corrected when calculated using arcs
// this factor adjust calculations and must be changed if SEGM_COUNT_PER_360DEG is modified
// because trace using segment is shorter the corresponding arc
// ADJUST_SIZE is the ratio between tline len and the arc len for an arc
// of 360/ADJUST_SIZE angle
#define ADJUST_SIZE 0.988
wxPoint pt = aEndPoint - aStartPoint;
double angle = -ArcTangente( pt.y, pt.x );
int min_len = KiROUND( EuclideanNorm( pt ) );
int segm_len = 0; // length of segments
int full_len; // full len of shape (sum of length of all segments + arcs)
/* Note: calculations are made for a vertical coil (more easy calculations)
* and after points are rotated to their actual position
* So the main direction is the Y axis.
* the 2 stubs are on the Y axis
* the others segments are parallel to the X axis.
*/
// Calculate the size of area (for a vertical shape)
size.x = min_len / 2;
size.y = min_len;
// Choose a reasonable starting value for the radius of the arcs.
int radius = std::min( aWidth * 5, size.x / 4 );
int segm_count; // number of full len segments
// the half size segments (first and last segment) are not counted here
int stubs_len = 0; // length of first or last segment (half size of others segments)
for( segm_count = 0; ; segm_count++ )
{
stubs_len = ( size.y - ( radius * 2 * (segm_count + 2 ) ) ) / 2;
if( stubs_len < size.y / 10 ) // Reduce radius.
{
stubs_len = size.y / 10;
radius = ( size.y - (2 * stubs_len) ) / ( 2 * (segm_count + 2) );
if( radius < aWidth ) // Radius too small.
{
// Unable to create line: Requested length value is too large for room
return 0;
}
}
segm_len = size.x - ( radius * 2 );
full_len = 2 * stubs_len; // Length of coil connections.
full_len += segm_len * segm_count; // Length of full length segments.
full_len += KiROUND( ( segm_count + 2 ) * M_PI * ADJUST_SIZE * radius ); // Ard arcs len
full_len += segm_len - (2 * radius); // Length of first and last segments
// (half size segments len = segm_len/2 - radius).
if( full_len >= aLength )
break;
}
// Adjust len by adjusting segm_len:
int delta_size = full_len - aLength;
// reduce len of the segm_count segments + 2 half size segments (= 1 full size segment)
segm_len -= delta_size / (segm_count + 1);
// Generate first line (the first stub) and first arc (90 deg arc)
pt = aStartPoint;
aBuffer.push_back( pt );
pt.y += stubs_len;
aBuffer.push_back( pt );
wxPoint centre = pt;
centre.x -= radius;
gen_arc( aBuffer, pt, centre, -900 );
pt = aBuffer.back();
int half_size_seg_len = segm_len / 2 - radius;
if( half_size_seg_len )
{
pt.x -= half_size_seg_len;
aBuffer.push_back( pt );
}
// Create shape.
int ii;
int sign = 1;
segm_count += 1; // increase segm_count to create the last half_size segment
for( ii = 0; ii < segm_count; ii++ )
{
int arc_angle;
if( ii & 1 ) // odd order arcs are greater than 0
sign = -1;
else
sign = 1;
arc_angle = 1800 * sign;
centre = pt;
centre.y += radius;
gen_arc( aBuffer, pt, centre, arc_angle );
pt = aBuffer.back();
pt.x += segm_len * sign;
aBuffer.push_back( pt );
}
// The last point is false:
// it is the end of a full size segment, but must be
// the end of the second half_size segment. Change it.
sign *= -1;
aBuffer.back().x = aStartPoint.x + radius * sign;
// create last arc
pt = aBuffer.back();
centre = pt;
centre.y += radius;
gen_arc( aBuffer, pt, centre, 900 * sign );
aBuffer.back();
// Rotate point
angle += 900;
for( unsigned jj = 0; jj < aBuffer.size(); jj++ )
{
RotatePoint( &aBuffer[jj].x, &aBuffer[jj].y, aStartPoint.x, aStartPoint.y, angle );
}
// push last point (end point)
aBuffer.push_back( aEndPoint );
return 1;
}
MODULE* PCB_EDIT_FRAME::CreateMuWaveBaseFootprint( const wxString& aValue,
int aTextSize, int aPadCount )
{