Commit d536f9d9 authored by jean-pierre charras's avatar jean-pierre charras

DRC code cleaning, and added DRC tests for trapezoidal pads. Needs more tests.

parents e149951b f1df65c5
...@@ -96,6 +96,7 @@ set(PCBNEW_SRCS ...@@ -96,6 +96,7 @@ set(PCBNEW_SRCS
dist.cpp dist.cpp
dragsegm.cpp dragsegm.cpp
drc.cpp drc.cpp
drc_clearance_test_functions.cpp
drc_marker_functions.cpp drc_marker_functions.cpp
edgemod.cpp edgemod.cpp
edit.cpp edit.cpp
......
...@@ -764,17 +764,15 @@ bool D_PAD::IsOnLayer( int aLayer ) const ...@@ -764,17 +764,15 @@ bool D_PAD::IsOnLayer( int aLayer ) const
*/ */
bool D_PAD::HitTest( const wxPoint& ref_pos ) bool D_PAD::HitTest( const wxPoint& ref_pos )
{ {
int deltaX, deltaY;
int dx, dy; int dx, dy;
double dist; double dist;
wxPoint shape_pos = ReturnShapePos(); wxPoint shape_pos = ReturnShapePos();
deltaX = ref_pos.x - shape_pos.x; wxPoint delta = ref_pos - shape_pos;
deltaY = ref_pos.y - shape_pos.y;
/* Quick test: a test point must be inside the circle. */ /* Quick test: a test point must be inside the circle. */
if( ( abs( deltaX ) > m_ShapeMaxRadius ) || ( abs( deltaY ) > m_ShapeMaxRadius ) ) if( ( abs( delta.x ) > m_ShapeMaxRadius ) || ( abs( delta.y ) > m_ShapeMaxRadius ) )
return false; return false;
dx = m_Size.x >> 1; // dx also is the radius for rounded pads dx = m_Size.x >> 1; // dx also is the radius for rounded pads
...@@ -783,7 +781,7 @@ bool D_PAD::HitTest( const wxPoint& ref_pos ) ...@@ -783,7 +781,7 @@ bool D_PAD::HitTest( const wxPoint& ref_pos )
switch( m_PadShape & 0x7F ) switch( m_PadShape & 0x7F )
{ {
case PAD_CIRCLE: case PAD_CIRCLE:
dist = hypot( deltaX, deltaY ); dist = hypot( delta.x, delta.y );
if( wxRound( dist ) <= dx ) if( wxRound( dist ) <= dx )
return true; return true;
break; break;
...@@ -792,22 +790,13 @@ bool D_PAD::HitTest( const wxPoint& ref_pos ) ...@@ -792,22 +790,13 @@ bool D_PAD::HitTest( const wxPoint& ref_pos )
{ {
wxPoint poly[4]; wxPoint poly[4];
BuildPadPolygon( poly, wxSize(0,0), 0 ); BuildPadPolygon( poly, wxSize(0,0), 0 );
// Build the same polygon with CPolyPt corners, RotatePoint( &delta, -m_Orient );
// to use TestPointInsidePolygon return TestPointInsidePolygon( poly, 4, delta );
static std::vector <CPolyPt> polysList; // Is static to avoid memory reallocation
polysList.clear();
for(int ii= 0; ii < 4; ii++ )
{
CPolyPt corner(poly[ii].x, poly[ii].y);
polysList.push_back(corner);
}
RotatePoint( &deltaX, &deltaY, -m_Orient );
return TestPointInsidePolygon( polysList, 0, 3, deltaX, deltaY );
} }
default: default:
RotatePoint( &deltaX, &deltaY, -m_Orient ); RotatePoint( &delta, -m_Orient );
if( (abs( deltaX ) <= dx ) && (abs( deltaY ) <= dy) ) if( (abs( delta.x ) <= dx ) && (abs( delta.y ) <= dy) )
return true; return true;
break; break;
} }
......
...@@ -148,7 +148,7 @@ public: ...@@ -148,7 +148,7 @@ public:
* Function GetShape * Function GetShape
* @return the shape of this pad. * @return the shape of this pad.
*/ */
int GetShape() { return m_PadShape & 0xFF; } int GetShape() const { return m_PadShape & 0xFF; }
/** /**
* Function GetPosition * Function GetPosition
...@@ -239,14 +239,26 @@ public: ...@@ -239,14 +239,26 @@ public:
void DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ); void DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo );
/** function BuildPadPolygon /** function BuildPadPolygon
* Has meaning only for polygonal pads (trapeziod and rectangular) * Has meaning only for polygonal pads (trapezoid and rectangular)
* Build the Corner list of the polygonal shape, * Build the Corner list of the polygonal shape,
* depending on shape, extra size (clearance ...) and orientation * depending on shape, extra size (clearance ...) and orientation
* @param aCoord[4] = a buffer to fill. * @param aCoord[4] = a buffer to fill.
* @param aInflateValue = wxSize: the clearance or margin value. value > 0: inflate, < 0 deflate * @param aInflateValue = wxSize: the clearance or margin value. value > 0: inflate, < 0 deflate
* @param aRotation = full rotation of the polygon * @param aRotation = full rotation of the polygon
*/ */
void BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, int aRotation ); void BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, int aRotation ) const;
/** function BuildSegmentFromOvalShape
* Has meaning only for OVAL (and ROUND) pads
* Build an equivalent segment having the same shape as the OVAL shape,
* Useful in draw function and in DRC and HitTest functions,
* because segments are already well handled by track tests
* @param aSegStart = the starting point of the equivalent segment, relative to the shape position.
* @param aSegEnd = the ending point of the equivalent segment, relative to the shape position
* @param aRotation = full rotation of the segment
* @return the width of the segment
*/
int BuildSegmentFromOvalShape(wxPoint& aSegStart, wxPoint& aSegEnd, int aRotation) const;
// others // others
void SetPadName( const wxString& name ); // Change pad name void SetPadName( const wxString& name ); // Change pad name
......
...@@ -357,9 +357,9 @@ void D_PAD::Draw( WinEDA_DrawPanel* aPanel, wxDC* aDC, int aDraw_mode, ...@@ -357,9 +357,9 @@ void D_PAD::Draw( WinEDA_DrawPanel* aPanel, wxDC* aDC, int aDraw_mode,
void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ) void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
{ {
wxPoint coord[4]; wxPoint coord[4];
int rotdx, int delta_cx, delta_cy;
delta_cx, delta_cy;
int angle = m_Orient; int angle = m_Orient;
int seg_width;
GRSetDrawMode( aDC, aDrawInfo.m_DrawMode ); GRSetDrawMode( aDC, aDrawInfo.m_DrawMode );
...@@ -392,44 +392,30 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ) ...@@ -392,44 +392,30 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
break; break;
case PAD_OVAL: case PAD_OVAL:
if( halfsize.x > halfsize.y ) /* horizontal */ {
{ wxPoint segStart, segEnd;
delta_cx = halfsize.x - halfsize.y; seg_width = BuildSegmentFromOvalShape(segStart, segEnd, angle);
delta_cy = 0; segStart += shape_pos;
rotdx = m_Size.y + ( aDrawInfo.m_Mask_margin.y * 2 ); segEnd += shape_pos;
}
else /* vertical */
{
delta_cx = 0;
delta_cy = halfsize.y - halfsize.x;
rotdx = m_Size.x + ( aDrawInfo.m_Mask_margin.x * 2 );
}
RotatePoint( &delta_cx, &delta_cy, angle );
if( aDrawInfo.m_ShowPadFilled ) if( aDrawInfo.m_ShowPadFilled )
{ {
GRFillCSegm( aClipBox, aDC, GRFillCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
shape_pos.x + delta_cx, shape_pos.y + delta_cy, seg_width, aDrawInfo.m_Color );
shape_pos.x - delta_cx, shape_pos.y - delta_cy,
rotdx, aDrawInfo.m_Color );
} }
else else
{ {
GRCSegm( aClipBox, aDC, GRCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
shape_pos.x + delta_cx, shape_pos.y + delta_cy, seg_width, m_PadSketchModePenSize, aDrawInfo.m_Color );
shape_pos.x - delta_cx, shape_pos.y - delta_cy,
rotdx, m_PadSketchModePenSize, aDrawInfo.m_Color );
} }
/* Draw the isolation line. */ /* Draw the isolation line. */
if( aDrawInfo.m_PadClearance ) if( aDrawInfo.m_PadClearance )
{ {
rotdx = rotdx + 2 * aDrawInfo.m_PadClearance; seg_width += 2 * aDrawInfo.m_PadClearance;
GRCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
GRCSegm( aClipBox, aDC, shape_pos.x + delta_cx, shape_pos.y + delta_cy, seg_width, aDrawInfo.m_Color );
shape_pos.x - delta_cx, shape_pos.y - delta_cy,
rotdx, aDrawInfo.m_Color );
} }
}
break; break;
case PAD_RECT: case PAD_RECT:
...@@ -486,9 +472,6 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ) ...@@ -486,9 +472,6 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
#else #else
if( aDrawInfo.m_Scale * hole > 1 ) /* draw hole if its size is enough */ if( aDrawInfo.m_Scale * hole > 1 ) /* draw hole if its size is enough */
#endif #endif
GRFilledCircle( aClipBox, aDC, holepos.x, holepos.y, hole, 0, GRFilledCircle( aClipBox, aDC, holepos.x, holepos.y, hole, 0,
aDrawInfo.m_Color, aDrawInfo.m_HoleColor ); aDrawInfo.m_Color, aDrawInfo.m_HoleColor );
break; break;
...@@ -501,18 +484,18 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ) ...@@ -501,18 +484,18 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
{ {
delta_cx = halfsize.x - halfsize.y; delta_cx = halfsize.x - halfsize.y;
delta_cy = 0; delta_cy = 0;
rotdx = m_Drill.y; seg_width = m_Drill.y;
} }
else /* vertical */ else /* vertical */
{ {
delta_cx = 0; delta_cx = 0;
delta_cy = halfsize.y - halfsize.x; delta_cy = halfsize.y - halfsize.x;
rotdx = m_Drill.x; seg_width = m_Drill.x;
} }
RotatePoint( &delta_cx, &delta_cy, angle ); RotatePoint( &delta_cx, &delta_cy, angle );
GRFillCSegm( aClipBox, aDC, holepos.x + delta_cx, holepos.y + delta_cy, GRFillCSegm( aClipBox, aDC, holepos.x + delta_cx, holepos.y + delta_cy,
holepos.x - delta_cx, holepos.y - delta_cy, rotdx, holepos.x - delta_cx, holepos.y - delta_cy, seg_width,
aDrawInfo.m_HoleColor ); aDrawInfo.m_HoleColor );
break; break;
...@@ -637,6 +620,42 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ) ...@@ -637,6 +620,42 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
} }
} }
/** function BuildSegmentFromOvalShape
* Has meaning only for OVAL (and ROUND) pads.
* Build an equivalent segment having the same shape as the OVAL shape,
* aSegStart and aSegEnd are the ending points of the equivalent segment of the shape
* aRotation is the asked rotation of the segment (usually m_Orient)
*/
int D_PAD::BuildSegmentFromOvalShape(wxPoint& aSegStart, wxPoint& aSegEnd, int aRotation) const
{
int width;
if( m_Size.y < m_Size.x ) // Build an horizontal equiv segment
{
int delta = ( m_Size.x - m_Size.y ) / 2;
aSegStart.x = -delta;
aSegStart.y = 0;
aSegEnd.x = delta;
aSegEnd.y = 0;
width = m_Size.y;
}
else // Vertical oval: build a vertical equiv segment
{
int delta = ( m_Size.y -m_Size.x ) / 2;
aSegStart.x = 0;
aSegStart.y = -delta;
aSegEnd.x = 0;
aSegEnd.y = delta;
width = m_Size.x;
}
if( aRotation )
{
RotatePoint( &aSegStart, aRotation);
RotatePoint( &aSegEnd, aRotation);
}
return width;
}
/** function BuildPadPolygon /** function BuildPadPolygon
* Has meaning only for polygonal pads (trapeziod and rectangular) * Has meaning only for polygonal pads (trapeziod and rectangular)
...@@ -646,7 +665,7 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo ) ...@@ -646,7 +665,7 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
* @param aInflateValue = wxSize: the clearance or margin value. value > 0: inflate, < 0 deflate * @param aInflateValue = wxSize: the clearance or margin value. value > 0: inflate, < 0 deflate
* @param aRotation = full rotation of the polygon, usually m_Orient * @param aRotation = full rotation of the polygon, usually m_Orient
*/ */
void D_PAD::BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, int aRotation ) void D_PAD::BuildPadPolygon( wxPoint aCoord[4], wxSize aInflateValue, int aRotation ) const
{ {
if( (GetShape() != PAD_RECT) && (GetShape() != PAD_TRAPEZOID) ) if( (GetShape() != PAD_RECT) && (GetShape() != PAD_TRAPEZOID) )
return; return;
......
...@@ -44,21 +44,17 @@ bool DIALOG_DRC_CONTROL::Show( bool show ) ...@@ -44,21 +44,17 @@ bool DIALOG_DRC_CONTROL::Show( bool show )
{ {
bool ret; bool ret;
D(printf("%s %d\n", __func__, show );)
if( show ) if( show )
{ {
ret = DIALOG_DRC_CONTROL_BASE::Show( show ); ret = DIALOG_DRC_CONTROL_BASE::Show( show );
if( s_LastPos.x != -1 ) if( s_LastPos.x != -1 )
{ {
D(printf("setting window pos to (%d,%d)\n", s_LastPos.x, s_LastPos.y );)
//SetPosition( s_LastPos );
SetSize( s_LastPos.x, s_LastPos.y, s_LastSize.x, s_LastSize.y, 0 ); SetSize( s_LastPos.x, s_LastPos.y, s_LastSize.x, s_LastSize.y, 0 );
} }
else else
{ {
D(printf("not setting window pos (%d,%d)\n", s_LastPos.x, s_LastPos.y );) // Do nothing: last position not yet saved.
} }
} }
else else
...@@ -66,9 +62,6 @@ bool DIALOG_DRC_CONTROL::Show( bool show ) ...@@ -66,9 +62,6 @@ bool DIALOG_DRC_CONTROL::Show( bool show )
// Save the dialog's position before hiding // Save the dialog's position before hiding
s_LastPos = GetPosition(); s_LastPos = GetPosition();
s_LastSize = GetSize(); s_LastSize = GetSize();
D(printf("saving window pos as (%d,%d)\n", s_LastPos.x, s_LastPos.y );)
ret = DIALOG_DRC_CONTROL_BASE::Show( show ); ret = DIALOG_DRC_CONTROL_BASE::Show( show );
} }
......
...@@ -195,8 +195,7 @@ void DIALOG_PAD_PROPERTIES::initValues() ...@@ -195,8 +195,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
{ {
SetFocus(); // Required under wxGTK if we want to demiss the dialog with the ESC key SetFocus(); // Required under wxGTK if we want to demiss the dialog with the ESC key
int tmp; int internalUnits = m_Parent->m_InternalUnits;
int internalUnits = m_Parent->m_InternalUnits;
wxString msg; wxString msg;
m_isFlipped = false; m_isFlipped = false;
if( m_CurrentPad ) if( m_CurrentPad )
...@@ -298,7 +297,7 @@ void DIALOG_PAD_PROPERTIES::initValues() ...@@ -298,7 +297,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
SetPadLayersList( m_dummyPad->m_Masque_Layer ); SetPadLayersList( m_dummyPad->m_Masque_Layer );
msg.Clear(); msg.Clear();
msg << tmp; msg << m_dummyPad->m_Orient;
m_PadOrientCtrl->SetValue( msg ); m_PadOrientCtrl->SetValue( msg );
// Pad Orient // Pad Orient
...@@ -350,11 +349,10 @@ void DIALOG_PAD_PROPERTIES::initValues() ...@@ -350,11 +349,10 @@ void DIALOG_PAD_PROPERTIES::initValues()
m_PadOrientCtrl->SetValue( msg ); m_PadOrientCtrl->SetValue( msg );
// Selection du type // Selection du type
tmp = m_dummyPad->m_Attribut;
m_PadType->SetSelection( 0 ); m_PadType->SetSelection( 0 );
for( int ii = 0; ii < NBTYPES; ii++ ) for( int ii = 0; ii < NBTYPES; ii++ )
{ {
if( CodeType[ii] == tmp ) if( CodeType[ii] == m_dummyPad->m_Attribut )
{ {
m_PadType->SetSelection( ii ); m_PadType->SetSelection( ii );
break; break;
......
...@@ -30,12 +30,9 @@ ...@@ -30,12 +30,9 @@
#include "fctsys.h" #include "fctsys.h"
#include "common.h" #include "common.h"
#include "class_drawpanel.h"
#include "pcbnew.h" #include "pcbnew.h"
#include "wxPcbStruct.h" #include "wxPcbStruct.h"
#include "autorout.h"
#include "trigo.h" #include "trigo.h"
#include "gestfich.h"
#include "class_board_design_settings.h" #include "class_board_design_settings.h"
#include "protos.h" #include "protos.h"
...@@ -55,13 +52,13 @@ void DRC::ShowDialog() ...@@ -55,13 +52,13 @@ void DRC::ShowDialog()
PutValueInLocalUnits( *m_ui->m_SetTrackMinWidthCtrl, PutValueInLocalUnits( *m_ui->m_SetTrackMinWidthCtrl,
m_pcb->GetBoardDesignSettings()->m_TrackMinWidth, m_pcb->GetBoardDesignSettings()->m_TrackMinWidth,
m_mainWindow->m_InternalUnits );; m_mainWindow->m_InternalUnits );
PutValueInLocalUnits( *m_ui->m_SetViaMinSizeCtrl, PutValueInLocalUnits( *m_ui->m_SetViaMinSizeCtrl,
m_pcb->GetBoardDesignSettings()->m_ViasMinSize, m_pcb->GetBoardDesignSettings()->m_ViasMinSize,
m_mainWindow->m_InternalUnits );; m_mainWindow->m_InternalUnits );
PutValueInLocalUnits( *m_ui->m_SetMicroViakMinSizeCtrl, PutValueInLocalUnits( *m_ui->m_SetMicroViakMinSizeCtrl,
m_pcb->GetBoardDesignSettings()->m_MicroViasMinSize, m_pcb->GetBoardDesignSettings()->m_MicroViasMinSize,
m_mainWindow->m_InternalUnits );; m_mainWindow->m_InternalUnits );
m_ui->m_CreateRptCtrl->SetValue( m_doCreateRptFile ); m_ui->m_CreateRptCtrl->SetValue( m_doCreateRptFile );
m_ui->m_RptFilenameCtrl->SetValue( m_rptFilename ); m_ui->m_RptFilenameCtrl->SetValue( m_rptFilename );
...@@ -415,20 +412,6 @@ bool DRC::testNetClasses() ...@@ -415,20 +412,6 @@ bool DRC::testNetClasses()
} }
void DRC::testTracks()
{
for( TRACK* segm = m_pcb->m_Track; segm && segm->Next(); segm = segm->Next() )
{
if( !doTrackDrc( segm, segm->Next(), true ) )
{
wxASSERT( m_currentMarker );
m_pcb->Add( m_currentMarker );
m_currentMarker = 0;
}
}
}
/***********************/ /***********************/
void DRC::testPad2Pad() void DRC::testPad2Pad()
/***********************/ /***********************/
...@@ -468,6 +451,20 @@ void DRC::testPad2Pad() ...@@ -468,6 +451,20 @@ void DRC::testPad2Pad()
} }
void DRC::testTracks()
{
for( TRACK* segm = m_pcb->m_Track; segm && segm->Next(); segm = segm->Next() )
{
if( !doTrackDrc( segm, segm->Next(), true ) )
{
wxASSERT( m_currentMarker );
m_pcb->Add( m_currentMarker );
m_currentMarker = 0;
}
}
}
void DRC::testUnconnected() void DRC::testUnconnected()
{ {
if( (m_pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 ) if( (m_pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
...@@ -552,430 +549,6 @@ void DRC::testZones( bool adoTestFillSegments ) ...@@ -552,430 +549,6 @@ void DRC::testZones( bool adoTestFillSegments )
} }
/***********************************************************************/
bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
/***********************************************************************/
{
TRACK* track;
int dx, dy; // utilise pour calcul des dim x et dim y des segments
int layerMask;
int net_code_ref;
wxPoint shape_pos;
NETCLASS* netclass = aRefSeg->GetNetClass();
/* In order to make some calculations more easier or faster,
* pads and tracks coordinates will be made relative to the reference segment origin
*/
wxPoint origin = aRefSeg->m_Start; // origin will be the origin of other coordinates
m_segmEnd.x = dx = aRefSeg->m_End.x - origin.x;
m_segmEnd.y = dy = aRefSeg->m_End.y - origin.y;
layerMask = aRefSeg->ReturnMaskLayer();
net_code_ref = aRefSeg->GetNet();
m_segmAngle = 0;
// Phase 0 : Test vias
if( aRefSeg->Type() == TYPE_VIA )
{
// test if the via size is smaller than minimum
if( aRefSeg->Shape() == VIA_MICROVIA )
{
if( aRefSeg->m_Width < netclass->GetuViaMinDiameter() )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_TOO_SMALL_MICROVIA, m_currentMarker );
return false;
}
}
else
{
if( aRefSeg->m_Width < netclass->GetViaMinDiameter() )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_TOO_SMALL_VIA, m_currentMarker );
return false;
}
}
// 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( aRefSeg->GetDrillValue() > aRefSeg->m_Width )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_VIA_HOLE_BIGGER, m_currentMarker );
return false;
}
// 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( aRefSeg->Shape() == VIA_MICROVIA )
{
int layer1, layer2;
bool err = true;
( (SEGVIA*) aRefSeg )->ReturnLayerPair( &layer1, &layer2 );
if( layer1> layer2 )
EXCHG( layer1, layer2 );
// test:
if( layer1 == LAYER_N_BACK && layer2 == LAYER_N_2 )
err = false;
if( layer1 == (m_pcb->GetBoardDesignSettings()->GetCopperLayerCount() - 2 )
&& layer2 == LAYER_N_FRONT )
err = false;
if( err )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR, m_currentMarker );
return false;
}
}
}
else // This is a track segment
{
if( aRefSeg->m_Width < netclass->GetTrackMinWidth() )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_TOO_SMALL_TRACK_WIDTH, m_currentMarker );
return false;
}
}
// for a non horizontal or vertical segment Compute the segment angle
// in tenths of degrees and its length
if( dx || dy )
{
// Compute the segment angle in 0,1 degrees
m_segmAngle = ArcTangente( dy, dx );
// Compute the segment length: we build an equivalent rotated segment,
// this segment is horizontal, therefore dx = length
RotatePoint( &dx, &dy, m_segmAngle ); // dx = length, dy = 0
}
m_segmLength = dx;
/******************************************/
/* Phase 1 : test DRC track to pads : */
/******************************************/
// Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers
// but having a hole
D_PAD dummypad( (MODULE*) NULL ); // construct this once outside following loop
dummypad.m_Masque_Layer = ALL_CU_LAYERS; // Ensure the hole is on all layers
// Compute the min distance to pads
if( testPads )
{
for( unsigned ii = 0; ii<m_pcb->GetPadsCount(); ++ii )
{
D_PAD* pad = m_pcb->m_NetInfo->GetPad( ii );
/* No problem if pads are on an other layer,
* But if a drill hole exists (a pad on a single layer can have a hole!)
* we must test the hole
*/
if( (pad->m_Masque_Layer & layerMask ) == 0 )
{
/* We must test the pad hole. In order to use the function checkClearanceSegmToPad(),
* a pseudo pad is used, with a shape and a size like the hole
*/
if( pad->m_Drill.x == 0 )
continue;
dummypad.m_Size = pad->m_Drill;
dummypad.SetPosition( pad->GetPosition() );
dummypad.m_PadShape = pad->m_DrillShape;
dummypad.m_Orient = pad->m_Orient;
dummypad.ComputeShapeMaxRadius(); // compute the radius of the circle containing this pad
m_padToTestPos.x = dummypad.GetPosition().x - origin.x;
m_padToTestPos.y = dummypad.GetPosition().y - origin.y;
if( !checkClearanceSegmToPad( &dummypad, aRefSeg->m_Width,
netclass->GetClearance() ) )
{
m_currentMarker = fillMarker( aRefSeg, pad,
DRCE_TRACK_NEAR_THROUGH_HOLE, m_currentMarker );
return false;
}
continue;
}
/* The pad must be in a net (i.e pt_pad->GetNet() != 0 )
* but no problem if the pad netcode is the current netcode (same net)
*/
if( pad->GetNet() // the pad must be connected
&& net_code_ref == pad->GetNet() ) // the pad net is the same as current net -> Ok
continue;
// DRC for the pad
shape_pos = pad->ReturnShapePos();
m_padToTestPos.x = shape_pos.x - origin.x;
m_padToTestPos.y = shape_pos.y - origin.y;
if( !checkClearanceSegmToPad( pad, aRefSeg->m_Width, aRefSeg->GetClearance( pad ) ) )
{
m_currentMarker = fillMarker( aRefSeg, pad,
DRCE_TRACK_NEAR_PAD, m_currentMarker );
return false;
}
}
}
/***********************************************/
/* Phase 2: test DRC with other track segments */
/***********************************************/
// At this point the reference segment is the X axis
// Test the reference segment with other track segments
for( track = aStart; track; track = track->Next() )
{
// coord des extremites du segment teste dans le repere modifie
int x0;
int y0;
int xf;
int yf;
// No problem if segments have the same net code:
if( net_code_ref == track->GetNet() )
continue;
// No problem if segment are on different layers :
if( ( layerMask & track->ReturnMaskLayer() ) == 0 )
continue;
// the minimum distance = clearance plus half the reference track
// width plus half the other track's width
int w_dist = aRefSeg->GetClearance( track );
w_dist += (aRefSeg->m_Width + track->m_Width) / 2;
// If the reference segment is a via, we test it here
if( aRefSeg->Type() == TYPE_VIA )
{
int angle = 0; // angle du segment a tester;
dx = track->m_End.x - track->m_Start.x;
dy = track->m_End.y - track->m_Start.y;
x0 = aRefSeg->m_Start.x - track->m_Start.x;
y0 = aRefSeg->m_Start.y - track->m_Start.y;
if( track->Type() == TYPE_VIA )
{
// Test distance between two vias, i.e. two circles, trivial case
if( (int) hypot( x0, y0 ) < w_dist )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_VIA_NEAR_VIA, m_currentMarker );
return false;
}
}
else // test via to segment
{
// Compute l'angle
angle = ArcTangente( dy, dx );
// Compute new coordinates ( the segment become horizontal)
RotatePoint( &dx, &dy, angle );
RotatePoint( &x0, &y0, angle );
if( !checkMarginToCircle( x0, y0, w_dist, dx ) )
{
m_currentMarker = fillMarker( track, aRefSeg,
DRCE_VIA_NEAR_TRACK, m_currentMarker );
return false;
}
}
continue;
}
/* We compute x0,y0, xf,yf = starting and ending point coordinates for
* the segment to test in the new axis : the new X axis is the
* reference segment. We must translate and rotate the segment to test
*/
x0 = track->m_Start.x - origin.x;
y0 = track->m_Start.y - origin.y;
xf = track->m_End.x - origin.x;
yf = track->m_End.y - origin.y;
RotatePoint( &x0, &y0, m_segmAngle );
RotatePoint( &xf, &yf, m_segmAngle );
if( track->Type() == TYPE_VIA )
{
if( checkMarginToCircle( x0, y0, w_dist, m_segmLength ) )
continue;
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_NEAR_VIA, m_currentMarker );
return false;
}
/* We have changed axis:
* the reference segment is Horizontal.
* 3 cases : the segment to test can be parallel, perpendicular or have an other direction
*/
if( y0 == yf ) // parallel segments
{
if( abs( y0 ) >= w_dist )
continue;
if( x0 > xf )
EXCHG( x0, xf ); /* pour que x0 <= xf */
if( x0 > (-w_dist) && x0 < (m_segmLength + w_dist) ) /* possible error drc */
{
/* Fine test : we consider the rounded shape of the ends */
if( x0 >= 0 && x0 <= m_segmLength )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS1, m_currentMarker );
return false;
}
if( !checkMarginToCircle( x0, y0, w_dist, m_segmLength ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS2, m_currentMarker );
return false;
}
}
if( xf > (-w_dist) && xf < (m_segmLength + w_dist) )
{
/* Fine test : we consider the rounded shape of the ends */
if( xf >= 0 && xf <= m_segmLength )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS3, m_currentMarker );
return false;
}
if( !checkMarginToCircle( xf, yf, w_dist, m_segmLength ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS4, m_currentMarker );
return false;
}
}
if( x0 <=0 && xf >= 0 )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_UNKNOWN1, m_currentMarker );
return false;
}
}
else if( x0 == xf ) // perpendicular segments
{
if( ( x0 <= (-w_dist) ) || ( x0 >= (m_segmLength + w_dist) ) )
continue;
// Test if segments are crossing
if( y0 > yf )
EXCHG( y0, yf );
if( (y0 < 0) && (yf > 0) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACKS_CROSSING, m_currentMarker );
return false;
}
// At this point the drc error is due to an end near a reference segm end
if( !checkMarginToCircle( x0, y0, w_dist, m_segmLength ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM1, m_currentMarker );
return false;
}
if( !checkMarginToCircle( xf, yf, w_dist, m_segmLength ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM2, m_currentMarker );
return false;
}
}
else // segments quelconques entre eux
{
// calcul de la "surface de securite du segment de reference
// First rought 'and fast) test : the track segment is like a rectangle
m_xcliplo = m_ycliplo = -w_dist;
m_xcliphi = m_segmLength + w_dist;
m_ycliphi = w_dist;
// A fine test is needed because a serment is not exactly a
// rectangle, it has rounded ends
if( !checkLine( x0, y0, xf, yf ) )
{
/* 2eme passe : the track has rounded ends.
* we must a fine test for each rounded end and the
* rectangular zone
*/
m_xcliplo = 0;
m_xcliphi = m_segmLength;
if( !checkLine( x0, y0, xf, yf ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM3, m_currentMarker );
return false;
}
else // The drc error is due to the starting or the ending point of the reference segment
{
// Test the starting and the ending point
int angle, rx0, ry0, rxf, ryf;
x0 = track->m_Start.x;
y0 = track->m_Start.y;
xf = track->m_End.x;
yf = track->m_End.y;
dx = xf - x0;
dy = yf - y0;
/* Compute the segment orientation (angle) en 0,1 degre */
angle = ArcTangente( dy, dx );
/* Compute the segment lenght: dx = longueur */
RotatePoint( &dx, &dy, angle );
/* Comute the reference segment coordinates relatives to a
* X axis = current tested segment
*/
rx0 = aRefSeg->m_Start.x - x0;
ry0 = aRefSeg->m_Start.y - y0;
rxf = aRefSeg->m_End.x - x0;
ryf = aRefSeg->m_End.y - y0;
RotatePoint( &rx0, &ry0, angle );
RotatePoint( &rxf, &ryf, angle );
if( !checkMarginToCircle( rx0, ry0, w_dist, dx ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM4, m_currentMarker );
return false;
}
if( !checkMarginToCircle( rxf, ryf, w_dist, dx ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM5, m_currentMarker );
return false;
}
}
}
}
}
return true;
}
/*****************************************************************************/ /*****************************************************************************/
bool DRC::doPadToPadsDrc( D_PAD* aRefPad, LISTE_PAD* aStart, LISTE_PAD* aEnd, bool DRC::doPadToPadsDrc( D_PAD* aRefPad, LISTE_PAD* aStart, LISTE_PAD* aEnd,
int x_limit ) int x_limit )
...@@ -987,7 +560,7 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, LISTE_PAD* aStart, LISTE_PAD* aEnd, ...@@ -987,7 +560,7 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, LISTE_PAD* aStart, LISTE_PAD* aEnd,
// pad to pad hole DRC, using pad to pad DRC test. // pad to pad hole DRC, using pad to pad DRC test.
// this dummy pad is a circle or an oval. // this dummy pad is a circle or an oval.
static D_PAD dummypad( (MODULE*) NULL ); static D_PAD dummypad( (MODULE*) NULL );
dummypad.m_Masque_Layer = ALL_CU_LAYERS; // za hole is on all layers dummypad.m_Masque_Layer |= ALL_CU_LAYERS; // Ensure the hole is on all copper layers
dummypad.m_LocalClearance = 1; /* Use the minimal local clerance value for the dummy pad dummypad.m_LocalClearance = 1; /* Use the minimal local clerance value for the dummy pad
* the clearance of the active pad will be used * the clearance of the active pad will be used
* as minimum distance to a hole * as minimum distance to a hole
...@@ -1088,562 +661,3 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, LISTE_PAD* aStart, LISTE_PAD* aEnd, ...@@ -1088,562 +661,3 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, LISTE_PAD* aStart, LISTE_PAD* aEnd,
return true; return true;
} }
// Rotate a vector by an angle
wxPoint rotate( wxPoint p, int angle )
{
wxPoint n;
double theta = M_PI * (double) angle / 1800.0;
n.x = wxRound( (double) p.x * cos( theta ) - (double) p.y * sin( theta ) );
n.y = wxRound( p.x * sin( theta ) + p.y * cos( theta ) );
return n;
}
/* test DRC between 2 pads.
* this function can be also used to test DRC between a pas and a hole,
* because a hole is like a round pad.
*/
bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
{
int dist;
int pad_angle;
// Get the clerance between the 2 pads. this is the min distance between aRefPad and aPad
int dist_min = aRefPad->GetClearance( aPad );
// relativePadPos is the aPad shape position relative to the aRefPad shape position
wxPoint relativePadPos = aPad->ReturnShapePos() - aRefPad->ReturnShapePos();
dist = (int) hypot( relativePadPos.x, relativePadPos.y );
// return true if clearance between aRefPad and aPad is >= dist_min, else false
bool diag = true;
// Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
if( (dist - aRefPad->m_ShapeMaxRadius - aPad->m_ShapeMaxRadius) >= dist_min )
goto exit;
/* Here, pads are near and DRC depend 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, try to have
* aRefPad shape type = PAD_CIRCLE or PAD_OVAL. Swap aRefPad and aPad if needed
*/
bool swap_pads;
swap_pads = false;
if( (aRefPad->m_PadShape != PAD_CIRCLE) && (aPad->m_PadShape == PAD_CIRCLE) )
swap_pads = true;
else if( (aRefPad->m_PadShape != PAD_OVAL) && (aPad->m_PadShape == PAD_OVAL) )
swap_pads = true;
if( swap_pads )
{
EXCHG( aRefPad, aPad );
relativePadPos = -relativePadPos;
}
/* Because pad exchange, aRefPad shape is PAD_CIRCLE or PAD_OVAL,
* if one of the 2 pads was a PAD_CIRCLE or PAD_OVAL.
* Therefore, if aRefPad is a PAD_RECT or a PAD_TRAPEZOID,
* aPad is also a PAD_RECT or a PAD_TRAPEZOID
*/
switch( aRefPad->m_PadShape )
{
case PAD_CIRCLE:
/* One can use checkClearanceSegmToPad to test clearance
* aRefPad is like a track segment with a null lenght and a witdth = m_Size.x
*/
m_segmLength = 0;
m_segmAngle = 0;
m_segmEnd.x = m_segmEnd.y = 0;
m_padToTestPos.x = relativePadPos.x;
m_padToTestPos.y = relativePadPos.y;
diag = checkClearanceSegmToPad( aPad, aRefPad->m_Size.x, dist_min );
break;
case PAD_RECT:
RotatePoint( &relativePadPos, aRefPad->m_Orient );
// pad_angle = pad orient relative to the aRefPad orient
pad_angle = aRefPad->m_Orient + aPad->m_Orient;
NORMALIZE_ANGLE_POS( pad_angle );
if( aPad->m_PadShape == PAD_RECT )
{
wxSize size = aPad->m_Size;
// The trivial case is if both rects are rotated by multiple of 90 deg
// Most of time this is the case, and the test is fast
if( ( (aRefPad->m_Orient == 0) || (aRefPad->m_Orient == 900)
|| (aRefPad->m_Orient == 1800) || (aRefPad->m_Orient == 2700) )
&& ( (aPad->m_Orient == 0) || (aPad->m_Orient == 900) || (aPad->m_Orient == 1800)
|| (aPad->m_Orient == 2700) ) )
{
if( (pad_angle == 900) || (pad_angle == 2700) )
{
EXCHG( size.x, size.y );
}
// Test DRC:
diag = false;
relativePadPos.x = ABS( relativePadPos.x );
relativePadPos.y = ABS( relativePadPos.y );
if( ( relativePadPos.x - ( (size.x + aRefPad->m_Size.x) / 2 ) ) >= dist_min )
diag = true;
if( ( relativePadPos.y - ( (size.y + aRefPad->m_Size.y) / 2 ) ) >= dist_min )
diag = true;
}
else // al least on pad has any other orient. Test is more tricky
{
/* Use TestForIntersectionOfStraightLineSegments() for all 4 edges (segments).*/
/* Test if one center point is contained in the other and thus the pads overlap.
* This case is not covered by the following check if one pad is
* completely contained in the other (because edges don't intersect)!
*/
if( ( (dist < aPad->m_Size.x) && (dist < aPad->m_Size.y) )
|| ( (dist < aRefPad->m_Size.x) && (dist < aRefPad->m_Size.y) ) )
{
diag = false;
}
// Vectors from center to corner
wxPoint aPad_c2c = wxPoint( aPad->m_Size.x / 2, aPad->m_Size.y / 2 );
wxPoint aRefPad_c2c = wxPoint( aRefPad->m_Size.x / 2, aRefPad->m_Size.y / 2 );
for( int i = 0; i<4; i++ ) // for all edges in aPad
{
wxPoint p11 = aPad->ReturnShapePos() + rotate( aPad_c2c, aPad->m_Orient );
// flip the center-to-corner vector
if( i % 2 == 0 )
{
aPad_c2c.x = -aPad_c2c.x;
}
else
{
aPad_c2c.y = -aPad_c2c.y;
}
wxPoint p12 = aPad->ReturnShapePos() + rotate( aPad_c2c, aPad->m_Orient );
for( int j = 0; j<4; j++ ) // for all edges in aRefPad
{
wxPoint p21 = aRefPad->ReturnShapePos() + rotate( aRefPad_c2c,
aRefPad->m_Orient );
// flip the center-to-corner vector
if( j % 2 == 0 )
{
aRefPad_c2c.x = -aRefPad_c2c.x;
}
else
{
aRefPad_c2c.y = -aRefPad_c2c.y;
}
wxPoint p22 = aRefPad->ReturnShapePos() + rotate( aRefPad_c2c,
aRefPad->m_Orient );
int x, y;
double d;
int intersect = TestForIntersectionOfStraightLineSegments( p11.x,
p11.y,
p12.x,
p12.y,
p21.x,
p21.y,
p22.x,
p22.y,
&x,
&y,
&d );
if( intersect || (d< dist_min) )
{
diag = false;
}
}
}
}
}
else
{
// TODO: Pad -> other shape! (PAD_TRAPEZOID)
}
break;
case PAD_OVAL: /* an oval pad is like a track segment */
{
/* Create a track segment with same dimensions as the oval aRefPad
* and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
*/
int segm_width;
m_segmAngle = aRefPad->m_Orient; // Segment orient.
if( aRefPad->m_Size.y < aRefPad->m_Size.x ) // Build an horizontal equiv segment
{
segm_width = aRefPad->m_Size.y;
m_segmLength = aRefPad->m_Size.x - aRefPad->m_Size.y;
}
else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
{
segm_width = aRefPad->m_Size.x;
m_segmLength = aRefPad->m_Size.y - aRefPad->m_Size.x;
m_segmAngle += 900;
}
/* the start point must be 0,0 and currently relativePadPos
* is relative the center of pad coordinate */
wxPoint segstart;
segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment
RotatePoint( &segstart, m_segmAngle ); // True start point coordinate of the equivalent segment
// move pad position relative to the segment origin
m_padToTestPos = relativePadPos - segstart;
// Calculate segment end
m_segmEnd.x = -2 * segstart.x;
m_segmEnd.y = -2 * segstart.y; // end of segment coordinate
diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
break;
}
case PAD_TRAPEZOID:
default:
/* TODO...*/
break;
}
exit: // the only way out (hopefully) for simpler debugging
return diag;
}
/* test if distance between a segment is > aMinDist
* segment start point is assumed in (0,0) and segment start point in m_segmEnd
* and have aSegmentWidth.
*/
bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
{
wxSize padHalfsize; // half the dimension of the pad
int orient;
int x0, y0, xf, yf;
int seuil;
int deltay;
int segmHalfWidth = aSegmentWidth / 2;
seuil = segmHalfWidth + aMinDist;
padHalfsize.x = aPad->m_Size.x >> 1;
padHalfsize.y = aPad->m_Size.y >> 1;
if( aPad->m_PadShape == PAD_CIRCLE )
{
/* Easy case: just test the distance between segment and pad centre
* calculate pad coordinates in the X,Y axis with X axis = segment to test
*/
RotatePoint( &m_padToTestPos.x, &m_padToTestPos.y, m_segmAngle );
return checkMarginToCircle( m_padToTestPos.x, m_padToTestPos.y,
seuil + padHalfsize.x, m_segmLength );
}
else
{
/* calculate the bounding box of the pad, including the clearance and the segment width
* if the line from 0 to m_segmEnd does not intersect this bounding box,
* the clearance is always OK
* But if intersect, a better analysis of the pad shape must be done.
*/
m_xcliplo = m_padToTestPos.x - seuil - padHalfsize.x;
m_ycliplo = m_padToTestPos.y - seuil - padHalfsize.y;
m_xcliphi = m_padToTestPos.x + seuil + padHalfsize.x;
m_ycliphi = m_padToTestPos.y + seuil + padHalfsize.y;
x0 = y0 = 0;
xf = m_segmEnd.x;
yf = m_segmEnd.y;
orient = aPad->m_Orient;
RotatePoint( &x0, &y0, m_padToTestPos.x, m_padToTestPos.y, -orient );
RotatePoint( &xf, &yf, m_padToTestPos.x, m_padToTestPos.y, -orient );
if( checkLine( x0, y0, xf, yf ) )
return true;
/* segment intersects the bounding box. But there is not always a DRC error.
* A fine analysis of the pad shape must be done.
*/
switch( aPad->m_PadShape )
{
default:
return false;
case PAD_OVAL:
/* an oval is a complex shape, but is a rectangle and 2 circles
* these 3 basic shapes are more easy to test.
*/
/* We use a vertical oval shape. for horizontal ovals, swap x and y size and rotate the shape*/
if( padHalfsize.x > padHalfsize.y )
{
EXCHG( padHalfsize.x, padHalfsize.y );
orient += 900;
if( orient >= 3600 )
orient -= 3600;
}
deltay = padHalfsize.y - padHalfsize.x;
// ici: padHalfsize.x = rayon, delta = dist centre cercles a centre pad
// Test the rectangle area between the two circles
m_xcliplo = m_padToTestPos.x - seuil - padHalfsize.x;
m_ycliplo = m_padToTestPos.y - segmHalfWidth - deltay;
m_xcliphi = m_padToTestPos.x + seuil + padHalfsize.x;
m_ycliphi = m_padToTestPos.y + segmHalfWidth + deltay;
if( !checkLine( x0, y0, xf, yf ) )
return false;
// test the first circle
x0 = m_padToTestPos.x; // x0,y0 = centre of the upper circle of the oval shape
y0 = m_padToTestPos.y + deltay;
// Calculate the actual position of the circle, given the pad orientation:
RotatePoint( &x0, &y0, m_padToTestPos.x, m_padToTestPos.y, orient );
// Calculate the actual position of the circle in the new X,Y axis:
RotatePoint( &x0, &y0, m_segmAngle );
if( !checkMarginToCircle( x0, y0, padHalfsize.x + seuil, m_segmLength ) )
return false;
// test the second circle
x0 = m_padToTestPos.x; // x0,y0 = centre of the lower circle of the oval shape
y0 = m_padToTestPos.y - deltay;
RotatePoint( &x0, &y0, m_padToTestPos.x, m_padToTestPos.y, orient );
RotatePoint( &x0, &y0, m_segmAngle );
if( !checkMarginToCircle( x0, y0, padHalfsize.x + seuil, m_segmLength ) )
return false;
break;
case PAD_RECT: /* 2 rectangle + 4 1/4 cercles a tester */
/* Test du rectangle dimx + seuil, dimy */
m_xcliplo = m_padToTestPos.x - padHalfsize.x - seuil;
m_ycliplo = m_padToTestPos.y - padHalfsize.y;
m_xcliphi = m_padToTestPos.x + padHalfsize.x + seuil;
m_ycliphi = m_padToTestPos.y + padHalfsize.y;
if( !checkLine( x0, y0, xf, yf ) )
{
return false;
}
/* Test du rectangle dimx , dimy + seuil */
m_xcliplo = m_padToTestPos.x - padHalfsize.x;
m_ycliplo = m_padToTestPos.y - padHalfsize.y - seuil;
m_xcliphi = m_padToTestPos.x + padHalfsize.x;
m_ycliphi = m_padToTestPos.y + padHalfsize.y + seuil;
if( !checkLine( x0, y0, xf, yf ) )
{
return false;
}
/* test des 4 cercles ( surface d'solation autour des sommets */
/* test du coin sup. gauche du pad */
x0 = m_padToTestPos.x - padHalfsize.x;
y0 = m_padToTestPos.y - padHalfsize.y;
RotatePoint( &x0, &y0, m_padToTestPos.x, m_padToTestPos.y, orient );
RotatePoint( &x0, &y0, m_segmAngle );
if( !checkMarginToCircle( x0, y0, seuil, m_segmLength ) )
{
return false;
}
/* test du coin sup. droit du pad */
x0 = m_padToTestPos.x + padHalfsize.x;
y0 = m_padToTestPos.y - padHalfsize.y;
RotatePoint( &x0, &y0, m_padToTestPos.x, m_padToTestPos.y, orient );
RotatePoint( &x0, &y0, m_segmAngle );
if( !checkMarginToCircle( x0, y0, seuil, m_segmLength ) )
{
return false;
}
/* test du coin inf. gauche du pad */
x0 = m_padToTestPos.x - padHalfsize.x;
y0 = m_padToTestPos.y + padHalfsize.y;
RotatePoint( &x0, &y0, m_padToTestPos.x, m_padToTestPos.y, orient );
RotatePoint( &x0, &y0, m_segmAngle );
if( !checkMarginToCircle( x0, y0, seuil, m_segmLength ) )
{
return false;
}
/* test du coin inf. droit du pad */
x0 = m_padToTestPos.x + padHalfsize.x;
y0 = m_padToTestPos.y + padHalfsize.y;
RotatePoint( &x0, &y0, m_padToTestPos.x, m_padToTestPos.y, orient );
RotatePoint( &x0, &y0, m_segmAngle );
if( !checkMarginToCircle( x0, y0, seuil, m_segmLength ) )
{
return false;
}
break;
case PAD_TRAPEZOID: //TODO
break;
}
}
return true;
}
/**********************************************************************/
bool DRC::checkMarginToCircle( int cx, int cy, int radius, int longueur )
/**********************************************************************/
{
if( abs( cy ) > radius )
return true;
if( (cx >= -radius ) && ( cx <= (longueur + radius) ) )
{
if( (cx >= 0) && (cx <= longueur) )
return false;
if( cx > longueur )
cx -= longueur;
if( hypot( cx, cy ) < radius )
return false;
}
return true;
}
/**********************************************/
/* int Tst_Ligne(int x1,int y1,int x2,int y2) */
/**********************************************/
static inline int USCALE( unsigned arg, unsigned num, unsigned den )
{
int ii;
ii = (int) ( ( (double) arg * num ) / den );
return ii;
}
#define WHEN_OUTSIDE return true
#define WHEN_INSIDE
bool DRC::checkLine( int x1, int y1, int x2, int y2 )
{
int temp;
if( x1 > x2 )
{
EXCHG( x1, x2 );
EXCHG( y1, y2 );
}
if( (x2 < m_xcliplo) || (x1 > m_xcliphi) )
{
WHEN_OUTSIDE;
}
if( y1 < y2 )
{
if( (y2 < m_ycliplo) || (y1 > m_ycliphi) )
{
WHEN_OUTSIDE;
}
if( y1 < m_ycliplo )
{
temp = USCALE( (x2 - x1), (m_ycliplo - y1), (y2 - y1) );
if( (x1 += temp) > m_xcliphi )
{
WHEN_OUTSIDE;
}
y1 = m_ycliplo;
WHEN_INSIDE;
}
if( y2 > m_ycliphi )
{
temp = USCALE( (x2 - x1), (y2 - m_ycliphi), (y2 - y1) );
if( (x2 -= temp) < m_xcliplo )
{
WHEN_OUTSIDE;
}
y2 = m_ycliphi;
WHEN_INSIDE;
}
if( x1 < m_xcliplo )
{
temp = USCALE( (y2 - y1), (m_xcliplo - x1), (x2 - x1) );
y1 += temp;
x1 = m_xcliplo;
WHEN_INSIDE;
}
if( x2 > m_xcliphi )
{
temp = USCALE( (y2 - y1), (x2 - m_xcliphi), (x2 - x1) );
y2 -= temp;
x2 = m_xcliphi;
WHEN_INSIDE;
}
}
else
{
if( (y1 < m_ycliplo) || (y2 > m_ycliphi) )
{
WHEN_OUTSIDE;
}
if( y1 > m_ycliphi )
{
temp = USCALE( (x2 - x1), (y1 - m_ycliphi), (y1 - y2) );
if( (x1 += temp) > m_xcliphi )
{
WHEN_OUTSIDE;
}
y1 = m_ycliphi;
WHEN_INSIDE;
}
if( y2 < m_ycliplo )
{
temp = USCALE( (x2 - x1), (m_ycliplo - y2), (y1 - y2) );
if( (x2 -= temp) < m_xcliplo )
{
WHEN_OUTSIDE;
}
y2 = m_ycliplo;
WHEN_INSIDE;
}
if( x1 < m_xcliplo )
{
temp = USCALE( (y1 - y2), (m_xcliplo - x1), (x2 - x1) );
y1 -= temp;
x1 = m_xcliplo;
WHEN_INSIDE;
}
if( x2 > m_xcliphi )
{
temp = USCALE( (y1 - y2), (x2 - m_xcliphi), (x2 - x1) );
y2 += temp;
x2 = m_xcliphi;
WHEN_INSIDE;
}
}
if( ( (x2 + x1) / 2 <= m_xcliphi ) && ( (x2 + x1) / 2 >= m_xcliplo ) \
&& ( (y2 + y1) / 2 <= m_ycliphi ) && ( (y2 + y1) / 2 >= m_ycliplo ) )
{
return false;
}
else
return true;
}
/**
*@file drc_clearance_test_functions.cpp
*/
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2004-2007 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
* Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2007 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
*/
/****************************/
/* DRC control */
/****************************/
#include "fctsys.h"
#include "common.h"
#include "pcbnew.h"
#include "wxPcbStruct.h"
#include "trigo.h"
#include "protos.h"
#include "drc_stuff.h"
/* compare 2 trapezoids (can be rectangle) and return true if distance > aDist
* i.e if for each edge of the first polygon distance from each edge of the other polygon
* is >= aDist
*/
bool trapezoid2trapezoidDRC( wxPoint aTref[4], wxPoint aTcompare[4], int aDist )
{
/* 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 polygond is
* completely contained in the other (because edges don't intersect)!
*/
if( TestPointInsidePolygon( aTref, 4, aTcompare[0] ) )
return false;
if( TestPointInsidePolygon( aTcompare, 4, aTref[0] ) )
return false;
int ii, jj, kk, ll;
for( ii = 0, jj = 3; ii<4; jj = ii, ii++ ) // for all edges in aTref
{
for( kk = 0, ll = 3; kk < 4; ll = kk, kk++ ) // for all edges in aTcompare
{
double d;
int intersect = TestForIntersectionOfStraightLineSegments( aTref[ii].x,
aTref[ii].y,
aTref[jj].x,
aTref[jj].y,
aTcompare[kk].x,
aTcompare[kk].y,
aTcompare[ll].x,
aTcompare[ll].y,
NULL, NULL, &d );
if( intersect || (d< aDist) )
return false;
}
}
return true;
}
/* compare a trapezoids (can be rectangle) and a segment and return true if distance > aDist
*/
bool trapezoid2segmentDRC( wxPoint aTref[4], wxPoint aSegStart, wxPoint aSegEnd, int aDist )
{
/* 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, 4, aSegStart ) )
return false;
int ii, jj;
for( ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edges in aTref
{
double d;
int intersect = TestForIntersectionOfStraightLineSegments( aTref[ii].x,
aTref[ii].y,
aTref[jj].x,
aTref[jj].y,
aSegStart.x,
aSegStart.y,
aSegEnd.x,
aSegEnd.y,
NULL, NULL, &d );
if( intersect || (d< aDist) )
return false;
}
return true;
}
/* compare a trapezoid to a point and return true if distance > aDist
* do not use this function for horizontal or vertical rectangles
* because there is a faster an easier way to compare the distance
*/
bool trapezoid2pointDRC( wxPoint aTref[4], wxPoint aPcompare, int aDist )
{
/* Test if aPcompare point is contained in the polygon.
* This case is not covered by the following check if this point is inside the polygon
*/
if( TestPointInsidePolygon( aTref, 4, aPcompare ) )
{
return false;
}
// Test distance between aPcompare and polygon edges:
int ii, jj;
double dist = (double) aDist;
for( ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edges in polygon
{
if( TestLineHit( aTref[ii].x, aTref[ii].y,
aTref[jj].x, aTref[jj].y,
aPcompare.x, aPcompare.y,
dist ) )
return false;
}
return true;
}
// Rotate a vector by an angle
wxPoint rotate( wxPoint p, int angle )
{
wxPoint n;
double theta = M_PI * (double) angle / 1800.0;
n.x = wxRound( (double) p.x * cos( theta ) - (double) p.y * sin( theta ) );
n.y = wxRound( p.x * sin( theta ) + p.y * cos( theta ) );
return n;
}
/***********************************************************************/
bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
/***********************************************************************/
{
TRACK* track;
wxPoint delta; // lenght on X and Y axis of segments
int layerMask;
int net_code_ref;
wxPoint shape_pos;
NETCLASS* netclass = aRefSeg->GetNetClass();
/* In order to make some calculations more easier or faster,
* pads and tracks coordinates will be made relative to the reference segment origin
*/
wxPoint origin = aRefSeg->m_Start; // origin will be the origin of other coordinates
m_segmEnd = delta = aRefSeg->m_End - origin;
m_segmAngle = 0;
layerMask = aRefSeg->ReturnMaskLayer();
net_code_ref = aRefSeg->GetNet();
// Phase 0 : Test vias
if( aRefSeg->Type() == TYPE_VIA )
{
// test if the via size is smaller than minimum
if( aRefSeg->Shape() == VIA_MICROVIA )
{
if( aRefSeg->m_Width < netclass->GetuViaMinDiameter() )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_TOO_SMALL_MICROVIA, m_currentMarker );
return false;
}
}
else
{
if( aRefSeg->m_Width < netclass->GetViaMinDiameter() )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_TOO_SMALL_VIA, m_currentMarker );
return false;
}
}
// 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( aRefSeg->GetDrillValue() > aRefSeg->m_Width )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_VIA_HOLE_BIGGER, m_currentMarker );
return false;
}
// 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( aRefSeg->Shape() == VIA_MICROVIA )
{
int layer1, layer2;
bool err = true;
( (SEGVIA*) aRefSeg )->ReturnLayerPair( &layer1, &layer2 );
if( layer1> layer2 )
EXCHG( layer1, layer2 );
// test:
if( layer1 == LAYER_N_BACK && layer2 == LAYER_N_2 )
err = false;
if( layer1 == (m_pcb->GetBoardDesignSettings()->GetCopperLayerCount() - 2 )
&& layer2 == LAYER_N_FRONT )
err = false;
if( err )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR, m_currentMarker );
return false;
}
}
}
else // This is a track segment
{
if( aRefSeg->m_Width < netclass->GetTrackMinWidth() )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_TOO_SMALL_TRACK_WIDTH, m_currentMarker );
return false;
}
}
// for a non horizontal or vertical segment Compute the segment angle
// in tenths of degrees and its length
if( delta.x || delta.y )
{
// 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;
/******************************************/
/* Phase 1 : test DRC track to pads : */
/******************************************/
// Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers
// but having a hole
D_PAD dummypad( (MODULE*) NULL ); // construct this once outside following loop
dummypad.m_Masque_Layer = ALL_CU_LAYERS; // Ensure the hole is on all layers
// Compute the min distance to pads
if( testPads )
{
for( unsigned ii = 0; ii<m_pcb->GetPadsCount(); ++ii )
{
D_PAD* pad = m_pcb->m_NetInfo->GetPad( ii );
/* No problem if pads are on an other layer,
* But if a drill hole exists (a pad on a single layer can have a hole!)
* we must test the hole
*/
if( (pad->m_Masque_Layer & layerMask ) == 0 )
{
/* We must test the pad hole. In order to use the function checkClearanceSegmToPad(),
* a pseudo pad is used, with a shape and a size like the hole
*/
if( pad->m_Drill.x == 0 )
continue;
dummypad.m_Size = pad->m_Drill;
dummypad.SetPosition( pad->GetPosition() );
dummypad.m_PadShape = pad->m_DrillShape;
dummypad.m_Orient = pad->m_Orient;
dummypad.ComputeShapeMaxRadius(); // compute the radius of the circle containing this pad
m_padToTestPos = dummypad.GetPosition() - origin;
if( !checkClearanceSegmToPad( &dummypad, aRefSeg->m_Width,
netclass->GetClearance() ) )
{
m_currentMarker = fillMarker( aRefSeg, pad,
DRCE_TRACK_NEAR_THROUGH_HOLE, m_currentMarker );
return false;
}
continue;
}
/* The pad must be in a net (i.e pt_pad->GetNet() != 0 )
* but no problem if the pad netcode is the current netcode (same net)
*/
if( pad->GetNet() // the pad must be connected
&& net_code_ref == pad->GetNet() ) // the pad net is the same as current net -> Ok
continue;
// DRC for the pad
shape_pos = pad->ReturnShapePos();
m_padToTestPos = shape_pos - origin;
if( !checkClearanceSegmToPad( pad, aRefSeg->m_Width, aRefSeg->GetClearance( pad ) ) )
{
m_currentMarker = fillMarker( aRefSeg, pad,
DRCE_TRACK_NEAR_PAD, m_currentMarker );
return false;
}
}
}
/***********************************************/
/* Phase 2: test DRC with other track segments */
/***********************************************/
// At this point the reference segment is the X axis
// Test the reference segment with other track segments
for( track = aStart; track; track = track->Next() )
{
// coord des extremites du segment teste dans le repere modifie
wxPoint segStartPoint;
wxPoint segEndPoint;
// No problem if segments have the same net code:
if( net_code_ref == track->GetNet() )
continue;
// No problem if segment are on different layers :
if( ( layerMask & track->ReturnMaskLayer() ) == 0 )
continue;
// the minimum distance = clearance plus half the reference track
// width plus half the other track's width
int w_dist = aRefSeg->GetClearance( track );
w_dist += (aRefSeg->m_Width + track->m_Width) / 2;
// If the reference segment is a via, we test it here
if( aRefSeg->Type() == TYPE_VIA )
{
int angle = 0; // angle du segment a tester;
delta = track->m_End - track->m_Start;
segStartPoint = aRefSeg->m_Start - track->m_Start;
if( track->Type() == TYPE_VIA )
{
// Test distance between two vias, i.e. two circles, trivial case
if( (int) hypot( segStartPoint.x, segStartPoint.y ) < w_dist )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_VIA_NEAR_VIA, m_currentMarker );
return false;
}
}
else // test via to segment
{
// Compute l'angle
angle = ArcTangente( delta.y, delta.x );
// Compute new coordinates ( the segment become horizontal)
RotatePoint( &delta, angle );
RotatePoint( &segStartPoint, angle );
if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) )
{
m_currentMarker = fillMarker( track, aRefSeg,
DRCE_VIA_NEAR_TRACK, m_currentMarker );
return false;
}
}
continue;
}
/* We compute segStartPoint, segEndPoint = starting and ending point coordinates for
* the segment to test in the new axis : the new X axis is the
* reference segment. We must translate and rotate the segment to test
*/
segStartPoint = track->m_Start - origin;
segEndPoint = track->m_End - origin;
RotatePoint( &segStartPoint, m_segmAngle );
RotatePoint( &segEndPoint, m_segmAngle );
if( track->Type() == TYPE_VIA )
{
if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
continue;
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_NEAR_VIA, m_currentMarker );
return false;
}
/* We have changed axis:
* the reference segment is Horizontal.
* 3 cases : the segment to test can be parallel, perpendicular or have an other direction
*/
if( segStartPoint.y == segEndPoint.y ) // parallel segments
{
if( abs( segStartPoint.y ) >= w_dist )
continue;
// Ensure segStartPoint.x <= segEndPoint.x
if( segStartPoint.x > segEndPoint.x )
EXCHG( segStartPoint.x, segEndPoint.x );
if( segStartPoint.x > (-w_dist) && segStartPoint.x < (m_segmLength + w_dist) ) /* possible error drc */
{
// Fine test : we consider the rounded shape of each end of the track segment:
if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS1, m_currentMarker );
return false;
}
if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS2, m_currentMarker );
return false;
}
}
if( segEndPoint.x > (-w_dist) && segEndPoint.x < (m_segmLength + w_dist) )
{
/* Fine test : we consider the rounded shape of the ends */
if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS3, m_currentMarker );
return false;
}
if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS4, m_currentMarker );
return false;
}
}
if( segStartPoint.x <=0 && segEndPoint.x >= 0 )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACK_UNKNOWN1, m_currentMarker );
return false;
}
}
else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments
{
if( ( segStartPoint.x <= (-w_dist) ) || ( segStartPoint.x >= (m_segmLength + w_dist) ) )
continue;
// Test if segments are crossing
if( segStartPoint.y > segEndPoint.y )
EXCHG( segStartPoint.y, segEndPoint.y );
if( (segStartPoint.y < 0) && (segEndPoint.y > 0) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_TRACKS_CROSSING, m_currentMarker );
return false;
}
// At this point the drc error is due to an end near a reference segm end
if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM1, m_currentMarker );
return false;
}
if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM2, m_currentMarker );
return false;
}
}
else // segments quelconques entre eux
{
// calcul de la "surface de securite du segment de reference
// First rought 'and fast) test : the track segment is like a rectangle
m_xcliplo = m_ycliplo = -w_dist;
m_xcliphi = m_segmLength + w_dist;
m_ycliphi = w_dist;
// A fine test is needed because a serment is not exactly a
// rectangle, it has rounded ends
if( !checkLine( segStartPoint, segEndPoint ) )
{
/* 2eme passe : the track has rounded ends.
* we must a fine test for each rounded end and the
* rectangular zone
*/
m_xcliplo = 0;
m_xcliphi = m_segmLength;
if( !checkLine( segStartPoint, segEndPoint ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM3, m_currentMarker );
return false;
}
else // The drc error is due to the starting or the ending point of the reference segment
{
// Test the starting and the ending point
segStartPoint = track->m_Start;
segEndPoint = track->m_End;
delta = segEndPoint - segStartPoint;
/* Compute the segment orientation (angle) en 0,1 degre */
int angle = ArcTangente( delta.y, delta.x );
// Compute the segment lenght: delta.x = lenght after rotation
RotatePoint( &delta, angle );
/* Comute the reference segment coordinates relatives to a
* X axis = current tested segment
*/
wxPoint relStartPos = aRefSeg->m_Start - segStartPoint;
wxPoint relEndPos = aRefSeg->m_End - segStartPoint;
RotatePoint( &relStartPos, angle );
RotatePoint( &relEndPos, angle );
if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM4, m_currentMarker );
return false;
}
if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) )
{
m_currentMarker = fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM5, m_currentMarker );
return false;
}
}
}
}
}
return true;
}
/* test DRC between 2 pads.
* this function can be also used to test DRC between a pas and a hole,
* because a hole is like a round pad.
*/
bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
{
int dist;
int pad_angle;
// Get the clerance between the 2 pads. this is the min distance between aRefPad and aPad
int dist_min = aRefPad->GetClearance( aPad );
// relativePadPos is the aPad shape position relative to the aRefPad shape position
wxPoint relativePadPos = aPad->ReturnShapePos() - aRefPad->ReturnShapePos();
dist = (int) hypot( relativePadPos.x, relativePadPos.y );
// Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
if( (dist - aRefPad->m_ShapeMaxRadius - aPad->m_ShapeMaxRadius) >= dist_min )
return true;
/* Here, pads are near and DRC depend 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, try to have
* aRefPad shape type = PAD_CIRCLE or PAD_OVAL.
* if aRefPad = TRAP. and aPad = RECT, also swap pads
* Swap aRefPad and aPad if needed
*/
bool swap_pads;
swap_pads = false;
if( (aRefPad->m_PadShape != PAD_CIRCLE) && (aPad->m_PadShape == PAD_CIRCLE) )
swap_pads = true;
else if( (aRefPad->m_PadShape != PAD_OVAL) && (aPad->m_PadShape == PAD_OVAL) )
swap_pads = true;
else if( (aRefPad->m_PadShape != PAD_RECT) && (aPad->m_PadShape == PAD_RECT) )
swap_pads = true;
if( swap_pads )
{
EXCHG( aRefPad, aPad );
relativePadPos = -relativePadPos;
}
/* Because pad exchange, aRefPad shape is PAD_CIRCLE or PAD_OVAL,
* if one of the 2 pads was a PAD_CIRCLE or PAD_OVAL.
* Therefore, if aRefPad is a PAD_RECT or a PAD_TRAPEZOID,
* aPad is also a PAD_RECT or a PAD_TRAPEZOID
*/
bool diag = true;
switch( aRefPad->m_PadShape )
{
case PAD_CIRCLE:
/* One can use checkClearanceSegmToPad to test clearance
* aRefPad is like a track segment with a null lenght and a witdth = m_Size.x
*/
m_segmLength = 0;
m_segmAngle = 0;
m_segmEnd.x = m_segmEnd.y = 0;
m_padToTestPos = relativePadPos;
diag = checkClearanceSegmToPad( aPad, aRefPad->m_Size.x, dist_min );
break;
case PAD_RECT:
// pad_angle = pad orient relative to the aRefPad orient
pad_angle = aRefPad->m_Orient + aPad->m_Orient;
NORMALIZE_ANGLE_POS( pad_angle );
if( aPad->m_PadShape == PAD_RECT )
{
wxSize size = aPad->m_Size;
// The trivial case is if both rects are rotated by multiple of 90 deg
// Most of time this is the case, and the test is fast
if( ( (aRefPad->m_Orient == 0) || (aRefPad->m_Orient == 900)
|| (aRefPad->m_Orient == 1800) || (aRefPad->m_Orient == 2700) )
&& ( (aPad->m_Orient == 0) || (aPad->m_Orient == 900) || (aPad->m_Orient == 1800)
|| (aPad->m_Orient == 2700) ) )
{
if( (pad_angle == 900) || (pad_angle == 2700) )
{
EXCHG( size.x, size.y );
}
// Test DRC:
diag = false;
RotatePoint( &relativePadPos, aRefPad->m_Orient );
relativePadPos.x = ABS( relativePadPos.x );
relativePadPos.y = ABS( relativePadPos.y );
if( ( relativePadPos.x - ( (size.x + aRefPad->m_Size.x) / 2 ) ) >= dist_min )
diag = true;
if( ( relativePadPos.y - ( (size.y + aRefPad->m_Size.y) / 2 ) ) >= dist_min )
diag = true;
}
else // at least one pad has any other orient. Test is more tricky
{ // Use the trapezoid2trapezoidDRC which also compare 2 rectangles with any orientation
wxPoint polyref[4]; // Shape of aRefPad
wxPoint polycompare[4]; // Shape of aPad
aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->m_Orient );
aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->m_Orient );
// Move aPad shape to relativePadPos
for( int ii = 0; ii < 4; ii++ )
polycompare[ii] += relativePadPos;
// And now test polygons:
if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) )
diag = false;
}
}
else if( aPad->m_PadShape == PAD_TRAPEZOID )
{
wxPoint polyref[4]; // Shape of aRefPad
wxPoint polycompare[4]; // Shape of aPad
aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->m_Orient );
aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->m_Orient );
// Move aPad shape to relativePadPos
for( int ii = 0; ii < 4; ii++ )
polycompare[ii] += relativePadPos;
// And now test polygons:
if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) )
diag = false;
}
else // Should not occurs, because aPad and aRefPad are swapped
// to have only aPad shape RECT or TRAP and aRefPad shape TRAP or RECT.
{
wxLogDebug( wxT( "unexpected pad shape" ) );
}
break;
case PAD_OVAL: /* an oval pad is like a track segment */
{
/* Create a track segment with same dimensions as the oval aRefPad
* and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
*/
int segm_width;
m_segmAngle = aRefPad->m_Orient; // Segment orient.
if( aRefPad->m_Size.y < aRefPad->m_Size.x ) // Build an horizontal equiv segment
{
segm_width = aRefPad->m_Size.y;
m_segmLength = aRefPad->m_Size.x - aRefPad->m_Size.y;
}
else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
{
segm_width = aRefPad->m_Size.x;
m_segmLength = aRefPad->m_Size.y - aRefPad->m_Size.x;
m_segmAngle += 900;
}
/* the start point must be 0,0 and currently relativePadPos
* is relative the center of pad coordinate */
wxPoint segstart;
segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment
RotatePoint( &segstart, m_segmAngle ); // True start point coordinate of the equivalent segment
// move pad position relative to the segment origin
m_padToTestPos = relativePadPos - segstart;
// Calculate segment end
m_segmEnd.x = -2 * segstart.x;
m_segmEnd.y = -2 * segstart.y; // end of segment coordinate
diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
break;
}
case PAD_TRAPEZOID:
// at this point, aPad is also a trapezoid, because all other shapes
// have priority, and are already tested
wxASSERT( aPad->m_PadShape == PAD_TRAPEZOID );
{
wxPoint polyref[4]; // Shape of aRefPad
wxPoint polycompare[4]; // Shape of aPad
aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->m_Orient );
aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->m_Orient );
// Move aPad shape to relativePadPos
for( int ii = 0; ii < 4; ii++ )
polycompare[ii] += relativePadPos;
// And now test polygons:
if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) )
diag = false;
}
break;
default:
wxLogDebug( wxT( "unexpected pad shape" ) );
break;
}
return diag;
}
/* test if distance between a segment is > aMinDist
* segment start point is assumed in (0,0) and segment start point in m_segmEnd
* and have aSegmentWidth.
*/
bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
{
wxSize padHalfsize; // half the dimension of the pad
int orient;
wxPoint startPoint, endPoint;
int seuil;
int deltay;
int segmHalfWidth = aSegmentWidth / 2;
seuil = segmHalfWidth + aMinDist;
padHalfsize.x = aPad->m_Size.x >> 1;
padHalfsize.y = aPad->m_Size.y >> 1;
if( aPad->m_PadShape == PAD_TRAPEZOID ) // The size is bigger, due to m_DeltaSize extra size
{
padHalfsize.x += ABS(aPad->m_DeltaSize.y) / 2; // Remember: m_DeltaSize.y is the m_Size.x change
padHalfsize.y += ABS(aPad->m_DeltaSize.x) / 2; // Remember: m_DeltaSize.x is the m_Size.x change
}
if( aPad->m_PadShape == PAD_CIRCLE )
{
/* Easy case: just test the distance between segment and pad centre
* calculate pad coordinates in the X,Y axis with X axis = segment to test
*/
RotatePoint( &m_padToTestPos, m_segmAngle );
return checkMarginToCircle( m_padToTestPos, seuil + padHalfsize.x, m_segmLength );
}
/* calculate the bounding box of the pad, including the clearance and the segment width
* if the line from 0 to m_segmEnd does not intersect this bounding box,
* the clearance is always OK
* But if intersect, a better analysis of the pad shape must be done.
*/
m_xcliplo = m_padToTestPos.x - seuil - padHalfsize.x;
m_ycliplo = m_padToTestPos.y - seuil - padHalfsize.y;
m_xcliphi = m_padToTestPos.x + seuil + padHalfsize.x;
m_ycliphi = m_padToTestPos.y + seuil + padHalfsize.y;
startPoint.x = startPoint.y = 0;
endPoint = m_segmEnd;
orient = aPad->m_Orient;
RotatePoint( &startPoint, m_padToTestPos, -orient );
RotatePoint( &endPoint, m_padToTestPos, -orient );
if( checkLine( startPoint, endPoint ) )
return true;
/* segment intersects the bounding box. But there is not always a DRC error.
* A fine analysis of the pad shape must be done.
*/
switch( aPad->m_PadShape )
{
default:
return false;
case PAD_OVAL:
/* an oval is a complex shape, but is a rectangle and 2 circles
* these 3 basic shapes are more easy to test.
*/
/* We use a vertical oval shape. for horizontal ovals, swap x and y size and rotate the shape*/
if( padHalfsize.x > padHalfsize.y )
{
EXCHG( padHalfsize.x, padHalfsize.y );
orient += 900;
if( orient >= 3600 )
orient -= 3600;
}
deltay = padHalfsize.y - padHalfsize.x;
// ici: padHalfsize.x = rayon, delta = dist centre cercles a centre pad
// Test the rectangle area between the two circles
m_xcliplo = m_padToTestPos.x - seuil - padHalfsize.x;
m_ycliplo = m_padToTestPos.y - segmHalfWidth - deltay;
m_xcliphi = m_padToTestPos.x + seuil + padHalfsize.x;
m_ycliphi = m_padToTestPos.y + segmHalfWidth + deltay;
if( !checkLine( startPoint, endPoint ) )
return false;
// test the first circle
startPoint.x = m_padToTestPos.x; // segStartPoint.x,segStartPoint.y = centre of the upper circle of the oval shape
startPoint.y = m_padToTestPos.y + deltay;
// Calculate the actual position of the circle, given the pad orientation:
RotatePoint( &startPoint, m_padToTestPos, orient );
// Calculate the actual position of the circle in the new X,Y axis:
RotatePoint( &startPoint, m_segmAngle );
if( !checkMarginToCircle( startPoint, padHalfsize.x + seuil, m_segmLength ) )
return false;
// test the second circle
startPoint.x = m_padToTestPos.x; // segStartPoint.x,segStartPoint.y = centre of the lower circle of the oval shape
startPoint.y = m_padToTestPos.y - deltay;
RotatePoint( &startPoint, m_padToTestPos, orient );
RotatePoint( &startPoint, m_segmAngle );
if( !checkMarginToCircle( startPoint, padHalfsize.x + seuil, m_segmLength ) )
return false;
break;
case PAD_RECT: /* 2 rectangle + 4 1/4 cercles a tester */
/* Test du rectangle dimx + seuil, dimy */
m_xcliplo = m_padToTestPos.x - padHalfsize.x - seuil;
m_ycliplo = m_padToTestPos.y - padHalfsize.y;
m_xcliphi = m_padToTestPos.x + padHalfsize.x + seuil;
m_ycliphi = m_padToTestPos.y + padHalfsize.y;
if( !checkLine( startPoint, endPoint ) )
return false;
/* Test du rectangle dimx , dimy + seuil */
m_xcliplo = m_padToTestPos.x - padHalfsize.x;
m_ycliplo = m_padToTestPos.y - padHalfsize.y - seuil;
m_xcliphi = m_padToTestPos.x + padHalfsize.x;
m_ycliphi = m_padToTestPos.y + padHalfsize.y + seuil;
if( !checkLine( startPoint, endPoint ) )
return false;
/* test des 4 cercles ( surface d'solation autour des sommets */
/* test du coin sup. gauche du pad */
startPoint.x = m_padToTestPos.x - padHalfsize.x;
startPoint.y = m_padToTestPos.y - padHalfsize.y;
RotatePoint( &startPoint, m_padToTestPos, orient );
RotatePoint( &startPoint, m_segmAngle );
if( !checkMarginToCircle( startPoint, seuil, m_segmLength ) )
return false;
/* test du coin sup. droit du pad */
startPoint.x = m_padToTestPos.x + padHalfsize.x;
startPoint.y = m_padToTestPos.y - padHalfsize.y;
RotatePoint( &startPoint, m_padToTestPos, orient );
RotatePoint( &startPoint, m_segmAngle );
if( !checkMarginToCircle( startPoint, seuil, m_segmLength ) )
return false;
/* test du coin inf. gauche du pad */
startPoint.x = m_padToTestPos.x - padHalfsize.x;
startPoint.y = m_padToTestPos.y + padHalfsize.y;
RotatePoint( &startPoint, m_padToTestPos, orient );
RotatePoint( &startPoint, m_segmAngle );
if( !checkMarginToCircle( startPoint, seuil, m_segmLength ) )
return false;
/* test du coin inf. droit du pad */
startPoint.x = m_padToTestPos.x + padHalfsize.x;
startPoint.y = m_padToTestPos.y + padHalfsize.y;
RotatePoint( &startPoint, m_padToTestPos, orient );
RotatePoint( &startPoint, m_segmAngle );
if( !checkMarginToCircle( startPoint, seuil, m_segmLength ) )
return false;
break;
case PAD_TRAPEZOID:
{
wxPoint poly[4];
aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );
// Move shape to m_padToTestPos
for( int ii = 0; ii < 4; ii++ )
{
poly[ii] += m_padToTestPos;
RotatePoint( &poly[ii], m_segmAngle );
}
if( !trapezoid2segmentDRC( poly, wxPoint( 0, 0 ), wxPoint(m_segmLength,0), seuil ) )
return false;
}
break;
}
return true;
}
/**
* Helper function checkMarginToCircle
* Check the distance between a circle (round pad, via or round end of track)
* and a segment. the segment is expected starting at 0,0, and on the X axis
* return true if distance >= aRadius
*/
bool DRC::checkMarginToCircle( wxPoint aCentre, int aRadius, int aLength )
{
if( abs( aCentre.y ) > aRadius ) // trivial case
return true;
// Here, didstance between aCentre and X axis is < aRadius
if( (aCentre.x >= -aRadius ) && ( aCentre.x <= (aLength + aRadius) ) )
{
if( (aCentre.x >= 0) && (aCentre.x <= aLength) )
return false; // aCentre is between the starting point and the ending point of the segm
if( aCentre.x > aLength ) // aCentre is after the ending point
aCentre.x -= aLength; // move aCentre to the starting point of the segment
if( hypot( aCentre.x, aCentre.y ) < aRadius )
// distance between aCentre and the starting point or the ending point is < aRadius
return false;
}
return true;
}
// Helper function used in checkLine::
static inline int USCALE( unsigned arg, unsigned num, unsigned den )
{
int ii;
ii = (int) ( ( (double) arg * num ) / den );
return ii;
}
/** Helper function checkLine
* Test if a line intersects a bounding box (a rectangle)
* The rectangle is defined by m_xcliplo, m_ycliplo and m_xcliphi, m_ycliphi
* return true if the line from aSegStart to aSegEnd is outside the bounding box
*/
bool DRC::checkLine( wxPoint aSegStart, wxPoint aSegEnd )
{
#define WHEN_OUTSIDE return true
#define WHEN_INSIDE
int temp;
if( aSegStart.x > aSegEnd.x )
EXCHG( aSegStart, aSegEnd );
if( (aSegEnd.x < m_xcliplo) || (aSegStart.x > m_xcliphi) )
{
WHEN_OUTSIDE;
}
if( aSegStart.y < aSegEnd.y )
{
if( (aSegEnd.y < m_ycliplo) || (aSegStart.y > m_ycliphi) )
{
WHEN_OUTSIDE;
}
if( aSegStart.y < m_ycliplo )
{
temp =
USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y),
(aSegEnd.y - aSegStart.y) );
if( (aSegStart.x += temp) > m_xcliphi )
{
WHEN_OUTSIDE;
}
aSegStart.y = m_ycliplo;
WHEN_INSIDE;
}
if( aSegEnd.y > m_ycliphi )
{
temp =
USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi),
(aSegEnd.y - aSegStart.y) );
if( (aSegEnd.x -= temp) < m_xcliplo )
{
WHEN_OUTSIDE;
}
aSegEnd.y = m_ycliphi;
WHEN_INSIDE;
}
if( aSegStart.x < m_xcliplo )
{
temp =
USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x),
(aSegEnd.x - aSegStart.x) );
aSegStart.y += temp;
aSegStart.x = m_xcliplo;
WHEN_INSIDE;
}
if( aSegEnd.x > m_xcliphi )
{
temp =
USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi),
(aSegEnd.x - aSegStart.x) );
aSegEnd.y -= temp;
aSegEnd.x = m_xcliphi;
WHEN_INSIDE;
}
}
else
{
if( (aSegStart.y < m_ycliplo) || (aSegEnd.y > m_ycliphi) )
{
WHEN_OUTSIDE;
}
if( aSegStart.y > m_ycliphi )
{
temp =
USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi),
(aSegStart.y - aSegEnd.y) );
if( (aSegStart.x += temp) > m_xcliphi )
{
WHEN_OUTSIDE;
}
aSegStart.y = m_ycliphi;
WHEN_INSIDE;
}
if( aSegEnd.y < m_ycliplo )
{
temp =
USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y),
(aSegStart.y - aSegEnd.y) );
if( (aSegEnd.x -= temp) < m_xcliplo )
{
WHEN_OUTSIDE;
}
aSegEnd.y = m_ycliplo;
WHEN_INSIDE;
}
if( aSegStart.x < m_xcliplo )
{
temp =
USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x),
(aSegEnd.x - aSegStart.x) );
aSegStart.y -= temp;
aSegStart.x = m_xcliplo;
WHEN_INSIDE;
}
if( aSegEnd.x > m_xcliphi )
{
temp =
USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi),
(aSegEnd.x - aSegStart.x) );
aSegEnd.y += temp;
aSegEnd.x = m_xcliphi;
WHEN_INSIDE;
}
}
if( ( (aSegEnd.x + aSegStart.x) / 2 <= m_xcliphi )
&& ( (aSegEnd.x + aSegStart.x) / 2 >= m_xcliplo ) \
&& ( (aSegEnd.y + aSegStart.y) / 2 <= m_ycliphi )
&& ( (aSegEnd.y + aSegStart.y) / 2 >= m_ycliplo ) )
{
return false;
}
else
return true;
}
...@@ -173,10 +173,10 @@ private: ...@@ -173,10 +173,10 @@ private:
/* variables used in checkLine to test DRC segm to segm: /* variables used in checkLine to test DRC segm to segm:
* define the area relative to the ref segment that does not contains anu other segment * define the area relative to the ref segment that does not contains anu other segment
*/ */
int m_xcliplo; int m_xcliplo;
int m_ycliplo; int m_ycliplo;
int m_xcliphi; int m_xcliphi;
int m_ycliphi; int m_ycliphi;
WinEDA_PcbFrame* m_mainWindow; WinEDA_PcbFrame* m_mainWindow;
BOARD* m_pcb; BOARD* m_pcb;
...@@ -329,30 +329,27 @@ private: ...@@ -329,30 +329,27 @@ private:
/** /**
* Function checkMarginToCircle * Helper function checkMarginToCircle
* @todo this translation is no good, fix this: * Check the distance from a point to
* calculates the distance from a circle (via or round end of track) to the * a segment. the segment is expected starting at 0,0, and on the X axis
* segment of reference on the right hand side. * (used to test DRC between a segment and a round pad, via or round end of a track
* * @param aCentre The coordinate of the circle's center
* @param cx The x coordinate of the circle's center * @param aRadius A "keep out" radius centered over the circle
* @param cy The y coordinate of the circle's center * @param aLength The length of the segment (i.e. coordinate of end, becuase it is on the X axis)
* @param radius A "keep out" radius centered over the circle
* @param length The length of the segment (i.e. coordinate of end)
* @return bool - true if distance >= radius, else * @return bool - true if distance >= radius, else
* false when distance < radius * false when distance < aRadius
*/ */
static bool checkMarginToCircle( int cx, int cy, int radius, int length ); static bool checkMarginToCircle( wxPoint aCentre, int aRadius, int aLength );
/** /**
* Function checkLine * Function checkLine
* tests to see if one track is in contact with another track. * (helper function used in drc calculations to see if one track is in contact with another track).
* * Test if a line intersects a bounding box (a rectangle)
* Cette routine controle si la ligne (x1,y1 x2,y2) a une partie s'inscrivant * The rectangle is defined by m_xcliplo, m_ycliplo and m_xcliphi, m_ycliphi
* dans le cadre (xcliplo,ycliplo xcliphi,ycliphi) (variables globales, * return true if the line from aSegStart to aSegEnd is outside the bounding box
* locales a ce fichier)
*/ */
bool checkLine( int x1, int y1, int x2, int y2 ); bool checkLine( wxPoint aSegStart, wxPoint aSegEnd );
//-----</single tests>--------------------------------------------- //-----</single tests>---------------------------------------------
......
// math for graphics utility routines, from FreePCB // math for graphics utility routines and RC, from FreePCB
#include <vector> #include <vector>
...@@ -13,13 +13,14 @@ ...@@ -13,13 +13,14 @@
using namespace std; using namespace std;
// test for hit on line segment /** function TestLineHit
// i.e. cursor within a given distance from segment * test for hit on line segment i.e. a point within a given distance from segment
// enter with: x,y = cursor coords * @param x, y = cursor coords
// (xi,yi) and (xf,yf) are the end-points of the line segment * @param xi,yi and xf,yf = the end-points of the line segment
// dist = maximum distance for hit * @param dist = maximum distance for hit
// * return true if dist < distance between the point and the segment
int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist ) */
bool TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist )
{ {
double dd; double dd;
...@@ -29,14 +30,14 @@ int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist ) ...@@ -29,14 +30,14 @@ int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist )
// vertical segment // vertical segment
dd = fabs( (double)(x-xi) ); dd = fabs( (double)(x-xi) );
if( dd<dist && ( (yf>yi && y<yf && y>yi) || (yf<yi && y>yf && y<yi) ) ) if( dd<dist && ( (yf>yi && y<yf && y>yi) || (yf<yi && y>yf && y<yi) ) )
return 1; return true;
} }
else if( yf==yi ) else if( yf==yi )
{ {
// horizontal segment // horizontal segment
dd = fabs( (double)(y-yi) ); dd = fabs( (double)(y-yi) );
if( dd<dist && ( (xf>xi && x<xf && x>xi) || (xf<xi && x>xf && x<xi) ) ) if( dd<dist && ( (xf>xi && x<xf && x>xi) || (xf<xi && x>xf && x<xi) ) )
return 1; return true;
} }
else else
{ {
...@@ -62,10 +63,10 @@ int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist ) ...@@ -62,10 +63,10 @@ int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist )
{ {
// line segment more horizontal than vertical // line segment more horizontal than vertical
if( dd<dist && ( (xf>xi && xp<xf && xp>xi) || (xf<xi && xp>xf && xp<xi) ) ) if( dd<dist && ( (xf>xi && xp<xf && xp>xi) || (xf<xi && xp>xf && xp<xi) ) )
return 1; return true;
} }
} }
return 0; // no hit return false; // no hit
} }
...@@ -482,12 +483,12 @@ int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int ...@@ -482,12 +483,12 @@ int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int
return 1; return 1;
} }
// Test for intersection of line segments /** function TestForIntersectionOfStraightLineSegments
// If lines are parallel, returns false * Test for intersection of line segments
// If true, returns intersection coords in x, y * If lines are parallel, returns false
// if false, returns min. distance in dist (may be 0.0 if parallel) * If true, returns also intersection coords in x, y
// and coords on nearest point in one of the segments in (x,y) * if false, returns min. distance in dist (may be 0.0 if parallel)
// */
bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f, bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
int x2i, int y2i, int x2f, int y2f, int x2i, int y2i, int x2f, int y2f,
int * x, int * y, double * d ) int * x, int * y, double * d )
......
...@@ -13,8 +13,6 @@ typedef struct PointTag ...@@ -13,8 +13,6 @@ typedef struct PointTag
typedef struct EllipseTag typedef struct EllipseTag
{ {
PointT Center; /* ellipse center */ PointT Center; /* ellipse center */
// double MaxRad,MinRad; /* major and minor axis */
// double Phi; /* major axis rotation */
double xrad, yrad; // radii on x and y double xrad, yrad; // radii on x and y
double theta1, theta2; // start and end angle for arc double theta1, theta2; // start and end angle for arc
} EllipseKH; } EllipseKH;
...@@ -22,7 +20,16 @@ typedef struct EllipseTag ...@@ -22,7 +20,16 @@ typedef struct EllipseTag
// math stuff for graphics // math stuff for graphics
bool Quadratic( double a, double b, double c, double *x1, double *x2 ); bool Quadratic( double a, double b, double c, double *x1, double *x2 );
int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist );
/** function TestLineHit
* test for hit on line segment i.e. a point within a given distance from segment
* @param xi,yi and xf,yf = the end-points of the line segment
* @param dist = maximum distance for hit
* @param x, y = point to test coords
* @return true if hit (i.e dist < distance between the point and the segment, false if not.
*/
bool TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist );
int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style, int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style,
double * x1, double * y1, double * x2, double * y2, double * dist=NULL ); double * x1, double * y1, double * x2, double * y2, double * dist=NULL );
int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style, int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style,
...@@ -30,9 +37,23 @@ int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style, ...@@ -30,9 +37,23 @@ int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style,
double x[]=NULL, double y[]=NULL ); double x[]=NULL, double y[]=NULL );
bool FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 ); bool FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 );
bool FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 ); bool FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 );
/** function TestForIntersectionOfStraightLineSegments
* Test for intersection of line segments
* If lines are parallel, returns false
* If true, returns also intersection coords in x, y
* if false, returns min. distance in dist (may be 0.0 if parallel)
* and coords on nearest point in one of the segments in (x,y)
* @param x1i, y1i, x1f, y1f = integer coordinates of the first segment
* @param x2i, y2i, x2f, y2f = integer coordinates of the other segment
* @param x, y = pointers on 2 integer to store the intersection coordinates (can be NULL)
* @param dist = pointeur on a double to store the dist.
* @return true if intersect.
*/
bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f, bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
int x2i, int y2i, int x2f, int y2f, int x2i, int y2i, int x2f, int y2f,
int * x=NULL, int * y=NULL, double * dist=NULL ); int * x=NULL, int * y=NULL, double * dist=NULL );
int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1, int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1,
int x2i, int y2i, int x2f, int y2f, int style2, int w2, int x2i, int y2i, int x2f, int y2f, int style2, int w2,
int max_cl, int * x, int * y ); int max_cl, int * x, int * y );
......
///////////////////////////////////////////////////////////////////////////// /**
* @file polygon_test_point_inside.cpp
// Name: polygon_test_point_inside.cpp */
/////////////////////////////////////////////////////////////////////////////
#include <math.h> #include <math.h>
#include <vector> #include <vector>
#include "PolyLine.h" #include "PolyLine.h"
using namespace std;
/* this algo uses the the Jordan curve theorem to find if a point is inside or outside a polygon: /* this algo uses the the Jordan curve theorem to find if a point is inside or outside a polygon:
* It run a semi-infinite line horizontally (increasing x, fixed y) * It run a semi-infinite line horizontally (increasing x, fixed y)
* out from the test point, and count how many edges it crosses. * out from the test point, and count how many edges it crosses.
* At each crossing, the ray switches between inside and outside. * At each crossing, the ray switches between inside and outside.
* If odd count, the test point is inside the polygon * If odd count, the test point is inside the polygon
* This is called the Jordan curve theorem, or sometimes referred to as the "even-odd" test. * This is called the Jordan curve theorem, or sometimes referred to as the "even-odd" test.
* Take care to starting and ending points of segments outlines, when the horizontal line crosses a segment outline * Take care to starting and ending points of segments outlines, when the horizontal line crosses a segment outline
* exactly on an ending point: * exactly on an ending point:
* Because the starting point of a segment is also the ending point of the previous, only one must be used. * Because the starting point of a segment is also the ending point of the previous, only one must be used.
* And we do no use twice the same segment, so we do NOT use both starting and ending points of these 2 segments. * And we do no use twice the same segment, so we do NOT use both starting and ending points of these 2 segments.
...@@ -30,16 +27,19 @@ using namespace std; ...@@ -30,16 +27,19 @@ using namespace std;
#define INSIDE true #define INSIDE true
bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList, bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
int istart, int iend, int refx, int refy ) int aIdxstart,
int aIdxend,
int aRefx,
int aRefy)
/** Function TestPointInsidePolygon /** Function TestPointInsidePolygon
* test if a point is inside or outside a polygon. * test if a point is inside or outside a polygon.
* the polygon must have only lines (not arcs) for outlines. * the polygon must have only lines (not arcs) for outlines.
* Use TestPointInside or TestPointInsideContour for more complex polygons * Use TestPointInside or TestPointInsideContour for more complex polygons
* @param aPolysList: the list of polygons * @param aPolysList: the list of polygons
* @param istart: the starting point of a given polygon in m_FilledPolysList. * @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
* @param iend: the ending point of the polygon in m_FilledPolysList. * @param aIdxend: the ending point of the polygon in m_FilledPolysList.
* @param refx,refy: the point coordinate to test * @param aRefx, aRefy: the point coordinate to test
* @return true if the point is inside, false for outside * @return true if the point is inside, false for outside
*/ */
{ {
...@@ -48,7 +48,62 @@ bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList, ...@@ -48,7 +48,62 @@ bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
int count = 0; int count = 0;
// find all intersection points of line with polyline sides // find all intersection points of line with polyline sides
for( ics = istart, ice = iend; ics <= iend; ice = ics++ ) for( ics = aIdxstart, ice = aIdxend; ics <= aIdxend; ice = ics++ )
{
int seg_startX = aPolysList[ics].x;
int seg_startY = aPolysList[ics].y;
int seg_endX = aPolysList[ice].x;
int seg_endY = aPolysList[ice].y;
/* Trivial cases: skip if ref above or below the segment to test */
if( ( seg_startY > aRefy ) && (seg_endY > aRefy ) )
continue;
// segment below ref point, or one of its ends has the same Y pos as the ref point: skip
// So we eliminate one end point of 2 consecutive segments.
// Note: also we skip horizontal segments if ref point is on this horizontal line
// So reference points on horizontal segments outlines always are seen as outside the polygon
if( ( seg_startY <= aRefy ) && (seg_endY <= aRefy ) )
continue;
/* refy is between seg_startY and seg_endY.
* note: here: horizontal segments (seg_startY == seg_endY) are skipped,
* either by the first test or by the second test
* see if an horizontal semi infinite line from refx is intersecting the segment
*/
// calculate the x position of the intersection of this segment and the semi infinite line
// this is more easier if we move the X,Y axis origin to the segment start point:
seg_endX -= seg_startX;
seg_endY -= seg_startY;
double newrefx = (double) (aRefx - seg_startX);
double newrefy = (double) (aRefy - seg_startY);
// Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
// with the horizontal line at the new refy position
// the line slope = seg_endY/seg_endX;
// and the x pos relative to the new origin is intersec_x = refy/slope
// Note: because horizontal segments are skipped, 1/slope exists (seg_end_y never == O)
double intersec_x = newrefy * seg_endX / seg_endY;
if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
count++;
}
return count & 1 ? INSIDE : OUTSIDE;
}
/* Function TestPointInsidePolygon (overlaid)
* same as previous, but use wxPoint and aCount corners
*/
bool TestPointInsidePolygon( wxPoint *aPolysList, int aCount,wxPoint aRefPoint )
{
// count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
int ics, ice;
int count = 0;
// find all intersection points of line with polyline sides
for( ics = 0, ice = aCount-1; ics < aCount; ice = ics++ )
{ {
int seg_startX = aPolysList[ics].x; int seg_startX = aPolysList[ics].x;
int seg_startY = aPolysList[ics].y; int seg_startY = aPolysList[ics].y;
...@@ -56,14 +111,14 @@ bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList, ...@@ -56,14 +111,14 @@ bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
int seg_endY = aPolysList[ice].y; int seg_endY = aPolysList[ice].y;
/* Trivial cases: skip if ref above or below the segment to test */ /* Trivial cases: skip if ref above or below the segment to test */
if( ( seg_startY > refy ) && (seg_endY > refy ) ) if( ( seg_startY > aRefPoint.y ) && (seg_endY > aRefPoint.y ) )
continue; continue;
// segment below ref point, or one of its ends has the same Y pos as the ref point: skip // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
// So we eliminate one end point of 2 consecutive segments. // So we eliminate one end point of 2 consecutive segments.
// Note: also we skip horizontal segments if ref point is on this horizontal line // Note: also we skip horizontal segments if ref point is on this horizontal line
// So reference points on horizontal segments outlines always are seen as outside the polygon // So reference points on horizontal segments outlines always are seen as outside the polygon
if( ( seg_startY <= refy ) && (seg_endY <= refy ) ) if( ( seg_startY <= aRefPoint.y ) && (seg_endY <= aRefPoint.y ) )
continue; continue;
/* refy is between seg_startY and seg_endY. /* refy is between seg_startY and seg_endY.
...@@ -76,8 +131,8 @@ bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList, ...@@ -76,8 +131,8 @@ bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
// this is more easier if we move the X,Y axis origin to the segment start point: // this is more easier if we move the X,Y axis origin to the segment start point:
seg_endX -= seg_startX; seg_endX -= seg_startX;
seg_endY -= seg_startY; seg_endY -= seg_startY;
double newrefx = (double) (refx - seg_startX); double newrefx = (double) (aRefPoint.x - seg_startX);
double newrefy = (double) (refy - seg_startY); double newrefy = (double) (aRefPoint.y - seg_startY);
// Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY) // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
// with the horizontal line at the new refy position // with the horizontal line at the new refy position
......
...@@ -2,18 +2,34 @@ ...@@ -2,18 +2,34 @@
// Name: polygon_test_point_inside.h // Name: polygon_test_point_inside.h
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
using namespace std; #ifndef __WXWINDOWS__
// define here wxPoint if we want to compile outside wxWidgets
class wxPoint
{
public:
int x, y;
};
#endif
/** Function TestPointInsidePolygon /** Function TestPointInsidePolygon
* test if a point is inside or outside a polygon. * test if a point is inside or outside a polygon.
* @param aPolysList: the list of polygons * @param aPolysList: the list of polygons
* @param istart: the starting point of a given polygon in m_FilledPolysList. * @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
* @param iend: the ending point of the polygon in m_FilledPolysList. * @param aIdxend: the ending point of the polygon in m_FilledPolysList.
* @param refx, refy: the point coordinate to test * @param aRefx, aRefy: the point coordinate to test
* @return true if the point is inside, false for outside * @return true if the point is inside, false for outside
*/ */
bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList, bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
int istart, int aIdxstart,
int iend, int aIdxend,
int refx, int aRefx,
int refy); int aRefy);
/** Function TestPointInsidePolygon (overlaid)
* same as previous, but mainly use wxPoint
* @param aPolysList: the list of polygons
* @param aCount: corners count in aPolysList.
* @param aRefPoint: the point coordinate to test
* @return true if the point is inside, false for outside
*/
bool TestPointInsidePolygon( wxPoint* aPolysList,
int aCount,
wxPoint aRefPoint );
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment