drc_proto: wip
This commit is contained in:
parent
af5afb5dd7
commit
085698d17c
|
@ -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
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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 );
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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("<") );
|
||||
aString.Replace( wxT(">"), wxT(">") );
|
||||
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> %s<br> %s" ),
|
||||
escapeHtml( msg ),
|
||||
escapeHtml( mainText ),
|
||||
escapeHtml( auxText ) );
|
||||
}
|
||||
else if( mainItem )
|
||||
{
|
||||
return wxString::Format( wxT( "<b>%s</b><br> %s" ),
|
||||
escapeHtml( msg ),
|
||||
escapeHtml( mainText ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return wxString::Format( wxT( "<b>%s</b>" ),
|
||||
escapeHtml( msg ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 );
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -0,0 +1,17 @@
|
|||
condition
|
||||
max
|
||||
min
|
||||
opt
|
||||
allow
|
||||
rule
|
||||
version
|
||||
name
|
||||
priority
|
||||
expression
|
||||
enable
|
||||
severity
|
||||
type
|
||||
error
|
||||
warning
|
||||
info
|
||||
ignore
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue