diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 9a2b8038b1..a8b2fad1f3 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -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 diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 0c0261094b..6db2778247 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.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 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; } diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index 79803aea19..ceb15010ee 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -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 diff --git a/pcbnew/class_pad_draw_functions.cpp b/pcbnew/class_pad_draw_functions.cpp index 2986bc8d56..c28f762dae 100644 --- a/pcbnew/class_pad_draw_functions.cpp +++ b/pcbnew/class_pad_draw_functions.cpp @@ -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; diff --git a/pcbnew/dialog_drc.cpp b/pcbnew/dialog_drc.cpp index 32819f24f6..1d40ae0ff7 100644 --- a/pcbnew/dialog_drc.cpp +++ b/pcbnew/dialog_drc.cpp @@ -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 ); } diff --git a/pcbnew/dialog_pad_properties.cpp b/pcbnew/dialog_pad_properties.cpp index 2c8ba6a07a..73b6576b5d 100644 --- a/pcbnew/dialog_pad_properties.cpp +++ b/pcbnew/dialog_pad_properties.cpp @@ -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; diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index 8ea9b90709..1c32d7a345 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -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; iiGetPadsCount(); ++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; -} diff --git a/pcbnew/drc_clearance_test_functions.cpp b/pcbnew/drc_clearance_test_functions.cpp new file mode 100644 index 0000000000..cfe7e97092 --- /dev/null +++ b/pcbnew/drc_clearance_test_functions.cpp @@ -0,0 +1,1112 @@ +/** + *@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; iiGetPadsCount(); ++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; +} diff --git a/pcbnew/drc_stuff.h b/pcbnew/drc_stuff.h index 53b282bacc..362302dc5e 100644 --- a/pcbnew/drc_stuff.h +++ b/pcbnew/drc_stuff.h @@ -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 ); //-------------------------------------------------- diff --git a/polygon/math_for_graphics.cpp b/polygon/math_for_graphics.cpp index 25398948d2..7d514906fb 100644 --- a/polygon/math_for_graphics.cpp +++ b/polygon/math_for_graphics.cpp @@ -1,4 +1,4 @@ -// math for graphics utility routines, from FreePCB +// math for graphics utility routines and RC, from FreePCB #include @@ -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( ddyi && yyi) || (yfyf && yxi && xxi) || (xfxf && xxi && xpxi) || (xfxf && xp #include #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 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,7 @@ bool TestPointInsidePolygon( std::vector 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; @@ -56,14 +56,14 @@ bool TestPointInsidePolygon( std::vector 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 > 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 <= refy ) && (seg_endY <= refy ) ) + if( ( seg_startY <= aRefy ) && (seg_endY <= aRefy ) ) continue; /* refy is between seg_startY and seg_endY. @@ -76,8 +76,63 @@ bool TestPointInsidePolygon( std::vector 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) (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; + 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 > 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 <= aRefPoint.y ) && (seg_endY <= aRefPoint.y ) ) + 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) (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 diff --git a/polygon/polygon_test_point_inside.h b/polygon/polygon_test_point_inside.h index 924423951a..92e36fff15 100644 --- a/polygon/polygon_test_point_inside.h +++ b/polygon/polygon_test_point_inside.h @@ -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 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 );