bool GERBER_DRAW_ITEM::HitTest( const wxPoint& aRefPos ) const { // In case the item has a very tiny width defined, allow it to be selected const int MIN_HIT_TEST_RADIUS = Millimeter2iu( 0.01 ); // calculate aRefPos in XY gerber axis: wxPoint ref_pos = GetXYPosition( aRefPos ); SHAPE_POLY_SET poly; switch( m_Shape ) { case GBR_POLYGON: poly = m_Polygon; return poly.Contains( VECTOR2I( ref_pos ), 0 ); case GBR_SPOT_POLY: poly = GetDcodeDescr()->m_Polygon; poly.Move( m_Start ); return poly.Contains( VECTOR2I( ref_pos ), 0 ); case GBR_SPOT_RECT: return GetBoundingBox().Contains( aRefPos ); case GBR_ARC: { double radius = GetLineLength( m_Start, m_ArcCentre ); VECTOR2D test_radius = VECTOR2D( ref_pos ) - VECTOR2D( m_ArcCentre ); int size = ( ( m_Size.x < MIN_HIT_TEST_RADIUS ) ? MIN_HIT_TEST_RADIUS : m_Size.x ); // Are we close enough to the radius? bool radius_hit = ( std::fabs( test_radius.EuclideanNorm() - radius) < size ); if( radius_hit ) { // Now check that we are within the arc angle VECTOR2D start = VECTOR2D( m_Start ) - VECTOR2D( m_ArcCentre ); VECTOR2D end = VECTOR2D( m_End ) - VECTOR2D( m_ArcCentre ); double start_angle = NormalizeAngleRadiansPos( start.Angle() ); double end_angle = NormalizeAngleRadiansPos( end.Angle() ); if( m_Start == m_End ) { start_angle = 0; end_angle = 2 * M_PI; } else if( end_angle < start_angle ) { end_angle += 2 * M_PI; } double test_angle = NormalizeAngleRadiansPos( test_radius.Angle() ); return ( test_angle > start_angle && test_angle < end_angle ); } return false; } case GBR_SPOT_MACRO: // Aperture macro polygons are already in absolute coordinates auto p = GetDcodeDescr()->GetMacro()->GetApertureMacroShape( this, m_Start ); for( int i = 0; i < p->OutlineCount(); ++i ) { if( p->Contains( VECTOR2I( aRefPos ), i ) ) return true; } return false; } // TODO: a better analyze of the shape (perhaps create a D_CODE::HitTest for flashed items) int radius = std::min( m_Size.x, m_Size.y ) >> 1; if( radius < MIN_HIT_TEST_RADIUS ) radius = MIN_HIT_TEST_RADIUS; if( m_Flashed ) return HitTestPoints( m_Start, ref_pos, radius ); else return TestSegmentHit( ref_pos, m_Start, m_End, radius ); }
bool D_PAD::GetBestAnchorPosition( VECTOR2I& aPos ) { SHAPE_POLY_SET poly; if( !buildCustomPadPolygon( &poly, ARC_LOW_DEF ) ) return false; const int minSteps = 10; const int maxSteps = 50; int stepsX, stepsY; auto bbox = poly.BBox(); if( bbox.GetWidth() < bbox.GetHeight() ) { stepsX = minSteps; stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1); } else { stepsY = minSteps; stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1); } stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) ); stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) ); auto center = bbox.Centre(); auto minDist = std::numeric_limits<int64_t>::max(); int64_t minDistEdge; if( GetAnchorPadShape() == PAD_SHAPE_CIRCLE ) { minDistEdge = GetSize().x; } else { minDistEdge = std::max( GetSize().x, GetSize().y ); } OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() ); for ( int y = 0; y < stepsY ; y++ ) { for ( int x = 0; x < stepsX; x++ ) { VECTOR2I p = bbox.GetPosition(); p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) ); p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) ); if ( poly.Contains(p) ) { auto dist = (center - p).EuclideanNorm(); auto distEdge = poly.COutline(0).Distance( p, true ); if ( distEdge >= minDistEdge ) { if ( dist < minDist ) { bestAnchor = p; minDist = dist; } } } } } if ( bestAnchor ) { aPos = *bestAnchor; return true; } return false; }
void ZONE_FILLER::buildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, const ZONE_CONTAINER* aZone, const SHAPE_POLY_SET& aRawFilledArea, double aArcCorrection, double aRoundPadThermalRotation ) const { SHAPE_LINE_CHAIN spokes; BOX2I itemBB; VECTOR2I ptTest[4]; auto zoneBB = aRawFilledArea.BBox(); int zone_clearance = aZone->GetZoneClearance(); int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zoneBB.Inflate( biggest_clearance ); // half size of the pen used to draw/plot zones outlines int pen_radius = aZone->GetMinThickness() / 2; for( auto module : m_board->Modules() ) { for( auto pad : module->Pads() ) { // Rejects non-standard pads with tht-only thermal reliefs if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( aZone->GetLayer() ) ) continue; if( pad->GetNetCode() != aZone->GetNetCode() ) continue; // Calculate thermal bridge half width int thermalBridgeWidth = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( thermalBridgeWidth <= 0 ) continue; // we need the thermal bridge half width // with a small extra size to be sure we create a stub // slightly larger than the actual stub thermalBridgeWidth = ( thermalBridgeWidth + 4 ) / 2; int thermalReliefGap = aZone->GetThermalReliefGap( pad ); itemBB = pad->GetBoundingBox(); itemBB.Inflate( thermalReliefGap ); if( !( itemBB.Intersects( zoneBB ) ) ) continue; // Thermal bridges are like a segment from a starting point inside the pad // to an ending point outside the pad // calculate the ending point of the thermal pad, outside the pad VECTOR2I endpoint; endpoint.x = ( pad->GetSize().x / 2 ) + thermalReliefGap; endpoint.y = ( pad->GetSize().y / 2 ) + thermalReliefGap; // Calculate the starting point of the thermal stub // inside the pad VECTOR2I startpoint; int copperThickness = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( copperThickness < 0 ) copperThickness = 0; // Leave a small extra size to the copper area inside to pad copperThickness += KiROUND( IU_PER_MM * 0.04 ); startpoint.x = std::min( pad->GetSize().x, copperThickness ); startpoint.y = std::min( pad->GetSize().y, copperThickness ); startpoint.x /= 2; startpoint.y /= 2; // This is a CIRCLE pad tweak // for circle pads, the thermal stubs orientation is 45 deg double fAngle = pad->GetOrientation(); if( pad->GetShape() == PAD_SHAPE_CIRCLE ) { endpoint.x = KiROUND( endpoint.x * aArcCorrection ); endpoint.y = endpoint.x; fAngle = aRoundPadThermalRotation; } // contour line width has to be taken into calculation to avoid "thermal stub bleed" endpoint.x += pen_radius; endpoint.y += pen_radius; // compute north, south, west and east points for zone connection. ptTest[0] = VECTOR2I( 0, endpoint.y ); // lower point ptTest[1] = VECTOR2I( 0, -endpoint.y ); // upper point ptTest[2] = VECTOR2I( endpoint.x, 0 ); // right point ptTest[3] = VECTOR2I( -endpoint.x, 0 ); // left point // Test all sides for( int i = 0; i < 4; i++ ) { // rotate point RotatePoint( ptTest[i], fAngle ); // translate point ptTest[i] += pad->ShapePos(); if( aRawFilledArea.Contains( ptTest[i] ) ) continue; spokes.Clear(); // polygons are rectangles with width of copper bridge value switch( i ) { case 0: // lower stub spokes.Append( -thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, startpoint.y ); spokes.Append( -thermalBridgeWidth, startpoint.y ); break; case 1: // upper stub spokes.Append( -thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -startpoint.y ); spokes.Append( -thermalBridgeWidth, -startpoint.y ); break; case 2: // right stub spokes.Append( endpoint.x, -thermalBridgeWidth ); spokes.Append( endpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, -thermalBridgeWidth ); break; case 3: // left stub spokes.Append( -endpoint.x, -thermalBridgeWidth ); spokes.Append( -endpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, -thermalBridgeWidth ); break; } aCornerBuffer.NewOutline(); // add computed polygon to list for( int ic = 0; ic < spokes.PointCount(); ic++ ) { auto cpos = spokes.CPoint( ic ); RotatePoint( cpos, fAngle ); // Rotate according to module orientation cpos += pad->ShapePos(); // Shift origin to position aCornerBuffer.Append( cpos ); } } } } }
int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_Examine, bool aCreate_Markers ) { int nerrors = 0; // iterate through all areas for( int ia = 0; ia < GetAreaCount(); ia++ ) { ZONE_CONTAINER* Area_Ref = GetArea( ia ); SHAPE_POLY_SET* refSmoothedPoly = Area_Ref->GetSmoothedPoly(); if( !Area_Ref->IsOnCopperLayer() ) continue; // When testing only a single area, skip all others if( aArea_To_Examine && (aArea_To_Examine != Area_Ref) ) continue; for( int ia2 = 0; ia2 < GetAreaCount(); ia2++ ) { ZONE_CONTAINER* area_to_test = GetArea( ia2 ); SHAPE_POLY_SET* testSmoothedPoly = area_to_test->GetSmoothedPoly(); if( Area_Ref == area_to_test ) continue; // test for same layer if( Area_Ref->GetLayer() != area_to_test->GetLayer() ) continue; // Test for same net if( Area_Ref->GetNetCode() == area_to_test->GetNetCode() && Area_Ref->GetNetCode() >= 0 ) continue; // test for different priorities if( Area_Ref->GetPriority() != area_to_test->GetPriority() ) continue; // test for different types if( Area_Ref->GetIsKeepout() != area_to_test->GetIsKeepout() ) continue; // Examine a candidate zone: compare area_to_test to Area_Ref // Get clearance used in zone to zone test. The policy used to // obtain that value is now part of the zone object itself by way of // ZONE_CONTAINER::GetClearance(). int zone2zoneClearance = Area_Ref->GetClearance( area_to_test ); // Keepout areas have no clearance, so set zone2zoneClearance to 1 // ( zone2zoneClearance = 0 can create problems in test functions) if( Area_Ref->GetIsKeepout() ) zone2zoneClearance = 1; // test for some corners of Area_Ref inside area_to_test for( auto iterator = refSmoothedPoly->IterateWithHoles(); iterator; iterator++ ) { VECTOR2I currentVertex = *iterator; if( testSmoothedPoly->Contains( currentVertex ) ) { // COPPERAREA_COPPERAREA error: copper area ref corner inside copper area if( aCreate_Markers ) { int x = currentVertex.x; int y = currentVertex.y; wxString msg1 = Area_Ref->GetSelectMenuText(); wxString msg2 = area_to_test->GetSelectMenuText(); MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA, wxPoint( x, y ), msg1, wxPoint( x, y ), msg2, wxPoint( x, y ) ); Add( marker ); } nerrors++; } } // test for some corners of area_to_test inside Area_Ref for( auto iterator = testSmoothedPoly->IterateWithHoles(); iterator; iterator++ ) { VECTOR2I currentVertex = *iterator; if( refSmoothedPoly->Contains( currentVertex ) ) { // COPPERAREA_COPPERAREA error: copper area corner inside copper area ref if( aCreate_Markers ) { int x = currentVertex.x; int y = currentVertex.y; wxString msg1 = area_to_test->GetSelectMenuText(); wxString msg2 = Area_Ref->GetSelectMenuText(); MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_INSIDE_COPPERAREA, wxPoint( x, y ), msg1, wxPoint( x, y ), msg2, wxPoint( x, y ) ); Add( marker ); } nerrors++; } } // Iterate through all the segments of refSmoothedPoly for( auto refIt = refSmoothedPoly->IterateSegmentsWithHoles(); refIt; refIt++ ) { // Build ref segment SEG refSegment = *refIt; // Iterate through all the segments in testSmoothedPoly for( auto testIt = testSmoothedPoly->IterateSegmentsWithHoles(); testIt; testIt++ ) { // Build test segment SEG testSegment = *testIt; int x, y; int ax1, ay1, ax2, ay2; ax1 = refSegment.A.x; ay1 = refSegment.A.y; ax2 = refSegment.B.x; ay2 = refSegment.B.y; int bx1, by1, bx2, by2; bx1 = testSegment.A.x; by1 = testSegment.A.y; bx2 = testSegment.B.x; by2 = testSegment.B.y; int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0, ax1, ay1, ax2, ay2, 0, zone2zoneClearance, &x, &y ); if( d < zone2zoneClearance ) { // COPPERAREA_COPPERAREA error : intersect or too close if( aCreate_Markers ) { wxString msg1 = Area_Ref->GetSelectMenuText(); wxString msg2 = area_to_test->GetSelectMenuText(); MARKER_PCB* marker = new MARKER_PCB( COPPERAREA_CLOSE_TO_COPPERAREA, wxPoint( x, y ), msg1, wxPoint( x, y ), msg2, wxPoint( x, y ) ); Add( marker ); } nerrors++; } } } } } return nerrors; }