1 #define __SP_SPRAY_CONTEXT_C__
3 /*
4 * Spray Tool
5 *
6 * Authors:
7 * Pierre-Antoine MARC
8 * Pierre CACLIN
9 * Aurel-Aimé MARMION
10 * Julien LERAY
11 * Benoît LAVORATA
12 * Vincent MONTAGNE
13 * Pierre BARBRY-BLOT
14 * Steren GIANNINI (steren.giannini@gmail.com)
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-stop-fns.h"
60 #include "sp-gradient-reference.h"
61 #include "sp-linear-gradient.h"
62 #include "sp-radial-gradient.h"
63 #include "gradient-chemistry.h"
64 #include "sp-text.h"
65 #include "sp-flowtext.h"
66 #include "display/canvas-bpath.h"
67 #include "display/canvas-arena.h"
68 #include "display/curve.h"
69 #include "livarot/Shape.h"
70 #include <2geom/isnan.h>
71 #include <2geom/transforms.h>
72 #include "preferences.h"
73 #include "style.h"
74 #include "box3d.h"
75 #include "sp-item-transform.h"
76 #include "filter-chemistry.h"
77 #include "sp-gaussian-blur-fns.h"
78 #include "sp-gaussian-blur.h"
80 #include "spray-context.h"
81 #include "ui/dialog/dialog-manager.h"
82 #include "helper/action.h"
84 #include <iostream>
85 using namespace std;
88 #define DDC_RED_RGBA 0xff0000ff
90 #define DYNA_MIN_WIDTH 1.0e-6
92 static void sp_spray_context_class_init(SPSprayContextClass *klass);
93 static void sp_spray_context_init(SPSprayContext *ddc);
94 static void sp_spray_context_dispose(GObject *object);
96 static void sp_spray_context_setup(SPEventContext *ec);
97 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val);
98 static gint sp_spray_context_root_handler(SPEventContext *ec, GdkEvent *event);
100 static SPEventContextClass *parent_class = 0;
103 /**
104 * This function returns pseudo-random numbers from a normal distribution
105 * @param mu : mean
106 * @param sigma : standard deviation ( > 0 )
107 */
108 inline double NormalDistribution(double mu,double sigma)
109 {
110 // use Box Muller's algorithm
111 return mu + sigma * sqrt( -2.0 * log(g_random_double_range(0, 1)) ) * cos( 2.0*M_PI*g_random_double_range(0, 1) );
112 }
115 GtkType sp_spray_context_get_type(void)
116 {
117 static GType type = 0;
118 if (!type) {
119 GTypeInfo info = {
120 sizeof(SPSprayContextClass),
121 NULL, NULL,
122 (GClassInitFunc) sp_spray_context_class_init,
123 NULL, NULL,
124 sizeof(SPSprayContext),
125 4,
126 (GInstanceInitFunc) sp_spray_context_init,
127 NULL, /* value_table */
128 };
129 type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSprayContext", &info, (GTypeFlags)0);
130 }
131 return type;
132 }
134 static void sp_spray_context_class_init(SPSprayContextClass *klass)
135 {
136 GObjectClass *object_class = (GObjectClass *) klass;
137 SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
139 parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
141 object_class->dispose = sp_spray_context_dispose;
143 event_context_class->setup = sp_spray_context_setup;
144 event_context_class->set = sp_spray_context_set;
145 event_context_class->root_handler = sp_spray_context_root_handler;
146 }
148 /* Method to rotate items */
149 void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom::Rotate const &rotation)
150 {
151 Geom::Point center = c;
152 Geom::Translate const s(c);
153 Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s);
154 // Rotate item.
155 sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * (Geom::Matrix)affine);
156 // Use each item's own transform writer, consistent with sp_selection_apply_affine()
157 sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform);
158 // Restore the center position (it's changed because the bbox center changed)
159 if (item->isCenterSet()) {
160 item->setCenter(c);
161 item->updateRepr();
162 }
163 }
165 /* Method to scale items */
166 void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale const &scale)
167 {
168 Geom::Translate const s(c);
169 sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * s.inverse() * scale * s );
170 sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform);
171 }
173 static void sp_spray_context_init(SPSprayContext *tc)
174 {
175 SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
177 event_context->cursor_shape = cursor_spray_xpm;
178 event_context->hot_x = 4;
179 event_context->hot_y = 4;
181 /* attributes */
182 tc->dragging = FALSE;
183 tc->distrib = 1;
184 tc->width = 0.2;
185 tc->force = 0.2;
186 tc->ratio = 0;
187 tc->tilt=0;
188 tc->mean = 0.2;
189 tc->rotation_variation=0;
190 tc->standard_deviation=0.2;
191 tc->scale=1;
192 tc->scale_variation = 1;
193 tc->pressure = TC_DEFAULT_PRESSURE;
195 tc->is_dilating = false;
196 tc->has_dilated = false;
198 tc->do_h = true;
199 tc->do_s = true;
200 tc->do_l = true;
201 tc->do_o = false;
203 new (&tc->style_set_connection) sigc::connection();
204 }
206 static void sp_spray_context_dispose(GObject *object)
207 {
208 SPSprayContext *tc = SP_SPRAY_CONTEXT(object);
210 tc->style_set_connection.disconnect();
211 tc->style_set_connection.~connection();
213 if (tc->dilate_area) {
214 gtk_object_destroy(GTK_OBJECT(tc->dilate_area));
215 tc->dilate_area = NULL;
216 }
218 if (tc->_message_context) {
219 delete tc->_message_context;
220 }
222 G_OBJECT_CLASS(parent_class)->dispose(object);
223 }
225 bool is_transform_modes(gint mode)
226 {
227 return (mode == SPRAY_MODE_COPY ||
228 mode == SPRAY_MODE_CLONE ||
229 mode == SPRAY_MODE_SINGLE_PATH ||
230 mode == SPRAY_OPTION);
231 }
233 void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/)
234 {
235 SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
236 SPDesktop *desktop = event_context->desktop;
238 guint num = 0;
239 gchar *sel_message = NULL;
240 if (!desktop->selection->isEmpty()) {
241 num = g_slist_length((GSList *) desktop->selection->itemList());
242 sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
243 } else {
244 sel_message = g_strdup_printf(_("<b>Nothing</b> selected"));
245 }
248 switch (tc->mode) {
249 case SPRAY_MODE_COPY:
250 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>copies</b> of the initial selection"), sel_message);
251 break;
252 case SPRAY_MODE_CLONE:
253 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>clones</b> of the initial selection"), sel_message);
254 break;
255 case SPRAY_MODE_SINGLE_PATH:
256 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);
257 break;
258 default:
259 break;
260 }
261 sp_event_context_update_cursor(event_context);
262 g_free(sel_message);
263 }
265 static void sp_spray_context_setup(SPEventContext *ec)
266 {
267 SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
269 if (((SPEventContextClass *) parent_class)->setup)
270 ((SPEventContextClass *) parent_class)->setup(ec);
272 {
273 /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
274 SPCurve *c = new SPCurve();
275 const double C1 = 0.552;
276 c->moveto(-1,0);
277 c->curveto(-1, C1, -C1, 1, 0, 1 );
278 c->curveto(C1, 1, 1, C1, 1, 0 );
279 c->curveto(1, -C1, C1, -1, 0, -1 );
280 c->curveto(-C1, -1, -1, -C1, -1, 0 );
281 c->closepath();
282 tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c);
283 c->unref();
284 sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0);
285 sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
286 sp_canvas_item_hide(tc->dilate_area);
287 }
289 tc->is_drawing = false;
291 tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
293 sp_event_context_read(ec, "distrib");
294 sp_event_context_read(ec, "width");
295 sp_event_context_read(ec, "ratio");
296 sp_event_context_read(ec, "tilt");
297 sp_event_context_read(ec, "rotation_variation");
298 sp_event_context_read(ec, "scale_variation");
299 sp_event_context_read(ec, "mode");
300 sp_event_context_read(ec, "population");
301 sp_event_context_read(ec, "force");
302 sp_event_context_read(ec, "mean");
303 sp_event_context_read(ec, "standard_deviation");
304 sp_event_context_read(ec, "usepressure");
305 sp_event_context_read(ec, "Scale");
306 sp_event_context_read(ec, "doh");
307 sp_event_context_read(ec, "dol");
308 sp_event_context_read(ec, "dos");
309 sp_event_context_read(ec, "doo");
311 ;
313 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
314 if (prefs->getBool("/tools/spray/selcue")) {
315 ec->enableSelectionCue();
316 }
318 if (prefs->getBool("/tools/spray/gradientdrag")) {
319 ec->enableGrDrag();
320 }
321 }
323 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
324 {
325 SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
326 Glib::ustring path = val->getEntryName();
328 if (path == "width") {
329 tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100);
330 } else if (path == "mode") {
331 tc->mode = val->getInt();
332 sp_spray_update_cursor(tc, false);
333 } else if (path == "distribution") {
334 tc->distrib = val->getInt(1);
335 } else if (path == "population") {
336 tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100);
337 } else if (path == "tilt") {
338 tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0);
339 } else if (path == "ratio") {
340 tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9);
341 } else if (path == "force") {
342 tc->force = CLAMP(val->getDouble(1.0), 0, 1.0);
343 } else if (path == "rotation_variation") {
344 tc->rotation_variation = CLAMP(val->getDouble(0.0), 0, 100.0);
345 } else if (path == "scale_variation") {
346 tc->scale_variation = CLAMP(val->getDouble(1.0), 0, 100.0);
347 } else if (path == "mean") {
348 tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100);
349 } else if (path == "standard_deviation") {
350 tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100);
351 } else if (path == "usepressure") {
352 tc->usepressure = val->getBool();
353 } else if (path == "doh") {
354 tc->do_h = val->getBool();
355 } else if (path == "dos") {
356 tc->do_s = val->getBool();
357 } else if (path == "dol") {
358 tc->do_l = val->getBool();
359 } else if (path == "doo") {
360 tc->do_o = val->getBool();
361 }
362 }
364 static void sp_spray_extinput(SPSprayContext *tc, GdkEvent *event)
365 {
366 if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure))
367 tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
368 else
369 tc->pressure = TC_DEFAULT_PRESSURE;
370 }
372 double get_dilate_radius(SPSprayContext *tc)
373 {
375 return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom();
378 }
380 double get_path_force(SPSprayContext *tc)
381 {
382 double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
383 /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom());
384 if (force > 3) {
385 force += 4 * (force - 3);
386 }
387 return force * tc->force;
388 }
390 double get_path_mean(SPSprayContext *tc)
391 {
392 return tc->mean;
393 }
395 double get_path_standard_deviation(SPSprayContext *tc)
396 {
397 return tc->standard_deviation;
398 }
400 double get_move_force(SPSprayContext *tc)
401 {
402 double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
403 return force * tc->force;
404 }
406 double get_move_mean(SPSprayContext *tc)
407 {
408 return tc->mean;
409 }
411 double get_move_standard_deviation(SPSprayContext *tc)
412 {
413 return tc->standard_deviation;
414 }
416 /**
417 * Method to handle the distribution of the items
418 * @param[out] radius : radius of the position of the sprayed object
419 * @param[out] angle : angle of the position of the sprayed object
420 * @param[in] a : mean
421 * @param[in] s : standard deviation
422 * @param[in] choice :
424 */
425 void random_position( double &radius, double &angle, double &a, double &s, int choice)
426 {
427 // angle is taken from an uniform distribution
428 angle = g_random_double_range(0, M_PI*2.0);
430 // radius is taken from a Normal Distribution
431 double radius_temp =-1;
432 while(!((radius_temp>=0)&&(radius_temp<=1)))
433 {
434 radius_temp = NormalDistribution( a, s );
435 }
436 // Because we are in polar coordinates, a special treatment has to be done to the radius.
437 // Otherwise, positions taken from an uniform repartition on radius and angle will not seam to
438 // be uniformily distributed on the disk (more at the center and less at the boundary).
439 // We counter this effect with a 0.5 exponent. This is empiric.
440 radius = pow( radius_temp, 0.5);
442 }
448 bool sp_spray_recursive(SPDesktop *desktop,
449 Inkscape::Selection *selection,
450 SPItem *item,
451 Geom::Point p,
452 Geom::Point /*vector*/,
453 gint mode,
454 double radius,
455 double /*force*/,
456 double population,
457 double &scale,
458 double scale_variation,
459 bool /*reverse*/,
460 double mean,
461 double standard_deviation,
462 double ratio,
463 double tilt,
464 double rotation_variation,
465 gint _distrib )
466 {
467 bool did = false;
469 if (SP_IS_BOX3D(item) ) {
470 // convert 3D boxes to ordinary groups before spraying their shapes
471 item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item)));
472 selection->add(item);
473 }
475 double _fid = g_random_double_range(0,1);
476 double angle = g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI );
477 double _scale = g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 );
478 double dr; double dp;
479 random_position( dr, dp, mean, standard_deviation, _distrib );
480 dr=dr*radius;
482 if (mode == SPRAY_MODE_COPY) {
483 Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
484 if (a) {
485 SPItem *item_copied;
486 if(_fid<=population)
487 {
488 // duplicate
489 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
490 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
491 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
492 Inkscape::XML::Node *parent = old_repr->parent();
493 Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
494 parent->appendChild(copy);
496 SPObject *new_obj = doc->getObjectByRepr(copy);
497 item_copied = (SPItem *) new_obj; //convertion object->item
498 Geom::Point center=item->getCenter();
499 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
500 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
502 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
503 //Move the cursor p
504 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());
505 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
506 did = true;
507 }
508 }
509 } else if (mode == SPRAY_MODE_SINGLE_PATH) {
511 SPItem *father; //initial Object
512 SPItem *item_copied; //Projected Object
513 SPItem *unionResult; //previous union
514 SPItem *son; //father copy
516 int i=1;
517 for (GSList *items = g_slist_copy((GSList *) selection->itemList());
518 items != NULL;
519 items = items->next) {
521 SPItem *item1 = (SPItem *) items->data;
522 if (i==1) {
523 father=item1;
524 }
525 if (i==2) {
526 unionResult=item1;
527 }
528 i++;
529 }
530 SPDocument *doc = SP_OBJECT_DOCUMENT(father);
531 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
532 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(father);
533 Inkscape::XML::Node *parent = old_repr->parent();
535 Geom::OptRect a = father->getBounds(sp_item_i2doc_affine(father));
536 if (a) {
537 if (i==2) {
538 Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
539 parent->appendChild(copy1);
540 SPObject *new_obj1 = doc->getObjectByRepr(copy1);
541 son = (SPItem *) new_obj1; // conversion object->item
542 unionResult=son;
543 Inkscape::GC::release(copy1);
544 }
546 if (_fid<=population) { // Rules the population of objects sprayed
547 // duplicates the father
548 Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
549 parent->appendChild(copy2);
550 SPObject *new_obj2 = doc->getObjectByRepr(copy2);
551 item_copied = (SPItem *) new_obj2;
553 // Move around the cursor
554 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());
556 Geom::Point center=father->getCenter();
557 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
558 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
559 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
560 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
562 // union and duplication
563 selection->clear();
564 selection->add(item_copied);
565 selection->add(unionResult);
566 sp_selected_path_union(selection->desktop());
567 selection->add(father);
568 Inkscape::GC::release(copy2);
569 did = true;
570 }
571 }
572 } else if (mode == SPRAY_MODE_CLONE) {
573 Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
574 if (a) {
575 if(_fid<=population) {
576 SPItem *item_copied;
577 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
578 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
579 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
580 Inkscape::XML::Node *parent = old_repr->parent();
582 // Creation of the clone
583 Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
584 // Ad the clone to the list of the father's sons
585 parent->appendChild(clone);
586 // Generates the link between father and son attributes
587 clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false);
589 SPObject *clone_object = doc->getObjectByRepr(clone);
590 // conversion object->item
591 item_copied = (SPItem *) clone_object;
592 Geom::Point center=item->getCenter();
593 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
594 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
595 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
596 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());
597 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
599 Inkscape::GC::release(clone);
601 did = true;
602 }
603 }
604 }
606 return did;
607 }
609 bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse)
610 {
611 Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop);
612 SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop;
615 if (selection->isEmpty()) {
616 return false;
617 }
619 bool did = false;
620 double radius = get_dilate_radius(tc);
624 bool do_fill = false, do_stroke = false, do_opacity = false;
625 guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill);
626 guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke);
627 double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity);
628 if (reverse) {
629 // RGB inversion
630 fill_goal = SP_RGBA32_U_COMPOSE(
631 (255 - SP_RGBA32_R_U(fill_goal)),
632 (255 - SP_RGBA32_G_U(fill_goal)),
633 (255 - SP_RGBA32_B_U(fill_goal)),
634 (255 - SP_RGBA32_A_U(fill_goal)));
635 stroke_goal = SP_RGBA32_U_COMPOSE(
636 (255 - SP_RGBA32_R_U(stroke_goal)),
637 (255 - SP_RGBA32_G_U(stroke_goal)),
638 (255 - SP_RGBA32_B_U(stroke_goal)),
639 (255 - SP_RGBA32_A_U(stroke_goal)));
640 opacity_goal = 1 - opacity_goal;
641 }
643 double path_force = get_path_force(tc);
644 if (radius == 0 || path_force == 0) {
645 return false;
646 }
647 double path_mean = get_path_mean(tc);
648 if (radius == 0 || path_mean == 0) {
649 return false;
650 }
651 double path_standard_deviation = get_path_standard_deviation(tc);
652 if (radius == 0 || path_standard_deviation == 0) {
653 return false;
654 }
655 double move_force = get_move_force(tc);
656 double move_mean = get_move_mean(tc);
657 double move_standard_deviation = get_move_standard_deviation(tc);
660 for (GSList *items = g_slist_copy((GSList *) selection->itemList());
661 items != NULL;
662 items = items->next) {
664 SPItem *item = (SPItem *) items->data;
666 if (is_transform_modes(tc->mode)) {
667 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))
668 did = true;
669 } else {
670 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))
671 did = true;
672 }
673 }
675 return did;
676 }
678 void sp_spray_update_area(SPSprayContext *tc)
679 {
680 double radius = get_dilate_radius(tc);
681 Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
682 sp_canvas_item_affine_absolute(tc->dilate_area, (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
683 sp_canvas_item_show(tc->dilate_area);
684 }
686 void sp_spray_switch_mode(SPSprayContext *tc, gint mode, bool with_shift)
687 {
688 // select the button mode
689 SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
690 // need to set explicitly, because the prefs may not have changed by the previous
691 tc->mode = mode;
692 sp_spray_update_cursor (tc, with_shift);
693 }
695 void sp_spray_switch_mode_temporarily(SPSprayContext *tc, gint mode, bool with_shift)
696 {
697 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
698 // Juggling about so that prefs have the old value but tc->mode and the button show new mode:
699 gint now_mode = prefs->getInt("/tools/spray/mode", 0);
700 SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
701 // button has changed prefs, restore
702 prefs->setInt("/tools/spray/mode", now_mode);
703 // changing prefs changed tc->mode, restore back :)
704 tc->mode = mode;
705 sp_spray_update_cursor (tc, with_shift);
706 }
708 gint sp_spray_context_root_handler(SPEventContext *event_context,
709 GdkEvent *event)
710 {
711 SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context);
712 SPDesktop *desktop = event_context->desktop;
714 gint ret = FALSE;
716 switch (event->type) {
717 case GDK_ENTER_NOTIFY:
718 sp_canvas_item_show(tc->dilate_area);
719 break;
720 case GDK_LEAVE_NOTIFY:
721 sp_canvas_item_hide(tc->dilate_area);
722 break;
723 case GDK_BUTTON_PRESS:
724 if (event->button.button == 1 && !event_context->space_panning) {
726 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) {
727 return TRUE;
728 }
730 Geom::Point const motion_w(event->button.x,
731 event->button.y);
732 Geom::Point const motion_dt(desktop->w2d(motion_w));
733 tc->last_push = desktop->dt2doc(motion_dt);
735 sp_spray_extinput(tc, event);
737 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
738 tc->is_drawing = true;
739 tc->is_dilating = true;
740 tc->has_dilated = false;
744 if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning)
746 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
750 tc->has_dilated=true;
752 ret = TRUE;
753 }
754 break;
755 case GDK_MOTION_NOTIFY:
756 {
757 Geom::Point const motion_w(event->motion.x,
758 event->motion.y);
759 Geom::Point motion_dt(desktop->w2d(motion_w));
760 Geom::Point motion_doc(desktop->dt2doc(motion_dt));
761 sp_spray_extinput(tc, event);
763 // draw the dilating cursor
764 double radius = get_dilate_radius(tc);
765 Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
766 sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w)));
767 sp_canvas_item_show(tc->dilate_area);
769 guint num = 0;
770 if (!desktop->selection->isEmpty()) {
771 num = g_slist_length((GSList *) desktop->selection->itemList());
772 }
773 if (num == 0) {
774 tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
775 }
777 // dilating:
778 if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) {
779 sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
780 //tc->last_push = motion_doc;
781 tc->has_dilated = true;
783 // it's slow, so prevent clogging up with events
784 gobble_motion_events(GDK_BUTTON1_MASK);
785 return TRUE;
786 }
788 }
789 break;
790 /*Spray with the scroll*/
791 case GDK_SCROLL:
792 {
793 if (event->scroll.state & GDK_BUTTON1_MASK)
794 {
795 double temp ;
796 temp=tc->population;
797 tc->population=1.0;
798 desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
799 Geom::Point const scroll_w(event->button.x,event->button.y);
800 Geom::Point const scroll_dt = desktop->point();;
801 Geom::Point motion_doc(desktop->dt2doc(scroll_dt));
802 switch (event->scroll.direction)
803 {
804 case GDK_SCROLL_UP:
805 {
806 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
807 {
808 return TRUE;
809 }
810 tc->last_push = desktop->dt2doc(scroll_dt);
811 sp_spray_extinput(tc, event);
812 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
813 tc->is_drawing = true;
814 tc->is_dilating = true;
815 tc->has_dilated = false;
816 if(tc->is_dilating && !event_context->space_panning)
818 sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false);
822 tc->has_dilated=true;
823 tc->population=temp;
825 desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
827 ret = TRUE;
828 }
829 break;
830 case GDK_SCROLL_DOWN:
831 {
832 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
833 {
834 return TRUE;
835 }
836 tc->last_push = desktop->dt2doc(scroll_dt);
837 sp_spray_extinput(tc, event);
838 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
839 tc->is_drawing = true;
840 tc->is_dilating = true;
841 tc->has_dilated = false;
842 if(tc->is_dilating && !event_context->space_panning)
843 sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false);
845 tc->has_dilated=true;
847 ret = TRUE;
850 }
851 break;
852 case GDK_SCROLL_RIGHT:
853 {} break;
854 case GDK_SCROLL_LEFT:
855 {} break;
856 }
857 }
860 break;
862 }
863 case GDK_BUTTON_RELEASE:
864 {
865 Geom::Point const motion_w(event->button.x, event->button.y);
866 Geom::Point const motion_dt(desktop->w2d(motion_w));
868 sp_canvas_end_forced_full_redraws(desktop->canvas);
869 tc->is_drawing = false;
871 if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) {
872 if (!tc->has_dilated) {
873 // if we did not rub, do a light tap
874 tc->pressure = 0.03;
875 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
876 }
877 tc->is_dilating = false;
878 tc->has_dilated = false;
879 switch (tc->mode) {
880 case SPRAY_MODE_COPY:
881 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
882 SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
883 break;
884 case SPRAY_MODE_CLONE:
885 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
886 SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
887 break;
888 case SPRAY_MODE_SINGLE_PATH:
889 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
890 SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
891 break;
892 }
893 }
894 break;
895 }
897 case GDK_KEY_PRESS:
898 switch (get_group0_keyval (&event->key)) {
899 case GDK_j: if (MOD__SHIFT_ONLY) {
900 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
901 ret = TRUE;
902 }
903 case GDK_J: if (MOD__SHIFT_ONLY) {
904 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
905 ret = TRUE;
906 }
908 break;
909 case GDK_m:
910 case GDK_M:
911 case GDK_0:
913 break;
914 case GDK_i:
915 case GDK_I:
916 case GDK_k: if (MOD__SHIFT_ONLY) {
917 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
918 ret = TRUE;
919 }
920 case GDK_K:if (MOD__SHIFT_ONLY) {
921 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
922 ret = TRUE;
923 }
924 break;
926 case GDK_l: if (MOD__SHIFT_ONLY) {
927 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
928 ret = TRUE;
929 }
931 case GDK_L:
932 if (MOD__SHIFT_ONLY) {
933 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
934 ret = TRUE;
935 }
936 break;
937 case GDK_Up:
938 case GDK_KP_Up:
939 if (!MOD__CTRL_ONLY) {
940 tc->scale += 0.05;
942 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
943 ret = TRUE;
944 }
945 break;
946 case GDK_Down:
947 case GDK_KP_Down:
948 if (!MOD__CTRL_ONLY) {
950 tc->scale -= 0.05;
951 if (tc->scale < 0.0)
952 tc->scale = 0.0;
953 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
955 ret = TRUE;
957 }
958 break;
959 case GDK_Right:
960 case GDK_KP_Right:
961 if (!MOD__CTRL_ONLY) {
962 tc->width += 0.01;
963 if (tc->width > 1.0)
964 tc->width = 1.0;
965 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x
966 sp_spray_update_area(tc);
967 ret = TRUE;
968 }
969 break;
970 case GDK_Left:
971 case GDK_KP_Left:
972 if (!MOD__CTRL_ONLY) {
973 tc->width -= 0.01;
974 if (tc->width < 0.01)
975 tc->width = 0.01;
976 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
977 sp_spray_update_area(tc);
978 ret = TRUE;
979 }
980 break;
981 case GDK_Home:
982 case GDK_KP_Home:
983 tc->width = 0.01;
984 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
985 sp_spray_update_area(tc);
986 ret = TRUE;
987 break;
988 case GDK_End:
989 case GDK_KP_End:
990 tc->width = 1.0;
991 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
992 sp_spray_update_area(tc);
993 ret = TRUE;
994 break;
995 case GDK_x:
996 case GDK_X:
997 if (MOD__ALT_ONLY) {
998 desktop->setToolboxFocusTo ("altx-spray");
999 ret = TRUE;
1000 }
1001 break;
1003 case GDK_Shift_L:
1004 case GDK_Shift_R:
1005 sp_spray_update_cursor(tc, true);
1006 break;
1007 /*Set the scale to 1*/
1008 case GDK_Control_L:
1009 tc->scale=1;
1010 default:
1011 break;
1012 }
1013 break;
1015 case GDK_KEY_RELEASE: {
1016 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1017 switch (get_group0_keyval(&event->key)) {
1018 case GDK_Shift_L:
1019 case GDK_Shift_R:
1020 sp_spray_update_cursor(tc, false);
1021 break;
1022 case GDK_Control_L:
1023 case GDK_Control_R:
1024 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1025 tc->_message_context->clear();
1026 break;
1027 default:
1028 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1029 break;
1030 }
1031 }
1033 default:
1034 break;
1035 }
1037 if (!ret) {
1038 if (((SPEventContextClass *) parent_class)->root_handler) {
1039 ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
1040 }
1041 }
1043 return ret;
1044 }
1046 /*
1047 Local Variables:
1048 mode:c++
1049 c-file-style:"stroustrup"
1050 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1051 indent-tabs-mode:nil
1052 fill-column:99
1053 End:
1054 */
1055 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :