From 1f4ca54938f667645476c1ea2071ec2abb16c71b Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sat, 26 Jan 2013 18:49:48 +0100 Subject: [PATCH] Pcbnew: fix a very old bug in DRC, pad to pad drc tests: sometimes a test between a rectangular pad and an oval pad was skipped. Segment Hit Test: enhanced detection near segment ends (noticeable for instance when clicking on short tracks in Pcbnew. Remove duplicate code about segment hit test. remove unused file. --- common/trigo.cpp | 263 ++++++++---------- include/trigo.h | 28 +- pcbnew/drc_clearance_test_functions.cpp | 49 ++-- .../gen_holes_and_tools_lists_for_drill.cpp | 176 ------------ pcbnew/pcb_parser.cpp | 2 +- polygon/math_for_graphics.cpp | 61 ---- polygon/math_for_graphics.h | 10 - 7 files changed, 181 insertions(+), 408 deletions(-) delete mode 100644 pcbnew/gen_holes_and_tools_lists_for_drill.cpp diff --git a/common/trigo.cpp b/common/trigo.cpp index 8465feb699..f4453f916c 100644 --- a/common/trigo.cpp +++ b/common/trigo.cpp @@ -1,176 +1,159 @@ /** * @file trigo.cpp - * @brief Trigonometric functions. + * @brief Trigonometric and geometric basic functions. */ #include #include #include #include +#include -static bool DistanceTest( int seuil, int dx, int dy, int spot_cX, int spot_cY ); - +/* Function TestSegmentHit + * test for hit on line segment + * i.e. a reference point is within a given distance from segment + * aRefPoint = reference point to test + * aStart, aEnd are coordinates of end points segment + * aDist = maximum distance for hit + * Note: for calculation time reasons, the distance between the ref point + * and the segment is not always exactly calculated + * (we only know if the actual dist is < aDist, not exactly know this dist. + * Because many times we have horizontal or vertical segments, + * a special calcultaion is made for them + * Note: sometimes we need to calculate the distande between 2 points + * A square root should be calculated. + * However, because we just compare 2 distnaces, to avoid calculating square root, + * the square of distances are compared. +*/ +static inline double square( int x ) // helper function to calculate x*x +{ + return (double) x * x; +} bool TestSegmentHit( wxPoint aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist ) { - return DistanceTest( aDist, aEnd.x - aStart.x, aEnd.y - aStart.y, - aRefPoint.x - aStart.x, aRefPoint.y - aStart.y ); -} - - -bool DistanceTest( int seuil, int dx, int dy, int spot_cX, int spot_cY ) -{ - /* We can have 4 cases:: - * horizontal segment - * vertical segment - * 45 degrees segment - * other slopes - */ - int cXrot, cYrot, segX, segY; - int pointX, pointY; - - segX = dx; - segY = dy; - pointX = spot_cX; - pointY = spot_cY; - - /* Recalculating coord for the segment is in 1st quadrant (coord >= 0) */ - if( segX < 0 ) /* set > 0 by symmetry about the Y axis */ + // test for vertical or horizontal segment + if( aEnd.x == aStart.x ) { - segX = -segX; - pointX = -pointX; - } + // vertical segment + int ll = abs( aRefPoint.x - aStart.x ); - if( segY < 0 ) /* set > 0 by symmetry about the X axis */ - { - segY = -segY; - pointY = -pointY; - } + if( ll >= aDist ) + return false; + // To have only one case to examine, ensure aEnd.y > aStart.y + if( aEnd.y < aStart.y ) + EXCHG( aStart.y, aEnd.y ); - if( segY == 0 ) /* horizontal */ - { - if( abs( pointY ) <= seuil ) + if( aRefPoint.y < aEnd.y && aRefPoint.y > aStart.y ) + return true; + + // there is a special case: x,y near an end point (distance < dist ) + // the distance should be carefully calculated + if( (aStart.y - aRefPoint.y) < aDist ) { - if( ( pointX >= 0 ) && ( pointX <= segX ) ) - return 1; - - if( ( pointX < 0 ) && ( pointX >= -seuil ) ) - { - if( ( ( pointX * pointX ) + ( pointY * pointY ) ) <= ( seuil * seuil ) ) - return true; - } - if( ( pointX > segX ) && ( pointX <= ( segX + seuil ) ) ) - { - if( ( ( ( pointX - segX ) * ( pointX - segX ) ) - + ( pointY * pointY ) ) <= ( seuil * seuil ) ) - return true; - } - } - } - else if( segX == 0 ) /* vertical */ - { - if( abs( pointX ) <= seuil ) - { - if( ( pointY >= 0 ) && ( pointY <= segY ) ) + double dd = square( aRefPoint.x - aStart.x) + + square( aRefPoint.y - aStart.y ); + if( dd < square( aDist ) ) return true; - - if( ( pointY < 0 ) && ( pointY >= -seuil ) ) - { - if( ( ( pointY * pointY ) + ( pointX * pointX ) ) <= ( seuil * seuil ) ) - return true; - } - - if( ( pointY > segY ) && ( pointY <= ( segY + seuil ) ) ) - { - if( ( ( ( pointY - segY ) * ( pointY - segY ) ) - + ( pointX * pointX ) ) <= ( seuil * seuil ) ) - return true; - } } - } - else if( segX == segY ) /* 45 degrees */ - { - /* Rotate axes of 45 degrees. mouse was then - * Coord: x1 = x * y * cos45 + sin45 - * y1 = y * cos45 - sin45 x * - * And the segment of track is horizontal. - * Coord recalculation of the mouse (sin45 = cos45 = .707 = 7 / 10 - * Note: sin or cos45 = .707, and when recalculating coord - * dx45 and dy45, lect coeff .707 is neglected, dx and dy are - * actually 0707 times - * Too big. (security hole too small) - * Spot_cX, Y * must be by .707 * .707 = 0.5 - */ - cXrot = (pointX + pointY) >> 1; - cYrot = (pointY - pointX) >> 1; - - /* Recalculating coord of segment extremity, which will be vertical - * following the orientation of axes on the screen: dx45 = pointx - * (or pointy) and 1.414 is actually greater, and dy45 = 0 - */ - - // * Threshold should be .707 to reflect the change in coeff dx, dy - seuil *= 7; - seuil /= 10; - - if( abs( cYrot ) <= seuil ) /* ok on vertical axis */ + if( (aRefPoint.y - aEnd.y) < aDist ) { - if( ( cXrot >= 0 ) && ( cXrot <= segX ) ) + double dd = square( aRefPoint.x - aEnd.x ) + + square( aRefPoint.y - aEnd.y ); + if( dd < square( aDist ) ) return true; - - /* Check extremes using the radius of a circle. */ - if( ( cXrot < 0 ) && ( cXrot >= -seuil ) ) - { - if( ( ( cXrot * cXrot ) + ( cYrot * cYrot ) ) <= ( seuil * seuil ) ) - return true; - } - if( ( cXrot > segX ) && ( cXrot <= ( segX + seuil ) ) ) - { - if( ( ( ( cXrot - segX ) * ( cXrot - segX ) ) - + ( cYrot * cYrot ) ) <= ( seuil * seuil ) ) - return true; - } } } - else /* any orientation */ + else if( aEnd.y == aStart.y ) { - /* There is a change of axis (rotation), so that the segment - * track is horizontal in the new reference - */ - int angle; + // horizontal segment + int ll = abs( aRefPoint.y - aStart.y ); - angle = KiROUND( ( atan2( (double) segY, (double) segX ) * 1800.0 / M_PI ) ); - cXrot = pointX; - cYrot = pointY; + if( ll >= aDist ) + return false; - RotatePoint( &cXrot, &cYrot, angle ); /* Rotate the point to be tested */ - RotatePoint( &segX, &segY, angle ); /* Rotate the segment */ + // To have only one case to examine, ensure xf > xi + if( aEnd.x < aStart.x ) + EXCHG( aStart.x, aEnd.x ); - /* The track is horizontal, following the amendments to coordinate - * axis and, therefore segX = length of segment - */ - if( abs( cYrot ) <= seuil ) /* vertical axis */ + if( aRefPoint.x < aEnd.x && aRefPoint.x > aStart.x ) + return true; + + // there is a special case: x,y near an end point (distance < dist ) + // the distance should be carefully calculated + if( (aStart.x - aRefPoint.x) < aDist ) { - if( ( cXrot >= 0 ) && ( cXrot <= segX ) ) + double dd = square( aRefPoint.x - aStart.x) + + square( aRefPoint.y - aStart.y ); + if( dd < square( aDist ) ) return true; + } - if( ( cXrot < 0 ) && ( cXrot >= -seuil ) ) - { - if( ( ( cXrot * cXrot ) + ( cYrot * cYrot ) ) <= ( seuil * seuil ) ) - return true; - } - - if( ( cXrot > segX ) && ( cXrot <= ( segX + seuil ) ) ) - { - if( ( ( ( cXrot - segX ) * ( cXrot - segX ) ) - + ( cYrot * cYrot ) ) <= ( seuil * seuil ) ) - return true; - } + if( (aRefPoint.x - aEnd.x) < aDist ) + { + double dd = square(aRefPoint.x - aEnd.x) + + square( aRefPoint.y - aEnd.y); + if( dd < square( aDist ) ) + return true; } } + else + { + // oblique segment: + // First, we need to calculate the distance between the point + // and the line defined by aStart and aEnd + // this dist should be < dist + // + // find a,slope such that aStart and aEnd lie on y = a + slope*x + double slope = (double) (aEnd.y - aStart.y) / (aEnd.x - aStart.x); + double a = (double) aStart.y - slope * aStart.x; + // find c,orthoslope such that (x,y) lies on y = c + orthoslope*x, + // where orthoslope=(-1/slope) + // to calculate xp, yp = near point from aRefPoint + // which is on the line defined by aStart, aEnd + double orthoslope = -1.0 / slope; + double c = (double) aRefPoint.y - orthoslope * aRefPoint.x; + // find nearest point to (x,y) on line defined by aStart, aEnd + double xp = (a - c) / (orthoslope - slope); + double yp = a + slope * xp; + // find distance to line, in fact the square of dist, + // because we just know if it is > or < aDist + double dd = square( aRefPoint.x - xp ) + square( aRefPoint.y - yp ); + double dist = square( aDist ); - return false; + if( dd >= dist ) // this reference point is not a good candiadte. + return false; + + // dd is < dist, therefore we should make a fine test + if( fabs( slope ) > 0.7 ) + { + // line segment more vertical than horizontal + if( (aEnd.y > aStart.y && yp < aEnd.y && yp > aStart.y) || + (aEnd.y < aStart.y && yp > aEnd.y && yp < aStart.y) ) + return true; + } + else + { + // line segment more horizontal than vertical + if( (aEnd.x > aStart.x && xp < aEnd.x && xp > aStart.x) || + (aEnd.x < aStart.x && xp > aEnd.x && xp < aStart.x) ) + return true; + } + + // Here, the test point is still a good candidate, + // however it is not "between" the end points of the segment. + // It is "outside" the segment, but it could be near a segment end point + // Therefore, we test the dist from the test point to each segment end point + dd = square( aRefPoint.x - aEnd.x ) + square( aRefPoint.y - aEnd.y ); + if( dd < dist ) + return true; + dd = square( aRefPoint.x - aStart.x ) + square( aRefPoint.y - aStart.y ); + if( dd < dist ) + return true; + } + + return false; // no hit } diff --git a/include/trigo.h b/include/trigo.h index 886f88eeb8..fd868703b7 100644 --- a/include/trigo.h +++ b/include/trigo.h @@ -2,6 +2,30 @@ * @file trigo.h */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + + #ifndef TRIGO_H #define TRIGO_H @@ -72,8 +96,8 @@ double CrossProduct( wxPoint vectorA, wxPoint vectorB ); /** * Function TestSegmentHit * test for hit on line segment - * i.e. cursor within a given distance from segment - * @param aRefPoint = cursor (point to test) coords + * i.e. a reference point is within a given distance from segment + * @param aRefPoint = reference point to test * @param aStart is the first end-point of the line segment * @param aEnd is the second end-point of the line segment * @param aDist = maximum distance for hit diff --git a/pcbnew/drc_clearance_test_functions.cpp b/pcbnew/drc_clearance_test_functions.cpp index fad1445297..804e33dca5 100644 --- a/pcbnew/drc_clearance_test_functions.cpp +++ b/pcbnew/drc_clearance_test_functions.cpp @@ -136,16 +136,10 @@ bool trapezoid2pointDRC( wxPoint aTref[4], wxPoint aPcompare, int aDist ) 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 + // Test distance between aPcompare and each segment of the polygon: + for( int ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edge in polygon { - if( TestLineHit( aTref[ii].x, aTref[ii].y, - aTref[jj].x, aTref[jj].y, - aPcompare.x, aPcompare.y, - dist ) ) + if( TestSegmentHit( aTref[ii], aTref[jj], aPcompare, aDist ) ) return false; } @@ -602,12 +596,30 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) bool swap_pads; swap_pads = false; - if( aRefPad->GetShape() != PAD_CIRCLE && aPad->GetShape() == PAD_CIRCLE ) - swap_pads = true; - else if( aRefPad->GetShape() != PAD_OVAL && aPad->GetShape() == PAD_OVAL ) - swap_pads = true; - else if( aRefPad->GetShape() != PAD_RECT && aPad->GetShape() == PAD_RECT ) - swap_pads = true; + // swap pads to make comparisons easier + // priority is aRefPad = ROUND then OVAL then RECT then other + if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_CIRCLE ) + { + // pad ref shape is here oval, rect or trapezoid + switch( aPad->GetShape() ) + { + case PAD_CIRCLE: + swap_pads = true; + break; + + case PAD_OVAL: + swap_pads = true; + break; + + case PAD_RECT: + if( aRefPad->GetShape() != PAD_OVAL ) + swap_pads = true; + break; + + default: + break; + } + } if( swap_pads ) { @@ -639,7 +651,6 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) break; case PAD_RECT: - // pad_angle = pad orient relative to the aRefPad orient pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation(); NORMALIZE_ANGLE_POS( pad_angle ); @@ -707,7 +718,9 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) { // Should not occur, 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 %d") , aPad->GetShape() ); + wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad ref RECT @ %d, %d to pad shape %d @ %d, %d"), + aRefPad->GetPosition().x, aRefPad->GetPosition().y, + aPad->GetShape(), aPad->GetPosition().x, aPad->GetPosition().y ); } break; @@ -775,7 +788,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) break; default: - wxLogDebug( wxT( "unexpected pad shape" ) ); + wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape" ) ); break; } diff --git a/pcbnew/gen_holes_and_tools_lists_for_drill.cpp b/pcbnew/gen_holes_and_tools_lists_for_drill.cpp deleted file mode 100644 index 187982a127..0000000000 --- a/pcbnew/gen_holes_and_tools_lists_for_drill.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/*************************************************************************/ -/* Functions to create drill data used to create files and report files */ -/*************************************************************************/ - -#include // sort - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - - -// Local Functions - -/* Compare function used for sorting holes by increasing diameter value - * and X value - */ -static bool CmpHoleDiameterValue( const HOLE_INFO& a, const HOLE_INFO& b ) -{ - if( a.m_Hole_Diameter != b.m_Hole_Diameter ) - return a.m_Hole_Diameter < b.m_Hole_Diameter; - - if( a.m_Hole_Pos.x != b.m_Hole_Pos.x ) - return a.m_Hole_Pos.x < b.m_Hole_Pos.x; - - return a.m_Hole_Pos.y < b.m_Hole_Pos.y; -} - - -/* - * Function BuildHolesList - * Create the list of holes and tools for a given board - * The list is sorted by increasing drill values - * Only holes from aFirstLayer to aLastLayer copper layers are listed (for vias, because pad holes are always through holes) - * param aPcb : the given board - * param aHoleListBuffer : the std::vector to fill with pcb holes info - * param aToolListBuffer : the std::vector to fill with tools to use - * param aFirstLayer = first layer to consider. if < 0 aFirstLayer is ignored (used to creates report file) - * param aLastLayer = last layer to consider. if < 0 aLastLayer is ignored - * param aExcludeThroughHoles : if true, exclude through holes ( pads and vias through ) - * param aGenerateNPTH_list : - * true to create NPTH only list (with no plated holes) - * false to created plated holes list (with no NPTH ) - */ -void Build_Holes_List( BOARD* aPcb, - std::vector& aHoleListBuffer, - std::vector& aToolListBuffer, - int aFirstLayer, int aLastLayer, bool aExcludeThroughHoles, - bool aGenerateNPTH_list ) -{ - HOLE_INFO new_hole; - int hole_value; - - aHoleListBuffer.clear(); - aToolListBuffer.clear(); - - if( (aFirstLayer >= 0) && (aLastLayer >= 0) ) - { - if( aFirstLayer > aLastLayer ) - EXCHG( aFirstLayer, aLastLayer ); - } - - /* build hole list for vias - */ - if( ! aGenerateNPTH_list ) // vias are always plated ! - { - for( TRACK* track = aPcb->m_Track; track; track = track->Next() ) - { - if( track->Type() != PCB_VIA_T ) - continue; - - SEGVIA* via = (SEGVIA*) track; - hole_value = via->GetDrillValue(); - - if( hole_value == 0 ) - continue; - - new_hole.m_Tool_Reference = -1; // Flag value for Not initialized - new_hole.m_Hole_Orient = 0; - new_hole.m_Hole_Diameter = hole_value; - new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; - - new_hole.m_Hole_Shape = 0; // hole shape: round - new_hole.m_Hole_Pos = via->m_Start; - via->ReturnLayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer ); - - // ReturnLayerPair return params with m_Hole_Bottom_Layer < m_Hole_Top_Layer - if( (new_hole.m_Hole_Bottom_Layer > aFirstLayer) && (aFirstLayer >= 0) ) - continue; - - if( (new_hole.m_Hole_Top_Layer < aLastLayer) && (aLastLayer >= 0) ) - continue; - - if( aExcludeThroughHoles && (new_hole.m_Hole_Bottom_Layer == LAYER_N_BACK) - && (new_hole.m_Hole_Top_Layer == LAYER_N_FRONT) ) - continue; - - aHoleListBuffer.push_back( new_hole ); - } - } - - // build hole list for pads (assumed always through holes) - if( !aExcludeThroughHoles || aGenerateNPTH_list ) - { - for( MODULE* module = aPcb->m_Modules; module; module = module->Next() ) - { - // Read and analyse pads - for( D_PAD* pad = module->m_Pads; pad; pad = pad->Next() ) - { - if( ! aGenerateNPTH_list && pad->GetAttribute() == PAD_HOLE_NOT_PLATED ) - continue; - - if( aGenerateNPTH_list && pad->GetAttribute() != PAD_HOLE_NOT_PLATED ) - continue; - - if( pad->GetDrillSize().x == 0 ) - continue; - - new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_HOLE_NOT_PLATED); - new_hole.m_Tool_Reference = -1; // Flag is: Not initialized - new_hole.m_Hole_Orient = pad->GetOrientation(); - new_hole.m_Hole_Shape = 0; // hole shape: round - new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y ); - new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter; - - if( pad->GetDrillShape() != PAD_CIRCLE ) - new_hole.m_Hole_Shape = 1; // oval flag set - - new_hole.m_Hole_Size = pad->GetDrillSize(); - new_hole.m_Hole_Pos = pad->GetPosition(); // hole position - new_hole.m_Hole_Bottom_Layer = LAYER_N_BACK; - new_hole.m_Hole_Top_Layer = LAYER_N_FRONT;// pad holes are through holes - aHoleListBuffer.push_back( new_hole ); - } - } - } - - // Sort holes per increasing diameter value - sort( aHoleListBuffer.begin(), aHoleListBuffer.end(), CmpHoleDiameterValue ); - - // build the tool list - int LastHole = -1; /* Set to not initialised (this is a value not used - * for aHoleListBuffer[ii].m_Hole_Diameter) */ - DRILL_TOOL new_tool( 0 ); - unsigned jj; - - for( unsigned ii = 0; ii < aHoleListBuffer.size(); ii++ ) - { - if( aHoleListBuffer[ii].m_Hole_Diameter != LastHole ) - { - new_tool.m_Diameter = ( aHoleListBuffer[ii].m_Hole_Diameter ); - aToolListBuffer.push_back( new_tool ); - LastHole = new_tool.m_Diameter; - } - - jj = aToolListBuffer.size(); - - if( jj == 0 ) - continue; // Should not occurs - - aHoleListBuffer[ii].m_Tool_Reference = jj; // Tool value Initialized (value >= 1) - - aToolListBuffer.back().m_TotalCount++; - - if( aHoleListBuffer[ii].m_Hole_Shape ) - aToolListBuffer.back().m_OvalCount++; - } -} diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 74c1f1eef7..f081aae45e 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -715,7 +715,7 @@ void PCB_PARSER::parseLayers() throw( IO_ERROR, PARSE_ERROR ) layer.SetFixedListIndex( layerIndex ); m_board->SetLayer( layerIndex, layer ); - wxLogDebug( wxT( "Mapping layer %s to index %d" ), GetChars( wname ), layerIndex ); +// wxLogDebug( wxT( "Mapping layer %s to index %d" ), GetChars( wname ), layerIndex ); if( layerType != LT_UNDEFINED ) copperLayerCount++; diff --git a/polygon/math_for_graphics.cpp b/polygon/math_for_graphics.cpp index dc2d67a974..2510177abe 100644 --- a/polygon/math_for_graphics.cpp +++ b/polygon/math_for_graphics.cpp @@ -18,67 +18,6 @@ double Distance( double x1, double y1, double x2, double y2 ) return hypot( x1 - x2, y1 - y2 ); } -/** - * 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,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; - - // test for vertical or horizontal segment - if( xf==xi ) - { - // vertical segment - dd = fabs( (double) (x - xi) ); - - if( ddyi && yyi) || (yfyf && yxi && xxi) || (xfxf && x0.7 ) - { - // line segment more vertical than horizontal - if( ddyi && ypyi) || (yfyf && ypxi && xpxi) || (xfxf && xp DBL_MAX/10, assume vertical line at x = a