2020-09-13 11:37:20 +01:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2025-01-01 13:30:11 -08:00
|
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
2020-09-13 11:37:20 +01:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2025-03-09 17:50:38 +00:00
|
|
|
#include "pcb_table.h"
|
2020-09-11 14:41:45 +01:00
|
|
|
#include <drc/drc_item.h>
|
2020-09-11 08:40:36 -07:00
|
|
|
#include <drc/drc_test_provider.h>
|
2021-06-11 22:07:02 +01:00
|
|
|
#include <pcb_track.h>
|
2020-11-12 20:19:22 +00:00
|
|
|
#include <footprint.h>
|
|
|
|
#include <pad.h>
|
2020-11-11 23:05:59 +00:00
|
|
|
#include <zone.h>
|
2020-10-05 00:34:59 +01:00
|
|
|
#include <pcb_text.h>
|
2020-06-14 01:28:08 +02:00
|
|
|
|
2020-10-25 20:24:47 +00:00
|
|
|
|
|
|
|
// A list of all basic (ie: non-compound) board geometry items
|
|
|
|
std::vector<KICAD_T> DRC_TEST_PROVIDER::s_allBasicItems;
|
2020-10-27 17:09:27 +00:00
|
|
|
std::vector<KICAD_T> DRC_TEST_PROVIDER::s_allBasicItemsButZones;
|
2020-10-25 20:24:47 +00:00
|
|
|
|
|
|
|
|
2020-12-29 14:05:09 -05:00
|
|
|
DRC_TEST_PROVIDER_REGISTRY::~DRC_TEST_PROVIDER_REGISTRY()
|
|
|
|
{
|
|
|
|
for( DRC_TEST_PROVIDER* provider : m_providers )
|
|
|
|
delete provider;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-11 16:04:11 +01:00
|
|
|
DRC_TEST_PROVIDER::DRC_TEST_PROVIDER() :
|
2025-03-01 20:24:37 +00:00
|
|
|
UNITS_PROVIDER( pcbIUScale, EDA_UNITS::MM ),
|
|
|
|
m_drcEngine( nullptr )
|
2020-06-14 01:28:08 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-09-12 20:28:22 +01:00
|
|
|
|
2022-03-11 20:13:47 +00:00
|
|
|
void DRC_TEST_PROVIDER::Init()
|
|
|
|
{
|
|
|
|
if( s_allBasicItems.size() == 0 )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
|
|
|
|
{
|
|
|
|
if( i != PCB_FOOTPRINT_T && i != PCB_GROUP_T )
|
|
|
|
{
|
|
|
|
s_allBasicItems.push_back( (KICAD_T) i );
|
|
|
|
|
2023-03-30 12:49:23 +01:00
|
|
|
if( i != PCB_ZONE_T )
|
2022-03-11 20:13:47 +00:00
|
|
|
s_allBasicItemsButZones.push_back( (KICAD_T) i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-03-11 21:16:52 +00:00
|
|
|
const wxString DRC_TEST_PROVIDER::GetName() const { return wxT( "<no name test>" ); }
|
2020-06-14 01:28:08 +02:00
|
|
|
|
2020-09-12 20:28:22 +01:00
|
|
|
|
2021-07-26 15:35:12 -04:00
|
|
|
void DRC_TEST_PROVIDER::reportViolation( std::shared_ptr<DRC_ITEM>& item,
|
2024-12-23 15:29:29 +00:00
|
|
|
const VECTOR2I& aMarkerPos, int aMarkerLayer,
|
|
|
|
DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
|
2020-06-18 00:36:54 +02:00
|
|
|
{
|
2020-09-14 18:54:14 +01:00
|
|
|
item->SetViolatingTest( this );
|
2024-12-23 15:29:29 +00:00
|
|
|
m_drcEngine->ReportViolation( item, aMarkerPos, aMarkerLayer, aCustomHandler );
|
2020-06-18 00:36:54 +02:00
|
|
|
}
|
|
|
|
|
2020-09-12 20:28:22 +01:00
|
|
|
|
2024-01-04 11:49:06 +00:00
|
|
|
bool DRC_TEST_PROVIDER::reportProgress( size_t aCount, size_t aSize, size_t aDelta )
|
2020-06-18 00:36:54 +02:00
|
|
|
{
|
2020-09-18 20:57:54 +01:00
|
|
|
if( ( aCount % aDelta ) == 0 || aCount == aSize - 1 )
|
|
|
|
{
|
2024-01-04 11:49:06 +00:00
|
|
|
if( !m_drcEngine->ReportProgress( static_cast<double>( aCount ) / aSize ) )
|
2020-09-18 20:57:54 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2020-06-18 00:36:54 +02:00
|
|
|
}
|
|
|
|
|
2020-09-12 20:28:22 +01:00
|
|
|
|
2020-09-18 20:57:54 +01:00
|
|
|
bool DRC_TEST_PROVIDER::reportPhase( const wxString& aMessage )
|
2020-06-18 00:36:54 +02:00
|
|
|
{
|
2025-06-09 18:38:28 +01:00
|
|
|
REPORT_AUX( aMessage );
|
2020-09-18 20:57:54 +01:00
|
|
|
return m_drcEngine->ReportPhase( aMessage );
|
2020-06-18 18:55:22 +02:00
|
|
|
}
|
2020-06-18 00:36:54 +02:00
|
|
|
|
2020-09-12 20:28:22 +01:00
|
|
|
|
2025-03-23 20:33:22 +00:00
|
|
|
int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes, const LSET& aLayers,
|
2020-09-12 20:28:22 +01:00
|
|
|
const std::function<bool( BOARD_ITEM*)>& aFunc )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
BOARD *brd = m_drcEngine->GetBoard();
|
2020-08-12 00:15:50 +02:00
|
|
|
std::bitset<MAX_STRUCT_TYPE_ID> typeMask;
|
2025-03-09 15:48:22 +00:00
|
|
|
int n = 0;
|
2020-08-12 00:15:50 +02:00
|
|
|
|
|
|
|
if( aTypes.size() == 0 )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
|
2020-09-12 20:28:22 +01:00
|
|
|
typeMask[ i ] = true;
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-09-12 20:28:22 +01:00
|
|
|
for( KICAD_T aType : aTypes )
|
|
|
|
typeMask[ aType ] = true;
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
|
2021-06-11 22:07:02 +01:00
|
|
|
for( PCB_TRACK* item : brd->Tracks() )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( (item->GetLayerSet() & aLayers).any() )
|
|
|
|
{
|
|
|
|
if( typeMask[ PCB_TRACE_T ] && item->Type() == PCB_TRACE_T )
|
|
|
|
{
|
|
|
|
aFunc( item );
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_VIA_T ] && item->Type() == PCB_VIA_T )
|
|
|
|
{
|
|
|
|
aFunc( item );
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_ARC_T ] && item->Type() == PCB_ARC_T )
|
|
|
|
{
|
|
|
|
aFunc( item );
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
|
2020-09-11 16:04:11 +01:00
|
|
|
for( BOARD_ITEM* item : brd->Drawings() )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( (item->GetLayerSet() & aLayers).any() )
|
2025-03-09 10:46:31 +00:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( typeMask[ PCB_DIMENSION_T ] && BaseType( item->Type() ) == PCB_DIMENSION_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( item ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_SHAPE_T ] && item->Type() == PCB_SHAPE_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( item ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_TEXT_T ] && item->Type() == PCB_TEXT_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( item ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_TEXTBOX_T ] && item->Type() == PCB_TEXTBOX_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( item ) )
|
|
|
|
return n;
|
2025-03-09 10:46:31 +00:00
|
|
|
|
2025-03-09 15:48:22 +00:00
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_TARGET_T ] && item->Type() == PCB_TARGET_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( item ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
2025-03-09 17:50:38 +00:00
|
|
|
else if( item->Type() == PCB_TABLE_T )
|
|
|
|
{
|
|
|
|
if( typeMask[ PCB_TABLE_T ] )
|
|
|
|
{
|
|
|
|
if( !aFunc( item ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( typeMask[ PCB_TABLECELL_T ] )
|
|
|
|
{
|
|
|
|
for( PCB_TABLECELL* cell : static_cast<PCB_TABLE*>( item )->GetCells() )
|
|
|
|
{
|
|
|
|
if( !aFunc( cell ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-03-09 15:48:22 +00:00
|
|
|
}
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
if( typeMask[ PCB_ZONE_T ] )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2020-11-11 23:05:59 +00:00
|
|
|
for( ZONE* item : brd->Zones() )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2020-11-12 23:50:33 +00:00
|
|
|
if( ( item->GetLayerSet() & aLayers ).any() )
|
2020-08-25 19:42:52 +02:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( !aFunc( item ) )
|
|
|
|
return n;
|
|
|
|
|
2020-08-25 19:42:52 +02:00
|
|
|
n++;
|
|
|
|
}
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
for( FOOTPRINT* footprint : brd->Footprints() )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2023-06-24 15:48:27 +01:00
|
|
|
if( typeMask[ PCB_FIELD_T ] )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2023-06-15 15:37:07 -04:00
|
|
|
for( PCB_FIELD* field : footprint->GetFields() )
|
2020-08-25 19:42:52 +02:00
|
|
|
{
|
2023-06-15 15:37:07 -04:00
|
|
|
if( ( field->GetLayerSet() & aLayers ).any() )
|
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( !aFunc( field ) )
|
|
|
|
return n;
|
|
|
|
|
2023-06-15 15:37:07 -04:00
|
|
|
n++;
|
|
|
|
}
|
2020-08-25 19:42:52 +02:00
|
|
|
}
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
|
2020-10-15 11:36:37 +01:00
|
|
|
if( typeMask[ PCB_PAD_T ] )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2020-11-12 23:50:33 +00:00
|
|
|
for( PAD* pad : footprint->Pads() )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2021-01-31 00:30:40 +00:00
|
|
|
// Careful: if a pad has a hole then it pierces all layers
|
2022-07-15 16:14:11 +01:00
|
|
|
if( pad->HasHole() || ( pad->GetLayerSet() & aLayers ).any() )
|
2020-08-25 19:42:52 +02:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( !aFunc( pad ) )
|
|
|
|
return n;
|
|
|
|
|
2020-08-25 19:42:52 +02:00
|
|
|
n++;
|
|
|
|
}
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-12 23:50:33 +00:00
|
|
|
for( BOARD_ITEM* dwg : footprint->GraphicalItems() )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( (dwg->GetLayerSet() & aLayers).any() )
|
2025-03-09 10:46:31 +00:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( typeMask[ PCB_DIMENSION_T ] && BaseType( dwg->Type() ) == PCB_DIMENSION_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( dwg ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_TEXT_T ] && dwg->Type() == PCB_TEXT_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( dwg ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_TEXTBOX_T ] && dwg->Type() == PCB_TEXTBOX_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( dwg ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
else if( typeMask[ PCB_SHAPE_T ] && dwg->Type() == PCB_SHAPE_T )
|
|
|
|
{
|
|
|
|
if( !aFunc( dwg ) )
|
|
|
|
return n;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-30 12:49:23 +01:00
|
|
|
if( typeMask[ PCB_ZONE_T ] )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2020-11-12 23:50:33 +00:00
|
|
|
for( ZONE* zone : footprint->Zones() )
|
2020-08-12 00:15:50 +02:00
|
|
|
{
|
2020-10-15 11:36:37 +01:00
|
|
|
if( (zone->GetLayerSet() & aLayers).any() )
|
2020-08-25 19:42:52 +02:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( !aFunc( zone ) )
|
|
|
|
return n;
|
|
|
|
|
2020-08-25 19:42:52 +02:00
|
|
|
n++;
|
|
|
|
}
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-25 15:02:49 +00:00
|
|
|
|
2020-11-13 12:21:02 +00:00
|
|
|
if( typeMask[ PCB_FOOTPRINT_T ] )
|
2020-10-25 15:02:49 +00:00
|
|
|
{
|
2025-03-09 15:48:22 +00:00
|
|
|
if( !aFunc( footprint ) )
|
|
|
|
return n;
|
|
|
|
|
2020-10-25 15:02:49 +00:00
|
|
|
n++;
|
|
|
|
}
|
2020-08-12 00:15:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
2020-10-02 20:58:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
bool DRC_TEST_PROVIDER::isInvisibleText( const BOARD_ITEM* aItem ) const
|
|
|
|
{
|
2025-02-11 12:55:33 +00:00
|
|
|
if( const PCB_FIELD* field = dynamic_cast<const PCB_FIELD*>( aItem ) )
|
2020-10-02 20:58:12 +02:00
|
|
|
{
|
2025-02-11 12:55:33 +00:00
|
|
|
if( !field->IsVisible() )
|
2020-10-02 20:58:12 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2020-12-29 14:05:09 -05:00
|
|
|
}
|
2022-10-06 21:52:17 +01:00
|
|
|
|
|
|
|
|
2025-06-02 15:27:07 +01:00
|
|
|
wxString DRC_TEST_PROVIDER::formatMsg( const wxString& aFormatString, const wxString& aSource,
|
|
|
|
double aConstraint, double aActual, EDA_DATA_TYPE aType )
|
2022-10-06 21:52:17 +01:00
|
|
|
{
|
2025-02-10 20:57:51 +00:00
|
|
|
wxString constraint_str = MessageTextFromValue( aConstraint, true, aType );
|
|
|
|
wxString actual_str = MessageTextFromValue( aActual, true, aType );
|
2022-10-06 21:52:17 +01:00
|
|
|
|
|
|
|
if( constraint_str == actual_str )
|
|
|
|
{
|
|
|
|
// Use more precise formatting if the message-text strings were equal.
|
2025-02-10 20:57:51 +00:00
|
|
|
constraint_str = StringFromValue( aConstraint, true, aType );
|
|
|
|
actual_str = StringFromValue( aActual, true, aType );
|
2022-10-06 21:52:17 +01:00
|
|
|
}
|
|
|
|
|
2025-06-02 15:27:07 +01:00
|
|
|
return wxString::Format( aFormatString, aSource, std::move( constraint_str ), std::move( actual_str ) );
|
2023-06-06 11:09:34 -04:00
|
|
|
}
|
2024-10-16 19:16:42 +00:00
|
|
|
|
|
|
|
wxString DRC_TEST_PROVIDER::formatMsg( const wxString& aFormatString, const wxString& aSource,
|
|
|
|
const EDA_ANGLE& aConstraint, const EDA_ANGLE& aActual )
|
|
|
|
{
|
|
|
|
wxString constraint_str = MessageTextFromValue( aConstraint );
|
|
|
|
wxString actual_str = MessageTextFromValue( aActual );
|
|
|
|
|
|
|
|
if( constraint_str == actual_str )
|
|
|
|
{
|
|
|
|
// Use more precise formatting if the message-text strings were equal.
|
|
|
|
constraint_str = StringFromValue( aConstraint, true );
|
|
|
|
actual_str = StringFromValue( aActual, true );
|
|
|
|
}
|
|
|
|
|
2025-06-02 15:27:07 +01:00
|
|
|
return wxString::Format( aFormatString, aSource, std::move( constraint_str ), std::move( actual_str ) );
|
2024-10-16 19:16:42 +00:00
|
|
|
}
|