/** * Function Merge_SubNets_Connected_By_CopperAreas(BOARD* aPcb) * Calls Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode ) for each * netcode found in zone list * @param aPcb = the current board */ void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb ) { for( int index = 0; index < aPcb->GetAreaCount(); index++ ) { ZONE_CONTAINER* zone = aPcb->GetArea( index ); if ( ! zone->IsOnCopperLayer() ) continue; if ( zone->GetNetCode() <= 0 ) continue; Merge_SubNets_Connected_By_CopperAreas( aPcb, zone->GetNetCode() ); } }
bool BOARD_NETLIST_UPDATER::testConnectivity( NETLIST& aNetlist ) { // Last step: Some tests: // verify all pads found in netlist: // They should exist in footprints, otherwise the footprint is wrong // note also references or time stamps are updated, so we use only // the reference to find a footprint // // Also verify if zones have acceptable nets, i.e. nets with pads. // Zone with no pad belongs to a "dead" net which happens after changes in schematic // when no more pad use this net name. wxString msg; wxString padname; for( int i = 0; i < (int) aNetlist.GetCount(); i++ ) { const COMPONENT* component = aNetlist.GetComponent( i ); MODULE* footprint = m_board->FindModuleByReference( component->GetReference() ); if( footprint == NULL ) // It can be missing in partial designs continue; // Explore all pins/pads in component for( unsigned jj = 0; jj < component->GetNetCount(); jj++ ) { COMPONENT_NET net = component->GetNet( jj ); padname = net.GetPinName(); if( footprint->FindPadByName( padname ) ) continue; // OK, pad found // not found: bad footprint, report error msg.Printf( _( "Component %s pad %s not found in footprint %s\n" ), GetChars( component->GetReference() ), GetChars( padname ), GetChars( footprint->GetFPID().Format() ) ); m_reporter->Report( msg, REPORTER::RPT_ERROR ); ++m_errorCount; } } // Test copper zones to detect "dead" nets (nets without any pad): for( int i = 0; i < m_board->GetAreaCount(); i++ ) { ZONE_CONTAINER* zone = m_board->GetArea( i ); if( !zone->IsOnCopperLayer() || zone->GetIsKeepout() ) continue; int nc = zone->GetNet()->GetNodesCount(); if( nc == 0 ) { msg.Printf( _( "Copper zone (net name %s): net has no pads connected." ), GetChars( zone->GetNet()->GetNetname() ) ); m_reporter->Report( msg, REPORTER::RPT_WARNING ); ++m_warningCount; } } return true; }
bool BOARD_NETLIST_UPDATER::deleteSinglePadNets() { int count = 0; wxString netname; wxString msg; D_PAD* pad = NULL; D_PAD* previouspad = NULL; // We need the pad list, for next tests. // padlist is the list of pads, sorted by netname. m_board->BuildListOfNets(); if( m_isDryRun ) return false; std::vector<D_PAD*> padlist = m_board->GetPads(); for( unsigned kk = 0; kk < padlist.size(); kk++ ) { pad = padlist[kk]; if( pad->GetNetname().IsEmpty() ) continue; if( netname != pad->GetNetname() ) // End of net { if( previouspad && count == 1 ) { // First, see if we have a copper zone attached to this pad. // If so, this is not really a single pad net for( int ii = 0; ii < m_board->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = m_board->GetArea( ii ); if( !zone->IsOnCopperLayer() ) continue; if( zone->GetIsKeepout() ) continue; if( zone->GetNet() == previouspad->GetNet() ) { count++; break; } } if( count == 1 ) // Really one pad, and nothing else { msg.Printf( _( "Remove single pad net %s." ), GetChars( previouspad->GetNetname() ) ); m_reporter->Report( msg, REPORTER::RPT_ACTION ); msg.Printf( _( "Remove single pad net \"%s\" on \"%s\" pad '%s'\n" ), GetChars( previouspad->GetNetname() ), GetChars( previouspad->GetParent()->GetReference() ), GetChars( previouspad->GetPadName() ) ); m_reporter->Report( msg, REPORTER::RPT_ACTION ); previouspad->SetNetCode( NETINFO_LIST::UNCONNECTED ); } } netname = pad->GetNetname(); count = 1; } else { count++; } previouspad = pad; } // Examine last pad if( pad && count == 1 ) pad->SetNetCode( NETINFO_LIST::UNCONNECTED ); return true; }
bool PCB_EDIT_FRAME::End_Zone( wxDC* DC ) { ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour; if( !zone ) return true; // Validate the current outline: if( zone->GetNumCorners() <= 2 ) // An outline must have 3 corners or more { Abort_Zone_Create_Outline( m_canvas, DC ); return true; } // Remove the last corner if is is at the same location as the prevoius corner zone->m_Poly->RemoveNullSegments(); // Validate the current edge: int icorner = zone->GetNumCorners() - 1; if( zone->IsOnCopperLayer() ) { if( Drc_On && m_drc->Drc( zone, icorner - 1 ) == BAD_DRC ) // we can't validate last edge return false; if( Drc_On && m_drc->Drc( zone, icorner ) == BAD_DRC ) // we can't validate the closing edge { DisplayError( this, _( "DRC error: closing this area creates a drc error with an other area" ) ); m_canvas->MoveCursorToCrossHair(); return false; } } zone->ClearFlags(); zone->DrawWhileCreateOutline( m_canvas, DC, GR_XOR ); m_canvas->SetMouseCapture( NULL, NULL ); // Undraw old drawings, because they can have important changes int layer = zone->GetLayer(); GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_XOR, layer ); GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_XOR, layer ); // Save initial zones configuration, for undo/redo, before adding new zone s_AuxiliaryList.ClearListAndDeleteItems(); s_PickedList.ClearListAndDeleteItems(); SaveCopyOfZones(s_PickedList, GetBoard(), zone->GetNet(), zone->GetLayer() ); // Put new zone in list if( !s_CurrentZone ) { zone->m_Poly->CloseLastContour(); // Close the current corner list GetBoard()->Add( zone ); GetBoard()->m_CurrentZoneContour = NULL; // Add this zone in picked list, as new item ITEM_PICKER picker( zone, UR_NEW ); s_PickedList.PushItem( picker ); } else // Append this outline as a cutout to an existing zone { for( int ii = 0; ii < zone->GetNumCorners(); ii++ ) { s_CurrentZone->AppendCorner( zone->GetCornerPosition( ii ) ); } s_CurrentZone->m_Poly->CloseLastContour(); // Close the current corner list zone->RemoveAllContours(); // All corners are copied in s_CurrentZone. Free corner list. zone = s_CurrentZone; } s_AddCutoutToCurrentZone = false; s_CurrentZone = NULL; GetScreen()->SetCurItem( NULL ); // This outline can be deleted when merging outlines // Combine zones if possible : GetBoard()->OnAreaPolygonModified( &s_AuxiliaryList, zone ); // Redraw the real edge zone : GetBoard()->RedrawAreasOutlines( m_canvas, DC, GR_OR, layer ); GetBoard()->RedrawFilledAreas( m_canvas, DC, GR_OR, layer ); int ii = GetBoard()->GetAreaIndex( zone ); // test if zone exists if( ii < 0 ) zone = NULL; // was removed by combining zones int error_count = GetBoard()->Test_Drc_Areas_Outlines_To_Areas_Outlines( zone, true ); if( error_count ) { DisplayError( this, _( "Area: DRC outline error" ) ); } UpdateCopyOfZonesList( s_PickedList, s_AuxiliaryList, GetBoard() ); SaveCopyInUndoList(s_PickedList, UR_UNSPECIFIED); s_PickedList.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items OnModify(); return true; }
int PCB_EDIT_FRAME::Begin_Zone( wxDC* DC ) { ZONE_SETTINGS zoneInfo = GetZoneSettings(); // verify if s_CurrentZone exists (could be deleted since last selection) : int ii; for( ii = 0; ii < GetBoard()->GetAreaCount(); ii++ ) { if( s_CurrentZone == GetBoard()->GetArea( ii ) ) break; } if( ii >= GetBoard()->GetAreaCount() ) // Not found: could be deleted since last selection { s_AddCutoutToCurrentZone = false; s_CurrentZone = NULL; } // If no zone contour in progress, a new zone is being created: if( !GetBoard()->m_CurrentZoneContour ) { if( GetToolId() == ID_PCB_KEEPOUT_AREA_BUTT && getActiveLayer() >= FIRST_NON_COPPER_LAYER ) { DisplayError( this, _( "Error: a keepout area is allowed only on copper layers" ) ); return 0; } else GetBoard()->m_CurrentZoneContour = new ZONE_CONTAINER( GetBoard() ); } ZONE_CONTAINER* zone = GetBoard()->m_CurrentZoneContour; if( zone->GetNumCorners() == 0 ) // Start a new contour: init zone params (net, layer ...) { if( !s_CurrentZone ) // A new outline is created, from scratch { ZONE_EDIT_T edited; // Init zone params to reasonable values zone->SetLayer( getActiveLayer() ); // Prompt user for parameters: m_canvas->SetIgnoreMouseEvents( true ); if( zone->IsOnCopperLayer() ) { // Put a zone on a copper layer if( GetBoard()->GetHighLightNetCode() > 0 ) { zoneInfo.m_NetcodeSelection = GetBoard()->GetHighLightNetCode(); zone->SetNet( zoneInfo.m_NetcodeSelection ); zone->SetNetNameFromNetCode( ); } double tmp = ZONE_THERMAL_RELIEF_GAP_MIL; wxGetApp().GetSettings()->Read( ZONE_THERMAL_RELIEF_GAP_STRING_KEY, &tmp ); zoneInfo.m_ThermalReliefGap = KiROUND( tmp * IU_PER_MILS); tmp = ZONE_THERMAL_RELIEF_COPPER_WIDTH_MIL; wxGetApp().GetSettings()->Read( ZONE_THERMAL_RELIEF_COPPER_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ThermalReliefCopperBridge = KiROUND( tmp * IU_PER_MILS ); tmp = ZONE_CLEARANCE_MIL; wxGetApp().GetSettings()->Read( ZONE_CLEARANCE_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ZoneClearance = KiROUND( tmp * IU_PER_MILS ); tmp = ZONE_THICKNESS_MIL; wxGetApp().GetSettings()->Read( ZONE_MIN_THICKNESS_WIDTH_STRING_KEY, &tmp ); zoneInfo.m_ZoneMinThickness = KiROUND( tmp * IU_PER_MILS ); zoneInfo.m_CurrentZone_Layer = zone->GetLayer(); if( GetToolId() == ID_PCB_KEEPOUT_AREA_BUTT ) { zoneInfo.SetIsKeepout( true ); edited = InvokeKeepoutAreaEditor( this, &zoneInfo ); } else { zoneInfo.SetIsKeepout( false ); edited = InvokeCopperZonesEditor( this, &zoneInfo ); } } else // Put a zone on a non copper layer (technical layer) { zoneInfo.SetIsKeepout( false ); zoneInfo.m_NetcodeSelection = 0; // No net for non copper zones edited = InvokeNonCopperZonesEditor( this, zone, &zoneInfo ); } m_canvas->MoveCursorToCrossHair(); m_canvas->SetIgnoreMouseEvents( false ); if( edited == ZONE_ABORT ) return 0; // Switch active layer to the selected zone layer setActiveLayer( zoneInfo.m_CurrentZone_Layer ); SetZoneSettings( zoneInfo ); } else { // Start a new contour: init zone params (net and layer) from an existing // zone (add cutout or similar zone) zoneInfo.m_CurrentZone_Layer = s_CurrentZone->GetLayer(); setActiveLayer( s_CurrentZone->GetLayer() ); zoneInfo << *s_CurrentZone; SetZoneSettings( zoneInfo ); } // Show the Net for zones on copper layers if( zoneInfo.m_CurrentZone_Layer < FIRST_NO_COPPER_LAYER && ! zoneInfo.GetIsKeepout() ) { if( s_CurrentZone ) { zoneInfo.m_NetcodeSelection = s_CurrentZone->GetNet(); GetBoard()->SetZoneSettings( zoneInfo ); } if( GetBoard()->IsHighLightNetON() ) { HighLight( DC ); // Remove old highlight selection } GetBoard()->SetHighLightNet( zoneInfo.m_NetcodeSelection ); HighLight( DC ); } if( !s_AddCutoutToCurrentZone ) s_CurrentZone = NULL; // the zone is used only once ("add similar zone" command) } // if first segment if( zone->GetNumCorners() == 0 ) { zone->SetFlags( IS_NEW ); zone->SetTimeStamp( GetNewTimeStamp() ); zoneInfo.ExportSetting( *zone ); zone->m_Poly->Start( zoneInfo.m_CurrentZone_Layer, GetScreen()->GetCrossHairPosition().x, GetScreen()->GetCrossHairPosition().y, zone->GetHatchStyle() ); zone->AppendCorner( GetScreen()->GetCrossHairPosition() ); if( Drc_On && (m_drc->Drc( zone, 0 ) == BAD_DRC) && zone->IsOnCopperLayer() ) { zone->ClearFlags(); zone->RemoveAllContours(); // use the form of SetCurItem() which does not write to the msg panel, // SCREEN::SetCurItem(), so the DRC error remains on screen. // PCB_EDIT_FRAME::SetCurItem() calls DisplayInfo(). GetScreen()->SetCurItem( NULL ); DisplayError( this, _( "DRC error: this start point is inside or too close an other area" ) ); return 0; } SetCurItem( zone ); m_canvas->SetMouseCapture( Show_New_Edge_While_Move_Mouse, Abort_Zone_Create_Outline ); } else // edge in progress: { ii = zone->GetNumCorners() - 1; // edge in progress : the current corner coordinate was set // by Show_New_Edge_While_Move_Mouse if( zone->GetCornerPosition( ii - 1 ) != zone->GetCornerPosition( ii ) ) { if( !Drc_On || !zone->IsOnCopperLayer() || ( m_drc->Drc( zone, ii - 1 ) == OK_DRC ) ) { // Ok, we can add a new corner if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); zone->AppendCorner( GetScreen()->GetCrossHairPosition() ); SetCurItem( zone ); // calls DisplayInfo(). if( m_canvas->IsMouseCaptured() ) m_canvas->CallMouseCapture( DC, wxPoint(0,0), false ); } } } return zone->GetNumCorners(); }
/** * Function Test_Connection_To_Copper_Areas * init .m_ZoneSubnet parameter in tracks and pads according to the connections to areas found * @param aNetcode = netcode to analyse. if -1, analyse all nets */ void BOARD::Test_Connections_To_Copper_Areas( int aNetcode ) { // list of pads and tracks candidates on this layer and on this net. // It is static to avoid multiple memory realloc. static std::vector <BOARD_CONNECTED_ITEM*> candidates; // clear .m_ZoneSubnet parameter for pads for( MODULE* module = m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) if( aNetcode < 0 || aNetcode == pad->GetNetCode() ) pad->SetZoneSubNet( 0 ); } // clear .m_ZoneSubnet parameter for tracks and vias for( TRACK* track = m_Track; track; track = track->Next() ) { if( aNetcode < 0 || aNetcode == track->GetNetCode() ) track->SetZoneSubNet( 0 ); } // examine all zones, net by net: int subnet = 0; // Build zones candidates list std::vector<ZONE_CONTAINER*> zones_candidates; zones_candidates.reserve( GetAreaCount() ); for( int index = 0; index < GetAreaCount(); index++ ) { ZONE_CONTAINER* zone = GetArea( index ); if( !zone->IsOnCopperLayer() ) continue; if( aNetcode >= 0 && aNetcode != zone->GetNetCode() ) continue; if( zone->GetFilledPolysList().GetCornersCount() == 0 ) continue; zones_candidates.push_back( zone ); } // sort them by netcode then vertices count. // For a given net, examine the smaller zones first slightly speed up calculation // (25% faster) // this is only noticeable with very large boards and depends on board zones topology // This is due to the fact some items are connected by small zones ares, // before examining large zones areas and these items are not tested after a connection is found sort( zones_candidates.begin(), zones_candidates.end(), sort_areas ); int oldnetcode = -1; for( unsigned idx = 0; idx < zones_candidates.size(); idx++ ) { ZONE_CONTAINER* zone = zones_candidates[idx]; int netcode = zone->GetNetCode(); // Build a list of candidates connected to the net: // At this point, layers are not considered, because areas on different layers can // be connected by a via or a pad. // (because zones are sorted by netcode, there is made only once per net) NETINFO_ITEM* net = FindNet( netcode ); wxASSERT( net ); if( net == NULL ) continue; if( oldnetcode != netcode ) { oldnetcode = netcode; candidates.clear(); // Build the list of pads candidates connected to the net: candidates.reserve( net->m_PadInNetList.size() ); for( unsigned ii = 0; ii < net->m_PadInNetList.size(); ii++ ) candidates.push_back( net->m_PadInNetList[ii] ); // Build the list of track candidates connected to the net: TRACK* track = m_Track.GetFirst()->GetStartNetCode( netcode ); for( ; track; track = track->Next() ) { if( track->GetNetCode() != netcode ) break; candidates.push_back( track ); } } // test if a candidate is inside a filled area of this zone unsigned indexstart = 0, indexend; const CPOLYGONS_LIST& polysList = zone->GetFilledPolysList(); for( indexend = 0; indexend < polysList.GetCornersCount(); indexend++ ) { // end of a filled sub-area found if( polysList.IsEndContour( indexend ) ) { subnet++; EDA_RECT bbox = zone->CalculateSubAreaBoundaryBox( indexstart, indexend ); for( unsigned ic = 0; ic < candidates.size(); ic++ ) { // test if this area is connected to a board item: BOARD_CONNECTED_ITEM* item = candidates[ic]; if( item->GetZoneSubNet() == subnet ) // Already merged continue; if( !item->IsOnLayer( zone->GetLayer() ) ) continue; wxPoint pos1, pos2; if( item->Type() == PCB_PAD_T ) { // For pads we use the shape position instead of // the pad position, because the zones are connected // to the center of the shape, not the pad position // (this is important for pads with thermal relief) pos1 = pos2 = ( (D_PAD*) item )->ShapePos(); } else if( item->Type() == PCB_VIA_T ) { const VIA *via = static_cast<const VIA*>( item ); pos1 = via->GetStart(); pos2 = pos1; } else if( item->Type() == PCB_TRACE_T ) { const TRACK *trk = static_cast<const TRACK*>( item ); pos1 = trk->GetStart(); pos2 = trk->GetEnd(); } else { continue; } bool connected = false; if( bbox.Contains( pos1 ) ) { if( TestPointInsidePolygon( polysList, indexstart, indexend, pos1.x, pos1.y ) ) connected = true; } if( !connected && (pos1 != pos2 ) ) { if( bbox.Contains( pos2 ) ) { if( TestPointInsidePolygon( polysList, indexstart, indexend, pos2.x, pos2.y ) ) connected = true; } } if( connected ) { // Set ZoneSubnet to the current subnet value. // If the previous subnet is not 0, merge all items with old subnet // to the new one int old_subnet = item->GetZoneSubNet(); item->SetZoneSubNet( subnet ); // Merge previous subnet with the current if( (old_subnet > 0) && (old_subnet != subnet) ) { for( unsigned jj = 0; jj < candidates.size(); jj++ ) { BOARD_CONNECTED_ITEM* item_to_merge = candidates[jj]; if( old_subnet == item_to_merge->GetZoneSubNet() ) { item_to_merge->SetZoneSubNet( subnet ); } } } // End if ( old_subnet > 0 ) } // End if( connected ) } // End test candidates for the current filled area indexstart = indexend + 1; // prepare test next area, starting at indexend+1 // (if exists). End read one area in // zone->m_FilledPolysList } } // End read all segments in zone } // End read all zones candidates }