2 * Copyright (c) 2016, S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
29 uint_fast8_t enough_colors
= 0;
31 /* what the program is doing */
37 /* strings for arrow bodies */
40 [(-1 + 1) * 3 + (-1 + 1)] = "/", /* */
41 [(-1 + 1) * 3 + (0 + 1)] = "|", /* */
42 [(-1 + 1) * 3 + (1 + 1)] = "\\", /* */
43 [(0 + 1) * 3 + (-1 + 1)] = "-", /* */
44 [(0 + 1) * 3 + (0 + 1)] = "o", /* */
45 [(0 + 1) * 3 + (1 + 1)] = "-", /* */
46 [(1 + 1) * 3 + (-1 + 1)] = "\\", /* */
47 [(1 + 1) * 3 + (0 + 1)] = "|", /* */
48 [(1 + 1) * 3 + (1 + 1)] = "/", /* */
51 /* strings for arrow heads */
54 [(-1 + 1) * 3 + (-1 + 1)] = "\u2b69", /* */
55 [(-1 + 1) * 3 + (0 + 1)] = "v", /* */
56 [(-1 + 1) * 3 + (1 + 1)] = "\u2b68", /* */
57 [(0 + 1) * 3 + (-1 + 1)] = "<", /* */
58 [(0 + 1) * 3 + (0 + 1)] = "\u2b59", /* */
59 [(0 + 1) * 3 + (1 + 1)] = ">", /* */
60 [(1 + 1) * 3 + (-1 + 1)] = "\u2b66", /* */
61 [(1 + 1) * 3 + (0 + 1)] = "^", /* */
62 [(1 + 1) * 3 + (1 + 1)] = "\u2b67", /* */
66 typedef struct arrow
{
67 /* index of the point from */
70 /* index of the point to */
73 /* multiplicity of the arrow */
78 typedef struct coord
{
86 /* A cluster algebra */
88 /* contains leftmost and bottommost */
91 /* contains rightmost and topmost */
97 /* x-coords of each point */
100 /* how many arrows */
107 /* given x and y, return the index of the point (possibly created) */
109 find_make_point(int x
,
118 for (i
= 0; i
< data
->point_num
; ++i
) {
119 c
= &data
->point_coord
[i
];
133 if (!(newmem
= realloc(data
->point_coord
, data
->point_num
*
134 sizeof *data
->point_coord
))) {
140 data
->point_coord
= newmem
;
141 data
->point_coord
[i
] = (coord
) { .x
= x
, .y
= y
};
150 /* add an arrow p1 -> p2 in data */
161 if (p1
>= data
->point_num
||
162 p2
>= data
->point_num
||
167 for (i
= 0; i
< data
->arrow_num
; ++i
) {
172 a
->multiplicity
+= mult
;
175 } else if (a
->from
== p2
&&
177 a
->multiplicity
-= mult
;
185 if (!(newmem
= realloc(data
->arrow
, data
->arrow_num
*
186 sizeof *data
->arrow
))) {
192 data
->arrow
= newmem
;
193 data
->arrow
[data
->arrow_num
- 1] = (arrow
) { .from
= p1
, .to
= p2
,
194 .multiplicity
= mult
};
199 /* fix up the bl and ur data */
201 recalc_extents(ca
*data
)
205 data
->bl
= data
->point_coord
[0];
206 data
->ur
= data
->point_coord
[0];
208 for (i
= 0; i
< data
->point_num
; ++i
) {
209 coord
*c
= &data
->point_coord
[i
];
211 if (data
->bl
.x
> c
->x
) {
215 if (data
->bl
.y
> c
->y
) {
219 if (data
->ur
.x
< c
->x
) {
223 if (data
->ur
.y
< c
->y
) {
229 /* read a file descriptor, initialize from it */
231 init_from_file(FILE *f
,
235 * each line should be in form
241 * all referenced points exist, referenced arrows affect
248 while (getline(&line
, &n
, f
) >= 0) {
257 if (!(s
= strchr(line
, '('))) {
262 x1
= strtol(++s
, 0, 10);
268 if (!(s
= strchr(s
, ','))) {
272 y1
= strtol(++s
, 0, 10);
278 if ((err
= find_make_point(x1
, y1
, &p1
, data
))) {
282 if (!(s
= strstr(s
, "->"))) {
286 if (!(s
= strchr(s
, '('))) {
291 x2
= strtol(++s
, 0, 10);
297 if (!(s
= strchr(s
, ','))) {
301 y2
= strtol(++s
, 0, 10);
307 if ((err
= find_make_point(x2
, y2
, &p2
, data
))) {
311 if ((err
= add_arrow(p1
, p2
, 1, data
))) {
316 if (errno
!= EINVAL
&&
321 if (data
->point_num
== 0) {
325 recalc_extents(data
);
332 /* draw the algebra */
344 for (i
= 0; i
< data
->arrow_num
; ++i
) {
345 arrow
*a
= &data
->arrow
[i
];
347 coord target
= { 0 };
352 if (!a
->multiplicity
||
357 cur
= data
->point_coord
[(a
->multiplicity
> 0) ? a
->from
:
359 target
= data
->point_coord
[(a
->multiplicity
> 0) ? a
->to
:
361 xdelta_orig
= target
.x
- cur
.x
;
362 ydelta_orig
= target
.y
- cur
.y
;
363 cpair
= (int) (M_PI
/ 8.0 + 4.0 * M_1_PI
* atan2(
364 (double) ydelta_orig
,
365 (double) xdelta_orig
) + 8);
366 cpair
= ((cpair
+ 8) % 8) + 1;
368 while (cur
.x
!= target
.x
||
370 int xdelta_cur
= target
.x
- cur
.x
;
371 int ydelta_cur
= target
.y
- cur
.y
;
372 int xdir
= (xdelta_cur
> 0) - (xdelta_cur
< 0);
373 int ydir
= (ydelta_cur
> 0) - (ydelta_cur
< 0);
375 if (xdelta_orig
== 0 ||
378 } else if (ydelta_orig
== 0 ||
381 } else if ((xdelta_orig
/ ydelta_orig
!= xdelta_cur
/
383 (ydelta_orig
/ xdelta_orig
!= ydelta_cur
/
385 xdir
*= !!(xdelta_cur
/ ydelta_cur
);
386 ydir
*= !!(ydelta_cur
/ xdelta_cur
);
389 if (height
- cur
.y
+ cy
- (height
/ 2) >= 0 &&
390 -cur
.y
+ cy
- (height
/ 2) < -1 &&
391 cur
.x
- cx
+ (width
/ 2) < width
&&
392 cur
.x
- cx
+ (width
/ 2) >= 0) {
393 move(height
- cur
.y
+ cy
- (height
/ 2), cur
.x
-
396 if (a
->multiplicity
> 1) {
401 attron(COLOR_PAIR(cpair
));
404 if (cur
.x
+ xdir
== target
.x
&&
405 cur
.y
+ ydir
== target
.y
) {
406 addstr(arrstr
[(ydir
+ 1) * 3 + (xdir
+
409 addstr(dirstr
[(ydir
+ 1) * 3 + (xdir
+
421 /* now draw the points */
422 for (i
= 0; i
< data
->point_num
; ++i
) {
423 coord
*c
= &data
->point_coord
[i
];
424 int x
= c
->x
- cx
+ (width
/ 2);
425 int y
= c
->y
- cy
+ (height
/ 2);
436 if (s
== SH_NORMAL
) {
444 /* prompt for a point to mutate at, mutate there */
459 memset(&input
, 0, sizeof input
);
462 for (i
= 0; i
< width
; ++i
) {
467 addstr("Mutate at (lists ok): ");
470 getnstr(input
, (sizeof input
) - 1);
476 l
= strtoll(s
, &t
, 16);
481 (size_t) l
>= data
->point_num
) {
487 /* add all needed new arrows */
488 for (i
= 0; i
< data
->arrow_num
; ++i
) {
489 arrow
*a
= &data
->arrow
[i
];
491 for (j
= i
+ 1; j
< data
->arrow_num
; ++j
) {
492 arrow
*b
= &data
->arrow
[j
];
496 if ((err
= add_arrow(a
->from
, b
->to
,
502 } else if (a
->from
== p
&&
504 if ((err
= add_arrow(b
->from
, a
->to
,
514 /* reverse all arrows */
515 for (i
= 0; i
< data
->arrow_num
; ++i
) {
516 arrow
*a
= &data
->arrow
[i
];
521 } else if (a
->to
== p
) {
528 s
= (s
== t
) ? (s
+ 1) : t
;
534 /* prompt for a point to delete, delete it */
547 memset(&input
, 0, sizeof input
);
550 for (i
= 0; i
< width
; ++i
) {
555 addstr("Delete point(s) (lists ok): ");
558 getnstr(input
, (sizeof input
) - 1);
564 l
= strtoll(s
, &t
, 16);
569 (size_t) l
>= data
->point_num
) {
575 /* kill arrows and swap with other */
576 for (i
= 0; i
< data
->arrow_num
; ++i
) {
577 arrow
*a
= &data
->arrow
[i
];
582 } else if (a
->from
== data
->point_num
- 1) {
584 } else if (a
->to
== data
->point_num
- 1) {
589 data
->point_coord
[p
] = data
->point_coord
[data
->point_num
- 1];
592 s
= (s
== t
) ? (s
+ 1) : t
;
598 /* prompt for a point to insert, insert it */
607 int orig_x
= (width
) / 2;
608 int orig_y
= (height
) / 2;
615 for (i
= 0; i
< width
; ++i
) {
620 addstr("h/j/k/l to move, Return to add");
626 } else if (x
>= width
) {
632 } else if (y
>= height
) {
639 switch (c
= getch()) {
712 return find_make_point(x
- orig_x
+ cx
, y
- orig_y
+ cy
, 0, data
);
715 /* prompt for two points to add an arrow between, add it */
730 memset(&input
, 0, sizeof input
);
733 for (i
= 0; i
< width
; ++i
) {
741 getnstr(input
, (sizeof input
) - 1);
745 l
= strtoll(s
, &t
, 16);
750 (size_t) l
>= data
->point_num
) {
757 memset(&input
, 0, sizeof input
);
760 for (i
= 0; i
< width
; ++i
) {
768 getnstr(input
, (sizeof input
) - 1);
772 l
= strtoll(s
, &t
, 16);
777 (size_t) l
>= data
->point_num
) {
784 memset(&input
, 0, sizeof input
);
787 for (i
= 0; i
< width
; ++i
) {
792 addstr("Multiplicity: ");
795 getnstr(input
, (sizeof input
) - 1);
803 l
= strtoll(s
, &t
, 10);
811 return add_arrow(p1
, p2
, l
, data
);
814 /* prompt for a file to save state to, save it */
825 memset(&input
, 0, sizeof input
);
828 for (i
= 0; i
< width
; ++i
) {
833 addstr("Save to file: ");
836 getnstr(input
, (sizeof input
) - 1);
844 if (!(f
= fopen(input
, "w"))) {
847 for (i
= 0; i
< width
; ++i
) {
852 addstr("Could not write to file!");
862 for (i
= 0; i
< data
->point_num
; ++i
) {
863 coord
*c
= &data
->point_coord
[i
];
864 fprintf(f
, "(%d,%d)\n", c
->x
, c
->y
);
869 for (i
= 0; i
< data
->arrow_num
; ++i
) {
870 arrow
*a
= &data
->arrow
[i
];
871 coord
*p1
= &data
->point_coord
[a
->from
];
872 coord
*p2
= &data
->point_coord
[a
->to
];
874 for (m
= 0; m
< a
->multiplicity
; ++m
) {
875 fprintf(f
, "(%d,%d) -> (%d,%d)\n", p1
->x
, p1
->y
, p2
->x
,
879 for (m
= 0; m
> a
->multiplicity
; --m
) {
880 fprintf(f
, "(%d,%d) -> (%d,%d)\n", p2
->x
, p2
->y
, p1
->x
,
888 for (i
= 0; i
< width
; ++i
) {
903 /* read keys, do stuff */
905 displayloop(ca
*data
)
909 uint_fast8_t should_quit
= 0;
911 int cx
= data
->bl
.x
+ (data
->ur
.x
- data
->bl
.x
+ 1) / 2;
912 int cy
= data
->bl
.y
+ (data
->ur
.y
- data
->bl
.y
+ 1) / 2;
917 getmaxyx(stdscr
, height
, width
);
918 height
= (height
>= 0) ? height
: 0;
919 width
= (width
>= 0) ? width
: 0;
921 draw_ca(data
, SH_NORMAL
, width
, height
, cx
, cy
);
924 addch('M' | A_STANDOUT
);
927 addch('p' | A_STANDOUT
);
930 addch('a' | A_STANDOUT
);
932 addch('D' | A_STANDOUT
);
933 addstr("elete point ");
934 addch('S' | A_STANDOUT
);
935 addstr("ave state ");
936 addch('Q' | A_STANDOUT
);
939 for (i
= 74; i
< width
; ++i
) {
943 switch ((c
= getch())) {
951 draw_ca(data
, SH_NAMES
, width
, height
, cx
, cy
);
954 if ((err
= mutate_at(data
, width
, height
))) {
961 draw_ca(data
, SH_NAMES
, width
, height
, cx
, cy
);
964 if ((err
= del_point(data
, width
, height
))) {
972 if ((err
= inp_point(data
, width
, height
, cx
, cy
))) {
979 draw_ca(data
, SH_NAMES
, width
, height
, cx
, cy
);
982 if ((err
= inp_arrow(data
, width
, height
))) {
989 save_state(data
, width
, height
);
1007 } while (!should_quit
);
1021 setlocale(LC_ALL
, "");
1024 if (!(template = fopen(argv
[1], "r"))) {
1025 perror("Could not open template file");
1030 err
= init_from_file(template, &data
);
1036 } else if (argc
!= 1) {
1037 fprintf(stderr
, "Usage: %s [path-to-input-file]\n", argv
[0]);
1050 if (COLORS
>= 256) {
1052 use_default_colors();
1054 /* no special reasons for these, I don't know color */
1055 init_pair(1, 1, -1);
1056 init_pair(2, 3, -1);
1057 init_pair(3, 2, -1);
1058 init_pair(4, 5, -1);
1059 init_pair(5, 4, -1);
1060 init_pair(6, 58, -1);
1061 init_pair(7, 202, -1);
1062 init_pair(8, 6, -1);
1070 intrflush(stdscr
, FALSE
);
1071 keypad(stdscr
, TRUE
);
1072 err
= displayloop(&data
);