1 /*
2  * tkgeomapLnArr.c --
3  *
4  * 	This file defines functions that display geolinearrays as canvas items.
5  *
6  * Copyright (c) 2004 Gordon D. Carrie. All rights reserved.
7  *
8  * Licensed under the Open Software License version 2.1
9  *
10  * Please address questions and feedback to user0@tkgeomap.org
11  *
12  * @(#) $Id: tkgeomapLnArr.c,v 1.20 2009/10/23 20:37:25 tkgeomap Exp $
13  *
14  **********************************************************************
15  *
16  */
17 
18 #include "tkgeomap.h"
19 #include "tkgeomapInt.h"
20 
21 /*
22  * This structure stores one geomap_lnarr item.
23  */
24 
25 typedef struct {
26     Tk_Item header;		/* Generic stuff that's the same for all
27 				 * types.  MUST BE FIRST IN STRUCTURE. */
28     Tcl_Interp *interp;		/* Interpreter in which item was created */
29     Tk_Canvas canvas;		/* Canvas in which item is displayed */
30     double xRef, yRef;		/* Canvas coordinates of reference point */
31     GeoPt refPt;		/* Geographic coordinates of reference point */
32     Tclgeomap_Proj proj;	/* Cartographic projection */
33     double scale;		/* Cartographic scale (pure number ratio) */
34     Tclgeomap_LnArr tclGeoLnArr;/* Geolinearray the item displays */
35     int nLines;			/* Number of lines to draw */
36     int nMax;			/* Number of points in longest line */
37     MapLnArr mapLnArr;		/* Array of map coordinates for the lines */
38     double **coordPtrPtr;	/* Array of canvas coordinates corresponding to
39 				 * the points in the map.  Dimensions are
40 				 * [nLines][num pts in line] */
41     int *nPtsPtr;		/* Number of points in each line of
42 				 * coordPtrPtr.  Dimension is [num lines] */
43     int	updateMap;		/* If true, mapLnArr is out of date */
44     int updateCvs;		/* If true, coordPtrPtr is out of date */
45     int width;			/* Line width */
46     XColor *lineColor;		/* Outline color */
47     int lineStyle;		/* LineSolid, LineOnOffDash, or
48 				 * LineDoubleDash */
49     int dashes;			/* Dash length */
50     GC lineGC;			/* Graphics context for drawing outline */
51     int smooth;			/* If true, smooth the line.  Currently
52     				 * smoothing is done only with Tk's bezier
53 				 * function */
54     int splineSteps;		/* Number of steps in each spline segment */
55     XColor *fillColor;		/* Color to fill shapes */
56     Pixmap stipple;		/* Stipple pattern to fill shapes */
57     int shape;			/* Complex, Convex, or Nonconvex */
58     GC fillGC;			/* Graphics context for filling */
59     int dotSize;		/* Dot size at segment ends */
60 } LnArrItem;
61 
62 /*
63  * Prototypes for procedures defined in this file:
64  */
65 
66 static int		createProc _ANSI_ARGS_((Tcl_Interp *interp,
67 				Tk_Canvas canvas, struct Tk_Item *itemPtr,
68 				int objc, Tcl_Obj *CONST objv[]));
69 static int		configProc _ANSI_ARGS_((Tcl_Interp *interp,
70 				Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
71 				Tcl_Obj *CONST objv[], int flags));
72 static int		coordProc _ANSI_ARGS_((Tcl_Interp *interp,
73 				Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
74 				Tcl_Obj *CONST objv[]));
75 static void		deleteProc _ANSI_ARGS_((Tk_Canvas canvas,
76 				Tk_Item *itemPtr, Display *display));
77 static void		forgetLnArr _ANSI_ARGS_((ClientData clientData));
78 static void		forgetProj _ANSI_ARGS_((ClientData clientData));
79 static void		hide _ANSI_ARGS_((LnArrItem *lnArrItemPtr));
80 static void		displayProc _ANSI_ARGS_((Tk_Canvas canvas,
81 				Tk_Item *itemPtr, Display *display,
82 				Drawable dst, int x, int y, int width,
83 				int height));
84 static double		pointProc _ANSI_ARGS_((Tk_Canvas canvas,
85 				Tk_Item *itemPtr, double *coordPtr));
86 static int		areaProc _ANSI_ARGS_((Tk_Canvas canvas,
87 				Tk_Item *itemPtr, double *rectPtr));
88 static int		postscriptProc _ANSI_ARGS_((Tcl_Interp *interp,
89 				Tk_Canvas canvas, Tk_Item *itemPtr,
90 				int prepass));
91 static void		scaleProc _ANSI_ARGS_((Tk_Canvas canvas,
92 				Tk_Item *itemPtr, double originX,
93 				double originY, double scaleX, double scaleY));
94 static void		translateProc _ANSI_ARGS_((Tk_Canvas canvas,
95 				Tk_Item *itemPtr, double deltaX,
96 				double deltaY));
97 static int		parseGeoLnArrOption _ANSI_ARGS_((ClientData clientData,
98 				Tcl_Interp *interp, Tk_Window tkwin,
99 				char *value, char *widgRec, int offset));
100 static char *		printGeoLnArrOption _ANSI_ARGS_((ClientData clientData,
101 				Tk_Window tkwin, char *widgRec, int offset,
102 				Tcl_FreeProc **freeProcPtr));
103 static int		parseRefPtOption _ANSI_ARGS_((ClientData clientData,
104 				Tcl_Interp *interp, Tk_Window tkwin,
105 				char *value, char *widgRec, int offset));
106 static char *		printRefPtOption _ANSI_ARGS_((ClientData clientData,
107 				Tk_Window tkwin, char *widgRec, int offset,
108 				Tcl_FreeProc **freeProcPtr));
109 static int		parseProjOption _ANSI_ARGS_((ClientData clientData,
110 				Tcl_Interp *interp, Tk_Window tkwin,
111 				char *value, char *widgRec, int offset));
112 static char *		printProjOption _ANSI_ARGS_((ClientData clientData,
113 				Tk_Window tkwin, char *widgRec, int offset,
114 				Tcl_FreeProc **freeProcPtr));
115 static int		parseLineStyleOption _ANSI_ARGS_((ClientData clientData,
116 				Tcl_Interp *interp, Tk_Window tkwin,
117 				char *value, char *widgRec, int offset));
118 static char *		printLineStyleOption _ANSI_ARGS_((ClientData clientData,
119 				Tk_Window tkwin, char *widgRec, int offset,
120 				Tcl_FreeProc **freeProcPtr));
121 static int		parseShapeOption _ANSI_ARGS_((ClientData clientData,
122 				Tcl_Interp *interp, Tk_Window tkwin,
123 				char *value, char *widgRec, int offset));
124 static char *		printShapeOption _ANSI_ARGS_((ClientData clientData,
125 				Tk_Window tkwin, char *widgRec, int offset,
126 				Tcl_FreeProc **freeProcPtr));
127 static void		updateMap _ANSI_ARGS_((ClientData clientData));
128 static void		updateCvs _ANSI_ARGS_((ClientData clientData));
129 
130 /*
131  * Custom configuration options
132  */
133 
134 static Tk_CustomOption geoLnArrOption = {
135     parseGeoLnArrOption,
136     printGeoLnArrOption,
137     NULL
138 };
139 
140 static Tk_CustomOption refPtOption = {
141     parseRefPtOption,
142     printRefPtOption,
143     NULL
144 };
145 
146 static Tk_CustomOption projOption = {
147     parseProjOption,
148     printProjOption,
149     NULL
150 };
151 
152 static Tk_CustomOption shapeOption = {
153     parseShapeOption,
154     printShapeOption,
155     NULL
156 };
157 
158 static Tk_CustomOption lineStyleOption = {
159     parseLineStyleOption,
160     printLineStyleOption,
161     NULL
162 };
163 
164 /*
165  * The members of tagsOption will be set in TkgeomapLnArrInit
166  */
167 
168 static Tk_CustomOption tagsOption = {
169     NULL, NULL, NULL
170 };
171 
172 /*
173  * Configuration options for the geomap_lnarr item
174  */
175 
176 static Tk_ConfigSpec configSpecs[] = {
177     {TK_CONFIG_CUSTOM, "-lnarr", NULL, NULL, "", 0, TK_CONFIG_NULL_OK,
178 	&geoLnArrOption},
179     {TK_CONFIG_CUSTOM, "-refpoint", NULL, NULL, "0.0 0.0", 0, 0,
180 	&refPtOption},
181     {TK_CONFIG_CUSTOM, "-projection", NULL, NULL, "", 0, 0,
182 	&projOption},
183     {TK_CONFIG_DOUBLE, "-scale", NULL, NULL, "1.0e-7",
184 	Tk_Offset(LnArrItem, scale), 0, NULL},
185     {TK_CONFIG_COLOR, "-outline", NULL, NULL, "NavyBlue",
186 	Tk_Offset(LnArrItem, lineColor), TK_CONFIG_NULL_OK, NULL},
187     {TK_CONFIG_PIXELS, "-width", NULL, NULL, "1",
188 	Tk_Offset(LnArrItem, width), 0, NULL},
189     {TK_CONFIG_CUSTOM, "-linestyle", NULL, NULL, "LineSolid",
190 	0, 0, &lineStyleOption},
191     {TK_CONFIG_INT, "-dashes", NULL, NULL, "4",
192 	Tk_Offset(LnArrItem, dashes), 0, NULL},
193     {TK_CONFIG_BOOLEAN, "-smooth", NULL, NULL, "0",
194 	Tk_Offset(LnArrItem, smooth), 0, NULL},
195     {TK_CONFIG_INT, "-splinesteps", NULL, NULL, "12",
196 	Tk_Offset(LnArrItem, splineSteps), 0, NULL},
197     {TK_CONFIG_COLOR, "-fill", NULL, NULL, "",
198 	Tk_Offset(LnArrItem, fillColor), TK_CONFIG_NULL_OK, NULL},
199     {TK_CONFIG_BITMAP, "-stipple", NULL, NULL, NULL,
200 	Tk_Offset(LnArrItem, stipple), TK_CONFIG_NULL_OK, NULL},
201     {TK_CONFIG_CUSTOM, "-shape", "shape", "Shape", "Complex", 0, 0,
202 	&shapeOption},
203     {TK_CONFIG_PIXELS, "-dotsize", NULL, NULL, "0",
204 	Tk_Offset(LnArrItem, dotSize), 0, NULL},
205     {TK_CONFIG_CUSTOM, "-tags", NULL, NULL, NULL, 0, TK_CONFIG_NULL_OK,
206 	&tagsOption},
207     {TK_CONFIG_END,  NULL,  NULL,  NULL, NULL, 0, 0, NULL}
208 };
209 
210 /*
211  * The structures below defines the geomap_lnarr item type in terms of
212  * procedures that can be invoked by generic item code.
213  */
214 
215 static Tk_ItemType geoLnArrType = {
216     "geomap_lnarr",			/* item type name */
217     sizeof(LnArrItem),			/* itemSize */
218     createProc,				/* createProc */
219     configSpecs,			/* configSpecs */
220     configProc,				/* configureProc */
221     coordProc,				/* coordProc */
222     deleteProc,				/* deleteProc */
223     displayProc,			/* displayProc */
224     TK_CONFIG_OBJS,			/* alwaysredraw or flags (?) */
225     pointProc,				/* pointProc */
226     areaProc,				/* areaProc */
227     postscriptProc,	                /* postscriptProc */
228     scaleProc,				/* scaleProc */
229     translateProc,			/* translateProc */
230     (Tk_ItemIndexProc *) NULL,		/* indexProc */
231     (Tk_ItemCursorProc *) NULL,		/* icursorProc */
232     (Tk_ItemSelectionProc *) NULL,	/* selectionProc */
233     (Tk_ItemInsertProc *) NULL,		/* insertProc */
234     (Tk_ItemDCharsProc *) NULL,		/* dTextProc */
235     (Tk_ItemType *) NULL,		/* nextPtr */
236 };
237 
238 /*
239  *------------------------------------------------------------------------
240  *
241  * TkgeomapLnArrInit --
242  *
243  *	This procedure adds the geomap_lnarr item type to the canvas widget.
244  *	See the user documentation for a description of the its effect
245  *	on Tcl.
246  *
247  * Results:
248  *	A standard Tcl result.
249  *
250  * Side effects:
251  * 	Prerequisite packages are initialized, and the "geomap_lnarr" item is
252  * 	added to the Tk canvas interface.
253  *
254  *------------------------------------------------------------------------
255  */
256 
257 int
TkgeomapLnArrInit(interp)258 TkgeomapLnArrInit(interp)
259     Tcl_Interp *interp;			/* Current interpreter */
260 {
261     static int loaded;			/* If true, package is loaded */
262 
263     if (loaded) {
264 	return TCL_OK;
265     }
266 #ifdef USE_TCL_STUBS
267     if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
268 	return TCL_ERROR;
269     }
270 #endif
271 #ifdef USE_TK_STUBS
272     if (Tk_InitStubs(interp, TK_VERSION, 0) == NULL) {
273 	return TCL_ERROR;
274     }
275 #endif
276 
277     /*
278      * Set tagsOption.  This must be done at run time because the stubs
279      * library set the functions to non-static values, so they cannot be
280      * used in an initializer.
281      */
282 
283     tagsOption.parseProc = Tk_CanvasTagsParseProc;
284     tagsOption.printProc = Tk_CanvasTagsPrintProc;
285 
286     Tk_CreateItemType(&geoLnArrType);
287     loaded = 1;
288     return TCL_OK;
289 }
290 
291 /*
292  *--------------------------------------------------------------
293  *
294  * createProc --
295  *
296  *	This procedure is invoked to create a new geomap_lnarr
297  *	item in a canvas.
298  *
299  * Results:
300  *	A standard Tcl return value.  If an error occurred in
301  *	creating the item, then an error message is left in
302  *	the interp's result;  in this case itemPtr is left uninitialized,
303  *	so it can be safely freed by the caller.
304  *
305  * Side effects:
306  *	A new geomap_lnarr item is created.
307  *
308  *--------------------------------------------------------------
309  */
310 
311 static int
createProc(interp,canvas,itemPtr,objc,objv)312 createProc(interp, canvas, itemPtr, objc, objv)
313     Tcl_Interp *interp;			/* Interpreter for error reporting. */
314     Tk_Canvas canvas;			/* Canvas to hold new item. */
315     Tk_Item *itemPtr;			/* Record to hold new item;  header
316 					 * has been initialized by caller. */
317     int objc;				/* Number of arguments in objv. */
318     Tcl_Obj *CONST objv[];		/* Arguments describing linearray. */
319 {
320     LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
321     double xRef, yRef;
322 
323 
324     /*
325      * Set location of reference point from coordinates given on command line.
326      */
327 
328     if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0], &xRef) != TCL_OK)
329 	    || (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1], &yRef)
330 		!= TCL_OK)) {
331 	return TCL_ERROR;
332     }
333     lnArrItemPtr->xRef = xRef;
334     lnArrItemPtr->yRef = yRef;
335 
336     /*
337      * Initialize values.  Bogus initializers will be set properly
338      * when the item is configured.
339      */
340 
341     lnArrItemPtr->interp = interp;
342     lnArrItemPtr->canvas = canvas;
343     lnArrItemPtr->refPt = GeoPtFmDeg(0.0, 0.0);
344     lnArrItemPtr->proj = NULL;
345     lnArrItemPtr->scale = 0.0;
346     lnArrItemPtr->tclGeoLnArr = NULL;
347     lnArrItemPtr->nLines = 0;
348     lnArrItemPtr->mapLnArr = NULL;
349     lnArrItemPtr->coordPtrPtr = NULL;
350     lnArrItemPtr->nPtsPtr = NULL;
351     lnArrItemPtr->updateMap = 1;
352     lnArrItemPtr->updateCvs = 1;
353     lnArrItemPtr->width = 0;
354     lnArrItemPtr->lineColor = None;
355     lnArrItemPtr->lineStyle = LineSolid;
356     lnArrItemPtr->dashes = 4;
357     lnArrItemPtr->lineGC = None;
358     lnArrItemPtr->fillColor = None;
359     lnArrItemPtr->stipple = None;
360     lnArrItemPtr->shape = Nonconvex;
361     lnArrItemPtr->fillGC = None;
362     lnArrItemPtr->dotSize = 0;
363 
364     /*
365      * Configure the new geomap_lnarr item.
366      */
367 
368     if (configProc(interp, canvas, itemPtr, objc - 2, objv + 2, 0) != TCL_OK) {
369 	deleteProc(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
370 	return TCL_ERROR;
371     }
372 
373     return TCL_OK;
374 }
375 
376 /*
377  *------------------------------------------------------------------------
378  *
379  * parseGeoLnArrOption --
380  *
381  *	This procedure is invoked during option processing to handle
382  *	the "-lnarr" option.
383  *
384  * Results:
385  *	A standard Tcl result.
386  *
387  * Side effects:
388  *	The state for a given item gets replaced by the state
389  *	indicated in the value argument.
390  *
391  *------------------------------------------------------------------------
392  */
393 
394 static int
parseGeoLnArrOption(clientData,interp,tkwin,value,widgRec,offset)395 parseGeoLnArrOption(clientData, interp, tkwin, value, widgRec, offset)
396     ClientData clientData;	/* Not used */
397     Tcl_Interp *interp;		/* Current interpreter */
398     Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
399     char *value;		/* Value of the option */
400     char *widgRec;		/* Pointer to item record */
401     int offset;			/* Offset into widgRec */
402 {
403     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
404     char *arrNm = value;
405     Tclgeomap_LnArr tclGeoLnArr;
406     int n;
407 
408     if ( strlen(value) > 0 ) {
409 	/*
410 	 * User has requested that a linearray be displayed in the item.
411 	 */
412 
413 	tclGeoLnArr = Tclgeomap_GetLnArr(interp, arrNm);
414 	if ( !tclGeoLnArr ) {
415 	    Tcl_AppendResult(interp, "No linearray named ", value, NULL);
416 	    return TCL_ERROR;
417 	}
418 	if ( tclGeoLnArr == lnArrItemPtr->tclGeoLnArr ) {
419 	    return TCL_OK;
420 	}
421 	if (lnArrItemPtr->tclGeoLnArr) {
422 	    Tclgeomap_CnxLnArrDeleteTask(lnArrItemPtr->tclGeoLnArr,
423 		    lnArrItemPtr);
424 	}
425 	MapLnArrDestroy(lnArrItemPtr->mapLnArr);
426 	lnArrItemPtr->mapLnArr = NULL;
427 	lnArrItemPtr->updateMap = 1;
428 	for (n = 0; n < lnArrItemPtr->nLines; n++) {
429 	    CKFREE((char *)lnArrItemPtr->coordPtrPtr[n]);
430 	    lnArrItemPtr->coordPtrPtr[n] = NULL;
431 	}
432 	lnArrItemPtr->tclGeoLnArr = tclGeoLnArr;
433 	Tclgeomap_AddLnArrDeleteTask(tclGeoLnArr, forgetLnArr, lnArrItemPtr);
434 	lnArrItemPtr->nLines = 0;
435     } else {
436 	/*
437 	 * User has requested that NO linearray be displayed in the item.
438 	 */
439 
440 	if (lnArrItemPtr->tclGeoLnArr) {
441 	    Tclgeomap_CnxLnArrDeleteTask(lnArrItemPtr->tclGeoLnArr,
442 		    lnArrItemPtr);
443 	    for (n = 0; n < lnArrItemPtr->nLines; n++) {
444 		CKFREE((char *)lnArrItemPtr->coordPtrPtr[n]);
445 		lnArrItemPtr->coordPtrPtr[n] = NULL;
446 	    }
447 	    lnArrItemPtr->tclGeoLnArr = NULL;
448 	    MapLnArrDestroy(lnArrItemPtr->mapLnArr);
449 	    lnArrItemPtr->mapLnArr = NULL;
450 	    lnArrItemPtr->updateMap = 0;
451 	    lnArrItemPtr->nLines = 0;
452 	}
453     }
454     return TCL_OK;
455 }
456 
457 /*
458  *------------------------------------------------------------------------
459  *
460  * printGeoLnArrOption --
461  *
462  *	This procedure is invoked by the Tk configuration code
463  *	to produce a printable string for the "-refpoint" configuration
464  *	option.
465  *
466  * Results:
467  *	The return value is a string describing the state for
468  *	the item referred to by "widgRec".  In addition, *freeProcPtr
469  *	is filled in with the address of a procedure to call to free
470  *	the result string when it's no longer needed.
471  *
472  * Side effects:
473  * 	None.
474  *
475  *------------------------------------------------------------------------
476  */
477 
478 static char *
printGeoLnArrOption(clientData,tkwin,widgRec,offset,freeProc)479 printGeoLnArrOption(clientData, tkwin, widgRec, offset, freeProc)
480     ClientData clientData;	/* Not used */
481     Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
482     char *widgRec;		/* Pointer to item record */
483     int offset;			/* Offset into widgRec */
484     Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
485 				 * information about how to reclaim
486 				 * storage for return string. */
487 {
488     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
489     CONST char *arrNm;
490     char *rtn;
491 
492     arrNm = lnArrItemPtr->tclGeoLnArr
493 	?  Tclgeomap_LnArrName(lnArrItemPtr->tclGeoLnArr)
494 	: "";
495     rtn = Tcl_Alloc(strlen(arrNm) + 1);
496     strcpy(rtn, arrNm);
497     *freeProc = Tcl_Free;
498     return rtn;
499 }
500 
501 /*
502  *------------------------------------------------------------------------
503  *
504  * parseRefPtOption --
505  *
506  *	This procedure is invoked during option processing to handle
507  *	the "-refpoint" option.
508  *
509  * Results:
510  *	A standard Tcl result.
511  *
512  * Side effects:
513  *	The state for a given item gets replaced by the state
514  *	indicated in the value argument.
515  *
516  *------------------------------------------------------------------------
517  */
518 
519 static int
parseRefPtOption(clientData,interp,tkwin,value,widgRec,offset)520 parseRefPtOption(clientData, interp, tkwin, value, widgRec, offset)
521     ClientData clientData;	/* Not used */
522     Tcl_Interp *interp;		/* Current interpreter */
523     Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
524     char *value;		/* Value of the option */
525     char *widgRec;		/* Pointer to item record */
526     int offset;			/* Offset into widgRec */
527 {
528 
529     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
530     GeoPt refPt;
531     double lat, lon;
532 
533     if (sscanf(value, "%lf %lf", &lat, &lon) == 2) {
534 	refPt = GeoPtFmDeg(lat, lon);
535 	lnArrItemPtr->refPt = GwchLonPt(refPt);
536 	lnArrItemPtr->updateMap = lnArrItemPtr->updateCvs = 1;
537 	return TCL_OK;
538     } else {
539 	Tcl_AppendResult(interp, "Expected {lat lon}, got ", value, NULL);
540 	return TCL_ERROR;
541     }
542 }
543 
544 /*
545  *------------------------------------------------------------------------
546  *
547  * printRefPtOption --
548  *
549  *	This procedure is invoked by the Tk configuration code
550  *	to produce a printable string for the "-refpoint" configuration
551  *	option.
552  *
553  * Results:
554  *	The return value is a string describing the state for
555  *	the item referred to by "widgRec".  In addition, *freeProcPtr
556  *	is filled in with the address of a procedure to call to free
557  *	the result string when it's no longer needed.
558  *
559  * Side effects:
560  * 	None.
561  *
562  *------------------------------------------------------------------------
563  */
564 
565 static char *
printRefPtOption(clientData,tkwin,widgRec,offset,freeProc)566 printRefPtOption(clientData, tkwin, widgRec, offset, freeProc)
567     ClientData clientData;	/* Not used */
568     Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
569     char *widgRec;		/* Pointer to item record */
570     int offset;			/* Offset into widgRec */
571     Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
572 				 * information about how to reclaim
573 				 * storage for return string. */
574 {
575     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
576     char lat[TCL_DOUBLE_SPACE], lon[TCL_DOUBLE_SPACE];
577     char *coordPtr[2];
578     double dLat, dLon;
579 
580     coordPtr[0] = lat;
581     coordPtr[1] = lon;
582     *freeProc = Tcl_Free;
583     GeoPtGetDeg(lnArrItemPtr->refPt, &dLat, &dLon);
584     Tcl_PrintDouble(NULL, dLat, lat);
585     Tcl_PrintDouble(NULL, dLon, lon);
586     return Tcl_Merge(2, coordPtr);
587 }
588 
589 /*
590  *------------------------------------------------------------------------
591  *
592  * parseProjOption --
593  *
594  *	This procedure is invoked during option processing to handle
595  *	the "-projection" option.
596  *
597  * Results:
598  *	A standard Tcl result.
599  *
600  * Side effects:
601  *	The state for a given item gets replaced by the state
602  *	indicated in the value argument.
603  *
604  *------------------------------------------------------------------------
605  */
606 
607 static int
parseProjOption(clientData,interp,tkwin,value,widgRec,offset)608 parseProjOption(clientData, interp, tkwin, value, widgRec, offset)
609     ClientData clientData;	/* Not used */
610     Tcl_Interp *interp;		/* Current interpreter */
611     Tk_Window tkwin;		/* Window containing the linearray item */
612     char *value;		/* Value of the option */
613     char *widgRec;		/* Pointer to item record */
614     int offset;			/* Offset into widgRec */
615 {
616     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
617     Tclgeomap_Proj proj;
618 
619     if (*value == '\0') {
620 	if (lnArrItemPtr->proj) {
621 	    Tclgeomap_CnxProjUpdateTask(lnArrItemPtr->proj, lnArrItemPtr);
622 	    Tclgeomap_CnxProjDeleteTask(lnArrItemPtr->proj, lnArrItemPtr);
623 	}
624 	lnArrItemPtr->proj = NULL;
625 	return TCL_OK;
626     }
627     if ( !(proj = Tclgeomap_GetProj(interp, value)) ) {
628 	Tcl_AppendResult(interp, "No projection named ", value, NULL);
629 	return TCL_ERROR;
630     }
631     if (lnArrItemPtr->proj) {
632 	Tclgeomap_CnxProjUpdateTask(lnArrItemPtr->proj, lnArrItemPtr);
633 	Tclgeomap_CnxProjDeleteTask(lnArrItemPtr->proj, lnArrItemPtr);
634     }
635     lnArrItemPtr->proj = proj;
636     Tclgeomap_AddProjUpdateTask(lnArrItemPtr->proj, updateMap, lnArrItemPtr);
637     Tclgeomap_AddProjDeleteTask(lnArrItemPtr->proj, forgetProj, lnArrItemPtr);
638     lnArrItemPtr->updateMap = 1;
639     return TCL_OK;
640 }
641 
642 /*
643  *------------------------------------------------------------------------
644  *
645  * printProjOption --
646  *
647  *	This procedure is invoked by the Tk configuration code
648  *	to produce a printable string for the "-projection" configuration
649  *	option.
650  *
651  * Results:
652  *	The return value is a string describing the state for
653  *	the item referred to by "widgRec".  In addition, *freeProcPtr
654  *	is filled in with the address of a procedure to call to free
655  *	the result string when it's no longer needed.
656  *
657  * Side effects:
658  * 	None.
659  *
660  *------------------------------------------------------------------------
661  */
662 
663 static char *
printProjOption(clientData,tkwin,widgRec,offset,freeProc)664 printProjOption(clientData, tkwin, widgRec, offset, freeProc)
665     ClientData clientData;	/* Not used */
666     Tk_Window tkwin;		/* Window containing the geomap item */
667     char *widgRec;		/* Pointer to item record */
668     int offset;			/* Offset into widgRec */
669     Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
670 				 * information about how to reclaim
671 				 * storage for return string. */
672 {
673     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
674     CONST char *projNm;
675     char *rtn;
676 
677     projNm = lnArrItemPtr->proj ? Tclgeomap_ProjName(lnArrItemPtr->proj) : "";
678     rtn = Tcl_Alloc(strlen(projNm) + 1);
679     strcpy(rtn, projNm);
680     *freeProc = Tcl_Free;
681     return rtn;
682 }
683 
684 /*
685  *------------------------------------------------------------------------
686  *
687  * parseShapeOption --
688  *
689  *	This procedure is invoked during option processing to handle
690  *	the "-shape" option.
691  *
692  * Results:
693  *	A standard Tcl result.
694  *
695  * Side effects:
696  *	The state for a given item gets replaced by the state
697  *	indicated in the value argument.
698  *
699  *------------------------------------------------------------------------
700  */
701 
702 static int
parseShapeOption(clientData,interp,tkwin,value,widgRec,offset)703 parseShapeOption(clientData, interp, tkwin, value, widgRec, offset)
704     ClientData clientData;	/* Not used */
705     Tcl_Interp *interp;		/* Current interpreter */
706     Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
707     char *value;		/* Value of the option */
708     char *widgRec;		/* Pointer to item record */
709     int offset;			/* Offset into widgRec */
710 {
711     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
712 
713     if (strcmp(value, "Convex") == 0) {
714 	lnArrItemPtr->shape = Convex;
715     } else if (strcmp(value, "Nonconvex") == 0) {
716 	lnArrItemPtr->shape = Nonconvex;
717     } else if (strcmp(value, "Complex") == 0) {
718 	lnArrItemPtr->shape = Complex;
719     } else {
720 	Tcl_AppendResult(interp,
721 		"Shape must be \"Convex\", \"Nonconvex\" or \"Complex\"", NULL);
722 	return TCL_ERROR;
723     }
724     return TCL_OK;
725 }
726 
727 /*
728  *------------------------------------------------------------------------
729  *
730  * printShapeOption --
731  *
732  *	This procedure is invoked by the Tk configuration code
733  *	to produce a printable string for the "-shape" configuration
734  *	option.
735  *
736  * Results:
737  *	The return value is a string describing the state for
738  *	the item referred to by "widgRec".  In addition, *freeProcPtr
739  *	is filled in with the address of a procedure to call to free
740  *	the result string when it's no longer needed.
741  *
742  * Side effects:
743  * 	None.
744  *
745  *------------------------------------------------------------------------
746  */
747 
748 static char *
printShapeOption(clientData,tkwin,widgRec,offset,freeProc)749 printShapeOption(clientData, tkwin, widgRec, offset, freeProc)
750     ClientData clientData;	/* Not used */
751     Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
752     char *widgRec;		/* Pointer to item record */
753     int offset;			/* Offset into widgRec */
754     Tcl_FreeProc **freeProc;	/* Not used */
755 {
756     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
757     char *rslt = "Convex";
758 
759     switch (lnArrItemPtr->shape) {
760 	case Convex:
761 	    rslt = "Convex";
762 	    break;
763 	case Nonconvex:
764 	    rslt = "Nonconvex";
765 	    break;
766 	case Complex:
767 	    rslt = "Complex";
768 	    break;
769     }
770     return rslt;
771 }
772 
773 /*
774  *------------------------------------------------------------------------
775  *
776  * parseLineStyleOption --
777  *
778  *	This procedure is invoked during option processing to handle
779  *	the "-linestyle" option.
780  *
781  * Results:
782  *	A standard Tcl result.
783  *
784  * Side effects:
785  *	The state for a given item gets replaced by the state
786  *	indicated in the value argument.
787  *
788  *------------------------------------------------------------------------
789  */
790 
791 static int
parseLineStyleOption(clientData,interp,tkwin,value,widgRec,offset)792 parseLineStyleOption(clientData, interp, tkwin, value, widgRec, offset)
793     ClientData clientData;	/* Not used */
794     Tcl_Interp *interp;		/* Current interpreter */
795     Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
796     char *value;		/* Value of the option */
797     char *widgRec;		/* Pointer to item record */
798     int offset;			/* Offset into widgRec */
799 {
800     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
801 
802     if (strcmp(value, "LineSolid") == 0) {
803 	lnArrItemPtr->lineStyle = LineSolid;
804     } else if (strcmp(value, "LineOnOffDash") == 0) {
805 	lnArrItemPtr->lineStyle = LineOnOffDash;
806     } else if (strcmp(value, "LineDoubleDash") == 0) {
807 	lnArrItemPtr->lineStyle = LineDoubleDash;
808     } else {
809 	Tcl_AppendResult(interp,
810 		"LineStyle must be \"LineSolid\", \"LineOnOffDash\" or"
811 		" \"LineDoubleDash\"", NULL);
812 	return TCL_ERROR;
813     }
814     return TCL_OK;
815 }
816 
817 /*
818  *------------------------------------------------------------------------
819  *
820  * printLineStyleOption --
821  *
822  *	This procedure is invoked by the Tk configuration code
823  *	to produce a printable string for the "-linestyle" configuration
824  *	option.
825  *
826  * Results:
827  *	The return value is a string describing the state for
828  *	the item referred to by "widgRec".  In addition, *freeProcPtr
829  *	is filled in with the address of a procedure to call to free
830  *	the result string when it's no longer needed.
831  *
832  * Side effects:
833  * 	None.
834  *
835  *------------------------------------------------------------------------
836  */
837 
838 static char *
printLineStyleOption(clientData,tkwin,widgRec,offset,freeProc)839 printLineStyleOption(clientData, tkwin, widgRec, offset, freeProc)
840     ClientData clientData;	/* Not used */
841     Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
842     char *widgRec;		/* Pointer to item record */
843     int offset;			/* Offset into widgRec */
844     Tcl_FreeProc **freeProc;	/* Not used */
845 {
846     LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
847     char *rslt = "Convex";
848 
849     switch (lnArrItemPtr->lineStyle) {
850 	case LineSolid:
851 	    rslt = "LineSolid";
852 	    break;
853 	case LineOnOffDash:
854 	    rslt = "LineOnOffDash";
855 	    break;
856 	case LineDoubleDash:
857 	    rslt = "LineDoubleDash";
858 	    break;
859     }
860     return rslt;
861 }
862 
863 /*
864  *--------------------------------------------------------------
865  *
866  * configProc --
867  *
868  *	This procedure is invoked to configure various aspects
869  *	of a geomap_lnarr item, such as its anchor position.
870  *
871  * Results:
872  *	A standard Tcl result code.  If an error occurs, then
873  *	an error message is left in the interp's result.
874  *
875  * Side effects:
876  *	Configuration information may be set for itemPtr.
877  *
878  *--------------------------------------------------------------
879  */
880 
881 static int
configProc(interp,canvas,itemPtr,objc,objv,flags)882 configProc(interp, canvas, itemPtr, objc, objv, flags)
883     Tcl_Interp *interp;		/* Used for error reporting. */
884     Tk_Canvas canvas;		/* Canvas containing itemPtr. */
885     Tk_Item *itemPtr;		/* Geolinearray item to reconfigure. */
886     int objc;			/* Number of elements in objv.  */
887     Tcl_Obj *CONST objv[];	/* Arguments describing things to configure. */
888     int flags;			/* Flags to pass to Tk_ConfigureWidget. */
889 {
890     LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
891     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
892     unsigned long mask;
893     XGCValues gcVals;
894     GC gc;
895 
896     /*
897      * Configure the item.
898      */
899 
900     if (Tk_ConfigureWidget(interp, Tk_CanvasTkwin(canvas),
901 		geoLnArrType.configSpecs, objc, (char **)objv,
902 		(char *)lnArrItemPtr, flags|TK_CONFIG_OBJS) != TCL_OK) {
903 	return TCL_ERROR;
904     }
905     if (lnArrItemPtr->lineColor != None) {
906 	gcVals.foreground = lnArrItemPtr->lineColor->pixel;
907 	gcVals.line_width = lnArrItemPtr->width;
908 	gcVals.line_style = lnArrItemPtr->lineStyle;
909 	gcVals.dashes = lnArrItemPtr->dashes;
910 	mask = GCForeground|GCLineWidth|GCLineStyle|GCDashList;
911 	gc = Tk_GetGC(tkwin, mask, &gcVals);
912 	if (lnArrItemPtr->lineGC != None) {
913 	    Tk_FreeGC(Tk_Display(tkwin), lnArrItemPtr->lineGC);
914 	}
915 	lnArrItemPtr->lineGC = gc;
916     }
917 
918     if (lnArrItemPtr->fillColor != None) {
919 	gcVals.foreground = lnArrItemPtr->fillColor->pixel;
920 	mask = GCForeground;
921 	if (lnArrItemPtr->stipple != None) {
922 	    gcVals.stipple = lnArrItemPtr->stipple;
923 	    gcVals.fill_style = FillStippled;
924 	    mask |= GCStipple|GCFillStyle;
925 	}
926 	gc = Tk_GetGC(tkwin, mask, &gcVals);
927 	if (lnArrItemPtr->fillGC != None) {
928 	    Tk_FreeGC(Tk_Display(tkwin), lnArrItemPtr->fillGC);
929 	}
930 	lnArrItemPtr->fillGC = gc;
931     }
932     updateCvs(lnArrItemPtr);
933     return TCL_OK;
934 }
935 
936 /*
937  *--------------------------------------------------------------
938  *
939  * coordProc --
940  *
941  *	This procedure is invoked to process the "coords" widget
942  *	command on geomap_lnarr items.  See the user documentation for
943  *	details on what it does.
944  *
945  * Results:
946  *	Returns TCL_OK or TCL_ERROR, and sets the interp's result.
947  *
948  * Side effects:
949  *	The coordinates for the given item may be changed.
950  *
951  *--------------------------------------------------------------
952  */
953 
954 static int
coordProc(interp,canvas,itemPtr,objc,objv)955 coordProc(interp, canvas, itemPtr, objc, objv)
956     Tcl_Interp *interp;			/* Used for error reporting. */
957     Tk_Canvas canvas;			/* Canvas containing item. */
958     Tk_Item *itemPtr;			/* Item whose coordinates are to be
959 					 * read or modified. */
960     int objc;				/* Number of coordinates supplied in
961 					 * objv. */
962     Tcl_Obj *CONST objv[];		/* Array of coordinates: x1, y1,
963 					 * x2, y2, ... */
964 {
965     LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
966 
967     if (objc == 0) {
968 	/*
969 	 * Set result to item coordinates
970 	 */
971 
972 	Tcl_Obj *obj = Tcl_NewObj();
973 	Tcl_Obj *subobj = Tcl_NewDoubleObj(lnArrItemPtr->xRef);
974 	Tcl_ListObjAppendElement(interp, obj, subobj);
975 	subobj = Tcl_NewDoubleObj(lnArrItemPtr->yRef);
976 	Tcl_ListObjAppendElement(interp, obj, subobj);
977 	Tcl_SetObjResult(interp, obj);
978     } else if (objc == 1) {
979 	Tcl_Obj *lineObj, *rslt;	/* Result */
980 	int nl, np;			/* Loop indeces */
981 	double *coordPtr;		/* Canvas coordinate to put in result */
982 
983 	if (strcmp(Tcl_GetString(objv[0]), "dump") == 0) {
984 	    /*
985 	     * User wants dump of canvas coordinates for the linearray.
986 	     */
987 
988 	    updateCvs(lnArrItemPtr);
989 	    rslt = Tcl_NewObj();
990 	    if (lnArrItemPtr->nLines == 1) {
991 		/*
992 		 * Array has one line.  Set result to list of {x y x y ...}
993 		 * values.
994 		 */
995 
996 		for (coordPtr = lnArrItemPtr->coordPtrPtr[0], np = 0;
997 			np < lnArrItemPtr->nPtsPtr[0];
998 			np++) {
999 		    Tcl_ListObjAppendElement(interp, rslt,
1000 			    Tcl_NewDoubleObj(*coordPtr++));
1001 		    Tcl_ListObjAppendElement(interp, rslt,
1002 			    Tcl_NewDoubleObj(*coordPtr++));
1003 		}
1004 	    } else {
1005 		/*
1006 		 * If array has several lines, set result to a list of lists of
1007 		 * {x y x y ...} values.
1008 		 */
1009 
1010 		for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1011 		    lineObj = Tcl_NewObj();
1012 		    for (coordPtr = lnArrItemPtr->coordPtrPtr[nl], np = 0;
1013 			    np < lnArrItemPtr->nPtsPtr[nl];
1014 			    np++) {
1015 			Tcl_ListObjAppendElement(interp, lineObj,
1016 				Tcl_NewDoubleObj(*coordPtr++));
1017 			Tcl_ListObjAppendElement(interp, lineObj,
1018 				Tcl_NewDoubleObj(*coordPtr++));
1019 		    }
1020 		    Tcl_ListObjAppendElement(interp, rslt, lineObj);
1021 		}
1022 	    }
1023 	    Tcl_SetObjResult(interp, rslt);
1024 	    return TCL_OK;
1025 	} else {
1026 	    /*
1027 	     * New coordinates given on command line in form
1028 	     * "pathName coords {x y}"
1029 	     */
1030 
1031 	    Tcl_Obj **coordsPtr;
1032 	    int n;
1033 
1034 	    if (Tcl_ListObjGetElements(interp, objv[0], &n, &coordsPtr)
1035 		    != TCL_OK || n != 2) {
1036 		Tcl_AppendResult(interp, "Could not split coords list\n", NULL);
1037 		return TCL_ERROR;
1038 	    }
1039 	    if ((Tk_CanvasGetCoordFromObj(interp, canvas, coordsPtr[0],
1040 			    &lnArrItemPtr->xRef) != TCL_OK)
1041 		    || (Tk_CanvasGetCoordFromObj(interp, canvas, coordsPtr[1],
1042 			    &lnArrItemPtr->yRef) != TCL_OK)) {
1043 		return TCL_ERROR;
1044 	    }
1045 	    lnArrItemPtr-> updateCvs = 1;
1046 	    updateCvs(lnArrItemPtr);
1047 	}
1048     } else if (objc == 2) {
1049 	/*
1050 	 * New coordinates given on command line in form "pathName coords x y"
1051 	 */
1052 
1053 	if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0],
1054 			&lnArrItemPtr->xRef) != TCL_OK)
1055 		|| (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1],
1056 			&lnArrItemPtr->yRef) != TCL_OK)) {
1057 	    return TCL_ERROR;
1058 	}
1059 	lnArrItemPtr-> updateCvs = 1;
1060 	updateCvs(lnArrItemPtr);
1061     } else {
1062 	Tcl_AppendResult(interp, "Coordinates must be specified as",
1063 		" \"x y\" or \"{x y}\" or \"dump\"", NULL);
1064 	return TCL_ERROR;
1065     }
1066     return TCL_OK;
1067 }
1068 
1069 /*
1070  *--------------------------------------------------------------
1071  *
1072  * deleteProc --
1073  *
1074  *	This procedure is called to clean up the data structure
1075  *	associated with a geomap_lnarr item.
1076  *
1077  * Results:
1078  *	None.
1079  *
1080  * Side effects:
1081  *	Resources associated with itemPtr are released.
1082  *
1083  *--------------------------------------------------------------
1084  */
1085 
1086 static void
deleteProc(canvas,itemPtr,display)1087 deleteProc(canvas, itemPtr, display)
1088     Tk_Canvas canvas;			/* Info about overall canvas widget. */
1089     Tk_Item *itemPtr;			/* Item that is being deleted. */
1090     Display *display;			/* Display containing window for
1091 					 * canvas. */
1092 {
1093     LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1094     int nl;
1095 
1096     Tclgeomap_CnxProjUpdateTask(lnArrItemPtr->proj, lnArrItemPtr);
1097     Tclgeomap_CnxProjDeleteTask(lnArrItemPtr->proj, lnArrItemPtr);
1098     Tclgeomap_CnxLnArrDeleteTask(lnArrItemPtr->tclGeoLnArr, lnArrItemPtr);
1099     for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1100 	CKFREE((char *)lnArrItemPtr->coordPtrPtr[nl]);
1101     }
1102     CKFREE((char *)lnArrItemPtr->nPtsPtr);
1103     CKFREE((char *)lnArrItemPtr->coordPtrPtr);
1104     if (lnArrItemPtr->lineGC != None) {
1105 	Tk_FreeGC(display, lnArrItemPtr->lineGC);
1106     }
1107     if (lnArrItemPtr->fillGC != None) {
1108 	Tk_FreeGC(display, lnArrItemPtr->fillGC);
1109     }
1110     Tk_FreeOptions(geoLnArrType.configSpecs, (char *)lnArrItemPtr,
1111 	    display, 0);
1112 }
1113 
1114 /*
1115  *--------------------------------------------------------------
1116  *
1117  * forgetLnArr --
1118  *
1119  * 	This procedure modifies a LnArrItem if the linearray it is
1120  * 	displaying is deleted.
1121  *
1122  * Results:
1123  * 	None.
1124  *
1125  * Side effects:
1126  * 	The item is removed from the display and appropriate members are
1127  * 	updated.
1128  *
1129  *--------------------------------------------------------------
1130  */
1131 
1132 static void
forgetLnArr(clientData)1133 forgetLnArr(clientData)
1134     ClientData clientData;	/* Geolinearray item */
1135 {
1136     LnArrItem *lnArrItemPtr = (LnArrItem *)clientData;
1137     Tk_Window tkwin = Tk_CanvasTkwin(lnArrItemPtr->canvas);
1138 
1139     lnArrItemPtr->tclGeoLnArr = NULL;
1140     MapLnArrDestroy(lnArrItemPtr->mapLnArr);
1141     lnArrItemPtr->mapLnArr = NULL;
1142     lnArrItemPtr->updateMap = 0;
1143     if (lnArrItemPtr->lineGC != None) {
1144 	Tk_FreeGC(Tk_Display(tkwin), lnArrItemPtr->lineGC);
1145     }
1146     lnArrItemPtr->lineGC = None;
1147     if (lnArrItemPtr->fillGC != None) {
1148 	Tk_FreeGC(Tk_Display(tkwin), lnArrItemPtr->fillGC);
1149     }
1150     lnArrItemPtr->fillGC = None;
1151     hide(lnArrItemPtr);
1152 }
1153 
1154 /*
1155  *--------------------------------------------------------------
1156  *
1157  * forgetProj --
1158  *
1159  * 	This procedure modifies a LnArrItem if the projection it is
1160  * 	using is deleted.
1161  *
1162  * Results:
1163  * 	None.
1164  *
1165  * Side effects:
1166  * 	The item is removed from the display and appropriate members are
1167  * 	updated.
1168  *
1169  *--------------------------------------------------------------
1170  */
1171 
1172 static void
forgetProj(clientData)1173 forgetProj(clientData)
1174     ClientData clientData;	/* Geolinearray item */
1175 {
1176     LnArrItem *lnArrItemPtr = (LnArrItem *)clientData;
1177 
1178     lnArrItemPtr->proj = NULL;
1179     MapLnArrDestroy(lnArrItemPtr->mapLnArr);
1180     lnArrItemPtr->mapLnArr = NULL;
1181     lnArrItemPtr->updateMap = 0;
1182     hide(lnArrItemPtr);
1183 }
1184 
1185 /*
1186  *--------------------------------------------------------------
1187  *
1188  * hide --
1189  *
1190  * 	This procedure removes a linearray from the display, although the
1191  * 	item continues to exist.
1192  *
1193  * Results:
1194  * 	None.
1195  *
1196  * Side effects:
1197  * 	None.
1198  *
1199  *--------------------------------------------------------------
1200  */
1201 
1202 static void
hide(lnArrItemPtr)1203 hide(lnArrItemPtr)
1204     LnArrItem *lnArrItemPtr;	/* Linearray to remove from display */
1205 {
1206     int n;
1207 
1208     for (n = 0; n < lnArrItemPtr->nLines; n++) {
1209 	CKFREE((char *)lnArrItemPtr->coordPtrPtr[n]);
1210 	lnArrItemPtr->coordPtrPtr[n] = NULL;
1211     }
1212     lnArrItemPtr->nLines = 0;
1213     Tk_CanvasEventuallyRedraw(lnArrItemPtr->canvas,
1214 	    lnArrItemPtr->header.x1, lnArrItemPtr->header.y1,
1215 	    lnArrItemPtr->header.x2, lnArrItemPtr->header.y2);
1216     lnArrItemPtr->header.x1 = lnArrItemPtr->header.y1 = INT_MAX;
1217     lnArrItemPtr->header.x2 = lnArrItemPtr->header.y2 = INT_MIN;
1218     lnArrItemPtr->updateCvs = 0;
1219 }
1220 
1221 /*
1222  *--------------------------------------------------------------
1223  *
1224  * displayProc --
1225  *
1226  *	This procedure is invoked to draw a geomap_lnarr item in a given
1227  *	drawable.
1228  *
1229  * Results:
1230  *	None.
1231  *
1232  * Side effects:
1233  *	ItemPtr is drawn in drawable using the transformation
1234  *	information in canvas.
1235  *
1236  *--------------------------------------------------------------
1237  */
1238 
1239 static void
displayProc(canvas,itemPtr,display,drawable,x,y,width,height)1240 displayProc(canvas, itemPtr, display, drawable, x, y, width, height)
1241     Tk_Canvas canvas;			/* Canvas that contains item. */
1242     Tk_Item *itemPtr;			/* Item to be displayed. */
1243     Display *display;			/* Display on which to draw item. */
1244     Drawable drawable;			/* Pixmap or window in which to draw
1245 					 * item. */
1246     int x, y, width, height;		/* Describes region of canvas that
1247 					 * must be redisplayed. */
1248 {
1249     LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1250     static XPoint *xPtsPtr;
1251     XPoint *pPtr, *ePtr;
1252     double *coordPtr, xc, yc;
1253     unsigned sz, nl, nMax;
1254     int nPts;
1255     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
1256 
1257     if (!tkwin || !Tk_IsMapped(tkwin)) {
1258 	return;
1259     }
1260     if ( lnArrItemPtr->nLines == 0 || !lnArrItemPtr->nPtsPtr
1261 	    || !lnArrItemPtr->coordPtrPtr ) {
1262 	return;
1263     }
1264     if (lnArrItemPtr->updateMap) {
1265 	updateMap(lnArrItemPtr);
1266     }
1267     if ( !lnArrItemPtr->mapLnArr ) {
1268 	return;
1269     }
1270     if (lnArrItemPtr->updateCvs) {
1271 	updateCvs(lnArrItemPtr);
1272     }
1273 
1274     nMax = lnArrItemPtr->nMax;
1275     if ((lnArrItemPtr->smooth) && (nMax > 2)) {
1276 	sz = sizeof(XPoint) * TkgeomapMakeBezierCurve(canvas, NULL,
1277 		(int)nMax, lnArrItemPtr->splineSteps, NULL, NULL);
1278     } else {
1279 	sz = sizeof(XPoint) * nMax;
1280     }
1281     if (sz == 0) {
1282 	return;
1283     } else {
1284 	xPtsPtr = (XPoint *)CKREALLOC((char *)xPtsPtr, sz);
1285     }
1286 
1287     for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1288 
1289 	nPts = lnArrItemPtr->nPtsPtr[nl];
1290 
1291 	/*
1292 	 * Put the shape into drawable (X) coordinates.
1293 	 */
1294 
1295 	if ((lnArrItemPtr->smooth) && (nMax > 2)) {
1296 	    nPts = TkgeomapMakeBezierCurve(canvas,
1297 		    lnArrItemPtr->coordPtrPtr[nl], nPts,
1298 		    lnArrItemPtr->splineSteps, xPtsPtr, NULL);
1299 	} else {
1300 	    for (pPtr = xPtsPtr, ePtr = pPtr + nPts,
1301 		    coordPtr = lnArrItemPtr->coordPtrPtr[nl];
1302 		    pPtr < ePtr; pPtr++) {
1303 		xc = *coordPtr++;
1304 		yc = *coordPtr++;
1305 		Tk_CanvasDrawableCoords(canvas, xc, yc, &pPtr->x, &pPtr->y);
1306 	    }
1307 	}
1308 
1309 	/*
1310 	 * Fill in the area.
1311 	 */
1312 
1313 	if (lnArrItemPtr->fillColor != None) {
1314 	    XFillPolygon(display, drawable, lnArrItemPtr->fillGC, xPtsPtr,
1315 		    nPts, lnArrItemPtr->shape, CoordModeOrigin);
1316 	}
1317 
1318 
1319 	/*
1320 	 * Draw the line segments.
1321 	 */
1322 
1323 	if (lnArrItemPtr->width > 0
1324 		&& lnArrItemPtr->lineColor != None) {
1325 	    XDrawLines(display, drawable, lnArrItemPtr->lineGC, xPtsPtr,
1326 		    nPts, CoordModeOrigin);
1327 	}
1328 
1329 	/*
1330 	 * Draw dots at segment endpoints.
1331 	 */
1332 
1333 	if (lnArrItemPtr->dotSize > 0 && lnArrItemPtr->lineColor != None) {
1334 	    for (pPtr = xPtsPtr, ePtr = pPtr + nPts,
1335 		    coordPtr = lnArrItemPtr->coordPtrPtr[nl];
1336 		    pPtr < ePtr; pPtr++) {
1337 		unsigned dotSize = lnArrItemPtr->dotSize;
1338 		int spShift = dotSize / 2;
1339 		short x, y;
1340 
1341 		xc = *coordPtr++;
1342 		yc = *coordPtr++;
1343 		Tk_CanvasDrawableCoords(canvas, xc, yc, &x, &y);
1344 		if (dotSize == 1) {
1345 		    XDrawPoint(display, drawable, lnArrItemPtr->lineGC, x, y);
1346 		} else {
1347 		    XFillArc(display, drawable, lnArrItemPtr->lineGC,
1348 			    x - spShift, y - spShift, dotSize, dotSize,
1349 			    0, 23040);
1350 		}
1351 	    }
1352 	}
1353     }
1354 }
1355 
1356 /*
1357  *--------------------------------------------------------------
1358  *
1359  * pointProc --
1360  *
1361  *	Computes the distance from a given point to a given
1362  *	geomap_lnarr item, in canvas units.
1363  *
1364  * Results:
1365  *	The return value is 0.0 if the point whose x and y coordinates
1366  *	are pointPtr[0] and pointPtr[1] is inside the geomap_lnarr.  If the
1367  *	point isn't inside the geomap_lnarr then the return value is the
1368  *	distance from the point to the geomap_lnarr.
1369  *
1370  * Side effects:
1371  *	None.
1372  *
1373  *--------------------------------------------------------------
1374  */
1375 
1376 static double
pointProc(canvas,itemPtr,pointPtr)1377 pointProc(canvas, itemPtr, pointPtr)
1378     Tk_Canvas canvas;			/* Canvas containing item. */
1379     Tk_Item *itemPtr;			/* Item to check against point. */
1380     double *pointPtr;			/* Pointer to x and y coordinates. */
1381 {
1382     LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1383     int nl, np, nPair;
1384     double **coordPtrPtr;
1385     int *nPtsPtr;
1386     double x0 = pointPtr[0], y0 = pointPtr[1];
1387     double end1Ptr[2], end2Ptr[2];
1388     double *coordPtr;
1389     double d, dx, dy, closest;
1390 
1391     if (lnArrItemPtr->updateCvs) {
1392 	updateCvs(lnArrItemPtr);
1393     }
1394     if ( !itemPtr || !lnArrItemPtr->coordPtrPtr || lnArrItemPtr->nLines == 0 ) {
1395 	return DBL_MAX;
1396     }
1397     coordPtrPtr = lnArrItemPtr->coordPtrPtr;
1398     nPtsPtr = lnArrItemPtr->nPtsPtr;
1399     if (lnArrItemPtr->fillColor != None) {
1400 	/*
1401 	 * Treat all lines as closed polygons.
1402 	 */
1403 
1404 	closest = DBL_MAX;
1405 	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1406 	    d = TkgeomapPolygonToPoint(coordPtrPtr[nl], nPtsPtr[nl], pointPtr);
1407 	    if (d == 0.0) {
1408 		return 0.0;
1409 	    } else if (d < closest) {
1410 		closest = d;
1411 	    }
1412 	}
1413     } else if (lnArrItemPtr->width < 0) {
1414 	/*
1415 	 * Points are not connected.  Return distance to nearest point.
1416 	 */
1417 
1418 	closest = DBL_MAX;
1419 	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1420 	    coordPtr = coordPtrPtr[nl];
1421 	    for (np = 0; np < nPtsPtr[nl]; np++) {
1422 		dx = x0 - *coordPtr++;
1423 		dy = y0 - *coordPtr++;
1424 		d = dx * dx + dy * dy;
1425 		closest = (d < closest) ? d : closest;
1426 	    }
1427 	}
1428 	closest = sqrt(closest);
1429     } else {
1430 	/*
1431 	 * Lines are either closed polygons that are not filled, or they
1432 	 * are open.
1433 	 */
1434 
1435 	closest = DBL_MAX;
1436 	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1437 	    coordPtr = coordPtrPtr[nl];
1438 	    nPair = nPtsPtr[nl] - 1;
1439 	    if (nPair == 0) {
1440 		continue;
1441 	    }
1442 	    for (np = 0, coordPtr = coordPtrPtr[nl];
1443 		    np < nPair; np++, coordPtr += 2) {
1444 		end1Ptr[0] = coordPtr[0];
1445 		end1Ptr[1] = coordPtr[1];
1446 		end2Ptr[0] = coordPtr[2];
1447 		end2Ptr[1] = coordPtr[3];
1448 		d = TkgeomapLineToPoint(end1Ptr, end2Ptr, pointPtr);
1449 		closest = (d < closest) ? d : closest;
1450 	    }
1451 	}
1452     }
1453     return closest;
1454 }
1455 
1456 /*
1457  *--------------------------------------------------------------
1458  *
1459  * areaProc --
1460  *
1461  *	This procedure is called to determine whether an item
1462  *	lies entirely inside, entirely outside, or overlapping
1463  *	a given rectangle.
1464  *
1465  * Results:
1466  *	-1 is returned if the item is entirely outside the area
1467  *	given by rectPtr, 0 if it overlaps, and 1 if it is entirely
1468  *	inside the given area.
1469  *
1470  * Side effects:
1471  *	None.
1472  *
1473  *--------------------------------------------------------------
1474  */
1475 
areaProc(canvas,itemPtr,rectPtr)1476 static int areaProc(canvas, itemPtr, rectPtr)
1477     Tk_Canvas canvas;		/* Canvas containing item. */
1478     Tk_Item *itemPtr;		/* Item to check against rectangle. */
1479     double *rectPtr;		/* Pointer to array of four coordinates
1480 				 * (x1, y1, x2, y2) describing rectangular
1481 				 * area.  */
1482 {
1483     LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1484     double x1, y1, x2, y2;	/* Limits of rectPtr */
1485     double x, y;
1486     int nl, np, nPts;
1487     double **coordPtrPtr = lnArrItemPtr->coordPtrPtr;
1488     int *nPtsPtr = lnArrItemPtr->nPtsPtr;
1489     double end1Ptr[2], end2Ptr[2];
1490     double *coordPtr;
1491     int currInside, prevInside;	/* Status of current and previous polygon, line,
1492 				 * or point.  1 means inside, 0 means
1493 				 * overlapping, and -1 means outside. */
1494 
1495     if (lnArrItemPtr->updateCvs) {
1496 	updateCvs(lnArrItemPtr);
1497     }
1498     if ( !lnArrItemPtr->coordPtrPtr || lnArrItemPtr->nLines == 0 ) {
1499 	return -1;
1500     }
1501     x1 = rectPtr[0];
1502     y1 = rectPtr[1];
1503     x2 = rectPtr[2];
1504     y2 = rectPtr[3];
1505     if (lnArrItemPtr->fillColor != None) {
1506 	/*
1507 	 * Treat all lines as closed polygons.
1508 	 */
1509 
1510 	prevInside = currInside
1511 	    = TkgeomapPolygonToArea(coordPtrPtr[0], nPtsPtr[0], rectPtr);
1512 	for (nl = 1; nl < lnArrItemPtr->nLines; nl++) {
1513 	    currInside = TkgeomapPolygonToPoint(coordPtrPtr[nl], nPtsPtr[nl],
1514 		    rectPtr);
1515 	    if (currInside == 0 || currInside != prevInside) {
1516 		return 0;
1517 	    }
1518 	    prevInside = currInside;
1519 	}
1520 	return currInside;
1521     } else if (lnArrItemPtr->width < 0) {
1522 	/*
1523 	 * Points are not connected.
1524 	 */
1525 
1526 	x = coordPtrPtr[0][0];
1527 	y = coordPtrPtr[0][1];
1528 	prevInside = currInside
1529 	    = (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1530 	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1531 	    coordPtr = coordPtrPtr[nl];
1532 	    for (np = 0; np < nPtsPtr[nl]; np++) {
1533 		x = *coordPtr++;
1534 		y = *coordPtr++;
1535 		currInside = (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1536 		if (currInside != prevInside) {
1537 		    return 0;
1538 		}
1539 		prevInside = currInside;
1540 	    }
1541 	}
1542 	return currInside;
1543     } else {
1544 	/*
1545 	 * Lines are either closed polygons that are not filled, or they
1546 	 * are open lines.
1547 	 */
1548 
1549 	nPts = nPtsPtr[0];
1550 	end1Ptr[0] = coordPtrPtr[0][0];
1551 	end1Ptr[1] = coordPtrPtr[0][1];
1552 	end2Ptr[0] = coordPtrPtr[0][2 * nPts - 2];
1553 	end2Ptr[1] = coordPtrPtr[0][2 * nPts - 1];
1554 	if ((end1Ptr[0] == end2Ptr[0]) && (end1Ptr[1] == end2Ptr[1])) {
1555 	    /*
1556 	     * First line in the linearray is closed.  Treat it as a
1557 	     * polygon.
1558 	     */
1559 
1560 	    prevInside = currInside
1561 		= TkgeomapPolygonToArea(coordPtrPtr[0], nPts, rectPtr);
1562 	    if (lnArrItemPtr->nLines == 1) {
1563 		return prevInside;
1564 	    }
1565 
1566 	} else {
1567 	    /*
1568 	     * First line in the linearray is open.  Compute the distances
1569 	     * to the segments.
1570 	     */
1571 
1572 	    x = coordPtrPtr[0][0];
1573 	    y = coordPtrPtr[0][1];
1574 	    prevInside = currInside
1575 		= (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1576 	    coordPtr = coordPtrPtr[0];
1577 	    for (np = 0; np < nPtsPtr[0]; np++) {
1578 		x = *coordPtr++;
1579 		y = *coordPtr++;
1580 		currInside = (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1581 		if (currInside != prevInside) {
1582 		    return 0;
1583 		}
1584 		currInside = prevInside;
1585 	    }
1586 	}
1587 	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1588 	    coordPtr = coordPtrPtr[nl];
1589 	    nPts = nPtsPtr[nl];
1590 	    end1Ptr[0] = coordPtr[0];
1591 	    end1Ptr[1] = coordPtr[1];
1592 	    end2Ptr[0] = coordPtr[2 * nPts - 2];
1593 	    end2Ptr[1] = coordPtr[2 * nPts - 1];
1594 	    if ((end1Ptr[0] == end2Ptr[0]) && (end1Ptr[1] == end2Ptr[1])) {
1595 		/*
1596 		 * This line in the linearray is closed.  Treat it as a
1597 		 * polygon.
1598 		 */
1599 
1600 		currInside = TkgeomapPolygonToArea(coordPtrPtr[0], nPts,
1601 			rectPtr);
1602 		if (currInside != prevInside) {
1603 		    return 0;
1604 		}
1605 		currInside = prevInside;
1606 	    } else {
1607 		/*
1608 		 * This line in the linearray is open.  Compute the distances
1609 		 * to the segments.
1610 		 */
1611 
1612 		for (np = 0; np < nPts; np++) {
1613 		    x = *coordPtr++;
1614 		    y = *coordPtr++;
1615 		    currInside = (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1616 		    if (currInside != prevInside) {
1617 			return 0;
1618 		    }
1619 		    currInside = prevInside;
1620 		}
1621 	    }
1622 	}
1623     }
1624     return -1;
1625 }
1626 
1627 /*
1628  *--------------------------------------------------------------
1629  *
1630  * postscriptProc --
1631  *
1632  *	This procedure is called to generate Postscript for
1633  *	geomap_lnarr items.
1634  *
1635  * Results:
1636  *	The return value is a standard Tcl result.  If an error
1637  *	occurs in generating Postscript then an error message is
1638  *	left in interp->result, replacing whatever used to be there.
1639  *	If no error occurs, then Postscript for the item is appended
1640  *	to the result.
1641  *
1642  * Side effects:
1643  *	None.
1644  *
1645  *--------------------------------------------------------------
1646  */
1647 
1648 static int
postscriptProc(interp,canvas,itemPtr,prepass)1649 postscriptProc(interp, canvas, itemPtr, prepass)
1650     Tcl_Interp *interp;			/* Leave Postscript or error message
1651 					 * here. */
1652     Tk_Canvas canvas;			/* Information about overall canvas. */
1653     Tk_Item *itemPtr;			/* Item for which Postscript is
1654 					 * wanted. */
1655     int prepass;			/* 1 means this is a prepass to
1656 					 * collect font information;  0 means
1657 					 * final Postscript is being created.*/
1658 {
1659     LnArrItem *lnArrItemPtr;	/* Geomap in which to draw line array */
1660     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
1661     static double *coord1Ptr;		/* Array of canvas coordinates for
1662     					 * one line.  This pointer holds
1663 					 * the result of CKREALLOC. */
1664     double *coordPtr;			/* Array of canvas coordinates for
1665     					 * one line.  May be the same as
1666 					 * coord1Ptr, or may point into the
1667 					 * item's coordinates array. */
1668     double x, y;
1669     int nl, nPts, np;			/* Loop indices and limits */
1670     char bytes[200];			/* Printing space */
1671     double cnr[8];			/* 4 corner points in canvas coord's */
1672 
1673     lnArrItemPtr = (LnArrItem *)itemPtr;
1674     if ( !lnArrItemPtr->tclGeoLnArr ) {
1675 	return TCL_OK;
1676     }
1677     if (lnArrItemPtr->updateMap) {
1678 	updateMap(lnArrItemPtr);
1679     }
1680     if (lnArrItemPtr->updateCvs) {
1681 	updateCvs(lnArrItemPtr);
1682     }
1683     if ( !lnArrItemPtr->mapLnArr || lnArrItemPtr->nLines == 0
1684 	    || !lnArrItemPtr->nPtsPtr || !lnArrItemPtr->coordPtrPtr) {
1685 	Tcl_AppendResult(interp,
1686 		"Could not generate postscript for linearray ",
1687 		Tclgeomap_LnArrName(lnArrItemPtr->tclGeoLnArr), NULL);
1688 	return TCL_ERROR;
1689     }
1690 
1691     Tcl_AppendResult(interp, "%% Drawing linearray ",
1692 		Tclgeomap_LnArrName(lnArrItemPtr->tclGeoLnArr), "\n", NULL);
1693 
1694     /*
1695      * Create clipping region for item
1696      */
1697 
1698     Tcl_AppendResult(interp, "%% Create clipping region for item\n", NULL);
1699     cnr[0] = 0;
1700     cnr[1] = 0;
1701     cnr[2] = Tk_Width(tkwin);
1702     cnr[3] = 0;
1703     cnr[4] = Tk_Width(tkwin);
1704     cnr[5] = Tk_Height(tkwin);
1705     cnr[6] = 0;
1706     cnr[7] = Tk_Height(tkwin);
1707     Tk_CanvasPsPath(interp, canvas, cnr, 4);
1708     Tcl_AppendResult(interp, "closepath clip\n", NULL);
1709 
1710     for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1711 
1712 	nPts = lnArrItemPtr->nPtsPtr[nl];
1713 	if (nPts == 0) {
1714 	    continue;
1715 	}
1716 	if ( !lnArrItemPtr->smooth || nPts < 3 ) {
1717 	    coordPtr = lnArrItemPtr->coordPtrPtr[nl];
1718 	} else {
1719 	    nPts = TkgeomapMakeBezierCurve(canvas, NULL,
1720 		    lnArrItemPtr->nPtsPtr[nl], lnArrItemPtr->splineSteps,
1721 		    NULL, NULL);
1722 	    coordPtr = coord1Ptr = (double *)CKREALLOC((char *)coord1Ptr,
1723 		    nPts * 2 * sizeof(double));
1724 	    nPts = TkgeomapMakeBezierCurve(canvas,
1725 		    lnArrItemPtr->coordPtrPtr[nl], lnArrItemPtr->nPtsPtr[nl],
1726 		    lnArrItemPtr->splineSteps, NULL, coordPtr);
1727 	}
1728 
1729 	/*
1730 	 * Fill in the area.
1731 	 */
1732 
1733 	if (lnArrItemPtr->fillColor) {
1734 	    Tk_CanvasPsColor(interp, canvas, lnArrItemPtr->fillColor);
1735 	    Tcl_AppendResult(interp, "newpath\n", NULL);
1736 	    Tk_CanvasPsPath(interp, canvas, coordPtr, nPts);
1737 	    Tcl_AppendResult(interp, "closepath\n", NULL);
1738 	    if (lnArrItemPtr->stipple != None) {
1739 		Tcl_AppendResult(interp, "clip\n", NULL);
1740 		Tcl_AppendResult(interp, "newpath\n", NULL);
1741 		Tk_CanvasPsPath(interp, canvas, coordPtr, nPts);
1742 		Tcl_AppendResult(interp, "closepath\n", NULL);
1743 		if (Tk_CanvasPsStipple(interp, canvas, lnArrItemPtr->stipple)
1744 			== TCL_ERROR) {
1745 		    return TCL_ERROR;
1746 		}
1747 	    } else {
1748 		Tcl_AppendResult(interp, "fill\n", NULL);
1749 	    }
1750 	}
1751 
1752 	/*
1753 	 * Draw the outline.
1754 	 */
1755 
1756 	if (lnArrItemPtr->width > 0
1757 		&& lnArrItemPtr->lineColor != None) {
1758 	    sprintf(bytes, "%d", lnArrItemPtr->width);
1759 	    Tcl_AppendResult(interp, bytes, " setlinewidth\n", NULL);
1760 	    if (lnArrItemPtr->lineStyle == LineSolid) {
1761 		Tcl_AppendResult(interp, "[] 0 setdash\n", NULL);
1762 	    } else if (lnArrItemPtr->lineStyle == LineOnOffDash) {
1763 		sprintf(bytes, "[%d %d] 0 setdash\n",
1764 			lnArrItemPtr->dashes, lnArrItemPtr->dashes);
1765 		Tcl_AppendResult(interp, bytes, NULL);
1766 	    }
1767 	    Tk_CanvasPsColor(interp, canvas, lnArrItemPtr->lineColor);
1768 	    Tcl_AppendResult(interp, "newpath\n", NULL);
1769 	    Tk_CanvasPsPath(interp, canvas, coordPtr, nPts);
1770 	    Tcl_AppendResult(interp, "stroke\n", NULL);
1771 	}
1772 
1773 	/*
1774 	 * Draw dots at segment endpoints.
1775 	 */
1776 
1777 	if (lnArrItemPtr->dotSize != 0) {
1778 	    Tk_CanvasPsColor(interp, canvas, lnArrItemPtr->lineColor);
1779 	    for (np = 0; np < nPts; np++) {
1780 		x = *coordPtr++;
1781 		y = *coordPtr++;
1782 		if (lnArrItemPtr->dotSize != 0) {
1783 		    sprintf(bytes, "newpath %f %f %f",
1784 			    x, y, 0.5 * lnArrItemPtr->dotSize);
1785 		    Tcl_AppendResult(interp, bytes, " 0 360 arc fill\n", NULL);
1786 		}
1787 	    }
1788 	}
1789     }
1790 
1791     return TCL_OK;
1792 }
1793 
1794 /*
1795  *--------------------------------------------------------------
1796  *
1797  * scaleProc --
1798  *
1799  *	This function rescales a geomap_lnarr item.  It should not be confused
1800  *	with the "-scale" configuration option, which sets the cartographic
1801  *	scale of the map containing the item.
1802  *
1803  * Results:
1804  *	None.
1805  *
1806  * Side effects:
1807  * 	The reference point for a geomap_lnarr item is moved according to the
1808  * 	transformation:
1809  *		xRef' = originX + scaleX*(xRef-originX)
1810  *		yRef' = originY + scaleY*(yRef-originY)
1811  *	The geomap_lnarr's points on the canvas may or may not move.
1812  *
1813  *--------------------------------------------------------------
1814  */
1815 
1816 static void
scaleProc(canvas,itemPtr,originX,originY,scaleX,scaleY)1817 scaleProc(canvas, itemPtr, originX, originY, scaleX, scaleY)
1818     Tk_Canvas canvas;			/* Canvas containing rectangle. */
1819     Tk_Item *itemPtr;			/* Rectangle to be scaled. */
1820     double originX, originY;		/* Origin about which to scale rect. */
1821     double scaleX;			/* Amount to scale in X direction. */
1822     double scaleY;			/* Amount to scale in Y direction. */
1823 {
1824     LnArrItem *lnArrItemPtr = (LnArrItem *) itemPtr;
1825 
1826     lnArrItemPtr->xRef = originX + scaleX * (lnArrItemPtr->xRef - originX);
1827     lnArrItemPtr->yRef = originY + scaleY * (lnArrItemPtr->yRef - originY);
1828     updateCvs(lnArrItemPtr);
1829     return;
1830 }
1831 
1832 /*
1833  *--------------------------------------------------------------
1834  *
1835  * translateProc --
1836  *
1837  *	This procedure is called to move an item by a given amount.
1838  *
1839  * Results:
1840  *	None.
1841  *
1842  * Side effects:
1843  *	The position of the item is offset by (xDelta, yDelta), and
1844  *	the bounding box is updated in the generic part of the item
1845  *	structure.
1846  *
1847  *--------------------------------------------------------------
1848  */
1849 
1850 static void
translateProc(canvas,itemPtr,deltaX,deltaY)1851 translateProc(canvas, itemPtr, deltaX, deltaY)
1852     Tk_Canvas canvas;			/* Canvas containing item. */
1853     Tk_Item *itemPtr;			/* Item that is being moved. */
1854     double deltaX, deltaY;		/* Amount by which item is to be
1855 					 * moved. */
1856 {
1857     LnArrItem *lnArrItemPtr = (LnArrItem *) itemPtr;
1858 
1859     lnArrItemPtr->xRef += deltaX;
1860     lnArrItemPtr->yRef += deltaY;
1861     updateCvs(lnArrItemPtr);
1862 }
1863 /*
1864  *------------------------------------------------------------------------
1865  *
1866  * updateMap --
1867  *
1868  * 	This procedure updates the map coordinates of a geomap_lnarr item.
1869  *
1870  * Results:
1871  *	None.
1872  *
1873  * Side effects:
1874  * 	The mapLnArr member of the geomap_lnarr item are updated.
1875  *
1876  *------------------------------------------------------------------------
1877  */
1878 
1879 static void
updateMap(clientData)1880 updateMap(clientData)
1881     ClientData clientData;	/* Geolinearray item to update */
1882 {
1883     LnArrItem *lnArrItemPtr = (LnArrItem *)clientData;
1884     Tk_Canvas canvas = lnArrItemPtr->canvas;
1885     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
1886     Tclgeomap_LnArr tclGeoLnArr = lnArrItemPtr->tclGeoLnArr;
1887     GeoLnArr geoLnArr = (GeoLnArr)lnArrItemPtr->tclGeoLnArr;
1888     Tclgeomap_Proj tclGeoProj = lnArrItemPtr->proj;
1889     GeoProj proj = (GeoProj)tclGeoProj;
1890     MapLnArr mapLnArr = lnArrItemPtr->mapLnArr;
1891 
1892     if ( !tclGeoLnArr || !proj ) {
1893 	MapLnArrDestroy(lnArrItemPtr->mapLnArr);
1894 	lnArrItemPtr->mapLnArr = NULL;
1895 	lnArrItemPtr->updateMap = 0;
1896 	hide(lnArrItemPtr);
1897 	return;
1898     }
1899 
1900     if (lnArrItemPtr->mapLnArr) {
1901 	MapLnArrDestroy(lnArrItemPtr->mapLnArr);
1902 	lnArrItemPtr->mapLnArr = NULL;
1903     }
1904     if ( !(mapLnArr = GeoLnArrToMap(geoLnArr, proj)) ) {
1905 	lnArrItemPtr->mapLnArr = NULL;
1906 	hide(lnArrItemPtr);
1907 	return;
1908     }
1909     lnArrItemPtr->mapLnArr = mapLnArr;
1910     lnArrItemPtr->updateMap = 0;
1911     lnArrItemPtr->updateCvs = 1;
1912 }
1913 
1914 /*
1915  *------------------------------------------------------------------------
1916  *
1917  * updateCvs --
1918  *
1919  * 	This procedure updates the canvas coordinates of a geomap_lnarr item.
1920  *
1921  * Results:
1922  *	None.
1923  *
1924  * Side effects:
1925  * 	The coords member and bounding box of the geomap_lnarr item are updated.
1926  *
1927  *------------------------------------------------------------------------
1928  */
1929 
1930 static void
updateCvs(clientData)1931 updateCvs(clientData)
1932     ClientData clientData;	/* Geolinearray item to update */
1933 {
1934     LnArrItem *lnArrItemPtr = (LnArrItem *)clientData;
1935     Tk_Canvas canvas = lnArrItemPtr->canvas;
1936     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
1937     Tclgeomap_LnArr tclGeoLnArr = lnArrItemPtr->tclGeoLnArr;
1938     Tclgeomap_Proj tclGeoProj = lnArrItemPtr->proj;
1939     GeoProj proj = (GeoProj)tclGeoProj;
1940     MapLnArr mapLnArr = lnArrItemPtr->mapLnArr;
1941     static Tcl_Obj *cvsPerMObj;		/* Object storing measure for 1 meter */
1942     double cvsPerM;			/* Canvas units per meter on display */
1943     double cvsPerMapM;			/* Canvas units per meter on map
1944 					 * (2D surface which contains projected
1945 					 * and rotated lat-lons) */
1946     double xRef = lnArrItemPtr->xRef;
1947     double yRef = lnArrItemPtr->yRef;
1948     int x, y;
1949     int x1, y1, x2, y2;
1950     int width, height;
1951     MapPt mRefPt;			/* Reference point in map coordinates */
1952     double ordMax, absMax, ordMin, absMin;
1953     unsigned nl, nls, np;
1954     unsigned nlc;			/* Number of lines in canvas */
1955     unsigned r;				/* Dot radius */
1956     int noLines, noFill;		/* Booleans to indicate whether
1957 					 * anything visible in display */
1958 
1959     if ( !tclGeoLnArr || !proj ) {
1960 	hide(lnArrItemPtr);
1961 	return;
1962     }
1963     if (lnArrItemPtr->updateMap) {
1964 	updateMap(lnArrItemPtr);
1965     }
1966     if ( !lnArrItemPtr->mapLnArr ) {
1967 	hide(lnArrItemPtr);
1968 	return;
1969     }
1970     mapLnArr = lnArrItemPtr->mapLnArr;
1971     noLines = !lnArrItemPtr->lineColor
1972 	|| (lnArrItemPtr->width <= 0 && lnArrItemPtr->dotSize <= 0);
1973     noFill = !lnArrItemPtr->fillColor;
1974     if (noLines && noFill) {
1975 	hide(lnArrItemPtr);
1976 	return;
1977     }
1978     mRefPt = LatLonToProj(lnArrItemPtr->refPt, proj);
1979     if ( !cvsPerMObj ) {
1980 	cvsPerMObj = Tcl_NewStringObj("100.0c", -1);
1981     }
1982     Tk_CanvasGetCoordFromObj(NULL, canvas, cvsPerMObj, &cvsPerM);
1983     cvsPerMapM = cvsPerM * lnArrItemPtr->scale;
1984     Tk_CanvasEventuallyRedraw(canvas,
1985 	    lnArrItemPtr->header.x1, lnArrItemPtr->header.y1,
1986 	    lnArrItemPtr->header.x2, lnArrItemPtr->header.y2);
1987     if (!tkwin || !Tk_IsMapped(tkwin)) {
1988 	/*
1989 	 * If canvas limits are not know yet, draw everything.
1990 	 */
1991 
1992 	ordMax = DBL_MAX;
1993 	absMax = DBL_MAX;
1994 	ordMin = -DBL_MAX;
1995 	absMin = -DBL_MAX;
1996     } else {
1997 	/*
1998 	 * If canvas limits are known, only draw lines on canvas.
1999 	 */
2000 
2001 	width = Tk_Width(tkwin);
2002 	height = Tk_Height(tkwin);
2003 	ordMax = mRefPt.ord + yRef / cvsPerMapM;
2004 	absMax = mRefPt.abs + (width - xRef) / cvsPerMapM;
2005 	ordMin = mRefPt.ord - (height -yRef) / cvsPerMapM;
2006 	absMin = mRefPt.abs - xRef / cvsPerMapM;
2007     }
2008     nls = mapLnArr->nLines;
2009     if (nls == 0) {
2010 	hide(lnArrItemPtr);
2011 	return;
2012     }
2013 
2014     /*
2015      * Reinitialize coords arrays and array sizes in lnArrItemPtr.
2016      */
2017 
2018     for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
2019 	CKFREE((char *)lnArrItemPtr->coordPtrPtr[nl]);
2020     }
2021     CKFREE((char *)lnArrItemPtr->coordPtrPtr);
2022     lnArrItemPtr->nLines = 0;
2023     lnArrItemPtr->coordPtrPtr = (double **)CKALLOC(nls * sizeof(double *));
2024     for (nl = 0, nlc = 0; nl < nls; nl++) {
2025 	lnArrItemPtr->coordPtrPtr[nl] = NULL;
2026     }
2027 
2028     CKFREE((char *)lnArrItemPtr->nPtsPtr);
2029     lnArrItemPtr->nPtsPtr = (int *)CKALLOC(nls * sizeof(int));
2030     for (nl = 0, nlc = 0; nl < nls; nl++) {
2031 	lnArrItemPtr->nPtsPtr[nl] = 0;
2032     }
2033 
2034     x1 = y1 = INT_MAX;
2035     x2 = y2 = INT_MIN;
2036     for (nl = 0, nlc = 0; nl < nls; nl++) {
2037 	size_t sz;
2038 
2039 	/*
2040 	 * Compute canvas coordinates for one line from mapLnArr,
2041 	 * if it has points in the window.
2042 	 */
2043 
2044 	MapLn mapLn = MapLnArrGetLine(mapLnArr, nl);
2045 
2046 	/*
2047 	 * Update bounding box of the linearray.
2048 	 */
2049 
2050 	if (mapLn->ordMax > ordMin && mapLn->ordMin < ordMax
2051 		&& mapLn->absMax > absMin && mapLn->absMin < absMax) {
2052 	    double *coordPtr;
2053 
2054 	    lnArrItemPtr->nPtsPtr[nlc] = mapLn->nPts;
2055 	    sz = lnArrItemPtr->nPtsPtr[nlc] * 2 * sizeof(double);
2056 	    lnArrItemPtr->coordPtrPtr[nlc] = (double *)CKALLOC(sz);
2057 	    for (np = 0, coordPtr = lnArrItemPtr->coordPtrPtr[nlc];
2058 		    np < lnArrItemPtr->nPtsPtr[nlc];
2059 		    np++) {
2060 		MapPt mapPt = MapLnGetPt(mapLn, np);
2061 		x = xRef + (mapPt.abs - mRefPt.abs) * cvsPerMapM;
2062 		x1 = (x < x1) ? x : x1;
2063 		x2 = (x > x2) ? x : x2;
2064 		*coordPtr++ = x;
2065 		y = yRef - (mapPt.ord - mRefPt.ord) * cvsPerMapM;
2066 		y1 = (y < y1) ? y : y1;
2067 		y2 = (y > y2) ? y : y2;
2068 		*coordPtr++ = y;
2069 	    }
2070 
2071 	    nlc++;
2072 	}
2073     }
2074 
2075     /*
2076      * Store new coords arrays and array sizes in lnArrItemPtr.
2077      */
2078 
2079     if (nlc == 0) {
2080 	hide(lnArrItemPtr);
2081 	return;
2082     }
2083     lnArrItemPtr->nLines = nlc;
2084     lnArrItemPtr->nMax = mapLnArr->nMax;
2085     r = (lnArrItemPtr->dotSize + 1) / 2;
2086     lnArrItemPtr->header.x1 = x1 - lnArrItemPtr->width - r - 1;
2087     lnArrItemPtr->header.x2 = x2 + lnArrItemPtr->width + r + 1;
2088     lnArrItemPtr->header.y1 = y1 - lnArrItemPtr->width - r - 1;
2089     lnArrItemPtr->header.y2 = y2 + lnArrItemPtr->width + r + 1;
2090     if ( lnArrItemPtr->header.x1 >= lnArrItemPtr->header.x2
2091 	    || lnArrItemPtr->header.y1 >= lnArrItemPtr->header.y2 ) {
2092 	hide(lnArrItemPtr);
2093 	return;
2094     }
2095     Tk_CanvasEventuallyRedraw(lnArrItemPtr->canvas,
2096 	    lnArrItemPtr->header.x1, lnArrItemPtr->header.y1,
2097 	    lnArrItemPtr->header.x2, lnArrItemPtr->header.y2);
2098     lnArrItemPtr->updateCvs = 0;
2099 }
2100