Parent Directory | Revision Log | Patch
revision 44 by dashley, Fri Oct 14 02:09:58 2016 UTC | revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC | |
---|---|---|
# | Line 1 | Line 1 |
/* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkcanvline.c,v 1.1.1.1 2001/06/13 04:56:46 dtashley Exp $ */ | ||
/* | ||
* tkCanvLine.c -- | ||
* | ||
* This file implements line items for canvas widgets. | ||
* | ||
* Copyright (c) 1991-1994 The Regents of the University of California. | ||
* Copyright (c) 1994-1997 Sun Microsystems, Inc. | ||
* Copyright (c) 1998-1999 by Scriptics Corporation. | ||
* | ||
* See the file "license.terms" for information on usage and redistribution | ||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. | ||
* | ||
* RCS: @(#) $Id: tkcanvline.c,v 1.1.1.1 2001/06/13 04:56:46 dtashley Exp $ | ||
*/ | ||
#include <stdio.h> | ||
#include "tkInt.h" | ||
#include "tkPort.h" | ||
#include "tkCanvas.h" | ||
/* | ||
* The structure below defines the record for each line item. | ||
*/ | ||
typedef enum { | ||
ARROWS_NONE, ARROWS_FIRST, ARROWS_LAST, ARROWS_BOTH | ||
} Arrows; | ||
typedef struct LineItem { | ||
Tk_Item header; /* Generic stuff that's the same for all | ||
* types. MUST BE FIRST IN STRUCTURE. */ | ||
Tk_Outline outline; /* Outline structure */ | ||
Tk_Canvas canvas; /* Canvas containing item. Needed for | ||
* parsing arrow shapes. */ | ||
int numPoints; /* Number of points in line (always >= 0). */ | ||
double *coordPtr; /* Pointer to malloc-ed array containing | ||
* x- and y-coords of all points in line. | ||
* X-coords are even-valued indices, y-coords | ||
* are corresponding odd-valued indices. If | ||
* the line has arrowheads then the first | ||
* and last points have been adjusted to refer | ||
* to the necks of the arrowheads rather than | ||
* their tips. The actual endpoints are | ||
* stored in the *firstArrowPtr and | ||
* *lastArrowPtr, if they exist. */ | ||
int capStyle; /* Cap style for line. */ | ||
int joinStyle; /* Join style for line. */ | ||
GC arrowGC; /* Graphics context for drawing arrowheads. */ | ||
Arrows arrow; /* Indicates whether or not to draw arrowheads: | ||
* "none", "first", "last", or "both". */ | ||
float arrowShapeA; /* Distance from tip of arrowhead to center. */ | ||
float arrowShapeB; /* Distance from tip of arrowhead to trailing | ||
* point, measured along shaft. */ | ||
float arrowShapeC; /* Distance of trailing points from outside | ||
* edge of shaft. */ | ||
double *firstArrowPtr; /* Points to array of PTS_IN_ARROW points | ||
* describing polygon for arrowhead at first | ||
* point in line. First point of arrowhead | ||
* is tip. Malloc'ed. NULL means no arrowhead | ||
* at first point. */ | ||
double *lastArrowPtr; /* Points to polygon for arrowhead at last | ||
* point in line (PTS_IN_ARROW points, first | ||
* of which is tip). Malloc'ed. NULL means | ||
* no arrowhead at last point. */ | ||
Tk_SmoothMethod *smooth; /* Non-zero means draw line smoothed (i.e. | ||
* with Bezier splines). */ | ||
int splineSteps; /* Number of steps in each spline segment. */ | ||
} LineItem; | ||
/* | ||
* Number of points in an arrowHead: | ||
*/ | ||
#define PTS_IN_ARROW 6 | ||
/* | ||
* Prototypes for procedures defined in this file: | ||
*/ | ||
static int ArrowheadPostscript _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tk_Canvas canvas, LineItem *linePtr, | ||
double *arrowPtr)); | ||
static void ComputeLineBbox _ANSI_ARGS_((Tk_Canvas canvas, | ||
LineItem *linePtr)); | ||
static int ConfigureLine _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tk_Canvas canvas, Tk_Item *itemPtr, int argc, | ||
Tcl_Obj *CONST argv[], int flags)); | ||
static int ConfigureArrows _ANSI_ARGS_((Tk_Canvas canvas, | ||
LineItem *linePtr)); | ||
static int CreateLine _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tk_Canvas canvas, struct Tk_Item *itemPtr, | ||
int argc, Tcl_Obj *CONST argv[])); | ||
static void DeleteLine _ANSI_ARGS_((Tk_Canvas canvas, | ||
Tk_Item *itemPtr, Display *display)); | ||
static void DisplayLine _ANSI_ARGS_((Tk_Canvas canvas, | ||
Tk_Item *itemPtr, Display *display, Drawable dst, | ||
int x, int y, int width, int height)); | ||
static int GetLineIndex _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tk_Canvas canvas, Tk_Item *itemPtr, | ||
Tcl_Obj *obj, int *indexPtr)); | ||
static int LineCoords _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tk_Canvas canvas, Tk_Item *itemPtr, | ||
int argc, Tcl_Obj *CONST argv[])); | ||
static void LineDeleteCoords _ANSI_ARGS_((Tk_Canvas canvas, | ||
Tk_Item *itemPtr, int first, int last)); | ||
static void LineInsert _ANSI_ARGS_((Tk_Canvas canvas, | ||
Tk_Item *itemPtr, int beforeThis, Tcl_Obj *obj)); | ||
static int LineToArea _ANSI_ARGS_((Tk_Canvas canvas, | ||
Tk_Item *itemPtr, double *rectPtr)); | ||
static double LineToPoint _ANSI_ARGS_((Tk_Canvas canvas, | ||
Tk_Item *itemPtr, double *coordPtr)); | ||
static int LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp, | ||
Tk_Canvas canvas, Tk_Item *itemPtr, int prepass)); | ||
static int ArrowParseProc _ANSI_ARGS_((ClientData clientData, | ||
Tcl_Interp *interp, Tk_Window tkwin, | ||
CONST char *value, char *recordPtr, int offset)); | ||
static char * ArrowPrintProc _ANSI_ARGS_((ClientData clientData, | ||
Tk_Window tkwin, char *recordPtr, int offset, | ||
Tcl_FreeProc **freeProcPtr)); | ||
static int ParseArrowShape _ANSI_ARGS_((ClientData clientData, | ||
Tcl_Interp *interp, Tk_Window tkwin, | ||
CONST char *value, char *recordPtr, int offset)); | ||
static char * PrintArrowShape _ANSI_ARGS_((ClientData clientData, | ||
Tk_Window tkwin, char *recordPtr, int offset, | ||
Tcl_FreeProc **freeProcPtr)); | ||
static void ScaleLine _ANSI_ARGS_((Tk_Canvas canvas, | ||
Tk_Item *itemPtr, double originX, double originY, | ||
double scaleX, double scaleY)); | ||
static void TranslateLine _ANSI_ARGS_((Tk_Canvas canvas, | ||
Tk_Item *itemPtr, double deltaX, double deltaY)); | ||
/* | ||
* Information used for parsing configuration specs. If you change any | ||
* of the default strings, be sure to change the corresponding default | ||
* values in CreateLine. | ||
*/ | ||
static Tk_CustomOption arrowShapeOption = { | ||
(Tk_OptionParseProc *) ParseArrowShape, | ||
PrintArrowShape, (ClientData) NULL | ||
}; | ||
static Tk_CustomOption arrowOption = { | ||
(Tk_OptionParseProc *) ArrowParseProc, | ||
ArrowPrintProc, (ClientData) NULL | ||
}; | ||
static Tk_CustomOption smoothOption = { | ||
(Tk_OptionParseProc *) TkSmoothParseProc, | ||
TkSmoothPrintProc, (ClientData) NULL | ||
}; | ||
static Tk_CustomOption stateOption = { | ||
(Tk_OptionParseProc *) TkStateParseProc, | ||
TkStatePrintProc, (ClientData) 2 | ||
}; | ||
static Tk_CustomOption tagsOption = { | ||
(Tk_OptionParseProc *) Tk_CanvasTagsParseProc, | ||
Tk_CanvasTagsPrintProc, (ClientData) NULL | ||
}; | ||
static Tk_CustomOption dashOption = { | ||
(Tk_OptionParseProc *) TkCanvasDashParseProc, | ||
TkCanvasDashPrintProc, (ClientData) NULL | ||
}; | ||
static Tk_CustomOption offsetOption = { | ||
(Tk_OptionParseProc *) TkOffsetParseProc, | ||
TkOffsetPrintProc, | ||
(ClientData) (TK_OFFSET_RELATIVE|TK_OFFSET_INDEX) | ||
}; | ||
static Tk_CustomOption pixelOption = { | ||
(Tk_OptionParseProc *) TkPixelParseProc, | ||
TkPixelPrintProc, (ClientData) NULL | ||
}; | ||
static Tk_ConfigSpec configSpecs[] = { | ||
{TK_CONFIG_CUSTOM, "-activedash", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(LineItem, outline.activeDash), | ||
TK_CONFIG_NULL_OK, &dashOption}, | ||
{TK_CONFIG_COLOR, "-activefill", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(LineItem, outline.activeColor), | ||
TK_CONFIG_NULL_OK}, | ||
{TK_CONFIG_BITMAP, "-activestipple", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(LineItem, outline.activeStipple), | ||
TK_CONFIG_NULL_OK}, | ||
{TK_CONFIG_CUSTOM, "-activewidth", (char *) NULL, (char *) NULL, | ||
"0.0", Tk_Offset(LineItem, outline.activeWidth), | ||
TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, | ||
{TK_CONFIG_CUSTOM, "-arrow", (char *) NULL, (char *) NULL, | ||
"none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT, &arrowOption}, | ||
{TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL, | ||
"8 10 3", Tk_Offset(LineItem, arrowShapeA), | ||
TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption}, | ||
{TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL, | ||
"butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT}, | ||
{TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL, | ||
"black", Tk_Offset(LineItem, outline.color), TK_CONFIG_NULL_OK}, | ||
{TK_CONFIG_CUSTOM, "-dash", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(LineItem, outline.dash), | ||
TK_CONFIG_NULL_OK, &dashOption}, | ||
{TK_CONFIG_PIXELS, "-dashoffset", (char *) NULL, (char *) NULL, | ||
"0", Tk_Offset(LineItem, outline.offset), | ||
TK_CONFIG_DONT_SET_DEFAULT}, | ||
{TK_CONFIG_CUSTOM, "-disableddash", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(LineItem, outline.disabledDash), | ||
TK_CONFIG_NULL_OK, &dashOption}, | ||
{TK_CONFIG_COLOR, "-disabledfill", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(LineItem, outline.disabledColor), | ||
TK_CONFIG_NULL_OK}, | ||
{TK_CONFIG_BITMAP, "-disabledstipple", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(LineItem, outline.disabledStipple), | ||
TK_CONFIG_NULL_OK}, | ||
{TK_CONFIG_CUSTOM, "-disabledwidth", (char *) NULL, (char *) NULL, | ||
"0.0", Tk_Offset(LineItem, outline.disabledWidth), | ||
TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, | ||
{TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL, | ||
"round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT}, | ||
{TK_CONFIG_CUSTOM, "-offset", (char *) NULL, (char *) NULL, | ||
"0,0", Tk_Offset(LineItem, outline.tsoffset), | ||
TK_CONFIG_DONT_SET_DEFAULT, &offsetOption}, | ||
{TK_CONFIG_CUSTOM, "-smooth", (char *) NULL, (char *) NULL, | ||
"0", Tk_Offset(LineItem, smooth), | ||
TK_CONFIG_DONT_SET_DEFAULT, &smoothOption}, | ||
{TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL, | ||
"12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT}, | ||
{TK_CONFIG_CUSTOM, "-state", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK, | ||
&stateOption}, | ||
{TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL, | ||
(char *) NULL, Tk_Offset(LineItem, outline.stipple), | ||
TK_CONFIG_NULL_OK}, | ||
{TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL, | ||
(char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption}, | ||
{TK_CONFIG_CUSTOM, "-width", (char *) NULL, (char *) NULL, | ||
"1.0", Tk_Offset(LineItem, outline.width), | ||
TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, | ||
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | ||
(char *) NULL, 0, 0} | ||
}; | ||
/* | ||
* The structures below defines the line item type by means | ||
* of procedures that can be invoked by generic item code. | ||
*/ | ||
Tk_ItemType tkLineType = { | ||
"line", /* name */ | ||
sizeof(LineItem), /* itemSize */ | ||
CreateLine, /* createProc */ | ||
configSpecs, /* configSpecs */ | ||
ConfigureLine, /* configureProc */ | ||
LineCoords, /* coordProc */ | ||
DeleteLine, /* deleteProc */ | ||
DisplayLine, /* displayProc */ | ||
TK_CONFIG_OBJS, /* flags */ | ||
LineToPoint, /* pointProc */ | ||
LineToArea, /* areaProc */ | ||
LineToPostscript, /* postscriptProc */ | ||
ScaleLine, /* scaleProc */ | ||
TranslateLine, /* translateProc */ | ||
(Tk_ItemIndexProc *) GetLineIndex, /* indexProc */ | ||
(Tk_ItemCursorProc *) NULL, /* icursorProc */ | ||
(Tk_ItemSelectionProc *) NULL, /* selectionProc */ | ||
(Tk_ItemInsertProc *) LineInsert, /* insertProc */ | ||
LineDeleteCoords, /* dTextProc */ | ||
(Tk_ItemType *) NULL, /* nextPtr */ | ||
}; | ||
/* | ||
* The definition below determines how large are static arrays | ||
* used to hold spline points (splines larger than this have to | ||
* have their arrays malloc-ed). | ||
*/ | ||
#define MAX_STATIC_POINTS 200 | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* CreateLine -- | ||
* | ||
* This procedure is invoked to create a new line item in | ||
* a canvas. | ||
* | ||
* Results: | ||
* A standard Tcl return value. If an error occurred in | ||
* creating the item, then an error message is left in | ||
* the interp's result; in this case itemPtr is left uninitialized, | ||
* so it can be safely freed by the caller. | ||
* | ||
* Side effects: | ||
* A new line item is created. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
CreateLine(interp, canvas, itemPtr, argc, argv) | ||
Tcl_Interp *interp; /* Interpreter for error reporting. */ | ||
Tk_Canvas canvas; /* Canvas to hold new item. */ | ||
Tk_Item *itemPtr; /* Record to hold new item; header | ||
* has been initialized by caller. */ | ||
int argc; /* Number of arguments in argv. */ | ||
Tcl_Obj *CONST argv[]; /* Arguments describing line. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
int i; | ||
/* | ||
* Carry out initialization that is needed to set defaults and to | ||
* allow proper cleanup after errors during the the remainder of | ||
* this procedure. | ||
*/ | ||
Tk_CreateOutline(&(linePtr->outline)); | ||
linePtr->canvas = canvas; | ||
linePtr->numPoints = 0; | ||
linePtr->coordPtr = NULL; | ||
linePtr->capStyle = CapButt; | ||
linePtr->joinStyle = JoinRound; | ||
linePtr->arrowGC = None; | ||
linePtr->arrow = ARROWS_NONE; | ||
linePtr->arrowShapeA = (float)8.0; | ||
linePtr->arrowShapeB = (float)10.0; | ||
linePtr->arrowShapeC = (float)3.0; | ||
linePtr->firstArrowPtr = NULL; | ||
linePtr->lastArrowPtr = NULL; | ||
linePtr->smooth = (Tk_SmoothMethod *) NULL; | ||
linePtr->splineSteps = 12; | ||
/* | ||
* Count the number of points and then parse them into a point | ||
* array. Leading arguments are assumed to be points if they | ||
* start with a digit or a minus sign followed by a digit. | ||
*/ | ||
for (i = 0; i < argc; i++) { | ||
char *arg = Tcl_GetStringFromObj(argv[i], NULL); | ||
if ((arg[0] == '-') && (arg[1] >= 'a') | ||
&& (arg[1] <= 'z')) { | ||
break; | ||
} | ||
} | ||
if (i && (LineCoords(interp, canvas, itemPtr, i, argv) != TCL_OK)) { | ||
goto error; | ||
} | ||
if (ConfigureLine(interp, canvas, itemPtr, argc-i, argv+i, 0) == TCL_OK) { | ||
return TCL_OK; | ||
} | ||
error: | ||
DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas))); | ||
return TCL_ERROR; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* LineCoords -- | ||
* | ||
* This procedure is invoked to process the "coords" widget | ||
* command on lines. See the user documentation for details | ||
* on what it does. | ||
* | ||
* Results: | ||
* Returns TCL_OK or TCL_ERROR, and sets the interp's result. | ||
* | ||
* Side effects: | ||
* The coordinates for the given item may be changed. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
LineCoords(interp, canvas, itemPtr, argc, argv) | ||
Tcl_Interp *interp; /* Used for error reporting. */ | ||
Tk_Canvas canvas; /* Canvas containing item. */ | ||
Tk_Item *itemPtr; /* Item whose coordinates are to be | ||
* read or modified. */ | ||
int argc; /* Number of coordinates supplied in | ||
* argv. */ | ||
Tcl_Obj *CONST argv[]; /* Array of coordinates: x1, y1, | ||
* x2, y2, ... */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
int i, numPoints; | ||
double *coordPtr; | ||
if (argc == 0) { | ||
int numCoords; | ||
Tcl_Obj *subobj, *obj = Tcl_NewObj(); | ||
numCoords = 2*linePtr->numPoints; | ||
if (linePtr->firstArrowPtr != NULL) { | ||
coordPtr = linePtr->firstArrowPtr; | ||
} else { | ||
coordPtr = linePtr->coordPtr; | ||
} | ||
for (i = 0; i < numCoords; i++, coordPtr++) { | ||
if (i == 2) { | ||
coordPtr = linePtr->coordPtr+2; | ||
} | ||
if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) { | ||
coordPtr = linePtr->lastArrowPtr; | ||
} | ||
subobj = Tcl_NewDoubleObj(*coordPtr); | ||
Tcl_ListObjAppendElement(interp, obj, subobj); | ||
} | ||
Tcl_SetObjResult(interp, obj); | ||
return TCL_OK; | ||
} | ||
if (argc == 1) { | ||
if (Tcl_ListObjGetElements(interp, argv[0], &argc, | ||
(Tcl_Obj ***) &argv) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} | ||
if (argc & 1) { | ||
Tcl_AppendResult(interp, | ||
"odd number of coordinates specified for line", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} else if (argc < 4) { | ||
Tcl_AppendResult(interp, | ||
"too few coordinates specified for line", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} else { | ||
numPoints = argc/2; | ||
if (linePtr->numPoints != numPoints) { | ||
coordPtr = (double *) ckalloc((unsigned) | ||
(sizeof(double) * argc)); | ||
if (linePtr->coordPtr != NULL) { | ||
ckfree((char *) linePtr->coordPtr); | ||
} | ||
linePtr->coordPtr = coordPtr; | ||
linePtr->numPoints = numPoints; | ||
} | ||
coordPtr = linePtr->coordPtr; | ||
for (i = 0; i <argc; i++) { | ||
if (Tk_CanvasGetCoordFromObj(interp, canvas, argv[i], | ||
coordPtr++) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} | ||
/* | ||
* Update arrowheads by throwing away any existing arrow-head | ||
* information and calling ConfigureArrows to recompute it. | ||
*/ | ||
if (linePtr->firstArrowPtr != NULL) { | ||
ckfree((char *) linePtr->firstArrowPtr); | ||
linePtr->firstArrowPtr = NULL; | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
ckfree((char *) linePtr->lastArrowPtr); | ||
linePtr->lastArrowPtr = NULL; | ||
} | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
ConfigureArrows(canvas, linePtr); | ||
} | ||
ComputeLineBbox(canvas, linePtr); | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ConfigureLine -- | ||
* | ||
* This procedure is invoked to configure various aspects | ||
* of a line item such as its background color. | ||
* | ||
* Results: | ||
* A standard Tcl result code. If an error occurs, then | ||
* an error message is left in the interp's result. | ||
* | ||
* Side effects: | ||
* Configuration information, such as colors and stipple | ||
* patterns, may be set for itemPtr. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
ConfigureLine(interp, canvas, itemPtr, argc, argv, flags) | ||
Tcl_Interp *interp; /* Used for error reporting. */ | ||
Tk_Canvas canvas; /* Canvas containing itemPtr. */ | ||
Tk_Item *itemPtr; /* Line item to reconfigure. */ | ||
int argc; /* Number of elements in argv. */ | ||
Tcl_Obj *CONST argv[]; /* Arguments describing things to configure. */ | ||
int flags; /* Flags to pass to Tk_ConfigureWidget. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
XGCValues gcValues; | ||
GC newGC, arrowGC; | ||
unsigned long mask; | ||
Tk_Window tkwin; | ||
Tk_State state; | ||
tkwin = Tk_CanvasTkwin(canvas); | ||
if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, (char **) argv, | ||
(char *) linePtr, flags|TK_CONFIG_OBJS) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* A few of the options require additional processing, such as | ||
* graphics contexts. | ||
*/ | ||
state = itemPtr->state; | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
if (linePtr->outline.activeWidth > linePtr->outline.width || | ||
linePtr->outline.activeDash.number != 0 || | ||
linePtr->outline.activeColor != NULL || | ||
linePtr->outline.activeStipple != None) { | ||
itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT; | ||
} else { | ||
itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT; | ||
} | ||
mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr, | ||
&(linePtr->outline)); | ||
if (mask) { | ||
if (linePtr->arrow == ARROWS_NONE) { | ||
gcValues.cap_style = linePtr->capStyle; | ||
mask |= GCCapStyle; | ||
} | ||
gcValues.join_style = linePtr->joinStyle; | ||
mask |= GCJoinStyle; | ||
newGC = Tk_GetGC(tkwin, mask, &gcValues); | ||
gcValues.line_width = 0; | ||
arrowGC = Tk_GetGC(tkwin, mask, &gcValues); | ||
} else { | ||
newGC = arrowGC = None; | ||
} | ||
if (linePtr->outline.gc != None) { | ||
Tk_FreeGC(Tk_Display(tkwin), linePtr->outline.gc); | ||
} | ||
if (linePtr->arrowGC != None) { | ||
Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC); | ||
} | ||
linePtr->outline.gc = newGC; | ||
linePtr->arrowGC = arrowGC; | ||
/* | ||
* Keep spline parameters within reasonable limits. | ||
*/ | ||
if (linePtr->splineSteps < 1) { | ||
linePtr->splineSteps = 1; | ||
} else if (linePtr->splineSteps > 100) { | ||
linePtr->splineSteps = 100; | ||
} | ||
if ((!linePtr->numPoints) || (state==TK_STATE_HIDDEN)) { | ||
ComputeLineBbox(canvas, linePtr); | ||
return TCL_OK; | ||
} | ||
/* | ||
* Setup arrowheads, if needed. If arrowheads are turned off, | ||
* restore the line's endpoints (they were shortened when the | ||
* arrowheads were added). | ||
*/ | ||
if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != ARROWS_FIRST) | ||
&& (linePtr->arrow != ARROWS_BOTH)) { | ||
linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; | ||
linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; | ||
ckfree((char *) linePtr->firstArrowPtr); | ||
linePtr->firstArrowPtr = NULL; | ||
} | ||
if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != ARROWS_LAST) | ||
&& (linePtr->arrow != ARROWS_BOTH)) { | ||
int i; | ||
i = 2*(linePtr->numPoints-1); | ||
linePtr->coordPtr[i] = linePtr->lastArrowPtr[0]; | ||
linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1]; | ||
ckfree((char *) linePtr->lastArrowPtr); | ||
linePtr->lastArrowPtr = NULL; | ||
} | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
ConfigureArrows(canvas, linePtr); | ||
} | ||
/* | ||
* Recompute bounding box for line. | ||
*/ | ||
ComputeLineBbox(canvas, linePtr); | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* DeleteLine -- | ||
* | ||
* This procedure is called to clean up the data structure | ||
* associated with a line item. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Resources associated with itemPtr are released. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
DeleteLine(canvas, itemPtr, display) | ||
Tk_Canvas canvas; /* Info about overall canvas widget. */ | ||
Tk_Item *itemPtr; /* Item that is being deleted. */ | ||
Display *display; /* Display containing window for | ||
* canvas. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
Tk_DeleteOutline(display, &(linePtr->outline)); | ||
if (linePtr->coordPtr != NULL) { | ||
ckfree((char *) linePtr->coordPtr); | ||
} | ||
if (linePtr->arrowGC != None) { | ||
Tk_FreeGC(display, linePtr->arrowGC); | ||
} | ||
if (linePtr->firstArrowPtr != NULL) { | ||
ckfree((char *) linePtr->firstArrowPtr); | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
ckfree((char *) linePtr->lastArrowPtr); | ||
} | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ComputeLineBbox -- | ||
* | ||
* This procedure is invoked to compute the bounding box of | ||
* all the pixels that may be drawn as part of a line. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The fields x1, y1, x2, and y2 are updated in the header | ||
* for itemPtr. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
ComputeLineBbox(canvas, linePtr) | ||
Tk_Canvas canvas; /* Canvas that contains item. */ | ||
LineItem *linePtr; /* Item whose bbos is to be | ||
* recomputed. */ | ||
{ | ||
double *coordPtr; | ||
int i, intWidth; | ||
double width; | ||
Tk_State state = linePtr->header.state; | ||
Tk_TSOffset *tsoffset; | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
if (!(linePtr->numPoints) || (state==TK_STATE_HIDDEN)) { | ||
linePtr->header.x1 = -1; | ||
linePtr->header.x2 = -1; | ||
linePtr->header.y1 = -1; | ||
linePtr->header.y2 = -1; | ||
return; | ||
} | ||
width = linePtr->outline.width; | ||
if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) { | ||
if (linePtr->outline.activeWidth>width) { | ||
width = linePtr->outline.activeWidth; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.disabledWidth>0) { | ||
width = linePtr->outline.disabledWidth; | ||
} | ||
} | ||
coordPtr = linePtr->coordPtr; | ||
linePtr->header.x1 = linePtr->header.x2 = (int) *coordPtr; | ||
linePtr->header.y1 = linePtr->header.y2 = (int) coordPtr[1]; | ||
/* | ||
* Compute the bounding box of all the points in the line, | ||
* then expand in all directions by the line's width to take | ||
* care of butting or rounded corners and projecting or | ||
* rounded caps. This expansion is an overestimate (worst-case | ||
* is square root of two over two) but it's simple. Don't do | ||
* anything special for curves. This causes an additional | ||
* overestimate in the bounding box, but is faster. | ||
*/ | ||
for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint((Tk_Item *) linePtr, coordPtr); | ||
} | ||
width = linePtr->outline.width; | ||
if (width < 1.0) { | ||
width = 1.0; | ||
} | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
if (linePtr->arrow != ARROWS_LAST) { | ||
TkIncludePoint((Tk_Item *) linePtr, linePtr->firstArrowPtr); | ||
} | ||
if (linePtr->arrow != ARROWS_FIRST) { | ||
TkIncludePoint((Tk_Item *) linePtr, linePtr->lastArrowPtr); | ||
} | ||
} | ||
tsoffset = &linePtr->outline.tsoffset; | ||
if (tsoffset->flags & TK_OFFSET_INDEX) { | ||
double *coordPtr = linePtr->coordPtr + (tsoffset->flags & ~TK_OFFSET_INDEX); | ||
if (tsoffset->flags <= 0) { | ||
coordPtr = linePtr->coordPtr; | ||
if ((linePtr->arrow == ARROWS_FIRST) || (linePtr->arrow == ARROWS_BOTH)) { | ||
coordPtr = linePtr->firstArrowPtr; | ||
} | ||
} | ||
if (tsoffset->flags > (linePtr->numPoints * 2)) { | ||
coordPtr = linePtr->coordPtr + (linePtr->numPoints * 2); | ||
if ((linePtr->arrow == ARROWS_LAST) || (linePtr->arrow == ARROWS_BOTH)) { | ||
coordPtr = linePtr->lastArrowPtr; | ||
} | ||
} | ||
tsoffset->xoffset = (int) (coordPtr[0] + 0.5); | ||
tsoffset->yoffset = (int) (coordPtr[1] + 0.5); | ||
} else { | ||
if (tsoffset->flags & TK_OFFSET_LEFT) { | ||
tsoffset->xoffset = linePtr->header.x1; | ||
} else if (tsoffset->flags & TK_OFFSET_CENTER) { | ||
tsoffset->xoffset = (linePtr->header.x1 + linePtr->header.x2)/2; | ||
} else if (tsoffset->flags & TK_OFFSET_RIGHT) { | ||
tsoffset->xoffset = linePtr->header.x2; | ||
} | ||
if (tsoffset->flags & TK_OFFSET_TOP) { | ||
tsoffset->yoffset = linePtr->header.y1; | ||
} else if (tsoffset->flags & TK_OFFSET_MIDDLE) { | ||
tsoffset->yoffset = (linePtr->header.y1 + linePtr->header.y2)/2; | ||
} else if (tsoffset->flags & TK_OFFSET_BOTTOM) { | ||
tsoffset->yoffset = linePtr->header.y2; | ||
} | ||
} | ||
intWidth = (int) (width + 0.5); | ||
linePtr->header.x1 -= intWidth; | ||
linePtr->header.x2 += intWidth; | ||
linePtr->header.y1 -= intWidth; | ||
linePtr->header.y2 += intWidth; | ||
if (linePtr->numPoints==1) { | ||
linePtr->header.x1 -= 1; | ||
linePtr->header.x2 += 1; | ||
linePtr->header.y1 -= 1; | ||
linePtr->header.y2 += 1; | ||
return; | ||
} | ||
/* | ||
* For mitered lines, make a second pass through all the points. | ||
* Compute the locations of the two miter vertex points and add | ||
* those into the bounding box. | ||
*/ | ||
if (linePtr->joinStyle == JoinMiter) { | ||
for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3; | ||
i--, coordPtr += 2) { | ||
double miter[4]; | ||
int j; | ||
if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, | ||
width, miter, miter+2)) { | ||
for (j = 0; j < 4; j += 2) { | ||
TkIncludePoint((Tk_Item *) linePtr, miter+j); | ||
} | ||
} | ||
} | ||
} | ||
/* | ||
* Add in the sizes of arrowheads, if any. | ||
*/ | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
if (linePtr->arrow != ARROWS_LAST) { | ||
for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint((Tk_Item *) linePtr, coordPtr); | ||
} | ||
} | ||
if (linePtr->arrow != ARROWS_FIRST) { | ||
for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint((Tk_Item *) linePtr, coordPtr); | ||
} | ||
} | ||
} | ||
/* | ||
* Add one more pixel of fudge factor just to be safe (e.g. | ||
* X may round differently than we do). | ||
*/ | ||
linePtr->header.x1 -= 1; | ||
linePtr->header.x2 += 1; | ||
linePtr->header.y1 -= 1; | ||
linePtr->header.y2 += 1; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* DisplayLine -- | ||
* | ||
* This procedure is invoked to draw a line item in a given | ||
* drawable. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* ItemPtr is drawn in drawable using the transformation | ||
* information in canvas. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
DisplayLine(canvas, itemPtr, display, drawable, x, y, width, height) | ||
Tk_Canvas canvas; /* Canvas that contains item. */ | ||
Tk_Item *itemPtr; /* Item to be displayed. */ | ||
Display *display; /* Display on which to draw item. */ | ||
Drawable drawable; /* Pixmap or window in which to draw | ||
* item. */ | ||
int x, y, width, height; /* Describes region of canvas that | ||
* must be redisplayed (not used). */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
XPoint staticPoints[MAX_STATIC_POINTS]; | ||
XPoint *pointPtr; | ||
XPoint *pPtr; | ||
double *coordPtr, linewidth; | ||
int i, numPoints; | ||
Tk_State state = itemPtr->state; | ||
Pixmap stipple = linePtr->outline.stipple; | ||
if ((!linePtr->numPoints)||(linePtr->outline.gc==None)) { | ||
return; | ||
} | ||
if (state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
linewidth = linePtr->outline.width; | ||
if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | ||
if (linePtr->outline.activeStipple!=None) { | ||
stipple = linePtr->outline.activeStipple; | ||
} | ||
if (linePtr->outline.activeWidth>linewidth) { | ||
linewidth = linePtr->outline.activeWidth; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.disabledStipple!=None) { | ||
stipple = linePtr->outline.disabledStipple; | ||
} | ||
if (linePtr->outline.disabledWidth>linewidth) { | ||
linewidth = linePtr->outline.activeWidth; | ||
} | ||
} | ||
/* | ||
* Build up an array of points in screen coordinates. Use a | ||
* static array unless the line has an enormous number of points; | ||
* in this case, dynamically allocate an array. For smoothed lines, | ||
* generate the curve points on each redisplay. | ||
*/ | ||
if ((linePtr->smooth) && (linePtr->numPoints > 2)) { | ||
numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL, | ||
linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | ||
(double *) NULL); | ||
} else { | ||
numPoints = linePtr->numPoints; | ||
} | ||
if (numPoints <= MAX_STATIC_POINTS) { | ||
pointPtr = staticPoints; | ||
} else { | ||
pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint))); | ||
} | ||
if ((linePtr->smooth) && (linePtr->numPoints > 2)) { | ||
numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, | ||
linePtr->numPoints, linePtr->splineSteps, pointPtr, | ||
(double *) NULL); | ||
} else { | ||
for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr; | ||
i < linePtr->numPoints; i += 1, coordPtr += 2, pPtr++) { | ||
Tk_CanvasDrawableCoords(canvas, coordPtr[0], coordPtr[1], | ||
&pPtr->x, &pPtr->y); | ||
} | ||
} | ||
/* | ||
* Display line, the free up line storage if it was dynamically | ||
* allocated. If we're stippling, then modify the stipple offset | ||
* in the GC. Be sure to reset the offset when done, since the | ||
* GC is supposed to be read-only. | ||
*/ | ||
if (Tk_ChangeOutlineGC(canvas, itemPtr, &(linePtr->outline))) { | ||
Tk_CanvasSetOffset(canvas, linePtr->arrowGC, &linePtr->outline.tsoffset); | ||
} | ||
if (numPoints>1) { | ||
XDrawLines(display, drawable, linePtr->outline.gc, pointPtr, numPoints, | ||
CoordModeOrigin); | ||
} else { | ||
int intwidth = (int) (linewidth + 0.5); | ||
if (intwidth<1) { | ||
intwidth=1; | ||
} | ||
XFillArc(display, drawable, linePtr->outline.gc, | ||
pointPtr->x - intwidth/2, pointPtr->y - intwidth/2, | ||
(unsigned int)intwidth+1, (unsigned int)intwidth+1, 0, 64*360); | ||
} | ||
if (pointPtr != staticPoints) { | ||
ckfree((char *) pointPtr); | ||
} | ||
/* | ||
* Display arrowheads, if they are wanted. | ||
*/ | ||
if (linePtr->firstArrowPtr != NULL) { | ||
TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW, | ||
display, drawable, linePtr->arrowGC, NULL); | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW, | ||
display, drawable, linePtr->arrowGC, NULL); | ||
} | ||
if (Tk_ResetOutlineGC(canvas, itemPtr, &(linePtr->outline))) { | ||
XSetTSOrigin(display, linePtr->arrowGC, 0, 0); | ||
} | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* LineInsert -- | ||
* | ||
* Insert coords into a line item at a given index. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The coords in the given item is modified. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
LineInsert(canvas, itemPtr, beforeThis, obj) | ||
Tk_Canvas canvas; /* Canvas containing text item. */ | ||
Tk_Item *itemPtr; /* Line item to be modified. */ | ||
int beforeThis; /* Index before which new coordinates | ||
* are to be inserted. */ | ||
Tcl_Obj *obj; /* New coordinates to be inserted. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
int length, argc, i; | ||
double *new, *coordPtr; | ||
Tk_State state = itemPtr->state; | ||
Tcl_Obj **objv; | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
if (!obj || (Tcl_ListObjGetElements((Tcl_Interp *) NULL, obj, &argc, &objv) != TCL_OK) | ||
|| !argc || argc&1) { | ||
return; | ||
} | ||
length = 2*linePtr->numPoints; | ||
if (beforeThis < 0) { | ||
beforeThis = 0; | ||
} | ||
if (beforeThis > length) { | ||
beforeThis = length; | ||
} | ||
if (linePtr->firstArrowPtr != NULL) { | ||
linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; | ||
linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0]; | ||
linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1]; | ||
} | ||
new = (double *) ckalloc((unsigned)(sizeof(double) * (length + argc))); | ||
for(i=0; i<beforeThis; i++) { | ||
new[i] = linePtr->coordPtr[i]; | ||
} | ||
for(i=0; i<argc; i++) { | ||
if (Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,objv[i], | ||
new+(i+beforeThis))!=TCL_OK) { | ||
Tcl_ResetResult(((TkCanvas *)canvas)->interp); | ||
ckfree((char *) new); | ||
return; | ||
} | ||
} | ||
for(i=beforeThis; i<length; i++) { | ||
new[i+argc] = linePtr->coordPtr[i]; | ||
} | ||
if(linePtr->coordPtr) ckfree((char *)linePtr->coordPtr); | ||
linePtr->coordPtr = new; | ||
linePtr->numPoints = (length + argc)/2; | ||
if ((length>3) && (state != TK_STATE_HIDDEN)) { | ||
/* | ||
* This is some optimizing code that will result that only the part | ||
* of the polygon that changed (and the objects that are overlapping | ||
* with that part) need to be redrawn. A special flag is set that | ||
* instructs the general canvas code not to redraw the whole | ||
* object. If this flag is not set, the canvas will do the redrawing, | ||
* otherwise I have to do it here. | ||
*/ | ||
itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW; | ||
if (beforeThis>0) {beforeThis -= 2; argc+=2; } | ||
if ((beforeThis+argc)<length) argc+=2; | ||
if (linePtr->smooth) { | ||
if(beforeThis>0) { | ||
beforeThis-=2; argc+=2; | ||
} | ||
if((beforeThis+argc+2)<length) { | ||
argc+=2; | ||
} | ||
} | ||
itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[beforeThis]; | ||
itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[beforeThis+1]; | ||
if ((linePtr->firstArrowPtr != NULL) && (beforeThis<1)) { | ||
/* include old first arrow */ | ||
for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
} | ||
} | ||
if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+argc)>=length)) { | ||
/* include old last arrow */ | ||
for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
} | ||
} | ||
coordPtr = linePtr->coordPtr+beforeThis+2; | ||
for(i=2; i<argc; i+=2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
coordPtr+=2; | ||
} | ||
} | ||
if (linePtr->firstArrowPtr != NULL) { | ||
ckfree((char *) linePtr->firstArrowPtr); | ||
linePtr->firstArrowPtr = NULL; | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
ckfree((char *) linePtr->lastArrowPtr); | ||
linePtr->lastArrowPtr = NULL; | ||
} | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
ConfigureArrows(canvas, linePtr); | ||
} | ||
if(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) { | ||
double width; | ||
int intWidth; | ||
if ((linePtr->firstArrowPtr != NULL) && (beforeThis>2)) { | ||
/* include new first arrow */ | ||
for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
} | ||
} | ||
if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+argc)<(length-2))) { | ||
/* include new right arrow */ | ||
for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
} | ||
} | ||
width = linePtr->outline.width; | ||
if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | ||
if (linePtr->outline.activeWidth>width) { | ||
width = linePtr->outline.activeWidth; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.disabledWidth>0) { | ||
width = linePtr->outline.disabledWidth; | ||
} | ||
} | ||
intWidth = (int) (width + 0.5); | ||
if (intWidth < 1) { | ||
intWidth = 1; | ||
} | ||
itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth; | ||
itemPtr->x2 += intWidth; itemPtr->y2 += intWidth; | ||
Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1, | ||
itemPtr->x2, itemPtr->y2); | ||
} | ||
ComputeLineBbox(canvas, linePtr); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* LineDeleteCoords -- | ||
* | ||
* Delete one or more coordinates from a line item. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* Characters between "first" and "last", inclusive, get | ||
* deleted from itemPtr. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
LineDeleteCoords(canvas, itemPtr, first, last) | ||
Tk_Canvas canvas; /* Canvas containing itemPtr. */ | ||
Tk_Item *itemPtr; /* Item in which to delete characters. */ | ||
int first; /* Index of first character to delete. */ | ||
int last; /* Index of last character to delete. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
int count, i, first1, last1; | ||
int length = 2*linePtr->numPoints; | ||
double *coordPtr; | ||
Tk_State state = itemPtr->state; | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
first &= -2; | ||
last &= -2; | ||
if (first < 0) { | ||
first = 0; | ||
} | ||
if (last >= length) { | ||
last = length-2; | ||
} | ||
if (first > last) { | ||
return; | ||
} | ||
if (linePtr->firstArrowPtr != NULL) { | ||
linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; | ||
linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0]; | ||
linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1]; | ||
} | ||
first1 = first; last1 = last; | ||
if(first1>0) first1 -= 2; | ||
if(last1<length-2) last1 += 2; | ||
if (linePtr->smooth) { | ||
if(first1>0) first1 -= 2; | ||
if(last1<length-2) last1 += 2; | ||
} | ||
if((first1<2) && (last1 >= length-2)) { | ||
/* | ||
* This is some optimizing code that will result that only the part | ||
* of the line that changed (and the objects that are overlapping | ||
* with that part) need to be redrawn. A special flag is set that | ||
* instructs the general canvas code not to redraw the whole | ||
* object. If this flag is set, the redrawing has to be done here, | ||
* otherwise the general Canvas code will take care of it. | ||
*/ | ||
itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW; | ||
itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[first1]; | ||
itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[first1+1]; | ||
if ((linePtr->firstArrowPtr != NULL) && (first1<2)) { | ||
/* include old first arrow */ | ||
for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
} | ||
} | ||
if ((linePtr->lastArrowPtr != NULL) && (last1>=length-2)) { | ||
/* include old last arrow */ | ||
for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
} | ||
} | ||
coordPtr = linePtr->coordPtr+first1+2; | ||
for (i=first1+2; i<=last1; i+=2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
coordPtr+=2; | ||
} | ||
} | ||
count = last + 2 - first; | ||
for (i=last+2; i<length; i++) { | ||
linePtr->coordPtr[i-count] = linePtr->coordPtr[i]; | ||
} | ||
linePtr->numPoints -= count/2; | ||
if (linePtr->firstArrowPtr != NULL) { | ||
ckfree((char *) linePtr->firstArrowPtr); | ||
linePtr->firstArrowPtr = NULL; | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
ckfree((char *) linePtr->lastArrowPtr); | ||
linePtr->lastArrowPtr = NULL; | ||
} | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
ConfigureArrows(canvas, linePtr); | ||
} | ||
if(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) { | ||
double width; | ||
int intWidth; | ||
if ((linePtr->firstArrowPtr != NULL) && (first1<4)) { | ||
/* include new first arrow */ | ||
for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
} | ||
} | ||
if ((linePtr->lastArrowPtr != NULL) && (last1>(length-4))) { | ||
/* include new right arrow */ | ||
for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
TkIncludePoint(itemPtr, coordPtr); | ||
} | ||
} | ||
width = linePtr->outline.width; | ||
if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | ||
if (linePtr->outline.activeWidth>width) { | ||
width = linePtr->outline.activeWidth; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.disabledWidth>0) { | ||
width = linePtr->outline.disabledWidth; | ||
} | ||
} | ||
intWidth = (int) (width + 0.5); | ||
if (intWidth < 1) { | ||
intWidth = 1; | ||
} | ||
itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth; | ||
itemPtr->x2 += intWidth; itemPtr->y2 += intWidth; | ||
Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1, | ||
itemPtr->x2, itemPtr->y2); | ||
} | ||
ComputeLineBbox(canvas, linePtr); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* LineToPoint -- | ||
* | ||
* Computes the distance from a given point to a given | ||
* line, in canvas units. | ||
* | ||
* Results: | ||
* The return value is 0 if the point whose x and y coordinates | ||
* are pointPtr[0] and pointPtr[1] is inside the line. If the | ||
* point isn't inside the line then the return value is the | ||
* distance from the point to the line. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
static double | ||
LineToPoint(canvas, itemPtr, pointPtr) | ||
Tk_Canvas canvas; /* Canvas containing item. */ | ||
Tk_Item *itemPtr; /* Item to check against point. */ | ||
double *pointPtr; /* Pointer to x and y coordinates. */ | ||
{ | ||
Tk_State state = itemPtr->state; | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
double *coordPtr, *linePoints; | ||
double staticSpace[2*MAX_STATIC_POINTS]; | ||
double poly[10]; | ||
double bestDist, dist, width; | ||
int numPoints, count; | ||
int changedMiterToBevel; /* Non-zero means that a mitered corner | ||
* had to be treated as beveled after all | ||
* because the angle was < 11 degrees. */ | ||
bestDist = 1.0e36; | ||
/* | ||
* Handle smoothed lines by generating an expanded set of points | ||
* against which to do the check. | ||
*/ | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
width = linePtr->outline.width; | ||
if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | ||
if (linePtr->outline.activeWidth>width) { | ||
width = linePtr->outline.activeWidth; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.disabledWidth>0) { | ||
width = linePtr->outline.disabledWidth; | ||
} | ||
} | ||
if ((linePtr->smooth) && (linePtr->numPoints > 2)) { | ||
numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL, | ||
linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | ||
(double *) NULL); | ||
if (numPoints <= MAX_STATIC_POINTS) { | ||
linePoints = staticSpace; | ||
} else { | ||
linePoints = (double *) ckalloc((unsigned) | ||
(2*numPoints*sizeof(double))); | ||
} | ||
numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, | ||
linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | ||
linePoints); | ||
} else { | ||
numPoints = linePtr->numPoints; | ||
linePoints = linePtr->coordPtr; | ||
} | ||
if (width < 1.0) { | ||
width = 1.0; | ||
} | ||
if (!numPoints || itemPtr->state==TK_STATE_HIDDEN) { | ||
return bestDist; | ||
} else if (numPoints == 1) { | ||
bestDist = hypot(linePoints[0] - pointPtr[0], linePoints[1] - pointPtr[1]) | ||
- width/2.0; | ||
if (bestDist < 0) bestDist = 0; | ||
return bestDist; | ||
} | ||
/* | ||
* The overall idea is to iterate through all of the edges of | ||
* the line, computing a polygon for each edge and testing the | ||
* point against that polygon. In addition, there are additional | ||
* tests to deal with rounded joints and caps. | ||
*/ | ||
changedMiterToBevel = 0; | ||
for (count = numPoints, coordPtr = linePoints; count >= 2; | ||
count--, coordPtr += 2) { | ||
/* | ||
* If rounding is done around the first point then compute | ||
* the distance between the point and the point. | ||
*/ | ||
if (((linePtr->capStyle == CapRound) && (count == numPoints)) | ||
|| ((linePtr->joinStyle == JoinRound) | ||
&& (count != numPoints))) { | ||
dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) | ||
- width/2.0; | ||
if (dist <= 0.0) { | ||
bestDist = 0.0; | ||
goto done; | ||
} else if (dist < bestDist) { | ||
bestDist = dist; | ||
} | ||
} | ||
/* | ||
* Compute the polygonal shape corresponding to this edge, | ||
* consisting of two points for the first point of the edge | ||
* and two points for the last point of the edge. | ||
*/ | ||
if (count == numPoints) { | ||
TkGetButtPoints(coordPtr+2, coordPtr, width, | ||
linePtr->capStyle == CapProjecting, poly, poly+2); | ||
} else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) { | ||
poly[0] = poly[6]; | ||
poly[1] = poly[7]; | ||
poly[2] = poly[4]; | ||
poly[3] = poly[5]; | ||
} else { | ||
TkGetButtPoints(coordPtr+2, coordPtr, width, 0, | ||
poly, poly+2); | ||
/* | ||
* If this line uses beveled joints, then check the distance | ||
* to a polygon comprising the last two points of the previous | ||
* polygon and the first two from this polygon; this checks | ||
* the wedges that fill the mitered joint. | ||
*/ | ||
if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) { | ||
poly[8] = poly[0]; | ||
poly[9] = poly[1]; | ||
dist = TkPolygonToPoint(poly, 5, pointPtr); | ||
if (dist <= 0.0) { | ||
bestDist = 0.0; | ||
goto done; | ||
} else if (dist < bestDist) { | ||
bestDist = dist; | ||
} | ||
changedMiterToBevel = 0; | ||
} | ||
} | ||
if (count == 2) { | ||
TkGetButtPoints(coordPtr, coordPtr+2, width, | ||
linePtr->capStyle == CapProjecting, poly+4, poly+6); | ||
} else if (linePtr->joinStyle == JoinMiter) { | ||
if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, | ||
width, poly+4, poly+6) == 0) { | ||
changedMiterToBevel = 1; | ||
TkGetButtPoints(coordPtr, coordPtr+2, width, | ||
0, poly+4, poly+6); | ||
} | ||
} else { | ||
TkGetButtPoints(coordPtr, coordPtr+2, width, 0, | ||
poly+4, poly+6); | ||
} | ||
poly[8] = poly[0]; | ||
poly[9] = poly[1]; | ||
dist = TkPolygonToPoint(poly, 5, pointPtr); | ||
if (dist <= 0.0) { | ||
bestDist = 0.0; | ||
goto done; | ||
} else if (dist < bestDist) { | ||
bestDist = dist; | ||
} | ||
} | ||
/* | ||
* If caps are rounded, check the distance to the cap around the | ||
* final end point of the line. | ||
*/ | ||
if (linePtr->capStyle == CapRound) { | ||
dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) | ||
- width/2.0; | ||
if (dist <= 0.0) { | ||
bestDist = 0.0; | ||
goto done; | ||
} else if (dist < bestDist) { | ||
bestDist = dist; | ||
} | ||
} | ||
/* | ||
* If there are arrowheads, check the distance to the arrowheads. | ||
*/ | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
if (linePtr->arrow != ARROWS_LAST) { | ||
dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW, | ||
pointPtr); | ||
if (dist <= 0.0) { | ||
bestDist = 0.0; | ||
goto done; | ||
} else if (dist < bestDist) { | ||
bestDist = dist; | ||
} | ||
} | ||
if (linePtr->arrow != ARROWS_FIRST) { | ||
dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW, | ||
pointPtr); | ||
if (dist <= 0.0) { | ||
bestDist = 0.0; | ||
goto done; | ||
} else if (dist < bestDist) { | ||
bestDist = dist; | ||
} | ||
} | ||
} | ||
done: | ||
if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) { | ||
ckfree((char *) linePoints); | ||
} | ||
return bestDist; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* LineToArea -- | ||
* | ||
* This procedure is called to determine whether an item | ||
* lies entirely inside, entirely outside, or overlapping | ||
* a given rectangular area. | ||
* | ||
* Results: | ||
* -1 is returned if the item is entirely outside the | ||
* area, 0 if it overlaps, and 1 if it is entirely | ||
* inside the given area. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
static int | ||
LineToArea(canvas, itemPtr, rectPtr) | ||
Tk_Canvas canvas; /* Canvas containing item. */ | ||
Tk_Item *itemPtr; /* Item to check against line. */ | ||
double *rectPtr; | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
double staticSpace[2*MAX_STATIC_POINTS]; | ||
double *linePoints; | ||
int numPoints, result; | ||
double radius, width; | ||
Tk_State state = itemPtr->state; | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
width = linePtr->outline.width; | ||
if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | ||
if (linePtr->outline.activeWidth>width) { | ||
width = linePtr->outline.activeWidth; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.disabledWidth>0) { | ||
width = linePtr->outline.disabledWidth; | ||
} | ||
} | ||
radius = (width+1.0)/2.0; | ||
if ((state==TK_STATE_HIDDEN) || !linePtr->numPoints) { | ||
return -1; | ||
} else if (linePtr->numPoints == 1) { | ||
double oval[4]; | ||
oval[0] = linePtr->coordPtr[0]-radius; | ||
oval[1] = linePtr->coordPtr[1]-radius; | ||
oval[2] = linePtr->coordPtr[0]+radius; | ||
oval[3] = linePtr->coordPtr[1]+radius; | ||
return TkOvalToArea(oval, rectPtr); | ||
} | ||
/* | ||
* Handle smoothed lines by generating an expanded set of points | ||
* against which to do the check. | ||
*/ | ||
if ((linePtr->smooth) && (linePtr->numPoints > 2)) { | ||
numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL, | ||
linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | ||
(double *) NULL); | ||
if (numPoints <= MAX_STATIC_POINTS) { | ||
linePoints = staticSpace; | ||
} else { | ||
linePoints = (double *) ckalloc((unsigned) | ||
(2*numPoints*sizeof(double))); | ||
} | ||
numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, | ||
linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | ||
linePoints); | ||
} else { | ||
numPoints = linePtr->numPoints; | ||
linePoints = linePtr->coordPtr; | ||
} | ||
/* | ||
* Check the segments of the line. | ||
*/ | ||
if (width < 1.0) { | ||
width = 1.0; | ||
} | ||
result = TkThickPolyLineToArea(linePoints, numPoints, | ||
width, linePtr->capStyle, linePtr->joinStyle, | ||
rectPtr); | ||
if (result == 0) { | ||
goto done; | ||
} | ||
/* | ||
* Check arrowheads, if any. | ||
*/ | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
if (linePtr->arrow != ARROWS_LAST) { | ||
if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW, | ||
rectPtr) != result) { | ||
result = 0; | ||
goto done; | ||
} | ||
} | ||
if (linePtr->arrow != ARROWS_FIRST) { | ||
if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW, | ||
rectPtr) != result) { | ||
result = 0; | ||
goto done; | ||
} | ||
} | ||
} | ||
done: | ||
if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) { | ||
ckfree((char *) linePoints); | ||
} | ||
return result; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ScaleLine -- | ||
* | ||
* This procedure is invoked to rescale a line item. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The line referred to by itemPtr is rescaled so that the | ||
* following transformation is applied to all point | ||
* coordinates: | ||
* x' = originX + scaleX*(x-originX) | ||
* y' = originY + scaleY*(y-originY) | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
ScaleLine(canvas, itemPtr, originX, originY, scaleX, scaleY) | ||
Tk_Canvas canvas; /* Canvas containing line. */ | ||
Tk_Item *itemPtr; /* Line to be scaled. */ | ||
double originX, originY; /* Origin about which to scale rect. */ | ||
double scaleX; /* Amount to scale in X direction. */ | ||
double scaleY; /* Amount to scale in Y direction. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
double *coordPtr; | ||
int i; | ||
/* | ||
* Delete any arrowheads before scaling all the points (so that | ||
* the end-points of the line get restored). | ||
*/ | ||
if (linePtr->firstArrowPtr != NULL) { | ||
linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; | ||
linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; | ||
ckfree((char *) linePtr->firstArrowPtr); | ||
linePtr->firstArrowPtr = NULL; | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
int i; | ||
i = 2*(linePtr->numPoints-1); | ||
linePtr->coordPtr[i] = linePtr->lastArrowPtr[0]; | ||
linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1]; | ||
ckfree((char *) linePtr->lastArrowPtr); | ||
linePtr->lastArrowPtr = NULL; | ||
} | ||
for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints; | ||
i++, coordPtr += 2) { | ||
coordPtr[0] = originX + scaleX*(*coordPtr - originX); | ||
coordPtr[1] = originY + scaleY*(coordPtr[1] - originY); | ||
} | ||
if (linePtr->arrow != ARROWS_NONE) { | ||
ConfigureArrows(canvas, linePtr); | ||
} | ||
ComputeLineBbox(canvas, linePtr); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* GetLineIndex -- | ||
* | ||
* Parse an index into a line item and return either its value | ||
* or an error. | ||
* | ||
* Results: | ||
* A standard Tcl result. If all went well, then *indexPtr is | ||
* filled in with the index (into itemPtr) corresponding to | ||
* string. Otherwise an error message is left in | ||
* interp->result. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
GetLineIndex(interp, canvas, itemPtr, obj, indexPtr) | ||
Tcl_Interp *interp; /* Used for error reporting. */ | ||
Tk_Canvas canvas; /* Canvas containing item. */ | ||
Tk_Item *itemPtr; /* Item for which the index is being | ||
* specified. */ | ||
Tcl_Obj *obj; /* Specification of a particular coord | ||
* in itemPtr's line. */ | ||
int *indexPtr; /* Where to store converted index. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
size_t length; | ||
char *string = Tcl_GetStringFromObj(obj, (int *) &length); | ||
if (string[0] == 'e') { | ||
if (strncmp(string, "end", length) == 0) { | ||
*indexPtr = 2*linePtr->numPoints; | ||
} else { | ||
badIndex: | ||
/* | ||
* Some of the paths here leave messages in interp->result, | ||
* so we have to clear it out before storing our own message. | ||
*/ | ||
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC); | ||
Tcl_AppendResult(interp, "bad index \"", string, "\"", | ||
(char *) NULL); | ||
return TCL_ERROR; | ||
} | ||
} else if (string[0] == '@') { | ||
int i; | ||
double x ,y, bestDist, dist, *coordPtr; | ||
char *end, *p; | ||
p = string+1; | ||
x = strtod(p, &end); | ||
if ((end == p) || (*end != ',')) { | ||
goto badIndex; | ||
} | ||
p = end+1; | ||
y = strtod(p, &end); | ||
if ((end == p) || (*end != 0)) { | ||
goto badIndex; | ||
} | ||
bestDist = 1.0e36; | ||
coordPtr = linePtr->coordPtr; | ||
*indexPtr = 0; | ||
for(i=0; i<linePtr->numPoints; i++) { | ||
dist = hypot(coordPtr[0] - x, coordPtr[1] - y); | ||
if (dist<bestDist) { | ||
bestDist = dist; | ||
*indexPtr = 2*i; | ||
} | ||
coordPtr += 2; | ||
} | ||
} else { | ||
if (Tcl_GetIntFromObj(interp, obj, indexPtr) != TCL_OK) { | ||
goto badIndex; | ||
} | ||
*indexPtr &= -2; /* if index is odd, make it even */ | ||
if (*indexPtr < 0){ | ||
*indexPtr = 0; | ||
} else if (*indexPtr > (2*linePtr->numPoints)) { | ||
*indexPtr = (2*linePtr->numPoints); | ||
} | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* TranslateLine -- | ||
* | ||
* This procedure is called to move a line by a given amount. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* The position of the line is offset by (xDelta, yDelta), and | ||
* the bounding box is updated in the generic part of the item | ||
* structure. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static void | ||
TranslateLine(canvas, itemPtr, deltaX, deltaY) | ||
Tk_Canvas canvas; /* Canvas containing item. */ | ||
Tk_Item *itemPtr; /* Item that is being moved. */ | ||
double deltaX, deltaY; /* Amount by which item is to be | ||
* moved. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
double *coordPtr; | ||
int i; | ||
for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints; | ||
i++, coordPtr += 2) { | ||
coordPtr[0] += deltaX; | ||
coordPtr[1] += deltaY; | ||
} | ||
if (linePtr->firstArrowPtr != NULL) { | ||
for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
coordPtr[0] += deltaX; | ||
coordPtr[1] += deltaY; | ||
} | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | ||
i++, coordPtr += 2) { | ||
coordPtr[0] += deltaX; | ||
coordPtr[1] += deltaY; | ||
} | ||
} | ||
ComputeLineBbox(canvas, linePtr); | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ParseArrowShape -- | ||
* | ||
* This procedure is called back during option parsing to | ||
* parse arrow shape information. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result: TCL_OK means | ||
* that the arrow shape information was parsed ok, and | ||
* TCL_ERROR means it couldn't be parsed. | ||
* | ||
* Side effects: | ||
* Arrow information in recordPtr is updated. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
static int | ||
ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset) | ||
ClientData clientData; /* Not used. */ | ||
Tcl_Interp *interp; /* Used for error reporting. */ | ||
Tk_Window tkwin; /* Not used. */ | ||
CONST char *value; /* Textual specification of arrow shape. */ | ||
char *recordPtr; /* Pointer to item record in which to | ||
* store arrow information. */ | ||
int offset; /* Offset of shape information in widget | ||
* record. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) recordPtr; | ||
double a, b, c; | ||
int argc; | ||
char **argv = NULL; | ||
if (offset != Tk_Offset(LineItem, arrowShapeA)) { | ||
panic("ParseArrowShape received bogus offset"); | ||
} | ||
if (Tcl_SplitList(interp, (char *) value, &argc, &argv) != TCL_OK) { | ||
syntaxError: | ||
Tcl_ResetResult(interp); | ||
Tcl_AppendResult(interp, "bad arrow shape \"", value, | ||
"\": must be list with three numbers", (char *) NULL); | ||
if (argv != NULL) { | ||
ckfree((char *) argv); | ||
} | ||
return TCL_ERROR; | ||
} | ||
if (argc != 3) { | ||
goto syntaxError; | ||
} | ||
if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK) | ||
|| (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[1], &b) | ||
!= TCL_OK) | ||
|| (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[2], &c) | ||
!= TCL_OK)) { | ||
goto syntaxError; | ||
} | ||
linePtr->arrowShapeA = (float)a; | ||
linePtr->arrowShapeB = (float)b; | ||
linePtr->arrowShapeC = (float)c; | ||
ckfree((char *) argv); | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* PrintArrowShape -- | ||
* | ||
* This procedure is a callback invoked by the configuration | ||
* code to return a printable value describing an arrow shape. | ||
* | ||
* Results: | ||
* None. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
static char * | ||
PrintArrowShape(clientData, tkwin, recordPtr, offset, freeProcPtr) | ||
ClientData clientData; /* Not used. */ | ||
Tk_Window tkwin; /* Window associated with linePtr's widget. */ | ||
char *recordPtr; /* Pointer to item record containing current | ||
* shape information. */ | ||
int offset; /* Offset of arrow information in record. */ | ||
Tcl_FreeProc **freeProcPtr; /* Store address of procedure to call to | ||
* free string here. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) recordPtr; | ||
char *buffer; | ||
buffer = (char *) ckalloc(120); | ||
sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA, | ||
linePtr->arrowShapeB, linePtr->arrowShapeC); | ||
*freeProcPtr = TCL_DYNAMIC; | ||
return buffer; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ArrowParseProc -- | ||
* | ||
* This procedure is invoked during option processing to handle | ||
* the "-arrow" option. | ||
* | ||
* Results: | ||
* A standard Tcl return value. | ||
* | ||
* Side effects: | ||
* The arrow for a given item gets replaced by the arrow | ||
* indicated in the value argument. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
ArrowParseProc(clientData, interp, tkwin, value, widgRec, offset) | ||
ClientData clientData; /* some flags.*/ | ||
Tcl_Interp *interp; /* Used for reporting errors. */ | ||
Tk_Window tkwin; /* Window containing canvas widget. */ | ||
CONST char *value; /* Value of option. */ | ||
char *widgRec; /* Pointer to record for item. */ | ||
int offset; /* Offset into item. */ | ||
{ | ||
int c; | ||
size_t length; | ||
register Arrows *arrowPtr = (Arrows *) (widgRec + offset); | ||
if(value == NULL || *value == 0) { | ||
*arrowPtr = ARROWS_NONE; | ||
return TCL_OK; | ||
} | ||
c = value[0]; | ||
length = strlen(value); | ||
if ((c == 'n') && (strncmp(value, "none", length) == 0)) { | ||
*arrowPtr = ARROWS_NONE; | ||
return TCL_OK; | ||
} | ||
if ((c == 'f') && (strncmp(value, "first", length) == 0)) { | ||
*arrowPtr = ARROWS_FIRST; | ||
return TCL_OK; | ||
} | ||
if ((c == 'l') && (strncmp(value, "last", length) == 0)) { | ||
*arrowPtr = ARROWS_LAST; | ||
return TCL_OK; | ||
} | ||
if ((c == 'b') && (strncmp(value, "both", length) == 0)) { | ||
*arrowPtr = ARROWS_BOTH; | ||
return TCL_OK; | ||
} | ||
Tcl_AppendResult(interp, "bad arrow spec \"", value, | ||
"\": must be none, first, last, or both", | ||
(char *) NULL); | ||
*arrowPtr = ARROWS_NONE; | ||
return TCL_ERROR; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ArrowPrintProc -- | ||
* | ||
* This procedure is invoked by the Tk configuration code | ||
* to produce a printable string for the "-arrow" | ||
* configuration option. | ||
* | ||
* Results: | ||
* The return value is a string describing the arrows 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. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static char * | ||
ArrowPrintProc(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 Arrows *arrowPtr = (Arrows *) (widgRec + offset); | ||
switch (*arrowPtr) { | ||
case ARROWS_FIRST: | ||
return "first"; | ||
case ARROWS_LAST: | ||
return "last"; | ||
case ARROWS_BOTH: | ||
return "both"; | ||
default: | ||
return "none"; | ||
} | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ConfigureArrows -- | ||
* | ||
* If arrowheads have been requested for a line, this | ||
* procedure makes arrangements for the arrowheads. | ||
* | ||
* Results: | ||
* Always returns TCL_OK. | ||
* | ||
* Side effects: | ||
* Information in linePtr is set up for one or two arrowheads. | ||
* the firstArrowPtr and lastArrowPtr polygons are allocated | ||
* and initialized, if need be, and the end points of the line | ||
* are adjusted so that a thick line doesn't stick out past | ||
* the arrowheads. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
/* ARGSUSED */ | ||
static int | ||
ConfigureArrows(canvas, linePtr) | ||
Tk_Canvas canvas; /* Canvas in which arrows will be | ||
* displayed (interp and tkwin | ||
* fields are needed). */ | ||
LineItem *linePtr; /* Item to configure for arrows. */ | ||
{ | ||
double *poly, *coordPtr; | ||
double dx, dy, length, sinTheta, cosTheta, temp; | ||
double fracHeight; /* Line width as fraction of | ||
* arrowhead width. */ | ||
double backup; /* Distance to backup end points | ||
* so the line ends in the middle | ||
* of the arrowhead. */ | ||
double vertX, vertY; /* Position of arrowhead vertex. */ | ||
double shapeA, shapeB, shapeC; /* Adjusted coordinates (see | ||
* explanation below). */ | ||
double width; | ||
Tk_State state = linePtr->header.state; | ||
if (linePtr->numPoints <2) { | ||
return TCL_OK; | ||
} | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
width = linePtr->outline.width; | ||
if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) { | ||
if (linePtr->outline.activeWidth>width) { | ||
width = linePtr->outline.activeWidth; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.disabledWidth>0) { | ||
width = linePtr->outline.disabledWidth; | ||
} | ||
} | ||
/* | ||
* The code below makes a tiny increase in the shape parameters | ||
* for the line. This is a bit of a hack, but it seems to result | ||
* in displays that more closely approximate the specified parameters. | ||
* Without the adjustment, the arrows come out smaller than expected. | ||
*/ | ||
shapeA = linePtr->arrowShapeA + 0.001; | ||
shapeB = linePtr->arrowShapeB + 0.001; | ||
shapeC = linePtr->arrowShapeC + width/2.0 + 0.001; | ||
/* | ||
* If there's an arrowhead on the first point of the line, compute | ||
* its polygon and adjust the first point of the line so that the | ||
* line doesn't stick out past the leading edge of the arrowhead. | ||
*/ | ||
fracHeight = (width/2.0)/shapeC; | ||
backup = fracHeight*shapeB + shapeA*(1.0 - fracHeight)/2.0; | ||
if (linePtr->arrow != ARROWS_LAST) { | ||
poly = linePtr->firstArrowPtr; | ||
if (poly == NULL) { | ||
poly = (double *) ckalloc((unsigned) | ||
(2*PTS_IN_ARROW*sizeof(double))); | ||
poly[0] = poly[10] = linePtr->coordPtr[0]; | ||
poly[1] = poly[11] = linePtr->coordPtr[1]; | ||
linePtr->firstArrowPtr = poly; | ||
} | ||
dx = poly[0] - linePtr->coordPtr[2]; | ||
dy = poly[1] - linePtr->coordPtr[3]; | ||
length = hypot(dx, dy); | ||
if (length == 0) { | ||
sinTheta = cosTheta = 0.0; | ||
} else { | ||
sinTheta = dy/length; | ||
cosTheta = dx/length; | ||
} | ||
vertX = poly[0] - shapeA*cosTheta; | ||
vertY = poly[1] - shapeA*sinTheta; | ||
temp = shapeC*sinTheta; | ||
poly[2] = poly[0] - shapeB*cosTheta + temp; | ||
poly[8] = poly[2] - 2*temp; | ||
temp = shapeC*cosTheta; | ||
poly[3] = poly[1] - shapeB*sinTheta - temp; | ||
poly[9] = poly[3] + 2*temp; | ||
poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight); | ||
poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight); | ||
poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight); | ||
poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight); | ||
/* | ||
* Polygon done. Now move the first point towards the second so | ||
* that the corners at the end of the line are inside the | ||
* arrowhead. | ||
*/ | ||
linePtr->coordPtr[0] = poly[0] - backup*cosTheta; | ||
linePtr->coordPtr[1] = poly[1] - backup*sinTheta; | ||
} | ||
/* | ||
* Similar arrowhead calculation for the last point of the line. | ||
*/ | ||
if (linePtr->arrow != ARROWS_FIRST) { | ||
coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2); | ||
poly = linePtr->lastArrowPtr; | ||
if (poly == NULL) { | ||
poly = (double *) ckalloc((unsigned) | ||
(2*PTS_IN_ARROW*sizeof(double))); | ||
poly[0] = poly[10] = coordPtr[2]; | ||
poly[1] = poly[11] = coordPtr[3]; | ||
linePtr->lastArrowPtr = poly; | ||
} | ||
dx = poly[0] - coordPtr[0]; | ||
dy = poly[1] - coordPtr[1]; | ||
length = hypot(dx, dy); | ||
if (length == 0) { | ||
sinTheta = cosTheta = 0.0; | ||
} else { | ||
sinTheta = dy/length; | ||
cosTheta = dx/length; | ||
} | ||
vertX = poly[0] - shapeA*cosTheta; | ||
vertY = poly[1] - shapeA*sinTheta; | ||
temp = shapeC*sinTheta; | ||
poly[2] = poly[0] - shapeB*cosTheta + temp; | ||
poly[8] = poly[2] - 2*temp; | ||
temp = shapeC*cosTheta; | ||
poly[3] = poly[1] - shapeB*sinTheta - temp; | ||
poly[9] = poly[3] + 2*temp; | ||
poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight); | ||
poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight); | ||
poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight); | ||
poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight); | ||
coordPtr[2] = poly[0] - backup*cosTheta; | ||
coordPtr[3] = poly[1] - backup*sinTheta; | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* LineToPostscript -- | ||
* | ||
* This procedure is called to generate Postscript for | ||
* line items. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result. If an error | ||
* occurs in generating Postscript then an error message is | ||
* left in the interp's result, replacing whatever used | ||
* to be there. If no error occurs, then Postscript for the | ||
* item is appended to the result. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
LineToPostscript(interp, canvas, itemPtr, prepass) | ||
Tcl_Interp *interp; /* Leave Postscript or error message | ||
* here. */ | ||
Tk_Canvas canvas; /* Information about overall canvas. */ | ||
Tk_Item *itemPtr; /* Item for which Postscript is | ||
* wanted. */ | ||
int prepass; /* 1 means this is a prepass to | ||
* collect font information; 0 means | ||
* final Postscript is being created. */ | ||
{ | ||
LineItem *linePtr = (LineItem *) itemPtr; | ||
char buffer[64 + TCL_INTEGER_SPACE]; | ||
char *style; | ||
double width; | ||
XColor *color; | ||
Pixmap stipple; | ||
Tk_State state = itemPtr->state; | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
width = linePtr->outline.width; | ||
color = linePtr->outline.color; | ||
stipple = linePtr->outline.stipple; | ||
if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | ||
if (linePtr->outline.activeWidth>width) { | ||
width = linePtr->outline.activeWidth; | ||
} | ||
if (linePtr->outline.activeColor!=NULL) { | ||
color = linePtr->outline.activeColor; | ||
} | ||
if (linePtr->outline.activeStipple!=None) { | ||
stipple = linePtr->outline.activeStipple; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.disabledWidth>0) { | ||
width = linePtr->outline.disabledWidth; | ||
} | ||
if (linePtr->outline.disabledColor!=NULL) { | ||
color = linePtr->outline.disabledColor; | ||
} | ||
if (linePtr->outline.disabledStipple!=None) { | ||
stipple = linePtr->outline.disabledStipple; | ||
} | ||
} | ||
if (color == NULL || linePtr->numPoints<1 || linePtr->coordPtr==NULL) { | ||
return TCL_OK; | ||
} | ||
if (linePtr->numPoints==1) { | ||
sprintf(buffer, "%.15g %.15g translate %.15g %.15g", | ||
linePtr->coordPtr[0], Tk_CanvasPsY(canvas, linePtr->coordPtr[1]), | ||
width/2.0, width/2.0); | ||
Tcl_AppendResult(interp, "matrix currentmatrix\n",buffer, | ||
" scale 1 0 moveto 0 0 1 0 360 arc\nsetmatrix\n", (char *) NULL); | ||
if (Tk_CanvasPsColor(interp, canvas, color) | ||
!= TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
if (stipple != None) { | ||
Tcl_AppendResult(interp, "clip ", (char *) NULL); | ||
if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} else { | ||
Tcl_AppendResult(interp, "fill\n", (char *) NULL); | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
* Generate a path for the line's center-line (do this differently | ||
* for straight lines and smoothed lines). | ||
*/ | ||
if ((!linePtr->smooth) || (linePtr->numPoints < 3)) { | ||
Tk_CanvasPsPath(interp, canvas, linePtr->coordPtr, linePtr->numPoints); | ||
} else { | ||
if ((stipple == None) && linePtr->smooth->postscriptProc) { | ||
linePtr->smooth->postscriptProc(interp, canvas, | ||
linePtr->coordPtr, linePtr->numPoints, linePtr->splineSteps); | ||
} else { | ||
/* | ||
* Special hack: Postscript printers don't appear to be able | ||
* to turn a path drawn with "curveto"s into a clipping path | ||
* without exceeding resource limits, so TkMakeBezierPostscript | ||
* won't work for stippled curves. Instead, generate all of | ||
* the intermediate points here and output them into the | ||
* Postscript file with "lineto"s instead. | ||
*/ | ||
double staticPoints[2*MAX_STATIC_POINTS]; | ||
double *pointPtr; | ||
int numPoints; | ||
numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL, | ||
linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | ||
(double *) NULL); | ||
pointPtr = staticPoints; | ||
if (numPoints > MAX_STATIC_POINTS) { | ||
pointPtr = (double *) ckalloc((unsigned) | ||
(numPoints * 2 * sizeof(double))); | ||
} | ||
numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, | ||
linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | ||
pointPtr); | ||
Tk_CanvasPsPath(interp, canvas, pointPtr, numPoints); | ||
if (pointPtr != staticPoints) { | ||
ckfree((char *) pointPtr); | ||
} | ||
} | ||
} | ||
/* | ||
* Set other line-drawing parameters and stroke out the line. | ||
*/ | ||
style = "0 setlinecap\n"; | ||
if (linePtr->capStyle == CapRound) { | ||
style = "1 setlinecap\n"; | ||
} else if (linePtr->capStyle == CapProjecting) { | ||
style = "2 setlinecap\n"; | ||
} | ||
Tcl_AppendResult(interp, style, (char *) NULL); | ||
style = "0 setlinejoin\n"; | ||
if (linePtr->joinStyle == JoinRound) { | ||
style = "1 setlinejoin\n"; | ||
} else if (linePtr->joinStyle == JoinBevel) { | ||
style = "2 setlinejoin\n"; | ||
} | ||
Tcl_AppendResult(interp, style, (char *) NULL); | ||
if (Tk_CanvasPsOutline(canvas, itemPtr, | ||
&(linePtr->outline)) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
/* | ||
* Output polygons for the arrowheads, if there are any. | ||
*/ | ||
if (linePtr->firstArrowPtr != NULL) { | ||
if (stipple != None) { | ||
Tcl_AppendResult(interp, "grestore gsave\n", | ||
(char *) NULL); | ||
} | ||
if (ArrowheadPostscript(interp, canvas, linePtr, | ||
linePtr->firstArrowPtr) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} | ||
if (linePtr->lastArrowPtr != NULL) { | ||
if (stipple != None) { | ||
Tcl_AppendResult(interp, "grestore gsave\n", (char *) NULL); | ||
} | ||
if (ArrowheadPostscript(interp, canvas, linePtr, | ||
linePtr->lastArrowPtr) != TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} | ||
return TCL_OK; | ||
} | ||
/* | ||
*-------------------------------------------------------------- | ||
* | ||
* ArrowheadPostscript -- | ||
* | ||
* This procedure is called to generate Postscript for | ||
* an arrowhead for a line item. | ||
* | ||
* Results: | ||
* The return value is a standard Tcl result. If an error | ||
* occurs in generating Postscript then an error message is | ||
* left in the interp's result, replacing whatever used | ||
* to be there. If no error occurs, then Postscript for the | ||
* arrowhead is appended to the result. | ||
* | ||
* Side effects: | ||
* None. | ||
* | ||
*-------------------------------------------------------------- | ||
*/ | ||
static int | ||
ArrowheadPostscript(interp, canvas, linePtr, arrowPtr) | ||
Tcl_Interp *interp; /* Leave Postscript or error message | ||
* here. */ | ||
Tk_Canvas canvas; /* Information about overall canvas. */ | ||
LineItem *linePtr; /* Line item for which Postscript is | ||
* being generated. */ | ||
double *arrowPtr; /* Pointer to first of five points | ||
* describing arrowhead polygon. */ | ||
{ | ||
Pixmap stipple; | ||
Tk_State state = linePtr->header.state; | ||
if(state == TK_STATE_NULL) { | ||
state = ((TkCanvas *)canvas)->canvas_state; | ||
} | ||
stipple = linePtr->outline.stipple; | ||
if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) { | ||
if (linePtr->outline.activeStipple!=None) { | ||
stipple = linePtr->outline.activeStipple; | ||
} | ||
} else if (state==TK_STATE_DISABLED) { | ||
if (linePtr->outline.activeStipple!=None) { | ||
stipple = linePtr->outline.disabledStipple; | ||
} | ||
} | ||
Tk_CanvasPsPath(interp, canvas, arrowPtr, PTS_IN_ARROW); | ||
if (stipple != None) { | ||
Tcl_AppendResult(interp, "clip ", (char *) NULL); | ||
if (Tk_CanvasPsStipple(interp, canvas, stipple) | ||
!= TCL_OK) { | ||
return TCL_ERROR; | ||
} | ||
} else { | ||
Tcl_AppendResult(interp, "fill\n", (char *) NULL); | ||
} | ||
return TCL_OK; | ||
} | ||
/* $History: tkCanvLine.c $ | ||
* | ||
* ***************** Version 1 ***************** | ||
* User: Dtashley Date: 1/02/01 Time: 2:35a | ||
* Created in $/IjuScripter, IjuConsole/Source/Tk Base | ||
* Initial check-in. | ||
*/ | ||
/* End of TKCANVLINE.C */ | ||
1 | /* $Header$ */ | |
2 | ||
3 | /* | |
4 | * tkCanvLine.c -- | |
5 | * | |
6 | * This file implements line items for canvas widgets. | |
7 | * | |
8 | * Copyright (c) 1991-1994 The Regents of the University of California. | |
9 | * Copyright (c) 1994-1997 Sun Microsystems, Inc. | |
10 | * Copyright (c) 1998-1999 by Scriptics Corporation. | |
11 | * | |
12 | * See the file "license.terms" for information on usage and redistribution | |
13 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. | |
14 | * | |
15 | * RCS: @(#) $Id: tkcanvline.c,v 1.1.1.1 2001/06/13 04:56:46 dtashley Exp $ | |
16 | */ | |
17 | ||
18 | #include <stdio.h> | |
19 | #include "tkInt.h" | |
20 | #include "tkPort.h" | |
21 | #include "tkCanvas.h" | |
22 | ||
23 | /* | |
24 | * The structure below defines the record for each line item. | |
25 | */ | |
26 | ||
27 | typedef enum { | |
28 | ARROWS_NONE, ARROWS_FIRST, ARROWS_LAST, ARROWS_BOTH | |
29 | } Arrows; | |
30 | ||
31 | typedef struct LineItem { | |
32 | Tk_Item header; /* Generic stuff that's the same for all | |
33 | * types. MUST BE FIRST IN STRUCTURE. */ | |
34 | Tk_Outline outline; /* Outline structure */ | |
35 | Tk_Canvas canvas; /* Canvas containing item. Needed for | |
36 | * parsing arrow shapes. */ | |
37 | int numPoints; /* Number of points in line (always >= 0). */ | |
38 | double *coordPtr; /* Pointer to malloc-ed array containing | |
39 | * x- and y-coords of all points in line. | |
40 | * X-coords are even-valued indices, y-coords | |
41 | * are corresponding odd-valued indices. If | |
42 | * the line has arrowheads then the first | |
43 | * and last points have been adjusted to refer | |
44 | * to the necks of the arrowheads rather than | |
45 | * their tips. The actual endpoints are | |
46 | * stored in the *firstArrowPtr and | |
47 | * *lastArrowPtr, if they exist. */ | |
48 | int capStyle; /* Cap style for line. */ | |
49 | int joinStyle; /* Join style for line. */ | |
50 | GC arrowGC; /* Graphics context for drawing arrowheads. */ | |
51 | Arrows arrow; /* Indicates whether or not to draw arrowheads: | |
52 | * "none", "first", "last", or "both". */ | |
53 | float arrowShapeA; /* Distance from tip of arrowhead to center. */ | |
54 | float arrowShapeB; /* Distance from tip of arrowhead to trailing | |
55 | * point, measured along shaft. */ | |
56 | float arrowShapeC; /* Distance of trailing points from outside | |
57 | * edge of shaft. */ | |
58 | double *firstArrowPtr; /* Points to array of PTS_IN_ARROW points | |
59 | * describing polygon for arrowhead at first | |
60 | * point in line. First point of arrowhead | |
61 | * is tip. Malloc'ed. NULL means no arrowhead | |
62 | * at first point. */ | |
63 | double *lastArrowPtr; /* Points to polygon for arrowhead at last | |
64 | * point in line (PTS_IN_ARROW points, first | |
65 | * of which is tip). Malloc'ed. NULL means | |
66 | * no arrowhead at last point. */ | |
67 | Tk_SmoothMethod *smooth; /* Non-zero means draw line smoothed (i.e. | |
68 | * with Bezier splines). */ | |
69 | int splineSteps; /* Number of steps in each spline segment. */ | |
70 | } LineItem; | |
71 | ||
72 | /* | |
73 | * Number of points in an arrowHead: | |
74 | */ | |
75 | ||
76 | #define PTS_IN_ARROW 6 | |
77 | ||
78 | /* | |
79 | * Prototypes for procedures defined in this file: | |
80 | */ | |
81 | ||
82 | static int ArrowheadPostscript _ANSI_ARGS_((Tcl_Interp *interp, | |
83 | Tk_Canvas canvas, LineItem *linePtr, | |
84 | double *arrowPtr)); | |
85 | static void ComputeLineBbox _ANSI_ARGS_((Tk_Canvas canvas, | |
86 | LineItem *linePtr)); | |
87 | static int ConfigureLine _ANSI_ARGS_((Tcl_Interp *interp, | |
88 | Tk_Canvas canvas, Tk_Item *itemPtr, int argc, | |
89 | Tcl_Obj *CONST argv[], int flags)); | |
90 | static int ConfigureArrows _ANSI_ARGS_((Tk_Canvas canvas, | |
91 | LineItem *linePtr)); | |
92 | static int CreateLine _ANSI_ARGS_((Tcl_Interp *interp, | |
93 | Tk_Canvas canvas, struct Tk_Item *itemPtr, | |
94 | int argc, Tcl_Obj *CONST argv[])); | |
95 | static void DeleteLine _ANSI_ARGS_((Tk_Canvas canvas, | |
96 | Tk_Item *itemPtr, Display *display)); | |
97 | static void DisplayLine _ANSI_ARGS_((Tk_Canvas canvas, | |
98 | Tk_Item *itemPtr, Display *display, Drawable dst, | |
99 | int x, int y, int width, int height)); | |
100 | static int GetLineIndex _ANSI_ARGS_((Tcl_Interp *interp, | |
101 | Tk_Canvas canvas, Tk_Item *itemPtr, | |
102 | Tcl_Obj *obj, int *indexPtr)); | |
103 | static int LineCoords _ANSI_ARGS_((Tcl_Interp *interp, | |
104 | Tk_Canvas canvas, Tk_Item *itemPtr, | |
105 | int argc, Tcl_Obj *CONST argv[])); | |
106 | static void LineDeleteCoords _ANSI_ARGS_((Tk_Canvas canvas, | |
107 | Tk_Item *itemPtr, int first, int last)); | |
108 | static void LineInsert _ANSI_ARGS_((Tk_Canvas canvas, | |
109 | Tk_Item *itemPtr, int beforeThis, Tcl_Obj *obj)); | |
110 | static int LineToArea _ANSI_ARGS_((Tk_Canvas canvas, | |
111 | Tk_Item *itemPtr, double *rectPtr)); | |
112 | static double LineToPoint _ANSI_ARGS_((Tk_Canvas canvas, | |
113 | Tk_Item *itemPtr, double *coordPtr)); | |
114 | static int LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp, | |
115 | Tk_Canvas canvas, Tk_Item *itemPtr, int prepass)); | |
116 | static int ArrowParseProc _ANSI_ARGS_((ClientData clientData, | |
117 | Tcl_Interp *interp, Tk_Window tkwin, | |
118 | CONST char *value, char *recordPtr, int offset)); | |
119 | static char * ArrowPrintProc _ANSI_ARGS_((ClientData clientData, | |
120 | Tk_Window tkwin, char *recordPtr, int offset, | |
121 | Tcl_FreeProc **freeProcPtr)); | |
122 | static int ParseArrowShape _ANSI_ARGS_((ClientData clientData, | |
123 | Tcl_Interp *interp, Tk_Window tkwin, | |
124 | CONST char *value, char *recordPtr, int offset)); | |
125 | static char * PrintArrowShape _ANSI_ARGS_((ClientData clientData, | |
126 | Tk_Window tkwin, char *recordPtr, int offset, | |
127 | Tcl_FreeProc **freeProcPtr)); | |
128 | static void ScaleLine _ANSI_ARGS_((Tk_Canvas canvas, | |
129 | Tk_Item *itemPtr, double originX, double originY, | |
130 | double scaleX, double scaleY)); | |
131 | static void TranslateLine _ANSI_ARGS_((Tk_Canvas canvas, | |
132 | Tk_Item *itemPtr, double deltaX, double deltaY)); | |
133 | ||
134 | /* | |
135 | * Information used for parsing configuration specs. If you change any | |
136 | * of the default strings, be sure to change the corresponding default | |
137 | * values in CreateLine. | |
138 | */ | |
139 | ||
140 | static Tk_CustomOption arrowShapeOption = { | |
141 | (Tk_OptionParseProc *) ParseArrowShape, | |
142 | PrintArrowShape, (ClientData) NULL | |
143 | }; | |
144 | static Tk_CustomOption arrowOption = { | |
145 | (Tk_OptionParseProc *) ArrowParseProc, | |
146 | ArrowPrintProc, (ClientData) NULL | |
147 | }; | |
148 | static Tk_CustomOption smoothOption = { | |
149 | (Tk_OptionParseProc *) TkSmoothParseProc, | |
150 | TkSmoothPrintProc, (ClientData) NULL | |
151 | }; | |
152 | static Tk_CustomOption stateOption = { | |
153 | (Tk_OptionParseProc *) TkStateParseProc, | |
154 | TkStatePrintProc, (ClientData) 2 | |
155 | }; | |
156 | static Tk_CustomOption tagsOption = { | |
157 | (Tk_OptionParseProc *) Tk_CanvasTagsParseProc, | |
158 | Tk_CanvasTagsPrintProc, (ClientData) NULL | |
159 | }; | |
160 | static Tk_CustomOption dashOption = { | |
161 | (Tk_OptionParseProc *) TkCanvasDashParseProc, | |
162 | TkCanvasDashPrintProc, (ClientData) NULL | |
163 | }; | |
164 | static Tk_CustomOption offsetOption = { | |
165 | (Tk_OptionParseProc *) TkOffsetParseProc, | |
166 | TkOffsetPrintProc, | |
167 | (ClientData) (TK_OFFSET_RELATIVE|TK_OFFSET_INDEX) | |
168 | }; | |
169 | static Tk_CustomOption pixelOption = { | |
170 | (Tk_OptionParseProc *) TkPixelParseProc, | |
171 | TkPixelPrintProc, (ClientData) NULL | |
172 | }; | |
173 | ||
174 | static Tk_ConfigSpec configSpecs[] = { | |
175 | {TK_CONFIG_CUSTOM, "-activedash", (char *) NULL, (char *) NULL, | |
176 | (char *) NULL, Tk_Offset(LineItem, outline.activeDash), | |
177 | TK_CONFIG_NULL_OK, &dashOption}, | |
178 | {TK_CONFIG_COLOR, "-activefill", (char *) NULL, (char *) NULL, | |
179 | (char *) NULL, Tk_Offset(LineItem, outline.activeColor), | |
180 | TK_CONFIG_NULL_OK}, | |
181 | {TK_CONFIG_BITMAP, "-activestipple", (char *) NULL, (char *) NULL, | |
182 | (char *) NULL, Tk_Offset(LineItem, outline.activeStipple), | |
183 | TK_CONFIG_NULL_OK}, | |
184 | {TK_CONFIG_CUSTOM, "-activewidth", (char *) NULL, (char *) NULL, | |
185 | "0.0", Tk_Offset(LineItem, outline.activeWidth), | |
186 | TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, | |
187 | {TK_CONFIG_CUSTOM, "-arrow", (char *) NULL, (char *) NULL, | |
188 | "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT, &arrowOption}, | |
189 | {TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL, | |
190 | "8 10 3", Tk_Offset(LineItem, arrowShapeA), | |
191 | TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption}, | |
192 | {TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL, | |
193 | "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT}, | |
194 | {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL, | |
195 | "black", Tk_Offset(LineItem, outline.color), TK_CONFIG_NULL_OK}, | |
196 | {TK_CONFIG_CUSTOM, "-dash", (char *) NULL, (char *) NULL, | |
197 | (char *) NULL, Tk_Offset(LineItem, outline.dash), | |
198 | TK_CONFIG_NULL_OK, &dashOption}, | |
199 | {TK_CONFIG_PIXELS, "-dashoffset", (char *) NULL, (char *) NULL, | |
200 | "0", Tk_Offset(LineItem, outline.offset), | |
201 | TK_CONFIG_DONT_SET_DEFAULT}, | |
202 | {TK_CONFIG_CUSTOM, "-disableddash", (char *) NULL, (char *) NULL, | |
203 | (char *) NULL, Tk_Offset(LineItem, outline.disabledDash), | |
204 | TK_CONFIG_NULL_OK, &dashOption}, | |
205 | {TK_CONFIG_COLOR, "-disabledfill", (char *) NULL, (char *) NULL, | |
206 | (char *) NULL, Tk_Offset(LineItem, outline.disabledColor), | |
207 | TK_CONFIG_NULL_OK}, | |
208 | {TK_CONFIG_BITMAP, "-disabledstipple", (char *) NULL, (char *) NULL, | |
209 | (char *) NULL, Tk_Offset(LineItem, outline.disabledStipple), | |
210 | TK_CONFIG_NULL_OK}, | |
211 | {TK_CONFIG_CUSTOM, "-disabledwidth", (char *) NULL, (char *) NULL, | |
212 | "0.0", Tk_Offset(LineItem, outline.disabledWidth), | |
213 | TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, | |
214 | {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL, | |
215 | "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT}, | |
216 | {TK_CONFIG_CUSTOM, "-offset", (char *) NULL, (char *) NULL, | |
217 | "0,0", Tk_Offset(LineItem, outline.tsoffset), | |
218 | TK_CONFIG_DONT_SET_DEFAULT, &offsetOption}, | |
219 | {TK_CONFIG_CUSTOM, "-smooth", (char *) NULL, (char *) NULL, | |
220 | "0", Tk_Offset(LineItem, smooth), | |
221 | TK_CONFIG_DONT_SET_DEFAULT, &smoothOption}, | |
222 | {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL, | |
223 | "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT}, | |
224 | {TK_CONFIG_CUSTOM, "-state", (char *) NULL, (char *) NULL, | |
225 | (char *) NULL, Tk_Offset(Tk_Item, state), TK_CONFIG_NULL_OK, | |
226 | &stateOption}, | |
227 | {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL, | |
228 | (char *) NULL, Tk_Offset(LineItem, outline.stipple), | |
229 | TK_CONFIG_NULL_OK}, | |
230 | {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL, | |
231 | (char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption}, | |
232 | {TK_CONFIG_CUSTOM, "-width", (char *) NULL, (char *) NULL, | |
233 | "1.0", Tk_Offset(LineItem, outline.width), | |
234 | TK_CONFIG_DONT_SET_DEFAULT, &pixelOption}, | |
235 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
236 | (char *) NULL, 0, 0} | |
237 | }; | |
238 | ||
239 | /* | |
240 | * The structures below defines the line item type by means | |
241 | * of procedures that can be invoked by generic item code. | |
242 | */ | |
243 | ||
244 | Tk_ItemType tkLineType = { | |
245 | "line", /* name */ | |
246 | sizeof(LineItem), /* itemSize */ | |
247 | CreateLine, /* createProc */ | |
248 | configSpecs, /* configSpecs */ | |
249 | ConfigureLine, /* configureProc */ | |
250 | LineCoords, /* coordProc */ | |
251 | DeleteLine, /* deleteProc */ | |
252 | DisplayLine, /* displayProc */ | |
253 | TK_CONFIG_OBJS, /* flags */ | |
254 | LineToPoint, /* pointProc */ | |
255 | LineToArea, /* areaProc */ | |
256 | LineToPostscript, /* postscriptProc */ | |
257 | ScaleLine, /* scaleProc */ | |
258 | TranslateLine, /* translateProc */ | |
259 | (Tk_ItemIndexProc *) GetLineIndex, /* indexProc */ | |
260 | (Tk_ItemCursorProc *) NULL, /* icursorProc */ | |
261 | (Tk_ItemSelectionProc *) NULL, /* selectionProc */ | |
262 | (Tk_ItemInsertProc *) LineInsert, /* insertProc */ | |
263 | LineDeleteCoords, /* dTextProc */ | |
264 | (Tk_ItemType *) NULL, /* nextPtr */ | |
265 | }; | |
266 | ||
267 | /* | |
268 | * The definition below determines how large are static arrays | |
269 | * used to hold spline points (splines larger than this have to | |
270 | * have their arrays malloc-ed). | |
271 | */ | |
272 | ||
273 | #define MAX_STATIC_POINTS 200 | |
274 | ||
275 | /* | |
276 | *-------------------------------------------------------------- | |
277 | * | |
278 | * CreateLine -- | |
279 | * | |
280 | * This procedure is invoked to create a new line item in | |
281 | * a canvas. | |
282 | * | |
283 | * Results: | |
284 | * A standard Tcl return value. If an error occurred in | |
285 | * creating the item, then an error message is left in | |
286 | * the interp's result; in this case itemPtr is left uninitialized, | |
287 | * so it can be safely freed by the caller. | |
288 | * | |
289 | * Side effects: | |
290 | * A new line item is created. | |
291 | * | |
292 | *-------------------------------------------------------------- | |
293 | */ | |
294 | ||
295 | static int | |
296 | CreateLine(interp, canvas, itemPtr, argc, argv) | |
297 | Tcl_Interp *interp; /* Interpreter for error reporting. */ | |
298 | Tk_Canvas canvas; /* Canvas to hold new item. */ | |
299 | Tk_Item *itemPtr; /* Record to hold new item; header | |
300 | * has been initialized by caller. */ | |
301 | int argc; /* Number of arguments in argv. */ | |
302 | Tcl_Obj *CONST argv[]; /* Arguments describing line. */ | |
303 | { | |
304 | LineItem *linePtr = (LineItem *) itemPtr; | |
305 | int i; | |
306 | ||
307 | /* | |
308 | * Carry out initialization that is needed to set defaults and to | |
309 | * allow proper cleanup after errors during the the remainder of | |
310 | * this procedure. | |
311 | */ | |
312 | ||
313 | Tk_CreateOutline(&(linePtr->outline)); | |
314 | linePtr->canvas = canvas; | |
315 | linePtr->numPoints = 0; | |
316 | linePtr->coordPtr = NULL; | |
317 | linePtr->capStyle = CapButt; | |
318 | linePtr->joinStyle = JoinRound; | |
319 | linePtr->arrowGC = None; | |
320 | linePtr->arrow = ARROWS_NONE; | |
321 | linePtr->arrowShapeA = (float)8.0; | |
322 | linePtr->arrowShapeB = (float)10.0; | |
323 | linePtr->arrowShapeC = (float)3.0; | |
324 | linePtr->firstArrowPtr = NULL; | |
325 | linePtr->lastArrowPtr = NULL; | |
326 | linePtr->smooth = (Tk_SmoothMethod *) NULL; | |
327 | linePtr->splineSteps = 12; | |
328 | ||
329 | /* | |
330 | * Count the number of points and then parse them into a point | |
331 | * array. Leading arguments are assumed to be points if they | |
332 | * start with a digit or a minus sign followed by a digit. | |
333 | */ | |
334 | ||
335 | for (i = 0; i < argc; i++) { | |
336 | char *arg = Tcl_GetStringFromObj(argv[i], NULL); | |
337 | if ((arg[0] == '-') && (arg[1] >= 'a') | |
338 | && (arg[1] <= 'z')) { | |
339 | break; | |
340 | } | |
341 | } | |
342 | if (i && (LineCoords(interp, canvas, itemPtr, i, argv) != TCL_OK)) { | |
343 | goto error; | |
344 | } | |
345 | if (ConfigureLine(interp, canvas, itemPtr, argc-i, argv+i, 0) == TCL_OK) { | |
346 | return TCL_OK; | |
347 | } | |
348 | ||
349 | error: | |
350 | DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas))); | |
351 | return TCL_ERROR; | |
352 | } | |
353 | ||
354 | /* | |
355 | *-------------------------------------------------------------- | |
356 | * | |
357 | * LineCoords -- | |
358 | * | |
359 | * This procedure is invoked to process the "coords" widget | |
360 | * command on lines. See the user documentation for details | |
361 | * on what it does. | |
362 | * | |
363 | * Results: | |
364 | * Returns TCL_OK or TCL_ERROR, and sets the interp's result. | |
365 | * | |
366 | * Side effects: | |
367 | * The coordinates for the given item may be changed. | |
368 | * | |
369 | *-------------------------------------------------------------- | |
370 | */ | |
371 | ||
372 | static int | |
373 | LineCoords(interp, canvas, itemPtr, argc, argv) | |
374 | Tcl_Interp *interp; /* Used for error reporting. */ | |
375 | Tk_Canvas canvas; /* Canvas containing item. */ | |
376 | Tk_Item *itemPtr; /* Item whose coordinates are to be | |
377 | * read or modified. */ | |
378 | int argc; /* Number of coordinates supplied in | |
379 | * argv. */ | |
380 | Tcl_Obj *CONST argv[]; /* Array of coordinates: x1, y1, | |
381 | * x2, y2, ... */ | |
382 | { | |
383 | LineItem *linePtr = (LineItem *) itemPtr; | |
384 | int i, numPoints; | |
385 | double *coordPtr; | |
386 | ||
387 | if (argc == 0) { | |
388 | int numCoords; | |
389 | Tcl_Obj *subobj, *obj = Tcl_NewObj(); | |
390 | ||
391 | numCoords = 2*linePtr->numPoints; | |
392 | if (linePtr->firstArrowPtr != NULL) { | |
393 | coordPtr = linePtr->firstArrowPtr; | |
394 | } else { | |
395 | coordPtr = linePtr->coordPtr; | |
396 | } | |
397 | for (i = 0; i < numCoords; i++, coordPtr++) { | |
398 | if (i == 2) { | |
399 | coordPtr = linePtr->coordPtr+2; | |
400 | } | |
401 | if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) { | |
402 | coordPtr = linePtr->lastArrowPtr; | |
403 | } | |
404 | subobj = Tcl_NewDoubleObj(*coordPtr); | |
405 | Tcl_ListObjAppendElement(interp, obj, subobj); | |
406 | } | |
407 | Tcl_SetObjResult(interp, obj); | |
408 | return TCL_OK; | |
409 | } | |
410 | if (argc == 1) { | |
411 | if (Tcl_ListObjGetElements(interp, argv[0], &argc, | |
412 | (Tcl_Obj ***) &argv) != TCL_OK) { | |
413 | return TCL_ERROR; | |
414 | } | |
415 | } | |
416 | if (argc & 1) { | |
417 | Tcl_AppendResult(interp, | |
418 | "odd number of coordinates specified for line", | |
419 | (char *) NULL); | |
420 | return TCL_ERROR; | |
421 | } else if (argc < 4) { | |
422 | Tcl_AppendResult(interp, | |
423 | "too few coordinates specified for line", | |
424 | (char *) NULL); | |
425 | return TCL_ERROR; | |
426 | } else { | |
427 | numPoints = argc/2; | |
428 | if (linePtr->numPoints != numPoints) { | |
429 | coordPtr = (double *) ckalloc((unsigned) | |
430 | (sizeof(double) * argc)); | |
431 | if (linePtr->coordPtr != NULL) { | |
432 | ckfree((char *) linePtr->coordPtr); | |
433 | } | |
434 | linePtr->coordPtr = coordPtr; | |
435 | linePtr->numPoints = numPoints; | |
436 | } | |
437 | coordPtr = linePtr->coordPtr; | |
438 | for (i = 0; i <argc; i++) { | |
439 | if (Tk_CanvasGetCoordFromObj(interp, canvas, argv[i], | |
440 | coordPtr++) != TCL_OK) { | |
441 | return TCL_ERROR; | |
442 | } | |
443 | } | |
444 | ||
445 | /* | |
446 | * Update arrowheads by throwing away any existing arrow-head | |
447 | * information and calling ConfigureArrows to recompute it. | |
448 | */ | |
449 | ||
450 | if (linePtr->firstArrowPtr != NULL) { | |
451 | ckfree((char *) linePtr->firstArrowPtr); | |
452 | linePtr->firstArrowPtr = NULL; | |
453 | } | |
454 | if (linePtr->lastArrowPtr != NULL) { | |
455 | ckfree((char *) linePtr->lastArrowPtr); | |
456 | linePtr->lastArrowPtr = NULL; | |
457 | } | |
458 | if (linePtr->arrow != ARROWS_NONE) { | |
459 | ConfigureArrows(canvas, linePtr); | |
460 | } | |
461 | ComputeLineBbox(canvas, linePtr); | |
462 | } | |
463 | return TCL_OK; | |
464 | } | |
465 | ||
466 | /* | |
467 | *-------------------------------------------------------------- | |
468 | * | |
469 | * ConfigureLine -- | |
470 | * | |
471 | * This procedure is invoked to configure various aspects | |
472 | * of a line item such as its background color. | |
473 | * | |
474 | * Results: | |
475 | * A standard Tcl result code. If an error occurs, then | |
476 | * an error message is left in the interp's result. | |
477 | * | |
478 | * Side effects: | |
479 | * Configuration information, such as colors and stipple | |
480 | * patterns, may be set for itemPtr. | |
481 | * | |
482 | *-------------------------------------------------------------- | |
483 | */ | |
484 | ||
485 | static int | |
486 | ConfigureLine(interp, canvas, itemPtr, argc, argv, flags) | |
487 | Tcl_Interp *interp; /* Used for error reporting. */ | |
488 | Tk_Canvas canvas; /* Canvas containing itemPtr. */ | |
489 | Tk_Item *itemPtr; /* Line item to reconfigure. */ | |
490 | int argc; /* Number of elements in argv. */ | |
491 | Tcl_Obj *CONST argv[]; /* Arguments describing things to configure. */ | |
492 | int flags; /* Flags to pass to Tk_ConfigureWidget. */ | |
493 | { | |
494 | LineItem *linePtr = (LineItem *) itemPtr; | |
495 | XGCValues gcValues; | |
496 | GC newGC, arrowGC; | |
497 | unsigned long mask; | |
498 | Tk_Window tkwin; | |
499 | Tk_State state; | |
500 | ||
501 | tkwin = Tk_CanvasTkwin(canvas); | |
502 | if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, (char **) argv, | |
503 | (char *) linePtr, flags|TK_CONFIG_OBJS) != TCL_OK) { | |
504 | return TCL_ERROR; | |
505 | } | |
506 | ||
507 | /* | |
508 | * A few of the options require additional processing, such as | |
509 | * graphics contexts. | |
510 | */ | |
511 | ||
512 | state = itemPtr->state; | |
513 | ||
514 | if(state == TK_STATE_NULL) { | |
515 | state = ((TkCanvas *)canvas)->canvas_state; | |
516 | } | |
517 | ||
518 | if (linePtr->outline.activeWidth > linePtr->outline.width || | |
519 | linePtr->outline.activeDash.number != 0 || | |
520 | linePtr->outline.activeColor != NULL || | |
521 | linePtr->outline.activeStipple != None) { | |
522 | itemPtr->redraw_flags |= TK_ITEM_STATE_DEPENDANT; | |
523 | } else { | |
524 | itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT; | |
525 | } | |
526 | mask = Tk_ConfigOutlineGC(&gcValues, canvas, itemPtr, | |
527 | &(linePtr->outline)); | |
528 | if (mask) { | |
529 | if (linePtr->arrow == ARROWS_NONE) { | |
530 | gcValues.cap_style = linePtr->capStyle; | |
531 | mask |= GCCapStyle; | |
532 | } | |
533 | gcValues.join_style = linePtr->joinStyle; | |
534 | mask |= GCJoinStyle; | |
535 | newGC = Tk_GetGC(tkwin, mask, &gcValues); | |
536 | gcValues.line_width = 0; | |
537 | arrowGC = Tk_GetGC(tkwin, mask, &gcValues); | |
538 | } else { | |
539 | newGC = arrowGC = None; | |
540 | } | |
541 | if (linePtr->outline.gc != None) { | |
542 | Tk_FreeGC(Tk_Display(tkwin), linePtr->outline.gc); | |
543 | } | |
544 | if (linePtr->arrowGC != None) { | |
545 | Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC); | |
546 | } | |
547 | linePtr->outline.gc = newGC; | |
548 | linePtr->arrowGC = arrowGC; | |
549 | ||
550 | /* | |
551 | * Keep spline parameters within reasonable limits. | |
552 | */ | |
553 | ||
554 | if (linePtr->splineSteps < 1) { | |
555 | linePtr->splineSteps = 1; | |
556 | } else if (linePtr->splineSteps > 100) { | |
557 | linePtr->splineSteps = 100; | |
558 | } | |
559 | ||
560 | if ((!linePtr->numPoints) || (state==TK_STATE_HIDDEN)) { | |
561 | ComputeLineBbox(canvas, linePtr); | |
562 | return TCL_OK; | |
563 | } | |
564 | ||
565 | /* | |
566 | * Setup arrowheads, if needed. If arrowheads are turned off, | |
567 | * restore the line's endpoints (they were shortened when the | |
568 | * arrowheads were added). | |
569 | */ | |
570 | ||
571 | if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != ARROWS_FIRST) | |
572 | && (linePtr->arrow != ARROWS_BOTH)) { | |
573 | linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; | |
574 | linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; | |
575 | ckfree((char *) linePtr->firstArrowPtr); | |
576 | linePtr->firstArrowPtr = NULL; | |
577 | } | |
578 | if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != ARROWS_LAST) | |
579 | && (linePtr->arrow != ARROWS_BOTH)) { | |
580 | int i; | |
581 | ||
582 | i = 2*(linePtr->numPoints-1); | |
583 | linePtr->coordPtr[i] = linePtr->lastArrowPtr[0]; | |
584 | linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1]; | |
585 | ckfree((char *) linePtr->lastArrowPtr); | |
586 | linePtr->lastArrowPtr = NULL; | |
587 | } | |
588 | if (linePtr->arrow != ARROWS_NONE) { | |
589 | ConfigureArrows(canvas, linePtr); | |
590 | } | |
591 | ||
592 | /* | |
593 | * Recompute bounding box for line. | |
594 | */ | |
595 | ||
596 | ComputeLineBbox(canvas, linePtr); | |
597 | ||
598 | return TCL_OK; | |
599 | } | |
600 | ||
601 | /* | |
602 | *-------------------------------------------------------------- | |
603 | * | |
604 | * DeleteLine -- | |
605 | * | |
606 | * This procedure is called to clean up the data structure | |
607 | * associated with a line item. | |
608 | * | |
609 | * Results: | |
610 | * None. | |
611 | * | |
612 | * Side effects: | |
613 | * Resources associated with itemPtr are released. | |
614 | * | |
615 | *-------------------------------------------------------------- | |
616 | */ | |
617 | ||
618 | static void | |
619 | DeleteLine(canvas, itemPtr, display) | |
620 | Tk_Canvas canvas; /* Info about overall canvas widget. */ | |
621 | Tk_Item *itemPtr; /* Item that is being deleted. */ | |
622 | Display *display; /* Display containing window for | |
623 | * canvas. */ | |
624 | { | |
625 | LineItem *linePtr = (LineItem *) itemPtr; | |
626 | ||
627 | Tk_DeleteOutline(display, &(linePtr->outline)); | |
628 | if (linePtr->coordPtr != NULL) { | |
629 | ckfree((char *) linePtr->coordPtr); | |
630 | } | |
631 | if (linePtr->arrowGC != None) { | |
632 | Tk_FreeGC(display, linePtr->arrowGC); | |
633 | } | |
634 | if (linePtr->firstArrowPtr != NULL) { | |
635 | ckfree((char *) linePtr->firstArrowPtr); | |
636 | } | |
637 | if (linePtr->lastArrowPtr != NULL) { | |
638 | ckfree((char *) linePtr->lastArrowPtr); | |
639 | } | |
640 | } | |
641 | ||
642 | /* | |
643 | *-------------------------------------------------------------- | |
644 | * | |
645 | * ComputeLineBbox -- | |
646 | * | |
647 | * This procedure is invoked to compute the bounding box of | |
648 | * all the pixels that may be drawn as part of a line. | |
649 | * | |
650 | * Results: | |
651 | * None. | |
652 | * | |
653 | * Side effects: | |
654 | * The fields x1, y1, x2, and y2 are updated in the header | |
655 | * for itemPtr. | |
656 | * | |
657 | *-------------------------------------------------------------- | |
658 | */ | |
659 | ||
660 | static void | |
661 | ComputeLineBbox(canvas, linePtr) | |
662 | Tk_Canvas canvas; /* Canvas that contains item. */ | |
663 | LineItem *linePtr; /* Item whose bbos is to be | |
664 | * recomputed. */ | |
665 | { | |
666 | double *coordPtr; | |
667 | int i, intWidth; | |
668 | double width; | |
669 | Tk_State state = linePtr->header.state; | |
670 | Tk_TSOffset *tsoffset; | |
671 | ||
672 | if(state == TK_STATE_NULL) { | |
673 | state = ((TkCanvas *)canvas)->canvas_state; | |
674 | } | |
675 | ||
676 | if (!(linePtr->numPoints) || (state==TK_STATE_HIDDEN)) { | |
677 | linePtr->header.x1 = -1; | |
678 | linePtr->header.x2 = -1; | |
679 | linePtr->header.y1 = -1; | |
680 | linePtr->header.y2 = -1; | |
681 | return; | |
682 | } | |
683 | ||
684 | width = linePtr->outline.width; | |
685 | if (((TkCanvas *)canvas)->currentItemPtr == (Tk_Item *)linePtr) { | |
686 | if (linePtr->outline.activeWidth>width) { | |
687 | width = linePtr->outline.activeWidth; | |
688 | } | |
689 | } else if (state==TK_STATE_DISABLED) { | |
690 | if (linePtr->outline.disabledWidth>0) { | |
691 | width = linePtr->outline.disabledWidth; | |
692 | } | |
693 | } | |
694 | ||
695 | coordPtr = linePtr->coordPtr; | |
696 | linePtr->header.x1 = linePtr->header.x2 = (int) *coordPtr; | |
697 | linePtr->header.y1 = linePtr->header.y2 = (int) coordPtr[1]; | |
698 | ||
699 | /* | |
700 | * Compute the bounding box of all the points in the line, | |
701 | * then expand in all directions by the line's width to take | |
702 | * care of butting or rounded corners and projecting or | |
703 | * rounded caps. This expansion is an overestimate (worst-case | |
704 | * is square root of two over two) but it's simple. Don't do | |
705 | * anything special for curves. This causes an additional | |
706 | * overestimate in the bounding box, but is faster. | |
707 | */ | |
708 | ||
709 | for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints; | |
710 | i++, coordPtr += 2) { | |
711 | TkIncludePoint((Tk_Item *) linePtr, coordPtr); | |
712 | } | |
713 | width = linePtr->outline.width; | |
714 | if (width < 1.0) { | |
715 | width = 1.0; | |
716 | } | |
717 | if (linePtr->arrow != ARROWS_NONE) { | |
718 | if (linePtr->arrow != ARROWS_LAST) { | |
719 | TkIncludePoint((Tk_Item *) linePtr, linePtr->firstArrowPtr); | |
720 | } | |
721 | if (linePtr->arrow != ARROWS_FIRST) { | |
722 | TkIncludePoint((Tk_Item *) linePtr, linePtr->lastArrowPtr); | |
723 | } | |
724 | } | |
725 | ||
726 | tsoffset = &linePtr->outline.tsoffset; | |
727 | if (tsoffset->flags & TK_OFFSET_INDEX) { | |
728 | double *coordPtr = linePtr->coordPtr + (tsoffset->flags & ~TK_OFFSET_INDEX); | |
729 | if (tsoffset->flags <= 0) { | |
730 | coordPtr = linePtr->coordPtr; | |
731 | if ((linePtr->arrow == ARROWS_FIRST) || (linePtr->arrow == ARROWS_BOTH)) { | |
732 | coordPtr = linePtr->firstArrowPtr; | |
733 | } | |
734 | } | |
735 | if (tsoffset->flags > (linePtr->numPoints * 2)) { | |
736 | coordPtr = linePtr->coordPtr + (linePtr->numPoints * 2); | |
737 | if ((linePtr->arrow == ARROWS_LAST) || (linePtr->arrow == ARROWS_BOTH)) { | |
738 | coordPtr = linePtr->lastArrowPtr; | |
739 | } | |
740 | } | |
741 | tsoffset->xoffset = (int) (coordPtr[0] + 0.5); | |
742 | tsoffset->yoffset = (int) (coordPtr[1] + 0.5); | |
743 | } else { | |
744 | if (tsoffset->flags & TK_OFFSET_LEFT) { | |
745 | tsoffset->xoffset = linePtr->header.x1; | |
746 | } else if (tsoffset->flags & TK_OFFSET_CENTER) { | |
747 | tsoffset->xoffset = (linePtr->header.x1 + linePtr->header.x2)/2; | |
748 | } else if (tsoffset->flags & TK_OFFSET_RIGHT) { | |
749 | tsoffset->xoffset = linePtr->header.x2; | |
750 | } | |
751 | if (tsoffset->flags & TK_OFFSET_TOP) { | |
752 | tsoffset->yoffset = linePtr->header.y1; | |
753 | } else if (tsoffset->flags & TK_OFFSET_MIDDLE) { | |
754 | tsoffset->yoffset = (linePtr->header.y1 + linePtr->header.y2)/2; | |
755 | } else if (tsoffset->flags & TK_OFFSET_BOTTOM) { | |
756 | tsoffset->yoffset = linePtr->header.y2; | |
757 | } | |
758 | } | |
759 | ||
760 | intWidth = (int) (width + 0.5); | |
761 | linePtr->header.x1 -= intWidth; | |
762 | linePtr->header.x2 += intWidth; | |
763 | linePtr->header.y1 -= intWidth; | |
764 | linePtr->header.y2 += intWidth; | |
765 | ||
766 | if (linePtr->numPoints==1) { | |
767 | linePtr->header.x1 -= 1; | |
768 | linePtr->header.x2 += 1; | |
769 | linePtr->header.y1 -= 1; | |
770 | linePtr->header.y2 += 1; | |
771 | return; | |
772 | } | |
773 | ||
774 | /* | |
775 | * For mitered lines, make a second pass through all the points. | |
776 | * Compute the locations of the two miter vertex points and add | |
777 | * those into the bounding box. | |
778 | */ | |
779 | ||
780 | if (linePtr->joinStyle == JoinMiter) { | |
781 | for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3; | |
782 | i--, coordPtr += 2) { | |
783 | double miter[4]; | |
784 | int j; | |
785 | ||
786 | if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, | |
787 | width, miter, miter+2)) { | |
788 | for (j = 0; j < 4; j += 2) { | |
789 | TkIncludePoint((Tk_Item *) linePtr, miter+j); | |
790 | } | |
791 | } | |
792 | } | |
793 | } | |
794 | ||
795 | /* | |
796 | * Add in the sizes of arrowheads, if any. | |
797 | */ | |
798 | ||
799 | if (linePtr->arrow != ARROWS_NONE) { | |
800 | if (linePtr->arrow != ARROWS_LAST) { | |
801 | for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | |
802 | i++, coordPtr += 2) { | |
803 | TkIncludePoint((Tk_Item *) linePtr, coordPtr); | |
804 | } | |
805 | } | |
806 | if (linePtr->arrow != ARROWS_FIRST) { | |
807 | for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | |
808 | i++, coordPtr += 2) { | |
809 | TkIncludePoint((Tk_Item *) linePtr, coordPtr); | |
810 | } | |
811 | } | |
812 | } | |
813 | ||
814 | /* | |
815 | * Add one more pixel of fudge factor just to be safe (e.g. | |
816 | * X may round differently than we do). | |
817 | */ | |
818 | ||
819 | linePtr->header.x1 -= 1; | |
820 | linePtr->header.x2 += 1; | |
821 | linePtr->header.y1 -= 1; | |
822 | linePtr->header.y2 += 1; | |
823 | } | |
824 | ||
825 | /* | |
826 | *-------------------------------------------------------------- | |
827 | * | |
828 | * DisplayLine -- | |
829 | * | |
830 | * This procedure is invoked to draw a line item in a given | |
831 | * drawable. | |
832 | * | |
833 | * Results: | |
834 | * None. | |
835 | * | |
836 | * Side effects: | |
837 | * ItemPtr is drawn in drawable using the transformation | |
838 | * information in canvas. | |
839 | * | |
840 | *-------------------------------------------------------------- | |
841 | */ | |
842 | ||
843 | static void | |
844 | DisplayLine(canvas, itemPtr, display, drawable, x, y, width, height) | |
845 | Tk_Canvas canvas; /* Canvas that contains item. */ | |
846 | Tk_Item *itemPtr; /* Item to be displayed. */ | |
847 | Display *display; /* Display on which to draw item. */ | |
848 | Drawable drawable; /* Pixmap or window in which to draw | |
849 | * item. */ | |
850 | int x, y, width, height; /* Describes region of canvas that | |
851 | * must be redisplayed (not used). */ | |
852 | { | |
853 | LineItem *linePtr = (LineItem *) itemPtr; | |
854 | XPoint staticPoints[MAX_STATIC_POINTS]; | |
855 | XPoint *pointPtr; | |
856 | XPoint *pPtr; | |
857 | double *coordPtr, linewidth; | |
858 | int i, numPoints; | |
859 | Tk_State state = itemPtr->state; | |
860 | Pixmap stipple = linePtr->outline.stipple; | |
861 | ||
862 | if ((!linePtr->numPoints)||(linePtr->outline.gc==None)) { | |
863 | return; | |
864 | } | |
865 | ||
866 | if (state == TK_STATE_NULL) { | |
867 | state = ((TkCanvas *)canvas)->canvas_state; | |
868 | } | |
869 | linewidth = linePtr->outline.width; | |
870 | if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | |
871 | if (linePtr->outline.activeStipple!=None) { | |
872 | stipple = linePtr->outline.activeStipple; | |
873 | } | |
874 | if (linePtr->outline.activeWidth>linewidth) { | |
875 | linewidth = linePtr->outline.activeWidth; | |
876 | } | |
877 | } else if (state==TK_STATE_DISABLED) { | |
878 | if (linePtr->outline.disabledStipple!=None) { | |
879 | stipple = linePtr->outline.disabledStipple; | |
880 | } | |
881 | if (linePtr->outline.disabledWidth>linewidth) { | |
882 | linewidth = linePtr->outline.activeWidth; | |
883 | } | |
884 | } | |
885 | /* | |
886 | * Build up an array of points in screen coordinates. Use a | |
887 | * static array unless the line has an enormous number of points; | |
888 | * in this case, dynamically allocate an array. For smoothed lines, | |
889 | * generate the curve points on each redisplay. | |
890 | */ | |
891 | ||
892 | if ((linePtr->smooth) && (linePtr->numPoints > 2)) { | |
893 | numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL, | |
894 | linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | |
895 | (double *) NULL); | |
896 | } else { | |
897 | numPoints = linePtr->numPoints; | |
898 | } | |
899 | ||
900 | if (numPoints <= MAX_STATIC_POINTS) { | |
901 | pointPtr = staticPoints; | |
902 | } else { | |
903 | pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint))); | |
904 | } | |
905 | ||
906 | if ((linePtr->smooth) && (linePtr->numPoints > 2)) { | |
907 | numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, | |
908 | linePtr->numPoints, linePtr->splineSteps, pointPtr, | |
909 | (double *) NULL); | |
910 | } else { | |
911 | for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr; | |
912 | i < linePtr->numPoints; i += 1, coordPtr += 2, pPtr++) { | |
913 | Tk_CanvasDrawableCoords(canvas, coordPtr[0], coordPtr[1], | |
914 | &pPtr->x, &pPtr->y); | |
915 | } | |
916 | } | |
917 | ||
918 | /* | |
919 | * Display line, the free up line storage if it was dynamically | |
920 | * allocated. If we're stippling, then modify the stipple offset | |
921 | * in the GC. Be sure to reset the offset when done, since the | |
922 | * GC is supposed to be read-only. | |
923 | */ | |
924 | ||
925 | if (Tk_ChangeOutlineGC(canvas, itemPtr, &(linePtr->outline))) { | |
926 | Tk_CanvasSetOffset(canvas, linePtr->arrowGC, &linePtr->outline.tsoffset); | |
927 | } | |
928 | if (numPoints>1) { | |
929 | XDrawLines(display, drawable, linePtr->outline.gc, pointPtr, numPoints, | |
930 | CoordModeOrigin); | |
931 | } else { | |
932 | int intwidth = (int) (linewidth + 0.5); | |
933 | if (intwidth<1) { | |
934 | intwidth=1; | |
935 | } | |
936 | XFillArc(display, drawable, linePtr->outline.gc, | |
937 | pointPtr->x - intwidth/2, pointPtr->y - intwidth/2, | |
938 | (unsigned int)intwidth+1, (unsigned int)intwidth+1, 0, 64*360); | |
939 | } | |
940 | if (pointPtr != staticPoints) { | |
941 | ckfree((char *) pointPtr); | |
942 | } | |
943 | ||
944 | /* | |
945 | * Display arrowheads, if they are wanted. | |
946 | */ | |
947 | ||
948 | if (linePtr->firstArrowPtr != NULL) { | |
949 | TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW, | |
950 | display, drawable, linePtr->arrowGC, NULL); | |
951 | } | |
952 | if (linePtr->lastArrowPtr != NULL) { | |
953 | TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW, | |
954 | display, drawable, linePtr->arrowGC, NULL); | |
955 | } | |
956 | if (Tk_ResetOutlineGC(canvas, itemPtr, &(linePtr->outline))) { | |
957 | XSetTSOrigin(display, linePtr->arrowGC, 0, 0); | |
958 | } | |
959 | } | |
960 | ||
961 | /* | |
962 | *-------------------------------------------------------------- | |
963 | * | |
964 | * LineInsert -- | |
965 | * | |
966 | * Insert coords into a line item at a given index. | |
967 | * | |
968 | * Results: | |
969 | * None. | |
970 | * | |
971 | * Side effects: | |
972 | * The coords in the given item is modified. | |
973 | * | |
974 | *-------------------------------------------------------------- | |
975 | */ | |
976 | ||
977 | static void | |
978 | LineInsert(canvas, itemPtr, beforeThis, obj) | |
979 | Tk_Canvas canvas; /* Canvas containing text item. */ | |
980 | Tk_Item *itemPtr; /* Line item to be modified. */ | |
981 | int beforeThis; /* Index before which new coordinates | |
982 | * are to be inserted. */ | |
983 | Tcl_Obj *obj; /* New coordinates to be inserted. */ | |
984 | { | |
985 | LineItem *linePtr = (LineItem *) itemPtr; | |
986 | int length, argc, i; | |
987 | double *new, *coordPtr; | |
988 | Tk_State state = itemPtr->state; | |
989 | Tcl_Obj **objv; | |
990 | ||
991 | if(state == TK_STATE_NULL) { | |
992 | state = ((TkCanvas *)canvas)->canvas_state; | |
993 | } | |
994 | ||
995 | if (!obj || (Tcl_ListObjGetElements((Tcl_Interp *) NULL, obj, &argc, &objv) != TCL_OK) | |
996 | || !argc || argc&1) { | |
997 | return; | |
998 | } | |
999 | length = 2*linePtr->numPoints; | |
1000 | if (beforeThis < 0) { | |
1001 | beforeThis = 0; | |
1002 | } | |
1003 | if (beforeThis > length) { | |
1004 | beforeThis = length; | |
1005 | } | |
1006 | if (linePtr->firstArrowPtr != NULL) { | |
1007 | linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; | |
1008 | linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; | |
1009 | } | |
1010 | if (linePtr->lastArrowPtr != NULL) { | |
1011 | linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0]; | |
1012 | linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1]; | |
1013 | } | |
1014 | new = (double *) ckalloc((unsigned)(sizeof(double) * (length + argc))); | |
1015 | for(i=0; i<beforeThis; i++) { | |
1016 | new[i] = linePtr->coordPtr[i]; | |
1017 | } | |
1018 | for(i=0; i<argc; i++) { | |
1019 | if (Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,objv[i], | |
1020 | new+(i+beforeThis))!=TCL_OK) { | |
1021 | Tcl_ResetResult(((TkCanvas *)canvas)->interp); | |
1022 | ckfree((char *) new); | |
1023 | return; | |
1024 | } | |
1025 | } | |
1026 | ||
1027 | for(i=beforeThis; i<length; i++) { | |
1028 | new[i+argc] = linePtr->coordPtr[i]; | |
1029 | } | |
1030 | if(linePtr->coordPtr) ckfree((char *)linePtr->coordPtr); | |
1031 | linePtr->coordPtr = new; | |
1032 | linePtr->numPoints = (length + argc)/2; | |
1033 | ||
1034 | if ((length>3) && (state != TK_STATE_HIDDEN)) { | |
1035 | /* | |
1036 | * This is some optimizing code that will result that only the part | |
1037 | * of the polygon that changed (and the objects that are overlapping | |
1038 | * with that part) need to be redrawn. A special flag is set that | |
1039 | * instructs the general canvas code not to redraw the whole | |
1040 | * object. If this flag is not set, the canvas will do the redrawing, | |
1041 | * otherwise I have to do it here. | |
1042 | */ | |
1043 | itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW; | |
1044 | ||
1045 | if (beforeThis>0) {beforeThis -= 2; argc+=2; } | |
1046 | if ((beforeThis+argc)<length) argc+=2; | |
1047 | if (linePtr->smooth) { | |
1048 | if(beforeThis>0) { | |
1049 | beforeThis-=2; argc+=2; | |
1050 | } | |
1051 | if((beforeThis+argc+2)<length) { | |
1052 | argc+=2; | |
1053 | } | |
1054 | } | |
1055 | itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[beforeThis]; | |
1056 | itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[beforeThis+1]; | |
1057 | if ((linePtr->firstArrowPtr != NULL) && (beforeThis<1)) { | |
1058 | /* include old first arrow */ | |
1059 | for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | |
1060 | i++, coordPtr += 2) { | |
1061 | TkIncludePoint(itemPtr, coordPtr); | |
1062 | } | |
1063 | } | |
1064 | if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+argc)>=length)) { | |
1065 | /* include old last arrow */ | |
1066 | for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | |
1067 | i++, coordPtr += 2) { | |
1068 | TkIncludePoint(itemPtr, coordPtr); | |
1069 | } | |
1070 | } | |
1071 | coordPtr = linePtr->coordPtr+beforeThis+2; | |
1072 | for(i=2; i<argc; i+=2) { | |
1073 | TkIncludePoint(itemPtr, coordPtr); | |
1074 | coordPtr+=2; | |
1075 | } | |
1076 | } | |
1077 | if (linePtr->firstArrowPtr != NULL) { | |
1078 | ckfree((char *) linePtr->firstArrowPtr); | |
1079 | linePtr->firstArrowPtr = NULL; | |
1080 | } | |
1081 | if (linePtr->lastArrowPtr != NULL) { | |
1082 | ckfree((char *) linePtr->lastArrowPtr); | |
1083 | linePtr->lastArrowPtr = NULL; | |
1084 | } | |
1085 | if (linePtr->arrow != ARROWS_NONE) { | |
1086 | ConfigureArrows(canvas, linePtr); | |
1087 | } | |
1088 | ||
1089 | if(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) { | |
1090 | double width; | |
1091 | int intWidth; | |
1092 | if ((linePtr->firstArrowPtr != NULL) && (beforeThis>2)) { | |
1093 | /* include new first arrow */ | |
1094 | for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | |
1095 | i++, coordPtr += 2) { | |
1096 | TkIncludePoint(itemPtr, coordPtr); | |
1097 | } | |
1098 | } | |
1099 | if ((linePtr->lastArrowPtr != NULL) && ((beforeThis+argc)<(length-2))) { | |
1100 | /* include new right arrow */ | |
1101 | for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | |
1102 | i++, coordPtr += 2) { | |
1103 | TkIncludePoint(itemPtr, coordPtr); | |
1104 | } | |
1105 | } | |
1106 | width = linePtr->outline.width; | |
1107 | if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | |
1108 | if (linePtr->outline.activeWidth>width) { | |
1109 | width = linePtr->outline.activeWidth; | |
1110 | } | |
1111 | } else if (state==TK_STATE_DISABLED) { | |
1112 | if (linePtr->outline.disabledWidth>0) { | |
1113 | width = linePtr->outline.disabledWidth; | |
1114 | } | |
1115 | } | |
1116 | intWidth = (int) (width + 0.5); | |
1117 | if (intWidth < 1) { | |
1118 | intWidth = 1; | |
1119 | } | |
1120 | itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth; | |
1121 | itemPtr->x2 += intWidth; itemPtr->y2 += intWidth; | |
1122 | Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1, | |
1123 | itemPtr->x2, itemPtr->y2); | |
1124 | } | |
1125 | ||
1126 | ComputeLineBbox(canvas, linePtr); | |
1127 | } | |
1128 | ||
1129 | /* | |
1130 | *-------------------------------------------------------------- | |
1131 | * | |
1132 | * LineDeleteCoords -- | |
1133 | * | |
1134 | * Delete one or more coordinates from a line item. | |
1135 | * | |
1136 | * Results: | |
1137 | * None. | |
1138 | * | |
1139 | * Side effects: | |
1140 | * Characters between "first" and "last", inclusive, get | |
1141 | * deleted from itemPtr. | |
1142 | * | |
1143 | *-------------------------------------------------------------- | |
1144 | */ | |
1145 | ||
1146 | static void | |
1147 | LineDeleteCoords(canvas, itemPtr, first, last) | |
1148 | Tk_Canvas canvas; /* Canvas containing itemPtr. */ | |
1149 | Tk_Item *itemPtr; /* Item in which to delete characters. */ | |
1150 | int first; /* Index of first character to delete. */ | |
1151 | int last; /* Index of last character to delete. */ | |
1152 | { | |
1153 | LineItem *linePtr = (LineItem *) itemPtr; | |
1154 | int count, i, first1, last1; | |
1155 | int length = 2*linePtr->numPoints; | |
1156 | double *coordPtr; | |
1157 | Tk_State state = itemPtr->state; | |
1158 | ||
1159 | if(state == TK_STATE_NULL) { | |
1160 | state = ((TkCanvas *)canvas)->canvas_state; | |
1161 | } | |
1162 | ||
1163 | first &= -2; | |
1164 | last &= -2; | |
1165 | ||
1166 | if (first < 0) { | |
1167 | first = 0; | |
1168 | } | |
1169 | if (last >= length) { | |
1170 | last = length-2; | |
1171 | } | |
1172 | if (first > last) { | |
1173 | return; | |
1174 | } | |
1175 | if (linePtr->firstArrowPtr != NULL) { | |
1176 | linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; | |
1177 | linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; | |
1178 | } | |
1179 | if (linePtr->lastArrowPtr != NULL) { | |
1180 | linePtr->coordPtr[length-2] = linePtr->lastArrowPtr[0]; | |
1181 | linePtr->coordPtr[length-1] = linePtr->lastArrowPtr[1]; | |
1182 | } | |
1183 | first1 = first; last1 = last; | |
1184 | if(first1>0) first1 -= 2; | |
1185 | if(last1<length-2) last1 += 2; | |
1186 | if (linePtr->smooth) { | |
1187 | if(first1>0) first1 -= 2; | |
1188 | if(last1<length-2) last1 += 2; | |
1189 | } | |
1190 | ||
1191 | if((first1<2) && (last1 >= length-2)) { | |
1192 | /* | |
1193 | * This is some optimizing code that will result that only the part | |
1194 | * of the line that changed (and the objects that are overlapping | |
1195 | * with that part) need to be redrawn. A special flag is set that | |
1196 | * instructs the general canvas code not to redraw the whole | |
1197 | * object. If this flag is set, the redrawing has to be done here, | |
1198 | * otherwise the general Canvas code will take care of it. | |
1199 | */ | |
1200 | ||
1201 | itemPtr->redraw_flags |= TK_ITEM_DONT_REDRAW; | |
1202 | itemPtr->x1 = itemPtr->x2 = (int) linePtr->coordPtr[first1]; | |
1203 | itemPtr->y1 = itemPtr->y2 = (int) linePtr->coordPtr[first1+1]; | |
1204 | if ((linePtr->firstArrowPtr != NULL) && (first1<2)) { | |
1205 | /* include old first arrow */ | |
1206 | for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | |
1207 | i++, coordPtr += 2) { | |
1208 | TkIncludePoint(itemPtr, coordPtr); | |
1209 | } | |
1210 | } | |
1211 | if ((linePtr->lastArrowPtr != NULL) && (last1>=length-2)) { | |
1212 | /* include old last arrow */ | |
1213 | for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | |
1214 | i++, coordPtr += 2) { | |
1215 | TkIncludePoint(itemPtr, coordPtr); | |
1216 | } | |
1217 | } | |
1218 | coordPtr = linePtr->coordPtr+first1+2; | |
1219 | for (i=first1+2; i<=last1; i+=2) { | |
1220 | TkIncludePoint(itemPtr, coordPtr); | |
1221 | coordPtr+=2; | |
1222 | } | |
1223 | } | |
1224 | ||
1225 | count = last + 2 - first; | |
1226 | for (i=last+2; i<length; i++) { | |
1227 | linePtr->coordPtr[i-count] = linePtr->coordPtr[i]; | |
1228 | } | |
1229 | linePtr->numPoints -= count/2; | |
1230 | if (linePtr->firstArrowPtr != NULL) { | |
1231 | ckfree((char *) linePtr->firstArrowPtr); | |
1232 | linePtr->firstArrowPtr = NULL; | |
1233 | } | |
1234 | if (linePtr->lastArrowPtr != NULL) { | |
1235 | ckfree((char *) linePtr->lastArrowPtr); | |
1236 | linePtr->lastArrowPtr = NULL; | |
1237 | } | |
1238 | if (linePtr->arrow != ARROWS_NONE) { | |
1239 | ConfigureArrows(canvas, linePtr); | |
1240 | } | |
1241 | if(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW) { | |
1242 | double width; | |
1243 | int intWidth; | |
1244 | if ((linePtr->firstArrowPtr != NULL) && (first1<4)) { | |
1245 | /* include new first arrow */ | |
1246 | for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | |
1247 | i++, coordPtr += 2) { | |
1248 | TkIncludePoint(itemPtr, coordPtr); | |
1249 | } | |
1250 | } | |
1251 | if ((linePtr->lastArrowPtr != NULL) && (last1>(length-4))) { | |
1252 | /* include new right arrow */ | |
1253 | for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | |
1254 | i++, coordPtr += 2) { | |
1255 | TkIncludePoint(itemPtr, coordPtr); | |
1256 | } | |
1257 | } | |
1258 | width = linePtr->outline.width; | |
1259 | if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | |
1260 | if (linePtr->outline.activeWidth>width) { | |
1261 | width = linePtr->outline.activeWidth; | |
1262 | } | |
1263 | } else if (state==TK_STATE_DISABLED) { | |
1264 | if (linePtr->outline.disabledWidth>0) { | |
1265 | width = linePtr->outline.disabledWidth; | |
1266 | } | |
1267 | } | |
1268 | intWidth = (int) (width + 0.5); | |
1269 | if (intWidth < 1) { | |
1270 | intWidth = 1; | |
1271 | } | |
1272 | itemPtr->x1 -= intWidth; itemPtr->y1 -= intWidth; | |
1273 | itemPtr->x2 += intWidth; itemPtr->y2 += intWidth; | |
1274 | Tk_CanvasEventuallyRedraw(canvas, itemPtr->x1, itemPtr->y1, | |
1275 | itemPtr->x2, itemPtr->y2); | |
1276 | } | |
1277 | ComputeLineBbox(canvas, linePtr); | |
1278 | } | |
1279 | ||
1280 | /* | |
1281 | *-------------------------------------------------------------- | |
1282 | * | |
1283 | * LineToPoint -- | |
1284 | * | |
1285 | * Computes the distance from a given point to a given | |
1286 | * line, in canvas units. | |
1287 | * | |
1288 | * Results: | |
1289 | * The return value is 0 if the point whose x and y coordinates | |
1290 | * are pointPtr[0] and pointPtr[1] is inside the line. If the | |
1291 | * point isn't inside the line then the return value is the | |
1292 | * distance from the point to the line. | |
1293 | * | |
1294 | * Side effects: | |
1295 | * None. | |
1296 | * | |
1297 | *-------------------------------------------------------------- | |
1298 | */ | |
1299 | ||
1300 | /* ARGSUSED */ | |
1301 | static double | |
1302 | LineToPoint(canvas, itemPtr, pointPtr) | |
1303 | Tk_Canvas canvas; /* Canvas containing item. */ | |
1304 | Tk_Item *itemPtr; /* Item to check against point. */ | |
1305 | double *pointPtr; /* Pointer to x and y coordinates. */ | |
1306 | { | |
1307 | Tk_State state = itemPtr->state; | |
1308 | LineItem *linePtr = (LineItem *) itemPtr; | |
1309 | double *coordPtr, *linePoints; | |
1310 | double staticSpace[2*MAX_STATIC_POINTS]; | |
1311 | double poly[10]; | |
1312 | double bestDist, dist, width; | |
1313 | int numPoints, count; | |
1314 | int changedMiterToBevel; /* Non-zero means that a mitered corner | |
1315 | * had to be treated as beveled after all | |
1316 | * because the angle was < 11 degrees. */ | |
1317 | ||
1318 | bestDist = 1.0e36; | |
1319 | ||
1320 | /* | |
1321 | * Handle smoothed lines by generating an expanded set of points | |
1322 | * against which to do the check. | |
1323 | */ | |
1324 | ||
1325 | if(state == TK_STATE_NULL) { | |
1326 | state = ((TkCanvas *)canvas)->canvas_state; | |
1327 | } | |
1328 | ||
1329 | width = linePtr->outline.width; | |
1330 | if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | |
1331 | if (linePtr->outline.activeWidth>width) { | |
1332 | width = linePtr->outline.activeWidth; | |
1333 | } | |
1334 | } else if (state==TK_STATE_DISABLED) { | |
1335 | if (linePtr->outline.disabledWidth>0) { | |
1336 | width = linePtr->outline.disabledWidth; | |
1337 | } | |
1338 | } | |
1339 | ||
1340 | if ((linePtr->smooth) && (linePtr->numPoints > 2)) { | |
1341 | numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL, | |
1342 | linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | |
1343 | (double *) NULL); | |
1344 | if (numPoints <= MAX_STATIC_POINTS) { | |
1345 | linePoints = staticSpace; | |
1346 | } else { | |
1347 | linePoints = (double *) ckalloc((unsigned) | |
1348 | (2*numPoints*sizeof(double))); | |
1349 | } | |
1350 | numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, | |
1351 | linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | |
1352 | linePoints); | |
1353 | } else { | |
1354 | numPoints = linePtr->numPoints; | |
1355 | linePoints = linePtr->coordPtr; | |
1356 | } | |
1357 | ||
1358 | if (width < 1.0) { | |
1359 | width = 1.0; | |
1360 | } | |
1361 | ||
1362 | if (!numPoints || itemPtr->state==TK_STATE_HIDDEN) { | |
1363 | return bestDist; | |
1364 | } else if (numPoints == 1) { | |
1365 | bestDist = hypot(linePoints[0] - pointPtr[0], linePoints[1] - pointPtr[1]) | |
1366 | - width/2.0; | |
1367 | if (bestDist < 0) bestDist = 0; | |
1368 | return bestDist; | |
1369 | } | |
1370 | ||
1371 | /* | |
1372 | * The overall idea is to iterate through all of the edges of | |
1373 | * the line, computing a polygon for each edge and testing the | |
1374 | * point against that polygon. In addition, there are additional | |
1375 | * tests to deal with rounded joints and caps. | |
1376 | */ | |
1377 | ||
1378 | changedMiterToBevel = 0; | |
1379 | for (count = numPoints, coordPtr = linePoints; count >= 2; | |
1380 | count--, coordPtr += 2) { | |
1381 | ||
1382 | /* | |
1383 | * If rounding is done around the first point then compute | |
1384 | * the distance between the point and the point. | |
1385 | */ | |
1386 | ||
1387 | if (((linePtr->capStyle == CapRound) && (count == numPoints)) | |
1388 | || ((linePtr->joinStyle == JoinRound) | |
1389 | && (count != numPoints))) { | |
1390 | dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) | |
1391 | - width/2.0; | |
1392 | if (dist <= 0.0) { | |
1393 | bestDist = 0.0; | |
1394 | goto done; | |
1395 | } else if (dist < bestDist) { | |
1396 | bestDist = dist; | |
1397 | } | |
1398 | } | |
1399 | ||
1400 | /* | |
1401 | * Compute the polygonal shape corresponding to this edge, | |
1402 | * consisting of two points for the first point of the edge | |
1403 | * and two points for the last point of the edge. | |
1404 | */ | |
1405 | ||
1406 | if (count == numPoints) { | |
1407 | TkGetButtPoints(coordPtr+2, coordPtr, width, | |
1408 | linePtr->capStyle == CapProjecting, poly, poly+2); | |
1409 | } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) { | |
1410 | poly[0] = poly[6]; | |
1411 | poly[1] = poly[7]; | |
1412 | poly[2] = poly[4]; | |
1413 | poly[3] = poly[5]; | |
1414 | } else { | |
1415 | TkGetButtPoints(coordPtr+2, coordPtr, width, 0, | |
1416 | poly, poly+2); | |
1417 | ||
1418 | /* | |
1419 | * If this line uses beveled joints, then check the distance | |
1420 | * to a polygon comprising the last two points of the previous | |
1421 | * polygon and the first two from this polygon; this checks | |
1422 | * the wedges that fill the mitered joint. | |
1423 | */ | |
1424 | ||
1425 | if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) { | |
1426 | poly[8] = poly[0]; | |
1427 | poly[9] = poly[1]; | |
1428 | dist = TkPolygonToPoint(poly, 5, pointPtr); | |
1429 | if (dist <= 0.0) { | |
1430 | bestDist = 0.0; | |
1431 | goto done; | |
1432 | } else if (dist < bestDist) { | |
1433 | bestDist = dist; | |
1434 | } | |
1435 | changedMiterToBevel = 0; | |
1436 | } | |
1437 | } | |
1438 | if (count == 2) { | |
1439 | TkGetButtPoints(coordPtr, coordPtr+2, width, | |
1440 | linePtr->capStyle == CapProjecting, poly+4, poly+6); | |
1441 | } else if (linePtr->joinStyle == JoinMiter) { | |
1442 | if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4, | |
1443 | width, poly+4, poly+6) == 0) { | |
1444 | changedMiterToBevel = 1; | |
1445 | TkGetButtPoints(coordPtr, coordPtr+2, width, | |
1446 | 0, poly+4, poly+6); | |
1447 | } | |
1448 | } else { | |
1449 | TkGetButtPoints(coordPtr, coordPtr+2, width, 0, | |
1450 | poly+4, poly+6); | |
1451 | } | |
1452 | poly[8] = poly[0]; | |
1453 | poly[9] = poly[1]; | |
1454 | dist = TkPolygonToPoint(poly, 5, pointPtr); | |
1455 | if (dist <= 0.0) { | |
1456 | bestDist = 0.0; | |
1457 | goto done; | |
1458 | } else if (dist < bestDist) { | |
1459 | bestDist = dist; | |
1460 | } | |
1461 | } | |
1462 | ||
1463 | /* | |
1464 | * If caps are rounded, check the distance to the cap around the | |
1465 | * final end point of the line. | |
1466 | */ | |
1467 | ||
1468 | if (linePtr->capStyle == CapRound) { | |
1469 | dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1]) | |
1470 | - width/2.0; | |
1471 | if (dist <= 0.0) { | |
1472 | bestDist = 0.0; | |
1473 | goto done; | |
1474 | } else if (dist < bestDist) { | |
1475 | bestDist = dist; | |
1476 | } | |
1477 | } | |
1478 | ||
1479 | /* | |
1480 | * If there are arrowheads, check the distance to the arrowheads. | |
1481 | */ | |
1482 | ||
1483 | if (linePtr->arrow != ARROWS_NONE) { | |
1484 | if (linePtr->arrow != ARROWS_LAST) { | |
1485 | dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW, | |
1486 | pointPtr); | |
1487 | if (dist <= 0.0) { | |
1488 | bestDist = 0.0; | |
1489 | goto done; | |
1490 | } else if (dist < bestDist) { | |
1491 | bestDist = dist; | |
1492 | } | |
1493 | } | |
1494 | if (linePtr->arrow != ARROWS_FIRST) { | |
1495 | dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW, | |
1496 | pointPtr); | |
1497 | if (dist <= 0.0) { | |
1498 | bestDist = 0.0; | |
1499 | goto done; | |
1500 | } else if (dist < bestDist) { | |
1501 | bestDist = dist; | |
1502 | } | |
1503 | } | |
1504 | } | |
1505 | ||
1506 | done: | |
1507 | if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) { | |
1508 | ckfree((char *) linePoints); | |
1509 | } | |
1510 | return bestDist; | |
1511 | } | |
1512 | ||
1513 | /* | |
1514 | *-------------------------------------------------------------- | |
1515 | * | |
1516 | * LineToArea -- | |
1517 | * | |
1518 | * This procedure is called to determine whether an item | |
1519 | * lies entirely inside, entirely outside, or overlapping | |
1520 | * a given rectangular area. | |
1521 | * | |
1522 | * Results: | |
1523 | * -1 is returned if the item is entirely outside the | |
1524 | * area, 0 if it overlaps, and 1 if it is entirely | |
1525 | * inside the given area. | |
1526 | * | |
1527 | * Side effects: | |
1528 | * None. | |
1529 | * | |
1530 | *-------------------------------------------------------------- | |
1531 | */ | |
1532 | ||
1533 | /* ARGSUSED */ | |
1534 | static int | |
1535 | LineToArea(canvas, itemPtr, rectPtr) | |
1536 | Tk_Canvas canvas; /* Canvas containing item. */ | |
1537 | Tk_Item *itemPtr; /* Item to check against line. */ | |
1538 | double *rectPtr; | |
1539 | { | |
1540 | LineItem *linePtr = (LineItem *) itemPtr; | |
1541 | double staticSpace[2*MAX_STATIC_POINTS]; | |
1542 | double *linePoints; | |
1543 | int numPoints, result; | |
1544 | double radius, width; | |
1545 | Tk_State state = itemPtr->state; | |
1546 | ||
1547 | if(state == TK_STATE_NULL) { | |
1548 | state = ((TkCanvas *)canvas)->canvas_state; | |
1549 | } | |
1550 | width = linePtr->outline.width; | |
1551 | if (((TkCanvas *)canvas)->currentItemPtr == itemPtr) { | |
1552 | if (linePtr->outline.activeWidth>width) { | |
1553 | width = linePtr->outline.activeWidth; | |
1554 | } | |
1555 | } else if (state==TK_STATE_DISABLED) { | |
1556 | if (linePtr->outline.disabledWidth>0) { | |
1557 | width = linePtr->outline.disabledWidth; | |
1558 | } | |
1559 | } | |
1560 | ||
1561 | radius = (width+1.0)/2.0; | |
1562 | ||
1563 | if ((state==TK_STATE_HIDDEN) || !linePtr->numPoints) { | |
1564 | return -1; | |
1565 | } else if (linePtr->numPoints == 1) { | |
1566 | double oval[4]; | |
1567 | oval[0] = linePtr->coordPtr[0]-radius; | |
1568 | oval[1] = linePtr->coordPtr[1]-radius; | |
1569 | oval[2] = linePtr->coordPtr[0]+radius; | |
1570 | oval[3] = linePtr->coordPtr[1]+radius; | |
1571 | return TkOvalToArea(oval, rectPtr); | |
1572 | } | |
1573 | ||
1574 | /* | |
1575 | * Handle smoothed lines by generating an expanded set of points | |
1576 | * against which to do the check. | |
1577 | */ | |
1578 | ||
1579 | if ((linePtr->smooth) && (linePtr->numPoints > 2)) { | |
1580 | numPoints = linePtr->smooth->coordProc(canvas, (double *) NULL, | |
1581 | linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | |
1582 | (double *) NULL); | |
1583 | if (numPoints <= MAX_STATIC_POINTS) { | |
1584 | linePoints = staticSpace; | |
1585 | } else { | |
1586 | linePoints = (double *) ckalloc((unsigned) | |
1587 | (2*numPoints*sizeof(double))); | |
1588 | } | |
1589 | numPoints = linePtr->smooth->coordProc(canvas, linePtr->coordPtr, | |
1590 | linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL, | |
1591 | linePoints); | |
1592 | } else { | |
1593 | numPoints = linePtr->numPoints; | |
1594 | linePoints = linePtr->coordPtr; | |
1595 | } | |
1596 | ||
1597 | /* | |
1598 | * Check the segments of the line. | |
1599 | */ | |
1600 | ||
1601 | if (width < 1.0) { | |
1602 | width = 1.0; | |
1603 | } | |
1604 | ||
1605 | result = TkThickPolyLineToArea(linePoints, numPoints, | |
1606 | width, linePtr->capStyle, linePtr->joinStyle, | |
1607 | rectPtr); | |
1608 | if (result == 0) { | |
1609 | goto done; | |
1610 | } | |
1611 | ||
1612 | /* | |
1613 | * Check arrowheads, if any. | |
1614 | */ | |
1615 | ||
1616 | if (linePtr->arrow != ARROWS_NONE) { | |
1617 | if (linePtr->arrow != ARROWS_LAST) { | |
1618 | if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW, | |
1619 | rectPtr) != result) { | |
1620 | result = 0; | |
1621 | goto done; | |
1622 | } | |
1623 | } | |
1624 | if (linePtr->arrow != ARROWS_FIRST) { | |
1625 | if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW, | |
1626 | rectPtr) != result) { | |
1627 | result = 0; | |
1628 | goto done; | |
1629 | } | |
1630 | } | |
1631 | } | |
1632 | ||
1633 | done: | |
1634 | if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) { | |
1635 | ckfree((char *) linePoints); | |
1636 | } | |
1637 | return result; | |
1638 | } | |
1639 | ||
1640 | /* | |
1641 | *-------------------------------------------------------------- | |
1642 | * | |
1643 | * ScaleLine -- | |
1644 | * | |
1645 | * This procedure is invoked to rescale a line item. | |
1646 | * | |
1647 | * Results: | |
1648 | * None. | |
1649 | * | |
1650 | * Side effects: | |
1651 | * The line referred to by itemPtr is rescaled so that the | |
1652 | * following transformation is applied to all point | |
1653 | * coordinates: | |
1654 | * x' = originX + scaleX*(x-originX) | |
1655 | * y' = originY + scaleY*(y-originY) | |
1656 | * | |
1657 | *-------------------------------------------------------------- | |
1658 | */ | |
1659 | ||
1660 | static void | |
1661 | ScaleLine(canvas, itemPtr, originX, originY, scaleX, scaleY) | |
1662 | Tk_Canvas canvas; /* Canvas containing line. */ | |
1663 | Tk_Item *itemPtr; /* Line to be scaled. */ | |
1664 | double originX, originY; /* Origin about which to scale rect. */ | |
1665 | double scaleX; /* Amount to scale in X direction. */ | |
1666 | double scaleY; /* Amount to scale in Y direction. */ | |
1667 | { | |
1668 | LineItem *linePtr = (LineItem *) itemPtr; | |
1669 | double *coordPtr; | |
1670 | int i; | |
1671 | ||
1672 | /* | |
1673 | * Delete any arrowheads before scaling all the points (so that | |
1674 | * the end-points of the line get restored). | |
1675 | */ | |
1676 | ||
1677 | if (linePtr->firstArrowPtr != NULL) { | |
1678 | linePtr->coordPtr[0] = linePtr->firstArrowPtr[0]; | |
1679 | linePtr->coordPtr[1] = linePtr->firstArrowPtr[1]; | |
1680 | ckfree((char *) linePtr->firstArrowPtr); | |
1681 | linePtr->firstArrowPtr = NULL; | |
1682 | } | |
1683 | if (linePtr->lastArrowPtr != NULL) { | |
1684 | int i; | |
1685 | ||
1686 | i = 2*(linePtr->numPoints-1); | |
1687 | linePtr->coordPtr[i] = linePtr->lastArrowPtr[0]; | |
1688 | linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1]; | |
1689 | ckfree((char *) linePtr->lastArrowPtr); | |
1690 | linePtr->lastArrowPtr = NULL; | |
1691 | } | |
1692 | for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints; | |
1693 | i++, coordPtr += 2) { | |
1694 | coordPtr[0] = originX + scaleX*(*coordPtr - originX); | |
1695 | coordPtr[1] = originY + scaleY*(coordPtr[1] - originY); | |
1696 | } | |
1697 | if (linePtr->arrow != ARROWS_NONE) { | |
1698 | ConfigureArrows(canvas, linePtr); | |
1699 | } | |
1700 | ComputeLineBbox(canvas, linePtr); | |
1701 | } | |
1702 | ||
1703 | /* | |
1704 | *-------------------------------------------------------------- | |
1705 | * | |
1706 | * GetLineIndex -- | |
1707 | * | |
1708 | * Parse an index into a line item and return either its value | |
1709 | * or an error. | |
1710 | * | |
1711 | * Results: | |
1712 | * A standard Tcl result. If all went well, then *indexPtr is | |
1713 | * filled in with the index (into itemPtr) corresponding to | |
1714 | * string. Otherwise an error message is left in | |
1715 | * interp->result. | |
1716 | * | |
1717 | * Side effects: | |
1718 | * None. | |
1719 | * | |
1720 | *-------------------------------------------------------------- | |
1721 | */ | |
1722 | ||
1723 | static int | |
1724 | GetLineIndex(interp, canvas, itemPtr, obj, indexPtr) | |
1725 | Tcl_Interp *interp; /* Used for error reporting. */ | |
1726 | Tk_Canvas canvas; /* Canvas containing item. */ | |
1727 | Tk_Item *itemPtr; /* Item for which the index is being | |
1728 | * specified. */ | |
1729 | Tcl_Obj *obj; /* Specification of a particular coord | |
1730 | * in itemPtr's line. */ | |
1731 | int *indexPtr; /* Where to store converted index. */ | |
1732 | { | |
1733 | LineItem *linePtr = (LineItem *) itemPtr; | |
1734 | size_t length; | |
1735 | char *string = Tcl_GetStringFromObj(obj, (int *) &length); | |
1736 | ||
1737 | if (string[0] == 'e') { | |
1738 | if (strncmp(string, "end", length) == 0) { | |
1739 | *indexPtr = 2*linePtr->numPoints; | |
1740 | } else { | |
1741 | badIndex: | |
1742 | ||
1743 | /* | |
1744 | * Some of the paths here leave messages in interp->result, | |
1745 | * so we have to clear it out before storing our own message. | |
1746 | */ | |
1747 | ||
1748 | Tcl_SetResult(interp, (char *) NULL, TCL_STATIC); | |
1749 | Tcl_AppendResult(interp, "bad index \"", string, "\"", | |
1750 | (char *) NULL); | |
1751 | return TCL_ERROR; | |
1752 | } | |
1753 | } else if (string[0] == '@') { | |
1754 | int i; | |
1755 | double x ,y, bestDist, dist, *coordPtr; | |
1756 | char *end, *p; | |
1757 | ||
1758 | p = string+1; | |
1759 | x = strtod(p, &end); | |
1760 | if ((end == p) || (*end != ',')) { | |
1761 | goto badIndex; | |
1762 | } | |
1763 | p = end+1; | |
1764 | y = strtod(p, &end); | |
1765 | if ((end == p) || (*end != 0)) { | |
1766 | goto badIndex; | |
1767 | } | |
1768 | bestDist = 1.0e36; | |
1769 | coordPtr = linePtr->coordPtr; | |
1770 | *indexPtr = 0; | |
1771 | for(i=0; i<linePtr->numPoints; i++) { | |
1772 | dist = hypot(coordPtr[0] - x, coordPtr[1] - y); | |
1773 | if (dist<bestDist) { | |
1774 | bestDist = dist; | |
1775 | *indexPtr = 2*i; | |
1776 | } | |
1777 | coordPtr += 2; | |
1778 | } | |
1779 | } else { | |
1780 | if (Tcl_GetIntFromObj(interp, obj, indexPtr) != TCL_OK) { | |
1781 | goto badIndex; | |
1782 | } | |
1783 | *indexPtr &= -2; /* if index is odd, make it even */ | |
1784 | if (*indexPtr < 0){ | |
1785 | *indexPtr = 0; | |
1786 | } else if (*indexPtr > (2*linePtr->numPoints)) { | |
1787 | *indexPtr = (2*linePtr->numPoints); | |
1788 | } | |
1789 | } | |
1790 | return TCL_OK; | |
1791 | } | |
1792 | ||
1793 | /* | |
1794 | *-------------------------------------------------------------- | |
1795 | * | |
1796 | * TranslateLine -- | |
1797 | * | |
1798 | * This procedure is called to move a line by a given amount. | |
1799 | * | |
1800 | * Results: | |
1801 | * None. | |
1802 | * | |
1803 | * Side effects: | |
1804 | * The position of the line is offset by (xDelta, yDelta), and | |
1805 | * the bounding box is updated in the generic part of the item | |
1806 | * structure. | |
1807 | * | |
1808 | *-------------------------------------------------------------- | |
1809 | */ | |
1810 | ||
1811 | static void | |
1812 | TranslateLine(canvas, itemPtr, deltaX, deltaY) | |
1813 | Tk_Canvas canvas; /* Canvas containing item. */ | |
1814 | Tk_Item *itemPtr; /* Item that is being moved. */ | |
1815 | double deltaX, deltaY; /* Amount by which item is to be | |
1816 | * moved. */ | |
1817 | { | |
1818 | LineItem *linePtr = (LineItem *) itemPtr; | |
1819 | double *coordPtr; | |
1820 | int i; | |
1821 | ||
1822 | for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints; | |
1823 | i++, coordPtr += 2) { | |
1824 | coordPtr[0] += deltaX; | |
1825 | coordPtr[1] += deltaY; | |
1826 | } | |
1827 | if (linePtr->firstArrowPtr != NULL) { | |
1828 | for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW; | |
1829 | i++, coordPtr += 2) { | |
1830 | coordPtr[0] += deltaX; | |
1831 | coordPtr[1] += deltaY; | |
1832 | } | |
1833 | } | |
1834 | if (linePtr->lastArrowPtr != NULL) { | |
1835 | for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW; | |
1836 | i++, coordPtr += 2) { | |
1837 | coordPtr[0] += deltaX; | |
1838 | coordPtr[1] += deltaY; | |
1839 | } | |
1840 | } | |
1841 | ComputeLineBbox(canvas, linePtr); | |
1842 | } | |
1843 | ||
1844 | /* | |
1845 | *-------------------------------------------------------------- | |
1846 | * | |
1847 | * ParseArrowShape -- | |
1848 | * | |
1849 | * This procedure is called back during option parsing to | |
1850 | * parse arrow shape information. | |
1851 | * | |
1852 | * Results: | |
1853 | * The return value is a standard Tcl result: TCL_OK means | |
1854 | * that the arrow shape information was parsed ok, and | |
1855 | * TCL_ERROR means it couldn't be parsed. | |
1856 | * | |
1857 | * Side effects: | |
1858 | * Arrow information in recordPtr is updated. | |
1859 | * | |
1860 | *-------------------------------------------------------------- | |
1861 | */ | |
1862 | ||
1863 | /* ARGSUSED */ | |
1864 | static int | |
1865 | ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset) | |
1866 | ClientData clientData; /* Not used. */ | |
1867 | Tcl_Interp *interp; /* Used for error reporting. */ | |
1868 | Tk_Window tkwin; /* Not used. */ | |
1869 | CONST char *value; /* Textual specification of arrow shape. */ | |
1870 | char *recordPtr; /* Pointer to item record in which to | |
1871 | * store arrow information. */ | |
1872 | int offset; /* Offset of shape information in widget | |
1873 | * record. */ | |
1874 | { | |
1875 | LineItem *linePtr = (LineItem *) recordPtr; | |
1876 | double a, b, c; | |
1877 | int argc; | |
1878 | char **argv = NULL; | |
1879 | ||
1880 | if (offset != Tk_Offset(LineItem, arrowShapeA)) { | |
1881 | panic("ParseArrowShape received bogus offset"); | |
1882 | } | |
1883 | ||
1884 | if (Tcl_SplitList(interp, (char *) value, &argc, &argv) != TCL_OK) { | |
1885 | syntaxError: | |
1886 | Tcl_ResetResult(interp); | |
1887 | Tcl_AppendResult(interp, "bad arrow shape \"", value, | |
1888 | "\": must be list with three numbers", (char *) NULL); | |
1889 | if (argv != NULL) { | |
1890 | ckfree((char *) argv); | |
1891 | } | |
1892 | return TCL_ERROR; | |
1893 | } | |
1894 | if (argc != 3) { | |
1895 | goto syntaxError; | |
1896 | } | |
1897 | if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK) | |
1898 | & |