Commit 78a8be0e authored by jean-pierre charras's avatar jean-pierre charras

Pcbnew: fix a very old bug in DRC, pad to pad drc tests: sometimes a test...

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.
parent 5d5a50dd
/** /**
* @file trigo.cpp * @file trigo.cpp
* @brief Trigonometric functions. * @brief Trigonometric and geometric basic functions.
*/ */
#include <fctsys.h> #include <fctsys.h>
#include <macros.h> #include <macros.h>
#include <trigo.h> #include <trigo.h>
#include <common.h> #include <common.h>
#include <math_for_graphics.h>
static bool DistanceTest( int seuil, int dx, int dy, int spot_cX, int spot_cY );
/* Function TestSegmentHit
bool TestSegmentHit( wxPoint aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist ) * 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 DistanceTest( aDist, aEnd.x - aStart.x, aEnd.y - aStart.y, return (double) x * x;
aRefPoint.x - aStart.x, aRefPoint.y - aStart.y );
} }
bool TestSegmentHit( wxPoint aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist )
bool DistanceTest( int seuil, int dx, int dy, int spot_cX, int spot_cY )
{ {
/* We can have 4 cases:: // test for vertical or horizontal segment
* horizontal segment if( aEnd.x == aStart.x )
* 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 */
{
segX = -segX;
pointX = -pointX;
}
if( segY < 0 ) /* set > 0 by symmetry about the X axis */
{ {
segY = -segY; // vertical segment
pointY = -pointY; int ll = abs( aRefPoint.x - aStart.x );
}
if( ll >= aDist )
return false;
if( segY == 0 ) /* horizontal */ // To have only one case to examine, ensure aEnd.y > aStart.y
{ if( aEnd.y < aStart.y )
if( abs( pointY ) <= seuil ) EXCHG( aStart.y, aEnd.y );
{
if( ( pointX >= 0 ) && ( pointX <= segX ) )
return 1;
if( ( pointX < 0 ) && ( pointX >= -seuil ) ) if( aRefPoint.y < aEnd.y && aRefPoint.y > aStart.y )
{
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 ) )
return true; return true;
if( ( pointY < 0 ) && ( pointY >= -seuil ) ) // 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( ( ( pointY * pointY ) + ( pointX * pointX ) ) <= ( seuil * seuil ) ) double dd = square( aRefPoint.x - aStart.x) +
square( aRefPoint.y - aStart.y );
if( dd < square( aDist ) )
return true; return true;
} }
if( ( pointY > segY ) && ( pointY <= ( segY + seuil ) ) ) if( (aRefPoint.y - aEnd.y) < aDist )
{ {
if( ( ( ( pointY - segY ) * ( pointY - segY ) ) double dd = square( aRefPoint.x - aEnd.x ) +
+ ( pointX * pointX ) ) <= ( seuil * seuil ) ) square( aRefPoint.y - aEnd.y );
if( dd < square( aDist ) )
return true; return true;
} }
} }
} else if( aEnd.y == aStart.y )
else if( segX == segY ) /* 45 degrees */
{ {
/* Rotate axes of 45 degrees. mouse was then // horizontal segment
* Coord: x1 = x * y * cos45 + sin45 int ll = abs( aRefPoint.y - aStart.y );
* 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 if( ll >= aDist )
* following the orientation of axes on the screen: dx45 = pointx return false;
* (or pointy) and 1.414 is actually greater, and dy45 = 0
*/
// * Threshold should be .707 to reflect the change in coeff dx, dy // To have only one case to examine, ensure xf > xi
seuil *= 7; if( aEnd.x < aStart.x )
seuil /= 10; EXCHG( aStart.x, aEnd.x );
if( abs( cYrot ) <= seuil ) /* ok on vertical axis */ if( aRefPoint.x < aEnd.x && aRefPoint.x > aStart.x )
{
if( ( cXrot >= 0 ) && ( cXrot <= segX ) )
return true; return true;
/* Check extremes using the radius of a circle. */ // there is a special case: x,y near an end point (distance < dist )
if( ( cXrot < 0 ) && ( cXrot >= -seuil ) ) // the distance should be carefully calculated
if( (aStart.x - aRefPoint.x) < aDist )
{ {
if( ( ( cXrot * cXrot ) + ( cYrot * cYrot ) ) <= ( seuil * seuil ) ) double dd = square( aRefPoint.x - aStart.x) +
square( aRefPoint.y - aStart.y );
if( dd < square( aDist ) )
return true; return true;
} }
if( ( cXrot > segX ) && ( cXrot <= ( segX + seuil ) ) )
if( (aRefPoint.x - aEnd.x) < aDist )
{ {
if( ( ( ( cXrot - segX ) * ( cXrot - segX ) ) double dd = square(aRefPoint.x - aEnd.x) +
+ ( cYrot * cYrot ) ) <= ( seuil * seuil ) ) square( aRefPoint.y - aEnd.y);
if( dd < square( aDist ) )
return true; return true;
} }
} }
} else
else /* any orientation */
{ {
/* There is a change of axis (rotation), so that the segment // oblique segment:
* track is horizontal in the new reference // First, we need to calculate the distance between the point
*/ // and the line defined by aStart and aEnd
int angle; // this dist should be < dist
//
angle = KiROUND( ( atan2( (double) segY, (double) segX ) * 1800.0 / M_PI ) ); // find a,slope such that aStart and aEnd lie on y = a + slope*x
cXrot = pointX; double slope = (double) (aEnd.y - aStart.y) / (aEnd.x - aStart.x);
cYrot = pointY; double a = (double) aStart.y - slope * aStart.x;
// find c,orthoslope such that (x,y) lies on y = c + orthoslope*x,
RotatePoint( &cXrot, &cYrot, angle ); /* Rotate the point to be tested */ // where orthoslope=(-1/slope)
RotatePoint( &segX, &segY, angle ); /* Rotate the segment */ // 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 );
if( dd >= dist ) // this reference point is not a good candiadte.
return false;
/* The track is horizontal, following the amendments to coordinate // dd is < dist, therefore we should make a fine test
* axis and, therefore segX = length of segment if( fabs( slope ) > 0.7 )
*/
if( abs( cYrot ) <= seuil ) /* vertical axis */
{ {
if( ( cXrot >= 0 ) && ( cXrot <= segX ) ) // 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; return true;
}
if( ( cXrot < 0 ) && ( cXrot >= -seuil ) ) else
{ {
if( ( ( cXrot * cXrot ) + ( cYrot * cYrot ) ) <= ( seuil * seuil ) ) // 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; return true;
} }
if( ( cXrot > segX ) && ( cXrot <= ( segX + seuil ) ) ) // Here, the test point is still a good candidate,
{ // however it is not "between" the end points of the segment.
if( ( ( ( cXrot - segX ) * ( cXrot - segX ) ) // It is "outside" the segment, but it could be near a segment end point
+ ( cYrot * cYrot ) ) <= ( seuil * seuil ) ) // 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 true;
}
}
} }
return false; return false; // no hit
} }
......
...@@ -2,6 +2,30 @@ ...@@ -2,6 +2,30 @@
* @file trigo.h * @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 #ifndef TRIGO_H
#define TRIGO_H #define TRIGO_H
...@@ -72,8 +96,8 @@ double CrossProduct( wxPoint vectorA, wxPoint vectorB ); ...@@ -72,8 +96,8 @@ double CrossProduct( wxPoint vectorA, wxPoint vectorB );
/** /**
* Function TestSegmentHit * Function TestSegmentHit
* test for hit on line segment * test for hit on line segment
* i.e. cursor within a given distance from segment * i.e. a reference point is within a given distance from segment
* @param aRefPoint = cursor (point to test) coords * @param aRefPoint = reference point to test
* @param aStart is the first end-point of the line segment * @param aStart is the first end-point of the line segment
* @param aEnd is the second end-point of the line segment * @param aEnd is the second end-point of the line segment
* @param aDist = maximum distance for hit * @param aDist = maximum distance for hit
......
...@@ -136,16 +136,10 @@ bool trapezoid2pointDRC( wxPoint aTref[4], wxPoint aPcompare, int aDist ) ...@@ -136,16 +136,10 @@ bool trapezoid2pointDRC( wxPoint aTref[4], wxPoint aPcompare, int aDist )
return false; return false;
} }
// Test distance between aPcompare and polygon edges: // Test distance between aPcompare and each segment of the polygon:
int ii, jj; for( int ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edge in polygon
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, if( TestSegmentHit( aTref[ii], aTref[jj], aPcompare, aDist ) )
aTref[jj].x, aTref[jj].y,
aPcompare.x, aPcompare.y,
dist ) )
return false; return false;
} }
...@@ -602,12 +596,30 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) ...@@ -602,12 +596,30 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
bool swap_pads; bool swap_pads;
swap_pads = false; swap_pads = false;
if( aRefPad->GetShape() != PAD_CIRCLE && aPad->GetShape() == PAD_CIRCLE ) // 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; swap_pads = true;
else if( aRefPad->GetShape() != PAD_OVAL && aPad->GetShape() == PAD_OVAL ) break;
case PAD_OVAL:
swap_pads = true; swap_pads = true;
else if( aRefPad->GetShape() != PAD_RECT && aPad->GetShape() == PAD_RECT ) break;
case PAD_RECT:
if( aRefPad->GetShape() != PAD_OVAL )
swap_pads = true; swap_pads = true;
break;
default:
break;
}
}
if( swap_pads ) if( swap_pads )
{ {
...@@ -639,7 +651,6 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) ...@@ -639,7 +651,6 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
break; break;
case PAD_RECT: case PAD_RECT:
// pad_angle = pad orient relative to the aRefPad orient // pad_angle = pad orient relative to the aRefPad orient
pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation(); pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
NORMALIZE_ANGLE_POS( pad_angle ); NORMALIZE_ANGLE_POS( pad_angle );
...@@ -707,7 +718,9 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) ...@@ -707,7 +718,9 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
{ {
// Should not occur, because aPad and aRefPad are swapped // Should not occur, because aPad and aRefPad are swapped
// to have only aPad shape RECT or TRAP and aRefPad shape TRAP or RECT. // 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; break;
...@@ -775,7 +788,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) ...@@ -775,7 +788,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
break; break;
default: default:
wxLogDebug( wxT( "unexpected pad shape" ) ); wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape" ) );
break; break;
} }
......
/*************************************************************************/
/* Functions to create drill data used to create files and report files */
/*************************************************************************/
#include <algorithm> // sort
#include <fctsys.h>
#include <common.h>
#include <plot_common.h>
#include <macros.h>
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <pcbnew.h>
#include <pcbplot.h>
#include <gendrill.h>
// 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<HOLE_INFO> to fill with pcb holes info
* param aToolListBuffer : the std::vector<DRILL_TOOL> 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<HOLE_INFO>& aHoleListBuffer,
std::vector<DRILL_TOOL>& 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++;
}
}
...@@ -715,7 +715,7 @@ void PCB_PARSER::parseLayers() throw( IO_ERROR, PARSE_ERROR ) ...@@ -715,7 +715,7 @@ void PCB_PARSER::parseLayers() throw( IO_ERROR, PARSE_ERROR )
layer.SetFixedListIndex( layerIndex ); layer.SetFixedListIndex( layerIndex );
m_board->SetLayer( layerIndex, layer ); 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 ) if( layerType != LT_UNDEFINED )
copperLayerCount++; copperLayerCount++;
......
...@@ -18,67 +18,6 @@ double Distance( double x1, double y1, double x2, double y2 ) ...@@ -18,67 +18,6 @@ double Distance( double x1, double y1, double x2, double y2 )
return hypot( x1 - x2, y1 - 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( dd<dist && ( (yf>yi && y<yf && y>yi) || (yf<yi && y>yf && y<yi) ) )
return true;
}
else if( yf==yi )
{
// horizontal segment
dd = fabs( (double) (y - yi) );
if( dd<dist && ( (xf>xi && x<xf && x>xi) || (xf<xi && x>xf && x<xi) ) )
return true;
}
else
{
// oblique segment
// find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx
double b = (double) (yf - yi) / (xf - xi);
double a = (double) yi - b * xi;
// find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
double d = -1.0 / b;
double c = (double) y - d * x;
// find nearest point to (x,y) on line segment (xi,yi) to (xf,yf)
double xp = (a - c) / (d - b);
double yp = a + b * xp;
// find distance
dd = sqrt( (x - xp) * (x - xp) + (y - yp) * (y - yp) );
if( fabs( b )>0.7 )
{
// line segment more vertical than horizontal
if( dd<dist && ( (yf>yi && yp<yf && yp>yi) || (yf<yi && yp>yf && yp<yi) ) )
return 1;
}
else
{
// line segment more horizontal than vertical
if( dd<dist && ( (xf>xi && xp<xf && xp>xi) || (xf<xi && xp>xf && xp<xi) ) )
return true;
}
}
return false; // no hit
}
/* Function FindSegmentIntersections /* Function FindSegmentIntersections
* find intersections between line segment (xi,yi) to (xf,yf) * find intersections between line segment (xi,yi) to (xf,yf)
......
// math stuff for graphics, from FreePCB // math stuff for graphics, from FreePCB
/**
* Function TestLineHit
* test for hit on line segment i.e. a point within a given distance from segment
* @param xi,yi and xf,yf = the end-points of the line segment
* @param dist = maximum distance for hit
* @param x, y = point to test coords
* @return true if hit (i.e dist < distance between the point and the segment, false if not.
*/
bool TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist );
/* Function FindLineSegmentIntersection /* Function FindLineSegmentIntersection
* find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf) * find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf)
* if b > DBL_MAX/10, assume vertical line at x = a * if b > DBL_MAX/10, assume vertical line at x = a
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment