#include <tracergn.h>
Inheritance diagram for TraceRegion:

Public Member Functions | |
| TraceRegion () | |
| Sets intial values up. | |
| ~TraceRegion () | |
| Destructs object. | |
| BOOL | UseBitmap (KernelBitmap *pBitmap) |
| Points the TraceRegion at a particular bitmap and claims the relevant size buffers. | |
| BOOL | UsePath (Path *pPath) |
| Specifies we should use a particular path. The path is initialised, cleared etc. | |
| BOOL | Trace (INT32 InitialX, INT32 InitialY, BfxPixelOp *thepBfxPixelOp) |
| Does a single boundary trace. | |
| BOOL | SetParams (double *pPixelError=NULL) |
| Sets the tracing parameters. | |
| BOOL | GetParams (double *pPixelError=NULL) |
| Sets the tracing parameters. | |
Static Public Member Functions | |
| static void | Test (UndoableOperation *Op) |
| Internal test routine. | |
Protected Member Functions | |
| BOOL | TraceBoundary (DocCoord Origin, DocCoord Point1, DocCoord Point2) |
| Does a single boundary trace. | |
| BOOL | FillBoundaryBuffer (BOOL *End) |
| Fills up the boundary buffer. | |
| BOOL | ProcessBoundaryBuffer (BOOL Done=FALSE) |
| Empties the boundary buffer. | |
| BOOL | FindInitialPoint (BOOL *End) |
| Finds an initial point to start at. | |
| void | FitCubic (INT32 FirstPoint, INT32 LastPoint, TraceBoundaryPoint Tangent1, TraceBoundaryPoint Tangent2, BOOL IsStartCusp, BOOL IsEndCusp) |
| This function is recursive. It tries to fit a cubic function to the coordinates in the path between FirstPoint and LastPoint. It then compares this function with the actual coordinates to determine how good a fit it has found. If the fit is good it is added to the path object. If the fit is bad then it is split at the point where the fit is at its worst and FitCubic is called again for the left and right halves. Scope: private. | |
| void | GenerateBezier (INT32 FirstPoint, INT32 LastPoint, TraceBoundaryPoint Tangent1, TraceBoundaryPoint Tangent2, TraceBoundaryPoint *Bezier) |
| This function supplies the maths to try and fit a cubic function to a set of points. It spends its time trying to come up with good lengths for the two tangents passed in to maximise the closeness of the fit. If it fails to come up with realistic results it simply sets the tangent lengths to be 1/3 of the distance between the start point and the end point. Scope: private. | |
| TraceBoundaryPoint | BezierPoint (TraceBoundaryPoint *Bez, double u) |
| This function simply evaluates the bezier function for a given position and is used to help when determining how good a fit we have obtained Scope: private. | |
| BOOL | CalcMaxError (INT32 LeftPoint, INT32 RightPoint, TraceBoundaryPoint *Bezier, INT32 *SplitPoint, double *MaxDist, INT32 Level) |
| Finds out how good a fit the bezier curve we have created is when compared with the data points Scope: private. | |
| void | Parameterize (INT32 FirstPoint, INT32 LastPoint) |
| This function determines the TPoints which will be fitted (i.e. the trace boundary points we use) and the TValues which will be assigned to them (i.e. the t values along the chord length to which the points correspond). Scope: private. | |
| TraceBoundaryPoint | LeftTangent (INT32 Start, INT32 Points) |
| Finds the Left tangent at the given point in the path Scope: private. | |
| TraceBoundaryPoint | RightTangent (INT32 End, INT32 Points) |
| Finds the Right tangent at the given point in the path Scope: private. | |
| TraceBoundaryPoint | CentreTangent (INT32 Centre, INT32 Points) |
| Finds the tangent at the centre of the path Scope: private. | |
| void | InsertBezier (TraceBoundaryPoint *Bezier, BOOL IsStartCusp, BOOL IsEndCusp) |
| Adds the bezier curve to the path. If it is that last curve in the fitting (ie Depth is 0) then the rotate flag will not be set Scope: private. | |
| void | InsertLine (const DocCoord &Start, const DocCoord &End, BOOL IsStartCusp, BOOL IsEndCusp) |
| Inserts a straight curve into the path. It keeps it as a curve so that the path will continue to look smooth after it is edited. If this is the last Path element in the fit, (ie Depth is zero) then the rotate flag will not be set. Scope: private. | |
| void | InsertStraightLine (const DocCoord &End) |
| Inserts a straight Line into the path. This is used in cases where the Line segment must be kept as a straight line (ie by using Straight Line Mode of the FreeHand tool) Scope: private. | |
| double | Bezier0 (double u) |
| double | Bezier1 (double u) |
| double | Bezier2 (double u) |
| double | Bezier3 (double u) |
| BOOL | IsCuspDirectionChange (INT32 dir1, INT32 dir2) |
Protected Attributes | |
| TraceBoundaryPoint * | BoundaryBuffer |
| BfxPixelOp * | pBfxPixelOp |
| KernelBitmap * | Bitmap |
| INT32 | xsize |
| INT32 | ysize |
| TraceBoundaryPoint | InitialPoint |
| TraceBoundaryPoint | HeadPoint |
| INT32 | InitialDirection |
| INT32 | TailDirection |
| INT32 | HeadDirection |
| TraceBoundaryPoint | FirstBufferPoint |
| INT32 | BoundaryTail |
| INT32 | BoundaryHead |
| INT32 | BoundaryRingMask |
| INT32 | BoundaryRingSize |
| double | Error |
| double | PixelError |
| BOOL | VirginBuffer |
| BOOL | VirginPath |
| BOOL | AtCusp |
| BOOL | TailCusp |
| Path * | ThePath |
| TraceBoundaryPoint | Directions [(0x7)+1] |
| TraceBoundaryPoint | DirectionPoint [(0x7)+1][(0x7)+1][4] |
| double | TValues [10+1] |
| INT32 | TPoints [10+1] |
| TraceBoundaryPoint | Q1 [3] |
| TraceBoundaryPoint | Q2 [2] |
| INT32 | NumTPoints |
Private Member Functions | |
| CC_DECLARE_DYNCREATE (TraceRegion) | |
Definition at line 318 of file tracergn.h.
|
|
Sets intial values up.
Definition at line 165 of file tracergn.cpp. 00166 { 00167 pBfxPixelOp = NULL; 00168 BoundaryBuffer = NULL; 00169 BoundaryRingSize = 0; 00170 BoundaryRingMask =0; 00171 Bitmap = NULL; 00172 xsize = 1; 00173 ysize = 1; 00174 VirginBuffer = TRUE; 00175 AtCusp = FALSE; 00176 TailCusp = FALSE; 00177 TailDirection = -1; 00178 ThePath = NULL; 00179 00180 double TheError = 1.00; 00181 SetParams(&TheError); 00182 00183 /* 00184 In each direction of travel we always leave with a particular corner of the pixel set. In some directinos 00185 of travel, we have to set 2 edges. Here goes: 00186 00187 ->1-X 00188 | | This is how an east going trace is represented. The top edge is the one we need to fill in. 00189 | | 00190 ---- 00191 00192 ->1- 00193 | V This is how a SE going trace is represented. We have 2 edges to fill in this time. 00194 | 2 00195 ---- 00196 00197 When there is a change in direction, things become more complex. Let us assume that the current 00198 direction is east, then note (for the following new directions) how will fill be buffer in: 00199 00200 Current direction East Current direction south east: 00201 00202 ->1-X ->1-X 00203 | | E | | E 00204 | | | | 00205 ---- ---- 00206 00207 ->1- ->1- 00208 | V SE | V SE 00209 | 2 | 2 00210 ----X ----X 00211 00212 ->1-C ->1- 00213 | V S | V S 00214 | 2 | 2 00215 ----X ----X 00216 00217 ->1-C ->1- 00218 | V SW | V SW 00219 | 2 | 2 00220 X-<3- X-<3-C 00221 00222 ->1-C ->1- 00223 | V W | V W 00224 | 2 | 2 00225 X-<3-(C) X-<3-C 00226 00227 X---- ->1- 00228 | | NW (theoretically impossible - no edges) 4 V NW 00229 | | ^ 2 00230 ---- -<3-C 00231 00232 C---- X---- 00233 | | N (no edges (correct place)) | | N (theoretically impossible) 00234 | | | | 00235 ---- ---- 00236 00237 ->1- ->1-X 00238 | | NE | | NE 00239 | | | | 00240 ---- ---- 00241 00242 00243 Note how strikingly similar these are! We use the RH column one as that's generic for both sides. 00244 00245 */ 00246 00247 00248 00249 00250 // Grrr... This should be static but lovely C++ wont let me compile it. Thanks a lot C++ 00251 // Heh what's 128 bytes when you have a bitmap to trace 00252 Directions[0].Init( (1<<8), (0<<8)); // East 00253 Directions[1].Init( (1<<8),-(1<<8)); // South East 00254 Directions[2].Init( (0<<8),-(1<<8)); // South 00255 Directions[3].Init(-(1<<8),-(1<<8)); // South West 00256 Directions[4].Init(-(1<<8), (0<<8)); // West 00257 Directions[5].Init(-(1<<8), (1<<8)); // North West 00258 Directions[6].Init( (0<<8), (1<<8)); // North 00259 Directions[7].Init( (1<<8), (1<<8)); // North East 00260 00261 // First sort out the non-diagonal table 00262 DirectionPoint[0][0][0].Init( (1<<7), (1<<7)); // East to East 00263 DirectionPoint[0][0][1].Init( -1, -1); // No point 2 00264 DirectionPoint[0][0][2].Init( -1, -1); // No point 3 00265 DirectionPoint[0][0][3].Init( -1, -1); // No point 4 00266 00267 DirectionPoint[0][1][0].Init( (1<<7), (1<<7)); // East to SE 00268 DirectionPoint[0][1][1].Init( (1<<7),-(1<<7)); // point 2 00269 DirectionPoint[0][1][2].Init( -1, -1); // No point 3 00270 DirectionPoint[0][1][3].Init( -1, -1); // No point 4 00271 00272 DirectionPoint[0][2][0].Init( (1<<7), (1<<7)); // East to S 00273 DirectionPoint[0][2][1].Init( -1, 2); // No point 2, go south 00274 DirectionPoint[0][2][2].Init( -1, -1); // No point 3 00275 DirectionPoint[0][2][3].Init( -1, -1); // No point 4 00276 00277 DirectionPoint[0][3][0].Init( (1<<7), (1<<7)); // East to SW 00278 DirectionPoint[0][3][1].Init( -1, 3); // No point 2, go SW 00279 DirectionPoint[0][3][2].Init( -1, -1); // point 3 00280 DirectionPoint[0][3][3].Init( -1, -1); // No point 4 00281 00282 DirectionPoint[0][4][0].Init( (1<<7), (1<<7)); // East to W 00283 DirectionPoint[0][4][1].Init( -1, 2); // No point 2, go S 00284 DirectionPoint[0][4][2].Init( -1, -1); // point 3 00285 DirectionPoint[0][4][3].Init( -1, -1); // No point 4 00286 00287 DirectionPoint[0][5][0].Init( (1<<7), (1<<7)); // South East to NW 00288 DirectionPoint[0][5][1].Init( (1<<7),-(1<<7)); // point 2 00289 DirectionPoint[0][5][2].Init( -1, 5); // No point 3, go NW 00290 DirectionPoint[0][5][3].Init( -1, -1); // No point 4 00291 00292 DirectionPoint[0][6][0].Init( -1, 6); // No point 1, go N 00293 DirectionPoint[0][6][1].Init( -1, -1); // No point 2 00294 DirectionPoint[0][6][2].Init( -1, -1); // No point 3 00295 DirectionPoint[0][6][3].Init( -1, -1); // No point 4 00296 00297 DirectionPoint[0][7][0].Init( (1<<7), (1<<7)); // East to NE 00298 DirectionPoint[0][7][1].Init( -1, -1); // No point 2 00299 DirectionPoint[0][7][2].Init( -1, -1); // No point 3 00300 DirectionPoint[0][7][3].Init( -1, -1); // No point 4 00301 00302 00303 00304 DirectionPoint[1][0][0].Init( (1<<7), (1<<7)); // South East to East 00305 DirectionPoint[1][0][1].Init( -1, -1); // No point 2 00306 DirectionPoint[1][0][2].Init( -1, -1); // No point 3 00307 DirectionPoint[1][0][3].Init( -1, -1); // No point 4 00308 00309 DirectionPoint[1][1][0].Init( (1<<7), (1<<7)); // South East to SE 00310 DirectionPoint[1][1][1].Init( (1<<7),-(1<<7)); // point 2 00311 DirectionPoint[1][1][2].Init( -1, -1); // No point 3 00312 DirectionPoint[1][1][3].Init( -1, -1); // No point 4 00313 00314 DirectionPoint[1][2][0].Init( (1<<7), (1<<7)); // South East to S 00315 DirectionPoint[1][2][1].Init( (1<<7),-(1<<7)); // point 2 00316 DirectionPoint[1][2][2].Init( -1, -1); // No point 3 00317 DirectionPoint[1][2][3].Init( -1, -1); // No point 4 00318 00319 DirectionPoint[1][3][0].Init( (1<<7), (1<<7)); // South East to SW 00320 DirectionPoint[1][3][1].Init( (1<<7), (0<<7)); // point 3 00321 DirectionPoint[1][3][2].Init( -1, 3); // No point 4, go SW 00322 DirectionPoint[1][3][3].Init( -1, -1); // No point 4 00323 00324 DirectionPoint[1][4][0].Init( (1<<7), (1<<7)); // South East to W 00325 DirectionPoint[1][4][1].Init( (1<<7),-(1<<7)); // point 2 00326 DirectionPoint[1][4][2].Init( -1, 4); // No point 3, go W 00327 DirectionPoint[1][4][3].Init( -1, -1); // No point 4 00328 00329 DirectionPoint[1][5][0].Init( (1<<7), (1<<7)); // South East to NW 00330 DirectionPoint[1][5][1].Init( (1<<7),-(1<<7)); // point 2 00331 DirectionPoint[1][5][2].Init( -1, 5); // No point 3, go NW 00332 DirectionPoint[1][5][3].Init( -1, -1); // No point 4 00333 00334 DirectionPoint[1][6][0].Init( -1, 6); // No point 1, go N 00335 DirectionPoint[1][6][1].Init( -1, -1); // No point 2 00336 DirectionPoint[1][6][2].Init( -1, -1); // No point 3 00337 DirectionPoint[1][6][3].Init( -1, -1); // No point 4 00338 00339 DirectionPoint[1][7][0].Init( (0<<7), (1<<7)); // South East to NE 00340 DirectionPoint[1][7][1].Init( -1, 7); // No point 2, go NE 00341 DirectionPoint[1][7][2].Init( -1, -1); // No point 3 00342 DirectionPoint[1][7][3].Init( -1, -1); // No point 4 00343 00344 INT32 i; 00345 INT32 j; 00346 INT32 k; 00347 for (i=2; i<=7; i+=1) for (j=0; j<=7; j++) for (k=0; k<=3; k++) 00348 { 00349 // Go from direction i to direction j, i even 00350 INT32 j2 = (j-(i & 0x6)) & TR_NUMDIRECTIONMASK; 00351 DirectionPoint[i][j][k]=DirectionPoint[i & 0x1][j2][k]; 00352 00353 if (DirectionPoint[i][j][k].x != -1) 00354 { 00355 DirectionPoint[i][j][k].RotateDirection(Directions[(i & 0x6)]); 00356 } 00357 else 00358 { 00359 if (DirectionPoint[i][j][k].y != -1) 00360 DirectionPoint[i][j][k].y = (((INT32)(DirectionPoint[(i & 0x1)][j2][k].y) + (i & 0x6)) & TR_NUMDIRECTIONMASK); 00361 } 00362 } 00363 00364 TraceBoundaryPoint Centre; 00365 Centre.Init((1<<7),(1<<7)); 00366 for (i=0; i<=7; i+=1) for (j=0; j<=7; j++) for (k=0; k<=3; k++) 00367 { 00368 if (DirectionPoint[i][j][k].x != -1) 00369 { 00370 DirectionPoint[i][j][k].translate(Centre); 00371 } 00372 } 00373 00374 }
|
|
|
Destructs object.
Definition at line 505 of file tracergn.cpp. 00506 { 00507 if (BoundaryBuffer) CCFree(BoundaryBuffer); 00508 // Clear some hanging pointers 00509 BoundaryBuffer = NULL; 00510 Bitmap = NULL; 00511 ThePath = NULL; 00512 }
|
|
|
Definition at line 373 of file tracergn.h.
|
|
|
Definition at line 374 of file tracergn.h.
|
|
|
Definition at line 375 of file tracergn.h.
|
|
|
Definition at line 376 of file tracergn.h.
|
|
||||||||||||
|
This function simply evaluates the bezier function for a given position and is used to help when determining how good a fit we have obtained Scope: private.
Definition at line 1383 of file tracergn.cpp. 01384 { 01385 double OneMinus = 1.0-u; 01386 double uSquared = u*u; 01387 double OneMinusSquared = OneMinus*OneMinus; 01388 01389 TraceBoundaryPoint Coord; 01390 Coord = Bez[0]*(OneMinusSquared*OneMinus); 01391 Coord = Coord + Bez[1]*(3.0*u*OneMinusSquared); 01392 Coord = Coord + Bez[2]*(3.0*uSquared*OneMinus); 01393 Coord = Coord + Bez[3]*(uSquared*u); 01394 01395 return Coord; 01396 }
|
|
||||||||||||||||||||||||||||
|
Finds out how good a fit the bezier curve we have created is when compared with the data points Scope: private.
Cunningly we move the TValues about if we think they are salvageable. Definition at line 1426 of file tracergn.cpp. 01428 { 01429 TraceBoundaryPoint Q_t, Q1_t, Q2_t; // t evaluated at Q, Q' & Q'' 01430 INT32 MidPoint = (LeftPoint + RightPoint+1)/2; // where we are going to evaluate it 01431 double Distance; 01432 01433 if (!((MidPoint == LeftPoint) || (MidPoint==RightPoint))) 01434 { 01435 01436 for (INT32 iterations=1; iterations<=3; iterations++) 01437 { 01438 double t = TValues[MidPoint]; 01439 TraceBoundaryPoint pt = BoundaryBuffer[TPoints[MidPoint]]; 01440 01441 // Calculate where the curve actually is and find the distance from where we want it 01442 Distance = (BezierPoint(Bezier, t) - pt).SquaredLength(); 01443 01444 if ((Distance > *MaxDist) && (Distance < Error * 2.0)) 01445 // If this is would be the new winner, and it's salvageable (less than twice as far away as we 01446 // we want), recalculate a better TValue 01447 { 01448 01449 // Evaluate Q, Q' & Q'' 01450 double OneMinus = 1.0-t; 01451 double tSquared = t*t; 01452 double OneMinusSquared = OneMinus*OneMinus; 01453 01454 Q_t = Bezier[0] *(OneMinusSquared*OneMinus) + Bezier[1]*(3.0*t*OneMinusSquared) 01455 +Bezier[2] *(3.0*tSquared*OneMinus) + Bezier[3]*(tSquared*t); 01456 Q1_t = Q1[0]*(OneMinusSquared) + Q1[1]*(2.0*t*OneMinus) + Q1[2]*(tSquared); 01457 Q2_t = Q2[0]*(OneMinus) + Q2[1]*(t); 01458 01459 // Now Newton Raphson tprime = t - f(t)/f'(t) 01460 TraceBoundaryPoint QtMinuspt = Q_t - pt; 01461 01462 // changed by Ed Cornes 31/10/95 in an attempt to remove an itermittent floating point exception in the tracer 01463 // t = t - ( (QtMinuspt.x)*(Q1_t.x) + (QtMinuspt.y) * (Q1_t.y) ) / 01464 // ( Q1_t.SquaredLength() + ((QtMinuspt.x) * (Q2_t.x) + (QtMinuspt.y) * (Q2_t.y)) ); 01465 double denom = Q1_t.SquaredLength() + ((QtMinuspt.x) * (Q2_t.x) + (QtMinuspt.y) * (Q2_t.y)); 01466 if (fabs(denom)<1e-50) 01467 denom = (denom<0) ? -1e-50 : 1e-50; 01468 01469 t = t - ( (QtMinuspt.x)*(Q1_t.x) + (QtMinuspt.y) * (Q1_t.y) ) / denom; 01470 if (t<0) t=0; 01471 if (t>1) t=1; 01472 //TRACEUSER( "Alex", _T("Point %f moves to %f\n"),TValues[MidPoint],t); 01473 TValues[MidPoint] = t; 01474 } 01475 else break; // Leave 'for' loop - point OK or point not salvageable 01476 } 01477 01478 if ( Distance >= *MaxDist) 01479 { 01480 *MaxDist = Distance; 01481 *SplitPoint = MidPoint; 01482 } 01483 01484 // If we haven't got a point to split at yet, or if we're in the 4 first points, 01485 // recurse down. This ensures we do 8 points. 01486 if ((*MaxDist<=Error) || (Level<3)) CalcMaxError(LeftPoint, MidPoint, 01487 Bezier, SplitPoint, MaxDist, Level+1); 01488 if ((*MaxDist<=Error) || (Level<3)) CalcMaxError(MidPoint, RightPoint, 01489 Bezier, SplitPoint, MaxDist, Level+1); 01490 01491 } 01492 return(*MaxDist>Error); 01493 }
|
|
|
|
|
||||||||||||
|
Finds the tangent at the centre of the path Scope: private.
Definition at line 1595 of file tracergn.cpp. 01596 { 01597 TraceBoundaryPoint Left, Right; 01598 TraceBoundaryPoint Tangent(0,0); 01599 01600 Points = 1+MIN (Points/2,MAXTANGENTPOINTS); 01601 if (Points<2) Points=2; 01602 01603 // Make sure that is not of zero length 01604 while ((Tangent.x==0) && (Tangent.y==0)) 01605 { 01606 if ((--Points)>0) 01607 { 01608 // Calc right tangent 01609 Left.x = BoundaryBuffer[Centre-Points].x - BoundaryBuffer[Centre].x; 01610 Left.y = BoundaryBuffer[Centre-Points].y - BoundaryBuffer[Centre].y; 01611 01612 // Calc left tangent 01613 Right.x = BoundaryBuffer[Centre].x - BoundaryBuffer[Centre+Points].x; 01614 Right.y = BoundaryBuffer[Centre].y - BoundaryBuffer[Centre+Points].y; 01615 01616 // Average to get the centre tangent 01617 Tangent.x = (Left.x + Right.x) / 2; 01618 Tangent.y = (Left.y + Right.y) / 2; 01619 01620 } 01621 else 01622 { 01623 ERROR3("Tangent was a zero length vector in the curve fitter (Centre)"); 01624 Tangent.x = -1; 01625 } 01626 } 01627 01628 return Tangent; 01629 }
|
|
|
Fills up the boundary buffer.
It also stops if it meets a cusp, or the intial point in the initial direction. Definition at line 824 of file tracergn.cpp. 00825 { 00826 INT32 NewDirection; 00827 INT32 Turns; 00828 00829 ERROR2IF((!Bitmap),FALSE,"TraceRegion incorrectly initialised"); 00830 00831 *End=FALSE; 00832 while (BoundaryHead<BoundaryRingSize - 6) // there is enough room in buffer 00833 { 00834 // Calculate the new direction. We start looking at direction-2 but as we add one before we start 00835 // we have -3. If we're at a cusp we know the NewDirection set to HeadDirection is the correct place, 00836 // i.e. we want +0, but because we add one before we start, we want -1. 00837 NewDirection = (HeadDirection-(AtCusp?1:3)) & TR_NUMDIRECTIONMASK; 00838 //NewDirection = (HeadDirection-3) & TR_NUMDIRECTIONMASK; 00839 Turns=0; 00840 TraceBoundaryPoint NewPoint=HeadPoint; // Initialisation unnecessary but quiets the compiler 00841 00842 00843 do 00844 { 00845 // This should only return(TRUE) if asked to trace a 00846 // one pixel shape - else we must have got to this pixel some how. 00847 if ((Turns++)==8) 00848 { 00849 *End=TRUE; 00850 // We want to force it to be the 4 corner case 00851 NewDirection=((HeadDirection & 0x6) + 6) & TR_NUMDIRECTIONMASK; 00852 ERROR3("One pixel regions NYI"); 00853 // The above won't work properly because it's not properly entered. I think we need a horrible 00854 // goto here. 00855 break; 00856 } 00857 // Find next direction 00858 NewDirection = (NewDirection + 1) & TR_NUMDIRECTIONMASK; 00859 NewPoint = HeadPoint; // Calculate the new point 00860 NewPoint.translate(Directions[NewDirection]); 00861 } while (!pBfxPixelOp->IsInRegion(((INT32)(NewPoint.x))>>8, ((INT32)(NewPoint.y))>>8)); 00862 00863 if (VirginBuffer) InitialDirection = TailDirection = NewDirection; 00864 00865 00866 // stick point in ring buffer 00867 // Limit is such that we know we can fit the maximum of 4 points in 00868 INT32 HintPoint=0; 00869 if (!VirginBuffer) 00870 { 00871 if (AtCusp) 00872 { 00873 // Only put the last point in if at a cusp 00874 while ((DirectionPoint[HeadDirection][NewDirection][HintPoint].x != -1) // sufficient check 00875 && (HintPoint<4)) {HintPoint++;} // Don't inc at final point 00876 BoundaryBuffer[BoundaryHead] = HeadPoint; 00877 BoundaryBuffer[BoundaryHead++].translate(DirectionPoint[HeadDirection][NewDirection][HintPoint-1]); 00878 // HintPoint still points at terminator 00879 } 00880 else 00881 { 00882 // Put all points in 00883 while ((DirectionPoint[HeadDirection][NewDirection][HintPoint].x != -1) // sufficient check 00884 && (HintPoint<4)) 00885 { 00886 BoundaryBuffer[BoundaryHead] = HeadPoint; 00887 BoundaryBuffer[BoundaryHead++].translate(DirectionPoint[HeadDirection][NewDirection][HintPoint++]); 00888 } 00889 } 00890 } else while ((DirectionPoint[HeadDirection][NewDirection][HintPoint].x != -1) // sufficient check 00891 && (HintPoint<4)) {HintPoint++;} // Don't inc at final point 00892 00893 00894 if (DirectionPoint[HeadDirection][NewDirection][HintPoint].y !=-1) 00895 { 00896 HeadDirection = (INT32)(DirectionPoint[HeadDirection][NewDirection][HintPoint].y); 00897 // HeadPoint = NewPoint; 00898 AtCusp = TRUE; 00899 if (TailDirection == -1) TailDirection = HeadDirection; 00900 // VirginBuffer = FALSE; 00901 return (TRUE); 00902 } 00903 00904 // check for cusp a with dirty unsigned arithmetic and return NOW if we are there. 00905 // AtCusp = ( ( ((unsigned)(Turns-2)) >=3) && (!AtCusp)); 00906 AtCusp = FALSE; 00907 00908 //if (AtCusp) return (TRUE); // cusp return 00909 00910 00911 if ( (HeadPoint == InitialPoint) && (NewDirection == InitialDirection) && !VirginBuffer ) 00912 { 00913 *End = TRUE; 00914 VirginBuffer = FALSE; 00915 return (TRUE); 00916 } 00917 00918 VirginBuffer = FALSE; 00919 00920 if (TailDirection == -1) TailDirection = NewDirection; 00921 00922 // Set up member vars 00923 HeadPoint = NewPoint; 00924 HeadDirection = NewDirection; 00925 00926 } 00927 00928 return(TRUE); // Out of buffer space, go process some & come back 00929 }
|
|
|
Finds an initial point to start at.
The boundary buffer is cleared. An initial point will be found if possible and End cleared. If one is not found, End will be set. Definition at line 650 of file tracergn.cpp. 00651 { 00652 INT32 x; 00653 INT32 y; 00654 00655 ERROR2IF((!Bitmap),FALSE,"How about giving us a bitmap first then?"); 00656 00657 InitialDirection = 0; // East 00658 VirginBuffer = TRUE; 00659 BoundaryHead =0; 00660 BoundaryTail =0; 00661 TailDirection = -1; 00662 HeadDirection = 0; 00663 AtCusp=FALSE; 00664 TailCusp = FALSE; 00665 *End = FALSE; 00666 00667 for (y = ysize-1; y>=0; y--) for (x = 0; x < xsize; x++) if (pBfxPixelOp->IsInRegion(x,y)) 00668 { 00669 InitialPoint.x = x<<8; 00670 InitialPoint.y = y<<8; 00671 HeadPoint = InitialPoint; 00672 return (TRUE); 00673 } 00674 00675 *End = TRUE; // Oh dear, no initial point 00676 00677 return(TRUE); 00678 }
|
|
||||||||||||||||||||||||||||
|
This function is recursive. It tries to fit a cubic function to the coordinates in the path between FirstPoint and LastPoint. It then compares this function with the actual coordinates to determine how good a fit it has found. If the fit is good it is added to the path object. If the fit is bad then it is split at the point where the fit is at its worst and FitCubic is called again for the left and right halves. Scope: private.
Definition at line 1029 of file tracergn.cpp. 01031 { 01032 // Will need space for a Bezier curve 01033 TraceBoundaryPoint Bezier[4]; 01034 TraceBoundaryPoint CentTangent; 01035 INT32 NumPoints = LastPoint - FirstPoint + 1; 01036 INT32 SplitPoint, Split; 01037 01038 01039 // If we have the flexibility, recalculate the tangents to the left and right 01040 if (IsStartCusp) Tangent1 = LeftTangent(FirstPoint, NumPoints); 01041 if (IsEndCusp) Tangent2 = RightTangent(LastPoint, NumPoints); 01042 01043 // The following is actually legal if we start on a cusp 01044 // ERROR3IF(NumPoints<2,"Too few points to fit a cubic"); 01045 if (NumPoints<2) return; 01046 #if 0 01047 // if this segment only has 2 points in it then do the special case 01048 if ( NumPoints == 2 ) 01049 { 01050 InsertLine( DocCoord((INT32)(BoundaryBuffer[FirstPoint].x), (INT32)(BoundaryBuffer[FirstPoint].y)), 01051 DocCoord((INT32)(BoundaryBuffer[LastPoint].x), (INT32)(BoundaryBuffer[FirstPoint].y)), 01052 IsStartCusp, IsEndCusp); 01053 return; 01054 } 01055 #endif 01056 /* Due to a bug in the algorithm we also have to consider 3 points a special case. 01057 01058 Here's what Mathematica reckons is the formula for a bezier A B C D that passes through a point Q 01059 with tangents tx & ty 01060 01061 4 t1x (Ay t2x + Dy t2x - 2 Qy t2x - Ax t2y - Dx t2y + 2 Qx t2y) 01062 {{Bx -> Ax - ---------------------------------------------------------------, 01063 3 (t1y t2x - t1x t2y) 01064 4 t1y (Ay t2x + Dy t2x - 2 Qy t2x - Ax t2y - Dx t2y + 2 Qx t2y) 01065 By -> Ay - ---------------------------------------------------------------, 01066 3 (t1y t2x - t1x t2y) 01067 (12 (Ay + Dy - 2 Qy) t1x - 12 (Ax + Dx - 2 Qx) t1y) t2x 01068 Cx -> Dx - -------------------------------------------------------, 01069 9 (-(t1y t2x) + t1x t2y) 01070 4 (Ay t1x + Dy t1x - 2 Qy t1x - Ax t1y - Dx t1y + 2 Qx t1y) t2y 01071 Cy -> Dy + ---------------------------------------------------------------, 01072 3 (t1y t2x - t1x t2y) 01073 01074 I reckon I can rewrite this better: 01075 01076 01077 F -> (4/3) (A + D - 2 Q) / (t2x t1y - t2y t1x) 01078 01079 B -> Fy t2x - Fx t2y 01080 01081 C -> Fx t1y - Fy t1x 01082 01083 Bx -> Ax - B t1x 01084 01085 By -> Ay - B t1y 01086 01087 Cx -> Dx - C t2x 01088 01089 Cy -> Dy - C t2y 01090 01091 This is all very well and good, but when does it occur? Here's an example: 01092 01093 01094 A B 01095 .... 01096 .... 01097 ...T1 01098 01099 T2....C 01100 01101 01102 Here A is is the FistPoint with tangent T1, and C is the last point with tangent T2. The only bezier that 01103 fits through B as well with tangents specified is a loop that goes left from C, back under it and up through T1. 01104 Though all the maths is fine, and we can even detect this case, it's just not worth it. We just miss out point B. 01105 01106 Oh well, so much for the beaury of mathematics. (PS - the real soln is to introduce 1 or more extra points. But that's 01107 getting silly). 01108 01109 */ 01110 01111 #if 0 01112 // Never appears to run to completion anyway :-( 01113 if (NumPoints == 3) 01114 { 01115 INT32 MidPoint = FirstPoint+1; 01116 double Bottom = 0.75 * (Tangent2.x * Tangent1.y - Tangent2.y * Tangent1.x); 01117 if (Abs(Bottom)>0.001) // Else ignore middle point - this happens if the tangents are nearly parallel 01118 { 01119 TraceBoundaryPoint F = (BoundaryBuffer[FirstPoint]+BoundaryBuffer[LastPoint]-BoundaryBuffer[MidPoint]*2.0)/Bottom; 01120 double B = F.y * Tangent2.x - F.x * Tangent2.y; 01121 double C = F.x * Tangent1.y - F.y * Tangent1.x; 01122 if ((B<0.0) && (C<0.0)) // Not generating a loop 01123 { 01124 Tangent1 = -(Tangent1 * B); 01125 Tangent2 = -(Tangent2 * C); // Don't worry - we just need their directions for the fallback case 01126 // Max distance is ((1<<8)/2)^2 = (1<<14) 01127 if ((Tangent1.SquaredLength()<(1<<14)) && (Tangent2.SquaredLength()<(1<<14))) 01128 { 01129 Bezier[0] = BoundaryBuffer[FirstPoint]; 01130 Bezier[3] = BoundaryBuffer[LastPoint]; 01131 Bezier[1] = BoundaryBuffer[FirstPoint] + Tangent1; 01132 Bezier[2] = BoundaryBuffer[LastPoint] + Tangent2; 01133 InsertBezier(Bezier, IsStartCusp, IsEndCusp); 01134 return; 01135 } 01136 } 01137 } 01138 } 01139 #endif 01140 01141 // We must consider 2 points (or unhandled 3 point cases) as a special case 01142 if ( NumPoints <=2) 01143 { 01144 // With 3 points the distance is always 2. 01145 INT32 Distance = (1<<8)*2/3; 01146 01147 // store the end points 01148 Bezier[0] = BoundaryBuffer[FirstPoint]; 01149 Bezier[3] = BoundaryBuffer[LastPoint]; 01150 01151 // calc the control points 01152 Bezier[1] = Bezier[0] + Tangent1.SetLength(Distance); 01153 Bezier[2] = Bezier[3] + Tangent2.SetLength(Distance); 01154 01155 // add it to the path 01156 InsertBezier(Bezier, IsStartCusp, IsEndCusp); 01157 return; 01158 } 01159 01160 01161 if (NumPoints ==3) 01162 { 01163 Split = FirstPoint + 1; // Midpoint 01164 } 01165 else 01166 { 01167 // Create a Bezier curve from the segemnt and see if it is a good fit 01168 Parameterize(FirstPoint, LastPoint); 01169 01170 GenerateBezier(FirstPoint, LastPoint, Tangent1, Tangent2, Bezier); 01171 01172 SplitPoint=NumTPoints-1; 01173 double MaxError = 0; 01174 if (!CalcMaxError(0, NumTPoints, Bezier, &SplitPoint, &MaxError, 0)) 01175 { 01176 // The mapping was good, so output the curve segment 01177 //TRACEUSER( "Alex", _T(" OK FP=%d, LP=%d, ME=%f\n"),FirstPoint,LastPoint,MaxError); 01178 InsertBezier(Bezier, IsStartCusp, IsEndCusp); 01179 return; 01180 } 01181 01182 Split = TPoints[SplitPoint]; 01183 } 01184 01185 //TRACEUSER( "Alex", _T("Split FP=%d, SP=%d, LP=%d\n"),FirstPoint,Split,LastPoint); 01186 // fitting failed -- split at max error point and fit recursively 01187 CentTangent = CentreTangent(Split, MIN(Split-FirstPoint+1,LastPoint-Split+1)); 01188 //Tangent1=LeftTangent(FirstPoint,SplitPoint-FirstPoint+1); 01189 FitCubic(FirstPoint, Split, Tangent1, CentTangent, IsStartCusp, FALSE); 01190 01191 CentTangent = -CentTangent; 01192 //Tangent2=RightTangent(LastPoint,LastPoint-SplitPoint+1); 01193 FitCubic(Split, LastPoint, CentTangent, Tangent2, FALSE, IsEndCusp); 01194 01195 }
|
|
||||||||||||||||||||||||
|
This function supplies the maths to try and fit a cubic function to a set of points. It spends its time trying to come up with good lengths for the two tangents passed in to maximise the closeness of the fit. If it fails to come up with realistic results it simply sets the tangent lengths to be 1/3 of the distance between the start point and the end point. Scope: private.
|