diff --git a/common/plotters/GERBER_plotter.cpp b/common/plotters/GERBER_plotter.cpp index e1b0b226bc..7654ad1b96 100644 --- a/common/plotters/GERBER_plotter.cpp +++ b/common/plotters/GERBER_plotter.cpp @@ -475,16 +475,29 @@ void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAn { SetCurrentLineWidth( aWidth ); + // aFill is not used here. + plotArc( aCenter, aStAngle, aEndAngle, aRadius, false ); +} + + +void GERBER_PLOTTER::plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle, + int aRadius, bool aPlotInRegion ) +{ wxPoint start, end; start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) ); start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) ); - MoveTo( start ); + + if( !aPlotInRegion ) + MoveTo( start ); + else + LineTo( start ); + end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) ); end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) ); DPOINT devEnd = userToDeviceCoordinates( end ); DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start ); - fprintf( outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode + fprintf( outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode if( aStAngle < aEndAngle ) fprintf( outputFile, "G03*\n" ); // Active circular interpolation, CCW @@ -841,36 +854,172 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS EDA_DRAW_MODE_T aTraceMode, void* aData ) { - GBR_METADATA *gbr_metadata = static_cast( aData ); - - // Currently, a Pad RoundRect is plotted as polygon. - // TODO: use Aperture macro and flash it - SHAPE_POLY_SET outline; - const int segmentToCircleCount = 64; - TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient, - aCornerRadius, segmentToCircleCount ); + GBR_METADATA* gbr_metadata = static_cast( aData ); if( aTraceMode != FILLED ) { + SHAPE_POLY_SET outline; + const int segmentToCircleCount = 64; + TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient, + aCornerRadius, segmentToCircleCount ); + SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata ); outline.Inflate( -GetCurrentLineWidth()/2, 16 ); + + std::vector< wxPoint > cornerList; + // TransformRoundRectToPolygon creates only one convex polygon + SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); + cornerList.reserve( poly.PointCount() + 1 ); + + for( int ii = 0; ii < poly.PointCount(); ++ii ) + cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y ); + + // Close polygon + cornerList.push_back( cornerList[0] ); + + // plot outlines + PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), gbr_metadata ); + } + else + { + // A Pad RoundRect is plotted as a Gerber region. + // Initialize region metadata: + bool clearTA_AperFunction = false; // true if a TA.AperFunction is used + + if( gbr_metadata ) + { + formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); + std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format ); + + if( !attrib.empty() ) + { + fputs( attrib.c_str(), outputFile ); + clearTA_AperFunction = true; + } + } + + // Plot the region using arcs in corners. + plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient ); + + // Clear the TA attribute, to avoid the next item to inherit it: + if( clearTA_AperFunction ) + { + if( m_useX2format ) + { + fputs( "%TD.AperFunction*%\n", outputFile ); + } + else + { + fputs( "G04 #@! TD.AperFunction*\n", outputFile ); + } + } + } +} + + +void GERBER_PLOTTER::plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize, + int aCornerRadius, double aOrient ) +{ + // The region outline is generated by 4 sides and 4 90 deg arcs + // 1 --- 2 + // | c | + // 4 --- 3 + + // Note also in user coordinates the Y axis is from top to bottom + // for historical reasons. + + // A helper structure to handle outlines coordinates (segments and arcs) + // in user coordinates + struct RR_EDGE + { + wxPoint m_start; + wxPoint m_end; + wxPoint m_center; + // in decidegrees: angle start. angle end = m_arc_angle_start+arc_angle + double m_arc_angle_start; + }; + + const double arc_angle = -900.0; // in decidegrees + int hsizeX = aSize.x/2; + int hsizeY = aSize.y/2; + + RR_EDGE curr_edge; + std::vector rr_outline; + + // Build outline coordinates, relative to rectangle center, rotation 0: + + // Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX) + curr_edge.m_start.x = -hsizeX; + curr_edge.m_start.y = hsizeY - aCornerRadius; + curr_edge.m_end.x = curr_edge.m_start.x; + curr_edge.m_end.y = -hsizeY + aCornerRadius; + curr_edge.m_center.x = -hsizeX + aCornerRadius; + curr_edge.m_center.y = curr_edge.m_end.y; + curr_edge.m_arc_angle_start = aOrient + 1800.0; // En decidegree + + rr_outline.push_back( curr_edge ); + + // Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY) + curr_edge.m_start.x = -hsizeX + aCornerRadius; + curr_edge.m_start.y = -hsizeY; + curr_edge.m_end.x = hsizeX - aCornerRadius; + curr_edge.m_end.y = curr_edge.m_start.y; + curr_edge.m_center.x = curr_edge.m_end.x; + curr_edge.m_center.y = -hsizeY + aCornerRadius; + curr_edge.m_arc_angle_start = aOrient + 900.0; // En decidegree + + rr_outline.push_back( curr_edge ); + + // bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX) + curr_edge.m_start.x = hsizeX; + curr_edge.m_start.y = -hsizeY + aCornerRadius; + curr_edge.m_end.x = curr_edge.m_start.x; + curr_edge.m_end.y = hsizeY - aCornerRadius; + curr_edge.m_center.x = hsizeX - aCornerRadius; + curr_edge.m_center.y = curr_edge.m_end.y; + curr_edge.m_arc_angle_start = aOrient + 0.0; // En decidegree + + rr_outline.push_back( curr_edge ); + + // bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY) + curr_edge.m_start.x = hsizeX - aCornerRadius; + curr_edge.m_start.y = hsizeY; + curr_edge.m_end.x = -hsizeX + aCornerRadius; + curr_edge.m_end.y = curr_edge.m_start.y; + curr_edge.m_center.x = curr_edge.m_end.x; + curr_edge.m_center.y = hsizeY - aCornerRadius; + curr_edge.m_arc_angle_start = aOrient - 900.0; // En decidegree + + rr_outline.push_back( curr_edge ); + + // Move relative coordinates to the actual location and rotation: + for( RR_EDGE& rr_edge: rr_outline ) + { + RotatePoint( &rr_edge.m_start, aOrient ); + RotatePoint( &rr_edge.m_end, aOrient ); + RotatePoint( &rr_edge.m_center, aOrient ); + rr_edge.m_start += aRectCenter; + rr_edge.m_end += aRectCenter; + rr_edge.m_center += aRectCenter; } - std::vector< wxPoint > cornerList; - // TransformRoundRectToPolygon creates only one convex polygon - SHAPE_LINE_CHAIN& poly = outline.Outline( 0 ); - cornerList.reserve( poly.PointCount() + 1 ); + fputs( "G36*\n", outputFile ); // Start region + fputs( "G01*\n", outputFile ); // Set linear interpolation. + MoveTo( rr_outline[0].m_start ); // Start point of region - for( int ii = 0; ii < poly.PointCount(); ++ii ) - cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) ); + for( RR_EDGE& rr_edge: rr_outline ) + { + if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0 + { + // LineTo( rr_edge.m_end ); // made in plotArc() + plotArc( rr_edge.m_center, rr_edge.m_arc_angle_start, rr_edge.m_arc_angle_start+arc_angle, + aCornerRadius, true ); + } + else + LineTo( rr_edge.m_end ); + } - // Close polygon - cornerList.push_back( cornerList[0] ); - - if( aTraceMode == SKETCH ) - PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), gbr_metadata ); - else - PlotGerberRegion( cornerList, gbr_metadata ); + fputs( "G37*\n", outputFile ); // Close region } diff --git a/include/plotter.h b/include/plotter.h index 5340e44239..63cc4e1bb9 100644 --- a/include/plotter.h +++ b/include/plotter.h @@ -1181,6 +1181,30 @@ public: protected: + /** Plot a round rect (a round rect shape in fact) as a Gerber region + * using lines and arcs for corners + * @param aRectCenter is the center of the rectangle + * @param aSize is the size of the rectangle + * @param aCornerRadius is the radius of the corners + * @param aOrient is the rotation of the rectangle + * Note: only the G36 ... G37 region is created. + */ + void plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize, + int aCornerRadius, double aOrient ); + /** + * Plot a Gerber arc. + * if aPlotInRegion = true, the current pen position will not be + * initialized to the arc start position, and therefore the arc can be used + * to define a region outline item + * a line will be created from current ^position to arc start point + * if aPlotInRegion = false, the current pen position will be initialized + * to the arc start position, to plot an usual arc item + * The line thickness is not initialized in plotArc, and must be initialized + * before calling it if needed. + */ + void plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle, + int aRadius, bool aPlotInRegion ); + /** * Pick an existing aperture or create a new one, matching the * size, type and attributes. diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp index c9bd6c1096..64b3532a72 100644 --- a/pcbnew/plot_brditems_plotter.cpp +++ b/pcbnew/plot_brditems_plotter.cpp @@ -762,6 +762,12 @@ void BRDITEMS_PLOTTER::PlotDrawSegment( DRAWSEGMENT* aSeg ) gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_EDGECUT ); } + if( IsCopperLayer( aSeg->GetLayer() ) ) + // Graphic items (DRAWSEGMENT, TEXT) having no net have the NonConductor attribute + // Graphic items having a net have the Conductor attribute, but are not (yet?) + // supported in Pcbnew + gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR ); + switch( aSeg->GetShape() ) { case S_CIRCLE: