/* compare 2 trapezoids (can be rectangle) and return true if distance > aDist * i.e if for each edge of the first polygon distance from each edge of the other polygon * is >= aDist */ bool trapezoid2trapezoidDRC( wxPoint aTref[4], wxPoint aTcompare[4], int aDist ) { /* Test if one polygon is contained in the other and thus the polygon overlap. * This case is not covered by the following check if one polygond is * completely contained in the other (because edges don't intersect)! */ if( TestPointInsidePolygon( aTref, 4, aTcompare[0] ) ) return false; if( TestPointInsidePolygon( aTcompare, 4, aTref[0] ) ) return false; int ii, jj, kk, ll; for( ii = 0, jj = 3; ii<4; jj = ii, ii++ ) // for all edges in aTref { for( kk = 0, ll = 3; kk < 4; ll = kk, kk++ ) // for all edges in aTcompare { double d; int intersect = TestForIntersectionOfStraightLineSegments( aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y, aTcompare[kk].x, aTcompare[kk].y, aTcompare[ll].x, aTcompare[ll].y, NULL, NULL, &d ); if( intersect || (d< aDist) ) return false; } } return true; }
/* compare 2 convex polygons and return true if distance > aDist * i.e if for each edge of the first polygon distance from each edge of the other polygon * is >= aDist */ bool poly2polyDRC( wxPoint* aTref, int aTrefCount, wxPoint* aTcompare, int aTcompareCount, int aDist ) { /* Test if one polygon is contained in the other and thus the polygon overlap. * This case is not covered by the following check if one polygone is * completely contained in the other (because edges don't intersect)! */ if( TestPointInsidePolygon( aTref, aTrefCount, aTcompare[0] ) ) return false; if( TestPointInsidePolygon( aTcompare, aTcompareCount, aTref[0] ) ) return false; for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ ) { // for all edges in aTref for( int kk = 0, ll = aTcompareCount - 1; kk < aTcompareCount; ll = kk, kk++ ) { // for all edges in aTcompare double d; int intersect = TestForIntersectionOfStraightLineSegments( aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y, aTcompare[kk].x, aTcompare[kk].y, aTcompare[ll].x, aTcompare[ll].y, NULL, NULL, &d ); if( intersect || ( d< aDist ) ) return false; } } return true; }
// test to see if a point is inside polyline // bool CPolyLine::TestPointInside( int px, int py ) { if( !GetClosed() ) { wxASSERT( 0 ); } // Test all polygons. // Since the first is the main outline, and other are holes, // if the tested point is inside only one contour, it is inside the whole polygon // (in fact inside the main outline, and outside all holes). // if inside 2 contours (the main outline + an hole), it is outside the poly. int polycount = GetContoursCount(); bool inside = false; for( int icont = 0; icont < polycount; icont++ ) { int istart = GetContourStart( icont ); int iend = GetContourEnd( icont ); // test point inside the current polygon if( TestPointInsidePolygon( m_CornersList, istart, iend, px, py ) ) inside = not inside; } return inside; }
/* compare a trapezoids (can be rectangle) and a segment and return true if distance > aDist */ bool trapezoid2segmentDRC( wxPoint aTref[4], wxPoint aSegStart, wxPoint aSegEnd, int aDist ) { /* Test if the segment is contained in the polygon. * This case is not covered by the following check if the segment is * completely contained in the polygon (because edges don't intersect)! */ if( TestPointInsidePolygon( aTref, 4, aSegStart ) ) return false; int ii, jj; for( ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edges in aTref { double d; int intersect = TestForIntersectionOfStraightLineSegments( aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y, aSegStart.x, aSegStart.y, aSegEnd.x, aSegEnd.y, NULL, NULL, &d ); if( intersect || (d< aDist) ) return false; } return true; }
/* compare a trapezoid to a point and return true if distance > aDist * do not use this function for horizontal or vertical rectangles * because there is a faster an easier way to compare the distance */ bool trapezoid2pointDRC( wxPoint aTref[4], wxPoint aPcompare, int aDist ) { /* Test if aPcompare point is contained in the polygon. * This case is not covered by the following check if this point is inside the polygon */ if( TestPointInsidePolygon( aTref, 4, aPcompare ) ) { return false; } // Test distance between aPcompare and polygon edges: int ii, jj; double dist = (double) aDist; for( ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edges in polygon { if( TestLineHit( aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y, aPcompare.x, aPcompare.y, dist ) ) return false; } return true; }
bool D_PAD::HitTest( const wxPoint& aPosition ) { int dx, dy; double dist; wxPoint shape_pos = ReturnShapePos(); wxPoint delta = aPosition - shape_pos; // first test: a test point must be inside a minimum sized bounding circle. int radius = GetBoundingRadius(); if( ( abs( delta.x ) > radius ) || ( abs( delta.y ) > radius ) ) return false; dx = m_Size.x >> 1; // dx also is the radius for rounded pads dy = m_Size.y >> 1; switch( m_PadShape & 0x7F ) { case PAD_CIRCLE: dist = hypot( delta.x, delta.y ); if( KiROUND( dist ) <= dx ) return true; break; case PAD_TRAPEZOID: { wxPoint poly[4]; BuildPadPolygon( poly, wxSize(0,0), 0 ); RotatePoint( &delta, -m_Orient ); return TestPointInsidePolygon( poly, 4, delta ); } default: RotatePoint( &delta, -m_Orient ); if( (abs( delta.x ) <= dx ) && (abs( delta.y ) <= dy) ) return true; break; } return false; }
/* compare a polygon to a point and return true if distance > aDist * do not use this function for horizontal or vertical rectangles * because there is a faster an easier way to compare the distance */ bool convex2pointDRC( wxPoint* aTref, int aTrefCount, wxPoint aPcompare, int aDist ) { /* Test if aPcompare point is contained in the polygon. * This case is not covered by the following check if this point is inside the polygon */ if( TestPointInsidePolygon( aTref, aTrefCount, aPcompare ) ) { return false; } // Test distance between aPcompare and each segment of the polygon: for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ ) // for all edge in polygon { if( TestSegmentHit( aPcompare, aTref[ii], aTref[jj], aDist ) ) return false; } return true; }
bool ZONE_CONTAINER::HitTestFilledArea( const wxPoint& aRefPos ) const { unsigned indexstart = 0, indexend; bool inside = false; for( indexend = 0; indexend < m_FilledPolysList.GetCornersCount(); indexend++ ) { if( m_FilledPolysList.IsEndContour( indexend ) ) // end of a filled sub-area found { if( TestPointInsidePolygon( m_FilledPolysList, indexstart, indexend, aRefPos.x, aRefPos.y ) ) { inside = true; break; } // Prepare test of next area which starts after the current index end (if exists) indexstart = indexend + 1; } } return inside; }
bool D_PAD::HitTest( const wxPoint& aPosition ) const { int dx, dy; wxPoint shape_pos = ShapePos(); wxPoint delta = aPosition - shape_pos; // first test: a test point must be inside a minimum sized bounding circle. int radius = GetBoundingRadius(); if( ( abs( delta.x ) > radius ) || ( abs( delta.y ) > radius ) ) return false; dx = m_Size.x >> 1; // dx also is the radius for rounded pads dy = m_Size.y >> 1; switch( GetShape() ) { case PAD_SHAPE_CIRCLE: if( KiROUND( EuclideanNorm( delta ) ) <= dx ) return true; break; case PAD_SHAPE_TRAPEZOID: { wxPoint poly[4]; BuildPadPolygon( poly, wxSize(0,0), 0 ); RotatePoint( &delta, -m_Orient ); return TestPointInsidePolygon( poly, 4, delta ); } case PAD_SHAPE_OVAL: { RotatePoint( &delta, -m_Orient ); // An oval pad has the same shape as a segment with rounded ends // After rotation, the test point is relative to an horizontal pad int dist; wxPoint offset; if( dy > dx ) // shape is a vertical oval { offset.y = dy - dx; dist = dx; } else //if( dy <= dx ) shape is an horizontal oval { offset.x = dy - dx; dist = dy; } return TestSegmentHit( delta, - offset, offset, dist ); } break; case PAD_SHAPE_RECT: RotatePoint( &delta, -m_Orient ); if( (abs( delta.x ) <= dx ) && (abs( delta.y ) <= dy) ) return true; break; } return false; }
bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const { EDA_RECT arect = aRect; arect.Normalize(); arect.Inflate( aAccuracy ); wxPoint shapePos = ShapePos(); EDA_RECT shapeRect; int r; EDA_RECT bb = GetBoundingBox(); wxPoint endCenter; int radius; if( !arect.Intersects( bb ) ) return false; // This covers total containment for all test cases if( arect.Contains( bb ) ) return true; switch( GetShape() ) { case PAD_SHAPE_CIRCLE: return arect.IntersectsCircle( GetPosition(), GetBoundingRadius() ); case PAD_SHAPE_RECT: shapeRect.SetOrigin( shapePos ); shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 ); return arect.Intersects( shapeRect, m_Orient ); case PAD_SHAPE_OVAL: // Circlular test if dimensions are equal if( m_Size.x == m_Size.y ) return arect.IntersectsCircle( shapePos, GetBoundingRadius() ); shapeRect.SetOrigin( shapePos ); // Horizontal dimension is greater if( m_Size.x > m_Size.y ) { radius = m_Size.y / 2; shapeRect.Inflate( m_Size.x / 2 - radius, radius ); endCenter = wxPoint( m_Size.x / 2 - radius, 0 ); RotatePoint( &endCenter, m_Orient ); // Test circular ends if( arect.IntersectsCircle( shapePos + endCenter, radius ) || arect.IntersectsCircle( shapePos - endCenter, radius ) ) { return true; } } else { radius = m_Size.x / 2; shapeRect.Inflate( radius, m_Size.y / 2 - radius ); endCenter = wxPoint( 0, m_Size.y / 2 - radius ); RotatePoint( &endCenter, m_Orient ); // Test circular ends if( arect.IntersectsCircle( shapePos + endCenter, radius ) || arect.IntersectsCircle( shapePos - endCenter, radius ) ) { return true; } } // Test rectangular portion between rounded ends if( arect.Intersects( shapeRect, m_Orient ) ) { return true; } break; case PAD_SHAPE_TRAPEZOID: /* Trapezoid intersection tests: * A) Any points of rect inside trapezoid * B) Any points of trapezoid inside rect * C) Any sides of trapezoid cross rect */ { wxPoint poly[4]; BuildPadPolygon( poly, wxSize( 0, 0 ), 0 ); wxPoint corners[4]; corners[0] = wxPoint( arect.GetLeft(), arect.GetTop() ); corners[1] = wxPoint( arect.GetRight(), arect.GetTop() ); corners[2] = wxPoint( arect.GetRight(), arect.GetBottom() ); corners[3] = wxPoint( arect.GetLeft(), arect.GetBottom() ); for( int i=0; i<4; i++ ) { RotatePoint( &poly[i], m_Orient ); poly[i] += shapePos; } for( int ii=0; ii<4; ii++ ) { if( TestPointInsidePolygon( poly, 4, corners[ii] ) ) { return true; } if( arect.Contains( poly[ii] ) ) { return true; } if( arect.Intersects( poly[ii], poly[(ii+1) % 4] ) ) { return true; } } return false; } case PAD_SHAPE_ROUNDRECT: /* RoundRect intersection can be broken up into simple tests: * a) Test intersection of horizontal rect * b) Test intersection of vertical rect * c) Test intersection of each corner */ r = GetRoundRectCornerRadius(); /* Test A - intersection of horizontal rect */ shapeRect.SetSize( 0, 0 ); shapeRect.SetOrigin( shapePos ); shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 - r ); // Short-circuit test for zero width or height if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 && arect.Intersects( shapeRect, m_Orient ) ) { return true; } /* Test B - intersection of vertical rect */ shapeRect.SetSize( 0, 0 ); shapeRect.SetOrigin( shapePos ); shapeRect.Inflate( m_Size.x / 2 - r, m_Size.y / 2 ); // Short-circuit test for zero width or height if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 && arect.Intersects( shapeRect, m_Orient ) ) { return true; } /* Test C - intersection of each corner */ endCenter = wxPoint( m_Size.x / 2 - r, m_Size.y / 2 - r ); RotatePoint( &endCenter, m_Orient ); if( arect.IntersectsCircle( shapePos + endCenter, r ) || arect.IntersectsCircle( shapePos - endCenter, r ) ) { return true; } endCenter = wxPoint( m_Size.x / 2 - r, -m_Size.y / 2 + r ); RotatePoint( &endCenter, m_Orient ); if( arect.IntersectsCircle( shapePos + endCenter, r ) || arect.IntersectsCircle( shapePos - endCenter, r ) ) { return true; } break; default: break; } return false; }
bool D_PAD::HitTest( const wxPoint& aPosition ) const { int dx, dy; wxPoint shape_pos = ShapePos(); wxPoint delta = aPosition - shape_pos; // first test: a test point must be inside a minimum sized bounding circle. int radius = GetBoundingRadius(); if( ( abs( delta.x ) > radius ) || ( abs( delta.y ) > radius ) ) return false; dx = m_Size.x >> 1; // dx also is the radius for rounded pads dy = m_Size.y >> 1; switch( GetShape() ) { case PAD_SHAPE_CIRCLE: if( KiROUND( EuclideanNorm( delta ) ) <= dx ) return true; break; case PAD_SHAPE_TRAPEZOID: { wxPoint poly[4]; BuildPadPolygon( poly, wxSize(0,0), 0 ); RotatePoint( &delta, -m_Orient ); return TestPointInsidePolygon( poly, 4, delta ); } case PAD_SHAPE_OVAL: { RotatePoint( &delta, -m_Orient ); // An oval pad has the same shape as a segment with rounded ends // After rotation, the test point is relative to an horizontal pad int dist; wxPoint offset; if( dy > dx ) // shape is a vertical oval { offset.y = dy - dx; dist = dx; } else //if( dy <= dx ) shape is an horizontal oval { offset.x = dy - dx; dist = dy; } return TestSegmentHit( delta, - offset, offset, dist ); } break; case PAD_SHAPE_RECT: RotatePoint( &delta, -m_Orient ); if( (abs( delta.x ) <= dx ) && (abs( delta.y ) <= dy) ) return true; break; case PAD_SHAPE_ROUNDRECT: { // Check for hit in polygon SHAPE_POLY_SET outline; const int segmentToCircleCount = 32; TransformRoundRectToPolygon( outline, wxPoint(0,0), GetSize(), m_Orient, GetRoundRectCornerRadius(), segmentToCircleCount ); const SHAPE_LINE_CHAIN &poly = outline.COutline( 0 ); return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta ); } break; case PAD_SHAPE_CUSTOM: // Check for hit in polygon RotatePoint( &delta, -m_Orient ); if( m_customShapeAsPolygon.OutlineCount() ) { const SHAPE_LINE_CHAIN& poly = m_customShapeAsPolygon.COutline( 0 ); return TestPointInsidePolygon( (const wxPoint*)&poly.CPoint(0), poly.PointCount(), delta ); } break; } return false; }
void ZONE_CONTAINER::TestForCopperIslandAndRemoveInsulatedIslands( BOARD* aPcb ) { if( m_FilledPolysList.GetCornersCount() == 0 ) return; // Build a list of points connected to the net: // list of coordinates of pads and vias on this layer and on this net. std::vector <wxPoint> listPointsCandidates; for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) { for( D_PAD* pad = module->Pads(); pad != NULL; pad = pad->Next() ) { if( !pad->IsOnLayer( GetLayer() ) ) continue; if( pad->GetNet() != GetNet() ) continue; listPointsCandidates.push_back( pad->GetPosition() ); } } for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) { if( !track->IsOnLayer( GetLayer() ) ) continue; if( track->GetNet() != GetNet() ) continue; listPointsCandidates.push_back( track->GetStart() ); if( track->Type() != PCB_VIA_T ) listPointsCandidates.push_back( track->GetEnd() ); } // test if a point is inside unsigned indexstart = 0, indexend; bool connected = false; for( indexend = 0; indexend < m_FilledPolysList.GetCornersCount(); indexend++ ) { if( m_FilledPolysList[indexend].end_contour ) // end of a filled sub-area found { EDA_RECT bbox = CalculateSubAreaBoundaryBox( indexstart, indexend ); for( unsigned ic = 0; ic < listPointsCandidates.size(); ic++ ) { // test if this area is connected to a board item: wxPoint pos = listPointsCandidates[ic]; if( !bbox.Contains( pos ) ) continue; if( TestPointInsidePolygon( m_FilledPolysList, indexstart, indexend, pos.x, pos.y ) ) { connected = true; break; } } if( connected ) // this polygon is connected: analyse next polygon { indexstart = indexend + 1; // indexstart points the first point of the next polygon connected = false; } else // Not connected: remove this polygon { m_FilledPolysList.DeleteCorners( indexstart, indexend ); indexend = indexstart; /* indexstart points the first point of the next polygon * because the current poly is removed */ } } } }
/** * 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 }