mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
Recommendation is to avoid using the year nomenclature as this information is already encoded in the git repo. Avoids needing to repeatly update. Also updates AUTHORS.txt from current repo with contributor names
648 lines
23 KiB
C++
648 lines
23 KiB
C++
/**
|
|
* @file am_primitive.cpp
|
|
*/
|
|
|
|
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 1992-2017 Jean-Pierre Charras <jp.charras at wanadoo.fr>
|
|
* Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
|
* Copyright The 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 <trigo.h>
|
|
#include <convert_basic_shapes_to_polygon.h>
|
|
#include <math/util.h> // for KiROUND
|
|
|
|
#include <gerbview.h>
|
|
#include <gerber_file_image.h>
|
|
|
|
|
|
/**
|
|
* Convert a distance given in floating point to our internal units.
|
|
*/
|
|
extern int scaletoIU( double aCoord, bool isMetric );
|
|
|
|
|
|
/**
|
|
* Translate a point from the aperture macro coordinate system to our
|
|
* deci-mils coordinate system.
|
|
*
|
|
* @return The GerbView coordinate system vector.
|
|
*/
|
|
static VECTOR2I mapPt( double x, double y, bool isMetric )
|
|
{
|
|
VECTOR2I ret( scaletoIU( x, isMetric ), scaletoIU( y, isMetric ) );
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool AM_PRIMITIVE::IsAMPrimitiveExposureOn( APERTURE_MACRO* aApertMacro ) const
|
|
{
|
|
/*
|
|
* Some but not all primitives use the first parameter as an exposure control.
|
|
* Others are always ON.
|
|
* In a aperture macro shape, a basic primitive with exposure off is a hole in the shape
|
|
* it is NOT a negative shape
|
|
*/
|
|
wxASSERT( m_Params.size() );
|
|
|
|
switch( m_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_POLYGON:
|
|
// All have an exposure parameter and can return a value (0 or 1)
|
|
return m_Params[0].GetValueFromMacro( aApertMacro ) != 0;
|
|
break;
|
|
|
|
case AMP_THERMAL: // Exposure is always on
|
|
case AMP_MOIRE: // Exposure is always on
|
|
case AMP_UNKNOWN:
|
|
default:
|
|
return 1; // All have no exposure parameter and are always 0N return true
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void AM_PRIMITIVE::ConvertBasicShapeToPolygon( APERTURE_MACRO* aApertMacro,
|
|
SHAPE_POLY_SET& aShapeBuffer )
|
|
{
|
|
// Draw the primitive shape for flashed items.
|
|
// Note: rotation of primitives inside a macro must be always done around the macro origin.
|
|
// Create a static buffer to avoid a lot of memory reallocation.
|
|
static std::vector<VECTOR2I> polybuffer;
|
|
polybuffer.clear();
|
|
|
|
aApertMacro->EvalLocalParams( *this );
|
|
|
|
switch( m_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, <rotation>
|
|
* <rotation> is a optional parameter: rotation from origin.
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
ConvertShapeToPolygon( aApertMacro, polybuffer );
|
|
|
|
// shape rotation (if any):
|
|
if( m_Params.size() >= 5 )
|
|
{
|
|
EDA_ANGLE rotation( m_Params[4].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
|
|
|
if( !rotation.IsZero() )
|
|
{
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
RotatePoint( polybuffer[ii], -rotation );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case AMP_LINE2:
|
|
case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
|
|
{
|
|
/* Vector Line, Primitive Code 20.
|
|
* A vector line is a rectangle defined by its line width, start and end points.
|
|
* The line ends are rectangular.
|
|
*/
|
|
/* 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( aApertMacro, polybuffer );
|
|
|
|
// shape rotation:
|
|
EDA_ANGLE rotation( m_Params[6].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
|
|
|
if( !rotation.IsZero() )
|
|
{
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
RotatePoint( polybuffer[ii], -rotation );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case AMP_LINE_CENTER:
|
|
{
|
|
/* Center Line, Primitive Code 21
|
|
* A center line primitive is a rectangle defined by its width, height, and center point
|
|
*/
|
|
/* 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( aApertMacro, polybuffer );
|
|
|
|
// shape rotation:
|
|
EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
|
|
|
if( !rotation.IsZero() )
|
|
{
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
RotatePoint( polybuffer[ii], -rotation );
|
|
}
|
|
|
|
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( aApertMacro, polybuffer );
|
|
|
|
// shape rotation:
|
|
EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
|
|
|
if( !rotation.IsZero() )
|
|
{
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
RotatePoint( polybuffer[ii], -rotation );
|
|
}
|
|
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
|
|
*
|
|
* The thermal primitive is a ring (annulus) interrupted by four gaps. Exposure is always
|
|
* on.
|
|
*/
|
|
std::vector<VECTOR2I> subshape_poly;
|
|
VECTOR2I center( mapPt( m_Params[0].GetValueFromMacro( aApertMacro ),
|
|
m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric ) );
|
|
ConvertShapeToPolygon( aApertMacro, subshape_poly );
|
|
|
|
// shape rotation:
|
|
EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
|
|
|
// Because a thermal shape has 4 identical sub-shapes, only one is created in subshape_poly.
|
|
// We must draw 4 sub-shapes rotated by 90 deg
|
|
for( int ii = 0; ii < 4; ii++ )
|
|
{
|
|
polybuffer = subshape_poly;
|
|
EDA_ANGLE sub_rotation = ANGLE_90 * ii;
|
|
|
|
for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
|
|
RotatePoint( polybuffer[jj], -sub_rotation );
|
|
|
|
// Move to center position given by the tool, and rotate the full shape around
|
|
// the center position (origin of the macro):
|
|
for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
|
|
{
|
|
polybuffer[jj] += center;
|
|
RotatePoint( polybuffer[jj], -rotation );
|
|
}
|
|
|
|
aShapeBuffer.NewOutline();
|
|
|
|
for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
|
|
aShapeBuffer.Append( polybuffer[jj] );
|
|
|
|
aShapeBuffer.Append( polybuffer[0] );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AMP_MOIRE:
|
|
{
|
|
/* Moire, Primitive Code 6
|
|
* The moire primitive is a cross hair centered on concentric rings (annuli).
|
|
* Exposure is always on.
|
|
*/
|
|
|
|
/* 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,
|
|
* crosshair len, rotation. The type is not stored in parameters list, so the first
|
|
* parameter is pos.x.
|
|
*/
|
|
int outerDiam = scaletoIU( m_Params[2].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
int penThickness = scaletoIU( m_Params[3].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
int gap = scaletoIU( m_Params[4].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
int numCircles = KiROUND( m_Params[5].GetValueFromMacro( aApertMacro ) );
|
|
|
|
// Adjust the allowed approx error to convert arcs to segments:
|
|
int arc_to_seg_error = gerbIUScale.mmToIU( 0.005 ); // Allow 5 microns
|
|
|
|
// Draw circles @ position pos.x, pos.y given by the tool:
|
|
VECTOR2I center( mapPt( m_Params[0].GetValueFromMacro( aApertMacro ),
|
|
m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric ) );
|
|
|
|
EDA_ANGLE rotation( m_Params[8].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
|
|
|
// adjust outerDiam by this on each nested circle
|
|
int diamAdjust = ( gap + penThickness ) * 2;
|
|
|
|
for( int i = 0; i < numCircles; ++i, outerDiam -= diamAdjust )
|
|
{
|
|
if( outerDiam <= 0 )
|
|
break;
|
|
|
|
// calculate the rotated position of the center:
|
|
VECTOR2I circle_center = center;
|
|
RotatePoint( circle_center, -rotation );
|
|
|
|
// Note: outerDiam is the outer diameter of the ring.
|
|
// the ring graphic diameter is (outerDiam - penThickness)
|
|
if( outerDiam <= penThickness )
|
|
{ // No room to draw a ring (no room for the hole):
|
|
// draw a circle instead (with no hole), with the right diameter
|
|
TransformCircleToPolygon( aShapeBuffer, circle_center, outerDiam / 2, arc_to_seg_error,
|
|
ERROR_INSIDE );
|
|
}
|
|
else
|
|
{
|
|
TransformRingToPolygon( aShapeBuffer, circle_center, ( outerDiam - penThickness ) / 2,
|
|
penThickness, arc_to_seg_error, ERROR_INSIDE );
|
|
}
|
|
}
|
|
|
|
// Draw the cross:
|
|
ConvertShapeToPolygon( aApertMacro, polybuffer );
|
|
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
// move crossair shape to center and rotate shape:
|
|
polybuffer[ii] += center;
|
|
RotatePoint( polybuffer[ii], -rotation );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case AMP_OUTLINE:
|
|
{
|
|
/* Outline, Primitive Code 4
|
|
* An outline primitive is an area enclosed by an n-point polygon defined by its start
|
|
* point and n
|
|
* subsequent points. The outline must be closed, i.e. the last point must be equal to
|
|
* the start point. There must be at least one subsequent point (to close the outline).
|
|
* The outline of the primitive is actually the contour (see 2.6) that consists of linear
|
|
* segments only, so it must conform to all the requirements described for contours.
|
|
* Warning: Make no mistake: n is the number of subsequent points, being the number of
|
|
* vertices of the outline or one less than the number of coordinate pairs.
|
|
*/
|
|
/* 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, ..., corner1.x, corner.1y,
|
|
* rotation
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
// m_Params[0] is the exposure and m_Params[1] is the corners count after the first corner
|
|
int numCorners = (int) m_Params[1].GetValueFromMacro( aApertMacro );
|
|
|
|
// the shape rotation is the last param of list, after corners
|
|
int last_prm = m_Params.size() - 1;
|
|
EDA_ANGLE rotation( m_Params[last_prm].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
|
VECTOR2I pos;
|
|
|
|
// Read points.
|
|
// Note: numCorners is the polygon corner count, following the first corner
|
|
// * the polygon is always closed,
|
|
// * therefore the last XY coordinate is the same as the first
|
|
int prm_idx = 2; // m_Params[2] is the first X coordinate
|
|
|
|
for( int i = 0; i <= numCorners; ++i )
|
|
{
|
|
pos.x = scaletoIU( m_Params[prm_idx].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
prm_idx++;
|
|
pos.y = scaletoIU( m_Params[prm_idx].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
prm_idx++;
|
|
polybuffer.push_back(pos);
|
|
|
|
// Guard: ensure prm_idx < last_prm
|
|
// I saw malformed gerber files with numCorners = number
|
|
// of coordinates instead of number of coordinates following the first point
|
|
if( prm_idx >= last_prm )
|
|
break;
|
|
}
|
|
|
|
// rotate polygon and move it to the actual position shape rotation:
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
RotatePoint( polybuffer[ii], -rotation );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case AMP_POLYGON:
|
|
{
|
|
/* Polygon, Primitive Code 5
|
|
* A polygon primitive is a regular polygon defined by the number of vertices n, the
|
|
* center point and the diameter of the circumscribed circle
|
|
*/
|
|
/* 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
|
|
*/
|
|
VECTOR2I curPos( mapPt( m_Params[2].GetValueFromMacro( aApertMacro ),
|
|
m_Params[3].GetValueFromMacro( aApertMacro ), m_GerbMetric ) );
|
|
|
|
// Creates the shape:
|
|
ConvertShapeToPolygon( aApertMacro, polybuffer );
|
|
|
|
// move and rotate polygonal shape
|
|
EDA_ANGLE rotation( m_Params[5].GetValueFromMacro( aApertMacro ), DEGREES_T );
|
|
|
|
for( unsigned ii = 0; ii < polybuffer.size(); ii++ )
|
|
{
|
|
polybuffer[ii] += curPos;
|
|
RotatePoint( polybuffer[ii], -rotation );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case AMP_COMMENT:
|
|
case AMP_UNKNOWN:
|
|
break;
|
|
}
|
|
|
|
if( polybuffer.size() > 1 ) // a valid polygon has more than 1 corner
|
|
{
|
|
aShapeBuffer.NewOutline();
|
|
|
|
for( unsigned jj = 0; jj < polybuffer.size(); jj++ )
|
|
aShapeBuffer.Append( polybuffer[jj] );
|
|
|
|
// Close the shape:
|
|
aShapeBuffer.Append( polybuffer[0] );
|
|
}
|
|
}
|
|
|
|
|
|
void AM_PRIMITIVE::ConvertShapeToPolygon( APERTURE_MACRO* aApertMacro,
|
|
std::vector<VECTOR2I>& aBuffer )
|
|
{
|
|
switch( m_Primitive_id )
|
|
{
|
|
case AMP_CIRCLE:
|
|
{
|
|
/* Generated by an aperture macro declaration like:
|
|
* "1,1,0.3,0.5, 1.0*"
|
|
* type (1), exposure, diameter, pos.x, pos.y, <rotation>
|
|
* <rotation> is a optional parameter: rotation from origin.
|
|
* type is not stored in parameters list, so the first parameter is exposure
|
|
*/
|
|
int radius = scaletoIU( m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
|
|
|
|
// A circle primitive can have a 0 size (for instance when used in roundrect macro),
|
|
// so skip it
|
|
if( radius <= 0 )
|
|
break;
|
|
|
|
VECTOR2I center = mapPt( m_Params[2].GetValueFromMacro( aApertMacro ), m_Params[3].GetValueFromMacro( aApertMacro ),
|
|
m_GerbMetric );
|
|
VECTOR2I corner;
|
|
|
|
const int seg_per_circle = 64; // Number of segments to approximate a circle
|
|
EDA_ANGLE delta = ANGLE_360 / seg_per_circle;
|
|
|
|
for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
|
|
{
|
|
corner.x = radius;
|
|
corner.y = 0;
|
|
RotatePoint( corner, angle );
|
|
corner += center;
|
|
aBuffer.push_back( corner );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case AMP_LINE2:
|
|
case AMP_LINE20: // Line with rectangle ends. (Width, start and end pos + rotation)
|
|
{
|
|
int width = scaletoIU( m_Params[1].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
VECTOR2I start =
|
|
mapPt( m_Params[2].GetValueFromMacro( aApertMacro ), m_Params[3].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
VECTOR2I end =
|
|
mapPt( m_Params[4].GetValueFromMacro( aApertMacro ), m_Params[5].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
VECTOR2I delta = end - start;
|
|
int len = delta.EuclideanNorm();
|
|
|
|
// To build the polygon, we must create a horizontal polygon starting to "start"
|
|
// and rotate it to have the end point to "end"
|
|
VECTOR2I 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; // lower left
|
|
aBuffer.push_back( currpt );
|
|
|
|
// Rotate rectangle and move it to the actual start point
|
|
EDA_ANGLE angle( delta );
|
|
|
|
for( unsigned ii = 0; ii < 4; ii++ )
|
|
{
|
|
RotatePoint( aBuffer[ii], -angle );
|
|
aBuffer[ii] += start;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case AMP_LINE_CENTER:
|
|
{
|
|
VECTOR2I size =
|
|
mapPt( m_Params[1].GetValueFromMacro( aApertMacro ), m_Params[2].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
VECTOR2I pos =
|
|
mapPt( m_Params[3].GetValueFromMacro( aApertMacro ), m_Params[4].GetValueFromMacro( aApertMacro ), 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:
|
|
{
|
|
VECTOR2I size =
|
|
mapPt( m_Params[1].GetValueFromMacro( aApertMacro ), m_Params[2].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
VECTOR2I lowerLeft =
|
|
mapPt( m_Params[3].GetValueFromMacro( aApertMacro ), m_Params[4].GetValueFromMacro( aApertMacro ), 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.
|
|
// m_Params = center.x (unused here), center.y (unused here), outside diam, inside diam,
|
|
// crosshair thickness.
|
|
int outerRadius = scaletoIU( m_Params[2].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
|
|
int innerRadius = scaletoIU( m_Params[3].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
|
|
|
|
// Safety checks to guarantee no divide-by-zero
|
|
outerRadius = std::max( 1, outerRadius );
|
|
innerRadius = std::max( 1, innerRadius );
|
|
|
|
int halfthickness = scaletoIU( m_Params[4].GetValueFromMacro( aApertMacro ), m_GerbMetric ) / 2;
|
|
EDA_ANGLE angle_start( asin( (double) halfthickness / innerRadius ), RADIANS_T );
|
|
|
|
// Draw shape in the first quadrant (X and Y > 0)
|
|
VECTOR2I pos, startpos;
|
|
|
|
// Inner arc
|
|
startpos.x = innerRadius;
|
|
EDA_ANGLE angle_end = ANGLE_90 - angle_start;
|
|
|
|
for( EDA_ANGLE angle = angle_start; angle < angle_end; angle += EDA_ANGLE( 10, DEGREES_T ) )
|
|
{
|
|
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 = EDA_ANGLE( asin( (double) halfthickness / outerRadius ), RADIANS_T );
|
|
angle_end = ANGLE_90 - angle_start;
|
|
|
|
// First point, near Y axis, outer arc
|
|
for( EDA_ANGLE angle = angle_end; angle > angle_start; angle -= EDA_ANGLE( 10, DEGREES_T ) )
|
|
{
|
|
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 cross is built as
|
|
// polygon because circles can be drawn easily
|
|
int crossHairThickness = scaletoIU( m_Params[6].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
int crossHairLength = scaletoIU( m_Params[7].GetValueFromMacro( aApertMacro ), m_GerbMetric );
|
|
|
|
// Create cross. First create 1/4 of the shape.
|
|
// Others point are the same, rotated by 90, 180 and 270 deg
|
|
VECTOR2I 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, ANGLE_90 * jj );
|
|
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( m_Params[1].GetValueFromMacro( aApertMacro ) );
|
|
int radius = scaletoIU( m_Params[4].GetValueFromMacro( aApertMacro ), 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++ )
|
|
{
|
|
VECTOR2I pos( radius, 0 );
|
|
RotatePoint( pos, ANGLE_360 * ii / vertexcount );
|
|
aBuffer.push_back( pos );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case AMP_COMMENT:
|
|
case AMP_UNKNOWN:
|
|
break;
|
|
}
|
|
}
|