From 54b6f51633b8e313dc231051f6ced89fb0e0a7dd Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 11 Jul 2021 18:15:40 +0200 Subject: [PATCH] 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 --- .../include/convert_basic_shapes_to_polygon.h | 16 +++ .../src/convert_basic_shapes_to_polygon.cpp | 18 +-- .../specctra_export.cpp | 128 ++++++++++++++---- 3 files changed, 129 insertions(+), 33 deletions(-) diff --git a/libs/kimath/include/convert_basic_shapes_to_polygon.h b/libs/kimath/include/convert_basic_shapes_to_polygon.h index 1dca385dbc..850c565537 100644 --- a/libs/kimath/include/convert_basic_shapes_to_polygon.h +++ b/libs/kimath/include/convert_basic_shapes_to_polygon.h @@ -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. * diff --git a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp index f80ee661eb..cb68741a70 100644 --- a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp +++ b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp @@ -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 ); diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp index 0b290449f7..29183aff4f 100644 --- a/pcbnew/specctra_import_export/specctra_export.cpp +++ b/pcbnew/specctra_import_export/specctra_export.cpp @@ -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 );