1c050ea06458cadc5a250f091800fc5ae38de1d1
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;
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)
134 {
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);
142 }
144 //Fin de la création de NormalDistribution
146 GtkType
147 sp_spray_context_get_type(void)
148 {
149 static GType type = 0;
150 if (!type) {
151 GTypeInfo info = {
152 sizeof(SPSprayContextClass),
153 NULL, NULL,
154 (GClassInitFunc) sp_spray_context_class_init,
155 NULL, NULL,
156 sizeof(SPSprayContext),
157 4,
158 (GInstanceInitFunc) sp_spray_context_init,
159 NULL, /* value_table */
160 };
161 type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSprayContext", &info, (GTypeFlags)0);
162 }
163 return type;
164 }
166 static void
167 sp_spray_context_class_init(SPSprayContextClass *klass)
168 {
169 GObjectClass *object_class = (GObjectClass *) klass;
170 SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
172 parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
174 object_class->dispose = sp_spray_context_dispose;
176 event_context_class->setup = sp_spray_context_setup;
177 event_context_class->set = sp_spray_context_set;
178 event_context_class->root_handler = sp_spray_context_root_handler;
179 }
180 /*Method to rotate items*/
181 void
182 sp_spray_rotate_rel(Geom::Point c,SPDesktop *desktop,SPItem *item, Geom::Rotate const &rotation)
183 {
185 Geom::Point center = c;
186 Geom::Translate const s(c);
187 Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s);
189 // Rotate item.
190 sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * (Geom::Matrix)affine);
191 // Use each item's own transform writer, consistent with sp_selection_apply_affine()
192 sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform);
194 // Restore the center position (it's changed because the bbox center changed)
195 if (item->isCenterSet()) {
196 item->setCenter(c);
197 item->updateRepr();
198 }
199 }
200 /*Method to scale items*/
201 void
202 sp_spray_scale_rel (Geom::Point c, SPDesktop *desktop, SPItem *item, Geom::Scale const &scale)
203 {
204 Geom::Translate const s(c);
207 sp_item_set_i2d_affine(item, sp_item_i2d_affine(item) * s.inverse() * scale * s );
208 sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform);
211 }
212 static void
213 sp_spray_context_init(SPSprayContext *tc)
214 {
215 SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
219 event_context->cursor_shape = cursor_spray_xpm;
220 event_context->hot_x = 4;
221 event_context->hot_y = 4;
223 /* attributes */
224 tc->dragging = FALSE;
225 tc->distrib = 1;
226 tc->width = 0.2;
227 tc->force = 0.2;
228 tc->ratio = 0;
229 tc->tilt=0;
230 tc->mean = 0.2;
231 tc->rot_min=0;
232 tc->rot_max=0;
233 tc->standard_deviation=0.2;
234 tc->scale=1;
235 tc->scale_min = 1;
236 tc->scale_max=1;
237 tc->pressure = TC_DEFAULT_PRESSURE;
239 tc->is_dilating = false;
240 tc->has_dilated = false;
242 tc->do_h = true;
243 tc->do_s = true;
244 tc->do_l = true;
245 tc->do_o = false;
247 new (&tc->style_set_connection) sigc::connection();
248 }
250 static void
251 sp_spray_context_dispose(GObject *object)
252 {
253 SPSprayContext *tc = SP_SPRAY_CONTEXT(object);
255 tc->style_set_connection.disconnect();
256 tc->style_set_connection.~connection();
258 if (tc->dilate_area) {
259 gtk_object_destroy(GTK_OBJECT(tc->dilate_area));
260 tc->dilate_area = NULL;
261 }
263 if (tc->_message_context) {
264 delete tc->_message_context;
265 }
267 G_OBJECT_CLASS(parent_class)->dispose(object);
268 }
270 bool is_transform_modes (gint mode)
271 {
272 return (mode == SPRAY_MODE_COPY ||
273 mode == SPRAY_MODE_CLONE ||
274 mode == SPRAY_MODE_SINGLE_PATH ||
275 mode == SPRAY_OPTION);
276 }
278 void
279 sp_spray_update_cursor (SPSprayContext *tc, bool with_shift)
280 {
281 SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
282 SPDesktop *desktop = event_context->desktop;
284 guint num = 0;
285 gchar *sel_message = NULL;
286 if (!desktop->selection->isEmpty()) {
287 num = g_slist_length((GSList *) desktop->selection->itemList());
288 sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
289 } else {
290 sel_message = g_strdup_printf(_("<b>Nothing</b> selected"));
291 }
294 switch (tc->mode) {
295 case SPRAY_MODE_COPY:
296 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>copies</b> of the initial selection"), sel_message);
297 break;
298 case SPRAY_MODE_CLONE:
299 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>clones</b> of the initial selection"), sel_message);
300 break;
301 case SPRAY_MODE_SINGLE_PATH:
302 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);
303 break;
304 case SPRAY_OPTION:
305 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Modify <b>spray</b> options"), sel_message);
306 break;
307 }
308 sp_event_context_update_cursor(event_context);
309 g_free(sel_message);
310 }
312 static void
313 sp_spray_context_setup(SPEventContext *ec)
314 {
315 SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
317 if (((SPEventContextClass *) parent_class)->setup)
318 ((SPEventContextClass *) parent_class)->setup(ec);
320 {
321 /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
322 SPCurve *c = new SPCurve();
323 const double C1 = 0.552;
324 c->moveto(-1,0);
325 c->curveto(-1, C1, -C1, 1, 0, 1 );
326 c->curveto(C1, 1, 1, C1, 1, 0 );
327 c->curveto(1, -C1, C1, -1, 0, -1 );
328 c->curveto(-C1, -1, -1, -C1, -1, 0 );
329 c->closepath();
330 tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c);
331 c->unref();
332 sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0);
333 sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
334 sp_canvas_item_hide(tc->dilate_area);
335 }
337 tc->is_drawing = false;
339 tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
341 sp_event_context_read(ec, "distrib");
342 sp_event_context_read(ec, "width");
343 sp_event_context_read(ec, "ratio");
344 sp_event_context_read(ec, "tilt");
345 sp_event_context_read(ec, "rot_min");
346 sp_event_context_read(ec, "rot_max");
347 sp_event_context_read(ec, "scale_min");
348 sp_event_context_read(ec, "scale_max");
349 sp_event_context_read(ec, "mode");
350 sp_event_context_read(ec, "population");
351 sp_event_context_read(ec, "force");
352 sp_event_context_read(ec, "mean");
353 sp_event_context_read(ec, "standard_deviation");
354 sp_event_context_read(ec, "usepressure");
355 sp_event_context_read(ec, "Rotation min");
356 sp_event_context_read(ec, "Rotation max");
357 sp_event_context_read(ec, "Scale");
358 sp_event_context_read(ec, "doh");
359 sp_event_context_read(ec, "dol");
360 sp_event_context_read(ec, "dos");
361 sp_event_context_read(ec, "doo");
363 ;
365 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
366 if (prefs->getBool("/tools/spray/selcue")) {
367 ec->enableSelectionCue();
368 }
370 if (prefs->getBool("/tools/spray/gradientdrag")) {
371 ec->enableGrDrag();
372 }
373 }
375 static void
376 sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
377 {
378 SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
379 Glib::ustring path = val->getEntryName();
381 if (path == "width") {
382 tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100);
383 } else if (path == "mode") {
384 tc->mode = val->getInt();
385 sp_spray_update_cursor(tc, false);
386 } else if (path == "distribution") {
387 tc->distrib = val->getInt(1);
388 } else if (path == "population") {
389 tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100);
390 } else if (path == "tilt") {
391 tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0);
392 } else if (path == "ratio") {
393 tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9);
394 } else if (path == "force") {
395 tc->force = CLAMP(val->getDouble(1.0), 0, 1.0);
396 } else if (path == "rot_min") {
397 tc->rot_min = CLAMP(val->getDouble(0), 0, 10.0);
398 } else if (path == "rot_max") {
399 tc->rot_max = CLAMP(val->getDouble(0), 0, 10.0);
400 } else if (path == "scale_min") {
401 tc->scale_min = CLAMP(val->getDouble(1.0), 0, 10.0);
402 } else if (path == "scale_max") {
403 tc->scale_max = CLAMP(val->getDouble(1.0), 0, 10.0);
404 } else if (path == "mean") {
405 tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100);
406 } else if (path == "standard_deviation") {
407 tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100);
408 } else if (path == "usepressure") {
409 tc->usepressure = val->getBool();
410 } else if (path == "doh") {
411 tc->do_h = val->getBool();
412 } else if (path == "dos") {
413 tc->do_s = val->getBool();
414 } else if (path == "dol") {
415 tc->do_l = val->getBool();
416 } else if (path == "doo") {
417 tc->do_o = val->getBool();
418 }
419 }
421 static void
422 sp_spray_extinput(SPSprayContext *tc, GdkEvent *event)
423 {
424 if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure))
425 tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
426 else
427 tc->pressure = TC_DEFAULT_PRESSURE;
428 }
430 double
431 get_dilate_radius (SPSprayContext *tc)
432 {
434 return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom();
437 }
439 double
440 get_path_force (SPSprayContext *tc)
441 {
442 double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
443 /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom());
444 if (force > 3) {
445 force += 4 * (force - 3);
446 }
447 return force * tc->force;
448 }
450 double
451 get_path_mean (SPSprayContext *tc)
452 {
453 return tc->mean;
454 }
456 double
457 get_path_standard_deviation (SPSprayContext *tc)
458 {
459 return tc->standard_deviation;
460 }
462 double
463 get_move_force (SPSprayContext *tc)
464 {
465 double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
466 return force * tc->force;
467 }
469 double
470 get_move_mean (SPSprayContext *tc)
471 {
472 return tc->mean;
473 }
475 double
476 get_move_standard_deviation (SPSprayContext *tc)
477 {
478 return tc->standard_deviation;
479 }
481 /* Method to handle the distribution of the items */
484 void random_position( double &r, double &p, double &a, double &s, int choix)
485 {
486 if (choix == 0) // Mode 1 : uniform repartition
487 {
488 r = (1-pow(g_random_double_range(0, 1),2));
489 p = g_random_double_range(0, M_PI*2);
490 }
491 if (choix == 1) //Mode 0 : gaussian repartition
492 {
493 double r_temp =-1;
494 while(!((r_temp>=0)&&(r_temp<=1)))
495 {
496 r_temp = NormalDistribution(a,s/4);
497 }
498 // generates a number following a normal distribution
499 p = g_random_double_range(0, M_PI*2);
500 r=r_temp;
501 /* if (r_temp<=0) r=0;
502 else
503 {
504 if (r_temp>1) r=1;
505 else r = r_temp;
506 }*/
507 }
508 }
514 bool
515 sp_spray_dilate_recursive (SPDesktop *desktop, Inkscape::Selection *selection, SPItem *item, Geom::Point p, Geom::Point vector, gint mode, double radius, double force, double population, double &scale, double scale_min, double scale_max, bool reverse, double mean, double standard_deviation, double ratio,double tilt, double rot_min, double rot_max, gint _distrib )
516 {
520 bool did = false;
522 if (SP_IS_BOX3D(item) /*&& !is_transform_modes(mode)*/) {
523 // convert 3D boxes to ordinary groups before spraying their shapes
524 item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item)));
525 selection->add(item);
526 }
528 /*if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
529 GSList *items = g_slist_prepend (NULL, item);
530 GSList *selected = NULL;
531 GSList *to_select = NULL;
532 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
533 sp_item_list_to_curves (items, &selected, &to_select);
534 g_slist_free (items);
535 SPObject* newObj = doc->getObjectByRepr((Inkscape::XML::Node *) to_select->data);
536 g_slist_free (to_select);
537 item = (SPItem *) newObj;
538 // selection->add(item);
539 }
540 */
541 /*if (SP_IS_GROUP(item) && !SP_IS_BOX3D(item)) {
542 for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
543 if (SP_IS_ITEM(child)) {
544 if (sp_spray_dilate_recursive (desktop,selection, SP_ITEM(child), p, vector, mode, radius, force, population, scale, scale_min, scale_max, reverse, mean, standard_deviation,ratio,tilt, rot_min, rot_max,_distrib))
545 did = true;
546 }
547 }
549 } else {*/
550 if (mode == SPRAY_MODE_COPY) {
552 Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
553 if (a) {
554 double dr; double dp;
555 random_position(dr,dp,mean,standard_deviation,_distrib);
556 dr=dr*radius;
557 double _fid = g_random_double_range(0,1);
558 SPItem *item_copied;
559 double angle = g_random_double_range(rot_min, rot_max);
560 double _scale = g_random_double_range(scale_min, scale_max);
561 if(_fid<=population)
562 {
563 // duplicate
564 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
565 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
566 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
567 Inkscape::XML::Node *parent = old_repr->parent();
568 Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
569 parent->appendChild(copy);
571 SPObject *new_obj = doc->getObjectByRepr(copy);
572 item_copied = (SPItem *) new_obj; //convertion object->item
573 Geom::Point center=item->getCenter();
574 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
575 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
577 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
578 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
579 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
585 did = true;
586 }
587 }
589 } else if (mode == SPRAY_MODE_SINGLE_PATH) {
592 SPItem *Pere; //Objet initial
593 SPItem *item_copied;//Objet projeté
594 SPItem *Union;//Union précédente
595 SPItem *fils;//Copie du père
597 // GSList *items = g_slist_copy((GSList *) selection->itemList()); //Récupère la liste des objects sélectionnés
598 //Pere = (SPItem *) items->data;//Le premier objet est le père du spray
600 int i=1;
601 for (GSList *items = g_slist_copy((GSList *) selection->itemList());
602 items != NULL;
603 items = items->next) {
605 SPItem *item1 = (SPItem *) items->data;
606 if (i==1) {
607 Pere=item1;
608 }
609 if (i==2) {
610 Union=item1;
611 }
612 i++;
613 }
614 SPDocument *doc = SP_OBJECT_DOCUMENT(Pere);
615 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
616 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(Pere);
617 //SPObject *old_obj = doc->getObjectByRepr(old_repr);
618 Inkscape::XML::Node *parent = old_repr->parent();
620 Geom::OptRect a = Pere->getBounds(sp_item_i2doc_affine(Pere));
621 if (a) {
622 double dr; double dp; //initialisation des variables
623 random_position(dr,dp,mean,standard_deviation,_distrib);
624 dr=dr*radius;
625 double _fid = g_random_double_range(0,1);
626 double angle = (g_random_double_range(rot_min, rot_max));
627 double _scale = g_random_double_range(scale_min, scale_max);
628 if (i==2) {
629 Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
630 parent->appendChild(copy1);
631 SPObject *new_obj1 = doc->getObjectByRepr(copy1);
632 fils = (SPItem *) new_obj1; //conversion object->item
633 Union=fils;
634 Inkscape::GC::release(copy1);
635 }
637 if (_fid<=population) { //Rules the population of objects sprayed
638 // duplicates the father
639 Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
640 parent->appendChild(copy2);
641 SPObject *new_obj2 = doc->getObjectByRepr(copy2);
642 item_copied = (SPItem *) new_obj2;
644 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
646 Geom::Point center=Pere->getCenter();
647 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
648 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
649 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
650 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
652 //UNION et surduplication
653 selection->clear();
654 selection->add(item_copied);
655 selection->add(Union);
656 sp_selected_path_union(selection->desktop());
657 selection->add(Pere);
658 Inkscape::GC::release(copy2);
659 did = true;
660 }
662 }
663 } else if (mode == SPRAY_MODE_CLONE) {
665 Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
666 if (a) {
667 double dr; double dp;
668 random_position(dr,dp,mean,standard_deviation,_distrib);
669 dr=dr*radius;
670 double _fid = g_random_double_range(0,1);
671 double angle = (g_random_double_range(rot_min, rot_max));
672 double _scale = g_random_double_range(scale_min, scale_max);
674 if(_fid<=population)
675 {
676 SPItem *item_copied;
677 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
678 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
679 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
680 Inkscape::XML::Node *parent = old_repr->parent();
682 //Creation of the clone
683 Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
684 parent->appendChild(clone); //Ajout du clone à la liste d'enfants du père (selection initiale
685 clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); //Génère le lien entre les attributs du père et du fils
687 SPObject *clone_object = doc->getObjectByRepr(clone);
688 item_copied = (SPItem *) clone_object;//conversion object->item
689 Geom::Point center=item->getCenter();
690 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
691 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
692 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
693 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());
694 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
696 Inkscape::GC::release(clone);
698 did = true;
699 } }}
700 return did;
702 }
705 bool
706 sp_spray_color_recursive (guint mode, SPItem *item, SPItem *item_at_point,
707 guint32 fill_goal, bool do_fill,
708 guint32 stroke_goal, bool do_stroke,
709 float opacity_goal, bool do_opacity,
710 bool do_blur, bool reverse,
711 Geom::Point p, double radius, double force,
712 bool do_h, bool do_s, bool do_l, bool do_o)
713 {
714 bool did = false;
716 return did;
717 }
720 bool
721 sp_spray_dilate (SPSprayContext *tc, Geom::Point event_p, Geom::Point p, Geom::Point vector, bool reverse)
722 {
723 Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop);
724 SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop;
727 if (selection->isEmpty()) {
728 return false;
729 }
731 bool did = false;
732 double radius = get_dilate_radius(tc);
736 bool do_fill = false, do_stroke = false, do_opacity = false;
737 guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill);
738 guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke);
739 double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity);
740 if (reverse) {
741 #if 0
742 // HSL inversion
743 float hsv[3];
744 float rgb[3];
745 sp_color_rgb_to_hsv_floatv (hsv,
746 SP_RGBA32_R_F(fill_goal),
747 SP_RGBA32_G_F(fill_goal),
748 SP_RGBA32_B_F(fill_goal));
749 sp_color_hsv_to_rgb_floatv (rgb, hsv[0]<.5? hsv[0]+.5 : hsv[0]-.5, 1 - hsv[1], 1 - hsv[2]);
750 fill_goal = SP_RGBA32_F_COMPOSE(rgb[0], rgb[1], rgb[2], 1);
751 sp_color_rgb_to_hsv_floatv (hsv,
752 SP_RGBA32_R_F(stroke_goal),
753 SP_RGBA32_G_F(stroke_goal),
754 SP_RGBA32_B_F(stroke_goal));
755 sp_color_hsv_to_rgb_floatv (rgb, hsv[0]<.5? hsv[0]+.5 : hsv[0]-.5, 1 - hsv[1], 1 - hsv[2]);
756 stroke_goal = SP_RGBA32_F_COMPOSE(rgb[0], rgb[1], rgb[2], 1);
757 #else
758 // RGB inversion
759 fill_goal = SP_RGBA32_U_COMPOSE(
760 (255 - SP_RGBA32_R_U(fill_goal)),
761 (255 - SP_RGBA32_G_U(fill_goal)),
762 (255 - SP_RGBA32_B_U(fill_goal)),
763 (255 - SP_RGBA32_A_U(fill_goal)));
764 stroke_goal = SP_RGBA32_U_COMPOSE(
765 (255 - SP_RGBA32_R_U(stroke_goal)),
766 (255 - SP_RGBA32_G_U(stroke_goal)),
767 (255 - SP_RGBA32_B_U(stroke_goal)),
768 (255 - SP_RGBA32_A_U(stroke_goal)));
769 #endif
770 opacity_goal = 1 - opacity_goal;
771 }
773 double path_force = get_path_force(tc);
774 if (radius == 0 || path_force == 0) {
775 return false;
776 }
777 double path_mean = get_path_mean(tc);
778 if (radius == 0 || path_mean == 0) {
779 return false;
780 }
781 double path_standard_deviation = get_path_standard_deviation(tc);
782 if (radius == 0 || path_standard_deviation == 0) {
783 return false;
784 }
785 double move_force = get_move_force(tc);
786 double move_mean = get_move_mean(tc);
787 double move_standard_deviation = get_move_standard_deviation(tc);
790 for (GSList *items = g_slist_copy((GSList *) selection->itemList());
791 items != NULL;
792 items = items->next) {
794 SPItem *item = (SPItem *) items->data;
796 /*if (is_color_modes (tc->mode)) {
797 if (do_fill || do_stroke || do_opacity) {
798 if (sp_spray_color_recursive (tc->mode, item, item_at_point,
799 fill_goal, do_fill,
800 stroke_goal, do_stroke,
801 opacity_goal, do_opacity,
802 tc->mode == SPRAY_MODE_BLUR, reverse,
803 p, radius, color_force, tc->do_h, tc->do_s, tc->do_l, tc->do_o))
804 did = true;
805 }
806 }else*/ if (is_transform_modes(tc->mode)) {
807 if (sp_spray_dilate_recursive (desktop,selection, item, p, vector, tc->mode, radius, move_force, tc->population,tc->scale, tc->scale_min, tc->scale_max, reverse, move_mean, move_standard_deviation,tc->ratio,tc->tilt, tc->rot_min, tc->rot_max, tc->distrib))
808 did = true;
809 } else {
810 if (sp_spray_dilate_recursive (desktop,selection, item, p, vector, tc->mode, radius, path_force, tc->population,tc->scale, tc->scale_min, tc->scale_max, reverse, path_mean, path_standard_deviation,tc->ratio,tc->tilt, tc->rot_min, tc->rot_max, tc->distrib))
811 did = true;
812 }
813 }
815 return did;
816 }
818 void
819 sp_spray_update_area (SPSprayContext *tc)
820 {
821 double radius = get_dilate_radius(tc);
822 Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
823 sp_canvas_item_affine_absolute(tc->dilate_area, (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
824 sp_canvas_item_show(tc->dilate_area);
825 }
827 void
828 sp_spray_switch_mode (SPSprayContext *tc, gint mode, bool with_shift)
829 {
830 SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); //sélectionne le bouton numéro "mode"
831 // need to set explicitly, because the prefs may not have changed by the previous
832 tc->mode = mode;
833 sp_spray_update_cursor (tc, with_shift);
834 }
836 void
837 sp_spray_switch_mode_temporarily (SPSprayContext *tc, gint mode, bool with_shift)
838 {
839 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
840 // Juggling about so that prefs have the old value but tc->mode and the button show new mode:
841 gint now_mode = prefs->getInt("/tools/spray/mode", 0);
842 SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
843 // button has changed prefs, restore
844 prefs->setInt("/tools/spray/mode", now_mode);
845 // changing prefs changed tc->mode, restore back :)
846 tc->mode = mode;
847 sp_spray_update_cursor (tc, with_shift);
848 }
850 gint
851 sp_spray_context_root_handler(SPEventContext *event_context,
852 GdkEvent *event)
853 {
854 SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context);
855 SPDesktop *desktop = event_context->desktop;
857 gint ret = FALSE;
859 switch (event->type) {
860 case GDK_ENTER_NOTIFY:
861 sp_canvas_item_show(tc->dilate_area);
862 break;
863 case GDK_LEAVE_NOTIFY:
864 sp_canvas_item_hide(tc->dilate_area);
865 break;
866 case GDK_BUTTON_PRESS:
867 if (event->button.button == 1 && !event_context->space_panning) {
869 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) {
870 return TRUE;
871 }
873 Geom::Point const motion_w(event->button.x,
874 event->button.y);
875 Geom::Point const motion_dt(desktop->w2d(motion_w));
876 tc->last_push = desktop->dt2doc(motion_dt);
878 sp_spray_extinput(tc, event);
880 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
881 tc->is_drawing = true;
882 tc->is_dilating = true;
883 tc->has_dilated = false;
887 if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning)
889 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
893 tc->has_dilated=true;
895 ret = TRUE;
896 }
897 break;
898 case GDK_MOTION_NOTIFY:
899 {
900 Geom::Point const motion_w(event->motion.x,
901 event->motion.y);
902 Geom::Point motion_dt(desktop->w2d(motion_w));
903 Geom::Point motion_doc(desktop->dt2doc(motion_dt));
904 sp_spray_extinput(tc, event);
906 // draw the dilating cursor
907 double radius = get_dilate_radius(tc);
908 Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
909 sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w)));
910 sp_canvas_item_show(tc->dilate_area);
912 guint num = 0;
913 if (!desktop->selection->isEmpty()) {
914 num = g_slist_length((GSList *) desktop->selection->itemList());
915 }
916 if (num == 0) {
917 tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
918 }
920 // dilating:
921 if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) {
922 sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
923 //tc->last_push = motion_doc;
924 tc->has_dilated = true;
926 // it's slow, so prevent clogging up with events
927 gobble_motion_events(GDK_BUTTON1_MASK);
928 return TRUE;
929 }
931 }
932 break;
933 /*Spray with the scroll*/
934 case GDK_SCROLL:
935 {
936 if (event->scroll.state & GDK_BUTTON1_MASK)
937 {
938 double temp ;
939 temp=tc->population;
940 tc->population=1.0;
941 desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
942 Geom::Point const scroll_w(event->button.x,event->button.y);
943 Geom::Point const scroll_dt = desktop->point();;
944 Geom::Point motion_doc(desktop->dt2doc(scroll_dt));
945 switch (event->scroll.direction)
946 {
947 case GDK_SCROLL_UP:
948 {
949 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
950 {
951 return TRUE;
952 }
953 tc->last_push = desktop->dt2doc(scroll_dt);
954 sp_spray_extinput(tc, event);
955 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
956 tc->is_drawing = true;
957 tc->is_dilating = true;
958 tc->has_dilated = false;
959 if(tc->is_dilating && !event_context->space_panning)
961 sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false);
965 tc->has_dilated=true;
966 tc->population=temp;
968 desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
970 ret = TRUE;
971 }
972 break;
973 case GDK_SCROLL_DOWN:
974 {
975 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
976 {
977 return TRUE;
978 }
979 tc->last_push = desktop->dt2doc(scroll_dt);
980 sp_spray_extinput(tc, event);
981 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
982 tc->is_drawing = true;
983 tc->is_dilating = true;
984 tc->has_dilated = false;
985 if(tc->is_dilating && !event_context->space_panning)
986 sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false);
988 tc->has_dilated=true;
990 ret = TRUE;
993 }
994 break;
995 case GDK_SCROLL_RIGHT:
996 {} break;
997 case GDK_SCROLL_LEFT:
998 {} break;
999 }
1000 }
1003 break;
1005 }
1006 case GDK_BUTTON_RELEASE:
1007 {
1008 Geom::Point const motion_w(event->button.x, event->button.y);
1009 Geom::Point const motion_dt(desktop->w2d(motion_w));
1011 sp_canvas_end_forced_full_redraws(desktop->canvas);
1012 tc->is_drawing = false;
1014 if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) {
1015 if (!tc->has_dilated) {
1016 // if we did not rub, do a light tap
1017 tc->pressure = 0.03;
1018 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
1019 }
1020 tc->is_dilating = false;
1021 tc->has_dilated = false;
1022 switch (tc->mode) {
1023 case SPRAY_MODE_COPY:
1024 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
1025 SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
1026 break;
1027 case SPRAY_MODE_CLONE:
1028 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
1029 SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
1030 break;
1031 case SPRAY_MODE_SINGLE_PATH:
1032 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
1033 SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
1034 break;
1035 }
1036 }
1037 break;
1038 }
1040 case GDK_KEY_PRESS:
1041 switch (get_group0_keyval (&event->key)) {
1042 case GDK_j: if (MOD__SHIFT_ONLY) {
1043 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
1044 ret = TRUE;
1045 }
1046 case GDK_J: if (MOD__SHIFT_ONLY) {
1047 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
1048 ret = TRUE;
1049 }
1051 break;
1052 case GDK_m:
1053 case GDK_M:
1054 case GDK_0:
1056 break;
1057 case GDK_i:
1058 case GDK_I:
1059 case GDK_k: if (MOD__SHIFT_ONLY) {
1060 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
1061 ret = TRUE;
1062 }
1063 case GDK_K:if (MOD__SHIFT_ONLY) {
1064 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
1065 ret = TRUE;
1066 }
1067 break;
1069 case GDK_l: if (MOD__SHIFT_ONLY) {
1070 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
1071 ret = TRUE;
1072 }
1074 case GDK_L:
1075 if (MOD__SHIFT_ONLY) {
1076 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
1077 ret = TRUE;
1078 }
1079 break;
1080 case GDK_Up:
1081 case GDK_KP_Up:
1082 if (!MOD__CTRL_ONLY) {
1083 tc->scale += 0.05;
1085 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
1086 ret = TRUE;
1087 }
1088 break;
1089 case GDK_Down:
1090 case GDK_KP_Down:
1091 if (!MOD__CTRL_ONLY) {
1093 tc->scale -= 0.05;
1094 if (tc->scale < 0.0)
1095 tc->scale = 0.0;
1096 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
1098 ret = TRUE;
1100 }
1101 break;
1102 case GDK_Right:
1103 case GDK_KP_Right:
1104 if (!MOD__CTRL_ONLY) {
1105 tc->width += 0.01;
1106 if (tc->width > 1.0)
1107 tc->width = 1.0;
1108 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x
1109 sp_spray_update_area(tc);
1110 ret = TRUE;
1111 }
1112 break;
1113 case GDK_Left:
1114 case GDK_KP_Left:
1115 if (!MOD__CTRL_ONLY) {
1116 tc->width -= 0.01;
1117 if (tc->width < 0.01)
1118 tc->width = 0.01;
1119 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1120 sp_spray_update_area(tc);
1121 ret = TRUE;
1122 }
1123 break;
1124 case GDK_Home:
1125 case GDK_KP_Home:
1126 tc->width = 0.01;
1127 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1128 sp_spray_update_area(tc);
1129 ret = TRUE;
1130 break;
1131 case GDK_End:
1132 case GDK_KP_End:
1133 tc->width = 1.0;
1134 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1135 sp_spray_update_area(tc);
1136 ret = TRUE;
1137 break;
1138 case GDK_x:
1139 case GDK_X:
1140 if (MOD__ALT_ONLY) {
1141 desktop->setToolboxFocusTo ("altx-spray");
1142 ret = TRUE;
1143 }
1144 break;
1146 case GDK_Shift_L:
1147 case GDK_Shift_R:
1148 sp_spray_update_cursor(tc, true);
1149 break;
1150 /*Set the scale to 1*/
1151 case GDK_Control_L:
1152 tc->scale=1;
1153 default:
1154 break;
1155 }
1156 break;
1158 case GDK_KEY_RELEASE: {
1159 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1160 switch (get_group0_keyval(&event->key)) {
1161 case GDK_Shift_L:
1162 case GDK_Shift_R:
1163 sp_spray_update_cursor(tc, false);
1164 break;
1165 case GDK_Control_L:
1166 case GDK_Control_R:
1167 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1168 tc->_message_context->clear();
1169 break;
1170 default:
1171 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1172 break;
1173 }
1174 }
1176 default:
1177 break;
1178 }
1180 if (!ret) {
1181 if (((SPEventContextClass *) parent_class)->root_handler) {
1182 ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
1183 }
1184 }
1186 return ret;
1187 }
1189 /*
1190 Local Variables:
1191 mode:c++
1192 c-file-style:"stroustrup"
1193 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1194 indent-tabs-mode:nil
1195 fill-column:99
1196 End:
1197 */
1198 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :