5f0b57eba12e01278f85dfe9340a50e187523a4f
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)
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 //End of NormalDistribution
146 GtkType sp_spray_context_get_type(void)
147 {
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;
163 }
165 static void sp_spray_context_class_init(SPSprayContextClass *klass)
166 {
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;
177 }
179 /*Method to rotate items*/
180 void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom::Rotate const &rotation)
181 {
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 }
197 }
199 /*Method to scale items*/
200 void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale const &scale)
201 {
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);
209 }
211 static void sp_spray_context_init(SPSprayContext *tc)
212 {
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_min=0;
230 tc->rot_max=0;
231 tc->standard_deviation=0.2;
232 tc->scale=1;
233 tc->scale_min = 1;
234 tc->scale_max=1;
235 tc->pressure = TC_DEFAULT_PRESSURE;
237 tc->is_dilating = false;
238 tc->has_dilated = false;
240 tc->do_h = true;
241 tc->do_s = true;
242 tc->do_l = true;
243 tc->do_o = false;
245 new (&tc->style_set_connection) sigc::connection();
246 }
248 static void sp_spray_context_dispose(GObject *object)
249 {
250 SPSprayContext *tc = SP_SPRAY_CONTEXT(object);
252 tc->style_set_connection.disconnect();
253 tc->style_set_connection.~connection();
255 if (tc->dilate_area) {
256 gtk_object_destroy(GTK_OBJECT(tc->dilate_area));
257 tc->dilate_area = NULL;
258 }
260 if (tc->_message_context) {
261 delete tc->_message_context;
262 }
264 G_OBJECT_CLASS(parent_class)->dispose(object);
265 }
267 bool is_transform_modes(gint mode)
268 {
269 return (mode == SPRAY_MODE_COPY ||
270 mode == SPRAY_MODE_CLONE ||
271 mode == SPRAY_MODE_SINGLE_PATH ||
272 mode == SPRAY_OPTION);
273 }
275 void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/)
276 {
277 SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
278 SPDesktop *desktop = event_context->desktop;
280 guint num = 0;
281 gchar *sel_message = NULL;
282 if (!desktop->selection->isEmpty()) {
283 num = g_slist_length((GSList *) desktop->selection->itemList());
284 sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
285 } else {
286 sel_message = g_strdup_printf(_("<b>Nothing</b> selected"));
287 }
290 switch (tc->mode) {
291 case SPRAY_MODE_COPY:
292 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>copies</b> of the initial selection"), sel_message);
293 break;
294 case SPRAY_MODE_CLONE:
295 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>clones</b> of the initial selection"), sel_message);
296 break;
297 case SPRAY_MODE_SINGLE_PATH:
298 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);
299 break;
300 case SPRAY_OPTION:
301 tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Modify <b>spray</b> options"), sel_message);
302 break;
303 }
304 sp_event_context_update_cursor(event_context);
305 g_free(sel_message);
306 }
308 static void sp_spray_context_setup(SPEventContext *ec)
309 {
310 SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
312 if (((SPEventContextClass *) parent_class)->setup)
313 ((SPEventContextClass *) parent_class)->setup(ec);
315 {
316 /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
317 SPCurve *c = new SPCurve();
318 const double C1 = 0.552;
319 c->moveto(-1,0);
320 c->curveto(-1, C1, -C1, 1, 0, 1 );
321 c->curveto(C1, 1, 1, C1, 1, 0 );
322 c->curveto(1, -C1, C1, -1, 0, -1 );
323 c->curveto(-C1, -1, -1, -C1, -1, 0 );
324 c->closepath();
325 tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c);
326 c->unref();
327 sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0);
328 sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
329 sp_canvas_item_hide(tc->dilate_area);
330 }
332 tc->is_drawing = false;
334 tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
336 sp_event_context_read(ec, "distrib");
337 sp_event_context_read(ec, "width");
338 sp_event_context_read(ec, "ratio");
339 sp_event_context_read(ec, "tilt");
340 sp_event_context_read(ec, "rot_min");
341 sp_event_context_read(ec, "rot_max");
342 sp_event_context_read(ec, "scale_min");
343 sp_event_context_read(ec, "scale_max");
344 sp_event_context_read(ec, "mode");
345 sp_event_context_read(ec, "population");
346 sp_event_context_read(ec, "force");
347 sp_event_context_read(ec, "mean");
348 sp_event_context_read(ec, "standard_deviation");
349 sp_event_context_read(ec, "usepressure");
350 sp_event_context_read(ec, "Rotation min");
351 sp_event_context_read(ec, "Rotation max");
352 sp_event_context_read(ec, "Scale");
353 sp_event_context_read(ec, "doh");
354 sp_event_context_read(ec, "dol");
355 sp_event_context_read(ec, "dos");
356 sp_event_context_read(ec, "doo");
358 ;
360 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
361 if (prefs->getBool("/tools/spray/selcue")) {
362 ec->enableSelectionCue();
363 }
365 if (prefs->getBool("/tools/spray/gradientdrag")) {
366 ec->enableGrDrag();
367 }
368 }
370 static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
371 {
372 SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
373 Glib::ustring path = val->getEntryName();
375 if (path == "width") {
376 tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100);
377 } else if (path == "mode") {
378 tc->mode = val->getInt();
379 sp_spray_update_cursor(tc, false);
380 } else if (path == "distribution") {
381 tc->distrib = val->getInt(1);
382 } else if (path == "population") {
383 tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100);
384 } else if (path == "tilt") {
385 tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0);
386 } else if (path == "ratio") {
387 tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9);
388 } else if (path == "force") {
389 tc->force = CLAMP(val->getDouble(1.0), 0, 1.0);
390 } else if (path == "rot_min") {
391 tc->rot_min = CLAMP(val->getDouble(0), 0, 10.0);
392 } else if (path == "rot_max") {
393 tc->rot_max = CLAMP(val->getDouble(0), 0, 10.0);
394 } else if (path == "scale_min") {
395 tc->scale_min = CLAMP(val->getDouble(1.0), 0, 10.0);
396 } else if (path == "scale_max") {
397 tc->scale_max = CLAMP(val->getDouble(1.0), 0, 10.0);
398 } else if (path == "mean") {
399 tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100);
400 } else if (path == "standard_deviation") {
401 tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100);
402 } else if (path == "usepressure") {
403 tc->usepressure = val->getBool();
404 } else if (path == "doh") {
405 tc->do_h = val->getBool();
406 } else if (path == "dos") {
407 tc->do_s = val->getBool();
408 } else if (path == "dol") {
409 tc->do_l = val->getBool();
410 } else if (path == "doo") {
411 tc->do_o = val->getBool();
412 }
413 }
415 static void sp_spray_extinput(SPSprayContext *tc, GdkEvent *event)
416 {
417 if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure))
418 tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
419 else
420 tc->pressure = TC_DEFAULT_PRESSURE;
421 }
423 double get_dilate_radius(SPSprayContext *tc)
424 {
426 return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom();
429 }
431 double get_path_force(SPSprayContext *tc)
432 {
433 double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
434 /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom());
435 if (force > 3) {
436 force += 4 * (force - 3);
437 }
438 return force * tc->force;
439 }
441 double get_path_mean(SPSprayContext *tc)
442 {
443 return tc->mean;
444 }
446 double get_path_standard_deviation(SPSprayContext *tc)
447 {
448 return tc->standard_deviation;
449 }
451 double get_move_force(SPSprayContext *tc)
452 {
453 double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
454 return force * tc->force;
455 }
457 double get_move_mean(SPSprayContext *tc)
458 {
459 return tc->mean;
460 }
462 double get_move_standard_deviation(SPSprayContext *tc)
463 {
464 return tc->standard_deviation;
465 }
467 /* Method to handle the distribution of the items */
470 void random_position( double &r, double &p, double &a, double &s, int choix)
471 {
472 if (choix == 0) // Mode 1 : uniform repartition
473 {
474 r = (1-pow(g_random_double_range(0, 1),2));
475 p = g_random_double_range(0, M_PI*2);
476 }
477 if (choix == 1) //Mode 0 : gaussian repartition
478 {
479 double r_temp =-1;
480 while(!((r_temp>=0)&&(r_temp<=1)))
481 {
482 r_temp = NormalDistribution(a,s/4);
483 }
484 // generates a number following a normal distribution
485 p = g_random_double_range(0, M_PI*2);
486 r=r_temp;
487 /* if (r_temp<=0) r=0;
488 else
489 {
490 if (r_temp>1) r=1;
491 else r = r_temp;
492 }*/
493 }
494 }
500 bool sp_spray_dilate_recursive(SPDesktop *desktop,
501 Inkscape::Selection *selection,
502 SPItem *item,
503 Geom::Point p,
504 Geom::Point /*vector*/,
505 gint mode,
506 double radius,
507 double /*force*/,
508 double population,
509 double &scale,
510 double scale_min,
511 double scale_max,
512 bool /*reverse*/,
513 double mean,
514 double standard_deviation,
515 double ratio,
516 double tilt,
517 double rot_min,
518 double rot_max,
519 gint _distrib )
520 {
524 bool did = false;
526 if (SP_IS_BOX3D(item) /*&& !is_transform_modes(mode)*/) {
527 // convert 3D boxes to ordinary groups before spraying their shapes
528 item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item)));
529 selection->add(item);
530 }
532 /*if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
533 GSList *items = g_slist_prepend (NULL, item);
534 GSList *selected = NULL;
535 GSList *to_select = NULL;
536 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
537 sp_item_list_to_curves (items, &selected, &to_select);
538 g_slist_free (items);
539 SPObject* newObj = doc->getObjectByRepr((Inkscape::XML::Node *) to_select->data);
540 g_slist_free (to_select);
541 item = (SPItem *) newObj;
542 // selection->add(item);
543 }
544 */
545 /*if (SP_IS_GROUP(item) && !SP_IS_BOX3D(item)) {
546 for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
547 if (SP_IS_ITEM(child)) {
548 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))
549 did = true;
550 }
551 }
553 } else {*/
554 if (mode == SPRAY_MODE_COPY) {
556 Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
557 if (a) {
558 double dr; double dp;
559 random_position(dr,dp,mean,standard_deviation,_distrib);
560 dr=dr*radius;
561 double _fid = g_random_double_range(0,1);
562 SPItem *item_copied;
563 double angle = g_random_double_range(rot_min, rot_max);
564 double _scale = g_random_double_range(scale_min, scale_max);
565 if(_fid<=population)
566 {
567 // duplicate
568 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
569 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
570 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
571 Inkscape::XML::Node *parent = old_repr->parent();
572 Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
573 parent->appendChild(copy);
575 SPObject *new_obj = doc->getObjectByRepr(copy);
576 item_copied = (SPItem *) new_obj; //convertion object->item
577 Geom::Point center=item->getCenter();
578 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
579 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
581 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
582 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
583 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
589 did = true;
590 }
591 }
593 } else if (mode == SPRAY_MODE_SINGLE_PATH) {
596 SPItem *father; //initial Object
597 SPItem *item_copied;//Projected Object
598 SPItem *unionResult;//previous union
599 SPItem *son;//father copy
601 int i=1;
602 for (GSList *items = g_slist_copy((GSList *) selection->itemList());
603 items != NULL;
604 items = items->next) {
606 SPItem *item1 = (SPItem *) items->data;
607 if (i==1) {
608 father=item1;
609 }
610 if (i==2) {
611 unionResult=item1;
612 }
613 i++;
614 }
615 SPDocument *doc = SP_OBJECT_DOCUMENT(father);
616 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
617 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(father);
618 //SPObject *old_obj = doc->getObjectByRepr(old_repr);
619 Inkscape::XML::Node *parent = old_repr->parent();
621 Geom::OptRect a = father->getBounds(sp_item_i2doc_affine(father));
622 if (a) {
623 double dr; double dp; //initialisation des variables
624 random_position(dr,dp,mean,standard_deviation,_distrib);
625 dr=dr*radius;
626 double _fid = g_random_double_range(0,1);
627 double angle = (g_random_double_range(rot_min, rot_max));
628 double _scale = g_random_double_range(scale_min, scale_max);
629 if (i==2) {
630 Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
631 parent->appendChild(copy1);
632 SPObject *new_obj1 = doc->getObjectByRepr(copy1);
633 son = (SPItem *) new_obj1; //conversion object->item
634 unionResult=son;
635 Inkscape::GC::release(copy1);
636 }
638 if (_fid<=population) { //Rules the population of objects sprayed
639 // duplicates the father
640 Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
641 parent->appendChild(copy2);
642 SPObject *new_obj2 = doc->getObjectByRepr(copy2);
643 item_copied = (SPItem *) new_obj2;
645 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
647 Geom::Point center=father->getCenter();
648 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
649 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
650 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
651 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
653 //unionResult and duplication
654 selection->clear();
655 selection->add(item_copied);
656 selection->add(unionResult);
657 sp_selected_path_union(selection->desktop());
658 selection->add(father);
659 Inkscape::GC::release(copy2);
660 did = true;
661 }
663 }
664 } else if (mode == SPRAY_MODE_CLONE) {
666 Geom::OptRect a = item->getBounds(sp_item_i2doc_affine(item));
667 if (a) {
668 double dr; double dp;
669 random_position(dr,dp,mean,standard_deviation,_distrib);
670 dr=dr*radius;
671 double _fid = g_random_double_range(0,1);
672 double angle = (g_random_double_range(rot_min, rot_max));
673 double _scale = g_random_double_range(scale_min, scale_max);
675 if(_fid<=population)
676 {
677 SPItem *item_copied;
678 SPDocument *doc = SP_OBJECT_DOCUMENT(item);
679 Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
680 Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
681 Inkscape::XML::Node *parent = old_repr->parent();
683 //Creation of the clone
684 Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
685 parent->appendChild(clone); // Ad the clone to the list of the father's sons
686 clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); // Generates the link between father and son attributes
688 SPObject *clone_object = doc->getObjectByRepr(clone);
689 item_copied = (SPItem *) clone_object;//conversion object->item
690 Geom::Point center=item->getCenter();
691 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
692 sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
693 sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
694 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());
695 sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
697 Inkscape::GC::release(clone);
699 did = true;
700 } }}
701 return did;
703 }
706 bool sp_spray_color_recursive(guint /*mode*/,
707 SPItem */*item*/,
708 SPItem */*item_at_point*/,
709 guint32 /*fill_goal*/,
710 bool /*do_fill*/,
711 guint32 /*stroke_goal*/,
712 bool /*do_stroke*/,
713 float /*opacity_goal*/,
714 bool /*do_opacity*/,
715 bool /*do_blur*/,
716 bool /*reverse*/,
717 Geom::Point /*p*/,
718 double /*radius*/,
719 double /*force*/,
720 bool /*do_h*/,
721 bool /*do_s*/,
722 bool /*do_l*/,
723 bool /*do_o*/)
724 {
725 bool did = false;
727 return did;
728 }
731 bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse)
732 {
733 Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop);
734 SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop;
737 if (selection->isEmpty()) {
738 return false;
739 }
741 bool did = false;
742 double radius = get_dilate_radius(tc);
746 bool do_fill = false, do_stroke = false, do_opacity = false;
747 guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill);
748 guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke);
749 double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity);
750 if (reverse) {
751 // RGB inversion
752 fill_goal = SP_RGBA32_U_COMPOSE(
753 (255 - SP_RGBA32_R_U(fill_goal)),
754 (255 - SP_RGBA32_G_U(fill_goal)),
755 (255 - SP_RGBA32_B_U(fill_goal)),
756 (255 - SP_RGBA32_A_U(fill_goal)));
757 stroke_goal = SP_RGBA32_U_COMPOSE(
758 (255 - SP_RGBA32_R_U(stroke_goal)),
759 (255 - SP_RGBA32_G_U(stroke_goal)),
760 (255 - SP_RGBA32_B_U(stroke_goal)),
761 (255 - SP_RGBA32_A_U(stroke_goal)));
762 opacity_goal = 1 - opacity_goal;
763 }
765 double path_force = get_path_force(tc);
766 if (radius == 0 || path_force == 0) {
767 return false;
768 }
769 double path_mean = get_path_mean(tc);
770 if (radius == 0 || path_mean == 0) {
771 return false;
772 }
773 double path_standard_deviation = get_path_standard_deviation(tc);
774 if (radius == 0 || path_standard_deviation == 0) {
775 return false;
776 }
777 double move_force = get_move_force(tc);
778 double move_mean = get_move_mean(tc);
779 double move_standard_deviation = get_move_standard_deviation(tc);
782 for (GSList *items = g_slist_copy((GSList *) selection->itemList());
783 items != NULL;
784 items = items->next) {
786 SPItem *item = (SPItem *) items->data;
788 if (is_transform_modes(tc->mode)) {
789 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))
790 did = true;
791 } else {
792 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))
793 did = true;
794 }
795 }
797 return did;
798 }
800 void sp_spray_update_area(SPSprayContext *tc)
801 {
802 double radius = get_dilate_radius(tc);
803 Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
804 sp_canvas_item_affine_absolute(tc->dilate_area, (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
805 sp_canvas_item_show(tc->dilate_area);
806 }
808 void sp_spray_switch_mode(SPSprayContext *tc, gint mode, bool with_shift)
809 {
810 SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); //sélectionne le bouton numéro "mode"
811 // need to set explicitly, because the prefs may not have changed by the previous
812 tc->mode = mode;
813 sp_spray_update_cursor (tc, with_shift);
814 }
816 void sp_spray_switch_mode_temporarily(SPSprayContext *tc, gint mode, bool with_shift)
817 {
818 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
819 // Juggling about so that prefs have the old value but tc->mode and the button show new mode:
820 gint now_mode = prefs->getInt("/tools/spray/mode", 0);
821 SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
822 // button has changed prefs, restore
823 prefs->setInt("/tools/spray/mode", now_mode);
824 // changing prefs changed tc->mode, restore back :)
825 tc->mode = mode;
826 sp_spray_update_cursor (tc, with_shift);
827 }
829 gint sp_spray_context_root_handler(SPEventContext *event_context,
830 GdkEvent *event)
831 {
832 SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context);
833 SPDesktop *desktop = event_context->desktop;
835 gint ret = FALSE;
837 switch (event->type) {
838 case GDK_ENTER_NOTIFY:
839 sp_canvas_item_show(tc->dilate_area);
840 break;
841 case GDK_LEAVE_NOTIFY:
842 sp_canvas_item_hide(tc->dilate_area);
843 break;
844 case GDK_BUTTON_PRESS:
845 if (event->button.button == 1 && !event_context->space_panning) {
847 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) {
848 return TRUE;
849 }
851 Geom::Point const motion_w(event->button.x,
852 event->button.y);
853 Geom::Point const motion_dt(desktop->w2d(motion_w));
854 tc->last_push = desktop->dt2doc(motion_dt);
856 sp_spray_extinput(tc, event);
858 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
859 tc->is_drawing = true;
860 tc->is_dilating = true;
861 tc->has_dilated = false;
865 if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning)
867 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
871 tc->has_dilated=true;
873 ret = TRUE;
874 }
875 break;
876 case GDK_MOTION_NOTIFY:
877 {
878 Geom::Point const motion_w(event->motion.x,
879 event->motion.y);
880 Geom::Point motion_dt(desktop->w2d(motion_w));
881 Geom::Point motion_doc(desktop->dt2doc(motion_dt));
882 sp_spray_extinput(tc, event);
884 // draw the dilating cursor
885 double radius = get_dilate_radius(tc);
886 Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
887 sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w)));
888 sp_canvas_item_show(tc->dilate_area);
890 guint num = 0;
891 if (!desktop->selection->isEmpty()) {
892 num = g_slist_length((GSList *) desktop->selection->itemList());
893 }
894 if (num == 0) {
895 tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
896 }
898 // dilating:
899 if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) {
900 sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
901 //tc->last_push = motion_doc;
902 tc->has_dilated = true;
904 // it's slow, so prevent clogging up with events
905 gobble_motion_events(GDK_BUTTON1_MASK);
906 return TRUE;
907 }
909 }
910 break;
911 /*Spray with the scroll*/
912 case GDK_SCROLL:
913 {
914 if (event->scroll.state & GDK_BUTTON1_MASK)
915 {
916 double temp ;
917 temp=tc->population;
918 tc->population=1.0;
919 desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
920 Geom::Point const scroll_w(event->button.x,event->button.y);
921 Geom::Point const scroll_dt = desktop->point();;
922 Geom::Point motion_doc(desktop->dt2doc(scroll_dt));
923 switch (event->scroll.direction)
924 {
925 case GDK_SCROLL_UP:
926 {
927 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
928 {
929 return TRUE;
930 }
931 tc->last_push = desktop->dt2doc(scroll_dt);
932 sp_spray_extinput(tc, event);
933 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
934 tc->is_drawing = true;
935 tc->is_dilating = true;
936 tc->has_dilated = false;
937 if(tc->is_dilating && !event_context->space_panning)
939 sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false);
943 tc->has_dilated=true;
944 tc->population=temp;
946 desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
948 ret = TRUE;
949 }
950 break;
951 case GDK_SCROLL_DOWN:
952 {
953 if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
954 {
955 return TRUE;
956 }
957 tc->last_push = desktop->dt2doc(scroll_dt);
958 sp_spray_extinput(tc, event);
959 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
960 tc->is_drawing = true;
961 tc->is_dilating = true;
962 tc->has_dilated = false;
963 if(tc->is_dilating && !event_context->space_panning)
964 sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false);
966 tc->has_dilated=true;
968 ret = TRUE;
971 }
972 break;
973 case GDK_SCROLL_RIGHT:
974 {} break;
975 case GDK_SCROLL_LEFT:
976 {} break;
977 }
978 }
981 break;
983 }
984 case GDK_BUTTON_RELEASE:
985 {
986 Geom::Point const motion_w(event->button.x, event->button.y);
987 Geom::Point const motion_dt(desktop->w2d(motion_w));
989 sp_canvas_end_forced_full_redraws(desktop->canvas);
990 tc->is_drawing = false;
992 if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) {
993 if (!tc->has_dilated) {
994 // if we did not rub, do a light tap
995 tc->pressure = 0.03;
996 sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
997 }
998 tc->is_dilating = false;
999 tc->has_dilated = false;
1000 switch (tc->mode) {
1001 case SPRAY_MODE_COPY:
1002 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
1003 SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
1004 break;
1005 case SPRAY_MODE_CLONE:
1006 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
1007 SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
1008 break;
1009 case SPRAY_MODE_SINGLE_PATH:
1010 sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
1011 SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
1012 break;
1013 }
1014 }
1015 break;
1016 }
1018 case GDK_KEY_PRESS:
1019 switch (get_group0_keyval (&event->key)) {
1020 case GDK_j: if (MOD__SHIFT_ONLY) {
1021 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
1022 ret = TRUE;
1023 }
1024 case GDK_J: if (MOD__SHIFT_ONLY) {
1025 sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
1026 ret = TRUE;
1027 }
1029 break;
1030 case GDK_m:
1031 case GDK_M:
1032 case GDK_0:
1034 break;
1035 case GDK_i:
1036 case GDK_I:
1037 case GDK_k: if (MOD__SHIFT_ONLY) {
1038 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
1039 ret = TRUE;
1040 }
1041 case GDK_K:if (MOD__SHIFT_ONLY) {
1042 sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
1043 ret = TRUE;
1044 }
1045 break;
1047 case GDK_l: if (MOD__SHIFT_ONLY) {
1048 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
1049 ret = TRUE;
1050 }
1052 case GDK_L:
1053 if (MOD__SHIFT_ONLY) {
1054 sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
1055 ret = TRUE;
1056 }
1057 break;
1058 case GDK_Up:
1059 case GDK_KP_Up:
1060 if (!MOD__CTRL_ONLY) {
1061 tc->scale += 0.05;
1063 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
1064 ret = TRUE;
1065 }
1066 break;
1067 case GDK_Down:
1068 case GDK_KP_Down:
1069 if (!MOD__CTRL_ONLY) {
1071 tc->scale -= 0.05;
1072 if (tc->scale < 0.0)
1073 tc->scale = 0.0;
1074 //desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
1076 ret = TRUE;
1078 }
1079 break;
1080 case GDK_Right:
1081 case GDK_KP_Right:
1082 if (!MOD__CTRL_ONLY) {
1083 tc->width += 0.01;
1084 if (tc->width > 1.0)
1085 tc->width = 1.0;
1086 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x
1087 sp_spray_update_area(tc);
1088 ret = TRUE;
1089 }
1090 break;
1091 case GDK_Left:
1092 case GDK_KP_Left:
1093 if (!MOD__CTRL_ONLY) {
1094 tc->width -= 0.01;
1095 if (tc->width < 0.01)
1096 tc->width = 0.01;
1097 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1098 sp_spray_update_area(tc);
1099 ret = TRUE;
1100 }
1101 break;
1102 case GDK_Home:
1103 case GDK_KP_Home:
1104 tc->width = 0.01;
1105 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1106 sp_spray_update_area(tc);
1107 ret = TRUE;
1108 break;
1109 case GDK_End:
1110 case GDK_KP_End:
1111 tc->width = 1.0;
1112 desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
1113 sp_spray_update_area(tc);
1114 ret = TRUE;
1115 break;
1116 case GDK_x:
1117 case GDK_X:
1118 if (MOD__ALT_ONLY) {
1119 desktop->setToolboxFocusTo ("altx-spray");
1120 ret = TRUE;
1121 }
1122 break;
1124 case GDK_Shift_L:
1125 case GDK_Shift_R:
1126 sp_spray_update_cursor(tc, true);
1127 break;
1128 /*Set the scale to 1*/
1129 case GDK_Control_L:
1130 tc->scale=1;
1131 default:
1132 break;
1133 }
1134 break;
1136 case GDK_KEY_RELEASE: {
1137 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1138 switch (get_group0_keyval(&event->key)) {
1139 case GDK_Shift_L:
1140 case GDK_Shift_R:
1141 sp_spray_update_cursor(tc, false);
1142 break;
1143 case GDK_Control_L:
1144 case GDK_Control_R:
1145 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1146 tc->_message_context->clear();
1147 break;
1148 default:
1149 sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
1150 break;
1151 }
1152 }
1154 default:
1155 break;
1156 }
1158 if (!ret) {
1159 if (((SPEventContextClass *) parent_class)->root_handler) {
1160 ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
1161 }
1162 }
1164 return ret;
1165 }
1167 /*
1168 Local Variables:
1169 mode:c++
1170 c-file-style:"stroustrup"
1171 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1172 indent-tabs-mode:nil
1173 fill-column:99
1174 End:
1175 */
1176 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :