static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb ) { double scale = aModel.scale; double x, y; for( int ii = 0; ii < aPcb->GetAreaCount(); ii++ ) { ZONE_CONTAINER* zone = aPcb->GetArea( ii ); VRML_LAYER* vl; if( !GetLayer( aModel, zone->GetLayer(), &vl ) ) continue; if( !zone->IsFilled() ) { zone->SetFillMode( 0 ); // use filled polygons zone->BuildFilledSolidAreasPolygons( aPcb ); } const CPOLYGONS_LIST& poly = zone->GetFilledPolysList(); int nvert = poly.GetCornersCount(); int i = 0; while( i < nvert ) { int seg = vl->NewContour(); bool first = true; if( seg < 0 ) break; while( i < nvert ) { x = poly.GetX(i) * scale; y = -(poly.GetY(i) * scale); if( poly.IsEndContour(i) ) break; if( !vl->AddVertex( seg, x, y ) ) throw( std::runtime_error( vl->GetError() ) ); ++i; } // KiCad ensures that the first polygon is the outline // and all others are holes vl->EnsureWinding( seg, first ? false : true ); if( first ) first = false; ++i; } } }
void ZONE_FILLER::Fill( std::vector<ZONE_CONTAINER*> aZones ) { std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> toFill; auto connectivity = m_board->GetConnectivity(); connectivity->Lock(); // Remove segment zones m_board->m_Zone.DeleteAll(); for( auto zone : aZones ) { // Keepout zones are not filled if( zone->GetIsKeepout() ) continue; CN_ZONE_ISOLATED_ISLAND_LIST l; l.m_zone = zone; toFill.push_back( l ); } for( unsigned i = 0; i < toFill.size(); i++ ) { if( m_commit ) { m_commit->Modify( toFill[i].m_zone ); } } if( m_progressReporter ) { m_progressReporter->Report( _( "Calculating zone fills..." ) ); m_progressReporter->SetMaxProgress( toFill.size() ); } #ifdef USE_OPENMP #pragma omp parallel #endif { #ifdef USE_OPENMP #pragma omp master if( m_progressReporter ) { m_progressReporter->KeepRefreshing( true ); } #endif #ifdef USE_OPENMP #pragma omp for schedule(dynamic) #endif for( unsigned i = 0; i < toFill.size(); i++ ) { SHAPE_POLY_SET rawPolys, finalPolys; ZONE_SEGMENT_FILL segFill; fillSingleZone( toFill[i].m_zone, rawPolys, finalPolys ); toFill[i].m_zone->SetRawPolysList( rawPolys ); toFill[i].m_zone->SetFilledPolysList( finalPolys ); toFill[i].m_zone->SetIsFilled( true ); if( m_progressReporter ) { m_progressReporter->AdvanceProgress(); } } } // Now remove insulated copper islands if( m_progressReporter ) { m_progressReporter->AdvancePhase(); m_progressReporter->Report( _( "Removing insulated copper islands..." ) ); } connectivity->SetProgressReporter( m_progressReporter ); connectivity->FindIsolatedCopperIslands( toFill ); for( auto& zone : toFill ) { std::sort( zone.m_islands.begin(), zone.m_islands.end(), std::greater<int>() ); SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList(); for( auto idx : zone.m_islands ) { poly.DeletePolygon( idx ); } zone.m_zone->SetFilledPolysList( poly ); } if( m_progressReporter ) { m_progressReporter->AdvancePhase(); m_progressReporter->Report( _( "Caching polygon triangulations..." ) ); m_progressReporter->SetMaxProgress( toFill.size() ); } #ifdef USE_OPENMP #pragma omp parallel #endif { #ifdef USE_OPENMP #pragma omp master if( m_progressReporter ) { m_progressReporter->KeepRefreshing( true ); } #endif #ifdef USE_OPENMP #pragma omp for schedule(dynamic) #endif for( unsigned i = 0; i < toFill.size(); i++ ) { if( m_progressReporter ) { m_progressReporter->AdvanceProgress(); } toFill[i].m_zone->CacheTriangulation(); } } // If some zones must be filled by segments, create the filling segments // (note, this is a outdated option, but it exists) int zones_to_fill_count = 0; for( unsigned i = 0; i < toFill.size(); i++ ) { if( toFill[i].m_zone->GetFillMode() == ZFM_SEGMENTS ) zones_to_fill_count++; } if( zones_to_fill_count ) { if( m_progressReporter ) { m_progressReporter->AdvancePhase(); m_progressReporter->Report( _( "Fill with segments..." ) ); m_progressReporter->SetMaxProgress( zones_to_fill_count ); } // TODO: use OPENMP to speedup calculations: for( unsigned i = 0; i < toFill.size(); i++ ) { ZONE_CONTAINER* zone = toFill[i].m_zone; if( zone->GetFillMode() != ZFM_SEGMENTS ) continue; if( m_progressReporter ) { m_progressReporter->AdvanceProgress(); } ZONE_SEGMENT_FILL segFill; fillZoneWithSegments( zone, zone->GetFilledPolysList(), segFill ); toFill[i].m_zone->SetFillSegments( segFill ); } } if( m_progressReporter ) { m_progressReporter->AdvancePhase(); m_progressReporter->Report( _( "Committing changes..." ) ); } connectivity->SetProgressReporter( nullptr ); if( m_commit ) { m_commit->Push( _( "Fill Zone(s)" ), false ); } else { for( unsigned i = 0; i < toFill.size(); i++ ) { connectivity->Update( toFill[i].m_zone ); } connectivity->RecalculateRatsnest(); } connectivity->Unlock(); }
/** * 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 }