1 /*
2  * Strings -- draws string art.
3  *
4  * Last modified: 2019-08-29
5  */
6 #include "tp_magic_api.h"
7 #include "SDL_image.h"
8 #include "SDL_mixer.h"
9 
10 unsigned int img_w, img_h;
11 static Uint8 string_r, string_g, string_b;
12 static int string_ox, string_oy;
13 static int string_vertex_x, string_vertex_y, string_vertex_done, string_vertex_distance;
14 static SDL_Surface *canvas_backup;
15 enum string_tools
16 {
17   STRING_TOOL_FULL_BY_OFFSET,
18   STRING_TOOL_TRIANGLE,
19   STRING_TOOL_ANGLE,
20   STRING_NUMTOOLS
21 };
22 
23 Mix_Chunk *string_snd[STRING_NUMTOOLS];
24 
25 // Custom function declarations
26 
27 void string_callback(void *ptr, int which_tool, SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y);
28 void string_draw_triangle(magic_api * api, int which,
29                           SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y,
30                           SDL_Rect * update_rect);
31 void string_draw_angle(magic_api * api, int which,
32                        SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y,
33                        SDL_Rect * update_rect);
34 void string_draw_triangle_preview(magic_api * api, int which,
35                                   SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y,
36                                   SDL_Rect * update_rect);
37 void string_draw_angle_preview(magic_api * api, int which,
38                                SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y,
39                                SDL_Rect * update_rect);
40 void scale_xcoord(int *xcoord);
41 void scale_ycoord(int *ycoord);
42 void scale_coords(int *ox, int *oy, int *x, int *y);
43 void string_draw_wrapper(magic_api * api, int which,
44                          SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y,
45                          SDL_Rect * update_rect);
46 void string_set_vertex(int x, int y);
47 void compute_middle(int start_point, int end_point, int vertex, int *middle);
48 
49 
50 // Prototypes for required functions
51 
52 void string_drag(magic_api * api, int which, SDL_Surface * canvas,
53                  SDL_Surface * snapshot, int ox, int oy, int x, int y, SDL_Rect * update_rect);
54 
55 
56 Uint32 string_api_version(void);
57 int string_modes(magic_api * api, int which);
58 void string_set_color(magic_api * api, Uint8 r, Uint8 g, Uint8 b);
59 int string_get_tool_count(magic_api * api);
60 SDL_Surface *string_get_icon(magic_api * api, int which);
61 char *string_get_name(magic_api * api, int which);
62 char *string_get_description(magic_api * api, int which, int mode);
63 int string_requires_colors(magic_api * api, int which);
64 void string_release(magic_api * api, int which,
65                     SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, SDL_Rect * update_rect);
66 int string_init(magic_api * api);
67 void string_shutdown(magic_api * api);
68 void string_switchin(magic_api * api, int which, int mode, SDL_Surface * canvas, SDL_Surface * snapshot);
69 void string_switchout(magic_api * api, int which, int mode, SDL_Surface * canvas, SDL_Surface * snapshot);
70 void string_click(magic_api * api, int which, int mode,
71                   SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, SDL_Rect * update_rect);
72 
73 // Required functions
74 
string_api_version(void)75 Uint32 string_api_version(void)
76 {
77   return (TP_MAGIC_API_VERSION);
78 }
79 
string_modes(magic_api * api,int which)80 int string_modes( __attribute__ ((unused)) magic_api * api, int which)
81 {
82   if (which == STRING_TOOL_FULL_BY_OFFSET)
83     return (MODE_PAINT);
84   else
85     return (MODE_PAINT_WITH_PREVIEW);
86 }
87 
string_set_color(magic_api * api,Uint8 r,Uint8 g,Uint8 b)88 void string_set_color( __attribute__ ((unused)) magic_api * api, Uint8 r, Uint8 g, Uint8 b)
89 {
90   string_r = r;
91   string_g = g;
92   string_b = b;
93 }
94 
95 
96 
string_get_tool_count(magic_api * api)97 int string_get_tool_count( __attribute__ ((unused)) magic_api * api)
98 {
99   return STRING_NUMTOOLS;
100 }
101 
string_get_icon(magic_api * api,int which)102 SDL_Surface *string_get_icon(magic_api * api, int which)
103 {
104   char fname[1024];
105 
106   switch (which)
107     {
108     case STRING_TOOL_FULL_BY_OFFSET:
109       snprintf(fname, sizeof(fname), "%s/images/magic/string_art_full_by_offset.png", api->data_directory);
110       break;
111     case STRING_TOOL_TRIANGLE:
112       snprintf(fname, sizeof(fname), "%s/images/magic/string_art_triangles.png", api->data_directory);
113       break;
114     case STRING_TOOL_ANGLE:
115       snprintf(fname, sizeof(fname), "%s/images/magic/string_art_angles.png", api->data_directory);
116       break;
117     }
118 
119   return (IMG_Load(fname));
120 }
121 
122 
string_get_name(magic_api * api,int which)123 char *string_get_name( __attribute__ ((unused)) magic_api * api, __attribute__ ((unused))
124                       int which)
125 {
126   switch (which)
127     {
128     case STRING_TOOL_FULL_BY_OFFSET:
129       return strdup(gettext_noop("String edges"));
130       break;
131     case STRING_TOOL_TRIANGLE:
132       return strdup(gettext_noop("String corner"));
133       break;
134     default:
135       return strdup(gettext_noop("String 'V'"));
136     }
137 }
138 
string_get_description(magic_api * api,int which,int mode)139 char *string_get_description( __attribute__ ((unused)) magic_api * api, int which, __attribute__ ((unused))
140                              int mode)
141 {
142   switch (which)
143     {
144     case STRING_TOOL_FULL_BY_OFFSET:
145       return
146         strdup(gettext_noop
147                ("Click and drag to draw string art. Drag top-bottom to draw less or more lines, left or right to make a bigger hole."));
148       break;
149     case STRING_TOOL_TRIANGLE:
150       return strdup(gettext_noop("Click and drag to draw arrows made of string art."));
151       break;
152     default:
153       return strdup(gettext_noop("Draw string art arrows with free angles."));
154     }
155 }
156 
string_requires_colors(magic_api * api,int which)157 int string_requires_colors( __attribute__ ((unused)) magic_api * api, __attribute__ ((unused))
158                            int which)
159 {
160   return 1;
161 }
162 
string_release(magic_api * api,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int x,int y,SDL_Rect * update_rect)163 void string_release(magic_api * api, int which,
164                     SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, SDL_Rect * update_rect)
165 {
166   int dx, dy;
167 
168   if (which == STRING_TOOL_TRIANGLE)
169     string_draw_triangle((void *)api, which, canvas, snapshot, string_ox, string_oy, x, y, update_rect);
170   if (which == STRING_TOOL_ANGLE)
171     {
172       if (!string_vertex_done)  // maybe we face small children, draw square angles aligned to the drag
173         {
174           dx = string_ox - x;
175           dy = string_oy - y;
176           y = y + dx;
177           x = x - dy;
178         }
179       string_draw_angle((void *)api, which, canvas, snapshot, string_ox, string_oy, x, y, update_rect);
180     }
181 }
182 
string_init(magic_api * api)183 int string_init( __attribute__ ((unused)) magic_api * api)
184 {
185   char fname[1024];
186 
187   snprintf(fname, sizeof(fname), "%s/sounds/magic/string.ogg", api->data_directory);
188   string_snd[STRING_TOOL_FULL_BY_OFFSET] = Mix_LoadWAV(fname);
189 
190   snprintf(fname, sizeof(fname), "%s/sounds/magic/string2.ogg", api->data_directory);
191   string_snd[STRING_TOOL_TRIANGLE] = Mix_LoadWAV(fname);
192 
193   snprintf(fname, sizeof(fname), "%s/sounds/magic/string3.ogg", api->data_directory);
194   string_snd[STRING_TOOL_ANGLE] = Mix_LoadWAV(fname);
195 
196   return (1);
197 }
198 
string_shutdown(magic_api * api)199 void string_shutdown( __attribute__ ((unused)) magic_api * api)
200 {
201   int i = 0;
202 
203   if (canvas_backup)
204     SDL_FreeSurface(canvas_backup);
205 
206   while (i < STRING_NUMTOOLS)
207     {
208       if (string_snd[i] != NULL)
209         Mix_FreeChunk(string_snd[i]);
210       i++;
211     }
212 }
213 
string_switchin(magic_api * api,int which,int mode,SDL_Surface * canvas,SDL_Surface * snapshot)214 void string_switchin( __attribute__ ((unused)) magic_api * api, __attribute__ ((unused))
215                      int which, __attribute__ ((unused))
216                      int mode, SDL_Surface * canvas, __attribute__ ((unused)) SDL_Surface * snapshot)
217 {
218   canvas_backup = SDL_CreateRGBSurface(SDL_ANYFORMAT, canvas->w, canvas->h, canvas->format->BitsPerPixel,
219                                        canvas->format->Rmask, canvas->format->Gmask, canvas->format->Bmask,
220                                        canvas->format->Amask);
221 }
222 
string_switchout(magic_api * api,int which,int mode,SDL_Surface * canvas,SDL_Surface * snapshot)223 void string_switchout( __attribute__ ((unused)) magic_api * api, __attribute__ ((unused))
224                       int which, __attribute__ ((unused))
225                       int mode, __attribute__ ((unused)) SDL_Surface * canvas,
226                       __attribute__ ((unused)) SDL_Surface * snapshot)
227 {
228   SDL_FreeSurface(canvas_backup);
229   canvas_backup = NULL;
230 }
231 
232 // Interactivity functions
233 
234 
string_callback(void * ptr,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int x,int y)235 void string_callback(void *ptr, __attribute__ ((unused))
236                      int which, SDL_Surface * canvas, __attribute__ ((unused)) SDL_Surface * snapshot, int x, int y)
237 {
238   magic_api *api = (magic_api *) ptr;
239 
240   api->putpixel(canvas, x, y, SDL_MapRGBA(canvas->format, string_r, string_g, string_b, 255));
241 }
242 
243 
string_click(magic_api * api,int which,int mode,SDL_Surface * canvas,SDL_Surface * snapshot,int x,int y,SDL_Rect * update_rect)244 void string_click(magic_api * api, int which, __attribute__ ((unused))
245                   int mode, SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, SDL_Rect * update_rect)
246 {
247   SDL_BlitSurface(canvas, NULL, canvas_backup, NULL);
248 
249   string_ox = x;
250   string_oy = y;
251   string_vertex_distance = 0;
252   string_vertex_done = 0;
253   string_drag(api, which, canvas, snapshot, x, y, x, y, update_rect);
254 }
255 
string_draw_full_by_offset(void * ptr,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int x,int y,SDL_Rect * update_rect)256 static void string_draw_full_by_offset(void *ptr, __attribute__ ((unused))
257                                        int which, SDL_Surface * canvas, __attribute__ ((unused)) SDL_Surface * snapshot,
258                                        int x, int y, SDL_Rect * update_rect)
259 {
260   magic_api *api = (magic_api *) ptr;
261   int u;
262   int i;
263   int o;                        //offset
264 
265   // int n=y/5;
266   int **a;
267   float step_w, step_h, aux;
268   int side = (int)(y / 3);
269 
270   SDL_BlitSurface(snapshot, 0, canvas, 0);
271 
272   if (side < 3)
273     side = 3;
274 
275   o = (int)(side * 4 * x / canvas->w);
276   step_w = canvas->w / (float)side;
277   step_h = canvas->h / (float)side;
278 
279   a = malloc(sizeof(int *) * side * 4 * 2);
280 
281   for (i = 0; i < side * 4; i++)
282     {
283       a[i] = malloc(sizeof(int *) * 2);
284       if (i < side)
285         {
286           a[i][0] = 0;
287           aux = step_h * (float)i;
288           a[i][1] = (int)aux;
289         }
290       else if (i < (side * 2))
291         {
292           a[i][0] = (int)((float)(i % side) * step_w);
293           a[i][1] = canvas->h;
294         }
295       else if (i < (int)(side * 3))
296         {
297           a[i][0] = canvas->w;
298           a[i][1] = (int)(canvas->h - (float)((i % side) * step_h));
299         }
300       else if (i < (int)(side * 4))
301         {
302           a[i][0] = (int)(canvas->w - ((float)((i % side) * step_w)));
303           a[i][1] = 0;
304         }
305     }
306 
307 
308   for (i = 0; i < side * 4; i++)
309     {
310       u = (i + o) % (side * 4);
311       api->line((void *)api, which, canvas, snapshot, a[i][0], a[i][1], a[u][0], a[u][1], 1, string_callback);
312     }
313 
314   for (i = 0; i < side * 4; i++)
315     {
316       free(a[i]);
317     }
318   free(a);
319 
320   update_rect->x = 0;
321   update_rect->y = 0;
322   update_rect->w = canvas->w;
323   update_rect->h = canvas->h;
324 }
325 
scale_xcoord(int * xcoord)326 void scale_xcoord(int *xcoord)
327 {
328   if (*xcoord < string_ox)
329     *xcoord = string_ox - (string_ox - *xcoord) * 4;
330   else
331     *xcoord = string_ox + (*xcoord - string_ox) * 4;
332 }
333 
scale_ycoord(int * ycoord)334 void scale_ycoord(int *ycoord)
335 {
336   if (*ycoord < string_oy)
337     *ycoord = string_oy - (string_oy - *ycoord) * 4;
338   else
339     *ycoord = string_oy + (*ycoord - string_oy) * 4;
340 }
341 
scale_coords(int * ox,int * oy,int * x,int * y)342 void scale_coords(int *ox, int *oy, int *x, int *y)
343 {
344   scale_xcoord(ox);
345   scale_xcoord(x);
346   scale_ycoord(oy);
347   scale_ycoord(y);
348 }
349 
compute_middle(int start_point,int end_point,int vertex,int * middle)350 void compute_middle(int start_point, int end_point, int vertex, int *middle)
351 {
352   *middle = min(start_point, end_point) + (max(start_point, end_point) - min(start_point, end_point)) / 2;
353   *middle = min(*middle, vertex) + (max(*middle, vertex) - min(*middle, vertex)) / 2;
354 }
355 
string_draw_triangle_preview(magic_api * api,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int ox,int oy,int x,int y,SDL_Rect * update_rect)356 void string_draw_triangle_preview(magic_api * api, int which,
357                                   SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y,
358                                   SDL_Rect * update_rect)
359 {
360   int middle_x, middle_y;
361 
362   scale_coords(&ox, &oy, &x, &y);
363 
364   update_rect->x = 0;
365   update_rect->y = 0;
366   update_rect->w = canvas->w;
367   update_rect->h = canvas->h;
368   SDL_BlitSurface(canvas_backup, update_rect, canvas, update_rect);
369 
370   compute_middle(x, string_ox, string_ox, &middle_x);
371   compute_middle(y, string_oy, string_oy, &middle_y);
372 
373   api->line((void *)api, which, canvas, snapshot, string_ox, string_oy, string_ox, y, 1, string_callback);
374   api->line((void *)api, which, canvas, snapshot, string_ox, string_oy, x, string_oy, 1, string_callback);
375   api->line((void *)api, which, canvas, snapshot, middle_x, middle_y, x, string_oy, 1, string_callback);
376   api->line((void *)api, which, canvas, snapshot, string_ox, y, middle_x, middle_y, 1, string_callback);
377 }
378 
string_draw_angle_preview(magic_api * api,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int ox,int oy,int x,int y,SDL_Rect * update_rect)379 void string_draw_angle_preview(magic_api * api, int which,
380                                SDL_Surface * canvas, SDL_Surface * snapshot, __attribute__ ((unused))
381                                int ox, __attribute__ ((unused))
382                                int oy, int x, int y, SDL_Rect * update_rect)
383 {
384   int middle_x, middle_y;
385   int dx, dy;
386 
387   update_rect->x = 0;
388   update_rect->y = 0;
389   update_rect->w = canvas->w;
390   update_rect->h = canvas->h;
391   SDL_BlitSurface(canvas_backup, update_rect, canvas, update_rect);
392 
393   api->line((void *)api, which, canvas, snapshot, string_ox, string_oy, string_vertex_x, string_vertex_y, 1,
394             string_callback);
395   if (!string_vertex_done)
396     {
397       //    if(!string_vertex_done) // maybe we face small children, draw square angles aligned to the drag
398       //{
399       dx = string_ox - x;
400       dy = string_oy - y;
401       y = y + dx;
402       x = x - dy;
403     }
404 
405   compute_middle(string_ox, x, string_vertex_x, &middle_x);
406   compute_middle(string_oy, y, string_vertex_y, &middle_y);
407 
408   api->line((void *)api, which, canvas, snapshot, string_vertex_x, string_vertex_y, x, y, 1, string_callback);
409   api->line((void *)api, which, canvas, snapshot, string_ox, string_oy, middle_x, middle_y, 1, string_callback);
410   api->line((void *)api, which, canvas, snapshot, x, y, middle_x, middle_y, 1, string_callback);
411 
412 }
413 
string_draw_angle(magic_api * api,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int ox,int oy,int x,int y,SDL_Rect * update_rect)414 void string_draw_angle(magic_api * api, __attribute__ ((unused))
415                        int which,
416                        SDL_Surface * canvas, __attribute__ ((unused)) SDL_Surface * snapshot, __attribute__ ((unused))
417                        int ox, __attribute__ ((unused))
418                        int oy, int x, int y, SDL_Rect * update_rect)
419 {
420   float first_arm_step_x, first_arm_step_y, second_arm_step_x, second_arm_step_y;
421   int i;
422   int max_wh, steps;
423   int max_separation = 10;
424 
425   update_rect->x = min(min(string_ox, string_vertex_x), x);
426   update_rect->y = min(min(string_oy, string_vertex_y), y);
427   update_rect->w = max(max(string_ox, string_vertex_x), x) - update_rect->x;
428   update_rect->h = max(max(string_oy, string_vertex_y), y) - update_rect->y;
429   SDL_BlitSurface(canvas_backup, update_rect, canvas, update_rect);
430 
431   max_wh =
432     max(max(max(string_ox, string_vertex_x), x) - min(min(string_vertex_x, x), string_ox),
433         max(max(string_oy, string_vertex_y), y) - min(min(string_vertex_y, y), string_oy));
434 
435   steps = max_wh / max_separation;
436   first_arm_step_x = (float)(string_ox - string_vertex_x) / (float)steps;
437   first_arm_step_y = (float)(string_oy - string_vertex_y) / (float)steps;
438   second_arm_step_x = (float)(string_vertex_x - x) / (float)steps;
439   second_arm_step_y = (float)(string_vertex_y - y) / (float)steps;
440 
441   for (i = 0; i <= steps; i++)
442     {
443       api->line((void *)api, 0, canvas, snapshot, string_ox - first_arm_step_x * i, string_oy - first_arm_step_y * i,
444                 string_vertex_x - second_arm_step_x * i, string_vertex_y - second_arm_step_y * i, 1, string_callback);
445     }
446 }
447 
string_draw_triangle(magic_api * api,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int ox,int oy,int x,int y,SDL_Rect * update_rect)448 void string_draw_triangle(magic_api * api, __attribute__ ((unused))
449                           int which,
450                           SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y,
451                           SDL_Rect * update_rect)
452 {
453   SDL_BlitSurface(canvas_backup, 0, canvas, 0);
454   scale_coords(&ox, &oy, &x, &y);
455 
456   string_vertex_x = string_ox;
457   string_vertex_y = string_oy;
458   string_ox = string_vertex_x;
459   string_oy = y;
460   y = string_vertex_y;
461 
462   string_draw_angle((void *)api, which, canvas, snapshot, string_ox, string_oy, x, y, update_rect);
463 }
464 
string_draw_wrapper(magic_api * api,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int ox,int oy,int x,int y,SDL_Rect * update_rect)465 void string_draw_wrapper(magic_api * api, int which,
466                          SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y,
467                          SDL_Rect * update_rect)
468 {
469   if (which == STRING_TOOL_FULL_BY_OFFSET)
470     string_draw_full_by_offset((void *)api, which, canvas, snapshot, x, y, update_rect);
471   else if (which == STRING_TOOL_TRIANGLE)
472     string_draw_triangle_preview((void *)api, which, canvas, snapshot, ox, oy, x, y, update_rect);
473   else if (which == STRING_TOOL_ANGLE)
474     string_draw_angle_preview((void *)api, which, canvas, snapshot, ox, oy, x, y, update_rect);
475 }
476 
string_set_vertex(int x,int y)477 void string_set_vertex(int x, int y)
478 {
479   int dx, dy;
480 
481   if (string_vertex_done)
482     return;
483   dx = max(string_ox, x) - min(string_ox, x);
484   dy = max(string_oy, y) - min(string_oy, y);
485   if (dx + dy > string_vertex_distance)
486     {
487       string_vertex_distance = dx + dy;
488       string_vertex_x = x;
489       string_vertex_y = y;
490     }
491   if (dx + dy + 30 < string_vertex_distance)
492     string_vertex_done = 1;
493 }
494 
string_drag(magic_api * api,int which,SDL_Surface * canvas,SDL_Surface * snapshot,int ox,int oy,int x,int y,SDL_Rect * update_rect)495 void string_drag(magic_api * api, int which,
496                  SDL_Surface * canvas, SDL_Surface * snapshot, int ox, int oy, int x, int y, SDL_Rect * update_rect)
497 {
498   if ((x < canvas->w) && (y < canvas->h) && (ox < canvas->w) && (oy < canvas->h) && ((signed)x > 0) && ((signed)y > 0)
499       && ((signed)ox > 0) && ((signed)oy > 0))
500     {
501       string_set_vertex(x, y);
502       string_draw_wrapper((void *)api, which, canvas, snapshot, ox, oy, x, y, update_rect);
503       api->playsound(string_snd[which], (x * 255) / canvas->w, 255);
504 
505     }
506 }
507