Better error reporting for DP uncoupled length.

Fixes https://gitlab.com/kicad/code/kicad/issues/10087

(cherry picked from commit 722b2588f4)
This commit is contained in:
Jeff Young 2022-04-08 11:37:27 +01:00
parent be2e0cc272
commit 529048dac1
8 changed files with 258 additions and 152 deletions

View File

@ -269,7 +269,7 @@ BITMAPS ElectricalPinTypeGetBitmap( ELECTRICAL_PINTYPE aType )
{
auto findIt = g_pinTypes.find( aType );
wxCHECK_MSG( findIt != pinTypes.end(), BITMAPS::INVALID_BITMAP,
wxCHECK_MSG( findIt != g_pinTypes.end(), BITMAPS::INVALID_BITMAP,
wxT( "Could not find pin type in lookup map" ) );
return findIt->second.bitmap;

View File

@ -218,6 +218,7 @@ public:
EDA_ITEM* GetItem( const KIID& aId ) const override;
void FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer = UNDEFINED_LAYER );
void FocusOnItems( std::vector<BOARD_ITEM*> aItems, PCB_LAYER_ID aLayer = UNDEFINED_LAYER );
// General
virtual void ReCreateOptToolbar() override { }

View File

@ -117,10 +117,12 @@ public:
m_ids.push_back( dItem );
}
KIID GetMainItemID() const { return m_ids.size() > 0 ? m_ids[0] : niluuid; }
KIID GetAuxItemID() const { return m_ids.size() > 1 ? m_ids[1] : niluuid;; }
KIID GetAuxItem2ID() const { return m_ids.size() > 2 ? m_ids[2] : niluuid;; }
KIID GetAuxItem3ID() const { return m_ids.size() > 3 ? m_ids[3] : niluuid;; }
virtual KIID GetMainItemID() const { return m_ids.size() > 0 ? m_ids[0] : niluuid; }
virtual KIID GetAuxItemID() const { return m_ids.size() > 1 ? m_ids[1] : niluuid; }
virtual KIID GetAuxItem2ID() const { return m_ids.size() > 2 ? m_ids[2] : niluuid; }
virtual KIID GetAuxItem3ID() const { return m_ids.size() > 3 ? m_ids[3] : niluuid; }
std::vector<KIID> GetIDs() const { return m_ids; }
void SetParent( MARKER_BASE* aMarker ) { m_parent = aMarker; }
MARKER_BASE* GetParent() const { return m_parent; }

View File

@ -396,7 +396,23 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
if( board->GetVisibleLayers().test( principalLayer ) )
m_frame->SetActiveLayer( principalLayer );
if( m_centerMarkerOnIdle )
if( rc_item->GetErrorCode() == DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG )
{
PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
int net = track->GetNetCode();
std::vector<BOARD_ITEM*> segs;
for( const KIID& id : rc_item->GetIDs() )
{
PCB_TRACK* candidate = dynamic_cast<PCB_TRACK*>( board->GetItem( id ) );
if( candidate && candidate->GetNetCode() == net )
segs.push_back( candidate );
}
m_frame->FocusOnItems( segs, principalLayer );
}
else if( m_centerMarkerOnIdle )
{
// we already came from a cross-probe of the marker in the document; don't go
// around in circles

View File

@ -328,6 +328,30 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( const wxString& aErrorKey )
}
KIID DRC_ITEM::GetAuxItem2ID() const
{
if( m_errorCode == DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG )
{
// we have lots of segments, but it's enough to show the first P and the first N
return niluuid;
}
return m_ids.size() > 2 ? m_ids[2] : niluuid;
}
KIID DRC_ITEM::GetAuxItem3ID() const
{
if( m_errorCode == DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG )
{
// we have lots of segments, but it's enough to show the first P and the first N
return niluuid;
}
return m_ids.size() > 3 ? m_ids[3] : niluuid;
}
wxString DRC_ITEM::GetViolatingRuleDesc() const
{
if( m_violatingRule )

View File

@ -115,6 +115,9 @@ public:
void SetViolatingTest( DRC_TEST_PROVIDER *aProvider ) { m_violatingTest = aProvider; }
DRC_TEST_PROVIDER* GetViolatingTest() const { return m_violatingTest; }
KIID GetAuxItem2ID() const override;
KIID GetAuxItem3ID() const override;
private:
DRC_ITEM( int aErrorCode = 0, const wxString& aTitle = "", const wxString& aSettingsKey = "" )
{

View File

@ -467,11 +467,17 @@ bool test::DRC_TEST_PROVIDER_DIFF_PAIR_COUPLING::Run()
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
for( BOARD_CONNECTED_ITEM* offendingTrack : it.second.itemsP )
drce->AddItem( offendingTrack );
auto pit = it.second.itemsP.begin();
auto nit = it.second.itemsN.begin();
for( BOARD_CONNECTED_ITEM* offendingTrack : it.second.itemsN )
drce->AddItem( offendingTrack );
drce->AddItem( *pit );
drce->AddItem( *nit );
for( pit++; pit != it.second.itemsP.end(); pit++ )
drce->AddItem( *pit );
for( nit++; nit != it.second.itemsN.end(); nit++ )
drce->AddItem( *nit );
uncoupledViolation = true;

View File

@ -203,128 +203,151 @@ EDA_ITEM* PCB_BASE_FRAME::GetItem( const KIID& aId ) const
void PCB_BASE_FRAME::FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer )
{
static KIID lastBrightenedItemID( niluuid );
std::vector<BOARD_ITEM*> items;
if( aItem )
items.push_back( aItem );
FocusOnItems( items, aLayer );
}
void PCB_BASE_FRAME::FocusOnItems( std::vector<BOARD_ITEM*> aItems, PCB_LAYER_ID aLayer )
{
static std::vector<KIID> lastBrightenedItemIDs;
BOARD_ITEM* lastItem = nullptr;
/// @todo The Boost entropy exception does not exist prior to 1.67. Once the minimum Boost
/// version is raise to 1.67 or greater, this version check can be removed.
#if BOOST_VERSION >= 106700
try
for( KIID lastBrightenedItemID : lastBrightenedItemIDs )
{
/// @todo The Boost entropy exception does not exist prior to 1.67. Once the minimum Boost
/// version is raise to 1.67 or greater, this version check can be removed.
#if BOOST_VERSION >= 106700
try
{
lastItem = GetBoard()->GetItem( lastBrightenedItemID );
}
catch( const boost::uuids::entropy_error& )
{
wxLogError( wxT( "A Boost UUID entropy exception was thrown in %s:%s." ),
__FILE__, __FUNCTION__ );
}
#else
lastItem = GetBoard()->GetItem( lastBrightenedItemID );
}
catch( const boost::uuids::entropy_error& )
{
wxLogError( wxT( "A Boost UUID entropy exception was thrown in %s:%s." ),
__FILE__, __FUNCTION__ );
}
#else
lastItem = GetBoard()->GetItem( lastBrightenedItemID );
#endif
#endif
if( lastItem && lastItem != aItem && lastItem != DELETED_BOARD_ITEM::GetInstance() )
{
lastItem->ClearBrightened();
if( lastItem->Type() == PCB_FOOTPRINT_T )
if( lastItem && lastItem != DELETED_BOARD_ITEM::GetInstance() )
{
static_cast<FOOTPRINT*>( lastItem )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
child->ClearBrightened();
} );
}
else if( lastItem->Type() == PCB_GROUP_T )
{
static_cast<PCB_GROUP*>( lastItem )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
child->ClearBrightened();
} );
}
lastItem->ClearBrightened();
GetCanvas()->GetView()->Update( lastItem );
lastBrightenedItemID = niluuid;
GetCanvas()->Refresh();
}
if( aItem && aItem != DELETED_BOARD_ITEM::GetInstance() )
{
aItem->SetBrightened();
if( aItem->Type() == PCB_FOOTPRINT_T )
{
static_cast<FOOTPRINT*>( aItem )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
child->SetBrightened();
});
}
else if( aItem->Type() == PCB_GROUP_T )
{
static_cast<PCB_GROUP*>( aItem )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
child->SetBrightened();
});
}
GetCanvas()->GetView()->Update( aItem );
lastBrightenedItemID = aItem->m_Uuid;
// Focus on the object's location. Prefer a visible part of the object to its anchor
// in order to keep from scrolling around.
wxPoint focusPt = aItem->GetFocusPosition();
KIGFX::VIEW* view = GetCanvas()->GetView();
SHAPE_POLY_SET viewportPoly( view->GetViewport() );
for( wxWindow* dialog : findDialogs() )
{
wxPoint dialogPos = GetCanvas()->ScreenToClient( dialog->GetScreenPosition() );
SHAPE_POLY_SET dialogPoly( BOX2D( view->ToWorld( dialogPos, true ),
view->ToWorld( dialog->GetSize(), false ) ) );
try
if( lastItem->Type() == PCB_FOOTPRINT_T )
{
viewportPoly.BooleanSubtract( dialogPoly, SHAPE_POLY_SET::PM_FAST );
static_cast<FOOTPRINT*>( lastItem )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
child->ClearBrightened();
} );
}
catch( const ClipperLib::clipperException& exc )
else if( lastItem->Type() == PCB_GROUP_T )
{
// This may be overkill and could be an assertion but we are more likely to find
// any clipper errors this way.
wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
}
}
SHAPE_POLY_SET itemPoly, clippedPoly;
if( aLayer == UNDEFINED_LAYER )
aLayer = aItem->GetLayer();
switch( aItem->Type() )
{
case PCB_FOOTPRINT_T:
try
{
itemPoly = static_cast<FOOTPRINT*>( aItem )->GetBoundingHull();
}
catch( const ClipperLib::clipperException& exc )
{
// This may be overkill and could be an assertion but we are more likely to find
// any clipper errors this way.
wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
static_cast<PCB_GROUP*>( lastItem )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
child->ClearBrightened();
} );
}
break;
case PCB_PAD_T:
case PCB_MARKER_T:
case PCB_VIA_T:
FocusOnLocation( focusPt );
GetCanvas()->GetView()->Update( lastItem );
lastBrightenedItemID = niluuid;
GetCanvas()->Refresh();
return;
}
}
lastBrightenedItemIDs.clear();
if( aItems.empty() )
return;
wxPoint focusPt;
KIGFX::VIEW* view = GetCanvas()->GetView();
SHAPE_POLY_SET viewportPoly( view->GetViewport() );
for( wxWindow* dialog : findDialogs() )
{
wxPoint dialogPos = GetCanvas()->ScreenToClient( dialog->GetScreenPosition() );
SHAPE_POLY_SET dialogPoly( BOX2D( view->ToWorld( dialogPos, true ),
view->ToWorld( dialog->GetSize(), false ) ) );
try
{
viewportPoly.BooleanSubtract( dialogPoly, SHAPE_POLY_SET::PM_FAST );
}
catch( const ClipperLib::clipperException& exc )
{
// This may be overkill and could be an assertion but we are more likely to
// find any clipper errors this way.
wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
}
}
SHAPE_POLY_SET itemPoly, clippedPoly;
for( BOARD_ITEM* item : aItems )
{
if( item && item != DELETED_BOARD_ITEM::GetInstance() )
{
item->SetBrightened();
if( item->Type() == PCB_FOOTPRINT_T )
{
static_cast<FOOTPRINT*>( item )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
child->SetBrightened();
});
}
else if( item->Type() == PCB_GROUP_T )
{
static_cast<PCB_GROUP*>( item )->RunOnChildren(
[&]( BOARD_ITEM* child )
{
child->SetBrightened();
});
}
GetCanvas()->GetView()->Update( item );
lastBrightenedItemIDs.push_back( item->m_Uuid );
// Focus on the object's location. Prefer a visible part of the object to its anchor
// in order to keep from scrolling around.
focusPt = item->GetPosition();
if( aLayer == UNDEFINED_LAYER )
aLayer = item->GetLayerSet().Seq()[0];
switch( item->Type() )
{
case PCB_FOOTPRINT_T:
try
{
itemPoly = static_cast<FOOTPRINT*>( item )->GetBoundingHull();
}
catch( const ClipperLib::clipperException& exc )
{
// This may be overkill and could be an assertion but we are more likely to
// find any clipper errors this way.
wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
}
break;
case PCB_PAD_T:
case PCB_MARKER_T:
case PCB_VIA_T:
FocusOnLocation( item->GetFocusPosition() );
GetCanvas()->Refresh();
return;
case PCB_SHAPE_T:
case PCB_TEXT_T:
@ -338,55 +361,86 @@ void PCB_BASE_FRAME::FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer )
case PCB_DIM_LEADER_T:
case PCB_DIM_CENTER_T:
case PCB_DIM_ORTHOGONAL_T:
case PCB_ZONE_T:
aItem->TransformShapeWithClearanceToPolygon( itemPoly, aLayer, 0, Millimeter2iu( 0.1 ),
ERROR_INSIDE );
item->TransformShapeWithClearanceToPolygon( itemPoly, aLayer, 0, Millimeter2iu( 0.1 ),
ERROR_INSIDE );
break;
default:
case PCB_ZONE_T:
{
BOX2I item_bbox = aItem->GetBoundingBox();
itemPoly.NewOutline();
itemPoly.Append( item_bbox.GetOrigin() );
itemPoly.Append( item_bbox.GetOrigin() + VECTOR2I( item_bbox.GetWidth(), 0 ) );
itemPoly.Append( item_bbox.GetOrigin() + VECTOR2I( 0, item_bbox.GetHeight() ) );
itemPoly.Append( item_bbox.GetOrigin() + VECTOR2I( item_bbox.GetWidth(),
item_bbox.GetHeight() ) );
ZONE* zone = static_cast<ZONE*>( item );
#if 0
// Using the filled area shapes to find a Focus point can give good results, but
// unfortunately the calculations are highly time consuming, even for not very
// large areas (can be easily a few minutes for large areas).
// so we used only the zone outline that usually do not have too many vertices.
zone->TransformShapeWithClearanceToPolygon( itemPoly, aLayer, 0, Millimeter2iu( 0.1 ),
ERROR_INSIDE );
if( itemPoly.IsEmpty() )
itemPoly = *zone->Outline();
#else
// much faster calculation time when using only the zone outlines
itemPoly = *zone->Outline();
#endif
break;
}
default:
{
BOX2I item_bbox = item->GetBoundingBox();
itemPoly.NewOutline();
itemPoly.Append( item_bbox.GetOrigin() );
itemPoly.Append( item_bbox.GetOrigin() + VECTOR2I( item_bbox.GetWidth(), 0 ) );
itemPoly.Append( item_bbox.GetOrigin() + VECTOR2I( 0, item_bbox.GetHeight() ) );
itemPoly.Append( item_bbox.GetOrigin() + VECTOR2I( item_bbox.GetWidth(),
item_bbox.GetHeight() ) );
break;
}
}
try
{
clippedPoly.BooleanIntersection( itemPoly, viewportPoly, SHAPE_POLY_SET::PM_FAST );
}
catch( const ClipperLib::clipperException& exc )
{
// This may be overkill and could be an assertion but we are more likely to
// find any clipper errors this way.
wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
}
if( !clippedPoly.IsEmpty() )
itemPoly = clippedPoly;
}
}
/*
* Perform a step-wise deflate to find the visual-center-of-mass
*/
BOX2I bbox = itemPoly.BBox();
int step = std::min( bbox.GetWidth(), bbox.GetHeight() ) / 10;
while( !itemPoly.IsEmpty() )
{
focusPt = (wxPoint) itemPoly.BBox().Centre();
try
{
clippedPoly.BooleanIntersection( itemPoly, viewportPoly, SHAPE_POLY_SET::PM_FAST );
itemPoly.Deflate( step, 4, SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS );
}
catch( const ClipperLib::clipperException& exc )
{
// This may be overkill and could be an assertion but we are more likely to find
// any clipper errors this way.
// This may be overkill and could be an assertion but we are more likely to
// find any clipper errors this way.
wxLogError( wxT( "Clipper library exception '%s' occurred." ), exc.what() );
}
if( !clippedPoly.IsEmpty() )
itemPoly = clippedPoly;
/*
* Perform a step-wise deflate to find the visual-center-of-mass
*/
BOX2I bbox = itemPoly.BBox();
int step = std::min( bbox.GetWidth(), bbox.GetHeight() ) / 10;
while( !itemPoly.IsEmpty() )
{
focusPt = (wxPoint) itemPoly.BBox().Centre();
itemPoly.Deflate( step, 4 );
}
FocusOnLocation( focusPt );
GetCanvas()->Refresh();
}
FocusOnLocation( focusPt );
GetCanvas()->Refresh();
}