From 47f9c505c578d63cab0882a1ac7616247e2e8627 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 6 Oct 2019 13:39:50 +0200 Subject: [PATCH] Gerber plot: better handling of polygonal pad shapes plotting update GBR_CMP_PNP_METADATA. --- common/gbr_metadata.cpp | 6 + common/plotters/GERBER_plotter.cpp | 162 +++++++++---------- include/gbr_netlist_metadata.h | 12 +- include/plotter.h | 17 +- pcbnew/exporters/gerber_placefile_writer.cpp | 50 +++--- pcbnew/exporters/gerber_placefile_writer.h | 8 +- 6 files changed, 139 insertions(+), 116 deletions(-) diff --git a/common/gbr_metadata.cpp b/common/gbr_metadata.cpp index 94cc3619da..f26344d047 100644 --- a/common/gbr_metadata.cpp +++ b/common/gbr_metadata.cpp @@ -590,6 +590,12 @@ wxString GBR_CMP_PNP_METADATA::FormatCmpPnPMetadata() if( !m_Value.IsEmpty() ) text << start_of_line << "CVal," << m_Value << end_of_line; + if( !m_LibraryName.IsEmpty() ) + text << start_of_line << "CLbN," << m_LibraryName << end_of_line; + + if( !m_LibraryDescr.IsEmpty() ) + text << start_of_line << "CLbD," << m_LibraryDescr << end_of_line; + text << start_of_line << "CMnt," << mounType[m_MountType] << end_of_line; text << start_of_line << "CRot," << m_Orientation << end_of_line; diff --git a/common/plotters/GERBER_plotter.cpp b/common/plotters/GERBER_plotter.cpp index 405222e5ad..eb87338fb8 100644 --- a/common/plotters/GERBER_plotter.cpp +++ b/common/plotters/GERBER_plotter.cpp @@ -105,6 +105,17 @@ void GERBER_PLOTTER::emitDcode( const DPOINT& pt, int dcode ) KiROUND( pt.x ), KiROUND( pt.y ), dcode ); } +void GERBER_PLOTTER::ClearAllAttributes() +{ + // Remove all attributes from object attributes dictionary (TO. and TA commands) + if( m_useX2format ) + fputs( "%TD*%\n", outputFile ); + else + fputs( "G04 #@! TD*\n", outputFile ); + + m_objectAttributesDictionnary.clear(); +} + void GERBER_PLOTTER::clearNetAttribute() { @@ -535,6 +546,43 @@ void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAn } +void GERBER_PLOTTER::PlotGerberRegion( const std::vector< wxPoint >& aCornerList, + void * aData ) +{ + if( aCornerList.size() <= 2 ) + return; + + GBR_METADATA* gbr_metadata = static_cast( aData ); + + bool clearTA_AperFunction = false; // true if a TA.AperFunction is used + + if( gbr_metadata ) + { + std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format ); + + if( !attrib.empty() ) + { + fputs( attrib.c_str(), outputFile ); + clearTA_AperFunction = true; + } + } + + PlotPoly( aCornerList, FILLED_SHAPE, 0 , gbr_metadata ); + + // 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::PlotPoly( const std::vector< wxPoint >& aCornerList, FILL_T aFill, int aWidth, void * aData ) { @@ -546,7 +594,8 @@ void GERBER_PLOTTER::PlotPoly( const std::vector< wxPoint >& aCornerList, // one should plot outline as thick segments GBR_METADATA* gbr_metadata = static_cast( aData ); - SetCurrentLineWidth( aWidth, gbr_metadata ); + if( !aFill ) + SetCurrentLineWidth( aWidth, gbr_metadata ); if( gbr_metadata ) formatNetAttribute( &gbr_metadata->m_NetlistMetadata ); @@ -716,7 +765,7 @@ void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, doub wxSize size( aSize ); GBR_METADATA* gbr_metadata = static_cast( aData ); - /* Plot a flashed shape. */ + // Flash a vertical or horizontal shape (this is a basic aperture). if( ( orient == 0 || orient == 900 || orient == 1800 || orient == 2700 ) && trace_mode == FILLED ) { @@ -732,7 +781,7 @@ void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, doub emitDcode( pos_dev, 3 ); } - else /* Plot pad as a segment. */ + else // Plot pad as a segment. { if( size.x > size.y ) { @@ -747,10 +796,8 @@ void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, doub if( trace_mode == FILLED ) { // TODO: use an aperture macro to declare the rotated pad - - // Flash a pad anchor, if a netlist attribute is set - if( aData ) - FlashPadCircle( pos, size.x, trace_mode, aData ); + // to be able to flash the shape + // For now, the pad is painted. // The pad is reduced to an segment with dy > dx int delta = size.y - size.x; @@ -766,14 +813,12 @@ void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, doub { metadata = *gbr_metadata; - // If the pad is drawn on a copper layer, - // set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR - if( metadata.IsCopper() ) - metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); - +#if 0 // See if one can use TO.P attribute in this case (only one segment to draw a pad) // Clear .P attribute, only allowed for flashed items + // (not allowed for painted pads) wxString attrname( ".P" ); metadata.m_NetlistMetadata.ClearAttribute( &attrname ); +#endif } ThickSegment( wxPoint( pos.x + x0, pos.y + y0 ), @@ -861,22 +906,10 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS EDA_DRAW_MODE_T aTraceMode, void* aData ) { - GBR_METADATA gbr_metadata; + GBR_METADATA* gbr_metadata; if( aData ) - { - gbr_metadata = *static_cast( aData ); - // If the pad is drawn on a copper layer, - // set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR - if( gbr_metadata.IsCopper() ) - gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); - - wxString attrname( ".P" ); - gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers - } - - if( aTraceMode != FILLED ) - SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata ); + gbr_metadata = static_cast( aData ); // Currently, a Pad RoundRect is plotted as polygon. // TODO: use Aperture macro and flash it @@ -898,16 +931,10 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS // Close polygon cornerList.push_back( cornerList[0] ); - PlotPoly( cornerList, aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL, - aTraceMode == FILLED ? 0 : GetCurrentLineWidth(), &gbr_metadata ); - - // Now, flash a pad anchor, if a netlist attribute is set - // (remove me when a Aperture macro will be used) - if( aData && aTraceMode == FILLED ) - { - int diameter = std::min( aSize.x, aSize.y ); - FlashPadCircle( aPadPos, diameter, aTraceMode , aData ); - } + if( aTraceMode == SKETCH ) + PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), gbr_metadata ); + else + PlotGerberRegion( cornerList, gbr_metadata ); } void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize, @@ -915,28 +942,12 @@ void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize EDA_DRAW_MODE_T aTraceMode, void* aData ) { - // A Pad custom is plotted as polygon. - - // A flashed circle @aPadPos is added (anchor pad) - // However, because the anchor pad can be circle or rect, we use only - // a circle not bigger than the rect. - // the main purpose is to print a flashed DCode as pad anchor - if( aTraceMode == FILLED ) - FlashPadCircle( aPadPos, std::min( aSize.x, aSize.y ), aTraceMode, aData ); + // A Pad custom is plotted as polygon (a region in Gerber language). GBR_METADATA gbr_metadata; if( aData ) - { gbr_metadata = *static_cast( aData ); - // If the pad is drawn on a copper layer, - // set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR - if( gbr_metadata.IsCopper() ) - gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); - - wxString attrname( ".P" ); - gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers - } SHAPE_POLY_SET polyshape = *aPolygons; @@ -960,9 +971,10 @@ void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize // Close polygon cornerList.push_back( cornerList[0] ); - PlotPoly( cornerList, - aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL, - aTraceMode == FILLED ? 0 : GetCurrentLineWidth(), &gbr_metadata ); + if( aTraceMode == SKETCH ) + PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), &gbr_metadata ); + else + PlotGerberRegion( cornerList, &gbr_metadata ); } } @@ -971,7 +983,6 @@ void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCo double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode, void* aData ) { - // Currently, a Pad Trapezoid is plotted as polygon. // TODO: use Aperture macro and flash it // polygon corners list @@ -980,25 +991,6 @@ void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCo for( int ii = 0; ii < 4; ii++ ) cornerList.push_back( aCorners[ii] ); - // Now, flash a pad anchor, if a netlist attribute is set - // (remove me when a Aperture macro will be used) - if( aData && ( aTrace_Mode == FILLED ) ) - { - // Calculate the radius of the circle inside the shape - // It is the smaller dist from shape pos to edges - int radius = INT_MAX; - - for( unsigned ii = 0, jj = cornerList.size()-1; ii < cornerList.size(); - jj = ii, ii++ ) - { - SEG segment( aCorners[ii], aCorners[jj] ); - int dist = segment.LineDistance( VECTOR2I( 0, 0) ); - radius = std::min( radius, dist ); - } - - FlashPadCircle( aPadPos, radius*2, aTrace_Mode, aData ); - } - // Draw the polygon and fill the interior as required for( unsigned ii = 0; ii < 4; ii++ ) { @@ -1013,21 +1005,13 @@ void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCo GBR_METADATA metadata; if( gbr_metadata ) - { metadata = *gbr_metadata; - // If the pad is drawn on a copper layer, - // set attribute to GBR_APERTURE_ATTRIB_CONDUCTOR - if( metadata.IsCopper() ) - metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); - wxString attrname( ".P" ); - metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers - } - - SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &metadata ); - PlotPoly( cornerList, aTrace_Mode == FILLED ? FILLED_SHAPE : NO_FILL, - aTrace_Mode == FILLED ? 0 : GetCurrentLineWidth(), - &metadata ); + if( aTrace_Mode == SKETCH ) + PlotPoly( cornerList, NO_FILL, USE_DEFAULT_LINE_WIDTH, + &metadata ); + else + PlotGerberRegion( cornerList, &metadata ); } diff --git a/include/gbr_netlist_metadata.h b/include/gbr_netlist_metadata.h index ec29e80e93..31eb7eb6ec 100644 --- a/include/gbr_netlist_metadata.h +++ b/include/gbr_netlist_metadata.h @@ -40,11 +40,17 @@ * %TO.CMfr, Manufacturer * %TO.CMPN, Manufacturer part number * %TO.Cpkg, Package, as per IPC-7351 + * %TO.CVal, Value, a string. E.g. 220nF + * %TO.CMnt, Mount type: (SMD|BGA|TH|Other) * %TO.CFtp, Footprint name, a string. E.g. LQFP-100_14x14mm_P0.5mm This is the footprint name coming from the CAD tool libraries. - * %TO.CVal, Value, a string. E.g. 220nF - * %TO.CMnt, Mount type: (SMD|TH|Other) + * %TO.CPgN, Package name, like the JEDEC JEP95 standard. + * %TO.CPgD, Package description. * %TO.CHgt, Height, a decimal, in the unit of the file. + * %TO.CLbN, Library name. + * %TO.CLbD, Library description. + * %TO.Sup,, SN is a field with the supppier name. + * SPN is a field with the supppier part name */ class GBR_CMP_PNP_METADATA { @@ -62,6 +68,8 @@ public: wxString m_MPN; // Manufacturer part number wxString m_Package; // Package, as per IPC-7351 wxString m_Footprint; // Footprint name, from library + wxString m_LibraryName; // Library name, containing the footprint + wxString m_LibraryDescr; // Library description wxString m_Value; // Component value MOUNT_TYPE m_MountType; // SMD|TH|Other diff --git a/include/plotter.h b/include/plotter.h index 0f7b78a6db..11cbd0001d 100644 --- a/include/plotter.h +++ b/include/plotter.h @@ -1174,7 +1174,7 @@ public: */ virtual void PlotPoly( const std::vector< wxPoint >& aCornerList, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH, - void * aData = NULL ) override; + void* aData = nullptr ) override; virtual void PenTo( const wxPoint& pos, char plume ) override; @@ -1232,6 +1232,14 @@ public: virtual void FlashRegularPolygon( const wxPoint& aShapePos, int aDiameter, int aCornerCount, double aOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) override; + /** + * Plot a Gerber region: similar to PlotPoly but plot only filled polygon, + * and add the TA.AperFunction if aData contains this attribute, and clear it + * after plotting + */ + void PlotGerberRegion( const std::vector< wxPoint >& aCornerList, + void * aData = NULL ); + /** * Change the plot polarity and begin a new layer * Used to 'scratch off' silk screen away from solder mask @@ -1268,6 +1276,11 @@ public: */ virtual void EndBlock( void* aData ) override; + /** Remove (clear) all attributes from object attributes dictionary (TO. and TA commands) + * similar to clearNetAttribute(), this is an unconditional reset of TO. and TA. attributes + */ + void ClearAllAttributes(); + protected: /** * Pick an existing aperture or create a new one, matching the @@ -1304,9 +1317,11 @@ protected: /** * clear a Gerber net attribute record (clear object attribute dictionary) * and output the clear object attribute dictionary command to gerber file + * has effect only if a net attribute is stored in m_objectAttributesDictionnary */ void clearNetAttribute(); + // Remove all attributes from object attributes dictionary (TO. and TA commands) /** * Function getAperture returns a reference to the aperture which meets the size anf type of tool * if the aperture does not exist, it is created and entered in aperture list diff --git a/pcbnew/exporters/gerber_placefile_writer.cpp b/pcbnew/exporters/gerber_placefile_writer.cpp index 3379547b24..0d7c73894f 100644 --- a/pcbnew/exporters/gerber_placefile_writer.cpp +++ b/pcbnew/exporters/gerber_placefile_writer.cpp @@ -49,10 +49,10 @@ PLACEFILE_GERBER_WRITER::PLACEFILE_GERBER_WRITER( BOARD* aPcb ) { m_pcb = aPcb; /* Set conversion scale depending on drill file units */ - m_conversionUnits = 1.0 / IU_PER_MM; // Gerber units = mm + m_conversionUnits = 1.0 / IU_PER_MM; // Gerber units = mm m_forceSmdItems = false; - m_plotPad1Marker = true; // Place a marker to pin 1 (or A1) position - m_plotOtherPadsMarker = false; // Place a marker to other pins position + m_plotPad1Marker = true; // Place a marker to pin 1 (or A1) position + m_plotOtherPadsMarker = true; // Place a marker to other pins position } @@ -146,8 +146,12 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, pnpAttrib.m_Value = FormatStringFromGerber( footprint->GetValue() ); // Add component footprint info: - wxString fp_name = FROM_UTF8( footprint->GetFPID().GetLibItemName().c_str() ); - pnpAttrib.m_Footprint = FormatStringFromGerber( fp_name ); + wxString fp_info = FROM_UTF8( footprint->GetFPID().GetLibItemName().c_str() ); + pnpAttrib.m_Footprint = FormatStringFromGerber( fp_info ); + + // Add footprint lib name: + fp_info = FROM_UTF8( footprint->GetFPID().GetLibNickname().c_str() ); + pnpAttrib.m_LibraryName = FormatStringFromGerber( fp_info ); gbr_metadata.m_NetlistMetadata.SetExtraData( pnpAttrib.FormatCmpPnPMetadata() ); @@ -174,13 +178,13 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, } } - D_PAD* pad1 = nullptr; + std::vectorpad_key_list; if( m_plotPad1Marker ) { - pad1 = findPad1( footprint ); + findPads1( pad_key_list, footprint ); - if( pad1 ) + for( D_PAD* pad1 : pad_key_list ) { gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_PAD1_POSITION ); @@ -205,7 +209,18 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, for( D_PAD* pad: footprint->Pads() ) { - if( pad == pad1 ) // Already plotted + bool skip_pad = false; + + for( D_PAD* pad1 : pad_key_list ) + { + if( pad == pad1 ) // Already plotted + { + skip_pad = true; + break; + } + } + + if( skip_pad ) continue; // Skip also pads not on the current layer, like pads only @@ -216,13 +231,13 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, gbr_metadata.SetPadName( pad->GetName() ); // Flashes a round, 0 sized round shape at pad position - int mark_size = Millimeter2iu( 0.1 ); + int mark_size = 0; plotter.FlashPadCircle( pad->GetPosition() + m_offset, mark_size, - FILLED, &gbr_metadata ); + FILLED, &gbr_metadata ); } } - plotter.EndBlock( nullptr ); // Close all .TO attributes + plotter.ClearAllAttributes(); // Unconditionally close all .TO attributes cmp_count++; } @@ -241,27 +256,20 @@ double PLACEFILE_GERBER_WRITER::mapRotationAngle( double aAngle ) } -D_PAD* PLACEFILE_GERBER_WRITER::findPad1( MODULE* aFootprint ) +void PLACEFILE_GERBER_WRITER::findPads1( std::vector& aPadList, MODULE* aFootprint ) const { // Fint the pad "1" or pad "A1" // this is possible only if only one pad is found // Usefull to place a marker in this position - std::vector pad1_list; - for( D_PAD* pad : aFootprint->Pads() ) { if( !pad->IsOnLayer( m_layer ) ) continue; if( pad->GetName() == "1" || pad->GetName() == "A1") - pad1_list.push_back( pad ); + aPadList.push_back( pad ); } - - if( pad1_list.size() == 1 ) - return pad1_list[0]; - - return nullptr; } diff --git a/pcbnew/exporters/gerber_placefile_writer.h b/pcbnew/exporters/gerber_placefile_writer.h index 0a20ec3fe4..64e2be53ee 100644 --- a/pcbnew/exporters/gerber_placefile_writer.h +++ b/pcbnew/exporters/gerber_placefile_writer.h @@ -93,10 +93,12 @@ private: */ double mapRotationAngle( double aAngle ); - /** Find the pad 1 (or pad "A1") of a footprint - * Usefull to plot a marker at this position + /** Find the pad(s) 1 (or pad "A1") of a footprint + * Usefull to plot a marker at this (these) position(s) + * @param aPadList is the list to fill + * @param aFootprint is the footprint to test */ - D_PAD* findPad1( MODULE* aFootprint ); + void findPads1( std::vector& aPadList, MODULE* aFootprint ) const; }; #endif // #ifndef PLACEFILE_GERBER_WRITER_H