summaryrefslogtreecommitdiff
path: root/pcbnew/drc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/drc.cpp')
-rw-r--r--pcbnew/drc.cpp968
1 files changed, 968 insertions, 0 deletions
diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp
new file mode 100644
index 0000000..31ebc50
--- /dev/null
+++ b/pcbnew/drc.cpp
@@ -0,0 +1,968 @@
+
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2014 Dick Hollenbeck, dick@softplc.com
+ * Copyright (C) 2016 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
+ */
+
+/**
+ * @file drc.cpp
+ */
+
+#include <fctsys.h>
+#include <wxPcbStruct.h>
+#include <trigo.h>
+#include <base_units.h>
+#include <class_board_design_settings.h>
+
+#include <class_module.h>
+#include <class_track.h>
+#include <class_pad.h>
+#include <class_zone.h>
+#include <class_pcb_text.h>
+#include <class_draw_panel_gal.h>
+#include <view/view.h>
+#include <geometry/seg.h>
+
+#include <tool/tool_manager.h>
+#include <tools/common_actions.h>
+
+#include <pcbnew.h>
+#include <drc_stuff.h>
+
+#include <dialog_drc.h>
+#include <wx/progdlg.h>
+
+
+void DRC::ShowDialog()
+{
+ if( !m_drcDialog )
+ {
+ m_mainWindow->GetToolManager()->RunAction( COMMON_ACTIONS::selectionClear, true );
+ m_drcDialog = new DIALOG_DRC_CONTROL( this, m_mainWindow );
+ updatePointers();
+
+ m_drcDialog->SetRptSettings( m_doCreateRptFile, m_rptFilename);
+ }
+ else
+ updatePointers();
+
+ m_drcDialog->Show( true );
+}
+
+
+void DRC::DestroyDialog( int aReason )
+{
+ if( m_drcDialog )
+ {
+ if( aReason == wxID_OK )
+ {
+ // if user clicked OK, save his choices in this DRC object.
+ m_drcDialog->GetRptSettings( &m_doCreateRptFile, m_rptFilename);
+ }
+
+ m_drcDialog->Destroy();
+ m_drcDialog = NULL;
+ }
+}
+
+
+DRC::DRC( PCB_EDIT_FRAME* aPcbWindow )
+{
+ m_mainWindow = aPcbWindow;
+ m_pcb = aPcbWindow->GetBoard();
+ m_drcDialog = NULL;
+
+ // establish initial values for everything:
+ m_doPad2PadTest = true; // enable pad to pad clearance tests
+ m_doUnconnectedTest = true; // enable unconnected tests
+ m_doZonesTest = true; // enable zone to items clearance tests
+ m_doKeepoutTest = true; // enable keepout areas to items clearance tests
+ m_abortDRC = false;
+ m_drcInProgress = false;
+
+ m_doCreateRptFile = false;
+
+ // m_rptFilename set to empty by its constructor
+
+ m_currentMarker = NULL;
+
+ m_segmAngle = 0;
+ m_segmLength = 0;
+
+ m_xcliplo = 0;
+ m_ycliplo = 0;
+ m_xcliphi = 0;
+ m_ycliphi = 0;
+}
+
+
+DRC::~DRC()
+{
+ // maybe someday look at pointainer.h <- google for "pointainer.h"
+ for( unsigned i = 0; i<m_unconnected.size(); ++i )
+ delete m_unconnected[i];
+}
+
+
+int DRC::Drc( TRACK* aRefSegm, TRACK* aList )
+{
+ updatePointers();
+
+ if( !doTrackDrc( aRefSegm, aList, true ) )
+ {
+ wxASSERT( m_currentMarker );
+
+ m_mainWindow->SetMsgPanel( m_currentMarker );
+ return BAD_DRC;
+ }
+
+ if( !doTrackKeepoutDrc( aRefSegm ) )
+ {
+ wxASSERT( m_currentMarker );
+
+ m_mainWindow->SetMsgPanel( m_currentMarker );
+ return BAD_DRC;
+ }
+
+ return OK_DRC;
+}
+
+
+int DRC::Drc( ZONE_CONTAINER* aArea, int aCornerIndex )
+{
+ updatePointers();
+
+ if( !doEdgeZoneDrc( aArea, aCornerIndex ) )
+ {
+ wxASSERT( m_currentMarker );
+ m_mainWindow->SetMsgPanel( m_currentMarker );
+ return BAD_DRC;
+ }
+
+ return OK_DRC;
+}
+
+
+void DRC::RunTests( wxTextCtrl* aMessages )
+{
+ // be sure m_pcb is the current board, not a old one
+ // ( the board can be reloaded )
+ m_pcb = m_mainWindow->GetBoard();
+
+ // Ensure ratsnest is up to date:
+ if( (m_pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
+ {
+ if( aMessages )
+ {
+ aMessages->AppendText( _( "Compile ratsnest...\n" ) );
+ wxSafeYield();
+ }
+
+ m_mainWindow->Compile_Ratsnest( NULL, true );
+ }
+
+ // someone should have cleared the two lists before calling this.
+
+ if( !testNetClasses() )
+ {
+ // testing the netclasses is a special case because if the netclasses
+ // do not pass the BOARD_DESIGN_SETTINGS checks, then every member of a net
+ // class (a NET) will cause its items such as tracks, vias, and pads
+ // to also fail. So quit after *all* netclass errors have been reported.
+ if( aMessages )
+ aMessages->AppendText( _( "Aborting\n" ) );
+
+ // update the m_drcDialog listboxes
+ updatePointers();
+
+ return;
+ }
+
+ // test pad to pad clearances, nothing to do with tracks, vias or zones.
+ if( m_doPad2PadTest )
+ {
+ if( aMessages )
+ {
+ aMessages->AppendText( _( "Pad clearances...\n" ) );
+ wxSafeYield();
+ }
+
+ testPad2Pad();
+ }
+
+ // test track and via clearances to other tracks, pads, and vias
+ if( aMessages )
+ {
+ aMessages->AppendText( _( "Track clearances...\n" ) );
+ wxSafeYield();
+ }
+
+ testTracks( aMessages ? aMessages->GetParent() : m_mainWindow, true );
+
+ // Before testing segments and unconnected, refill all zones:
+ // this is a good caution, because filled areas can be outdated.
+ if( aMessages )
+ {
+ aMessages->AppendText( _( "Fill zones...\n" ) );
+ wxSafeYield();
+ }
+
+ m_mainWindow->Fill_All_Zones( aMessages ? aMessages->GetParent() : m_mainWindow,
+ false );
+
+ // test zone clearances to other zones
+ if( aMessages )
+ {
+ aMessages->AppendText( _( "Test zones...\n" ) );
+ wxSafeYield();
+ }
+
+ testZones();
+
+ // find and gather unconnected pads.
+ if( m_doUnconnectedTest )
+ {
+ if( aMessages )
+ {
+ aMessages->AppendText( _( "Unconnected pads...\n" ) );
+ aMessages->Refresh();
+ }
+
+ testUnconnected();
+ }
+
+ // find and gather vias, tracks, pads inside keepout areas.
+ if( m_doKeepoutTest )
+ {
+ if( aMessages )
+ {
+ aMessages->AppendText( _( "Keepout areas ...\n" ) );
+ aMessages->Refresh();
+ }
+
+ testKeepoutAreas();
+ }
+
+ // find and gather vias, tracks, pads inside text boxes.
+ if( aMessages )
+ {
+ aMessages->AppendText( _( "Test texts...\n" ) );
+ wxSafeYield();
+ }
+
+ testTexts();
+
+ // update the m_drcDialog listboxes
+ updatePointers();
+
+ if( aMessages )
+ {
+ // no newline on this one because it is last, don't want the window
+ // to unnecessarily scroll.
+ aMessages->AppendText( _( "Finished" ) );
+ }
+}
+
+
+void DRC::ListUnconnectedPads()
+{
+ testUnconnected();
+
+ // update the m_drcDialog listboxes
+ updatePointers();
+}
+
+
+void DRC::updatePointers()
+{
+ // update my pointers, m_mainWindow is the only unchangeable one
+ m_pcb = m_mainWindow->GetBoard();
+
+ if( m_drcDialog ) // Use diag list boxes only in DRC dialog
+ {
+ m_drcDialog->m_ClearanceListBox->SetList( new DRC_LIST_MARKERS( m_pcb ) );
+ m_drcDialog->m_UnconnectedListBox->SetList( new DRC_LIST_UNCONNECTED( &m_unconnected ) );
+ }
+}
+
+
+bool DRC::doNetClass( NETCLASSPTR nc, wxString& msg )
+{
+ bool ret = true;
+
+ const BOARD_DESIGN_SETTINGS& g = m_pcb->GetDesignSettings();
+
+#define FmtVal( x ) GetChars( StringFromValue( g_UserUnit, x ) )
+
+#if 0 // set to 1 when (if...) BOARD_DESIGN_SETTINGS has a m_MinClearance value
+ if( nc->GetClearance() < g.m_MinClearance )
+ {
+ msg.Printf( _( "NETCLASS: '%s' has Clearance:%s which is less than global:%s" ),
+ GetChars( nc->GetName() ),
+ FmtVal( nc->GetClearance() ),
+ FmtVal( g.m_TrackClearance )
+ );
+
+ m_currentMarker = fillMarker( DRCE_NETCLASS_CLEARANCE, msg, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ ret = false;
+ }
+#endif
+
+ if( nc->GetTrackWidth() < g.m_TrackMinWidth )
+ {
+ msg.Printf( _( "NETCLASS: '%s' has TrackWidth:%s which is less than global:%s" ),
+ GetChars( nc->GetName() ),
+ FmtVal( nc->GetTrackWidth() ),
+ FmtVal( g.m_TrackMinWidth )
+ );
+
+ m_currentMarker = fillMarker( DRCE_NETCLASS_TRACKWIDTH, msg, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ ret = false;
+ }
+
+ if( nc->GetViaDiameter() < g.m_ViasMinSize )
+ {
+ msg.Printf( _( "NETCLASS: '%s' has Via Dia:%s which is less than global:%s" ),
+ GetChars( nc->GetName() ),
+ FmtVal( nc->GetViaDiameter() ),
+ FmtVal( g.m_ViasMinSize )
+ );
+
+ m_currentMarker = fillMarker( DRCE_NETCLASS_VIASIZE, msg, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ ret = false;
+ }
+
+ if( nc->GetViaDrill() < g.m_ViasMinDrill )
+ {
+ msg.Printf( _( "NETCLASS: '%s' has Via Drill:%s which is less than global:%s" ),
+ GetChars( nc->GetName() ),
+ FmtVal( nc->GetViaDrill() ),
+ FmtVal( g.m_ViasMinDrill )
+ );
+
+ m_currentMarker = fillMarker( DRCE_NETCLASS_VIADRILLSIZE, msg, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ ret = false;
+ }
+
+ if( nc->GetuViaDiameter() < g.m_MicroViasMinSize )
+ {
+ msg.Printf( _( "NETCLASS: '%s' has uVia Dia:%s which is less than global:%s" ),
+ GetChars( nc->GetName() ),
+ FmtVal( nc->GetuViaDiameter() ),
+ FmtVal( g.m_MicroViasMinSize )
+ );
+
+ m_currentMarker = fillMarker( DRCE_NETCLASS_uVIASIZE, msg, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ ret = false;
+ }
+
+ if( nc->GetuViaDrill() < g.m_MicroViasMinDrill )
+ {
+ msg.Printf( _( "NETCLASS: '%s' has uVia Drill:%s which is less than global:%s" ),
+ GetChars( nc->GetName() ),
+ FmtVal( nc->GetuViaDrill() ),
+ FmtVal( g.m_MicroViasMinDrill )
+ );
+
+ m_currentMarker = fillMarker( DRCE_NETCLASS_uVIADRILLSIZE, msg, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+bool DRC::testNetClasses()
+{
+ bool ret = true;
+
+ NETCLASSES& netclasses = m_pcb->GetDesignSettings().m_NetClasses;
+
+ wxString msg; // construct this only once here, not in a loop, since somewhat expensive.
+
+ if( !doNetClass( netclasses.GetDefault(), msg ) )
+ ret = false;
+
+ for( NETCLASSES::const_iterator i = netclasses.begin(); i != netclasses.end(); ++i )
+ {
+ NETCLASSPTR nc = i->second;
+
+ if( !doNetClass( nc, msg ) )
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+void DRC::testPad2Pad()
+{
+ std::vector<D_PAD*> sortedPads;
+
+ m_pcb->GetSortedPadListByXthenYCoord( sortedPads );
+
+ // find the max size of the pads (used to stop the test)
+ int max_size = 0;
+
+ for( unsigned i = 0; i < sortedPads.size(); ++i )
+ {
+ D_PAD* pad = sortedPads[i];
+
+ // GetBoundingRadius() is the radius of the minimum sized circle fully containing the pad
+ int radius = pad->GetBoundingRadius();
+ if( radius > max_size )
+ max_size = radius;
+ }
+
+ // Test the pads
+ D_PAD** listEnd = &sortedPads[ sortedPads.size() ];
+
+ for( unsigned i = 0; i< sortedPads.size(); ++i )
+ {
+ D_PAD* pad = sortedPads[i];
+
+ int x_limit = max_size + pad->GetClearance() +
+ pad->GetBoundingRadius() + pad->GetPosition().x;
+
+ if( !doPadToPadsDrc( pad, &sortedPads[i], listEnd, x_limit ) )
+ {
+ wxASSERT( m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ }
+ }
+}
+
+
+void DRC::testTracks( wxWindow *aActiveWindow, bool aShowProgressBar )
+{
+ wxProgressDialog * progressDialog = NULL;
+ const int delta = 500; // This is the number of tests between 2 calls to the
+ // progress bar
+ int count = 0;
+ for( TRACK* segm = m_pcb->m_Track; segm && segm->Next(); segm = segm->Next() )
+ count++;
+
+ int deltamax = count/delta;
+
+ if( aShowProgressBar && deltamax > 3 )
+ {
+ progressDialog = new wxProgressDialog( _( "Track clearances" ), wxEmptyString,
+ deltamax, aActiveWindow,
+ wxPD_AUTO_HIDE | wxPD_CAN_ABORT |
+ wxPD_APP_MODAL | wxPD_ELAPSED_TIME );
+ progressDialog->Update( 0, wxEmptyString );
+ }
+
+ int ii = 0;
+ count = 0;
+
+ for( TRACK* segm = m_pcb->m_Track; segm && segm->Next(); segm = segm->Next() )
+ {
+ if ( ii++ > delta )
+ {
+ ii = 0;
+ count++;
+
+ if( progressDialog )
+ {
+ if( !progressDialog->Update( count, wxEmptyString ) )
+ break; // Aborted by user
+#ifdef __WXMAC__
+ // Work around a dialog z-order issue on OS X
+ if( count == deltamax )
+ aActiveWindow->Raise();
+#endif
+ }
+ }
+
+ if( !doTrackDrc( segm, segm->Next(), true ) )
+ {
+ wxASSERT( m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ }
+ }
+
+ if( progressDialog )
+ progressDialog->Destroy();
+}
+
+
+void DRC::testUnconnected()
+{
+ if( (m_pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
+ {
+ wxClientDC dc( m_mainWindow->GetCanvas() );
+ m_mainWindow->Compile_Ratsnest( &dc, true );
+ }
+
+ if( m_pcb->GetRatsnestsCount() == 0 )
+ return;
+
+ wxString msg;
+
+ for( unsigned ii = 0; ii < m_pcb->GetRatsnestsCount(); ++ii )
+ {
+ RATSNEST_ITEM& rat = m_pcb->m_FullRatsnest[ii];
+
+ if( (rat.m_Status & CH_ACTIF) == 0 )
+ continue;
+
+ D_PAD* padStart = rat.m_PadStart;
+ D_PAD* padEnd = rat.m_PadEnd;
+
+ msg = padStart->GetSelectMenuText() + wxT( " net " ) + padStart->GetNetname();
+
+ DRC_ITEM* uncItem = new DRC_ITEM( DRCE_UNCONNECTED_PADS,
+ msg,
+ padEnd->GetSelectMenuText(),
+ padStart->GetPosition(), padEnd->GetPosition() );
+
+ m_unconnected.push_back( uncItem );
+ }
+}
+
+
+void DRC::testZones()
+{
+ // Test copper areas for valid netcodes
+ // if a netcode is < 0 the netname was not found when reading a netlist
+ // if a netcode is == 0 the netname is void, and the zone is not connected.
+ // This is allowed, but i am not sure this is a good idea
+ //
+ // In recent Pcbnew versions, the netcode is always >= 0, but an internal net name
+ // is stored, and initalized from the file or the zone properpies editor.
+ // if it differs from the net name from net code, there is a DRC issue
+ for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
+ {
+ ZONE_CONTAINER* test_area = m_pcb->GetArea( ii );
+
+ if( !test_area->IsOnCopperLayer() )
+ continue;
+
+ int netcode = test_area->GetNetCode();
+
+ // a netcode < 0 or > 0 and no pad in net is a error or strange
+ // perhaps a "dead" net, which happens when all pads in this net were removed
+ // Remark: a netcode < 0 should not happen (this is more a bug somewhere)
+ int pads_in_net = (test_area->GetNetCode() > 0) ?
+ test_area->GetNet()->GetNodesCount() : 1;
+
+ if( ( netcode < 0 ) || pads_in_net == 0 )
+ {
+ m_currentMarker = fillMarker( test_area,
+ DRCE_SUSPICIOUS_NET_FOR_ZONE_OUTLINE, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = NULL;
+ }
+ }
+
+ // Test copper areas outlines, and create markers when needed
+ m_pcb->Test_Drc_Areas_Outlines_To_Areas_Outlines( NULL, true );
+}
+
+
+void DRC::testKeepoutAreas()
+{
+ // Test keepout areas for vias, tracks and pads inside keepout areas
+ for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
+ {
+ ZONE_CONTAINER* area = m_pcb->GetArea( ii );
+
+ if( !area->GetIsKeepout() )
+ continue;
+
+ for( TRACK* segm = m_pcb->m_Track; segm != NULL; segm = segm->Next() )
+ {
+ if( segm->Type() == PCB_TRACE_T )
+ {
+ if( ! area->GetDoNotAllowTracks() )
+ continue;
+
+ if( segm->GetLayer() != area->GetLayer() )
+ continue;
+
+ if( area->Outline()->Distance( segm->GetStart(), segm->GetEnd(),
+ segm->GetWidth() ) == 0 )
+ {
+ m_currentMarker = fillMarker( segm, NULL,
+ DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ }
+ }
+ else if( segm->Type() == PCB_VIA_T )
+ {
+ if( ! area->GetDoNotAllowVias() )
+ continue;
+
+ if( ! ((VIA*)segm)->IsOnLayer( area->GetLayer() ) )
+ continue;
+
+ if( area->Outline()->Distance( segm->GetPosition() ) < segm->GetWidth()/2 )
+ {
+ m_currentMarker = fillMarker( segm, NULL,
+ DRCE_VIA_INSIDE_KEEPOUT, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = 0;
+ }
+ }
+ }
+ // Test pads: TODO
+ }
+}
+
+
+void DRC::testTexts()
+{
+ std::vector<wxPoint> textShape; // a buffer to store the text shape (set of segments)
+ std::vector<D_PAD*> padList = m_pcb->GetPads();
+
+ // Test text areas for vias, tracks and pads inside text areas
+ for( BOARD_ITEM* item = m_pcb->m_Drawings; item; item = item->Next() )
+ {
+ // Drc test only items on copper layers
+ if( ! IsCopperLayer( item->GetLayer() ) )
+ continue;
+
+ // only texts on copper layers are tested
+ if( item->Type() != PCB_TEXT_T )
+ continue;
+
+ textShape.clear();
+
+ // So far the bounding box makes up the text-area
+ TEXTE_PCB* text = (TEXTE_PCB*) item;
+ text->TransformTextShapeToSegmentList( textShape );
+
+ if( textShape.size() == 0 ) // Should not happen (empty text?)
+ continue;
+
+ for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() )
+ {
+ if( ! track->IsOnLayer( item->GetLayer() ) )
+ continue;
+
+ // Test the distance between each segment and the current track/via
+ int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 +
+ track->GetClearance(NULL);
+
+ if( track->Type() == PCB_TRACE_T )
+ {
+ SEG segref( track->GetStart(), track->GetEnd() );
+
+ // Error condition: Distance between text segment and track segment is
+ // smaller than the clearance of the segment
+ for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
+ {
+ SEG segtest( textShape[jj], textShape[jj+1] );
+ int dist = segref.Distance( segtest );
+
+ if( dist < min_dist )
+ {
+ m_currentMarker = fillMarker( track, text,
+ DRCE_TRACK_INSIDE_TEXT,
+ m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = NULL;
+ break;
+ }
+ }
+ }
+ else if( track->Type() == PCB_VIA_T )
+ {
+ // Error condition: Distance between text segment and via is
+ // smaller than the clearance of the via
+ for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
+ {
+ SEG segtest( textShape[jj], textShape[jj+1] );
+
+ if( segtest.PointCloserThan( track->GetPosition(), min_dist ) )
+ {
+ m_currentMarker = fillMarker( track, text,
+ DRCE_VIA_INSIDE_TEXT, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ // Test pads
+ for( unsigned ii = 0; ii < padList.size(); ii++ )
+ {
+ D_PAD* pad = padList[ii];
+
+ if( ! pad->IsOnLayer( item->GetLayer() ) )
+ continue;
+
+ wxPoint shape_pos = pad->ShapePos();
+
+ for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
+ {
+ /* In order to make some calculations more easier or faster,
+ * pads and tracks coordinates will be made relative
+ * to the segment origin
+ */
+ wxPoint origin = textShape[jj]; // origin will be the origin of other coordinates
+ m_segmEnd = textShape[jj+1] - origin;
+ wxPoint delta = m_segmEnd;
+ m_segmAngle = 0;
+
+ // for a non horizontal or vertical segment Compute the segment angle
+ // in tenths of degrees and its length
+ if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias
+ {
+ // Compute the segment angle in 0,1 degrees
+ m_segmAngle = ArcTangente( delta.y, delta.x );
+
+ // Compute the segment length: we build an equivalent rotated segment,
+ // this segment is horizontal, therefore dx = length
+ RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0
+ }
+
+ m_segmLength = delta.x;
+ m_padToTestPos = shape_pos - origin;
+
+ if( !checkClearanceSegmToPad( pad, text->GetThickness(),
+ pad->GetClearance(NULL) ) )
+ {
+ m_currentMarker = fillMarker( pad, text,
+ DRCE_PAD_INSIDE_TEXT, m_currentMarker );
+ m_pcb->Add( m_currentMarker );
+ m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
+ m_currentMarker = NULL;
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg )
+{
+ // Test keepout areas for vias, tracks and pads inside keepout areas
+ for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
+ {
+ ZONE_CONTAINER* area = m_pcb->GetArea( ii );
+
+ if( !area->GetIsKeepout() )
+ continue;
+
+ if( aRefSeg->Type() == PCB_TRACE_T )
+ {
+ if( ! area->GetDoNotAllowTracks() )
+ continue;
+
+ if( aRefSeg->GetLayer() != area->GetLayer() )
+ continue;
+
+ if( area->Outline()->Distance( aRefSeg->GetStart(), aRefSeg->GetEnd(),
+ aRefSeg->GetWidth() ) == 0 )
+ {
+ m_currentMarker = fillMarker( aRefSeg, NULL,
+ DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker );
+ return false;
+ }
+ }
+ else if( aRefSeg->Type() == PCB_VIA_T )
+ {
+ if( ! area->GetDoNotAllowVias() )
+ continue;
+
+ if( ! ((VIA*)aRefSeg)->IsOnLayer( area->GetLayer() ) )
+ continue;
+
+ if( area->Outline()->Distance( aRefSeg->GetPosition() ) < aRefSeg->GetWidth()/2 )
+ {
+ m_currentMarker = fillMarker( aRefSeg, NULL,
+ DRCE_VIA_INSIDE_KEEPOUT, m_currentMarker );
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+bool DRC::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;
+
+ /* used to test DRC pad to holes: this dummy pad has the size and shape of the hole
+ * to test pad to pad hole DRC, using the pad to pad DRC test function.
+ * Therefore, this dummy pad is a circle or an oval.
+ * A pad must have a parent because some functions expect a non null parent
+ * to find the parent board, and some other data
+ */
+ MODULE dummymodule( m_pcb ); // Creates a dummy parent
+ D_PAD dummypad( &dummymodule );
+
+ // Ensure the hole is on all copper layers
+ dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() );
+
+ // Use the minimal local clearance value for the dummy pad.
+ // The clearance of the active pad will be used as minimum distance to a hole
+ // (a value = 0 means use netclass value)
+ dummypad.SetLocalClearance( 1 );
+
+ 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;
+
+ // 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;
+ }
+
+ /* 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() );
+
+ if( !checkClearancePadToPad( aRefPad, &dummypad ) )
+ {
+ // here we have a drc error on pad!
+ m_currentMarker = fillMarker( pad, aRefPad,
+ DRCE_HOLE_NEAR_PAD, m_currentMarker );
+ 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() );
+
+ if( !checkClearancePadToPad( pad, &dummypad ) )
+ {
+ // here we have a drc error on aRefPad!
+ m_currentMarker = fillMarker( aRefPad, pad,
+ DRCE_HOLE_NEAR_PAD, m_currentMarker );
+ return false;
+ }
+ }
+
+ continue;
+ }
+
+ // 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;
+ }
+
+ if( !checkClearancePadToPad( aRefPad, pad ) )
+ {
+ // here we have a drc error!
+ m_currentMarker = fillMarker( aRefPad, pad, DRCE_PAD_NEAR_PAD1, m_currentMarker );
+ return false;
+ }
+ }
+
+ return true;
+}