// Emit PADS and PADSTACKS. They are sorted and emitted uniquely. // Via name is synthesized from their attributes, pads are numbered static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb ) { std::vector<D_PAD*> pads; std::vector<D_PAD*> padstacks; std::vector<VIA*> vias; std::vector<VIA*> viastacks; padstacks.resize( 1 ); // We count pads from 1 // The master layermask (i.e. the enabled layers) for padstack generation LSET master_layermask = aPcb->GetDesignSettings().GetEnabledLayers(); int cu_count = aPcb->GetCopperLayerCount(); fputs( "$PADS\n", aFile ); // Enumerate and sort the pads if( aPcb->GetPadCount() > 0 ) { pads = aPcb->GetPads(); qsort( &pads[0], aPcb->GetPadCount(), sizeof( D_PAD* ), PadListSortByShape ); } // The same for vias for( VIA* via = GetFirstVia( aPcb->m_Track ); via; via = GetFirstVia( via->Next() ) ) { vias.push_back( via ); } qsort( &vias[0], vias.size(), sizeof(VIA*), ViaSort ); // Emit vias pads TRACK* old_via = 0; for( unsigned i = 0; i < vias.size(); i++ ) { VIA* via = vias[i]; if( old_via && 0 == ViaSort( &old_via, &via ) ) continue; old_via = via; viastacks.push_back( via ); fprintf( aFile, "PAD V%d.%d.%s ROUND %g\nCIRCLE 0 0 %g\n", via->GetWidth(), via->GetDrillValue(), fmt_mask( via->GetLayerSet() ).c_str(), via->GetDrillValue() / SCALE_FACTOR, via->GetWidth() / (SCALE_FACTOR * 2) ); } // Emit component pads D_PAD* old_pad = 0; int pad_name_number = 0; for( unsigned i = 0; i<pads.size(); ++i ) { D_PAD* pad = pads[i]; pad->SetSubRatsnest( pad_name_number ); if( old_pad && 0==D_PAD::Compare( old_pad, pad ) ) continue; // already created old_pad = pad; pad_name_number++; pad->SetSubRatsnest( pad_name_number ); fprintf( aFile, "PAD P%d", pad->GetSubRatsnest() ); padstacks.push_back( pad ); // Will have its own padstack later int dx = pad->GetSize().x / 2; int dy = pad->GetSize().y / 2; switch( pad->GetShape() ) { default: case PAD_SHAPE_CIRCLE: fprintf( aFile, " ROUND %g\n", pad->GetDrillSize().x / SCALE_FACTOR ); /* Circle is center, radius */ fprintf( aFile, "CIRCLE %g %g %g\n", pad->GetOffset().x / SCALE_FACTOR, -pad->GetOffset().y / SCALE_FACTOR, pad->GetSize().x / (SCALE_FACTOR * 2) ); break; case PAD_SHAPE_RECT: fprintf( aFile, " RECTANGULAR %g\n", pad->GetDrillSize().x / SCALE_FACTOR ); // Rectangle is begin, size *not* begin, end! fprintf( aFile, "RECTANGLE %g %g %g %g\n", (-dx + pad->GetOffset().x ) / SCALE_FACTOR, (-dy - pad->GetOffset().y ) / SCALE_FACTOR, dx / (SCALE_FACTOR / 2), dy / (SCALE_FACTOR / 2) ); break; case PAD_SHAPE_OVAL: // Create outline by 2 lines and 2 arcs { // OrCAD Layout call them OVAL or OBLONG - GenCAD call them FINGERs fprintf( aFile, " FINGER %g\n", pad->GetDrillSize().x / SCALE_FACTOR ); int dr = dx - dy; if( dr >= 0 ) // Horizontal oval { int radius = dy; fprintf( aFile, "LINE %g %g %g %g\n", (-dr + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y - radius) / SCALE_FACTOR, (dr + pad->GetOffset().x ) / SCALE_FACTOR, (-pad->GetOffset().y - radius) / SCALE_FACTOR ); // GenCAD arcs are (start, end, center) fprintf( aFile, "ARC %g %g %g %g %g %g\n", (dr + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y - radius) / SCALE_FACTOR, (dr + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y + radius) / SCALE_FACTOR, (dr + pad->GetOffset().x) / SCALE_FACTOR, -pad->GetOffset().y / SCALE_FACTOR ); fprintf( aFile, "LINE %g %g %g %g\n", (dr + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y + radius) / SCALE_FACTOR, (-dr + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y + radius) / SCALE_FACTOR ); fprintf( aFile, "ARC %g %g %g %g %g %g\n", (-dr + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y + radius) / SCALE_FACTOR, (-dr + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y - radius) / SCALE_FACTOR, (-dr + pad->GetOffset().x) / SCALE_FACTOR, -pad->GetOffset().y / SCALE_FACTOR ); } else // Vertical oval { dr = -dr; int radius = dx; fprintf( aFile, "LINE %g %g %g %g\n", (-radius + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y - dr) / SCALE_FACTOR, (-radius + pad->GetOffset().x ) / SCALE_FACTOR, (-pad->GetOffset().y + dr) / SCALE_FACTOR ); fprintf( aFile, "ARC %g %g %g %g %g %g\n", (-radius + pad->GetOffset().x ) / SCALE_FACTOR, (-pad->GetOffset().y + dr) / SCALE_FACTOR, (radius + pad->GetOffset().x ) / SCALE_FACTOR, (-pad->GetOffset().y + dr) / SCALE_FACTOR, pad->GetOffset().x / SCALE_FACTOR, (-pad->GetOffset().y + dr) / SCALE_FACTOR ); fprintf( aFile, "LINE %g %g %g %g\n", (radius + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y + dr) / SCALE_FACTOR, (radius + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y - dr) / SCALE_FACTOR ); fprintf( aFile, "ARC %g %g %g %g %g %g\n", (radius + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y - dr) / SCALE_FACTOR, (-radius + pad->GetOffset().x) / SCALE_FACTOR, (-pad->GetOffset().y - dr) / SCALE_FACTOR, pad->GetOffset().x / SCALE_FACTOR, (-pad->GetOffset().y - dr) / SCALE_FACTOR ); } } break; case PAD_SHAPE_TRAPEZOID: fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR ); // XXX TO BE IMPLEMENTED! and I don't know if it could be actually imported by something break; } } fputs( "\n$ENDPADS\n\n", aFile ); // Now emit the padstacks definitions, using the combined layer masks fputs( "$PADSTACKS\n", aFile ); // Via padstacks for( unsigned i = 0; i < viastacks.size(); i++ ) { VIA* via = viastacks[i]; LSET mask = via->GetLayerSet() & master_layermask; fprintf( aFile, "PADSTACK VIA%d.%d.%s %g\n", via->GetWidth(), via->GetDrillValue(), fmt_mask( mask ).c_str(), via->GetDrillValue() / SCALE_FACTOR ); for( LSEQ seq = mask.Seq( gc_seq, DIM( gc_seq ) ); seq; ++seq ) { LAYER_ID layer = *seq; fprintf( aFile, "PAD V%d.%d.%s %s 0 0\n", via->GetWidth(), via->GetDrillValue(), fmt_mask( mask ).c_str(), GenCADLayerName( cu_count, layer ).c_str() ); } } /* Component padstacks * CAM350 don't apply correctly the FLIP semantics for padstacks, i.e. doesn't * swap the top and bottom layers... so I need to define the shape as MIRRORX * and define a separate 'flipped' padstack... until it appears yet another * noncompliant importer */ for( unsigned i = 1; i < padstacks.size(); i++ ) { D_PAD* pad = padstacks[i]; // Straight padstack fprintf( aFile, "PADSTACK PAD%u %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR ); LSET pad_set = pad->GetLayerSet() & master_layermask; // the special gc_seq for( LSEQ seq = pad_set.Seq( gc_seq, DIM( gc_seq ) ); seq; ++seq ) { LAYER_ID layer = *seq; fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerName( cu_count, layer ).c_str() ); } // Flipped padstack fprintf( aFile, "PADSTACK PAD%uF %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR ); // the normal LAYER_ID sequence is inverted from gc_seq[] for( LSEQ seq = pad_set.Seq(); seq; ++seq ) { LAYER_ID layer = *seq; fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerNameFlipped( cu_count, layer ).c_str() ); } } fputs( "$ENDPADSTACKS\n\n", aFile ); }
bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName ) { FILE_OUTPUTFORMATTER out( aFullFileName ); static const char separator[] = " =============================================================\n"; wxASSERT( m_pcb ); unsigned totalHoleCount; wxString brdFilename = m_pcb->GetFileName(); std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs(); out.Print( 0, "Drill report for %s\n", TO_UTF8( brdFilename ) ); out.Print( 0, "Created on %s\n\n", TO_UTF8( DateAndTime() ) ); // Output the cu layer stackup, so layer name references make sense. out.Print( 0, "Copper Layer Stackup:\n" ); out.Print( 0, separator ); LSET cu = m_pcb->GetEnabledLayers() & LSET::AllCuMask(); int conventional_layer_num = 1; for( LSEQ seq = cu.Seq(); seq; ++seq, ++conventional_layer_num ) { out.Print( 0, " L%-2d: %-25s %s\n", conventional_layer_num, TO_UTF8( m_pcb->GetLayerName( *seq ) ), layerName( *seq ).c_str() // generic layer name ); } out.Print( 0, "\n\n" ); /* output hole lists: * 1 - through holes * 2 - for partial holes only: by layer starting and ending pair * 3 - Non Plated through holes */ bool buildNPTHlist = false; // First pass: build PTH list only // in this loop are plated only: for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx ) { DRILL_LAYER_PAIR pair = hole_sets[pair_ndx]; buildHolesList( pair, buildNPTHlist ); if( pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) ) { out.Print( 0, "Drill file '%s' contains\n", TO_UTF8( getDrillFileName( pair, false, m_merge_PTH_NPTH ) ) ); out.Print( 0, " plated through holes:\n" ); out.Print( 0, separator ); totalHoleCount = printToolSummary( out, false ); out.Print( 0, " Total plated holes count %u\n", totalHoleCount ); } else // blind/buried { out.Print( 0, "Drill file '%s' contains\n", TO_UTF8( getDrillFileName( pair, false, m_merge_PTH_NPTH ) ) ); out.Print( 0, " holes connecting layer pair: '%s and %s' (%s vias):\n", TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.first ) ) ), TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.second ) ) ), pair.first == F_Cu || pair.second == B_Cu ? "blind" : "buried" ); out.Print( 0, separator ); totalHoleCount = printToolSummary( out, false ); out.Print( 0, " Total plated holes count %u\n", totalHoleCount ); } out.Print( 0, "\n\n" ); } // NPTHoles. Generate the full list (pads+vias) if PTH and NPTH are merged, // or only the NPTH list (which never has vias) if( !m_merge_PTH_NPTH ) buildNPTHlist = true; buildHolesList( DRILL_LAYER_PAIR( F_Cu, B_Cu ), buildNPTHlist ); // nothing wrong with an empty NPTH file in report. if( m_merge_PTH_NPTH ) out.Print( 0, "Not plated through holes are merged with plated holes\n" ); else out.Print( 0, "Drill file '%s' contains\n", TO_UTF8( getDrillFileName( DRILL_LAYER_PAIR( F_Cu, B_Cu ), true, m_merge_PTH_NPTH ) ) ); out.Print( 0, " unplated through holes:\n" ); out.Print( 0, separator ); totalHoleCount = printToolSummary( out, true ); out.Print( 0, " Total unplated holes count %u\n", totalHoleCount ); return true; }
/* Plot outlines of copper, for copper layer */ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt ) { BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt ); itemplotter.SetLayerSet( aLayerMask ); SHAPE_POLY_SET outlines; for( LSEQ seq = aLayerMask.Seq( plot_seq, DIM( plot_seq ) ); seq; ++seq ) { LAYER_ID layer = *seq; outlines.RemoveAllContours(); aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines ); outlines.Simplify(); // Plot outlines std::vector< wxPoint > cornerList; // Now we have one or more basic polygons: plot each polygon for( int ii = 0; ii < outlines.OutlineCount(); ii++ ) { for(int kk = 0; kk <= outlines.HoleCount (ii); kk++ ) { cornerList.clear(); const SHAPE_LINE_CHAIN& path = (kk == 0) ? outlines.COutline( ii ) : outlines.CHole( ii, kk - 1 ); for( int jj = 0; jj < path.PointCount(); jj++ ) cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) ); // Ensure the polygon is closed if( cornerList[0] != cornerList[cornerList.size() - 1] ) cornerList.push_back( cornerList[0] ); aPlotter->PlotPoly( cornerList, NO_FILL ); } } // Plot pad holes if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE ) { for( MODULE* module = aBoard->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { wxSize hole = pad->GetDrillSize(); if( hole.x == 0 || hole.y == 0 ) continue; if( hole.x == hole.y ) aPlotter->Circle( pad->GetPosition(), hole.x, NO_FILL ); else { wxPoint drl_start, drl_end; int width; pad->GetOblongDrillGeometry( drl_start, drl_end, width ); aPlotter->ThickSegment( pad->GetPosition() + drl_start, pad->GetPosition() + drl_end, width, SKETCH ); } } } } // Plot vias holes for( TRACK* track = aBoard->m_Track; track; track = track->Next() ) { const VIA* via = dyn_cast<const VIA*>( track ); if( via && via->IsOnLayer( layer ) ) // via holes can be not through holes { aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), NO_FILL ); } } } }