// math for graphics utility routines, from FreePCB using namespace std; #include #include #include #include #include "fctsys.h" #include "defs-macros.h" #include "PolyLine2Kicad.h" #include "freepcb_ids.h" #include "PolyLine.h" // function to find inflection-pont to create a "dogleg" of two straight-line segments // where one segment is vertical or horizontal and the other is at 45 degrees or 90 degrees // enter with: // pi = start point // pf = end point // mode = IM_90_45 or IM_45_90 or IM_90 // CPoint GetInflectionPoint( CPoint pi, CPoint pf, int mode ) { CPoint p = pi; if( mode == IM_NONE ) return p; int dx = pf.x - pi.x; int dy = pf.y - pi.y; if( dx == 0 || dy == 0 || abs(dx) == abs(dy) ) { // only one segment needed } else { if( abs(dy) > abs(dx) ) { // vertical > horizontal if( mode == IM_90 ) { p.x = pi.x; p.y = pf.y; } else if( mode == IM_45_90 || mode == IM_90_45 ) { int vert; // length of vertical line needed if( dy > 0 ) vert = dy - abs(dx); // positive else vert = dy + abs(dx); // negative if( mode == IM_90_45 ) p.y = pi.y + vert; else if( mode == IM_45_90 ) { p.y = pf.y - vert; p.x = pf.x; } } else wxASSERT(0); } else { // horizontal > vertical if( mode == IM_90 ) { p.x = pf.x; p.y = pi.y; } else if( mode == IM_45_90 || mode == IM_90_45 ) { int hor; // length of horizontal line needed if( dx > 0 ) hor = dx - abs(dy); // positive else hor = dx + abs(dy); // negative if( mode == IM_90_45 ) p.x = pi.x + hor; else if( mode == IM_45_90 ) { p.x = pf.x - hor; p.y = pf.y; } } else wxASSERT(0); } } return p; } // // function to rotate a point clockwise about another point // currently, angle must be 0, 90, 180 or 270 // void RotatePoint( CPoint *p, int angle, CPoint org ) { if( angle == 90 ) { int tempy = org.y + (org.x - p->x); p->x = org.x + (p->y - org.y); p->y = tempy; } else if( angle > 90 ) { for( int i=0; i<(angle/90); i++ ) RotatePoint( p, 90, org ); } } // function to rotate a rectangle clockwise about a point // angle must be 0, 90, 180 or 270 // on exit, r->top > r.bottom, r.right > r.left // void RotateRect( CRect *r, int angle, CPoint org ) { CRect tr; if( angle == 90 ) { tr.left = org.x + (r->bottom - org.y); tr.right = org.x + (r->top - org.y); tr.top = org.y + (org.x - r->right); tr.bottom = org.y + (org.x - r->left); if( tr.left > tr.right ) { int temp = tr.right; tr.left = tr.right; tr.left = temp; } if( tr.left > tr.right ) { int temp = tr.right; tr.left = tr.right; tr.left = temp; } if( tr.bottom > tr.top ) { int temp = tr.bottom; tr.bottom = tr.top; tr.top = temp; } } else if( angle > 90 ) { tr = *r; for( int i=0; i<(angle/90); i++ ) RotateRect( &tr, 90, org ); } *r = tr; } // 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 ) { 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 xxi && yyf > yyi ) { xo = xxf; yo = yyi; el->theta1 = M_PI; el->theta2 = M_PI/2.0; } else if( xxf < xxi && yyf > yyi ) { xo = xxi; yo = yyf; el->theta1 = -M_PI/2.0; el->theta2 = -M_PI; } else if( xxf < xxi && yyf < yyi ) { xo = xxf; yo = yyi; el->theta1 = 0.0; el->theta2 = -M_PI/2.0; } else if( xxf > xxi && yyf < yyi ) { xo = xxi; yo = yyf; el->theta1 = M_PI/2.0; el->theta2 = 0.0; } el->Center.X = xo; el->Center.Y = yo; el->xrad = abs(xf-xi); el->yrad = abs(yf-yi); #if 0 el->Phi = 0.0; el->MaxRad = el->xrad; el->MinRad = el->yrad; if( el->MaxRad < el->MinRad ) { el->MaxRad = el->yrad; el->MinRad = el->xrad; el->Phi = M_PI/2.0; } #endif return 0; } // find intersections between line segment (xi,yi) to (xf,yf) // and line segment (xi2,yi2) to (xf2,yf2) // the line segments may be arcs (i.e. quadrant of an ellipse) or straight // returns number of intersections found (max of 2) // returns coords of intersections in arrays x[2], y[2] // int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style, int xi2, int yi2, int xf2, int yf2, int style2, double x[], double y[] ) { double xr[12], yr[12]; int iret = 0; if( max(xi,xf) < min(xi2,xf2) || min(xi,xf) > max(xi2,xf2) || max(yi,yf) < min(yi2,yf2) || min(yi,yf) > max(yi2,yf2) ) return 0; if( style != CPolyLine::STRAIGHT && style2 != CPolyLine::STRAIGHT ) { // two identical arcs intersect if( style == style2 && xi == xi2 && yi == yi2 && xf == xf2 && yf == yf2 ) { if( x && y ) { x[0] = xi; y[0] = yi; } return 1; } else if( style != style2 && xi == xf2 && yi == yf2 && xf == xi2 && yf == yi2 ) { if( x && y ) { x[0] = xi; y[0] = yi; } return 1; } } if( style == CPolyLine::STRAIGHT && style2 == CPolyLine::STRAIGHT ) { // both straight-line segments int x, y; bool bYes = TestForIntersectionOfStraightLineSegments( xi, yi, xf, yf, xi2, yi2, xf2, yf2, &x, &y ); if( !bYes ) return 0; xr[0] = x; yr[0] = y; iret = 1; } else if( style == CPolyLine::STRAIGHT ) { // first segment is straight, second segment is an arc int ret; double x1r, y1r, x2r, y2r; if( xf == xi ) { // vertical first segment double a = xi; double b = DBL_MAX/2.0; ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2, &x1r, &y1r, &x2r, &y2r ); } else { double b = (double)(yf-yi)/(double)(xf-xi); double a = yf - b*xf; ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2, &x1r, &y1r, &x2r, &y2r ); } if( ret == 0 ) return 0; if( InRange( x1r, xi, xf ) && InRange( y1r, yi, yf ) ) { xr[iret] = x1r; yr[iret] = y1r; iret++; } if( ret == 2 ) { if( InRange( x2r, xi, xf ) && InRange( y2r, yi, yf ) ) { xr[iret] = x2r; yr[iret] = y2r; iret++; } } } else if( style2 == CPolyLine::STRAIGHT ) { // first segment is an arc, second segment is straight int ret; double x1r, y1r, x2r, y2r; if( xf2 == xi2 ) { // vertical second segment double a = xi2; double b = DBL_MAX/2.0; ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style, &x1r, &y1r, &x2r, &y2r ); } else { double b = (double)(yf2-yi2)/(double)(xf2-xi2); double a = yf2 - b*xf2; ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style, &x1r, &y1r, &x2r, &y2r ); } if( ret == 0 ) return 0; if( InRange( x1r, xi2, xf2 ) && InRange( y1r, yi2, yf2 ) ) { xr[iret] = x1r; yr[iret] = y1r; iret++; } if( ret == 2 ) { if( InRange( x2r, xi2, xf2 ) && InRange( y2r, yi2, yf2 ) ) { xr[iret] = x2r; yr[iret] = y2r; iret++; } } } else { // both segments are arcs EllipseKH el1; EllipseKH el2; MakeEllipseFromArc( xi, yi, xf, yf, style, &el1 ); MakeEllipseFromArc( xi2, yi2, xf2, yf2, style2, &el2 ); int n; if( el1.xrad+el1.yrad > el2.xrad+el2.yrad ) n = GetArcIntersections( &el1, &el2 ); else n = GetArcIntersections( &el2, &el1 ); iret = n; } if( x && y ) { for( int i=0; i DBL_MAX/10, assume vertical line at x = a // the line segment may be an arc (i.e. quadrant of an ellipse) // return 0 if no intersection // returns 1 or 2 if intersections found // sets coords of intersections in *x1, *y1, *x2, *y2 // if no intersection, returns min distance in dist // int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style, double * x1, double * y1, double * x2, double * y2, double * dist ) { double xx, yy; bool bVert = false; if( b > DBL_MAX/10.0 ) bVert = true; if( xf != xi ) { // non-vertical segment, get intersection if( style == CPolyLine::STRAIGHT || yf == yi ) { // 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 = min( abs(a-xi), abs(a-xf) ); return 0; } } if( fabs(b-d) < 1E-12 ) { // parallel lines if( dist ) { *dist = GetPointToLineDistance( a, b, xi, xf ); } return 0; // 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 xxi && yyf > yyi ) { xo = xxf; yo = yyi; } else if( xxf < xxi && yyf > yyi ) { xo = xxi; yo = yyf; } else if( xxf < xxi && yyf < yyi ) { xo = xxf; yo = yyi; } else if( xxf > xxi && yyf < yyi ) { xo = xxi; yo = yyf; } rx = fabs( (double)(xxi-xxf) ); ry = fabs( (double)(yyi-yyf) ); bool test; double xx1, xx2, yy1, yy2, aa; if( bVert ) { // shift vertical line to coordinate system of ellipse aa = a - xo; test = FindVerticalLineEllipseIntersections( rx, ry, aa, &yy1, &yy2 ); if( !test ) return 0; // shift back to PCB coordinates yy1 += yo; yy2 += yo; xx1 = a; xx2 = a; } else { // shift line to coordinate system of ellipse aa = a + b*xo - yo; test = FindLineEllipseIntersections( rx, ry, aa, b, &xx1, &xx2 ); if( !test ) return 0; // shift back to PCB coordinates yy1 = aa + b*xx1; xx1 += xo; yy1 += yo; yy2 = aa + b*xx2; xx2 += xo; yy2 += yo; } int npts = 0; if( (xxf>xxi && xx1xxi) || (xxfxxf) ) { if( (yyf>yyi && yy1yyi) || (yyfyyf) ) { *x1 = xx1; *y1 = yy1; npts = 1; } } if( (xxf>xxi && xx2xxi) || (xxfxxf) ) { if( (yyf>yyi && yy2yyi) || (yyfyyf) ) { if( npts == 0 ) { *x1 = xx2; *y1 = yy2; npts = 1; } else { *x2 = xx2; *y2 = yy2; npts = 2; } } } return npts; } else wxASSERT(0); } else { // vertical line segment if( bVert ) return 0; xx = xi; yy = a + b*xx; if( (yy>=yi && yy>yf) || (yy<=yi && yy= pivot) && (left < right)) right--; if (left != right) { numbers[left] = numbers[right]; index[left] = index[right]; left++; } while ((numbers[left] <= pivot) && (left < right)) left++; if (left != right) { numbers[right] = numbers[left]; index[right] = index[left]; right--; } } numbers[left] = pivot; index[left] = pivot_index; pivot = left; left = l_hold; right = r_hold; if (left < pivot) q_sort(numbers, index, left, pivot-1); if (right > pivot) q_sort(numbers, index, pivot+1, right); } // 3-way quicksort...useful where there are duplicate values // void q_sort_3way( int a[], int b[], int l, int r ) { #define EXCH(i,j) {int temp=a[i]; a[i]=a[j]; a[j]=temp; temp=b[i]; b[i]=b[j]; b[j]=temp;} int i = l - 1; int j = r; int p = l - 1; int q = r; int v = a[r]; if( r <= l ) return; for(;;) { while( a[++i] < v ); while( v < a[--j] ) if( j == 1 ) break; if( i >= j ) break; EXCH( i, j ); if( a[i] == v ) { p++; EXCH( p, i ); } if( v == a[j] ) { q--; EXCH( j, q ); } } EXCH( i, r ); j = i - 1; i = i + 1; for( int k=l; kq; k--, i++ ) EXCH( i, k ); q_sort_3way( a, b, l, j ); q_sort_3way( a, b, i, r ); } // solves quadratic equation // i.e. ax**2 + bx + c = 0 // returns true if solution exist, with solutions in x1 and x2 // else returns false // bool Quadratic( double a, double b, double c, double *x1, double *x2 ) { double root = b*b - 4.0*a*c; if( root < 0.0 ) return false; root = sqrt( root ); *x1 = (-b+root)/(2.0*a); *x2 = (-b-root)/(2.0*a); return true; } // finds intersections of vertical line at x // with ellipse defined by (x^2)/(a^2) + (y^2)/(b^2) = 1; // returns true if solution exist, with solutions in y1 and y2 // else returns false // bool FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 ) { double y_sqr = (1.0-(x*x)/(a*a))*b*b; if( y_sqr < 0.0 ) return false; *y1 = sqrt(y_sqr); *y2 = -*y1; return true; } // finds intersections of straight line y = c + dx // with ellipse defined by (x^2)/(a^2) + (y^2)/(b^2) = 1; // returns true if solution exist, with solutions in x1 and x2 // else returns false // bool FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 ) { // quadratic terms double A = d*d+b*b/(a*a); double B = 2.0*c*d; double C = c*c-b*b; return Quadratic( A, B, C, x1, x2 ); } #if 0 // draw a straight line or an arc between xi,yi and xf,yf // void DrawArc( CDC * pDC, int shape, int xxi, int yyi, int xxf, int yyf, bool bMeta ) { int xi, yi, xf, yf; if( shape == DL_LINE || xxi == xxf || yyi == yyf ) { // draw straight line pDC->MoveTo( xxi, yyi ); pDC->LineTo( xxf, yyf ); } else if( shape == DL_ARC_CCW || shape == DL_ARC_CW ) { // set endpoints so we can always draw counter-clockwise arc if( shape == DL_ARC_CW ) { xi = xxf; yi = yyf; xf = xxi; yf = yyi; } else { xi = xxi; yi = yyi; xf = xxf; yf = yyf; } pDC->MoveTo( xi, yi ); if( xf > xi && yf > yi ) { // quadrant 1 int w = (xf-xi)*2; int h = (yf-yi)*2; if( !bMeta ) pDC->Arc( xf-w, yi+h, xf, yi, xi, yi, xf, yf ); else pDC->Arc( xf-w, yi, xf, yi+h, xf, yf, xi, yi ); } else if( xf < xi && yf > yi ) { // quadrant 2 int w = -(xf-xi)*2; int h = (yf-yi)*2; if( !bMeta ) pDC->Arc( xi-w, yf, xi, yf-h, xi, yi, xf, yf ); else pDC->Arc( xi-w, yf-h, xi, yf, xf, yf, xi, yi ); } else if( xf < xi && yf < yi ) { // quadrant 3 int w = -(xf-xi)*2; int h = -(yf-yi)*2; if( !bMeta ) pDC->Arc( xf, yi, xf+w, yi-h, xi, yi, xf, yf ); else pDC->Arc( xf, yi-h, xf+w, yi, xf, yf, xi, yi ); } else if( xf > xi && yf < yi ) { // quadrant 4 int w = (xf-xi)*2; int h = -(yf-yi)*2; if( !bMeta ) pDC->Arc( xi, yf+h, xi+w, yf, xi, yi, xf, yf ); else pDC->Arc( xi, yf, xi+w, yf+h, xf, yf, xi, yi ); } pDC->MoveTo( xxf, yyf ); } else wxASSERT(0); // oops } #endif // Get arrays of circles, rects and line segments to represent pad // for purposes of drawing pad or calculating clearances // margins of circles and line segments represent pad outline // circles and rects are used to find points inside pad // void GetPadElements( int type, int x, int y, int wid, int len, int radius, int angle, int * nr, my_rect r[], int * nc, my_circle c[], int * ns, my_seg s[] ) { *nc = 0; *nr = 0; *ns = 0; if( type == PAD_ROUND ) { *nc = 1; c[0] = my_circle(x,y,wid/2); return; } if( type == PAD_SQUARE ) { *nr = 1; r[0] = my_rect(x-wid/2, y-wid/2,x+wid/2, y+wid/2); *ns = 4; s[0] = my_seg(x-wid/2, y+wid/2,x+wid/2, y+wid/2); // top s[1] = my_seg(x-wid/2, y-wid/2,x+wid/2, y-wid/2); // bottom s[2] = my_seg(x-wid/2, y-wid/2,x-wid/2, y+wid/2); // left s[3] = my_seg(x+wid/2, y-wid/2,x+wid/2, y+wid/2); // right return; } if( type == PAD_OCTAGON ) { const double pi = 3.14159265359; *nc = 1; // circle represents inside of polygon c[0] = my_circle(x, y, wid/2); *ns = 8; // now create sides of polygon double theta = pi/8.0; double radius = 0.5*(double)wid/cos(theta); double last_x = x + radius*cos(theta); double last_y = y + radius*sin(theta); for( int is=0; is<8; is++ ) { theta += pi/4.0; double dx = x + radius*cos(theta); double dy = y + radius*sin(theta); s[is] = my_seg(last_x, last_y, x, y); last_x = dx; last_y = dy; } return; } // int h; int v; if( angle == 90 || angle == 270 ) { h = wid; v = len; } else { v = wid; h = len; } if( type == PAD_RECT ) { *nr = 1; r[0] = my_rect(x-h/2, y-v/2, x+h/2, y+v/2); *ns = 4; s[0] = my_seg(x-h/2, y+v/2,x+h/2, y+v/2); // top s[1] = my_seg(x-h/2, y-v/2,x+h/2, y-v/2); // bottom s[2] = my_seg(x-h/2, y-v/2,x-h/2, y+v/2); // left s[3] = my_seg(x+h/2, y-v/2,x+h/2, y+v/2); // right return; } if( type == PAD_RRECT ) { *nc = 4; c[0] = my_circle(x-h/2+radius, y-v/2+radius, radius); // bottom left circle c[1] = my_circle(x+h/2-radius, y-v/2+radius, radius); // bottom right circle c[2] = my_circle(x-h/2+radius, y+v/2-radius, radius); // top left circle c[3] = my_circle(x+h/2-radius, y+v/2-radius, radius); // top right circle *ns = 4; s[0] = my_seg(x-h/2+radius, y+v/2, x+h/2-radius, y+v/2); // top s[1] = my_seg(x-h/2+radius, y-v/2, x+h/2-radius, y+v/2); // bottom s[2] = my_seg(x-h/2, y-v/2+radius, x-h/2, y+v/2-radius); // left s[3] = my_seg(x+h/2, y-v/2+radius, x+h/2, y+v/2-radius); // right return; } if( type == PAD_OVAL ) { if( h > v ) { // horizontal *nc = 2; c[0] = my_circle(x-h/2+v/2, y, v/2); // left circle c[1] = my_circle(x+h/2-v/2, y, v/2); // right circle *nr = 1; r[0] = my_rect(x-h/2+v/2, y-v/2, x+h/2-v/2, y+v/2); *ns = 2; s[0] = my_seg(x-h/2+v/2, y+v/2, x+h/2-v/2, y+v/2); // top s[1] = my_seg(x-h/2+v/2, y-v/2, x+h/2-v/2, y-v/2); // bottom } else { // vertical *nc = 2; c[0] = my_circle(x, y+v/2-h/2, h/2); // top circle c[1] = my_circle(x, y-v/2+h/2, h/2); // bottom circle *nr = 1; r[0] = my_rect(x-h/2, y-v/2+h/2, x+h/2, y+v/2-h/2); *ns = 2; s[0] = my_seg(x-h/2, y-v/2+h/2, x-h/2, y+v/2-h/2); // left s[1] = my_seg(x+h/2, y-v/2+h/2, x+h/2, y+v/2-h/2); // left } return; } wxASSERT(0); } // Find distance from a staright line segment to a pad // int GetClearanceBetweenSegmentAndPad( int x1, int y1, int x2, int y2, int w, int type, int x, int y, int wid, int len, int radius, int angle ) { if( type == PAD_NONE ) return INT_MAX; else { int nc, nr, ns; my_circle c[4]; my_rect r[2]; my_seg s[8]; GetPadElements( type, x, y, wid, len, radius, angle, &nr, r, &nc, c, &ns, s ); // first test for endpoints of line segment in rectangle for( int ir=0; ir= r[ir].xlo && x1 <= r[ir].xhi && y1 >= r[ir].ylo && y1 <= r[ir].yhi ) return 0; if( x2 >= r[ir].xlo && x2 <= r[ir].xhi && y2 >= r[ir].ylo && y2 <= r[ir].yhi ) return 0; } // now get distance from elements of pad outline int dist = INT_MAX; for( int ic=0; ic max_cl, just returns max_cl and doesn't return x,y // int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1, int x2i, int y2i, int x2f, int y2f, int style2, int w2, int max_cl, int * x, int * y ) { // check clearance between bounding rectangles int test = max_cl + w1/2 + w2/2; if( min(x1i,x1f)-max(x2i,x2f) > test ) return max_cl; if( min(x2i,x2f)-max(x1i,x1f) > test ) return max_cl; if( min(y1i,y1f)-max(y2i,y2f) > test ) return max_cl; if( min(y2i,y2f)-max(y1i,y1f) > test ) return max_cl; if( style1 == CPolyLine::STRAIGHT && style1 == CPolyLine::STRAIGHT ) { // both segments are straight lines int xx, yy; double dd; TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f, x2i, y2i, x2f, y2f, &xx, &yy, &dd ); int d = max( 0, dd - w1/2 - w2/2 ); if( x ) *x = xx; if( y ) *y = yy; return d; } // not both straight-line segments // see if segments intersect double xr[2]; double yr[2]; test = FindSegmentIntersections( x1i, y1i, x1f, y1f, style1, x2i, y2i, x2f, y2f, style2, xr, yr ); if( test ) { if( x ) *x = xr[0]; if( y ) *y = yr[0]; return 0.0; } // at least one segment is an arc EllipseKH el1; EllipseKH el2; bool bArcs; int xi, yi, xf, yf; if( style2 == CPolyLine::STRAIGHT ) { // style1 = arc, style2 = straight MakeEllipseFromArc( x1i, y1i, x1f, y1f, style1, &el1 ); xi = x2i; yi = y2i; xf = x2f; yf = y2f; bArcs = false; } else if( style1 == CPolyLine::STRAIGHT ) { // style2 = arc, style1 = straight xi = x1i; yi = y1i; xf = x1f; yf = y1f; MakeEllipseFromArc( x2i, y2i, x2f, y2f, style2, &el1 ); bArcs = false; } else { // style1 = arc, style2 = arc MakeEllipseFromArc( x1i, y1i, x1f, y1f, style1, &el1 ); MakeEllipseFromArc( x2i, y2i, x2f, y2f, style2, &el2 ); bArcs = true; } const int NSTEPS = 32; if( el1.theta2 > el1.theta1 ) wxASSERT(0); if( bArcs && el2.theta2 > el2.theta1 ) wxASSERT(0); // test multiple points in both segments double th1; double th2; double len2; if( bArcs ) { th1 = el2.theta1; th2 = el2.theta2; len2 = max(el2.xrad, el2.yrad); } else { th1 = 1.0; th2 = 0.0; len2 = abs(xf-xi)+abs(yf-yi); } double s_start = el1.theta1; double s_end = el1.theta2; double s_start2 = th1; double s_end2 = th2; double dmin = DBL_MAX; double xmin, ymin, smin, smin2; int nsteps = NSTEPS; int nsteps2 = NSTEPS; double step = (s_start-s_end)/(nsteps-1); double step2 = (s_start2-s_end2)/(nsteps2-1); while( (step * max(el1.xrad, el1.yrad)) > 0.1*NM_PER_MIL && (step2 * len2) > 0.1*NM_PER_MIL ) { step = (s_start-s_end)/(nsteps-1); for( int i=0; i step2 ) { s_start = min(el1.theta1, smin + step); s_end = max(el1.theta2, smin - step); step = (s_start - s_end)/nsteps; } else { s_start2 = min(th1, smin2 + step2); s_end2 = max(th2, smin2 - step2); step2 = (s_start2 - s_end2)/nsteps2; } } if( x ) *x = xmin; if( y ) *y = ymin; return max(0,dmin-w1/2-w2/2); // allow for widths } // Find clearance between pads // For each pad: // type = PAD_ROUND, PAD_SQUARE, etc. // x, y = center position // w, l = width and length // r = corner radius // angle = 0 or 90 (if 0, pad length is along x-axis) // int GetClearanceBetweenPads( int type1, int x1, int y1, int w1, int l1, int r1, int angle1, int type2, int x2, int y2, int w2, int l2, int r2, int angle2 ) { if( type1 == PAD_NONE ) return INT_MAX; if( type2 == PAD_NONE ) return INT_MAX; int dist = INT_MAX; int nr, nc, ns, nrr, ncc, nss; my_rect r[2], rr[2]; my_circle c[4], cc[4]; my_seg s[8], ss[8]; GetPadElements( type1, x1, y1, w1, l1, r1, angle1, &nr, r, &nc, c, &ns, s ); GetPadElements( type2, x2, y2, w2, l2, r2, angle2, &nrr, rr, &ncc, cc, &nss, ss ); // now find distance from every element of pad1 to every element of pad2 for( int ic=0; ic DBL_MAX/10, assume vertical line at x = a // returns closest point on line in xp, yp // 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 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 ); } /***********************************************************************************/ double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf ) /***********************************************************************************/ /** Function GetPointToLineSegmentDistance * Get distance between line segment and point * @param x,y = point * @param xi,yi and xf,yf = the end-points of the line segment * @return the distance */ { // test for vertical or horizontal segment if( xf==xi ) { // vertical line segment if( InRange( y, yi, yf ) ) return abs( x - xi ); else return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) ); } else if( yf==yi ) { // horizontal line segment if( InRange( x, xi, xf ) ) return abs( y - yi ); else return 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 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; } // Get distance between 2 points // double Distance( int x1, int y1, int x2, int y2 ) { double d; d = sqrt( (double)(x1-x2)*(x1-x2) + (double)(y1-y2)*(y1-y2) ); if( d > INT_MAX || d < INT_MIN ) wxASSERT(0); return (int)d; } // this finds approximate solutions // note: this works best if el2 is smaller than el1 // int GetArcIntersections( EllipseKH * el1, EllipseKH * el2, double * x1, double * y1, double * x2, double * y2 ) { if( el1->theta2 > el1->theta1 ) wxASSERT(0); if( el2->theta2 > el2->theta1 ) wxASSERT(0); const int NSTEPS = 32; double xret[2], yret[2]; double xscale = 1.0/el1->xrad; double yscale = 1.0/el1->yrad; // now transform params of second ellipse into reference frame // with origin at center if first ellipse, // scaled so the first ellipse is a circle of radius = 1.0 double xo = (el2->Center.X - el1->Center.X)*xscale; double yo = (el2->Center.Y - el1->Center.Y)*yscale; double xr = el2->xrad*xscale; double yr = el2->yrad*yscale; // now test NSTEPS positions in arc, moving clockwise (ie. decreasing theta) double step = M_PI/((NSTEPS-1)*2.0); double d_prev, th_prev; double th_interp; double th1; int n = 0; for( int i=0; itheta1 - i*step; else theta = el2->theta2; double x = xo + xr*cos(theta); double y = yo + yr*sin(theta); double d = 1.0 - sqrt(x*x + y*y); if( i>0 ) { bool bInt = false; if( d >= 0.0 && d_prev <= 0.0 ) { th_interp = theta + (step*(-d_prev))/(d-d_prev); bInt = true; } else if( d <= 0.0 && d_prev >= 0.0 ) { th_interp = theta + (step*d_prev)/(d_prev-d); bInt = true; } if( bInt ) { x = xo + xr*cos(th_interp); y = yo + yr*sin(th_interp); th1 = atan2( y, x ); if( th1 <= el1->theta1 && th1 >= el1->theta2 ) { xret[n] = x*el1->xrad + el1->Center.X; yret[n] = y*el1->yrad + el1->Center.Y; n++; if( n > 2 ) wxASSERT(0); } } } d_prev = d; th_prev = theta; } if( x1 ) *x1 = xret[0]; if( y1 ) *y1 = yret[0]; if( x2 ) *x2 = xret[1]; if( y2 ) *y2 = yret[1]; return n; } // this finds approximate solution // //double GetSegmentClearance( EllipseKH * el1, EllipseKH * el2, double GetArcClearance( EllipseKH * el1, EllipseKH * el2, double * x1, double * y1 ) { const int NSTEPS = 32; if( el1->theta2 > el1->theta1 ) wxASSERT(0); if( el2->theta2 > el2->theta1 ) wxASSERT(0); // test multiple positions in both arcs, moving clockwise (ie. decreasing theta) double th_start = el1->theta1; double th_end = el1->theta2; double th_start2 = el2->theta1; double th_end2 = el2->theta2; double dmin = DBL_MAX; double xmin, ymin, thmin, thmin2; int nsteps = NSTEPS; int nsteps2 = NSTEPS; double step = (th_start-th_end)/(nsteps-1); double step2 = (th_start2-th_end2)/(nsteps2-1); while( (step * max(el1->xrad, el1->yrad)) > 1.0*NM_PER_MIL && (step2 * max(el2->xrad, el2->yrad)) > 1.0*NM_PER_MIL ) { step = (th_start-th_end)/(nsteps-1); for( int i=0; iCenter.X + el1->xrad*cos(theta); double y = el1->Center.Y + el1->yrad*sin(theta); step2 = (th_start2-th_end2)/(nsteps2-1); for( int i2=0; i2Center.X + el2->xrad*cos(theta2); double y2 = el2->Center.Y + el2->yrad*sin(theta2); double d = Distance( x, y, x2, y2 ); if( d < dmin ) { dmin = d; xmin = x; ymin = y; thmin = theta; thmin2 = theta2; } } } if( step > step2 ) { th_start = min(el1->theta1, thmin + step); th_end = max(el1->theta2, thmin - step); step = (th_start - th_end)/nsteps; } else { th_start2 = min(el2->theta1, thmin2 + step2); th_end2 = max(el2->theta2, thmin2 - step2); step2 = (th_start2 - th_end2)/nsteps2; } } if( x1 ) *x1 = xmin; if( y1 ) *y1 = ymin; return dmin; }