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 <stdlib.h>
28 
29 #include <libgimp/gimp.h>
30 #include <libgimp/gimpui.h>
31 
32 #include "gfig.h"
33 #include "gfig-dobject.h"
34 #include "gfig-line.h"
35 #include "gfig-spiral.h"
36 #include "gfig-dialog.h"
37 
38 #include "libgimp/stdplugins-intl.h"
39 
40 static void        d_draw_spiral   (GfigObject *obj,
41                                     cairo_t    *cr);
42 static void        d_paint_spiral  (GfigObject *obj);
43 static GfigObject *d_copy_spiral   (GfigObject *obj);
44 
45 static void        d_update_spiral (GdkPoint  *pnt);
46 
47 static gint spiral_num_turns = 4; /* Default to 4 turns */
48 static gint spiral_toggle    = 0; /* 0 = clockwise -1 = anti-clockwise */
49 
50 void
tool_options_spiral(GtkWidget * notebook)51 tool_options_spiral (GtkWidget *notebook)
52 {
53   GtkWidget *sides;
54 
55   sides = num_sides_widget (_("Spiral Number of Turns"),
56                             &spiral_num_turns, &spiral_toggle, 1, 20);
57   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sides, NULL);
58 }
59 
60 static void
d_draw_spiral(GfigObject * obj,cairo_t * cr)61 d_draw_spiral (GfigObject *obj,
62                cairo_t    *cr)
63 {
64   DobjPoints *center_pnt;
65   DobjPoints *radius_pnt;
66   gint16      shift_x;
67   gint16      shift_y;
68   gdouble     ang_grid;
69   gdouble     ang_loop;
70   gdouble     radius;
71   gdouble     offset_angle;
72   gdouble     sp_cons;
73   gint        loop;
74   GdkPoint    start_pnt = { 0, 0 };
75   gboolean    do_line = FALSE;
76   gint        clock_wise = 1;
77 
78   center_pnt = obj->points;
79 
80   if (!center_pnt)
81     return; /* End-of-line */
82 
83   /* First point is the center */
84   /* Just draw a control point around it */
85 
86   draw_sqr (&center_pnt->pnt, obj == gfig_context->selected_obj, cr);
87 
88   /* Next point defines the radius */
89   radius_pnt = center_pnt->next; /* this defines the vetices */
90 
91   if (!radius_pnt)
92     {
93 #ifdef DEBUG
94       g_warning ("Internal error in spiral - no vertice point \n");
95 #endif /* DEBUG */
96       return;
97     }
98 
99   /* Other control point */
100   if (obj_creating == obj)
101     draw_circle (&radius_pnt->pnt, TRUE, cr);
102   else
103     draw_sqr (&radius_pnt->pnt, obj == gfig_context->selected_obj, cr);
104 
105   /* Have center and radius - draw spiral */
106 
107   shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
108   shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
109 
110   radius = sqrt ((shift_x * shift_x) + (shift_y * shift_y));
111 
112   offset_angle = atan2 (shift_y, shift_x);
113 
114   clock_wise = obj->type_data / abs (obj->type_data);
115 
116   if (offset_angle < 0)
117     offset_angle += 2.0 * G_PI;
118 
119   sp_cons = radius/(obj->type_data * 2 * G_PI + offset_angle);
120   /* Lines */
121   ang_grid = 2.0 * G_PI / 180.0;
122 
123 
124   for (loop = 0 ; loop <= abs (obj->type_data * 180) +
125          clock_wise * (gint)RINT (offset_angle/ang_grid) ; loop++)
126     {
127       gdouble  lx, ly;
128       GdkPoint calc_pnt;
129 
130       ang_loop = (gdouble)loop * ang_grid;
131 
132       lx = sp_cons * ang_loop * cos (ang_loop)*clock_wise;
133       ly = sp_cons * ang_loop * sin (ang_loop);
134 
135       calc_pnt.x = RINT (lx + center_pnt->pnt.x);
136       calc_pnt.y = RINT (ly + center_pnt->pnt.y);
137 
138       if (do_line)
139         {
140           /* Miss out points that come to the same location */
141           if (calc_pnt.x == start_pnt.x && calc_pnt.y == start_pnt.y)
142             continue;
143 
144           gfig_draw_line (calc_pnt.x, calc_pnt.y, start_pnt.x, start_pnt.y, cr);
145         }
146       else
147         {
148           do_line = TRUE;
149         }
150       start_pnt = calc_pnt;
151     }
152 }
153 
154 static void
d_paint_spiral(GfigObject * obj)155 d_paint_spiral (GfigObject *obj)
156 {
157   /* first point center */
158   /* Next point is radius */
159   gdouble    *line_pnts;
160   gint        seg_count = 0;
161   gint        i = 0;
162   DobjPoints *center_pnt;
163   DobjPoints *radius_pnt;
164   gint16      shift_x;
165   gint16      shift_y;
166   gdouble     ang_grid;
167   gdouble     ang_loop;
168   gdouble     radius;
169   gdouble     offset_angle;
170   gdouble     sp_cons;
171   gint        loop;
172   GdkPoint    last_pnt = { 0, 0 };
173   gint        clock_wise = 1;
174 
175   g_assert (obj != NULL);
176 
177   center_pnt = obj->points;
178 
179   if (!center_pnt || !center_pnt->next)
180     return; /* no-line */
181 
182   /* Go around all the points drawing a line from one to the next */
183 
184   radius_pnt = center_pnt->next; /* this defines the vetices */
185 
186   /* Have center and radius - get lines */
187   shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
188   shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
189 
190   radius = sqrt ((shift_x * shift_x) + (shift_y * shift_y));
191 
192   clock_wise = obj->type_data / abs (obj->type_data);
193 
194   offset_angle = atan2 (shift_y, shift_x);
195 
196   if (offset_angle < 0)
197     offset_angle += 2.0 * G_PI;
198 
199   sp_cons = radius/(obj->type_data * 2.0 * G_PI + offset_angle);
200   /* Lines */
201   ang_grid = 2.0 * G_PI / 180.0;
202 
203   /* count - */
204   seg_count = abs (obj->type_data * 180) + clock_wise * (gint)RINT (offset_angle/ang_grid);
205 
206   line_pnts = g_new0 (gdouble, 2 * seg_count + 3);
207 
208   for (loop = 0 ; loop <= seg_count; loop++)
209     {
210       gdouble  lx, ly;
211       GdkPoint calc_pnt;
212 
213       ang_loop = (gdouble)loop * ang_grid;
214 
215       lx = sp_cons * ang_loop * cos (ang_loop)*clock_wise;
216       ly = sp_cons * ang_loop * sin (ang_loop);
217 
218       calc_pnt.x = RINT (lx + center_pnt->pnt.x);
219       calc_pnt.y = RINT (ly + center_pnt->pnt.y);
220 
221       /* Miss out duped pnts */
222       if (!loop)
223         {
224           if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
225             {
226               continue;
227             }
228         }
229 
230       line_pnts[i++] = calc_pnt.x;
231       line_pnts[i++] = calc_pnt.y;
232       last_pnt = calc_pnt;
233     }
234 
235   /* Scale before drawing */
236   if (selvals.scaletoimage)
237     scale_to_original_xy (&line_pnts[0], i / 2);
238   else
239     scale_to_xy (&line_pnts[0], i / 2);
240 
241   /* One go */
242   if (obj->style.paint_type == PAINT_BRUSH_TYPE)
243     {
244       gfig_paint (selvals.brshtype,
245                   gfig_context->drawable_id,
246                   i, line_pnts);
247     }
248 
249   g_free (line_pnts);
250 }
251 
252 static GfigObject *
d_copy_spiral(GfigObject * obj)253 d_copy_spiral (GfigObject *obj)
254 {
255   GfigObject *np;
256 
257   g_assert (obj->type == SPIRAL);
258 
259   np = d_new_object (SPIRAL, obj->points->pnt.x, obj->points->pnt.y);
260   np->points->next = d_copy_dobjpoints (obj->points->next);
261   np->type_data = obj->type_data;
262 
263   return np;
264 }
265 
266 void
d_spiral_object_class_init(void)267 d_spiral_object_class_init (void)
268 {
269   GfigObjectClass *class = &dobj_class[SPIRAL];
270 
271   class->type      = SPIRAL;
272   class->name      = "SPIRAL";
273   class->drawfunc  = d_draw_spiral;
274   class->paintfunc = d_paint_spiral;
275   class->copyfunc  = d_copy_spiral;
276   class->update    = d_update_spiral;
277 }
278 
279 static void
d_update_spiral(GdkPoint * pnt)280 d_update_spiral (GdkPoint *pnt)
281 {
282   DobjPoints *center_pnt;
283   DobjPoints *edge_pnt;
284 
285   center_pnt = obj_creating->points;
286 
287   if (!center_pnt)
288     return; /* No points */
289 
290   if ((edge_pnt = center_pnt->next))
291     {
292       edge_pnt->pnt = *pnt;
293     }
294   else
295     {
296       /* Radius is a few pixels away */
297       /* First edge point */
298       d_pnt_add_line (obj_creating, pnt->x, pnt->y, -1);
299     }
300 }
301 
302 void
d_spiral_start(GdkPoint * pnt,gboolean shift_down)303 d_spiral_start (GdkPoint *pnt,
304                 gboolean  shift_down)
305 {
306   obj_creating = d_new_object (SPIRAL, pnt->x, pnt->y);
307   obj_creating->type_data = spiral_num_turns * ((spiral_toggle == 0) ? 1 : -1);
308 }
309 
310 void
d_spiral_end(GdkPoint * pnt,gboolean shift_down)311 d_spiral_end (GdkPoint *pnt,
312               gboolean  shift_down)
313 {
314   add_to_all_obj (gfig_context->current_obj, obj_creating);
315   obj_creating = NULL;
316 }
317