1 /*
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This is a plug-in for GIMP.
5  *
6  * Generates images containing vector type drawings.
7  *
8  * Copyright (C) 1997 Andy Thomas  alt@picnic.demon.co.uk
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  *
23  */
24 
25 #include "config.h"
26 
27 #include <libgimp/gimp.h>
28 #include <libgimp/gimpui.h>
29 
30 #include "gfig.h"
31 #include "gfig-dobject.h"
32 #include "gfig-line.h"
33 #include "gfig-dialog.h"
34 #include "gfig-poly.h"
35 
36 #include "libgimp/stdplugins-intl.h"
37 
38 static gint poly_num_sides = 3; /* Default to three sided object */
39 
40 static void        d_draw_poly   (GfigObject *obj,
41                                   cairo_t    *cr);
42 static GfigObject *d_copy_poly   (GfigObject *obj);
43 
44 static void        d_update_poly (GdkPoint   *pnt);
45 
46 void
tool_options_poly(GtkWidget * notebook)47 tool_options_poly (GtkWidget *notebook)
48 {
49   GtkWidget *sides;
50 
51   sides = num_sides_widget (_("Regular Polygon Number of Sides"),
52                             &poly_num_sides, NULL, 3, 200);
53   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sides, NULL);
54 }
55 
56 static void
d_draw_poly(GfigObject * obj,cairo_t * cr)57 d_draw_poly (GfigObject *obj,
58              cairo_t    *cr)
59 {
60   DobjPoints *center_pnt;
61   DobjPoints *radius_pnt;
62   gint16      shift_x;
63   gint16      shift_y;
64   gdouble     ang_grid;
65   gdouble     ang_loop;
66   gdouble     radius;
67   gdouble     offset_angle;
68   gint        loop;
69   GdkPoint    start_pnt = { 0, 0 };
70   GdkPoint    first_pnt = { 0, 0 };
71   gboolean    do_line = FALSE;
72 
73   center_pnt = obj->points;
74 
75   if (!center_pnt)
76     return; /* End-of-line */
77 
78   /* First point is the center */
79   /* Just draw a control point around it */
80 
81   draw_sqr (&center_pnt->pnt, obj == gfig_context->selected_obj, cr);
82 
83   /* Next point defines the radius */
84   radius_pnt = center_pnt->next; /* this defines the vertices */
85 
86   if (!radius_pnt)
87     {
88 #ifdef DEBUG
89       g_warning ("Internal error in polygon - no vertice point \n");
90 #endif /* DEBUG */
91       return;
92     }
93 
94   /* Other control point */
95   if (obj == obj_creating)
96     draw_circle (&radius_pnt->pnt, TRUE, cr);
97   else
98     draw_sqr (&radius_pnt->pnt, obj == gfig_context->selected_obj, cr);
99 
100   /* Have center and radius - draw polygon */
101 
102   shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
103   shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
104 
105   radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
106 
107   /* Lines */
108   ang_grid = 2 * G_PI / (gdouble) obj->type_data;
109   offset_angle = atan2 (shift_y, shift_x);
110 
111   for (loop = 0 ; loop < obj->type_data ; loop++)
112     {
113       gdouble  lx, ly;
114       GdkPoint calc_pnt;
115 
116       ang_loop = (gdouble)loop * ang_grid + offset_angle;
117 
118       lx = radius * cos (ang_loop);
119       ly = radius * sin (ang_loop);
120 
121       calc_pnt.x = RINT (lx + center_pnt->pnt.x);
122       calc_pnt.y = RINT (ly + center_pnt->pnt.y);
123 
124       if (do_line)
125         {
126 
127           /* Miss out points that come to the same location */
128           if (calc_pnt.x == start_pnt.x && calc_pnt.y == start_pnt.y)
129             continue;
130 
131           gfig_draw_line (calc_pnt.x, calc_pnt.y, start_pnt.x, start_pnt.y, cr);
132         }
133       else
134         {
135           do_line = TRUE;
136           first_pnt = calc_pnt;
137         }
138       start_pnt = calc_pnt;
139     }
140 
141   gfig_draw_line (first_pnt.x, first_pnt.y, start_pnt.x, start_pnt.y, cr);
142 }
143 
144 void
d_paint_poly(GfigObject * obj)145 d_paint_poly (GfigObject *obj)
146 {
147   /* first point center */
148   /* Next point is radius */
149   gdouble    *line_pnts;
150   gint        seg_count;
151   gint        i = 0;
152   DobjPoints *center_pnt;
153   DobjPoints *radius_pnt;
154   gint16      shift_x;
155   gint16      shift_y;
156   gdouble     ang_grid;
157   gdouble     ang_loop;
158   gdouble     radius;
159   gdouble     offset_angle;
160   gint        loop;
161   GdkPoint    first_pnt = { 0, 0 };
162   GdkPoint    last_pnt  = { 0, 0 };
163   gboolean    first = TRUE;
164   gdouble    *min_max;
165 
166   g_assert (obj != NULL);
167 
168   /* count - add one to close polygon */
169   seg_count = obj->type_data + 1;
170 
171   center_pnt = obj->points;
172 
173   if (!center_pnt || !seg_count || !center_pnt->next)
174     return; /* no-line */
175 
176   line_pnts = g_new0 (gdouble, 2 * seg_count + 1);
177   min_max   = g_new (gdouble, 4);
178 
179   /* Go around all the points drawing a line from one to the next */
180 
181   radius_pnt = center_pnt->next; /* this defines the vetices */
182 
183   /* Have center and radius - get lines */
184   shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
185   shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
186 
187   radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
188 
189   /* Lines */
190   ang_grid = 2.0 * G_PI/(gdouble) obj->type_data;
191   offset_angle = atan2 (shift_y, shift_x);
192 
193   for (loop = 0 ; loop < obj->type_data ; loop++)
194     {
195       gdouble  lx, ly;
196       GdkPoint calc_pnt;
197 
198       ang_loop = (gdouble)loop * ang_grid + offset_angle;
199 
200       lx = radius * cos (ang_loop);
201       ly = radius * sin (ang_loop);
202 
203       calc_pnt.x = RINT (lx + center_pnt->pnt.x);
204       calc_pnt.y = RINT (ly + center_pnt->pnt.y);
205 
206       /* Miss out duped pnts */
207       if (!first)
208         {
209           if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
210             {
211               continue;
212             }
213         }
214 
215       line_pnts[i++] = calc_pnt.x;
216       line_pnts[i++] = calc_pnt.y;
217       last_pnt = calc_pnt;
218 
219       if (first)
220         {
221           first_pnt = calc_pnt;
222           first = FALSE;
223           min_max[0] = min_max[2] = calc_pnt.x;
224           min_max[1] = min_max[3] = calc_pnt.y;
225         }
226       else
227         {
228           min_max[0] = MIN (min_max[0], calc_pnt.x);
229           min_max[1] = MIN (min_max[1], calc_pnt.y);
230           min_max[2] = MAX (min_max[2], calc_pnt.x);
231           min_max[3] = MAX (min_max[3], calc_pnt.y);
232         }
233     }
234 
235   line_pnts[i++] = first_pnt.x;
236   line_pnts[i++] = first_pnt.y;
237 
238   /* Scale before drawing */
239   if (selvals.scaletoimage)
240     {/* FIXME scale xmax and al. */
241       scale_to_original_xy (&line_pnts[0], i/2);
242       scale_to_original_xy (min_max, 2);
243     }
244   else
245     {
246       scale_to_xy (&line_pnts[0], i/2);
247       scale_to_xy (min_max, 2);
248     }
249 
250 
251   if (gfig_context_get_current_style ()->fill_type != FILL_NONE)
252     {
253       gimp_context_push ();
254       gimp_context_set_antialias (selopt.antia);
255       gimp_context_set_feather (selopt.feather);
256       gimp_context_set_feather_radius (selopt.feather_radius, selopt.feather_radius);
257       gimp_image_select_polygon (gfig_context->image_id,
258                                  selopt.type,
259                                  i, line_pnts);
260       gimp_context_pop ();
261 
262       paint_layer_fill (min_max[0], min_max[1], min_max[2], min_max[3]);
263       gimp_selection_none (gfig_context->image_id);
264     }
265 
266   if (obj->style.paint_type == PAINT_BRUSH_TYPE)
267     gfig_paint (selvals.brshtype, gfig_context->drawable_id, i, line_pnts);
268 
269   g_free (line_pnts);
270   g_free (min_max);
271 }
272 
273 void
d_poly2lines(GfigObject * obj)274 d_poly2lines (GfigObject *obj)
275 {
276   /* first point center */
277   /* Next point is radius */
278   DobjPoints *center_pnt;
279   DobjPoints *radius_pnt;
280   gint16      shift_x;
281   gint16      shift_y;
282   gdouble     ang_grid;
283   gdouble     ang_loop;
284   gdouble     radius;
285   gdouble     offset_angle;
286   gint        loop;
287   GdkPoint    first_pnt = { 0, 0 };
288   GdkPoint    last_pnt  = { 0, 0 };
289   gboolean    first = TRUE;
290 
291   g_assert (obj != NULL);
292 
293   center_pnt = obj->points;
294 
295   if (!center_pnt)
296     return; /* no-line */
297 
298   /* NULL out these points free later */
299   obj->points = NULL;
300 
301   /* Go around all the points creating line points */
302 
303   radius_pnt = center_pnt->next; /* this defines the vertices */
304 
305   /* Have center and radius - get lines */
306   shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
307   shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
308 
309   radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
310 
311   /* Lines */
312   ang_grid = 2.0 * G_PI / (gdouble) obj->type_data;
313   offset_angle = atan2 (shift_y, shift_x);
314 
315   for (loop = 0 ; loop < obj->type_data ; loop++)
316     {
317       gdouble lx, ly;
318       GdkPoint calc_pnt;
319 
320       ang_loop = (gdouble)loop * ang_grid + offset_angle;
321 
322       lx = radius * cos (ang_loop);
323       ly = radius * sin (ang_loop);
324 
325       calc_pnt.x = RINT (lx + center_pnt->pnt.x);
326       calc_pnt.y = RINT (ly + center_pnt->pnt.y);
327 
328       if (!first)
329         {
330           if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
331             {
332               continue;
333             }
334         }
335 
336       d_pnt_add_line (obj, calc_pnt.x, calc_pnt.y, 0);
337 
338       last_pnt = calc_pnt;
339 
340       if (first)
341         {
342           first_pnt = calc_pnt;
343           first = FALSE;
344         }
345     }
346 
347   d_pnt_add_line (obj, first_pnt.x, first_pnt.y, 0);
348   /* Free old pnts */
349   d_delete_dobjpoints (center_pnt);
350 
351   /* hey we're a line now */
352   obj->type = LINE;
353   obj->class = &dobj_class[LINE];
354 }
355 
356 void
d_star2lines(GfigObject * obj)357 d_star2lines (GfigObject *obj)
358 {
359   /* first point center */
360   /* Next point is radius */
361   DobjPoints *center_pnt;
362   DobjPoints *outer_radius_pnt;
363   DobjPoints *inner_radius_pnt;
364   gint16      shift_x;
365   gint16      shift_y;
366   gdouble     ang_grid;
367   gdouble     ang_loop;
368   gdouble     outer_radius;
369   gdouble     inner_radius;
370   gdouble     offset_angle;
371   gint        loop;
372   GdkPoint    first_pnt = { 0, 0 };
373   GdkPoint    last_pnt  = { 0, 0 };
374   gboolean    first = TRUE;
375 
376   g_assert (obj != NULL);
377 
378   center_pnt = obj->points;
379 
380   if (!center_pnt)
381     return; /* no-line */
382 
383   /* NULL out these points free later */
384   obj->points = NULL;
385 
386   /* Go around all the points creating line points */
387   /* Next point defines the radius */
388   outer_radius_pnt = center_pnt->next; /* this defines the vetices */
389 
390   if (!outer_radius_pnt)
391     {
392 #ifdef DEBUG
393       g_warning ("Internal error in star - no outer vertice point \n");
394 #endif /* DEBUG */
395       return;
396     }
397 
398   inner_radius_pnt = outer_radius_pnt->next; /* this defines the vetices */
399 
400   if (!inner_radius_pnt)
401     {
402 #ifdef DEBUG
403       g_warning ("Internal error in star - no inner vertice point \n");
404 #endif /* DEBUG */
405       return;
406     }
407 
408   shift_x = outer_radius_pnt->pnt.x - center_pnt->pnt.x;
409   shift_y = outer_radius_pnt->pnt.y - center_pnt->pnt.y;
410 
411   outer_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
412 
413   /* Lines */
414   ang_grid = 2.0 * G_PI / (2.0 * (gdouble) obj->type_data);
415   offset_angle = atan2 (shift_y, shift_x);
416 
417   shift_x = inner_radius_pnt->pnt.x - center_pnt->pnt.x;
418   shift_y = inner_radius_pnt->pnt.y - center_pnt->pnt.y;
419 
420   inner_radius = sqrt ((shift_x * shift_x) + (shift_y * shift_y));
421 
422   for (loop = 0 ; loop < 2 * obj->type_data ; loop++)
423     {
424       gdouble  lx, ly;
425       GdkPoint calc_pnt;
426 
427       ang_loop = (gdouble)loop * ang_grid + offset_angle;
428 
429       if (loop % 2)
430         {
431           lx = inner_radius * cos (ang_loop);
432           ly = inner_radius * sin (ang_loop);
433         }
434       else
435         {
436           lx = outer_radius * cos (ang_loop);
437           ly = outer_radius * sin (ang_loop);
438         }
439 
440       calc_pnt.x = RINT (lx + center_pnt->pnt.x);
441       calc_pnt.y = RINT (ly + center_pnt->pnt.y);
442 
443       if (!first)
444         {
445           if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
446             {
447               continue;
448             }
449         }
450 
451       d_pnt_add_line (obj, calc_pnt.x, calc_pnt.y, 0);
452 
453       last_pnt = calc_pnt;
454 
455       if (first)
456         {
457           first_pnt = calc_pnt;
458           first = FALSE;
459         }
460     }
461 
462   d_pnt_add_line (obj, first_pnt.x, first_pnt.y, 0);
463   /* Free old pnts */
464   d_delete_dobjpoints (center_pnt);
465 
466   /* hey we're a line now */
467   obj->type = LINE;
468   obj->class = &dobj_class[LINE];
469 }
470 
471 static GfigObject *
d_copy_poly(GfigObject * obj)472 d_copy_poly (GfigObject *obj)
473 {
474   GfigObject *np;
475 
476   g_assert (obj->type == POLY);
477 
478   np = d_new_object (POLY, obj->points->pnt.x, obj->points->pnt.y);
479   np->points->next = d_copy_dobjpoints (obj->points->next);
480   np->type_data = obj->type_data;
481 
482   return np;
483 }
484 
485 void
d_poly_object_class_init(void)486 d_poly_object_class_init (void)
487 {
488   GfigObjectClass *class = &dobj_class[POLY];
489 
490   class->type      = POLY;
491   class->name      = "POLY";
492   class->drawfunc  = d_draw_poly;
493   class->paintfunc = d_paint_poly;
494   class->copyfunc  = d_copy_poly;
495   class->update    = d_update_poly;
496 }
497 
498 static void
d_update_poly(GdkPoint * pnt)499 d_update_poly (GdkPoint *pnt)
500 {
501   DobjPoints *center_pnt;
502   DobjPoints *edge_pnt;
503 
504   center_pnt = obj_creating->points;
505 
506   if (!center_pnt)
507     return; /* No points */
508 
509   if ((edge_pnt = center_pnt->next))
510     {
511       edge_pnt->pnt = *pnt;
512     }
513   else
514     {
515       d_pnt_add_line (obj_creating, pnt->x, pnt->y, -1);
516     }
517 }
518 
519 void
d_poly_start(GdkPoint * pnt,gboolean shift_down)520 d_poly_start (GdkPoint *pnt,
521               gboolean  shift_down)
522 {
523   obj_creating = d_new_object (POLY, pnt->x, pnt->y);
524   obj_creating->type_data = poly_num_sides;
525 }
526 
527 void
d_poly_end(GdkPoint * pnt,gboolean shift_down)528 d_poly_end (GdkPoint *pnt,
529             gboolean  shift_down)
530 {
531   add_to_all_obj (gfig_context->current_obj, obj_creating);
532   obj_creating = NULL;
533 }
534