VIA* TRACK::GetVia( TRACK* aEndTrace, const wxPoint& aPosition, LAYER_MSK aLayerMask ) { for( VIA *via = GetFirstVia( this, aEndTrace ); via; via = GetFirstVia( via->Next() ) ) { if( via->HitTest( aPosition ) && !via->GetState( BUSY | IS_DELETED ) && (aLayerMask & via->GetLayerMask()) ) return via; } return NULL; }
VIA* TRACK::GetVia( const wxPoint& aPosition, LAYER_NUM aLayer) { for( VIA *via = GetFirstVia( this ); via; via = GetFirstVia( via->Next() ) ) { if( via->HitTest( aPosition ) && !via->GetState( BUSY | IS_DELETED ) && ((aLayer == UNDEFINED_LAYER) || (via->IsOnLayer( aLayer ))) ) return via; } return NULL; }
bool TRACKS_CLEANER::clean_vias() { bool modified = false; for( VIA* via = GetFirstVia( m_Brd->m_Track ); via != NULL; via = GetFirstVia( via->Next() ) ) { // Correct via m_End defects (if any), should never happen if( via->GetStart() != via->GetEnd() ) { wxFAIL_MSG( wxT( "Via with mismatching ends" ) ); via->SetEnd( via->GetStart() ); } /* Important: these cleanups only do thru hole vias, they don't * (yet) handle high density interconnects */ if( via->GetViaType() != VIA_THROUGH ) { modified |= remove_duplicates_of_via( via ); /* To delete through Via on THT pads at same location * Examine the list of connected pads: * if one through pad is found, the via can be removed */ for( unsigned ii = 0; ii < via->m_PadsConnected.size(); ++ii ) { const D_PAD *pad = via->m_PadsConnected[ii]; const LSET all_cu = LSET::AllCuMask(); if( (pad->GetLayerSet() & all_cu) == all_cu ) { // redundant: delete the via m_Brd->GetRatsnest()->Remove( via ); via->ViewRelease(); via->DeleteStructure(); modified = true; break; } } } } return modified; }
void EXCELLON_WRITER::BuildHolesList( LAYER_PAIR aLayerPair, bool aGenerateNPTH_list ) { HOLE_INFO new_hole; m_holeListBuffer.clear(); m_toolListBuffer.clear(); wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller // build hole list for vias if( ! aGenerateNPTH_list ) // vias are always plated ! { for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) ) { int hole_sz = via->GetDrillValue(); if( hole_sz == 0 ) // Should not occur. continue; new_hole.m_Tool_Reference = -1; // Flag value for Not initialized new_hole.m_Hole_Orient = 0; new_hole.m_Hole_Diameter = hole_sz; new_hole.m_Hole_NotPlated = false; new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Pos = via->GetStart(); via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer ); // LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer // Remember: top layer = 0 and bottom layer = 31 for through hole vias // Any captured via should be from aLayerPair.first to aLayerPair.second exactly. if( new_hole.m_Hole_Top_Layer != aLayerPair.first || new_hole.m_Hole_Bottom_Layer != aLayerPair.second ) continue; m_holeListBuffer.push_back( new_hole ); } } if( aLayerPair == LAYER_PAIR( F_Cu, B_Cu ) ) { // add holes for thru hole pads for( MODULE* module = m_pcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { if( !m_merge_PTH_NPTH ) { if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ) continue; if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED ) continue; } if( pad->GetDrillSize().x == 0 ) continue; new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED); new_hole.m_Tool_Reference = -1; // Flag is: Not initialized new_hole.m_Hole_Orient = pad->GetOrientation(); new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ); new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE ) new_hole.m_Hole_Shape = 1; // oval flag set new_hole.m_Hole_Size = pad->GetDrillSize(); new_hole.m_Hole_Pos = pad->GetPosition(); // hole position new_hole.m_Hole_Bottom_Layer = B_Cu; new_hole.m_Hole_Top_Layer = F_Cu; // pad holes are through holes m_holeListBuffer.push_back( new_hole ); } } } // Sort holes per increasing diameter value sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleSettings ); // build the tool list int last_hole = -1; // Set to not initialized (this is a value not used // for m_holeListBuffer[ii].m_Hole_Diameter) bool last_notplated_opt = false; DRILL_TOOL new_tool( 0, false ); unsigned jj; for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ ) { if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole || m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt ) { new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter; new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated; m_toolListBuffer.push_back( new_tool ); last_hole = new_tool.m_Diameter; last_notplated_opt = new_tool.m_Hole_NotPlated; } jj = m_toolListBuffer.size(); if( jj == 0 ) continue; // Should not occurs m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1) m_toolListBuffer.back().m_TotalCount++; if( m_holeListBuffer[ii].m_Hole_Shape ) m_toolListBuffer.back().m_OvalCount++; } }
void EXCELLON_WRITER::BuildHolesList( int aFirstLayer, int aLastLayer, bool aExcludeThroughHoles, bool aGenerateNPTH_list, bool aMerge_PTH_NPTH ) { HOLE_INFO new_hole; int hole_value; m_holeListBuffer.clear(); m_toolListBuffer.clear(); if( (aFirstLayer >= 0) && (aLastLayer >= 0) ) { if( aFirstLayer > aLastLayer ) std::swap( aFirstLayer, aLastLayer ); } if ( aGenerateNPTH_list && aMerge_PTH_NPTH ) { return; } // build hole list for vias if( ! aGenerateNPTH_list ) // vias are always plated ! { for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) ) { hole_value = via->GetDrillValue(); if( hole_value == 0 ) // Should not occur. continue; new_hole.m_Tool_Reference = -1; // Flag value for Not initialized new_hole.m_Hole_Orient = 0; new_hole.m_Hole_Diameter = hole_value; new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Pos = via->GetStart(); via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer ); // LayerPair return params with m_Hole_Bottom_Layer > m_Hole_Top_Layer // Remember: top layer = 0 and bottom layer = 31 for through hole vias // the via should be at least from aFirstLayer to aLastLayer if( (new_hole.m_Hole_Top_Layer > aFirstLayer) && (aFirstLayer >= 0) ) continue; // via above the first layer if( (new_hole.m_Hole_Bottom_Layer < aLastLayer) && (aLastLayer >= 0) ) continue; // via below the last layer if( aExcludeThroughHoles && (new_hole.m_Hole_Bottom_Layer == B_Cu) && (new_hole.m_Hole_Top_Layer == F_Cu) ) continue; m_holeListBuffer.push_back( new_hole ); } } // build hole list for pads (assumed always through holes) if( !aExcludeThroughHoles || aGenerateNPTH_list ) { for( MODULE* module = m_pcb->m_Modules; module; module = module->Next() ) { // Read and analyse pads for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) { if( ! aGenerateNPTH_list && pad->GetAttribute() == PAD_HOLE_NOT_PLATED && ! aMerge_PTH_NPTH ) continue; if( aGenerateNPTH_list && pad->GetAttribute() != PAD_HOLE_NOT_PLATED ) continue; if( pad->GetDrillSize().x == 0 ) continue; new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_HOLE_NOT_PLATED); new_hole.m_Tool_Reference = -1; // Flag is: Not initialized new_hole.m_Hole_Orient = pad->GetOrientation(); new_hole.m_Hole_Shape = 0; // hole shape: round new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ); new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; if( pad->GetDrillShape() != PAD_DRILL_CIRCLE ) new_hole.m_Hole_Shape = 1; // oval flag set new_hole.m_Hole_Size = pad->GetDrillSize(); new_hole.m_Hole_Pos = pad->GetPosition(); // hole position new_hole.m_Hole_Bottom_Layer = B_Cu; new_hole.m_Hole_Top_Layer = F_Cu;// pad holes are through holes m_holeListBuffer.push_back( new_hole ); } } } // Sort holes per increasing diameter value sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleDiameterValue ); // build the tool list int LastHole = -1; /* Set to not initialized (this is a value not used * for m_holeListBuffer[ii].m_Hole_Diameter) */ DRILL_TOOL new_tool( 0 ); unsigned jj; for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ ) { if( m_holeListBuffer[ii].m_Hole_Diameter != LastHole ) { new_tool.m_Diameter = ( m_holeListBuffer[ii].m_Hole_Diameter ); m_toolListBuffer.push_back( new_tool ); LastHole = new_tool.m_Diameter; } jj = m_toolListBuffer.size(); if( jj == 0 ) continue; // Should not occurs m_holeListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1) m_toolListBuffer.back().m_TotalCount++; if( m_holeListBuffer[ii].m_Hole_Shape ) m_toolListBuffer.back().m_OvalCount++; } }
// 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 ); }