diff --git a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp index bb539456e7..39ce3a1b4b 100644 --- a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp +++ b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp @@ -430,7 +430,7 @@ COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValu } else // Oblong hole { - const std::shared_ptr& seg = aPad->GetEffectiveHoleShape(); + const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape(); float width = seg->GetWidth() + aInflateValue * 2; SFVEC2F start3DU( seg->GetSeg().A.x * m_biuTo3Dunits, diff --git a/libs/kimath/include/geometry/shape_rect.h b/libs/kimath/include/geometry/shape_rect.h index 85de5782a1..e5c012db8c 100644 --- a/libs/kimath/include/geometry/shape_rect.h +++ b/libs/kimath/include/geometry/shape_rect.h @@ -90,6 +90,16 @@ public: return VECTOR2I( m_w, m_h ).EuclideanNorm(); } + bool Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const override + { + return SHAPE::Collide( aShape, aClearance, aMTV ); + } + + bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const override + { + return SHAPE::Collide( aShape, aClearance, aActual ); + } + /// @copydoc SHAPE::Collide() bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; diff --git a/libs/kimath/include/geometry/shape_segment.h b/libs/kimath/include/geometry/shape_segment.h index 0c9f4a73e1..ec8e58e835 100644 --- a/libs/kimath/include/geometry/shape_segment.h +++ b/libs/kimath/include/geometry/shape_segment.h @@ -54,6 +54,16 @@ public: return BOX2I( m_seg.A, m_seg.B - m_seg.A ).Inflate( aClearance + ( m_width + 1 ) / 2 ); } + bool Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const override + { + return SHAPE::Collide( aShape, aClearance, aMTV ); + } + + bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const override + { + return SHAPE::Collide( aShape, aClearance, aActual ); + } + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override { int min_dist = ( m_width + 1 ) / 2 + aClearance; diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 51c31b8fe0..f626406aef 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -651,7 +651,7 @@ bool D_PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, if( !drillsize.x || !drillsize.y ) return false; - const std::shared_ptr& seg = GetEffectiveHoleShape(); + const SHAPE_SEGMENT* seg = GetEffectiveHoleShape(); TransformSegmentToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B, aError, seg->GetWidth() + aInflateValue * 2 ); diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 632cdf6dd3..7b7b952254 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -200,12 +200,12 @@ const std::vector>& D_PAD::GetEffectiveShapes() const } -const std::shared_ptr& D_PAD::GetEffectiveHoleShape() const +const SHAPE_SEGMENT* D_PAD::GetEffectiveHoleShape() const { if( m_shapesDirty ) BuildEffectiveShapes(); - return m_effectiveHoleShape; + return m_effectiveHoleShape.get(); } @@ -761,39 +761,6 @@ void D_PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector } -void D_PAD::GetOblongGeometry( const wxSize& aDrillOrPadSize, - wxPoint* aStartPoint, wxPoint* aEndPoint, int* aWidth ) const -{ - // calculates the start point, end point and width - // of an equivalent segment which have the same position and width as the pad or hole - int delta_cx, delta_cy; - - wxSize halfsize = aDrillOrPadSize / 2; - wxPoint offset; - - if( aDrillOrPadSize.x > aDrillOrPadSize.y ) // horizontal - { - delta_cx = halfsize.x - halfsize.y; - delta_cy = 0; - *aWidth = aDrillOrPadSize.y; - } - else // vertical - { - delta_cx = 0; - delta_cy = halfsize.y - halfsize.x; - *aWidth = aDrillOrPadSize.x; - } - - RotatePoint( &delta_cx, &delta_cy, m_Orient ); - - aStartPoint->x = delta_cx + offset.x; - aStartPoint->y = delta_cy + offset.y; - - aEndPoint->x = - delta_cx + offset.x; - aEndPoint->y = - delta_cy + offset.y; -} - - bool D_PAD::HitTest( const wxPoint& aPosition, int aAccuracy ) const { VECTOR2I delta = aPosition - GetPosition(); @@ -846,6 +813,64 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con } +bool D_PAD::Collide( const D_PAD* aPad, int aMinClearance, int* aActual ) +{ + int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - ShapePos() ) ); + + // Quick test: Clearance is OK if the bounding circles are further away than aMinClearance + if( center2center - GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance ) + return false; + + int actual = INT_MAX; + + for( const std::shared_ptr& aShape : GetEffectiveShapes() ) + { + for( const std::shared_ptr& bShape : aPad->GetEffectiveShapes() ) + { + int this_dist; + + if( aShape->Collide( bShape.get(), aMinClearance, &this_dist ) ) + actual = std::min( actual, this_dist ); + } + } + + if( actual < INT_MAX ) + { + // returns the actual clearance (clearance < aMinClearance) for diags: + if( aActual ) + *aActual = std::max( 0, actual ); + + return true; + } + + return false; +} + + +bool D_PAD::Collide( const SHAPE_SEGMENT* aSeg, int aMinClearance, int* aActual ) +{ + int actual = INT_MAX; + + for( const std::shared_ptr& shape : GetEffectiveShapes() ) + { + int this_dist; + + if( shape->Collide( aSeg, aMinClearance, &this_dist ) ) + actual = std::min( actual, this_dist ); + } + + if( actual < INT_MAX ) + { + if( aActual ) + *aActual = std::max( 0, actual ); + + return true; + } + + return false; +} + + int D_PAD::Compare( const D_PAD* padref, const D_PAD* padcmp ) { int diff; diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index 2f0514191d..7d04fc5ed5 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -321,12 +321,6 @@ public: bool IsDirty() const { return m_shapesDirty; } - /** - * JEY TODO: temporary until Tom is done with DRC stuff.... - */ - void GetOblongGeometry( const wxSize& aDrillOrPadSize, - wxPoint* aStartPoint, wxPoint* aEndPoint, int* aWidth ) const; - void SetLayerSet( LSET aLayerMask ) { m_layerMask = aLayerMask; } LSET GetLayerSet() const override { return m_layerMask; } @@ -394,7 +388,7 @@ public: * Function GetEffectiveHoleShape * Returns a list of SHAPE objects representing the pad's hole. */ - const std::shared_ptr& GetEffectiveHoleShape() const; + const SHAPE_SEGMENT* GetEffectiveHoleShape() const; /** * Function GetBoundingRadius @@ -513,6 +507,9 @@ public: bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override; bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override; + bool Collide( const SHAPE_SEGMENT* aSeg, int aMinClearance, int* aActual = nullptr ); + bool Collide( const D_PAD* aPad, int aMinClearance, int* aActual = nullptr ); + wxString GetClass() const override { return wxT( "PAD" ); diff --git a/pcbnew/drc/drc.cpp b/pcbnew/drc/drc.cpp index 88d63be51d..e9da02fb61 100644 --- a/pcbnew/drc/drc.cpp +++ b/pcbnew/drc/drc.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,7 @@ #include #include + DRC::DRC() : PCB_TOOL_BASE( "pcbnew.DRCTool" ), m_editFrame( nullptr ), @@ -538,11 +540,11 @@ void DRC::testPadClearances( BOARD_COMMIT& aCommit ) for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ ) { + SHAPE_SEGMENT edge( *it ); int actual; - if( !checkClearanceSegmToPad( *it, 0, pad, minClearance, &actual ) ) + if( pad->Collide( &edge, minClearance, &actual ) ) { - actual = std::max( 0, actual ); DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_COPPER_EDGE_CLEARANCE ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), @@ -906,101 +908,26 @@ void DRC::testCopperTextAndGraphics( BOARD_COMMIT& aCommit ) void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem ) { - EDA_RECT bbox; - std::vector itemShape; - int itemWidth; - DRAWSEGMENT* drawItem = dynamic_cast( aItem ); - EDA_TEXT* textItem = dynamic_cast( aItem ); + EDA_RECT bbox; + std::vector itemShapes; + DRAWSEGMENT* drawItem = dynamic_cast( aItem ); + EDA_TEXT* textItem = dynamic_cast( aItem ); if( drawItem ) { bbox = drawItem->GetBoundingBox(); - itemWidth = drawItem->GetWidth(); - - switch( drawItem->GetShape() ) - { - case S_ARC: - { - SHAPE_ARC arc( drawItem->GetCenter(), drawItem->GetArcStart(), - (double) drawItem->GetAngle() / 10.0 ); - - SHAPE_LINE_CHAIN l = arc.ConvertToPolyline(); - - for( int i = 0; i < l.SegmentCount(); i++ ) - itemShape.push_back( l.Segment( i ) ); - - break; - } - - case S_SEGMENT: - itemShape.emplace_back( SEG( drawItem->GetStart(), drawItem->GetEnd() ) ); - break; - - case S_RECT: - { - std::vector pts; - drawItem->GetRectCorners( &pts ); - - itemShape.emplace_back( SEG( pts[0], pts[1] ) ); - itemShape.emplace_back( SEG( pts[1], pts[2] ) ); - itemShape.emplace_back( SEG( pts[2], pts[3] ) ); - itemShape.emplace_back( SEG( pts[3], pts[0] ) ); - } - break; - - case S_CIRCLE: - { - // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC - SHAPE_ARC circle( drawItem->GetCenter(), drawItem->GetEnd(), 360.0 ); - - SHAPE_LINE_CHAIN l = circle.ConvertToPolyline(); - - for( int i = 0; i < l.SegmentCount(); i++ ) - itemShape.push_back( l.Segment( i ) ); - - break; - } - - case S_CURVE: - { - drawItem->RebuildBezierToSegmentsPointsList( drawItem->GetWidth() ); - wxPoint start_pt = drawItem->GetBezierPoints()[0]; - - for( unsigned int jj = 1; jj < drawItem->GetBezierPoints().size(); jj++ ) - { - wxPoint end_pt = drawItem->GetBezierPoints()[jj]; - itemShape.emplace_back( SEG( start_pt, end_pt ) ); - start_pt = end_pt; - } - - break; - } - - case S_POLYGON: - { - SHAPE_LINE_CHAIN l = drawItem->GetPolyShape().Outline( 0 ); - - for( int i = 0; i < l.SegmentCount(); i++ ) - itemShape.push_back( l.Segment( i ) ); - } - break; - - default: - wxFAIL_MSG( "DRC::testCopperDrawItem unsupported DRAWSEGMENT shape: " - + STROKE_T_asString( drawItem->GetShape() ) ); - break; - } + itemShapes = drawItem->MakeEffectiveShapes(); } - else if( textItem ) + else if( textItem ) { bbox = textItem->GetTextBox(); - itemWidth = textItem->GetEffectiveTextPenWidth(); - std::vector textShape; - textItem->TransformTextShapeToSegmentList( textShape ); + int penWidth = textItem->GetEffectiveTextPenWidth(); + std::vector pts; + textItem->TransformTextShapeToSegmentList( pts ); - for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) - itemShape.emplace_back( SEG( textShape[jj], textShape[jj+1] ) ); + for( unsigned jj = 0; jj < pts.size(); jj += 2 ) + itemShapes.push_back( new SHAPE_SEGMENT( pts[jj], pts[jj+1], penWidth ) ); } else { @@ -1008,10 +935,7 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem ) return; } - SHAPE_RECT rect_area( bbox.GetX(), bbox.GetY(), bbox.GetWidth(), bbox.GetHeight() ); - - if( itemShape.empty() ) - return; + SHAPE_RECT bboxShape( bbox.GetX(), bbox.GetY(), bbox.GetWidth(), bbox.GetHeight() ); // Test tracks and vias for( auto track : m_pcb->Tracks() ) @@ -1019,44 +943,42 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem ) if( !track->IsOnLayer( aItem->GetLayer() ) ) continue; - int minClearance = track->GetClearance( aItem, &m_clearanceSource ); - int widths = ( track->GetWidth() + itemWidth ) / 2; - int center2centerAllowed = minClearance + widths; + int minClearance = track->GetClearance( aItem, &m_clearanceSource ); + int actual = INT_MAX; + wxPoint pos; - SEG trackSeg( track->GetStart(), track->GetEnd() ); + SHAPE_SEGMENT trackSeg( track->GetStart(), track->GetEnd(), track->GetWidth() ); // Fast test to detect a track segment candidate inside the text bounding box - if( !rect_area.Collide( trackSeg, center2centerAllowed ) ) + if( !bboxShape.Collide( &trackSeg, 0 ) ) continue; - OPT minSeg; - SEG::ecoord center2center_squared = 0; - - for( const SEG& itemSeg : itemShape ) + for( const SHAPE* shape : itemShapes ) { - SEG::ecoord thisDist_squared = trackSeg.SquaredDistance( itemSeg ); + int this_dist; - if( !minSeg || thisDist_squared < center2center_squared ) + if( shape->Collide( &trackSeg, minClearance, &this_dist ) ) { - minSeg = itemSeg; - center2center_squared = thisDist_squared; + if( this_dist < actual ) + { + actual = this_dist; + pos = (wxPoint) shape->Centre(); + } } } - if( center2center_squared < SEG::Square( center2centerAllowed ) ) + if( actual < INT_MAX ) { - int actual = std::max( 0.0, sqrt( center2center_squared ) - widths ); DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), m_clearanceSource, MessageTextFromValue( userUnits(), minClearance, true ), - MessageTextFromValue( userUnits(), actual, true ) ); + MessageTextFromValue( userUnits(), std::max( 0, actual ), true ) ); drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( track, aItem ); - wxPoint pos = GetLocation( track, minSeg.get() ); MARKER_PCB* marker = new MARKER_PCB( drcItem, pos ); addMarkerToPcb( aCommit, marker ); } @@ -1073,42 +995,34 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem ) continue; int minClearance = pad->GetClearance( aItem, &m_clearanceSource ); - int widths = itemWidth / 2; - int center2centerAllowed = minClearance + widths; + int actual = INT_MAX; // Fast test to detect a pad candidate inside the text bounding box // Finer test (time consumming) is made only for pads near the text. int bb_radius = pad->GetBoundingRadius() + minClearance; - if( !rect_area.Collide( SEG( pad->GetPosition(), pad->GetPosition() ), bb_radius ) ) + if( !bboxShape.Collide( SEG( pad->GetPosition(), pad->GetPosition() ), bb_radius ) ) continue; - SHAPE_POLY_SET padOutline; - pad->TransformShapeWithClearanceToPolygon( padOutline, 0 ); - - OPT minSeg; - SEG::ecoord center2center_squared = 0; - - for( const SEG& itemSeg : itemShape ) + for( const std::shared_ptr& aShape : pad->GetEffectiveShapes() ) { - SEG::ecoord thisCenter2center_squared = padOutline.SquaredDistance( itemSeg ); - - if( !minSeg || thisCenter2center_squared < center2center_squared ) + for( const SHAPE* bShape : itemShapes ) { - minSeg = itemSeg; - center2center_squared = thisCenter2center_squared; + int this_dist; + + if( aShape->Collide( bShape, minClearance, &this_dist ) ) + actual = std::min( actual, this_dist ); } } - if( center2center_squared < SEG::Square( center2centerAllowed ) ) + if( actual < INT_MAX ) { - int actual = std::max( 0.0, sqrt( center2center_squared ) - widths ); DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), m_clearanceSource, MessageTextFromValue( userUnits(), minClearance, true ), - MessageTextFromValue( userUnits(), actual, true ) ); + MessageTextFromValue( userUnits(), std::max( 0, actual ), true ) ); drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( pad, aItem ); @@ -1117,6 +1031,9 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem ) addMarkerToPcb( aCommit, marker ); } } + + for( SHAPE* shape : itemShapes ) + delete shape; } @@ -1217,15 +1134,6 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart, LSET layerMask = aRefPad->GetLayerSet() & all_cu; - // For hole testing we use a dummy pad which is given the shape of the hole. Note that - // this pad must have a parent because some functions expect a non-null parent to find - // the pad's board. - MODULE dummymodule( m_pcb ); // Creates a dummy parent - D_PAD dummypad( &dummymodule ); - - // Ensure the hole is on all copper layers - dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() ); - for( D_PAD** pad_list = aStart; pad_listGetDrillSize().x ) { - // pad under testing has a hole, test this hole against pad reference - dummypad.SetPosition( pad->GetPosition() ); - dummypad.SetSize( pad->GetDrillSize() ); - dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? - PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); - dummypad.SetOrientation( pad->GetOrientation() ); - int minClearance = aRefPad->GetClearance( nullptr, &m_clearanceSource ); int actual; - if( !checkClearancePadToPad( aRefPad, &dummypad, minClearance, &actual ) ) + if( aRefPad->Collide( pad->GetEffectiveHoleShape(), minClearance, &actual ) ) { DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); @@ -1293,18 +1191,12 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart, } } - if( aRefPad->GetDrillSize().x ) // pad reference has a hole + if( aRefPad->GetDrillSize().x ) { - dummypad.SetPosition( aRefPad->GetPosition() ); - dummypad.SetSize( aRefPad->GetDrillSize() ); - dummypad.SetShape( aRefPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? - PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); - dummypad.SetOrientation( aRefPad->GetOrientation() ); - int minClearance = pad->GetClearance( nullptr, &m_clearanceSource ); int actual; - if( !checkClearancePadToPad( pad, &dummypad, minClearance, &actual ) ) + if( pad->Collide( aRefPad->GetEffectiveHoleShape(), minClearance, &actual ) ) { DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); @@ -1353,7 +1245,7 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart, int clearanceAllowed = minClearance - m_pcb->GetDesignSettings().GetDRCEpsilon(); int actual; - if( !checkClearancePadToPad( aRefPad, pad, clearanceAllowed, &actual ) ) + if( aRefPad->Collide( pad, clearanceAllowed, &actual ) ) { DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); diff --git a/pcbnew/drc/drc.h b/pcbnew/drc/drc.h index 924992dd12..6330a84d56 100644 --- a/pcbnew/drc/drc.h +++ b/pcbnew/drc/drc.h @@ -219,38 +219,6 @@ private: void doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterator aEndIt, bool aTestZones ); - //--------------------------------------------------- - - /** - * @param aRefPad The reference pad to check - * @param aPad Another pad to check against - * @param aMinClearance is the minimum allowed distance between the pads - * @param aActualDist [out] it the actual distance - * (only guaranteed to be set for violations) - * @return true if clearance between aRefPad and aPad is >= aMinClearance, else false - */ - bool checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, - int aMinClearance, int* aActualDist ); - - - /** - * Check the distance from a pad to segment. This function uses several - * instance variable not passed in: - * @param aPad Is the pad involved in the check - * @param aSegmentWidth width of the segment to test - * @param aMinDist Is the minimum clearance needed - * @param aActualDist [out] Is the actual clearance (only guarantted to be set on violations) - * - * @return true distance >= dist_min, - * false if distance < dist_min - */ - bool checkClearanceSegmToPad( const SEG& seg, int segWidth, const D_PAD* pad, - int minClearance, int* aActualDist ); - - - - //-------------------------------------------------- - public: /** * Load the DRC rules. Must be called after the netclasses have been read. diff --git a/pcbnew/drc/drc_clearance_test_functions.cpp b/pcbnew/drc/drc_clearance_test_functions.cpp index f2935ebe15..2d6c4db42f 100644 --- a/pcbnew/drc/drc_clearance_test_functions.cpp +++ b/pcbnew/drc/drc_clearance_test_functions.cpp @@ -30,49 +30,10 @@ #include #include #include -#include #include -#include #include - - -/* - * compare a trapezoid (can be rectangle) and a segment and return true if distance > aDist - */ -bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd, - int aDist, int* aActual ) -{ - /* 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, aTrefCount, aSegStart ) ) - { - *aActual = 0; - return false; - } - - for( int ii = 0, jj = aTrefCount-1; ii < aTrefCount; jj = ii, ii++ ) - { // for all edges in polygon - double d; - - if( TestForIntersectionOfStraightLineSegments( aTref[ii].x, aTref[ii].y, aTref[jj].x, - aTref[jj].y, aSegStart.x, aSegStart.y, - aSegEnd.x, aSegEnd.y, NULL, NULL, &d ) ) - { - *aActual = 0; - return false; - } - - if( d < aDist ) - { - *aActual = KiROUND( d ); - return false; - } - } - - return true; -} +#include +#include void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aStartIt, @@ -80,12 +41,12 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS { BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings(); - SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); - PCB_LAYER_ID refLayer = aRefSeg->GetLayer(); - LSET refLayerSet = aRefSeg->GetLayerSet(); + SHAPE_SEGMENT refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd(), aRefSeg->GetWidth() ); + PCB_LAYER_ID refLayer = aRefSeg->GetLayer(); + LSET refLayerSet = aRefSeg->GetLayerSet(); - EDA_RECT refSegBB = aRefSeg->GetBoundingBox(); - int refSegWidth = aRefSeg->GetWidth(); + EDA_RECT refSegBB = aRefSeg->GetBoundingBox(); + int refSegWidth = aRefSeg->GetWidth(); /******************************************/ @@ -319,18 +280,11 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS if( pad->GetDrillSize().x > 0 ) { - // For hole testing we use a dummy pad which is a copy of the current pad - // shrunk down to nothing but its hole. - D_PAD dummypad( *pad ); - dummypad.SetSize( pad->GetDrillSize() ); - dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? - PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); - // Ensure the hole is on all copper layers - const static LSET all_cu = LSET::AllCuMask(); - dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() ); + const SHAPE_SEGMENT* slot = pad->GetEffectiveHoleShape(); + DRC_RULE* rule = GetRule( aRefSeg, pad, CLEARANCE_CONSTRAINT ); - int minClearance; - DRC_RULE* rule = GetRule( aRefSeg, &dummypad, CLEARANCE_CONSTRAINT ); + int minClearance; + int actual; if( rule ) { @@ -342,27 +296,8 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS minClearance = aRefSeg->GetClearance( nullptr, &m_clearanceSource ); } - /* Treat an oval hole as a line segment along the hole's major axis, - * shortened by half its minor axis. - * A circular hole is just a degenerate case of an oval hole. - */ - wxPoint slotStart, slotEnd; - int slotWidth; - - pad->GetOblongGeometry( pad->GetDrillSize(), &slotStart, &slotEnd, &slotWidth ); - slotStart += pad->GetPosition(); - slotEnd += pad->GetPosition(); - - SEG slotSeg( slotStart, slotEnd ); - int widths = ( slotWidth + refSegWidth ) / 2; - int center2centerAllowed = minClearance + widths + bds.GetDRCEpsilon(); - - // Avoid square-roots if possible (for performance) - SEG::ecoord center2center_squared = refSeg.SquaredDistance( slotSeg ); - - if( center2center_squared < SEG::Square( center2centerAllowed ) ) + if( slot->Collide( &refSeg, minClearance + bds.GetDRCEpsilon(), &actual ) ) { - int actual = std::max( 0.0, sqrt( center2center_squared ) - widths ); DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), @@ -373,7 +308,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( aRefSeg, pad ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, slotSeg ) ); + MARKER_PCB* marker = new MARKER_PCB( drcItem, pad->GetPosition() ); addMarkerToPcb( aCommit, marker ); if( !m_reportAllTrackErrors ) @@ -382,13 +317,10 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS } int minClearance = aRefSeg->GetClearance( pad, &m_clearanceSource ); - int clearanceAllowed = minClearance - bds.GetDRCEpsilon(); int actual; - if( !checkClearanceSegmToPad( refSeg, refSegWidth, pad, clearanceAllowed, &actual ) ) + if( pad->Collide( &refSeg, minClearance - bds.GetDRCEpsilon(), &actual ) ) { - actual = std::max( 0, actual ); - SEG padSeg( pad->GetPosition(), pad->GetPosition() ); DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), @@ -399,7 +331,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( aRefSeg, pad ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, padSeg ) ); + MARKER_PCB* marker = new MARKER_PCB( drcItem, pad->GetPosition() ); addMarkerToPcb( aCommit, marker ); if( !m_reportAllTrackErrors ) @@ -451,17 +383,12 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS if( !trackBB.Intersects( refSegBB ) ) continue; - int minClearance = aRefSeg->GetClearance( track, &m_clearanceSource ); - SEG trackSeg( track->GetStart(), track->GetEnd() ); - int widths = ( refSegWidth + track->GetWidth() ) / 2; - int center2centerAllowed = minClearance + widths; - - // Avoid square-roots if possible (for performance) - SEG::ecoord center2center_squared = refSeg.SquaredDistance( trackSeg ); - OPT_VECTOR2I intersection = refSeg.Intersect( trackSeg ); + int minClearance = aRefSeg->GetClearance( track, &m_clearanceSource ); + int actual; + SHAPE_SEGMENT trackSeg( track->GetStart(), track->GetEnd(), track->GetWidth() ); // Check two tracks crossing first as it reports a DRCE without distances - if( intersection ) + if( OPT_VECTOR2I intersection = refSeg.GetSeg().Intersect( trackSeg.GetSeg() ) ) { DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING ); drcItem->SetErrorMessage( m_msg ); @@ -473,9 +400,9 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS if( !m_reportAllTrackErrors ) return; } - else if( center2center_squared < SEG::Square( center2centerAllowed ) ) + else if( refSeg.Collide( &trackSeg, minClearance, &actual ) ) { - int actual = std::max( 0.0, sqrt( center2center_squared ) - widths ); + wxPoint pos = GetLocation( aRefSeg, trackSeg.GetSeg() ); DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), @@ -486,7 +413,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( aRefSeg, track ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, trackSeg ) ); + MARKER_PCB* marker = new MARKER_PCB( drcItem, pos ); addMarkerToPcb( aCommit, marker ); if( !m_reportAllTrackErrors ) @@ -515,28 +442,25 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS if( zone->GetNetCode() && zone->GetNetCode() == aRefSeg->GetNetCode() ) continue; - int minClearance = aRefSeg->GetClearance( zone, &m_clearanceSource ); - int widths = refSegWidth / 2; - int center2centerAllowed = minClearance + widths; - SHAPE_POLY_SET* outline = - const_cast( &zone->GetFilledPolysList( layer ) ); - - SEG::ecoord center2center_squared = outline->SquaredDistance( testSeg ); - // to avoid false positive, due to rounding issues and approxiamtions // in distance and clearance calculations, use a small threshold for distance // (1 micron) #define THRESHOLD_DIST Millimeter2iu( 0.001 ) - if( center2center_squared + THRESHOLD_DIST < SEG::Square( center2centerAllowed ) ) + int minClearance = aRefSeg->GetClearance( zone, &m_clearanceSource ); + int widths = refSegWidth / 2; + int allowedDist = minClearance + widths + THRESHOLD_DIST; + int actual; + + if( zone->GetFilledPolysList( layer ).Collide( testSeg, allowedDist, &actual ) ) { - int actual = std::max( 0.0, sqrt( center2center_squared ) - widths ); + actual = std::max( 0, actual - widths ); DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), - m_clearanceSource, - MessageTextFromValue( userUnits(), minClearance, true ), - MessageTextFromValue( userUnits(), actual, true ) ); + m_clearanceSource, + MessageTextFromValue( userUnits(), minClearance, true ), + MessageTextFromValue( userUnits(), actual, true ) ); drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( aRefSeg, zone ); @@ -616,119 +540,3 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS } } - -bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual ) -{ - int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - aRefPad->ShapePos() ) ); - - // Quick test: Clearance is OK if the bounding circles are further away than aMinClearance - if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance ) - return true; - - int actual = INT_MAX; - - for( const std::shared_ptr& aShape : aRefPad->GetEffectiveShapes() ) - { - for( const std::shared_ptr& bShape : aPad->GetEffectiveShapes() ) - { - int this_dist; - - if( aShape->Collide( bShape.get(), aMinClearance, &this_dist ) ) - actual = std::min( actual, this_dist ); - } - } - - if( actual < INT_MAX ) - { - // returns the actual clearance (clearance < aMinClearance) for diags: - if( aActual ) - *aActual = std::max( 0, actual ); - - return false; - } - - return true; -} - - -/* - * Test if distance between a segment and a pad is > minClearance. Return the actual - * distance if it is less. - */ -bool DRC::checkClearanceSegmToPad( const SEG& refSeg, int refSegWidth, const D_PAD* pad, - int minClearance, int* aActualDist ) -{ - if( ( pad->GetShape() == PAD_SHAPE_CIRCLE || pad->GetShape() == PAD_SHAPE_OVAL ) ) - { - /* Treat an oval pad as a line segment along the hole's major axis, - * shortened by half its minor axis. - * A circular pad is just a degenerate case of an oval hole. - */ - wxPoint padStart, padEnd; - int padWidth; - - pad->GetOblongGeometry( pad->GetSize(), &padStart, &padEnd, &padWidth ); - padStart += pad->ShapePos(); - padEnd += pad->ShapePos(); - - SEG padSeg( padStart, padEnd ); - int widths = ( padWidth + refSegWidth ) / 2; - int center2centerAllowed = minClearance + widths; - - // Avoid square-roots if possible (for performance) - SEG::ecoord center2center_squared = refSeg.SquaredDistance( padSeg ); - - if( center2center_squared < SEG::Square( center2centerAllowed ) ) - { - *aActualDist = std::max( 0.0, sqrt( center2center_squared ) - widths ); - return false; - } - } - else if( ( pad->GetShape() == PAD_SHAPE_RECT || pad->GetShape() == PAD_SHAPE_ROUNDRECT ) - && ( (int) pad->GetOrientation() % 900 == 0 ) ) - { - EDA_RECT padBBox = pad->GetBoundingBox(); - int widths = refSegWidth / 2; - - // Note a ROUNDRECT pad with a corner radius = r can be treated as a smaller - // RECT (size - 2*r) with a clearance increased by r - if( pad->GetShape() == PAD_SHAPE_ROUNDRECT ) - { - padBBox.Inflate( - pad->GetRoundRectCornerRadius() ); - widths += pad->GetRoundRectCornerRadius(); - } - - SHAPE_RECT padShape( padBBox.GetPosition(), padBBox.GetWidth(), padBBox.GetHeight() ); - int actual; - - if( padShape.Collide( refSeg, minClearance + widths, &actual ) ) - { - *aActualDist = std::max( 0, actual - widths ); - return false; - } - } - else // Convert the rest to polygons - { - SHAPE_POLY_SET polyset; - - BOARD* board = pad->GetBoard(); - int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF; - - pad->TransformShapeWithClearanceToPolygon( polyset, 0, maxError ); - - const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 ); - int widths = refSegWidth / 2; - int actual; - - if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), - (wxPoint) refSeg.A, (wxPoint) refSeg.B, - minClearance + widths, &actual ) ) - { - *aActualDist = std::max( 0, actual - widths ); - return false; - } - } - - return true; -} - diff --git a/pcbnew/drc/drc_keepout_tester.cpp b/pcbnew/drc/drc_keepout_tester.cpp index 94afa94c67..24329f984f 100644 --- a/pcbnew/drc/drc_keepout_tester.cpp +++ b/pcbnew/drc/drc_keepout_tester.cpp @@ -23,12 +23,11 @@ #include - +#include #include #include #include - DRC_KEEPOUT_TESTER::DRC_KEEPOUT_TESTER( MARKER_HANDLER aMarkerHandler ) : DRC_TEST_PROVIDER( std::move( aMarkerHandler ) ), m_units( EDA_UNITS::MILLIMETRES ), @@ -91,11 +90,9 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias() if( !m_zone->IsOnLayer( segm->GetLayer() ) ) continue; - int widths = segm->GetWidth() / 2; - SEG trackSeg( segm->GetStart(), segm->GetEnd() ); - SEG::ecoord center2center_squared = m_zone->Outline()->SquaredDistance( trackSeg ); + SEG trackSeg( segm->GetStart(), segm->GetEnd() ); - if( center2center_squared <= SEG::Square( widths) ) + if( m_zone->Outline()->Collide( trackSeg, segm->GetWidth() / 2 ) ) { DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_KEEPOUT ); @@ -111,42 +108,37 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias() } else if( segm->Type() == PCB_VIA_T && ( m_keepoutFlags & CHECK_VIAS_MASK ) != 0 ) { - VIA* via = static_cast( segm ); - int sourceId = 0; + VIA* via = static_cast( segm ); + SEG seg( via->GetPosition(), via->GetPosition() ); + int test = 0; + int clearance = via->GetWidth() / 2; if( ( m_keepoutFlags & DISALLOW_VIAS ) > 0 ) { - sourceId = DISALLOW_VIAS; + test = DISALLOW_VIAS; } else if( via->GetViaType() == VIATYPE::MICROVIA && ( m_keepoutFlags & DISALLOW_MICRO_VIAS ) > 0 ) { - sourceId = DISALLOW_MICRO_VIAS; + test = DISALLOW_MICRO_VIAS; } else if( via->GetViaType() == VIATYPE::BLIND_BURIED && ( m_keepoutFlags & DISALLOW_BB_VIAS ) > 0 ) { - sourceId = DISALLOW_BB_VIAS; + test = DISALLOW_BB_VIAS; } else if( ( m_keepoutFlags & DISALLOW_HOLES ) > 0 ) { - sourceId = DISALLOW_HOLES; + test = DISALLOW_HOLES; + clearance = via->GetDrillValue() / 2; } else continue; - int widths = via->GetWidth() / 2; - wxPoint viaPos = via->GetPosition(); - - if( sourceId == DISALLOW_HOLES ) - widths = via->GetDrillValue() / 2; - - SEG::ecoord center2center_squared = m_zone->Outline()->SquaredDistance( viaPos ); - - if( center2center_squared <= SEG::Square( widths ) ) + if( m_zone->Outline()->Collide( seg, clearance ) ) { DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_KEEPOUT ); - m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ), m_sources.at( sourceId ) ); + m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ), m_sources.at( test ) ); drcItem->SetErrorMessage( m_msg ); drcItem->SetItems( segm, m_zone ); @@ -259,18 +251,9 @@ bool DRC_KEEPOUT_TESTER::checkPads( MODULE* aModule ) } else if( ( m_keepoutFlags & DISALLOW_HOLES ) > 0 ) { - wxPoint slotStart, slotEnd; - int slotWidth; + const SHAPE_SEGMENT* slot = pad->GetEffectiveHoleShape(); - pad->GetOblongGeometry( pad->GetDrillSize(), &slotStart, &slotEnd, &slotWidth ); - slotStart += pad->GetPosition(); - slotEnd += pad->GetPosition(); - - SEG slotSeg( slotStart, slotEnd ); - SHAPE_POLY_SET* outline = m_zone->Outline(); - SEG::ecoord center2center_sq = outline->SquaredDistance( slotSeg ); - - if( center2center_sq <= SEG::Square( slotWidth) ) + if( m_zone->Outline()->Collide( slot->GetSeg(), slot->GetWidth() / 2 ) ) { DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_KEEPOUT ); diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 9f12390e82..566ef46833 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -765,8 +765,7 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer ) // Choose drawing settings depending on if we are drawing a pad itself or a hole if( aLayer == LAYER_PADS_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES ) { - const std::shared_ptr& seg = aPad->GetEffectiveHoleShape(); - + const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape(); m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() ); } else diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index 92f4ee29de..0e4a123370 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -698,7 +698,7 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, else { // Note: small drill marks have no significance when applied to slots - const std::shared_ptr& seg = pad->GetEffectiveHoleShape(); + const SHAPE_SEGMENT* seg = pad->GetEffectiveHoleShape(); aPlotter->ThickSegment( (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B, seg->GetWidth(), SKETCH, NULL );