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