Code

Keeping up with trunk
[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;
110 /*
111    RAND is a macro which returns a pseudo-random numbers from a uniform
112    distribution on the interval [0 1]
113 */
114 #define RAND ((double) rand())/((double) RAND_MAX)
116 /*
117     TWOPI = 2.0*pi
118 */
119 #define TWOPI 2.0*3.141592653589793238462643383279502884197169399375
121 /*
122    RANDN is a macro which returns a pseudo-random numbers from a normal
123    distribution with mean zero and standard deviation one. This macro uses Box
124    Muller's algorithm
125 */
126 #define RANDN sqrt(-2.0*log(RAND))*cos(TWOPI*RAND)
129 double NormalDistribution(double mu,double sigma)
131 /*
132    This function returns a pseudo-random numbers from a normal distribution with
133    mean equal at mu and standard deviation equal at sigma > 0
134 */
136   return (mu+sigma*RANDN);
141 GtkType sp_spray_context_get_type(void)
143     static GType type = 0;
144     if (!type) {
145         GTypeInfo info = {
146             sizeof(SPSprayContextClass),
147             NULL, NULL,
148             (GClassInitFunc) sp_spray_context_class_init,
149             NULL, NULL,
150             sizeof(SPSprayContext),
151             4,
152             (GInstanceInitFunc) sp_spray_context_init,
153             NULL,   /* value_table */
154         };
155         type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSprayContext", &info, (GTypeFlags)0);
156     }
157     return type;
160 static void sp_spray_context_class_init(SPSprayContextClass *klass)
162     GObjectClass *object_class = (GObjectClass *) klass;
163     SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
165     parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
167     object_class->dispose = sp_spray_context_dispose;
169     event_context_class->setup = sp_spray_context_setup;
170     event_context_class->set = sp_spray_context_set;
171     event_context_class->root_handler = sp_spray_context_root_handler;
174 /* Method to rotate items */
175 void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom::Rotate const &rotation)
177     Geom::Point center = c;
178     Geom::Translate const s(c);
179     Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s);
180     // Rotate item.
181     sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * (Geom::Matrix)affine);
182     // Use each item's own transform writer, consistent with sp_selection_apply_affine()
183     sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform);
184     // Restore the center position (it's changed because the bbox center changed)
185     if (item->isCenterSet()) {
186         item->setCenter(c);
187         item->updateRepr();
188     }
191 /* Method to scale items */
192 void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale  const &scale)
194     Geom::Translate const s(c);
195     sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * s.inverse() * scale * s  );
196     sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform);
199 static void sp_spray_context_init(SPSprayContext *tc)
201     SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
203     event_context->cursor_shape = cursor_spray_xpm;
204     event_context->hot_x = 4;
205     event_context->hot_y = 4;
207     /* attributes */
208     tc->dragging = FALSE;
209     tc->distrib = 1;
210     tc->width = 0.2;
211     tc->force = 0.2;
212     tc->ratio = 0;
213     tc->tilt=0;
214     tc->mean = 0.2;
215     tc->rotation_variation=0;
216     tc->standard_deviation=0.2;
217     tc->scale=1;
218     tc->scale_variation = 1;
219     tc->pressure = TC_DEFAULT_PRESSURE;
221     tc->is_dilating = false;
222     tc->has_dilated = false;
224     tc->do_h = true;
225     tc->do_s = true;
226     tc->do_l = true;
227     tc->do_o = false;
229     new (&tc->style_set_connection) sigc::connection();
232 static void sp_spray_context_dispose(GObject *object)
234     SPSprayContext *tc = SP_SPRAY_CONTEXT(object);
236     tc->style_set_connection.disconnect();
237     tc->style_set_connection.~connection();
239     if (tc->dilate_area) {
240         gtk_object_destroy(GTK_OBJECT(tc->dilate_area));
241         tc->dilate_area = NULL;
242     }
244     if (tc->_message_context) {
245         delete tc->_message_context;
246     }
248     G_OBJECT_CLASS(parent_class)->dispose(object);
251 bool is_transform_modes(gint mode)
253     return (mode == SPRAY_MODE_COPY ||
254             mode == SPRAY_MODE_CLONE ||
255             mode == SPRAY_MODE_SINGLE_PATH ||
256             mode == SPRAY_OPTION);
259 void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/)
261    SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
262    SPDesktop *desktop = event_context->desktop;
264                 guint num = 0;
265                 gchar *sel_message = NULL;
266                 if (!desktop->selection->isEmpty()) {
267                     num = g_slist_length((GSList *) desktop->selection->itemList());
268                     sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
269                 } else {
270                     sel_message = g_strdup_printf(_("<b>Nothing</b> selected"));
271                 }
274    switch (tc->mode) {
275        case SPRAY_MODE_COPY:
276            tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>copies</b> of the initial selection"), sel_message);
277            break;
278        case SPRAY_MODE_CLONE:
279            tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>clones</b> of the initial selection"), sel_message);
280            break;
281        case SPRAY_MODE_SINGLE_PATH:
282            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);
283            break;
284        default:
285            break;
286    }
287    sp_event_context_update_cursor(event_context);
288    g_free(sel_message);
291 static void sp_spray_context_setup(SPEventContext *ec)
293     SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
295     if (((SPEventContextClass *) parent_class)->setup)
296         ((SPEventContextClass *) parent_class)->setup(ec);
298     {
299         /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
300         SPCurve *c = new SPCurve();
301         const double C1 = 0.552;
302         c->moveto(-1,0);
303         c->curveto(-1, C1, -C1, 1, 0, 1 );
304         c->curveto(C1, 1, 1, C1, 1, 0 );
305         c->curveto(1, -C1, C1, -1, 0, -1 );
306         c->curveto(-C1, -1, -1, -C1, -1, 0 );
307         c->closepath();
308         tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c);
309         c->unref();
310         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0);
311         sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
312         sp_canvas_item_hide(tc->dilate_area);
313     }
315     tc->is_drawing = false;
317     tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
319     sp_event_context_read(ec, "distrib");
320     sp_event_context_read(ec, "width");
321     sp_event_context_read(ec, "ratio");
322     sp_event_context_read(ec, "tilt");
323     sp_event_context_read(ec, "rotation_variation");
324     sp_event_context_read(ec, "scale_variation");
325     sp_event_context_read(ec, "mode");
326     sp_event_context_read(ec, "population");
327     sp_event_context_read(ec, "force");
328     sp_event_context_read(ec, "mean");
329     sp_event_context_read(ec, "standard_deviation");
330     sp_event_context_read(ec, "usepressure");
331     sp_event_context_read(ec, "Scale");
332     sp_event_context_read(ec, "doh");
333     sp_event_context_read(ec, "dol");
334     sp_event_context_read(ec, "dos");
335     sp_event_context_read(ec, "doo");
337     ;
339     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
340     if (prefs->getBool("/tools/spray/selcue")) {
341         ec->enableSelectionCue();
342     }
344     if (prefs->getBool("/tools/spray/gradientdrag")) {
345         ec->enableGrDrag();
346     }
349 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
351     SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
352     Glib::ustring path = val->getEntryName();
354     if (path == "width") {
355         tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100);
356     } else if (path == "mode") {
357         tc->mode = val->getInt();
358         sp_spray_update_cursor(tc, false);
359     } else if (path == "distribution") {
360         tc->distrib = val->getInt(1);
361     } else if (path == "population") {
362         tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100);
363      } else if (path == "tilt") {
364         tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0);
365      } else if (path == "ratio") {
366         tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9);
367     } else if (path == "force") {
368         tc->force = CLAMP(val->getDouble(1.0), 0, 1.0);
369     } else if (path == "rotation_variation") {
370         tc->rotation_variation = CLAMP(val->getDouble(0.0), 0, 100.0);
371     } else if (path == "scale_variation") {
372         tc->scale_variation = CLAMP(val->getDouble(1.0), 0, 100.0);
373     } else if (path == "mean") {
374         tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100);
375     } else if (path == "standard_deviation") {
376         tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100);
377     } else if (path == "usepressure") {
378         tc->usepressure = val->getBool();
379     } else if (path == "doh") {
380         tc->do_h = val->getBool();
381     } else if (path == "dos") {
382         tc->do_s = val->getBool();
383     } else if (path == "dol") {
384         tc->do_l = val->getBool();
385     } else if (path == "doo") {
386         tc->do_o = val->getBool();
387     }
390 static void sp_spray_extinput(SPSprayContext *tc, GdkEvent *event)
392     if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure))
393         tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
394     else
395         tc->pressure = TC_DEFAULT_PRESSURE;
398 double get_dilate_radius(SPSprayContext *tc)
401     return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom();
406 double get_path_force(SPSprayContext *tc)
408     double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
409         /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom());
410     if (force > 3) {
411         force += 4 * (force - 3);
412     }
413     return force * tc->force;
416 double get_path_mean(SPSprayContext *tc)
418     return tc->mean;
421 double get_path_standard_deviation(SPSprayContext *tc)
423     return tc->standard_deviation;
426 double get_move_force(SPSprayContext *tc)
428     double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
429     return force * tc->force;
432 double get_move_mean(SPSprayContext *tc)
434     return tc->mean;
437 double get_move_standard_deviation(SPSprayContext *tc)
439     return tc->standard_deviation;
442 /** Method to handle the distribution of the items */
443 void random_position( double &r, double &p, double &a, double &s, int choice)
445     if (choice == 0) // 1 : uniform repartition
446     {
447         r = (1-pow(g_random_double_range(0, 1),2));
448         p = g_random_double_range(0, M_PI*2);
449     }
450     if (choice == 1) // 0 : gaussian repartition
451     {
452         double r_temp =-1;
453         while(!((r_temp>=0)&&(r_temp<=1)))
454         {
455             r_temp = NormalDistribution(a,s/4);
456         }
457         // generates a number following a normal distribution
458         p = g_random_double_range(0, M_PI*2);
459         r=r_temp;
460     }
467 bool sp_spray_recursive(SPDesktop *desktop,
468                                Inkscape::Selection *selection,
469                                SPItem *item,
470                                Geom::Point p,
471                                Geom::Point /*vector*/,
472                                gint mode,
473                                double radius,
474                                double /*force*/,
475                                double population,
476                                double &scale,
477                                double scale_variation,
478                                bool /*reverse*/,
479                                double mean,
480                                double standard_deviation,
481                                double ratio,
482                                double tilt,
483                                double rotation_variation,
484                                gint _distrib )
486     bool did = false;
487     
488     if (SP_IS_BOX3D(item) ) {
489         // convert 3D boxes to ordinary groups before spraying their shapes
490         item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item)));
491         selection->add(item);
492     }
494     double _fid = g_random_double_range(0,1);
495     double angle = g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI );
496     double _scale = g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 );
497     double dr; double dp;
498     random_position(dr,dp,mean,standard_deviation,_distrib);
499     dr=dr*radius;
501     if (mode == SPRAY_MODE_COPY) {
502         Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
503         if (a) {
504             SPItem *item_copied;
505             if(_fid<=population)
506             {
507                 // duplicate
508                 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
509                 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
510                 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
511                 Inkscape::XML::Node *parent = old_repr->parent();
512                 Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
513                 parent->appendChild(copy);
515                 SPObject *new_obj = doc->getObjectByRepr(copy);
516                 item_copied = (SPItem *) new_obj;   //convertion object->item
517                 Geom::Point center=item->getCenter();
518                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
519                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
521                 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
522                 //Move the cursor p
523                 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());
524                 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
525                 did = true;
526             }
527         }
528     } else if (mode == SPRAY_MODE_SINGLE_PATH) {
530         SPItem *father;         //initial Object
531         SPItem *item_copied;    //Projected Object
532         SPItem *unionResult;    //previous union
533         SPItem *son;            //father copy
535         int i=1;
536         for (GSList *items = g_slist_copy((GSList *) selection->itemList());
537                 items != NULL;
538                 items = items->next) {
540             SPItem *item1 = (SPItem *) items->data;
541             if (i==1) {
542                 father=item1;
543             }
544             if (i==2) {
545                 unionResult=item1;
546             }
547             i++;
548         }
549         SPDocument *doc = SP_OBJECT_DOCUMENT(father);
550         Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
551         Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(father);
552         Inkscape::XML::Node *parent = old_repr->parent();
554         Geom::OptRect a = father->getBounds(sp_item_i2doc_affine(father));
555         if (a) {
556             if (i==2) {
557                 Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
558                 parent->appendChild(copy1);
559                 SPObject *new_obj1 = doc->getObjectByRepr(copy1);
560                 son = (SPItem *) new_obj1;   // conversion object->item
561                 unionResult=son;
562                 Inkscape::GC::release(copy1);
563                }
565             if (_fid<=population) { // Rules the population of objects sprayed
566                 // duplicates the father
567                 Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
568                 parent->appendChild(copy2);
569                 SPObject *new_obj2 = doc->getObjectByRepr(copy2);
570                 item_copied = (SPItem *) new_obj2;
572                 // Move around the cursor
573                 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()); 
575                 Geom::Point center=father->getCenter();
576                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
577                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
578                 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
579                 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
581                 // union and duplication
582                 selection->clear();
583                 selection->add(item_copied);
584                 selection->add(unionResult);
585                 sp_selected_path_union(selection->desktop());
586                 selection->add(father);
587                 Inkscape::GC::release(copy2);
588                 did = true;
589             }
590         }
591     } else if (mode == SPRAY_MODE_CLONE) {
592         Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
593         if (a) {
594             if(_fid<=population) {
595                 SPItem *item_copied;
596                 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
597                 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
598                 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
599                 Inkscape::XML::Node *parent = old_repr->parent();
601                 // Creation of the clone
602                 Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
603                 // Ad the clone to the list of the father's sons
604                 parent->appendChild(clone);
605                 // Generates the link between father and son attributes
606                 clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); 
608                 SPObject *clone_object = doc->getObjectByRepr(clone);
609                 // conversion object->item
610                 item_copied = (SPItem *) clone_object;
611                 Geom::Point center=item->getCenter();
612                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
613                 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
614                 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
615                 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());
616                 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
618                 Inkscape::GC::release(clone);
620                 did = true;
621             }
622         }
623     }
625     return did;
629 bool sp_spray_color_recursive(guint /*mode*/,
630                               SPItem */*item*/,
631                               SPItem */*item_at_point*/,
632                               guint32 /*fill_goal*/,
633                               bool /*do_fill*/,
634                               guint32 /*stroke_goal*/,
635                               bool /*do_stroke*/,
636                               float /*opacity_goal*/,
637                               bool /*do_opacity*/,
638                               bool /*do_blur*/,
639                               bool /*reverse*/,
640                               Geom::Point /*p*/,
641                               double /*radius*/,
642                               double /*force*/,
643                               bool /*do_h*/,
644                               bool /*do_s*/,
645                               bool /*do_l*/,
646                               bool /*do_o*/)
648     bool did = false;
650     return did;
654 bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse)
656     Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop);
657     SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop;
660     if (selection->isEmpty()) {
661         return false;
662     }
664     bool did = false;
665     double radius = get_dilate_radius(tc);
669     bool do_fill = false, do_stroke = false, do_opacity = false;
670     guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill);
671     guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke);
672     double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity);
673     if (reverse) {
674         // RGB inversion
675         fill_goal = SP_RGBA32_U_COMPOSE(
676             (255 - SP_RGBA32_R_U(fill_goal)),
677             (255 - SP_RGBA32_G_U(fill_goal)),
678             (255 - SP_RGBA32_B_U(fill_goal)),
679             (255 - SP_RGBA32_A_U(fill_goal)));
680         stroke_goal = SP_RGBA32_U_COMPOSE(
681             (255 - SP_RGBA32_R_U(stroke_goal)),
682             (255 - SP_RGBA32_G_U(stroke_goal)),
683             (255 - SP_RGBA32_B_U(stroke_goal)),
684             (255 - SP_RGBA32_A_U(stroke_goal)));
685         opacity_goal = 1 - opacity_goal;
686     }
688     double path_force = get_path_force(tc);
689     if (radius == 0 || path_force == 0) {
690         return false;
691     }
692     double path_mean = get_path_mean(tc);
693     if (radius == 0 || path_mean == 0) {
694         return false;
695     }
696     double path_standard_deviation = get_path_standard_deviation(tc);
697     if (radius == 0 || path_standard_deviation == 0) {
698         return false;
699     }
700     double move_force = get_move_force(tc);
701     double move_mean = get_move_mean(tc);
702     double move_standard_deviation = get_move_standard_deviation(tc);
705     for (GSList *items = g_slist_copy((GSList *) selection->itemList());
706          items != NULL;
707          items = items->next) {
709         SPItem *item = (SPItem *) items->data;
711         if (is_transform_modes(tc->mode)) {
712             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))
713                 did = true;
714         } else {
715             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))
716                 did = true;
717         }
718     }
720     return did;
723 void sp_spray_update_area(SPSprayContext *tc)
725         double radius = get_dilate_radius(tc);
726         Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
727         sp_canvas_item_affine_absolute(tc->dilate_area,   (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
728         sp_canvas_item_show(tc->dilate_area);
731 void sp_spray_switch_mode(SPSprayContext *tc, gint mode, bool with_shift)
733     SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); //sélectionne le bouton numéro "mode"
734     // need to set explicitly, because the prefs may not have changed by the previous
735     tc->mode = mode;
736     sp_spray_update_cursor (tc, with_shift);
739 void sp_spray_switch_mode_temporarily(SPSprayContext *tc, gint mode, bool with_shift)
741     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
742    // Juggling about so that prefs have the old value but tc->mode and the button show new mode:
743    gint now_mode = prefs->getInt("/tools/spray/mode", 0);
744    SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
745    // button has changed prefs, restore
746    prefs->setInt("/tools/spray/mode", now_mode);
747    // changing prefs changed tc->mode, restore back :)
748    tc->mode = mode;
749    sp_spray_update_cursor (tc, with_shift);
752 gint sp_spray_context_root_handler(SPEventContext *event_context,
753                                   GdkEvent *event)
755     SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context);
756     SPDesktop *desktop = event_context->desktop;
758     gint ret = FALSE;
760     switch (event->type) {
761         case GDK_ENTER_NOTIFY:
762             sp_canvas_item_show(tc->dilate_area);
763             break;
764         case GDK_LEAVE_NOTIFY:
765             sp_canvas_item_hide(tc->dilate_area);
766             break;
767         case GDK_BUTTON_PRESS:
768             if (event->button.button == 1 && !event_context->space_panning) {
770                 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) {
771                     return TRUE;
772                 }
774                 Geom::Point const motion_w(event->button.x,
775                                          event->button.y);
776                 Geom::Point const motion_dt(desktop->w2d(motion_w));
777                 tc->last_push = desktop->dt2doc(motion_dt);
779                 sp_spray_extinput(tc, event);
781                 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
782                 tc->is_drawing = true;
783                 tc->is_dilating = true;
784                                 tc->has_dilated = false;
788                 if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning)
790                                 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
794                                                           tc->has_dilated=true;
796                 ret = TRUE;
797             }
798             break;
799         case GDK_MOTION_NOTIFY:
800         {
801             Geom::Point const motion_w(event->motion.x,
802                                      event->motion.y);
803             Geom::Point motion_dt(desktop->w2d(motion_w));
804             Geom::Point motion_doc(desktop->dt2doc(motion_dt));
805             sp_spray_extinput(tc, event);
807             // draw the dilating cursor
808                 double radius = get_dilate_radius(tc);
809                 Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
810                 sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w)));
811                 sp_canvas_item_show(tc->dilate_area);
813                 guint num = 0;
814                 if (!desktop->selection->isEmpty()) {
815                     num = g_slist_length((GSList *) desktop->selection->itemList());
816                 }
817                 if (num == 0) {
818                     tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
819                 }
821             // dilating:
822             if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) {
823                 sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
824                 //tc->last_push = motion_doc;
825                 tc->has_dilated = true;
827                 // it's slow, so prevent clogging up with events
828                 gobble_motion_events(GDK_BUTTON1_MASK);
829                 return TRUE;
830             }
832         }
833         break;
834 /*Spray with the scroll*/
835         case GDK_SCROLL:
836                {
837               if (event->scroll.state & GDK_BUTTON1_MASK)
838                                   {
839                   double temp ;
840                   temp=tc->population;
841                   tc->population=1.0;
842                   desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
843                   Geom::Point const scroll_w(event->button.x,event->button.y);
844                   Geom::Point const scroll_dt = desktop->point();;
845                   Geom::Point motion_doc(desktop->dt2doc(scroll_dt));
846                        switch (event->scroll.direction)
847                        {
848                          case GDK_SCROLL_UP:
849                            {
850                                    if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
851                                    {
852                                     return TRUE;
853                                    }
854                                    tc->last_push = desktop->dt2doc(scroll_dt);
855                                    sp_spray_extinput(tc, event);
856                                    sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
857                                    tc->is_drawing = true;
858                                    tc->is_dilating = true;
859                                    tc->has_dilated = false;
860                                    if(tc->is_dilating && !event_context->space_panning)
862                                                         sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false);
866                                    tc->has_dilated=true;
867                                    tc->population=temp;
869                                    desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
871                                    ret = TRUE;
872                            }
873                                break;
874                            case GDK_SCROLL_DOWN:
875                            {
876                                    if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
877                                                                    {
878                                                                     return TRUE;
879                                                                    }
880                                                                    tc->last_push = desktop->dt2doc(scroll_dt);
881                                                                    sp_spray_extinput(tc, event);
882                                                                    sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
883                                                                    tc->is_drawing = true;
884                                                                    tc->is_dilating = true;
885                                                                    tc->has_dilated = false;
886                                                                    if(tc->is_dilating && !event_context->space_panning)
887                                                                                         sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false);
889                                                                    tc->has_dilated=true;
891                                                                    ret = TRUE;
894                            }
895                                break;
896 case GDK_SCROLL_RIGHT:
897                            {} break;
898 case GDK_SCROLL_LEFT:
899                            {} break;
900                                                                                                                    }
901                                   }
904                                break;
906                }
907     case GDK_BUTTON_RELEASE:
908     {
909         Geom::Point const motion_w(event->button.x, event->button.y);
910         Geom::Point const motion_dt(desktop->w2d(motion_w));
912         sp_canvas_end_forced_full_redraws(desktop->canvas);
913         tc->is_drawing = false;
915         if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) {
916             if (!tc->has_dilated) {
917                 // if we did not rub, do a light tap
918                 tc->pressure = 0.03;
919                 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
920             }
921             tc->is_dilating = false;
922             tc->has_dilated = false;
923             switch (tc->mode) {
924                 case SPRAY_MODE_COPY:
925                     sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
926                                      SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
927                     break;
928                 case SPRAY_MODE_CLONE:
929                     sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
930                                      SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
931                     break;
932                 case SPRAY_MODE_SINGLE_PATH:
933                     sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
934                                      SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
935                     break;
936             }
937         }
938         break;
939     }
941     case GDK_KEY_PRESS:
942         switch (get_group0_keyval (&event->key)) {
943 case GDK_j:  if (MOD__SHIFT_ONLY) {
944                 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
945                 ret = TRUE;
946             }
947 case GDK_J: if (MOD__SHIFT_ONLY) {
948                 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
949                 ret = TRUE;
950             }
952 break;
953         case GDK_m:
954         case GDK_M:
955         case GDK_0:
957             break;
958         case GDK_i:
959         case GDK_I:
960         case GDK_k:    if (MOD__SHIFT_ONLY) {
961                 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
962                 ret = TRUE;
963             }
964         case GDK_K:if (MOD__SHIFT_ONLY) {
965                 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
966                 ret = TRUE;
967             }
968 break;
970         case GDK_l: if (MOD__SHIFT_ONLY) {
971                 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
972                 ret = TRUE;
973             }
975         case GDK_L:
976  if (MOD__SHIFT_ONLY) {
977                 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
978                 ret = TRUE;
979             }
980             break;
981         case GDK_Up:
982         case GDK_KP_Up:
983             if (!MOD__CTRL_ONLY) {
984                 tc->scale += 0.05;
986                 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
987                 ret = TRUE;
988             }
989             break;
990         case GDK_Down:
991         case GDK_KP_Down:
992             if (!MOD__CTRL_ONLY) {
994                 tc->scale -= 0.05;
995                 if (tc->scale < 0.0)
996                     tc->scale = 0.0;
997                 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
999                 ret = TRUE;
1001             }
1002             break;
1003         case GDK_Right:
1004         case GDK_KP_Right:
1005             if (!MOD__CTRL_ONLY) {
1006                 tc->width += 0.01;
1007                 if (tc->width > 1.0)
1008                     tc->width = 1.0;
1009                 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x
1010                 sp_spray_update_area(tc);
1011                 ret = TRUE;
1012             }
1013             break;
1014         case GDK_Left:
1015         case GDK_KP_Left:
1016             if (!MOD__CTRL_ONLY) {
1017                 tc->width -= 0.01;
1018                 if (tc->width < 0.01)
1019                     tc->width = 0.01;
1020                 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1021                 sp_spray_update_area(tc);
1022                 ret = TRUE;
1023             }
1024             break;
1025         case GDK_Home:
1026         case GDK_KP_Home:
1027             tc->width = 0.01;
1028             desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1029             sp_spray_update_area(tc);
1030             ret = TRUE;
1031             break;
1032         case GDK_End:
1033         case GDK_KP_End:
1034             tc->width = 1.0;
1035             desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1036             sp_spray_update_area(tc);
1037             ret = TRUE;
1038             break;
1039         case GDK_x:
1040         case GDK_X:
1041             if (MOD__ALT_ONLY) {
1042                 desktop->setToolboxFocusTo ("altx-spray");
1043                 ret = TRUE;
1044             }
1045             break;
1047         case GDK_Shift_L:
1048         case GDK_Shift_R:
1049             sp_spray_update_cursor(tc, true);
1050             break;
1051 /*Set the scale to 1*/
1052         case GDK_Control_L:
1053                 tc->scale=1;
1054         default:
1055             break;
1056         }
1057         break;
1059     case GDK_KEY_RELEASE: {
1060         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1061         switch (get_group0_keyval(&event->key)) {
1062             case GDK_Shift_L:
1063             case GDK_Shift_R:
1064                 sp_spray_update_cursor(tc, false);
1065                 break;
1066             case GDK_Control_L:
1067             case GDK_Control_R:
1068                 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1069                 tc->_message_context->clear();
1070                 break;
1071             default:
1072                 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1073                 break;
1074         }
1075     }
1077     default:
1078         break;
1079     }
1081     if (!ret) {
1082         if (((SPEventContextClass *) parent_class)->root_handler) {
1083             ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
1084         }
1085     }
1087     return ret;
1090 /*
1091   Local Variables:
1092   mode:c++
1093   c-file-style:"stroustrup"
1094   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1095   indent-tabs-mode:nil
1096   fill-column:99
1097   End:
1098 */
1099 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :