Code

8005f06adef9df134b32339dde3259d46d11dd02
[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  *
15  * Copyright (C) 2009 authors
16  *
17  * Released under GNU GPL, read the file 'COPYING' for more information
18  */
20 #include "config.h"
22 #include <gtk/gtk.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <glibmm/i18n.h>
26 #include <numeric>
28 #include "svg/svg.h"
29 #include "display/canvas-bpath.h"
31 #include <glib/gmem.h>
32 #include "macros.h"
33 #include "document.h"
34 #include "selection.h"
35 #include "desktop.h"
36 #include "desktop-events.h"
37 #include "desktop-handles.h"
38 #include "unistd.h"
39 #include "desktop-style.h"
40 #include "message-context.h"
41 #include "pixmaps/cursor-spray.xpm"
42 #include "pixmaps/cursor-spray-move.xpm"
43 #include "pixmaps/cursor-thin.xpm"
44 #include "pixmaps/cursor-thicken.xpm"
45 #include "pixmaps/cursor-attract.xpm"
46 #include "pixmaps/cursor-repel.xpm"
47 #include "pixmaps/cursor-push.xpm"
48 #include "pixmaps/cursor-roughen.xpm"
49 #include "pixmaps/cursor-color.xpm"
50 #include <boost/optional.hpp>
51 #include "libnr/nr-matrix-ops.h"
52 #include "libnr/nr-scale-translate-ops.h"
53 #include "xml/repr.h"
54 #include "context-fns.h"
55 #include "sp-item.h"
56 #include "inkscape.h"
57 #include "color.h"
58 #include "svg/svg-color.h"
59 #include "splivarot.h"
60 #include "sp-item-group.h"
61 #include "sp-shape.h"
62 #include "sp-path.h"
63 #include "path-chemistry.h"
64 #include "sp-gradient.h"
65 #include "sp-stop.h"
66 #include "sp-stop-fns.h"
67 #include "sp-gradient-reference.h"
68 #include "sp-linear-gradient.h"
69 #include "sp-radial-gradient.h"
70 #include "gradient-chemistry.h"
71 #include "sp-text.h"
72 #include "sp-flowtext.h"
73 #include "display/canvas-bpath.h"
74 #include "display/canvas-arena.h"
75 #include "display/curve.h"
76 #include "livarot/Shape.h"
77 #include <2geom/isnan.h>
78 #include <2geom/transforms.h>
79 #include "preferences.h"
80 #include "style.h"
81 #include "box3d.h"
82 #include "sp-item-transform.h"
83 #include "filter-chemistry.h"
84 #include "sp-gaussian-blur-fns.h"
85 #include "sp-gaussian-blur.h"
87 #include "spray-context.h"
88 #include "ui/dialog/dialog-manager.h"
89 #include "helper/action.h"
91 #include <iostream>
92 using namespace std;
95 #define DDC_RED_RGBA 0xff0000ff
97 #define DYNA_MIN_WIDTH 1.0e-6
99 static void sp_spray_context_class_init(SPSprayContextClass *klass);
100 static void sp_spray_context_init(SPSprayContext *ddc);
101 static void sp_spray_context_dispose(GObject *object);
103 static void sp_spray_context_setup(SPEventContext *ec);
104 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val);
105 static gint sp_spray_context_root_handler(SPEventContext *ec, GdkEvent *event);
107 static SPEventContextClass *parent_class = 0;
111 // The following code implements NormalDistribution wich is used for the density of the spray
114 /*
115    RAND is a macro which returns a pseudo-random numbers from a uniform
116    distribution on the interval [0 1]
117 */
118 #define RAND ((double) rand())/((double) RAND_MAX)
120 /*
121    TWOPI = 2.0*pi
122 */
123 #define TWOPI 2.0*3.141592653589793238462643383279502884197169399375
125 /*
126    RANDN is a macro which returns a pseudo-random numbers from a normal
127    distribution with mean zero and standard deviation one. This macro uses Box
128    Muller's algorithm
129 */
130 #define RANDN sqrt(-2.0*log(RAND))*cos(TWOPI*RAND)
133 double NormalDistribution(double mu,double sigma)
135 /*
136    This function returns a pseudo-random numbers from a normal distribution with
137    mean equal at mu and standard deviation equal at sigma > 0
138 */
140   return (mu+sigma*RANDN);
144 //End of NormalDistribution
146 GtkType sp_spray_context_get_type(void)
148     static GType type = 0;
149     if (!type) {
150         GTypeInfo info = {
151             sizeof(SPSprayContextClass),
152             NULL, NULL,
153             (GClassInitFunc) sp_spray_context_class_init,
154             NULL, NULL,
155             sizeof(SPSprayContext),
156             4,
157             (GInstanceInitFunc) sp_spray_context_init,
158             NULL,   /* value_table */
159         };
160         type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSprayContext", &info, (GTypeFlags)0);
161     }
162     return type;
165 static void sp_spray_context_class_init(SPSprayContextClass *klass)
167     GObjectClass *object_class = (GObjectClass *) klass;
168     SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
170     parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
172     object_class->dispose = sp_spray_context_dispose;
174     event_context_class->setup = sp_spray_context_setup;
175     event_context_class->set = sp_spray_context_set;
176     event_context_class->root_handler = sp_spray_context_root_handler;
179 /*Method to rotate items*/
180 void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom::Rotate const &rotation)
183     Geom::Point center = c;
184     Geom::Translate const s(c);
185     Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s);
187     // Rotate item.
188     sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * (Geom::Matrix)affine);
189     // Use each item's own transform writer, consistent with sp_selection_apply_affine()
190     sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform);
192     // Restore the center position (it's changed because the bbox center changed)
193     if (item->isCenterSet()) {
194         item->setCenter(c);
195         item->updateRepr();
196     }
199 /*Method to scale items*/
200 void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale  const &scale)
202         Geom::Translate const s(c);
205         sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * s.inverse() * scale * s  );
206         sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform);
211 static void sp_spray_context_init(SPSprayContext *tc)
213    SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
217     event_context->cursor_shape = cursor_spray_xpm;
218     event_context->hot_x = 4;
219     event_context->hot_y = 4;
221     /* attributes */
222     tc->dragging = FALSE;
223     tc->distrib = 1;
224     tc->width = 0.2;
225     tc->force = 0.2;
226     tc->ratio = 0;
227     tc->tilt=0;
228     tc->mean = 0.2;
229     tc->rot_variation=0;
230     tc->standard_deviation=0.2;
231     tc->scale=1;
232     tc->scale_variation = 1;
233     tc->pressure = TC_DEFAULT_PRESSURE;
235     tc->is_dilating = false;
236     tc->has_dilated = false;
238     tc->do_h = true;
239     tc->do_s = true;
240     tc->do_l = true;
241     tc->do_o = false;
243     new (&tc->style_set_connection) sigc::connection();
246 static void sp_spray_context_dispose(GObject *object)
248     SPSprayContext *tc = SP_SPRAY_CONTEXT(object);
250     tc->style_set_connection.disconnect();
251     tc->style_set_connection.~connection();
253     if (tc->dilate_area) {
254         gtk_object_destroy(GTK_OBJECT(tc->dilate_area));
255         tc->dilate_area = NULL;
256     }
258     if (tc->_message_context) {
259         delete tc->_message_context;
260     }
262     G_OBJECT_CLASS(parent_class)->dispose(object);
265 bool is_transform_modes(gint mode)
267     return (mode == SPRAY_MODE_COPY ||
268             mode == SPRAY_MODE_CLONE ||
269             mode == SPRAY_MODE_SINGLE_PATH ||
270             mode == SPRAY_OPTION);
273 void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/)
275    SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
276    SPDesktop *desktop = event_context->desktop;
278                 guint num = 0;
279                 gchar *sel_message = NULL;
280                 if (!desktop->selection->isEmpty()) {
281                     num = g_slist_length((GSList *) desktop->selection->itemList());
282                     sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
283                 } else {
284                     sel_message = g_strdup_printf(_("<b>Nothing</b> selected"));
285                 }
288    switch (tc->mode) {
289        case SPRAY_MODE_COPY:
290            tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>copies</b> of the initial selection"), sel_message);
291            break;
292        case SPRAY_MODE_CLONE:
293            tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>clones</b> of the initial selection"), sel_message);
294            break;
295        case SPRAY_MODE_SINGLE_PATH:
296            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);
297            break;
298        case SPRAY_OPTION:
299            tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Modify <b>spray</b> options"), sel_message);
300            break;
301    }
302    sp_event_context_update_cursor(event_context);
303    g_free(sel_message);
306 static void sp_spray_context_setup(SPEventContext *ec)
308     SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
310     if (((SPEventContextClass *) parent_class)->setup)
311         ((SPEventContextClass *) parent_class)->setup(ec);
313     {
314         /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
315         SPCurve *c = new SPCurve();
316         const double C1 = 0.552;
317         c->moveto(-1,0);
318         c->curveto(-1, C1, -C1, 1, 0, 1 );
319         c->curveto(C1, 1, 1, C1, 1, 0 );
320         c->curveto(1, -C1, C1, -1, 0, -1 );
321         c->curveto(-C1, -1, -1, -C1, -1, 0 );
322         c->closepath();
323         tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c);
324         c->unref();
325         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0);
326         sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
327         sp_canvas_item_hide(tc->dilate_area);
328     }
330     tc->is_drawing = false;
332     tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
334     sp_event_context_read(ec, "distrib");
335     sp_event_context_read(ec, "width");
336     sp_event_context_read(ec, "ratio");
337     sp_event_context_read(ec, "tilt");
338     sp_event_context_read(ec, "rot_variation");
339     sp_event_context_read(ec, "scale_variation");
340     sp_event_context_read(ec, "mode");
341     sp_event_context_read(ec, "population");
342     sp_event_context_read(ec, "force");
343     sp_event_context_read(ec, "mean");
344     sp_event_context_read(ec, "standard_deviation");
345     sp_event_context_read(ec, "usepressure");
346     sp_event_context_read(ec, "Scale");
347     sp_event_context_read(ec, "doh");
348     sp_event_context_read(ec, "dol");
349     sp_event_context_read(ec, "dos");
350     sp_event_context_read(ec, "doo");
352     ;
354     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
355     if (prefs->getBool("/tools/spray/selcue")) {
356         ec->enableSelectionCue();
357     }
359     if (prefs->getBool("/tools/spray/gradientdrag")) {
360         ec->enableGrDrag();
361     }
364 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
366     SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
367     Glib::ustring path = val->getEntryName();
369     if (path == "width") {
370         tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100);
371     } else if (path == "mode") {
372         tc->mode = val->getInt();
373         sp_spray_update_cursor(tc, false);
374     } else if (path == "distribution") {
375         tc->distrib = val->getInt(1);
376     } else if (path == "population") {
377         tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100);
378      } else if (path == "tilt") {
379         tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0);
380      } else if (path == "ratio") {
381         tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9);
382     } else if (path == "force") {
383         tc->force = CLAMP(val->getDouble(1.0), 0, 1.0);
384     } else if (path == "rotation_variation") {
385         tc->rot_variation = CLAMP(val->getDouble(0), 0, 10.0);
386     } else if (path == "scale_variation") {
387         tc->scale_variation = CLAMP(val->getDouble(1.0), 0, 10.0);
388     } else if (path == "mean") {
389         tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100);
390     } else if (path == "standard_deviation") {
391         tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100);
392     } else if (path == "usepressure") {
393         tc->usepressure = val->getBool();
394     } else if (path == "doh") {
395         tc->do_h = val->getBool();
396     } else if (path == "dos") {
397         tc->do_s = val->getBool();
398     } else if (path == "dol") {
399         tc->do_l = val->getBool();
400     } else if (path == "doo") {
401         tc->do_o = val->getBool();
402     }
405 static void sp_spray_extinput(SPSprayContext *tc, GdkEvent *event)
407     if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure))
408         tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
409     else
410         tc->pressure = TC_DEFAULT_PRESSURE;
413 double get_dilate_radius(SPSprayContext *tc)
416     return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom();
421 double get_path_force(SPSprayContext *tc)
423     double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
424         /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom());
425     if (force > 3) {
426         force += 4 * (force - 3);
427     }
428     return force * tc->force;
431 double get_path_mean(SPSprayContext *tc)
433     return tc->mean;
436 double get_path_standard_deviation(SPSprayContext *tc)
438     return tc->standard_deviation;
441 double get_move_force(SPSprayContext *tc)
443     double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
444     return force * tc->force;
447 double get_move_mean(SPSprayContext *tc)
449     return tc->mean;
452 double get_move_standard_deviation(SPSprayContext *tc)
454     return tc->standard_deviation;
457 /* Method to handle the distribution of the items */
460 void random_position( double &r, double &p, double &a, double &s, int choix)
462        if (choix == 0) // Mode 1 : uniform repartition
463     {
464         r = (1-pow(g_random_double_range(0, 1),2));
465         p = g_random_double_range(0, M_PI*2);
466     }
467     if (choix == 1) //Mode 0 : gaussian repartition
468     {
469         double r_temp =-1;
470 while(!((r_temp>=0)&&(r_temp<=1)))
472          r_temp = NormalDistribution(a,s/4);
474 // generates a number following a normal distribution
475         p = g_random_double_range(0, M_PI*2);
476         r=r_temp;
477        /* if (r_temp<=0) r=0;
478         else
479         {
480             if (r_temp>1) r=1;
481             else r = r_temp;
482         }*/
483     }
490 bool sp_spray_dilate_recursive(SPDesktop *desktop,
491                                Inkscape::Selection *selection,
492                                SPItem *item,
493                                Geom::Point p,
494                                Geom::Point /*vector*/,
495                                gint mode,
496                                double radius,
497                                double /*force*/,
498                                double population,
499                                double &scale,
500                                double scale_variation,
501                                bool /*reverse*/,
502                                double mean,
503                                double standard_deviation,
504                                double ratio,
505                                double tilt,
506                                double rotation_variation,
507                                gint _distrib )
512     bool did = false;
514     if (SP_IS_BOX3D(item) /*&& !is_transform_modes(mode)*/) {
515         // convert 3D boxes to ordinary groups before spraying their shapes
516         item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item)));
517         selection->add(item);
518     }
520         if (mode == SPRAY_MODE_COPY) {
522             Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
523             if (a) {
524                 double dr; double dp;
525                 random_position(dr,dp,mean,standard_deviation,_distrib);
526                 dr=dr*radius;
527                 double _fid = g_random_double_range(0,1);
528                 SPItem *item_copied;
529                 double angle = g_random_double_range(1.0 - rotation_variation/2.0, 1.0 + rotation_variation/2.0);
530                 double _scale = g_random_double_range( 1.0 - scale_variation/2.0, 1.0 + scale_variation/2.0);
531                 if(_fid<=population)
532                 {
533                           // duplicate
534                             SPDocument *doc = SP_OBJECT_DOCUMENT(item);
535                             Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
536                             Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
537                             Inkscape::XML::Node *parent = old_repr->parent();
538                             Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
539                             parent->appendChild(copy);
541                             SPObject *new_obj = doc->getObjectByRepr(copy);
542                             item_copied = (SPItem *) new_obj;   //convertion object->item
543                             Geom::Point center=item->getCenter();
544                             sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
545                             sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
547                             sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
548                             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());//Move the cursor p
549                             sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
555                         did = true;
556                         }
557             }
559         } else if (mode == SPRAY_MODE_SINGLE_PATH) {
562             SPItem *father; //initial Object
563             SPItem *item_copied;//Projected Object
564             SPItem *unionResult;//previous union
565             SPItem *son;//father copy
567             int i=1;
568             for (GSList *items = g_slist_copy((GSList *) selection->itemList());
569                     items != NULL;
570                     items = items->next) {
572                 SPItem *item1 = (SPItem *) items->data;
573                 if (i==1) {
574                     father=item1;
575                 }
576                 if (i==2) {
577                     unionResult=item1;
578                 }
579                 i++;
580             }
581             SPDocument *doc = SP_OBJECT_DOCUMENT(father);
582             Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
583             Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(father);
584             //SPObject *old_obj = doc->getObjectByRepr(old_repr);
585             Inkscape::XML::Node *parent = old_repr->parent();
587             Geom::OptRect a = father->getBounds(sp_item_i2doc_affine(father));
588             if (a) {
589                 double dr; double dp; //initialisation des variables
590                 random_position(dr,dp,mean,standard_deviation,_distrib);
591                 dr=dr*radius;
592                 double _fid = g_random_double_range(0,1);
593                 double angle = g_random_double_range(1.0 - rotation_variation/2.0, 1.0 + rotation_variation/2.0);
594                 double _scale = g_random_double_range( 1.0 - scale_variation/2.0, 1.0 + scale_variation/2.0);
595                 if (i==2) {
596                     Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
597                     parent->appendChild(copy1);
598                     SPObject *new_obj1 = doc->getObjectByRepr(copy1);
599                     son = (SPItem *) new_obj1;   //conversion object->item
600                     unionResult=son;
601                     Inkscape::GC::release(copy1);
602                    }
604                 if (_fid<=population) { //Rules the population of objects sprayed
605                     // duplicates the father
606                     Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
607                     parent->appendChild(copy2);
608                     SPObject *new_obj2 = doc->getObjectByRepr(copy2);
609                     item_copied = (SPItem *) new_obj2;
611                     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());//Move around the cursor
613                             Geom::Point center=father->getCenter();
614                             sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
615                             sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
616                             sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
617                             sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
619 //unionResult and duplication
620                     selection->clear();
621                     selection->add(item_copied);
622                     selection->add(unionResult);
623                     sp_selected_path_union(selection->desktop());
624                     selection->add(father);
625                     Inkscape::GC::release(copy2);
626                     did = true;
627                 }
629             }
630         } else if (mode == SPRAY_MODE_CLONE) {
632         Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
633                 if (a) {
634                 double dr; double dp;
635                 random_position(dr,dp,mean,standard_deviation,_distrib);
636                 dr=dr*radius;
637                 double _fid = g_random_double_range(0,1);
638                 double angle = g_random_double_range(1.0 - rotation_variation/2.0, 1.0 + rotation_variation/2.0);
639                 double _scale = g_random_double_range( 1.0 - scale_variation/2.0, 1.0 + scale_variation/2.0);
641         if(_fid<=population)
642                 {
643                             SPItem *item_copied;
644                             SPDocument *doc = SP_OBJECT_DOCUMENT(item);
645                             Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
646                             Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
647                             Inkscape::XML::Node *parent = old_repr->parent();
649                             //Creation of the clone
650                             Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
651                             parent->appendChild(clone); // Ad the clone to the list of the father's sons
652                             clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); // Generates the link between father and son attributes
654                             SPObject *clone_object = doc->getObjectByRepr(clone);
655                             item_copied = (SPItem *) clone_object;//conversion object->item
656                               Geom::Point center=item->getCenter();
657                             sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
658                             sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
659                             sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
660                             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());
661                             sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
663                             Inkscape::GC::release(clone);
665                             did = true;
666                 } }}
667                 return did;
672 bool sp_spray_color_recursive(guint /*mode*/,
673                               SPItem */*item*/,
674                               SPItem */*item_at_point*/,
675                               guint32 /*fill_goal*/,
676                               bool /*do_fill*/,
677                               guint32 /*stroke_goal*/,
678                               bool /*do_stroke*/,
679                               float /*opacity_goal*/,
680                               bool /*do_opacity*/,
681                               bool /*do_blur*/,
682                               bool /*reverse*/,
683                               Geom::Point /*p*/,
684                               double /*radius*/,
685                               double /*force*/,
686                               bool /*do_h*/,
687                               bool /*do_s*/,
688                               bool /*do_l*/,
689                               bool /*do_o*/)
691     bool did = false;
693     return did;
697 bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse)
699     Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop);
700     SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop;
703     if (selection->isEmpty()) {
704         return false;
705     }
707     bool did = false;
708     double radius = get_dilate_radius(tc);
712     bool do_fill = false, do_stroke = false, do_opacity = false;
713     guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill);
714     guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke);
715     double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity);
716     if (reverse) {
717         // RGB inversion
718         fill_goal = SP_RGBA32_U_COMPOSE(
719             (255 - SP_RGBA32_R_U(fill_goal)),
720             (255 - SP_RGBA32_G_U(fill_goal)),
721             (255 - SP_RGBA32_B_U(fill_goal)),
722             (255 - SP_RGBA32_A_U(fill_goal)));
723         stroke_goal = SP_RGBA32_U_COMPOSE(
724             (255 - SP_RGBA32_R_U(stroke_goal)),
725             (255 - SP_RGBA32_G_U(stroke_goal)),
726             (255 - SP_RGBA32_B_U(stroke_goal)),
727             (255 - SP_RGBA32_A_U(stroke_goal)));
728         opacity_goal = 1 - opacity_goal;
729     }
731     double path_force = get_path_force(tc);
732     if (radius == 0 || path_force == 0) {
733         return false;
734     }
735     double path_mean = get_path_mean(tc);
736     if (radius == 0 || path_mean == 0) {
737         return false;
738     }
739     double path_standard_deviation = get_path_standard_deviation(tc);
740     if (radius == 0 || path_standard_deviation == 0) {
741         return false;
742     }
743     double move_force = get_move_force(tc);
744     double move_mean = get_move_mean(tc);
745     double move_standard_deviation = get_move_standard_deviation(tc);
748     for (GSList *items = g_slist_copy((GSList *) selection->itemList());
749          items != NULL;
750          items = items->next) {
752         SPItem *item = (SPItem *) items->data;
754         if (is_transform_modes(tc->mode)) {
755             if (sp_spray_dilate_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->rot_variation, tc->distrib))
756                 did = true;
757         } else {
758             if (sp_spray_dilate_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->rot_variation, tc->distrib))
759                 did = true;
760         }
761     }
763     return did;
766 void sp_spray_update_area(SPSprayContext *tc)
768         double radius = get_dilate_radius(tc);
769         Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
770         sp_canvas_item_affine_absolute(tc->dilate_area,   (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
771         sp_canvas_item_show(tc->dilate_area);
774 void sp_spray_switch_mode(SPSprayContext *tc, gint mode, bool with_shift)
776     SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); //sélectionne le bouton numéro "mode"
777     // need to set explicitly, because the prefs may not have changed by the previous
778     tc->mode = mode;
779     sp_spray_update_cursor (tc, with_shift);
782 void sp_spray_switch_mode_temporarily(SPSprayContext *tc, gint mode, bool with_shift)
784     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
785    // Juggling about so that prefs have the old value but tc->mode and the button show new mode:
786    gint now_mode = prefs->getInt("/tools/spray/mode", 0);
787    SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
788    // button has changed prefs, restore
789    prefs->setInt("/tools/spray/mode", now_mode);
790    // changing prefs changed tc->mode, restore back :)
791    tc->mode = mode;
792    sp_spray_update_cursor (tc, with_shift);
795 gint sp_spray_context_root_handler(SPEventContext *event_context,
796                                   GdkEvent *event)
798     SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context);
799     SPDesktop *desktop = event_context->desktop;
801     gint ret = FALSE;
803     switch (event->type) {
804         case GDK_ENTER_NOTIFY:
805             sp_canvas_item_show(tc->dilate_area);
806             break;
807         case GDK_LEAVE_NOTIFY:
808             sp_canvas_item_hide(tc->dilate_area);
809             break;
810         case GDK_BUTTON_PRESS:
811             if (event->button.button == 1 && !event_context->space_panning) {
813                 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) {
814                     return TRUE;
815                 }
817                 Geom::Point const motion_w(event->button.x,
818                                          event->button.y);
819                 Geom::Point const motion_dt(desktop->w2d(motion_w));
820                 tc->last_push = desktop->dt2doc(motion_dt);
822                 sp_spray_extinput(tc, event);
824                 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
825                 tc->is_drawing = true;
826                 tc->is_dilating = true;
827                                 tc->has_dilated = false;
831                 if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning)
833                                 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
837                                                           tc->has_dilated=true;
839                 ret = TRUE;
840             }
841             break;
842         case GDK_MOTION_NOTIFY:
843         {
844             Geom::Point const motion_w(event->motion.x,
845                                      event->motion.y);
846             Geom::Point motion_dt(desktop->w2d(motion_w));
847             Geom::Point motion_doc(desktop->dt2doc(motion_dt));
848             sp_spray_extinput(tc, event);
850             // draw the dilating cursor
851                 double radius = get_dilate_radius(tc);
852                 Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
853                 sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w)));
854                 sp_canvas_item_show(tc->dilate_area);
856                 guint num = 0;
857                 if (!desktop->selection->isEmpty()) {
858                     num = g_slist_length((GSList *) desktop->selection->itemList());
859                 }
860                 if (num == 0) {
861                     tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
862                 }
864             // dilating:
865             if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) {
866                 sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
867                 //tc->last_push = motion_doc;
868                 tc->has_dilated = true;
870                 // it's slow, so prevent clogging up with events
871                 gobble_motion_events(GDK_BUTTON1_MASK);
872                 return TRUE;
873             }
875         }
876         break;
877 /*Spray with the scroll*/
878         case GDK_SCROLL:
879                {
880               if (event->scroll.state & GDK_BUTTON1_MASK)
881                                   {
882                   double temp ;
883                   temp=tc->population;
884                   tc->population=1.0;
885                   desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
886                   Geom::Point const scroll_w(event->button.x,event->button.y);
887                   Geom::Point const scroll_dt = desktop->point();;
888                   Geom::Point motion_doc(desktop->dt2doc(scroll_dt));
889                        switch (event->scroll.direction)
890                        {
891                          case GDK_SCROLL_UP:
892                            {
893                                    if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
894                                    {
895                                     return TRUE;
896                                    }
897                                    tc->last_push = desktop->dt2doc(scroll_dt);
898                                    sp_spray_extinput(tc, event);
899                                    sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
900                                    tc->is_drawing = true;
901                                    tc->is_dilating = true;
902                                    tc->has_dilated = false;
903                                    if(tc->is_dilating && !event_context->space_panning)
905                                                         sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false);
909                                    tc->has_dilated=true;
910                                    tc->population=temp;
912                                    desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
914                                    ret = TRUE;
915                            }
916                                break;
917                            case GDK_SCROLL_DOWN:
918                            {
919                                    if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
920                                                                    {
921                                                                     return TRUE;
922                                                                    }
923                                                                    tc->last_push = desktop->dt2doc(scroll_dt);
924                                                                    sp_spray_extinput(tc, event);
925                                                                    sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
926                                                                    tc->is_drawing = true;
927                                                                    tc->is_dilating = true;
928                                                                    tc->has_dilated = false;
929                                                                    if(tc->is_dilating && !event_context->space_panning)
930                                                                                         sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false);
932                                                                    tc->has_dilated=true;
934                                                                    ret = TRUE;
937                            }
938                                break;
939 case GDK_SCROLL_RIGHT:
940                            {} break;
941 case GDK_SCROLL_LEFT:
942                            {} break;
943                                                                                                                    }
944                                   }
947                                break;
949                }
950     case GDK_BUTTON_RELEASE:
951     {
952         Geom::Point const motion_w(event->button.x, event->button.y);
953         Geom::Point const motion_dt(desktop->w2d(motion_w));
955         sp_canvas_end_forced_full_redraws(desktop->canvas);
956         tc->is_drawing = false;
958         if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) {
959             if (!tc->has_dilated) {
960                 // if we did not rub, do a light tap
961                 tc->pressure = 0.03;
962                 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
963             }
964             tc->is_dilating = false;
965             tc->has_dilated = false;
966             switch (tc->mode) {
967                 case SPRAY_MODE_COPY:
968                     sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
969                                      SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
970                     break;
971                 case SPRAY_MODE_CLONE:
972                     sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
973                                      SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
974                     break;
975                 case SPRAY_MODE_SINGLE_PATH:
976                     sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
977                                      SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
978                     break;
979             }
980         }
981         break;
982     }
984     case GDK_KEY_PRESS:
985         switch (get_group0_keyval (&event->key)) {
986 case GDK_j:  if (MOD__SHIFT_ONLY) {
987                 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
988                 ret = TRUE;
989             }
990 case GDK_J: if (MOD__SHIFT_ONLY) {
991                 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
992                 ret = TRUE;
993             }
995 break;
996         case GDK_m:
997         case GDK_M:
998         case GDK_0:
1000             break;
1001         case GDK_i:
1002         case GDK_I:
1003         case GDK_k:    if (MOD__SHIFT_ONLY) {
1004                 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
1005                 ret = TRUE;
1006             }
1007         case GDK_K:if (MOD__SHIFT_ONLY) {
1008                 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
1009                 ret = TRUE;
1010             }
1011 break;
1013         case GDK_l: if (MOD__SHIFT_ONLY) {
1014                 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
1015                 ret = TRUE;
1016             }
1018         case GDK_L:
1019  if (MOD__SHIFT_ONLY) {
1020                 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
1021                 ret = TRUE;
1022             }
1023             break;
1024         case GDK_Up:
1025         case GDK_KP_Up:
1026             if (!MOD__CTRL_ONLY) {
1027                 tc->scale += 0.05;
1029                 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
1030                 ret = TRUE;
1031             }
1032             break;
1033         case GDK_Down:
1034         case GDK_KP_Down:
1035             if (!MOD__CTRL_ONLY) {
1037                 tc->scale -= 0.05;
1038                 if (tc->scale < 0.0)
1039                     tc->scale = 0.0;
1040                 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
1042                 ret = TRUE;
1044             }
1045             break;
1046         case GDK_Right:
1047         case GDK_KP_Right:
1048             if (!MOD__CTRL_ONLY) {
1049                 tc->width += 0.01;
1050                 if (tc->width > 1.0)
1051                     tc->width = 1.0;
1052                 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x
1053                 sp_spray_update_area(tc);
1054                 ret = TRUE;
1055             }
1056             break;
1057         case GDK_Left:
1058         case GDK_KP_Left:
1059             if (!MOD__CTRL_ONLY) {
1060                 tc->width -= 0.01;
1061                 if (tc->width < 0.01)
1062                     tc->width = 0.01;
1063                 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1064                 sp_spray_update_area(tc);
1065                 ret = TRUE;
1066             }
1067             break;
1068         case GDK_Home:
1069         case GDK_KP_Home:
1070             tc->width = 0.01;
1071             desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1072             sp_spray_update_area(tc);
1073             ret = TRUE;
1074             break;
1075         case GDK_End:
1076         case GDK_KP_End:
1077             tc->width = 1.0;
1078             desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1079             sp_spray_update_area(tc);
1080             ret = TRUE;
1081             break;
1082         case GDK_x:
1083         case GDK_X:
1084             if (MOD__ALT_ONLY) {
1085                 desktop->setToolboxFocusTo ("altx-spray");
1086                 ret = TRUE;
1087             }
1088             break;
1090         case GDK_Shift_L:
1091         case GDK_Shift_R:
1092             sp_spray_update_cursor(tc, true);
1093             break;
1094 /*Set the scale to 1*/
1095         case GDK_Control_L:
1096                 tc->scale=1;
1097         default:
1098             break;
1099         }
1100         break;
1102     case GDK_KEY_RELEASE: {
1103         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1104         switch (get_group0_keyval(&event->key)) {
1105             case GDK_Shift_L:
1106             case GDK_Shift_R:
1107                 sp_spray_update_cursor(tc, false);
1108                 break;
1109             case GDK_Control_L:
1110             case GDK_Control_R:
1111                 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1112                 tc->_message_context->clear();
1113                 break;
1114             default:
1115                 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1116                 break;
1117         }
1118     }
1120     default:
1121         break;
1122     }
1124     if (!ret) {
1125         if (((SPEventContextClass *) parent_class)->root_handler) {
1126             ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
1127         }
1128     }
1130     return ret;
1133 /*
1134   Local Variables:
1135   mode:c++
1136   c-file-style:"stroustrup"
1137   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1138   indent-tabs-mode:nil
1139   fill-column:99
1140   End:
1141 */
1142 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :