#include <pathstrk.h>
Inheritance diagram for PathStrokerVector:
Public Member Functions | |
PathStrokerVector (ValueFunction *WidthFunction, INT32 LineWidth, LineCapType CapType, DocRect *pSourceBounds) | |
Constructor. | |
void | PrepareToStroke (TrapEdgeList *pTraps) |
MUST be called before any calls to StrokePath or MapCoord. | |
virtual BOOL | StrokePath (TrapsList *pTraps, Path *pOutput) |
DO NOT CALL this variant of StrokePath! | |
virtual BOOL | StrokePath (Path *pSourceVector, Path *pOutput) |
** Call PrepareToStroke before calling this function ** "Strokes" a path according to the "style" set in construction. | |
DocCoord | MapCoord (DocCoord *pCoord) |
** Call PrepareToStroke before calling this function ** Maps the given point into the output stroke path, and returns the new coordinate to be output at your discretion. | |
double | GetScaleFactor (void) |
** Call PrepareToStroke before calling this function ** Determines a rough scaling factor by which the stroke is changing size during the mapping process. Used to map things like LineWidth attributes so that their size remains roughly proportional to the new size of the stroke. The scale factor is based on the source and destination line widths, which may not always be brilliant, but is the best we can do. | |
Static Public Member Functions | |
static StrokeHandle | GetCurrentBrush (void) |
static void | BodgeRipSelection (BOOL Repeating=FALSE) |
Static Public Attributes | |
static StrokeHandle | CurrentStroke = StrokeHandle_NoStroke |
Protected Member Functions | |
void | FindEdgeForCoord (DocCoord *pCoord, UINT32 StartIndex, INT32 Direction, TrapEdge *pOutput) |
** Call PrepareToStroke before calling this function ** Determines a TrapEdge Centre point and Normal to be used when mapping the given point into the destination path. | |
void | MapMove (DocCoord *pCoord) |
Maps the given point into the output stroke path, writing a MOVETO element. | |
void | MapLine (DocCoord *pCoord1, DocCoord *pCoord2) |
Maps the given line into the output stroke path Assumes that the first point (pCoord1) has already been output to the destination path (i.e. it appends the line to pOutput). | |
void | MapBezier (DocCoord *pCoords) |
Maps the given bezier curve into the output stroke path. | |
void | RecursiveMapLine (TrapEdge *pEdge1, DocCoord &Point1, double Width1, TrapEdge *pEdge2, DocCoord &Point2, double Width2, INT32 Direction, Path *pOutput) |
Recursive method which maps points within a given trapezoid by recursively "flattening" the curve between the two trapezoid edges. | |
void | RecursiveMapBezier (DocCoord *pCoords, DocCoord *p1, double t1, DocCoord *p2, double t2) |
Maps the given bezier curve into the output stroke path, recursing as necessary to produce a nice smooth result. | |
Private Member Functions | |
CC_DECLARE_MEMDUMP (PathStrokerVector) | |
Private Attributes | |
Path * | pPath |
DocRect * | pBounds |
double | BoundsWidth |
double | BoundsHalfHeight |
double | BoundsYCentre |
Path * | pOutput |
TrapEdgeList * | pCurrentEdgeList |
UINT32 | FirstEdge |
UINT32 | LastEdge |
Definition at line 196 of file pathstrk.h.
|
Constructor.
Definition at line 860 of file pathstrk.cpp. 00862 : PathStroker(WidthFunction, LineWidth, CapType) 00863 00864 { 00865 ERROR3IF(pSourceBounds == NULL , "Illegal NULL params"); 00866 00867 pPath = NULL; 00868 pBounds = pSourceBounds; 00869 BoundsYCentre = (double) (pSourceBounds->lo.y/2 + pSourceBounds->hi.y/2); 00870 BoundsWidth = (double) pSourceBounds->Width(); 00871 BoundsHalfHeight = (double) pSourceBounds->Height() / 2.0; 00872 pOutput = NULL; 00873 pCurrentEdgeList = NULL; 00874 FirstEdge = 0; 00875 LastEdge = 0; 00876 }
|
|
Definition at line 1704 of file pathstrk.cpp. 01705 { 01706 SelRange *pSel = GetApplication()->FindSelection(); 01707 01708 Node *pNode = pSel->FindFirst(); 01709 if (pNode == NULL || !IS_A(pNode, NodeGroup)) 01710 { 01711 ERROR3("Brush can only be made from a selected GROUP"); 01712 return; 01713 } 01714 01715 NodeGroup* pNewGroup = new NodeGroup; 01716 if (pNewGroup == NULL) 01717 { 01718 ERROR3("Couldn't create new brush (1)"); 01719 return; 01720 } 01721 01722 NodeGroup* pNewGroup1 = new NodeGroup; 01723 if (pNewGroup1 == NULL) 01724 { 01725 ERROR3("Couldn't create new brush (2)"); 01726 delete pNewGroup; 01727 return; 01728 } 01729 01730 // Attach this new group to the tree 01731 pNewGroup->AttachNode(pNode, NEXT); 01732 pNewGroup1->AttachNode(pNewGroup, FIRSTCHILD); 01733 01734 // Copy the children across to this new group, and 01735 // make sure we have all the attributes we need (We must ensure the default attrs 01736 // are copied too, else bits of the clipart relying on the default attrs will 01737 // use current attrs when rendered, giving unpredictanble results - to override 01738 // things like fill colours, we have to provide a better interface...) 01739 if (!pNode->CopyChildrenTo(pNewGroup1) || 01740 !pNewGroup->MakeAttributeComplete(NULL, TRUE, NULL, TRUE)) 01741 { 01742 // CascadeDelete unlinks the node from the tree so don't panic. 01743 ERROR3("Couldn't create new brush (3)"); 01744 pNewGroup->CascadeDelete(); 01745 delete pNewGroup; 01746 return; 01747 } 01748 01749 // --- Now, convert all IndexedColours (which are document-dependant) into standalone DocColours 01750 // BLOCK 01751 { 01752 Node *pCurNode = pNewGroup->FindFirstDepthFirst(); 01753 Node *pNextNode; 01754 01755 while (pCurNode !=NULL) 01756 { 01757 // We may be about to chop this node out of the tree, so get the next node now 01758 pNextNode = pCurNode->FindNextDepthFirst(pNewGroup); 01759 01760 // Use to scan the colour fields of the attribute. 01761 if (pCurNode->IsAnAttribute()) 01762 { 01763 UINT32 Context = 0; 01764 NodeAttribute *pNodeAttr = (NodeAttribute *) pCurNode; 01765 01766 // Get the next colour field from the attribute 01767 DocColour *pColour = pNodeAttr->EnumerateColourFields(Context++); 01768 01769 while (pColour != NULL) 01770 { 01771 // For each colour field, make sure the colour is a local DocColour so that 01772 // the sub-tree is entirely stand-alone 01773 if (pColour->FindParentIndexedColour() != NULL) 01774 { 01775 ColourGeneric ColDef; 01776 ColourContext *cc = ColourManager::GetColourContext(pColour->GetColourModel()); 01777 ERROR3IF(cc == NULL, "Can't find colour context?!"); 01778 01779 // Get the IndexedColour definition as a standalone colour definition 01780 cc->ConvertColour(pColour->FindParentIndexedColour(), &ColDef); 01781 01782 // Make the DocColour into a simple standalone "lookalike" of the parent colour 01783 *pColour = DocColour(pColour->GetColourModel(), &ColDef); 01784 } 01785 01786 pColour = pNodeAttr->EnumerateColourFields(Context++); 01787 } 01788 } 01789 pCurNode = pNextNode; 01790 } 01791 } 01792 01793 // --- Rip the node out of the tree and stick it in our cache. 01794 pNewGroup->UnlinkNodeFromTree(); 01795 01796 // Create a new spread & layer to stick the group into 01797 01798 Spread *pBrush = new Spread; 01799 if (pBrush == NULL) 01800 { 01801 ERROR3("Couldn't create new brush (4)"); 01802 pNewGroup->CascadeDelete(); 01803 delete pNewGroup; 01804 return; 01805 } 01806 01807 Layer *pBrushLayer = new Layer(pBrush, FIRSTCHILD, String_256(TEXT("Jason did this"))); 01808 if (pBrushLayer == NULL) 01809 { 01810 ERROR3("Couldn't create new brush (5)"); 01811 delete pBrush; 01812 pNewGroup->CascadeDelete(); 01813 delete pNewGroup; 01814 return; 01815 } 01816 01817 // And attach the clipart tree to our new layer 01818 pNewGroup->AttachNode(pBrushLayer, FIRSTCHILD, FALSE, TRUE); 01819 01820 StrokeDefinition *pStrokeDef = new StrokeDefinition(pBrush, Repeating); 01821 if (pStrokeDef == NULL) 01822 { 01823 ERROR3("Couldn't create new brush (6)"); 01824 pBrush->CascadeDelete(); 01825 delete pBrush; 01826 return; 01827 } 01828 01829 CurrentStroke = StrokeComponent::AddNewStroke(pStrokeDef); 01830 01831 // DebugTreeDlg::DumpSubTree(pBodgeBrush); 01832 }
|
|
|
|
** Call PrepareToStroke before calling this function ** Determines a TrapEdge Centre point and Normal to be used when mapping the given point into the destination path.
Definition at line 1103 of file pathstrk.cpp. 01105 { 01106 ERROR3IF(pCurrentEdgeList == NULL || pBounds == NULL, "Call PrepareToStroke first!"); 01107 ERROR3IF(pCoord == NULL || pOutput == NULL, "Illegal NULL params"); 01108 01109 // Find the parametric position of the coordinate in the source brush 01110 pOutput->Position = ((double)(pCoord->x - pBounds->lo.x)) / BoundsWidth; 01111 01112 // --- If this is a grad fill point, or somebody screwed up the brush bounds, then 01113 // the point could lie outside the "legal" range - but we must try to map it sensibly, 01114 // or else things like fills will screw up big time! 01115 if (pOutput->Position < 0.0 || pOutput->Position > 1.0) 01116 { 01117 // Find the nearest TrapEdge to the point, and adjust Position to be the "distance" from that TrapEdge 01118 TrapEdge *pEdge1; 01119 if (pOutput->Position > 1.0) 01120 { 01121 pEdge1 = pCurrentEdgeList->GetLastTrapEdge(); 01122 pOutput->Position -= 1.0; 01123 } 01124 else 01125 pEdge1 = pCurrentEdgeList->GetTrapEdge(0); 01126 01127 // Calculate a new centre position 01128 // (pretend that the path continues as an infinite straight line in its last known direction) 01129 const double PathLength = pCurrentEdgeList->GetPathLength(); 01130 pOutput->Centre.x = pEdge1->Centre.x - (INT32) (pOutput->Position * PathLength * (-pEdge1->Normal.y)); 01131 pOutput->Centre.y = pEdge1->Centre.y - (INT32) (pOutput->Position * PathLength * pEdge1->Normal.x); 01132 01133 // All the other variables are the same as the end TrapEdge 01134 pOutput->Normal = pEdge1->Normal; 01135 pOutput->PrevTrapJoin = pEdge1->PrevTrapJoin; 01136 01137 // Now clip the position value so the caller thinks we're within the stroke bounds 01138 // (Necessary so that they use the correct Position with Width ValueFunctions, etc) 01139 if (pOutput->Position < 0.0) 01140 pOutput->Position = 0.0; 01141 else 01142 pOutput->Position = 1.0; 01143 return; 01144 } 01145 01146 01147 // --- Find the trapezoid edges on either side of that position. Start from FirstEdge, 01148 // as that is where the path starts, so vastly reduces needless searching for 01149 // points lying within the path. (Note that fill points can lie outside the 01150 // path... see the if statement below) 01151 UINT32 TrapIndex = pCurrentEdgeList->FindTrapEdge(pOutput->Position, StartIndex, 0); 01152 TrapEdge *pEdge1 = pCurrentEdgeList->GetTrapEdge(TrapIndex); 01153 01154 // --- Generate the new TrapEdge information 01155 if (pOutput->Position <= pEdge1->Position || (TrapIndex >= pCurrentEdgeList->GetNumEdges() - 1)) 01156 { 01157 // The point lies exactly on the pEdge position, so return the exact edge 01158 // (The if statement checks for it lying outside the trapezoid, but this should never happen, 01159 // due to the position checks at the top of this function, so it's safe for us to use 01160 // a catch-all at this point. 01161 pOutput->Normal = pEdge1->Normal; 01162 pOutput->Centre = pEdge1->Centre; 01163 if (Direction > 0 && TrapIndex < pCurrentEdgeList->GetNumEdges() - 1) 01164 { 01165 TrapEdge *pEdge2 = pCurrentEdgeList->GetTrapEdge(TrapIndex+1); 01166 pOutput->PrevTrapJoin = pEdge2->PrevTrapJoin; 01167 } 01168 else 01169 pOutput->PrevTrapJoin = pEdge1->PrevTrapJoin; 01170 } 01171 else 01172 { 01173 // The point lies between 2 TrapEdges, so we interpolate the trapezoid centre and 01174 // normal to approximate a trapedge for this exact Position on the path 01175 TrapEdge *pEdge2 = pCurrentEdgeList->GetTrapEdge(TrapIndex+1); 01176 ERROR3IF(pEdge2->Position <= pEdge1->Position, "Non-ascending trapezoid positions?!"); 01177 01178 // Determine how far between the 2 positions the point lies 01179 const double Fraction = (pOutput->Position - pEdge1->Position) / (pEdge2->Position - pEdge1->Position); 01180 const double InvFraction = 1.0 - Fraction; 01181 01182 // Compute an interpolated normal 01183 pOutput->Normal.x = (InvFraction * pEdge1->Normal.x) + (Fraction * pEdge2->Normal.x); 01184 pOutput->Normal.y = (InvFraction * pEdge1->Normal.y) + (Fraction * pEdge2->Normal.y); 01185 pOutput->Normal.Normalise(); 01186 01187 // Compute an interpolated centreline point 01188 pOutput->Centre.x = (INT32) ((InvFraction * (double)pEdge1->Centre.x) + (Fraction * (double)pEdge2->Centre.x)); 01189 pOutput->Centre.y = (INT32) ((InvFraction * (double)pEdge1->Centre.y) + (Fraction * (double)pEdge2->Centre.y)); 01190 01191 // Duplicate any join flags. We must take care to replicate in the correct 01192 // direction or joins will be stroked incorrectly 01193 if (Direction > 0) 01194 pOutput->PrevTrapJoin = pEdge2->PrevTrapJoin; 01195 else 01196 pOutput->PrevTrapJoin = pEdge1->PrevTrapJoin; 01197 } 01198 }
|
|
Definition at line 1834 of file pathstrk.cpp. 01835 { 01836 return(CurrentStroke); 01837 }
|
|
** Call PrepareToStroke before calling this function ** Determines a rough scaling factor by which the stroke is changing size during the mapping process. Used to map things like LineWidth attributes so that their size remains roughly proportional to the new size of the stroke. The scale factor is based on the source and destination line widths, which may not always be brilliant, but is the best we can do.
Definition at line 1281 of file pathstrk.cpp. 01282 { 01283 if (BoundsHalfHeight < 1.0) // Avoid that nasty div by zero! 01284 return(1.0); 01285 01286 return( ((double) MaxWidth) / BoundsHalfHeight ); 01287 }
|
|
Maps the given bezier curve into the output stroke path.
Definition at line 1580 of file pathstrk.cpp. 01581 { 01582 RecursiveMapBezier(pCoords, &pCoords[0], 0.0, &pCoords[3], 1.0); 01583 }
|
|
** Call PrepareToStroke before calling this function ** Maps the given point into the output stroke path, and returns the new coordinate to be output at your discretion.
Note that only "floating" endpoints need to be mapped in this computationally expensive manner - midpoints are mapped elsewhere (by intersecting the source path with TrapEdges, and mapping on those exact intersections, thus eliminating the need for so much interpolation)
Definition at line 1237 of file pathstrk.cpp. 01238 { 01239 ERROR3IF(pCurrentEdgeList == NULL || pBounds == NULL, "Call PrepareToStroke first!"); 01240 01241 // Get a trapezoid edge description which lies exactly on this point's mapping Position 01242 TrapEdge MapEdge; 01243 FindEdgeForCoord(pCoord, FirstEdge, +1, &MapEdge); 01244 ERROR3IF(MapEdge.Position < 0.0 || MapEdge.Position > 1.0, "Position value was not properly clipped"); 01245 01246 // Calculate how far the point is from the Y centre of the brush, and scale 01247 // this distance by the variable width value for its Position 01248 const double SourceDist = (((double)pCoord->y) - BoundsYCentre) / BoundsHalfHeight; 01249 const double MappedDist = ((double)MaxWidth) * SourceDist * pWidthFunction->GetValue(MapEdge.Position); 01250 01251 // Map that width onto the destination path 01252 return(DocCoord(MapEdge.Centre.x + (INT32)(MapEdge.Normal.x * MappedDist), 01253 MapEdge.Centre.y + (INT32)(MapEdge.Normal.y * MappedDist) )); 01254 }
|
|
Maps the given line into the output stroke path Assumes that the first point (pCoord1) has already been output to the destination path (i.e. it appends the line to pOutput).
Definition at line 1327 of file pathstrk.cpp. 01328 { 01329 // Find the parametric positions of the coordinates in the source brush 01330 double Position1 = ((double)(pCoord1->x - pBounds->lo.x)) / BoundsWidth; 01331 double Position2 = ((double)(pCoord2->x - pBounds->lo.x)) / BoundsWidth; 01332 01333 // Work out the range of positions this line covers. If this is non-zero then we will map 01334 // a point at each trapezoid edge intersected by the line - otherwise, the line is vertical 01335 // and can just be output directly, so we don't bother interpolating 01336 const double PosRange = Position2 - Position1; 01337 if (PosRange == 0.0) 01338 { 01339 pOutput->AddLineTo(MapCoord(pCoord2)); 01340 return; 01341 } 01342 01343 // Find the trapezoid edge relating to the first position 01344 UINT32 TrapIndex = pCurrentEdgeList->FindTrapEdge(Position1, FirstEdge, LastEdge); 01345 ERROR3IF(TrapIndex < FirstEdge || TrapIndex > LastEdge, "TrapIndex outside expected range"); 01346 01347 UINT32 EndIndex = pCurrentEdgeList->FindTrapEdge(Position2, FirstEdge, LastEdge); 01348 ERROR3IF(EndIndex < FirstEdge || EndIndex > LastEdge, "Trap EndIndex outside expected range"); 01349 01350 // Calculate TrapEdge data for the first & last points 01351 TrapEdge FirstEdgeData; 01352 TrapEdge LastEdgeData; 01353 const INT32 Direction = (PosRange > 0.0) ? +1 : -1; 01354 FindEdgeForCoord(pCoord1, TrapIndex, Direction, &FirstEdgeData); 01355 FindEdgeForCoord(pCoord2, EndIndex, Direction, &LastEdgeData); 01356 01357 // Calculate the values for the 1st point 01358 TrapEdge *pLastEdge = &FirstEdgeData; 01359 double LastWidth = ((double)MaxWidth) * 01360 (((double)pCoord1->y - BoundsYCentre) / BoundsHalfHeight); 01361 double BaseWidth = LastWidth * pWidthFunction->GetValue(pLastEdge->Position); 01362 DocCoord LastPoint( (INT32) (pLastEdge->Centre.x + pLastEdge->Normal.x * BaseWidth), 01363 (INT32) (pLastEdge->Centre.y + pLastEdge->Normal.y * BaseWidth) ); 01364 01365 TrapEdge *pEdge = NULL; 01366 01367 // Now, map the line through each trapezoid in turn. There are 2 variants of this 01368 // loop - 1 for the "forwards" outline, and one for the "reverse" outline. 01369 if (PosRange > 0.0) 01370 { 01371 // Because TrapIndex and EndIndex point at the entry edge of the relevant trapezoids, 01372 // we want to increment them both to refer to the exit edge instead. 01373 TrapIndex++; 01374 01375 while (TrapIndex <= EndIndex) 01376 { 01377 pEdge = pCurrentEdgeList->GetTrapEdge(TrapIndex); 01378 01379 // Interpolate the line between its intersections with the sides of this trapezoid 01380 // The LastXXXX variables hold the intersection values for the entry-intersection, 01381 // so now we find the values for the exit-intersection 01382 const double Fraction = (pEdge->Position - Position1) / PosRange; 01383 const double Y = ((1.0 - Fraction) * (double)pCoord1->y) + (Fraction * (double)pCoord2->y); 01384 const double CentreDist = (Y - BoundsYCentre) / BoundsHalfHeight; 01385 BaseWidth = ((double)MaxWidth) * CentreDist; 01386 const double Width = BaseWidth * pWidthFunction->GetValue(pEdge->Position); 01387 01388 DocCoord NewPoint(pEdge->Centre.x + (INT32) (pEdge->Normal.x * Width), 01389 pEdge->Centre.y + (INT32) (pEdge->Normal.y * Width) ); 01390 01391 RecursiveMapLine(pLastEdge, LastPoint, LastWidth, 01392 pEdge, NewPoint, BaseWidth, 01393 +1, pOutput); 01394 01395 pLastEdge = pEdge; 01396 LastWidth = BaseWidth; 01397 LastPoint = NewPoint; 01398 01399 TrapIndex++; 01400 } 01401 } 01402 else 01403 { 01404 // Now, map the line through each trapezoid in turn 01405 // This condition is really nasty; Translation: while (TrapIndex has not passed EndIndex) 01406 while (TrapIndex > EndIndex) 01407 { 01408 pEdge = pCurrentEdgeList->GetTrapEdge(TrapIndex); 01409 01410 // Interpolate the line between its intersections with the sides of this trapezoid 01411 // The LastXXXX variables hold the intersection values for the entry-intersection, 01412 // so now we find the values for the exit-intersection 01413 const double Fraction = (pEdge->Position - Position1) / PosRange; 01414 const double Y = ((1.0 - Fraction) * (double)pCoord1->y) + (Fraction * (double)pCoord2->y); 01415 const double CentreDist = (Y - BoundsYCentre) / BoundsHalfHeight; 01416 BaseWidth = ((double)MaxWidth) * CentreDist; 01417 const double Width = BaseWidth * pWidthFunction->GetValue(pEdge->Position); 01418 01419 DocCoord NewPoint(pEdge->Centre.x + (INT32) (pEdge->Normal.x * Width), 01420 pEdge->Centre.y + (INT32) (pEdge->Normal.y * Width) ); 01421 01422 RecursiveMapLine(pLastEdge, LastPoint, LastWidth, 01423 pEdge, NewPoint, BaseWidth, 01424 -1, pOutput); 01425 01426 pLastEdge = pEdge; 01427 LastWidth = BaseWidth; 01428 LastPoint = NewPoint; 01429 01430 TrapIndex--; 01431 } 01432 } 01433 01434 // And then map the last trapezoid 01435 pEdge = &LastEdgeData; 01436 01437 // const double Fraction = (pEdge->Position - Position1) / PosRange; 01438 const double CentreDist = (pCoord2->y - BoundsYCentre) / BoundsHalfHeight; 01439 BaseWidth = ((double)MaxWidth) * CentreDist; 01440 const double Width = BaseWidth * pWidthFunction->GetValue(pEdge->Position); 01441 01442 DocCoord NewPoint(pEdge->Centre.x + (INT32) (pEdge->Normal.x * Width), 01443 pEdge->Centre.y + (INT32) (pEdge->Normal.y * Width) ); 01444 01445 RecursiveMapLine(pLastEdge, LastPoint, LastWidth, 01446 pEdge, NewPoint, BaseWidth, 01447 (PosRange > 0) ? +1 : -1, pOutput); 01448 }
|
|
Maps the given point into the output stroke path, writing a MOVETO element.
Definition at line 1304 of file pathstrk.cpp.
|
|
MUST be called before any calls to StrokePath or MapCoord.
Definition at line 902 of file pathstrk.cpp. 00903 { 00904 // Remember the new TrapsList 00905 ERROR3IF(pTraps == NULL, "Illegal NULL param"); 00906 pCurrentEdgeList = pTraps; 00907 00908 // And reset our edge markers to make sure they're in range and safe to use 00909 FirstEdge = LastEdge = 0; 00910 }
|
|
Maps the given bezier curve into the output stroke path, recursing as necessary to produce a nice smooth result.
p2 - The second point on the SOURCE curve t2 - The parametric position of p2 Notes: Always recurses to subdivide the curve into at least 2 segments, to guarantee that no features of the bezier are missed. Definition at line 1611 of file pathstrk.cpp. 01612 { 01613 // --- Watch out for infinite recursion. This can come about when there is a discontinuity 01614 // in the width function, where no amount of flattening will reduce the distance between p1 & p2 01615 RecursionDepth++; 01616 if (RecursionDepth > MaxRecursionDepth) 01617 { 01618 TRACEUSER( "Jason", _T(">> Recursion limit hit when stroking\n")); 01619 pOutput->AddLineTo(MapCoord(p2)); 01620 return; 01621 } 01622 01623 #define x0 (pCoords[0].x) 01624 #define y0 (pCoords[0].y) 01625 #define x1 (pCoords[1].x) 01626 #define y1 (pCoords[1].y) 01627 #define x2 (pCoords[2].x) 01628 #define y2 (pCoords[2].y) 01629 #define x3 (pCoords[3].x) 01630 #define y3 (pCoords[3].y) 01631 01632 // Calculate the point at the middle t value 01633 const double t = (t1 + t2) / 2.0; 01634 01635 const double tx = x1+t*(x2-x1); 01636 const double ty = y1+t*(y2-y1); 01637 01638 const double Lx1 = x0 + t*(x1-x0); 01639 const double Ly1 = y0 + t*(y1-y0); 01640 const double Rx2 = x2 + t*(x3-x2); 01641 const double Ry2 = y2 + t*(y3-y2); 01642 const double Lx2 = Lx1 + t*(tx-Lx1); 01643 const double Ly2 = Ly1 + t*(ty-Ly1); 01644 const double Rx1 = tx + t*(Rx2-tx); 01645 const double Ry1 = ty + t*(Ry2-ty); 01646 const double Rx0 = Lx2 + t*(Rx1-Lx2); 01647 const double Ry0 = Ly2 + t*(Ry1-Ly2); 01648 01649 DocCoord MidPoint((INT32)(Rx0+0.5), (INT32)(Ry0+0.5)); 01650 01651 #undef x0 01652 #undef y0 01653 #undef x1 01654 #undef y1 01655 #undef x2 01656 #undef y2 01657 #undef x3 01658 #undef y3 01659 01660 // If the distance between the actual point and the current approximation is too great, 01661 // we will recursively flatten. However, we make sure that we divide the bezier into at 01662 // least 2 sections, so if the difference in t parameters is too great, we set Dist 01663 // large enough to cause a recursive flattening. 01664 double Dist2 = Flatness2; 01665 01666 if (t2 - t1 < 0.45) 01667 { 01668 // calculate the mid point of the straight-line approximation 01669 DocCoord ApproxMidPoint(p1->x/2 + p2->x/2, p1->y/2 + p2->y/2); 01670 01671 // Now map both mid-points into the stroke envelope 01672 DocCoord MappedMid = MapCoord(&MidPoint); 01673 DocCoord MappedApprox = MapCoord(&ApproxMidPoint); 01674 01675 // And see how far the approximation is from the ideal position 01676 const double dx = MappedMid.x - MappedApprox.x; 01677 const double dy = MappedMid.y - MappedApprox.y; 01678 Dist2 = dx*dx + dy*dy; 01679 } 01680 01681 // If we're too far away, then flatten further, else map the flattened line segment 01682 if (Dist2 >= Flatness2) 01683 { 01684 RecursiveMapBezier(pCoords, p1, t1, &MidPoint, t); 01685 RecursiveMapBezier(pCoords, &MidPoint, t, p2, t2); 01686 } 01687 else 01688 MapLine(p1, p2); 01689 01690 // And decrement the recursion depth counter 01691 RecursionDepth--; 01692 }
|
|
Recursive method which maps points within a given trapezoid by recursively "flattening" the curve between the two trapezoid edges.
Direction - +1 when traversing the path in a forwards direction -1 when traversing the path in a backwards direction
Definition at line 1484 of file pathstrk.cpp. 01487 { 01488 ERROR3IF(pEdge1 == NULL || pEdge2 == NULL || pOutput == NULL, "Illegal NULL params"); 01489 01490 // --- Watch out for infinite recursion. This can come about when there is a discontinuity 01491 // in the width function, where no amount of flattening will reduce the distance between 01492 // Point1 and Point2. Note that we are flattening into a very small (already flattened) 01493 // region of space, so we don't need very great recursion depth to get a decent approximation 01494 // to the actual curve required, hence I stop the recursion after 20 iterations. 01495 RecursionDepth++; 01496 if (RecursionDepth > MaxRecursionDepth) 01497 { 01498 TRACEUSER( "Jason", _T(">> Recursion limit hit when stroking\n")); 01499 pOutput->AddLineTo(Point2); 01500 return; 01501 } 01502 01503 // --- Calculate the midpoint of the curve we are mapping 01504 // Calculate the midpoint "position" value 01505 TrapEdge MidEdge; 01506 MidEdge.Position = (pEdge1->Position + pEdge2->Position) / 2.0; 01507 01508 // Calculate the midpoint normal vector 01509 MidEdge.Normal.x = (pEdge1->Normal.x + pEdge2->Normal.x) / 2.0; 01510 MidEdge.Normal.y = (pEdge1->Normal.y + pEdge2->Normal.y) / 2.0; 01511 01512 // We should normalise the normal vector, but in the case of mitred/bevelled joins 01513 // we want a straight edge, which means leaving the normal vector with an interpolated 01514 // length as well as direction. This is indicated by the end-edge of the trapezoid 01515 // having the PrevTrapIsJoin flag set. We also have to duplicate the flag on all 01516 // intermediate points so that they are also handled correctly. 01517 // (Note in the 'forwards' case the relevant "PrevTrap" flag is in pEdge2) 01518 if (Direction > 0) 01519 MidEdge.PrevTrapJoin = pEdge2->PrevTrapJoin; 01520 else 01521 MidEdge.PrevTrapJoin = pEdge1->PrevTrapJoin; 01522 if (MidEdge.PrevTrapJoin != TrapJoin_MitredOrBevelled) 01523 MidEdge.Normal.Normalise(); 01524 01525 // Calculate the (approximate) centreline midpoint 01526 // (approximate because we're using the flattened line rather than the source curve, 01527 // but this is still close enough to give acceptable quality). 01528 // We take care with the average to avoid overflowing our INT32s 01529 MidEdge.Centre.x = (pEdge1->Centre.x / 2) + (pEdge2->Centre.x / 2); 01530 MidEdge.Centre.y = (pEdge1->Centre.y / 2) + (pEdge2->Centre.y / 2); 01531 01532 // Now, calculate the mapped midpoint 01533 const INT32 MidWidth = (INT32) ((Width1 + Width2) / 2.0); 01534 const INT32 MidWidthAll = (INT32) ((double)MidWidth * pWidthFunction->GetValue(MidEdge.Position)); 01535 DocCoord MidPoint(MidEdge.Centre); 01536 MidPoint.x += (INT32) (MidEdge.Normal.x * MidWidthAll); 01537 MidPoint.y += (INT32) (MidEdge.Normal.y * MidWidthAll); 01538 01539 // --- Calculate the midpoint of the straight line segment 01540 DocCoord ApproximateMidPoint(Point1.x/2 + Point2.x/2, Point1.y/2 + Point2.y/2); 01541 01542 // --- Calculate the error in the midpoint position 01543 const double dx = MidPoint.x - ApproximateMidPoint.x; 01544 const double dy = MidPoint.y - ApproximateMidPoint.y; 01545 const double Dist2 = dx * dx + dy * dy; 01546 01547 // --- If the straight-line approximation is not close enough to the curve, recurse, 01548 // else (recursion-tail) output the end-point that was passed in. We also recurse 01549 // if the Position distance between the endpoints is large, as otherwise it can 01550 // fail to flatten width functions properly. 01551 if ( Dist2>Flatness2 || fabs(pEdge1->Position-pEdge2->Position)>0.10 ) 01552 { 01553 // Recurse on the left and right sides of the new midpoint 01554 RecursiveMapLine(pEdge1, Point1, Width1, &MidEdge, MidPoint, MidWidth, Direction, pOutput); 01555 RecursiveMapLine(&MidEdge, MidPoint, MidWidth, pEdge2, Point2, Width2, Direction, pOutput); 01556 } 01557 else 01558 pOutput->AddLineTo(Point2); 01559 01560 // And decrement the recursion depth counter 01561 RecursionDepth--; 01562 }
|
|
** Call PrepareToStroke before calling this function ** "Strokes" a path according to the "style" set in construction.
Stroking is achieved by mapping the intersections of trapezoid edges with the width function, and then recursively flattening the mapped curve between those points until required "flatness" is achieved. Vector stroking is achieved by moulding path(s) to lie inside the stroke envelope, in order to produce media (brush, pen, crayon, etc) simulations. Notes: This function must be called individually for each source path of the brush, for each stroke in the trapezoid list. (This is to allow attributes to be mapped alongside, as the mappings are different for different subpaths of the path being stroked)
Definition at line 973 of file pathstrk.cpp. 00974 { 00975 ERROR3IF(pCurrentEdgeList == NULL || pBounds == NULL, "Call PrepareToStroke first!"); 00976 ERROR3IF(pSourceVector == NULL || pOutputPath == NULL, "Illegal NULL params"); 00977 00978 if (BoundsWidth <= 0.0 || BoundsHalfHeight <= 0.0) 00979 return(FALSE); 00980 00981 pPath = pSourceVector; 00982 pOutput = pOutputPath; 00983 00984 // Precalculate the position values for the extents of the path within the brush 00985 DocRect PathBounds = pPath->GetBoundingRect(); 00986 const double MinPathPos = ((double)(PathBounds.lo.x - pBounds->lo.x)) / BoundsWidth; 00987 const double MaxPathPos = ((double)(PathBounds.hi.x - pBounds->lo.x)) / BoundsWidth; 00988 00989 // When doing repeating brushes, the last repeat can be clipped at the end of the path, 00990 // so we check if this entire path should be clipped off 00991 // BLOCK 00992 { 00993 TrapEdge *pLastEdge = pCurrentEdgeList->GetLastTrapEdge(); 00994 if (pLastEdge != NULL && MinPathPos > pLastEdge->Position) 00995 return(TRUE); 00996 } 00997 00998 // For each curve segment in the source path, we output a mapped curve, which 00999 // consists of a number of flattened straight line segments. 01000 DocCoord *pCoords = pPath->GetCoordArray(); 01001 PathVerb *pVerbs = pPath->GetVerbArray(); 01002 const INT32 NumCoords = pPath->GetNumCoords(); 01003 01004 // Determine the range of trapezoids this path will map into in the current list 01005 // This makes searching for appropriate trapezoids much more efficient. 01006 FirstEdge = pCurrentEdgeList->FindTrapEdge(MinPathPos, 0, 0); 01007 LastEdge = pCurrentEdgeList->FindTrapEdge(MaxPathPos, FirstEdge, pCurrentEdgeList->GetNumEdges() - 1); 01008 01009 // And map the path into this stroke 01010 INT32 Element = 0; 01011 while (Element < NumCoords) 01012 { 01013 switch (pVerbs[Element] & ~PT_CLOSEFIGURE) 01014 { 01015 case PT_MOVETO: 01016 MapMove(&pCoords[Element]); 01017 break; 01018 01019 case PT_LINETO: 01020 { 01021 ERROR3IF(Element < 1, "Path has LINETO as first element?!"); 01022 01023 RecursionDepth = 0; // Reset recursion depth counter to 0 01024 MapLine(&pCoords[Element-1], &pCoords[Element]); 01025 01026 if ((pVerbs[Element] & PT_CLOSEFIGURE) != 0) 01027 { 01028 // If this was a CLOSEFIGURE verb, then set the last output point to be CloseFigure 01029 PathVerb *pOutVerbs = pOutput->GetVerbArray(); 01030 ERROR3IF(pOutVerbs == NULL || pOutput->GetNumCoords() < 1, "Oh dear. That shouldn't be like that"); 01031 pOutVerbs[pOutput->GetNumCoords() - 1] |= PT_CLOSEFIGURE; 01032 } 01033 } 01034 break; 01035 01036 case PT_BEZIERTO: 01037 { 01038 ERROR3IF(Element < 1, "Path has BEZIERTO as first element?!"); 01039 01040 RecursionDepth = 0; // Reset recursion depth counter to 0 01041 MapBezier(&pCoords[Element-1]); 01042 01043 if ((pVerbs[Element+2] & PT_CLOSEFIGURE) != 0) 01044 { 01045 // If this was a CLOSEFIGURE verb, then set the last output point to be CloseFigure 01046 PathVerb *pOutVerbs = pOutput->GetVerbArray(); 01047 ERROR3IF(pOutVerbs == NULL || pOutput->GetNumCoords() < 1, "Oh dear. That shouldn't be like that"); 01048 pOutVerbs[pOutput->GetNumCoords() - 1] |= PT_CLOSEFIGURE; 01049 } 01050 01051 Element += 2; // Skip the 2 bezier control coordinates 01052 } 01053 break; 01054 01055 default: 01056 ERROR3("PathStrokerVector::StrokePath() - unknown path verb!"); 01057 break; 01058 } 01059 01060 Element++; 01061 } 01062 01063 pOutput->IsFilled = pSourceVector->IsFilled; 01064 pOutput->IsStroked = pSourceVector->IsStroked; 01065 01066 return(TRUE); 01067 }
|
|
DO NOT CALL this variant of StrokePath!
Reimplemented from PathStroker. Definition at line 925 of file pathstrk.cpp.
|
|
Definition at line 256 of file pathstrk.h. |
|
Definition at line 255 of file pathstrk.h. |
|
Definition at line 257 of file pathstrk.h. |
|
Definition at line 248 of file pathstrk.h. |
|
Definition at line 262 of file pathstrk.h. |
|
Definition at line 263 of file pathstrk.h. |
|
Definition at line 254 of file pathstrk.h. |
|
Definition at line 261 of file pathstrk.h. |
|
Definition at line 259 of file pathstrk.h. |
|
Definition at line 253 of file pathstrk.h. |