1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 /*
15  * Tcl binding to drive Stephen North's and
16  * Emden Gansner's shortest path code.
17  *
18  * ellson@graphviz.org   October 2nd, 1996
19  */
20 
21 #include "config.h"
22 
23 /* avoid compiler warnings with template changes in Tcl8.4 */
24 /*    specifically just the change to Tcl_CmdProc */
25 #define USE_NON_CONST
26 
27 /* for sincos */
28 #define _GNU_SOURCE 1
29 
30 #include                <sys/types.h>
31 #include                <stdlib.h>
32 #include                <string.h>
33 #include                <unistd.h>
34 
35 #include <inttypes.h>
36 #include <assert.h>
37 #include <math.h>
38 #include <pathutil.h>
39 #include <vispath.h>
40 #include <tri.h>
41 #include <tcl.h>
42 #include "tclhandle.h"
43 
44 #ifndef CONST84
45 #define CONST84
46 #endif
47 
48 #if ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6)) || ( TCL_MAJOR_VERSION > 8)
49 #else
50 #ifndef Tcl_GetStringResult
51 #define Tcl_GetStringResult(interp) interp->result
52 #endif
53 #endif
54 
55 typedef Ppoint_t point;
56 
57 typedef struct poly_s {
58     int id;
59     Ppoly_t boundary;
60 } poly;
61 
62 typedef struct vgpane_s {
63     int Npoly;			/* number of polygons */
64     poly *poly;			/* set of polygons */
65     int N_poly_alloc;		/* for allocation */
66     vconfig_t *vc;		/* visibility graph handle */
67     Tcl_Interp *interp;		/* interpreter that owns the binding */
68     char *triangle_cmd;		/* why is this here any more */
69 } vgpane_t;
70 
71 #ifdef HAVE_SINCOS
72 extern void sincos(double x, double *s, double *c);
73 #else
74 # define sincos(x,s,c) *s = sin(x); *c = cos(x)
75 #endif
76 
77 tblHeader_pt vgpaneTable;
78 
79 extern void make_CW(Ppoly_t * poly);
80 extern int Plegal_arrangement(Ppoly_t ** polys, int n_polys);
81 
82 static int polyid = 0;		/* unique and unchanging id for each poly */
83 
allocpoly(vgpane_t * vgp,int id,int npts)84 static poly *allocpoly(vgpane_t * vgp, int id, int npts)
85 {
86     poly *rv;
87     if (vgp->Npoly >= vgp->N_poly_alloc) {
88 	vgp->N_poly_alloc *= 2;
89 	vgp->poly = realloc(vgp->poly, vgp->N_poly_alloc * sizeof(poly));
90     }
91     rv = &(vgp->poly[vgp->Npoly++]);
92     rv->id = id;
93     rv->boundary.pn = 0;
94     rv->boundary.ps = malloc(npts * sizeof(point));
95     return rv;
96 }
97 
vc_stale(vgpane_t * vgp)98 static void vc_stale(vgpane_t * vgp)
99 {
100     if (vgp->vc) {
101 	Pobsclose(vgp->vc);
102 	vgp->vc = (vconfig_t *) 0;
103     }
104 }
105 
vc_refresh(vgpane_t * vgp)106 static int vc_refresh(vgpane_t * vgp)
107 {
108     int i;
109     Ppoly_t **obs;
110 
111     if (vgp->vc == (vconfig_t *) 0) {
112 	obs = malloc(vgp->Npoly * sizeof(Ppoly_t));
113 	for (i = 0; i < vgp->Npoly; i++)
114 	    obs[i] = &(vgp->poly[i].boundary);
115 	if (NOT(Plegal_arrangement(obs, vgp->Npoly)))
116 	    fprintf(stderr, "bad arrangement\n");
117 	else
118 	    vgp->vc = Pobsopen(obs, vgp->Npoly);
119 	free(obs);
120     }
121     return (vgp->vc != 0);
122 }
123 
dgsprintxy(Tcl_DString * result,int npts,point p[])124 static void dgsprintxy(Tcl_DString * result, int npts, point p[])
125 {
126     int i;
127     char buf[20];
128 
129     if (npts != 1)
130 	Tcl_DStringStartSublist(result);
131     for (i = 0; i < npts; i++) {
132 	sprintf(buf, "%g", p[i].x);
133 	Tcl_DStringAppendElement(result, buf);
134 	sprintf(buf, "%g", p[i].y);
135 	Tcl_DStringAppendElement(result, buf);
136     }
137     if (npts != 1)
138 	Tcl_DStringEndSublist(result);
139 }
140 
expandPercentsEval(Tcl_Interp * interp,register char * before,char * r,int npts,point * ppos)141 static void expandPercentsEval(Tcl_Interp * interp,	/* interpreter context */
142 			       register char *before,	/* Command with percent expressions */
143 			       char *r,	/* vgpaneHandle string to substitute for "%r" */
144 			       int npts,	/* number of coordinates */
145 			       point * ppos	/* Cordinates to substitute for %t */
146     )
147 {
148     register char *string;
149     Tcl_DString scripts;
150 
151     Tcl_DStringInit(&scripts);
152     while (1) {
153 	/*
154 	 * Find everything up to the next % character and append it to the
155 	 * result string.
156 	 */
157 
158 	for (string = before; (*string != 0) && (*string != '%'); string++) {
159 	    /* Empty loop body. */
160 	}
161 	if (string != before) {
162 	    Tcl_DStringAppend(&scripts, before, string - before);
163 	    before = string;
164 	}
165 	if (*before == 0) {
166 	    break;
167 	}
168 	/*
169 	 * There's a percent sequence here.  Process it.
170 	 */
171 
172 	switch (before[1]) {
173 	case 'r':
174 	    Tcl_DStringAppend(&scripts, r, strlen(r));	/* vgcanvasHandle */
175 	    break;
176 	case 't':
177 	    dgsprintxy(&scripts, npts, ppos);
178 	    break;
179 	default:
180 	    Tcl_DStringAppend(&scripts, before + 1, 1);
181 	    break;
182 	}
183 	before += 2;
184     }
185     if (Tcl_GlobalEval(interp, Tcl_DStringValue(&scripts)) != TCL_OK)
186 	fprintf(stderr, "%s while in binding: %s\n\n",
187 		Tcl_GetStringResult(interp), Tcl_DStringValue(&scripts));
188     Tcl_DStringFree(&scripts);
189 }
190 
triangle_callback(void * vgparg,point pqr[])191 void triangle_callback(void *vgparg, point pqr[])
192 {
193     char vbuf[20];
194     vgpane_t *vgp;
195 
196     vgp = vgparg;
197 
198 /*	    TBL_ENTRY((tblHeader_pt)vgpaneTable, (ubyte_pt)vgp));*/
199 
200     if (vgp->triangle_cmd) {
201 	sprintf(vbuf, "vgpane%lu",
202 		(uint64_t) (((ubyte_pt) vgp - (vgpaneTable->bodyPtr))
203 				 / (vgpaneTable->entrySize)));
204 	expandPercentsEval(vgp->interp, vgp->triangle_cmd, vbuf, 3, pqr);
205     }
206 }
207 
buildBindings(char * s1,char * s2)208 static char *buildBindings(char *s1, char *s2)
209 /*
210  * previous binding in s1 binding to be added in s2 result in s3
211  *
212  * if s2 begins with + then append (separated by \n) else s2 replaces if
213  * resultant string is null then bindings are deleted
214  */
215 {
216     char *s3;
217     size_t l;
218 
219     if (s2[0] == '+') {
220 	if (s1) {
221 	    l = strlen(s2) - 1;
222 	    if (l) {
223 		s3 = malloc(strlen(s1) + l + 2);
224 		strcpy(s3, s1);
225 		strcat(s3, "\n");
226 		strcat(s3, s2 + 1);
227 		free(s1);
228 	    } else {
229 		s3 = s1;
230 	    }
231 	} else {
232 	    l = strlen(s2) - 1;
233 	    if (l) {
234 		s3 = malloc(l + 2);
235 		strcpy(s3, s2 + 1);
236 	    } else {
237 		s3 = (char *) NULL;
238 	    }
239 	}
240     } else {
241 	if (s1)
242 	    free(s1);
243 	l = strlen(s2);
244 	if (l) {
245 	    s3 = malloc(l + 2);
246 	    strcpy(s3, s2);
247 	} else {
248 	    s3 = (char *) NULL;
249 	}
250     }
251     return s3;
252 }
253 
254 
255 
256 /* convert x and y string args to point */
scanpoint(Tcl_Interp * interp,char * argv[],point * p)257 static int scanpoint(Tcl_Interp * interp, char *argv[], point * p)
258 {
259     if (sscanf(argv[0], "%lg", &(p->x)) != 1) {
260 	Tcl_AppendResult(interp, "invalid x coordinate: \"", argv[0],
261 			 "\"", (char *) NULL);
262 	return TCL_ERROR;
263     }
264     if (sscanf(argv[1], "%lg", &(p->y)) != 1) {
265 	Tcl_AppendResult(interp, "invalid y coordinate: \"", argv[1],
266 			 "\"", (char *) NULL);
267 	return TCL_ERROR;
268     }
269     return TCL_OK;
270 }
271 
center(point vertex[],int n)272 static point center(point vertex[], int n)
273 {
274     int i;
275     point c;
276 
277     c.x = c.y = 0;
278     for (i = 0; i < n; i++) {
279 	c.x += vertex[i].x;
280 	c.y += vertex[i].y;
281     }
282     c.x /= n;
283     c.y /= n;
284     return c;
285 }
286 
distance(point p,point q)287 static double distance(point p, point q)
288 {
289     double dx, dy;
290 
291     dx = p.x - q.x;
292     dy = p.y - q.y;
293     return sqrt(dx * dx + dy * dy);
294 }
295 
rotate(point c,point p,double alpha)296 static point rotate(point c, point p, double alpha)
297 {
298     point q;
299     double beta, r, sina, cosa;
300 
301     r = distance(c, p);
302     beta = atan2(p.x - c.x, p.y - c.y);
303     sincos(beta + alpha, &sina, &cosa);
304     q.x = c.x + r * sina;
305     q.y = c.y - r * cosa;	/* adjust for tk y-down */
306     return q;
307 }
308 
scale(point c,point p,double gain)309 static point scale(point c, point p, double gain)
310 {
311     point q;
312 
313     q.x = c.x + gain * (p.x - c.x);
314     q.y = c.y + gain * (p.y - c.y);
315     return q;
316 }
317 
remove_poly(vgpane_t * vgp,int polyid)318 static int remove_poly(vgpane_t * vgp, int polyid)
319 {
320     int i, j;
321 
322     for (i = 0; i < vgp->Npoly; i++) {
323 	if (vgp->poly[i].id == polyid) {
324 	    free(vgp->poly[i].boundary.ps);
325 	    for (j = i++; i < vgp->Npoly; i++, j++) {
326 		vgp->poly[j] = vgp->poly[i];
327 	    }
328 	    vgp->Npoly -= 1;
329 	    vc_stale(vgp);
330 	    return TRUE;
331 	}
332     }
333     return FALSE;
334 }
335 
336 static int
insert_poly(Tcl_Interp * interp,vgpane_t * vgp,int polyid,char * vargv[],int vargc)337 insert_poly(Tcl_Interp * interp, vgpane_t * vgp, int polyid, char *vargv[],
338 	    int vargc)
339 {
340     poly *np;
341     int i, result;
342 
343     np = allocpoly(vgp, polyid, vargc);
344     for (i = 0; i < vargc; i += 2) {
345 	result =
346 	    scanpoint(interp, &vargv[i],
347 		      &(np->boundary.ps[np->boundary.pn]));
348 	if (result != TCL_OK)
349 	    return result;
350 	np->boundary.pn++;
351     }
352     make_CW(&(np->boundary));
353     vc_stale(vgp);
354     return TCL_OK;
355 }
356 
357 static void
make_barriers(vgpane_t * vgp,int pp,int qp,Pedge_t ** barriers,int * n_barriers)358 make_barriers(vgpane_t * vgp, int pp, int qp, Pedge_t ** barriers,
359 	      int *n_barriers)
360 {
361     int i, j, k, n, b;
362     Pedge_t *bar;
363 
364     n = 0;
365     for (i = 0; i < vgp->Npoly; i++) {
366 	if (vgp->poly[i].id == pp)
367 	    continue;
368 	if (vgp->poly[i].id == qp)
369 	    continue;
370 	n = n + vgp->poly[i].boundary.pn;
371     }
372     bar = malloc(n * sizeof(Pedge_t));
373     b = 0;
374     for (i = 0; i < vgp->Npoly; i++) {
375 	if (vgp->poly[i].id == pp)
376 	    continue;
377 	if (vgp->poly[i].id == qp)
378 	    continue;
379 	for (j = 0; j < vgp->poly[i].boundary.pn; j++) {
380 	    k = j + 1;
381 	    if (k >= vgp->poly[i].boundary.pn)
382 		k = 0;
383 	    bar[b].a = vgp->poly[i].boundary.ps[j];
384 	    bar[b].b = vgp->poly[i].boundary.ps[k];
385 	    b++;
386 	}
387     }
388     assert(b == n);
389     *barriers = bar;
390     *n_barriers = n;
391 }
392 
393 /* append the x and y coordinates of a point to the Tcl result */
appendpoint(Tcl_Interp * interp,point p)394 static void appendpoint(Tcl_Interp * interp, point p)
395 {
396     char buf[30];
397 
398     sprintf(buf, "%g", p.x);
399     Tcl_AppendElement(interp, buf);
400     sprintf(buf, "%g", p.y);
401     Tcl_AppendElement(interp, buf);
402 }
403 
404 /* process vgpane methods */
405 static int
vgpanecmd(ClientData clientData,Tcl_Interp * interp,int argc,char * argv[])406 vgpanecmd(ClientData clientData, Tcl_Interp * interp, int argc,
407 	  char *argv[])
408 {
409     int vargc, i, j, n, result;
410     char c, *s, **vargv, vbuf[30];
411     vgpane_t *vgp, **vgpp;
412     point p, q, *ps;
413     poly *tpp;
414     double alpha, gain;
415     Pvector_t slopes[2];
416     Ppolyline_t line, spline;
417     int pp, qp;			/* polygon indices for p, q */
418     Pedge_t *barriers;
419     int n_barriers;
420 
421     if (argc < 2) {
422 	Tcl_AppendResult(interp, "wrong # args: should be \"",
423 			 " ", argv[0], " method ?arg arg ...?\"",
424 			 (char *) NULL);
425 	return TCL_ERROR;
426     }
427     if (!(vgpp = (vgpane_t **) tclhandleXlate(vgpaneTable, argv[0]))) {
428 	Tcl_AppendResult(interp, "Invalid handle: \"", argv[0],
429 			 "\"", (char *) NULL);
430 	return TCL_ERROR;
431     }
432     vgp = *vgpp;
433 
434     c = argv[1][0];
435     size_t length = strlen(argv[1]);
436 
437     if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)) {
438 	if ((argc < 3)) {
439 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
440 			     " ", argv[1], " id ?x1 y1 x2 y2...?\"",
441 			     (char *) NULL);
442 	    return TCL_ERROR;
443 	}
444 	if (sscanf(argv[2], "%d", &polyid) != 1) {
445 	    Tcl_AppendResult(interp, "not an integer: ", argv[2],
446 			     (char *) NULL);
447 	    return TCL_ERROR;
448 	}
449 	if (argc == 3) {
450 	    /* find poly and return its coordinates */
451 	    for (i = 0; i < vgp->Npoly; i++) {
452 		if (vgp->poly[i].id == polyid) {
453 		    n = vgp->poly[i].boundary.pn;
454 		    for (j = 0; j < n; j++) {
455 			appendpoint(interp, vgp->poly[i].boundary.ps[j]);
456 		    }
457 		    return TCL_OK;
458 		}
459 	    }
460 	    Tcl_AppendResult(interp, " no such polygon: ", argv[2],
461 			     (char *) NULL);
462 	    return TCL_ERROR;
463 	}
464 	/* accept either inline or delimited list */
465 	if (argc == 4) {
466 	    result =
467 		Tcl_SplitList(interp, argv[3], &vargc,
468 			      (CONST84 char ***) &vargv);
469 	    if (result != TCL_OK) {
470 		return result;
471 	    }
472 	} else {
473 	    vargc = argc - 3;
474 	    vargv = &argv[3];
475 	}
476 	if (!vargc || vargc % 2) {
477 	    Tcl_AppendResult(interp,
478 			     "There must be a multiple of two terms in the list.",
479 			     (char *) NULL);
480 	    return TCL_ERROR;
481 	}
482 
483 	/* remove old poly, add modified polygon to the end with
484 	   the same id as the original */
485 
486 	if (!(remove_poly(vgp, polyid))) {
487 	    Tcl_AppendResult(interp, " no such polygon: ", argv[2],
488 			     (char *) NULL);
489 	    return TCL_ERROR;
490 	}
491 
492 	return (insert_poly(interp, vgp, polyid, vargv, vargc));
493 
494     } else if ((c == 'd') && (strncmp(argv[1], "debug", length) == 0)) {
495 	/* debug only */
496 	printf("debug output goes here\n");
497 	return TCL_OK;
498 
499     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
500 	/* delete a vgpane and all memory associated with it */
501 	if (vgp->vc)
502 	    Pobsclose(vgp->vc);
503 	free(vgp->poly);	/* ### */
504 	Tcl_DeleteCommand(interp, argv[0]);
505 	free((char *) tclhandleFree(vgpaneTable, argv[0]));
506 	return TCL_OK;
507 
508     } else if ((c == 'f') && (strncmp(argv[1], "find", length) == 0)) {
509 	/* find the polygon that the point is inside and return it
510 	   id, or null */
511 	if ((argc < 3)) {
512 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
513 			     " ", argv[1], " x y\"", (char *) NULL);
514 	    return TCL_ERROR;
515 	}
516 	if (argc == 3) {
517 	    result =
518 		Tcl_SplitList(interp, argv[2], &vargc,
519 			      (CONST84 char ***) &vargv);
520 	    if (result != TCL_OK) {
521 		return result;
522 	    }
523 	} else {
524 	    vargc = argc - 2;
525 	    vargv = &argv[2];
526 	}
527 	result = scanpoint(interp, &vargv[0], &p);
528 	if (result != TCL_OK)
529 	    return result;
530 
531 	/* determine the polygons (if any) that contain the point */
532 	for (i = 0; i < vgp->Npoly; i++) {
533 	    if (in_poly(vgp->poly[i].boundary, p)) {
534 		sprintf(vbuf, "%d", vgp->poly[i].id);
535 		Tcl_AppendElement(interp, vbuf);
536 	    }
537 	}
538 	return TCL_OK;
539 
540     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)) {
541 	/* add poly to end poly list, and it coordinates to the end of
542 	   the point list */
543 	if ((argc < 3)) {
544 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
545 			     " ", argv[1], " x1 y1 x2 y2 ...\"",
546 			     (char *) NULL);
547 	    return TCL_ERROR;
548 	}
549 	/* accept either inline or delimited list */
550 	if (argc == 3) {
551 	    result =
552 		Tcl_SplitList(interp, argv[2], &vargc,
553 			      (CONST84 char ***) &vargv);
554 	    if (result != TCL_OK) {
555 		return result;
556 	    }
557 	} else {
558 	    vargc = argc - 2;
559 	    vargv = &argv[2];
560 	}
561 
562 	if (!vargc || vargc % 2) {
563 	    Tcl_AppendResult(interp,
564 			     "There must be a multiple of two terms in the list.",
565 			     (char *) NULL);
566 	    return TCL_ERROR;
567 	}
568 
569 	polyid++;
570 
571 	result = insert_poly(interp, vgp, polyid, vargv, vargc);
572 	if (result != TCL_OK)
573 	    return result;
574 
575 	sprintf(vbuf, "%d", polyid);
576 	Tcl_AppendResult(interp, vbuf, (char *) NULL);
577 	return TCL_OK;
578 
579     } else if ((c == 'l') && (strncmp(argv[1], "list", length) == 0)) {
580 	/* return list of polygon ids */
581 	for (i = 0; i < vgp->Npoly; i++) {
582 	    sprintf(vbuf, "%d", vgp->poly[i].id);
583 	    Tcl_AppendElement(interp, vbuf);
584 	}
585 	return TCL_OK;
586 
587     } else if ((c == 'p') && (strncmp(argv[1], "path", length) == 0)) {
588 	/* return a list of points corresponding to the shortest path
589 	   that does not cross the remaining "visible" polygons. */
590 	if ((argc < 3)) {
591 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
592 			     " ", argv[1], " x1 y1 x2 y2\"",
593 			     (char *) NULL);
594 	    return TCL_ERROR;
595 	}
596 	if (argc == 3) {
597 	    result =
598 		Tcl_SplitList(interp, argv[2], &vargc,
599 			      (CONST84 char ***) &vargv);
600 	    if (result != TCL_OK) {
601 		return result;
602 	    }
603 	} else {
604 	    vargc = argc - 2;
605 	    vargv = &argv[2];
606 	}
607 	if ((vargc < 4)) {
608 	    Tcl_AppendResult(interp,
609 			     "invalid points: should be: \"x1 y1 x2 y2\"",
610 			     (char *) NULL);
611 	    return TCL_ERROR;
612 	}
613 	result = scanpoint(interp, &vargv[0], &p);
614 	if (result != TCL_OK)
615 	    return result;
616 	result = scanpoint(interp, &vargv[2], &q);
617 	if (result != TCL_OK)
618 	    return result;
619 
620 	/* only recompute the visibility graph if we have to */
621 	if ((vc_refresh(vgp))) {
622 	    Pobspath(vgp->vc, p, POLYID_UNKNOWN, q, POLYID_UNKNOWN, &line);
623 
624 	    for (i = 0; i < line.pn; i++) {
625 		appendpoint(interp, line.ps[i]);
626 	    }
627 	}
628 
629 	return TCL_OK;
630 
631     } else if ((c == 'b') && (strncmp(argv[1], "bind", length) == 0)) {
632 	if ((argc < 2) || (argc > 4)) {
633 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
634 			     argv[0], " bind triangle ?command?\"",
635 			     (char *) NULL);
636 	    return TCL_ERROR;
637 	}
638 	if (argc == 2) {
639 	    Tcl_AppendElement(interp, "triangle");
640 	    return TCL_OK;
641 	}
642 	length = strlen(argv[2]);
643 	if (strncmp(argv[2], "triangle", length) == 0) {
644 	    s = vgp->triangle_cmd;
645 	    if (argc == 4)
646 		vgp->triangle_cmd = s = buildBindings(s, argv[3]);
647 	} else {
648 	    Tcl_AppendResult(interp, "unknown event \"", argv[2],
649 			     "\": must be one of:\n\ttriangle.",
650 			     (char *) NULL);
651 	    return TCL_ERROR;
652 	}
653 	if (argc == 3)
654 	    Tcl_AppendResult(interp, s, (char *) NULL);
655 	return TCL_OK;
656 
657     } else if ((c == 'b') && (strncmp(argv[1], "bpath", length) == 0)) {
658 	/* return a list of points corresponding to the shortest path
659 	   that does not cross the remaining "visible" polygons. */
660 	if ((argc < 3)) {
661 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
662 			     " ", argv[1], " x1 y1 x2 y2\"",
663 			     (char *) NULL);
664 	    return TCL_ERROR;
665 	}
666 	if (argc == 3) {
667 	    result =
668 		Tcl_SplitList(interp, argv[2], &vargc,
669 			      (CONST84 char ***) &vargv);
670 	    if (result != TCL_OK) {
671 		return result;
672 	    }
673 	} else {
674 	    vargc = argc - 2;
675 	    vargv = &argv[2];
676 	}
677 	if ((vargc < 4)) {
678 	    Tcl_AppendResult(interp,
679 			     "invalid points: should be: \"x1 y1 x2 y2\"",
680 			     (char *) NULL);
681 	    return TCL_ERROR;
682 	}
683 
684 	result = scanpoint(interp, &vargv[0], &p);
685 	if (result != TCL_OK)
686 	    return result;
687 	result = scanpoint(interp, &vargv[2], &q);
688 	if (result != TCL_OK)
689 	    return result;
690 
691 	/* determine the polygons (if any) that contain the endpoints */
692 	pp = qp = POLYID_NONE;
693 	for (i = 0; i < vgp->Npoly; i++) {
694 	    tpp = &(vgp->poly[i]);
695 	    if ((pp == POLYID_NONE) && in_poly(tpp->boundary, p))
696 		pp = i;
697 	    if ((qp == POLYID_NONE) && in_poly(tpp->boundary, q))
698 		qp = i;
699 	}
700 
701 	if (vc_refresh(vgp)) {
702 	    /*Pobspath(vgp->vc, p, pp, q, qp, &line); */
703 	    Pobspath(vgp->vc, p, POLYID_UNKNOWN, q, POLYID_UNKNOWN, &line);
704 	    make_barriers(vgp, pp, qp, &barriers, &n_barriers);
705 	    slopes[0].x = slopes[0].y = 0.0;
706 	    slopes[1].x = slopes[1].y = 0.0;
707 	    Proutespline(barriers, n_barriers, line, slopes, &spline);
708 
709 	    for (i = 0; i < spline.pn; i++) {
710 		appendpoint(interp, spline.ps[i]);
711 	    }
712 	}
713 	return TCL_OK;
714 
715     } else if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
716 	if ((argc < 3)) {
717 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
718 			     " ", argv[1], " id\"", (char *) NULL);
719 	    return TCL_ERROR;
720 	}
721 	if (sscanf(argv[2], "%d", &polyid) != 1) {
722 	    Tcl_AppendResult(interp, "not an integer: ", argv[2],
723 			     (char *) NULL);
724 	    return TCL_ERROR;
725 	}
726 	for (i = 0; i < vgp->Npoly; i++) {
727 	    if (vgp->poly[i].id == polyid) {
728 		Ppoly_t pp = vgp->poly[i].boundary;
729 		point LL, UR;
730 		LL = UR = pp.ps[0];
731 		for (j = 1; j < pp.pn; j++) {
732 		    p = pp.ps[j];
733 		    if (p.x > UR.x)
734 			UR.x = p.x;
735 		    if (p.y > UR.y)
736 			UR.y = p.y;
737 		    if (p.x < LL.x)
738 			LL.x = p.x;
739 		    if (p.y < LL.y)
740 			LL.y = p.y;
741 		}
742 		appendpoint(interp, LL);
743 		appendpoint(interp, UR);
744 		return TCL_OK;
745 	    }
746 	}
747 	Tcl_AppendResult(interp, " no such polygon: ", argv[2],
748 			 (char *) NULL);
749 	return TCL_ERROR;
750 
751     } else if ((c == 'c') && (strncmp(argv[1], "center", length) == 0)) {
752 	if ((argc < 3)) {
753 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
754 			     " ", argv[1], " id\"", (char *) NULL);
755 	    return TCL_ERROR;
756 	}
757 	if (sscanf(argv[2], "%d", &polyid) != 1) {
758 	    Tcl_AppendResult(interp, "not an integer: ", argv[2],
759 			     (char *) NULL);
760 	    return TCL_ERROR;
761 	}
762 	for (i = 0; i < vgp->Npoly; i++) {
763 	    if (vgp->poly[i].id == polyid) {
764 		appendpoint(interp, center(vgp->poly[i].boundary.ps,
765 					   vgp->poly[i].boundary.pn));
766 		return TCL_OK;
767 	    }
768 	}
769 	Tcl_AppendResult(interp, " no such polygon: ", argv[2],
770 			 (char *) NULL);
771 	return TCL_ERROR;
772 
773     } else if ((c == 't')
774 	       && (strncmp(argv[1], "triangulate", length) == 0)) {
775 	if ((argc < 2)) {
776 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
777 			     " id ", (char *) NULL);
778 	    return TCL_ERROR;
779 	}
780 
781 	if (sscanf(argv[2], "%d", &polyid) != 1) {
782 	    Tcl_AppendResult(interp, "not an integer: ", argv[2],
783 			     (char *) NULL);
784 	    return TCL_ERROR;
785 	}
786 
787 	for (i = 0; i < vgp->Npoly; i++) {
788 	    if (vgp->poly[i].id == polyid) {
789 		Ptriangulate(&(vgp->poly[i].boundary), triangle_callback,
790 			     vgp);
791 		return TCL_OK;
792 	    }
793 	}
794 	Tcl_AppendResult(interp, " no such polygon: ", argv[2],
795 			 (char *) NULL);
796 	return TCL_ERROR;
797     } else if ((c == 'r') && (strncmp(argv[1], "rotate", length) == 0)) {
798 	if ((argc < 4)) {
799 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
800 			     " ", argv[1], " id alpha\"", (char *) NULL);
801 	    return TCL_ERROR;
802 	}
803 	if (sscanf(argv[2], "%d", &polyid) != 1) {
804 	    Tcl_AppendResult(interp, "not an integer: ", argv[2],
805 			     (char *) NULL);
806 	    return TCL_ERROR;
807 	}
808 	if (sscanf(argv[3], "%lg", &alpha) != 1) {
809 	    Tcl_AppendResult(interp, "not an angle in radians: ", argv[3],
810 			     (char *) NULL);
811 	    return TCL_ERROR;
812 	}
813 	for (i = 0; i < vgp->Npoly; i++) {
814 	    if (vgp->poly[i].id == polyid) {
815 		n = vgp->poly[i].boundary.pn;
816 		ps = vgp->poly[i].boundary.ps;
817 		p = center(ps, n);
818 		for (j = 0; j < n; j++) {
819 		    appendpoint(interp, rotate(p, ps[j], alpha));
820 		}
821 		return TCL_OK;
822 	    }
823 	}
824 	Tcl_AppendResult(interp, " no such polygon: ", argv[2],
825 			 (char *) NULL);
826 	return TCL_ERROR;
827 
828     } else if ((c == 's') && (strncmp(argv[1], "scale", length) == 0)) {
829 	if ((argc < 4)) {
830 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
831 			     " ", argv[1], " id gain\"", (char *) NULL);
832 	    return TCL_ERROR;
833 	}
834 	if (sscanf(argv[2], "%d", &polyid) != 1) {
835 	    Tcl_AppendResult(interp, "not an integer: ", argv[2],
836 			     (char *) NULL);
837 	    return TCL_ERROR;
838 	}
839 	if (sscanf(argv[3], "%lg", &gain) != 1) {
840 	    Tcl_AppendResult(interp, "not a number: ", argv[3],
841 			     (char *) NULL);
842 	    return TCL_ERROR;
843 	}
844 	for (i = 0; i < vgp->Npoly; i++) {
845 	    if (vgp->poly[i].id == polyid) {
846 		n = vgp->poly[i].boundary.pn;
847 		ps = vgp->poly[i].boundary.ps;
848 		for (j = 0; j < n; j++) {
849 		    appendpoint(interp, scale(p, ps[j], gain));
850 		}
851 		return TCL_OK;
852 	    }
853 	}
854 	Tcl_AppendResult(interp, " no such polygon: ", argv[2],
855 			 (char *) NULL);
856 	return TCL_ERROR;
857 
858     } else if ((c == 'r') && (strncmp(argv[1], "remove", length) == 0)) {
859 	if ((argc < 3)) {
860 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
861 			     " ", argv[1], " id\"", (char *) NULL);
862 	    return TCL_ERROR;
863 	}
864 	if (sscanf(argv[2], "%d", &polyid) != 1) {
865 	    Tcl_AppendResult(interp, "not an integer: ", argv[2],
866 			     (char *) NULL);
867 	    return TCL_ERROR;
868 	}
869 
870 	if (remove_poly(vgp, polyid))
871 	    return TCL_OK;
872 
873 	Tcl_AppendResult(interp, " no such polygon: ", argv[2],
874 			 (char *) NULL);
875 	return TCL_ERROR;
876     }
877 
878     Tcl_AppendResult(interp, "bad method \"", argv[1],
879 		     "\" must be one of:",
880 		     "\n\tbbox, bind, bpath, center, coords, delete, find,",
881 		     "\n\tinsert, list, path, remove, rotate, scale, triangulate.",
882 		     (char *) NULL);
883     return TCL_ERROR;
884 }
885 
886 static int
vgpane(ClientData clientData,Tcl_Interp * interp,int argc,char * argv[])887 vgpane(ClientData clientData, Tcl_Interp * interp, int argc, char *argv[])
888 {
889     char vbuf[30];
890     vgpane_t *vgp;
891 
892     vgp = (vgpane_t *) malloc(sizeof(vgpane_t));
893     *(vgpane_t **) tclhandleAlloc(vgpaneTable, vbuf, NULL) = vgp;
894 
895     vgp->vc = (vconfig_t *) 0;
896     vgp->Npoly = 0;
897     vgp->N_poly_alloc = 250;
898     vgp->poly = malloc(vgp->N_poly_alloc * sizeof(poly));
899     vgp->interp = interp;
900     vgp->triangle_cmd = (char *) NULL;
901 
902     Tcl_CreateCommand(interp, vbuf, vgpanecmd,
903 		      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
904     Tcl_AppendResult(interp, vbuf, (char *) NULL);
905     return TCL_OK;
906 }
907 
Tclpathplan_Init(Tcl_Interp * interp)908 int Tclpathplan_Init(Tcl_Interp * interp)
909 {
910 #ifdef USE_TCL_STUBS
911     if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
912 	return TCL_ERROR;
913     }
914 #else
915     if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL) {
916 	return TCL_ERROR;
917     }
918 #endif
919     if (Tcl_PkgProvide(interp, "Tclpathplan", PACKAGE_VERSION) != TCL_OK) {
920 	return TCL_ERROR;
921     }
922 
923     Tcl_CreateCommand(interp, "vgpane", vgpane,
924 		      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
925 
926     vgpaneTable = tclhandleInit("vgpane", sizeof(vgpane_t), 10);
927 
928     return TCL_OK;
929 }
930 
Tclpathplan_SafeInit(Tcl_Interp * interp)931 int Tclpathplan_SafeInit(Tcl_Interp * interp)
932 {
933     return Tclpathplan_Init(interp);
934 }
935