1 /*
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This is a plug-in for GIMP.
5 *
6 * Plugin to convert a selection to a path.
7 *
8 * Copyright (C) 1999 Andy Thomas alt@gimp.org
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 /* Change log:-
26 * 0.1 First version.
27 */
28
29 #include "config.h"
30
31 #include <libgimp/gimp.h>
32 #include <libgimp/gimpui.h>
33
34 #include "types.h"
35
36 #include "selection-to-path.h"
37
38 #include "libgimp/stdplugins-intl.h"
39
40 #define SCALE_WIDTH 100
41 #define SCALE_DIGITS 8
42
43
44 static GSList * adjust_widgets = NULL;
45
46
47 /* Reset to recommended defaults */
48 void
reset_adv_dialog(void)49 reset_adv_dialog (void)
50 {
51 GSList *list;
52 GtkObject *widget;
53 gdouble *value;
54
55 for (list = adjust_widgets; list; list = g_slist_next (list))
56 {
57 widget = GTK_OBJECT (list->data);
58 value = (gdouble *) g_object_get_data (G_OBJECT (widget),
59 "default_value");
60
61 if (GTK_IS_ADJUSTMENT (widget))
62 {
63 gtk_adjustment_set_value (GTK_ADJUSTMENT (widget),
64 *value);
65 }
66 else if (GTK_IS_TOGGLE_BUTTON (widget))
67 {
68 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
69 (gboolean)(*value));
70 }
71 else
72 g_warning ("Internal widget list error");
73 }
74 }
75
76 static gpointer
def_val(gdouble default_value)77 def_val (gdouble default_value)
78 {
79 gdouble *value = g_new0 (gdouble, 1);
80 *value = default_value;
81 return (value);
82 }
83
84 GtkWidget *
dialog_create_selection_area(SELVALS * sels)85 dialog_create_selection_area (SELVALS *sels)
86 {
87 GtkWidget *scrolled_win;
88 GtkWidget *viewport;
89 GtkWidget *table;
90 GtkWidget *check;
91 GtkObject *adj;
92 gint row;
93
94 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
95 gtk_widget_set_size_request (scrolled_win, -1, 400);
96 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
97 GTK_SHADOW_NONE);
98 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
99 GTK_POLICY_NEVER,
100 GTK_POLICY_ALWAYS);
101
102 viewport = gtk_viewport_new (NULL, NULL);
103 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
104 gtk_container_add (GTK_CONTAINER (scrolled_win), viewport);
105 gtk_widget_show (viewport);
106
107 table = gtk_table_new (20, 3, FALSE);
108 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
109 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
110 gtk_container_add (GTK_CONTAINER (viewport), table);
111 gtk_widget_show (table);
112
113 row = 0;
114
115 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
116 _("Align Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
117 sels->align_threshold,
118 0.2, 2.0, 0.1, 0.1, 2,
119 TRUE, 0, 0,
120 _("If two endpoints are closer than this, "
121 "they are made to be equal."), NULL);
122 g_signal_connect (adj, "value-changed",
123 G_CALLBACK (gimp_double_adjustment_update),
124 &sels->align_threshold);
125 adjust_widgets = g_slist_append (adjust_widgets, adj);
126 g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.5));
127
128 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
129 _("Corner Always Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
130 sels->corner_always_threshold,
131 30, 180, 1, 1, 2,
132 TRUE, 0, 0,
133 _("If the angle defined by a point and its predecessors "
134 "and successors is smaller than this, it's a corner, "
135 "even if it's within 'corner_surround' pixels of a "
136 "point with a smaller angle."), NULL);
137 g_signal_connect (adj, "value-changed",
138 G_CALLBACK (gimp_double_adjustment_update),
139 &sels->corner_always_threshold);
140 adjust_widgets = g_slist_append (adjust_widgets, adj);
141 g_object_set_data (G_OBJECT (adj), "default_value", def_val (60.0));
142
143 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
144 _("Corner Surround:"), SCALE_WIDTH, SCALE_DIGITS,
145 sels->corner_surround,
146 3, 8, 1, 1, 0,
147 TRUE, 0, 0,
148 _("Number of points to consider when determining if a "
149 "point is a corner or not."), NULL);
150 g_signal_connect (adj, "value-changed",
151 G_CALLBACK (gimp_double_adjustment_update),
152 &sels->corner_surround);
153 adjust_widgets = g_slist_append (adjust_widgets, adj);
154 g_object_set_data (G_OBJECT (adj), "default_value", def_val (4.0));
155
156 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
157 _("Corner Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
158 sels->corner_threshold,
159 0, 180, 1, 1, 2,
160 TRUE, 0, 0,
161 _("If a point, its predecessors, and its successors "
162 "define an angle smaller than this, it's a corner."),
163 NULL);
164 g_signal_connect (adj, "value-changed",
165 G_CALLBACK (gimp_double_adjustment_update),
166 &sels->corner_threshold);
167 adjust_widgets = g_slist_append (adjust_widgets, adj);
168 g_object_set_data (G_OBJECT (adj), "default_value", def_val (100.0));
169
170
171 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
172 _("Error Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
173 sels->error_threshold,
174 0.2, 10, 0.1, 0.1, 2,
175 TRUE, 0, 0,
176 _("Amount of error at which a fitted spline is "
177 "unacceptable. If any pixel is further away "
178 "than this from the fitted curve, we try again."),
179 NULL);
180 g_signal_connect (adj, "value-changed",
181 G_CALLBACK (gimp_double_adjustment_update),
182 &sels->error_threshold);
183 adjust_widgets = g_slist_append (adjust_widgets, adj);
184 g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.40));
185
186 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
187 _("Filter Alternative Surround:"), SCALE_WIDTH, SCALE_DIGITS,
188 sels->filter_alternative_surround,
189 1, 10, 1, 1, 0,
190 TRUE, 0, 0,
191 _("A second number of adjacent points to consider "
192 "when filtering."), NULL);
193 g_signal_connect (adj, "value-changed",
194 G_CALLBACK (gimp_double_adjustment_update),
195 &sels->filter_alternative_surround);
196 adjust_widgets = g_slist_append (adjust_widgets, adj);
197 g_object_set_data (G_OBJECT (adj), "default_value", def_val (1.0));
198
199
200 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
201 _("Filter Epsilon:"), SCALE_WIDTH, SCALE_DIGITS,
202 sels->filter_epsilon,
203 5, 40, 1, 1, 2,
204 TRUE, 0, 0,
205 _("If the angles between the vectors produced by "
206 "filter_surround and filter_alternative_surround "
207 "points differ by more than this, use the one from "
208 "filter_alternative_surround."), NULL);
209 g_signal_connect (adj, "value-changed",
210 G_CALLBACK (gimp_double_adjustment_update),
211 &sels->filter_epsilon);
212 adjust_widgets = g_slist_append (adjust_widgets, adj);
213 g_object_set_data (G_OBJECT (adj), "default_value", def_val (10.0));
214
215 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
216 _("Filter Iteration Count:"), SCALE_WIDTH, SCALE_DIGITS,
217 sels->filter_iteration_count,
218 4, 70, 1, 1, 0,
219 TRUE, 0, 0,
220 _("Number of times to smooth original data points. "
221 "Increasing this number dramatically --- to 50 or "
222 "so --- can produce vastly better results. But if "
223 "any points that 'should' be corners aren't found, "
224 "the curve goes to hell around that point."), NULL);
225 g_signal_connect (adj, "value-changed",
226 G_CALLBACK (gimp_double_adjustment_update),
227 &sels->filter_iteration_count);
228 adjust_widgets = g_slist_append (adjust_widgets, adj);
229 g_object_set_data (G_OBJECT (adj), "default_value", def_val (4.0));
230
231 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
232 _("Filter Percent:"), SCALE_WIDTH, SCALE_DIGITS,
233 sels->filter_percent,
234 0, 1, 0.05, 0.01, 2,
235 TRUE, 0, 0,
236 _("To produce the new point, use the old point plus "
237 "this times the neighbors."), NULL);
238 g_signal_connect (adj, "value-changed",
239 G_CALLBACK (gimp_double_adjustment_update),
240 &sels->filter_percent);
241 adjust_widgets = g_slist_append (adjust_widgets, adj);
242 g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.33));
243
244 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
245 _("Filter Secondary Surround:"), SCALE_WIDTH, SCALE_DIGITS,
246 sels->filter_secondary_surround,
247 3, 10, 1, 1, 0,
248 TRUE, 0, 0,
249 _("Number of adjacent points to consider if "
250 "'filter_surround' points defines a straight line."),
251 NULL);
252 g_signal_connect (adj, "value-changed",
253 G_CALLBACK (gimp_double_adjustment_update),
254 &sels->filter_secondary_surround);
255 adjust_widgets = g_slist_append (adjust_widgets, adj);
256 g_object_set_data (G_OBJECT (adj), "default_value", def_val (3.0));
257
258 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
259 _("Filter Surround:"), SCALE_WIDTH, SCALE_DIGITS,
260 sels->filter_surround,
261 2, 10, 1, 1, 0,
262 TRUE, 0, 0,
263 _("Number of adjacent points to consider when filtering."),
264 NULL);
265 g_signal_connect (adj, "value-changed",
266 G_CALLBACK (gimp_double_adjustment_update),
267 &sels->filter_surround);
268 adjust_widgets = g_slist_append (adjust_widgets, adj);
269 g_object_set_data (G_OBJECT (adj), "default_value", def_val (2.0));
270
271 check = gtk_check_button_new_with_label (_("Keep Knees"));
272 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), sels->keep_knees);
273 gtk_table_attach (GTK_TABLE (table), check, 1, 3, row, row + 1,
274 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
275 gimp_help_set_help_data (GTK_WIDGET (check),
276 _("Says whether or not to remove 'knee' "
277 "points after finding the outline."), NULL);
278 g_signal_connect (check, "toggled",
279 G_CALLBACK (gimp_toggle_button_update),
280 &sels->keep_knees);
281 gtk_widget_show (check);
282 adjust_widgets = g_slist_append (adjust_widgets, check);
283 g_object_set_data (G_OBJECT (check), "default_value", def_val ((gdouble)FALSE));
284 row++;
285
286 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
287 _("Line Reversion Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
288 sels->line_reversion_threshold,
289 0.01, 0.2, 0.01, 0.01, 3,
290 TRUE, 0, 0,
291 _("If a spline is closer to a straight line than this, "
292 "it remains a straight line, even if it would otherwise "
293 "be changed back to a curve. This is weighted by the "
294 "square of the curve length, to make shorter curves "
295 "more likely to be reverted."), NULL);
296 g_signal_connect (adj, "value-changed",
297 G_CALLBACK (gimp_double_adjustment_update),
298 &sels->line_reversion_threshold);
299 adjust_widgets = g_slist_append (adjust_widgets, adj);
300 g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.01));
301
302 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
303 _("Line Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
304 sels->line_threshold,
305 0.2, 4, 0.1, 0.01, 2,
306 TRUE, 0, 0,
307 _("How many pixels (on the average) a spline can "
308 "diverge from the line determined by its endpoints "
309 "before it is changed to a straight line."), NULL);
310 g_signal_connect (adj, "value-changed",
311 G_CALLBACK (gimp_double_adjustment_update),
312 &sels->line_threshold);
313 adjust_widgets = g_slist_append (adjust_widgets, adj);
314 g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.5));
315
316 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
317 _("Reparametrize Improvement:"), SCALE_WIDTH, SCALE_DIGITS,
318 sels->reparameterize_improvement,
319 0, 1, 0.05, 0.01, 2,
320 TRUE, 0, 0,
321 _("If reparameterization doesn't improve the fit by this "
322 "much percent, stop doing it. ""Amount of error at which "
323 "it is pointless to reparameterize."), NULL);
324 g_signal_connect (adj, "value-changed",
325 G_CALLBACK (gimp_double_adjustment_update),
326 &sels->reparameterize_improvement);
327 adjust_widgets = g_slist_append (adjust_widgets, adj);
328 g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.01));
329
330 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
331 _("Reparametrize Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
332 sels->reparameterize_threshold,
333 1, 50, 0.5, 0.5, 2,
334 TRUE, 0, 0,
335 _("Amount of error at which it is pointless to reparameterize. "
336 "This happens, for example, when we are trying to fit the "
337 "outline of the outside of an 'O' with a single spline. "
338 "The initial fit is not good enough for the Newton-Raphson "
339 "iteration to improve it. It may be that it would be better "
340 "to detect the cases where we didn't find any corners."), NULL);
341 g_signal_connect (adj, "value-changed",
342 G_CALLBACK (gimp_double_adjustment_update),
343 &sels->reparameterize_threshold);
344 adjust_widgets = g_slist_append (adjust_widgets, adj);
345 g_object_set_data (G_OBJECT (adj), "default_value", def_val (1.0));
346
347 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
348 _("Subdivide Search:"), SCALE_WIDTH, SCALE_DIGITS,
349 sels->subdivide_search,
350 0.05, 1, 0.05, 0.01, 2,
351 TRUE, 0, 0,
352 _("Percentage of the curve away from the worst point "
353 "to look for a better place to subdivide."), NULL);
354 g_signal_connect (adj, "value-changed",
355 G_CALLBACK (gimp_double_adjustment_update),
356 &sels->subdivide_search);
357 adjust_widgets = g_slist_append (adjust_widgets, adj);
358 g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.1));
359
360 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
361 _("Subdivide Surround:"), SCALE_WIDTH, SCALE_DIGITS,
362 sels->subdivide_surround,
363 2, 10, 1, 1, 0,
364 TRUE, 0, 0,
365 _("Number of points to consider when deciding whether "
366 "a given point is a better place to subdivide."),
367 NULL);
368 g_signal_connect (adj, "value-changed",
369 G_CALLBACK (gimp_double_adjustment_update),
370 &sels->subdivide_surround);
371 adjust_widgets = g_slist_append (adjust_widgets, adj);
372 g_object_set_data (G_OBJECT (adj), "default_value", def_val (4.0));
373
374 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
375 _("Subdivide Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
376 sels->subdivide_threshold,
377 0.01, 1, 0.01, 0.01, 2,
378 TRUE, 0, 0,
379 _("How many pixels a point can diverge from a straight "
380 "line and still be considered a better place to "
381 "subdivide."), NULL);
382 g_signal_connect (adj, "value-changed",
383 G_CALLBACK (gimp_double_adjustment_update),
384 &sels->subdivide_threshold);
385 adjust_widgets = g_slist_append (adjust_widgets, adj);
386 g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.03));
387
388 adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
389 _("Tangent Surround:"), SCALE_WIDTH, SCALE_DIGITS,
390 sels->tangent_surround,
391 2, 10, 1, 1, 0,
392 TRUE, 0, 0,
393 _("Number of points to look at on either side of a "
394 "point when computing the approximation to the "
395 "tangent at that point."), NULL);
396 g_signal_connect (adj, "value-changed",
397 G_CALLBACK (gimp_double_adjustment_update),
398 &sels->tangent_surround);
399 adjust_widgets = g_slist_append (adjust_widgets, adj);
400 g_object_set_data (G_OBJECT (adj), "default_value", def_val (3.0));
401
402 return scrolled_win;
403 }
404