mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 10:13:19 +02:00
The plan goes like this: - eeschema still uses int in decidegrees - all the other things internally use double in decidegrees (or radians in temporaries) - in pcbnew UI the unit is *still* int in decidegrees The idea is to have better precision everywhere while keeping the user with int i angles. Hopefully, if a fractional angle doesn't come in from the outside, everything should *look* like an integer angle (unless I forgot something and it broke) When the time comes, simply updating the UI for allowing doubles from the user should be enough to get arbitrary angles in pcbnew.
812 lines
28 KiB
C++
812 lines
28 KiB
C++
/**
|
|
* @file class_aperture_macro.cpp
|
|
*/
|
|
|
|
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 1992-2010 Jean-Pierre Charras <jean-pierre.charras@gipsa-lab.inpg.fr>
|
|
* Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
|
* Copyright (C) 1992-2010 KiCad Developers, see change_log.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 <fctsys.h>
|
|
#include <common.h>
|
|
#include <macros.h>
|
|
#include <trigo.h>
|
|
#include <gr_basic.h>
|
|
|
|
#include <gerbview.h>
|
|
#include <class_GERBER.h>
|
|
|
|
|
|
|
|
/**
|
|
* Function scaletoIU
|
|
* converts a distance given in floating point to our internal units
|
|
*/
|
|
extern int scaletoIU( double aCoord, bool isMetric ); // defined it rs274d_read_XY_and_IJ_coordiantes.cpp
|
|
|
|
/**
|
|
* Function mapPt
|
|
* translates a point from the aperture macro coordinate system to our
|
|
* deci-mils coordinate system.
|
|
* @return wxPoint - The GerbView coordinate system vector.
|
|
*/
|
|
static wxPoint mapPt( double x, double y, bool isMetric )
|
|
{
|
|
wxPoint ret( scaletoIU( x, isMetric ), scaletoIU( y, isMetric ) );
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Function mapExposure
|
|
* translates the first parameter from an aperture macro into a current
|
|
* exposure setting.
|
|
* @param aParent = a GERBER_DRAW_ITEM that handle:
|
|
* ** m_Exposure A dynamic setting which can change throughout the
|
|
* reading of the gerber file, and it indicates whether the current tool
|
|
* is lit or not.
|
|
* ** m_ImageNegative A dynamic setting which can change throughout the reading
|
|
* of the gerber file, and it indicates whether the current D codes are to
|
|
* be interpreted as erasures or not.
|
|
* @return true to draw with current color, false to draw with alt color (erase)
|
|
*/
|
|
bool AM_PRIMITIVE::mapExposure( GERBER_DRAW_ITEM* aParent )
|
|
{
|
|
bool exposure;
|
|
switch( primitive_id )
|
|
{
|
|
case AMP_CIRCLE:
|
|
case AMP_LINE2:
|
|
case AMP_LINE20:
|
|
case AMP_LINE_CENTER:
|
|
case AMP_LINE_LOWER_LEFT:
|
|
case AMP_OUTLINE:
|
|
case AMP_THERMAL:
|
|
case AMP_POLYGON:
|
|
// All have an exposure parameter and can return true or false
|
|
switch( GetExposure(aParent) )
|
|
{
|
|
case 0: // exposure always OFF
|
|
exposure = false;
|
|
break;
|
|
|
|
default:
|
|
case 1: // exposure always OON
|
|
exposure = true;
|
|
break;
|
|
|
|
case 2: // reverse exposure
|
|
exposure = !aParent->GetLayerPolarity();
|
|
}
|
|
break;
|
|
|
|
case AMP_MOIRE:
|
|
case AMP_EOF:
|
|
case AMP_UNKNOWN:
|
|
default:
|
|
return true; // All have no exposure parameter and must return true (no change for exposure)
|
|
break;
|
|
}
|
|
|
|
return exposure ^ aParent->m_imageParams->m_ImageNegative;
|
|
}
|
|
|
|
|
|
/**
|
|
* Function GetExposure
|
|
* returns the first parameter in integer form. Some but not all primitives
|
|
* use the first parameter as an exposure control.
|
|
*/
|
|
int AM_PRIMITIVE::GetExposure(GERBER_DRAW_ITEM* aParent) const
|
|
{
|
|
// No D_CODE* for GetValue()
|
|
wxASSERT( params.size() && params[0].IsImmediate() );
|
|
return (int) params[0].GetValue( aParent->GetDcodeDescr() );
|
|
}
|
|
|
|
/**
|
|
* Function DrawBasicShape
|
|
* Draw the primitive shape for flashed items.
|
|
*/
|
|
void AM_PRIMITIVE::DrawBasicShape( GERBER_DRAW_ITEM* aParent,
|
|
EDA_RECT* aClipBox,
|
|
wxDC* aDC,
|
|
EDA_COLOR_T aColor, EDA_COLOR_T aAltColor,
|
|
wxPoint aShapePos,
|
|
bool aFilledShape )
|
|
{
|
|
static std::vector<wxPoint> polybuffer; // create a static buffer to avoid a lot of memory reallocation
|
|
polybuffer.clear();
|
|
|
|
wxPoint curPos = aShapePos;
|
|
D_CODE* tool = aParent->GetDcodeDescr();
|
|
double rotation;
|
|
if( mapExposure( aParent ) == false )
|
|
{
|
|
EXCHG(aColor, aAltColor);
|
|
}
|
|
|
|
switch( primitive_id )
|
|
{
|
|
case AMP_CIRCLE: // Circle, given diameter and position
|
|
{
|
|
/* Generated by an aperture macro declaration like:
|
|
* "1,1,0.3,0.5, 1.0*"
|
|
* type (1), exposure, diameter, pos.x, pos.y
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric );
|
|
curPos = aParent->GetABPosition( curPos );
|
|
int radius = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ) / 2;
|
|
if( !aFilledShape )
|
|
GRCircle( aClipBox, aDC, curPos, radius, 0, aColor );
|
|
else
|
|
GRFilledCircle( aClipBox, aDC, curPos, radius, aColor );
|
|
}
|
|
break;
|
|
|
|
case AMP_LINE2:
|
|
case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
|
|
{
|
|
/* Generated by an aperture macro declaration like:
|
|
* "2,1,0.3,0,0, 0.5, 1.0,-135*"
|
|
* type (2), exposure, width, start.x, start.y, end.x, end.y, rotation
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
ConvertShapeToPolygon( aParent, polybuffer );
|
|
|
|
// shape rotation:
|
|
rotation = params[6].GetValue( tool ) * 10.0;
|
|
if( rotation != 0)
|
|
{
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
RotatePoint( &polybuffer[ii], -rotation );
|
|
}
|
|
|
|
// Move to current position:
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
polybuffer[ii] += curPos;
|
|
polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
|
|
}
|
|
|
|
GRClosedPoly( aClipBox, aDC,
|
|
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
|
|
}
|
|
break;
|
|
|
|
case AMP_LINE_CENTER:
|
|
{
|
|
/* Generated by an aperture macro declaration like:
|
|
* "21,1,0.3,0.03,0,0,-135*"
|
|
* type (21), exposure, ,width, height, center pos.x, center pos.y, rotation
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
ConvertShapeToPolygon( aParent, polybuffer );
|
|
|
|
// shape rotation:
|
|
rotation = params[5].GetValue( tool ) * 10.0;
|
|
if( rotation != 0 )
|
|
{
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
RotatePoint( &polybuffer[ii], -rotation );
|
|
}
|
|
|
|
// Move to current position:
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
polybuffer[ii] += curPos;
|
|
polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
|
|
}
|
|
|
|
GRClosedPoly( aClipBox, aDC,
|
|
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
|
|
}
|
|
break;
|
|
|
|
case AMP_LINE_LOWER_LEFT:
|
|
{
|
|
/* Generated by an aperture macro declaration like:
|
|
* "22,1,0.3,0.03,0,0,-135*"
|
|
* type (22), exposure, ,width, height, corner pos.x, corner pos.y, rotation
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
ConvertShapeToPolygon( aParent, polybuffer );
|
|
|
|
// shape rotation:
|
|
rotation = params[5].GetValue( tool ) * 10.0;
|
|
if( rotation != 0)
|
|
{
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
RotatePoint( &polybuffer[ii], -rotation );
|
|
}
|
|
|
|
// Move to current position:
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
polybuffer[ii] += curPos;
|
|
polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
|
|
}
|
|
|
|
GRClosedPoly( aClipBox, aDC,
|
|
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
|
|
}
|
|
break;
|
|
|
|
case AMP_THERMAL:
|
|
{
|
|
/* Generated by an aperture macro declaration like:
|
|
* "7, 0,0,1.0,0.3,0.01,-13*"
|
|
* type (7), center.x , center.y, outside diam, inside diam, crosshair thickness, rotation
|
|
* type is not stored in parameters list, so the first parameter is center.x
|
|
*/
|
|
curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), m_GerbMetric );
|
|
ConvertShapeToPolygon( aParent, polybuffer );
|
|
|
|
// shape rotation:
|
|
rotation = params[5].GetValue( tool ) * 10.0;
|
|
|
|
// Because a thermal shape has 4 identical sub-shapes, only one is created in polybuffer.
|
|
// We must draw 4 sub-shapes rotated by 90 deg
|
|
std::vector<wxPoint> subshape_poly;
|
|
for( int ii = 0; ii < 4; ii++ )
|
|
{
|
|
subshape_poly = polybuffer;
|
|
double sub_rotation = rotation + 900 * ii;
|
|
for( unsigned jj = 0; jj < subshape_poly.size(); jj++ )
|
|
RotatePoint( &subshape_poly[jj], -sub_rotation );
|
|
|
|
// Move to current position:
|
|
for( unsigned jj = 0; jj < subshape_poly.size(); jj++ )
|
|
{
|
|
subshape_poly[jj] += curPos;
|
|
subshape_poly[jj] = aParent->GetABPosition( subshape_poly[jj] );
|
|
}
|
|
|
|
GRClosedPoly( aClipBox, aDC,
|
|
subshape_poly.size(), &subshape_poly[0], true, aAltColor,
|
|
aAltColor );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AMP_MOIRE: // A cross hair with n concentric circles
|
|
{
|
|
curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ),
|
|
m_GerbMetric );
|
|
|
|
/* Generated by an aperture macro declaration like:
|
|
* "6,0,0,0.125,.01,0.01,3,0.003,0.150,0"
|
|
* type(6), pos.x, pos.y, diam, penwidth, gap, circlecount, crosshair thickness, crosshaire len, rotation
|
|
* type is not stored in parameters list, so the first parameter is pos.x
|
|
*/
|
|
int outerDiam = scaletoIU( params[2].GetValue( tool ), m_GerbMetric );
|
|
int penThickness = scaletoIU( params[3].GetValue( tool ), m_GerbMetric );
|
|
int gap = scaletoIU( params[4].GetValue( tool ), m_GerbMetric );
|
|
int numCircles = KiROUND( params[5].GetValue( tool ) );
|
|
|
|
// Draw circles:
|
|
wxPoint center = aParent->GetABPosition( curPos );
|
|
// adjust outerDiam by this on each nested circle
|
|
int diamAdjust = (gap + penThickness); //*2; //Should we use * 2 ?
|
|
for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust )
|
|
{
|
|
if( outerDiam <= 0 )
|
|
break;
|
|
if( !aFilledShape )
|
|
{
|
|
// draw the border of the pen's path using two circles, each as narrow as possible
|
|
GRCircle( aClipBox, aDC, center, outerDiam / 2, 0, aColor );
|
|
GRCircle( aClipBox, aDC, center, outerDiam / 2 - penThickness, 0, aColor );
|
|
}
|
|
else // Filled mode
|
|
{
|
|
GRCircle( aClipBox, aDC, center,
|
|
(outerDiam - penThickness) / 2, penThickness, aColor );
|
|
}
|
|
}
|
|
|
|
// Draw the cross:
|
|
ConvertShapeToPolygon( aParent, polybuffer );
|
|
|
|
rotation = params[8].GetValue( tool ) * 10.0;
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
// shape rotation:
|
|
RotatePoint( &polybuffer[ii], -rotation );
|
|
// Move to current position:
|
|
polybuffer[ii] += curPos;
|
|
polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
|
|
}
|
|
|
|
GRClosedPoly( aClipBox, aDC,
|
|
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
|
|
}
|
|
break;
|
|
|
|
case AMP_OUTLINE:
|
|
{
|
|
/* Generated by an aperture macro declaration like:
|
|
* "4,1,3,0.0,0.0,0.0,0.5,0.5,0.5,0.5,0.0,-25"
|
|
* type(4), exposure, corners count, corner1.x, corner.1y, ..., rotation
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
int numPoints = (int) params[1].GetValue( tool );
|
|
rotation = params[numPoints * 2 + 4].GetValue( tool ) * 10.0;
|
|
wxPoint pos;
|
|
// Read points. numPoints does not include the starting point, so add 1.
|
|
for( int i = 0; i<numPoints + 1; ++i )
|
|
{
|
|
int jj = i * 2 + 2;
|
|
pos.x = scaletoIU( params[jj].GetValue( tool ), m_GerbMetric );
|
|
pos.y = scaletoIU( params[jj + 1].GetValue( tool ), m_GerbMetric );
|
|
polybuffer.push_back(pos);
|
|
}
|
|
// rotate polygon and move it to the actual position
|
|
// shape rotation:
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
RotatePoint( &polybuffer[ii], -rotation );
|
|
}
|
|
|
|
// Move to current position:
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
polybuffer[ii] += curPos;
|
|
polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
|
|
}
|
|
|
|
GRClosedPoly( aClipBox, aDC,
|
|
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
|
|
}
|
|
break;
|
|
|
|
case AMP_POLYGON: // Is a regular polygon
|
|
/* Generated by an aperture macro declaration like:
|
|
* "5,1,0.6,0,0,0.5,25"
|
|
* type(5), exposure, vertices count, pox.x, pos.y, diameter, rotation
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
curPos += mapPt( params[2].GetValue( tool ), params[3].GetValue( tool ), m_GerbMetric );
|
|
// Creates the shape:
|
|
ConvertShapeToPolygon( aParent, polybuffer );
|
|
|
|
// rotate polygon and move it to the actual position
|
|
rotation = params[5].GetValue( tool ) * 10.0;
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
RotatePoint( &polybuffer[ii], -rotation );
|
|
polybuffer[ii] += curPos;
|
|
polybuffer[ii] = aParent->GetABPosition( polybuffer[ii] );
|
|
}
|
|
GRClosedPoly( aClipBox, aDC,
|
|
polybuffer.size(), &polybuffer[0], aFilledShape, aColor, aColor );
|
|
break;
|
|
|
|
case AMP_EOF:
|
|
// not yet supported, waiting for you.
|
|
break;
|
|
|
|
case AMP_UNKNOWN:
|
|
default:
|
|
D( printf( "AM_PRIMITIVE::DrawBasicShape() err: unknown prim id %d\n",primitive_id) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Function ConvertShapeToPolygon (virtual)
|
|
* convert a shape to an equivalent polygon.
|
|
* Arcs and circles are approximated by segments
|
|
* Useful when a shape is not a graphic primitive (shape with hole,
|
|
* rotated shape ... ) and cannot be easily drawn.
|
|
* note for some schapes conbining circles and solid lines (rectangles), only rectangles are converted
|
|
* because circles are very easy to draw (no rotation problem) so convert them in polygons,
|
|
* and draw them as polygons is not a good idea.
|
|
*/
|
|
void AM_PRIMITIVE::ConvertShapeToPolygon( GERBER_DRAW_ITEM* aParent,
|
|
std::vector<wxPoint>& aBuffer )
|
|
{
|
|
D_CODE* tool = aParent->GetDcodeDescr();
|
|
|
|
switch( primitive_id )
|
|
{
|
|
case AMP_CIRCLE: // Circle, currently convertion not needed
|
|
break;
|
|
|
|
case AMP_LINE2:
|
|
case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
|
|
{
|
|
int width = scaletoIU( params[1].GetValue( tool ), m_GerbMetric );
|
|
wxPoint start = mapPt( params[2].GetValue( tool ),
|
|
params[3].GetValue( tool ), m_GerbMetric );
|
|
wxPoint end = mapPt( params[4].GetValue( tool ),
|
|
params[5].GetValue( tool ), m_GerbMetric );
|
|
wxPoint delta = end - start;
|
|
int len = KiROUND( EuclideanNorm( delta ) );
|
|
|
|
// To build the polygon, we must create a horizonta polygon starting to "start"
|
|
// and rotate it to have it end point to "end"
|
|
wxPoint currpt;
|
|
currpt.y += width / 2; // Upper left
|
|
aBuffer.push_back( currpt );
|
|
currpt.x = len; // Upper right
|
|
aBuffer.push_back( currpt );
|
|
currpt.y -= width; // lower right
|
|
aBuffer.push_back( currpt );
|
|
currpt.x = 0; // Upper left
|
|
aBuffer.push_back( currpt );
|
|
|
|
// Rotate rectangle and move it to the actual start point
|
|
double angle = ArcTangente( delta.y, delta.x );
|
|
|
|
for( unsigned ii = 0; ii < 4; ii++ )
|
|
{
|
|
RotatePoint( &aBuffer[ii], -angle );
|
|
aBuffer[ii] += start;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AMP_LINE_CENTER:
|
|
{
|
|
wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric );
|
|
wxPoint pos = mapPt( params[3].GetValue( tool ), params[4].GetValue( tool ), m_GerbMetric );
|
|
|
|
// Build poly:
|
|
pos.x -= size.x / 2;
|
|
pos.y -= size.y / 2; // Lower left
|
|
aBuffer.push_back( pos );
|
|
pos.y += size.y; // Upper left
|
|
aBuffer.push_back( pos );
|
|
pos.x += size.x; // Upper right
|
|
aBuffer.push_back( pos );
|
|
pos.y -= size.y; // lower right
|
|
aBuffer.push_back( pos );
|
|
}
|
|
break;
|
|
|
|
case AMP_LINE_LOWER_LEFT:
|
|
{
|
|
wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric );
|
|
wxPoint lowerLeft = mapPt( params[3].GetValue( tool ), params[4].GetValue(
|
|
tool ), m_GerbMetric );
|
|
|
|
// Build poly:
|
|
aBuffer.push_back( lowerLeft );
|
|
lowerLeft.y += size.y; // Upper left
|
|
aBuffer.push_back( lowerLeft );
|
|
lowerLeft.x += size.x; // Upper right
|
|
aBuffer.push_back( lowerLeft );
|
|
lowerLeft.y -= size.y; // lower right
|
|
aBuffer.push_back( lowerLeft );
|
|
}
|
|
break;
|
|
|
|
case AMP_THERMAL:
|
|
{
|
|
// Only 1/4 of the full shape is built, because the other 3 shapes will be draw from this first
|
|
// rotated by 90, 180 and 270 deg.
|
|
// params = center.x (unused here), center.y (unused here), outside diam, inside diam, crosshair thickness
|
|
int outerRadius = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2;
|
|
int innerRadius = scaletoIU( params[3].GetValue( tool ), m_GerbMetric ) / 2;
|
|
int halfthickness = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2;
|
|
double angle_start = RAD2DECIDEG( asin( (double) halfthickness / innerRadius ) );
|
|
|
|
// Draw shape in the first cadrant (X and Y > 0)
|
|
wxPoint pos, startpos;
|
|
|
|
// Inner arc
|
|
startpos.x = innerRadius;
|
|
double angle_end = 900 - angle_start;
|
|
for( double angle = angle_start; angle < angle_end; angle += 100 )
|
|
{
|
|
pos = startpos;
|
|
RotatePoint( &pos, angle );
|
|
aBuffer.push_back( pos );
|
|
}
|
|
|
|
// Last point
|
|
pos = startpos;
|
|
RotatePoint( &pos, angle_end );
|
|
aBuffer.push_back( pos );
|
|
|
|
// outer arc
|
|
startpos.x = outerRadius;
|
|
startpos.y = 0;
|
|
angle_start = RAD2DECIDEG( asin( (double) halfthickness / outerRadius ) );
|
|
angle_end = 900 - angle_start;
|
|
|
|
// First point, near Y axis, outer arc
|
|
for( double angle = angle_end; angle > angle_start; angle -= 100 )
|
|
{
|
|
pos = startpos;
|
|
RotatePoint( &pos, angle );
|
|
aBuffer.push_back( pos );
|
|
}
|
|
|
|
// last point
|
|
pos = startpos;
|
|
RotatePoint( &pos, angle_start );
|
|
aBuffer.push_back( pos );
|
|
|
|
aBuffer.push_back( aBuffer[0] ); // Close poly
|
|
}
|
|
break;
|
|
|
|
case AMP_MOIRE: // A cross hair with n concentric circles. Only the cros is build as polygon
|
|
// because circles can be drawn easily
|
|
{
|
|
int crossHairThickness = scaletoIU( params[6].GetValue( tool ), m_GerbMetric );
|
|
int crossHairLength = scaletoIU( params[7].GetValue( tool ), m_GerbMetric );
|
|
|
|
// Create cross. First create 1/4 of the shape.
|
|
// Others point are the same, totated by 90, 180 and 270 deg
|
|
wxPoint pos( crossHairThickness / 2, crossHairLength / 2 );
|
|
aBuffer.push_back( pos );
|
|
pos.y = crossHairThickness / 2;
|
|
aBuffer.push_back( pos );
|
|
pos.x = -crossHairLength / 2;
|
|
aBuffer.push_back( pos );
|
|
pos.y = -crossHairThickness / 2;
|
|
aBuffer.push_back( pos );
|
|
|
|
// Copy the 4 shape, rotated by 90, 180 and 270 deg
|
|
for( int jj = 1; jj <= 3; jj ++ )
|
|
{
|
|
for( int ii = 0; ii < 4; ii++ )
|
|
{
|
|
pos = aBuffer[ii];
|
|
RotatePoint( &pos, jj*900 );
|
|
aBuffer.push_back( pos );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AMP_OUTLINE:
|
|
// already is a polygon. Do nothing
|
|
break;
|
|
|
|
case AMP_POLYGON: // Creates a regular polygon
|
|
{
|
|
int vertexcount = KiROUND( params[1].GetValue( tool ) );
|
|
int radius = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2;
|
|
// rs274x said: vertex count = 3 ... 10, and the first corner is on the X axis
|
|
if( vertexcount < 3 )
|
|
vertexcount = 3;
|
|
if( vertexcount > 10 )
|
|
vertexcount = 10;
|
|
for( int ii = 0; ii <= vertexcount; ii++ )
|
|
{
|
|
wxPoint pos( radius, 0);
|
|
RotatePoint( &pos, ii * 3600 / vertexcount );
|
|
aBuffer.push_back( pos );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AMP_COMMENT:
|
|
case AMP_UNKNOWN:
|
|
case AMP_EOF:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** GetShapeDim
|
|
* Calculate a value that can be used to evaluate the size of text
|
|
* when displaying the D-Code of an item
|
|
* due to the complexity of the shape of some primitives
|
|
* one cannot calculate the "size" of a shape (only abounding box)
|
|
* but here, the "dimension" of the shape is the diameter of the primitive
|
|
* or for lines the width of the line
|
|
* @param aParent = the parent GERBER_DRAW_ITEM which is actually drawn
|
|
* @return a dimension, or -1 if no dim to calculate
|
|
*/
|
|
int AM_PRIMITIVE::GetShapeDim( GERBER_DRAW_ITEM* aParent )
|
|
{
|
|
int dim = -1;
|
|
D_CODE* tool = aParent->GetDcodeDescr();
|
|
|
|
switch( primitive_id )
|
|
{
|
|
case AMP_CIRCLE:
|
|
// params = exposure, diameter, pos.x, pos.y
|
|
dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // Diameter
|
|
break;
|
|
|
|
case AMP_LINE2:
|
|
case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
|
|
dim = scaletoIU( params[1].GetValue( tool ), m_GerbMetric ); // linne width
|
|
break;
|
|
|
|
case AMP_LINE_CENTER:
|
|
{
|
|
wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric );
|
|
dim = std::min(size.x, size.y);
|
|
}
|
|
break;
|
|
|
|
case AMP_LINE_LOWER_LEFT:
|
|
{
|
|
wxPoint size = mapPt( params[1].GetValue( tool ), params[2].GetValue( tool ), m_GerbMetric );
|
|
dim = std::min(size.x, size.y);
|
|
}
|
|
break;
|
|
|
|
case AMP_THERMAL:
|
|
{
|
|
// Only 1/4 of the full shape is built, because the other 3 shapes will be draw from this first
|
|
// rotated by 90, 180 and 270 deg.
|
|
// params = center.x (unused here), center.y (unused here), outside diam, inside diam, crosshair thickness
|
|
dim = scaletoIU( params[2].GetValue( tool ), m_GerbMetric ) / 2; // Outer diam
|
|
}
|
|
break;
|
|
|
|
case AMP_MOIRE: // A cross hair with n concentric circles.
|
|
dim = scaletoIU( params[7].GetValue( tool ), m_GerbMetric ); // = cross hair len
|
|
break;
|
|
|
|
case AMP_OUTLINE: // a free polygon :
|
|
// dim = min side of the bounding box (this is a poor criteria, but what is a good criteria b?)
|
|
{
|
|
// exposure, corners count, corner1.x, corner.1y, ..., rotation
|
|
int numPoints = (int) params[1].GetValue( tool );
|
|
// Read points. numPoints does not include the starting point, so add 1.
|
|
// and calculate the bounding box;
|
|
wxSize pos_min, pos_max, pos;
|
|
for( int i = 0; i<numPoints + 1; ++i )
|
|
{
|
|
int jj = i * 2 + 2;
|
|
pos.x = scaletoIU( params[jj].GetValue( tool ), m_GerbMetric );
|
|
pos.y = scaletoIU( params[jj + 1].GetValue( tool ), m_GerbMetric );
|
|
if( i == 0 )
|
|
pos_min = pos_max = pos;
|
|
else
|
|
{
|
|
// upper right corner:
|
|
if( pos_min.x > pos.x )
|
|
pos_min.x = pos.x;
|
|
if( pos_min.y > pos.y )
|
|
pos_min.y = pos.y;
|
|
// lower left corner:
|
|
if( pos_max.x < pos.x )
|
|
pos_max.x = pos.x;
|
|
if( pos_max.y < pos.y )
|
|
pos_max.y = pos.y;
|
|
}
|
|
}
|
|
// calculate dim
|
|
wxSize size;
|
|
size.x = pos_max.x - pos_min.x;
|
|
size.y = pos_max.y - pos_min.y;
|
|
dim = std::min( size.x, size.y );
|
|
}
|
|
break;
|
|
|
|
case AMP_POLYGON: // Regular polygon
|
|
dim = scaletoIU( params[4].GetValue( tool ), m_GerbMetric ) / 2; // Radius
|
|
break;
|
|
|
|
case AMP_COMMENT:
|
|
case AMP_UNKNOWN:
|
|
case AMP_EOF:
|
|
break;
|
|
}
|
|
return dim;
|
|
}
|
|
|
|
|
|
/**
|
|
* Function DrawApertureMacroShape
|
|
* Draw the primitive shape for flashed items.
|
|
* When an item is flashed, this is the shape of the item
|
|
*/
|
|
void APERTURE_MACRO::DrawApertureMacroShape( GERBER_DRAW_ITEM* aParent,
|
|
EDA_RECT* aClipBox, wxDC* aDC,
|
|
EDA_COLOR_T aColor, EDA_COLOR_T aAltColor,
|
|
wxPoint aShapePos, bool aFilledShape )
|
|
{
|
|
for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
|
|
prim_macro != primitives.end(); ++prim_macro )
|
|
{
|
|
prim_macro->DrawBasicShape( aParent, aClipBox, aDC,
|
|
aColor, aAltColor,
|
|
aShapePos,
|
|
aFilledShape );
|
|
}
|
|
}
|
|
|
|
/* Function HasNegativeItems
|
|
* return true if this macro has at least one aperture primitives
|
|
* that must be drawn in background color
|
|
* used to optimize screen refresh
|
|
*/
|
|
bool APERTURE_MACRO::HasNegativeItems( GERBER_DRAW_ITEM* aParent )
|
|
{
|
|
for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
|
|
prim_macro != primitives.end(); ++prim_macro )
|
|
{
|
|
if( prim_macro->mapExposure( aParent ) == false ) // = is negative
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/** GetShapeDim
|
|
* Calculate a value that can be used to evaluate the size of text
|
|
* when displaying the D-Code of an item
|
|
* due to the complexity of a shape using many primitives
|
|
* one cannot calculate the "size" of a shape (only abounding box)
|
|
* but most of aperture macro are using one or few primitives
|
|
* and the "dimension" of the shape is the diameter of the primitive
|
|
* (or the max diameter of primitives)
|
|
* @return a dimension, or -1 if no dim to calculate
|
|
*/
|
|
int APERTURE_MACRO::GetShapeDim( GERBER_DRAW_ITEM* aParent )
|
|
{
|
|
int dim = -1;
|
|
for( AM_PRIMITIVES::iterator prim_macro = primitives.begin();
|
|
prim_macro != primitives.end(); ++prim_macro )
|
|
{
|
|
int pdim = prim_macro->GetShapeDim( aParent );
|
|
if( dim < pdim )
|
|
dim = pdim;
|
|
}
|
|
|
|
return dim;
|
|
}
|
|
|
|
|
|
/**
|
|
* function GetLocalParam
|
|
* Usually, parameters are defined inside the aperture primitive
|
|
* using immediate mode or defered mode.
|
|
* in defered mode the value is defined in a DCODE that want to use the aperture macro.
|
|
* But some parameters are defined outside the aperture primitive
|
|
* and are local to the aperture macro
|
|
* @return the value of a defered parameter defined inside the aperture macro
|
|
* @param aParamId = the param id (defined by $3 or $5 ..) to evaluate
|
|
*/
|
|
double APERTURE_MACRO::GetLocalParam( const D_CODE* aDcode, unsigned aParamId ) const
|
|
{
|
|
// find parameter descr.
|
|
const AM_PARAM * param = NULL;
|
|
for( unsigned ii = 0; ii < m_localparamStack.size(); ii ++ )
|
|
{
|
|
if( m_localparamStack[ii].GetIndex() == aParamId )
|
|
{
|
|
param = &m_localparamStack[ii];
|
|
break;
|
|
}
|
|
}
|
|
if ( param == NULL ) // not found
|
|
return 0.0;
|
|
// Evaluate parameter
|
|
double value = param->GetValue( aDcode );
|
|
return value;
|
|
}
|