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