/******************************Module*Header*******************************\ * Module Name: pathwide.hxx * * Path widening defines. * * CLASS HIERARCHY * * In constructing the wide-line, we keep track of 4 paths: * * 1) The original path. * 2) The path holding the vertices of the polygonized pen. * 3) The 'left' side of the wide-line outline. * 4) The 'right' side of the wide-line outline. * * To keep track of the latter 3 paths, we use: * * PATHMEMOBJ // Regular path * | * v * WIDEPATHOBJ // Path object to keep track of one side of the * | // wide-line. Adds methods for easily adding * | // one point at a time. * v * WIDEPENOBJ // Path object to keep the pen shape. A pen is * // composed of an arbitrary number of points, * // like a path, so we make it a path. Plus we * // add some pen functionality. * * It was useful to compartmentalize the task of the widener into separate * base classes: * * READER // Reads the original path a point at a time * | * v * LINER // Reads the path a line at a time (flattening * | // Beziers and generating close-figure lines as * | // it goes). * v * STYLER // Handles styling by hacking the lines into * | // itty bitty pieces. * v * WIDENER // Widens the path by generating the points at * // every line-join and end-cap. * * Created: 9-Oct-1991 * Author: J. Andrew Goossen [andrewgo] * * Copyright (c) 1991-1999 Microsoft Corporation \**************************************************************************/ #if DBG // #define DEBUG_WIDE #endif class WIDENER; #define SQUARE(x) ((x) * (x)) #define LPLUSHALFTOFX(x) (LTOFX(x) + (LTOFX(1) >> 1)) /***********************************Struct*********************************\ * struct HOBBY * * Structure used for containing half a Hobby pen. * * History: * 9-Oct-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ typedef struct _HOBBY { PPOINTFIX pptfx; COUNT cptfx; } HOBBY, *PHOBBY; /* hob, phob */ /*********************************class************************************\ * class EVECTORFLEXT * * Can use VECTORFX's. * * History: * 22-Oct-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ class EVECTORFLEXT : public EVECTORFL /* evecfl */ { public: EVECTORFLEXT(VECTORFX& vec) { x.vFxToEf(vec.x); y.vFxToEf(vec.y); } BOOL bToVECTORFX(VECTORFX& vec) { return(x.bEfToFx(vec.x) && y.bEfToFx(vec.y)); } }; /*********************************class************************************\ * class LINEDATA * * Keeps track of useful stuff about the current line in the original * path that we're processing. * * History: * 22-Oct-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ // LINEDATA flags: #define LDF_INVERT 0x0001L #define LDF_VECSQUARECOMPUTED 0x0002L #define LDF_VECPERPCOMPUTED 0x0004L #define LDF_VECDRAWCOMPUTED 0x0008L #define LDF_NORMALIZEDCOMPUTED 0x0010L class LINEDATA /* ld */ { public: FLONG fl; // LINEDATA flags, as above PPATHREC ppr; // Pathrecord containing point PPOINTFIX pptfx; // *pptfx == -ptfx if bInvert is TRUE LONGLONG fxCrossStart; LONGLONG fxCrossEnd; EVECTORFX vecLine; // Actual line vector EVECTORFX vecTangent; // True tangent to current point. Direction // only -- to be used for calculating perps // Cached values: EVECTORFX vecSquare; // Vector to offset to square cap start EVECTORFX vecPerp; // Perpendicular vector to line EVECTORFX vecDraw; // Drawing vector for line POINTFL ptflNormalized; // Normalized version of line vector BOOL bInvert() { return(fl & LDF_INVERT); } VOID vSetInvert() { fl |= LDF_INVERT; } VOID vClearInvert() { fl &= ~LDF_INVERT; } VOID vSetVecSquareComputed() { fl |= LDF_VECSQUARECOMPUTED; } VOID vSetVecPerpComputed() { fl |= LDF_VECPERPCOMPUTED; } VOID vSetVecDrawComputed() { fl |= LDF_VECDRAWCOMPUTED; } VOID vSetNormalizedComputed() { fl |= LDF_NORMALIZEDCOMPUTED; } BOOL bVecSquareComputed() { return(fl & LDF_VECSQUARECOMPUTED); } BOOL bVecPerpComputed() { return(fl & LDF_VECPERPCOMPUTED); } BOOL bVecDrawComputed() { return(fl & LDF_VECDRAWCOMPUTED); } BOOL bNormalizedComputed() { return(fl & LDF_NORMALIZEDCOMPUTED); } VOID vInit() { fl = 0; } VOID vInit(POINTFIX& ptfxEnd, POINTFIX& ptfxStart) { fl = 0; vecLine = ptfxEnd; vecLine -= ptfxStart; vecTangent = vecLine; } // bToLeftSide() returns true if the perpendicular for the line // falls to the left of the draw vector: BOOL bToLeftSide() { return(fxCrossStart > fxCrossEnd); } // bSamePenSection() returns true if the two lines have perpendicular // vectors in the same area: BOOL bSamePenSection(LINEDATA& ld) { return((pptfx == ld.pptfx) && (bToLeftSide() == ld.bToLeftSide())); } }; typedef LINEDATA *PLINEDATA; /*********************************Class************************************\ * class WIDEPATHOBJ * * History: * 12-Sep-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ class WIDEPATHOBJ : public PATHMEMOBJ /* wpath */ { private: #ifdef DEBUG_WIDE BOOL bOpenPath; #endif protected: BOOL bOutOfMemory; PPOINTFIX pptfxPathRecCurrent; PPOINTFIX pptfxPathRecEnd; PPATHREC pprFigureStart; BOOL bGrowPath(); VOID vGrowPathAndAddPoint( PPOINTFIX, PEVECTORFX = (PEVECTORFX) NULL, BOOL = FALSE); public: WIDEPATHOBJ() { bOutOfMemory = FALSE; #ifdef DEBUG_WIDE bOpenPath = FALSE; #endif } ~WIDEPATHOBJ() {} VOID vAddPoint(PPOINTFIX pptfx, BOOL bAddRecordIfNeeded = TRUE) { #ifdef DEBUG_WIDE ASSERTGDI(bOpenPath, "vAddPoint path not open!"); #endif if (pptfxPathRecCurrent >= pptfxPathRecEnd) { #ifdef DEBUG_WIDE ASSERTGDI(pptfxPathRecCurrent == pptfxPathRecEnd, "vAddPoint out of bounds!"); #endif if (bAddRecordIfNeeded) vGrowPathAndAddPoint(pptfx); } else { #ifdef DEBUG_WIDE ASSERTGDI((PBYTE) pptfxPathRecCurrent - (PBYTE) ppath->ppachain < (LONG) ppath->ppachain->siztPathAlloc, "vAddPoint out of bounds."); #endif *pptfxPathRecCurrent++ = *pptfx; } } VOID vAddPoint(PPOINTFIX pptfx, PEVECTORFX pvec, BOOL bInvert) { #ifdef DEBUG_WIDE ASSERTGDI(bOpenPath, "vAddPoint2 path not open!"); #endif if (pptfxPathRecCurrent >= pptfxPathRecEnd) { #ifdef DEBUG_WIDE ASSERTGDI(pptfxPathRecCurrent == pptfxPathRecEnd, "vAddPoint2 out of bounds!"); #endif vGrowPathAndAddPoint(pptfx, pvec, bInvert); } else { #ifdef DEBUG_WIDE ASSERTGDI((PBYTE) pptfxPathRecCurrent - (PBYTE) ppath->ppachain < (LONG) ppath->ppachain->siztPathAlloc, "vAddPoint2 out of bounds."); #endif // 'bInvert' will usually be constant, and so one of these code // paths will optimize away: if (bInvert) { pptfxPathRecCurrent->x = pptfx->x - pvec->x; pptfxPathRecCurrent->y = pptfx->y - pvec->y; } else { pptfxPathRecCurrent->x = pptfx->x + pvec->x; pptfxPathRecCurrent->y = pptfx->y + pvec->y; } pptfxPathRecCurrent++; } } VOID vReverseConcatenate(WIDEPATHOBJ&); VOID vPrependBeforeFigure(); VOID vPrependBeforeSubpath(); VOID vMarkFigureStart() { pprFigureStart = ppath->pprlast; } BOOL bBeginFigure(); VOID vEndFigure(); VOID vCloseFigure() { ppath->pprlast->flags |= PD_CLOSEFIGURE; } BOOL bValid() { return(PATHMEMOBJ::bValid() && !bOutOfMemory); } VOID vSetError() { bOutOfMemory = TRUE; } }; /******************************Public*Routine******************************\ * WIDEPATHOBJ::vAddPoint(pptfx, pvec, bInvert) * * Adds a single point to the path. The added point is the given point * plus the specified vector (minus the vector if bInvert is TRUE). * * History: * 1-Oct-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ /*********************************Class************************************\ * class WIDEPENOBJ * * History: * 12-Sep-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ class WIDEPENOBJ : public WIDEPATHOBJ /* wpen */ { private: // Pre-computed look-up tables containing points of circular Hobby pens // for widths 1 through 6: #define HOBBY_TABLE_SIZE 6L static POINTFIX aptfxHobby1[4]; static POINTFIX aptfxHobby2[5]; static POINTFIX aptfxHobby3[6]; static POINTFIX aptfxHobby4[8]; static POINTFIX aptfxHobby5[10]; static POINTFIX aptfxHobby6[10]; static HOBBY ahob[HOBBY_TABLE_SIZE]; BOOL bHobby; // TRUE if this is a Hobby pen BOOL bThicken(PPOINTFIX pptfx); // Thickens pen if need-be BOOL bHobbyize(EVECTORFX avec[]);// Makes a Hobby pen if need-be BOOL bPenFlatten(PPOINTFIX); // Converts Beziers to lines VOID vAddPenPoint(PPOINTFIX); // Adds a point to the end of our path public: WIDEPENOBJ() { bHobby = FALSE; } BOOL bIsHobby() { return(bHobby); } BOOL bIsHobby(BOOL b) { return(bHobby = b); } BOOL bPolygonizePen(EXFORMOBJ&, LONG); VOID vDetermineDrawVertex(EVECTORFX&, LINEDATA&); COUNT cptAddRound(WIDENER&, LINEDATA&, LINEDATA&, BOOL, BOOL, BOOL); VOID vAddRoundEndCap(WIDENER&, LINEDATA&, BOOL, BOOL); }; /*********************************class************************************\ * class READER * * Reads the path. * * History: * 22-Oct-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ // Path widening flags: #define FW_MORETOENUM 0x00000001L #define FW_DOINGSTYLES 0x00000002L #define FW_STYLENEXT 0x00000004L #define FW_FIGURESTYLED 0x00000008L #define FW_ALLROUND 0x00000010L class READER { private: EPATHOBJ* pepoSpine; PATHDATA pd; VOID vMoreToEnum(BOOL b) { vSetFlag(b, FW_MORETOENUM); } BOOL bMoreToEnum() { return(fl & FW_MORETOENUM); } PPOINTFIX pptfxRead; PPOINTFIX pptfxEnd; protected: FLONG fl; // For widening flags, as above VOID vSetFlag(BOOL b, FLONG flBit) { if (b) fl |= flBit; else fl &= ~flBit; } READER(EPATHOBJ& epo) { pepoSpine = &epo; pepoSpine->vEnumStart(); vMoreToEnum(TRUE); } BOOL bIsBezier() { return(pd.flags & PD_BEZIERS); } BOOL bNextFigure(); BOOL bNextPoint(POINTFIX& ptfx); // Only valid when !bNextPoint(): BOOL bIsClosedFigure() { return(pd.flags & PD_CLOSEFIGURE); } }; /*********************************class************************************\ * class LINER * * Cracks Beziers and handles CloseFigures. * * History: * 22-Oct-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ enum WIDENEVENT { WE_STARTFIGURE, // Figure starts at 'ptfxThis' WE_ENDFIGURE, // Figure ends at 'ptfxThis' WE_CLOSEFIGURE, // Do a close figure join at 'ptfxThis' WE_JOIN, // Do a join at 'ptfxThis' WE_BEZIERJOIN, // Do a Bezier join at 'ptfxThis' WE_STOPDASH, // Stop a styling dash at 'ptfxThis' WE_STARTDASH, // Start a styling dash at 'ptfxThis' WE_ZEROFIGURE, // Figure has zero length at 'ptfxThis' WE_FINISHFIGURE, // Do start-cap of figure at 'ptfxThis' WE_DONEPATH }; enum LINESTATE { LS_READPATH, // Next point in figure comes from path LS_STARTFIGURE, // Next point in path is the start of // a figure LS_FINISHFIGURE, // Go back to first point in figure LS_READBEZIER, // Next point comes from current Bezier LS_DONEPATH }; class LINER : public READER { private: BEZIER bez; BOOL bReadLine(LINEDATA& ld); POINTFIX ptfxNext; // Next point in path after 'ptfxThis' (see below) POINTFIX ptfxStartFigure; // Keep around for later LINEDATA ldStartFigure; // Start point for current figure LINEDATA ldBuf[2]; // Line vector data buffer for current lines LINESTATE ls; // Reading path state VOID vNextPoint(); VOID vZeroFigure(); // Sets up for a zero-length figure protected: LINEDATA ldEndTangent; // Line vector data for last line in figure LINEDATA ldStartTangent; // Line vector data for first line in figure public: LINER(EPATHOBJ& epo) : READER(epo) { if (!bNextFigure()) ls = LS_DONEPATH; else { bNextPoint(ptfxNext); ptfxStartFigure = ptfxNext; ls = LS_STARTFIGURE; } } VOID vNextEvent(); // Keeps track of all the info needed to add a join, start-cap or end-cap: WIDENEVENT we; // Flag to indicate start-cap, end-cap, etc. POINTFIX ptfxThis; // Point at which to add cap or join PLINEDATA pldIn; // Vector of line entering 'ptfxThis' (used for // end-caps and joins) PLINEDATA pldOut; // Vector of line exiting 'ptfxThis' (used for // start-caps and joins) }; /*********************************class************************************\ * class STYLER * * Hacks up the path into itty bitty pieces to feed to the widener. * * History: * 22-Oct-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ class STYLER : public LINER { private: VOID vStyleNext(BOOL b) { vSetFlag(b, FW_STYLENEXT); } VOID vDoingStyles(BOOL b) { vSetFlag(b, FW_DOINGSTYLES); } BOOL bStyleNext() { return(fl & FW_STYLENEXT); } BOOL bDoingStyles() { return(fl & FW_DOINGSTYLES); } PFLOAT_LONG pstyleStart; PFLOAT_LONG pstyleCurrent; PFLOAT_LONG pstyleEnd; EFLOAT efRemainingLength;// Length remaining in current line EFLOAT efStyleLength; // Length of current dash or gap EFLOAT efDoneLength; // Length of current line done EFLOAT efLineLength; // efLineLength = efDoneLength + efRemainingLength POINTFIX ptfxLineStart; // Start point of line protected: EFLOAT efWorldLength(EVECTORFX); EFLOAT efNextStyleLength(); VOID vResetStyles() { pstyleCurrent = pstyleStart; } // These two actually get initialized by the derived class because it's // a bit of work, and it will know if we'll need them: MATRIX mxDeviceToWorld; EXFORMOBJ exoDeviceToWorld; public: STYLER(EPATHOBJ&, PLINEATTRS); VOID vNextStyleEvent(); }; // Prototype used in WIDENER: VOID vAddNice(WIDEPATHOBJ&, PPOINTFIX, PEVECTORFX, BOOL); /*********************************class************************************\ * class WIDENER * * Widens the path. * * History: * 22-Oct-1991 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ class WIDENER : public STYLER { friend VOID WIDEPENOBJ::vAddRoundEndCap(WIDENER&, LINEDATA&, BOOL, BOOL); friend COUNT WIDEPENOBJ::cptAddRound(WIDENER&, LINEDATA&, LINEDATA&, BOOL, BOOL, BOOL); private: WIDEPENOBJ wpen; WIDEPATHOBJ wpathLeft; WIDEPATHOBJ wpathRight; ULONG iJoin; ULONG iEndCap; VOID vFigureStyled(BOOL b) { vSetFlag(b, FW_FIGURESTYLED); } VOID vAllRound(BOOL b) { vSetFlag(b, FW_ALLROUND); } BOOL bFigureStyled() { return(fl & FW_FIGURESTYLED); } BOOL bAllRound() { return(fl & FW_ALLROUND); } EFLOAT efHalfWidthMiterLimitSquared; EFLOAT efHalfWidth; VOID vVecPerpCompute(LINEDATA&); VOID vVecSquareCompute(LINEDATA&); VOID vVecDrawCompute(LINEDATA&); protected: BOOL bMiterInLimit(EVECTORFX); EVECTORFX vecInDraw() { if (!pldIn->bVecDrawComputed()) vVecDrawCompute(*pldIn); return(pldIn->vecDraw); } EVECTORFX vecInPerp() { if (!pldIn->bVecPerpComputed()) vVecPerpCompute(*pldIn); return(pldIn->vecPerp); } EVECTORFX vecInSquare() { if (!pldIn->bVecSquareComputed()) vVecSquareCompute(*pldIn); return(pldIn->vecSquare); } EVECTORFX vecOutDraw() { if (!pldOut->bVecDrawComputed()) vVecDrawCompute(*pldOut); return(pldOut->vecDraw); } EVECTORFX vecOutPerp() { if (!pldOut->bVecPerpComputed()) vVecPerpCompute(*pldOut); return(pldOut->vecPerp); } EVECTORFX vecOutSquare() { if (!pldOut->bVecSquareComputed()) vVecSquareCompute(*pldOut); return(pldOut->vecSquare); } VOID vAddLeft(EVECTORFX& vec, BOOL bInvert = FALSE) { wpathLeft.vAddPoint(&ptfxThis, &vec, !bInvert); } VOID vAddLeft(PEVECTORFX pvec, BOOL bInvert) { wpathLeft.vAddPoint(&ptfxThis, pvec, !bInvert); } VOID vAddLeft() { wpathLeft.vAddPoint(&ptfxThis); } VOID vAddLeftNice(PEVECTORFX pvec, BOOL bInvert) { vAddNice(wpathLeft, &ptfxThis, pvec, !bInvert); } VOID vAddRight(EVECTORFX& vec, BOOL bInvert = FALSE) { wpathRight.vAddPoint(&ptfxThis, &vec, bInvert); } VOID vAddRight(PEVECTORFX pvec, BOOL bInvert) { wpathRight.vAddPoint(&ptfxThis, pvec, bInvert); } VOID vAddRight() { wpathRight.vAddPoint(&ptfxThis); } VOID vAddRightNice(PEVECTORFX pvec, BOOL bInvert) { vAddNice(wpathRight, &ptfxThis, pvec, bInvert); } VOID vAddJoin(BOOL); VOID vAddRoundJoin(BOOL); VOID vAddEndCap(); VOID vAddStartCap(); BOOL bWiden(); VOID vSetError() { wpathRight.vSetError(); } public: WIDENER(EPATHOBJ&, EXFORMOBJ&, PLINEATTRS); BOOL bValid() { return(wpathRight.bValid() && wpathLeft.bValid() && wpen.bValid()); } VOID vMakeItWide(EPATHOBJ&); };