math_for_graphics.cpp 36.6 KB
Newer Older
1
// math for graphics utility routines and RC, from FreePCB
2 3 4 5 6 7 8

#include <vector>

#include <math.h>
#include <float.h>
#include <limits.h>

9
#include <fctsys.h>
10

11
#include <PolyLine.h>
12

13
#define NM_PER_MIL 25400
14

15 16 17 18 19 20 21 22
double Distance( double x1, double y1, double x2, double y2 )
{
    double dx = x1 - x2;
    double dy = y1 - y2;
    double d  = sqrt( dx * dx + dy * dy );
    return d;
}

23

24 25
/**
 * Function TestLineHit
26 27
 * test for hit on line segment i.e. a point within a given distance from segment
 * @param x, y = cursor coords
28
 * @param xi,yi,xf,yf = the end-points of the line segment
29 30 31 32
 * @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 )
33
{
34
    double dd;
35

36 37 38 39
    // test for vertical or horizontal segment
    if( xf==xi )
    {
        // vertical segment
40 41
        dd = fabs( (double) (x - xi) );

42 43 44 45 46 47
        if( dd<dist && ( (yf>yi && y<yf && y>yi) || (yf<yi && y>yf && y<yi) ) )
            return true;
    }
    else if( yf==yi )
    {
        // horizontal segment
48 49
        dd = fabs( (double) (y - yi) );

50 51 52 53 54 55 56
        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
57 58
        double  b   = (double) (yf - yi) / (xf - xi);
        double  a   = (double) yi - b * xi;
59
        // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
60 61
        double  d   = -1.0 / b;
        double  c   = (double) y - d * x;
62
        // find nearest point to (x,y) on line segment (xi,yi) to (xf,yf)
63 64
        double  xp  = (a - c) / (d - b);
        double  yp  = a + b * xp;
65
        // find distance
66 67 68
        dd = sqrt( (x - xp) * (x - xp) + (y - yp) * (y - yp) );

        if( fabs( b )>0.7 )
69 70 71 72 73 74 75 76 77 78 79 80
        {
            // 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;
        }
    }
81 82

    return false;    // no hit
83 84 85 86 87
}


// set EllipseKH struct to describe the ellipse for an arc
//
88
int MakeEllipseFromArc( int xi, int yi, int xf, int yf, int style, EllipseKH* el )
89
{
90 91 92
    // arc (quadrant of ellipse)
    // convert to clockwise arc
    int xxi, xxf, yyi, yyf;
93

94 95 96 97 98 99 100 101 102 103 104 105 106 107
    if( style == CPolyLine::ARC_CCW )
    {
        xxi = xf;
        xxf = xi;
        yyi = yf;
        yyf = yi;
    }
    else
    {
        xxi = xi;
        xxf = xf;
        yyi = yi;
        yyf = yf;
    }
108

109
    // find center and radii of ellipse
110 111
    double xo = 0, yo = 0;

112 113
    if( xxf > xxi && yyf > yyi )
    {
114 115 116 117
        xo  = xxf;
        yo  = yyi;
        el->theta1  = M_PI;
        el->theta2  = M_PI / 2.0;
118 119 120
    }
    else if( xxf < xxi && yyf > yyi )
    {
121 122 123 124
        xo  = xxi;
        yo  = yyf;
        el->theta1  = -M_PI / 2.0;
        el->theta2  = -M_PI;
125 126 127
    }
    else if( xxf < xxi && yyf < yyi )
    {
128 129 130 131
        xo  = xxf;
        yo  = yyi;
        el->theta1  = 0.0;
        el->theta2  = -M_PI / 2.0;
132 133 134
    }
    else if( xxf > xxi && yyf < yyi )
    {
135 136 137 138
        xo  = xxi;
        yo  = yyf;
        el->theta1  = M_PI / 2.0;
        el->theta2  = 0.0;
139
    }
140 141 142 143 144

    el->Center.X    = xo;
    el->Center.Y    = yo;
    el->xrad        = abs( xf - xi );
    el->yrad        = abs( yf - yi );
145
#if 0
146 147 148 149
    el->Phi     = 0.0;
    el->MaxRad  = el->xrad;
    el->MinRad  = el->yrad;

150 151
    if( el->MaxRad < el->MinRad )
    {
152 153 154
        el->MaxRad  = el->yrad;
        el->MinRad  = el->xrad;
        el->Phi     = M_PI / 2.0;
155
    }
156

157
#endif
158
    return 0;
159 160
}

161

162 163 164 165 166 167
// 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]
//
168
int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style,
169 170
                              int xi2, int yi2, int xf2, int yf2, int style2,
                              double x[], double y[] )
171
{
172 173
    double  xr[12], yr[12];
    int     iret = 0;
174

175 176 177 178
    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 ) )
179
        return 0;
180

181 182 183 184 185 186 187
    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 )
            {
188 189
                x[0]    = xi;
                y[0]    = yi;
190
            }
191

192 193 194 195 196 197
            return 1;
        }
        else if( style != style2 && xi == xf2 && yi == yf2 && xf == xi2 && yf == yi2 )
        {
            if( x && y )
            {
198 199
                x[0]    = xi;
                y[0]    = yi;
200
            }
201

202 203 204
            return 1;
        }
    }
205

206 207 208
    if( style == CPolyLine::STRAIGHT && style2 == CPolyLine::STRAIGHT )
    {
        // both straight-line segments
209 210 211 212 213 214 215 216 217 218 219 220
        int     x, y;
        bool    bYes = TestForIntersectionOfStraightLineSegments( xi,
                                                                  yi,
                                                                  xf,
                                                                  yf,
                                                                  xi2,
                                                                  yi2,
                                                                  xf2,
                                                                  yf2,
                                                                  &x,
                                                                  &y );

221 222
        if( !bYes )
            return 0;
223 224 225 226

        xr[0]   = x;
        yr[0]   = y;
        iret    = 1;
227 228 229 230
    }
    else if( style == CPolyLine::STRAIGHT )
    {
        // first segment is straight, second segment is an arc
231 232 233
        int     ret;
        double  x1r, y1r, x2r, y2r;

234 235 236
        if( xf == xi )
        {
            // vertical first segment
237 238
            double  a   = xi;
            double  b   = DBL_MAX / 2.0;
239
            ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2,
240
                                               &x1r, &y1r, &x2r, &y2r );
241 242 243
        }
        else
        {
244 245
            double  b   = (double) (yf - yi) / (double) (xf - xi);
            double  a   = yf - b * xf;
246
            ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2,
247
                                               &x1r, &y1r, &x2r, &y2r );
248
        }
249

250 251
        if( ret == 0 )
            return 0;
252

253 254
        if( InRange( x1r, xi, xf ) && InRange( y1r, yi, yf ) )
        {
255 256
            xr[iret]    = x1r;
            yr[iret]    = y1r;
257 258
            iret++;
        }
259

260 261 262 263
        if( ret == 2 )
        {
            if( InRange( x2r, xi, xf ) && InRange( y2r, yi, yf ) )
            {
264 265
                xr[iret]    = x2r;
                yr[iret]    = y2r;
266 267 268 269 270 271 272
                iret++;
            }
        }
    }
    else if( style2 == CPolyLine::STRAIGHT )
    {
        // first segment is an arc, second segment is straight
273 274 275
        int     ret;
        double  x1r, y1r, x2r, y2r;

276 277 278
        if( xf2 == xi2 )
        {
            // vertical second segment
279 280
            double  a   = xi2;
            double  b   = DBL_MAX / 2.0;
281
            ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style,
282
                                               &x1r, &y1r, &x2r, &y2r );
283 284 285
        }
        else
        {
286 287
            double  b   = (double) (yf2 - yi2) / (double) (xf2 - xi2);
            double  a   = yf2 - b * xf2;
288
            ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style,
289
                                               &x1r, &y1r, &x2r, &y2r );
290
        }
291

292 293
        if( ret == 0 )
            return 0;
294

295 296
        if( InRange( x1r, xi2, xf2 ) && InRange( y1r, yi2, yf2 ) )
        {
297 298
            xr[iret]    = x1r;
            yr[iret]    = y1r;
299 300
            iret++;
        }
301

302 303 304 305
        if( ret == 2 )
        {
            if( InRange( x2r, xi2, xf2 ) && InRange( y2r, yi2, yf2 ) )
            {
306 307
                xr[iret]    = x2r;
                yr[iret]    = y2r;
308 309 310 311 312 313 314
                iret++;
            }
        }
    }
    else
    {
        // both segments are arcs
315 316
        EllipseKH   el1;
        EllipseKH   el2;
317 318
        MakeEllipseFromArc( xi, yi, xf, yf, style, &el1 );
        MakeEllipseFromArc( xi2, yi2, xf2, yf2, style2, &el2 );
319 320 321
        int         n;

        if( el1.xrad + el1.yrad > el2.xrad + el2.yrad )
322 323 324
            n = GetArcIntersections( &el1, &el2 );
        else
            n = GetArcIntersections( &el2, &el1 );
325

326 327
        iret = n;
    }
328

329 330
    if( x && y )
    {
331
        for( int i = 0; i<iret; i++ )
332
        {
333 334
            x[i]    = xr[i];
            y[i]    = yr[i];
335 336
        }
    }
337

338
    return iret;
339 340
}

341

342 343 344 345 346 347 348 349
// 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
// 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
//
350
int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style,
351 352
                                 double* x1, double* y1, double* x2, double* y2,
                                 double* dist )
353
{
354 355 356 357
    double  xx = 0, yy = 0; // Init made to avoid C compil "uninitialized" warning
    bool    bVert = false;

    if( b > DBL_MAX / 10.0 )
358
        bVert = true;
359

360 361 362 363 364 365 366
    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;
367 368 369
            double  d   = (double) (yf - yi) / (double) (xf - xi);
            double  c   = yf - d * xf;

370 371 372 373 374 375
            if( bVert )
            {
                // if vertical line, easy
                if( InRange( a, xi, xf ) )
                {
                    *x1 = a;
376
                    *y1 = c + d * a;
377 378 379 380 381
                    return 1;
                }
                else
                {
                    if( dist )
382 383
                        *dist = min( abs( a - xi ), abs( a - xf ) );

384 385 386
                    return 0;
                }
            }
387 388

            if( fabs( b - d ) < 1E-12 )
389 390 391 392 393 394
            {
                // parallel lines
                if( dist )
                {
                    *dist = GetPointToLineDistance( a, b, xi, xf );
                }
395 396

                return 0;    // lines parallel
397
            }
398

399
            // calculate intersection
400 401 402
            xx  = (c - a) / (b - d);
            yy  = a + b * (xx);

403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
            // see if intersection is within the line segment
            if( yf == yi )
            {
                // horizontal line
                if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf) )
                    return 0;
            }
            else
            {
                // oblique line
                if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf)
                    || (yy>yi && yy>yf) || (yy<yi && yy<yf) )
                    return 0;
            }
        }
        else if( style == CPolyLine::ARC_CW || style == CPolyLine::ARC_CCW )
        {
            // arc (quadrant of ellipse)
            // convert to clockwise arc
            int xxi, xxf, yyi, yyf;
423

424 425 426 427 428 429 430 431 432 433 434 435 436 437
            if( style == CPolyLine::ARC_CCW )
            {
                xxi = xf;
                xxf = xi;
                yyi = yf;
                yyf = yi;
            }
            else
            {
                xxi = xi;
                xxf = xf;
                yyi = yi;
                yyf = yf;
            }
438

439
            // find center and radii of ellipse
440 441
            double xo = xxf, yo = yyi, rx, ry;      // Init made to avoid C compil warnings

442 443
            if( xxf > xxi && yyf > yyi )
            {
444 445
                xo  = xxf;
                yo  = yyi;
446 447 448
            }
            else if( xxf < xxi && yyf > yyi )
            {
449 450
                xo  = xxi;
                yo  = yyf;
451 452 453
            }
            else if( xxf < xxi && yyf < yyi )
            {
454 455
                xo  = xxf;
                yo  = yyi;
456 457 458
            }
            else if( xxf > xxi && yyf < yyi )
            {
459 460
                xo  = xxi;
                yo  = yyf;
461
            }
462 463 464 465 466 467

            rx  = fabs( (double) (xxi - xxf) );
            ry  = fabs( (double) (yyi - yyf) );
            bool    test;
            double  xx1, xx2, yy1, yy2, aa;

468 469 470 471 472
            if( bVert )
            {
                // shift vertical line to coordinate system of ellipse
                aa = a - xo;
                test = FindVerticalLineEllipseIntersections( rx, ry, aa, &yy1, &yy2 );
473

474 475
                if( !test )
                    return 0;
476

477 478 479 480 481 482 483 484 485
                // shift back to PCB coordinates
                yy1 += yo;
                yy2 += yo;
                xx1 = a;
                xx2 = a;
            }
            else
            {
                // shift line to coordinate system of ellipse
486
                aa = a + b * xo - yo;
487
                test = FindLineEllipseIntersections( rx, ry, aa, b, &xx1, &xx2 );
488

489 490
                if( !test )
                    return 0;
491

492
                // shift back to PCB coordinates
493
                yy1 = aa + b * xx1;
494 495
                xx1 += xo;
                yy1 += yo;
496
                yy2 = aa + b * xx2;
497 498 499
                xx2 += xo;
                yy2 += yo;
            }
500

501
            int npts = 0;
502

503 504 505 506
            if( (xxf>xxi && xx1<xxf && xx1>xxi) || (xxf<xxi && xx1<xxi && xx1>xxf) )
            {
                if( (yyf>yyi && yy1<yyf && yy1>yyi) || (yyf<yyi && yy1<yyi && yy1>yyf) )
                {
507 508 509
                    *x1     = xx1;
                    *y1     = yy1;
                    npts    = 1;
510 511
                }
            }
512

513 514 515 516 517 518
            if( (xxf>xxi && xx2<xxf && xx2>xxi) || (xxf<xxi && xx2<xxi && xx2>xxf) )
            {
                if( (yyf>yyi && yy2<yyf && yy2>yyi) || (yyf<yyi && yy2<yyi && yy2>yyf) )
                {
                    if( npts == 0 )
                    {
519 520 521
                        *x1     = xx2;
                        *y1     = yy2;
                        npts    = 1;
522 523 524
                    }
                    else
                    {
525 526 527
                        *x2     = xx2;
                        *y2     = yy2;
                        npts    = 2;
528 529 530
                    }
                }
            }
531

532 533 534
            return npts;
        }
        else
535
            wxASSERT( 0 );
536 537 538 539 540 541
    }
    else
    {
        // vertical line segment
        if( bVert )
            return 0;
542 543 544 545

        xx  = xi;
        yy  = a + b * xx;

546 547 548
        if( (yy>=yi && yy>yf) || (yy<=yi && yy<yf) )
            return 0;
    }
549

550 551 552
    *x1 = xx;
    *y1 = yy;
    return 1;
553 554
}

555

556
/*
557
 * Function TestForIntersectionOfStraightLineSegments
558 559 560 561 562
 * Test for intersection of line segments
 * If lines are parallel, returns false
 * If true, returns also intersection coords in x, y
 * if false, returns min. distance in dist (may be 0.0 if parallel)
 */
563
bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
564 565
                                                int x2i, int y2i, int x2f, int y2f,
                                                int* x, int* y, double* d )
566
{
567
    double a, b, dist;
568

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
    // first, test for intersection
    if( x1i == x1f && x2i == x2f )
    {
        // both segments are vertical, can't intersect
    }
    else if( y1i == y1f && y2i == y2f )
    {
        // both segments are horizontal, can't intersect
    }
    else if( x1i == x1f && y2i == y2f )
    {
        // first seg. vertical, second horizontal, see if they cross
        if( InRange( x1i, x2i, x2f )
            && InRange( y2i, y1i, y1f ) )
        {
            if( x )
                *x = x1i;
586

587 588
            if( y )
                *y = y2i;
589

590 591
            if( d )
                *d = 0.0;
592

593 594 595 596 597 598 599 600 601 602 603
            return true;
        }
    }
    else if( y1i == y1f && x2i == x2f )
    {
        // first seg. horizontal, second vertical, see if they cross
        if( InRange( y1i, y2i, y2f )
            && InRange( x2i, x1i, x1f ) )
        {
            if( x )
                *x = x2i;
604

605 606
            if( y )
                *y = y1i;
607

608 609
            if( d )
                *d = 0.0;
610

611 612 613 614 615 616 617
            return true;
        }
    }
    else if( x1i == x1f )
    {
        // first segment vertical, second oblique
        // get a and b for second line segment, so that y = a + bx;
618 619 620 621 622 623 624
        b   = double( y2f - y2i ) / (x2f - x2i);
        a   = (double) y2i - b * x2i;

        double  x1, y1, x2, y2;
        int     test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f, CPolyLine::STRAIGHT,
                                                    &x1, &y1, &x2, &y2 );

625 626 627 628 629 630
        if( test )
        {
            if( InRange( y1, y1i, y1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
            {
                if( x )
                    *x = (int) x1;
631

632 633
                if( y )
                    *y = (int) y1;
634

635 636
                if( d )
                    *d = 0.0;
637

638 639 640 641 642 643 644 645
                return true;
            }
        }
    }
    else if( y1i == y1f )
    {
        // first segment horizontal, second oblique
        // get a and b for second line segment, so that y = a + bx;
646 647 648 649 650 651 652
        b   = double( y2f - y2i ) / (x2f - x2i);
        a   = (double) y2i - b * x2i;

        double  x1, y1, x2, y2;
        int     test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f, CPolyLine::STRAIGHT,
                                                    &x1, &y1, &x2, &y2 );

653 654 655 656 657 658
        if( test )
        {
            if( InRange( x1, x1i, x1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
            {
                if( x )
                    *x = (int) x1;
659

660 661
                if( y )
                    *y = (int) y1;
662

663 664
                if( d )
                    *d = 0.0;
665

666 667 668 669 670 671 672 673
                return true;
            }
        }
    }
    else if( x2i == x2f )
    {
        // second segment vertical, first oblique
        // get a and b for first line segment, so that y = a + bx;
674 675 676 677 678 679 680
        b   = double( y1f - y1i ) / (x1f - x1i);
        a   = (double) y1i - b * x1i;

        double  x1, y1, x2, y2;
        int     test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, CPolyLine::STRAIGHT,
                                                    &x1, &y1, &x2, &y2 );

681 682 683 684 685 686
        if( test )
        {
            if( InRange( x1, x1i, x1f ) &&  InRange( y1, y1i, y1f ) && InRange( y1, y2i, y2f ) )
            {
                if( x )
                    *x = (int) x1;
687

688 689
                if( y )
                    *y = (int) y1;
690

691 692
                if( d )
                    *d = 0.0;
693

694 695 696 697 698 699 700 701
                return true;
            }
        }
    }
    else if( y2i == y2f )
    {
        // second segment horizontal, first oblique
        // get a and b for second line segment, so that y = a + bx;
702 703 704 705 706 707 708
        b   = double( y1f - y1i ) / (x1f - x1i);
        a   = (double) y1i - b * x1i;

        double  x1, y1, x2, y2;
        int     test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f, CPolyLine::STRAIGHT,
                                                    &x1, &y1, &x2, &y2 );

709 710 711 712 713 714
        if( test )
        {
            if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
            {
                if( x )
                    *x = (int) x1;
715

716 717
                if( y )
                    *y = (int) y1;
718

719 720
                if( d )
                    *d = 0.0;
721

722 723 724 725 726 727 728
                return true;
            }
        }
    }
    else
    {
        // both segments oblique
729
        if( long( y1f - y1i ) * (x2f - x2i) != long( y2f - y2i ) * (x1f - x1i) )
730 731
        {
            // not parallel, get a and b for first line segment, so that y = a + bx;
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
            b   = double( y1f - y1i ) / (x1f - x1i);
            a   = (double) y1i - b * x1i;

            double  x1, y1, x2, y2;
            int     test = FindLineSegmentIntersection( a,
                                                        b,
                                                        x2i,
                                                        y2i,
                                                        x2f,
                                                        y2f,
                                                        CPolyLine::STRAIGHT,
                                                        &x1,
                                                        &y1,
                                                        &x2,
                                                        &y2 );

748 749 750 751 752 753 754
            // both segments oblique
            if( test )
            {
                if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
                {
                    if( x )
                        *x = (int) x1;
755

756 757
                    if( y )
                        *y = (int) y1;
758

759 760
                    if( d )
                        *d = 0.0;
761

762 763 764 765 766
                    return true;
                }
            }
        }
    }
767

768 769
    // don't intersect, get shortest distance between each endpoint and the other line segment
    dist = GetPointToLineSegmentDistance( x1i, y1i, x2i, y2i, x2f, y2f );
770 771 772 773 774

    double  xx  = x1i;
    double  yy  = y1i;
    double  dd  = GetPointToLineSegmentDistance( x1f, y1f, x2i, y2i, x2f, y2f );

775 776
    if( dd < dist )
    {
777 778 779
        dist    = dd;
        xx      = x1f;
        yy      = y1f;
780
    }
781

782
    dd = GetPointToLineSegmentDistance( x2i, y2i, x1i, y1i, x1f, y1f );
783

784 785
    if( dd < dist )
    {
786 787 788
        dist    = dd;
        xx      = x2i;
        yy      = y2i;
789
    }
790

791
    dd = GetPointToLineSegmentDistance( x2f, y2f, x1i, y1i, x1f, y1f );
792

793 794
    if( dd < dist )
    {
795 796 797
        dist    = dd;
        xx      = x2f;
        yy      = y2f;
798
    }
799

800 801
    if( x )
        *x = (int) xx;
802

803 804
    if( y )
        *y = (int) yy;
805

806 807
    if( d )
        *d = dist;
808

809
    return false;
810 811 812
}


813
/*solves the Quadratic equation = a*x*x + b*x + c
814 815
 */
bool Quadratic( double a, double b, double c, double* x1, double* x2 )
816
{
817 818
    double root = b * b - 4.0 * a * c;

819 820
    if( root < 0.0 )
        return false;
821 822 823 824

    root    = sqrt( root );
    *x1     = (-b + root) / (2.0 * a);
    *x2     = (-b - root) / (2.0 * a);
825
    return true;
826 827
}

828

829 830 831 832 833
// 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
//
834
bool FindVerticalLineEllipseIntersections( double a, double b, double x, double* y1, double* y2 )
835
{
836 837
    double y_sqr = ( 1.0 - (x * x) / (a * a) ) * b * b;

838 839
    if( y_sqr < 0.0 )
        return false;
840 841

    *y1 = sqrt( y_sqr );
842 843
    *y2 = -*y1;
    return true;
844 845
}

846

847 848 849 850 851
// 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
//
852
bool FindLineEllipseIntersections( double a, double b, double c, double d, double* x1, double* x2 )
853
{
854
    // quadratic terms
855 856 857 858
    double  A   = d * d + b * b / (a * a);
    double  B   = 2.0 * c * d;
    double  C   = c * c - b * b;

859
    return Quadratic( A, B, C, x1, x2 );
860 861 862 863 864 865 866 867
}


// Get clearance between 2 segments
// Returns point in segment closest to other segment in x, y
// in clearance > 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,
868 869
                                 int x2i, int y2i, int x2f, int y2f, int style2, int w2,
                                 int max_cl, int* x, int* y )
870
{
871
    // check clearance between bounding rectangles
872 873 874
    int test = max_cl + w1 / 2 + w2 / 2;

    if( min( x1i, x1f ) - max( x2i, x2f ) > test )
875
        return max_cl;
876 877

    if( min( x2i, x2f ) - max( x1i, x1f ) > test )
878
        return max_cl;
879 880

    if( min( y1i, y1f ) - max( y2i, y2f ) > test )
881
        return max_cl;
882 883

    if( min( y2i, y2f ) - max( y1i, y1f ) > test )
884
        return max_cl;
885

886 887 888
    if( style1 == CPolyLine::STRAIGHT && style1 == CPolyLine::STRAIGHT )
    {
        // both segments are straight lines
889 890
        int     xx, yy;
        double  dd;
891
        TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f,
892 893 894
                                                   x2i, y2i, x2f, y2f, &xx, &yy, &dd );
        int     d = max( 0, (int) dd - w1 / 2 - w2 / 2 );

895 896
        if( x )
            *x = xx;
897

898 899
        if( y )
            *y = yy;
900

901 902
        return d;
    }
903

904 905
    // not both straight-line segments
    // see if segments intersect
906 907 908 909 910
    double  xr[2];
    double  yr[2];
    test =
        FindSegmentIntersections( x1i, y1i, x1f, y1f, style1, x2i, y2i, x2f, y2f, style2, xr, yr );

911 912 913 914
    if( test )
    {
        if( x )
            *x = (int) xr[0];
915

916 917
        if( y )
            *y = (int) yr[0];
918

919 920
        return 0;
    }
921

922
    // at least one segment is an arc
923 924 925 926 927
    EllipseKH   el1;
    EllipseKH   el2;
    bool        bArcs;
    int         xi = 0, yi = 0, xf = 0, yf = 0;

928 929 930 931 932 933 934 935 936 937 938 939 940
    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
941 942 943 944
        xi  = x1i;
        yi  = y1i;
        xf  = x1f;
        yf  = y1f;
945 946 947 948 949 950 951 952 953 954
        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;
    }
955

956
    const int NSTEPS = 32;
957

958
    if( el1.theta2 > el1.theta1 )
959
    {
960
        wxASSERT( 0 );
961
    }
962

963
    if( bArcs && el2.theta2 > el2.theta1 )
964
    {
965
        wxASSERT( 0 );
966
    }
967

968
    // test multiple points in both segments
969 970 971 972
    double  th1;
    double  th2;
    double  len2;

973 974
    if( bArcs )
    {
975 976 977
        th1     = el2.theta1;
        th2     = el2.theta2;
        len2    = max( el2.xrad, el2.yrad );
978 979 980
    }
    else
    {
981 982 983
        th1     = 1.0;
        th2     = 0.0;
        len2    = abs( xf - xi ) + abs( yf - yi );
984
    }
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999

    double  s_start     = el1.theta1;
    double  s_end       = el1.theta2;
    double  s_start2    = th1;
    double  s_end2      = th2;
    double  dmin        = DBL_MAX;
    double  xmin        = 0, ymin = 0, smin = 0, smin2 = 0; // Init made to avoid C compil warnings

    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 )
1000
    {
1001 1002 1003
        step = (s_start - s_end) / (nsteps - 1);

        for( int i = 0; i<nsteps; i++ )
1004 1005
        {
            double s;
1006 1007 1008

            if( i < nsteps - 1 )
                s = s_start - i * step;
1009 1010
            else
                s = s_end;
1011 1012 1013 1014 1015

            double  x = el1.Center.X + el1.xrad * cos( s );

            double  y = el1.Center.Y + el1.yrad * sin( s );

1016
            // if not an arc, use s2 as fractional distance along line
1017 1018 1019
            step2 = (s_start2 - s_end2) / (nsteps2 - 1);

            for( int i2 = 0; i2<nsteps2; i2++ )
1020 1021
            {
                double s2;
1022 1023 1024

                if( i2 < nsteps2 - 1 )
                    s2 = s_start2 - i2 * step2;
1025 1026
                else
                    s2 = s_end2;
1027

1028
                double x2, y2;
1029

1030 1031
                if( !bArcs )
                {
1032 1033
                    x2  = xi + (xf - xi) * s2;
                    y2  = yi + (yf - yi) * s2;
1034 1035 1036
                }
                else
                {
1037 1038 1039
                    x2 = el2.Center.X + el2.xrad*   cos( s2 );

                    y2 = el2.Center.Y + el2.yrad*   sin( s2 );
1040
                }
1041

1042
                double d = Distance( x, y, x2, y2 );
1043

1044 1045
                if( d < dmin )
                {
1046 1047 1048 1049 1050
                    dmin    = d;
                    xmin    = x;
                    ymin    = y;
                    smin    = s;
                    smin2   = s2;
1051 1052 1053
                }
            }
        }
1054

1055 1056
        if( step > step2 )
        {
1057 1058 1059
            s_start = min( el1.theta1, smin + step );
            s_end   = max( el1.theta2, smin - step );
            step    = (s_start - s_end) / nsteps;
1060 1061 1062
        }
        else
        {
1063 1064 1065
            s_start2    = min( th1, smin2 + step2 );
            s_end2      = max( th2, smin2 - step2 );
            step2       = (s_start2 - s_end2) / nsteps2;
1066 1067
        }
    }
1068

1069 1070
    if( x )
        *x = (int) xmin;
1071

1072 1073
    if( y )
        *y = (int) ymin;
1074 1075

    return max( 0, (int) dmin - w1 / 2 - w2 / 2 );    // allow for widths
1076 1077 1078 1079 1080 1081 1082
}


// 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 xp, yp
//
1083
double GetPointToLineDistance( double a, double b, int x, int y, double* xpp, double* ypp )
1084
{
1085
    if( b > DBL_MAX / 10 )
1086 1087 1088 1089
    {
        // vertical line
        if( xpp && ypp )
        {
1090 1091
            *xpp    = a;
            *ypp    = y;
1092
        }
1093 1094

        return abs( a - x );
1095
    }
1096

1097
    // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
1098 1099 1100
    double  d   = -1.0 / b;
    double  c   = (double) y - d * x;

1101
    // find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
1102 1103 1104
    double  xp  = (a - c) / (d - b);
    double  yp  = a + b * xp;

1105 1106
    if( xpp && ypp )
    {
1107 1108
        *xpp    = xp;
        *ypp    = yp;
1109
    }
1110

1111
    // find distance
1112
    return Distance( x, y, xp, yp );
1113 1114
}

1115

1116 1117 1118
/***********************************************************************************/
double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf )
/***********************************************************************************/
1119 1120
/**
 * Function GetPointToLineSegmentDistance
1121 1122
 * Get distance between line segment and point
 * @param x,y = point
1123 1124
 * @param xi,yi Start point of the line segament
 * @param xf,yf End point of the line segment
1125 1126 1127
 * @return the distance
 */
{
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
    // 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
1149 1150 1151
        double  b   = (double) (yf - yi) / (xf - xi);
        double  a   = (double) yi - b * xi;

1152
        // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
1153 1154 1155
        double  d   = -1.0 / b;
        double  c   = (double) y - d * x;

1156
        // find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
1157 1158 1159
        double  xp  = (a - c) / (d - b);
        double  yp  = a + b * xp;

1160 1161
        // find distance
        if( InRange( xp, xi, xf ) && InRange( yp, yi, yf ) )
1162
            return Distance( x, y, xp, yp );
1163 1164 1165
        else
            return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
    }
1166 1167
}

1168

1169 1170 1171 1172
// test for value within range
//
bool InRange( double x, double xi, double xf )
{
1173
    if( xf > xi )
1174 1175 1176 1177 1178 1179 1180 1181 1182
    {
        if( x >= xi && x <= xf )
            return true;
    }
    else
    {
        if( x >= xf && x <= xi )
            return true;
    }
1183

1184
    return false;
1185 1186
}

1187

1188 1189 1190
// this finds approximate solutions
// note: this works best if el2 is smaller than el1
//
1191 1192
int GetArcIntersections( EllipseKH* el1, EllipseKH* el2,
                         double* x1, double* y1, double* x2, double* y2 )
1193
{
1194
    if( el1->theta2 > el1->theta1 )
1195
    {
1196
        wxASSERT( 0 );
1197
    }
1198

1199
    if( el2->theta2 > el2->theta1 )
1200
    {
1201
        wxASSERT( 0 );
1202
    }
1203

1204 1205 1206 1207 1208
    const int   NSTEPS = 32;
    double      xret[2], yret[2];

    double      xscale  = 1.0 / el1->xrad;
    double      yscale  = 1.0 / el1->yrad;
1209

1210 1211 1212
    // 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
1213 1214 1215 1216 1217
    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;

1218
    // now test NSTEPS positions in arc, moving clockwise (ie. decreasing theta)
1219 1220 1221 1222 1223 1224 1225 1226
    double  step    = M_PI / ( (NSTEPS - 1) * 2.0 );
    double  d_prev  = 0;
    double  th_interp;
    double  th1;

    int     n = 0;

    for( int i = 0; i<NSTEPS; i++ )
1227 1228
    {
        double theta;
1229 1230 1231

        if( i < NSTEPS - 1 )
            theta = el2->theta1 - i * step;
1232 1233
        else
            theta = el2->theta2;
1234 1235 1236 1237 1238 1239 1240

        double  x = xo + xr * cos( theta );

        double  y = yo + yr * sin( theta );

        double  d = 1.0 - sqrt( x * x + y * y );

1241 1242 1243
        if( i>0 )
        {
            bool bInt = false;
1244

1245 1246
            if( d >= 0.0 && d_prev <= 0.0 )
            {
1247
                th_interp = theta + ( step * (-d_prev) ) / (d - d_prev);
1248 1249 1250 1251
                bInt = true;
            }
            else if( d <= 0.0 && d_prev >= 0.0 )
            {
1252
                th_interp = theta + (step * d_prev) / (d_prev - d);
1253 1254
                bInt = true;
            }
1255

1256 1257
            if( bInt )
            {
1258 1259 1260 1261
                x = xo + xr * cos( th_interp );

                y = yo + yr * sin( th_interp );

1262
                th1 = atan2( y, x );
1263

1264 1265
                if( th1 <= el1->theta1 && th1 >= el1->theta2 )
                {
1266 1267
                    xret[n] = x * el1->xrad + el1->Center.X;
                    yret[n] = y * el1->yrad + el1->Center.Y;
1268
                    n++;
1269

1270
                    if( n > 2 )
1271
                    {
1272
                        wxASSERT( 0 );
1273
                    }
1274 1275 1276
                }
            }
        }
1277

1278 1279
        d_prev = d;
    }
1280

1281 1282
    if( x1 )
        *x1 = xret[0];
1283

1284 1285
    if( y1 )
        *y1 = yret[0];
1286

1287 1288
    if( x2 )
        *x2 = xret[1];
1289

1290 1291
    if( y2 )
        *y2 = yret[1];
1292

1293
    return n;
1294 1295
}

1296

1297 1298
// this finds approximate solution
//
1299 1300 1301
// double GetSegmentClearance( EllipseKH * el1, EllipseKH * el2,
double GetArcClearance( EllipseKH* el1, EllipseKH* el2,
                        double* x1, double* y1 )
1302
{
1303
    const int NSTEPS = 32;
1304

1305
    if( el1->theta2 > el1->theta1 )
1306
    {
1307
        wxASSERT( 0 );
1308
    }
1309

1310
    if( el2->theta2 > el2->theta1 )
1311
    {
1312
        wxASSERT( 0 );
1313
    }
1314

1315
    // test multiple positions in both arcs, moving clockwise (ie. decreasing theta)
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
    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    = 0, ymin = 0, thmin = 0, thmin2 = 0;

    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 )
1330
    {
1331 1332 1333
        step = (th_start - th_end) / (nsteps - 1);

        for( int i = 0; i<nsteps; i++ )
1334 1335
        {
            double theta;
1336 1337 1338

            if( i < nsteps - 1 )
                theta = th_start - i * step;
1339 1340
            else
                theta = th_end;
1341 1342 1343 1344 1345 1346 1347 1348

            double  x = el1->Center.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; i2<nsteps2; i2++ )
1349 1350
            {
                double theta2;
1351 1352 1353

                if( i2 < nsteps2 - 1 )
                    theta2 = th_start2 - i2 * step2;
1354 1355
                else
                    theta2 = th_end2;
1356 1357 1358

                double  x2 = el2->Center.X + el2->xrad * cos( theta2 );
                double  y2 = el2->Center.Y + el2->yrad * sin( theta2 );
1359
                double  d = Distance( x,  y, x2, y2 );
1360

1361 1362
                if( d < dmin )
                {
1363 1364 1365 1366 1367
                    dmin    = d;
                    xmin    = x;
                    ymin    = y;
                    thmin   = theta;
                    thmin2  = theta2;
1368 1369 1370
                }
            }
        }
1371

1372 1373
        if( step > step2 )
        {
1374 1375 1376
            th_start    = min( el1->theta1, thmin + step );
            th_end      = max( el1->theta2, thmin - step );
            step        = (th_start - th_end) / nsteps;
1377 1378 1379
        }
        else
        {
1380 1381 1382
            th_start2   = min( el2->theta1, thmin2 + step2 );
            th_end2     = max( el2->theta2, thmin2 - step2 );
            step2       = (th_start2 - th_end2) / nsteps2;
1383 1384
        }
    }
1385

1386 1387
    if( x1 )
        *x1 = xmin;
1388

1389 1390
    if( y1 )
        *y1 = ymin;
1391

1392
    return dmin;
1393
}