20#include "../tcl-compat.h"
42#if (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 6) || TCL_MAJOR_VERSION > 8
44#ifndef Tcl_GetStringResult
45#define Tcl_GetStringResult(interp) interp->result
57#define HANDLE_FORMAT ("vgpane%" PRISIZE_T)
70static vgpanes_t vgpaneTable;
92 if (vgp->vc ==
NULL) {
94 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++)
95 obs[i] = &
LIST_AT(&vgp->poly, i)->boundary;
97 fprintf(stderr,
"bad arrangement\n");
102 return vgp->vc !=
NULL;
113 const char *separator =
"";
114 for (i = 0; i < npts; i++) {
115 agxbprint(result,
"%s%g %g", separator, p[i].x, p[i].y);
127 size_t vgcanvasHandle,
int npts,
137 char *
string = strchr(before,
'%');
138 if (
string !=
NULL) {
139 agxbput_n(&scripts, before, (
size_t)(
string - before));
160 if (before[1] ==
'\0') {
165 const char *script_value =
agxbuse(&scripts);
166 if (Tcl_GlobalEval(interp, script_value) != TCL_OK)
167 fprintf(stderr,
"%s while in binding: %s\n\n",
174 if (vgp->triangle_cmd) {
175 const size_t vgcanvasHandle = vgp->index;
227 if (sscanf(argv[0],
"%lg", &p->
x) != 1) {
228 Tcl_AppendResult(interp,
"invalid x coordinate: \"", argv[0],
"\"",
NULL);
231 if (sscanf(argv[1],
"%lg", &p->
y) != 1) {
232 Tcl_AppendResult(interp,
"invalid y coordinate: \"", argv[1],
"\"",
NULL);
242 for (
size_t i = 0; i < n; i++) {
257 return hypot(
dx,
dy);
266 beta = atan2(p.
x - c.
x, p.
y - c.
y);
267 const double sina = sin(beta +
alpha);
268 const double cosa = cos(beta +
alpha);
269 q.
x = c.
x + r * sina;
270 q.
y = c.
y - r * cosa;
278 q.
x = c.
x + gain * (p.
x - c.
x);
279 q.
y = c.
y + gain * (p.
y - c.
y);
284 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
287 for (
size_t j = i++; i <
LIST_SIZE(&vgp->poly); i++, j++) {
299 const char *vargv[],
Tcl_Size vargc) {
303 np = allocpoly(vgp,
id, vargc);
304 for (
Tcl_Size i = 0; i < vargc; i += 2) {
306 if (result != TCL_OK)
316 size_t *n_barriers) {
319 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
320 if (
LIST_GET(&vgp->poly, i).id == pp)
322 if (
LIST_GET(&vgp->poly, i).id == qp)
324 n +=
LIST_GET(&vgp->poly, i).boundary.pn;
328 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
329 if (
LIST_GET(&vgp->poly, i).id == pp)
331 if (
LIST_GET(&vgp->poly, i).id == qp)
333 for (
size_t j = 0; j <
LIST_GET(&vgp->poly, i).boundary.pn; j++) {
335 if (k >=
LIST_GET(&vgp->poly, i).boundary.pn)
337 bar[b].
a =
LIST_GET(&vgp->poly, i).boundary.ps[j];
338 bar[b].
b =
LIST_GET(&vgp->poly, i).boundary.ps[k];
352 snprintf(buf,
sizeof(buf),
"%g", p.
x);
353 Tcl_AppendElement(interp, buf);
354 snprintf(buf,
sizeof(buf),
"%g", p.
y);
355 Tcl_AppendElement(interp, buf);
364 assert(panes !=
NULL);
365 assert(handle !=
NULL);
395 assert(panes !=
NULL);
412vgpanecmd(ClientData clientData, Tcl_Interp * interp,
int argc,
426 Tcl_AppendResult(interp,
"wrong # args: should be \"",
427 " ", argv[0],
" method ?arg arg ...?\"",
NULL);
431 Tcl_AppendResult(interp,
"Invalid handle: \"", argv[0],
"\"",
NULL);
435 if (strcmp(argv[1],
"coords") == 0) {
437 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
438 " ", argv[1],
" id ?x1 y1 x2 y2...?\"",
NULL);
441 if (sscanf(argv[2],
"%d", &polyid) != 1) {
442 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
447 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
448 if (
LIST_GET(&vgp->poly, i).id == polyid) {
449 const size_t n =
LIST_GET(&vgp->poly, i).boundary.pn;
450 for (
size_t j = 0; j < n; j++) {
456 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
462 bool vargv_needs_free =
false;
464 result = Tcl_SplitList(interp, argv[3], &vargc, &vargv);
465 if (result != TCL_OK) {
468 vargv_needs_free =
true;
473 if (!vargc || vargc % 2) {
474 Tcl_AppendResult(interp,
475 "There must be a multiple of two terms in the list.",
NULL);
476 if (vargv_needs_free) {
477 Tcl_Free((
char *)vargv);
486 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
487 if (vargv_needs_free) {
488 Tcl_Free((
char *)vargv);
493 result =
insert_poly(interp, vgp, polyid, vargv, vargc);
494 if (vargv_needs_free) {
495 Tcl_Free((
char *)vargv);
499 }
else if (strcmp(argv[1],
"debug") == 0) {
501 printf(
"debug output goes here\n");
504 }
else if (strcmp(argv[1],
"delete") == 0) {
506 free(vgp->triangle_cmd);
510 Tcl_DeleteCommand(interp, argv[0]);
515 }
else if (strcmp(argv[1],
"find") == 0) {
519 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
520 " ", argv[1],
" x y\"",
NULL);
524 bool vargv_needs_free =
false;
526 result = Tcl_SplitList(interp, argv[2], &(
Tcl_Size){0}, &vargv);
527 if (result != TCL_OK) {
530 vargv_needs_free =
true;
534 result =
scanpoint(interp, &vargv[0], &p);
535 if (vargv_needs_free) {
536 Tcl_Free((
char *)vargv);
538 if (result != TCL_OK)
542 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
544 Tcl_AppendElement(interp,
ITOS(
LIST_GET(&vgp->poly, i).id));
549 }
else if (strcmp(argv[1],
"insert") == 0) {
553 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
554 " ", argv[1],
" x1 y1 x2 y2 ...\"",
NULL);
560 bool vargv_needs_free =
false;
562 result = Tcl_SplitList(interp, argv[2], &vargc, &vargv);
563 if (result != TCL_OK) {
566 vargv_needs_free =
true;
572 if (!vargc || vargc % 2) {
573 Tcl_AppendResult(interp,
574 "There must be a multiple of two terms in the list.",
NULL);
575 if (vargv_needs_free) {
576 Tcl_Free((
char *)vargv);
583 result =
insert_poly(interp, vgp, polyid, vargv, vargc);
584 if (vargv_needs_free) {
585 Tcl_Free((
char *)vargv);
587 if (result != TCL_OK)
590 Tcl_AppendResult(interp,
ITOS(polyid),
NULL);
593 }
else if (strcmp(argv[1],
"list") == 0) {
595 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
596 Tcl_AppendElement(interp,
ITOS(
LIST_GET(&vgp->poly, i).id));
600 }
else if (strcmp(argv[1],
"path") == 0) {
604 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
605 " ", argv[1],
" x1 y1 x2 y2\"",
NULL);
610 bool vargv_needs_free =
false;
612 result = Tcl_SplitList(interp, argv[2], &vargc, &vargv);
613 if (result != TCL_OK) {
616 vargv_needs_free =
true;
622 Tcl_AppendResult(interp,
623 "invalid points: should be: \"x1 y1 x2 y2\"",
NULL);
624 if (vargv_needs_free) {
625 Tcl_Free((
char *)vargv);
629 result =
scanpoint(interp, &vargv[0], &p);
630 if (result != TCL_OK) {
631 if (vargv_needs_free) {
632 Tcl_Free((
char *)vargv);
636 result =
scanpoint(interp, &vargv[2], &q);
637 if (vargv_needs_free) {
638 Tcl_Free((
char *)vargv);
640 if (result != TCL_OK)
647 for (
size_t i = 0; i < line.
pn; i++) {
654 }
else if (strcmp(argv[1],
"bind") == 0) {
655 if (argc < 2 || argc > 4) {
656 Tcl_AppendResult(interp,
"wrong # args: should be \"",
657 argv[0],
" bind triangle ?command?\"",
NULL);
661 Tcl_AppendElement(interp,
"triangle");
665 if (strcmp(argv[2],
"triangle") == 0) {
666 s = vgp->triangle_cmd;
670 Tcl_AppendResult(interp,
"unknown event \"", argv[2],
671 "\": must be one of:\n\ttriangle.",
NULL);
675 Tcl_AppendResult(interp,
s,
NULL);
678 }
else if (strcmp(argv[1],
"bpath") == 0) {
682 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
683 " ", argv[1],
" x1 y1 x2 y2\"",
NULL);
688 bool vargv_needs_free =
false;
690 result = Tcl_SplitList(interp, argv[2], &vargc, &vargv);
691 if (result != TCL_OK) {
694 vargv_needs_free =
true;
700 Tcl_AppendResult(interp,
701 "invalid points: should be: \"x1 y1 x2 y2\"",
NULL);
705 result =
scanpoint(interp, &vargv[0], &p);
706 if (result != TCL_OK) {
707 if (vargv_needs_free) {
708 Tcl_Free((
char *)vargv);
712 result =
scanpoint(interp, &vargv[2], &q);
713 if (vargv_needs_free) {
714 Tcl_Free((
char *)vargv);
716 if (result != TCL_OK)
722 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
734 slopes[0].
x = slopes[0].
y = 0.0;
735 slopes[1].
x = slopes[1].
y = 0.0;
736 Proutespline(barriers, n_barriers, line, slopes, &spline);
738 for (
size_t i = 0; i < spline.
pn; i++) {
744 }
else if (strcmp(argv[1],
"bbox") == 0) {
746 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
747 " ", argv[1],
" id\"",
NULL);
750 if (sscanf(argv[2],
"%d", &polyid) != 1) {
751 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
754 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
755 if (
LIST_GET(&vgp->poly, i).id == polyid) {
759 for (
size_t j = 1; j < pp.
pn; j++) {
761 UR.
x = fmax(UR.
x, p.
x);
762 UR.
y = fmax(UR.
y, p.
y);
763 LL.
x = fmin(LL.
x, p.
x);
764 LL.
y = fmin(LL.
y, p.
y);
771 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
774 }
else if (strcmp(argv[1],
"center") == 0) {
776 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
777 " ", argv[1],
" id\"",
NULL);
780 if (sscanf(argv[2],
"%d", &polyid) != 1) {
781 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
784 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
785 if (
LIST_GET(&vgp->poly, i).id == polyid) {
787 LIST_GET(&vgp->poly, i).boundary.pn));
791 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
794 }
else if (strcmp(argv[1],
"triangulate") == 0) {
796 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
797 " ", argv[1],
" id\"",
NULL);
801 if (sscanf(argv[2],
"%d", &polyid) != 1) {
802 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
806 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
807 if (
LIST_GET(&vgp->poly, i).id == polyid) {
810 Tcl_AppendResult(interp,
"polygon ", argv[2],
" has fewer than 3 points "
811 "and thus cannot be triangulated",
NULL);
818 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
820 }
else if (strcmp(argv[1],
"rotate") == 0) {
822 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
823 " ", argv[1],
" id alpha\"",
NULL);
826 if (sscanf(argv[2],
"%d", &polyid) != 1) {
827 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
830 if (sscanf(argv[3],
"%lg", &
alpha) != 1) {
831 Tcl_AppendResult(interp,
"not an angle in radians: ", argv[3],
NULL);
834 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
835 if (
LIST_GET(&vgp->poly, i).id == polyid) {
836 const size_t n =
LIST_GET(&vgp->poly, i).boundary.pn;
839 for (
size_t j = 0; j < n; j++) {
845 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
848 }
else if (strcmp(argv[1],
"scale") == 0) {
850 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
851 " ", argv[1],
" id gain\"",
NULL);
854 if (sscanf(argv[2],
"%d", &polyid) != 1) {
855 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
858 if (sscanf(argv[3],
"%lg", &gain) != 1) {
859 Tcl_AppendResult(interp,
"not a number: ", argv[3],
NULL);
862 for (
size_t i = 0; i <
LIST_SIZE(&vgp->poly); i++) {
863 if (
LIST_GET(&vgp->poly, i).id == polyid) {
864 const size_t n =
LIST_GET(&vgp->poly, i).boundary.pn;
867 for (
size_t j = 0; j < n; j++) {
873 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
876 }
else if (strcmp(argv[1],
"remove") == 0) {
878 Tcl_AppendResult(interp,
"wrong # args: should be \"", argv[0],
879 " ", argv[1],
" id\"",
NULL);
882 if (sscanf(argv[2],
"%d", &polyid) != 1) {
883 Tcl_AppendResult(interp,
"not an integer: ", argv[2],
NULL);
890 Tcl_AppendResult(interp,
" no such polygon: ", argv[2],
NULL);
894 Tcl_AppendResult(interp,
"bad method \"", argv[1],
895 "\" must be one of:",
896 "\n\tbbox, bind, bpath, center, coords, delete, find,",
897 "\n\tinsert, list, path, remove, rotate, scale, triangulate.",
NULL);
902vgpane(ClientData clientData, Tcl_Interp * interp,
int argc,
const char *argv[])
908 const vgpane_t vg = {.interp = interp};
911 vgp->index =
LIST_SIZE(&vgpaneTable) - 1;
916 const char *
const vbuf =
agxbuse(&buffer);
919 Tcl_AppendResult(interp, vbuf,
NULL);
928 if (Tcl_InitStubs(interp, TCL_VERSION, 0) ==
NULL) {
932 if (Tcl_PkgRequire(interp,
"Tcl", TCL_VERSION, 0) ==
NULL) {
938 char adjusted_version[
sizeof(PACKAGE_VERSION)] = PACKAGE_VERSION;
939 char *tilde_dev = strstr(adjusted_version,
"~dev.");
940 if (tilde_dev !=
NULL) {
942 memmove(tilde_dev + 1, tilde_dev + strlen(
"~dev."),
943 strlen(tilde_dev + strlen(
"~dev.")) + 1);
945 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)
type-generic dynamically expanding list
#define LIST_AT(list, index)
#define LIST_APPEND(list, item)
#define LIST_POP_BACK(list)
#define LIST_SET(list, index, item)
#define LIST_IS_EMPTY(list)
#define LIST_GET(list, index)
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)
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 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)
TRI_API int Ptriangulate(Ppoly_t *polygon, void(*fn)(void *closure, const Ppoint_t tri[]), void *vc)