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