drc_proto: wip

This commit is contained in:
Tomasz Wlostowski 2020-06-14 01:28:08 +02:00
parent af5afb5dd7
commit 085698d17c
33 changed files with 8654 additions and 0 deletions

111
qa/drc_proto/CMakeLists.txt Normal file
View File

@ -0,0 +1,111 @@
#
# This program source code file is part of KiCad, a free EDA CAD application.
#
# Copyright (C) 2017 CERN
# @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
#
# 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
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
find_package( wxWidgets 3.0.0 COMPONENTS gl aui adv html core net base xml stc REQUIRED )
add_definitions(-DBOOST_TEST_DYN_LINK -DPCBNEW -DDRC_PROTO)
if( BUILD_GITHUB_PLUGIN )
set( GITHUB_PLUGIN_LIBRARIES github_plugin )
endif()
add_dependencies( pnsrouter pcbcommon pcad2kicadpcb ${GITHUB_PLUGIN_LIBRARIES} )
add_executable( drc_proto
drc_proto_test.cpp
drc_rule.cpp
drc_rule_parser.cpp
drc_rules_proto_keywords.cpp
drc_test_provider.cpp
drc_clearance_test.cpp
drc_engine.cpp
../qa_utils/mocks.cpp
../pcbnew_utils/board_file_utils.cpp
../qa_utils/stdstream_line_reader.cpp
../../common/base_units.cpp
../../3d-viewer/3d_viewer/3d_viewer_settings.cpp
)
include_directories( BEFORE ${INC_BEFORE} )
include_directories(
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/3d-viewer
${CMAKE_SOURCE_DIR}/common
${CMAKE_SOURCE_DIR}/pcbnew
${CMAKE_SOURCE_DIR}/pcbnew/router
${CMAKE_SOURCE_DIR}/pcbnew/tools
${CMAKE_SOURCE_DIR}/pcbnew/dialogs
${CMAKE_SOURCE_DIR}/polygon
${CMAKE_SOURCE_DIR}/common/geometry
${CMAKE_SOURCE_DIR}/qa/common
${CMAKE_SOURCE_DIR}/qa
${CMAKE_SOURCE_DIR}/qa/qa_utils
${CMAKE_SOURCE_DIR}/qa/qa_utils/include
${CMAKE_SOURCE_DIR}/qa/pcbnew_utils/include
${Boost_INCLUDE_DIR}
${INC_AFTER}
)
target_link_libraries( drc_proto
pnsrouter
common
pcbcommon
bitmaps
pnsrouter
common
pcbcommon
bitmaps
pnsrouter
common
pcbcommon
bitmaps
pnsrouter
common
pcbcommon
bitmaps
gal
pcad2kicadpcb
altium2kicadpcb
common
pcbcommon
${GITHUB_PLUGIN_LIBRARIES}
common
pcbcommon
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
${wxWidgets_LIBRARIES}
)
# auto-generate drc_rules_lexer.h and drc_rules_keywords.cpp
make_lexer(
drc_proto
drc_rules_proto.keywords
drc_rules_proto_lexer.h
drc_rules_proto_keywords.cpp
DRCRULEPROTO_T
)

1433
qa/drc_proto/drc.cpp Normal file

File diff suppressed because it is too large Load Diff

356
qa/drc_proto/drc.h Normal file
View File

@ -0,0 +1,356 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2007-2016 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2017-2019 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_H
#define DRC_H
#include <board_commit.h>
#include <class_board.h>
#include <class_track.h>
#include <class_marker_pcb.h>
#include <geometry/seg.h>
#include <geometry/shape_poly_set.h>
#include <memory>
#include <vector>
#include <tools/pcb_tool_base.h>
/// DRC error codes:
enum PCB_DRC_CODE {
DRCE_FIRST = 1,
DRCE_UNCONNECTED_ITEMS = DRCE_FIRST, ///< items are unconnected
DRCE_TRACK_NEAR_HOLE, ///< thru hole is too close to track
DRCE_TRACK_NEAR_PAD, ///< pad too close to track
DRCE_TRACK_NEAR_VIA, ///< track too close to via
DRCE_TRACK_NEAR_ZONE, ///< track & zone collide or are too close together
DRCE_TRACK_NEAR_COPPER, ///< track & copper graphic collide or are too close
DRCE_VIA_NEAR_VIA, ///< via too close to via
DRCE_VIA_NEAR_TRACK, ///< via too close to track
DRCE_VIA_NEAR_COPPER, ///< via and copper graphic collide or are too close
DRCE_TRACK_ENDS, ///< track ends are too close
DRCE_TRACK_SEGMENTS_TOO_CLOSE, ///< 2 parallel track segments too close: segm ends between segref ends
DRCE_TRACKS_CROSSING, ///< tracks are crossing
DRCE_TRACK_NEAR_EDGE, ///< track too close to board edge
DRCE_VIA_NEAR_EDGE, ///< via too close to board edge
DRCE_PAD_NEAR_EDGE, ///< pad too close to board edge
DRCE_PAD_NEAR_PAD, ///< pad too close to pad
DRCE_PAD_NEAR_COPPER, ///< pad and copper graphic collide or are too close
DRCE_ZONES_INTERSECT, ///< copper area outlines intersect
DRCE_ZONES_TOO_CLOSE, ///< copper area outlines are too close
DRCE_ZONE_HAS_EMPTY_NET, ///< copper area has a net but no pads in nets, which is suspicious
DRCE_DANGLING_VIA, ///< via which isn't connected to anything
DRCE_DANGLING_TRACK, ///< track with at least one end not connected to anything
DRCE_HOLE_NEAR_PAD, ///< hole too close to pad
DRCE_HOLE_NEAR_TRACK, ///< hole too close to track
DRCE_DRILLED_HOLES_TOO_CLOSE, ///< overlapping drilled holes break drill bits
DRCE_TOO_SMALL_TRACK_WIDTH, ///< Too small track width
DRCE_TOO_LARGE_TRACK_WIDTH, ///< Too small track width
DRCE_TOO_SMALL_VIA, ///< Too small via size
DRCE_TOO_SMALL_VIA_ANNULUS, ///< Via size and drill leave annulus too small
DRCE_TOO_SMALL_VIA_DRILL, ///< Too small via drill
DRCE_TOO_SMALL_PAD_DRILL, ///< Too small via drill
DRCE_VIA_HOLE_BIGGER, ///< via's hole is bigger than its diameter
DRCE_MICROVIA_NOT_ALLOWED, ///< micro vias are not allowed
DRCE_MICROVIA_TOO_MANY_LAYERS, ///< micro via's layer pair incorrect (layers must be adjacent)
DRCE_TOO_SMALL_MICROVIA, ///< Too small micro via size
DRCE_TOO_SMALL_MICROVIA_DRILL, ///< Too small micro via drill
DRCE_BURIED_VIA_NOT_ALLOWED, ///< buried vias are not allowed
DRCE_NETCLASS_TRACKWIDTH, ///< netclass has TrackWidth < board.m_designSettings->m_TrackMinWidth
DRCE_NETCLASS_CLEARANCE, ///< netclass has Clearance < board.m_designSettings->m_TrackClearance
DRCE_NETCLASS_VIAANNULUS, ///< netclass ViaSize & ViaDrill leave annulus < board.m_designSettings->m_ViasMinAnnulus
DRCE_NETCLASS_VIASIZE, ///< netclass has ViaSize < board.m_designSettings->m_ViasMinSize
DRCE_NETCLASS_VIADRILLSIZE, ///< netclass has ViaDrillSize < board.m_designSettings->m_MinThroughDrill
DRCE_NETCLASS_uVIASIZE, ///< netclass has ViaSize < board.m_designSettings->m_MicroViasMinSize
DRCE_NETCLASS_uVIADRILLSIZE, ///< netclass has ViaSize < board.m_designSettings->m_MicroViasMinDrill
DRCE_VIA_INSIDE_KEEPOUT,
DRCE_MICROVIA_INSIDE_KEEPOUT,
DRCE_BBVIA_INSIDE_KEEPOUT,
DRCE_TRACK_INSIDE_KEEPOUT,
DRCE_PAD_INSIDE_KEEPOUT,
DRCE_FOOTPRINT_INSIDE_KEEPOUT,
DRCE_HOLE_INSIDE_KEEPOUT,
DRCE_TEXT_INSIDE_KEEPOUT,
DRCE_GRAPHICS_INSIDE_KEEPOUT,
DRCE_OVERLAPPING_FOOTPRINTS, ///< footprint courtyards overlap
DRCE_MISSING_COURTYARD, ///< footprint has no courtyard defined
DRCE_MALFORMED_COURTYARD, ///< footprint has a courtyard but malformed
///< (not convertible to a closed polygon with holes)
DRCE_PTH_IN_COURTYARD,
DRCE_NPTH_IN_COURTYARD,
DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
DRCE_INVALID_OUTLINE, ///< invalid board outline
DRCE_MISSING_FOOTPRINT, ///< footprint not found for netlist item
DRCE_DUPLICATE_FOOTPRINT, ///< more than one footprints found for netlist item
DRCE_EXTRA_FOOTPRINT, ///< netlist item not found for footprint
DRCE_UNRESOLVED_VARIABLE,
DRCE_LAST = DRCE_UNRESOLVED_VARIABLE,
// These are actually Cleanup Tracks and Vias actions, not DRCE errors
CLEANUP_SHORT,
CLEANUP_REDUNDANT_VIA,
CLEANUP_DUPLICATE_TRACK,
CLEANUP_MERGE_TRACKS,
CLEANUP_DANGLING_TRACK,
CLEANUP_DANGLING_VIA,
CLEANUP_ZERO_LENGTH_TRACK,
CLEANUP_TRACK_IN_PAD
};
class PCB_EDIT_FRAME;
class DIALOG_DRC;
class BOARD_ITEM;
class BOARD;
class D_PAD;
class ZONE_CONTAINER;
class TRACK;
class MARKER_PCB;
class DRC_ITEM;
class NETCLASS;
class EDA_TEXT;
class DRAWSEGMENT;
class NETLIST;
class wxWindow;
class wxString;
class wxTextCtrl;
class DRC_ENGINE
{
}
/**
* Design Rule Checker object that performs all the DRC tests. The output of
* the checking goes to the BOARD file in the form of two MARKER lists. Those
* two lists are displayable in the drc dialog box. And they can optionally
* be sent to a text file on disk.
* This class is given access to the windows and the BOARD
* that it needs via its constructor or public access functions.
*/
class DRC : public PCB_TOOL_BASE
{
friend class DIALOG_DRC;
public:
DRC();
~DRC();
/// @copydoc TOOL_INTERACTIVE::Reset()
void Reset( RESET_REASON aReason ) override;
private:
bool m_doUnconnectedTest; // enable unconnected tests
bool m_testTracksAgainstZones; // enable zone to items clearance tests
bool m_doKeepoutTest; // enable keepout areas to items clearance tests
bool m_refillZones; // refill zones if requested (by user).
bool m_reportAllTrackErrors; // Report all tracks errors (or only 4 first errors)
bool m_testFootprints; // Test footprints against schematic
PCB_EDIT_FRAME* m_editFrame; // The pcb frame editor which owns the board
BOARD* m_pcb;
SHAPE_POLY_SET m_board_outlines; // The board outline including cutouts
bool m_board_outline_valid;
DIALOG_DRC* m_drcDialog;
std::vector<DRC_ITEM*> m_unconnected; // list of unconnected pads
std::vector<DRC_ITEM*> m_footprints; // list of footprint warnings
bool m_drcRun; // indicates DRC has been run at least once
bool m_footprintsTested; // indicates footprints were tested in last run
std::vector<DRC_SELECTOR*> m_ruleSelectors;
std::vector<DRC_RULE*> m_rules;
// Temp variables for performance during a single DRC run
//
// wxString's c'tor is surprisingly expensive, and in the world of DRC everything matters
//
wxString m_msg;
wxString m_clearanceSource;
int m_largestClearance;
private:
///> Sets up handlers for various events.
void setTransitions() override;
/**
* Update needed pointers from the one pointer which is known not to change.
*/
void updatePointers();
EDA_UNITS userUnits() const { return m_editFrame->GetUserUnits(); }
/**
* Adds a DRC marker to the PCB through the COMMIT mechanism.
*/
void addMarkerToPcb( BOARD_COMMIT& aCommit, MARKER_PCB* aMarker );
//-----<categorical group tests>-----------------------------------------
/**
* Perform the DRC on all tracks.
*
* This test can take a while, a progress bar can be displayed
* @param aActiveWindow = the active window ued as parent for the progress bar
* @param aShowProgressBar = true to show a progress bar
* (Note: it is shown only if there are many tracks)
*/
void testTracks( BOARD_COMMIT& aCommit, wxWindow * aActiveWindow, bool aShowProgressBar );
void testPadClearances( BOARD_COMMIT& aCommit );
void testUnconnected();
void testZones( BOARD_COMMIT& aCommit );
void testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aDrawing );
void testCopperTextAndGraphics( BOARD_COMMIT& aCommit );
// Tests for items placed on disabled layers (causing false connections).
void testDisabledLayers( BOARD_COMMIT& aCommit );
/**
* Test that the board outline is contiguous and composed of valid elements
*/
void testOutline( BOARD_COMMIT& aCommit );
//-----<single "item" tests>-----------------------------------------
/**
* Test the clearance between aRefPad and other pads.
*
* The pad list must be sorted by x coordinate.
*
* @param aRefPad is the pad to test
* @param aStart is the first pad of the list to test against aRefPad
* @param aEnd is the end of the list and is not included
* @param x_limit is used to stop the test
* (i.e. when the current pad pos X in list exceeds this limit, because the list
* is sorted by X coordinate)
*/
bool doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd,
int x_limit );
/**
* Test the current segment.
*
* @param aRefSeg The segment to test
* @param aStartIt the iterator to the first track to test
* @param aEndIt the marker for the iterator end
* @param aTestZones true if should do copper zones test. This can be very time consumming
* @return bool - true if no problems, else false and m_currentMarker is
* filled in with the problem information.
*/
void doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aStartIt,
TRACKS::iterator aEndIt, bool aTestZones );
//-----<single tests>----------------------------------------------
/**
* @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 aActual [out] it the actual distance (only guaranteed to be set for violations)
* @return bool - true if clearance between aRefPad and aPad is >= aMinClearance, else false
*/
bool checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual );
/**
* 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 );
//-----</single tests>---------------------------------------------
public:
/**
* Load the DRC rules. Must be called after the netclasses have been read.
*/
bool LoadRules();
/**
* Fetches a reasonable point for marking a violoation between two non-point objects.
*/
static wxPoint GetLocation( TRACK* aTrack, ZONE_CONTAINER* aConflictZone );
static wxPoint GetLocation( TRACK* aTrack, const SEG& aConflictSeg );
/**
* Open a dialog and prompts the user, then if a test run button is
* clicked, runs the test(s) and creates the MARKERS. The dialog is only
* created if it is not already in existence.
*
* @param aParent is the parent window for wxWidgets. Usually the PCB editor frame
* but can be another dialog
* if aParent == NULL (default), the parent will be the PCB editor frame
* and the dialog will be not modal (just float on parent
* if aParent is specified, the dialog will be modal.
* The modal mode is mandatory if the dialog is created from another dialog, not
* from the PCB editor frame
*/
void ShowDRCDialog( wxWindow* aParent );
int ShowDRCDialog( const TOOL_EVENT& aEvent );
/**
* Check to see if the DRC dialog is currently shown
*
* @return true if the dialog is shown
*/
bool IsDRCDialogShown();
/**
* Deletes this ui dialog box and zeros out its pointer to remember
* the state of the dialog's existence.
*
* @param aReason Indication of which button was clicked to cause the destruction.
* if aReason == wxID_OK, design parameters values which can be entered from the dialog
* will bbe saved in design parameters list
*/
void DestroyDRCDialog( int aReason );
/**
* Run all the tests specified with a previous call to
* SetSettings()
* @param aMessages = a wxTextControl where to display some activity messages. Can be NULL
*/
void RunTests( wxTextCtrl* aMessages = NULL );
};
#endif // DRC_H

View File

@ -0,0 +1,758 @@
#include <common.h>
#include <class_board.h>
#include <class_drawsegment.h>
#include <class_pad.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/polygon_test_point_inside.h>
#include <geometry/seg.h>
#include <geometry/shape_poly_set.h>
#include <geometry/shape_rect.h>
#include <drc_proto/drc_engine.h>
#include <drc_proto/drc_test_provider.h>
#include <drc_proto/drc_item.h>
#include <drc_proto/drc_rule.h>
namespace test {
class DRC_TEST_PROVIDER_CLEARANCE : public DRC_TEST_PROVIDER
{
public:
DRC_TEST_PROVIDER_CLEARANCE ( DRC_ENGINE *aDrc ) :
DRC_TEST_PROVIDER( aDrc )
{
}
virtual ~DRC_TEST_PROVIDER_CLEARANCE()
{
}
virtual bool Run() override;
virtual const wxString GetName() const override { return "clearance"; };
virtual const wxString GetDescription() const override { return "Tests copper item clearance"; }
virtual std::set<test::DRC_RULE_ID_T> GetMatchingRuleIds() const override;
private:
void testPadClearances( );
bool doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_limit );
bool checkClearanceSegmToPad( const SEG& refSeg, int refSegWidth, const D_PAD* pad,
int minClearance, int* aActualDist );
bool checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual );
bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd,
int aDist, int* aActual );
bool poly2polyDRC( wxPoint* aTref, int aTrefCount, wxPoint* aTtest, int aTtestCount,
int aAllowedDist, int* actualDist );
BOARD* m_board;
int m_largestClearance;
SHAPE_POLY_SET m_boardOutline; // The board outline including cutouts
bool m_boardOutlineValid;
};
};
bool test::DRC_TEST_PROVIDER_CLEARANCE::Run()
{
auto bds = m_drcEngine->GetDesignSettings();
m_board = m_drcEngine->GetBoard();
m_largestClearance = bds->GetBiggestClearanceValue();
testPadClearances();
return false;
}
void test::DRC_TEST_PROVIDER_CLEARANCE::testPadClearances( )
{
auto bds = m_drcEngine->GetDesignSettings();
std::vector<D_PAD*> sortedPads;
m_board->GetSortedPadListByXthenYCoord( sortedPads );
if( sortedPads.empty() )
return;
// find the max size of the pads (used to stop the pad-to-pad tests)
int max_size = 0;
for( D_PAD* pad : sortedPads )
{
// GetBoundingRadius() is the radius of the minimum sized circle fully containing the pad
int radius = pad->GetBoundingRadius();
if( radius > max_size )
max_size = radius;
}
// Better to be fast than accurate; this keeps us from having to look up / calculate the
// actual clearances
max_size += m_largestClearance;
// Upper limit of pad list (limit not included)
D_PAD** listEnd = &sortedPads[0] + sortedPads.size();
// Test the pads
for( auto& pad : sortedPads )
{
#if 0
// fixme: move Board outline clearance to separate provider
if( m_boardOutlineValid )
{
//int minClearance = bds->m_CopperEdgeClearance;
//m_clearanceSource = _( "board edge" );
static DRAWSEGMENT dummyEdge;
dummyEdge.SetLayer( Edge_Cuts );
//if( pad->GetRuleClearance( &dummyEdge, &minClearance, &m_clearanceSource ) )
// /* minClearance and m_clearanceSource set in GetRuleClearance() */;
// FIXME
// auto rule = m_drcEngine->EvalRuleForItems( pad, dummyEdge, DRC_RULE_CLEARANCE );
// min_clearance = rule->Constraint().Min();
int minClearance;
for( auto it = m_boardOutline.IterateSegmentsWithHoles(); it; it++ )
{
int actual;
if( !checkClearanceSegmToPad( *it, 0, pad, minClearance, &actual ) )
{
actual = std::max( 0, actual );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_PAD_NEAR_EDGE );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
/*m_clearanceSource FIXME*/ "" ,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( pad );
MARKER_PCB* marker = nullptr; // fixme new MARKER_PCB( drcItem, pad->GetPosition() );
AddMarkerToPcb( marker );
break;
}
}
}
#endif
//if( !bds->Ignore( DRCE_PAD_NEAR_PAD ) || !bds->Ignore( DRCE_HOLE_NEAR_PAD ) )
{
int x_limit = pad->GetPosition().x + pad->GetBoundingRadius() + max_size;
doPadToPadsDrc( pad, &pad, listEnd, x_limit );
}
}
}
bool test::DRC_TEST_PROVIDER_CLEARANCE::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd,
int x_limit )
{
const static LSET all_cu = LSET::AllCuMask();
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_drcEngine->GetBoard() ); // 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_list<aEnd; ++pad_list )
{
D_PAD* pad = *pad_list;
if( pad == aRefPad )
continue;
// We can stop the test when pad->GetPosition().x > x_limit
// because the list is sorted by X values
if( pad->GetPosition().x > x_limit )
break;
#if 0
// fixme move hole clearance check to another provider
// No problem if pads which are on copper layers are on different copper layers,
// (pads can be only on a technical layer, to build complex pads)
// but their hole (if any ) can create DRC error because they are on all
// copper layers, so we test them
if( ( pad->GetLayerSet() & layerMask ) == 0 &&
( pad->GetLayerSet() & all_cu ) != 0 &&
( aRefPad->GetLayerSet() & all_cu ) != 0 )
{
// if holes are in the same location and have the same size and shape,
// this can be accepted
if( pad->GetPosition() == aRefPad->GetPosition()
&& pad->GetDrillSize() == aRefPad->GetDrillSize()
&& pad->GetDrillShape() == aRefPad->GetDrillShape() )
{
if( aRefPad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
continue;
// for oval holes: must also have the same orientation
if( pad->GetOrientation() == aRefPad->GetOrientation() )
continue;
}
// fixme move hole clearance check to another providers
/* Here, we must test clearance between holes and pads
* dummy pad size and shape is adjusted to pad drill size and shape
*/
if( pad->GetDrillSize().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 = 0; // fixme aRefPad->GetClearance( nullptr, &m_clearanceSource );
auto rule = m_drcEngine->MatchRulesForItems( DRC_RULE_ID_CLEARANCE, pad, &dummypad );
int minClearance = rule->m_Value.Min();
int actual;
if( !checkClearancePadToPad( aRefPad, &dummypad, minClearance, &actual ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_NEAR_PAD );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
/* fixme m_clearanceSource */"",
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( pad, aRefPad );
MARKER_PCB* marker = nullptr; // fixme new MARKER_PCB( drcItem, pad->GetPosition() );
AddMarkerToPcb( marker );
return false;
}
}
if( aRefPad->GetDrillSize().x ) // pad reference has a hole
{
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() );
// FIXME min_clearance = rule->Constraint().Min();
//int minClearance = pad->GetClearance( nullptr, &m_clearanceSource );
int minClearance;
int actual;
if( !checkClearancePadToPad( pad, &dummypad, minClearance, &actual ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_NEAR_PAD );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
/*m_clearanceSource FIXME */ "",
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( aRefPad, pad );
MARKER_PCB* marker = nullptr; // fixme new MARKER_PCB( drcItem, aRefPad->GetPosition() );
AddMarkerToPcb( marker );
return false;
}
}
continue;
}
#endif
// The pad must be in a net (i.e pt_pad->GetNet() != 0 ),
// But no problem if pads have the same netcode (same net)
if( pad->GetNetCode() && ( aRefPad->GetNetCode() == pad->GetNetCode() ) )
continue;
// if pads are from the same footprint
if( pad->GetParent() == aRefPad->GetParent() )
{
// and have the same pad number ( equivalent pads )
// one can argue that this 2nd test is not necessary, that any
// two pads from a single module are acceptable. This 2nd test
// should eventually be a configuration option.
if( pad->PadNameEqual( aRefPad ) )
continue;
}
// if either pad has no drill and is only on technical layers, not a clearance violation
if( ( ( pad->GetLayerSet() & layerMask ) == 0 && !pad->GetDrillSize().x ) ||
( ( aRefPad->GetLayerSet() & layerMask ) == 0 && !aRefPad->GetDrillSize().x ) )
{
continue;
}
auto constraint = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_CLEARANCE, aRefPad, pad );
//int minClearance = aRefPad->GetClearance( pad, *&m_clearanceSource );
int minClearance; // fixme
int clearanceAllowed = minClearance - m_drcEngine->GetDesignSettings()->GetDRCEpsilon();
int actual;
if( !checkClearancePadToPad( aRefPad, pad, clearanceAllowed, &actual ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_PAD_NEAR_PAD );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
/*m_clearanceSource fixme*/ "",
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( aRefPad, pad );
MARKER_PCB* marker = nullptr; // fixme new MARKER_PCB( drcItem, aRefPad->GetPosition() );
AddMarkerToPcb( marker );
return false;
}
}
return true;
}
/*
* Test if distance between a segment and a pad is > minClearance. Return the actual
* distance if it is less.
*/
bool test::DRC_TEST_PROVIDER_CLEARANCE::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.DoCollide( 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;
}
bool test::DRC_TEST_PROVIDER_CLEARANCE::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual )
{
// relativePadPos is the aPad shape position relative to the aRefPad shape position
wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
int center2center = KiROUND( EuclideanNorm( relativePadPos ) );
// Quick test: Clearance is OK if the bounding circles are further away than aMinClearance
if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance )
return true;
/* Here, pads are near and DRC depends on the pad shapes. We must compare distance using
* a fine shape analysis.
* Because a circle or oval shape is the easier shape to test, swap pads to have aRefPad be
* a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. If aRefPad = TRAPEZOID and aPad = RECT, also swap.
*/
bool swap_pads;
swap_pads = false;
// swap pads to make comparisons easier
// Note also a ROUNDRECT pad with a corner radius = r can be considered as
// a smaller RECT (size - 2*r) with a clearance increased by r
// priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
{
// pad ref shape is here oval, rect, roundrect, chamfered rect, trapezoid or custom
switch( aPad->GetShape() )
{
case PAD_SHAPE_CIRCLE:
swap_pads = true;
break;
case PAD_SHAPE_OVAL:
swap_pads = true;
break;
case PAD_SHAPE_RECT:
case PAD_SHAPE_ROUNDRECT:
if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
swap_pads = true;
break;
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_CUSTOM:
break;
}
}
if( swap_pads )
{
std::swap( aRefPad, aPad );
relativePadPos = -relativePadPos;
}
bool diag = true;
if( ( aRefPad->GetShape() == PAD_SHAPE_CIRCLE || aRefPad->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 refPadStart, refPadEnd;
int refPadWidth;
aRefPad->GetOblongGeometry( aRefPad->GetSize(), &refPadStart, &refPadEnd, &refPadWidth );
refPadStart += aRefPad->ShapePos();
refPadEnd += aRefPad->ShapePos();
SEG refPadSeg( refPadStart, refPadEnd );
diag = checkClearanceSegmToPad( refPadSeg, refPadWidth, aPad, aMinClearance, aActual );
}
else
{
int dist_extra = 0;
// corners of aRefPad (used only for rect/roundrect/trap pad)
wxPoint polyref[4];
// corners of aRefPad (used only for custom pad)
SHAPE_POLY_SET polysetref;
if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
{
int padRadius = aRefPad->GetRoundRectCornerRadius();
dist_extra = padRadius;
GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ), aRefPad->GetSize(),
aRefPad->GetOrientation() );
}
else if( aRefPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
{
BOARD* board = aRefPad->GetBoard();
int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
// The reference pad can be rotated. Calculate the rotated coordinates.
// (note, the ref pad position is the origin of coordinates for this drc test)
int padRadius = aRefPad->GetRoundRectCornerRadius();
TransformRoundChamferedRectToPolygon( polysetref, wxPoint( 0, 0 ), aRefPad->GetSize(),
aRefPad->GetOrientation(),
padRadius, aRefPad->GetChamferRectRatio(),
aRefPad->GetChamferPositions(), maxError );
}
else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
{
polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
// The reference pad can be rotated. Calculate the rotated coordinates.
// (note, the ref pad position is the origin of coordinates for this drc test)
aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref, wxPoint( 0, 0 ),
aRefPad->GetOrientation() );
}
else
{
// BuildPadPolygon has meaning for rect a trapeziod shapes and returns the 4 corners.
aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
}
// corners of aPad (used only for rect/roundrect/trap pad)
wxPoint polycompare[4];
// corners of aPad (used only custom pad)
SHAPE_POLY_SET polysetcompare;
switch( aPad->GetShape() )
{
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_RECT:
case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CUSTOM:
if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
{
int padRadius = aPad->GetRoundRectCornerRadius();
dist_extra = padRadius;
GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos, aPad->GetSize(),
aPad->GetOrientation() );
}
else if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
{
BOARD* board = aRefPad->GetBoard();
int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
// The pad to compare can be rotated. Calculate the rotated coordinates.
// ( note, the pad to compare position is the relativePadPos for this drc test)
int padRadius = aPad->GetRoundRectCornerRadius();
TransformRoundChamferedRectToPolygon( polysetcompare, relativePadPos,
aPad->GetSize(), aPad->GetOrientation(),
padRadius, aPad->GetChamferRectRatio(),
aPad->GetChamferPositions(), maxError );
}
else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{
polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
// The pad to compare can be rotated. Calculate the rotated coordinates.
// ( note, the pad to compare position is the relativePadPos for this drc test)
aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare, relativePadPos,
aPad->GetOrientation() );
}
else
{
aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
// Move aPad shape to relativePadPos
for( int ii = 0; ii < 4; ii++ )
polycompare[ii] += relativePadPos;
}
// And now test polygons: We have 3 cases:
// one poly is complex and the other is basic (has only 4 corners)
// both polys are complex
// both polys are basic (have only 4 corners) the most usual case
if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
{
const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
polycompare, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
{
const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC((wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
polyref, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
{
const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC((wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
(wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else
{
if( !poly2polyDRC( polyref, 4, polycompare, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
break;
default:
wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
break;
}
}
return diag;
}
bool test::DRC_TEST_PROVIDER_CLEARANCE::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;
}
/**
* compare 2 convex polygons and return true if distance > aDist (if no error DRC)
* i.e if for each edge of the first polygon distance from each edge of the other polygon
* is >= aDist
*/
bool test::DRC_TEST_PROVIDER_CLEARANCE::poly2polyDRC( wxPoint* aTref, int aTrefCount, wxPoint* aTtest, int aTtestCount,
int aAllowedDist, int* actualDist )
{
/* 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, aTtest[0] ) )
{
*actualDist = 0;
return false;
}
if( TestPointInsidePolygon( aTtest, aTtestCount, aTref[0] ) )
{
*actualDist = 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 = aTtestCount - 1; kk < aTtestCount; ll = kk, kk++ )
{
// for all edges in aTtest
double d;
int intersect = TestForIntersectionOfStraightLineSegments(
aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
aTtest[kk].x, aTtest[kk].y, aTtest[ll].x, aTtest[ll].y,
nullptr, nullptr, &d );
if( intersect )
{
*actualDist = 0;
return false;
}
if( d < aAllowedDist )
{
*actualDist = KiROUND( d );
return false;
}
}
}
return true;
}
std::set<test::DRC_RULE_ID_T> test::DRC_TEST_PROVIDER_CLEARANCE::GetMatchingRuleIds() const
{
return { DRC_RULE_ID_T::DRC_RULE_ID_CLEARANCE };
}
test::DRC_TEST_PROVIDER *drcCreateClearanceTestProvider( test::DRC_ENGINE *engine )
{
return new test::DRC_TEST_PROVIDER_CLEARANCE( engine );
}

View File

@ -0,0 +1,995 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
#include <fctsys.h>
#include <pcb_edit_frame.h>
#include <trigo.h>
#include <pcbnew.h>
#include <drc/drc.h>
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <class_zone.h>
#include <class_drawsegment.h>
#include <class_marker_pcb.h>
#include <math_for_graphics.h>
#include <geometry/polygon_test_point_inside.h>
#include <convert_basic_shapes_to_polygon.h>
#include <board_commit.h>
#include <math/util.h> // for KiROUND
#include <geometry/shape_rect.h>
#include <macros.h>
/**
* compare 2 convex polygons and return true if distance > aDist (if no error DRC)
* 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* aTtest, int aTtestCount,
int aAllowedDist, int* actualDist )
{
/* 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, aTtest[0] ) )
{
*actualDist = 0;
return false;
}
if( TestPointInsidePolygon( aTtest, aTtestCount, aTref[0] ) )
{
*actualDist = 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 = aTtestCount - 1; kk < aTtestCount; ll = kk, kk++ )
{
// for all edges in aTtest
double d;
int intersect = TestForIntersectionOfStraightLineSegments(
aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
aTtest[kk].x, aTtest[kk].y, aTtest[ll].x, aTtest[ll].y,
nullptr, nullptr, &d );
if( intersect )
{
*actualDist = 0;
return false;
}
if( d < aAllowedDist )
{
*actualDist = KiROUND( d );
return false;
}
}
}
return true;
}
/*
* 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;
}
void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aStartIt,
TRACKS::iterator aEndIt, bool aTestZones )
{
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
PCB_LAYER_ID refLayer = aRefSeg->GetLayer();
LSET refLayerSet = aRefSeg->GetLayerSet();
EDA_RECT refSegBB = aRefSeg->GetBoundingBox();
int refSegWidth = aRefSeg->GetWidth();
/******************************************/
/* Phase 0 : via DRC tests : */
/******************************************/
if( aRefSeg->Type() == PCB_VIA_T )
{
VIA *refvia = static_cast<VIA*>( aRefSeg );
int viaAnnulus = ( refvia->GetWidth() - refvia->GetDrill() ) / 2;
int minAnnulus = refvia->GetMinAnnulus( &m_clearanceSource );
// test if the via size is smaller than minimum
if( refvia->GetViaType() == VIATYPE::MICROVIA )
{
if( viaAnnulus < minAnnulus )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_ANNULUS );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minAnnulus, true ),
MessageTextFromValue( userUnits(), viaAnnulus, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
if( refvia->GetWidth() < bds.m_MicroViasMinSize )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), bds.m_MicroViasMinSize, true ),
MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
}
else
{
if( bds.m_ViasMinAnnulus > minAnnulus )
{
minAnnulus = bds.m_ViasMinAnnulus;
m_clearanceSource = _( "board minimum" );
}
if( viaAnnulus < minAnnulus )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_ANNULUS );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minAnnulus, true ),
MessageTextFromValue( userUnits(), viaAnnulus, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
if( refvia->GetWidth() < bds.m_ViasMinSize )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), bds.m_ViasMinSize, true ),
MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
}
// test if via's hole is bigger than its diameter
// This test is necessary since the via hole size and width can be modified
// and a default via hole can be bigger than some vias sizes
if( refvia->GetDrillValue() > refvia->GetWidth() )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_VIA_HOLE_BIGGER );
m_msg.Printf( drcItem->GetErrorText() + _( " (diameter %s; drill %s)" ),
MessageTextFromValue( userUnits(), refvia->GetWidth(), true ),
MessageTextFromValue( userUnits(), refvia->GetDrillValue(), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
// test if the type of via is allowed due to design rules
if( refvia->GetViaType() == VIATYPE::MICROVIA && !bds.m_MicroViasAllowed )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MICROVIA_NOT_ALLOWED );
m_msg.Printf( drcItem->GetErrorText() + _( " (board design rule constraints)" ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
// test if the type of via is allowed due to design rules
if( refvia->GetViaType() == VIATYPE::BLIND_BURIED && !bds.m_BlindBuriedViaAllowed )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_BURIED_VIA_NOT_ALLOWED );
m_msg.Printf( drcItem->GetErrorText() + _( " (board design rule constraints)" ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
// For microvias: test if they are blind vias and only between 2 layers
// because they are used for very small drill size and are drill by laser
// and **only one layer** can be drilled
if( refvia->GetViaType() == VIATYPE::MICROVIA )
{
PCB_LAYER_ID layer1, layer2;
bool err = true;
refvia->LayerPair( &layer1, &layer2 );
if( layer1 > layer2 )
std::swap( layer1, layer2 );
if( layer2 == B_Cu && layer1 == bds.GetCopperLayerCount() - 2 )
err = false;
else if( layer1 == F_Cu && layer2 == In1_Cu )
err = false;
if( err )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MICROVIA_TOO_MANY_LAYERS );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s and %s not adjacent)" ),
m_pcb->GetLayerName( layer1 ),
m_pcb->GetLayerName( layer2 ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
}
}
else // This is a track segment
{
int minWidth, maxWidth;
aRefSeg->GetWidthConstraints( &minWidth, &maxWidth, &m_clearanceSource );
int errorCode = 0;
int constraintWidth;
if( refSegWidth < minWidth )
{
errorCode = DRCE_TOO_SMALL_TRACK_WIDTH;
constraintWidth = minWidth;
}
else if( refSegWidth > maxWidth )
{
errorCode = DRCE_TOO_LARGE_TRACK_WIDTH;
constraintWidth = maxWidth;
}
if( errorCode )
{
wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2;
DRC_ITEM* drcItem = new DRC_ITEM( errorCode );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), constraintWidth, true ),
MessageTextFromValue( userUnits(), refSegWidth, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refsegMiddle );
addMarkerToPcb( aCommit, marker );
}
}
/******************************************/
/* Phase 1 : test DRC track to pads : */
/******************************************/
// Compute the min distance to pads
for( MODULE* mod : m_pcb->Modules() )
{
// Don't preflight at the module level. Getting a module's bounding box goes
// through all its pads anyway (so it's no faster), and also all its drawings
// (so it's in fact slower).
for( D_PAD* pad : mod->Pads() )
{
// Preflight based on bounding boxes.
EDA_RECT inflatedBB = refSegBB;
inflatedBB.Inflate( pad->GetBoundingRadius() + m_largestClearance );
if( !inflatedBB.Contains( pad->GetPosition() ) )
continue;
if( !( pad->GetLayerSet() & refLayerSet ).any() )
continue;
// No need to check pads with the same net as the refSeg.
if( pad->GetNetCode() && aRefSeg->GetNetCode() == pad->GetNetCode() )
continue;
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() );
int minClearance;
DRC_RULE* rule = GetRule( aRefSeg, &dummypad, CLEARANCE_CONSTRAINT );
if( rule )
{
m_clearanceSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
minClearance = rule->m_Clearance.Min;
}
else
{
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 ) )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_HOLE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, pad );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, slotSeg ) );
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
return;
}
}
int minClearance = aRefSeg->GetClearance( pad, &m_clearanceSource );
int clearanceAllowed = minClearance - bds.GetDRCEpsilon();
int actual;
if( !checkClearanceSegmToPad( refSeg, refSegWidth, pad, clearanceAllowed, &actual ) )
{
actual = std::max( 0, actual );
SEG padSeg( pad->GetPosition(), pad->GetPosition() );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_PAD );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, pad );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, padSeg ) );
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
return;
}
}
}
/***********************************************/
/* Phase 2: test DRC with other track segments */
/***********************************************/
// Test the reference segment with other track segments
for( auto it = aStartIt; it != aEndIt; it++ )
{
TRACK* track = *it;
// No problem if segments have the same net code:
if( aRefSeg->GetNetCode() == track->GetNetCode() )
continue;
// No problem if tracks are on different layers:
// Note that while the general case of GetLayerSet intersection always works,
// the others are much faster.
bool sameLayers;
if( aRefSeg->Type() == PCB_VIA_T )
{
if( track->Type() == PCB_VIA_T )
sameLayers = ( refLayerSet & track->GetLayerSet() ).any();
else
sameLayers = refLayerSet.test( track->GetLayer() );
}
else
{
if( track->Type() == PCB_VIA_T )
sameLayers = track->GetLayerSet().test( refLayer );
else
sameLayers = track->GetLayer() == refLayer;
}
if( !sameLayers )
continue;
// Preflight based on worst-case inflated bounding boxes:
EDA_RECT trackBB = track->GetBoundingBox();
trackBB.Inflate( m_largestClearance );
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 );
// Check two tracks crossing first as it reports a DRCE without distances
if( intersection )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACKS_CROSSING );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, track );
MARKER_PCB* marker = new MARKER_PCB( drcItem, (wxPoint) intersection.get() );
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
return;
}
else if( center2center_squared < SEG::Square( center2centerAllowed ) )
{
int errorCode = DRCE_TRACK_ENDS;
if( aRefSeg->Type() == PCB_VIA_T && track->Type() == PCB_VIA_T )
errorCode = DRCE_VIA_NEAR_VIA;
else if( aRefSeg->Type() == PCB_VIA_T || track->Type() == PCB_VIA_T )
errorCode = DRCE_VIA_NEAR_TRACK;
else if( refSeg.ApproxParallel( trackSeg ) )
errorCode = DRCE_TRACK_SEGMENTS_TOO_CLOSE;
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = new DRC_ITEM( errorCode );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, track );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, trackSeg ) );
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
return;
}
}
/***************************************/
/* Phase 3: test DRC with copper zones */
/***************************************/
// Can be *very* time consumming.
if( aTestZones )
{
SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
for( ZONE_CONTAINER* zone : m_pcb->Zones() )
{
if( zone->GetFilledPolysList().IsEmpty() || zone->GetIsKeepout() )
continue;
if( !( refLayerSet & zone->GetLayerSet() ).any() )
continue;
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<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
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 actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_ZONE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, zone );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, zone ) );
addMarkerToPcb( aCommit, marker );
}
}
}
/***********************************************/
/* Phase 4: test DRC with to board edge */
/***********************************************/
if( m_board_outline_valid )
{
int minClearance = bds.m_CopperEdgeClearance;
m_clearanceSource = _( "board edge" );
static DRAWSEGMENT dummyEdge;
dummyEdge.SetLayer( Edge_Cuts );
if( aRefSeg->GetRuleClearance( &dummyEdge, &minClearance, &m_clearanceSource ) )
/* minClearance and m_clearanceSource set in GetRuleClearance() */;
SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
int halfWidth = refSegWidth / 2;
int center2centerAllowed = minClearance + halfWidth;
for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
{
SEG::ecoord center2center_squared = testSeg.SquaredDistance( *it );
if( center2center_squared < SEG::Square( center2centerAllowed ) )
{
VECTOR2I pt = testSeg.NearestPoint( *it );
KICAD_T types[] = { PCB_LINE_T, EOT };
DRAWSEGMENT* edge = nullptr;
INSPECTOR_FUNC inspector =
[&] ( EDA_ITEM* item, void* testData )
{
DRAWSEGMENT* test_edge = dynamic_cast<DRAWSEGMENT*>( item );
if( !test_edge || test_edge->GetLayer() != Edge_Cuts )
return SEARCH_RESULT::CONTINUE;
if( test_edge->HitTest( (wxPoint) pt, minClearance + halfWidth ) )
{
edge = test_edge;
return SEARCH_RESULT::QUIT;
}
return SEARCH_RESULT::CONTINUE;
};
// Best-efforts search for edge segment
BOARD::IterateForward<BOARD_ITEM*>( m_pcb->Drawings(), inspector, nullptr, types );
int actual = std::max( 0.0, sqrt( center2center_squared ) - halfWidth );
int errorCode = ( aRefSeg->Type() == PCB_VIA_T ) ? DRCE_VIA_NEAR_EDGE
: DRCE_TRACK_NEAR_EDGE;
DRC_ITEM* drcItem = new DRC_ITEM( errorCode );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, edge );
MARKER_PCB* marker = new MARKER_PCB( drcItem, (wxPoint) pt );
addMarkerToPcb( aCommit, marker );
}
}
}
}
bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual )
{
// relativePadPos is the aPad shape position relative to the aRefPad shape position
wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
int center2center = KiROUND( EuclideanNorm( relativePadPos ) );
// Quick test: Clearance is OK if the bounding circles are further away than aMinClearance
if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance )
return true;
/* Here, pads are near and DRC depends on the pad shapes. We must compare distance using
* a fine shape analysis.
* Because a circle or oval shape is the easier shape to test, swap pads to have aRefPad be
* a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. If aRefPad = TRAPEZOID and aPad = RECT, also swap.
*/
bool swap_pads;
swap_pads = false;
// swap pads to make comparisons easier
// Note also a ROUNDRECT pad with a corner radius = r can be considered as
// a smaller RECT (size - 2*r) with a clearance increased by r
// priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
{
// pad ref shape is here oval, rect, roundrect, chamfered rect, trapezoid or custom
switch( aPad->GetShape() )
{
case PAD_SHAPE_CIRCLE:
swap_pads = true;
break;
case PAD_SHAPE_OVAL:
swap_pads = true;
break;
case PAD_SHAPE_RECT:
case PAD_SHAPE_ROUNDRECT:
if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
swap_pads = true;
break;
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_CUSTOM:
break;
}
}
if( swap_pads )
{
std::swap( aRefPad, aPad );
relativePadPos = -relativePadPos;
}
bool diag = true;
if( ( aRefPad->GetShape() == PAD_SHAPE_CIRCLE || aRefPad->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 refPadStart, refPadEnd;
int refPadWidth;
aRefPad->GetOblongGeometry( aRefPad->GetSize(), &refPadStart, &refPadEnd, &refPadWidth );
refPadStart += aRefPad->ShapePos();
refPadEnd += aRefPad->ShapePos();
SEG refPadSeg( refPadStart, refPadEnd );
diag = checkClearanceSegmToPad( refPadSeg, refPadWidth, aPad, aMinClearance, aActual );
}
else
{
int dist_extra = 0;
// corners of aRefPad (used only for rect/roundrect/trap pad)
wxPoint polyref[4];
// corners of aRefPad (used only for custom pad)
SHAPE_POLY_SET polysetref;
if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
{
int padRadius = aRefPad->GetRoundRectCornerRadius();
dist_extra = padRadius;
GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ), aRefPad->GetSize(),
aRefPad->GetOrientation() );
}
else if( aRefPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
{
BOARD* board = aRefPad->GetBoard();
int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
// The reference pad can be rotated. Calculate the rotated coordinates.
// (note, the ref pad position is the origin of coordinates for this drc test)
int padRadius = aRefPad->GetRoundRectCornerRadius();
TransformRoundChamferedRectToPolygon( polysetref, wxPoint( 0, 0 ), aRefPad->GetSize(),
aRefPad->GetOrientation(),
padRadius, aRefPad->GetChamferRectRatio(),
aRefPad->GetChamferPositions(), maxError );
}
else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
{
polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
// The reference pad can be rotated. Calculate the rotated coordinates.
// (note, the ref pad position is the origin of coordinates for this drc test)
aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref, wxPoint( 0, 0 ),
aRefPad->GetOrientation() );
}
else
{
// BuildPadPolygon has meaning for rect a trapeziod shapes and returns the 4 corners.
aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
}
// corners of aPad (used only for rect/roundrect/trap pad)
wxPoint polycompare[4];
// corners of aPad (used only custom pad)
SHAPE_POLY_SET polysetcompare;
switch( aPad->GetShape() )
{
case PAD_SHAPE_ROUNDRECT:
case PAD_SHAPE_RECT:
case PAD_SHAPE_CHAMFERED_RECT:
case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_CUSTOM:
if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
{
int padRadius = aPad->GetRoundRectCornerRadius();
dist_extra = padRadius;
GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos, aPad->GetSize(),
aPad->GetOrientation() );
}
else if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
{
BOARD* board = aRefPad->GetBoard();
int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
// The pad to compare can be rotated. Calculate the rotated coordinates.
// ( note, the pad to compare position is the relativePadPos for this drc test)
int padRadius = aPad->GetRoundRectCornerRadius();
TransformRoundChamferedRectToPolygon( polysetcompare, relativePadPos,
aPad->GetSize(), aPad->GetOrientation(),
padRadius, aPad->GetChamferRectRatio(),
aPad->GetChamferPositions(), maxError );
}
else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
{
polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
// The pad to compare can be rotated. Calculate the rotated coordinates.
// ( note, the pad to compare position is the relativePadPos for this drc test)
aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare, relativePadPos,
aPad->GetOrientation() );
}
else
{
aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
// Move aPad shape to relativePadPos
for( int ii = 0; ii < 4; ii++ )
polycompare[ii] += relativePadPos;
}
// And now test polygons: We have 3 cases:
// one poly is complex and the other is basic (has only 4 corners)
// both polys are complex
// both polys are basic (have only 4 corners) the most usual case
if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
{
const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
polycompare, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
{
const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC((wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
polyref, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
{
const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
// And now test polygons:
if( !poly2polyDRC((wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
(wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
else
{
if( !poly2polyDRC( polyref, 4, polycompare, 4, aMinClearance + dist_extra, aActual ) )
{
*aActual = std::max( 0, *aActual - dist_extra );
diag = false;
}
}
break;
default:
wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
break;
}
}
return diag;
}
/*
* 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.DoCollide( 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;
}

View File

@ -0,0 +1,189 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2010 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2004-2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
#include <drc/drc_courtyard_tester.h>
#include <class_module.h>
#include <drc/drc.h>
#include <widgets/ui_common.h>
#include <memory>
DRC_COURTYARD_TESTER::DRC_COURTYARD_TESTER( MARKER_HANDLER aMarkerHandler ) :
DRC_TEST_PROVIDER( std::move( aMarkerHandler ) )
{
}
bool DRC_COURTYARD_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard )
{
// Detects missing (or malformed) footprint courtyards and courtyard incursions (for those
// with a courtyard).
wxString msg;
bool success = true;
// Update courtyard polygons, and test for missing courtyard definition:
for( MODULE* footprint : aBoard.Modules() )
{
if( footprint->BuildPolyCourtyard() )
{
if( !aBoard.GetDesignSettings().Ignore( DRCE_MISSING_COURTYARD )
&& footprint->GetPolyCourtyardFront().OutlineCount() == 0
&& footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MISSING_COURTYARD );
drcItem->SetItems( footprint );
HandleMarker( new MARKER_PCB( drcItem, footprint->GetPosition() ) );
success = false;
}
else
{
footprint->GetPolyCourtyardFront().BuildBBoxCaches();
footprint->GetPolyCourtyardBack().BuildBBoxCaches();
}
}
else
{
if( !aBoard.GetDesignSettings().Ignore( DRCE_MALFORMED_COURTYARD ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_MALFORMED_COURTYARD );
msg.Printf( drcItem->GetErrorText() + _( " (not a closed shape)" ) );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( footprint );
HandleMarker( new MARKER_PCB( drcItem, footprint->GetPosition() ) );
success = false;
}
}
}
if( !aBoard.GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) )
{
for( auto it1 = aBoard.Modules().begin(); it1 != aBoard.Modules().end(); it1++ )
{
MODULE* footprint = *it1;
SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront();
SHAPE_POLY_SET& footprintBack = footprint->GetPolyCourtyardBack();
if( footprintFront.OutlineCount() == 0 && footprintBack.OutlineCount() == 0 )
continue; // No courtyards defined
for( auto it2 = it1 + 1; it2 != aBoard.Modules().end(); it2++ )
{
MODULE* test = *it2;
SHAPE_POLY_SET& testFront = test->GetPolyCourtyardFront();
SHAPE_POLY_SET& testBack = test->GetPolyCourtyardBack();
SHAPE_POLY_SET intersection;
bool overlap = false;
wxPoint pos;
if( footprintFront.OutlineCount() > 0 && testFront.OutlineCount() > 0
&& footprintFront.BBoxFromCaches().Intersects( testFront.BBoxFromCaches() ) )
{
intersection.RemoveAllContours();
intersection.Append( footprintFront );
// Build the common area between footprint and the test:
intersection.BooleanIntersection( testFront, SHAPE_POLY_SET::PM_FAST );
// If the intersection exists then they overlap
if( intersection.OutlineCount() > 0 )
{
overlap = true;
pos = (wxPoint) intersection.CVertex( 0, 0, -1 );
}
}
if( footprintBack.OutlineCount() > 0 && testBack.OutlineCount() > 0
&& footprintBack.BBoxFromCaches().Intersects( testBack.BBoxFromCaches() ) )
{
intersection.RemoveAllContours();
intersection.Append( footprintBack );
intersection.BooleanIntersection( testBack, SHAPE_POLY_SET::PM_FAST );
if( intersection.OutlineCount() > 0 )
{
overlap = true;
pos = (wxPoint) intersection.CVertex( 0, 0, -1 );
}
}
if( overlap )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_OVERLAPPING_FOOTPRINTS );
drcItem->SetItems( footprint, test );
HandleMarker( new MARKER_PCB( drcItem, pos ) );
success = false;
}
}
}
}
if( !aBoard.GetDesignSettings().Ignore( DRCE_PTH_IN_COURTYARD )
|| !aBoard.GetDesignSettings().Ignore( DRCE_NPTH_IN_COURTYARD ) )
{
for( MODULE* footprint : aBoard.Modules() )
{
SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront();
SHAPE_POLY_SET& footprintBack = footprint->GetPolyCourtyardBack();
if( footprintFront.OutlineCount() == 0 && footprintBack.OutlineCount() == 0 )
continue; // No courtyards defined
for( MODULE* candidate : aBoard.Modules() )
{
if( footprint == candidate )
continue;
for( D_PAD* pad : candidate->Pads() )
{
if( pad->GetDrillSize().x == 0 || pad->GetDrillSize().y == 0 )
continue;
wxPoint pos = pad->GetPosition();
if( footprintFront.Contains( pos, -1, 0, true /* use bbox caches */ )
|| footprintBack.Contains( pos, -1, 0, true /* use bbox caches */ ) )
{
int code = pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED ?
DRCE_NPTH_IN_COURTYARD :
DRCE_PTH_IN_COURTYARD;
DRC_ITEM* drcItem = new DRC_ITEM( code );
drcItem->SetItems( footprint, pad );
HandleMarker( new MARKER_PCB( drcItem, pos ) );
success = false;
}
}
}
}
}
return success;
}

View File

@ -0,0 +1,43 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_COURTYARD_OVERLAP__H
#define DRC_COURTYARD_OVERLAP__H
#include <drc/drc_provider.h>
class BOARD;
class DRC_COURTYARD_TESTER : public DRC_TEST_PROVIDER
{
public:
DRC_COURTYARD_TESTER( MARKER_HANDLER aMarkerHandler );
virtual ~DRC_COURTYARD_TESTER() {};
bool RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) override;
};
#endif // DRC_COURTYARD_OVERLAP__H

View File

@ -0,0 +1,283 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
#include <drc/drc_drilled_hole_tester.h>
#include <class_module.h>
#include <drc/drc.h>
#include <widgets/ui_common.h>
DRC_DRILLED_HOLE_TESTER::DRC_DRILLED_HOLE_TESTER( MARKER_HANDLER aMarkerHandler ) :
DRC_TEST_PROVIDER( std::move( aMarkerHandler ) ),
m_units( EDA_UNITS::MILLIMETRES ),
m_board( nullptr ),
m_largestRadius( 0 )
{
}
bool DRC_DRILLED_HOLE_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard )
{
bool success = true;
// Test drilled holes to minimize drill bit breakage.
//
// Check pad & std. via circular holes for hole-to-hole-min (non-circular holes are milled)
// Check pad & std. via holes for via-min-drill (minimum hole classification)
// Check uvia holes for uvia-min-drill (laser drill classification)
m_units = aUnits;
m_board = &aBoard;
m_holes.clear();
m_largestRadius = 0;
for( MODULE* mod : aBoard.Modules() )
{
for( D_PAD* pad : mod->Pads( ) )
success &= checkPad( pad );
}
for( TRACK* track : aBoard.Tracks() )
{
VIA* via = dynamic_cast<VIA*>( track );
if( via )
{
if( via->GetViaType() == VIATYPE::MICROVIA )
success &= checkMicroVia( via );
else
success &= checkVia( via );
}
}
success &= checkHoles();
return success;
}
bool DRC_DRILLED_HOLE_TESTER::checkPad( D_PAD* aPad )
{
bool success = true;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
int holeSize = std::min( aPad->GetDrillSize().x, aPad->GetDrillSize().y );
if( holeSize == 0 )
return true;
if( !bds.Ignore( DRCE_TOO_SMALL_PAD_DRILL ) )
{
int minHole = bds.m_MinThroughDrill;
wxString minHoleSource = _( "board minimum" );
DRC_RULE* rule = GetRule( aPad, nullptr, HOLE_CONSTRAINT );
if( rule )
{
minHole = rule->m_MinHole;
minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
}
if( holeSize < minHole )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_PAD_DRILL );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
minHoleSource,
MessageTextFromValue( m_units, minHole, true ),
MessageTextFromValue( m_units, holeSize, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aPad );
HandleMarker( new MARKER_PCB( drcItem, aPad->GetPosition() ) );
success = false;
}
}
if( !bds.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) && bds.m_HoleToHoleMin != 0 )
{
if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
addHole( aPad->GetPosition(), aPad->GetDrillSize().x / 2, aPad );
}
return success;
}
bool DRC_DRILLED_HOLE_TESTER::checkVia( VIA* via )
{
bool success = true;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
if( !bds.Ignore( DRCE_TOO_SMALL_VIA_DRILL ) )
{
int minHole = bds.m_MinThroughDrill;
wxString minHoleSource = _( "board minimum" );
DRC_RULE* rule = GetRule( via, nullptr, HOLE_CONSTRAINT );
if( rule )
{
minHole = rule->m_MinHole;
minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
}
if( via->GetDrillValue() < minHole )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_DRILL );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
minHoleSource,
MessageTextFromValue( m_units, minHole, true ),
MessageTextFromValue( m_units, via->GetDrillValue(), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( via );
HandleMarker( new MARKER_PCB( drcItem, via->GetPosition() ) );
success = false;
}
}
if( !bds.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) && bds.m_HoleToHoleMin != 0 )
{
addHole( via->GetPosition(), via->GetDrillValue() / 2, via );
}
return success;
}
bool DRC_DRILLED_HOLE_TESTER::checkMicroVia( VIA* via )
{
bool success = true;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
if( !bds.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL ) )
{
int minHole = bds.m_MicroViasMinDrill;
wxString minHoleSource = _( "board minimum" );
DRC_RULE* rule = GetRule( via, nullptr, HOLE_CONSTRAINT );
if( rule )
{
minHole = rule->m_MinHole;
minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
}
if( via->GetDrillValue() < minHole )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA_DRILL );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
minHoleSource,
MessageTextFromValue( m_units, minHole, true ),
MessageTextFromValue( m_units, via->GetDrillValue(), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( via );
HandleMarker( new MARKER_PCB( drcItem, via->GetPosition() ) );
success = false;
}
}
return success;
}
void DRC_DRILLED_HOLE_TESTER::addHole( const wxPoint& aLocation, int aRadius, BOARD_ITEM* aOwner )
{
DRILLED_HOLE hole;
hole.m_location = aLocation;
hole.m_drillRadius = aRadius;
hole.m_owner = aOwner;
m_largestRadius = std::max( m_largestRadius, aRadius );
m_holes.push_back( hole );
}
bool DRC_DRILLED_HOLE_TESTER::checkHoles()
{
bool success = true;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
// No need to check if we're ignoring DRCE_DRILLED_HOLES_TOO_CLOSE; if we are then we
// won't have collected any holes to test.
// Sort holes by X for performance. In the nested iteration we then need to look at
// following holes only while they are within the refHole's neighborhood as defined by
// the refHole radius + the minimum hole-to-hole clearance + the largest radius any of
// the following holes can have.
std::sort( m_holes.begin(), m_holes.end(),
[]( const DRILLED_HOLE& a, const DRILLED_HOLE& b )
{
if( a.m_location.x == b.m_location.x )
return a.m_location.y < b.m_location.y;
else
return a.m_location.x < b.m_location.x;
} );
for( size_t ii = 0; ii < m_holes.size(); ++ii )
{
const DRILLED_HOLE& refHole = m_holes[ ii ];
int neighborhood = refHole.m_drillRadius + bds.m_HoleToHoleMin + m_largestRadius;
for( size_t jj = ii + 1; jj < m_holes.size(); ++jj )
{
const DRILLED_HOLE& checkHole = m_holes[ jj ];
if( refHole.m_location.x + neighborhood < checkHole.m_location.x )
break;
// Holes with identical locations are allowable
if( checkHole.m_location == refHole.m_location )
continue;
int actual = KiROUND( GetLineLength( checkHole.m_location, refHole.m_location ) );
actual = std::max( 0, actual - checkHole.m_drillRadius - refHole.m_drillRadius );
if( actual < bds.m_HoleToHoleMin )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_DRILLED_HOLES_TOO_CLOSE );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( m_units, bds.m_HoleToHoleMin, true ),
MessageTextFromValue( m_units, actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refHole.m_owner, checkHole.m_owner );
HandleMarker( new MARKER_PCB( drcItem, refHole.m_location ) );
success = false;
}
}
}
return success;
}

View File

@ -0,0 +1,68 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_DRILLED_HOLE_TESTER__H
#define DRC_DRILLED_HOLE_TESTER__H
#include <drc/drc_provider.h>
class BOARD;
class BOARD_ITEM;
class DRC_DRILLED_HOLE_TESTER : public DRC_TEST_PROVIDER
{
public:
DRC_DRILLED_HOLE_TESTER( MARKER_HANDLER aMarkerHandler );
virtual ~DRC_DRILLED_HOLE_TESTER() {};
bool RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) override;
private:
bool checkPad( D_PAD* aPad );
bool checkVia( VIA* aVia );
bool checkMicroVia( VIA* aVia );
void addHole( const wxPoint& aLocation, int aRadius, BOARD_ITEM* aOwner );
bool checkHoles();
private:
struct DRILLED_HOLE
{
wxPoint m_location;
int m_drillRadius = 0;
BOARD_ITEM* m_owner = nullptr;
};
EDA_UNITS m_units;
BOARD* m_board;
std::vector<DRILLED_HOLE> m_holes;
int m_largestRadius;
wxString m_msg; // Construct only once for performance
};
#endif // DRC_DRILLED_HOLE_TESTER__H

1419
qa/drc_proto/drc_engine.cpp Normal file

File diff suppressed because it is too large Load Diff

232
qa/drc_proto/drc_engine.h Normal file
View File

@ -0,0 +1,232 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2007-2016 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2017-2019 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_ENGINE_H
#define DRC_ENGINE_H
#include <board_commit.h>
#include <class_board.h>
#include <class_marker_pcb.h>
#include <class_track.h>
#include <geometry/seg.h>
#include <geometry/shape_poly_set.h>
#include <memory>
#include <tools/pcb_tool_base.h>
#include <vector>
#include <drc_proto/drc_rule.h>
class BOARD_DESIGN_SETTINGS;
class PCB_EDIT_FRAME;
class DIALOG_DRC;
class BOARD_ITEM;
class BOARD;
class D_PAD;
class ZONE_CONTAINER;
class TRACK;
class MARKER_PCB;
class NETCLASS;
class EDA_TEXT;
class DRAWSEGMENT;
class NETLIST;
class wxWindow;
class wxString;
class wxTextCtrl;
class PROGRESS_REPORTER;
namespace test
{
class DRC_RULE_CONDITION;
class DRC_ITEM;
class DRC_RULE;
class DRC_TEST_PROVIDER;
class DRC_CONSTRAINT;
/// DRC error codes:
enum PCB_DRC_CODE
{
DRCE_FIRST = 1,
DRCE_UNCONNECTED_ITEMS = DRCE_FIRST, ///< items are unconnected
DRCE_TRACK_NEAR_HOLE, ///< thru hole is too close to track
DRCE_TRACK_NEAR_PAD, ///< pad too close to track
DRCE_TRACK_NEAR_VIA, ///< track too close to via
DRCE_TRACK_NEAR_ZONE, ///< track & zone collide or are too close together
DRCE_TRACK_NEAR_COPPER, ///< track & copper graphic collide or are too close
DRCE_VIA_NEAR_VIA, ///< via too close to via
DRCE_VIA_NEAR_TRACK, ///< via too close to track
DRCE_VIA_NEAR_COPPER, ///< via and copper graphic collide or are too close
DRCE_TRACK_ENDS, ///< track ends are too close
DRCE_TRACK_SEGMENTS_TOO_CLOSE, ///< 2 parallel track segments too close: segm ends between segref ends
DRCE_TRACKS_CROSSING, ///< tracks are crossing
DRCE_TRACK_NEAR_EDGE, ///< track too close to board edge
DRCE_VIA_NEAR_EDGE, ///< via too close to board edge
DRCE_PAD_NEAR_EDGE, ///< pad too close to board edge
DRCE_PAD_NEAR_PAD, ///< pad too close to pad
DRCE_PAD_NEAR_COPPER, ///< pad and copper graphic collide or are too close
DRCE_ZONES_INTERSECT, ///< copper area outlines intersect
DRCE_ZONES_TOO_CLOSE, ///< copper area outlines are too close
DRCE_ZONE_HAS_EMPTY_NET, ///< copper area has a net but no pads in nets, which is suspicious
DRCE_DANGLING_VIA, ///< via which isn't connected to anything
DRCE_DANGLING_TRACK, ///< track with at least one end not connected to anything
DRCE_HOLE_NEAR_PAD, ///< hole too close to pad
DRCE_HOLE_NEAR_TRACK, ///< hole too close to track
DRCE_DRILLED_HOLES_TOO_CLOSE, ///< overlapping drilled holes break drill bits
DRCE_TOO_SMALL_TRACK_WIDTH, ///< Too small track width
DRCE_TOO_LARGE_TRACK_WIDTH, ///< Too small track width
DRCE_TOO_SMALL_VIA, ///< Too small via size
DRCE_TOO_SMALL_VIA_ANNULUS, ///< Via size and drill leave annulus too small
DRCE_TOO_SMALL_VIA_DRILL, ///< Too small via drill
DRCE_TOO_SMALL_PAD_DRILL, ///< Too small via drill
DRCE_VIA_HOLE_BIGGER, ///< via's hole is bigger than its diameter
DRCE_MICROVIA_NOT_ALLOWED, ///< micro vias are not allowed
DRCE_MICROVIA_TOO_MANY_LAYERS, ///< micro via's layer pair incorrect (layers must be adjacent)
DRCE_TOO_SMALL_MICROVIA, ///< Too small micro via size
DRCE_TOO_SMALL_MICROVIA_DRILL, ///< Too small micro via drill
DRCE_BURIED_VIA_NOT_ALLOWED, ///< buried vias are not allowed
DRCE_NETCLASS_TRACKWIDTH, ///< netclass has TrackWidth < board.m_designSettings->m_TrackMinWidth
DRCE_NETCLASS_CLEARANCE, ///< netclass has Clearance < board.m_designSettings->m_TrackClearance
DRCE_NETCLASS_VIAANNULUS, ///< netclass ViaSize & ViaDrill leave annulus < board.m_designSettings->m_ViasMinAnnulus
DRCE_NETCLASS_VIASIZE, ///< netclass has ViaSize < board.m_designSettings->m_ViasMinSize
DRCE_NETCLASS_VIADRILLSIZE, ///< netclass has ViaDrillSize < board.m_designSettings->m_MinThroughDrill
DRCE_NETCLASS_uVIASIZE, ///< netclass has ViaSize < board.m_designSettings->m_MicroViasMinSize
DRCE_NETCLASS_uVIADRILLSIZE, ///< netclass has ViaSize < board.m_designSettings->m_MicroViasMinDrill
DRCE_VIA_INSIDE_KEEPOUT,
DRCE_MICROVIA_INSIDE_KEEPOUT,
DRCE_BBVIA_INSIDE_KEEPOUT,
DRCE_TRACK_INSIDE_KEEPOUT,
DRCE_PAD_INSIDE_KEEPOUT,
DRCE_FOOTPRINT_INSIDE_KEEPOUT,
DRCE_HOLE_INSIDE_KEEPOUT,
DRCE_TEXT_INSIDE_KEEPOUT,
DRCE_GRAPHICS_INSIDE_KEEPOUT,
DRCE_OVERLAPPING_FOOTPRINTS, ///< footprint courtyards overlap
DRCE_MISSING_COURTYARD, ///< footprint has no courtyard defined
DRCE_MALFORMED_COURTYARD, ///< footprint has a courtyard but malformed
///< (not convertible to a closed polygon with holes)
DRCE_PTH_IN_COURTYARD,
DRCE_NPTH_IN_COURTYARD,
DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
DRCE_INVALID_OUTLINE, ///< invalid board outline
DRCE_MISSING_FOOTPRINT, ///< footprint not found for netlist item
DRCE_DUPLICATE_FOOTPRINT, ///< more than one footprints found for netlist item
DRCE_EXTRA_FOOTPRINT, ///< netlist item not found for footprint
DRCE_UNRESOLVED_VARIABLE,
DRCE_LAST = DRCE_UNRESOLVED_VARIABLE,
// These are actually Cleanup Tracks and Vias actions, not DRCE errors
CLEANUP_SHORT,
CLEANUP_REDUNDANT_VIA,
CLEANUP_DUPLICATE_TRACK,
CLEANUP_MERGE_TRACKS,
CLEANUP_DANGLING_TRACK,
CLEANUP_DANGLING_VIA,
CLEANUP_ZERO_LENGTH_TRACK,
CLEANUP_TRACK_IN_PAD
};
/**
* Design Rule Checker object that performs all the DRC tests. The output of
* the checking goes to the BOARD file in the form of two MARKER lists. Those
* two lists are displayable in the drc dialog box. And they can optionally
* be sent to a text file on disk.
* This class is given access to the windows and the BOARD
* that it needs via its constructor or public access functions.
*/
class DRC_ENGINE
{
public:
DRC_ENGINE( BOARD* aBoard, BOARD_DESIGN_SETTINGS* aSettings );
~DRC_ENGINE();
void SetProgressReporter( PROGRESS_REPORTER* aProgRep );
bool LoadRules( wxFileName aPath );
void RunTests();
void SetErrorLimit( int aLimit );
BOARD_DESIGN_SETTINGS* GetDesignSettings() const
{
return m_designSettings;
}
BOARD* GetBoard() const
{
return m_board;
}
DRC_CONSTRAINT EvalRulesForItems(
DRC_RULE_ID_T ruleID, const BOARD_ITEM* a, const BOARD_ITEM* b = nullptr );
//void IterateOverMatchingRules( DRC_RULE_ID_IT ruleID );
EDA_UNITS UserUnits() const
{
return EDA_UNITS::MILLIMETRES;
}
bool CompileRules();
private:
struct RULE_WITH_CONDITIONS
{
std::vector<test::DRC_RULE_CONDITION*> conditions;
test::DRC_RULE* rule;
};
struct RULE_SET
{
std::vector<RULE_WITH_CONDITIONS*> sortedRules;
DRC_TEST_PROVIDER* provider;
};
typedef std::unordered_map<test::DRC_RULE_ID_T, RULE_SET*> RULE_MAP;
void inferImplicitRules();
void loadTestProviders();
BOARD_DESIGN_SETTINGS* m_designSettings;
BOARD* m_board;
std::vector<DRC_ITEM*> m_drcItems;
std::vector<DRC_RULE_CONDITION*> m_ruleConditions;
std::vector<DRC_RULE*> m_rules;
std::vector<DRC_TEST_PROVIDER*> m_testProviders;
std::vector<::MARKER_PCB*> m_markers;
RULE_MAP m_ruleMap;
// condition -> rule -> provider
};
}; // namespace test
#endif // DRC_H

207
qa/drc_proto/drc_item.cpp Normal file
View File

@ -0,0 +1,207 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2015-2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#include <fctsys.h>
#include <common.h>
#include "wx/html/m_templ.h"
#include "wx/html/styleparams.h"
#include <drc/drc.h>
#include <drc/drc_item.h>
#include <class_board.h>
DRC_ITEM::DRC_ITEM( int aErrorCode )
{
m_errorCode = aErrorCode;
}
DRC_ITEM::DRC_ITEM( const wxString& aErrorText )
{
for( int errorCode = DRCE_FIRST; errorCode <= DRCE_LAST; ++errorCode )
{
if( aErrorText == GetErrorText( errorCode, false ) )
{
m_errorCode = errorCode;
break;
}
}
}
wxString DRC_ITEM::GetErrorText( int aCode, bool aTranslate ) const
{
wxString msg;
if( aCode < 0 )
aCode = m_errorCode;
switch( aCode )
{
case DRCE_UNCONNECTED_ITEMS: msg = _HKI( "Unconnected items" ); break;
case DRCE_TRACK_NEAR_HOLE: msg = _HKI( "Track too close to hole" ); break;
case DRCE_TRACK_NEAR_PAD: msg = _HKI( "Track too close to pad" ); break;
case DRCE_TRACK_NEAR_VIA: msg = _HKI( "Track too close to via" ); break;
case DRCE_VIA_NEAR_VIA: msg = _HKI( "Vias too close" ); break;
case DRCE_VIA_NEAR_TRACK: msg = _HKI( "Via too close to track" ); break;
case DRCE_TRACK_ENDS: msg = _HKI( "Track ends too close" ); break;
case DRCE_TRACK_SEGMENTS_TOO_CLOSE: msg = _HKI( "Parallel tracks too close" ); break;
case DRCE_TRACKS_CROSSING: msg = _HKI( "Tracks crossing" ); break;
case DRCE_TRACK_NEAR_ZONE: msg = _HKI( "Track too close to copper area" ); break;
case DRCE_PAD_NEAR_PAD: msg = _HKI( "Pads too close" ); break;
case DRCE_VIA_HOLE_BIGGER: msg = _HKI( "Via hole larger than diameter" ); break;
case DRCE_MICROVIA_TOO_MANY_LAYERS: msg = _HKI( "Micro via through too many layers" ); break;
case DRCE_MICROVIA_NOT_ALLOWED: msg = _HKI( "Micro via not allowed" ); break;
case DRCE_BURIED_VIA_NOT_ALLOWED: msg = _HKI( "Buried via not allowed" ); break;
case DRCE_DISABLED_LAYER_ITEM: msg = _HKI( "Item on a disabled layer" ); break;
case DRCE_ZONES_INTERSECT: msg = _HKI( "Copper areas intersect" ); break;
case DRCE_ZONES_TOO_CLOSE: msg = _HKI( "Copper areas too close" ); break;
case DRCE_ZONE_HAS_EMPTY_NET: msg = _HKI( "Copper zone net has no pads" ); break;
case DRCE_DANGLING_VIA: msg = _HKI( "Via is not connected" ); break;
case DRCE_DANGLING_TRACK: msg = _HKI( "Track has unconnected end" ); break;
case DRCE_HOLE_NEAR_PAD: msg = _HKI( "Hole too close to pad" ); break;
case DRCE_HOLE_NEAR_TRACK: msg = _HKI( "Hole too close to track" ); break;
case DRCE_TOO_SMALL_TRACK_WIDTH: msg = _HKI( "Track width too small" ); break;
case DRCE_TOO_LARGE_TRACK_WIDTH: msg = _HKI( "Track width too large" ); break;
case DRCE_TOO_SMALL_VIA: msg = _HKI( "Via size too small" ); break;
case DRCE_TOO_SMALL_VIA_ANNULUS: msg = _HKI( "Via annulus too small" ); break;
case DRCE_TOO_SMALL_MICROVIA: msg = _HKI( "Micro via size too small" ); break;
case DRCE_TOO_SMALL_VIA_DRILL: msg = _HKI( "Via drill too small" ); break;
case DRCE_TOO_SMALL_PAD_DRILL: msg = _HKI( "Pad drill too small" ); break;
case DRCE_TOO_SMALL_MICROVIA_DRILL: msg = _HKI( "Micro via drill too small" ); break;
case DRCE_DRILLED_HOLES_TOO_CLOSE: msg = _HKI( "Drilled holes too close together" ); break;
case DRCE_TRACK_NEAR_EDGE: msg = _HKI( "Track too close to board edge" ); break;
case DRCE_VIA_NEAR_EDGE: msg = _HKI( "Via too close to board edge" ); break;
case DRCE_PAD_NEAR_EDGE: msg = _HKI( "Pad too close to board edge" ); break;
case DRCE_INVALID_OUTLINE: msg = _HKI( "Board has malformed outline" ); break;
case DRCE_NETCLASS_TRACKWIDTH: msg = _HKI( "NetClass Track Width too small" ); break;
case DRCE_NETCLASS_CLEARANCE: msg = _HKI( "NetClass Clearance too small" ); break;
case DRCE_NETCLASS_VIAANNULUS: msg = _HKI( "NetClass via annulus too small" ); break;
case DRCE_NETCLASS_VIASIZE: msg = _HKI( "NetClass Via Dia too small" ); break;
case DRCE_NETCLASS_VIADRILLSIZE: msg = _HKI( "NetClass Via Drill too small" ); break;
case DRCE_NETCLASS_uVIASIZE: msg = _HKI( "NetClass uVia Dia too small" ); break;
case DRCE_NETCLASS_uVIADRILLSIZE: msg = _HKI( "NetClass uVia Drill too small" ); break;
case DRCE_VIA_INSIDE_KEEPOUT: msg = _HKI( "Via inside keepout area" ); break;
case DRCE_MICROVIA_INSIDE_KEEPOUT: msg = _HKI( "Micro via inside keepout area" ); break;
case DRCE_BBVIA_INSIDE_KEEPOUT: msg = _HKI( "Buried via inside keepout area" ); break;
case DRCE_TRACK_INSIDE_KEEPOUT: msg = _HKI( "Track inside keepout area" ); break;
case DRCE_PAD_INSIDE_KEEPOUT: msg = _HKI( "Pad inside keepout area" ); break;
case DRCE_FOOTPRINT_INSIDE_KEEPOUT: msg = _HKI( "Footprint inside keepout area" ); break;
case DRCE_HOLE_INSIDE_KEEPOUT: msg = _HKI( "Hole inside keepout area" ); break;
case DRCE_TEXT_INSIDE_KEEPOUT: msg = _HKI( "Text inside keepout area" ); break;
case DRCE_GRAPHICS_INSIDE_KEEPOUT: msg = _HKI( "Graphic inside keepout area" ); break;
case DRCE_VIA_NEAR_COPPER: msg = _HKI( "Via too close to copper item" ); break;
case DRCE_TRACK_NEAR_COPPER: msg = _HKI( "Track too close to copper item" ); break;
case DRCE_PAD_NEAR_COPPER: msg = _HKI( "Pad too close to copper item" ); break;
case DRCE_OVERLAPPING_FOOTPRINTS: msg = _HKI( "Courtyards overlap" ); break;
case DRCE_MISSING_COURTYARD: msg = _HKI( "Footprint has no courtyard defined" ); break;
case DRCE_MALFORMED_COURTYARD: msg = _HKI( "Footprint has malformed courtyard" ); break;
case DRCE_PTH_IN_COURTYARD: msg = _HKI( "PTH inside courtyard" ); break;
case DRCE_NPTH_IN_COURTYARD: msg = _HKI( "NPTH inside courtyard" ); break;
case DRCE_DUPLICATE_FOOTPRINT: msg = _HKI( "Duplicate footprints" ); break;
case DRCE_MISSING_FOOTPRINT: msg = _HKI( "Missing footprint" ); break;
case DRCE_EXTRA_FOOTPRINT: msg = _HKI( "Extra footprint" ); break;
// For cleanup tracks and vias:
case CLEANUP_SHORT: msg = _HKI( "Remove track shorting two nets" ); break;
case CLEANUP_REDUNDANT_VIA: msg = _HKI( "Remove redundant via" ); break;
case CLEANUP_DUPLICATE_TRACK: msg = _HKI( "Remove duplicate track" ); break;
case CLEANUP_MERGE_TRACKS: msg = _HKI( "Merge co-linear tracks" ); break;
case CLEANUP_DANGLING_TRACK: msg = _HKI( "Remove dangling track" ); break;
case CLEANUP_DANGLING_VIA: msg = _HKI( "Remove dangling via" ); break;
case CLEANUP_ZERO_LENGTH_TRACK: msg = _HKI( "Remove zero-length track" ); break;
case CLEANUP_TRACK_IN_PAD: msg = _HKI( "Remove track inside pad" ); break;
case DRCE_UNRESOLVED_VARIABLE: msg = _HKI( "Unresolved text variable" ); break;
default:
wxFAIL_MSG( "Missing DRC error description" );
msg = _HKI( "Unknown DRC violation" );
break;
}
if( aTranslate )
return wxGetTranslation( msg );
else
return msg;
}
wxString escapeHtml( wxString aString )
{
aString.Replace( wxT("<"), wxT("&lt;") );
aString.Replace( wxT(">"), wxT("&gt;") );
return aString;
}
wxString DRC_ITEM::ShowHtml( PCB_BASE_FRAME* aFrame ) const
{
BOARD_ITEM* mainItem = nullptr;
BOARD_ITEM* auxItem = nullptr;
wxString msg = m_errorMessage.IsEmpty() ? GetErrorText( m_errorCode ) : m_errorMessage;
wxString mainText;
wxString auxText;
if( m_mainItemUuid != niluuid )
mainItem = aFrame->GetBoard()->GetItem( m_mainItemUuid );
if( m_auxItemUuid != niluuid )
auxItem = aFrame->GetBoard()->GetItem( m_auxItemUuid );
if( mainItem )
mainText = mainItem->GetSelectMenuText( aFrame->GetUserUnits() );
if( auxItem )
auxText = auxItem->GetSelectMenuText( aFrame->GetUserUnits() );
if( mainItem && auxItem )
{
// an html fragment for the entire message in the listbox. feel free
// to add color if you want:
return wxString::Format( wxT( "<b>%s</b><br>&nbsp;&nbsp; %s<br>&nbsp;&nbsp; %s" ),
escapeHtml( msg ),
escapeHtml( mainText ),
escapeHtml( auxText ) );
}
else if( mainItem )
{
return wxString::Format( wxT( "<b>%s</b><br>&nbsp;&nbsp; %s" ),
escapeHtml( msg ),
escapeHtml( mainText ) );
}
else
{
return wxString::Format( wxT( "<b>%s</b>" ),
escapeHtml( msg ) );
}
}

69
qa/drc_proto/drc_item.h Normal file
View File

@ -0,0 +1,69 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
#ifndef DRC_ITEM_PROTO_H
#define DRC_ITEM_PROTO_H
namespace test {
#include <macros.h>
#include <base_struct.h>
#include <rc_item.h>
#include <marker_base.h>
#include <class_board.h>
#include <pcb_base_frame.h>
#include "drc_engine.h"
class DRC_RULE;
class DRC_ITEM : public RC_ITEM
{
public:
DRC_ITEM( int aErrorCode ) {}// fixme
DRC_ITEM( const wxString& aErrorText ) {} // fixme
/**
* Function GetErrorText
* returns the string form of a drc error code.
*/
::wxString GetErrorText( int aErrorCode = -1, bool aTranslate = true ) const override { return ""; } // fixme
/**
* Function ShowHtml
* translates this object into a fragment of HTML suitable for the wxHtmlListBox class.
* @return wxString - the html text.
*/
::wxString FormatHtml( ) const { return ""; } // fixme
protected:
DRC_RULE *m_violatingRule;
//std::vector<BOARD_ITEM*> m_violatingObjects;
};
};
#endif // DRC_ITEM_H

View File

@ -0,0 +1,351 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
#include <drc/drc_keepout_tester.h>
#include <class_module.h>
#include <drc/drc.h>
DRC_KEEPOUT_TESTER::DRC_KEEPOUT_TESTER( MARKER_HANDLER aMarkerHandler ) :
DRC_TEST_PROVIDER( std::move( aMarkerHandler ) ),
m_units( EDA_UNITS::MILLIMETRES ),
m_board( nullptr ),
m_zone( nullptr ),
m_keepoutFlags( 0 )
{
}
bool DRC_KEEPOUT_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard )
{
bool success = true;
m_units = aUnits;
m_board = &aBoard;
// Get a list of all zones to inspect, from both board and footprints
std::list<ZONE_CONTAINER*> areasToInspect = m_board->GetZoneList( true );
// Test keepout areas for vias, tracks and pads inside keepout areas
for( ZONE_CONTAINER* area : areasToInspect )
{
m_keepoutFlags = area->GetKeepouts( &m_sources );
if( m_keepoutFlags > 0 )
{
m_zone = area;
m_zoneBBox = area->GetBoundingBox();
success &= checkTracksAndVias();
success &= checkFootprints();
success &= checkDrawings();
}
}
return success;
}
bool DRC_KEEPOUT_TESTER::checkTracksAndVias()
{
constexpr int VIA_MASK = DISALLOW_VIAS | DISALLOW_MICRO_VIAS | DISALLOW_BB_VIAS;
constexpr int CHECK_VIAS_MASK = VIA_MASK | DISALLOW_HOLES;
constexpr int CHECK_TRACKS_AND_VIAS_MASK = CHECK_VIAS_MASK | DISALLOW_TRACKS;
if(( m_keepoutFlags & CHECK_TRACKS_AND_VIAS_MASK ) == 0 )
return true;
bool success = true;
for( TRACK* segm : m_board->Tracks() )
{
if( !m_zoneBBox.Intersects( segm->GetBoundingBox() ) )
continue;
if( segm->Type() == PCB_TRACE_T && ( m_keepoutFlags & DISALLOW_TRACKS ) != 0 )
{
// Ignore if the keepout zone is not on the same layer
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 );
if( center2center_squared <= SEG::Square( widths) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_INSIDE_KEEPOUT );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ),
m_sources.at(DISALLOW_TRACKS ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( segm, m_zone );
HandleMarker( new MARKER_PCB( drcItem, DRC::GetLocation( segm, m_zone ) ) );
success = false;
}
}
else if( segm->Type() == PCB_VIA_T && ( m_keepoutFlags & CHECK_VIAS_MASK ) != 0 )
{
VIA* via = static_cast<VIA*>( segm );
int errorCode = 0;
int sourceId = 0;
if( ( m_keepoutFlags & DISALLOW_VIAS ) > 0 )
{
errorCode = DRCE_VIA_INSIDE_KEEPOUT;
sourceId = DISALLOW_VIAS;
}
else if( via->GetViaType() == VIATYPE::MICROVIA
&& ( m_keepoutFlags & DISALLOW_MICRO_VIAS ) > 0 )
{
errorCode = DRCE_MICROVIA_INSIDE_KEEPOUT;
sourceId = DISALLOW_MICRO_VIAS;
}
else if( via->GetViaType() == VIATYPE::BLIND_BURIED
&& ( m_keepoutFlags & DISALLOW_BB_VIAS ) > 0 )
{
errorCode = DRCE_BBVIA_INSIDE_KEEPOUT;
sourceId = DISALLOW_BB_VIAS;
}
else if( ( m_keepoutFlags & DISALLOW_HOLES ) > 0 )
{
errorCode = DRCE_HOLE_INSIDE_KEEPOUT;
sourceId = DISALLOW_HOLES;
}
else
continue;
int widths = via->GetWidth() / 2;
wxPoint viaPos = via->GetPosition();
if( errorCode == DRCE_HOLE_INSIDE_KEEPOUT )
widths = via->GetDrillValue() / 2;
SEG::ecoord center2center_squared = m_zone->Outline()->SquaredDistance( viaPos );
if( center2center_squared <= SEG::Square( widths ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( errorCode );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ), m_sources.at( sourceId ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( segm, m_zone );
HandleMarker( new MARKER_PCB( drcItem, DRC::GetLocation( segm, m_zone ) ) );
success = false;
}
}
}
return success;
}
bool DRC_KEEPOUT_TESTER::checkFootprints()
{
constexpr int CHECK_PADS_MASK = DISALLOW_PADS | DISALLOW_HOLES;
constexpr int CHECK_FOOTPRINTS_MASK = CHECK_PADS_MASK | DISALLOW_FOOTPRINTS;
if(( m_keepoutFlags & CHECK_FOOTPRINTS_MASK ) == 0 )
return true;
bool success = true;
for( MODULE* fp : m_board->Modules() )
{
if( !m_zoneBBox.Intersects( fp->GetBoundingBox() ) )
continue;
if( ( m_keepoutFlags & DISALLOW_FOOTPRINTS ) > 0
&& ( fp->IsFlipped() ? m_zone->CommonLayerExists( LSET::BackMask() )
: m_zone->CommonLayerExists( LSET::FrontMask() ) ) )
{
SHAPE_POLY_SET poly;
if( fp->BuildPolyCourtyard() )
poly = fp->IsFlipped() ? fp->GetPolyCourtyardBack() : fp->GetPolyCourtyardFront();
if( poly.OutlineCount() == 0 )
poly = fp->GetBoundingPoly();
// Build the common area between footprint and the keepout area:
poly.BooleanIntersection( *m_zone->Outline(), SHAPE_POLY_SET::PM_FAST );
// If it's not empty then we have a violation
if( poly.OutlineCount() )
{
const VECTOR2I& pt = poly.CVertex( 0, 0, -1 );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_FOOTPRINT_INSIDE_KEEPOUT );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ),
m_sources.at( DISALLOW_FOOTPRINTS ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( fp, m_zone );
HandleMarker( new MARKER_PCB( drcItem, (wxPoint) pt ) );
success = false;
}
}
if( ( m_keepoutFlags & CHECK_PADS_MASK ) > 0 )
{
success &= checkPads( fp );
}
}
return success;
}
bool DRC_KEEPOUT_TESTER::checkPads( MODULE* aModule )
{
bool success = true;
for( D_PAD* pad : aModule->Pads() )
{
if( !m_zone->CommonLayerExists( pad->GetLayerSet() ) )
continue;
// Fast test to detect a pad inside the keepout area bounding box.
EDA_RECT padBBox( pad->ShapePos(), wxSize() );
padBBox.Inflate( pad->GetBoundingRadius() );
if( !m_zoneBBox.Intersects( padBBox ) )
continue;
if( ( m_keepoutFlags & DISALLOW_PADS ) > 0 )
{
SHAPE_POLY_SET outline;
pad->TransformShapeWithClearanceToPolygon( outline, 0 );
// Build the common area between pad and the keepout area:
outline.BooleanIntersection( *m_zone->Outline(), SHAPE_POLY_SET::PM_FAST );
// If it's not empty then we have a violation
if( outline.OutlineCount() )
{
const VECTOR2I& pt = outline.CVertex( 0, 0, -1 );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_PAD_INSIDE_KEEPOUT );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ),
m_sources.at( DISALLOW_PADS ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( pad, m_zone );
HandleMarker( new MARKER_PCB( drcItem, (wxPoint) pt ) );
success = false;
}
}
else if( ( m_keepoutFlags & DISALLOW_HOLES ) > 0 )
{
wxPoint slotStart, slotEnd;
int slotWidth;
pad->GetOblongGeometry( pad->GetDrillSize(), &slotStart, &slotEnd, &slotWidth );
slotStart += pad->GetPosition();
slotEnd += pad->GetPosition();
SEG slotSeg( slotStart, slotEnd );
SHAPE_POLY_SET* outline = const_cast<SHAPE_POLY_SET*>( &m_zone->GetFilledPolysList() );
SEG::ecoord center2center_sq = outline->SquaredDistance( slotSeg );
if( center2center_sq <= SEG::Square( slotWidth) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_INSIDE_KEEPOUT );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ),
m_sources.at( DISALLOW_HOLES ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( pad, m_zone );
HandleMarker( new MARKER_PCB( drcItem, pad->GetPosition() ) );
success = false;
}
}
}
return success;
}
bool DRC_KEEPOUT_TESTER::checkDrawings()
{
constexpr int CHECK_DRAWINGS_MASK = DISALLOW_TEXTS | DISALLOW_GRAPHICS;
constexpr KICAD_T graphicTypes[] = { PCB_LINE_T, PCB_DIMENSION_T, PCB_TARGET_T, EOT };
if(( m_keepoutFlags & CHECK_DRAWINGS_MASK ) == 0 )
return true;
bool success = true;
for( BOARD_ITEM* drawing : m_board->Drawings() )
{
if( !m_zoneBBox.Intersects( drawing->GetBoundingBox() ) )
continue;
int errorCode = 0;
int sourceId = 0;
if( drawing->IsType( graphicTypes ) && ( m_keepoutFlags & DISALLOW_GRAPHICS ) > 0 )
{
errorCode = DRCE_GRAPHICS_INSIDE_KEEPOUT;
sourceId = DISALLOW_GRAPHICS;
}
else if( drawing->Type() == PCB_TEXT_T && ( m_keepoutFlags & DISALLOW_TEXTS ) > 0 )
{
errorCode = DRCE_TEXT_INSIDE_KEEPOUT;
sourceId = DISALLOW_TEXTS;
}
else
continue;
SHAPE_POLY_SET poly;
drawing->TransformShapeWithClearanceToPolygon( poly, 0 );
// Build the common area between footprint and the keepout area:
poly.BooleanIntersection( *m_zone->Outline(), SHAPE_POLY_SET::PM_FAST );
// If it's not empty then we have a violation
if( poly.OutlineCount() )
{
const VECTOR2I& pt = poly.CVertex( 0, 0, -1 );
DRC_ITEM* drcItem = new DRC_ITEM( errorCode );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ), m_sources.at( sourceId ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( drawing, m_zone );
HandleMarker( new MARKER_PCB( drcItem, (wxPoint) pt ) );
success = false;
}
}
return success;
}

View File

@ -0,0 +1,61 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_KEEPOUT_TESTER__H
#define DRC_KEEPOUT_TESTER__H
#include <drc/drc_provider.h>
class BOARD;
class DRC_KEEPOUT_TESTER : public DRC_TEST_PROVIDER
{
public:
DRC_KEEPOUT_TESTER( MARKER_HANDLER aMarkerHandler );
virtual ~DRC_KEEPOUT_TESTER() {};
bool RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) override;
private:
bool checkTracksAndVias();
bool checkFootprints();
bool checkPads( MODULE* aModule );
bool checkDrawings();
private:
EDA_UNITS m_units;
BOARD* m_board;
// Temp variables for use while testing:
ZONE_CONTAINER* m_zone;
EDA_RECT m_zoneBBox;
int m_keepoutFlags; // bitset of DISALLOW_* flags
std::map<int, wxString> m_sources; // map of DISALLOW_* flag to source
wxString m_msg; // avoid lots of calls to wxString's c'tor.
};
#endif // DRC_KEEPOUT_TESTER__H

View File

@ -0,0 +1,117 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2009-2018 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
* Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
/**
* @file class_marker_pcb.h
* @brief Markers used to show a drc problem on boards.
*/
#ifndef CLASS_MARKER_PCB_H
#define CLASS_MARKER_PCB_H
#include <class_board_item.h>
#include <marker_base.h>
class DRC_ITEM;
// Coordinates count for the basic shape marker
#define MARKER_SHAPE_POINT_COUNT 9
class MSG_PANEL_ITEM;
class MARKER_PCB : public BOARD_ITEM, public MARKER_BASE
{
public:
MARKER_PCB( DRC_ITEM* aItem, const wxPoint& aPosition );
~MARKER_PCB();
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && PCB_MARKER_T == aItem->Type();
}
const KIID GetUUID() const override { return m_Uuid; }
wxString Serialize() const;
static MARKER_PCB* Deserialize( const wxString& data );
void Move(const wxPoint& aMoveVector) override
{
m_Pos += aMoveVector;
}
void Rotate( const wxPoint& aRotCentre, double aAngle ) override;
void Flip( const wxPoint& aCentre, bool aFlipLeftRight ) override;
wxPoint GetPosition() const override { return m_Pos; }
void SetPosition( const wxPoint& aPos ) override { m_Pos = aPos; }
bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override
{
return HitTestMarker( aPosition, aAccuracy );
}
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
GAL_LAYER_ID GetColorLayer() const;
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
bool Matches( wxFindReplaceData& aSearchData, void* aAuxData ) override
{
return BOARD_ITEM::Matches( m_rcItem->GetErrorMessage(), aSearchData );
}
wxString GetSelectMenuText( EDA_UNITS aUnits ) const override;
BITMAP_DEF GetMenuImage() const override;
const BOX2I ViewBBox() const override;
const EDA_RECT GetBoundingBox() const override;
void ViewGetLayers( int aLayers[], int& aCount ) const override;
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif
/** Get class name
* @return string "MARKER_PCB"
*/
virtual wxString GetClass() const override
{
return wxT( "MARKER_PCB" );
}
protected:
KIGFX::COLOR4D getColor() const override;
};
#endif // CLASS_MARKER_PCB_H

View File

@ -0,0 +1,162 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
#include <drc/drc_netclass_tester.h>
DRC_NETCLASS_TESTER::DRC_NETCLASS_TESTER( MARKER_HANDLER aMarkerHandler ) :
DRC_TEST_PROVIDER( std::move( aMarkerHandler ) ),
m_units( EDA_UNITS::MILLIMETRES ),
m_board( nullptr )
{
}
bool DRC_NETCLASS_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard )
{
m_units = aUnits;
m_board = &aBoard;
bool success = true;
NETCLASSES& netclasses = m_board->GetDesignSettings().m_NetClasses;
success &= checkNetClass( netclasses.GetDefault() );
for( NETCLASSES::const_iterator i = netclasses.begin(); i != netclasses.end(); ++i )
success &= checkNetClass( i->second );
return success;
}
bool DRC_NETCLASS_TESTER::checkNetClass( const NETCLASSPTR& nc )
{
bool ret = true;
const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
if( nc->GetClearance() < bds.m_MinClearance )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; %s netclass %s)" ),
MessageTextFromValue( m_units, bds.m_MinClearance, true ),
nc->GetName(),
MessageTextFromValue( m_units, nc->GetClearance(), true ) );
drcItem->SetErrorMessage( m_msg );
HandleMarker( new MARKER_PCB( drcItem, wxPoint() ) );
ret = false;
}
if( nc->GetTrackWidth() < bds.m_TrackMinWidth )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_TRACKWIDTH );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; %s netclass %s)" ),
MessageTextFromValue( m_units, bds.m_TrackMinWidth, true ),
nc->GetName(),
MessageTextFromValue( m_units, nc->GetTrackWidth(), true ) );
drcItem->SetErrorMessage( m_msg );
HandleMarker( new MARKER_PCB( drcItem, wxPoint() ) );
ret = false;
}
if( nc->GetViaDiameter() < bds.m_ViasMinSize )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_VIASIZE );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; %s netclass %s)" ),
MessageTextFromValue( m_units, bds.m_ViasMinSize, true ),
nc->GetName(),
MessageTextFromValue( m_units, nc->GetViaDiameter(), true ) );
drcItem->SetErrorMessage( m_msg );
HandleMarker( new MARKER_PCB( drcItem, wxPoint() ) );
ret = false;
}
if( nc->GetViaDrill() < bds.m_MinThroughDrill )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_VIADRILLSIZE );
m_msg.Printf( drcItem->GetErrorText() + _( " (board min through hole %s; %s netclass %s)" ),
MessageTextFromValue( m_units, bds.m_MinThroughDrill, true ),
nc->GetName(),
MessageTextFromValue( m_units, nc->GetViaDrill(), true ) );
drcItem->SetErrorMessage( m_msg );
HandleMarker( new MARKER_PCB( drcItem, wxPoint() ) );
ret = false;
}
int ncViaAnnulus = ( nc->GetViaDiameter() - nc->GetViaDrill() ) / 2;
if( ncViaAnnulus < bds.m_ViasMinAnnulus )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_VIAANNULUS );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; %s netclass %s)" ),
MessageTextFromValue( m_units, bds.m_ViasMinAnnulus, true ),
nc->GetName(),
MessageTextFromValue( m_units, ncViaAnnulus, true ) );
drcItem->SetErrorMessage( m_msg );
HandleMarker( new MARKER_PCB( drcItem, wxPoint() ) );
ret = false;
}
if( nc->GetuViaDiameter() < bds.m_MicroViasMinSize )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_uVIASIZE );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; %s netclass %s)" ),
MessageTextFromValue( m_units, bds.m_MicroViasMinSize, true ),
nc->GetName(),
MessageTextFromValue( m_units, nc->GetuViaDiameter(), true ) );
drcItem->SetErrorMessage( m_msg );
HandleMarker( new MARKER_PCB( drcItem, wxPoint() ) );
ret = false;
}
if( nc->GetuViaDrill() < bds.m_MicroViasMinDrill )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_uVIADRILLSIZE );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; %s netclass %s)" ),
MessageTextFromValue( m_units, bds.m_MicroViasMinDrill, true ),
nc->GetName(),
MessageTextFromValue( m_units, nc->GetuViaDrill(), true ) );
drcItem->SetErrorMessage( m_msg );
HandleMarker( new MARKER_PCB( drcItem, wxPoint() ) );
ret = false;
}
return ret;
}

View File

@ -0,0 +1,54 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_NETCLASS_TESTER__H
#define DRC_NETCLASS_TESTER__H
#include <drc/drc_provider.h>
class BOARD;
class BOARD_ITEM;
class DRC_NETCLASS_TESTER : public DRC_TEST_PROVIDER
{
public:
DRC_NETCLASS_TESTER( MARKER_HANDLER aMarkerHandler );
virtual ~DRC_NETCLASS_TESTER() {};
bool RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) override;
private:
bool checkNetClass( const NETCLASSPTR& nc );
private:
EDA_UNITS m_units;
BOARD* m_board;
wxString m_msg; // Construct only once for performance
};
#endif // DRC_NETCLASS_TESTER__H

View File

@ -0,0 +1,29 @@
#include <string>
#include <common.h>
#include <profile.h>
#include <wx/cmdline.h>
#include <pcbnew_utils/board_file_utils.h>
#include <drc_proto/drc_engine.h>
//#include <qa_utils/utility_registry.h>
int main( int argc, char *argv[] )
{
auto brd = KI_TEST::ReadBoardFromFileOrStream(argv[1]);
test::DRC_ENGINE drcEngine( brd.get(), &brd->GetDesignSettings() );
try {
drcEngine.LoadRules( wxString( argv[2] ) );
} catch( PARSE_ERROR& err )
{
printf("Exception %s\n", (const char*) err.What().c_str() );
}
drcEngine.RunTests();
return 0;
}

286
qa/drc_proto/drc_provider.h Normal file
View File

@ -0,0 +1,286 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_PROVIDER__H
#define DRC_PROVIDER__H
#include <class_board.h>
#include <class_marker_pcb.h>
#include <functional>
namespace test {
class DRC_ENGINE;
/**
* DRC_TEST_PROVIDER
* is a base class that represents a DRC "provider" which runs some DRC functions over a
* #BOARD and spits out #PCB_MARKERs as needed.
*/
class DRC_TEST_PROVIDER
{
public:
DRC_TEST_PROVIDER ( DRC_ENGINE *aDrc );
virtual ~DRC_TEST_PROVIDER() {}
/**
* Runs this provider against the given PCB with configured options (if any).
*
* Note: Board is non-const, as some DRC functions modify the board (e.g. zone fill
* or polygon coalescing)
*/
virtual bool Run() = 0;
virtual void Enable( bool enable );
virtual bool IsEnabled() const;
virtual const wxString GetName() const;
virtual const wxString GetDescription() const;
virtual std::set<test::DRC_RULE_ID_T> GetMatchingRuleIds() const;
/**
* Pass a given marker to the marker handler
*/
virtual void HandleMarker( MARKER_PCB* aMarker ) const;
private:
DRC_ENGINE *m_drcEngine;
};
#if 0
/**
* BOARD_DRC_ITEMS_PROVIDER
* is an implementation of the RC_ITEMS_PROVIDER interface which uses a BOARD instance
* to fulfill the interface.
*/
class BOARD_DRC_ITEMS_PROVIDER : public RC_ITEMS_PROVIDER
{
private:
BOARD* m_board;
int m_severities;
std::vector<MARKER_PCB*> m_filteredMarkers;
public:
BOARD_DRC_ITEMS_PROVIDER( BOARD* aBoard ) :
m_board( aBoard ),
m_severities( 0 )
{
}
void SetSeverities( int aSeverities ) override
{
m_severities = aSeverities;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
m_filteredMarkers.clear();
for( MARKER_PCB* marker : m_board->Markers() )
{
int markerSeverity;
if( marker->IsExcluded() )
markerSeverity = RPT_SEVERITY_EXCLUSION;
else
markerSeverity = bds.GetSeverity( marker->GetRCItem()->GetErrorCode() );
if( markerSeverity & m_severities )
m_filteredMarkers.push_back( marker );
}
}
int GetCount( int aSeverity = -1 ) override
{
if( aSeverity < 0 )
return m_filteredMarkers.size();
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
int count = 0;
for( MARKER_PCB* marker : m_board->Markers() )
{
int markerSeverity;
if( marker->IsExcluded() )
markerSeverity = RPT_SEVERITY_EXCLUSION;
else
markerSeverity = bds.GetSeverity( marker->GetRCItem()->GetErrorCode() );
if( markerSeverity == aSeverity )
count++;
}
return count;
}
DRC_ITEM* GetItem( int aIndex ) override
{
MARKER_PCB* marker = m_filteredMarkers[ aIndex ];
return marker ? static_cast<DRC_ITEM*>( marker->GetRCItem() ) : nullptr;
}
void DeleteItem( int aIndex, bool aDeep ) override
{
MARKER_PCB* marker = m_filteredMarkers[ aIndex ];
m_filteredMarkers.erase( m_filteredMarkers.begin() + aIndex );
if( aDeep )
m_board->Delete( marker );
}
void DeleteAllItems() override
{
m_board->DeleteMARKERs();
m_filteredMarkers.clear();
}
};
/**
* VECTOR_DRC_ITEMS_PROVIDER
* is an implementation of the interface named DRC_ITEMS_PROVIDER which uses a vector
* of pointers to DRC_ITEMs to fulfill the interface. No ownership is taken of the
* vector.
*/
class VECTOR_DRC_ITEMS_PROVIDER : public RC_ITEMS_PROVIDER
{
PCB_BASE_FRAME* m_frame;
std::vector<DRC_ITEM*>* m_sourceVector; // owns its DRC_ITEMs
int m_severities;
std::vector<DRC_ITEM*> m_filteredVector; // does not own its DRC_ITEMs
public:
VECTOR_DRC_ITEMS_PROVIDER( PCB_BASE_FRAME* aFrame, std::vector<DRC_ITEM*>* aList ) :
m_frame( aFrame ),
m_sourceVector( aList ),
m_severities( 0 )
{
}
void SetSeverities( int aSeverities ) override
{
m_severities = aSeverities;
BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings();
m_filteredVector.clear();
if( m_sourceVector )
{
for( DRC_ITEM* item : *m_sourceVector )
{
if( bds.GetSeverity( item->GetErrorCode() ) & aSeverities )
m_filteredVector.push_back( item );
}
}
}
int GetCount( int aSeverity = -1 ) override
{
if( aSeverity < 0 )
return m_filteredVector.size();
int count = 0;
BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings();
if( m_sourceVector )
{
for( DRC_ITEM* item : *m_sourceVector )
{
if( bds.GetSeverity( item->GetErrorCode() ) == aSeverity )
count++;
}
}
return count;
}
DRC_ITEM* GetItem( int aIndex ) override
{
return (m_filteredVector)[aIndex];
}
void DeleteItem( int aIndex, bool aDeep ) override
{
DRC_ITEM* item = m_filteredVector[aIndex];
m_filteredVector.erase( m_filteredVector.begin() + aIndex );
if( aDeep )
{
for( size_t i = 0; i < m_sourceVector->size(); ++i )
{
if( m_sourceVector->at( i ) == item )
{
delete item;
m_sourceVector->erase( m_sourceVector->begin() + i );
break;
}
}
}
}
void DeleteAllItems() override
{
if( m_sourceVector )
{
for( DRC_ITEM* item : *m_sourceVector )
delete item;
m_sourceVector->clear();
}
m_filteredVector.clear(); // no ownership of DRC_ITEM pointers
}
};
/**
* RATSNEST_DRC_ITEMS_PROVIDER
*/
class RATSNEST_DRC_ITEMS_PROVIDER : public VECTOR_DRC_ITEMS_PROVIDER
{
// TODO: for now this is just a vector, but we need to map it to some board-level
// data-structure so that deleting/excluding things can do a deep delete/exclusion
// which will be reflected in the ratsnest....
public:
RATSNEST_DRC_ITEMS_PROVIDER( PCB_BASE_FRAME* aFrame, std::vector<DRC_ITEM*>* aList ) :
VECTOR_DRC_ITEMS_PROVIDER( aFrame, aList )
{ }
};
#endif
};
#endif // DRC_PROVIDER__H

82
qa/drc_proto/drc_rule.cpp Normal file
View File

@ -0,0 +1,82 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#include <fctsys.h>
#include <board_design_settings.h>
#include <class_board.h>
#include <class_board_item.h>
#include <drc_proto/drc_rule.h>
#include <pcb_expr_evaluator.h>
test::DRC_RULE::DRC_RULE()
{
}
test::DRC_RULE::~DRC_RULE()
{
}
test::DRC_RULE_CONDITION::DRC_RULE_CONDITION()
{
if( m_ucode )
delete m_ucode;
}
test::DRC_RULE_CONDITION::~DRC_RULE_CONDITION()
{
}
bool test::DRC_RULE_CONDITION::EvaluateFor( BOARD_ITEM* aItemA, BOARD_ITEM* aItemB )
{
}
bool test::DRC_RULE_CONDITION::Compile()
{
PCB_EXPR_COMPILER compiler;
if (!m_ucode)
m_ucode = new PCB_EXPR_UCODE;
bool ok = compiler.Compile( (const char*) m_Expression.c_str(), m_ucode );
if( ok )
return true;
m_compileError = compiler.GetErrorStatus();
printf("Fail: %s", m_compileError.Format().c_str() );
return false;
}
LIBEVAL::ERROR_STATUS test::DRC_RULE_CONDITION::GetCompilationError()
{
return m_compileError;
}

139
qa/drc_proto/drc_rule.h Normal file
View File

@ -0,0 +1,139 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_RULE_PROTO_H
#define DRC_RULE_PROTO_H
#include <core/typeinfo.h>
#include <layers_id_colors_and_visibility.h>
#include <netclass.h>
#include <libeval_compiler/libeval_compiler.h>
class BOARD_ITEM;
namespace LIBEVAL
{
class UCODE;
class ERROR_STATUS;
};
namespace test
{
enum class DRC_RULE_ID_T {
DRC_RULE_ID_CLEARANCE = 0
};
enum class DRC_RULE_SEVERITY_T {
DRC_SEVERITY_IGNORE = 0,
DRC_SEVERITY_WARNING,
DRC_SEVERITY_ERROR
};
template<class T=int>
class MINOPTMAX
{
public:
T Min() const { assert( m_hasMin ); return m_min; };
T Max() const { assert( m_hasMax ); return m_max; };
T Opt() const { assert( m_hasOpt ); return m_opt; };
void SetMin( T v ) { m_min = v; m_hasMin = true; }
void SetMax( T v ) { m_max = v; m_hasMax = true; }
void SetOpt( T v ) { m_opt = v; m_hasOpt = true; }
private:
T m_min;
T m_opt;
T m_max;
bool m_hasMin = false;
bool m_hasOpt = false;
bool m_hasMax = false;
};
class DRC_CONSTRAINT
{
public:
MINOPTMAX<int> m_Value;
bool m_Allow;
};
class DRC_RULE
{
public:
DRC_RULE();
virtual ~DRC_RULE();
virtual bool IsImplicit() const { return false; };
virtual bool AppliesTo( const BOARD_ITEM* a, const BOARD_ITEM* b = nullptr ) const { return true; };
virtual bool IsEnabled() const { return m_Enabled; }
virtual bool HasSpecificItemSet() const { return false; };
virtual void FillSpecificItemSet( std::vector<BOARD_ITEM*> specificItems ) { };
int GetPriority() const { return m_Priority; }
DRC_RULE_SEVERITY_T GetSeverity() const { return m_Severity; }
const wxString GetName() const { return m_Name; }
const wxString GetTestProviderName() const { return m_TestProviderName; }
const DRC_CONSTRAINT& GetConstraint() const { return m_Constraint; }
public:
bool m_Unary;
wxString m_Name;
wxString m_TestProviderName;
DRC_RULE_SEVERITY_T m_Severity;
bool m_Enabled;
bool m_Conditional;
int m_Priority; // 0 indicates automatic priority generation
DRC_CONSTRAINT m_Constraint;
};
class DRC_RULE_CONDITION
{
public:
DRC_RULE_CONDITION();
~DRC_RULE_CONDITION();
bool EvaluateFor( BOARD_ITEM* aItemA, BOARD_ITEM* aItemB );
bool Compile();
LIBEVAL::ERROR_STATUS GetCompilationError();
public:
wxString m_Expression;
wxString m_TargetRuleName;
private:
LIBEVAL::ERROR_STATUS m_compileError;
LIBEVAL::UCODE* m_ucode;
};
//DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstraint );
}; // namespace test
#endif // DRC_RULE_H

View File

@ -0,0 +1,258 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#include <class_board.h>
#include <class_board_item.h>
#include <drc_proto/drc_rule.h>
#include <drc_proto/drc_rule_parser.h>
#include <drc_proto/drc_rules_lexer.h>
#include <fctsys.h>
#include <pcb_expr_evaluator.h>
using namespace DRCRULE_T;
test::DRC_RULES_PARSER::DRC_RULES_PARSER( BOARD* aBoard, FILE* aFile, const wxString& aFilename )
: DRC_RULES_LEXER( aFile, aFilename ),
m_board( aBoard ),
m_requiredVersion( 0 ),
m_tooRecent( false )
{
for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
{
std::string untranslated = TO_UTF8( wxString( LSET::Name( PCB_LAYER_ID( layer ) ) ) );
m_layerMap[untranslated] = PCB_LAYER_ID( layer );
}
}
void test::DRC_RULES_PARSER::Parse(
std::vector<test::DRC_RULE_CONDITION*>& aConditions, std::vector<test::DRC_RULE*>& aRules )
{
bool haveVersion = false;
for( T token = NextTok(); token != T_EOF; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( !haveVersion && token != T_version )
Expecting( "version" );
switch( token )
{
case T_version:
NeedNUMBER( "version" );
m_requiredVersion = (int) strtol( CurText(), NULL, 10 );
m_tooRecent = ( m_requiredVersion > DRC_RULE_FILE_VERSION );
haveVersion = true;
NeedRIGHT();
break;
case T_condition:
aConditions.push_back( parseCONDITION() );
break;
case T_rule:
aRules.push_back( parseRULE() );
break;
default:
Expecting( "condition or rule" );
}
}
#if 0
// Hook up the selectors to their rules
std::map<wxString, DRC_RULE*> ruleMap;
for( DRC_RULE* rule : aRules )
ruleMap[ rule->m_Name ] = rule;
for( const std::pair<DRC_RULE_CONDITION*, wxString>& entry : conditionsRules )
{
if( ruleMap.count( entry.second ) )
{
entry.first->m_Rule = ruleMap[ entry.second ];
}
else
{
wxString errText = wxString::Format( _( "Rule \"%s\" not found." ), entry.second );
THROW_PARSE_ERROR( errText, CurSource(), "", 0, 0 );
}
}
#endif
}
test::DRC_RULE_CONDITION* test::DRC_RULES_PARSER::parseCONDITION()
{
test::DRC_RULE_CONDITION* cond = new test::DRC_RULE_CONDITION();
T token;
printf( "parsecondition\n" );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
printf( "Do token xxx %d '%s'\n", token, (const char*) FromUTF8().c_str() );
switch( token )
{
case T_expression:
NeedSYMBOL();
cond->m_Expression = FromUTF8();
break;
case T_rule:
NeedSYMBOL();
cond->m_TargetRuleName = FromUTF8();
break;
default:
Expecting( "rule or expression" );
break;
}
NeedRIGHT();
}
//NeedRIGHT()
return cond;
}
test::DRC_RULE* test::DRC_RULES_PARSER::parseRULE()
{
DRC_RULE* rule = new DRC_RULE();
T token = NextTok();
int val;
if( !IsSymbol( token ) )
Expecting( "rule name" );
rule->m_Name = FromUTF8();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
//printf( "Do token xxx %d '%s'\n", token, (const char*) FromUTF8().c_str() );
switch( token )
{
case T_type:
NeedSYMBOL();
rule->m_TestProviderName = FromUTF8();
break;
case T_min:
NeedSYMBOL();
parseValueWithUnits ( FromUTF8(), val );
rule->m_Constraint.m_Value.SetMin( val );
break;
case T_opt:
NeedSYMBOL();
parseValueWithUnits ( FromUTF8(), val );
rule->m_Constraint.m_Value.SetOpt( val );
break;
case T_max:
NeedSYMBOL();
parseValueWithUnits ( FromUTF8(), val );
rule->m_Constraint.m_Value.SetMax( val );
break;
case T_allow:
rule->m_Constraint.m_Allow = parseInt("allowed");
break;
case T_enable:
rule->m_Enabled = parseInt("enabled");
break;
case T_severity:
token = NextTok();
switch( token )
{
case T_error:
case T_warning:
case T_ignore: break; // fixme
default:
Expecting( "error, warning or ignore" );
break;
}
break;
case T_priority:
rule->m_Priority = parseInt("priotity");
break;
default:
Expecting( "type, min, opt, max, allow, enable, priority or severity" );
break;
}
NeedRIGHT();
}
return rule;
}
void test::DRC_RULES_PARSER::parseValueWithUnits( const wxString& aExpr, int& aResult )
{
PCB_EXPR_EVALUATOR evaluator;
bool ok = evaluator.Evaluate( aExpr );
if( !ok )
{
auto err = evaluator.GetErrorString();
printf( "eval error: '%s'\n", (const char*) err.c_str() );
THROW_PARSE_ERROR( err, "", "", 0, 0 );
}
aResult = evaluator.Result();
printf("parseValueWithUnits: value %d\n", aResult );
};

View File

@ -0,0 +1,80 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_RULE_PARSER_H
#define DRC_RULE_PARSER_H
#include <core/typeinfo.h>
#include <netclass.h>
#include <layers_id_colors_and_visibility.h>
#include <drc_proto/drc_rule.h>
#include <drc_rules_lexer.h>
class BOARD_ITEM;
namespace test {
class DRC_RULE_CONDITION;
class DRC_RULE;
#define DRC_RULE_FILE_VERSION 20200610
class DRC_RULES_PARSER : public DRC_RULES_LEXER
{
public:
DRC_RULES_PARSER( BOARD* aBoard, FILE* aFile, const wxString& aFilename );
void Parse( std::vector<DRC_RULE_CONDITION*>& aConditions, std::vector<DRC_RULE*>& aRules );
private:
DRC_RULE_CONDITION* parseCONDITION();
DRC_RULE* parseRULE();
void parseValueWithUnits( const wxString& aExpr, int& aResult );
inline int parseInt()
{
return (int)strtol( CurText(), NULL, 10 );
}
inline int parseInt( const char* aExpected )
{
NeedNUMBER( aExpected );
return parseInt();
}
// void parseConstraint( DRC_RULE* aRule );
//int parseValue( DRCRULE_T::T aToken );
private:
BOARD* m_board;
int m_requiredVersion;
bool m_tooRecent;
std::unordered_map<std::string, PCB_LAYER_ID> m_layerMap;
};
};
#endif // DRC_RULE_PARSER_H

View File

@ -0,0 +1,50 @@
/* Do not modify this file it was automatically generated by the
* TokenList2DsnLexer CMake script.
*
* Include this file in your lexer class to provide the keywords for
* your DSN lexer.
*/
#include </home/twl/Kicad-dev/kicad-build/debug-dev/common/drc_rules_lexer.h>
using namespace DRCRULE_T;
#define TOKDEF(x) { #x, T_##x }
const KEYWORD DRC_RULES_LEXER::keywords[] = {
TOKDEF( allow ),
TOKDEF( condition ),
TOKDEF( enable ),
TOKDEF( error ),
TOKDEF( expression ),
TOKDEF( ignore ),
TOKDEF( info ),
TOKDEF( max ),
TOKDEF( min ),
TOKDEF( name ),
TOKDEF( opt ),
TOKDEF( priority ),
TOKDEF( rule ),
TOKDEF( severity ),
TOKDEF( type ),
TOKDEF( version ),
TOKDEF( warning )
};
const unsigned DRC_RULES_LEXER::keyword_count = unsigned( sizeof( DRC_RULES_LEXER::keywords )/sizeof( DRC_RULES_LEXER::keywords[0] ) );
const char* DRC_RULES_LEXER::TokenName( T aTok )
{
const char* ret;
if( aTok < 0 )
ret = DSNLEXER::Syntax( aTok );
else if( (unsigned) aTok < keyword_count )
ret = keywords[aTok].name;
else
ret = "token too big";
return ret;
}

View File

@ -0,0 +1,195 @@
/* Do not modify this file it was automatically generated by the
* TokenList2DsnLexer CMake script.
*/
#ifndef DRC_RULES_LEXER_H_
#define DRC_RULES_LEXER_H_
#include <dsnlexer.h>
/**
* C++ does not put enum _values_ in separate namespaces unless the enum itself
* is in a separate namespace. All the token enums must be in separate namespaces
* otherwise the C++ compiler will eventually complain if it sees more than one
* DSNLEXER in the same compilation unit, say by mutliple header file inclusion.
* Plus this also enables re-use of the same enum name T. A typedef can always be used
* to clarify which enum T is in play should that ever be a problem. This is
* unlikely since Parse() functions will usually only be exposed to one header
* file like this one. But if there is a problem, then use:
* typedef DRCRULE_T::T T;
* within that problem area.
*/
namespace DRCRULE_T
{
/// enum T contains all this lexer's tokens.
enum T
{
// these first few are negative special ones for syntax, and are
// inherited from DSNLEXER.
T_NONE = DSN_NONE,
T_COMMENT = DSN_COMMENT,
T_STRING_QUOTE = DSN_STRING_QUOTE,
T_QUOTE_DEF = DSN_QUOTE_DEF,
T_DASH = DSN_DASH,
T_SYMBOL = DSN_SYMBOL,
T_NUMBER = DSN_NUMBER,
T_RIGHT = DSN_RIGHT, // right bracket: ')'
T_LEFT = DSN_LEFT, // left bracket: '('
T_STRING = DSN_STRING, // a quoted string, stripped of the quotes
T_EOF = DSN_EOF, // special case for end of file
T_allow = 0,
T_condition,
T_enable,
T_error,
T_expression,
T_ignore,
T_info,
T_max,
T_min,
T_name,
T_opt,
T_priority,
T_rule,
T_severity,
T_type,
T_version,
T_warning
};
} // namespace DRCRULE_T
/**
* Class DRC_RULES_LEXER
* is an automatically generated class using the TokenList2DnsLexer.cmake
* technology, based on keywords provided by file:
* /home/twl/Kicad-dev/kicad-git-dev/common/drc_rules.keywords
*/
class DRC_RULES_LEXER : public DSNLEXER
{
/// Auto generated lexer keywords table and length:
static const KEYWORD keywords[];
static const unsigned keyword_count;
public:
/**
* Constructor ( const std::string&, const wxString& )
* @param aSExpression is (utf8) text possibly from the clipboard that you want to parse.
* @param aSource is a description of the origin of @a aSExpression, such as a filename.
* If left empty, then _("clipboard") is used.
*/
DRC_RULES_LEXER( const std::string& aSExpression, const wxString& aSource = wxEmptyString ) :
DSNLEXER( keywords, keyword_count, aSExpression, aSource )
{
}
/**
* Constructor ( FILE* )
* takes @a aFile already opened for reading and @a aFilename as parameters.
* The opened file is assumed to be positioned at the beginning of the file
* for purposes of accurate line number reporting in error messages. The
* FILE is closed by this instance when its destructor is called.
* @param aFile is a FILE already opened for reading.
* @param aFilename is the name of the opened file, needed for error reporting.
*/
DRC_RULES_LEXER( FILE* aFile, const wxString& aFilename ) :
DSNLEXER( keywords, keyword_count, aFile, aFilename )
{
}
/**
* Constructor ( LINE_READER* )
* initializes a lexer and prepares to read from @a aLineReader which
* is assumed ready, and may be in use by other DSNLEXERs also. No ownership
* is taken of @a aLineReader. This enables it to be used by other lexers also.
* The transition between grammars in such a case, must happen on a text
* line boundary, not within the same line of text.
*
* @param aLineReader is any subclassed instance of LINE_READER, such as
* STRING_LINE_READER or FILE_LINE_READER. No ownership is taken of aLineReader.
*/
DRC_RULES_LEXER( LINE_READER* aLineReader ) :
DSNLEXER( keywords, keyword_count, aLineReader )
{
}
/**
* Function TokenName
* returns the name of the token in ASCII form.
*/
static const char* TokenName( DRCRULE_T::T aTok );
/**
* Function NextTok
* returns the next token found in the input file or T_EOF when reaching
* the end of file. Users should wrap this function to return an enum
* to aid in grammar debugging while running under a debugger, but leave
* this lower level function returning an int (so the enum does not collide
* with another usage).
* @return DRCRULE_T::T - the type of token found next.
* @throw IO_ERROR - only if the LINE_READER throws it.
*/
DRCRULE_T::T NextTok()
{
return (DRCRULE_T::T) DSNLEXER::NextTok();
}
/**
* Function NeedSYMBOL
* calls NextTok() and then verifies that the token read in
* satisfies bool IsSymbol().
* If not, an IO_ERROR is thrown.
* @return int - the actual token read in.
* @throw IO_ERROR, if the next token does not satisfy IsSymbol()
*/
DRCRULE_T::T NeedSYMBOL()
{
return (DRCRULE_T::T) DSNLEXER::NeedSYMBOL();
}
/**
* Function NeedSYMBOLorNUMBER
* calls NextTok() and then verifies that the token read in
* satisfies bool IsSymbol() or tok==T_NUMBER.
* If not, an IO_ERROR is thrown.
* @return int - the actual token read in.
* @throw IO_ERROR, if the next token does not satisfy the above test
*/
DRCRULE_T::T NeedSYMBOLorNUMBER()
{
return (DRCRULE_T::T) DSNLEXER::NeedSYMBOLorNUMBER();
}
/**
* Function CurTok
* returns whatever NextTok() returned the last time it was called.
*/
DRCRULE_T::T CurTok()
{
return (DRCRULE_T::T) DSNLEXER::CurTok();
}
/**
* Function PrevTok
* returns whatever NextTok() returned the 2nd to last time it was called.
*/
DRCRULE_T::T PrevTok()
{
return (DRCRULE_T::T) DSNLEXER::PrevTok();
}
};
// example usage
/**
* Class _PARSER
* holds data and functions pertinent to parsing a S-expression file .
*
class DRC_RULES_PARSER : public DRC_RULES_LEXER
{
};
*/
#endif // DRC_RULES_LEXER_H_

View File

@ -0,0 +1,17 @@
condition
max
min
opt
allow
rule
version
name
priority
expression
enable
severity
type
error
warning
info
ignore

View File

@ -0,0 +1,31 @@
#include <drc_proto/drc_engine.h>
#include <drc_proto/drc_test_provider.h>
test::DRC_TEST_PROVIDER::DRC_TEST_PROVIDER ( DRC_ENGINE *aDrc ) :
m_drcEngine( aDrc )
{
}
void test::DRC_TEST_PROVIDER::Enable( bool enable )
{
m_enable = enable;
}
bool test::DRC_TEST_PROVIDER::IsEnabled() const
{
return m_enable;
}
const wxString test::DRC_TEST_PROVIDER::GetName() const { return "<no name test>"; }
const wxString test::DRC_TEST_PROVIDER::GetDescription() const { return ""; }
void test::DRC_TEST_PROVIDER::AddMarkerToPcb( MARKER_PCB* aMarker )
{
}
EDA_UNITS test::DRC_TEST_PROVIDER::userUnits() const
{
return m_drcEngine->UserUnits();
}

View File

@ -0,0 +1,290 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_PROVIDER__H
#define DRC_PROVIDER__H
#include <class_board.h>
#include <class_marker_pcb.h>
#include <functional>
#include <set>
namespace test {
class DRC_ENGINE;
/**
* DRC_TEST_PROVIDER
* is a base class that represents a DRC "provider" which runs some DRC functions over a
* #BOARD and spits out #PCB_MARKERs as needed.
*/
class DRC_TEST_PROVIDER
{
public:
DRC_TEST_PROVIDER ( DRC_ENGINE *aDrc );
virtual ~DRC_TEST_PROVIDER() {}
/**
* Runs this provider against the given PCB with configured options (if any).
*
* Note: Board is non-const, as some DRC functions modify the board (e.g. zone fill
* or polygon coalescing)
*/
void SetRule ( DRC_RULE *aRule );
virtual bool Run() = 0;
virtual void Enable( bool enable );
virtual bool IsEnabled() const;
virtual const wxString GetName() const;
virtual const wxString GetDescription() const;
virtual void AddMarkerToPcb( MARKER_PCB* aMarker );
virtual std::set<test::DRC_RULE_ID_T> GetMatchingRuleIds() const = 0;
protected:
EDA_UNITS userUnits() const;
DRC_RULE *m_rule;
DRC_ENGINE *m_drcEngine;
bool m_enable;
};
#if 0
/**
* BOARD_DRC_ITEMS_PROVIDER
* is an implementation of the RC_ITEMS_PROVIDER interface which uses a BOARD instance
* to fulfill the interface.
*/
class BOARD_DRC_ITEMS_PROVIDER : public RC_ITEMS_PROVIDER
{
private:
BOARD* m_board;
int m_severities;
std::vector<MARKER_PCB*> m_filteredMarkers;
public:
BOARD_DRC_ITEMS_PROVIDER( BOARD* aBoard ) :
m_board( aBoard ),
m_severities( 0 )
{
}
void SetSeverities( int aSeverities ) override
{
m_severities = aSeverities;
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
m_filteredMarkers.clear();
for( MARKER_PCB* marker : m_board->Markers() )
{
int markerSeverity;
if( marker->IsExcluded() )
markerSeverity = RPT_SEVERITY_EXCLUSION;
else
markerSeverity = bds.GetSeverity( marker->GetRCItem()->GetErrorCode() );
if( markerSeverity & m_severities )
m_filteredMarkers.push_back( marker );
}
}
int GetCount( int aSeverity = -1 ) override
{
if( aSeverity < 0 )
return m_filteredMarkers.size();
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
int count = 0;
for( MARKER_PCB* marker : m_board->Markers() )
{
int markerSeverity;
if( marker->IsExcluded() )
markerSeverity = RPT_SEVERITY_EXCLUSION;
else
markerSeverity = bds.GetSeverity( marker->GetRCItem()->GetErrorCode() );
if( markerSeverity == aSeverity )
count++;
}
return count;
}
DRC_ITEM* GetItem( int aIndex ) override
{
MARKER_PCB* marker = m_filteredMarkers[ aIndex ];
return marker ? static_cast<DRC_ITEM*>( marker->GetRCItem() ) : nullptr;
}
void DeleteItem( int aIndex, bool aDeep ) override
{
MARKER_PCB* marker = m_filteredMarkers[ aIndex ];
m_filteredMarkers.erase( m_filteredMarkers.begin() + aIndex );
if( aDeep )
m_board->Delete( marker );
}
void DeleteAllItems() override
{
m_board->DeleteMARKERs();
m_filteredMarkers.clear();
}
};
/**
* VECTOR_DRC_ITEMS_PROVIDER
* is an implementation of the interface named DRC_ITEMS_PROVIDER which uses a vector
* of pointers to DRC_ITEMs to fulfill the interface. No ownership is taken of the
* vector.
*/
class VECTOR_DRC_ITEMS_PROVIDER : public RC_ITEMS_PROVIDER
{
PCB_BASE_FRAME* m_frame;
std::vector<DRC_ITEM*>* m_sourceVector; // owns its DRC_ITEMs
int m_severities;
std::vector<DRC_ITEM*> m_filteredVector; // does not own its DRC_ITEMs
public:
VECTOR_DRC_ITEMS_PROVIDER( PCB_BASE_FRAME* aFrame, std::vector<DRC_ITEM*>* aList ) :
m_frame( aFrame ),
m_sourceVector( aList ),
m_severities( 0 )
{
}
void SetSeverities( int aSeverities ) override
{
m_severities = aSeverities;
BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings();
m_filteredVector.clear();
if( m_sourceVector )
{
for( DRC_ITEM* item : *m_sourceVector )
{
if( bds.GetSeverity( item->GetErrorCode() ) & aSeverities )
m_filteredVector.push_back( item );
}
}
}
int GetCount( int aSeverity = -1 ) override
{
if( aSeverity < 0 )
return m_filteredVector.size();
int count = 0;
BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings();
if( m_sourceVector )
{
for( DRC_ITEM* item : *m_sourceVector )
{
if( bds.GetSeverity( item->GetErrorCode() ) == aSeverity )
count++;
}
}
return count;
}
DRC_ITEM* GetItem( int aIndex ) override
{
return (m_filteredVector)[aIndex];
}
void DeleteItem( int aIndex, bool aDeep ) override
{
DRC_ITEM* item = m_filteredVector[aIndex];
m_filteredVector.erase( m_filteredVector.begin() + aIndex );
if( aDeep )
{
for( size_t i = 0; i < m_sourceVector->size(); ++i )
{
if( m_sourceVector->at( i ) == item )
{
delete item;
m_sourceVector->erase( m_sourceVector->begin() + i );
break;
}
}
}
}
void DeleteAllItems() override
{
if( m_sourceVector )
{
for( DRC_ITEM* item : *m_sourceVector )
delete item;
m_sourceVector->clear();
}
m_filteredVector.clear(); // no ownership of DRC_ITEM pointers
}
};
/**
* RATSNEST_DRC_ITEMS_PROVIDER
*/
class RATSNEST_DRC_ITEMS_PROVIDER : public VECTOR_DRC_ITEMS_PROVIDER
{
// TODO: for now this is just a vector, but we need to map it to some board-level
// data-structure so that deleting/excluding things can do a deep delete/exclusion
// which will be reflected in the ratsnest....
public:
RATSNEST_DRC_ITEMS_PROVIDER( PCB_BASE_FRAME* aFrame, std::vector<DRC_ITEM*>* aList ) :
VECTOR_DRC_ITEMS_PROVIDER( aFrame, aList )
{ }
};
#endif
};
#endif // DRC_PROVIDER__H

View File

@ -0,0 +1,116 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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
*/
#include <drc/drc_textvar_tester.h>
#include <class_module.h>
#include <class_pcb_text.h>
#include <ws_draw_item.h>
#include <ws_proxy_view_item.h>
DRC_TEXTVAR_TESTER::DRC_TEXTVAR_TESTER( MARKER_HANDLER aMarkerHandler,
KIGFX::WS_PROXY_VIEW_ITEM* aWorksheet ) :
DRC_TEST_PROVIDER( std::move( aMarkerHandler ) ),
m_units( EDA_UNITS::MILLIMETRES ),
m_board( nullptr ),
m_worksheet( aWorksheet )
{
}
bool DRC_TEXTVAR_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard )
{
bool success = true;
m_units = aUnits;
m_board = &aBoard;
for( MODULE* module : m_board->Modules() )
{
module->RunOnChildren(
[&]( BOARD_ITEM* child )
{
if( child->Type() == PCB_MODULE_TEXT_T )
{
TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( child );
if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_UNRESOLVED_VARIABLE );
drcItem->SetItems( text );
HandleMarker( new MARKER_PCB( drcItem, text->GetPosition() ) );
success = false;
}
}
} );
}
for( BOARD_ITEM* drawing : m_board->Drawings() )
{
if( drawing->Type() == PCB_TEXT_T )
{
TEXTE_PCB* text = static_cast<TEXTE_PCB*>( drawing );
if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_UNRESOLVED_VARIABLE );
drcItem->SetItems( text );
HandleMarker( new MARKER_PCB( drcItem, text->GetPosition() ) );
success = false;
}
}
}
WS_DRAW_ITEM_LIST wsItems;
if( m_worksheet )
{
wsItems.SetMilsToIUfactor( IU_PER_MILS );
wsItems.BuildWorkSheetGraphicList( m_worksheet->GetPageInfo(),
m_worksheet->GetTitleBlock() );
for( WS_DRAW_ITEM_BASE* item = wsItems.GetFirst(); item; item = wsItems.GetNext() )
{
if( WS_DRAW_ITEM_TEXT* text = dynamic_cast<WS_DRAW_ITEM_TEXT*>( item ) )
{
if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_UNRESOLVED_VARIABLE );
drcItem->SetErrorMessage( _( "Unresolved text variable in worksheet." ) );
HandleMarker( new MARKER_PCB( drcItem, text->GetPosition() ) );
success = false;
}
}
}
}
// JEY TODO: Test text vars in worksheet...
return success;
}

View File

@ -0,0 +1,49 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef DRC_TEXTVAR_TESTER__H
#define DRC_TEXTVAR_TESTER__H
#include <drc/drc_provider.h>
class BOARD;
class DRC_TEXTVAR_TESTER : public DRC_TEST_PROVIDER
{
public:
DRC_TEXTVAR_TESTER( MARKER_HANDLER aMarkerHandler, KIGFX::WS_PROXY_VIEW_ITEM* aWorksheet );
virtual ~DRC_TEXTVAR_TESTER() {};
bool RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) override;
private:
EDA_UNITS m_units;
BOARD* m_board;
KIGFX::WS_PROXY_VIEW_ITEM* m_worksheet;
};
#endif // DRC_TEXTVAR_TESTER__H

View File

@ -0,0 +1,90 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#include <fctsys.h>
#include <netlist_reader/pcb_netlist.h>
#include <drc/footprint_tester.h>
void TestFootprints( NETLIST& aNetlist, BOARD* aBoard, std::vector<DRC_ITEM*>& aDRCList )
{
wxString msg;
auto comp = []( const MODULE* x, const MODULE* y )
{
return x->GetReference().CmpNoCase( y->GetReference() ) < 0;
};
auto mods = std::set<MODULE*, decltype( comp )>( comp );
if( !aBoard->GetDesignSettings().Ignore( DRCE_DUPLICATE_FOOTPRINT ) )
{
// Search for duplicate footprints on the board
for( MODULE* mod : aBoard->Modules() )
{
auto ins = mods.insert( mod );
if( !ins.second )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_DUPLICATE_FOOTPRINT );
item->SetItems( mod, *ins.first );
aDRCList.push_back( item );
}
}
}
if( !aBoard->GetDesignSettings().Ignore( DRCE_MISSING_FOOTPRINT ) )
{
// Search for component footprints in the netlist but not on the board.
for( unsigned ii = 0; ii < aNetlist.GetCount(); ii++ )
{
COMPONENT* component = aNetlist.GetComponent( ii );
MODULE* module = aBoard->FindModuleByReference( component->GetReference() );
if( module == NULL )
{
msg.Printf( _( "Missing footprint %s (%s)" ),
component->GetReference(),
component->GetValue() );
DRC_ITEM* item = new DRC_ITEM( DRCE_MISSING_FOOTPRINT );
item->SetErrorMessage( msg );
aDRCList.push_back( item );
}
}
}
if( !aBoard->GetDesignSettings().Ignore( DRCE_EXTRA_FOOTPRINT ) )
{
// Search for component footprints found on board but not in netlist.
for( auto module : mods )
{
COMPONENT* component = aNetlist.GetComponentByReference( module->GetReference() );
if( component == NULL )
{
DRC_ITEM* item = new DRC_ITEM( DRCE_EXTRA_FOOTPRINT );
item->SetItems( module );
aDRCList.push_back( item );
}
}
}
}

View File

@ -0,0 +1,34 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
*
* 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
*/
#ifndef FOOTPRINT_TESTER_H
#define FOOTPRINT_TESTER_H
#include <drc/drc.h>
class BOARD;
void TestFootprints( NETLIST& aNetlist, BOARD* aBoard, std::vector<DRC_ITEM*>& aDRCList );
#endif // FOOTPRINT_TESTER_H