14 #define TICKS_PER_FRAME (1000 / 60)
16 #define TRIG_PRECALC_NUM 4
18 /* What clicking does */
19 static enum ui_action
{
34 /* The window we'll be using */
35 static SDL_Window
*sdl_win
;
37 /* The renderer we'll be using */
38 static SDL_Renderer
*sdl_renderer
;
40 /* How to limit the framerate */
41 static Uint32 frame_start_ticks
;
43 /* If conf/deny, the texture onscreen */
44 static SDL_Texture
*conf_deny
;
46 /* The informative texture */
47 static SDL_Texture
*selected_info
;
49 /* Buffer for event queue */
50 static struct ui_event
*eq_buf
;
52 /* Current max length of event queue */
55 /* Current head of queue */
56 static size_t eq_head
;
58 /* Current tail of queue */
59 static size_t eq_tail
;
61 /* The quiver we'll be using */
62 static struct quiver
*q
;
64 /* Width of drawing area in pixels */
65 static int da_pix_width
;
67 /* Height of drawing area in pixels */
68 static int da_pix_height
;
70 /* The background color */
71 static SDL_Color color_bg
= { .r
= 0xe2, .g
= 0xe2, .b
= 0xe2, .a
= 0xff };
73 /* The normal vertex color */
74 static SDL_Color color_v
= { .r
= 0x82, .g
= 0x82, .b
= 0xb2, .a
= 0xff };
76 /* The vertex color for preview vertices */
77 static SDL_Color color_v_preview
= { .r
= 0x82, .g
= 0x82, .b
= 0xb2, .a
=
80 /* The normal vertex outline color */
81 static SDL_Color color_outline
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
= 0x80 };
83 /* The vertex outline color for preview vertices */
84 static SDL_Color color_outline_preview
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
=
87 /* The selected vertex outline color */
88 static SDL_Color color_outline_sel
= { .r
= 0x42, .g
= 0x42, .b
= 0xe2, .a
=
92 static SDL_Color color_font
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
= 0x80 };
94 /* The normal edge color */
95 static SDL_Color color_e_normal
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
=
98 /* The half edge color */
99 static SDL_Color color_e_half
= { .r
= 0x12, .g
= 0x12, .b
= 0x12, .a
= 0x60 };
101 /* The abnormal edge color */
102 static SDL_Color color_e_abnormal
= { .r
= 0xd0, .g
= 0x12, .b
= 0x12, .a
=
105 /* The selected edge color */
106 static SDL_Color color_e_sel
= { .r
= 0xb2, .g
= 0xb2, .b
= 0xe2, .a
= 0x40 };
108 /* The font for node names, instructions, etc */
109 static TTF_Font
*normal_font
;
111 /* The base font size */
112 static uint_fast8_t base_font_size
= 12;
114 /* How much space (pixels) between lower left of screen and bottom text */
115 static unsigned int text_border_padding
= 24;
117 /* Strings to display at bottom of screen */
118 static const char *bottom_string
[] = {
120 [UIA_NONE
] = "[m] Mutate\n[v] Create new vertex\n"
121 "[d] Delete vertex/arrow\n[e] Add edge\n"
122 "[h] Add half edge\n[f] Increase vertex fatness\n"
123 "[g] Decrease vertex fatness\n[q] Quit\n",
125 "[Mouse1] Create new vertex\n[ESC] Cancel", /* */
126 [UIA_DELETE
] = "[Mouse1] Delete vertex/edge\n[ESC] Cancel", /* */
127 [UIA_NEW_EDGE_1
] = "[Mouse1] Select start\n[ESC] Cancel", /* */
128 [UIA_NEW_EDGE_2
] = "[Mouse1] Select end\n[ESC] Cancel", /* */
129 [UIA_NEW_H_EDGE_1
] = "[Mouse1] Select start\n[ESC] Cancel", /* */
130 [UIA_NEW_H_EDGE_2
] = "[Mouse1] Select end\n[ESC] Cancel", /* */
131 [UIA_INC_FATNESS
] = "[Mouse1] Increase fatness\n[ESC] Cancel", /* */
132 [UIA_DEC_FATNESS
] = "[Mouse1] Decrease fatness\n[ESC] Cancel", /* */
133 [UIA_MUTATE
] = "[Mouse1] Mutate at vertex\n[ESC] Cancel", /* */
134 [UIA_CONF_DENY
] = "[y] Confirm\n[n] Cancel", /* */
138 /* The texture containing what to show on the bottom */
139 static SDL_Texture
*bottom_text
[UIA_LEN
];
141 /* The textures containing the names of all vertices - indexed just like q */
142 static SDL_Texture
**vertex_names
;
144 /* The number of elements of vertex_names */
145 static size_t vertex_names_len
;
147 /* Drawing offset x */
150 /* Drawing offset x */
153 /* How wide (in pixels) a fatness 1 node should be */
154 static unsigned int base_node_radius
= 8;
156 /* How much (in pixels) a node should widen for each fatness level */
157 static unsigned int node_radius_per_fatness
= 7;
159 /* How wide (in pixels) the outline should be */
160 static int outline_width
= 2;
162 /* How wide (in pixels) the arrowheads should be */
163 static int arrow_length
= 7;
165 /* How narrow the arrowheads should be */
166 static double arrow_angle
= 6.5 * M_PI
/ 8.0;
168 /* How close to an arrow (in pixels) for it to be selected */
169 static int edge_margin
= 5;
171 /* If we're interacting with a vertex, which one */
172 static size_t selected_vertex
= (size_t) -1;
174 /* If we're interacting with an edge, the i */
175 static size_t selected_edge_i
= (size_t) -1;
177 /* If we're interacting with an edge, the j */
178 static size_t selected_edge_j
= (size_t) -1;
180 /* If we're adding an edge, the last vertex we clicked on */
181 static size_t last_clicked_vertex
= (size_t) -1;
183 /* x-coordinate of last mouse position */
184 static int last_mouse_x
= -1;
186 /* y-coordinate of last mouse position */
187 static int last_mouse_y
= -1;
190 static double precalc_sins
[TRIG_PRECALC_NUM
];
193 static double precalc_coss
[TRIG_PRECALC_NUM
];
195 /* Precalculate sines and cosines */
196 static void precalc_trig(void)
198 precalc_coss
[0] = 0.0;
199 precalc_sins
[0] = 1.0;
201 for (size_t j
= 1; j
< TRIG_PRECALC_NUM
- 1; ++j
) {
202 double theta
= (M_PI
* j
) / (2 * (TRIG_PRECALC_NUM
- 1));
204 precalc_sins
[j
] = sin(theta
);
205 precalc_coss
[j
] = cos(theta
);
208 precalc_coss
[TRIG_PRECALC_NUM
- 1] = 0.0;
209 precalc_sins
[TRIG_PRECALC_NUM
- 1] = 1.0;
213 static int gcd(uint_fast8_t x
, uint_fast8_t y
)
231 /* Allocate and print a rational in the way a user expects */
232 static int pretty_fraction(struct rational
*r
, char **out
)
237 size_t len
= snprintf(0, 0, "%d", (int) r
->p
);
239 if (!(*out
= malloc(len
+ 1))) {
245 sprintf(*out
, "%d", (int) r
->p
);
249 size_t len
= snprintf(0, 0, "%d/%u", (int) r
->p
, (unsigned int) r
->q
);
251 if (!(*out
= malloc(len
+ 1))) {
257 sprintf(*out
, "%d/%u", (int) r
->p
, (unsigned int) r
->q
);
263 /* Render text to texture */
264 static int render_text(const char *text
, SDL_Texture
**out
)
271 SDL_DestroyTexture(*out
);
275 SDL_Surface
*surf
= 0;
277 if (!(surf
= TTF_RenderUTF8_Blended_Wrapped(normal_font
, text
,
279 fprintf(stderr
, L("TTF_RenderUTF8_Shaded(): %s\n"),
285 if (!(*out
= SDL_CreateTextureFromSurface(sdl_renderer
, surf
))) {
286 fprintf(stderr
, L("SDL_CreateTextureFromSurface(): %s\n"),
293 SDL_FreeSurface(surf
);
299 static int load_fonts(void)
304 TTF_CloseFont(normal_font
);
308 if (!(normal_font
= TTF_OpenFont(FONT_PATH
, base_font_size
))) {
310 fprintf(stderr
, L("TTF_OpenFont(): %s\n"), TTF_GetError());
314 for (size_t j
= 0; j
< UIA_LEN
; ++j
) {
315 if ((ret
= render_text(bottom_string
[j
], &bottom_text
[j
]))) {
324 TTF_CloseFont(normal_font
);
333 /* Convert `internal coordinates' to pixel coordinates */
334 static void internal_to_pixel_xy(int in_x
, int in_y
, int *out_x
, int *out_y
)
336 *out_x
= in_x
+ offset_x
;
337 *out_y
= in_y
+ offset_y
;
340 /* Convert pixel coordinates to `internal coordinates' */
341 static void pixel_to_internal_xy(int in_x
, int in_y
, int *out_x
, int *out_y
)
343 *out_x
= in_x
- offset_x
;
344 *out_y
= in_y
- offset_y
;
347 /* Set selected_vertex and selected_edge_{i,j} */
348 static int recalculate_selected_items(int mx
, int my
)
357 pixel_to_internal_xy(mx
, my
, &x
, &y
);
358 size_t last_vertex
= selected_vertex
;
359 size_t last_edge_i
= selected_edge_i
;
360 size_t last_edge_j
= selected_edge_j
;
362 selected_vertex
= (size_t) -1;
363 selected_edge_i
= (size_t) -1;
364 selected_edge_j
= (size_t) -1;
366 for (size_t j
= q
->v_num
; j
> 0; --j
) {
367 struct vertex
*v
= &(q
->v
[j
- 1]);
368 int r
= base_node_radius
+ v
->fatness
* node_radius_per_fatness
;
374 selected_vertex
= j
- 1;
379 for (size_t j
= 1; j
< q
->v_num
; ++j
) {
380 struct vertex
*v1
= &(q
->v
[j
]);
382 for (size_t i
= 0; i
< j
; ++i
) {
383 if (!q
->e
[i
* q
->v_len
+ j
].p
&&
384 !q
->e
[j
* q
->v_len
+ i
].p
) {
388 struct vertex
*v2
= &(q
->v
[i
]);
390 if ((x
- edge_margin
> v1
->x
&&
391 x
- edge_margin
> v2
->x
) ||
392 (x
+ edge_margin
< v1
->x
&&
393 x
+ edge_margin
< v2
->x
) ||
394 (y
- edge_margin
> v1
->y
&&
395 y
- edge_margin
> v2
->y
) ||
396 (y
+ edge_margin
< v1
->y
&&
397 y
+ edge_margin
< v2
->y
)) {
401 if (v1
->x
== v2
->x
) {
402 if (x
+ edge_margin
> v1
->x
&&
403 x
- edge_margin
< v1
->x
) {
408 } else if (v1
->y
== v2
->y
) {
409 if (y
+ edge_margin
> v1
->y
&&
410 y
- edge_margin
< v1
->y
) {
416 double m1
= ((double) (v2
->y
- v1
->y
)) /
417 ((double) (v2
->x
- v1
->x
));
418 double m2
= -1.0 / m1
;
419 double xint
= ((double) y
- (double) v1
->y
+
420 m1
* v1
->x
- m2
* x
) / (m1
- m2
);
421 double yint
= m1
* xint
- m1
* v1
->x
+
424 if ((x
- xint
) * (x
- xint
) + (y
- yint
) * (y
-
426 < edge_margin
* edge_margin
) {
437 if (selected_vertex
!= last_vertex
&&
438 selected_vertex
!= (size_t) -1) {
439 struct vertex
*v
= &(q
->v
[selected_vertex
]);
440 size_t len
= snprintf(0, 0, "Name: %s\nFatness: %d", v
->name
,
443 if (!(s
= malloc(len
+ 1))) {
449 sprintf(s
, "Name: %s\nFatness: %d", v
->name
,
452 if ((ret
= render_text(s
, &selected_info
))) {
455 } else if (last_edge_i
!= selected_edge_i
&&
456 last_edge_j
!= selected_edge_j
&&
457 selected_edge_i
!= (size_t) -1 &&
458 selected_edge_j
!= (size_t) -1) {
459 struct vertex
*i
= &(q
->v
[selected_edge_i
]);
460 struct vertex
*j
= &(q
->v
[selected_edge_j
]);
461 struct rational
*eij
= &(q
->e
[selected_edge_i
* q
->v_len
+
463 struct rational
*eji
= &(q
->e
[selected_edge_j
* q
->v_len
+
466 if ((ret
= pretty_fraction(eij
, &sij
))) {
470 if ((ret
= pretty_fraction(eji
, &sji
))) {
474 size_t len
= snprintf(0, 0,
475 "%s \u2192 %s: %s\n%s \u2192 %s: %s",
476 i
->name
, j
->name
, sij
,
477 j
->name
, i
->name
, sji
);
479 if (!(s
= malloc(len
+ 1))) {
485 sprintf(s
, "%s \u2192 %s: %s\n%s \u2192 %s: %s", i
->name
,
486 j
->name
, sij
, j
->name
, i
->name
, sji
);
488 if ((ret
= render_text(s
, &selected_info
))) {
501 /* Render vertex names as textures */
502 static int render_vertex_names(void)
505 for (size_t j
= 0; j
< vertex_names_len
; ++j
) {
506 SDL_DestroyTexture(vertex_names
[j
]);
511 if (!(vertex_names
= realloc(vertex_names
, q
->v_num
*
512 sizeof(*vertex_names
)))) {
515 perror(L("realloc()"));
516 vertex_names_len
= 0;
521 vertex_names_len
= q
->v_num
;
523 for (size_t j
= 0; j
< vertex_names_len
; ++j
) {
527 for (size_t j
= 0; j
< vertex_names_len
; ++j
) {
530 if ((ret
= render_text(q
->v
[j
].name
, &(vertex_names
[j
])))) {
538 /* Get information about the window */
539 static void react_to_window_resized(void)
541 int old_pix_width
= da_pix_width
;
542 int old_pix_height
= da_pix_height
;
544 SDL_GetWindowSize(sdl_win
, &da_pix_width
, &da_pix_height
);
546 if (old_pix_width
== da_pix_width
&&
547 old_pix_height
== da_pix_height
) {
551 offset_x
+= (da_pix_width
- old_pix_width
) / 2;
552 offset_y
+= (da_pix_height
- old_pix_height
) / 2;
556 static void eq_pop(struct ui_event
*out
)
558 if (eq_head
== eq_tail
) {
559 *out
= (struct ui_event
) { 0 };
564 memcpy(out
, eq_buf
+ eq_head
, sizeof *out
);
565 eq_buf
[eq_head
] = (struct ui_event
) { 0 };
566 eq_head
= (eq_head
+ 1) % eq_len
;
569 /* Push into queue */
570 static int eq_push(struct ui_event
*in
)
572 if (((eq_tail
+ 1) % eq_len
) == eq_head
) {
575 if ((eq_len
* sizeof *in
) >= ((size_t) -1) / 2) {
577 "eq_push: Impossibly large buffer\n"));
582 if (!(newmem
= realloc(eq_buf
, (eq_len
* 2) *
586 perror(L("realloc"));
591 eq_buf
= (struct ui_event
*) newmem
;
595 memcpy(eq_buf
+ eq_tail
, in
, sizeof *in
);
596 eq_tail
= (eq_tail
+ 1) % eq_len
;
602 int ui_init(struct quiver
*i_q
)
608 if (SDL_Init(SDL_INIT_VIDEO
) < 0) {
609 fprintf(stderr
, L("SDL_Init(): %s\n"), SDL_GetError());
614 sdl_win
= SDL_CreateWindow("Cluster Algebra Visualizer",
615 SDL_WINDOWPOS_UNDEFINED
,
616 SDL_WINDOWPOS_UNDEFINED
, 1000, 1000,
617 SDL_WINDOW_SHOWN
| SDL_WINDOW_RESIZABLE
);
620 fprintf(stderr
, L("SDL_CreateWindow(): %s\n"), SDL_GetError());
625 sdl_renderer
= SDL_CreateRenderer(sdl_win
, -1,
626 SDL_RENDERER_ACCELERATED
);
629 fprintf(stderr
, L("SDL_CreateRenderer(): %s\n"),
635 if (TTF_Init() < 0) {
636 fprintf(stderr
, L("TTF_Init(): %s\n"), TTF_GetError());
641 if ((ret
= load_fonts())) {
645 if ((ret
= render_vertex_names())) {
649 if ((ret
= SDL_SetRenderDrawColor(sdl_renderer
, color_bg
.r
, color_bg
.g
,
650 color_bg
.b
, color_bg
.a
))) {
651 fprintf(stderr
, L("SDL_SetRenderDrawColor(): %s\n"),
656 if ((ret
= SDL_RenderClear(sdl_renderer
))) {
657 fprintf(stderr
, L("SDL_RenderClear(): %s\n"), SDL_GetError());
661 SDL_RenderPresent(sdl_renderer
);
662 react_to_window_resized();
664 /* Set up queue for returning data */
665 if (!(eq_buf
= calloc(2, sizeof *eq_buf
))) {
675 /* Sines and cosines for drawing circles */
682 /* Deal with the fact that the quiver was changed */
683 int ui_respond_quiver_change(void)
685 return render_vertex_names();
689 int ui_teardown(void)
692 for (size_t j
= 0; j
< vertex_names_len
; ++j
) {
693 SDL_DestroyTexture(vertex_names
[j
]);
698 for (size_t j
= 0; j
< UIA_LEN
; ++j
) {
699 SDL_DestroyTexture(bottom_text
[j
]);
704 SDL_DestroyTexture(conf_deny
);
709 SDL_DestroyTexture(selected_info
);
714 TTF_CloseFont(normal_font
);
719 SDL_DestroyWindow(sdl_win
);
727 /* Record that a frame has been started */
728 int ui_start_frame(void)
738 frame_start_ticks
= SDL_GetTicks();
740 /* Draw the damn thing */
741 SDL_SetRenderDrawColor(sdl_renderer
, color_bg
.r
, color_bg
.g
, color_bg
.b
,
743 SDL_RenderClear(sdl_renderer
);
746 for (size_t j
= 0; j
< q
->v_num
; ++j
) {
747 for (size_t i
= 0; i
< j
; ++i
) {
748 /* First, determine if we're looking at a half-edge or a full-edge */
749 int d
= gcd(q
->v
[i
].fatness
, q
->v
[j
].fatness
);
750 struct rational
*eij
= &(q
->e
[i
* q
->v_len
+ j
]);
751 struct rational
*eji
= &(q
->e
[j
* q
->v_len
+ i
]);
763 internal_to_pixel_xy(q
->v
[i
].x
, q
->v
[i
].y
, &cx
, &cy
);
764 internal_to_pixel_xy(q
->v
[j
].x
, q
->v
[j
].y
, &cx2
, &cy2
);
766 if (selected_edge_i
== i
&&
767 selected_edge_j
== j
) {
768 if ((ret
= SDL_SetRenderDrawColor(sdl_renderer
,
775 "SDL_RenderDrawColor(): %s\n"),
780 for (int id
= -edge_margin
; id
< edge_margin
;
782 for (int jd
= -edge_margin
; jd
<
784 if ((ret
= SDL_RenderDrawLine(
790 "SDL_RenderDrawLine(): %s\n"),
798 /* This is the (eij)/dj = -(eji)/di condition */
799 if (eij
->p
* q
->v
[i
].fatness
* eji
->q
!= -eji
->p
*
800 q
->v
[j
].fatness
* eij
->q
) {
801 ret
= SDL_SetRenderDrawColor(sdl_renderer
,
806 } else if (abs(eij
->p
) * d
== q
->v
[j
].fatness
*
808 ret
= SDL_SetRenderDrawColor(sdl_renderer
,
813 } else if (2 * abs(eij
->p
) * d
== q
->v
[j
].fatness
*
815 ret
= SDL_SetRenderDrawColor(sdl_renderer
,
821 ret
= SDL_SetRenderDrawColor(sdl_renderer
,
830 "SDL_SetRenderDrawColor(): %s\n"),
835 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, cx
, cy
, cx2
,
837 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
843 theta
= (cy2
> cy
) ? M_PI
/ 2.0 : -M_PI
/ 2.0;
845 theta
= atan2f(cy2
- cy
, cx2
- cx
);
854 cx2
= cx
+ arrow_length
* cos(theta
+ arrow_angle
);
855 cy2
= cy
+ arrow_length
* sin(theta
+ arrow_angle
);
857 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, cx
, cy
, cx2
,
859 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
864 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, cx
, cy
, cx
+
865 arrow_length
* cos(theta
-
868 arrow_length
* sin(theta
-
871 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
878 /* Draw each vertex as a box */
879 for (size_t j
= 0; j
< q
->v_num
; ++j
) {
880 struct vertex
*v
= &(q
->v
[j
]);
884 internal_to_pixel_xy(v
->x
, v
->y
, &cx
, &cy
);
887 SDL_SetRenderDrawBlendMode(sdl_renderer
, SDL_BLENDMODE_NONE
);
888 SDL_SetRenderDrawColor(sdl_renderer
, color_v
.r
, color_v
.g
,
889 color_v
.b
, color_v
.a
);
890 rho
= base_node_radius
+ node_radius_per_fatness
* v
->fatness
;
895 SDL_RenderFillRect(sdl_renderer
, &r
);
898 SDL_SetRenderDrawBlendMode(sdl_renderer
, SDL_BLENDMODE_BLEND
);
900 if (j
== selected_vertex
) {
901 SDL_SetRenderDrawColor(sdl_renderer
,
905 color_outline_sel
.a
);
907 SDL_SetRenderDrawColor(sdl_renderer
, color_outline
.r
,
908 color_outline
.g
, color_outline
.b
,
914 r
.w
= 2 * rho
- outline_width
;
916 SDL_RenderFillRect(sdl_renderer
, &r
);
917 r
.x
= cx
+ rho
- outline_width
;
920 r
.h
= 2 * rho
- outline_width
;
921 SDL_RenderFillRect(sdl_renderer
, &r
);
922 r
.x
= cx
- rho
+ outline_width
;
923 r
.y
= cy
+ rho
- outline_width
;
924 r
.w
= 2 * rho
- outline_width
;
926 SDL_RenderFillRect(sdl_renderer
, &r
);
928 r
.y
= cy
- rho
+ outline_width
;
930 r
.h
= 2 * rho
- outline_width
;
931 SDL_RenderFillRect(sdl_renderer
, &r
);
934 if (j
>= vertex_names_len
) {
936 "render_vertex_names() was not called, somehow\n"));
941 if (SDL_QueryTexture(vertex_names
[j
], &dummy_format
,
942 &dummy_access
, &tex_w
, &tex_h
)) {
943 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"),
949 r
.x
= cx
- tex_w
/ 2;
950 r
.y
= cy
- tex_h
/ 2;
953 SDL_RenderCopy(sdl_renderer
, vertex_names
[j
], 0, &r
);
956 /* If adding a new vertex, draw preview */
957 if (ui_action
== UIA_NEW_VERTEX
&&
958 last_mouse_x
!= -1 &&
959 last_mouse_y
!= -1) {
961 SDL_SetRenderDrawBlendMode(sdl_renderer
, SDL_BLENDMODE_NONE
);
962 SDL_SetRenderDrawColor(sdl_renderer
, color_v_preview
.r
,
963 color_v_preview
.g
, color_v_preview
.b
,
965 rho
= base_node_radius
;
966 r
.x
= last_mouse_x
- rho
;
967 r
.y
= last_mouse_y
- rho
;
970 SDL_RenderFillRect(sdl_renderer
, &r
);
973 SDL_SetRenderDrawBlendMode(sdl_renderer
, SDL_BLENDMODE_BLEND
);
974 SDL_SetRenderDrawColor(sdl_renderer
, color_outline_preview
.r
,
975 color_outline_preview
.g
,
976 color_outline_preview
.b
,
977 color_outline_preview
.a
);
978 r
.x
= last_mouse_x
- rho
;
979 r
.y
= last_mouse_y
- rho
;
980 r
.w
= 2 * rho
- outline_width
;
982 SDL_RenderFillRect(sdl_renderer
, &r
);
983 r
.x
= last_mouse_x
+ rho
- outline_width
;
984 r
.y
= last_mouse_y
- rho
;
986 r
.h
= 2 * rho
- outline_width
;
987 SDL_RenderFillRect(sdl_renderer
, &r
);
988 r
.x
= last_mouse_x
- rho
+ outline_width
;
989 r
.y
= last_mouse_y
+ rho
- outline_width
;
990 r
.w
= 2 * rho
- outline_width
;
992 SDL_RenderFillRect(sdl_renderer
, &r
);
993 r
.x
= last_mouse_x
- rho
;
994 r
.y
= last_mouse_y
- rho
+ outline_width
;
996 r
.h
= 2 * rho
- outline_width
;
997 SDL_RenderFillRect(sdl_renderer
, &r
);
1000 /* If adding a new edge, draw possible */
1001 if ((ui_action
== UIA_NEW_EDGE_2
||
1002 ui_action
== UIA_NEW_H_EDGE_2
) &&
1004 /* last_clicked_vertex != (size_t) -1 && */
1005 last_mouse_x
!= -1 &&
1006 last_mouse_y
!= -1) {
1010 ret
= SDL_SetRenderDrawColor(sdl_renderer
, color_e_normal
.r
,
1011 color_e_normal
.g
, color_e_normal
.b
,
1013 internal_to_pixel_xy(q
->v
[last_clicked_vertex
].x
,
1014 q
->v
[last_clicked_vertex
].y
, &cx
, &cy
);
1016 if ((ret
= SDL_RenderDrawLine(sdl_renderer
, cx
, cy
,
1017 last_mouse_x
, last_mouse_y
))) {
1018 fprintf(stderr
, L("SDL_RenderDrawLine(): %s\n"),
1025 if (SDL_QueryTexture(bottom_text
[ui_action
], &dummy_format
,
1026 &dummy_access
, &tex_w
, &tex_h
)) {
1027 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"), SDL_GetError());
1032 r
.x
= text_border_padding
;
1033 r
.y
= da_pix_height
- tex_h
- text_border_padding
;
1036 SDL_RenderCopy(sdl_renderer
, bottom_text
[ui_action
], 0, &r
);
1038 /* Special case for if a question is being asked */
1039 if (ui_action
== UIA_CONF_DENY
) {
1040 if (SDL_QueryTexture(conf_deny
, &dummy_format
, &dummy_access
,
1042 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"),
1048 r
.x
= text_border_padding
;
1049 r
.y
= r
.y
- tex_h
- text_border_padding
;
1052 SDL_RenderCopy(sdl_renderer
, conf_deny
, 0, &r
);
1055 /* If something is selected */
1056 if (selected_info
&&
1057 (selected_vertex
!= (size_t) -1 ||
1058 (selected_edge_i
!= (size_t) -1 &&
1059 selected_edge_j
!= (size_t) -1))) {
1060 if (SDL_QueryTexture(selected_info
, &dummy_format
,
1061 &dummy_access
, &tex_w
, &tex_h
)) {
1062 fprintf(stderr
, L("SDL_QueryTexture(): %s\n"),
1068 r
.x
= text_border_padding
;
1069 r
.y
= text_border_padding
;
1072 SDL_RenderCopy(sdl_renderer
, selected_info
, 0, &r
);
1080 /* Draw a frame, possibly sleeping for framelimit */
1081 int ui_finish_frame(void)
1084 struct ui_event ui_e
= { 0 };
1085 SDL_Event sdl_e
= { 0 };
1088 SDL_RenderPresent(sdl_renderer
);
1090 /* Handle user input */
1091 while (SDL_PollEvent(&sdl_e
) != 0) {
1094 switch (sdl_e
.type
) {
1096 ui_e
= (struct ui_event
) { .type
= ET_FORCE_QUIT
};
1097 ret
= eq_push(&ui_e
);
1100 k
= sdl_e
.key
.keysym
.sym
;
1102 switch (ui_action
) {
1106 ui_e
= (struct ui_event
) { .type
=
1108 ret
= eq_push(&ui_e
);
1109 } else if (k
== SDLK_m
) {
1110 ui_action
= UIA_MUTATE
;
1111 } else if (k
== SDLK_v
) {
1112 ui_action
= UIA_NEW_VERTEX
;
1113 } else if (k
== SDLK_d
) {
1114 ui_action
= UIA_DELETE
;
1115 } else if (k
== SDLK_e
) {
1116 ui_action
= UIA_NEW_EDGE_1
;
1117 } else if (k
== SDLK_h
) {
1118 ui_action
= UIA_NEW_H_EDGE_1
;
1119 } else if (k
== SDLK_f
) {
1120 ui_action
= UIA_INC_FATNESS
;
1121 } else if (k
== SDLK_g
) {
1122 ui_action
= UIA_DEC_FATNESS
;
1130 ui_e
= (struct ui_event
) { .type
=
1132 ret
= eq_push(&ui_e
);
1133 ui_action
= UIA_NONE
;
1134 } else if (k
== SDLK_y
) {
1135 ui_e
= (struct ui_event
) { .type
=
1137 ret
= eq_push(&ui_e
);
1138 ui_action
= UIA_NONE
;
1146 ui_action
= UIA_NONE
;
1153 case SDL_WINDOWEVENT
:
1155 if (sdl_e
.window
.event
== SDL_WINDOWEVENT_RESIZED
||
1156 sdl_e
.window
.event
== SDL_WINDOWEVENT_MAXIMIZED
||
1157 sdl_e
.window
.event
== SDL_WINDOWEVENT_RESTORED
) {
1158 react_to_window_resized();
1159 } else if (sdl_e
.window
.event
==
1160 SDL_WINDOWEVENT_LEAVE
) {
1161 /* This tells the dragging code to not respond */
1167 case SDL_MOUSEMOTION
:
1169 if (sdl_e
.motion
.state
& SDL_BUTTON_LMASK
) {
1170 int x
= sdl_e
.motion
.x
;
1171 int y
= sdl_e
.motion
.y
;
1173 if (last_mouse_x
>= 0 &&
1174 last_mouse_y
>= 0) {
1175 if (selected_vertex
!= (size_t) -1) {
1176 q
->v
[selected_vertex
].x
+= (x
-
1178 q
->v
[selected_vertex
].y
+= (y
-
1181 offset_x
+= (x
- last_mouse_x
);
1182 offset_y
+= (y
- last_mouse_y
);
1186 recalculate_selected_items(sdl_e
.motion
.x
,
1190 last_mouse_x
= sdl_e
.motion
.x
;
1191 last_mouse_y
= sdl_e
.motion
.y
;
1193 case SDL_MOUSEBUTTONUP
:
1195 if ((sdl_e
.button
.state
& SDL_BUTTON_LMASK
) &&
1196 ui_action
!= UIA_NEW_VERTEX
) {
1201 recalculate_selected_items(sdl_e
.button
.x
,
1204 case SDL_MOUSEBUTTONDOWN
:
1206 if (!(sdl_e
.button
.state
& SDL_BUTTON_LMASK
)) {
1210 if (ui_action
!= UIA_NEW_VERTEX
) {
1215 switch (ui_action
) {
1218 if (selected_vertex
== (size_t) -1) {
1222 ui_e
= (struct ui_event
) {
1224 .type
= ET_MUTATE
, .idx_1
=
1227 ret
= eq_push(&ui_e
);
1228 ui_action
= UIA_NONE
;
1230 case UIA_NEW_VERTEX
:
1232 if (selected_vertex
!= (size_t) -1 ||
1233 selected_edge_i
!= (size_t) -1 ||
1234 selected_edge_j
!= (size_t) -1) {
1238 int cx
= sdl_e
.button
.x
- offset_x
;
1239 int cy
= sdl_e
.button
.y
- offset_y
;
1241 ui_e
= (struct ui_event
) {
1243 .type
= ET_NEW_VERTEX
, .int_1
= cx
,
1246 ret
= eq_push(&ui_e
);
1247 ui_action
= UIA_NONE
;
1249 case UIA_NEW_EDGE_1
:
1250 case UIA_NEW_H_EDGE_1
:
1252 if (selected_vertex
== (size_t) -1) {
1253 ui_action
= UIA_NONE
;
1257 last_clicked_vertex
= selected_vertex
;
1258 ui_action
= (ui_action
== UIA_NEW_EDGE_1
?
1259 UIA_NEW_EDGE_2
: UIA_NEW_H_EDGE_2
);
1261 case UIA_NEW_EDGE_2
:
1262 case UIA_NEW_H_EDGE_2
:
1264 if (selected_vertex
== (size_t) -1 ||
1265 selected_vertex
== last_clicked_vertex
) {
1266 ui_action
= UIA_NONE
;
1270 ui_e
= (struct ui_event
) {
1272 .type
= ET_NEW_EDGE
, .idx_1
=
1273 last_clicked_vertex
, .idx_2
=
1274 selected_vertex
, .a
= 1, .b
=
1275 (ui_action
== UIA_NEW_EDGE_2
?
1278 ret
= eq_push(&ui_e
);
1279 ui_action
= UIA_NONE
;
1283 if (selected_vertex
!= (size_t) -1) {
1284 ui_e
= (struct ui_event
) {
1286 .type
= ET_DELETE_VERTEX
,
1287 .idx_1
= selected_vertex
1289 ret
= eq_push(&ui_e
);
1290 } else if (selected_edge_i
!= (size_t) -1 &&
1291 selected_edge_j
!= (size_t) -1) {
1292 ui_e
= (struct ui_event
) {
1294 .type
= ET_DELETE_EDGE
, .idx_1
=
1299 ret
= eq_push(&ui_e
);
1302 ui_action
= UIA_NONE
;
1304 case UIA_INC_FATNESS
:
1305 case UIA_DEC_FATNESS
:
1307 if (selected_vertex
== (size_t) -1) {
1311 ui_e
= (struct ui_event
) {
1313 .type
= ET_CHANGE_FATNESS
, .idx_1
=
1314 selected_vertex
, .int_1
=
1319 ret
= eq_push(&ui_e
);
1320 ui_action
= UIA_NONE
;
1337 now
= SDL_GetTicks();
1339 if (frame_start_ticks
< now
) {
1340 Uint32 elapsed_time
= now
- frame_start_ticks
;
1342 if (elapsed_time
< TICKS_PER_FRAME
) {
1343 SDL_Delay(TICKS_PER_FRAME
- elapsed_time
);
1352 /* Return an event to the main loop */
1353 int ui_get_event(struct ui_event
*e
, uint_fast8_t *more
)
1356 *more
= eq_head
!= eq_tail
;
1361 /* Be told to display a confirm dialog */
1362 int ui_confirm_deny(const char *s
)
1366 if ((ret
= render_text(s
, &conf_deny
))) {
1370 ui_action
= UIA_CONF_DENY
;