1 /*
2 * Spray Tool
3 *
4 * Authors:
5 * Pierre-Antoine MARC
6 * Pierre CACLIN
7 * Aurel-Aimé MARMION
8 * Julien LERAY
9 * Benoît LAVORATA
10 * Vincent MONTAGNE
11 * Pierre BARBRY-BLOT
12 * Steren GIANNINI (steren.giannini@gmail.com)
13 * Jon A. Cruz <jon@joncruz.org>
14 * Abhishek Sharma
15 *
16 * Copyright (C) 2009 authors
17 *
18 * Released under GNU GPL, read the file 'COPYING' for more information
19 */
21 #include "config.h"
23 #include <gtk/gtk.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <glibmm/i18n.h>
27 #include <numeric>
29 #include "svg/svg.h"
30 #include "display/canvas-bpath.h"
32 #include <glib/gmem.h>
33 #include "macros.h"
34 #include "document.h"
35 #include "selection.h"
36 #include "desktop.h"
37 #include "desktop-events.h"
38 #include "desktop-handles.h"
39 #include "unistd.h"
40 #include "desktop-style.h"
41 #include "message-context.h"
42 #include "pixmaps/cursor-spray.xpm"
43 #include <boost/optional.hpp>
44 #include "libnr/nr-matrix-ops.h"
45 #include "libnr/nr-scale-translate-ops.h"
46 #include "xml/repr.h"
47 #include "context-fns.h"
48 #include "sp-item.h"
49 #include "inkscape.h"
50 #include "color.h"
51 #include "svg/svg-color.h"
52 #include "splivarot.h"
53 #include "sp-item-group.h"
54 #include "sp-shape.h"
55 #include "sp-path.h"
56 #include "path-chemistry.h"
57 #include "sp-gradient.h"
58 #include "sp-stop.h"
59 #include "sp-gradient-reference.h"
60 #include "sp-linear-gradient.h"
61 #include "sp-radial-gradient.h"
62 #include "gradient-chemistry.h"
63 #include "sp-text.h"
64 #include "sp-flowtext.h"
65 #include "display/canvas-bpath.h"
66 #include "display/canvas-arena.h"
67 #include "display/curve.h"
68 #include "livarot/Shape.h"
69 #include <2geom/isnan.h>
70 #include <2geom/transforms.h>
71 #include "preferences.h"
72 #include "style.h"
73 #include "box3d.h"
74 #include "sp-item-transform.h"
75 #include "filter-chemistry.h"
76 #include "sp-gaussian-blur-fns.h"
77 #include "sp-gaussian-blur.h"
79 #include "spray-context.h"
80 #include "ui/dialog/dialog-manager.h"
81 #include "helper/action.h"
83 #include <iostream>
85 using Inkscape::DocumentUndo;
86 using namespace std;
89 #define DDC_RED_RGBA 0xff0000ff
91 #define DYNA_MIN_WIDTH 1.0e-6
93 static void sp_spray_context_class_init(SPSprayContextClass *klass);
94 static void sp_spray_context_init(SPSprayContext *ddc);
95 static void sp_spray_context_dispose(GObject *object);
97 static void sp_spray_context_setup(SPEventContext *ec);
98 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val);
99 static gint sp_spray_context_root_handler(SPEventContext *ec, GdkEvent *event);
101 static SPEventContextClass *parent_class = 0;
104 /**
105 * This function returns pseudo-random numbers from a normal distribution
106 * @param mu : mean
107 * @param sigma : standard deviation ( > 0 )
108 */
109 inline double NormalDistribution(double mu,double sigma)
110 {
111 // use Box Muller's algorithm
112 return mu + sigma * sqrt( -2.0 * log(g_random_double_range(0, 1)) ) * cos( 2.0*M_PI*g_random_double_range(0, 1) );
113 }
116 GtkType sp_spray_context_get_type(void)
117 {
118 static GType type = 0;
119 if (!type) {
120 GTypeInfo info = {
121 sizeof(SPSprayContextClass),
122 NULL, NULL,
123 (GClassInitFunc) sp_spray_context_class_init,
124 NULL, NULL,
125 sizeof(SPSprayContext),
126 4,
127 (GInstanceInitFunc) sp_spray_context_init,
128 NULL, /* value_table */
129 };
130 type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSprayContext", &info, (GTypeFlags)0);
131 }
132 return type;
133 }
135 static void sp_spray_context_class_init(SPSprayContextClass *klass)
136 {
137 GObjectClass *object_class = (GObjectClass *) klass;
138 SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
140 parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
142 object_class->dispose = sp_spray_context_dispose;
144 event_context_class->setup = sp_spray_context_setup;
145 event_context_class->set = sp_spray_context_set;
146 event_context_class->root_handler = sp_spray_context_root_handler;
147 }
149 /* Method to rotate items */
150 void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom::Rotate const &rotation)
151 {
152 Geom::Point center = c;
153 Geom::Translate const s(c);
154 Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s);
155 // Rotate item.
156 item->set_i2d_affine(item->i2d_affine() * (Geom::Matrix)affine);
157 // Use each item's own transform writer, consistent with sp_selection_apply_affine()
158 item->doWriteTransform(SP_OBJECT_REPR(item), item->transform);
159 // Restore the center position (it's changed because the bbox center changed)
160 if (item->isCenterSet()) {
161 item->setCenter(c);
162 item->updateRepr();
163 }
164 }
166 /* Method to scale items */
167 void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale const &scale)
168 {
169 Geom::Translate const s(c);
170 item->set_i2d_affine(item->i2d_affine() * s.inverse() * scale * s );
171 item->doWriteTransform(SP_OBJECT_REPR(item), item->transform);
172 }
174 static void sp_spray_context_init(SPSprayContext *tc)
175 {
176 SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
178 event_context->cursor_shape = cursor_spray_xpm;
179 event_context->hot_x = 4;
180 event_context->hot_y = 4;
182 /* attributes */
183 tc->dragging = FALSE;
184 tc->distrib = 1;
185 tc->width = 0.2;
186 tc->force = 0.2;
187 tc->ratio = 0;
188 tc->tilt=0;
189 tc->mean = 0.2;
190 tc->rotation_variation=0;
191 tc->standard_deviation=0.2;
192 tc->scale=1;
193 tc->scale_variation = 1;
194 tc->pressure = TC_DEFAULT_PRESSURE;
196 tc->is_dilating = false;
197 tc->has_dilated = false;
199 tc->do_h = true;
200 tc->do_s = true;
201 tc->do_l = true;
202 tc->do_o = false;
204 new (&tc->style_set_connection) sigc::connection();
205 }
207 static void sp_spray_context_dispose(GObject *object)
208 {
209 SPSprayContext *tc = SP_SPRAY_CONTEXT(object);
211 tc->style_set_connection.disconnect();
212 tc->style_set_connection.~connection();
214 if (tc->dilate_area) {
215 gtk_object_destroy(GTK_OBJECT(tc->dilate_area));
216 tc->dilate_area = NULL;
217 }
219 if (tc->_message_context) {
220 delete tc->_message_context;
221 }
223 G_OBJECT_CLASS(parent_class)->dispose(object);
224 }
226 bool is_transform_modes(gint mode)
227 {
228 return (mode == SPRAY_MODE_COPY ||
229 mode == SPRAY_MODE_CLONE ||
230 mode == SPRAY_MODE_SINGLE_PATH ||
231 mode == SPRAY_OPTION);
232 }
234 void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/)
235 {
236 SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
237 SPDesktop *desktop = event_context->desktop;
239 guint num = 0;
240 gchar *sel_message = NULL;
241 if (!desktop->selection->isEmpty()) {
242 num = g_slist_length((GSList *) desktop->selection->itemList());
243 sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
244 } else {
245 sel_message = g_strdup_printf(_("<b>Nothing</b> selected"));
246 }
249 switch (tc->mode) {
250 case SPRAY_MODE_COPY:
251 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>copies</b> of the initial selection"), sel_message);
252 break;
253 case SPRAY_MODE_CLONE:
254 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>clones</b> of the initial selection"), sel_message);
255 break;
256 case SPRAY_MODE_SINGLE_PATH:
257 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray in a <b>single path</b> of the initial selection"), sel_message);
258 break;
259 default:
260 break;
261 }
262 sp_event_context_update_cursor(event_context);
263 g_free(sel_message);
264 }
266 static void sp_spray_context_setup(SPEventContext *ec)
267 {
268 SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
270 if (((SPEventContextClass *) parent_class)->setup)
271 ((SPEventContextClass *) parent_class)->setup(ec);
273 {
274 /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
275 SPCurve *c = new SPCurve();
276 const double C1 = 0.552;
277 c->moveto(-1,0);
278 c->curveto(-1, C1, -C1, 1, 0, 1 );
279 c->curveto(C1, 1, 1, C1, 1, 0 );
280 c->curveto(1, -C1, C1, -1, 0, -1 );
281 c->curveto(-C1, -1, -1, -C1, -1, 0 );
282 c->closepath();
283 tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c);
284 c->unref();
285 sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0);
286 sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
287 sp_canvas_item_hide(tc->dilate_area);
288 }
290 tc->is_drawing = false;
292 tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
294 sp_event_context_read(ec, "distrib");
295 sp_event_context_read(ec, "width");
296 sp_event_context_read(ec, "ratio");
297 sp_event_context_read(ec, "tilt");
298 sp_event_context_read(ec, "rotation_variation");
299 sp_event_context_read(ec, "scale_variation");
300 sp_event_context_read(ec, "mode");
301 sp_event_context_read(ec, "population");
302 sp_event_context_read(ec, "force");
303 sp_event_context_read(ec, "mean");
304 sp_event_context_read(ec, "standard_deviation");
305 sp_event_context_read(ec, "usepressure");
306 sp_event_context_read(ec, "Scale");
307 sp_event_context_read(ec, "doh");
308 sp_event_context_read(ec, "dol");
309 sp_event_context_read(ec, "dos");
310 sp_event_context_read(ec, "doo");
312 ;
314 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
315 if (prefs->getBool("/tools/spray/selcue")) {
316 ec->enableSelectionCue();
317 }
319 if (prefs->getBool("/tools/spray/gradientdrag")) {
320 ec->enableGrDrag();
321 }
322 }
324 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
325 {
326 SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
327 Glib::ustring path = val->getEntryName();
329 if (path == "width") {
330 tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100);
331 } else if (path == "mode") {
332 tc->mode = val->getInt();
333 sp_spray_update_cursor(tc, false);
334 } else if (path == "distribution") {
335 tc->distrib = val->getInt(1);
336 } else if (path == "population") {
337 tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100);
338 } else if (path == "tilt") {
339 tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0);
340 } else if (path == "ratio") {
341 tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9);
342 } else if (path == "force") {
343 tc->force = CLAMP(val->getDouble(1.0), 0, 1.0);
344 } else if (path == "rotation_variation") {
345 tc->rotation_variation = CLAMP(val->getDouble(0.0), 0, 100.0);
346 } else if (path == "scale_variation") {
347 tc->scale_variation = CLAMP(val->getDouble(1.0), 0, 100.0);
348 } else if (path == "mean") {
349 tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100);
350 } else if (path == "standard_deviation") {
351 tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100);
352 } else if (path == "usepressure") {
353 tc->usepressure = val->getBool();
354 } else if (path == "doh") {
355 tc->do_h = val->getBool();
356 } else if (path == "dos") {
357 tc->do_s = val->getBool();
358 } else if (path == "dol") {
359 tc->do_l = val->getBool();
360 } else if (path == "doo") {
361 tc->do_o = val->getBool();
362 }
363 }
365 static void sp_spray_extinput(SPSprayContext *tc, GdkEvent *event)
366 {
367 if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure))
368 tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
369 else
370 tc->pressure = TC_DEFAULT_PRESSURE;
371 }
373 double get_dilate_radius(SPSprayContext *tc)
374 {
376 return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom();
379 }
381 double get_path_force(SPSprayContext *tc)
382 {
383 double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
384 /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom());
385 if (force > 3) {
386 force += 4 * (force - 3);
387 }
388 return force * tc->force;
389 }
391 double get_path_mean(SPSprayContext *tc)
392 {
393 return tc->mean;
394 }
396 double get_path_standard_deviation(SPSprayContext *tc)
397 {
398 return tc->standard_deviation;
399 }
401 double get_move_force(SPSprayContext *tc)
402 {
403 double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
404 return force * tc->force;
405 }
407 double get_move_mean(SPSprayContext *tc)
408 {
409 return tc->mean;
410 }
412 double get_move_standard_deviation(SPSprayContext *tc)
413 {
414 return tc->standard_deviation;
415 }
417 /**
418 * Method to handle the distribution of the items
419 * @param[out] radius : radius of the position of the sprayed object
420 * @param[out] angle : angle of the position of the sprayed object
421 * @param[in] a : mean
422 * @param[in] s : standard deviation
423 * @param[in] choice :
425 */
426 void random_position( double &radius, double &angle, double &a, double &s, int /*choice*/)
427 {
428 // angle is taken from an uniform distribution
429 angle = g_random_double_range(0, M_PI*2.0);
431 // radius is taken from a Normal Distribution
432 double radius_temp =-1;
433 while(!((radius_temp>=0)&&(radius_temp<=1)))
434 {
435 radius_temp = NormalDistribution( a, s );
436 }
437 // Because we are in polar coordinates, a special treatment has to be done to the radius.
438 // Otherwise, positions taken from an uniform repartition on radius and angle will not seam to
439 // be uniformily distributed on the disk (more at the center and less at the boundary).
440 // We counter this effect with a 0.5 exponent. This is empiric.
441 radius = pow( radius_temp, 0.5);
443 }
449 bool sp_spray_recursive(SPDesktop *desktop,
450 Inkscape::Selection *selection,
451 SPItem *item,
452 Geom::Point p,
453 Geom::Point /*vector*/,
454 gint mode,
455 double radius,
456 double /*force*/,
457 double population,
458 double &scale,
459 double scale_variation,
460 bool /*reverse*/,
461 double mean,
462 double standard_deviation,
463 double ratio,
464 double tilt,
465 double rotation_variation,
466 gint _distrib )
467 {
468 bool did = false;
470 if (SP_IS_BOX3D(item) ) {
471 // convert 3D boxes to ordinary groups before spraying their shapes
472 item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item)));
473 selection->add(item);
474 }
476 double _fid = g_random_double_range(0,1);
477 double angle = g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI );
478 double _scale = g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 );
479 double dr; double dp;
480 random_position( dr, dp, mean, standard_deviation, _distrib );
481 dr=dr*radius;
483 if (mode == SPRAY_MODE_COPY) {
484 Geom::OptRect a = item->getBounds(item->i2doc_affine());
485 if (a) {
486 SPItem *item_copied;
487 if(_fid<=population)
488 {
489 // duplicate
490 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
491 Inkscape::XML::Document* xml_doc = doc->getReprDoc();
492 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
493 Inkscape::XML::Node *parent = old_repr->parent();
494 Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
495 parent->appendChild(copy);
497 SPObject *new_obj = doc->getObjectByRepr(copy);
498 item_copied = (SPItem *) new_obj; //convertion object->item
499 Geom::Point center=item->getCenter();
500 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
501 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
503 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
504 //Move the cursor p
505 Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());
506 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
507 did = true;
508 }
509 }
510 } else if (mode == SPRAY_MODE_SINGLE_PATH) {
512 SPItem *father; //initial Object
513 SPItem *item_copied; //Projected Object
514 SPItem *unionResult; //previous union
515 SPItem *son; //father copy
517 int i=1;
518 for (GSList *items = g_slist_copy((GSList *) selection->itemList());
519 items != NULL;
520 items = items->next) {
522 SPItem *item1 = (SPItem *) items->data;
523 if (i==1) {
524 father=item1;
525 }
526 if (i==2) {
527 unionResult=item1;
528 }
529 i++;
530 }
531 SPDocument *doc = SP_OBJECT_DOCUMENT(father);
532 Inkscape::XML::Document* xml_doc = doc->getReprDoc();
533 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(father);
534 Inkscape::XML::Node *parent = old_repr->parent();
536 Geom::OptRect a = father->getBounds(father->i2doc_affine());
537 if (a) {
538 if (i==2) {
539 Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
540 parent->appendChild(copy1);
541 SPObject *new_obj1 = doc->getObjectByRepr(copy1);
542 son = (SPItem *) new_obj1; // conversion object->item
543 unionResult=son;
544 Inkscape::GC::release(copy1);
545 }
547 if (_fid<=population) { // Rules the population of objects sprayed
548 // duplicates the father
549 Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
550 parent->appendChild(copy2);
551 SPObject *new_obj2 = doc->getObjectByRepr(copy2);
552 item_copied = (SPItem *) new_obj2;
554 // Move around the cursor
555 Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());
557 Geom::Point center=father->getCenter();
558 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
559 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
560 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
561 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
563 // union and duplication
564 selection->clear();
565 selection->add(item_copied);
566 selection->add(unionResult);
567 sp_selected_path_union_skip_undo(selection->desktop());
568 selection->add(father);
569 Inkscape::GC::release(copy2);
570 did = true;
571 }
572 }
573 } else if (mode == SPRAY_MODE_CLONE) {
574 Geom::OptRect a = item->getBounds(item->i2doc_affine());
575 if (a) {
576 if(_fid<=population) {
577 SPItem *item_copied;
578 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
579 Inkscape::XML::Document* xml_doc = doc->getReprDoc();
580 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
581 Inkscape::XML::Node *parent = old_repr->parent();
583 // Creation of the clone
584 Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
585 // Ad the clone to the list of the father's sons
586 parent->appendChild(clone);
587 // Generates the link between father and son attributes
588 clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false);
590 SPObject *clone_object = doc->getObjectByRepr(clone);
591 // conversion object->item
592 item_copied = (SPItem *) clone_object;
593 Geom::Point center=item->getCenter();
594 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
595 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
596 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
597 Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());
598 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
600 Inkscape::GC::release(clone);
602 did = true;
603 }
604 }
605 }
607 return did;
608 }
610 bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse)
611 {
612 Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop);
613 SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop;
616 if (selection->isEmpty()) {
617 return false;
618 }
620 bool did = false;
621 double radius = get_dilate_radius(tc);
625 bool do_fill = false, do_stroke = false, do_opacity = false;
626 guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill);
627 guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke);
628 double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity);
629 if (reverse) {
630 // RGB inversion
631 fill_goal = SP_RGBA32_U_COMPOSE(
632 (255 - SP_RGBA32_R_U(fill_goal)),
633 (255 - SP_RGBA32_G_U(fill_goal)),
634 (255 - SP_RGBA32_B_U(fill_goal)),
635 (255 - SP_RGBA32_A_U(fill_goal)));
636 stroke_goal = SP_RGBA32_U_COMPOSE(
637 (255 - SP_RGBA32_R_U(stroke_goal)),
638 (255 - SP_RGBA32_G_U(stroke_goal)),
639 (255 - SP_RGBA32_B_U(stroke_goal)),
640 (255 - SP_RGBA32_A_U(stroke_goal)));
641 opacity_goal = 1 - opacity_goal;
642 }
644 double path_force = get_path_force(tc);
645 if (radius == 0 || path_force == 0) {
646 return false;
647 }
648 double path_mean = get_path_mean(tc);
649 if (radius == 0 || path_mean == 0) {
650 return false;
651 }
652 double path_standard_deviation = get_path_standard_deviation(tc);
653 if (radius == 0 || path_standard_deviation == 0) {
654 return false;
655 }
656 double move_force = get_move_force(tc);
657 double move_mean = get_move_mean(tc);
658 double move_standard_deviation = get_move_standard_deviation(tc);
661 for (GSList *items = g_slist_copy((GSList *) selection->itemList());
662 items != NULL;
663 items = items->next) {
665 SPItem *item = (SPItem *) items->data;
667 if (is_transform_modes(tc->mode)) {
668 if (sp_spray_recursive (desktop,selection, item, p, vector, tc->mode, radius, move_force, tc->population,tc->scale, tc->scale_variation, reverse, move_mean, move_standard_deviation,tc->ratio,tc->tilt, tc->rotation_variation, tc->distrib))
669 did = true;
670 } else {
671 if (sp_spray_recursive (desktop,selection, item, p, vector, tc->mode, radius, path_force, tc->population,tc->scale, tc->scale_variation, reverse, path_mean, path_standard_deviation,tc->ratio,tc->tilt, tc->rotation_variation, tc->distrib))
672 did = true;
673 }
674 }
676 return did;
677 }
679 void sp_spray_update_area(SPSprayContext *tc)
680 {
681 double radius = get_dilate_radius(tc);
682 Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
683 sp_canvas_item_affine_absolute(tc->dilate_area, (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
684 sp_canvas_item_show(tc->dilate_area);
685 }
687 void sp_spray_switch_mode(SPSprayContext *tc, gint mode, bool with_shift)
688 {
689 // select the button mode
690 SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
691 // need to set explicitly, because the prefs may not have changed by the previous
692 tc->mode = mode;
693 sp_spray_update_cursor (tc, with_shift);
694 }
696 void sp_spray_switch_mode_temporarily(SPSprayContext *tc, gint mode, bool with_shift)
697 {
698 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
699 // Juggling about so that prefs have the old value but tc->mode and the button show new mode:
700 gint now_mode = prefs->getInt("/tools/spray/mode", 0);
701 SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
702 // button has changed prefs, restore
703 prefs->setInt("/tools/spray/mode", now_mode);
704 // changing prefs changed tc->mode, restore back :)
705 tc->mode = mode;
706 sp_spray_update_cursor (tc, with_shift);
707 }
709 gint sp_spray_context_root_handler(SPEventContext *event_context,
710 GdkEvent *event)
711 {
712 SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context);
713 SPDesktop *desktop = event_context->desktop;
715 gint ret = FALSE;
717 switch (event->type) {
718 case GDK_ENTER_NOTIFY:
719 sp_canvas_item_show(tc->dilate_area);
720 break;
721 case GDK_LEAVE_NOTIFY:
722 sp_canvas_item_hide(tc->dilate_area);
723 break;
724 case GDK_BUTTON_PRESS:
725 if (event->button.button == 1 && !event_context->space_panning) {
727 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) {
728 return TRUE;
729 }
731 Geom::Point const motion_w(event->button.x,
732 event->button.y);
733 Geom::Point const motion_dt(desktop->w2d(motion_w));
734 tc->last_push = desktop->dt2doc(motion_dt);
736 sp_spray_extinput(tc, event);
738 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
739 tc->is_drawing = true;
740 tc->is_dilating = true;
741 tc->has_dilated = false;
745 if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning)
747 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
751 tc->has_dilated=true;
753 ret = TRUE;
754 }
755 break;
756 case GDK_MOTION_NOTIFY:
757 {
758 Geom::Point const motion_w(event->motion.x,
759 event->motion.y);
760 Geom::Point motion_dt(desktop->w2d(motion_w));
761 Geom::Point motion_doc(desktop->dt2doc(motion_dt));
762 sp_spray_extinput(tc, event);
764 // draw the dilating cursor
765 double radius = get_dilate_radius(tc);
766 Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
767 sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w)));
768 sp_canvas_item_show(tc->dilate_area);
770 guint num = 0;
771 if (!desktop->selection->isEmpty()) {
772 num = g_slist_length((GSList *) desktop->selection->itemList());
773 }
774 if (num == 0) {
775 tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
776 }
778 // dilating:
779 if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) {
780 sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
781 //tc->last_push = motion_doc;
782 tc->has_dilated = true;
784 // it's slow, so prevent clogging up with events
785 gobble_motion_events(GDK_BUTTON1_MASK);
786 return TRUE;
787 }
789 }
790 break;
791 /*Spray with the scroll*/
792 case GDK_SCROLL:
793 {
794 if (event->scroll.state & GDK_BUTTON1_MASK)
795 {
796 double temp ;
797 temp=tc->population;
798 tc->population=1.0;
799 desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
800 Geom::Point const scroll_w(event->button.x,event->button.y);
801 Geom::Point const scroll_dt = desktop->point();;
802 Geom::Point motion_doc(desktop->dt2doc(scroll_dt));
803 switch (event->scroll.direction)
804 {
805 case GDK_SCROLL_UP:
806 {
807 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
808 {
809 return TRUE;
810 }
811 tc->last_push = desktop->dt2doc(scroll_dt);
812 sp_spray_extinput(tc, event);
813 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
814 tc->is_drawing = true;
815 tc->is_dilating = true;
816 tc->has_dilated = false;
817 if(tc->is_dilating && !event_context->space_panning)
819 sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false);
823 tc->has_dilated=true;
824 tc->population=temp;
826 desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
828 ret = TRUE;
829 }
830 break;
831 case GDK_SCROLL_DOWN:
832 {
833 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
834 {
835 return TRUE;
836 }
837 tc->last_push = desktop->dt2doc(scroll_dt);
838 sp_spray_extinput(tc, event);
839 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
840 tc->is_drawing = true;
841 tc->is_dilating = true;
842 tc->has_dilated = false;
843 if(tc->is_dilating && !event_context->space_panning)
844 sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false);
846 tc->has_dilated=true;
848 ret = TRUE;
851 }
852 break;
853 case GDK_SCROLL_RIGHT:
854 {} break;
855 case GDK_SCROLL_LEFT:
856 {} break;
857 }
858 }
861 break;
863 }
864 case GDK_BUTTON_RELEASE:
865 {
866 Geom::Point const motion_w(event->button.x, event->button.y);
867 Geom::Point const motion_dt(desktop->w2d(motion_w));
869 sp_canvas_end_forced_full_redraws(desktop->canvas);
870 tc->is_drawing = false;
872 if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) {
873 if (!tc->has_dilated) {
874 // if we did not rub, do a light tap
875 tc->pressure = 0.03;
876 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
877 }
878 tc->is_dilating = false;
879 tc->has_dilated = false;
880 switch (tc->mode) {
881 case SPRAY_MODE_COPY:
882 DocumentUndo::done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
883 SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
884 break;
885 case SPRAY_MODE_CLONE:
886 DocumentUndo::done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
887 SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
888 break;
889 case SPRAY_MODE_SINGLE_PATH:
890 DocumentUndo::done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
891 SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
892 break;
893 }
894 }
895 break;
896 }
898 case GDK_KEY_PRESS:
899 switch (get_group0_keyval (&event->key)) {
900 case GDK_j: if (MOD__SHIFT_ONLY) {
901 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
902 ret = TRUE;
903 }
904 case GDK_J: if (MOD__SHIFT_ONLY) {
905 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
906 ret = TRUE;
907 }
909 break;
910 case GDK_m:
911 case GDK_M:
912 case GDK_0:
914 break;
915 case GDK_i:
916 case GDK_I:
917 case GDK_k: if (MOD__SHIFT_ONLY) {
918 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
919 ret = TRUE;
920 }
921 case GDK_K:if (MOD__SHIFT_ONLY) {
922 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
923 ret = TRUE;
924 }
925 break;
927 case GDK_l: if (MOD__SHIFT_ONLY) {
928 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
929 ret = TRUE;
930 }
932 case GDK_L:
933 if (MOD__SHIFT_ONLY) {
934 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
935 ret = TRUE;
936 }
937 break;
938 case GDK_Up:
939 case GDK_KP_Up:
940 if (!MOD__CTRL_ONLY) {
941 tc->scale += 0.05;
943 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
944 ret = TRUE;
945 }
946 break;
947 case GDK_Down:
948 case GDK_KP_Down:
949 if (!MOD__CTRL_ONLY) {
951 tc->scale -= 0.05;
952 if (tc->scale < 0.0)
953 tc->scale = 0.0;
954 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
956 ret = TRUE;
958 }
959 break;
960 case GDK_Right:
961 case GDK_KP_Right:
962 if (!MOD__CTRL_ONLY) {
963 tc->width += 0.01;
964 if (tc->width > 1.0)
965 tc->width = 1.0;
966 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x
967 sp_spray_update_area(tc);
968 ret = TRUE;
969 }
970 break;
971 case GDK_Left:
972 case GDK_KP_Left:
973 if (!MOD__CTRL_ONLY) {
974 tc->width -= 0.01;
975 if (tc->width < 0.01)
976 tc->width = 0.01;
977 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
978 sp_spray_update_area(tc);
979 ret = TRUE;
980 }
981 break;
982 case GDK_Home:
983 case GDK_KP_Home:
984 tc->width = 0.01;
985 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
986 sp_spray_update_area(tc);
987 ret = TRUE;
988 break;
989 case GDK_End:
990 case GDK_KP_End:
991 tc->width = 1.0;
992 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
993 sp_spray_update_area(tc);
994 ret = TRUE;
995 break;
996 case GDK_x:
997 case GDK_X:
998 if (MOD__ALT_ONLY) {
999 desktop->setToolboxFocusTo ("altx-spray");
1000 ret = TRUE;
1001 }
1002 break;
1004 case GDK_Shift_L:
1005 case GDK_Shift_R:
1006 sp_spray_update_cursor(tc, true);
1007 break;
1008 /*Set the scale to 1*/
1009 case GDK_Control_L:
1010 tc->scale=1;
1011 default:
1012 break;
1013 }
1014 break;
1016 case GDK_KEY_RELEASE: {
1017 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1018 switch (get_group0_keyval(&event->key)) {
1019 case GDK_Shift_L:
1020 case GDK_Shift_R:
1021 sp_spray_update_cursor(tc, false);
1022 break;
1023 case GDK_Control_L:
1024 case GDK_Control_R:
1025 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1026 tc->_message_context->clear();
1027 break;
1028 default:
1029 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1030 break;
1031 }
1032 }
1034 default:
1035 break;
1036 }
1038 if (!ret) {
1039 if (((SPEventContextClass *) parent_class)->root_handler) {
1040 ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
1041 }
1042 }
1044 return ret;
1045 }
1047 /*
1048 Local Variables:
1049 mode:c++
1050 c-file-style:"stroustrup"
1051 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1052 indent-tabs-mode:nil
1053 fill-column:99
1054 End:
1055 */
1056 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :