tracergn.cpp

Go to the documentation of this file.
00001 // $Id: tracergn.cpp 1282 2006-06-09 09:46:49Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // This file implements region tracing
00099 
00100 /*
00101 */
00102 
00103 #include "camtypes.h"
00104 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00105 #include "tracergn.h"
00106 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 #include "nodepath.h"
00108 //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 
00110 // for test
00111 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 //#include "range.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "node.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 #include "nodebmp.h"
00115 #include "bitmpinf.h"
00116 #include "chapter.h"
00117 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 #include "page.h"
00119 #include "bfxtest.h"
00120 #include "bfxalu.h"
00121 #include "bfxpixop.h"
00122 //#include "ccmaths.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00123 
00124 // This is not compulsory, but you may as well put it in so that the correct version
00125 // of your file can be registered in the .exe
00126 DECLARE_SOURCE("$Revision: 1282 $");
00127 
00128 // An implement to match the Declare in the .h file.
00129 // If you have many classes, it is recommended to place them all together, here at the start of the file
00130 CC_IMPLEMENT_DYNCREATE(TraceRegion, CCObject)
00131 
00132 // This will get Camelot to display the filename and linenumber of any memory allocations
00133 // that are not released at program exit
00134 #define new CAM_DEBUG_NEW
00135 
00136 #define MAXTANGENTPOINTS    10
00137 
00138 #define MIN(a,b) (((a)<(b))?(a):(b))
00139 #define MAX(a,b) (((a)>(b))?(a):(b))
00140 
00141 // Statics
00142 /*
00143     {-1, 1} { 0, 1} { 1, 1}
00144     {-1, 0}         { 1, 0}
00145     {-1,-1} { 0,-1} { 1,-1}
00146 
00147 */
00148 
00149 /********************************************************************************************
00150 
00151 >   TraceRegion::TraceRegion()
00152                     
00153     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00154     Created:    21/11/94
00155     Inputs:     none
00156     Outputs:    (constructs object)
00157     Returns:    none
00158     Purpose:    Sets intial values up
00159     Errors:     None
00160     Scope:      Public
00161     SeeAlso:    -
00162 
00163 ********************************************************************************************/
00164 
00165 TraceRegion::TraceRegion()
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 }
00375 
00376 
00377 /********************************************************************************************
00378 
00379 >   BOOL TraceRegion::SetParams(double * pPixelError)
00380 
00381     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00382     Created:    17/1/95
00383     Inputs:     ^double values of parameters to set. May be set to NULL to not change them
00384     Outputs:    None
00385     Returns:    TRUE on success, FALSE (& error set) on failure
00386     Purpose:    Sets the tracing parameters
00387     Errors:     mainly memory.
00388     Scope:      Public
00389     SeeAlso:    -
00390 
00391 ********************************************************************************************/
00392 
00393 BOOL TraceRegion::SetParams(double * pPixelError)
00394 {
00395     if (pPixelError) PixelError = *pPixelError;
00396     Error = PixelError;
00397     Error *= 256.0; // For shift
00398     Error *= Error; // Squared
00399 
00400     return TRUE;
00401 }
00402 
00403 /********************************************************************************************
00404 
00405 >   BOOL TraceRegion::GetParams(double * pPixelError)
00406 
00407     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00408     Created:    17/1/95
00409     Inputs:     None
00410     Outputs:    ^double values of parameters to read May be set to NULL to not read them
00411     Returns:    TRUE on success, FALSE (& error set) on failure
00412     Purpose:    Sets the tracing parameters
00413     Errors:     mainly memory.
00414     Scope:      Public
00415     SeeAlso:    -
00416 
00417 ********************************************************************************************/
00418 
00419 BOOL TraceRegion::GetParams(double * pPixelError)
00420 {
00421     if (pPixelError) *pPixelError = PixelError;
00422     return TRUE;
00423 }
00424 
00425 /********************************************************************************************
00426 
00427 >   TraceBoundaryPoint::RotateDirection(const TraceBoundaryPoint & rotation)
00428                     
00429     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00430     Created:    21/11/94
00431     Inputs:     rotation = amount to rotate by
00432     Outputs:    point is rotated
00433     Returns:    none
00434     Purpose:    urmm.. rotation of trace boundary points to conform to a particular direction
00435     Errors:     None
00436     Scope:      Public
00437     SeeAlso:    -
00438 
00439 This rotates an trace boundary point which is assumed to have an eastern direction to that
00440 specified
00441 
00442 ********************************************************************************************/
00443 
00444 void TraceBoundaryPoint::RotateDirection(const TraceBoundaryPoint & rotation)
00445 {
00446 
00447 
00448     TraceBoundaryPoint temp;
00449     temp = *this;
00450 //
00451 // Parallelogram is (r.x, r.y) & (-r.y, r.x)
00452 //
00453 // (  r.x -r.y ) ( t.x )
00454 // (  r.y  r.x ) ( t.y )
00455 
00456     x = (temp.x * ((INT32)(rotation.x)>>8)) - (temp.y * ((INT32)(rotation.y)>>8));
00457     y = (temp.x * ((INT32)(rotation.y)>>8)) + (temp.y * ((INT32)(rotation.x)>>8));
00458 }
00459 
00460 /********************************************************************************************
00461 
00462 >   TraceBoundaryPoint TraceBoundaryPoint::SetLength( double NewLen )
00463 
00464     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00465     Created:    02/03/94
00466     Inputs:     NewLen - The length you want the vector to be
00467     Returns:    TraceBoundaryPoint - the new vector that points in the same direction as this vector,
00468                 only of magnitude NewLen
00469     Purpose:    Scales the TraceBoundaryPoint vector to by the specified length
00470 
00471 ********************************************************************************************/
00472 
00473 TraceBoundaryPoint TraceBoundaryPoint::SetLength( double NewLen )
00474 {
00475     TraceBoundaryPoint Result(x, y);
00476 
00477     double Len = Length();
00478     if (Len != 0.0)
00479     {
00480         Result.x *= (NewLen / Len);
00481         Result.y *= (NewLen / Len);
00482     }
00483 
00484     return Result;
00485 }
00486 
00487 /********************************************************************************************
00488 
00489 >   TraceRegion::~TraceRegion()
00490                     
00491     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00492     Created:    21/11/94
00493     Inputs:     none
00494     Outputs:    (destructs object)
00495     Returns:    none
00496     Purpose:    Destructs object
00497     Errors:     None
00498     Scope:      Public
00499     SeeAlso:    -
00500 
00501 This frees the boundary buffer it is is present.
00502 
00503 ********************************************************************************************/
00504 
00505 TraceRegion::~TraceRegion()
00506 {
00507     if (BoundaryBuffer) CCFree(BoundaryBuffer);
00508     // Clear some hanging pointers
00509     BoundaryBuffer = NULL;
00510     Bitmap = NULL;
00511     ThePath = NULL;
00512 }
00513 
00514 /********************************************************************************************
00515 
00516 >   BOOL TraceRegion::UseBitmap(KernelBitmap * pBitmap)
00517                     
00518     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00519     Created:    21/11/94
00520     Inputs:     pBitmap = pointer to bitmap object to use or NULL for none
00521     Outputs:    (sets object up)
00522     Returns:    TRUE if succeeded else FALSE and error set
00523     Purpose:    Points the TraceRegion at a particular bitmap and claims the relevant size
00524                 buffers.
00525     Errors:     None
00526     Scope:      Public
00527     SeeAlso:    -
00528 
00529 The TraceRegion is set up with a new boundary buffer of an appropriate size. If NULL is passed
00530 in the buffers are removed.
00531 
00532 Note (surprisingly, perhaps) we don't actually ever refer to pixels within this bitmap. Only
00533 its size is used. The BfxPixelOp does all the poking around and it may be referencing a
00534 different (but same sized) bitmap elsewhere.
00535 
00536 ********************************************************************************************/
00537 
00538 BOOL TraceRegion::UseBitmap(KernelBitmap * pBitmap)
00539 {
00540     if (BoundaryBuffer)
00541     {
00542         CCFree(BoundaryBuffer);
00543         BoundaryBuffer = NULL;
00544     }
00545                                                            
00546     xsize=1;
00547     ysize=1;
00548     Bitmap = NULL;
00549     
00550     if (pBitmap)
00551     {
00552         INT32 MaxDimension;
00553         BitmapInfo BMInfo;
00554         pBitmap->ActualBitmap->GetInfo(&BMInfo);
00555         
00556         // Max dimension is 8 times the largest of the width and height (enough to fit a bezier
00557         // curve of maximal usefulness into half the buffer)
00558         MaxDimension = ((BMInfo.PixelWidth>BMInfo.PixelHeight)?BMInfo.PixelWidth:BMInfo.PixelHeight)*8;
00559         ERROR2IF(MaxDimension<=0, FALSE, "Bitmap info structure is dead");
00560         BoundaryRingSize = 1;
00561         
00562         if (MaxDimension<80) MaxDimension=80;
00563 
00564         // Get next highest power of 2 & mask
00565         while (BoundaryRingSize < MaxDimension) BoundaryRingSize = BoundaryRingSize<<1;
00566         BoundaryRingMask = BoundaryRingSize -1;
00567         
00568         // Allocate memory (CCMalloc sets error)
00569         if (NULL== (BoundaryBuffer=(TraceBoundaryPoint *)CCMalloc(sizeof(TraceBoundaryPoint) * BoundaryRingSize)) )
00570             return FALSE;
00571 //      // fill the memory up with +S-NAN
00572 //      for (INT32 i=0; i<BoundaryRingSize; i++)
00573 //      {
00574 //          INT32* p = (INT32*)&(BoundaryBuffer[i].x);
00575 //          *p++ = 0xFFFFFFFF;
00576 //          *p++ = 0x7FF7FFFF;
00577 //          p = (INT32*)&(BoundaryBuffer[i].y);
00578 //          *p++ = 0xFFFFFFFF;
00579 //          *p++ = 0x7FF7FFFF;
00580 //      }
00581 
00582         xsize = BMInfo.PixelWidth;
00583         ysize = BMInfo.PixelHeight;
00584         Bitmap=pBitmap;
00585         VirginBuffer = TRUE;
00586         TailDirection = -1;
00587         BoundaryHead =0;
00588         BoundaryTail =0;
00589         TailDirection = 0;
00590         HeadDirection = 0;
00591         InitialDirection = 0;
00592         AtCusp = FALSE;
00593         TailCusp = FALSE;
00594     }
00595     return (TRUE);
00596 }
00597 
00598 /********************************************************************************************
00599 
00600 >   BOOL TraceRegion::UsePath(Path * pPath)
00601                     
00602     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00603     Created:    21/11/94
00604     Inputs:     pPath = pointer to Path object to use or NULL for none
00605     Outputs:    (sets object up)
00606     Returns:    TRUE if succeeded else FALSE and error set
00607     Purpose:    Specifies we should use a particular path. The path is initialised, cleared etc.
00608     Errors:     None
00609     Scope:      Public
00610     SeeAlso:    -
00611 
00612 
00613 ********************************************************************************************/
00614 
00615 BOOL TraceRegion::UsePath(Path * pPath)
00616 {
00617     ThePath = NULL;
00618     VirginPath = TRUE;
00619     
00620     if (pPath)
00621     {
00622 //      if (!pPath->Initialise()) return FALSE;
00623         if (!pPath->ClearPath()) return FALSE;
00624         ThePath = pPath;
00625     }
00626     return (TRUE);
00627 }
00628 
00629 /********************************************************************************************
00630 
00631 >   BOOL TraceRegion::FindInitialPoint(BOOL * End)
00632                     
00633     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00634     Created:    21/11/94
00635     Inputs:     None
00636     Outputs:    End set if no points
00637     Returns:    TRUE if succeeded else FALSE and error set
00638     Purpose:    Finds an initial point to start at.
00639     Errors:     Various
00640     Scope:      Protected
00641     SeeAlso:    -
00642 
00643 This routine is currently a horrible bodge creature.
00644 
00645 The boundary buffer is cleared. An initial point will be found if possible and End cleared. If
00646 one is not found, End will be set.
00647 
00648 ********************************************************************************************/
00649 
00650 BOOL TraceRegion::FindInitialPoint(BOOL * End)
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 }
00679 
00680 /********************************************************************************************
00681 
00682 >   BOOL TraceRegion::TraceBoundary(DocCoord Origin,DocCoord Point1,DocCoord Point2)
00683                     
00684     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00685     Created:    21/11/94
00686     Inputs:     3 points describing parallelogram
00687 
00688     Outputs:    None
00689     Returns:    TRUE if succeeded else FALSE and error set
00690     Purpose:    Does a single boundary trace
00691     Errors:     Various
00692     Scope:      Protected
00693     SeeAlso:    -
00694 
00695 This function is now defunct and is used to test porpoises only
00696 
00697 ********************************************************************************************/
00698 
00699 BOOL TraceRegion::TraceBoundary(DocCoord Origin,DocCoord Point1,DocCoord Point2)
00700 {
00701     BOOL Done;
00702     ERROR2IF((!Bitmap),FALSE,"How about giving us a bitmap first then?");
00703 
00704     // First find the initial point
00705     if (!(FindInitialPoint(&Done))) return (FALSE);
00706     if (Done) return (TRUE);
00707 
00708     do
00709     {
00710         if (!FillBoundaryBuffer(&Done)) return (FALSE);
00711         if (!ProcessBoundaryBuffer(Done)) return (FALSE);
00712     } while (!Done);
00713 
00714     ThePath->CloseSubPath();
00715     ThePath->IsFilled = TRUE; // What lovely encapsulation & abstraction...
00716 
00717     Matrix tMatrix(Div32By32(Point1.x-Origin.x,xsize<<8),Div32By32(Point2.x-Origin.x,xsize<<8),
00718                    Div32By32(Point1.y-Origin.y,ysize<<8),Div32By32(Point2.y-Origin.y,ysize<<8),
00719                    Origin.x,Origin.y);
00720     Trans2DMatrix Trans(tMatrix);
00721     Trans.Transform( (DocCoord*)ThePath->GetCoordArray(), ThePath->GetNumCoords() );
00722 
00723     return (TRUE);
00724 }
00725 
00726 /********************************************************************************************
00727 
00728 >   BOOL TraceRegion::Trace(INT32 InitialX, INT32 InitialY, BfxPixelOp * thepBfxPixelOp)
00729 
00730                     
00731     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00732     Created:    21/11/94
00733     Inputs:     Initial coordinates and pixel contour to trace
00734     Outputs:    None
00735     Returns:    TRUE if succeeded else FALSE and error set
00736     Purpose:    Does a single boundary trace
00737     Errors:     Various
00738     Scope:      Protected
00739     SeeAlso:    -
00740 
00741 ********************************************************************************************/
00742 
00743 BOOL TraceRegion::Trace(INT32 InitialX, INT32 InitialY, BfxPixelOp * thepBfxPixelOp)
00744 {
00745     BOOL Done=FALSE;
00746     ERROR2IF((!Bitmap),FALSE,"How about giving us a bitmap first then?");
00747 
00748     pBfxPixelOp = thepBfxPixelOp;
00749     
00750     ERROR2IF( ((!pBfxPixelOp->IsInRegion(InitialX,InitialY))), FALSE, "Initial point not in region" );
00751 
00752     BOOL Bottom=FALSE;
00753     while (!Bottom)
00754     {
00755         Bottom=TRUE;
00756         if (pBfxPixelOp->IsInRegion(InitialX-1,InitialY-1))
00757         {
00758             InitialY--;
00759             InitialX--;
00760             Bottom=FALSE;
00761         }
00762         if (pBfxPixelOp->IsInRegion(InitialX,InitialY-1))
00763         {
00764             InitialY--;
00765             Bottom=FALSE;
00766         }
00767         if (pBfxPixelOp->IsInRegion(InitialX-1,InitialY))
00768         {
00769             InitialX--;
00770             Bottom=FALSE;
00771         }
00772     }
00773 
00774     InitialDirection = 4; // West
00775     VirginBuffer = TRUE;
00776     BoundaryHead =0;
00777     BoundaryTail =0;
00778     TailDirection = -1;
00779     HeadDirection = 4;
00780     AtCusp=FALSE;
00781     TailCusp = FALSE;
00782 
00783     InitialPoint.x = InitialX<<8;
00784     InitialPoint.y = InitialY<<8;
00785     HeadPoint = FirstBufferPoint = InitialPoint;
00786 
00787     do
00788     {
00789         if (!FillBoundaryBuffer(&Done)) return (FALSE);
00790         if (!VirginBuffer) { if (!ProcessBoundaryBuffer(Done)) return (FALSE); } else BoundaryHead=0;
00791     } while (!Done);
00792 
00793     ERROR2IF(VirginPath, FALSE, "We've just traced a path that wasn't a path. Very odd");
00794 
00795     ThePath->CloseSubPath();
00796     ThePath->IsFilled = TRUE; // What lovely encapsulation & abstraction...
00797 
00798     return (TRUE);
00799 }
00800 
00801 
00802 /********************************************************************************************
00803 
00804 >   BOOL TraceRegion::FillBoundaryBuffer(BOOL * End)
00805                     
00806     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00807     Created:    21/11/94
00808     Inputs:     End ptr to BOOL to return in
00809     Outputs:    End set TRUE if last point else set FALSE
00810     Returns:    TRUE if worked, FALSE & error set if failed
00811     Purpose:    Fills up the boundary buffer
00812     Errors:     Error2 & Error3 (various)
00813     Scope:      Protected
00814     SeeAlso:    TraceRegion::ProcessBoundaryBuffer
00815 
00816 This routine fills the boundary buffer up with some more points. It stops if the buffer
00817 becomes nearly full.
00818 
00819 It also stops if it meets a cusp, or the intial point in the initial direction.
00820 
00821 ********************************************************************************************/
00822 
00823 
00824 BOOL TraceRegion::FillBoundaryBuffer(BOOL * End)
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 }
00930 
00931 
00932 /********************************************************************************************
00933 
00934 >   BOOL TraceRegion::ProcessBoundaryBuffer(BOOL Done = FALSE)
00935                     
00936     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
00937     Created:    21/11/94
00938     Inputs:     Done TRUE to set last point to FirstBufferPoint
00939     Outputs:    none
00940     Returns:    TRUE if worked, FALSE & error set if failed
00941     Purpose:    Empties the boundary buffer
00942     Errors:     Error2 & Error3 (various)
00943     Scope:      Protected
00944     SeeAlso:    TraceRegion::ProcessBoundaryBuffer
00945 
00946 This routine empties the boundary buffer. It leaves exactly one coordinate in it. The buffer
00947 must be non-empty.
00948 
00949 ********************************************************************************************/
00950 
00951 BOOL TraceRegion::ProcessBoundaryBuffer(BOOL Done)
00952 {
00953     BoundaryTail = 0;
00954 //  ERROR3IF( (((BoundaryHead - BoundaryTail) & BoundaryRingMask) <=0 ),
00955 //          "Boundary ring buffer is empty");
00956     if (((BoundaryHead - BoundaryTail) & BoundaryRingMask) <=0 ) return TRUE;
00957 
00958     if (VirginPath)
00959     {
00960         ThePath->FindStartOfPath();
00961         ThePath->InsertMoveTo(DocCoord((INT32)(BoundaryBuffer[BoundaryTail].x), (INT32)(BoundaryBuffer[BoundaryTail].y)));
00962         VirginPath = FALSE;
00963         FirstBufferPoint=BoundaryBuffer[BoundaryTail];
00964     }
00965 
00966     // Patch up initial hinting (done on inadequate information)
00967     if (Done)
00968     {
00969         BoundaryBuffer[BoundaryHead-1]=FirstBufferPoint;
00970         if (BoundaryBuffer[BoundaryHead-2]==FirstBufferPoint) BoundaryHead=(BoundaryHead-1)&BoundaryRingMask;
00971         if (((BoundaryHead - BoundaryTail) & BoundaryRingMask) <=0 ) return TRUE;
00972     }
00973 
00974 #if 0   
00975     while (BoundaryTail != (BoundaryHead-1))
00976     {
00977         InsertLine(DocCoord((INT32)(BoundaryBuffer[BoundaryTail].x),(INT32)(BoundaryBuffer[BoundaryTail].y)),
00978                    DocCoord((INT32)(BoundaryBuffer[BoundaryTail=(BoundaryTail + 1) & BoundaryRingMask].x),
00979                             (INT32)(BoundaryBuffer[BoundaryTail].y)), TRUE, TRUE);
00980     }
00981 #else
00982     // This case should never occur, but does very very occasionally - not sure why (Alex)
00983     // we ignore buffers with only one point in
00984     if (((BoundaryHead-1) & BoundaryRingMask)==BoundaryTail) return TRUE;
00985 
00986     // Just have to fit a curve from the last cusp to the end of the path
00987     TraceBoundaryPoint Tangent1 = LeftTangent(BoundaryTail,BoundaryHead-BoundaryTail);
00988     TraceBoundaryPoint Tangent2 = RightTangent(BoundaryHead-1,BoundaryHead-BoundaryTail);
00989     
00990     // and do a load of maths that will hopefully fit a nice curve on it
00991     FitCubic(BoundaryTail, BoundaryHead-1, Tangent1, Tangent2, TailCusp, AtCusp);
00992 #endif
00993     
00994     BoundaryBuffer[0]=BoundaryBuffer[BoundaryHead-1];
00995     BoundaryTail = 0;
00996     BoundaryHead = 1;
00997 
00998     TailDirection = -1;
00999     TailCusp = AtCusp;
01000     return (TRUE);
01001 }
01002 
01003 
01004 /********************************************************************************************
01005 
01006 >   void TraceRegion::FitCubic( INT32 FirstPoint, INT32 LastPoint,
01007                                    TraceBoundaryPoint Tangent1, TraceBoundaryPoint Tangent2,
01008                                    BOOL IsStartCusp = TRUE, BOOL IsEndCusp = TRUE);
01009 
01010     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex
01011     Created:    7/3/94
01012     Inputs:     FirstPoint - the index of the coordinate in the path to start fitting from
01013                 LastPoint - the index of the coordinate in the path to stop fitting at
01014                 Tangent1 - The tangent of the curve at the first point
01015                 Tangent2 - the tangent of the curve at the last point
01016                 IsStartCusp - TRUE if FirstPoint is next to a cusp
01017                 IsEndCusp - TRUE of EndPoint is next to a cusp
01018     Purpose:    This function is recursive. It tries to fit a cubic function to the
01019                 coordinates in the path between FirstPoint and LastPoint. It then compares
01020                 this function with the actual coordinates to determine how good a fit it
01021                 has found. If the fit is good it is added to the path object. If the fit
01022                 is bad then it is split at the point where the fit is at its worst and
01023                 FitCubic is called again for the left and right halves.
01024     Scope:      private
01025     SeeAlso:    TraceRegion::GenerateBezier; TraceRegion::CalcMaxError
01026 
01027 ********************************************************************************************/
01028 
01029 void TraceRegion::FitCubic(INT32 FirstPoint, INT32 LastPoint, TraceBoundaryPoint Tangent1, TraceBoundaryPoint Tangent2,
01030                               BOOL IsStartCusp, BOOL IsEndCusp)
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 }
01196 
01197 
01198 
01199 /********************************************************************************************
01200 
01201 >   void TraceRegion::Parameterize(INT32 FirstPoint, INT32 LastPoint)
01202 
01203     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
01204     Created:    12/12/94
01205     Inputs:     FirstPoint - the index of the coordinate in the path to start fitting from
01206                 LastPoint - the index of the coordinate in the path to stop fitting at
01207     Outputs:    Fills up TValues & TPoints array
01208     Purpose:    This function determines the TPoints which will be fitted (i.e. the trace
01209                 boundary points we use) and the TValues which will be assigned to them
01210                 (i.e. the t values along the chord length to which the points correspond).
01211     Scope:      private
01212 
01213 ********************************************************************************************/
01214 
01215 void TraceRegion::Parameterize(INT32 FirstPoint, INT32 LastPoint)
01216 {
01217     INT32 pos, i;
01218     
01219     INT32 NumPoints = LastPoint-FirstPoint;
01220     // Chord length parameterisation
01221 
01222     NumTPoints = MIN(NumPoints, FIT_STEPS);
01223 
01224     for (pos=0; pos<=NumTPoints; pos++)
01225     {
01226         i = (INT32)(((double)pos/(double)NumTPoints*(double)(NumPoints))+0.5);
01227         // In this loop, 
01228         TValues[pos] = ((double)i)/((double)NumPoints);
01229         TPoints[pos] = (i+FirstPoint);
01230     }
01231     
01232     // These should algorithmically never happen
01233     ERROR3IF(TPoints[0]!=FirstPoint,"TraceRegion::Parameterize fouled up first point");
01234     ERROR3IF(TPoints[NumTPoints]!=LastPoint,"TraceRegion::Parameterize fouled up last point");
01235     return;
01236 }
01237 
01238 /********************************************************************************************
01239 
01240 >   void TraceRegion::GenerateBezier(INT32 FirstPoint, INT32 LastPoint, TraceBoundaryPoint Tangent1, 
01241                                         TraceBoundaryPoint Tangent2, TraceBoundaryPoint* Bezier)
01242 
01243     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex
01244     Created:    7/3/94
01245     Inputs:     FirstPoint - the index of the coordinate in the path to start fitting from
01246                 LastPoint - the index of the coordinate in the path to stop fitting at
01247                 Tangent1 - The tangent of the curve at the first point
01248                 Tangent2 - the tangent of the curve at the last point
01249     Outputs:    Bezier - A pointer to a Bezier Curve
01250     Purpose:    This function supplies the maths to try and fit a cubic function to a 
01251                 set of points. It spends its time trying to come up with good lengths for
01252                 the two tangents passed in to maximise the closeness of the fit. If it
01253                 fails to come up with realistic results it simply sets the tangent lengths
01254                 to be 1/3 of the distance between the start point and the end point.
01255     Scope:      private
01256 
01257 Note the differential & 2nd differential are also generated
01258 
01259 ********************************************************************************************/
01260 
01261 void TraceRegion::GenerateBezier(INT32 FirstPoint, INT32 LastPoint, TraceBoundaryPoint Tangent1, 
01262                                     TraceBoundaryPoint Tangent2, TraceBoundaryPoint* Bezier)
01263 {
01264     INT32 pos;
01265     INT32 i;
01266 //  INT32 NumPoints = LastPoint - FirstPoint + 1;
01267     
01268     // Build a matrix type of thing that contains the tangents scaled by the offsets
01269     TraceBoundaryPoint A[FIT_STEPS+1][2];           //  Vector2 (*A)[2] = new Vector2[NumPoints+1][2];
01270     
01271     // Chord length parameterisation
01272     for (pos=0; pos<=NumTPoints; pos++)
01273     {
01274         // Fill the matrix A
01275         A[pos][0] = Tangent1.SetLength( Bezier1(TValues[pos]) );
01276         A[pos][1] = Tangent2.SetLength( Bezier2(TValues[pos]) );
01277     }
01278 
01279     // For a detailed description of the maths used here, please see Graphics Gems I
01280     // I have made some changes to the basic algorithm used there - the main one is
01281     // that this block of maths is only performed on a small selection of the points
01282     // in the data set, where-as the one in the book uses all the points
01283     double  C[2][2];
01284     double  X[2];
01285     
01286     C[0][0] = 0.0;
01287     C[0][1] = 0.0;
01288     C[1][0] = 0.0;
01289     C[1][1] = 0.0;
01290     X[0]    = 0.0;
01291     X[1]    = 0.0;
01292     
01293     TraceBoundaryPoint FirstCoord = BoundaryBuffer[FirstPoint];
01294     TraceBoundaryPoint LastCoord  = BoundaryBuffer[LastPoint];
01295     TraceBoundaryPoint ThisCoord, Combo;
01296 
01297     for (pos=0; pos<=NumTPoints; pos++)
01298     {
01299         C[0][0] += A[pos][0].SquaredLength();
01300         C[0][1] += A[pos][0].Dot(A[pos][1]);
01301         // Point C[1][0] is the same as C[0][1] and is set outside the loop below
01302         C[1][1] += A[pos][1].SquaredLength();
01303         
01304         // Go ahead and build a vector based on the bezier functions
01305         ThisCoord = BoundaryBuffer[TPoints[pos]];
01306         Combo = ThisCoord - ((FirstCoord * Bezier0(TValues[pos]))
01307                             + (FirstCoord * Bezier1(TValues[pos]))
01308                             + (LastCoord  * Bezier2(TValues[pos]))
01309                             + (LastCoord  * Bezier3(TValues[pos])));
01310 
01311         // Combine it with the other points
01312         X[0] += A[pos][0].Dot( Combo );
01313         X[1] += A[pos][1].Dot( Combo );
01314 
01315     }
01316 
01317     // This point in the matrix is the same, so we do not need to do it in the loop
01318     C[1][0] = C[0][1];
01319     
01320     // calc the determinants of C and X
01321     double det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
01322     double det_C0_X  = C[0][0] * X[1]    - C[0][1] * X[0];
01323     double det_X_C1  = X[0]    * C[1][1] - X[1]    * C[0][1];
01324     
01325     // finally, derive the length of the ideal tangents
01326     if (det_C0_C1 == 0.0)
01327         det_C0_C1 = (C[0][0] * C[1][1]) * 10e-12;   // oh err, whats it up to here then!
01328     
01329     double AlphaLeft  = det_X_C1 / det_C0_C1;
01330     double AlphaRight = det_C0_X / det_C0_C1;
01331     
01332     Bezier[0] = BoundaryBuffer[FirstPoint];
01333     Bezier[3] = BoundaryBuffer[LastPoint];
01334 
01335     // if alpha negative, the set the tangent length to a third of the distance between
01336     // the start and the end points of the curve segment    
01337     if ( AlphaLeft < 0.0 || AlphaRight < 0.0)
01338     {
01339         INT32 Distance = ((LastPoint - FirstPoint)<<8) / 3;
01340         
01341         Bezier[1] = Bezier[0] + Tangent1.SetLength(Distance);
01342         Bezier[2] = Bezier[3] + Tangent2.SetLength(Distance);
01343     }
01344     else
01345     {   
01346         Bezier[1] = Bezier[0] + Tangent1.SetLength(AlphaLeft);
01347         Bezier[2] = Bezier[3] + Tangent2.SetLength(AlphaRight);
01348     }
01349 
01350     // Calculate Q1 (differential)
01351     for (i = 0; i<=2; i++)
01352     {
01353         Q1[i]= (Bezier[i+1]-Bezier[i]) * 3.0;
01354     }
01355 
01356     // Calculate Q2 (2nd differential)
01357     for (i = 0; i<=1; i++)
01358     {
01359         Q2[i] = (Q1[i+1]-Q1[i]) * 2.0;
01360     }
01361 
01362 }
01363 
01364 
01365 /********************************************************************************************
01366 
01367 >   TraceBoundaryPoint TraceRegion::BezierPoint( TraceBoundaryPoint* Bez, double u)
01368 
01369     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex
01370     Created:    7/3/94
01371     Inputs:     Bez - The control points of a bezier curve
01372                 u - the normalised distance along the bezier that we are interested in
01373     Returns:    The coord of the point that is a distance u into the bezier. for example,
01374                 if u = 0.5 then the coord of the point half way along the bezier will be
01375                 returned
01376     Purpose:    This function simply evaluates the bezier function for a given position and
01377                 is used to help when determining how good a fit we have obtained
01378     Scope:      private
01379     SeeAlso:    TraceRegion::CalcMaxError
01380 
01381 ********************************************************************************************/
01382 
01383 TraceBoundaryPoint TraceRegion::BezierPoint( TraceBoundaryPoint* Bez, double u)
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 }
01397 
01398 
01399 
01400 /********************************************************************************************
01401 
01402 >   BOOL TraceRegion::CalcMaxError(INT32 LeftPoint, INT32 RightPoint,
01403                                    TraceBoundaryPoint* Bezier, INT32* SplitPoint, double * MaxDist, INT32 Level)
01404 
01405     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
01406     Created:    12/12/94
01407     Inputs:     LeftPoint, RightPoint - the index of the start and send of the section - note these are indexes
01408                 into the T Array
01409                 Bezier - the control points of the bezier we have fitted to the points
01410                 Level - a recursion level counter
01411     Outputs:    SplitPoint - the point to split the curve at if the error is too great
01412                 MaxDist - The biggest distance
01413     Returns:    TRUE if the bezier should be split
01414     Purpose:    Finds out how good a fit the bezier curve we have created is when compared
01415                 with the data points
01416     Scope:      private
01417 
01418 This is done by recursive splitting of the line (as the largest error terms are likely to be
01419 in the middle we are extremely likely to only do one split). However, in order to find the
01420 maximum error point we force 3 levels of recursion.
01421 
01422 Cunningly we move the TValues about if we think they are salvageable.
01423 
01424 ********************************************************************************************/
01425 
01426 BOOL TraceRegion::CalcMaxError(INT32 LeftPoint, INT32 RightPoint, TraceBoundaryPoint* Bezier,
01427                                  INT32* SplitPoint, double * MaxDist, INT32 Level)
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 }
01494 
01495 
01496 
01497 
01498 
01499 
01500 /********************************************************************************************
01501 
01502 >   TraceBoundaryPoint TraceRegion::LeftTangent(INT32 Start, INT32 Points)
01503 
01504     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex
01505     Created:    7/3/94
01506     Inputs:     Start - the index of the point at the start of the segment to fit
01507                 Points - the max number of points that can be scanned
01508     Returns:    The tangent at the point Start
01509     Purpose:    Finds the Left tangent at the given point in the path
01510     Scope:      private
01511 
01512 ********************************************************************************************/
01513 
01514 TraceBoundaryPoint TraceRegion::LeftTangent(INT32 Start, INT32 Points)
01515 {
01516     TraceBoundaryPoint Tangent(0,0);
01517 
01518     Points = 1+MIN (Points/2,MAXTANGENTPOINTS);
01519     if (Points<2) Points=2;
01520 
01521     // Make sure that is not of zero length
01522     while ((Tangent.x==0) && (Tangent.y==0))
01523     {
01524         if ((--Points)>0)
01525         {
01526             Tangent.x = BoundaryBuffer[Start+Points].x - BoundaryBuffer[Start].x;
01527             Tangent.y = BoundaryBuffer[Start+Points].y - BoundaryBuffer[Start].y;
01528         }
01529         else
01530         {
01531             ERROR3("Tangent was a zero length vector in the curve fitter (Left)");
01532             Tangent.x = -1;
01533         }
01534     }
01535 
01536     return Tangent;
01537 }
01538 
01539 
01540 /********************************************************************************************
01541 
01542 >   TraceBoundaryPoint TraceRegion::RightTangent(INT32 End, INT32 Points)
01543 
01544     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex
01545     Created:    7/3/94
01546     Inputs:     End - the index of the point at the end of the segment to fit
01547                 Points - the max number of points that can be scanned
01548     Returns:    The tangent at the point End
01549     Purpose:    Finds the Right tangent at the given point in the path
01550     Scope:      private
01551 
01552 ********************************************************************************************/
01553 
01554 TraceBoundaryPoint TraceRegion::RightTangent(INT32 End, INT32 Points)
01555 {
01556     TraceBoundaryPoint Tangent(0,0);
01557 
01558     Points = 1+MIN (Points/2,MAXTANGENTPOINTS);
01559     if (Points<2) Points=2;
01560 
01561     // Make sure that is not of zero length
01562     while ((Tangent.x==0) && (Tangent.y==0))
01563     {
01564         if ((--Points)>0)
01565         {
01566             Tangent.x = BoundaryBuffer[End-Points].x - BoundaryBuffer[End].x;
01567             Tangent.y = BoundaryBuffer[End-Points].y - BoundaryBuffer[End].y;
01568         }
01569         else
01570         {
01571             ERROR3("Tangent was a zero length vector in the curve fitter (Right)");
01572             Tangent.x = -1;
01573         }
01574     }
01575 
01576     return Tangent;
01577 }
01578 
01579 
01580 
01581 /********************************************************************************************
01582 
01583 >   TraceBoundaryPoint TraceRegion::CentreTangent(INT32 Centre, INT32 Points)
01584 
01585     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex
01586     Created:    7/3/94
01587     Inputs:     Centre - the index of the split point in the path
01588                 Points - the max number of points that can be scanned
01589     Returns:    The tangent at the point Centre
01590     Purpose:    Finds the tangent at the centre of the path
01591     Scope:      private
01592 
01593 ********************************************************************************************/
01594 
01595 TraceBoundaryPoint TraceRegion::CentreTangent(INT32 Centre, INT32 Points)
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 }
01630 
01631 
01632 
01633 /********************************************************************************************
01634 
01635 >   void TraceRegion::InsertBezier(TraceBoundaryPoint* Bezier, BOOL IsStartCusp, BOOL IsEndCusp)
01636 
01637     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex
01638     Created:    7/3/94
01639     Inputs:     Bezier - the control points of the bezier curve to add to the path
01640                 IsStartCusp - TRUE if the start of this bezier is at a cusp
01641                 IsEndCusp - TRUE if the End of this bezier is at a cusp
01642     Purpose:    Adds the bezier curve to the path. If it is that last curve in the fitting
01643                 (ie Depth is 0) then the rotate flag will not be set
01644     Scope:      private
01645 
01646 ********************************************************************************************/
01647 
01648 void TraceRegion::InsertBezier(TraceBoundaryPoint* Bezier, BOOL IsStartCusp, BOOL IsEndCusp)
01649 {
01650     // Prepare some flags
01651     PathFlags Flags;
01652     Flags.IsSelected = FALSE;
01653     Flags.IsSmooth = FALSE;
01654     Flags.IsRotate = TRUE;
01655 
01656     // Add this Bezier curve into the path
01657     ThePath->InsertCurveTo( DocCoord( (INT32)Bezier[1].x, (INT32)Bezier[1].y),
01658                              DocCoord( (INT32)Bezier[2].x, (INT32)Bezier[2].y),
01659                              DocCoord( (INT32)Bezier[3].x, (INT32)Bezier[3].y), &Flags);
01660 
01661     // Deal with cusps
01662     if (IsStartCusp || IsEndCusp)
01663     {
01664         // Go find out about the path
01665         PathFlags* AllFlags = ThePath->GetFlagArray();
01666         INT32 NumCoords = ThePath->GetNumCoords();
01667 
01668         if (IsStartCusp)
01669         {
01670             // Patch up the flags of the bits near that start
01671             AllFlags[NumCoords-3].IsRotate = FALSE;
01672         }
01673     
01674         if (IsEndCusp)
01675         {
01676             // Patch up the flags of the bits near that end
01677             AllFlags[NumCoords-2].IsRotate = FALSE;
01678             AllFlags[NumCoords-1].IsRotate = FALSE;
01679         }
01680     }
01681 }
01682 
01683 
01684 
01685 /********************************************************************************************
01686 
01687 >   void TraceRegion::InsertLine(const TraceBoundaryPoint& Start, const TraceBoundaryPoint& End, 
01688                                     TraceBoundaryPoint Tangent1, TraceBoundaryPoint Tangent2, BOOL IsStartCusp, BOOL IsEndCusp)
01689 
01690     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex
01691     Created:    7/3/94
01692     Inputs:     Start - the coord of the start point of the line
01693                 End - the coord of the end point of the line
01694                 IsStartCusp - TRUE if the start of this bezier is at a cusp
01695                 IsEndCusp - TRUE if the End of this bezier is at a cusp
01696     Purpose:    Inserts a straight curve into the path. It keeps it as a curve so that the
01697                 path will continue to look smooth after it is edited. If this is the last
01698                 Path element in the fit, (ie Depth is zero) then the rotate flag will not
01699                 be set.
01700     Scope:      private
01701 
01702 I've simplified the maths in this somewhat since over the FitCurve implementation.
01703 
01704 ********************************************************************************************/
01705 
01706 void TraceRegion::InsertLine(const DocCoord& Start, const DocCoord& End,
01707                              BOOL IsStartCusp, BOOL IsEndCusp)
01708 {
01709     // Prepare some flags
01710     PathFlags Flags;
01711     Flags.IsSelected = FALSE;
01712     Flags.IsSmooth = FALSE;
01713     Flags.IsRotate = TRUE;
01714 
01715     // Find out a third of the distance between the two points
01716     DocCoord StartPos;
01717     DocCoord EndPos;
01718 
01719     StartPos.x = (2 * Start.x + End.x) /3;
01720     StartPos.y = (2 * Start.y + End.y) /3;
01721     EndPos.x = (Start.x + 2 * End.x) /3;
01722     EndPos.y = (Start.y + 2 * End.y) /3;
01723 
01724     // Add the line to the curve
01725     ThePath->InsertCurveTo( (DocCoord) StartPos, (DocCoord) EndPos, (DocCoord) End, &Flags);    
01726     
01727     // Patch up the path
01728     PathVerb * AllVerbs = ThePath->GetVerbArray();
01729     PathFlags* AllFlags = ThePath->GetFlagArray();
01730     INT32 NumCoords = ThePath->GetNumCoords();
01731     ERROR3IF(NumCoords<4,"No initial moveto ?!");
01732 
01733     AllFlags[NumCoords-1].IsRotate = !IsEndCusp;    // Endpoint
01734     AllFlags[NumCoords-2].IsRotate = !IsEndCusp; // 2nd ctl
01735 
01736     if (AllVerbs[NumCoords-4] == PT_MOVETO)
01737     {
01738         AllFlags[NumCoords-3].IsRotate = FALSE;
01739         AllFlags[NumCoords-4].IsRotate = FALSE;
01740     }
01741     else
01742     {
01743         //BOOL IsStartCusp = AllFlags[NumCoords-5].IsRotate;
01744 
01745         AllFlags[NumCoords-3].IsRotate = !IsStartCusp;
01746         AllFlags[NumCoords-4].IsRotate = !IsStartCusp;
01747 
01748         
01749     }
01750 
01751 }
01752 
01753 
01754 
01755 
01756 /********************************************************************************************
01757 
01758 >   void TraceRegion::InsertStraightLine(const TraceBoundaryPoint& End)
01759 
01760     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com> & Alex   & Alex
01761     Created:    25/4/94
01762     Inputs:     End - the coord of the end point of the line
01763     Purpose:    Inserts a straight Line into the path. This is used in cases where the
01764                 Line segment must be kept as a straight line (ie by using Straight Line
01765                 Mode of the FreeHand tool)
01766     Scope:      private
01767 
01768 ********************************************************************************************/
01769 
01770 void TraceRegion::InsertStraightLine(const DocCoord& End)
01771 {
01772     // Prepare some flags
01773     PathFlags Flags;
01774     Flags.IsSelected = FALSE;
01775     Flags.IsRotate = FALSE;
01776     Flags.IsSmooth = FALSE;
01777 
01778     // Insert the line
01779     ThePath->InsertLineTo((DocCoord) End, &Flags);  
01780 }
01781 
01782 
01783 
01784 /********************************************************************************************
01785 
01786 >   void TraceRegion::Test(UndoableOperation * Op)
01787                     
01788     Author:     Alex_Bligh (Xara Group Ltd) <camelotdev@xara.com>
01789     Created:    21/11/94
01790     Inputs:     ^Op
01791     Outputs:    none
01792     Returns:    nothing
01793     Purpose:    Internal test routine
01794     Errors:     none
01795     SeeAlso:    -
01796 
01797 A test routine
01798 
01799 ********************************************************************************************/
01800 
01801 
01802 void TraceRegion::Test(UndoableOperation * Op)
01803 {
01804      
01805     BOOL CarryOn=TRUE;
01806     Range Sel(*(GetApplication()->FindSelection()));
01807 
01808     Node* FirstSelectedNode = Sel.FindFirst(); 
01809     if (FirstSelectedNode != NULL) // No nodes selected so End
01810     {
01811         Node* CurrentNode = FirstSelectedNode;       
01812         Node* NextCurrent; 
01813         
01814         // Do all bitmaps
01815         while ((CurrentNode != NULL) && CarryOn)
01816         {
01817             NextCurrent = Sel.FindNext(CurrentNode);
01818             if  ( (CurrentNode->IsSelected()) && (CurrentNode->GetRuntimeClass() == CC_RUNTIME_CLASS(NodeBitmap)) ) 
01819             {         
01820                 KernelBitmap * pBitmap = ((NodeBitmap *)(CurrentNode))->GetBitmap();
01821                 BitmapInfo BMInfo;
01822                 UINT32 bpp;
01823                 pBitmap->ActualBitmap->GetInfo(&BMInfo);
01824                 bpp=BMInfo.PixelDepth;
01825                 
01826                 TRACEUSER( "Alex", _T("Bitmap found %d bpp\n"),bpp);
01827 
01828                 if ((bpp==32) || TRUE)
01829                 {
01830                     CarryOn = FALSE;
01831 //                  NodeBitmap *pNodeBitmap = new NodeBitmap;
01832 //                  if ((pNodeBitmap == NULL) || (!pNodeBitmap->SetUpPath(12,12)))
01833 //                      return;
01834 
01835                     Spread *pSpread;
01836                     DocCoord Origin;
01837 
01838                     // For now, position Draw objects on 1st page of spread 1
01839                     Node *pNode = (Document::GetSelected())->GetFirstNode()->FindNext()->FindFirstChild();
01840                     while ((pNode != NULL) && (!pNode->IsKindOf(CC_RUNTIME_CLASS(Chapter))))
01841                         pNode = pNode->FindNext();
01842         
01843                     ENSURE(pNode->IsKindOf(CC_RUNTIME_CLASS(Chapter)), 
01844                            "Filter::GetFirstSpread(): Could not find Chapter");
01845                     Chapter *pChapter = (Chapter *) pNode;
01846     
01847                     // pSpread is a child of pChapter
01848                     pSpread = (Spread *) pChapter->FindFirstChild();
01849                     ENSURE(pSpread->IsKindOf(CC_RUNTIME_CLASS(Spread)),
01850                            "Filter::GetFirstSpread(): Could not find Spread");
01851 
01852                     Page *pPage = (Page *) pSpread->FindFirstPageInSpread();
01853                     ENSURE(pPage->IsKindOf(CC_RUNTIME_CLASS(Page)),
01854                            "BaseBitmapFilter::DoImport(): Could not find first Page");
01855         
01856                     // Use bottom left of page as origin
01857                     DocRect PageRect = pPage->GetPageRect();
01858                     Origin = PageRect.lo;
01859     
01860                     NodePath * pNewNode;
01861                     pNewNode = new NodePath;
01862 
01863                     TraceRegion TR;
01864 
01865 //                  if ((pNode = TR->Trace(pBitmap,((NodeBitmap *)(CurrentNode))->Parallel)) == NULL) {return;}
01866 
01867                     TR.UseBitmap(pBitmap);
01868                     TR.UsePath(&(pNewNode->InkPath));
01869                     TR.TraceBoundary(((NodeBitmap * )CurrentNode)->Parallel[3],
01870                                      ((NodeBitmap * )CurrentNode)->Parallel[2],
01871                                      ((NodeBitmap * )CurrentNode)->Parallel[0]);
01872 
01873                     // Insert the node, but don't invalidate its region
01874                     if (!Op->DoInsertNewNode(pNewNode, pSpread, FALSE))
01875                     {
01876                         // It didn't work - delete the sub-tree we just created, and report error.
01877                         delete pNewNode;
01878                         return;
01879                     }
01880                                                         
01881                     // Invalidate the region
01882                     Op->DoInvalidateNodeRegion(pNewNode, TRUE, FALSE);
01883                 
01884                 }
01885             }
01886             CurrentNode = NextCurrent; 
01887         }
01888 
01889     } 
01890 
01891 //  if (CarryOn) BitmapEffectSILT::RunA();
01892 
01893     return;
01894 }

Generated on Sat Nov 10 03:47:13 2007 for Camelot by  doxygen 1.4.4