Annotation of researchv9/X11/src/X.V11R1/server/ddx/mi/milines.c, revision 1.1.1.1

1.1       root        1: /***********************************************************
                      2: Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
                      3: and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
                      4: 
                      5:                    All Rights Reserved
                      6: 
                      7: Permission to use, copy, modify, and distribute this software and its 
                      8: documentation for any purpose and without fee is hereby granted, 
                      9: provided that the above copyright notice appear in all copies and that
                     10: both that copyright notice and this permission notice appear in 
                     11: supporting documentation, and that the names of Digital or MIT not be
                     12: used in advertising or publicity pertaining to distribution of the
                     13: software without specific, written prior permission.  
                     14: 
                     15: DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
                     16: ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
                     17: DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
                     18: ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
                     19: WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
                     20: ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
                     21: SOFTWARE.
                     22: 
                     23: ******************************************************************/
                     24: /* $Header: milines.c,v 1.45 87/09/11 07:20:09 toddb Exp $ */
                     25: 
                     26: /*
                     27:  *
                     28:  *  Written by Todd Newman 
                     29:  *  loosely based on long ago code and recent advice
                     30:  *  from Brian Kelleher, John Danskin
                     31:  *
                     32:  *  Draw fat lines using a convex polygon routine.
                     33:  */
                     34: 
                     35: #include <stdio.h>
                     36: extern double hypot();
                     37: #include "X.h"
                     38: #include "windowstr.h"
                     39: #include "Xprotostr.h"
                     40: #include "gcstruct.h"
                     41: #include "scrnintstr.h"
                     42: #include "miscstruct.h"
                     43: #include "pixmapstr.h"
                     44: #include "mifpoly.h"
                     45: #include "mi.h"
                     46: 
                     47: #define GCVALSALU      0
                     48: #define GCVALSFORE     1
                     49: #define GCVALSBACK     2
                     50: #define GCVALSWIDTH    3
                     51: #define GCVALSCAPSTYLE 4
                     52: #define GCVALSJOINSTYLE        5
                     53: #define GCVALSARCMODE  6
                     54: static int gcvals[] = {GXcopy, 1, 0, 0, 0, 0, ArcChord};
                     55: 
                     56: #define SAMESIGN(a, b)  ((((a) >= 0) && ((b) >= 0)) ||\
                     57:                     (((a) <= 0) && ((b) <= 0)))
                     58: 
                     59: SppPointRec IntersectLines();
                     60: 
                     61: /* MIWIDELINE - Public entry for PolyLine call
                     62:  * handles 1 segment wide lines specially.  Then it sets up the GC/Drawable
                     63:  * based on the raster op.  If the raster op is one that doesn't look at
                     64:  * the destination, then we can draw directly onto the drawable.  If the
                     65:  * raster op does look at the destination (e.g., Xor), then we will draw
                     66:  * with GXcopy onto a side bitmap and then squeegee the appropriate pattern
                     67:  * (foreground, or tile, or stipple) onto the real drawable.
                     68:  * We call one of two helpers (miMiter or miNonMiter) to actually draw
                     69:  * the line segments. Which helper we call is determined by the device
                     70:  * specific GC validator (e.g., mfbValidateGC).
                     71:  */
                     72: void
                     73: miWideLine(pDrawable, pGC, mode, npt, pPts)
                     74:     DrawablePtr pDrawable;
                     75:     GCPtr      pGC;
                     76:     int                mode;
                     77:     int                npt;
                     78:     DDXPointPtr pPts;
                     79: {
                     80:     int                width = pGC->lineWidth;
                     81:     SppPointPtr pSppPts, pSppT;
                     82:     int                gcflags, i, xOrg, yOrg, fTricky, arcmode;
                     83:     int                xMin, xMax, yMin, yMax, dx, dy;
                     84:     DrawablePtr        pDrawTo;
                     85:     GCPtr      pGCTo;
                     86: 
                     87: 
                     88:     /* make everything absolute */
                     89:     if (mode == CoordModePrevious)
                     90:     {
                     91:        DDXPointPtr pptTmp;
                     92:        int nptTmp;
                     93: 
                     94:        pptTmp = pPts + 1;
                     95:        nptTmp = npt - 1;
                     96:        while (nptTmp--)
                     97:        {
                     98:            pptTmp->x += (pptTmp-1)->x;
                     99:            pptTmp->y += (pptTmp-1)->y;
                    100:            pptTmp++;
                    101:        }
                    102:     }
                    103:     switch(pGC->alu)
                    104:     {
                    105:       case GXclear:            /* 0 */
                    106:       case GXcopy:             /* src */
                    107:       case GXcopyInverted:     /* NOT src */
                    108:       case GXset:              /* 1 */
                    109:        fTricky = FALSE;
                    110:         xOrg = yOrg = 0;
                    111:        pDrawTo = pDrawable;
                    112:        pGCTo = pGC;
                    113:        arcmode = pGCTo->arcMode;
                    114:        if(arcmode != ArcChord)
                    115:        {
                    116:            DoChangeGC(pGCTo, GCArcMode, &gcvals[GCVALSARCMODE], 0);
                    117:            ValidateGC(pDrawTo, pGCTo);
                    118:        }
                    119: 
                    120:        break;
                    121:       case GXand:              /* src AND dst */
                    122:       case GXandReverse:       /* src AND NOT dst */
                    123:       case GXandInverted:      /* NOT src AND dst */
                    124:       case GXnoop:             /* dst */
                    125:       case GXxor:              /* src XOR dst */
                    126:       case GXor        :               /* src OR dst */
                    127:       case GXnor:              /* NOT src AND NOT dst */
                    128:       case GXequiv:            /* NOT src XOR dst */
                    129:       case GXinvert:           /* NOT dst */
                    130:       case GXorReverse:                /* src OR NOT dst */
                    131:       case GXorInverted:       /* NOT src OR dst */
                    132:       case GXnand:             /* NOT src OR NOT dst */
                    133:        fTricky = TRUE;
                    134:        yMin = yMax = pPts[0].y;
                    135:        xMin = xMax = pPts[0].x;
                    136: 
                    137:        for (i = 1; i < npt; i++)
                    138:        {
                    139:            xMin = min(xMin, pPts[i].x);
                    140:            xMax = max(xMax, pPts[i].x);
                    141:            yMin = min(yMin, pPts[i].y);
                    142:            yMax = max(yMax, pPts[i].y);
                    143:        }
                    144:        xOrg = xMin - (width + 1)/2;
                    145:        yOrg = yMin - (width + 1)/2;
                    146:        dx = xMax - xMin + width + 1;
                    147:        dy = yMax - yMin + width + 1;
                    148:        pDrawTo = (DrawablePtr) (*pDrawable->pScreen->CreatePixmap)
                    149:          (pDrawable->pScreen, dx, dy, 1, XYBitmap);
                    150:        pGCTo = GetScratchGC(1, pDrawable->pScreen);
                    151:        gcvals[GCVALSWIDTH] = width;
                    152:        gcvals[GCVALSCAPSTYLE] = pGC->capStyle;
                    153:        gcvals[GCVALSJOINSTYLE] = pGC->joinStyle;
                    154:        gcflags = GCFunction|GCForeground|GCBackground|GCLineWidth |
                    155:                  GCCapStyle | GCJoinStyle;
                    156:        if(pGCTo->arcMode != ArcChord)
                    157:            gcflags |= GCArcMode;
                    158:        arcmode = ArcChord;
                    159:        DoChangeGC(pGCTo, gcflags, gcvals, 0);
                    160:        ValidateGC(pDrawTo, pGCTo);
                    161:        miClearDrawable(pDrawTo, pGCTo);
                    162: 
                    163:     }
                    164:     pSppPts = (SppPointPtr) Xalloc((npt + 1)  * sizeof(SppPointRec));
                    165:     pSppT = pSppPts;
                    166:     /* convert points to DblPts */
                    167:     for(i = 0; i < npt; i++)
                    168:     {
                    169:        pSppT->x = (double) pPts->x;
                    170:        pSppT++->y = (double) pPts++->y;
                    171:        
                    172:     }
                    173: 
                    174:     /* Draw the lines with the appropriate join style.
                    175:      * We always wants caps drawn. */
                    176:     (*pGCTo->LineHelper) (pDrawTo, pGCTo, TRUE, npt, pSppPts, xOrg, yOrg);
                    177: 
                    178:     if(arcmode != ArcChord)
                    179:     {
                    180:        DoChangeGC(pGCTo, GCArcMode, &arcmode, 0);
                    181:        ValidateGC(pDrawTo, pGCTo);
                    182:     }
                    183:     if(fTricky)
                    184:     {
                    185:        if (pGC->miTranslate && (pDrawable->type == DRAWABLE_WINDOW) )
                    186:        {
                    187:            xOrg += ((WindowPtr)pDrawable)->absCorner.x;
                    188:            yOrg += ((WindowPtr)pDrawable)->absCorner.y;
                    189:        }
                    190: 
                    191:        (*pGC->PushPixels)(pGC, pDrawTo, pDrawable, dx, dy, xOrg, yOrg);
                    192:        (*pDrawable->pScreen->DestroyPixmap)(pDrawTo);
                    193:        FreeScratchGC(pGCTo);
                    194:     }
                    195: }
                    196: 
                    197: /* MIONESEGWIDE -- private helper for wide line code
                    198:  * Draw wide line which only has 1 segment.  This is so easy that it's worth
                    199:  * special casing. Regardless of the raster op, this can't self-intersect.
                    200:  * Also handles fluke cases where we're called with only 1 point 
                    201:  */
                    202: static
                    203: void
                    204: miOneSegWide(pDrawable, pGC, npt, pPts, caps, xOrg, yOrg)
                    205:     DrawablePtr pDrawable;
                    206:     GCPtr      pGC;
                    207:     int                npt;
                    208:     SppPointPtr pPts;
                    209:     int                caps, xOrg, yOrg;
                    210: {
                    211:     SppPointRec        PolyPoints[4];
                    212:     int                width = pGC->lineWidth;
                    213: 
                    214:     if(npt == 1)
                    215:     {
                    216:        Xrealloc(pPts, 2 * sizeof(SppPointRec));
                    217:         pPts[1] = pPts[0];
                    218:     }
                    219:     if(caps && pGC->capStyle == CapProjecting)
                    220:     {
                    221:        if(PtEqual(pPts[0], pPts[1]))
                    222:        {
                    223:            pPts[0].y -= width/2.0;
                    224:            pPts[1].y += width/2.0;
                    225:        }
                    226:        else
                    227:        {
                    228:            pPts[0] = miExtendSegment(pPts[0], pPts[1], width/2);
                    229:            pPts[1] = miExtendSegment(pPts[1], pPts[0], width/2);
                    230:        }
                    231:     }
                    232:     miGetPts(pPts[0], pPts[1], &PolyPoints[0], 
                    233:           &PolyPoints[1], &PolyPoints[2], &PolyPoints[3], width);
                    234:     miFillSppPoly(pDrawable, pGC, 4, PolyPoints, -xOrg, -yOrg);
                    235:     if(caps && pGC->capStyle == CapRound)
                    236:     {
                    237:        miRoundCap(pDrawable, pGC, pPts[0], pPts[1], PolyPoints[0],
                    238:          PolyPoints[3], FirstEnd, xOrg, yOrg);
                    239:        miRoundCap(pDrawable, pGC, pPts[1], pPts[0], PolyPoints[2],
                    240:          PolyPoints[1], SecondEnd, xOrg, yOrg);
                    241:     }
                    242: }
                    243: 
                    244: /* MIMITER -- helper for wide line code, stuffed into GC.lineHelper by
                    245:  * validation routine
                    246:  * draw wide polylines by finding the polygon that outlines
                    247:  * each wide line segment, and drawing that. Uses GC's fill polygon routine.
                    248:  *
                    249:  * internal vertexes are joined with a mitre as in PostScript. 
                    250:  * vertices at the beginning and end of the polyline
                    251:  * are given the appropriate end unless they meet at the same point in which
                    252:  * case they are mitred together.  */
                    253: 
                    254: void
                    255: miMiter (pDraw, pGC, caps, npt, pPts, xOrg, yOrg)
                    256:     DrawablePtr pDraw;
                    257:     GCPtr      pGC;
                    258:     int                caps;
                    259:     int                npt;
                    260:     SppPointPtr pPts;
                    261:     int                xOrg, yOrg;
                    262: {
                    263:     int                width = pGC->lineWidth;
                    264:     SppPointRec        PolyPoints[4], FirstEdge[2];
                    265:     SppPointRec        p1, p2, p3, p4, p5, p6, p7, p8;
                    266:     int                edges_match, i,
                    267:                capStyle = pGC->capStyle;
                    268: 
                    269:     /*
                    270:      * special case length 1 polyline
                    271:      */
                    272:     if(npt <= 2)
                    273:     {
                    274:         miOneSegWide(pDraw, pGC, npt, pPts, caps, xOrg, yOrg);
                    275:        return;
                    276:     }
                    277:     miGetPts(pPts[0], pPts[1], &p1, &p2, &p3, &p4, width);
                    278: 
                    279:     /*
                    280:      * start on first edge of first point
                    281:      */
                    282:     if (PtEqual(pPts[0], pPts[npt-1]))
                    283:     {
                    284:        /*
                    285:         * edges match so mitre them
                    286:         */
                    287:        edges_match = 1;
                    288:        miGetPts(pPts[npt-2], pPts[npt-1], &p5, &p6, &p7, &p8, width);
                    289: 
                    290:        PolyPoints[3] = IntersectLines(p1, p2, p5, p6);
                    291:        PolyPoints[2] = IntersectLines(p3, p4, p7, p8);
                    292: 
                    293:        FirstEdge[0] = PolyPoints[3];
                    294:        FirstEdge[1] = PolyPoints[2];
                    295:     }
                    296:     else
                    297:     {
                    298:        edges_match = 0;
                    299:        PolyPoints[3] = p1;
                    300:        PolyPoints[2] = p4;
                    301:        if (caps && capStyle == CapProjecting)
                    302:        {
                    303:            pPts[0] = miExtendSegment(pPts[0], pPts[1], width/2);
                    304:            miGetPts(pPts[0], pPts[1], &p1, &p2, &p3, &p4, width);
                    305:            pPts[npt-1] = miExtendSegment(pPts[npt-1], pPts[npt-2], width/2);
                    306:        }
                    307:        else if (caps && capStyle == CapRound)
                    308:        {
                    309:            miRoundCap(pDraw, pGC, pPts[0], pPts[1], p1, p4, FirstEnd,
                    310:                     xOrg, yOrg);
                    311:            miGetPts(pPts[npt-1], pPts[npt-2], &p5, &p6, &p7, &p8, width);
                    312:            miRoundCap(pDraw, pGC, pPts[npt-1], pPts[npt-2], p5, p8, FirstEnd,
                    313:                     xOrg, yOrg);
                    314:        }
                    315:     }
                    316: 
                    317:     for (i = 1; i < (npt - 1); i++)
                    318:     {
                    319:        /*
                    320:         * find the next edge
                    321:         * build a polygon out of the next edge and the last one
                    322:         * send the polygon
                    323:         * bump to next
                    324:         */
                    325:        miGetPts(pPts[i], pPts[i+1], &p5, &p6, &p7, &p8, width);
                    326: 
                    327:        PolyPoints[0] = PolyPoints[3];
                    328:        PolyPoints[1] = PolyPoints[2];
                    329:        PolyPoints[2] = IntersectLines(p4, p3, p7, p8);
                    330:        PolyPoints[3] = IntersectLines(p1, p2, p5, p6);
                    331: 
                    332:        miFillSppPoly(pDraw, pGC, 4, PolyPoints, -xOrg, -yOrg);
                    333: 
                    334:        p1 = p5;  p2 = p6;  p3 = p7;  p4 = p8;
                    335: 
                    336:     }
                    337: 
                    338:     PolyPoints[0] = PolyPoints[3];
                    339:     PolyPoints[1] = PolyPoints[2];
                    340: 
                    341:     if (edges_match)
                    342:     {
                    343:        PolyPoints[2] = FirstEdge[1];
                    344:        PolyPoints[3] = FirstEdge[0];
                    345:     }
                    346:     else
                    347:     {
                    348:        miGetPts(pPts[npt-2], pPts[npt-1], &p5, &p6, &p7, &p8, width);
                    349:        PolyPoints[2] = p7;
                    350:        PolyPoints[3] = p6;
                    351:     }
                    352:        
                    353:     miFillSppPoly(pDraw, pGC, 4, PolyPoints, -xOrg, -yOrg);
                    354: }
                    355: 
                    356: /* MINOTMITER -- helper for wide line code, stuffed into GC.lineHelper by
                    357:  * validation routine
                    358:  *
                    359:  * internal vertexes are joined with a bevel or rounded joint as in PostScript. 
                    360:  * vertices at the beginning and end of the polyline
                    361:  * are given the appropriate end unless they meet at the same point in which
                    362:  * case they are drawn with the appropriate join.  */
                    363: 
                    364: typedef struct {
                    365:     int                c;
                    366:     SppPointRec        v0, v1, v2, v3, v4;
                    367:     SppPointRec        p1, p2, p3, p4;
                    368: } POLYLINEINFO;
                    369: 
                    370: void
                    371: miNotMiter (pDraw, pGC, caps, npt, pPts, xOrg, yOrg)
                    372:     DrawablePtr pDraw;
                    373:     GCPtr      pGC;
                    374:     int                caps;
                    375:     int                npt;
                    376:     SppPointPtr pPts;
                    377:     int                xOrg, yOrg;
                    378: {
                    379:     int                i, c,
                    380:                width = pGC->lineWidth,
                    381:                capStyle = pGC->capStyle,
                    382:                joinStyle = pGC->joinStyle;
                    383:     Bool       edges_match;
                    384:     SppPointRec        FirstEdge[4],   /* 2 extra for special npt == 2 */
                    385:                wedge[3],
                    386:                p, p1, p2, p3, p4, p5, p6, p7, p8, center;
                    387:     POLYLINEINFO *plinfo;
                    388: 
                    389:     /* special case length 1 polyline */
                    390:     if(npt <= 2)
                    391:     {
                    392:         miOneSegWide(pDraw, pGC, npt, pPts, caps, xOrg, yOrg);
                    393:        return;
                    394:     }
                    395:     if(!(plinfo = (POLYLINEINFO *) ALLOCATE_LOCAL(npt * sizeof(POLYLINEINFO))))
                    396:        return;
                    397:     miGetPts(pPts[0], pPts[1], &p1, &p2, &p3, &p4, width);
                    398:     /* save the points, in case this is hard */
                    399:     plinfo[0].p1 = p1;
                    400:     plinfo[0].p2 = p2;
                    401:     plinfo[0].p3 = p3;
                    402:     plinfo[0].p4 = p4;
                    403: 
                    404:     /*
                    405:      * If ends meet, calculate angle of final intersection and points
                    406:      * of intersection for it.
                    407:      */
                    408:     if(PtEqual(pPts[0], pPts[npt-1]))
                    409:     {
                    410:        edges_match = TRUE;
                    411:        miGetPts(pPts[npt-2], pPts[npt-1], &p5, &p6, &p7, &p8, width);
                    412: 
                    413:        c = (pPts[0].x - pPts[npt-2].x)*(pPts[1].y - pPts[0].y) -
                    414:            (pPts[1].x - pPts[0].x)*(pPts[0].y - pPts[npt-2].y);
                    415:        plinfo[0].c = c;
                    416: 
                    417:        if(c > 0)       /* it's a clockwise turn */
                    418:        {
                    419:            p = IntersectLines(p1, p2, p5, p6);
                    420:            plinfo[0].v0 = p4;
                    421:            plinfo[0].v1 = p7;
                    422:            plinfo[0].v2 = p;
                    423:            FirstEdge[0] = p;
                    424:            FirstEdge[1] = p7;
                    425:            if(joinStyle == JoinRound)
                    426:            {
                    427:                center.x = (p4.x + p7.x)/2;
                    428:                center.y = (p4.y + p7.y)/2;
                    429:                miRoundCap(pDraw, pGC, center, p, p4, p7, NotEnd, xOrg, yOrg);
                    430:            }
                    431:        }
                    432:        else if (c < 0) /* counterclockwise */
                    433:        {
                    434:            p = IntersectLines(p3, p4, p7, p8);
                    435:            plinfo[0].v0 = p;
                    436:            plinfo[0].v1 = p6;
                    437:            plinfo[0].v2 = p1;
                    438:            FirstEdge[0] = p6;
                    439:            FirstEdge[1] = p;
                    440:            if(joinStyle == JoinRound)
                    441:            {
                    442:                center.x = (p6.x + p1.x)/2;
                    443:                center.y = (p6.y + p1.y)/2;
                    444:                miRoundCap(pDraw, pGC, center, p, p1, p6, NotEnd, xOrg, yOrg);
                    445:            }
                    446:        }
                    447:        else /* straight line intersection */
                    448:        {
                    449:            plinfo[0].v1 = p4;
                    450:            plinfo[0].v2 = p1;
                    451:            FirstEdge[0] = p6;
                    452:            FirstEdge[1] = p7;
                    453: 
                    454:        }
                    455:     }
                    456:     else       /* Ends don't meet */
                    457:     {
                    458:        if(caps)
                    459:        {
                    460:            if(capStyle == CapProjecting)
                    461:            {
                    462:                pPts[0] = miExtendSegment(pPts[0], pPts[1], width/2);
                    463:                miGetPts(pPts[0], pPts[1], &p1, &p2, &p3, &p4, width);
                    464:                plinfo[0].p1 = p1;
                    465:                plinfo[0].p2 = p2;
                    466:                plinfo[0].p3 = p3;
                    467:                plinfo[0].p4 = p4;
                    468:                pPts[npt-1] =
                    469:                    miExtendSegment(pPts[npt-1], pPts[npt-2], width/2);
                    470:            } else if (capStyle == CapRound)
                    471:            {
                    472:                miRoundCap(pDraw, pGC, pPts[0], pPts[1], p1, p4, FirstEnd,
                    473:                         xOrg, yOrg);
                    474:                miGetPts(pPts[npt-1], pPts[npt-2], &p5, &p6, &p7, &p8, width);
                    475:                miRoundCap(pDraw, pGC, pPts[npt-1], pPts[npt-2], p5, p8,
                    476:                         FirstEnd, xOrg, yOrg);
                    477:            }
                    478:        }
                    479:        edges_match = FALSE;
                    480:        plinfo[0].c = 0;        /* treat no connection as straight connection */
                    481:        plinfo[0].v1 = p4;
                    482:        plinfo[0].v2 = p1;
                    483: 
                    484:     }
                    485: 
                    486:     for (i = 1; i < (npt - 1); i++)
                    487:     {
                    488:        /* Find the point of intersection with the previous segment.
                    489:         * Make sure it's on this segment */
                    490:        if(plinfo[i-1].c > 0)
                    491:        {
                    492:            p = plinfo[i-1].v2;
                    493:        }
                    494:        else if (plinfo[i-1].c < 0)
                    495:        {
                    496:            p = plinfo[i-1].v0;
                    497:        }
                    498:        miGetPts(pPts[i], pPts[i+1], &p5, &p6, &p7, &p8, width);
                    499:        
                    500:        plinfo[i].p1 = p5;
                    501:        plinfo[i].p2 = p6;
                    502:        plinfo[i].p3 = p7;
                    503:        plinfo[i].p4 = p8;
                    504: 
                    505:        /* Figure out whether this angle turns clockwise or not by taking
                    506:         * the total cross product of the three vectors formed by drawing
                    507:         * vectors between the start of the previous segment and the start
                    508:         * and end of this one */
                    509:        if(i == npt - 1)
                    510:            c = 0;
                    511:        else
                    512:            c = (pPts[i].x - pPts[i-1].x)*(pPts[i + 1].y - pPts[i].y) -
                    513:                 (pPts[i + 1].x - pPts[i].x)*(pPts[i].y - pPts[i-1].y);
                    514:        plinfo[i].c = c;
                    515: 
                    516: 
                    517:        if(c > 0)       /* counter-clockwise */         
                    518:        {
                    519:            p = IntersectLines(p1, p2, p5, p6);
                    520:            plinfo[i].v0 = p8;
                    521:            plinfo[i].v1 = p3;
                    522:            plinfo[i].v2 = p;
                    523: 
                    524:            plinfo[i-1].v3 = p;
                    525:            plinfo[i-1].v4 = p3;
                    526:            if(joinStyle == JoinRound)
                    527:            {
                    528:                center.x = (p8.x + p3.x)/2;
                    529:                center.y = (p8.y + p3.y)/2;
                    530:                miRoundCap(pDraw, pGC, center, p, p8, p3, NotEnd, xOrg, yOrg);
                    531:            }
                    532:        }
                    533:        else if (c < 0) 
                    534:        {
                    535:            p = IntersectLines(p3, p4, p7, p8);
                    536:            plinfo[i].v0 = p;
                    537:            plinfo[i].v1 = p2;
                    538:            plinfo[i].v2 = p5;
                    539: 
                    540:            plinfo[i-1].v3 = p2;
                    541:            plinfo[i-1].v4 = p;
                    542:            if(joinStyle == JoinRound)
                    543:            {
                    544:                center.x = (p2.x + p5.x)/2;
                    545:                center.y = (p2.y + p5.y)/2;
                    546:                miRoundCap(pDraw, pGC, center, p, p2, p5, NotEnd, xOrg, yOrg);
                    547:            }
                    548:        }
                    549:        else                    /* straight line intersection */
                    550:        {
                    551:            plinfo[i].v1 = p2;
                    552:            plinfo[i].v2 = p3;
                    553:            plinfo[i-1].v3 = p2;
                    554:            plinfo[i-1].v4 = p3;
                    555: 
                    556:        }
                    557: 
                    558:        p1 = p5;  p2 = p6;  p3 = p7;  p4 = p8;
                    559:     }
                    560: 
                    561:     /* Special case for final segment --
                    562:     */
                    563:     if(plinfo[i-1].c > 0)
                    564:     {
                    565:        p = plinfo[i-1].v2;
                    566:     }
                    567:     else if (plinfo[i-1].c < 0)
                    568:     {
                    569:        p = plinfo[i-1].v0;
                    570:     }
                    571: 
                    572:     if (edges_match)
                    573:     {
                    574:        plinfo[i-1].v3 = FirstEdge[0];
                    575:        plinfo[i-1].v4 = FirstEdge[1];
                    576:        if(plinfo[i-1].c > 0)
                    577:        {
                    578:            p = plinfo[i-1].v2;
                    579:        }
                    580:        else if (plinfo[i-1].c < 0)
                    581:        {
                    582:            p = plinfo[i-1].v0;
                    583:        }
                    584:     }
                    585:     else
                    586:     {
                    587:        miGetPts(pPts[npt-2], pPts[npt-1], &p5, &p6, &p7, &p8, width);
                    588:        plinfo[i-1].v3 = p6;
                    589:        plinfo[i-1].v4 = p7;
                    590:     }
                    591:     /*
                    592:      * plinfo[i].c is 0 if there is no notch, either because the lines meet
                    593:      * at 180 degrees, or the lines don't meet.  
                    594:      * Since we've set the alu to GCCopy, we don't worry about drawing
                    595:      * points twice.
                    596:      */
                    597:     for(i = 0; i < (npt - 1); i++)
                    598:     {
                    599:        if(plinfo[i].c < 0)
                    600:        {
                    601:            wedge[1] = plinfo[i].p1;
                    602:            wedge[2] = i ? plinfo[i - 1].p2 : plinfo[npt-2].p2;
                    603:        }
                    604:        else if (plinfo[i].c > 0)
                    605:        {
                    606:            wedge[1] = plinfo[i].p4;
                    607:            wedge[2] = i ? plinfo[i-1].p3 : plinfo[npt-2].p3;
                    608:        }
                    609: 
                    610:        if(plinfo[i].c)
                    611:        {
                    612:            wedge[0] = pPts[i];
                    613:            /* Check that points aren't colinear */
                    614:            if(!(wedge[0].x == wedge[1].x && wedge[1].x == wedge[2].x) &&
                    615:               !(wedge[0].y == wedge[1].y && wedge[1].y == wedge[2].y))
                    616:            {
                    617:                miFillSppPoly(pDraw, pGC, 3, wedge, -xOrg, -yOrg);
                    618:            }
                    619:        }
                    620:        miFillSppPoly(pDraw, pGC, 4, &plinfo[i].p1, -xOrg, -yOrg);
                    621:     }
                    622:     DEALLOCATE_LOCAL(plinfo);  
                    623: }
                    624: 
                    625: /* MIGETPTS -- helper function
                    626:  *  Get the corners of the polygon formed for a line with the endpoints
                    627:  *  p1In, p2In and the given width stuff the corners into *p1Out, *p2Out ...
                    628:  *  p1Out and p4Out are always on the same end as p1In.
                    629:  */
                    630: void
                    631: miGetPts(p1In, p2In, p1Out, p2Out, p3Out, p4Out, width)
                    632:     SppPointRec p1In, p2In;
                    633:     SppPointPtr p1Out, p2Out, p3Out, p4Out;
                    634:     int width;
                    635: {
                    636:     double hyp;         /* true distance of line (hypotenuse) */
                    637:     double dx, dy;
                    638:     double tmpdx, tmpdy;
                    639: 
                    640:     tmpdx = (double)(p1In.x - p2In.x);
                    641:     tmpdy = (double)(p1In.y - p2In.y);
                    642: 
                    643:     hyp = hypot(tmpdx, tmpdy);
                    644:     if (!ISZERO(hyp))
                    645:     {
                    646:     /* NOTE: the division by 2 must be done in floating point */
                    647:        dy = (tmpdx * width/2.0) / hyp;
                    648:        dx = (tmpdy * width/2.0) / hyp;
                    649: 
                    650:        p1Out->x = (p1In.x + dx);
                    651:        p1Out->y = (p1In.y - dy);
                    652: 
                    653:        p2Out->x = (p2In.x + dx);
                    654:        p2Out->y = (p2In.y - dy);
                    655: 
                    656:        p3Out->x = (p2In.x - dx);
                    657:        p3Out->y = (p2In.y + dy);
                    658: 
                    659:        p4Out->x = (p1In.x - dx);
                    660:        p4Out->y = (p1In.y + dy);
                    661:     }
                    662:     else
                    663:     {    
                    664:        /* Bizarre answer, but it's what the spec calls for */
                    665:        p1Out->x = p2Out->x = p1In.x - width/2.0;
                    666:        p3Out->x = p4Out->x = p1In.x + width/2.0;
                    667:        p1Out->y = p2Out->y = p3Out->y = p4Out->y = p1In.y;
                    668:     }
                    669: }
                    670: 
                    671: /* INTERSECTLINES -- private helper for line code 
                    672:  *  Return the intersection of the line from p1 to p2 with
                    673:  *  the line from p3 to p4.
                    674:  *
                    675:  *  First, reduce the lines to:   y = m1x + b,  and y = m2x + c.
                    676:  *  Then do the standard math.
                    677:  */
                    678: static
                    679: SppPointRec
                    680: IntersectLines(p1, p2, p3, p4)
                    681:     SppPointRec p1, p2, p3, p4;
                    682: {
                    683:     SppPointRec i;
                    684:     double m1, m2;
                    685:     double b1, b2;
                    686:     double dx1, dx2;
                    687:     double tx;
                    688: 
                    689:     dx1 = p2.x - p1.x;
                    690:     dx2 = p4.x - p3.x;
                    691: 
                    692:     if (ISZERO(dx1))
                    693:     {
                    694:        if (! ISZERO(dx2))
                    695:        {
                    696:            i.y = p3.y + ((p4.y - p3.y) * (p1.x - p3.x)) / (p4.x - p3.x);
                    697:            i.x = p1.x;
                    698:        }
                    699:        else
                    700:            i = p2;
                    701:     }
                    702:     else if (ISZERO(dx2))
                    703:     {
                    704:        i.y = p1.y + ((p2.y - p1.y) * (p3.x - p1.x)) / (p2.x - p1.x);
                    705:        i.x = p3.x;
                    706:     }
                    707:     else
                    708:     {
                    709:        m1 = (p2.y - p1.y) / dx1;
                    710:        m2 = (p4.y - p3.y) / dx2;
                    711: 
                    712:        b1 = p2.y - (m1 * p2.x);
                    713:        b2 = p4.y - (m2 * p4.x);
                    714: 
                    715:        if (! ISEQUAL(m1, m2))
                    716:            i.x = (tx = ((b2-b1) / (m1-m2)));
                    717:        else
                    718:            i.x = (tx = p2.x);
                    719: 
                    720:        i.y = m1 * tx + b1;
                    721:     }
                    722:     return(i);
                    723: }
                    724: 
                    725: 
                    726: /* MIEXTENDSEGMENT -- a private helper function 
                    727:  * Calcuate point p3 made by extending the segment backward (away from p2)
                    728:  * by a length d */
                    729: 
                    730: SppPointRec
                    731: miExtendSegment(p1, p2, d)
                    732:     SppPointRec        p1, p2;
                    733:     int                d;
                    734: {
                    735:     SppPointRec        p3;
                    736:     double     l, dx, dy, hypot();
                    737: 
                    738:     dx = p2.x - p1.x;
                    739:     dy = p2.y - p1.y;
                    740:     l = hypot(dx, dy);
                    741:     if(!ISZERO(l))
                    742:     {
                    743:        p3.x = (p2.x - (l + d)/l * dx);
                    744:        p3.y = (p2.y - (l + d)/l * dy);
                    745:     }
                    746:     else
                    747:     {
                    748:        ErrorF("ExtendSegment called with coincident points.\n");
                    749:        p3 = p2;
                    750:     }
                    751:     return(p3);
                    752: }
                    753: 
                    754: /* MIROUNDCAP -- a private helper function
                    755:  * Put Rounded cap on end. pCenter is the center of this end of the line
                    756:  * pEnd is the center of the other end of the line. pCorner is one of the
                    757:  * two corners at this end of the line.  It doesn't matter which of the two
                    758:  * corners you give it. pOtherCorner is the other one.
                    759:  */
                    760: void
                    761: miRoundCap(pDraw, pGC, pCenter, pEnd, pCorner, pOtherCorner, fLineEnd,
                    762:      xOrg, yOrg)
                    763:     DrawablePtr        pDraw;
                    764:     GCPtr      pGC;
                    765:     SppPointRec        pCenter, pEnd;
                    766:     SppPointRec        pCorner, pOtherCorner;
                    767:     int                fLineEnd, xOrg, yOrg;
                    768: {
                    769:     int                c, i, signc, cpt;
                    770:     double     width;
                    771:     double     dx, dy, hypot();
                    772:     xArc       arc;
                    773:     DDXPointPtr        pArcPts;
                    774:     SppPointPtr        pPts;
                    775:     int                arcmode = pGC->arcMode;
                    776: 
                    777:     if(arcmode != ArcChord)
                    778:     {
                    779:        DoChangeGC(pGC, GCArcMode, &gcvals[GCVALSARCMODE], 0);
                    780:        ValidateGC(pDraw, pGC);
                    781:     }
                    782:     if (fLineEnd == NotEnd)
                    783:     {
                    784:        dx = pCenter.x - pCorner.x;
                    785:        dy = pCenter.y - pCorner.y;
                    786:        width = hypot(dx, dy);
                    787:     }
                    788:     else
                    789:        width = pGC->lineWidth;
                    790:     if(PtEqual(pCenter, pEnd))
                    791:     {
                    792:        signc = 1;
                    793:     }
                    794:     else
                    795:     {
                    796:        c = (pCenter.x - pEnd.x) * (pCorner.y - pCenter.y) -
                    797:             (pCorner.x - pCenter.x) * (pCenter.y - pEnd.y);
                    798:        signc = -sign(c);
                    799:     }
                    800: 
                    801:     /* These come back scaled by 64 and all ready to use */
                    802:     arc.x = ROUNDTOINT(pCenter.x - width/2);
                    803:     arc.y = ROUNDTOINT(pCenter.y - width/2);
                    804:     arc.width = ROUNDTOINT(width);
                    805:     arc.height = arc.width;
                    806:     arc.angle1 = -PtToAngle(pCenter, pCorner);
                    807:     arc.angle2 = -signc * 180 * 64;
                    808:     pArcPts = (DDXPointPtr)NULL;
                    809:     if( cpt = miGetArcPts(&arc, 0, &pArcPts))
                    810:     {
                    811:        /* by drawing with miFillSppPoly and setting the endpoints of the arc
                    812:         * to be the corners, we assure that the cap will meet up with the
                    813:         * rest of the line */
                    814:        pPts = (SppPointPtr) Xalloc(cpt * sizeof(SppPointRec));
                    815:        for(i = 1; i < cpt -1; i++)
                    816:        {
                    817:            pPts[i].x = (double) pArcPts[i].x;
                    818:            pPts[i].y = (double) pArcPts[i].y;
                    819:        }
                    820:        pPts[0] = pCorner;
                    821:        pPts[cpt - 1] = pOtherCorner;
                    822:        miFillSppPoly(pDraw, pGC, cpt, pPts, -xOrg, -yOrg);
                    823:        Xfree((char *)pArcPts);
                    824:        Xfree((char *)pPts);
                    825:        if(arcmode != ArcChord)
                    826:        {
                    827:            DoChangeGC(pGC, GCArcMode, &arcmode, 0);
                    828:            ValidateGC(pDraw, pGC);
                    829:        }
                    830:     }
                    831: }
                    832: 
                    833: /* these are from <math.h>, but I'm told some systems don't have math.h Psi! */
                    834: extern double sqrt(), cos(), sin(), atan();
                    835: #define M_PI   3.14159265358979323846
                    836: #define M_PI_2 1.57079632679489661923
                    837: 
                    838: /* PTTOANGLE -- a private helper function
                    839:  * Given two points, compute the slope of the line segment
                    840:  * Result is in radians * 64
                    841:  */
                    842: static
                    843: int
                    844: PtToAngle(c, p)
                    845:     SppPointRec        c, p;
                    846: {
                    847:     double     dx, dy, theta;
                    848: 
                    849:     dx = p.x - c.x;
                    850:     dy  = p.y - c.y;
                    851:     if(dx == 0)
                    852:     {
                    853:        theta = (dy > 0) ? M_PI_2 : -M_PI_2;
                    854:     }
                    855:     else
                    856:         theta =(atan(dy/dx));
                    857:     if(dx < 0)
                    858:        theta += M_PI;
                    859:     theta = (theta * 64 * 180/M_PI);
                    860:     return(ROUNDTOINT(theta));
                    861: }
                    862: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.