Path: blob/master/sage/graphs/planarity/graphDrawPlanar_Extensions.c
4057 views
/*1Planarity-Related Graph Algorithms Project2Copyright (c) 1997-2010, John M. Boyer3All rights reserved. Includes a reference implementation of the following:45* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,6Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,7University of Victoria, 2001.89* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)10Planarity by Edge Addition". Journal of Graph Algorithms and Applications,11Vol. 8, No. 3, pp. 241-273, 2004.1213* John M. Boyer. "A New Method for Efficiently Generating Planar Graph14Visibility Representations". In P. Eades and P. Healy, editors,15Proceedings of the 13th International Conference on Graph Drawing 2005,16Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.1718Redistribution and use in source and binary forms, with or without modification,19are permitted provided that the following conditions are met:2021* Redistributions of source code must retain the above copyright notice, this22list of conditions and the following disclaimer.2324* Redistributions in binary form must reproduce the above copyright notice, this25list of conditions and the following disclaimer in the documentation and/or26other materials provided with the distribution.2728* Neither the name of the Planarity-Related Graph Algorithms Project nor the names29of its contributors may be used to endorse or promote products derived from this30software without specific prior written permission.3132THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"33AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE34IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE35DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR36ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES37(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;38LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON39ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT40(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS41SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.42*/4344#include <stdlib.h>4546#include "graphDrawPlanar.private.h"47#include "graphDrawPlanar.h"4849extern void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink);50extern int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink);5152extern int _ComputeVisibilityRepresentation(DrawPlanarContext *context);53extern int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context);5455/* Forward declarations of local functions */5657void _DrawPlanar_ClearStructures(DrawPlanarContext *context);58int _DrawPlanar_CreateStructures(DrawPlanarContext *context);59int _DrawPlanar_InitStructures(DrawPlanarContext *context);6061/* Forward declarations of overloading functions */6263int _DrawPlanar_MergeBicomps(graphP theGraph, int I, int RootVertex, int W, int WPrevLink);64int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink);65int _DrawPlanar_EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult);66int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph);67int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph);6869void _DrawPlanar_InitGraphNode(graphP theGraph, int I);70void _DrawPlanar_InitVertexRec(graphP theGraph, int I);71void _InitDrawGraphNode(DrawPlanarContext *context, int I);72void _InitDrawVertexRec(DrawPlanarContext *context, int I);7374int _DrawPlanar_InitGraph(graphP theGraph, int N);75void _DrawPlanar_ReinitializeGraph(graphP theGraph);76int _DrawPlanar_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity);77int _DrawPlanar_SortVertices(graphP theGraph);7879int _DrawPlanar_ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize);80int _DrawPlanar_WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize);8182/* Forward declarations of functions used by the extension system */8384void *_DrawPlanar_DupContext(void *pContext, void *theGraph);85void _DrawPlanar_FreeContext(void *);8687/****************************************************************************88* DRAWPLANAR_ID - the variable used to hold the integer identifier for this89* extension, enabling this feature's extension context to be distinguished90* from other features' extension contexts that may be attached to a graph.91****************************************************************************/9293int DRAWPLANAR_ID = 0;9495/****************************************************************************96gp_AttachDrawPlanar()9798This function adjusts the graph data structure to attach the planar graph99drawing feature.100101To activate this feature during gp_Embed(), use EMBEDFLAGS_DRAWPLANAR.102103This method may be called immediately after gp_New() in the case of104invoking gp_Read(). For generating graphs, gp_InitGraph() can be invoked105before or after this enabling method. This method detects if the core106graph has already been initialized, and if so, it will initialize the107additional data structures specific to planar graph drawing. This makes108it possible to invoke gp_New() and gp_InitGraph() together, and then attach109this feature only if it is requested at run-time.110111Returns OK for success, NOTOK for failure.112****************************************************************************/113114int gp_AttachDrawPlanar(graphP theGraph)115{116DrawPlanarContext *context = NULL;117118// If the drawing feature has already been attached to the graph,119// then there is no need to attach it again120gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);121if (context != NULL)122{123return OK;124}125126// Allocate a new extension context127context = (DrawPlanarContext *) malloc(sizeof(DrawPlanarContext));128if (context == NULL)129{130return NOTOK;131}132133// First, tell the context that it is not initialized134context->initialized = 0;135136// Save a pointer to theGraph in the context137context->theGraph = theGraph;138139// Put the overload functions into the context function table.140// gp_AddExtension will overload the graph's functions with these, and141// return the base function pointers in the context function table142memset(&context->functions, 0, sizeof(graphFunctionTable));143144context->functions.fpMergeBicomps = _DrawPlanar_MergeBicomps;145context->functions.fpHandleInactiveVertex = _DrawPlanar_HandleInactiveVertex;146context->functions.fpEmbedPostprocess = _DrawPlanar_EmbedPostprocess;147context->functions.fpCheckEmbeddingIntegrity = _DrawPlanar_CheckEmbeddingIntegrity;148context->functions.fpCheckObstructionIntegrity = _DrawPlanar_CheckObstructionIntegrity;149150context->functions.fpInitGraphNode = _DrawPlanar_InitGraphNode;151context->functions.fpInitVertexRec = _DrawPlanar_InitVertexRec;152153context->functions.fpInitGraph = _DrawPlanar_InitGraph;154context->functions.fpReinitializeGraph = _DrawPlanar_ReinitializeGraph;155context->functions.fpEnsureArcCapacity = _DrawPlanar_EnsureArcCapacity;156context->functions.fpSortVertices = _DrawPlanar_SortVertices;157158context->functions.fpReadPostprocess = _DrawPlanar_ReadPostprocess;159context->functions.fpWritePostprocess = _DrawPlanar_WritePostprocess;160161_DrawPlanar_ClearStructures(context);162163// Store the Draw context, including the data structure and the164// function pointers, as an extension of the graph165if (gp_AddExtension(theGraph, &DRAWPLANAR_ID, (void *) context,166_DrawPlanar_DupContext, _DrawPlanar_FreeContext,167&context->functions) != OK)168{169_DrawPlanar_FreeContext(context);170return NOTOK;171}172173// Create the Draw-specific structures if the size of the graph is known174// Attach functions are typically invoked after gp_New(), but if a graph175// extension must be attached before gp_Read(), then the attachment176// also happens before gp_InitGraph() because gp_Read() invokes init only177// after it reads the order N of the graph. Hence, this attach call would178// occur when N==0 in the case of gp_Read().179// But if a feature is attached after gp_InitGraph(), then N > 0 and so we180// need to create and initialize all the custom data structures181if (theGraph->N > 0)182{183if (_DrawPlanar_CreateStructures(context) != OK ||184_DrawPlanar_InitStructures(context) != OK)185{186_DrawPlanar_FreeContext(context);187return NOTOK;188}189}190191return OK;192}193194/********************************************************************195gp_DetachDrawPlanar()196********************************************************************/197198int gp_DetachDrawPlanar(graphP theGraph)199{200return gp_RemoveExtension(theGraph, DRAWPLANAR_ID);201}202203/********************************************************************204_DrawPlanar_ClearStructures()205********************************************************************/206207void _DrawPlanar_ClearStructures(DrawPlanarContext *context)208{209if (!context->initialized)210{211// Before initialization, the pointers are stray, not NULL212// Once NULL or allocated, free() or LCFree() can do the job213context->G = NULL;214context->V = NULL;215216context->initialized = 1;217}218else219{220if (context->G != NULL)221{222free(context->G);223context->G = NULL;224}225if (context->V != NULL)226{227free(context->V);228context->V = NULL;229}230}231}232233/********************************************************************234_DrawPlanar_CreateStructures()235Create uninitialized structures for the vertex and graph node236levels, and initialized structures for the graph level237********************************************************************/238int _DrawPlanar_CreateStructures(DrawPlanarContext *context)239{240int N = context->theGraph->N;241int Gsize = context->theGraph->edgeOffset + context->theGraph->arcCapacity;242243if (N <= 0)244return NOTOK;245246if ((context->G = (DrawPlanar_GraphNodeP) malloc(Gsize*sizeof(DrawPlanar_GraphNode))) == NULL ||247(context->V = (DrawPlanar_VertexRecP) malloc(N*sizeof(DrawPlanar_VertexRec))) == NULL248)249{250return NOTOK;251}252253return OK;254}255256/********************************************************************257_DrawPlanar_InitStructures()258Intended to be called when N>0.259Initializes vertex and graph node levels only. Graph level is260already initialized in _CreateStructures()261********************************************************************/262int _DrawPlanar_InitStructures(DrawPlanarContext *context)263{264int I, N = context->theGraph->N;265int Gsize = context->theGraph->edgeOffset + context->theGraph->arcCapacity;266267if (N <= 0)268return NOTOK;269270for (I = 0; I < Gsize; I++)271_InitDrawGraphNode(context, I);272273for (I = 0; I < N; I++)274_InitDrawVertexRec(context, I);275276return OK;277}278279/********************************************************************280_DrawPlanar_DupContext()281********************************************************************/282283void *_DrawPlanar_DupContext(void *pContext, void *theGraph)284{285DrawPlanarContext *context = (DrawPlanarContext *) pContext;286DrawPlanarContext *newContext = (DrawPlanarContext *) malloc(sizeof(DrawPlanarContext));287288if (newContext != NULL)289{290int N = ((graphP) theGraph)->N;291int Gsize = ((graphP) theGraph)->edgeOffset + ((graphP) theGraph)->arcCapacity;292293*newContext = *context;294295newContext->theGraph = (graphP) theGraph;296297newContext->initialized = 0;298_DrawPlanar_ClearStructures(newContext);299if (N > 0)300{301if (_DrawPlanar_CreateStructures(newContext) != OK)302{303_DrawPlanar_FreeContext(newContext);304return NULL;305}306307// Initialize custom data structures by copying308memcpy(newContext->G, context->G, Gsize*sizeof(DrawPlanar_GraphNode));309memcpy(newContext->V, context->V, N*sizeof(DrawPlanar_VertexRec));310}311}312313return newContext;314}315316/********************************************************************317_DrawPlanar_FreeContext()318********************************************************************/319320void _DrawPlanar_FreeContext(void *pContext)321{322DrawPlanarContext *context = (DrawPlanarContext *) pContext;323324_DrawPlanar_ClearStructures(context);325free(pContext);326}327328/********************************************************************329********************************************************************/330331int _DrawPlanar_InitGraph(graphP theGraph, int N)332{333DrawPlanarContext *context = NULL;334gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);335336if (context == NULL)337{338return NOTOK;339}340else341{342theGraph->N = N;343theGraph->edgeOffset = 2*N;344if (theGraph->arcCapacity == 0)345theGraph->arcCapacity = 2*DEFAULT_EDGE_LIMIT*N;346347// Create custom structures, initialized at graph level,348// uninitialized at vertex and graph node levels.349if (_DrawPlanar_CreateStructures(context) != OK)350{351return NOTOK;352}353354// This call initializes the base graph structures, but it also355// initializes the custom graphnode and vertex level structures356// due to the overloads of InitGraphNode and InitVertexRec357context->functions.fpInitGraph(theGraph, N);358}359360return OK;361}362363/********************************************************************364********************************************************************/365366void _DrawPlanar_ReinitializeGraph(graphP theGraph)367{368DrawPlanarContext *context = NULL;369gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);370371if (context != NULL)372{373// Reinitialization can go much faster if the underlying374// init graph node and vertex rec functions are called,375// rather than the overloads of this module, because it376// avoids lots of unnecessary gp_FindExtension() calls.377if (theGraph->functions.fpInitGraphNode == _DrawPlanar_InitGraphNode &&378theGraph->functions.fpInitVertexRec == _DrawPlanar_InitVertexRec)379{380// Restore the graph function pointers381theGraph->functions.fpInitGraphNode = context->functions.fpInitGraphNode;382theGraph->functions.fpInitVertexRec = context->functions.fpInitVertexRec;383384// Reinitialize the graph385context->functions.fpReinitializeGraph(theGraph);386387// Restore the function pointers that attach this feature388theGraph->functions.fpInitGraphNode = _DrawPlanar_InitGraphNode;389theGraph->functions.fpInitVertexRec = _DrawPlanar_InitVertexRec;390391// Do the reinitialization that is specific to this module392// InitStructures does vertex and graphnode levels393_DrawPlanar_InitStructures(context);394// Initialization of any graph level data structures follows here395}396397// If optimization is not possible, then just stick with what works.398// Reinitialize the graph-level structure (of which there are none)399// and then invoke the reinitialize function.400else401{402// No need to call _InitStructures(context) here because the underlying403// function fpReinitializeGraph() already does the vertex and graph node404// levels due to the overloads of fpInitGraphNode() and fpInitVertexRec().405context->functions.fpReinitializeGraph(theGraph);406// Graph level reintializations would follow here407}408}409}410411/********************************************************************412The current implementation does not support an increase of arc413(edge record) capacity once the extension is attached to the graph414data structure. This is only due to not being necessary to support.415For now, it is easy to ensure the correct capacity before attaching416the extension, but support could be added later if there is some417reason to do so.418********************************************************************/419420int _DrawPlanar_EnsureArcCapacity(graphP theGraph, int requiredArcCapacity)421{422return NOTOK;423}424425/********************************************************************426********************************************************************/427428int _DrawPlanar_SortVertices(graphP theGraph)429{430DrawPlanarContext *context = NULL;431gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);432433if (context != NULL)434{435if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR)436{437int I;438DrawPlanar_GraphNodeP newG = NULL;439DrawPlanar_VertexRecP newV = NULL;440441// Relabel the context data members that indicate vertices442for (I=0; I < theGraph->N; I++)443{444context->V[I].ancestor = theGraph->G[context->V[I].ancestor].v;445context->V[I].ancestorChild = theGraph->G[context->V[I].ancestorChild].v;446}447448// Now we have to sort the first N positions of context G and V arrays449// For simplicity we do this out-of-place with extra arrays450if ((newG = (DrawPlanar_GraphNodeP) malloc(theGraph->N * sizeof(DrawPlanar_GraphNode))) == NULL)451{452return NOTOK;453}454455if ((newV = (DrawPlanar_VertexRecP) malloc(theGraph->N * sizeof(DrawPlanar_VertexRec))) == NULL)456{457free(newG);458return NOTOK;459}460461// Let X==G[I].v be the location where the I^{th} record goes462// Given newG and newV arrays, we want to move context G[I] to newG[X]463// Then copy newG into G and newV into V464for (I=0; I < theGraph->N; I++)465{466newG[theGraph->G[I].v] = context->G[I];467newV[theGraph->G[I].v] = context->V[I];468}469470memcpy(context->G, newG, theGraph->N * sizeof(DrawPlanar_GraphNode));471memcpy(context->V, newV, theGraph->N * sizeof(DrawPlanar_VertexRec));472473free(newG);474free(newV);475}476477if (context->functions.fpSortVertices(theGraph) != OK)478return NOTOK;479480return OK;481}482483return NOTOK;484}485486/********************************************************************487Returns OK for a successful merge, NOTOK on an internal failure,488or NONEMBEDDABLE if the merge is blocked489********************************************************************/490491int _DrawPlanar_MergeBicomps(graphP theGraph, int I, int RootVertex, int W, int WPrevLink)492{493DrawPlanarContext *context = NULL;494gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);495496if (context != NULL)497{498if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR)499{500_CollectDrawingData(context, RootVertex, W, WPrevLink);501}502503return context->functions.fpMergeBicomps(theGraph, I, RootVertex, W, WPrevLink);504}505506return NOTOK;507}508509/********************************************************************510********************************************************************/511512int _DrawPlanar_HandleInactiveVertex(graphP theGraph, int BicompRoot, int *pW, int *pWPrevLink)513{514DrawPlanarContext *context = NULL;515gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);516517if (context != NULL)518{519int RetVal = context->functions.fpHandleInactiveVertex(theGraph, BicompRoot, pW, pWPrevLink);520521if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR)522{523if (_BreakTie(context, BicompRoot, *pW, *pWPrevLink) != OK)524return NOTOK;525}526527return RetVal;528}529530return NOTOK;531}532533/********************************************************************534********************************************************************/535536void _DrawPlanar_InitGraphNode(graphP theGraph, int I)537{538DrawPlanarContext *context = NULL;539gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);540541if (context != NULL)542{543context->functions.fpInitGraphNode(theGraph, I);544_InitDrawGraphNode(context, I);545}546}547548/********************************************************************549********************************************************************/550551void _InitDrawGraphNode(DrawPlanarContext *context, int I)552{553context->G[I].pos = 0;554context->G[I].start = 0;555context->G[I].end = 0;556}557558/********************************************************************559********************************************************************/560561void _DrawPlanar_InitVertexRec(graphP theGraph, int I)562{563DrawPlanarContext *context = NULL;564gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);565566if (context != NULL)567{568context->functions.fpInitVertexRec(theGraph, I);569_InitDrawVertexRec(context, I);570}571}572573/********************************************************************574********************************************************************/575576void _InitDrawVertexRec(DrawPlanarContext *context, int I)577{578context->V[I].drawingFlag = DRAWINGFLAG_BEYOND;579context->V[I].ancestorChild = 0;580context->V[I].ancestor = 0;581context->V[I].tie[0] = context->V[I].tie[1] = NIL;582}583584/********************************************************************585********************************************************************/586587int _DrawPlanar_EmbedPostprocess(graphP theGraph, int I, int edgeEmbeddingResult)588{589DrawPlanarContext *context = NULL;590gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);591592if (context != NULL)593{594int RetVal = context->functions.fpEmbedPostprocess(theGraph, I, edgeEmbeddingResult);595596if (theGraph->embedFlags == EMBEDFLAGS_DRAWPLANAR)597{598if (RetVal == OK)599{600RetVal = _ComputeVisibilityRepresentation(context);601}602}603604return RetVal;605}606607return NOTOK;608}609610/********************************************************************611********************************************************************/612613int _DrawPlanar_CheckEmbeddingIntegrity(graphP theGraph, graphP origGraph)614{615DrawPlanarContext *context = NULL;616gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);617618if (context != NULL)619{620if (context->functions.fpCheckEmbeddingIntegrity(theGraph, origGraph) != OK)621return NOTOK;622623return _CheckVisibilityRepresentationIntegrity(context);624}625626return NOTOK;627}628629/********************************************************************630********************************************************************/631632int _DrawPlanar_CheckObstructionIntegrity(graphP theGraph, graphP origGraph)633{634return OK;635}636637/********************************************************************638********************************************************************/639640int _DrawPlanar_ReadPostprocess(graphP theGraph, void *extraData, long extraDataSize)641{642DrawPlanarContext *context = NULL;643gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);644645if (context != NULL)646{647if (context->functions.fpReadPostprocess(theGraph, extraData, extraDataSize) != OK)648return NOTOK;649650else if (extraData != NULL && extraDataSize > 0)651{652int I, tempInt;653char line[64], tempChar;654655sprintf(line, "<%s>", DRAWPLANAR_NAME);656657// Find the start of the data for this feature658extraData = strstr(extraData, line);659if (extraData == NULL)660return NOTOK;661662// Advance past the start tag663extraData = (void *) ((char *) extraData + strlen(line)+1);664665// Read the N lines of vertex information666for (I = 0; I < theGraph->N; I++)667{668sscanf(extraData, " %d%c %d %d %d", &tempInt, &tempChar,669&context->G[I].pos,670&context->G[I].start,671&context->G[I].end);672673extraData = strchr(extraData, '\n') + 1;674}675676// Read the lines that contain edge information677for (I = theGraph->edgeOffset; I < theGraph->edgeOffset+2*theGraph->M; I++)678{679sscanf(extraData, " %d%c %d %d %d", &tempInt, &tempChar,680&context->G[I].pos,681&context->G[I].start,682&context->G[I].end);683684extraData = strchr(extraData, '\n') + 1;685}686}687688return OK;689}690691return NOTOK;692}693694/********************************************************************695********************************************************************/696697int _DrawPlanar_WritePostprocess(graphP theGraph, void **pExtraData, long *pExtraDataSize)698{699DrawPlanarContext *context = NULL;700gp_FindExtension(theGraph, DRAWPLANAR_ID, (void *)&context);701702if (context != NULL)703{704if (context->functions.fpWritePostprocess(theGraph, pExtraData, pExtraDataSize) != OK)705return NOTOK;706else707{708char line[64];709int maxLineSize = 64, extraDataPos = 0, I;710int GSize = theGraph->edgeOffset + theGraph->arcCapacity;711char *extraData = (char *) malloc((GSize + 2) * maxLineSize * sizeof(char));712713if (extraData == NULL)714return NOTOK;715716// Bit of an unlikely case, but for safety, a bigger maxLineSize717// and line array size are needed to handle very large graphs718if (theGraph->N > 2000000000)719{720free(extraData);721return NOTOK;722}723724sprintf(line, "<%s>\n", DRAWPLANAR_NAME);725strcpy(extraData+extraDataPos, line);726extraDataPos += (int) strlen(line);727728for (I = 0; I < theGraph->N; I++)729{730sprintf(line, "%d: %d %d %d\n", I,731context->G[I].pos,732context->G[I].start,733context->G[I].end);734strcpy(extraData+extraDataPos, line);735extraDataPos += (int) strlen(line);736}737738for (I = theGraph->edgeOffset; I < theGraph->edgeOffset+2*theGraph->M; I++)739{740sprintf(line, "%d: %d %d %d\n", I,741context->G[I].pos,742context->G[I].start,743context->G[I].end);744strcpy(extraData+extraDataPos, line);745extraDataPos += (int) strlen(line);746}747748sprintf(line, "</%s>\n", DRAWPLANAR_NAME);749strcpy(extraData+extraDataPos, line);750extraDataPos += (int) strlen(line);751752*pExtraData = (void *) extraData;753*pExtraDataSize = extraDataPos * sizeof(char);754}755756return OK;757}758759return NOTOK;760}761762763