00001 // $Id: pbecomea.cpp 1361 2006-06-25 16:43:38Z 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 // 00099 // Implementation of PathBecomeA. 00100 // 00101 00102 #include "camtypes.h" // pre-compiled header. 00103 #include "pbecomea.h" // make shapes stuff. 00104 00105 #include "strkattr.h" // AttrStrokeType use. 00106 #include "ppstroke.h" 00107 00108 #include "nodepath.h" // NodePath reference. 00109 #include "lineattr.h" // for line width & colour attributes. 00110 #include "ndclpcnt.h" // for ClipView path constants. 00111 #include "attrmap.h" 00112 00113 DECLARE_SOURCE("$Revision: 1361 $"); 00114 00115 // dynamic class creation stuff. 00116 CC_IMPLEMENT_MEMDUMP(PathBecomeA, BecomeA) 00117 CC_IMPLEMENT_MEMDUMP(ContourBecomeA2, PathBecomeA) 00118 00119 // Declare smart memory handling in Debug builds 00120 #define new CAM_DEBUG_NEW 00121 00122 00123 00124 /******************************************************************************************** 00125 00126 > PathBecomeA::PathBecomeA( BecomeAReason baReason, CCRuntimeClass* pClass, 00127 UndoableOperation* pUndoOp, BOOL bSel, Path* pResultPath, 00128 enum OutlineRules OutlineRule = IGNORE_OUTLINES ) 00129 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00130 Created: 10 February 2000 00131 Inputs: baReason the reason for the call to this function. 00132 pClass the type of class required - ie a NodePath. 00133 pUndoOp ptr to the Op requesting the BecomeA. 00134 bSel whether to select the newly created node (unused in this class). 00135 pResultPath points to a Path object in which to leave the results. 00136 00137 fRemoveOutlines if set, then we strip the area obscured by any object 00138 outlines, from the resultant path. 00139 00140 Outputs: After a call to DoBecomeA with this parameter object, baReason set to 00141 BECOMEA_PASSBACK and pClass set to NodePath, pResultPath should contain the 00142 outline(s) of whatever node was asked to DoBecomeA. 00143 00144 Notes: It is up to the caller to create, initialise and destroy the Path object 00145 pointed to by pResultPath. All we do is fill the path object with the 00146 required information. 00147 00148 Outline rules - as an example, imagine processing two overlapping circles 00149 with 20pt line width: 00150 IGNORE_OUTLINES the default; result - the path which would enclose 00151 both circles if their line widths were set to 0. 00152 00153 STRIP_OUTLINES the path which would result if you drew the circles, 00154 then erased the areas covered by their thick line widths. 00155 00156 ADD_OUTLINES the path which would result if you drew the circles, 00157 complete with thick line widths. 00158 00159 ********************************************************************************************/ 00160 PathBecomeA::PathBecomeA( BecomeAReason baReason, CCRuntimeClass* pClass, 00161 UndoableOperation* pUndoOp, BOOL bSel, Path* pResultPath, 00162 enum OutlineRules OutlineRule ) 00163 : BecomeA(baReason, pClass, pUndoOp, bSel) 00164 { 00165 // initialise our pointer to the result-path. 00166 m_pResultPath = pResultPath; 00167 00168 // initialise our outlines behaviour. 00169 m_OutlineRule = OutlineRule; 00170 if (m_OutlineRule == STRIP_OUTLINES) 00171 m_ClipPathRule = 4; 00172 else if (m_OutlineRule == ADD_OUTLINES) 00173 m_ClipPathRule = 7; 00174 } 00175 00176 00177 00178 PathBecomeA::~PathBecomeA() 00179 { 00180 // empty our pointer to the result-path. 00181 m_pResultPath = NULL; 00182 } 00183 00184 00185 00186 /******************************************************************************************** 00187 00188 > virtual BOOL PathBecomeA::Passback( NodeRenderableInk* pNewNode, 00189 NodeRenderableInk*& pCreatedByNode, 00190 CCAttrMap* pAttrMap = NULL) 00191 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00192 Created: 10 February 2000 00193 Inputs: pNewNode points to a new NodeRenderableInk created as a result of the 00194 BecomeA operation. 00195 pCreatedByNode the node which called us. 00196 pAttrMap an attribute map - useful for stroking paths... 00197 00198 Returns: TRUE if successful, FALSE otherwise. 00199 00200 Purpose: Called by DoBecomeA when a node is asked to convert itself to another type of 00201 node. The passed-in new node is expected to be a NodePath. We extract its 00202 actual Path, and add it to the path referenced by a pointer we were given at 00203 construction. 00204 00205 Notes: The possible node tree pointed to by pNewNode, together with pAttrMap, must 00206 both be destroyed by this function - no-one else is going to do it. 00207 00208 Errors: ERROR2 with FALSE if pNewNode is NULL or is not a path. 00209 See also: BecomeA::PassBack() 00210 00211 ********************************************************************************************/ 00212 BOOL PathBecomeA::PassBack( NodeRenderableInk* pNewNode, 00213 NodeRenderableInk* pCreatedByNode, 00214 CCAttrMap* pAttrMap ) 00215 { 00216 // validate inputs. 00217 ERROR2IF( pNewNode == NULL || !pNewNode->IsNodePath(), 00218 FALSE, 00219 "PathBecomeA::PassBack; Passed back node was NULL, or not a path" ); 00220 00221 ERROR2IF( m_pResultPath == NULL, FALSE, 00222 "PathBecomeA::PassBack; NULL result path!" ); 00223 00224 BOOL fDeleteAttributes = TRUE; 00225 BOOL fDeleteFillPath = FALSE; 00226 00227 const INT32 Flatness = NodeClipViewController::EstimatePathFlatness(); 00228 00229 // try to get an attribute map for converting the outline to shapes. 00230 if (pAttrMap == NULL) 00231 { 00232 fDeleteAttributes = FALSE; 00233 pAttrMap = CCAttrMap::MakeAppliedAttrMap(pCreatedByNode); 00234 } 00235 00236 NodePath* pNewNodePath = (NodePath*)pNewNode; 00237 00238 Path* pFillPath = &( pNewNodePath->InkPath ); 00239 Path* pLinePath = NULL; 00240 00241 // ok, first try to get the node's outline as a closed path. 00242 // we obviously won't get anything unless the outline's non-transparent. 00243 pLinePath = ExtractOutlineAsPath(pNewNodePath, pAttrMap); 00244 00245 // providing the fill-path is filled, we'll use it as is. 00246 if (pFillPath->IsFilled) 00247 { 00248 if (pFillPath->GetNumCoords() < 3) 00249 pFillPath->ClearPath(); 00250 } 00251 00252 // if the fill-path is open, then we'll use the line path instead. 00253 else 00254 { 00255 fDeleteFillPath = TRUE; 00256 pFillPath = pLinePath; 00257 pLinePath = NULL; 00258 } 00259 00260 // provided it exists, add the fill-path to the results path. 00261 if (pFillPath != NULL && pFillPath->GetNumCoords() > 0) 00262 { 00263 // if our results path is empty, just plonk the fill path in it. 00264 if (m_pResultPath->GetNumCoords() == 0) 00265 m_pResultPath->CloneFrom(*pFillPath); 00266 00267 // otherwise, add our fill path to the current results path. 00268 // there's a slight optimisation here - if the bounds of the two paths don't overlap, 00269 // then the path union can be done simply by merging them together. 00270 else 00271 { 00272 BOOL fBoundsOverlap = FALSE; 00273 DocRect drResultPath(0, 0, 0, 0); 00274 DocRect drFillPath(0, 0, 0, 0); 00275 if (m_pResultPath->GetTrueBoundingRect(&drResultPath)) 00276 if (pFillPath->GetTrueBoundingRect(&drFillPath)) 00277 if (drFillPath.IsIntersectedWith(drResultPath)) 00278 fBoundsOverlap = TRUE; 00279 00280 if (!fBoundsOverlap) 00281 m_pResultPath->MergeTwoPaths(*pFillPath); 00282 00283 else 00284 { 00285 m_pResultPath->ClipPathToPath( *pFillPath, m_pResultPath, 7, 00286 NodeClipViewController::CLIPVIEW_TOLERANCE, 00287 Flatness, 00288 Flatness ); 00289 00290 // there is a bug here somewhere. If pCreatedByNode just happens to have a stroke 00291 // applied to it, then the above call to ClipPathToPath seems to invert the path! 00292 // I'm not sure what to do about this, so for now it going to have to be left .... 00293 // NOTE: to observe the bug, group two (overlapping) quickshapes, apply a stroke to 00294 // the group and slice with a line. 00295 } 00296 } 00297 } 00298 00299 // if we're not ignoring outlines and we have a valid outline, 00300 // then throw it into the soup. 00301 if (m_OutlineRule != IGNORE_OUTLINES) 00302 { 00303 if (pLinePath != NULL) 00304 { 00305 m_pResultPath->ClipPathToPath( *pLinePath, m_pResultPath, m_ClipPathRule | 0x10, 00306 NodeClipViewController::CLIPVIEW_TOLERANCE, 00307 Flatness, 00308 Flatness ); 00309 } 00310 00311 // a side-effect of the ClipPathToPath call above is that it ensures the path is tidy. 00312 // this ClipPathToPath call does the same thing. 00313 else 00314 m_pResultPath->ClipPathToPath(*m_pResultPath, m_pResultPath, 3, 20, 75, 75); 00315 } 00316 00317 // PathBecomeA tries to return closed paths, so ensure our results path is marked so. 00318 if (!m_pResultPath->IsFilled) 00319 m_pResultPath->IsFilled = TRUE; 00320 00321 // clear away all our temporary info. 00322 // no NULL assignments required, as these vars are all about to go out of scope. 00323 if (pLinePath != NULL) 00324 delete pLinePath; 00325 00326 if (fDeleteFillPath && pFillPath != NULL) 00327 delete pFillPath; 00328 00329 if (pAttrMap != NULL) 00330 { 00331 if (fDeleteAttributes) 00332 pAttrMap->DeleteAttributes(); 00333 00334 delete pAttrMap; 00335 } 00336 00337 pNewNode->CascadeDelete(); 00338 delete pNewNode; 00339 00340 return TRUE; 00341 } 00342 00343 00344 00345 /******************************************************************************************** 00346 00347 > Path* PathBecomeA::ExtractOutlineAsPath(NodePath* pSrcNodePath, CCAttrMap* pAttrMap) 00348 00349 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00350 Created: 18/11/2000 00351 Inputs: pSrcNodePath a NodePath whose outline we're going to extract. 00352 pAttrMap the attribute map from which we'll get line attributes, 00353 eg width, cap style, join style etc. 00354 Returns: A ptr to a path if successful, 00355 NULL otherwise. 00356 00357 Purpose: Given a NodePath and an attribute map, construct the Path which delineates 00358 the NodePath's outline. If the line width of the path is 0pt, you'll get 00359 a NULL ptr back. 00360 00361 Notes: This method returns a Path allocated on the heap - it's *your* responsibility 00362 to delete it! 00363 00364 Either input can be NULL, in which case you'll get a NULL ptr back. 00365 00366 This method should really just take a pure Path* for flexibility, 00367 however we make use of MakeNodePathFromAttributes(), which is a NodePath 00368 method. I'm fed up of rewriting other people's code at the moment and 00369 I don't want to potentially destabilise the code base before a release. 00370 If you want to rewrite MakeNodePath... and this method to take a pure Path*, 00371 be my guest :o) 00372 00373 ********************************************************************************************/ 00374 Path* PathBecomeA::ExtractOutlineAsPath(NodePath* pSrcNodePath, CCAttrMap* pAttrMap) 00375 { 00376 // we can accept NULL inputs - we just don't do any processing for them. 00377 if (pAttrMap == NULL || pSrcNodePath == NULL) 00378 return NULL; 00379 00380 const INT32 Flatness = NodeClipViewController::EstimatePathFlatness(); 00381 00382 Path* pFillPath = &( pSrcNodePath->InkPath ); 00383 Path* pStrokePath = NULL; 00384 00385 // we only need to generate an outline path if the path has non-transparent line colour. 00386 BOOL bHaveAnOutline = FALSE; 00387 AttrStrokeColour* pAttrStrokeColour = NULL; 00388 pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStrokeColour), (void*&)pAttrStrokeColour ); 00389 if (pAttrStrokeColour != NULL) 00390 { 00391 DocColour* pLineColour = pAttrStrokeColour->GetStartColour(); 00392 if (pLineColour != NULL && !pLineColour->IsTransparent()) 00393 { 00394 bHaveAnOutline = TRUE; 00395 } 00396 } 00397 00398 // ok, if we don't have an outline, then go home now. 00399 if (!bHaveAnOutline) 00400 return NULL; 00401 00402 // if the line is stroked, then use the stroke's path-processor to give us the outline. 00403 AttrStrokeType* pAttrStrokeType = NULL; 00404 pAttrMap->Lookup( CC_RUNTIME_CLASS(AttrStrokeType), (void*&)pAttrStrokeType ); 00405 if (pAttrStrokeType != NULL && pAttrStrokeType->HasPathProcessor()) 00406 { 00407 PathProcessorStroke* pPPS = pAttrStrokeType->GetPathProcessor(); 00408 if (pPPS != NULL) 00409 { 00410 pStrokePath = pPPS->GetProcessedPath(pFillPath, pAttrMap); 00411 pStrokePath->ClipPathToPath(*pStrokePath, pStrokePath, 3 | 0x10, 20, 75, 75); 00412 } 00413 } 00414 00415 // if it's a simple line, then use DavidMc's MakeNodePath... method. 00416 else 00417 { 00418 NodePath* pNodeOutline = NULL; 00419 pNodeOutline = pSrcNodePath->MakeNodePathFromAttributes(Flatness, 00420 pAttrMap, 00421 TRUE); 00422 if (pNodeOutline != NULL) 00423 { 00424 pStrokePath = pNodeOutline->GetPathCopy(); 00425 delete pNodeOutline; 00426 pNodeOutline = NULL; 00427 } 00428 } 00429 00430 return pStrokePath; 00431 } 00432 00433 00434 00435 //------------------------------------------------------------------------------------------- 00436 //------------------------------------------------------------------------------------------- 00437 //------------------------------------------------------------------------------------------- 00438 //------------------------------------------------------------------------------------------- 00439 00440 00441 00442 /******************************************************************************************** 00443 00444 > ContourBecomeA2::ContourBecomeA2( UndoableOperation* pUndoOp, 00445 MILLIPOINT Width, 00446 Path* pResultPath ) 00447 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00448 Created: 21/11/2000 00449 Inputs: pUndoOp pointer to an Op, if you have one. 00450 Width how far out you want me to contour, in millipoints. 00451 pResultPath where you want me to plonk the contour - must point to 00452 an initialised path which you don't mind me emptying. 00453 00454 Purpose: Constructor for a new ContourBecomeA, to do your contouring dirty work :o) 00455 00456 ********************************************************************************************/ 00457 ContourBecomeA2::ContourBecomeA2( UndoableOperation* pUndoOp, 00458 MILLIPOINT Width, 00459 Path* pResultPath ) 00460 : PathBecomeA( BECOMEA_PASSBACK, 00461 CC_RUNTIME_CLASS(NodePath), 00462 pUndoOp, 00463 FALSE, 00464 pResultPath, 00465 ADD_OUTLINES ) 00466 { 00467 m_Width = Width; 00468 SetDoSilhouette(TRUE); 00469 SetDoShadowSilhouettes(TRUE); 00470 } 00471 00472 00473 00474 /******************************************************************************************** 00475 00476 > BOOL ContourBecomeA2::PassBack( NodeRenderableInk* pNewNode, 00477 NodeRenderableInk* pCreatedByNode, 00478 CCAttrMap* pAttrMap ) 00479 Author: Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com> 00480 Created: 21/11/2000 00481 00482 Inputs: pNewNode ptr to the NodePath which you're passing in to me 00483 (don't expect it back). 00484 pCreatedByNode ptr to the node which created the NodePath - useful to me 00485 if you don't supply an attr-map. 00486 pAttrMap ptr to an attr-map for me to get line attributes out of 00487 when I stroke the path. Not compulsory 00488 00489 Outputs: pResultPath will contain the result of doing a contour outwards from the 00490 DoBecomeA-ing objects. 00491 00492 Returns: TRUE if successful, FALSE if not. 00493 00494 Purpose: PassBack function fer lettin' me do ma contour thang. 00495 00496 ********************************************************************************************/ 00497 BOOL ContourBecomeA2::PassBack( NodeRenderableInk* pNewNode, 00498 NodeRenderableInk* pCreatedByNode, 00499 CCAttrMap* pAttrMap ) 00500 { 00501 Path* pContourPath = NULL; 00502 Path* pSummedContourPath = m_pResultPath; 00503 00504 m_pResultPath = new Path; 00505 BOOL ok = (m_pResultPath != NULL); 00506 if (ok) ok = m_pResultPath->Initialise(); 00507 if (ok) 00508 { 00509 PathBecomeA::PassBack(pNewNode, pCreatedByNode, pAttrMap); 00510 ok = (m_pResultPath->GetNumCoords() != 0); 00511 } 00512 if (ok) 00513 { 00514 pContourPath = new Path; 00515 ok = (pContourPath != NULL); 00516 } 00517 if (ok) ok = pContourPath->Initialise(); 00518 if (ok) 00519 { 00520 TRACEUSER( "Karim", _T("Node at 0x%x; ResultPath - %d coords.\n"), 00521 pCreatedByNode, 00522 m_pResultPath->GetNumCoords()); 00523 00524 m_pResultPath->InitializeContourValues(2 * m_Width); 00525 m_pResultPath->GetContourForStep(pContourPath); 00526 00527 delete m_pResultPath; 00528 m_pResultPath = pSummedContourPath; 00529 00530 if (pContourPath->GetNumCoords() != 0) 00531 pContourPath->ClipPathToPath(*m_pResultPath, m_pResultPath, 7, 20, 75, 75); 00532 00533 if (!m_pResultPath->IsFilled) 00534 m_pResultPath->IsFilled = TRUE; 00535 00536 delete pContourPath; 00537 } 00538 00539 return ok; 00540 }