DSN export: export RECT and ARC shapes (on silkscreen layer) from footprints

Also export arc track segments as lines. (Freerouter does not support QARCs)
Fixes #8769
https://gitlab.com/kicad/code/kicad/issues/8769
This commit is contained in:
jean-pierre charras 2021-07-11 18:15:40 +02:00
parent fb246403d2
commit 54b6f51633
3 changed files with 129 additions and 33 deletions

View File

@ -48,6 +48,22 @@ enum RECT_CHAMFER_POSITIONS : int
};
/**
* Generate a polyline to approximate a arc
*
* @param aPolyline is a buffer to store the polyline.
* @param aCenter is the center of the arc.
* @param aRadius is the radius of the arc.
* @param aStartAngleDeg is the starting point (in degrees) of the arc.
* @param aArcAngleDeg is the angle (in degrees) of the arc.
* @param aError is the internal units allowed for error approximation.
* @param aErrorLoc determines if the approximation error be placed outside or inside the polygon.
*/
int ConvertArcToPolyline( SHAPE_LINE_CHAIN& aPolyline, VECTOR2I aCenter, int aRadius,
double aStartAngleDeg, double aArcAngleDeg, double aAccuracy,
ERROR_LOC aErrorLoc );
/**
* Convert a circle to a polygon, using multiple straight lines.
*

View File

@ -393,27 +393,27 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const
}
static int convertArcToPolyline( SHAPE_LINE_CHAIN& aPolyline, VECTOR2I aCenter, int aRadius,
double aStartAngle, double aArcAngle, double aAccuracy,
int ConvertArcToPolyline( SHAPE_LINE_CHAIN& aPolyline, VECTOR2I aCenter, int aRadius,
double aStartAngleDeg, double aArcAngleDeg, double aAccuracy,
ERROR_LOC aErrorLoc )
{
double endAngle = aStartAngle + aArcAngle;
double endAngle = aStartAngleDeg + aArcAngleDeg;
int n = 2;
if( aRadius >= aAccuracy )
n = GetArcToSegmentCount( aRadius, aAccuracy, aArcAngle )+1; // n >= 3
n = GetArcToSegmentCount( aRadius, aAccuracy, aArcAngleDeg )+1; // n >= 3
if( aErrorLoc == ERROR_OUTSIDE )
{
int seg360 = std::abs( KiROUND( n * 360.0 / aArcAngle ) );
int seg360 = std::abs( KiROUND( n * 360.0 / aArcAngleDeg ) );
int actual_delta_radius = CircleToEndSegmentDeltaRadius( aRadius, seg360 );
aRadius += actual_delta_radius;
}
for( int i = 0; i <= n ; i++ )
{
double rot = aStartAngle;
rot += ( aArcAngle * i ) / n;
double rot = aStartAngleDeg;
rot += ( aArcAngleDeg * i ) / n;
double x = aCenter.x + aRadius * cos( rot * M_PI / 180.0 );
double y = aCenter.y + aRadius * sin( rot * M_PI / 180.0 );
@ -480,11 +480,11 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoi
polyshape.NewOutline();
convertArcToPolyline( polyshape.Outline(2), center, arc_outer_radius,
ConvertArcToPolyline( polyshape.Outline(2), center, arc_outer_radius,
arc_angle_start_deg, arc_angle, aError, errorLocOuter );
if( arc_inner_radius > 0 )
convertArcToPolyline( polyshape.Outline(2), center, arc_inner_radius,
ConvertArcToPolyline( polyshape.Outline(2), center, arc_inner_radius,
arc_angle_end_deg, -arc_angle, aError, errorLocInner );
else
polyshape.Append( center );

View File

@ -742,46 +742,126 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint )
{
// this is best done by 4 QARC's but freerouter does not yet support QARCs.
// for now, support by using line segments.
outline = new SHAPE( image, T_outline );
image->Append( outline );
path = new PATH( outline );
outline->SetShape( path );
path->SetAperture( scale( graphic->GetWidth() ) );
path->SetLayerId( "signal" );
// Do the math using KiCad units, that way we stay out of the
// scientific notation range of floating point numbers in the
// DSN file. We do not parse scientific notation in our own
// lexer/beautifier, and the spec is not clear that this is
// required. Fixed point floats are all that should be needed.
double radius = graphic->GetRadius();
wxPoint circle_centre = graphic->m_Start0;
double radius = GetLineLength( graphic->GetStart(), graphic->GetEnd() );
SHAPE_LINE_CHAIN polyline;
ConvertArcToPolyline( polyline, VECTOR2I( circle_centre ), radius,
0.0, 360.0, ARC_HIGH_DEF, ERROR_INSIDE );
// seg count to approximate circle by line segments
int seg_per_circle = GetArcToSegmentCount( radius, ARC_LOW_DEF, 360.0 );
for( int ii = 0; ii < seg_per_circle; ++ii )
for( int ii = 0; ii < polyline.PointCount(); ++ii )
{
double radians = 2*M_PI / seg_per_circle * ii;
wxPoint point( KiROUND( radius * cos( radians ) ),
KiROUND( radius * sin( radians ) ) );
point += graphic->m_Start0; // an offset
path->AppendPoint( mapPt( point ) );
wxPoint corner( polyline.CPoint( ii ).x, polyline.CPoint( ii ).y );
path->AppendPoint( mapPt( corner ) );
}
// The shape must be closed
wxPoint point( radius , 0 );
point += graphic->m_Start0;
path->AppendPoint( mapPt( point ) );
}
break;
case PCB_SHAPE_TYPE::RECT:
{
outline = new SHAPE( image, T_outline );
image->Append( outline );
path = new PATH( outline );
outline->SetShape( path );
path->SetAperture( scale( graphic->GetWidth() ) );
path->SetLayerId( "signal" );
wxPoint corner = graphic->GetStart0();
path->AppendPoint( mapPt( corner ) );
corner.x = graphic->GetEnd0().x;
path->AppendPoint( mapPt( corner ) );
corner.y = graphic->GetEnd0().y;
path->AppendPoint( mapPt( corner ) );
corner.x = graphic->GetStart0().x;
path->AppendPoint( mapPt( corner ) );
}
break;
case PCB_SHAPE_TYPE::ARC:
{
// this is best done by QARC's but freerouter does not yet support QARCs.
// for now, support by using line segments.
// So we use a polygon (PATH) to create a approximative arc shape
outline = new SHAPE( image, T_outline );
image->Append( outline );
path = new PATH( outline );
outline->SetShape( path );
path->SetAperture( 0 );//scale( graphic->GetWidth() ) );
path->SetLayerId( "signal" );
wxPoint arc_centre = graphic->m_Start0;
double radius = graphic->GetRadius()+ graphic->GetWidth()/2;
double arcStartDeg = graphic->GetArcAngleStart() / 10.0;
double arcAngleDeg = graphic->GetAngle() / 10.0;
// For some obscure reason, FreeRouter does not show the same polygonal
// shape for polygons CW and CCW. So used only the order of corners
// giving the best look.
if( arcAngleDeg < 0 )
{
arcStartDeg = graphic->GetArcAngleEnd() / 10.0;
arcAngleDeg = - arcAngleDeg;
}
SHAPE_LINE_CHAIN polyline;
ConvertArcToPolyline( polyline, VECTOR2I( arc_centre ), radius,
arcStartDeg, arcAngleDeg, ARC_HIGH_DEF, ERROR_INSIDE );
SHAPE_POLY_SET polyBuffer;
polyBuffer.AddOutline( polyline );
radius -= graphic->GetWidth();
if( radius > 0 )
{
polyline.Clear();
ConvertArcToPolyline( polyline, VECTOR2I( arc_centre ), radius,
arcStartDeg, arcAngleDeg, ARC_HIGH_DEF, ERROR_INSIDE );
// Add points in reverse order, to create a closed polygon
for( int ii = polyline.PointCount()-1; ii >= 0; --ii )
polyBuffer.Append( polyline.CPoint( ii ) );
}
// ensure the polygon is closed
polyBuffer.Append( polyBuffer.Outline(0).CPoint( 0 ) );
wxPoint move = graphic->GetCenter() - arc_centre;
TransformCircleToPolygon( polyBuffer, graphic->GetArcStart() - move,
graphic->GetWidth()/2,
ARC_HIGH_DEF, ERROR_INSIDE );
TransformCircleToPolygon( polyBuffer, graphic->GetArcEnd() - move,
graphic->GetWidth()/2,
ARC_HIGH_DEF, ERROR_INSIDE );
polyBuffer.Simplify( SHAPE_POLY_SET::PM_FAST );
SHAPE_LINE_CHAIN& poly = polyBuffer.Outline( 0 );
for( int ii = 0; ii < poly.PointCount(); ++ii )
{
wxPoint corner( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
path->AppendPoint( mapPt( corner ) );
}
}
break;
default:
continue;
}
@ -1571,7 +1651,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard )
{
// export all of them for now, later we'll decide what controls we need
// on this.
static const KICAD_T scanTRACKs[] = { PCB_TRACE_T, EOT };
static const KICAD_T scanTRACKs[] = { PCB_TRACE_T, PCB_ARC_T, EOT };
items.Collect( aBoard, scanTRACKs );