zones_polygons_test_connections.cpp 13.2 KB
Newer Older
1 2 3
/**
 * @file zones_polygons_test_connections.cpp
 */
4 5

using namespace std;
6

7 8 9
#include <algorithm> // sort


10 11 12
#include <fctsys.h>
#include <common.h>
#include <macros.h>
13

14 15 16 17
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <class_zone.h>
18

19 20
#include <pcbnew.h>
#include <zones.h>
21 22 23 24

static bool CmpZoneSubnetValue( const BOARD_CONNECTED_ITEM* a, const BOARD_CONNECTED_ITEM* b );
void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode );

25 26 27 28 29 30 31 32 33 34
// This helper function sort a list of zones by netcode,
// and for a given netcode by zone size
// zone size = size of the m_FilledPolysList buffer
bool sort_areas( const ZONE_CONTAINER* ref, const ZONE_CONTAINER* tst )
{
    if( ref->GetNet() == tst->GetNet() )
        return ref->m_FilledPolysList.size() < tst->m_FilledPolysList.size();
    else
        return ref->GetNet() < tst->GetNet();
}
35 36 37 38 39 40

/**
 * 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
 */
41
void BOARD::Test_Connections_To_Copper_Areas( int aNetcode )
42
{
43
    // list of pads and tracks candidates on this layer and on this net.
44 45 46
    // It is static to avoid multiple memory realloc.
    static std::vector <BOARD_CONNECTED_ITEM*> Candidates;

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
    // clear .m_ZoneSubnet parameter for pads
    for( MODULE* module = m_Modules;  module;  module = module->Next() )
    {
        for( D_PAD* pad = module->m_Pads; pad != NULL; pad = pad->Next() )
            if( (aNetcode < 0) || ( aNetcode == pad->GetNet() ) )
                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->GetNet() ) )
            track->SetZoneSubNet( 0 );
    }

    // examine all zones, net by net:
63
    int subnet = 0;
64 65 66

    // Build zones candidates list
    std::vector<ZONE_CONTAINER*> zones_candidates;
67 68
    for( int index = 0; index < GetAreaCount(); index++ )
    {
69
        ZONE_CONTAINER* curr_zone = GetArea( index );
70 71
        if( !curr_zone->IsOnCopperLayer() )
            continue;
72
        if( (aNetcode >= 0) && ( aNetcode != curr_zone->GetNet() ) )
73 74 75
            continue;
        if( curr_zone->m_FilledPolysList.size() == 0 )
            continue;
76 77 78 79 80 81 82 83 84 85 86 87 88 89
        zones_candidates.push_back(curr_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 bt 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* curr_zone = zones_candidates[idx];
90

91
        int netcode = curr_zone->GetNet();
92

93
        // Build a list of candidates connected to the net:
94 95
        // At this point, layers are not considered, because areas on different layers can
        // be connected by a via or a pad.
96
        // (because zones are sorted by netcode, there is made only once per net)
97 98 99 100
        NETINFO_ITEM* net = FindNet( netcode );
        wxASSERT( net );
        if( net == NULL )
            continue;
101
        if( oldnetcode != netcode )
102
        {
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
            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->GetNet() != netcode )
                    break;
                Candidates.push_back( track );
            }
119 120 121 122 123 124
        }

        // test if a candidate is inside a filled area of this zone
        unsigned indexstart = 0, indexend;
        for( indexend = 0; indexend < curr_zone->m_FilledPolysList.size(); indexend++ )
        {
125 126
            // end of a filled sub-area found
            if( curr_zone->m_FilledPolysList[indexend].end_contour )
127 128
            {
                subnet++;
129
                EDA_RECT bbox = curr_zone->CalculateSubAreaBoundaryBox( indexstart, indexend );
130

131 132
                for( unsigned ic = 0; ic < Candidates.size(); ic++ )
                { // test if this area is connected to a board item:
133
                    BOARD_CONNECTED_ITEM* item = Candidates[ic];
134

135 136 137
                    if( item->GetZoneSubNet() == subnet )   // Already merged
                        continue;

138 139
                    if( !item->IsOnLayer( curr_zone->GetLayer() ) )
                        continue;
140

141
                    wxPoint pos1, pos2;
142

143
                    if( item->Type() == PCB_PAD_T )
144
                    {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
145
                        pos1 = pos2 = ( (D_PAD*) item )->GetPosition();
146
                    }
147
                    else if( item->Type() == PCB_VIA_T )
148 149 150
                    {
                        pos1 = pos2 = ( (SEGVIA*) item )->m_Start;
                    }
151
                    else if( item->Type() == PCB_TRACE_T )
152 153 154 155 156
                    {
                        pos1 = ( (TRACK*) item )->m_Start;
                        pos2 = ( (TRACK*) item )->m_End;
                    }
                    else
157
                    {
158
                        continue;
159 160
                    }

161
                    bool connected = false;
162

163
                    if( bbox.Contains( pos1 ) )
164 165 166 167 168 169 170
                    {
                        if( TestPointInsidePolygon( curr_zone->m_FilledPolysList, indexstart,
                                                    indexend, pos1.x, pos1.y ) )
                            connected = true;
                    }
                    if( !connected && (pos1 != pos2 ) )
                    {
171
                        if( bbox.Contains( pos2 ) )
172 173 174 175
                        {
                            if( TestPointInsidePolygon( curr_zone->m_FilledPolysList,
                                                        indexstart, indexend,
                                                        pos2.x, pos2.y ) )
176
                                connected = true;
177
                        }
178 179 180 181
                    }

                    if( connected )
                    {   // Set ZoneSubnet to the current subnet value.
182 183
                        // If the previous subnet is not 0, merge all items with old subnet
                        // to the new one
184
                        int old_subnet = item->GetZoneSubNet();
185 186
                        item->SetZoneSubNet( subnet );

187 188
                        // Merge previous subnet with the current
                        if( (old_subnet > 0) && (old_subnet != subnet) )
189 190 191 192 193 194 195 196 197 198 199 200 201
                        {
                            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
202 203 204 205
                indexstart = indexend + 1;  // prepare test next area, starting at indexend+1
                                            // (if exists).  End read one area in
                                            // curr_zone->m_FilledPolysList
            }
206 207
        } // End read all segments in zone
    } // End read all zones candidates
208 209 210
}


211 212
/**
 * Function Merge_SubNets_Connected_By_CopperAreas(BOARD* aPcb)
213 214
 * Calls Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode ) for each
 * netcode found in zone list
215 216
 * @param aPcb = the current board
 */
217
void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb )
218 219 220 221
{
    for( int index = 0; index < aPcb->GetAreaCount(); index++ )
    {
        ZONE_CONTAINER* curr_zone = aPcb->GetArea( index );
222

223 224
        if ( ! curr_zone->IsOnCopperLayer() )
            continue;
225

226 227
        if ( curr_zone->GetNet() <= 0 )
            continue;
228

229 230 231 232 233
        Merge_SubNets_Connected_By_CopperAreas( aPcb, curr_zone->GetNet() );
    }
}


234 235
/**
 * Function Merge_SubNets_Connected_By_CopperAreas(BOARD* aPcb, int aNetcode)
236 237 238 239 240 241 242 243 244 245
 * Used after connections by tracks calculations
 * Merge subnets, in tracks ans pads when they are connected by a filled copper area
 * for pads, this is the .m_physical_connexion member which is tested and modified
 * for tracks, this is the .m_Subnet member which is tested and modified
 * these members are block numbers (or cluster numbers) for a given net,
 * calculated by Build_Pads_Info_Connections_By_Tracks()
 * The result is merging 2 blocks (or subnets)
 * @param aPcb = the current board
 * @param aNetcode = netcode to consider
 */
246
void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode )
247 248 249 250 251 252 253
{
    // Ensure a zone with the given netcode exists: examine all zones:
    bool found = false;

    for( int index = 0; index < aPcb->GetAreaCount(); index++ )
    {
        ZONE_CONTAINER* curr_zone = aPcb->GetArea( index );
254

255 256 257 258 259 260 261 262 263 264
        if( aNetcode == curr_zone->GetNet() )
        {
            found = true;
            break;
        }
    }

    if( !found )  // No zone with this netcode, therefore no connection by zone
        return;

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
    // list of pads and tracks candidates to test:
    // It is static to avoid multiple memory realloc.
    static std::vector <BOARD_CONNECTED_ITEM*> Candidates;
    Candidates.clear();

    // Build the list of pads candidates connected to the net:
    NETINFO_ITEM* net = aPcb->FindNet( aNetcode );
    wxASSERT( 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;
    track = aPcb->m_Track.GetFirst()->GetStartNetCode( aNetcode );
    for( ; track; track = track->Next() )
281
    {
282 283 284
        if( track->GetNet() != aNetcode )
            break;
        Candidates.push_back( track );
285 286 287 288 289
    }

    if( Candidates.size() == 0 )
        return;

290 291 292 293 294 295 296
    int next_subnet_free_number = 0;
    for( unsigned ii = 0; ii < Candidates.size(); ii++ )
    {
        int subnet = Candidates[ii]->GetSubNet();
        next_subnet_free_number = MAX( next_subnet_free_number, subnet );
    }

297 298
    next_subnet_free_number++;     // This is a subnet we can use with not connected items
                                   // by tracks, but connected by zone.
299 300 301 302 303

    // Sort by zone_subnet:
    sort( Candidates.begin(), Candidates.end(), CmpZoneSubnetValue );

    // Some items can be not connected, but they can be connected to a filled area:
304 305
    // give them a subnet common to these items connected only by the area,
    // and not already used.
306 307 308
    // a value like next_subnet_free_number+zone_subnet is right
    for( unsigned jj = 0; jj < Candidates.size(); jj++ )
    {
309
        BOARD_CONNECTED_ITEM* item = Candidates[jj];
310 311 312 313 314 315 316 317
        if ( item->GetSubNet() == 0 && (item->GetZoneSubNet() > 0) )
        {
            item->SetSubNet( next_subnet_free_number + item->GetZoneSubNet() );
        }
    }

    // Now, for each zone subnet, we search for 2 items with different subnets.
    // if found, the 2 subnet are merged in the whole candidate list.
318 319
    int old_subnet      = 0;
    int old_zone_subnet = 0;
320 321
    for( unsigned ii = 0; ii < Candidates.size(); ii++ )
    {
322 323
        BOARD_CONNECTED_ITEM* item = Candidates[ii];
        int zone_subnet = item->GetZoneSubNet();
324 325 326 327

        if( zone_subnet == 0 )  // Not connected by a filled area, skip it
            continue;

328
        int subnet = item->GetSubNet();
329 330 331 332 333 334 335 336 337 338

        if( zone_subnet != old_zone_subnet )  // a new zone subnet is found
        {
            old_subnet = subnet;
            old_zone_subnet = zone_subnet;
            continue;
        }

        zone_subnet = old_zone_subnet;

339 340
        // 2 successive items already from the same cluster: nothing to do
        if( subnet == old_subnet )
341 342 343 344 345 346 347 348 349
            continue;

        // Here we have 2 items connected by the same area have 2 differents subnets: merge subnets
        if( (subnet > old_subnet) || ( subnet <= 0) )
            EXCHG( subnet, old_subnet );

        for( unsigned jj = 0; jj < Candidates.size(); jj++ )
        {
            BOARD_CONNECTED_ITEM * item_to_merge = Candidates[jj];
350

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
            if( item_to_merge->GetSubNet() == old_subnet )
                item_to_merge->SetSubNet( subnet );
        }

        old_subnet = subnet;
    }
}


/* Compare function used for sorting candidates  by increasing zone zubnet
 */
static bool CmpZoneSubnetValue( const BOARD_CONNECTED_ITEM* a, const BOARD_CONNECTED_ITEM* b )
{
    int asubnet, bsubnet;

    asubnet = a->GetZoneSubNet();
    bsubnet = b->GetZoneSubNet();

    return asubnet < bsubnet;
}