kicad/gerbview/dcode.cpp

471 lines
13 KiB
C++
Raw Normal View History

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2009 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 1992-2011 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
*/
/**
* @file dcode.cpp
* @brief D_CODE class implementation
*/
#include <fctsys.h>
#include <common.h>
#include <class_drawpanel.h>
#include <trigo.h>
#include <gerbview_frame.h>
#include <class_gerber_file_image.h>
#include <convert_to_biu.h>
#define DCODE_DEFAULT_SIZE Millimeter2iu( 0.1 )
/* Format Gerber: NOTES:
* Tools and D_CODES
* tool number (identification of shapes)
* 1 to 999
*
* D_CODES:
2010-09-29 19:00:35 +00:00
* D01 ... D9 = command codes:
* D01 = activating light (pen down) while moving
* D02 = light extinction (pen up) while moving
* D03 = Flash
* D04 to D09 = non used
* D10 ... D999 = Identification Tool (Shape id)
2010-09-28 14:42:05 +00:00
*
2010-09-29 19:00:35 +00:00
* For tools defining a shape):
2010-09-28 14:42:05 +00:00
* DCode min = D10
* DCode max = 999
*/
/***************/
/* Class DCODE */
/***************/
D_CODE::D_CODE( int num_dcode )
{
m_Num_Dcode = num_dcode;
Clear_D_CODE_Data();
}
D_CODE::~D_CODE()
{
}
void D_CODE::Clear_D_CODE_Data()
{
m_Size.x = DCODE_DEFAULT_SIZE;
m_Size.y = DCODE_DEFAULT_SIZE;
2008-11-08 06:44:07 +00:00
m_Shape = APT_CIRCLE;
m_Drill.x = m_Drill.y = 0;
m_DrillShape = APT_DEF_NO_HOLE;
m_InUse = false;
m_Defined = false;
m_Macro = NULL;
2010-09-28 14:42:05 +00:00
m_Rotation = 0.0;
m_EdgesCount = 0;
m_PolyCorners.clear();
}
const wxChar* D_CODE::ShowApertureType( APERTURE_T aType )
{
const wxChar* ret;
switch( aType )
{
case APT_CIRCLE:
ret = wxT( "Round" ); break;
case APT_RECT:
ret = wxT( "Rect" ); break;
case APT_OVAL:
ret = wxT( "Oval" ); break;
case APT_POLYGON:
ret = wxT( "Poly" ); break;
case APT_MACRO:
ret = wxT( "Macro" ); break;
default:
ret = wxT( "???" ); break;
}
return ret;
}
int D_CODE::GetShapeDim( GERBER_DRAW_ITEM* aParent )
{
int dim = -1;
switch( m_Shape )
{
case APT_CIRCLE:
dim = m_Size.x;
break;
case APT_RECT:
case APT_OVAL:
dim = std::min( m_Size.x, m_Size.y );
break;
case APT_POLYGON:
dim = std::min( m_Size.x, m_Size.y );
break;
case APT_MACRO:
if( m_Macro )
dim = m_Macro->GetShapeDim( aParent );
break;
default:
break;
}
return dim;
}
void D_CODE::DrawFlashedShape( GERBER_DRAW_ITEM* aParent,
EDA_RECT* aClipBox, wxDC* aDC, EDA_COLOR_T aColor,
EDA_COLOR_T aAltColor,
wxPoint aShapePos, bool aFilledShape )
2010-09-28 14:42:05 +00:00
{
int radius;
switch( m_Shape )
{
case APT_MACRO:
GetMacro()->DrawApertureMacroShape( aParent, aClipBox, aDC, aColor, aAltColor,
aShapePos, aFilledShape);
break;
2010-09-28 14:42:05 +00:00
case APT_CIRCLE:
radius = m_Size.x >> 1;
if( !aFilledShape )
GRCircle( aClipBox, aDC, aParent->GetABPosition(aShapePos), radius, 0, aColor );
2010-09-28 14:42:05 +00:00
else
if( m_DrillShape == APT_DEF_NO_HOLE )
{
GRFilledCircle( aClipBox, aDC, aParent->GetABPosition(aShapePos),
radius, aColor );
}
else if( APT_DEF_ROUND_HOLE == 1 ) // round hole in shape
{
2010-09-28 14:42:05 +00:00
int width = (m_Size.x - m_Drill.x ) / 2;
GRCircle( aClipBox, aDC, aParent->GetABPosition(aShapePos),
radius - (width / 2), width, aColor );
}
2010-09-28 14:42:05 +00:00
else // rectangular hole
{
2010-09-28 14:42:05 +00:00
if( m_PolyCorners.size() == 0 )
ConvertShapeToPolygon();
DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos );
}
2010-09-28 14:42:05 +00:00
break;
case APT_RECT:
{
wxPoint start;
start.x = aShapePos.x - m_Size.x / 2;
start.y = aShapePos.y - m_Size.y / 2;
2010-09-28 14:42:05 +00:00
wxPoint end = start + m_Size;
start = aParent->GetABPosition( start );
end = aParent->GetABPosition( end );
2010-09-28 14:42:05 +00:00
if( !aFilledShape )
{
GRRect( aClipBox, aDC, start.x, start.y, end.x, end.y, 0, aColor );
2010-09-28 14:42:05 +00:00
}
else if( m_DrillShape == APT_DEF_NO_HOLE )
2010-09-28 14:42:05 +00:00
{
GRFilledRect( aClipBox, aDC, start.x, start.y, end.x, end.y, 0, aColor, aColor );
2010-09-28 14:42:05 +00:00
}
else
{
if( m_PolyCorners.size() == 0 )
ConvertShapeToPolygon();
DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos );
}
}
break;
2010-09-28 14:42:05 +00:00
case APT_OVAL:
{
wxPoint start = aShapePos;
wxPoint end = aShapePos;
2010-09-28 14:42:05 +00:00
if( m_Size.x > m_Size.y ) // horizontal oval
{
int delta = (m_Size.x - m_Size.y) / 2;
start.x -= delta;
end.x += delta;
radius = m_Size.y;
}
else // horizontal oval
{
int delta = (m_Size.y - m_Size.x) / 2;
start.y -= delta;
end.y += delta;
radius = m_Size.x;
}
start = aParent->GetABPosition( start );
end = aParent->GetABPosition( end );
2010-09-28 14:42:05 +00:00
if( !aFilledShape )
{
GRCSegm( aClipBox, aDC, start.x, start.y, end.x, end.y, radius, aColor );
2010-09-28 14:42:05 +00:00
}
else if( m_DrillShape == APT_DEF_NO_HOLE )
2010-09-28 14:42:05 +00:00
{
GRFillCSegm( aClipBox, aDC, start.x, start.y, end.x, end.y, radius, aColor );
2010-09-28 14:42:05 +00:00
}
else
{
if( m_PolyCorners.size() == 0 )
ConvertShapeToPolygon();
DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos );
2010-09-28 14:42:05 +00:00
}
}
break;
case APT_POLYGON:
if( m_PolyCorners.size() == 0 )
ConvertShapeToPolygon();
DrawFlashedPolygon( aParent, aClipBox, aDC, aColor, aFilledShape, aShapePos );
2010-09-28 14:42:05 +00:00
break;
}
}
void D_CODE::DrawFlashedPolygon( GERBER_DRAW_ITEM* aParent,
EDA_RECT* aClipBox, wxDC* aDC,
EDA_COLOR_T aColor, bool aFilled,
2010-09-28 14:42:05 +00:00
const wxPoint& aPosition )
{
2010-09-28 14:42:05 +00:00
if( m_PolyCorners.size() == 0 )
return;
2010-09-28 14:42:05 +00:00
std::vector<wxPoint> points;
points = m_PolyCorners;
2010-09-28 14:42:05 +00:00
for( unsigned ii = 0; ii < points.size(); ii++ )
{
points[ii] += aPosition;
points[ii] = aParent->GetABPosition( points[ii] );
2010-09-28 14:42:05 +00:00
}
2010-09-28 14:42:05 +00:00
GRClosedPoly( aClipBox, aDC, points.size(), &points[0], aFilled, aColor, aColor );
}
#define SEGS_CNT 32 // number of segments to approximate a circle
// A helper function for D_CODE::ConvertShapeToPolygon(). Add a hole to a polygon
static void addHoleToPolygon( std::vector<wxPoint>& aBuffer,
APERTURE_DEF_HOLETYPE aHoleShape,
wxSize aSize,
wxPoint aAnchorPos );
2010-09-28 14:42:05 +00:00
void D_CODE::ConvertShapeToPolygon()
{
wxPoint initialpos;
2011-03-03 19:08:13 +00:00
wxPoint currpos;
2010-09-28 14:42:05 +00:00
m_PolyCorners.clear();
switch( m_Shape )
{
2010-09-28 14:42:05 +00:00
case APT_CIRCLE: // creates only a circle with rectangular hole
currpos.x = m_Size.x >> 1;
initialpos = currpos;
2010-09-28 14:42:05 +00:00
for( unsigned ii = 0; ii <= SEGS_CNT; ii++ )
{
currpos = initialpos;
RotatePoint( &currpos, ii * 3600.0 / SEGS_CNT );
2010-09-28 14:42:05 +00:00
m_PolyCorners.push_back( currpos );
}
addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos );
2010-09-28 14:42:05 +00:00
break;
2008-11-08 06:44:07 +00:00
2010-09-28 14:42:05 +00:00
case APT_RECT:
currpos.x = m_Size.x / 2;
currpos.y = m_Size.y / 2;
2010-09-28 14:42:05 +00:00
initialpos = currpos;
m_PolyCorners.push_back( currpos );
currpos.x -= m_Size.x;
m_PolyCorners.push_back( currpos );
currpos.y -= m_Size.y;
m_PolyCorners.push_back( currpos );
currpos.x += m_Size.x;
m_PolyCorners.push_back( currpos );
currpos.y += m_Size.y;
m_PolyCorners.push_back( currpos ); // close polygon
addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos );
2010-09-28 14:42:05 +00:00
break;
2010-09-28 14:42:05 +00:00
case APT_OVAL:
{
int delta, radius;
// we create an horizontal oval shape. then rotate if needed
if( m_Size.x > m_Size.y ) // horizontal oval
{
delta = (m_Size.x - m_Size.y) / 2;
2010-09-28 14:42:05 +00:00
radius = m_Size.y / 2;
}
else // vertical oval
{
delta = (m_Size.y - m_Size.x) / 2;
radius = m_Size.x / 2;
}
2010-09-28 14:42:05 +00:00
currpos.y = radius;
initialpos = currpos;
m_PolyCorners.push_back( currpos );
// build the right arc of the shape
unsigned ii = 0;
2010-09-28 14:42:05 +00:00
for( ; ii <= SEGS_CNT / 2; ii++ )
{
currpos = initialpos;
RotatePoint( &currpos, ii * 3600.0 / SEGS_CNT );
2010-09-28 14:42:05 +00:00
currpos.x += delta;
m_PolyCorners.push_back( currpos );
}
// build the left arc of the shape
for( ii = SEGS_CNT / 2; ii <= SEGS_CNT; ii++ )
{
currpos = initialpos;
RotatePoint( &currpos, ii * 3600.0 / SEGS_CNT );
2010-09-28 14:42:05 +00:00
currpos.x -= delta;
m_PolyCorners.push_back( currpos );
}
2010-09-28 14:42:05 +00:00
m_PolyCorners.push_back( initialpos ); // close outline
if( m_Size.y > m_Size.x ) // vertical oval, rotate polygon.
2010-09-28 14:42:05 +00:00
{
for( unsigned jj = 0; jj < m_PolyCorners.size(); jj++ )
RotatePoint( &m_PolyCorners[jj], 900 );
}
addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos );
}
break;
case APT_POLYGON:
currpos.x = m_Size.x >> 1; // first point is on X axis
initialpos = currpos;
// rs274x said: m_EdgesCount = 3 ... 12
if( m_EdgesCount < 3 )
m_EdgesCount = 3;
if( m_EdgesCount > 12 )
m_EdgesCount = 12;
for( int ii = 0; ii <= m_EdgesCount; ii++ )
2010-09-28 14:42:05 +00:00
{
currpos = initialpos;
RotatePoint( &currpos, ii * 3600.0 / m_EdgesCount );
m_PolyCorners.push_back( currpos );
2010-09-28 14:42:05 +00:00
}
addHoleToPolygon( m_PolyCorners, m_DrillShape, m_Drill, initialpos );
if( m_Rotation ) // vertical oval, rotate polygon.
2010-09-28 14:42:05 +00:00
{
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
2012-04-19 06:55:45 +00:00
int angle = KiROUND( m_Rotation * 10 );
for( unsigned jj = 0; jj < m_PolyCorners.size(); jj++ )
{
RotatePoint( &m_PolyCorners[jj], -angle );
}
}
2010-09-28 14:42:05 +00:00
break;
2010-09-28 14:42:05 +00:00
case APT_MACRO:
2010-09-28 14:42:05 +00:00
// TODO
break;
}
}
// The helper function for D_CODE::ConvertShapeToPolygon().
// Add a hole to a polygon
static void addHoleToPolygon( std::vector<wxPoint>& aBuffer,
APERTURE_DEF_HOLETYPE aHoleShape,
wxSize aSize,
wxPoint aAnchorPos )
{
wxPoint currpos;
if( aHoleShape == APT_DEF_ROUND_HOLE ) // build a round hole
{
for( int ii = 0; ii <= SEGS_CNT; ii++ )
{
currpos.x = 0;
currpos.y = aSize.x / 2; // aSize.x / 2 is the radius of the hole
RotatePoint( &currpos, ii * 3600.0 / SEGS_CNT );
aBuffer.push_back( currpos );
}
aBuffer.push_back( aAnchorPos ); // link to outline
}
if( aHoleShape == APT_DEF_RECT_HOLE ) // Create rectangular hole
{
currpos.x = aSize.x / 2;
currpos.y = aSize.y / 2;
aBuffer.push_back( currpos ); // link to hole and begin hole
currpos.x -= aSize.x;
aBuffer.push_back( currpos );
currpos.y -= aSize.y;
aBuffer.push_back( currpos );
currpos.x += aSize.x;
aBuffer.push_back( currpos );
currpos.y += aSize.y;
aBuffer.push_back( currpos ); // close hole
aBuffer.push_back( aAnchorPos ); // link to outline
}
}