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