// math for graphics utility routines and RC, from FreePCB #include #include #include #include #include #include // for abs function on ints #include #include static bool InRange( double x, double xi, double xf ); /* Function FindSegmentIntersections * find intersections between line segment (xi,yi) to (xf,yf) * and line segment (xi2,yi2) to (xf2,yf2) * returns true if intersection found */ bool FindSegmentIntersections( int xi, int yi, int xf, int yf, int xi2, int yi2, int xf2, int yf2 ) { if( std::max( xi, xf ) < std::min( xi2, xf2 ) || std::min( xi, xf ) > std::max( xi2, xf2 ) || std::max( yi, yf ) < std::min( yi2, yf2 ) || std::min( yi, yf ) > std::max( yi2, yf2 ) ) return false; return TestForIntersectionOfStraightLineSegments( xi, yi, xf, yf, xi2, yi2, xf2, yf2 ); } /* Function FindLineSegmentIntersection * 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 * return false if no intersection or true if intersect * return coords of intersections in *x1, *y1, *x2, *y2 * if no intersection, returns min distance in dist */ bool FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, double* x1, double* y1, double* x2, double* y2, double* dist ) { double xx = 0, yy = 0; // Init made to avoid C compil "uninitialized" warning bool bVert = false; if( b > DBL_MAX / 10.0 ) bVert = true; if( xf != xi ) // non-vertical segment, get intersection { // horizontal or oblique straight segment // put into form y = c + dx; double d = (double) (yf - yi) / (double) (xf - xi); double c = yf - d * xf; if( bVert ) { // if vertical line, easy if( InRange( a, xi, xf ) ) { *x1 = a; *y1 = c + d * a; return 1; } else { if( dist ) *dist = std::min( std::abs( a - xi ), std::abs( a - xf ) ); return false; } } if( std::abs( b - d ) < 1E-12 ) { // parallel lines if( dist ) { *dist = GetPointToLineDistance( a, b, xi, xf ); } return false; // lines parallel } // calculate intersection xx = (c - a) / (b - d); yy = a + b * (xx); // see if intersection is within the line segment if( yf == yi ) { // horizontal line if( (xx>=xi && xx>xf) || (xx<=xi && xx=xi && xx>xf) || (xx<=xi && xxyi && yy>yf) || (yy=yi && yy>yf) || (yy<=yi && yy max_cl, just returns max_cl+1 and doesn't return x,y */ int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int w1, int x2i, int y2i, int x2f, int y2f, int w2, int max_cl, int* x, int* y ) { // check clearance between bounding rectangles int min_dist = max_cl + ( (w1 + w2) / 2 ); if( std::min( x1i, x1f ) - std::max( x2i, x2f ) > min_dist ) return max_cl+1; if( std::min( x2i, x2f ) - std::max( x1i, x1f ) > min_dist ) return max_cl+1; if( std::min( y1i, y1f ) - std::max( y2i, y2f ) > min_dist ) return max_cl+1; if( std::min( y2i, y2f ) - std::max( y1i, y1f ) > min_dist ) return max_cl+1; int xx, yy; double dist; TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f, x2i, y2i, x2f, y2f, &xx, &yy, &dist ); int d = KiROUND( dist ) - ((w1 + w2) / 2); if( d < 0 ) d = 0; if( x ) *x = xx; if( y ) *y = yy; return d; } /* Function GetPointToLineDistance * Get min. distance from (x,y) to line y = a + bx * if b > DBL_MAX/10, assume vertical line at x = a * returns closest point on line in xpp, ypp */ double GetPointToLineDistance( double a, double b, int x, int y, double* xpp, double* ypp ) { if( b > DBL_MAX / 10 ) { // vertical line if( xpp && ypp ) { *xpp = a; *ypp = y; } return std::abs( a - x ); } // 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 through (xi,yi) to (xf,yf) double xp = (a - c) / (d - b); double yp = a + b * xp; if( xpp && ypp ) { *xpp = xp; *ypp = yp; } // find distance return Distance( x, y, xp, yp ); } /** * Function GetPointToLineSegmentDistance * Get distance between line segment and point * @param x,y = point * @param xi,yi Start point of the line segament * @param xf,yf End point of the line segment * @return the distance */ double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf ) { // test for vertical or horizontal segment if( xf==xi ) { // vertical line segment if( InRange( y, yi, yf ) ) return std::abs( x - xi ); else return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) ); } else if( yf==yi ) { // horizontal line segment if( InRange( x, xi, xf ) ) return std::abs( y - yi ); else return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) ); } 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 through (xi,yi) to (xf,yf) double xp = (a - c) / (d - b); double yp = a + b * xp; // find distance if( InRange( xp, xi, xf ) && InRange( yp, yi, yf ) ) return Distance( x, y, xp, yp ); else return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) ); } } // test for value within range bool InRange( double x, double xi, double xf ) { if( xf > xi ) { if( x >= xi && x <= xf ) return true; } else { if( x >= xf && x <= xi ) return true; } return false; }