diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp index 9bd37d300b..f37f8ab956 100644 --- a/pcbnew/specctra_import_export/specctra_export.cpp +++ b/pcbnew/specctra_import_export/specctra_export.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2007-2015 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2015-2021 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 @@ -64,7 +64,8 @@ using namespace DSN; // that can create issues. // Especially Freerouter does not handle them very well: // - too complex shapes are not accepted, especially shapes with holes (dsn files are not loaded). -// - and Freerouter actually uses something like a convex hull of the shape (that works not very well). +// - and Freerouter actually uses something like a convex hull of the shape (that works not very +// well). // I am guessing non convex polygons with holes linked could create issues with any Router. #define EXPORT_CUSTOM_PADS_CONVEX_HULL @@ -95,7 +96,7 @@ bool PCB_EDIT_FRAME::ExportSpecctraFile( const wxString& aFullFilename ) // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the top view. So we // temporarily flip any footprints which are on the back side of the board to the front, // and record this in the FOOTPRINT's flag field. - db.FlipFOOTPRINTs( GetBoard()); + db.FlipFOOTPRINTs( GetBoard() ); try { @@ -115,7 +116,7 @@ bool PCB_EDIT_FRAME::ExportSpecctraFile( const wxString& aFullFilename ) } // done assuredly, even if an exception was thrown and caught. - db.RevertFOOTPRINTs( GetBoard()); + db.RevertFOOTPRINTs( GetBoard() ); // The two calls below to FOOTPRINT::Flip(), both set the // modified flag, yet their actions cancel each other out, so it should @@ -147,8 +148,7 @@ const KICAD_T SPECCTRA_DB::scanPADs[] = { PCB_PAD_T, EOT }; /** - * Function scale - * converts a distance from PCBNEW internal units to the reported specctra dsn units + * Convert a distance from Pcbnew internal units to the reported Specctra DSN units * in floating point format. */ static inline double scale( int kicadDist ) @@ -158,7 +158,7 @@ static inline double scale( int kicadDist ) } -// / Convert integer internal units to float um +///< Convert integer internal units to float um static inline double IU2um( int kicadDist ) { return kicadDist * (1000.0 / IU_PER_MM); @@ -178,10 +178,10 @@ static inline double mapY( int y ) /** - * Function mapPt - * converts a KiCad point into a DSN file point. Kicad's BOARD coordinates - * are in nanometers (called Internal Units or IU)and we are exporting in units - * of mils, so we have to scale them. + * Convert a KiCad point into a DSN file point. + * + * Kicad's #BOARD coordinates are in nanometers (called Internal Units or IU) and we are + * exporting in units of mils, so we have to scale them. */ static POINT mapPt( const wxPoint& pt ) { @@ -195,13 +195,11 @@ static POINT mapPt( const wxPoint& pt ) /** - * Function isRoundKeepout - * decides if the pad is a copper-less through hole which needs to be made into - * a round keepout. + * Decide if the pad is a copper-less through hole which needs to be made into a round keepout. */ static bool isRoundKeepout( PAD* aPad ) { - if( aPad->GetShape()==PAD_SHAPE_CIRCLE ) + if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) { if( aPad->GetDrillSize().x >= aPad->GetSize().x ) return true; @@ -215,8 +213,7 @@ static bool isRoundKeepout( PAD* aPad ) /** - * Function makePath - * creates a PATH element with a single straight line, a pair of vertices. + * Create a PATH element with a single straight line, a pair of vertices. */ static PATH* makePath( const POINT& aStart, const POINT& aEnd, const std::string& aLayerName ) { @@ -568,7 +565,8 @@ PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, PAD* aPad ) for( unsigned idx = 0; idx < polygonal_shape.size(); idx++ ) { - POINT corner( scale( polygonal_shape[idx].x ), scale( -polygonal_shape[idx].y ) ); + POINT corner( scale( polygonal_shape[idx].x ), + scale( -polygonal_shape[idx].y ) ); corner += dsnOffset; polygon->AppendPoint( corner ); } @@ -578,9 +576,9 @@ PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, PAD* aPad ) MD5_HASH hash = pad_shape.GetHash(); EDA_RECT rect = aPad->GetBoundingBox(); snprintf( name, sizeof(name), "Cust%sPad_%.6gx%.6g_%.6gx_%.6g_%d_um_%s", - uniqifier.c_str(), IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ), - IU2um( rect.GetWidth() ), IU2um( rect.GetHeight() ), - (int)polygonal_shape.size(), hash.Format( true ).c_str() ); + uniqifier.c_str(), IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ), + IU2um( rect.GetWidth() ), IU2um( rect.GetHeight() ), + (int)polygonal_shape.size(), hash.Format( true ).c_str() ); name[ sizeof(name)-1 ] = 0; padstack->SetPadstackId( name ); @@ -606,7 +604,7 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint ) // get all the FOOTPRINT's pads. fpItems.Collect( aFootprint, scanPADs ); - IMAGE* image = new IMAGE(0); + IMAGE* image = new IMAGE( 0 ); image->image_id = aFootprint->GetFPID().Format().c_str(); @@ -668,7 +666,7 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint ) padName = pad->GetName(); pin->pin_id = TO_UTF8( padName ); - if( padName!=wxEmptyString && pinmap.find( padName )==pinmap.end() ) + if( padName != wxEmptyString && pinmap.find( padName ) == pinmap.end() ) { pinmap[ padName ] = 0; } @@ -699,7 +697,6 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint ) } } -#if 1 // enable image (outline) scopes. static const KICAD_T scanEDGEs[] = { PCB_FP_SHAPE_T, EOT }; // get all the FOOTPRINT's FP_SHAPEs and convert those to DSN outlines. @@ -775,14 +772,130 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint ) } } -#endif + for( FP_ZONE* zone : aFootprint->Zones() ) + { + if( !zone->GetIsRuleArea() ) + continue; + + // IMAGE object coordinates are relative to the IMAGE not absolute board coordinates. + ZONE untransformedZone( *zone ); + + double angle = -aFootprint->GetOrientation(); + NORMALIZE_ANGLE_POS( angle ); + untransformedZone.Rotate( aFootprint->GetPosition(), angle ); + + // keepout areas have a type. types are + // T_place_keepout, T_via_keepout, T_wire_keepout, + // T_bend_keepout, T_elongate_keepout, T_keepout. + // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout + DSN_T keepout_type; + + if( zone->GetDoNotAllowVias() && zone->GetDoNotAllowTracks() ) + keepout_type = T_keepout; + else if( zone->GetDoNotAllowVias() ) + keepout_type = T_via_keepout; + else if( zone->GetDoNotAllowTracks() ) + keepout_type = T_wire_keepout; + else + keepout_type = T_keepout; + + // Now, build keepout polygon on each copper layer where the zone + // keepout is living (keepout zones can live on many copper layers) + const int copperCount = aBoard->GetCopperLayerCount(); + + for( int layer = 0; layer < copperCount; layer++ ) + { + if( layer == copperCount-1 ) + layer = B_Cu; + + if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) ) + continue; + + KEEPOUT* keepout = new KEEPOUT( m_pcb->structure, keepout_type ); + image->keepouts.push_back( keepout ); + + PATH* mainPolygon = new PATH( keepout, T_polygon ); + keepout->SetShape( mainPolygon ); + + mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ]; + + // Handle the main outlines + SHAPE_POLY_SET::ITERATOR iterator; + bool is_first_point = true; + wxPoint startpoint; + + for( iterator = untransformedZone.IterateWithHoles(); iterator; iterator++ ) + { + wxPoint point( iterator->x, iterator->y ); + + point -= aFootprint->GetPosition(); + + if( is_first_point ) + { + startpoint = point; + is_first_point = false; + } + + mainPolygon->AppendPoint( mapPt( point ) ); + + // this was the end of the main polygon + if( iterator.IsEndContour() ) + { + mainPolygon->AppendPoint( mapPt( startpoint ) ); + break; + } + } + + WINDOW* window = nullptr; + PATH* cutout = nullptr; + bool isStartContour = true; + + // handle the cutouts + for( iterator++; iterator; iterator++ ) + { + if( isStartContour ) + { + is_first_point = true; + window = new WINDOW( keepout ); + keepout->AddWindow( window ); + + cutout = new PATH( window, T_polygon ); + + window->SetShape( cutout ); + + cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ zone->GetLayer() ] ]; + } + + isStartContour = iterator.IsEndContour(); + + wxASSERT( window ); + wxASSERT( cutout ); + + wxPoint point( iterator->x, iterator->y ); + + point -= aFootprint->GetPosition(); + + if( is_first_point ) + { + startpoint = point; + is_first_point = false; + } + + cutout->AppendPoint( mapPt( point ) ); + + // Close the polygon + if( iterator.IsEndContour() ) + cutout->AppendPoint( mapPt( startpoint ) ); + } + } + } return image; } PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter, - int aTopLayer, int aBotLayer ) + int aTopLayer, int aBotLayer ) { char name[48]; PADSTACK* padstack = new PADSTACK(); @@ -802,11 +915,10 @@ PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter, circle->SetLayerId( m_layerIds[layer].c_str() ); } - snprintf( name, sizeof(name), "Via[%d-%d]_%.6g:%.6g_um", + snprintf( name, sizeof( name ), "Via[%d-%d]_%.6g:%.6g_um", aTopLayer, aBotLayer, dsnDiameter, // encode the drill value into the name for later import - IU2um( aDrillDiameter ) - ); + IU2um( aDrillDiameter ) ); name[ sizeof(name) - 1 ] = 0; padstack->SetPadstackId( name ); @@ -956,12 +1068,9 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) case LT_SIGNAL: layerType = T_signal; break; case LT_POWER: layerType = T_power; break; -#if 1 // Freerouter does not support type "mixed", only signal and power. + // Freerouter does not support type "mixed", only signal and power. // Remap "mixed" to "signal". case LT_MIXED: layerType = T_signal; break; -#else - case LT_MIXED: layerType = T_mixed; break; -#endif case LT_JUMPER: layerType = T_jumper; break; } @@ -988,7 +1097,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) m_pcb->unit->units = T_um; m_pcb->resolution->units = T_um; m_pcb->resolution->value = 10; // tenths of a um - // pcb->resolution->value = 1000; // "thousandths of a um" (i.e. "nm") } //----------------------------------------------- @@ -1002,7 +1110,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) fillBOUNDARY( aBoard, boundary ); } - //------------------------------------------------------------- { char rule[80]; @@ -1038,29 +1145,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) rules.push_back( rule ); - /* see: http://www.freerouting.net/usren/viewtopic.php?f=5&t=339#p474 - sprintf( rule, "(clearance %.6g (type pad_to_turn_gap))", clearance + safetyMargin ); - rules.push_back( rule ); - - sprintf( rule, "(clearance %.6g (type smd_to_turn_gap))", clearance + safetyMargin ); - rules.push_back( rule ); - - sprintf( rule, "(clearance %.6g (type via_via))", clearance + safetyMargin ); - rules.push_back( rule ); - - sprintf( rule, "(clearance %.6g (type via_smd))", clearance + safetyMargin ); - rules.push_back( rule ); - - sprintf( rule, "(clearance %.6g (type via_pin))", clearance + safetyMargin ); - rules.push_back( rule ); - - sprintf( rule, "(clearance %.6g (type pin_pin))", clearance + safetyMargin ); - rules.push_back( rule ); - - sprintf( rule, "(clearance %.6g (type smd_pin))", clearance + safetyMargin ); - rules.push_back( rule ); - */ - // Pad to pad spacing on a single SMT part can be closer than our // clearance, we don't want freerouter complaining about that, so // output a significantly smaller pad to pad clearance to freerouter. @@ -1070,9 +1154,8 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) rules.push_back( rule ); } - //------------------------------------- - // Note: only zones are output here, keepout areas be be created later + // Note: only zones are output here, keepout areas are created later. { int netlessZones = 0; @@ -1183,7 +1266,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) is_first_point = false; } - cutout->AppendPoint( mapPt(point) ); + cutout->AppendPoint( mapPt( point ) ); // Close the polygon if( iterator.IsEndContour() ) @@ -1225,7 +1308,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) for( int layer = 0; layer < copperCount; layer++ ) { - if( layer == copperCount-1) + if( layer == copperCount - 1 ) layer = B_Cu; if( !item->IsOnLayer( PCB_LAYER_ID( layer ) ) ) @@ -1254,7 +1337,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) is_first_point = false; } - mainPolygon->AppendPoint( mapPt(point) ); + mainPolygon->AppendPoint( mapPt( point ) ); // this was the end of the main polygon if( iterator.IsEndContour() ) @@ -1374,7 +1457,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) } } - IMAGE* registered = m_pcb->library->LookupIMAGE( image ); if( registered != image ) @@ -1432,7 +1514,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) } } - //-----< output vias used in netclasses >----------------------------------- { NETCLASSES& nclasses = aBoard->GetDesignSettings().GetNetClasses(); @@ -1442,19 +1523,8 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) // the netclass dialog, or such control in the specctra export dialog. - // if( aBoard->GetDesignSettings().m_CurrentViaType == VIA_THROUGH ) - { - m_top_via_layer = 0; // first specctra cu layer is number zero. - m_bot_via_layer = aBoard->GetCopperLayerCount()-1; - } - /* - else - { - // again, should be in the BOARD: - topLayer = kicadLayer2pcb[ GetScreen()->m_Route_Layer_TOP ]; - botLayer = kicadLayer2pcb[ GetScreen()->m_Route_Layer_BOTTOM ]; - } - */ + m_top_via_layer = 0; // first specctra cu layer is number zero. + m_bot_via_layer = aBoard->GetCopperLayerCount()-1; // Add the via from the Default netclass first. The via container // in pcb->library preserves the sequence of addition. @@ -1470,29 +1540,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) wxASSERT( m_pcb->library->vias.size() == 0 ); m_pcb->library->AppendVia( via ); -#if 0 - // I've seen no way to make stock vias useable by freerouter. Also the - // zero based diameter was leading to duplicates in the LookupVia() function. - // User should use netclass based vias when going to freerouter. - - // Output the stock vias, but preserve uniqueness in the via container by - // using LookupVia(). - for( unsigned i = 0; i < aBoard->m_ViasDimensionsList.size(); ++i ) - { - int viaSize = aBoard->m_ViasDimensionsList[i].m_Diameter; - int viaDrill = aBoard->m_ViasDimensionsList[i].m_Drill; - - via = makeVia( viaSize, viaDrill, - m_top_via_layer, m_bot_via_layer ); - - // maybe add 'via' to the library, but only if unique. - PADSTACK* registered = pcb->library->LookupVia( via ); - - if( registered != via ) - delete via; - } -#endif - // set the "spare via" index at the start of the // pcb->library->spareViaIndex = pcb->library->vias.size(); @@ -1512,9 +1559,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) } } - -#if 1 // do existing wires and vias - //---------------------------------------- { // export all of them for now, later we'll decide what controls we need @@ -1540,11 +1584,9 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) if( netcode == 0 ) continue; - if( old_netcode != netcode || - old_width != track->GetWidth() || - old_layer != track->GetLayer() || - (path && path->points.back() != mapPt(track->GetStart()) ) - ) + if( old_netcode != netcode || old_width != track->GetWidth() || + old_layer != track->GetLayer() || + ( path && path->points.back() != mapPt(track->GetStart() ) ) ) { old_width = track->GetWidth(); old_layer = track->GetLayer(); @@ -1582,7 +1624,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) } } - //---------------------- { // Export all vias, once per unique size and drill diameter combo. @@ -1626,8 +1667,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) } } -#endif // do existing wires and vias - //------------------------------------------------------ { // The pcb->library will output which is a combined @@ -1644,7 +1683,6 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) } } - //--------------------------------------------------------- NETCLASSES& nclasses = aBoard->GetDesignSettings().GetNetClasses(); @@ -1743,6 +1781,7 @@ void SPECCTRA_DB::FlipFOOTPRINTs( BOARD* aBoard ) for( FOOTPRINT* footprint : aBoard->Footprints() ) { footprint->SetFlag( 0 ); + if( footprint->GetLayer() == B_Cu ) { footprint->Flip( footprint->GetPosition(), false );