/*
 * tkCanvUtil.c --
 *
 *      This procedure contains a collection of utility procedures
 *      used by the implementations of various canvas item types.
 *
 * Copyright (c) 1994 Sun Microsystems, Inc.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkCanvUtil.c,v 1.10 2003/01/17 19:54:09 drh Exp $
 */

#include "tkPort.h"
#include "tkInt.h"
#include "tkCanvases.h"

/*
 * Custom option for handling "-tags" options for canvas items:
 */
#include "tkCanvas.h"
#include <assert.h>


/*
 *----------------------------------------------------------------------
 *
 * Tk_CanvasTkwin --
 *
 *      Given a token for a canvas, this procedure returns the
 *      widget that represents the canvas.
 *
 * Results:
 *      The return value is a handle for the widget.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Tk_Window
Tk_CanvasTkwin(canvas)
    Tk_Canvas canvas;                   /* Token for the canvas. */
{
    TkCanvas *canvasPtr = (TkCanvas *) canvas;
    return canvasPtr->tkwin;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_CanvasDrawableCoords --
 *
 *      Given an (x,y) coordinate pair within a canvas, this procedure
 *      returns the corresponding coordinates at which the point should
 *      be drawn in the drawable used for display.
 *
 * Results:
 *      There is no return value.  The values at *drawableXPtr and
 *      *drawableYPtr are filled in with the coordinates at which
 *      x and y should be drawn.  These coordinates are clipped
 *      to fit within a "short", since this is what X uses in
 *      most cases for drawing.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Tk_CanvasDrawableCoords(canvas, x, y, drawableXPtr, drawableYPtr)
    Tk_Canvas canvas;                   /* Token for the canvas. */
    double x, y;                        /* Coordinates in canvas space. */
    short *drawableXPtr, *drawableYPtr; /* Screen coordinates are stored
					 * here. */
{
    TkCanvas *canvasPtr = (TkCanvas *) canvas;
    double tmp;

    tmp = x - canvasPtr->drawableXOrigin;
    if (tmp > 0) {
	tmp += 0.5;
    } else {
	tmp -= 0.5;
    }
    if (tmp > 32767) {
	*drawableXPtr = 32767;
    } else if (tmp < -32768) {
	*drawableXPtr = -32768;
    } else {
	*drawableXPtr = (short) tmp;
    }

    tmp = y  - canvasPtr->drawableYOrigin;
    if (tmp > 0) {
	tmp += 0.5;
    } else {
	tmp -= 0.5;
    }
    if (tmp > 32767) {
	*drawableYPtr = 32767;
    } else if (tmp < -32768) {
	*drawableYPtr = -32768;
    } else {
	*drawableYPtr = (short) tmp;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_CanvasWindowCoords --
 *
 *      Given an (x,y) coordinate pair within a canvas, this procedure
 *      returns the corresponding coordinates in the canvas's window.
 *
 * Results:
 *      There is no return value.  The values at *screenXPtr and
 *      *screenYPtr are filled in with the coordinates at which
 *      (x,y) appears in the canvas's window.  These coordinates
 *      are clipped to fit within a "short", since this is what X
 *      uses in most cases for drawing.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Tk_CanvasWindowCoords(canvas, x, y, screenXPtr, screenYPtr)
    Tk_Canvas canvas;                   /* Token for the canvas. */
    double x, y;                        /* Coordinates in canvas space. */
    short *screenXPtr, *screenYPtr;     /* Screen coordinates are stored
					 * here. */
{
    TkCanvas *canvasPtr = (TkCanvas *) canvas;
    double tmp;

    tmp = x - canvasPtr->xOrigin;
    if (tmp > 0) {
	tmp += 0.5;
    } else {
	tmp -= 0.5;
    }
    if (tmp > 32767) {
	*screenXPtr = 32767;
    } else if (tmp < -32768) {
	*screenXPtr = -32768;
    } else {
	*screenXPtr = (short) tmp;
    }

    tmp = y  - canvasPtr->yOrigin;
    if (tmp > 0) {
	tmp += 0.5;
    } else {
	tmp -= 0.5;
    }
    if (tmp > 32767) {
	*screenYPtr = 32767;
    } else if (tmp < -32768) {
	*screenYPtr = -32768;
    } else {
	*screenYPtr = (short) tmp;
    }
}

/*
 *--------------------------------------------------------------
 *
 * Tk_CanvasGetCoord --
 *
 *      Given a string, returns a floating-point canvas coordinate
 *      corresponding to that string.
 *
 * Results:
 *      The return value is a standard Tcl return result.  If
 *      TCL_OK is returned, then everything went well and the
 *      canvas coordinate is stored at *doublePtr;  otherwise
 *      TCL_ERROR is returned and an error message is left in
 *      the interp's result.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

int
Tk_CanvasGetCoord(interp, canvas, string, doublePtr)
    Tcl_Interp *interp;         /* Interpreter for error reporting. */
    Tk_Canvas canvas;           /* Canvas to which coordinate applies. */
    CONST char *string;         /* Describes coordinate (any screen
				 * coordinate form may be used here). */
    double *doublePtr;          /* Place to store converted coordinate. */
{
    TkCanvas *canvasPtr = (TkCanvas *) canvas;
    if (Tk_GetScreenMM(canvasPtr->interp, canvasPtr->tkwin, string,
	    doublePtr) != TCL_OK) {
	return TCL_ERROR;
    }
    *doublePtr *= canvasPtr->pixelsPerMM;
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_CanvasGetCoordFromObj --
 *
 *      Given a string, returns a floating-point canvas coordinate
 *      corresponding to that string.
 *
 * Results:
 *      The return value is a standard Tcl return result.  If
 *      TCL_OK is returned, then everything went well and the
 *      canvas coordinate is stored at *doublePtr;  otherwise
 *      TCL_ERROR is returned and an error message is left in
 *      interp->result.
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

int
Tk_CanvasGetCoordFromObj(interp, canvas, obj, doublePtr)
    Tcl_Interp *interp;         /* Interpreter for error reporting. */
    Tk_Canvas canvas;           /* Canvas to which coordinate applies. */
    Tcl_Obj *obj;               /* Describes coordinate (any screen
				 * coordinate form may be used here). */
    double *doublePtr;          /* Place to store converted coordinate. */
{
    TkCanvas *canvasPtr = (TkCanvas *) canvas;
    if (Tk_GetMMFromObj(canvasPtr->interp, canvasPtr->tkwin, obj,
	    doublePtr) != TCL_OK) {
	return TCL_ERROR;
    }
    *doublePtr *= canvasPtr->pixelsPerMM;
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_CanvasSetStippleOrigin --
 *
 *      This procedure sets the stipple origin in a graphics context
 *      so that stipples drawn with the GC will line up with other
 *      stipples previously drawn in the canvas.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The graphics context is modified.
 *
 *----------------------------------------------------------------------
 */

void
Tk_CanvasSetStippleOrigin(canvas, gc)
    Tk_Canvas canvas;           /* Token for a canvas. */
    GC gc;                      /* Graphics context that is about to be
				 * used to draw a stippled pattern as
				 * part of redisplaying the canvas. */

{
    TkCanvas *canvasPtr = (TkCanvas *) canvas;

    XSetTSOrigin(canvasPtr->display, gc, -canvasPtr->drawableXOrigin,
	    -canvasPtr->drawableYOrigin);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_CanvasSetOffset--
 *
 *      This procedure sets the stipple offset in a graphics
 *      context so that stipples drawn with the GC will
 *      line up with other stipples with the same offset.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The graphics context is modified.
 *
 *----------------------------------------------------------------------
 */

void
Tk_CanvasSetOffset(canvas, gc, offset)
    Tk_Canvas canvas;           /* Token for a canvas. */
    GC gc;                      /* Graphics context that is about to be
				 * used to draw a stippled pattern as
				 * part of redisplaying the canvas. */
    Tk_TSOffset *offset;        /* offset (may be NULL pointer)*/
{
    TkCanvas *canvasPtr = (TkCanvas *) canvas;
    int flags = 0;
    int x = - canvasPtr->drawableXOrigin;
    int y = - canvasPtr->drawableYOrigin;

    if (offset != NULL) {
	flags = offset->flags;
	x += offset->xoffset;
	y += offset->yoffset;
    }
    if ((flags & TK_OFFSET_RELATIVE) && !(flags & TK_OFFSET_INDEX)) {
	Tk_SetTSOrigin(canvasPtr->tkwin, gc, x - canvasPtr->xOrigin,
		y - canvasPtr->yOrigin);
    } else {
	XSetTSOrigin(canvasPtr->display, gc, x, y);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_CanvasGetTextInfo --
 *
 *      This procedure returns a pointer to a structure containing
 *      information about the selection and insertion cursor for
 *      a canvas widget.  Items such as text items save the pointer
 *      and use it to share access to the information with the generic
 *      canvas code.
 *
 * Results:
 *      The return value is a pointer to the structure holding text
 *      information for the canvas.  Most of the fields should not
 *      be modified outside the generic canvas code;  see the user
 *      documentation for details.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Tk_CanvasTextInfo *
Tk_CanvasGetTextInfo(canvas)
    Tk_Canvas canvas;                   /* Token for the canvas widget. */
{
    return &((TkCanvas *) canvas)->textInfo;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_CanvasTagsParseProc --
 *
 *      This procedure is invoked during option processing to handle
 *      "-tags" options for canvas items.
 *
 * Results:
 *      A standard Tcl return value.
 *
 * Side effects:
 *      The tags for a given item get replaced by those indicated
 *      in the value argument.
 *
 *--------------------------------------------------------------
 */

int
Tk_CanvasTagsParseProc(clientData, interp, tkwin, arg, widgRec, offset)
    ClientData clientData;              /* Not used.*/
    Tcl_Interp *interp;                 /* Used for reporting errors. */
    Tk_Window tkwin;                    /* Window containing canvas widget. */
    Tcl_Obj *arg;                       /* Value of option (list of tag
					 * names). */
    char *widgRec;                      /* Pointer to record for item. */
    int offset;                         /* Offset into item (ignored). */
{
    register Tk_Item *itemPtr = (Tk_Item *) widgRec;
    int argc, i;
    Tcl_Obj **objv;
    Tk_Uid *newPtr;

    /*
     * Break the value up into the individual tag names.
     */

    if (Tcl_ListObjGetElements(interp, arg, &argc, &objv) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * Make sure that there's enough space in the item to hold the
     * tag names.
     */

    if (itemPtr->tagSpace < argc) {
	newPtr = (Tk_Uid *) ckalloc((unsigned) (argc * sizeof(Tk_Uid)));
	for (i = itemPtr->numTags-1; i >= 0; i--) {
	    newPtr[i] = itemPtr->tagPtr[i];
	}
	if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
	    ckfree((char *) itemPtr->tagPtr);
	}
	itemPtr->tagPtr = newPtr;
	itemPtr->tagSpace = argc;
    }
    itemPtr->numTags = argc;
    for (i = 0; i < argc; i++) {
	itemPtr->tagPtr[i] = Tk_GetUid(Tcl_GetString(objv[i]));
    }
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_CanvasTagsPrintProc --
 *
 *      This procedure is invoked by the Tk configuration code
 *      to produce a printable string for the "-tags" configuration
 *      option for canvas items.
 *
 * Results:
 *      The return value is a string describing all the tags for
 *      the item referred to by "widgRec".  In addition, *freeProcPtr
 *      is filled in with the address of a procedure to call to free
 *      the result string when it's no longer needed (or NULL to
 *      indicate that the string doesn't need to be freed).
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

Tcl_Obj *
Tk_CanvasTagsPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
    ClientData clientData;              /* Ignored. */
    Tk_Window tkwin;                    /* Window containing canvas widget. */
    char *widgRec;                      /* Pointer to record for item. */
    int offset;                         /* Ignored. */
    Tcl_FreeProc **freeProcPtr;         /* Pointer to variable to fill in with
					 * information about how to reclaim
					 * storage for return string. */
{
    register Tk_Item *itemPtr = (Tk_Item *) widgRec;
    Tcl_Obj *result = NULL;
    int i;

    result = Tcl_NewListObj(0,NULL);
    for (i=0; i < itemPtr->numTags; i++) {
	Tcl_ListObjAppendElement(NULL,result,
				Tcl_NewStringObj((char *)itemPtr->tagPtr[i],-1));
    }
    return result;
}


static int      DashConvert _ANSI_ARGS_((char *l, CONST char *p,
			int n, double width));
#define ABS(a) ((a>=0)?(a):(-(a)))

/*
 *--------------------------------------------------------------
 *
 * TkCanvasDashParseProc --
 *
 *      This procedure is invoked during option processing to handle
 *      "-dash", "-activedash" and "-disableddash" options for canvas
 *      objects.
 *
 * Results:
 *      A standard Tcl return value.
 *
 * Side effects:
 *      The dash list for a given canvas object gets replaced by
 *      those indicated in the value argument.
 *
 *--------------------------------------------------------------
 */

int
TkCanvasDashParseProc(clientData, interp, tkwin, value, widgRec, offset)
    ClientData clientData;              /* Not used.*/
    Tcl_Interp *interp;                 /* Used for reporting errors. */
    Tk_Window tkwin;                    /* Window containing canvas widget. */
    Tcl_Obj * value;                          /* Value of option. */
    char *widgRec;                      /* Pointer to record for item. */
    int offset;                         /* Offset into item. */
{
    return Tk_GetDash(interp, value, (Tk_Dash *)(widgRec+offset));
}

/*
 *--------------------------------------------------------------
 *
 * TkCanvasDashPrintProc --
 *
 *      This procedure is invoked by the Tk configuration code
 *      to produce a printable string for the "-dash", "-activedash"
 *      and "-disableddash" configuration options for canvas items.
 *
 * Results:
 *      The return value is a string describing all the dash list for
 *      the item referred to by "widgRec"and "offset".  In addition,
 *      *freeProcPtr is filled in with the address of a procedure to
 *      call to free the result string when it's no longer needed (or
 *      NULL to indicate that the string doesn't need to be freed).
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

Tcl_Obj *
TkCanvasDashPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
    ClientData clientData;              /* Ignored. */
    Tk_Window tkwin;                    /* Window containing canvas widget. */
    char *widgRec;                      /* Pointer to record for item. */
    int offset;                         /* Offset in record for item. */
    Tcl_FreeProc **freeProcPtr;         /* Pointer to variable to fill in with
					 * information about how to reclaim
					 * storage for return string. */
{
    Tk_Dash *dash = (Tk_Dash *) (widgRec+offset);
    char *buffer = NULL;
    char *p;
    int i = dash->number;
    Tcl_Obj * result = NULL;

    if (i<0) {
	p = (-i > (int) sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
	result = Tcl_NewStringObj(p,-i);
	return result;
    } else if (!i) {
	*freeProcPtr = (Tcl_FreeProc *) NULL;
	LangSetDefault(&result,"");
	return result;
    }
    result = Tcl_NewListObj(0,NULL);
    p = (i > (int) sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
    while(i--) {
	Tcl_ListObjAppendElement(NULL,result,Tcl_NewIntObj(*p++ & 0xff));
    }
    return result;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_CreateSmoothMethod --
 *
 *      This procedure is invoked to add additional values
 *      for the "-smooth" option to the list.
 *
 * Results:
 *      A standard Tcl return value.
 *
 * Side effects:
 *      In the future "-smooth <name>" will be accepted as
 *      smooth method for the line and polygon.
 *
 *--------------------------------------------------------------
 */

Tk_SmoothMethod tkBezierSmoothMethod = {
    "bezier",
    TkMakeBezierCurve,
    (void (*) _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas,
	    double *coordPtr, int numPoints, int numSteps)))
		TkMakeBezierPostscript,
};

static void SmoothMethodCleanupProc _ANSI_ARGS_((ClientData clientData,
		Tcl_Interp *interp));

typedef struct SmoothAssocData {
    struct SmoothAssocData *nextPtr;    /* pointer to next SmoothAssocData */
    Tk_SmoothMethod smooth;             /* name and functions associated with this
					 * option */
} SmoothAssocData;

void
Tk_CreateSmoothMethod(interp, smooth)
    Tcl_Interp *interp;
    Tk_SmoothMethod *smooth;
{
    SmoothAssocData *methods, *typePtr2, *prevPtr, *ptr;
    methods = (SmoothAssocData *) Tcl_GetAssocData(interp, "smoothMethod",
	    (Tcl_InterpDeleteProc **) NULL);

    /*
     * If there's already a smooth method with the given name, remove it.
     */

    for (typePtr2 = methods, prevPtr = NULL; typePtr2 != NULL;
	    prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) {
	if (!strcmp(typePtr2->smooth.name, smooth->name)) {
	    if (prevPtr == NULL) {
		methods = typePtr2->nextPtr;
	    } else {
		prevPtr->nextPtr = typePtr2->nextPtr;
	    }
	    ckfree((char *) typePtr2);
	    break;
	}
    }
    ptr = (SmoothAssocData *) ckalloc(sizeof(SmoothAssocData));
    ptr->smooth.name = smooth->name;
    ptr->smooth.coordProc = smooth->coordProc;
    ptr->smooth.postscriptProc = smooth->postscriptProc;
    ptr->nextPtr = methods;
    Tcl_SetAssocData(interp, "smoothMethod", SmoothMethodCleanupProc,
		(ClientData) ptr);
}
/*
 *----------------------------------------------------------------------
 *
 * SmoothMethodCleanupProc --
 *
 *      This procedure is invoked whenever an interpreter is deleted
 *      to cleanup the smooth methods.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Smooth methods are removed.
 *
 *----------------------------------------------------------------------
 */

static void
SmoothMethodCleanupProc(clientData, interp)
    ClientData clientData;      /* Points to "smoothMethod" AssocData
				 * for the interpreter. */
    Tcl_Interp *interp;         /* Interpreter that is being deleted. */
{
    SmoothAssocData *ptr, *methods = (SmoothAssocData *) clientData;

    while (methods != NULL) {
	methods = (ptr = methods)->nextPtr;
	ckfree((char *) ptr);
    }
}
/*
 *--------------------------------------------------------------
 *
 * TkSmoothParseProc --
 *
 *      This procedure is invoked during option processing to handle
 *      the "-smooth" option.
 *
 * Results:
 *      A standard Tcl return value.
 *
 * Side effects:
 *      The smooth option for a given item gets replaced by the value
 *      indicated in the value argument.
 *
 *--------------------------------------------------------------
 */

int
TkSmoothParseProc(clientData, interp, tkwin, ovalue, widgRec, offset)
    ClientData clientData;              /* some flags.*/
    Tcl_Interp *interp;                 /* Used for reporting errors. */
    Tk_Window tkwin;                    /* Window containing canvas widget. */
    Tcl_Obj * ovalue;                         /* Value of option. */
    char *widgRec;                      /* Pointer to record for item. */
    int offset;                         /* Offset into item. */
{
    register Tk_SmoothMethod **smoothPtr =
	(Tk_SmoothMethod **) (widgRec + offset);
    Tk_SmoothMethod *smooth = NULL;
    int b;
    size_t length;
    SmoothAssocData *methods;
    char *value = Tcl_GetString(ovalue);

    if (value == NULL || *value == 0) {
	*smoothPtr = (Tk_SmoothMethod *) NULL;
	return TCL_OK;
    }
    length = strlen(value);
    methods = (SmoothAssocData *) Tcl_GetAssocData(interp, "smoothMethod",
	    (Tcl_InterpDeleteProc **) NULL);
    while (methods != (SmoothAssocData *) NULL) {
	if (strncmp(value, methods->smooth.name, length) == 0) {
	    if (smooth != (Tk_SmoothMethod *) NULL) {
		Tcl_AppendResult(interp, "ambigeous smooth method \"", value,
			"\"", (char *) NULL);
		return TCL_ERROR;
	    }
	    smooth = &methods->smooth;
	}
	methods = methods->nextPtr;
    }
    if (smooth) {
	*smoothPtr = smooth;
	return TCL_OK;
    } else if (strncmp(value, tkBezierSmoothMethod.name, length) == 0) {
	/*
	 * We need to do handle the built-in bezier method.
	 */
	*smoothPtr = &tkBezierSmoothMethod;
	return TCL_OK;
    }


    if (Tcl_GetBooleanFromObj(interp, ovalue, &b) != TCL_OK) {
	return TCL_ERROR;
    }
    *smoothPtr = b ? &tkBezierSmoothMethod : (Tk_SmoothMethod*) NULL;
    return TCL_OK;
}
/*
 *--------------------------------------------------------------
 *
 * TkSmoothPrintProc --
 *
 *      This procedure is invoked by the Tk configuration code
 *      to produce a printable string for the "-smooth"
 *      configuration option.
 *
 * Results:
 *      The return value is a string describing the smooth option for
 *      the item referred to by "widgRec".  In addition, *freeProcPtr
 *      is filled in with the address of a procedure to call to free
 *      the result string when it's no longer needed (or NULL to
 *      indicate that the string doesn't need to be freed).
 *
 * Side effects:
 *      None.
 *
 *--------------------------------------------------------------
 */

Tcl_Obj *
TkSmoothPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
    ClientData clientData;              /* Ignored. */
    Tk_Window tkwin;                    /* Window containing canvas widget. */
    char *widgRec;                      /* Pointer to record for item. */
    int offset;                         /* Offset into item. */
    Tcl_FreeProc **freeProcPtr;         /* Pointer to variable to fill in with
					 * information about how to reclaim
					 * storage for return string. */
{
    register Tk_SmoothMethod **smoothPtr = (Tk_SmoothMethod **) (widgRec + offset);
    Tcl_Obj * result = NULL;
    if (*smoothPtr) {
	LangSetDefault(&result,(*smoothPtr)->name);
    }
    return result;
}
/*
 *--------------------------------------------------------------
 *
 * Tk_GetDash
 *
 *      This procedure is used to parse a string, assuming
 *      it is dash information.
 *
 * Results:
 *      The return value is a standard Tcl result:  TCL_OK means
 *      that the dash information was parsed ok, and
 *      TCL_ERROR means it couldn't be parsed.
 *
 * Side effects:
 *      Dash information in the dash structure is updated.
 *
 *--------------------------------------------------------------
 */

int
Tk_GetDash(interp, ovalue, dash)
    Tcl_Interp *interp;         /* Used for error reporting. */
    Tcl_Obj * ovalue;                 /* Textual specification of dash list. */
    Tk_Dash *dash;              /* Pointer to record in which to
				 * store dash information. */
{
    int argc, i;
    Tcl_Obj **largv = NULL;
    Tcl_Obj **objv = NULL;
    char *pt;
    char *value = Tcl_GetString(ovalue);

    if ((value==(char *) NULL) || (*value==0) ) {
	dash->number = 0;
	return TCL_OK;
    }
    if ((*value == '.') || (*value == ',') ||
	    (*value == '-') || (*value == '_')) {
	i = DashConvert((char *) NULL, value, -1, 0.0);
	if (i>0) {
	    i = strlen(value);
	} else {
	    goto badDashList;
	}
	if (i > (int) sizeof(char *)) {
	    dash->pattern.pt = pt = (char *) ckalloc(strlen(value));
	} else {
	    pt = dash->pattern.array;
	}
	memcpy(pt,value, (unsigned int) i);
	dash->number = -i;
	return TCL_OK;
    }
    if (Tcl_ListObjGetElements(interp, ovalue, &argc, &objv) != TCL_OK) {
	Tcl_ResetResult(interp);
    badDashList:
	Tcl_AppendResult(interp, "bad dash list \"", value,
		"\": must be a list of integers or a format like \"-..\"",
		(char *) NULL);
    syntaxError:
	if (ABS(dash->number) > sizeof(char *))
	    ckfree((char *) dash->pattern.pt);
	dash->number = 0;
	return TCL_ERROR;
    }

    if (ABS(dash->number) > sizeof(char *)) {
	ckfree((char *) dash->pattern.pt);
    }
    if (argc > (int) sizeof(char *)) {
	dash->pattern.pt = pt = (char *) ckalloc((unsigned int) argc);
    } else {
	pt = dash->pattern.array;
    }
    dash->number = argc;

    largv = objv;
    while(argc>0) {
	if (Tcl_GetIntFromObj(interp, *largv, &i) != TCL_OK ||
	    i < 1 || i>255) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, "expected integer in the range 1..255 but got \"",
			 *largv, "\"", (char *) NULL);
	    goto syntaxError;
	}
	*pt++ = i;
	argc--; largv++;
    }

#ifdef FIXME
    if (argv != NULL) {
	ckfree((char *) argv);
    }
#endif

    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_CreateOutline
 *
 *      This procedure initializes the Tk_Outline structure
 *      with default values.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *--------------------------------------------------------------
 */

void Tk_CreateOutline(outline)
    Tk_Outline *outline;
{
    outline->gc = None;
    outline->width = 1.0;
    outline->activeWidth = 0.0;
    outline->disabledWidth = 0.0;
    outline->offset = 0;
    outline->dash.number = 0;
    outline->activeDash.number = 0;
    outline->disabledDash.number = 0;
    outline->tsoffset.flags = 0;
    outline->tsoffset.xoffset = 0;
    outline->tsoffset.yoffset = 0;
    outline->color = NULL;
    outline->activeColor = NULL;
    outline->disabledColor = NULL;
    outline->stipple = None;
    outline->activeStipple = None;
    outline->disabledStipple = None;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_DeleteOutline
 *
 *      This procedure frees all memory that might be
 *      allocated and referenced in the Tk_Outline structure.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *--------------------------------------------------------------
 */

void Tk_DeleteOutline(display, outline)
    Display *display;                   /* Display containing window */
    Tk_Outline *outline;
{
    if (outline->gc != None) {
	Tk_FreeGC(display, outline->gc);
    }
    if (ABS(outline->dash.number) > sizeof(char *)) {
	ckfree((char *) outline->dash.pattern.pt);
    }
    if (ABS(outline->activeDash.number) > sizeof(char *)) {
	ckfree((char *) outline->activeDash.pattern.pt);
    }
    if (ABS(outline->disabledDash.number) > sizeof(char *)) {
	ckfree((char *) outline->disabledDash.pattern.pt);
    }
    if (outline->color != NULL) {
	Tk_FreeColor(outline->color);
    }
    if (outline->activeColor != NULL) {
	Tk_FreeColor(outline->activeColor);
    }
    if (outline->disabledColor != NULL) {
	Tk_FreeColor(outline->disabledColor);
    }
    if (outline->stipple != None) {
	Tk_FreeBitmap(display, outline->stipple);
    }
    if (outline->activeStipple != None) {
	Tk_FreeBitmap(display, outline->activeStipple);
    }
    if (outline->disabledStipple != None) {
	Tk_FreeBitmap(display, outline->disabledStipple);
    }
}

/*
 *--------------------------------------------------------------
 *
 * Tk_ConfigOutlineGC
 *
 *      This procedure should be called in the canvas object
 *      during the configure command. The graphics context
 *      description in gcValues is updated according to the
 *      information in the dash structure, as far as possible.
 *
 * Results:
 *      The return-value is a mask, indicating which
 *      elements of gcValues have been updated.
 *      0 means there is no outline.
 *
 * Side effects:
 *      GC information in gcValues is updated.
 *
 *--------------------------------------------------------------
 */

int Tk_ConfigOutlineGC(gcValues, canvas, item, outline)
    XGCValues *gcValues;
    Tk_Canvas canvas;
    Tk_Item *item;
    Tk_Outline *outline;
{
    int mask = 0;
    double width;
    Tk_Dash *dash;
    XColor *color;
    Pixmap stipple;
    Tk_State state = item->state;

    if (outline->width < 0.0) {
	outline->width = 0.0;
    }
    if (outline->activeWidth < 0.0) {
	outline->activeWidth = 0.0;
    }
    if (outline->disabledWidth < 0) {
	outline->disabledWidth = 0.0;
    }
    if (state==TK_STATE_HIDDEN) {
	return 0;
    }

    width = outline->width;
    dash = &(outline->dash);
    color = outline->color;
    stipple = outline->stipple;
    if (state == TK_STATE_NULL) {
	state = ((TkCanvas *)canvas)->canvas_state;
    }
    if (((TkCanvas *)canvas)->currentItemPtr == item) {
	if (outline->activeWidth>width) {
	    width = outline->activeWidth;
	}
	if (outline->activeDash.number != 0) {
	    dash = &(outline->activeDash);
	}
	if (outline->activeColor!=NULL) {
	    color = outline->activeColor;
	}
	if (outline->activeStipple!=None) {
	    stipple = outline->activeStipple;
	}
    } else if (state==TK_STATE_DISABLED) {
	if (outline->disabledWidth>0) {
	    width = outline->disabledWidth;
	}
	if (outline->disabledDash.number != 0) {
	    dash = &(outline->disabledDash);
	}
	if (outline->disabledColor!=NULL) {
	    color = outline->disabledColor;
	}
	if (outline->disabledStipple!=None) {
	    stipple = outline->disabledStipple;
	}
    }

    if (color==NULL) {
	return 0;
    }

    if (width < 1.0) {
	width = 1.0;
    }

    gcValues->line_width = (int) (width + 0.5);
    if (color != NULL) {
	gcValues->foreground = color->pixel;
	mask = GCForeground|GCLineWidth;
	if (stipple != None) {
	    gcValues->stipple = stipple;
	    gcValues->fill_style = FillStippled;
	    mask |= GCStipple|GCFillStyle;
	}
    }
    if (mask && (dash->number != 0)) {
	gcValues->line_style = LineOnOffDash;
	gcValues->dash_offset = outline->offset;
	if (dash->number >= 2) {
	    gcValues->dashes = 4;
	} else if (dash->number > 0) {
	    gcValues->dashes = dash->pattern.array[0];
	} else {
	    gcValues->dashes = (char) (4 * width);
	}
	mask |= GCLineStyle|GCDashList|GCDashOffset;
    }
    return mask;
}

/*
 *--------------------------------------------------------------
 *
 * Tk_ChangeOutlineGC
 *
 *      Updates the GC to represent the full information of
 *      the dash structure. Partly this is already done in
 *      Tk_ConfigOutlineGC().
 *      This function should be called just before drawing
 *      the dashed item.
 *
 * Results:
 *      1 if there is a stipple pattern.
 *      0 otherwise.
 *
 * Side effects:
 *      GC is updated.
 *
 *--------------------------------------------------------------
 */

int
Tk_ChangeOutlineGC(canvas, item, outline)
    Tk_Canvas canvas;
    Tk_Item *item;
    Tk_Outline *outline;
{
    CONST char *p;
    double width;
    Tk_Dash *dash;
    XColor *color;
    Pixmap stipple;
    Tk_State state = item->state;
    XGCValues values;
    Tk_Tile tile = outline->tile;

    width = outline->width;
    if (width < 1.0) {
	width = 1.0;
    }
    dash = &(outline->dash);
    color = outline->color;
    stipple = outline->stipple;
    if (state == TK_STATE_NULL) {
	state = ((TkCanvas *)canvas)->canvas_state;
    }
    if (((TkCanvas *)canvas)->currentItemPtr == item) {
	if (outline->activeWidth > width) {
	    width = outline->activeWidth;
	}
	if (outline->activeDash.number != 0) {
	    dash = &(outline->activeDash);
	}
	if (outline->activeColor != NULL) {
	    color = outline->activeColor;
	}
	if (outline->activeStipple != None) {
	    stipple = outline->activeStipple;
	}
    } else if (state == TK_STATE_DISABLED) {
	if (outline->disabledWidth > width) {
	    width = outline->disabledWidth;
	}
	if (outline->disabledDash.number != 0) {
	    dash = &(outline->disabledDash);
	}
	if (outline->disabledColor != NULL) {
	    color = outline->disabledColor;
	}
	if (outline->disabledStipple != None) {
	    stipple = outline->disabledStipple;
	}
    }
    if (color==NULL) {
	return 0;
    }

    if ((dash->number<-1) || ((dash->number == -1) && (dash->pattern.array[1]!=','))) {
	char *q;
	int i = -dash->number;

	p = (i > (int) sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
	q = (char *) ckalloc(2*(unsigned int)i);
	i = DashConvert(q, p, i, width);
	XSetDashes(((TkCanvas *)canvas)->display, outline->gc, outline->offset, q, i);
	values.line_style = LineOnOffDash;
	ckfree(q);
    } else if ( dash->number>2 || (dash->number==2 &&
		(dash->pattern.array[0]!=dash->pattern.array[1]))) {
	p = (char *) (dash->number > (int) sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
	XSetDashes(((TkCanvas *)canvas)->display, outline->gc, outline->offset, p, dash->number);
	values.line_style = LineOnOffDash;
    } else {
	values.line_style = LineSolid;
    }
    XChangeGC(((TkCanvas *)canvas)->display, outline->gc, GCLineStyle, &values);
    if ((tile!= NULL) || (stipple!=None)) {
	int w=0; int h=0;
	Tk_TSOffset *tsoffset = &outline->tsoffset;
	int flags = tsoffset->flags;
	if (!(flags & TK_OFFSET_INDEX) && (flags & (TK_OFFSET_CENTER|TK_OFFSET_MIDDLE))) {
	    Tk_SizeOfBitmap(((TkCanvas *)canvas)->display, stipple, &w, &h);
	    if (flags & TK_OFFSET_CENTER) {
		w /= 2;
	    } else {
		w = 0;
	    }
	    if (flags & TK_OFFSET_MIDDLE) {
		h /= 2;
	    } else {
		h = 0;
	    }
	}
	tsoffset->xoffset -= w;
	tsoffset->yoffset -= h;
	Tk_CanvasSetOffset(canvas, outline->gc, tsoffset);
	tsoffset->xoffset += w;
	tsoffset->yoffset += h;
	return 1;
    }
    return 0;
}


/*
 *--------------------------------------------------------------
 *
 * Tk_ResetOutlineGC
 *
 *      Restores the GC to the situation before
 *      Tk_ChangeDashGC() was called.
 *      This function should be called just after the dashed
 *      item is drawn, because the GC is supposed to be
 *      read-only.
 *
 * Results:
 *      1 if there is a stipple pattern.
 *      0 otherwise.
 *
 * Side effects:
 *      GC is updated.
 *
 *--------------------------------------------------------------
 */
int
Tk_ResetOutlineGC(canvas, item, outline)
    Tk_Canvas canvas;
    Tk_Item *item;
    Tk_Outline *outline;
{
    char dashList;
    double width;
    Tk_Dash *dash;
    XColor *color;
    Pixmap stipple;
    Tk_State state = item->state;

    width = outline->width;
    if (width < 1.0) {
	width = 1.0;
    }
    dash = &(outline->dash);
    color = outline->color;
    stipple = outline->stipple;
    if (state == TK_STATE_NULL) {
	state = ((TkCanvas *)canvas)->canvas_state;
    }
    if (((TkCanvas *)canvas)->currentItemPtr == item) {
	if (outline->activeWidth>width) {
	    width = outline->activeWidth;
	}
	if (outline->activeDash.number != 0) {
	    dash = &(outline->activeDash);
	}
	if (outline->activeColor!=NULL) {
	    color = outline->activeColor;
	}
	if (outline->activeStipple!=None) {
	    stipple = outline->activeStipple;
	}
    } else if (state==TK_STATE_DISABLED) {
	if (outline->disabledWidth>width) {
	    width = outline->disabledWidth;
	}
	if (outline->disabledDash.number != 0) {
	    dash = &(outline->disabledDash);
	}
	if (outline->disabledColor!=NULL) {
	    color = outline->disabledColor;
	}
	if (outline->disabledStipple!=None) {
	    stipple = outline->disabledStipple;
	}
    }
    if (color==NULL) {
	return 0;
    }

    if ((dash->number > 2) || (dash->number < -1) || (dash->number==2 &&
		(dash->pattern.array[0] != dash->pattern.array[1])) ||
		((dash->number == -1) && (dash->pattern.array[1] != ','))) {
	if (dash->number < 0) {
	    dashList = (int) (4 * width + 0.5);
	} else if (dash->number<3) {
	    dashList = dash->pattern.array[0];
	} else {
	    dashList = 4;
	}
	XSetDashes(((TkCanvas *)canvas)->display, outline->gc, outline->offset, &dashList , 1);
    } else {
	XGCValues values;
	values.line_style = LineSolid;
	XChangeGC(((TkCanvas *)canvas)->display, outline->gc, GCLineStyle, &values);
    }
    if (stipple != None) {
	XSetTSOrigin(((TkCanvas *)canvas)->display, outline->gc, 0, 0);
	return 1;
    }
    return 0;
}


/*
 *--------------------------------------------------------------
 *
 * Tk_CanvasPsOutline
 *
 *      Creates the postscript command for the correct
 *      Outline-information (width, dash, color and stipple).
 *
 * Results:
 *      TCL_OK if succeeded, otherwise TCL_ERROR.
 *
 * Side effects:
 *      canvas->interp->result contains the postscript string,
 *      or an error message if the result was TCL_ERROR.
 *
 *--------------------------------------------------------------
 */
int
Tk_CanvasPsOutline(canvas, item, outline)
    Tk_Canvas canvas;
    Tk_Item *item;
    Tk_Outline *outline;
{
    char string[41];
    char pattern[11];
    int i;
    char *ptr;
    char *str = string;
    char *lptr = pattern;
    Tcl_Interp *interp = ((TkCanvas *)canvas)->interp;
    double width;
    Tk_Dash *dash;
    XColor *color;
    Pixmap stipple;
    Tk_State state = item->state;

    width = outline->width;
    dash = &(outline->dash);
    color = outline->color;
    stipple = outline->stipple;
    if (state == TK_STATE_NULL) {
	state = ((TkCanvas *)canvas)->canvas_state;
    }
    if (((TkCanvas *)canvas)->currentItemPtr == item) {
	if (outline->activeWidth > width) {
	    width = outline->activeWidth;
	}
	if (outline->activeDash.number > 0) {
	    dash = &(outline->activeDash);
	}
	if (outline->activeColor != NULL) {
	    color = outline->activeColor;
	}
	if (outline->activeStipple != None) {
	    stipple = outline->activeStipple;
	}
    } else if (state == TK_STATE_DISABLED) {
	if (outline->disabledWidth > 0) {
	    width = outline->disabledWidth;
	}
	if (outline->disabledDash.number > 0) {
	    dash = &(outline->disabledDash);
	}
	if (outline->disabledColor != NULL) {
	    color = outline->disabledColor;
	}
	if (outline->disabledStipple != None) {
	    stipple = outline->disabledStipple;
	}
    }
    sprintf(string, "%.15g setlinewidth\n", width);
    Tcl_AppendResult(interp, string, (char *) NULL);

    if (dash->number > 10) {
	str = (char *)ckalloc((unsigned int) (1 + 4*dash->number));
    } else if (dash->number < -5) {
	str = (char *)ckalloc((unsigned int) (1 - 8*dash->number));
	lptr = (char *)ckalloc((unsigned int) (1 - 2*dash->number));
    }
    ptr = (char *) ((ABS(dash->number) > sizeof(char *)) ) ?
	dash->pattern.pt : dash->pattern.array;
    if (dash->number > 0) {
	char *ptr0 = ptr;
	sprintf(str, "[%d", *ptr++ & 0xff);
	i = dash->number-1;
	while (i--) {
	    sprintf(str+strlen(str), " %d", *ptr++ & 0xff);
	}
	Tcl_AppendResult(interp, str, (char *)NULL);
	if (dash->number&1) {
	    Tcl_AppendResult(interp, " ", str+1, (char *)NULL);
	}
	sprintf(str, "] %d setdash\n", outline->offset);
	Tcl_AppendResult(interp, str, (char *)NULL);
	ptr = ptr0;
    } else if (dash->number < 0) {
	if ((i = DashConvert(lptr, ptr, -dash->number, width)) != 0) {
	    char *lptr0 = lptr;
	    sprintf(str, "[%d", *lptr++ & 0xff);
	    while (--i) {
		sprintf(str+strlen(str), " %d", *lptr++ & 0xff);
	    }
	    Tcl_AppendResult(interp, str, (char *)NULL);
	    sprintf(str, "] %d setdash\n", outline->offset);
	    Tcl_AppendResult(interp, str, (char *)NULL);
	    lptr = lptr0;
	} else {
	    Tcl_AppendResult(interp, "[] 0 setdash\n", (char *)NULL);
	}
    } else {
	Tcl_AppendResult(interp, "[] 0 setdash\n", (char *)NULL);
    }
    if (str != string) {
	ckfree(str);
    }
    if (lptr != pattern) {
	ckfree(lptr);
    }
    if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) {
	return TCL_ERROR;
    }
    if (stipple != None) {
	Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL);
	if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) {
	    return TCL_ERROR;
	}
    } else {
	Tcl_AppendResult(interp, "stroke\n", (char *) NULL);
    }

    return TCL_OK;
}


/*
 *--------------------------------------------------------------
 *
 * DashConvert
 *
 *      Converts a character-like dash-list (e.g. "-..")
 *      into an X11-style. l must point to a string that
 *      holds room to at least 2*n characters. if
 *      l == NULL, this function can be used for
 *      syntax checking only.
 *
 * Results:
 *      The length of the resulting X11 compatible
 *      dash-list. -1 if failed.
 *
 * Side effects:
 *      None
 *
 *--------------------------------------------------------------
 */

static int
DashConvert (l, p, n, width)
    char *l;
    CONST char *p;
    int n;
    double width;
{
    int result = 0;
    int size, intWidth;

    if (n<0) {
	n = strlen(p);
    }
    intWidth = (int) (width + 0.5);
    if (intWidth < 1) {
	intWidth = 1;
    }
    while (n-- && *p) {
	switch (*p++) {
	    case ' ':
		if (result) {
		    if (l) {
			l[-1] += intWidth + 1;
		    }
		    continue;
		} else {
		    return 0;
		}
		break;
	    case '_':
		size = 8;
		break;
	    case '-':
		size = 6;
		break;
	    case ',':
		size = 4;
		break;
	    case '.':
		size = 2;
		break;
	    default:
		return -1;
	}
	if (l) {
	    *l++ = size * intWidth;
	    *l++ = 4 * intWidth;
	}
	result += 2;
    }
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * translateAndAppendCoords --
 *
 *      This is a helper routine for TkCanvTranslatePath() below.
 *
 *      Given an (x,y) coordinate pair within a canvas, this procedure
 *      computes the corresponding coordinates at which the point should
 *      be drawn in the drawable used for display.  Those coordinates are
 *      then written into outArr[numOut*2] and outArr[numOut*2+1].
 *
 * Results:
 *      There is no return value.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
translateAndAppendCoords(canvPtr, x, y, outArr, numOut)
    TkCanvas *canvPtr;                  /* The canvas. */
    double x, y;                        /* Coordinates in canvas space. */
    XPoint *outArr;                     /* Write results into this array */
    int numOut;                         /* Num of prior entries in outArr[] */
{
    double tmp;

    tmp = x - canvPtr->drawableXOrigin;
    if (tmp > 0) {
	tmp += 0.5;
    } else {
	tmp -= 0.5;
    }
    outArr[numOut].x = (short) tmp;

    tmp = y  - canvPtr->drawableYOrigin;
    if (tmp > 0) {
	tmp += 0.5;
    } else {
	tmp -= 0.5;
    }
    outArr[numOut].y = (short) tmp;
}

/*
 *--------------------------------------------------------------
 *
 * TkCanvTranslatePath
 *
 *      Translate a line or polygon path so that all vertices are
 *      within a rectangle that is 1000 pixels larger than the total
 *      size of the canvas window.  This will prevent pixel coordinates
 *      from overflowing the 16-bit integer size limitation imposed by
 *      most windowing systems.
 *
 *      coordPtr must point to an array of doubles, two doubles per
 *      vertex.  There are a total of numVertex vertices, or 2*numVertex
 *      entries in coordPtr.  The result vertices written into outArr
 *      have their coordinate origin shifted to canvPtr->drawableXOrigin
 *      by canvPtr->drawableYOrigin.  There might be as many as 3 times
 *      more output vertices than there are input vertices.  The calling
 *      function should allocate space accordingly.
 *
 *      This routine limits the width and height of a canvas window
 *      to 31767 pixels.  At the highest resolution display devices
 *      available today (210 ppi in Jan 2003) that's a window that is
 *      over 13 feet wide and tall.  Should be enough for the near
 *      future.
 *
 * Results:
 *      Clipped and translated path vertices are written into outArr[].
 *      There might be as many as twice the vertices in outArr[] as there
 *      are in coordPtr[].  The return value is the number of vertices
 *      actually written into outArr[].
 *
 * Side effects:
 *      None
 *
 *--------------------------------------------------------------
 */
int
TkCanvTranslatePath (canvPtr, numVertex, coordArr, closedPath, outArr)
    TkCanvas *canvPtr;  /* The canvas */
    int numVertex;      /* Number of vertices specified by coordArr[] */
    double *coordArr;   /* X and Y coordinates for each vertex */
    int closedPath;     /* True if this is a closed polygon */
    XPoint *outArr;     /* Write results here, if not NULL */
{
    int numOutput = 0;  /* Number of output coordinates */
    double lft, rgh;    /* Left and right sides of the bounding box */
    double top, btm;    /* Top and bottom sizes of the bounding box */
    double *tempArr;    /* Temporary storage used by the clipper */
    double *a, *b, *t;  /* Pointers to parts of the temporary storage */
    int i, j;           /* Loop counters */
    int maxOutput;      /* Maximum number of outputs that we will allow */
    double limit[4];          /* Boundries at which clipping occurs */
    double staticSpace[480];  /* Temp space from the stack */

    /*
    ** Constrain all vertices of the path to be within a box that is no
    ** larger than 32000 pixels wide or height.  The top-left corner of
    ** this clipping box is 1000 pixels above and to the left of the top
    ** left corner of the window on which the canvas is displayed.
    **
    ** This means that a canvas will not display properly on a canvas
    ** window that is larger than 31000 pixels wide or high.  That is not
    ** a problem today, but might someday become a factor for ultra-high
    ** resolutions displays.
    **
    ** The X11 protocol allows us (in theory) to expand the size of the
    ** clipping box to 32767 pixels.  But we have found experimentally that
    ** XFree86 sometimes fails to draw lines correctly if they are longer
    ** than about 32500 pixels.  So we have left a little margin in the
    ** size to mask that bug.
    */
    lft = canvPtr->xOrigin - 1000.0;
    top = canvPtr->yOrigin - 1000.0;
    rgh = lft + 32000.0;
    btm = top + 32000.0;

    /* Try the common case first - no clipping.  Loop over the input
    ** coordinates and translate them into appropriate output coordinates.
    ** But if a vertex outside of the bounding box is seen, break out of
    ** the loop.
    **
    ** Most of the time, no clipping is needed, so this one loop is
    ** sufficient to do the translation.
    */
    for(i=0; i<numVertex; i++){
	double x, y;
	x = coordArr[i*2];
	y = coordArr[i*2+1];
	if( x<lft || x>rgh || y<top || y>btm ) break;
	translateAndAppendCoords(canvPtr, x, y, outArr, numOutput++);
    }
    if( i==numVertex ){
	assert( numOutput==numVertex );
	return numOutput;
    }

    /* If we reach this point, it means that some clipping is required.
    ** Begin by allocating some working storage - at least 6 times as much space
    ** as coordArr[] requires.  Divide this space into two separate arrays
    ** a[] and b[].  Initialize a[] to be equal to coordArr[].
    */
    if( numVertex*12 <= (int) (sizeof(staticSpace)/sizeof(staticSpace[0])) ){
	tempArr = staticSpace;
    } else {
	tempArr = (double*)ckalloc( numVertex*12*sizeof(tempArr[0]) );
    }
    for(i=0; i<numVertex*2; i++){
	tempArr[i] = coordArr[i];
    }
    a = tempArr;
    b = &tempArr[numVertex*6];

    /* We will make four passes through the input data.  On each pass,
    ** we copy the contents of a[] over into b[].  As we copy, we clip
    ** any line segments that extend to the right past xClip then we
    ** rotate the coordinate system 90 degrees clockwise.  After each
    ** pass is complete, we interchange a[] and b[] in preparation for
    ** the next pass.
    **
    ** Each pass clips line segments that extend beyond a single side
    ** of the bounding box, and four passes rotate the coordinate system
    ** back to its original value.  I'm not an expert on graphics
    ** algorithms, but I think this is called Cohen-Sutherland polygon
    ** clipping.
    **
    ** The limit[] array contains the xClip value used for each of the
    ** four passes.
    */
    limit[0] = rgh;
    limit[1] = -top;
    limit[2] = -lft;
    limit[3] = btm;

    /* This is the loop that makes the four passes through the data.
    */
    maxOutput = numVertex*3;
    for(j=0; j<4; j++){
	double xClip = limit[j];
	int inside = a[0]<xClip;
	double priorY = a[1];
	numOutput = 0;

	/* Clip everything to the right of xClip.  Store the results in
	** b[] rotated by 90 degrees clockwise.
	*/
	for(i=0; i<numVertex; i++){
	    double x = a[i*2];
	    double y = a[i*2+1];
	    if( x>=xClip ){
		/* The current vertex is to the right of xClip.
		*/
		if( inside ){
		    /* If the current vertex is to the right of xClip but
		    ** the previous vertex was left of xClip, then draw a
		    ** line segment from the previous vertex to until it
		    ** intersects the vertical at xClip.
		    */
		    double x0, y0, yN;
		    assert( i>0 );
		    x0 = a[i*2-2];
		    y0 = a[i*2-1];
		    yN = y0 + (y - y0)*(xClip-x0)/(x-x0);
		    b[numOutput*2] = -yN;
		    b[numOutput*2+1] = xClip;
		    numOutput++;
		    assert( numOutput<=maxOutput );
		    priorY = yN;
		    inside = 0;
		}else if( i==0 ){
		    /* If the first vertex is to the right of xClip, add
		    ** a vertex that is the projection of the first vertex
		    ** onto the vertical xClip line.
		    */
		    b[0] = -y;
		    b[1] = xClip;
		    numOutput = 1;
		    priorY = y;
		}
	    }else{
		/* The current vertex is to the left of xClip
		*/
		if( !inside ){
		    /* If the current vertex is on the left of xClip and
		    ** one or more prior vertices where to the right, then
		    ** we have to draw a line segment along xClip that extends
		    ** from the spot where we first crossed from left to right
		    ** to the spot where we cross back from right to left.
		    */
		    double x0, y0, yN;
		    assert( i>0 );
		    x0 = a[i*2-2];
		    y0 = a[i*2-1];
		    yN = y0 + (y - y0)*(xClip-x0)/(x-x0);
		    if( yN!=priorY ){
			b[numOutput*2] = -yN;
			b[numOutput*2+1] = xClip;
			numOutput++;
			assert( numOutput<=maxOutput );
		    }
		    inside = 1;
		}
		b[numOutput*2] = -y;
		b[numOutput*2+1] = x;
		numOutput++;
		assert( numOutput<=maxOutput );
	    }
	}

	/* Interchange a[] and b[] in preparation for the next pass.
	*/
	t = a;
	a = b;
	b = t;
	numVertex = numOutput;
    }

    /* All clipping is now finished.  Convert the coordinates from doubles
    ** into XPoints and translate the origin for the drawable.
    */
    for(i=0; i<numVertex; i++){
	translateAndAppendCoords(canvPtr, a[i*2], a[i*2+1], outArr, i);
    }
    if( tempArr!=staticSpace ){
	ckfree((char *) tempArr);
    }
    return numOutput;
}

