/*********************************************************** Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, and the Massachusetts Institute of Technology, Cambridge, Massachusetts. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Digital or MIT not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* $Header: miarc.c,v 1.41 87/06/14 01:38:24 todd Exp $ */ /* Author: Todd Newman */ #include "X.h" #include "Xprotostr.h" #include "misc.h" #include "gcstruct.h" #include "scrnintstr.h" #include "pixmapstr.h" #include "mifpoly.h" #include "opaque.h" /* these are from <math.h>, but I'm told some systems don't have math.h Psi! */ extern double sqrt(), cos(), sin(), atan(); #define M←PI 3.14159265358979323846 #define M←PI←2 1.57079632679489661923 #define SAMESIGN(a, b) ((((a) >= 0) && ((b) >= 0)) ||\ (((a) <= 0) && ((b) <= 0))) typedef struct { int cpt; DDXPointPtr pPts; } POLYARCINFO; #define GCValsFunction 0 #define GCValsForeground 1 #define GCValsBackground 2 #define GCValsLineWidth 3 #define GCValsCapStyle 4 #define GCValsJoinStyle 5 #define GCValsArcMode 6 static long gcvals[]= {GXcopy, 1, 0, 0, 0, 0, 0}; void miPolyArc(pDraw, pGC, narcs, parcs) DrawablePtr pDraw; GCPtr pGC; int narcs; xArc *parcs; { register int i, j; register DDXPointPtr ppt; /* Points for the current arc */ register DDXPointPtr pAllPts; /* Points for all arcs so far */ register int cpt, cptAll; DDXPointPtr pPts; /* Points for the current arc */ int xMin, xMax, yMin, yMax, yOrg, xOrg, dx, dy, ifirst, ilast, count, width; long gcmode, arcmode; Bool fAllOne, fTricky; PixmapPtr pDrawTo; GCPtr pGCTo; POLYARCINFO *polyarcs; DDXPointRec LastPt; width = pGC->lineWidth; if(width == 0 || narcs == 1) { /* don't have to worry about overlap or joinstyles */ for(i = 0; i < narcs; i++) { pPts = (DDXPointPtr)NULL; if( cpt = miGetArcPts(&parcs[i], 0, &pPts)) { (*pGC->Polylines)(pDraw, pGC, CoordModeOrigin, cpt, pPts); Xfree(pPts); } } } else { count = 0; ifirst = 0; fAllOne = FALSE; pPts = (DDXPointPtr) NULL; pAllPts = (DDXPointPtr) NULL; polyarcs = (POLYARCINFO *)ALLOCATE←LOCAL(narcs * sizeof(POLYARCINFO)); if(!polyarcs) return; xMin = yMin = MAXSHORT; xMax = yMax = MINSHORT; /* Get all points for all the arcs. */ for(i = 0; i < narcs; i++) { pPts = (DDXPointPtr) NULL; if((cpt = miGetArcPts(&parcs[i], 0, &pPts)) == 0) { /* One of the arcs was empty. Give up */ while(--i >= 0) { Xfree(polyarcs[i].pPts); } DEALLOCATE←LOCAL(polyarcs); return; } polyarcs[i].cpt = cpt; polyarcs[i].pPts = pPts; ppt = pPts; for(j = 0; j < cpt; j++) { xMin = min(xMin, ppt->x); yMin = min(yMin, ppt->y); xMax = max(xMax, ppt->x); yMax = max(yMax, ppt->y); ppt++; } } switch(pGC->alu) { case GXclear: /* 0 */ case GXcopy: /* src */ case GXcopyInverted: /* NOT src */ case GXset: /* 1 */ fTricky = FALSE; pDrawTo = (PixmapPtr) pDraw; pGCTo = pGC; if(pGCTo->arcMode != ArcChord) { arcmode = pGCTo->arcMode; ChangeGC(pGCTo, GCArcMode, &gcvals[GCValsArcMode]); ValidateGC((DrawablePtr)pDrawTo, pGCTo); } else arcmode = ArcChord; break; case GXand: /* src AND dst */ case GXandReverse: /* src AND NOT dst */ case GXandInverted: /* NOT src AND dst */ case GXnoop: /* dst */ case GXxor: /* src XOR dst */ case GXor: /* src OR dst */ case GXnor: /* NOT src AND NOT dst */ case GXequiv: /* NOT src XOR dst */ case GXinvert: /* NOT dst */ case GXorReverse: /* src OR NOT dst */ case GXorInverted: /* NOT src OR dst */ case GXnand: /* NOT src OR NOT dst */ fTricky = TRUE; pGCTo = GetScratchGC(1, pDraw->pScreen); gcvals[GCValsLineWidth] = pGC->lineWidth; gcvals[GCValsCapStyle] = pGC->capStyle; gcvals[GCValsJoinStyle] = pGC->joinStyle; /* Also what about arcmode?? */ gcmode = GCFunction | GCForeground | GCBackground | GCLineWidth | GCCapStyle | GCJoinStyle; ChangeGC(pGCTo, gcmode, gcvals); xOrg = xMin - (width + 1)/2; yOrg = yMin - (width + 1)/2; dx = xMax - xMin + width + 1; dy = yMax - yMin + width + 1; for(i = 0; i < narcs; i++) { cpt = polyarcs[i].cpt; ppt = polyarcs[i].pPts; for(j = 0; j < cpt; j++) { ppt->x -= xOrg; ppt->y -= yOrg; ppt++; } } /* allocate a 1 bit deep pixmap of the appropriate size, and * validate it */ pDrawTo = (PixmapPtr)(*pDraw->pScreen->CreatePixmap) (pDraw->pScreen, dx, dy, 1, XYBitmap); ValidateGC((DrawablePtr)pDrawTo, pGCTo); ClearDrawable((DrawablePtr)pDrawTo, pGCTo, dx, dy); } ilast = narcs - 1; /* If the last arc joins with the first, find all the final arcs * that join together and join them with the initial ones. */ if(PtEqual(polyarcs[0].pPts[0], polyarcs[ilast].pPts[polyarcs[ilast].cpt - 1]) ) { count = 1; while(PtEqual (polyarcs[ilast].pPts[0], polyarcs[ilast - 1].pPts[polyarcs[ilast - 1].cpt - 1]) ) { if(ilast <= 1) { fAllOne = TRUE; break; } else { ilast--; count++; } } if(!fAllOne) { while(PtEqual(polyarcs[ifirst].pPts[polyarcs[ifirst].cpt -1], polyarcs[ifirst + 1].pPts[0])) ifirst++; cptAll = polyarcs[ilast].cpt; pAllPts = polyarcs[ilast++].pPts; for(; ilast < narcs; ilast++) { cpt = polyarcs[ilast].cpt; pAllPts = (DDXPointPtr) Xrealloc(pAllPts, (cptAll+cpt-1) * sizeof(DDXPointRec)); bcopy((char *) &polyarcs[ilast].pPts[1], (char *) &pAllPts[cptAll], (cpt - 1) * sizeof(DDXPointRec)); cptAll += cpt - 1; } for( i = 0; i <= ifirst; i++) { cpt = polyarcs[i].cpt; pAllPts = (DDXPointPtr) Xrealloc(pAllPts, (cptAll+cpt-1) * sizeof(DDXPointRec)); bcopy((char *) &polyarcs[i].pPts[1], (char *) &pAllPts[cptAll], (cpt - 1) * sizeof(DDXPointRec)); cptAll += cpt - 1; } (*pGCTo->Polylines)(pDrawTo, pGCTo, CoordModeOrigin, cptAll, pAllPts); if(fTricky) { (*pGC->PushPixels)(pGC, pDrawTo, pDraw, dx, dy, xOrg, yOrg); ClearDrawable((DrawablePtr)pDrawTo, pGCTo, dx, dy); } } ifirst++; } narcs -= count; /* Now draw all the (remaining) arcs */ cptAll = 0; for(i = ifirst; i < narcs; i++) { cpt = polyarcs[i].cpt; pPts = polyarcs[i].pPts; if((i > ifirst) && PtEqual(pPts[0], LastPt)) { pAllPts = (DDXPointPtr) Xrealloc(pAllPts, (cptAll-1 + cpt) * sizeof(DDXPointRec)); bcopy((char *)&pPts[1], (char *)&pAllPts[cptAll], (cpt - 1) * sizeof(DDXPointRec)); cptAll += cpt - 1; } else { /* Flush what we have so far and start collecting again */ if(cptAll > 0) { (*pGC->Polylines)(pDrawTo, pGCTo, CoordModeOrigin, cptAll, pAllPts); if(fTricky) { (*pGC->PushPixels)(pGC, pDrawTo, pDraw, dx, dy, xOrg, yOrg); ClearDrawable((DrawablePtr)pDrawTo, pGCTo, dx, dy); } } cptAll = cpt; pAllPts = pPts; } LastPt = pPts[cpt-1]; } /* end for each remaining arc */ if(cptAll > 0) { (*pGC->Polylines)(pDrawTo,pGCTo, CoordModeOrigin, cptAll, pAllPts); } if(fTricky) (*pGC->PushPixels)(pGC, pDrawTo, pDraw, dx, dy, xOrg, yOrg); for(i = 0; i < narcs; i++) { Xfree(polyarcs[i].pPts); } DEALLOCATE←LOCAL(polyarcs); if(fTricky) { (*pGCTo->pScreen->DestroyPixmap)(pDrawTo); FreeScratchGC(pGCTo); } else if(arcmode != ArcChord) { ChangeGC(pGCTo, GCArcMode, &arcmode); ValidateGC((DrawablePtr)pDrawTo, pGCTo); } } } /* 360 degrees * 64 sub-degree positions */ #define FULLCIRCLE 64 * 360 void miPolyFillArc(pDraw, pGC, narcs, parcs) DrawablePtr pDraw; GCPtr pGC; int narcs; xArc *parcs; { int i, cpt; DDXPointPtr pPts; for(i = 0; i < narcs; i++) { /* We do this test every time because a full circle PieSlice isn't * really a slice, but a full pie, and the Chord code (below) should * handle it better */ if(pGC->arcMode == ArcPieSlice && parcs[i].angle2 < FULLCIRCLE) { cpt = 1; pPts = (DDXPointPtr)Xalloc(sizeof(DDXPointRec)); if(cpt = miGetArcPts(&parcs[i], cpt, &pPts)) { pPts[0].x = parcs[i].x + parcs[i].width/2; pPts[0].y = parcs[i].y + parcs[i].height/2; (*pGC->FillPolygon)(pDraw, pGC, Convex, CoordModeOrigin, cpt + 1, pPts); Xfree((char *) pPts); } } else /* Chord */ { pPts = (DDXPointPtr)NULL; if(cpt = miGetArcPts(&parcs[i], 0, &pPts)) { (*pGC->FillPolygon)(pDraw, pGC, Convex, CoordModeOrigin, cpt, pPts); Xfree((char *) pPts); } } } } int miGetArcPts(parc, cpt, ppPts) xArc *parc; /* points to an arc */ int cpt; /* number of points already in arc list */ DDXPointPtr *ppPts; /* pointer to pointer to arc-list -- modified */ { double st, /* Start Theta, start angle */ et, /* End Theta, offset from start theta */ dt, /* Delta Theta, angle to sweep ellipse */ cdt, /* Cos Delta Theta, actually 2 cos(dt) */ x0, y0, /* the recurrence formula needs two points to start */ x1, y1, x2, y2, /* this will be the new point generated */ xc, yc; /* the center point */ int count, i; DDXPointPtr poly; /* The spec says that positive angles indicate counterclockwise motion. * Given our coordinate system (with 0,0 in the upper left corner), * the screen appears flipped in Y. The easiest fix is to negate the * angles given */ /* Throw out multiples of 360 degrees. */ i = -parc->angle1; if(i < 0) { while (i < -FULLCIRCLE) i += FULLCIRCLE; } else { while(i > FULLCIRCLE) i -= FULLCIRCLE; } st = (double ) i; st *= ((double)M←PI) / (64 * 180); /* convert to degrees, then to rads */ i = -parc->angle2; /* If it's more than one full rotation, make it exactly 1 rotation */ if(i > FULLCIRCLE || i < -FULLCIRCLE) { i = FULLCIRCLE; } et = (double) i; et *= ((double)M←PI) / (64 * 180); /* convert to degrees, then to rads */ /* Try to get a delta theta that is within 1/2 pixel. Then adjust it * so that it divides evenly into the total. * I'm just using cdt 'cause I'm lazy. */ cdt = max(parc->width, parc->height)/2; if(cdt <= 0) return 0; dt = ( (double)2.0 / sqrt(cdt)); count = et/dt; count = abs(count) + 1; dt = et/count; count++; cdt = 2 * cos(dt); poly = (DDXPointPtr) Xrealloc(*ppPts, (count + cpt) * sizeof(DDXPointRec)); *ppPts = poly; xc = parc->width/2.0; /* store half width and half height */ yc = parc->height/2.0; x0 = xc * cos(st); y0 = yc * sin(st); x1 = xc * cos(st + dt); y1 = yc * sin(st + dt); xc += parc->x; /* by adding initial point, these become */ yc += parc->y; /* the center point */ poly[cpt].x = ROUNDTOINT(xc + x0); poly[cpt].y = ROUNDTOINT(yc + y0); poly[cpt + 1].x = ROUNDTOINT(xc + x1); poly[cpt + 1].y = ROUNDTOINT(yc + y1); for(i = 2; i < count; i++) { x2 = cdt * x1 - x0; y2 = cdt * y1 - y0; poly[cpt + i].x = ROUNDTOINT(xc + x2); poly[cpt + i].y = ROUNDTOINT(yc + y2); x0 = x1; y0 = y1; x1 = x2; y1 = y2; } /* adjust the last point */ poly[cpt +i -1].x = ROUNDTOINT(cos(st + et) * parc->width/2.0 + xc); poly[cpt +i -1].y = ROUNDTOINT(sin(st + et) * parc->height/2.0 + yc); return(count); } int PtToAngle(c, p) SppPointRec c, p; { double dx, dy, theta; dx = p.x - c.x; dy = p.y - c.y; if(dx == 0) { theta = (dy > 0) ? M←PI←2 : -M←PI←2; } else theta =(atan(dy/dx)); if(dx < 0) theta += M←PI; theta = (theta * 64 * 180/M←PI); return(ROUNDTOINT(theta)); } ClearDrawable(pDraw, pGC, dx, dy) DrawablePtr pDraw; GCPtr pGC; int dx, dy; { long fg = pGC->fgPixel; long bg = pGC->bgPixel; xRectangle rect; rect.x = 0; rect.y = 0; rect.width = dx; rect.height = dy; ChangeGC(pGC, GCForeground, &bg); ValidateGC(pDraw, pGC); (*pGC->PolyFillRect)(pDraw, pGC, 1, &rect); ChangeGC(pGC, GCForeground, &fg); ValidateGC(pDraw, pGC); }