Code

A simple layout document as to what, why and how is cppification.
[inkscape.git] / src / spray-context.cpp
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-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>
84 using namespace std;
87 #define DDC_RED_RGBA 0xff0000ff
89 #define DYNA_MIN_WIDTH 1.0e-6
91 static void sp_spray_context_class_init(SPSprayContextClass *klass);
92 static void sp_spray_context_init(SPSprayContext *ddc);
93 static void sp_spray_context_dispose(GObject *object);
95 static void sp_spray_context_setup(SPEventContext *ec);
96 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val);
97 static gint sp_spray_context_root_handler(SPEventContext *ec, GdkEvent *event);
99 static SPEventContextClass *parent_class = 0;
102 /**
103  * This function returns pseudo-random numbers from a normal distribution
104  * @param mu : mean
105  * @param sigma : standard deviation ( > 0 )
106  */
107 inline double NormalDistribution(double mu,double sigma)
109   // use Box Muller's algorithm
110   return mu + sigma * sqrt( -2.0 * log(g_random_double_range(0, 1)) ) * cos( 2.0*M_PI*g_random_double_range(0, 1) );
114 GtkType sp_spray_context_get_type(void)
116     static GType type = 0;
117     if (!type) {
118         GTypeInfo info = {
119             sizeof(SPSprayContextClass),
120             NULL, NULL,
121             (GClassInitFunc) sp_spray_context_class_init,
122             NULL, NULL,
123             sizeof(SPSprayContext),
124             4,
125             (GInstanceInitFunc) sp_spray_context_init,
126             NULL,   /* value_table */
127         };
128         type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSprayContext", &info, (GTypeFlags)0);
129     }
130     return type;
133 static void sp_spray_context_class_init(SPSprayContextClass *klass)
135     GObjectClass *object_class = (GObjectClass *) klass;
136     SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
138     parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
140     object_class->dispose = sp_spray_context_dispose;
142     event_context_class->setup = sp_spray_context_setup;
143     event_context_class->set = sp_spray_context_set;
144     event_context_class->root_handler = sp_spray_context_root_handler;
147 /* Method to rotate items */
148 void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom::Rotate const &rotation)
150     Geom::Point center = c;
151     Geom::Translate const s(c);
152     Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s);
153     // Rotate item.
154     item->set_i2d_affine(item->i2d_affine() * (Geom::Matrix)affine);
155     // Use each item's own transform writer, consistent with sp_selection_apply_affine()
156     item->doWriteTransform(SP_OBJECT_REPR(item), item->transform);
157     // Restore the center position (it's changed because the bbox center changed)
158     if (item->isCenterSet()) {
159         item->setCenter(c);
160         item->updateRepr();
161     }
164 /* Method to scale items */
165 void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale  const &scale)
167     Geom::Translate const s(c);
168     item->set_i2d_affine(item->i2d_affine() * s.inverse() * scale * s  );
169     item->doWriteTransform(SP_OBJECT_REPR(item), item->transform);
172 static void sp_spray_context_init(SPSprayContext *tc)
174     SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
176     event_context->cursor_shape = cursor_spray_xpm;
177     event_context->hot_x = 4;
178     event_context->hot_y = 4;
180     /* attributes */
181     tc->dragging = FALSE;
182     tc->distrib = 1;
183     tc->width = 0.2;
184     tc->force = 0.2;
185     tc->ratio = 0;
186     tc->tilt=0;
187     tc->mean = 0.2;
188     tc->rotation_variation=0;
189     tc->standard_deviation=0.2;
190     tc->scale=1;
191     tc->scale_variation = 1;
192     tc->pressure = TC_DEFAULT_PRESSURE;
194     tc->is_dilating = false;
195     tc->has_dilated = false;
197     tc->do_h = true;
198     tc->do_s = true;
199     tc->do_l = true;
200     tc->do_o = false;
202     new (&tc->style_set_connection) sigc::connection();
205 static void sp_spray_context_dispose(GObject *object)
207     SPSprayContext *tc = SP_SPRAY_CONTEXT(object);
209     tc->style_set_connection.disconnect();
210     tc->style_set_connection.~connection();
212     if (tc->dilate_area) {
213         gtk_object_destroy(GTK_OBJECT(tc->dilate_area));
214         tc->dilate_area = NULL;
215     }
217     if (tc->_message_context) {
218         delete tc->_message_context;
219     }
221     G_OBJECT_CLASS(parent_class)->dispose(object);
224 bool is_transform_modes(gint mode)
226     return (mode == SPRAY_MODE_COPY ||
227             mode == SPRAY_MODE_CLONE ||
228             mode == SPRAY_MODE_SINGLE_PATH ||
229             mode == SPRAY_OPTION);
232 void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/)
234    SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
235    SPDesktop *desktop = event_context->desktop;
237     guint num = 0;
238     gchar *sel_message = NULL;
239     if (!desktop->selection->isEmpty()) {
240         num = g_slist_length((GSList *) desktop->selection->itemList());
241         sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
242     } else {
243         sel_message = g_strdup_printf(_("<b>Nothing</b> selected"));
244     }
247    switch (tc->mode) {
248        case SPRAY_MODE_COPY:
249            tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>copies</b> of the initial selection"), sel_message);
250            break;
251        case SPRAY_MODE_CLONE:
252            tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>clones</b> of the initial selection"), sel_message);
253            break;
254        case SPRAY_MODE_SINGLE_PATH:
255            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);
256            break;
257        default:
258            break;
259    }
260    sp_event_context_update_cursor(event_context);
261    g_free(sel_message);
264 static void sp_spray_context_setup(SPEventContext *ec)
266     SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
268     if (((SPEventContextClass *) parent_class)->setup)
269         ((SPEventContextClass *) parent_class)->setup(ec);
271     {
272         /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
273         SPCurve *c = new SPCurve();
274         const double C1 = 0.552;
275         c->moveto(-1,0);
276         c->curveto(-1, C1, -C1, 1, 0, 1 );
277         c->curveto(C1, 1, 1, C1, 1, 0 );
278         c->curveto(1, -C1, C1, -1, 0, -1 );
279         c->curveto(-C1, -1, -1, -C1, -1, 0 );
280         c->closepath();
281         tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c);
282         c->unref();
283         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0);
284         sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
285         sp_canvas_item_hide(tc->dilate_area);
286     }
288     tc->is_drawing = false;
290     tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
292     sp_event_context_read(ec, "distrib");
293     sp_event_context_read(ec, "width");
294     sp_event_context_read(ec, "ratio");
295     sp_event_context_read(ec, "tilt");
296     sp_event_context_read(ec, "rotation_variation");
297     sp_event_context_read(ec, "scale_variation");
298     sp_event_context_read(ec, "mode");
299     sp_event_context_read(ec, "population");
300     sp_event_context_read(ec, "force");
301     sp_event_context_read(ec, "mean");
302     sp_event_context_read(ec, "standard_deviation");
303     sp_event_context_read(ec, "usepressure");
304     sp_event_context_read(ec, "Scale");
305     sp_event_context_read(ec, "doh");
306     sp_event_context_read(ec, "dol");
307     sp_event_context_read(ec, "dos");
308     sp_event_context_read(ec, "doo");
310     ;
312     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
313     if (prefs->getBool("/tools/spray/selcue")) {
314         ec->enableSelectionCue();
315     }
317     if (prefs->getBool("/tools/spray/gradientdrag")) {
318         ec->enableGrDrag();
319     }
322 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
324     SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
325     Glib::ustring path = val->getEntryName();
327     if (path == "width") {
328         tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100);
329     } else if (path == "mode") {
330         tc->mode = val->getInt();
331         sp_spray_update_cursor(tc, false);
332     } else if (path == "distribution") {
333         tc->distrib = val->getInt(1);
334     } else if (path == "population") {
335         tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100);
336      } else if (path == "tilt") {
337         tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0);
338      } else if (path == "ratio") {
339         tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9);
340     } else if (path == "force") {
341         tc->force = CLAMP(val->getDouble(1.0), 0, 1.0);
342     } else if (path == "rotation_variation") {
343         tc->rotation_variation = CLAMP(val->getDouble(0.0), 0, 100.0);
344     } else if (path == "scale_variation") {
345         tc->scale_variation = CLAMP(val->getDouble(1.0), 0, 100.0);
346     } else if (path == "mean") {
347         tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100);
348     } else if (path == "standard_deviation") {
349         tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100);
350     } else if (path == "usepressure") {
351         tc->usepressure = val->getBool();
352     } else if (path == "doh") {
353         tc->do_h = val->getBool();
354     } else if (path == "dos") {
355         tc->do_s = val->getBool();
356     } else if (path == "dol") {
357         tc->do_l = val->getBool();
358     } else if (path == "doo") {
359         tc->do_o = val->getBool();
360     }
363 static void sp_spray_extinput(SPSprayContext *tc, GdkEvent *event)
365     if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure))
366         tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
367     else
368         tc->pressure = TC_DEFAULT_PRESSURE;
371 double get_dilate_radius(SPSprayContext *tc)
374     return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom();
379 double get_path_force(SPSprayContext *tc)
381     double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
382         /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom());
383     if (force > 3) {
384         force += 4 * (force - 3);
385     }
386     return force * tc->force;
389 double get_path_mean(SPSprayContext *tc)
391     return tc->mean;
394 double get_path_standard_deviation(SPSprayContext *tc)
396     return tc->standard_deviation;
399 double get_move_force(SPSprayContext *tc)
401     double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
402     return force * tc->force;
405 double get_move_mean(SPSprayContext *tc)
407     return tc->mean;
410 double get_move_standard_deviation(SPSprayContext *tc)
412     return tc->standard_deviation;
415 /**
416  * Method to handle the distribution of the items
417  * @param[out]  radius : radius of the position of the sprayed object
418  * @param[out]  angle : angle of the position of the sprayed object
419  * @param[in]   a : mean
420  * @param[in]   s : standard deviation
421  * @param[in]   choice : 
423  */
424 void random_position( double &radius, double &angle, double &a, double &s, int /*choice*/)
426     // angle is taken from an uniform distribution
427     angle = g_random_double_range(0, M_PI*2.0);
429     // radius is taken from a Normal Distribution
430     double radius_temp =-1;
431     while(!((radius_temp>=0)&&(radius_temp<=1)))
432     {
433         radius_temp = NormalDistribution( a, s );
434     }
435     // Because we are in polar coordinates, a special treatment has to be done to the radius.
436     // Otherwise, positions taken from an uniform repartition on radius and angle will not seam to 
437     // be uniformily distributed on the disk (more at the center and less at the boundary).
438     // We counter this effect with a 0.5 exponent. This is empiric.
439     radius = pow( radius_temp, 0.5);
447 bool sp_spray_recursive(SPDesktop *desktop,
448                                Inkscape::Selection *selection,
449                                SPItem *item,
450                                Geom::Point p,
451                                Geom::Point /*vector*/,
452                                gint mode,
453                                double radius,
454                                double /*force*/,
455                                double population,
456                                double &scale,
457                                double scale_variation,
458                                bool /*reverse*/,
459                                double mean,
460                                double standard_deviation,
461                                double ratio,
462                                double tilt,
463                                double rotation_variation,
464                                gint _distrib )
466     bool did = false;
467     
468     if (SP_IS_BOX3D(item) ) {
469         // convert 3D boxes to ordinary groups before spraying their shapes
470         item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item)));
471         selection->add(item);
472     }
474     double _fid = g_random_double_range(0,1);
475     double angle = g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI );
476     double _scale = g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 );
477     double dr; double dp;
478     random_position( dr, dp, mean, standard_deviation, _distrib );
479     dr=dr*radius;
481     if (mode == SPRAY_MODE_COPY) {
482         Geom::OptRect a = item->getBounds(item->i2doc_affine());
483         if (a) {
484             SPItem *item_copied;
485             if(_fid<=population)
486             {
487                 // duplicate
488                 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
489                 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
490                 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
491                 Inkscape::XML::Node *parent = old_repr->parent();
492                 Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
493                 parent->appendChild(copy);
495                 SPObject *new_obj = doc->getObjectByRepr(copy);
496                 item_copied = (SPItem *) new_obj;   //convertion object->item
497                 Geom::Point center=item->getCenter();
498                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
499                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
501                 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
502                 //Move the cursor p
503                 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());
504                 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
505                 did = true;
506             }
507         }
508     } else if (mode == SPRAY_MODE_SINGLE_PATH) {
510         SPItem *father;         //initial Object
511         SPItem *item_copied;    //Projected Object
512         SPItem *unionResult;    //previous union
513         SPItem *son;            //father copy
515         int i=1;
516         for (GSList *items = g_slist_copy((GSList *) selection->itemList());
517                 items != NULL;
518                 items = items->next) {
520             SPItem *item1 = (SPItem *) items->data;
521             if (i==1) {
522                 father=item1;
523             }
524             if (i==2) {
525                 unionResult=item1;
526             }
527             i++;
528         }
529         SPDocument *doc = SP_OBJECT_DOCUMENT(father);
530         Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
531         Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(father);
532         Inkscape::XML::Node *parent = old_repr->parent();
534         Geom::OptRect a = father->getBounds(father->i2doc_affine());
535         if (a) {
536             if (i==2) {
537                 Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
538                 parent->appendChild(copy1);
539                 SPObject *new_obj1 = doc->getObjectByRepr(copy1);
540                 son = (SPItem *) new_obj1;   // conversion object->item
541                 unionResult=son;
542                 Inkscape::GC::release(copy1);
543                }
545             if (_fid<=population) { // Rules the population of objects sprayed
546                 // duplicates the father
547                 Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
548                 parent->appendChild(copy2);
549                 SPObject *new_obj2 = doc->getObjectByRepr(copy2);
550                 item_copied = (SPItem *) new_obj2;
552                 // Move around the cursor
553                 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()); 
555                 Geom::Point center=father->getCenter();
556                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
557                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
558                 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
559                 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
561                 // union and duplication
562                 selection->clear();
563                 selection->add(item_copied);
564                 selection->add(unionResult);
565                 sp_selected_path_union_skip_undo(selection->desktop());
566                 selection->add(father);
567                 Inkscape::GC::release(copy2);
568                 did = true;
569             }
570         }
571     } else if (mode == SPRAY_MODE_CLONE) {
572         Geom::OptRect a = item->getBounds(item->i2doc_affine());
573         if (a) {
574             if(_fid<=population) {
575                 SPItem *item_copied;
576                 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
577                 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
578                 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
579                 Inkscape::XML::Node *parent = old_repr->parent();
581                 // Creation of the clone
582                 Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
583                 // Ad the clone to the list of the father's sons
584                 parent->appendChild(clone);
585                 // Generates the link between father and son attributes
586                 clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); 
588                 SPObject *clone_object = doc->getObjectByRepr(clone);
589                 // conversion object->item
590                 item_copied = (SPItem *) clone_object;
591                 Geom::Point center=item->getCenter();
592                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
593                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
594                 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
595                 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());
596                 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
598                 Inkscape::GC::release(clone);
600                 did = true;
601             }
602         }
603     }
605     return did;
608 bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse)
610     Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop);
611     SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop;
614     if (selection->isEmpty()) {
615         return false;
616     }
618     bool did = false;
619     double radius = get_dilate_radius(tc);
623     bool do_fill = false, do_stroke = false, do_opacity = false;
624     guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill);
625     guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke);
626     double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity);
627     if (reverse) {
628         // RGB inversion
629         fill_goal = SP_RGBA32_U_COMPOSE(
630             (255 - SP_RGBA32_R_U(fill_goal)),
631             (255 - SP_RGBA32_G_U(fill_goal)),
632             (255 - SP_RGBA32_B_U(fill_goal)),
633             (255 - SP_RGBA32_A_U(fill_goal)));
634         stroke_goal = SP_RGBA32_U_COMPOSE(
635             (255 - SP_RGBA32_R_U(stroke_goal)),
636             (255 - SP_RGBA32_G_U(stroke_goal)),
637             (255 - SP_RGBA32_B_U(stroke_goal)),
638             (255 - SP_RGBA32_A_U(stroke_goal)));
639         opacity_goal = 1 - opacity_goal;
640     }
642     double path_force = get_path_force(tc);
643     if (radius == 0 || path_force == 0) {
644         return false;
645     }
646     double path_mean = get_path_mean(tc);
647     if (radius == 0 || path_mean == 0) {
648         return false;
649     }
650     double path_standard_deviation = get_path_standard_deviation(tc);
651     if (radius == 0 || path_standard_deviation == 0) {
652         return false;
653     }
654     double move_force = get_move_force(tc);
655     double move_mean = get_move_mean(tc);
656     double move_standard_deviation = get_move_standard_deviation(tc);
659     for (GSList *items = g_slist_copy((GSList *) selection->itemList());
660          items != NULL;
661          items = items->next) {
663         SPItem *item = (SPItem *) items->data;
665         if (is_transform_modes(tc->mode)) {
666             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))
667                 did = true;
668         } else {
669             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))
670                 did = true;
671         }
672     }
674     return did;
677 void sp_spray_update_area(SPSprayContext *tc)
679         double radius = get_dilate_radius(tc);
680         Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
681         sp_canvas_item_affine_absolute(tc->dilate_area,   (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
682         sp_canvas_item_show(tc->dilate_area);
685 void sp_spray_switch_mode(SPSprayContext *tc, gint mode, bool with_shift)
687     // select the button mode
688     SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); 
689     // need to set explicitly, because the prefs may not have changed by the previous
690     tc->mode = mode;
691     sp_spray_update_cursor (tc, with_shift);
694 void sp_spray_switch_mode_temporarily(SPSprayContext *tc, gint mode, bool with_shift)
696     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
697    // Juggling about so that prefs have the old value but tc->mode and the button show new mode:
698    gint now_mode = prefs->getInt("/tools/spray/mode", 0);
699    SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
700    // button has changed prefs, restore
701    prefs->setInt("/tools/spray/mode", now_mode);
702    // changing prefs changed tc->mode, restore back :)
703    tc->mode = mode;
704    sp_spray_update_cursor (tc, with_shift);
707 gint sp_spray_context_root_handler(SPEventContext *event_context,
708                                   GdkEvent *event)
710     SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context);
711     SPDesktop *desktop = event_context->desktop;
713     gint ret = FALSE;
715     switch (event->type) {
716         case GDK_ENTER_NOTIFY:
717             sp_canvas_item_show(tc->dilate_area);
718             break;
719         case GDK_LEAVE_NOTIFY:
720             sp_canvas_item_hide(tc->dilate_area);
721             break;
722         case GDK_BUTTON_PRESS:
723             if (event->button.button == 1 && !event_context->space_panning) {
725                 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) {
726                     return TRUE;
727                 }
729                 Geom::Point const motion_w(event->button.x,
730                                          event->button.y);
731                 Geom::Point const motion_dt(desktop->w2d(motion_w));
732                 tc->last_push = desktop->dt2doc(motion_dt);
734                 sp_spray_extinput(tc, event);
736                 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
737                 tc->is_drawing = true;
738                 tc->is_dilating = true;
739                                 tc->has_dilated = false;
743                 if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning)
745                                 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
749                                                           tc->has_dilated=true;
751                 ret = TRUE;
752             }
753             break;
754         case GDK_MOTION_NOTIFY:
755         {
756             Geom::Point const motion_w(event->motion.x,
757                                      event->motion.y);
758             Geom::Point motion_dt(desktop->w2d(motion_w));
759             Geom::Point motion_doc(desktop->dt2doc(motion_dt));
760             sp_spray_extinput(tc, event);
762             // draw the dilating cursor
763                 double radius = get_dilate_radius(tc);
764                 Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
765                 sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w)));
766                 sp_canvas_item_show(tc->dilate_area);
768                 guint num = 0;
769                 if (!desktop->selection->isEmpty()) {
770                     num = g_slist_length((GSList *) desktop->selection->itemList());
771                 }
772                 if (num == 0) {
773                     tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
774                 }
776             // dilating:
777             if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) {
778                 sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
779                 //tc->last_push = motion_doc;
780                 tc->has_dilated = true;
782                 // it's slow, so prevent clogging up with events
783                 gobble_motion_events(GDK_BUTTON1_MASK);
784                 return TRUE;
785             }
787         }
788         break;
789 /*Spray with the scroll*/
790         case GDK_SCROLL:
791                {
792               if (event->scroll.state & GDK_BUTTON1_MASK)
793                                   {
794                   double temp ;
795                   temp=tc->population;
796                   tc->population=1.0;
797                   desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
798                   Geom::Point const scroll_w(event->button.x,event->button.y);
799                   Geom::Point const scroll_dt = desktop->point();;
800                   Geom::Point motion_doc(desktop->dt2doc(scroll_dt));
801                        switch (event->scroll.direction)
802                        {
803                          case GDK_SCROLL_UP:
804                            {
805                                    if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
806                                    {
807                                     return TRUE;
808                                    }
809                                    tc->last_push = desktop->dt2doc(scroll_dt);
810                                    sp_spray_extinput(tc, event);
811                                    sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
812                                    tc->is_drawing = true;
813                                    tc->is_dilating = true;
814                                    tc->has_dilated = false;
815                                    if(tc->is_dilating && !event_context->space_panning)
817                                                         sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false);
821                                    tc->has_dilated=true;
822                                    tc->population=temp;
824                                    desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
826                                    ret = TRUE;
827                            }
828                                break;
829                            case GDK_SCROLL_DOWN:
830                            {
831                                    if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
832                                                                    {
833                                                                     return TRUE;
834                                                                    }
835                                                                    tc->last_push = desktop->dt2doc(scroll_dt);
836                                                                    sp_spray_extinput(tc, event);
837                                                                    sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
838                                                                    tc->is_drawing = true;
839                                                                    tc->is_dilating = true;
840                                                                    tc->has_dilated = false;
841                                                                    if(tc->is_dilating && !event_context->space_panning)
842                                                                                         sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false);
844                                                                    tc->has_dilated=true;
846                                                                    ret = TRUE;
849                            }
850                                break;
851 case GDK_SCROLL_RIGHT:
852                            {} break;
853 case GDK_SCROLL_LEFT:
854                            {} break;
855                                                                                                                    }
856                                   }
859                                break;
861                }
862     case GDK_BUTTON_RELEASE:
863     {
864         Geom::Point const motion_w(event->button.x, event->button.y);
865         Geom::Point const motion_dt(desktop->w2d(motion_w));
867         sp_canvas_end_forced_full_redraws(desktop->canvas);
868         tc->is_drawing = false;
870         if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) {
871             if (!tc->has_dilated) {
872                 // if we did not rub, do a light tap
873                 tc->pressure = 0.03;
874                 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
875             }
876             tc->is_dilating = false;
877             tc->has_dilated = false;
878             switch (tc->mode) {
879                 case SPRAY_MODE_COPY:
880                     SPDocumentUndo::done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
881                                      SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
882                     break;
883                 case SPRAY_MODE_CLONE:
884                     SPDocumentUndo::done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
885                                      SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
886                     break;
887                 case SPRAY_MODE_SINGLE_PATH:
888                     SPDocumentUndo::done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
889                                      SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
890                     break;
891             }
892         }
893         break;
894     }
896     case GDK_KEY_PRESS:
897         switch (get_group0_keyval (&event->key)) {
898 case GDK_j:  if (MOD__SHIFT_ONLY) {
899                 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
900                 ret = TRUE;
901             }
902 case GDK_J: if (MOD__SHIFT_ONLY) {
903                 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
904                 ret = TRUE;
905             }
907 break;
908         case GDK_m:
909         case GDK_M:
910         case GDK_0:
912             break;
913         case GDK_i:
914         case GDK_I:
915         case GDK_k:    if (MOD__SHIFT_ONLY) {
916                 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
917                 ret = TRUE;
918             }
919         case GDK_K:if (MOD__SHIFT_ONLY) {
920                 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
921                 ret = TRUE;
922             }
923 break;
925         case GDK_l: if (MOD__SHIFT_ONLY) {
926                 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
927                 ret = TRUE;
928             }
930         case GDK_L:
931  if (MOD__SHIFT_ONLY) {
932                 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
933                 ret = TRUE;
934             }
935             break;
936         case GDK_Up:
937         case GDK_KP_Up:
938             if (!MOD__CTRL_ONLY) {
939                 tc->scale += 0.05;
941                 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
942                 ret = TRUE;
943             }
944             break;
945         case GDK_Down:
946         case GDK_KP_Down:
947             if (!MOD__CTRL_ONLY) {
949                 tc->scale -= 0.05;
950                 if (tc->scale < 0.0)
951                     tc->scale = 0.0;
952                 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
954                 ret = TRUE;
956             }
957             break;
958         case GDK_Right:
959         case GDK_KP_Right:
960             if (!MOD__CTRL_ONLY) {
961                 tc->width += 0.01;
962                 if (tc->width > 1.0)
963                     tc->width = 1.0;
964                 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x
965                 sp_spray_update_area(tc);
966                 ret = TRUE;
967             }
968             break;
969         case GDK_Left:
970         case GDK_KP_Left:
971             if (!MOD__CTRL_ONLY) {
972                 tc->width -= 0.01;
973                 if (tc->width < 0.01)
974                     tc->width = 0.01;
975                 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
976                 sp_spray_update_area(tc);
977                 ret = TRUE;
978             }
979             break;
980         case GDK_Home:
981         case GDK_KP_Home:
982             tc->width = 0.01;
983             desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
984             sp_spray_update_area(tc);
985             ret = TRUE;
986             break;
987         case GDK_End:
988         case GDK_KP_End:
989             tc->width = 1.0;
990             desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
991             sp_spray_update_area(tc);
992             ret = TRUE;
993             break;
994         case GDK_x:
995         case GDK_X:
996             if (MOD__ALT_ONLY) {
997                 desktop->setToolboxFocusTo ("altx-spray");
998                 ret = TRUE;
999             }
1000             break;
1002         case GDK_Shift_L:
1003         case GDK_Shift_R:
1004             sp_spray_update_cursor(tc, true);
1005             break;
1006 /*Set the scale to 1*/
1007         case GDK_Control_L:
1008                 tc->scale=1;
1009         default:
1010             break;
1011         }
1012         break;
1014     case GDK_KEY_RELEASE: {
1015         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1016         switch (get_group0_keyval(&event->key)) {
1017             case GDK_Shift_L:
1018             case GDK_Shift_R:
1019                 sp_spray_update_cursor(tc, false);
1020                 break;
1021             case GDK_Control_L:
1022             case GDK_Control_R:
1023                 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1024                 tc->_message_context->clear();
1025                 break;
1026             default:
1027                 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1028                 break;
1029         }
1030     }
1032     default:
1033         break;
1034     }
1036     if (!ret) {
1037         if (((SPEventContextClass *) parent_class)->root_handler) {
1038             ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
1039         }
1040     }
1042     return ret;
1045 /*
1046   Local Variables:
1047   mode:c++
1048   c-file-style:"stroustrup"
1049   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1050   indent-tabs-mode:nil
1051   fill-column:99
1052   End:
1053 */
1054 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :