20#include "../tcl-compat.h"
41#if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 6) || TCL_MAJOR_VERSION > 8
43#ifndef Tcl_GetStringResult
44#define Tcl_GetStringResult(interp) interp->result
58#define HANDLE_FORMAT ("vgpane%" PRISIZE_T)
76 polys_append(&vgp->poly, (
poly){.id = id});
77 poly *rv = polys_back(&vgp->poly);
95 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++)
96 obs[i] = &polys_at(&vgp->
poly, i)->boundary;
98 fprintf(stderr,
"bad arrangement\n");
114 const char *separator =
"";
115 for (i = 0; i < npts; i++) {
116 agxbprint(result,
"%s%g %g", separator, p[i].x, p[i].y);
128 size_t vgcanvasHandle,
int npts,
138 char *
string = strchr(before,
'%');
139 if (
string !=
NULL) {
140 agxbput_n(&scripts, before, (
size_t)(
string - before));
161 if (before[1] ==
'\0') {
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",
176 const size_t vgcanvasHandle = vgp->
index;
228 if (sscanf(argv[0],
"%lg", &p->
x) != 1) {
229 Tcl_AppendResult(interp,
"invalid x coordinate: \"", argv[0],
"\"",
NULL);
232 if (sscanf(argv[1],
"%lg", &p->
y) != 1) {
233 Tcl_AppendResult(interp,
"invalid y coordinate: \"", argv[1],
"\"",
NULL);
243 for (
size_t i = 0; i < n; i++) {
258 return hypot(
dx,
dy);
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;
279 q.
x = c.
x + gain * (p.
x - c.
x);
280 q.
y = c.
y + gain * (p.
y - c.
y);
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));
291 polys_resize(&vgp->
poly, polys_size(&vgp->
poly) - 1, (
poly){0});
300 const char *vargv[],
Tcl_Size vargc) {
305 for (
Tcl_Size i = 0; i < vargc; i += 2) {
307 if (result != TCL_OK)
317 size_t *n_barriers) {
320 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
321 if (polys_get(&vgp->
poly, i).id == pp)
323 if (polys_get(&vgp->
poly, i).id == qp)
325 n += polys_get(&vgp->
poly, i).boundary.pn;
329 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
330 if (polys_get(&vgp->
poly, i).id == pp)
332 if (polys_get(&vgp->
poly, i).id == qp)
334 for (
size_t j = 0; j < polys_get(&vgp->
poly, i).boundary.pn; j++) {
336 if (k >= polys_get(&vgp->
poly, i).boundary.pn)
338 bar[b].
a = polys_get(&vgp->
poly, i).boundary.ps[j];
339 bar[b].
b = polys_get(&vgp->
poly, i).boundary.ps[k];
353 snprintf(buf,
sizeof(buf),
"%g", p.
x);
354 Tcl_AppendElement(interp, buf);
355 snprintf(buf,
sizeof(buf),
"%g", p.
y);
356 Tcl_AppendElement(interp, buf);
365 assert(panes !=
NULL);
366 assert(handle !=
NULL);
372 if (index >= vgpanes_size(panes)) {
375 vgpane_t *
const pane = vgpanes_at(panes, index);
396 assert(panes !=
NULL);
399 while (!vgpanes_is_empty(panes)) {
400 if (!vgpanes_back(panes)->valid) {
401 (void)vgpanes_pop_back(panes);
406 if (vgpanes_is_empty(panes)) {
413vgpanecmd(ClientData clientData, Tcl_Interp * interp,
int argc,
428 Tcl_AppendResult(interp,
"wrong # args: should be \"",
429 " ", argv[0],
" method ?arg arg ...?\"",
NULL);
433 Tcl_AppendResult(interp,
"Invalid handle: \"", argv[0],
"\"",
NULL);
437 if (strcmp(argv[1],
"coords") == 0) {
439 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
440 " ", argv[1],
" id ?x1 y1 x2 y2...?\"",
NULL);
443 if (sscanf(argv[2],
"%d", &
polyid) != 1) {
444 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
449 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
451 const size_t n = polys_get(&vgp->
poly, i).boundary.pn;
452 for (
size_t j = 0; j < n; j++) {
458 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
464 bool vargv_needs_free =
false;
466 result = Tcl_SplitList(interp, argv[3], &vargc, &vargv);
467 if (result != TCL_OK) {
470 vargv_needs_free =
true;
475 if (!vargc || vargc % 2) {
476 Tcl_AppendResult(interp,
477 "There must be a multiple of two terms in the list.",
NULL);
478 if (vargv_needs_free) {
479 Tcl_Free((
char *)vargv);
488 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
489 if (vargv_needs_free) {
490 Tcl_Free((
char *)vargv);
496 if (vargv_needs_free) {
497 Tcl_Free((
char *)vargv);
501 }
else if (strcmp(argv[1],
"debug") == 0) {
503 printf(
"debug output goes here\n");
506 }
else if (strcmp(argv[1],
"delete") == 0) {
511 polys_free(&vgp->
poly);
512 Tcl_DeleteCommand(interp, argv[0]);
517 }
else if (strcmp(argv[1],
"find") == 0) {
521 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
522 " ", argv[1],
" x y\"",
NULL);
526 bool vargv_needs_free =
false;
528 result = Tcl_SplitList(interp, argv[2], &(
Tcl_Size){0}, &vargv);
529 if (result != TCL_OK) {
532 vargv_needs_free =
true;
536 result =
scanpoint(interp, &vargv[0], &p);
537 if (vargv_needs_free) {
538 Tcl_Free((
char *)vargv);
540 if (result != TCL_OK)
544 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
545 if (
in_poly(polys_get(&vgp->
poly, i).boundary, p)) {
546 snprintf(vbuf,
sizeof(vbuf),
"%d", polys_get(&vgp->
poly, i).id);
547 Tcl_AppendElement(interp, vbuf);
552 }
else if (strcmp(argv[1],
"insert") == 0) {
556 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
557 " ", argv[1],
" x1 y1 x2 y2 ...\"",
NULL);
563 bool vargv_needs_free =
false;
565 result = Tcl_SplitList(interp, argv[2], &vargc, &vargv);
566 if (result != TCL_OK) {
569 vargv_needs_free =
true;
575 if (!vargc || vargc % 2) {
576 Tcl_AppendResult(interp,
577 "There must be a multiple of two terms in the list.",
NULL);
578 if (vargv_needs_free) {
579 Tcl_Free((
char *)vargv);
587 if (vargv_needs_free) {
588 Tcl_Free((
char *)vargv);
590 if (result != TCL_OK)
593 snprintf(vbuf,
sizeof(vbuf),
"%d",
polyid);
594 Tcl_AppendResult(interp, vbuf,
NULL);
597 }
else if (strcmp(argv[1],
"list") == 0) {
599 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
600 snprintf(vbuf,
sizeof(vbuf),
"%d", polys_get(&vgp->
poly, i).id);
601 Tcl_AppendElement(interp, vbuf);
605 }
else if (strcmp(argv[1],
"path") == 0) {
609 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
610 " ", argv[1],
" x1 y1 x2 y2\"",
NULL);
615 bool vargv_needs_free =
false;
617 result = Tcl_SplitList(interp, argv[2], &vargc, &vargv);
618 if (result != TCL_OK) {
621 vargv_needs_free =
true;
627 Tcl_AppendResult(interp,
628 "invalid points: should be: \"x1 y1 x2 y2\"",
NULL);
629 if (vargv_needs_free) {
630 Tcl_Free((
char *)vargv);
634 result =
scanpoint(interp, &vargv[0], &p);
635 if (result != TCL_OK) {
636 if (vargv_needs_free) {
637 Tcl_Free((
char *)vargv);
641 result =
scanpoint(interp, &vargv[2], &q);
642 if (vargv_needs_free) {
643 Tcl_Free((
char *)vargv);
645 if (result != TCL_OK)
652 for (
size_t i = 0; i < line.
pn; i++) {
659 }
else if (strcmp(argv[1],
"bind") == 0) {
660 if (argc < 2 || argc > 4) {
661 Tcl_AppendResult(interp,
"wrong # args: should be \"",
662 argv[0],
" bind triangle ?command?\"",
NULL);
666 Tcl_AppendElement(interp,
"triangle");
670 if (strcmp(argv[2],
"triangle") == 0) {
675 Tcl_AppendResult(interp,
"unknown event \"", argv[2],
676 "\": must be one of:\n\ttriangle.",
NULL);
680 Tcl_AppendResult(interp,
s,
NULL);
683 }
else if (strcmp(argv[1],
"bpath") == 0) {
687 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
688 " ", argv[1],
" x1 y1 x2 y2\"",
NULL);
693 bool vargv_needs_free =
false;
695 result = Tcl_SplitList(interp, argv[2], &vargc, &vargv);
696 if (result != TCL_OK) {
699 vargv_needs_free =
true;
705 Tcl_AppendResult(interp,
706 "invalid points: should be: \"x1 y1 x2 y2\"",
NULL);
710 result =
scanpoint(interp, &vargv[0], &p);
711 if (result != TCL_OK) {
712 if (vargv_needs_free) {
713 Tcl_Free((
char *)vargv);
717 result =
scanpoint(interp, &vargv[2], &q);
718 if (vargv_needs_free) {
719 Tcl_Free((
char *)vargv);
721 if (result != TCL_OK)
727 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
728 const poly tpp = polys_get(&vgp->
poly, i);
739 slopes[0].
x = slopes[0].
y = 0.0;
740 slopes[1].
x = slopes[1].
y = 0.0;
741 Proutespline(barriers, n_barriers, line, slopes, &spline);
743 for (
size_t i = 0; i < spline.
pn; i++) {
749 }
else if (strcmp(argv[1],
"bbox") == 0) {
751 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
752 " ", argv[1],
" id\"",
NULL);
755 if (sscanf(argv[2],
"%d", &
polyid) != 1) {
756 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
759 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
764 for (
size_t j = 1; j < pp.
pn; j++) {
766 UR.
x = fmax(UR.
x, p.
x);
767 UR.
y = fmax(UR.
y, p.
y);
768 LL.
x = fmin(LL.
x, p.
x);
769 LL.
y = fmin(LL.
y, p.
y);
776 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
779 }
else if (strcmp(argv[1],
"center") == 0) {
781 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
782 " ", argv[1],
" id\"",
NULL);
785 if (sscanf(argv[2],
"%d", &
polyid) != 1) {
786 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
789 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
792 polys_get(&vgp->
poly, i).boundary.pn));
796 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
799 }
else if (strcmp(argv[1],
"triangulate") == 0) {
801 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
802 " ", argv[1],
" id\"",
NULL);
806 if (sscanf(argv[2],
"%d", &
polyid) != 1) {
807 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
811 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
815 Tcl_AppendResult(interp,
"polygon ", argv[2],
" has fewer than 3 points "
816 "and thus cannot be triangulated",
NULL);
823 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
825 }
else if (strcmp(argv[1],
"rotate") == 0) {
827 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
828 " ", argv[1],
" id alpha\"",
NULL);
831 if (sscanf(argv[2],
"%d", &
polyid) != 1) {
832 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
835 if (sscanf(argv[3],
"%lg", &
alpha) != 1) {
836 Tcl_AppendResult(interp,
"not an angle in radians: ", argv[3],
NULL);
839 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
841 const size_t n = polys_get(&vgp->
poly, i).boundary.pn;
842 ps = polys_get(&vgp->
poly, i).boundary.ps;
844 for (
size_t j = 0; j < n; j++) {
850 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
853 }
else if (strcmp(argv[1],
"scale") == 0) {
855 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
856 " ", argv[1],
" id gain\"",
NULL);
859 if (sscanf(argv[2],
"%d", &
polyid) != 1) {
860 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
863 if (sscanf(argv[3],
"%lg", &gain) != 1) {
864 Tcl_AppendResult(interp,
"not a number: ", argv[3],
NULL);
867 for (
size_t i = 0; i < polys_size(&vgp->
poly); i++) {
869 const size_t n = polys_get(&vgp->
poly, i).boundary.pn;
870 ps = polys_get(&vgp->
poly, i).boundary.ps;
872 for (
size_t j = 0; j < n; j++) {
878 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
881 }
else if (strcmp(argv[1],
"remove") == 0) {
883 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
884 " ", argv[1],
" id\"",
NULL);
887 if (sscanf(argv[2],
"%d", &
polyid) != 1) {
888 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
895 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
899 Tcl_AppendResult(interp,
"bad method \"", argv[1],
900 "\" must be one of:",
901 "\n\tbbox, bind, bpath, center, coords, delete, find,",
902 "\n\tinsert, list, path, remove, rotate, scale, triangulate.",
NULL);
907vgpane(ClientData clientData, Tcl_Interp * interp,
int argc,
const char *argv[])
921 const char *
const vbuf =
agxbuse(&buffer);
924 Tcl_AppendResult(interp, vbuf,
NULL);
933 if (Tcl_InitStubs(interp, TCL_VERSION, 0) ==
NULL) {
937 if (Tcl_PkgRequire(interp,
"Tcl", TCL_VERSION, 0) ==
NULL) {
943 char adjusted_version[
sizeof(PACKAGE_VERSION)] = PACKAGE_VERSION;
944 char *tilde_dev = strstr(adjusted_version,
"~dev.");
945 if (tilde_dev !=
NULL) {
947 memmove(tilde_dev + 1, tilde_dev + strlen(
"~dev."),
948 strlen(tilde_dev + strlen(
"~dev.")) + 1);
950 if (Tcl_PkgProvide(interp,
"Tclpathplan", adjusted_version) != TCL_OK) {
static void agxbfree(agxbuf *xb)
free any malloced resources
static size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz)
append string s of length ssz into xb
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
static WUR char * agxbuse(agxbuf *xb)
static size_t agxblen(const agxbuf *xb)
return number of characters currently stored
static int agxbputc(agxbuf *xb, char c)
add character to buffer
static char * agxbdisown(agxbuf *xb)
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
static void * gv_calloc(size_t nmemb, size_t size)
vconfig_t * Pobsopen(Ppoly_t **obs, int n_obs)
void Pobsclose(vconfig_t *config)
void Pobspath(vconfig_t *config, Ppoint_t p0, int poly0, Ppoint_t p1, int poly1, Ppolyline_t *output_route)
bool in_poly(const Ppoly_t poly, Ppoint_t q)
int Plegal_arrangement(Ppoly_t **polys, int n_polys)
#define DEFINE_LIST(name, type)
void make_CW(Ppoly_t *poly)
NEATOPROCS_API void s1(graph_t *, node_t *)
int Proutespline(Pedge_t *barriers, size_t n_barriers, Ppolyline_t input_route, Pvector_t endpoint_slopes[2], Ppolyline_t *output_route)
size_t index
position within allocated handles list
bool valid
is this pane currently allocated?
static void vc_stale(vgpane_t *vgp)
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)
static void garbage_collect_vgpanes(vgpanes_t *panes)
static vgpanes_t vgpaneTable
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)
#define HANDLE_FORMAT
printf format for TCL handles
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 insert_poly(Tcl_Interp *interp, vgpane_t *vgp, int id, const char *vargv[], Tcl_Size vargc)
static int vgpanecmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[])
static point center(point vertex[], size_t n)
static double distance(point p, point q)
static point rotate(point c, point p, double alpha)
#define Tcl_GetStringResult(interp)
static int scanpoint(Tcl_Interp *interp, const char *argv[], point *p)
static bool remove_poly(vgpane_t *vgp, int id)
static poly * allocpoly(vgpane_t *vgp, int id, Tcl_Size npts)
TRI_API int Ptriangulate(Ppoly_t *polygon, void(*fn)(void *closure, const Ppoint_t tri[]), void *vc)