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
dist.cpp
dragsegm.cpp
drc.cpp
drc_clearance_test_functions.cpp
drc_marker_functions.cpp
edgemod.cpp
edit.cpp
......
......@@ -764,17 +764,15 @@ bool D_PAD::IsOnLayer( int aLayer ) const
*/
bool D_PAD::HitTest( const wxPoint& ref_pos )
{
int deltaX, deltaY;
int dx, dy;
double dist;
wxPoint shape_pos = ReturnShapePos();
deltaX = ref_pos.x - shape_pos.x;
deltaY = ref_pos.y - shape_pos.y;
wxPoint delta = ref_pos - shape_pos;
/* 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;
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 )
switch( m_PadShape & 0x7F )
{
case PAD_CIRCLE:
dist = hypot( deltaX, deltaY );
dist = hypot( delta.x, delta.y );
if( wxRound( dist ) <= dx )
return true;
break;
......@@ -792,22 +790,13 @@ bool D_PAD::HitTest( const wxPoint& ref_pos )
{
wxPoint poly[4];
BuildPadPolygon( poly, wxSize(0,0), 0 );
// Build the same polygon with CPolyPt corners,
// to use TestPointInsidePolygon
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 );
RotatePoint( &delta, -m_Orient );
return TestPointInsidePolygon( poly, 4, delta );
}
default:
RotatePoint( &deltaX, &deltaY, -m_Orient );
if( (abs( deltaX ) <= dx ) && (abs( deltaY ) <= dy) )
RotatePoint( &delta, -m_Orient );
if( (abs( delta.x ) <= dx ) && (abs( delta.y ) <= dy) )
return true;
break;
}
......
......@@ -148,7 +148,7 @@ public:
* Function GetShape
* @return the shape of this pad.
*/
int GetShape() { return m_PadShape & 0xFF; }
int GetShape() const { return m_PadShape & 0xFF; }
/**
* Function GetPosition
......@@ -239,14 +239,26 @@ public:
void DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo );
/** 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,
* depending on shape, extra size (clearance ...) and orientation
* @param aCoord[4] = a buffer to fill.
* @param aInflateValue = wxSize: the clearance or margin value. value > 0: inflate, < 0 deflate
* @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
void SetPadName( const wxString& name ); // Change pad name
......
......@@ -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 )
{
wxPoint coord[4];
int rotdx,
delta_cx, delta_cy;
int delta_cx, delta_cy;
int angle = m_Orient;
int seg_width;
GRSetDrawMode( aDC, aDrawInfo.m_DrawMode );
......@@ -392,44 +392,30 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
break;
case PAD_OVAL:
if( halfsize.x > halfsize.y ) /* horizontal */
{
delta_cx = halfsize.x - halfsize.y;
delta_cy = 0;
rotdx = m_Size.y + ( aDrawInfo.m_Mask_margin.y * 2 );
}
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 );
{
wxPoint segStart, segEnd;
seg_width = BuildSegmentFromOvalShape(segStart, segEnd, angle);
segStart += shape_pos;
segEnd += shape_pos;
if( aDrawInfo.m_ShowPadFilled )
{
GRFillCSegm( aClipBox, aDC,
shape_pos.x + delta_cx, shape_pos.y + delta_cy,
shape_pos.x - delta_cx, shape_pos.y - delta_cy,
rotdx, aDrawInfo.m_Color );
GRFillCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
seg_width, aDrawInfo.m_Color );
}
else
{
GRCSegm( aClipBox, aDC,
shape_pos.x + delta_cx, shape_pos.y + delta_cy,
shape_pos.x - delta_cx, shape_pos.y - delta_cy,
rotdx, m_PadSketchModePenSize, aDrawInfo.m_Color );
GRCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
seg_width, m_PadSketchModePenSize, aDrawInfo.m_Color );
}
/* Draw the isolation line. */
if( aDrawInfo.m_PadClearance )
{
rotdx = rotdx + 2 * aDrawInfo.m_PadClearance;
GRCSegm( aClipBox, aDC, shape_pos.x + delta_cx, shape_pos.y + delta_cy,
shape_pos.x - delta_cx, shape_pos.y - delta_cy,
rotdx, aDrawInfo.m_Color );
seg_width += 2 * aDrawInfo.m_PadClearance;
GRCSegm( aClipBox, aDC, segStart.x, segStart.y, segEnd.x, segEnd.y,
seg_width, aDrawInfo.m_Color );
}
}
break;
case PAD_RECT:
......@@ -486,9 +472,6 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
#else
if( aDrawInfo.m_Scale * hole > 1 ) /* draw hole if its size is enough */
#endif
GRFilledCircle( aClipBox, aDC, holepos.x, holepos.y, hole, 0,
aDrawInfo.m_Color, aDrawInfo.m_HoleColor );
break;
......@@ -501,18 +484,18 @@ void D_PAD::DrawShape( EDA_Rect* aClipBox, wxDC* aDC, PAD_DRAWINFO& aDrawInfo )
{
delta_cx = halfsize.x - halfsize.y;
delta_cy = 0;
rotdx = m_Drill.y;
seg_width = m_Drill.y;
}
else /* vertical */
{
delta_cx = 0;
delta_cy = halfsize.y - halfsize.x;
rotdx = m_Drill.x;
seg_width = m_Drill.x;
}
RotatePoint( &delta_cx, &delta_cy, angle );
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 );
break;
......@@ -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
* 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 )
* @param aInflateValue = wxSize: the clearance or margin value. value > 0: inflate, < 0 deflate
* @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) )
return;
......
......@@ -44,21 +44,17 @@ bool DIALOG_DRC_CONTROL::Show( bool show )
{
bool ret;
D(printf("%s %d\n", __func__, show );)
if( show )
{
ret = DIALOG_DRC_CONTROL_BASE::Show( show );
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 );
}
else
{
D(printf("not setting window pos (%d,%d)\n", s_LastPos.x, s_LastPos.y );)
// Do nothing: last position not yet saved.
}
}
else
......@@ -66,9 +62,6 @@ bool DIALOG_DRC_CONTROL::Show( bool show )
// Save the dialog's position before hiding
s_LastPos = GetPosition();
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 );
}
......
......@@ -195,8 +195,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
{
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;
m_isFlipped = false;
if( m_CurrentPad )
......@@ -298,7 +297,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
SetPadLayersList( m_dummyPad->m_Masque_Layer );
msg.Clear();
msg << tmp;
msg << m_dummyPad->m_Orient;
m_PadOrientCtrl->SetValue( msg );
// Pad Orient
......@@ -350,11 +349,10 @@ void DIALOG_PAD_PROPERTIES::initValues()
m_PadOrientCtrl->SetValue( msg );
// Selection du type
tmp = m_dummyPad->m_Attribut;
m_PadType->SetSelection( 0 );
for( int ii = 0; ii < NBTYPES; ii++ )
{
if( CodeType[ii] == tmp )
if( CodeType[ii] == m_dummyPad->m_Attribut )
{
m_PadType->SetSelection( ii );
break;
......
......@@ -30,12 +30,9 @@
#include "fctsys.h"
#include "common.h"
#include "class_drawpanel.h"
#include "pcbnew.h"
#include "wxPcbStruct.h"
#include "autorout.h"
#include "trigo.h"
#include "gestfich.h"
#include "class_board_design_settings.h"
#include "protos.h"
......@@ -55,13 +52,13 @@ void DRC::ShowDialog()
PutValueInLocalUnits( *m_ui->m_SetTrackMinWidthCtrl,
m_pcb->GetBoardDesignSettings()->m_TrackMinWidth,
m_mainWindow->m_InternalUnits );;
m_mainWindow->m_InternalUnits );
PutValueInLocalUnits( *m_ui->m_SetViaMinSizeCtrl,
m_pcb->GetBoardDesignSettings()->m_ViasMinSize,
m_mainWindow->m_InternalUnits );;
m_mainWindow->m_InternalUnits );
PutValueInLocalUnits( *m_ui->m_SetMicroViakMinSizeCtrl,
m_pcb->GetBoardDesignSettings()->m_MicroViasMinSize,
m_mainWindow->m_InternalUnits );;
m_mainWindow->m_InternalUnits );
m_ui->m_CreateRptCtrl->SetValue( m_doCreateRptFile );
m_ui->m_RptFilenameCtrl->SetValue( m_rptFilename );
......@@ -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()
/***********************/
......@@ -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()
{
if( (m_pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
......@@ -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,
int x_limit )
......@@ -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.
// this dummy pad is a circle or an oval.
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
* the clearance of the active pad will be used
* as minimum distance to a hole
......@@ -1088,562 +661,3 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, LISTE_PAD* aStart, LISTE_PAD* aEnd,
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:
/* 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
*/
int m_xcliplo;
int m_ycliplo;
int m_xcliphi;
int m_ycliphi;
int m_xcliplo;
int m_ycliplo;
int m_xcliphi;
int m_ycliphi;
WinEDA_PcbFrame* m_mainWindow;
BOARD* m_pcb;
......@@ -329,30 +329,27 @@ private:
/**
* Function checkMarginToCircle
* @todo this translation is no good, fix this:
* calculates the distance from a circle (via or round end of track) to the
* segment of reference on the right hand side.
*
* @param cx The x coordinate of the circle's center
* @param cy The y coordinate of the circle's center
* @param radius A "keep out" radius centered over the circle
* @param length The length of the segment (i.e. coordinate of end)
* Helper function checkMarginToCircle
* Check the distance from a point to
* a segment. the segment is expected starting at 0,0, and on the X axis
* (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 aRadius A "keep out" radius centered over the circle
* @param aLength The length of the segment (i.e. coordinate of end, becuase it is on the X axis)
* @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
* tests to see if one track is in contact with another track.
*
* Cette routine controle si la ligne (x1,y1 x2,y2) a une partie s'inscrivant
* dans le cadre (xcliplo,ycliplo xcliphi,ycliphi) (variables globales,
* locales a ce fichier)
* (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)
* 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 checkLine( int x1, int y1, int x2, int y2 );
bool checkLine( wxPoint aSegStart, wxPoint aSegEnd );
//-----</single tests>---------------------------------------------
......
// math for graphics utility routines, from FreePCB
// math for graphics utility routines and RC, from FreePCB
#include <vector>
......@@ -13,13 +13,14 @@
using namespace std;
// test for hit on line segment
// i.e. cursor within a given distance from segment
// enter with: x,y = cursor coords
// (xi,yi) and (xf,yf) are the end-points of the line segment
// dist = maximum distance for hit
//
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 x, y = cursor coords
* @param xi,yi and xf,yf = the end-points of the line segment
* @param dist = maximum distance for hit
* return true if dist < distance between the point and the segment
*/
bool TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist )
{
double dd;
......@@ -29,14 +30,14 @@ int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist )
// vertical segment
dd = fabs( (double)(x-xi) );
if( dd<dist && ( (yf>yi && y<yf && y>yi) || (yf<yi && y>yf && y<yi) ) )
return 1;
return true;
}
else if( yf==yi )
{
// horizontal segment
dd = fabs( (double)(y-yi) );
if( dd<dist && ( (xf>xi && x<xf && x>xi) || (xf<xi && x>xf && x<xi) ) )
return 1;
return true;
}
else
{
......@@ -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
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
return 1;
}
// Test for intersection of line segments
// If lines are parallel, returns false
// If true, returns 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)
//
/** 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)
*/
bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
int x2i, int y2i, int x2f, int y2f,
int * x, int * y, double * d )
......
......@@ -13,8 +13,6 @@ typedef struct PointTag
typedef struct EllipseTag
{
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 theta1, theta2; // start and end angle for arc
} EllipseKH;
......@@ -22,7 +20,16 @@ typedef struct EllipseTag
// math stuff for graphics
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,
double * x1, double * y1, double * x2, double * y2, double * dist=NULL );
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 );
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 );
/** 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,
int x2i, int y2i, int x2f, int y2f,
int * x=NULL, int * y=NULL, double * dist=NULL );
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 max_cl, int * x, int * y );
......
/////////////////////////////////////////////////////////////////////////////
// Name: polygon_test_point_inside.cpp
/////////////////////////////////////////////////////////////////////////////
/**
* @file polygon_test_point_inside.cpp
*/
#include <math.h>
#include <vector>
#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:
* It run a semi-infinite line horizontally (increasing x, fixed y)
* out from the test point, and count how many edges it crosses.
* At each crossing, the ray switches between inside and outside.
* 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.
* 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:
* 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.
......@@ -30,16 +27,19 @@ using namespace std;
#define INSIDE true
bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
int istart, int iend, int refx, int refy )
int aIdxstart,
int aIdxend,
int aRefx,
int aRefy)
/** Function TestPointInsidePolygon
* test if a point is inside or outside a polygon.
* the polygon must have only lines (not arcs) for outlines.
* Use TestPointInside or TestPointInsideContour for more complex polygons
* @param aPolysList: the list of polygons
* @param istart: the starting point of a given polygon in m_FilledPolysList.
* @param iend: the ending point of the polygon in m_FilledPolysList.
* @param refx,refy: the point coordinate to test
* @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
* @param aIdxend: the ending point of the polygon in m_FilledPolysList.
* @param aRefx, aRefy: the point coordinate to test
* @return true if the point is inside, false for outside
*/
{
......@@ -48,7 +48,62 @@ bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
int count = 0;
// 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_startY = aPolysList[ics].y;
......@@ -56,14 +111,14 @@ bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
int seg_endY = aPolysList[ice].y;
/* 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;
// 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 <= refy ) && (seg_endY <= refy ) )
if( ( seg_startY <= aRefPoint.y ) && (seg_endY <= aRefPoint.y ) )
continue;
/* refy is between seg_startY and seg_endY.
......@@ -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:
seg_endX -= seg_startX;
seg_endY -= seg_startY;
double newrefx = (double) (refx - seg_startX);
double newrefy = (double) (refy - seg_startY);
double newrefx = (double) (aRefPoint.x - seg_startX);
double newrefy = (double) (aRefPoint.y - 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
......
......@@ -2,18 +2,34 @@
// 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
* test if a point is inside or outside a polygon.
* @param aPolysList: the list of polygons
* @param istart: the starting point of a given polygon in m_FilledPolysList.
* @param iend: the ending point of the polygon in m_FilledPolysList.
* @param refx, refy: the point coordinate to test
* @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
* @param aIdxend: the ending point of the polygon in m_FilledPolysList.
* @param aRefx, aRefy: the point coordinate to test
* @return true if the point is inside, false for outside
*/
bool TestPointInsidePolygon( std::vector <CPolyPt> aPolysList,
int istart,
int iend,
int refx,
int refy);
int aIdxstart,
int aIdxend,
int aRefx,
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