Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
tweak-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * tweaking paths without node editing
4 *
5 * Authors:
6 * bulia byak
7 * Jon A. Cruz <jon@joncruz.org>
8 * Abhishek Sharma
9 *
10 * Copyright (C) 2007 authors
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include "tweak-tool.h"
16
17#include <gdk/gdkkeysyms.h>
18#include <glibmm/i18n.h>
19
20#include <2geom/circle.h>
21
22#include "context-fns.h"
23#include "desktop-style.h"
24#include "desktop.h"
25#include "document-undo.h"
26#include "document.h"
27#include "filter-chemistry.h"
28#include "gradient-chemistry.h"
29#include "message-context.h"
30#include "path-chemistry.h"
31#include "selection.h"
32#include "style.h"
33
35
36#include "livarot/Path.h"
37#include "livarot/Shape.h"
38
39#include "object/box3d.h"
41#include "object/sp-flowtext.h"
44#include "object/sp-path.h"
46#include "object/sp-stop.h"
47#include "object/sp-text.h"
48
49#include "path/path-util.h"
50
51#include "ui/icon-names.h"
54
55
57
58#define DDC_RED_RGBA 0xff0000ff
59
60#define DYNA_MIN_WIDTH 1.0e-6
61
62namespace Inkscape::UI::Tools {
63
65 : ToolBase(desktop, "/tools/tweak", "tweak-push.svg")
66 , pressure(TC_DEFAULT_PRESSURE)
67 , usepressure(false)
68 , usetilt(false)
69 , width(0.2)
70 , force(0.2)
71 , fidelity(0)
72 , mode(0)
73 , is_drawing(false)
74 , is_dilating(false)
75 , has_dilated(false)
76 , do_h(true)
77 , do_s(true)
78 , do_l(true)
79 , do_o(false)
80{
81 dilate_area = make_canvasitem<CanvasItemBpath>(desktop->getCanvasSketch());
82 dilate_area->set_stroke(0xff9900ff);
83 dilate_area->set_fill(0x0, SP_WIND_RULE_EVENODD);
84 dilate_area->set_visible(false);
85
86 sp_event_context_read(this, "width");
87 sp_event_context_read(this, "mode");
88 sp_event_context_read(this, "fidelity");
89 sp_event_context_read(this, "force");
90 sp_event_context_read(this, "usepressure");
91 sp_event_context_read(this, "doh");
92 sp_event_context_read(this, "dol");
93 sp_event_context_read(this, "dos");
94 sp_event_context_read(this, "doo");
95
96 style_set_connection = desktop->connectSetStyle( // catch style-setting signal in this tool
97 sigc::hide(sigc::mem_fun(*this, &TweakTool::set_style))
98 );
99
101 if (prefs->getBool("/tools/tweak/selcue")) {
103 }
104 if (prefs->getBool("/tools/tweak/gradientdrag")) {
105 enableGrDrag();
106 }
107}
108
110{
111 enableGrDrag(false);
112}
113
114static bool is_transform_mode (gint mode)
115{
116 return (mode == TWEAK_MODE_MOVE ||
122}
123
124static bool is_color_mode(gint mode)
125{
127}
128
129void TweakTool::update_cursor (bool with_shift) {
130 guint num = 0;
131 gchar *sel_message = nullptr;
132
133 if (!_desktop->getSelection()->isEmpty()) {
134 num = (guint)boost::distance(_desktop->getSelection()->items());
135 sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
136 } else {
137 sel_message = g_strdup_printf("%s", _("<b>Nothing</b> selected"));
138 }
139
140 switch (this->mode) {
141 case TWEAK_MODE_MOVE:
142 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag to <b>move</b>."), sel_message);
143 this->set_cursor("tweak-move.svg");
144 break;
146 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>move in</b>; with Shift to <b>move out</b>."), sel_message);
147 if (with_shift) {
148 this->set_cursor("tweak-move-out.svg");
149 } else {
150 this->set_cursor("tweak-move-in.svg");
151 }
152 break;
154 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>move randomly</b>."), sel_message);
155 this->set_cursor("tweak-move-jitter.svg");
156 break;
157 case TWEAK_MODE_SCALE:
158 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>scale down</b>; with Shift to <b>scale up</b>."), sel_message);
159 if (with_shift) {
160 this->set_cursor("tweak-scale-up.svg");
161 } else {
162 this->set_cursor("tweak-scale-down.svg");
163 }
164 break;
166 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>rotate clockwise</b>; with Shift, <b>counterclockwise</b>."), sel_message);
167 if (with_shift) {
168 this->set_cursor("tweak-rotate-counterclockwise.svg");
169 } else {
170 this->set_cursor("tweak-rotate-clockwise.svg");
171 }
172 break;
174 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>duplicate</b>; with Shift, <b>delete</b>."), sel_message);
175 if (with_shift) {
176 this->set_cursor("tweak-less.svg");
177 } else {
178 this->set_cursor("tweak-more.svg");
179 }
180 break;
181 case TWEAK_MODE_PUSH:
182 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag to <b>push paths</b>."), sel_message);
183 this->set_cursor("tweak-push.svg");
184 break;
186 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>inset paths</b>; with Shift to <b>outset</b>."), sel_message);
187 if (with_shift) {
188 this->set_cursor("tweak-outset.svg");
189 } else {
190 this->set_cursor("tweak-inset.svg");
191 }
192 break;
194 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>attract paths</b>; with Shift to <b>repel</b>."), sel_message);
195 if (with_shift) {
196 this->set_cursor("tweak-repel.svg");
197 } else {
198 this->set_cursor("tweak-attract.svg");
199 }
200 break;
202 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>roughen paths</b>."), sel_message);
203 this->set_cursor("tweak-roughen.svg");
204 break;
206 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>paint objects</b> with color."), sel_message);
207 this->set_cursor("tweak-color.svg");
208 break;
210 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>randomize colors</b>."), sel_message);
211 this->set_cursor("tweak-color.svg");
212 break;
213 case TWEAK_MODE_BLUR:
214 this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag or click to <b>increase blur</b>; with Shift to <b>decrease</b>."), sel_message);
215 this->set_cursor("tweak-color.svg");
216 break;
217 }
218 g_free(sel_message);
219}
220
222 if (this->mode == TWEAK_MODE_COLORPAINT) { // intercept color setting only in this mode
223 // we cannot store properties with uris
224 css = sp_css_attr_unset_uris(const_cast<SPCSSAttr *>(css));
226 prefs->setStyle("/tools/tweak/style", const_cast<SPCSSAttr *>(css));
227 return true;
228 }
229
230 return false;
231}
232
234 Glib::ustring path = val.getEntryName();
235
236 if (path == "width") {
237 this->width = CLAMP(val.getDouble(0.1), -1000.0, 1000.0);
238 } else if (path == "mode") {
239 this->mode = val.getInt();
240 this->update_cursor(false);
241 } else if (path == "fidelity") {
242 this->fidelity = CLAMP(val.getDouble(), 0.0, 1.0);
243 } else if (path == "force") {
244 this->force = CLAMP(val.getDouble(1.0), 0, 1.0);
245 } else if (path == "usepressure") {
246 this->usepressure = val.getBool();
247 } else if (path == "doh") {
248 this->do_h = val.getBool();
249 } else if (path == "dos") {
250 this->do_s = val.getBool();
251 } else if (path == "dol") {
252 this->do_l = val.getBool();
253 } else if (path == "doo") {
254 this->do_o = val.getBool();
255 }
256}
257
258static void sp_tweak_extinput(TweakTool *tc, ExtendedInput const &ext)
259{
260 if (ext.pressure) {
261 tc->pressure = std::clamp(*ext.pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
262 } else {
263 tc->pressure = TC_DEFAULT_PRESSURE;
264 }
265}
266
267static double
269{
270 // 10 times the pen width:
271 return 500 * tc->width/tc->getDesktop()->current_zoom();
272}
273
274static double
276{
277 double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
278 /sqrt(tc->getDesktop()->current_zoom());
279 if (force > 3) {
280 force += 4 * (force - 3);
281 }
282 return force * tc->force;
283}
284
285static double
287{
288 double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
289 return force * tc->force;
290}
291
292static bool
293sp_tweak_dilate_recursive (Inkscape::Selection *selection, SPItem *item, Geom::Point p, Geom::Point vector, gint mode, double radius, double force, double fidelity, bool reverse)
294{
295 bool did = false;
296
297 {
298 auto box = cast<SPBox3D>(item);
299 if (box && !is_transform_mode(mode) && !is_color_mode(mode)) {
300 // convert 3D boxes to ordinary groups before tweaking their shapes
301 item = box->convert_to_group();
302 selection->add(item);
303 }
304 }
305
306 if (is<SPText>(item) || is<SPFlowtext>(item)) {
307 std::vector<SPItem*> items;
308 items.push_back(item);
309 std::vector<SPItem*> selected;
310 std::vector<Inkscape::XML::Node*> to_select;
311 SPDocument *doc = item->document;
312 sp_item_list_to_curves (items, selected, to_select);
313 SPObject* newObj = doc->getObjectByRepr(to_select[0]);
314 item = cast<SPItem>(newObj);
315 g_assert(item != nullptr);
316 selection->add(item);
317 }
318
319 if (is<SPGroup>(item) && !is<SPBox3D>(item)) {
320 std::vector<SPItem *> children;
321 for (auto& child: item->children) {
322 if (is<SPItem>(&child)) {
323 children.push_back(cast<SPItem>(&child));
324 }
325 }
326
327 for (auto i = children.rbegin(); i!= children.rend(); ++i) {
328 SPItem *child = *i;
329 g_assert(child != nullptr);
330 if (sp_tweak_dilate_recursive (selection, child, p, vector, mode, radius, force, fidelity, reverse)) {
331 did = true;
332 }
333 }
334 } else {
335 if (mode == TWEAK_MODE_MOVE) {
336
338 if (a) {
339 double x = Geom::L2(a->midpoint() - p)/radius;
340 if (a->contains(p)) x = 0;
341 if (x < 1) {
342 Geom::Point move = force * 0.5 * (cos(M_PI * x) + 1) * vector;
343 item->move_rel(Geom::Translate(move * selection->desktop()->doc2dt().withoutTranslation()));
344 did = true;
345 }
346 }
347
348 } else if (mode == TWEAK_MODE_MOVE_IN_OUT) {
349
351 if (a) {
352 double x = Geom::L2(a->midpoint() - p)/radius;
353 if (a->contains(p)) x = 0;
354 if (x < 1) {
355 Geom::Point move = force * 0.5 * (cos(M_PI * x) + 1) *
356 (reverse? (a->midpoint() - p) : (p - a->midpoint()));
357 item->move_rel(Geom::Translate(move * selection->desktop()->doc2dt().withoutTranslation()));
358 did = true;
359 }
360 }
361
362 } else if (mode == TWEAK_MODE_MOVE_JITTER) {
363
365 if (a) {
366 double dp = g_random_double_range(0, M_PI*2);
367 double dr = g_random_double_range(0, radius);
368 double x = Geom::L2(a->midpoint() - p)/radius;
369 if (a->contains(p)) x = 0;
370 if (x < 1) {
371 Geom::Point move = force * 0.5 * (cos(M_PI * x) + 1) * Geom::Point(cos(dp)*dr, sin(dp)*dr);
372 item->move_rel(Geom::Translate(move * selection->desktop()->doc2dt().withoutTranslation()));
373 did = true;
374 }
375 }
376
377 } else if (mode == TWEAK_MODE_SCALE) {
378
380 if (a) {
381 double x = Geom::L2(a->midpoint() - p)/radius;
382 if (a->contains(p)) x = 0;
383 if (x < 1) {
384 double scale = 1 + (reverse? force : -force) * 0.05 * (cos(M_PI * x) + 1);
386 did = true;
387 }
388 }
389
390 } else if (mode == TWEAK_MODE_ROTATE) {
391
393 if (a) {
394 double x = Geom::L2(a->midpoint() - p)/radius;
395 if (a->contains(p)) x = 0;
396 if (x < 1) {
397 double angle = (reverse? force : -force) * 0.05 * (cos(M_PI * x) + 1) * M_PI;
398 angle *= -selection->desktop()->yaxisdir();
400 did = true;
401 }
402 }
403
404 } else if (mode == TWEAK_MODE_MORELESS) {
405
407 if (a) {
408 double x = Geom::L2(a->midpoint() - p)/radius;
409 if (a->contains(p)) x = 0;
410 if (x < 1) {
411 double prob = force * 0.5 * (cos(M_PI * x) + 1);
412 double chance = g_random_double_range(0, 1);
413 if (chance <= prob) {
414 if (reverse) { // delete
415 item->deleteObject(true, true);
416 } else { // duplicate
417 SPDocument *doc = item->document;
418 Inkscape::XML::Document* xml_doc = doc->getReprDoc();
419 Inkscape::XML::Node *old_repr = item->getRepr();
420 SPObject *old_obj = doc->getObjectByRepr(old_repr);
421 Inkscape::XML::Node *parent = old_repr->parent();
422 Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
423 parent->appendChild(copy);
424 SPObject *new_obj = doc->getObjectByRepr(copy);
425 if (selection->includes(old_obj)) {
426 selection->add(new_obj);
427 }
429 }
430 did = true;
431 }
432 }
433 }
434
435 } else if (is<SPPath>(item) || is<SPShape>(item)) {
436
437 Inkscape::XML::Node *newrepr = nullptr;
438 gint pos = 0;
439 Inkscape::XML::Node *parent = nullptr;
440 char const *id = nullptr;
441 if (!is<SPPath>(item)) {
443 if (!newrepr) {
444 return false;
445 }
446
447 // remember the position of the item
448 pos = item->getRepr()->position();
449 // remember parent
450 parent = item->getRepr()->parent();
451 // remember id
452 id = item->getRepr()->attribute("id");
453 }
454
455 // skip those paths whose bboxes are entirely out of reach with our radius
457 if (bbox) {
458 bbox->expandBy(radius);
459 if (!bbox->contains(p)) {
460 return false;
461 }
462 }
463
464 auto orig = Path_for_item(item, false);
465 if (!orig) {
466 return false;
467 }
468
469 Path *res = new Path;
470 res->SetBackData(false);
471
472 Shape *theShape = new Shape;
473 Shape *theRes = new Shape;
475
476 orig->ConvertWithBackData((0.08 - (0.07 * fidelity)) / i2doc.descrim()); // default 0.059
477 orig->Fill(theShape, 0);
478
479 SPCSSAttr *css = sp_repr_css_attr(item->getRepr(), "style");
480 gchar const *val = sp_repr_css_property(css, "fill-rule", nullptr);
481 if (val && strcmp(val, "nonzero") == 0) {
482 theRes->ConvertToShape(theShape, fill_nonZero);
483 } else if (val && strcmp(val, "evenodd") == 0) {
484 theRes->ConvertToShape(theShape, fill_oddEven);
485 } else {
486 theRes->ConvertToShape(theShape, fill_nonZero);
487 }
488
489 if (Geom::L2(vector) != 0) {
490 vector = 1/Geom::L2(vector) * vector;
491 }
492
493 bool did_this = false;
495 if (theShape->MakeTweak(tweak_mode_grow, theRes,
496 reverse? force : -force,
497 join_straight, 4.0,
498 true, p, Geom::Point(0,0), radius, &i2doc) == 0) // 0 means the shape was actually changed
499 did_this = true;
500 } else if (mode == TWEAK_MODE_ATTRACT_REPEL) {
501 if (theShape->MakeTweak(tweak_mode_repel, theRes,
502 reverse? force : -force,
503 join_straight, 4.0,
504 true, p, Geom::Point(0,0), radius, &i2doc) == 0)
505 did_this = true;
506 } else if (mode == TWEAK_MODE_PUSH) {
507 if (theShape->MakeTweak(tweak_mode_push, theRes,
508 1.0,
509 join_straight, 4.0,
510 true, p, force*2*vector, radius, &i2doc) == 0)
511 did_this = true;
512 } else if (mode == TWEAK_MODE_ROUGHEN) {
513 if (theShape->MakeTweak(tweak_mode_roughen, theRes,
514 force,
515 join_straight, 4.0,
516 true, p, Geom::Point(0,0), radius, &i2doc) == 0)
517 did_this = true;
518 }
519
520 // the rest only makes sense if we actually changed the path
521 if (did_this) {
522 theRes->ConvertToShape(theShape, fill_positive);
523
524 res->Reset();
525 theRes->ConvertToForme(res);
526
527 double th_max = (0.6 - 0.59*sqrt(fidelity)) / i2doc.descrim();
528 double threshold = MAX(th_max, th_max*force);
529 res->ConvertEvenLines(threshold);
530 res->Simplify(threshold / (selection->desktop()->current_zoom()));
531
532 if (newrepr) { // converting to path, need to replace the repr
533 bool is_selected = selection->includes(item);
534 if (is_selected) {
535 selection->remove(item);
536 }
537
538 // It's going to resurrect, so we delete without notifying listeners.
539 item->deleteObject(false);
540
541 // restore id
542 newrepr->setAttribute("id", id);
543 // add the new repr to the parent
544 // move to the saved position
545 parent->addChildAtPos(newrepr, pos);
546
547 if (is_selected)
548 selection->add(newrepr);
549 }
550
551 if (res->descr_cmd.size() > 1) {
552 auto str = res->svg_dump_path();
553 if (newrepr) {
554 newrepr->setAttribute("d", str.c_str());
555 } else {
556 auto lpeitem = cast<SPLPEItem>(item);
557 if (lpeitem && lpeitem->hasPathEffectRecursive()) {
558 item->setAttribute("inkscape:original-d", str.c_str());
559 } else {
560 item->setAttribute("d", str.c_str());
561 }
562 }
563 } else {
564 // TODO: if there's 0 or 1 node left, delete this path altogether
565 }
566
567 if (newrepr) {
568 Inkscape::GC::release(newrepr);
569 newrepr = nullptr;
570 }
571 }
572
573 delete theShape;
574 delete theRes;
575 delete res;
576
577 if (did_this) {
578 did = true;
579 }
580 }
581
582 }
583
584 return did;
585}
586
587static Color tweak_color(guint mode, Color const &color, Color const &goal, double force, bool do_h, bool do_s, bool do_l)
588{
589 // Tweak colors are entirely based on HSL values;
590 if (auto hsl = color.converted(Colors::Space::Type::HSL)) {
591 unsigned int pin = (do_h * 1) + (do_s * 2) + (do_l * 4);
593 hsl->average(goal, force, pin);
594 } else if (mode == TWEAK_MODE_COLORJITTER) {
595 hsl->jitter(force, pin);
596 }
597 if (auto copy = hsl->converted(color.getSpace()))
598 return *copy;
599 }
600 return color; // Bad conversion
601}
602
603static void tweak_stop_color(guint mode, SPStop *stop, Color const &goal, double force, bool do_h, bool do_s, bool do_l)
604{
605 auto copy = stop->getColor();
606 tweak_color(mode, copy, goal, force, do_h, do_s, do_l);
607 stop->setColor(copy);
608}
609
610 static void
611tweak_opacity (guint mode, SPIScale24 *style_opacity, double opacity_goal, double force)
612{
613 double opacity = SP_SCALE24_TO_FLOAT (style_opacity->value);
614
616 double d = opacity_goal - opacity;
617 opacity += d * force;
618 } else if (mode == TWEAK_MODE_COLORJITTER) {
619 opacity += g_random_double_range(-opacity, 1 - opacity) * force;
620 }
621
622 style_opacity->value = SP_SCALE24_FROM_FLOAT(opacity);
623}
624
625
626 static double
627tweak_profile (double dist, double radius)
628{
629 if (radius == 0) {
630 return 0;
631 }
632 double x = dist / radius;
633 double alpha = 1;
634 if (x >= 1) {
635 return 0;
636 } else if (x <= 0) {
637 return 1;
638 } else {
639 return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
640 }
641}
642
644 Color const &goal, Geom::Point p_w, double radius, double force, guint mode,
645 bool do_h, bool do_s, bool do_l, bool /*do_o*/)
646{
647 SPGradient *gradient = getGradient(item, fill_or_stroke);
648
649 if (!gradient) {
650 return;
651 }
652
654 Geom::Point p = p_w * i2d.inverse();
655 p *= (gradient->gradientTransform).inverse();
656 // now p is in gradient's original coordinates
657
658 auto lg = cast<SPLinearGradient>(gradient);
659 auto rg = cast<SPRadialGradient>(gradient);
660 if (lg || rg) {
661
662 double pos = 0;
663 double r = 0;
664
665 if (lg) {
666 Geom::Point p1(lg->x1.computed, lg->y1.computed);
667 Geom::Point p2(lg->x2.computed, lg->y2.computed);
668 Geom::Point pdiff(p2 - p1);
669 double vl = Geom::L2(pdiff);
670
671 // This is the matrix which moves and rotates the gradient line
672 // so it's oriented along the X axis:
674 Geom::Affine(Geom::Rotate(-atan2(pdiff[Geom::Y], pdiff[Geom::X])));
675
676 // Transform the mouse point by it to find out its projection onto the gradient line:
677 Geom::Point pnorm = p * norm;
678
679 // Scale its X coordinate to match the length of the gradient line:
680 pos = pnorm[Geom::X] / vl;
681 // Calculate radius in length-of-gradient-line units
682 r = radius / vl;
683
684 }
685 if (rg) {
686 Geom::Point c (rg->cx.computed, rg->cy.computed);
687 pos = Geom::L2(p - c) / rg->r.computed;
688 r = radius / rg->r.computed;
689 }
690
691 // Normalize pos to 0..1, taking into account gradient spread:
692 double pos_e = pos;
693 if (gradient->getSpread() == SP_GRADIENT_SPREAD_PAD) {
694 if (pos > 1) {
695 pos_e = 1;
696 }
697 if (pos < 0) {
698 pos_e = 0;
699 }
700 } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REPEAT) {
701 if (pos > 1 || pos < 0) {
702 pos_e = pos - floor(pos);
703 }
704 } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REFLECT) {
705 if (pos > 1 || pos < 0) {
706 bool odd = ((int)(floor(pos)) % 2 == 1);
707 pos_e = pos - floor(pos);
708 if (odd) {
709 pos_e = 1 - pos_e;
710 }
711 }
712 }
713
715
716 double offset_l = 0;
717 double offset_h = 0;
718 SPObject *child_prev = nullptr;
719 for (auto& child: vector->children) {
720 auto stop = cast<SPStop>(&child);
721 if (!stop) {
722 continue;
723 }
724
725 offset_h = stop->offset;
726
727 if (child_prev) {
728 auto prevStop = cast<SPStop>(child_prev);
729 g_assert(prevStop != nullptr);
730
731 if (offset_h - offset_l > r && pos_e >= offset_l && pos_e <= offset_h) {
732 // the summit falls in this interstop, and the radius is small,
733 // so it only affects the ends of this interstop;
734 // distribute the force between the two endstops so that they
735 // get all the painting even if they are not touched by the brush
736 tweak_stop_color(mode, stop, goal,
737 force * (pos_e - offset_l) / (offset_h - offset_l),
738 do_h, do_s, do_l);
739 tweak_stop_color(mode, prevStop, goal,
740 force * (offset_h - pos_e) / (offset_h - offset_l),
741 do_h, do_s, do_l);
742 stop->updateRepr();
743 child_prev->updateRepr();
744 break;
745 } else {
746 // wide brush, may affect more than 2 stops,
747 // paint each stop by the force from the profile curve
748 if (offset_l <= pos_e && offset_l > pos_e - r) {
749 tweak_stop_color(mode, prevStop, goal,
750 force * tweak_profile (fabs (pos_e - offset_l), r),
751 do_h, do_s, do_l);
752 child_prev->updateRepr();
753 }
754
755 if (offset_h >= pos_e && offset_h < pos_e + r) {
756 tweak_stop_color(mode, prevStop, goal,
757 force * tweak_profile (fabs (pos_e - offset_h), r),
758 do_h, do_s, do_l);
759 stop->updateRepr();
760 }
761 }
762 }
763
764 offset_l = offset_h;
765 child_prev = &child;
766 }
767 } else {
768 // Mesh
769 auto mg = cast<SPMeshGradient>(gradient);
770 if (mg) {
771 auto mg_array = cast<SPMeshGradient>(mg->getArray());
772 SPMeshNodeArray *array = &(mg_array->array);
773 // Every third node is a corner node
774 for( unsigned i=0; i < array->nodes.size(); i+=3 ) {
775 for( unsigned j=0; j < array->nodes[i].size(); j+=3 ) {
776 SPStop *stop = array->nodes[i][j]->stop;
777 double distance = Geom::L2(Geom::Point(p - array->nodes[i][j]->p));
778 tweak_stop_color(mode, stop, goal,
779 force * tweak_profile (distance, radius), do_h, do_s, do_l);
780 stop->updateRepr();
781 }
782 }
783 }
784 }
785}
786
787static bool
789 std::optional<Color> &fill_goal, std::optional<Color> &stroke_goal,
790 float opacity_goal, bool do_opacity,
791 bool do_blur, bool reverse,
792 Geom::Point p, double radius, double force,
793 bool do_h, bool do_s, bool do_l, bool do_o)
794{
795 bool did = false;
796
797 if (is<SPGroup>(item)) {
798 for (auto& child: item->children) {
799 auto childItem = cast<SPItem>(&child);
800 if (childItem) {
801 if (sp_tweak_color_recursive (mode, childItem, item_at_point,
802 fill_goal, stroke_goal,
803 opacity_goal, do_opacity,
804 do_blur, reverse,
805 p, radius, force, do_h, do_s, do_l, do_o)) {
806 did = true;
807 }
808 }
809 }
810
811 } else {
812 SPStyle *style = item->style;
813 if (!style) {
814 return false;
815 }
817 if (!bbox) {
818 return false;
819 }
820
821 Geom::Rect brush(p - Geom::Point(radius, radius), p + Geom::Point(radius, radius));
822
823 Geom::Point center = bbox->midpoint();
824 double this_force;
825
826 // if item == item_at_point, use max force
827 if (item == item_at_point) {
828 this_force = force;
829 // else if no overlap of bbox and brush box, skip:
830 } else if (!bbox->intersects(brush)) {
831 return false;
832 //TODO:
833 // else if object > 1.5 brush: test 4/8/16 points in the brush on hitting the object, choose max
834 //} else if (bbox->maxExtent() > 3 * radius) {
835 //}
836 // else if object > 0.5 brush: test 4 corners of bbox and center on being in the brush, choose max
837 // else if still smaller, then check only the object center:
838 } else {
839 this_force = force * tweak_profile (Geom::L2 (p - center), radius);
840 }
841
842 if (this_force > 0.002) {
843
844 if (do_blur) {
846 if (!bbox) {
847 return did;
848 }
849
850 double blur_now = 0;
851 Geom::Affine i2dt = item->i2dt_affine ();
852 if (style->filter.set && style->getFilter()) {
853 //cycle through filter primitives
854 for (auto& primitive_obj: style->getFilter()->children) {
855 auto primitive = cast<SPFilterPrimitive>(&primitive_obj);
856 if (primitive) {
857 //if primitive is gaussianblur
858 auto spblur = cast<SPGaussianBlur>(primitive);
859 if (spblur) {
860 float num = spblur->get_std_deviation().getNumber();
861 blur_now += num * i2dt.descrim(); // sum all blurs in the filter
862 }
863 }
864 }
865 }
866 double perimeter = bbox->dimensions()[Geom::X] + bbox->dimensions()[Geom::Y];
867 blur_now = blur_now / perimeter;
868
869 double blur_new;
870 if (reverse) {
871 blur_new = blur_now - 0.06 * force;
872 } else {
873 blur_new = blur_now + 0.06 * force;
874 }
875 if (blur_new < 0.0005 && blur_new < blur_now) {
876 blur_new = 0;
877 }
878 if (blur_new == 0) {
879 remove_filter(item, false);
880 } else {
881 double radius = blur_new * perimeter;
883 sp_style_set_property_url(item, "filter", filter, false);
884 }
885 return true; // do not do colors, blur is a separate mode
886 }
887
888 if (fill_goal) {
889 if (style->fill.isPaintserver()) {
890 tweak_colors_in_gradient(item, Inkscape::FOR_FILL, *fill_goal, p, radius, this_force, mode, do_h, do_s, do_l, do_o);
891 did = true;
892 } else if (style->fill.isColor()) {
893 style->fill.setColor(tweak_color(mode, style->fill.getColor(), *fill_goal, this_force, do_h, do_s, do_l));
894 item->updateRepr();
895 did = true;
896 }
897 }
898 if (stroke_goal) {
899 if (style->stroke.isPaintserver()) {
900 tweak_colors_in_gradient(item, Inkscape::FOR_STROKE, *stroke_goal, p, radius, this_force, mode, do_h, do_s, do_l, do_o);
901 did = true;
902 } else if (style->stroke.isColor()) {
903 style->stroke.setColor(tweak_color(mode, style->stroke.getColor(), *stroke_goal, this_force, do_h, do_s, do_l));
904 item->updateRepr();
905 did = true;
906 }
907 }
908 if (do_opacity && do_o) {
909 tweak_opacity (mode, &style->opacity, opacity_goal, this_force);
910 }
911 }
912}
913
914return did;
915}
916
917
918static bool
919sp_tweak_dilate (TweakTool *tc, Geom::Point event_p, Geom::Point p, Geom::Point vector, bool reverse)
920{
923
924 if (selection->isEmpty()) {
925 return false;
926 }
927
928 bool did = false;
929 double radius = get_dilate_radius(tc);
930
931 SPItem *item_at_point = tc->getDesktop()->getItemAtPoint(event_p, true);
932
933 bool do_opacity = false;
934 auto fill_goal = sp_desktop_get_color_tool(desktop, "/tools/tweak", true);
935 auto stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/tweak", false);
936 double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/tweak", &do_opacity);
937 if (reverse) {
938 if (fill_goal)
939 fill_goal->invert();
940 if (stroke_goal)
941 stroke_goal->invert();
942 opacity_goal = 1 - opacity_goal;
943 }
944
945 double path_force = get_path_force(tc);
946 if (radius == 0 || path_force == 0) {
947 return false;
948 }
949 double move_force = get_move_force(tc);
950 double color_force = MIN(sqrt(path_force)/20.0, 1);
951
952 // auto items= selection->items();
953 std::vector<SPItem*> items(selection->items().begin(), selection->items().end());
954 for(auto item : items){
955 if (is_color_mode (tc->mode)) {
956 if (fill_goal || stroke_goal || do_opacity) {
957 if (sp_tweak_color_recursive (tc->mode, item, item_at_point,
958 fill_goal, stroke_goal,
959 opacity_goal, do_opacity,
960 tc->mode == TWEAK_MODE_BLUR, reverse,
961 p, radius, color_force, tc->do_h, tc->do_s, tc->do_l, tc->do_o)) {
962 did = true;
963 }
964 }
965 } else if (is_transform_mode(tc->mode)) {
966 if (sp_tweak_dilate_recursive (selection, item, p, vector, tc->mode, radius, move_force, tc->fidelity, reverse)) {
967 did = true;
968 }
969 } else {
970 if (sp_tweak_dilate_recursive (selection, item, p, vector, tc->mode, radius, path_force, tc->fidelity, reverse)) {
971 did = true;
972 }
973 }
974 }
975
976 return did;
977}
978
979 static void
981{
982 double radius = get_dilate_radius(tc);
983 Geom::Affine const sm (Geom::Scale(radius, radius) * Geom::Translate(tc->getDesktop()->point()));
984
985 Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1)); // Unit circle centered at origin.
986 path *= sm;
987 tc->dilate_area->set_bpath(path);
988 tc->dilate_area->set_visible(true);
989}
990
991 static void
992sp_tweak_switch_mode (TweakTool *tc, gint mode, bool with_shift)
993{
994 auto tb = dynamic_cast<UI::Toolbar::TweakToolbar*>(tc->getDesktop()->get_toolbar_by_name("TweakToolbar"));
995
996 if(tb) {
997 tb->setMode(mode);
998 } else {
999 std::cerr << "Could not access Tweak toolbar" << std::endl;
1000 }
1001
1002 // need to set explicitly, because the prefs may not have changed by the previous
1003 tc->mode = mode;
1004 tc->update_cursor(with_shift);
1005}
1006
1007 static void
1009{
1011 // Juggling about so that prefs have the old value but tc->mode and the button show new mode:
1012 gint now_mode = prefs->getInt("/tools/tweak/mode", 0);
1013
1014 auto tb = dynamic_cast<UI::Toolbar::TweakToolbar*>(tc->getDesktop()->get_toolbar_by_name("TweakToolbar"));
1015
1016 if(tb) {
1017 tb->setMode(mode);
1018 } else {
1019 std::cerr << "Could not access Tweak toolbar" << std::endl;
1020 }
1021
1022 // button has changed prefs, restore
1023 prefs->setInt("/tools/tweak/mode", now_mode);
1024 // changing prefs changed tc->mode, restore back :
1025 tc->mode = mode;
1026 tc->update_cursor(with_shift);
1027}
1028
1030{
1031 bool ret = false;
1032
1033 inspect_event(event,
1034 [&] (EnterEvent const &event) {
1035 dilate_area->set_visible(true);
1036 },
1037 [&] (LeaveEvent const &event) {
1038 dilate_area->set_visible(false);
1039 },
1040 [&] (ButtonPressEvent const &event) {
1041 if (event.num_press == 1 && event.button == 1) {
1043 ret = true;
1044 } else {
1045
1046 Geom::Point const button_dt(_desktop->w2d(event.pos));
1047 last_push = _desktop->dt2doc(button_dt);
1048
1049 sp_tweak_extinput(this, event.extinput);
1050
1051 is_drawing = true;
1052 is_dilating = true;
1053 has_dilated = false;
1054
1055 ret = true;
1056 }
1057 }
1058 },
1059 [&] (MotionEvent const &event) {
1060
1061 Geom::Point motion_dt(_desktop->w2d(event.pos));
1062 Geom::Point motion_doc(_desktop->dt2doc(motion_dt));
1063 sp_tweak_extinput(this, event.extinput);
1064
1065 // Draw the dilating cursor.
1066 double radius = get_dilate_radius(this);
1067 Geom::Affine const sm(Geom::Scale(radius, radius) * Geom::Translate(motion_dt));
1068 Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1)); // Unit circle centered at origin.
1069 path *= sm;
1070 dilate_area->set_bpath(path);
1071 dilate_area->set_visible(true);
1072
1073 unsigned num = 0;
1074 if (!_desktop->getSelection()->isEmpty()) {
1075 num = (unsigned)boost::distance(_desktop->getSelection()->items());
1076 }
1077 if (num == 0) {
1078 message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to tweak."));
1079 }
1080
1081 // dilating:
1082 if (is_drawing && ( event.modifiers & GDK_BUTTON1_MASK )) {
1083 sp_tweak_dilate (this, event.pos, motion_doc, motion_doc - last_push, event.modifiers & GDK_SHIFT_MASK? true : false);
1084 //this->last_push = motion_doc;
1085 has_dilated = true;
1086 // It's slow, so prevent clogging up with events.
1087 gobble_motion_events(GDK_BUTTON1_MASK);
1088 ret = true;
1089 }
1090 },
1091 [&] (ButtonReleaseEvent const &event) {
1092
1093 Geom::Point const motion_dt(_desktop->w2d(event.pos));
1094
1095 is_drawing = false;
1096
1097 if (is_dilating && event.button == 1) {
1098 if (has_dilated) {
1099 // If we did not rub, do a light tap.
1100 pressure = 0.03;
1101 sp_tweak_dilate(this, event.pos, _desktop->dt2doc(motion_dt), Geom::Point(0, 0), event.modifiers & GDK_SHIFT_MASK);
1102 }
1103 is_dilating = false;
1104 has_dilated = false;
1105 Glib::ustring text;
1106 switch (mode) {
1107 case TWEAK_MODE_MOVE:
1108 text = _("Move tweak");
1109 break;
1111 text = _("Move in/out tweak");
1112 break;
1114 text = _("Move jitter tweak");
1115 break;
1116 case TWEAK_MODE_SCALE:
1117 text = _("Scale tweak");
1118 break;
1119 case TWEAK_MODE_ROTATE:
1120 text = _("Rotate tweak");
1121 break;
1123 text = _("Duplicate/delete tweak");
1124 break;
1125 case TWEAK_MODE_PUSH:
1126 text = _("Push path tweak");
1127 break;
1129 text = _("Shrink/grow path tweak");
1130 break;
1132 text = _("Attract/repel path tweak");
1133 break;
1134 case TWEAK_MODE_ROUGHEN:
1135 text = _("Roughen path tweak");
1136 break;
1138 text = _("Color paint tweak");
1139 break;
1141 text = _("Color jitter tweak");
1142 break;
1143 case TWEAK_MODE_BLUR:
1144 text = _("Blur tweak");
1145 break;
1146 }
1147 DocumentUndo::done(_desktop->getDocument(), text.c_str(), INKSCAPE_ICON("tool-tweak"));
1148 }
1149 },
1150 [&] (KeyPressEvent const &event) {
1151 switch (get_latin_keyval (event)) {
1152 case GDK_KEY_m:
1153 case GDK_KEY_M:
1154 case GDK_KEY_0:
1155 if (mod_shift_only(event)) {
1157 ret = true;
1158 }
1159 break;
1160 case GDK_KEY_i:
1161 case GDK_KEY_I:
1162 case GDK_KEY_1:
1163 if (mod_shift_only(event)) {
1165 ret = true;
1166 }
1167 break;
1168 case GDK_KEY_z:
1169 case GDK_KEY_Z:
1170 case GDK_KEY_2:
1171 if (mod_shift_only(event)) {
1173 ret = true;
1174 }
1175 break;
1176 case GDK_KEY_less:
1177 case GDK_KEY_comma:
1178 case GDK_KEY_greater:
1179 case GDK_KEY_period:
1180 case GDK_KEY_3:
1181 if (mod_shift_only(event)) {
1183 ret = true;
1184 }
1185 break;
1186 case GDK_KEY_bracketright:
1187 case GDK_KEY_bracketleft:
1188 case GDK_KEY_4:
1189 if (mod_shift_only(event)) {
1191 ret = true;
1192 }
1193 break;
1194 case GDK_KEY_d:
1195 case GDK_KEY_D:
1196 case GDK_KEY_5:
1197 if (mod_shift_only(event)) {
1199 ret = true;
1200 }
1201 break;
1202 case GDK_KEY_p:
1203 case GDK_KEY_P:
1204 case GDK_KEY_6:
1205 if (mod_shift_only(event)) {
1207 ret = true;
1208 }
1209 break;
1210 case GDK_KEY_s:
1211 case GDK_KEY_S:
1212 case GDK_KEY_7:
1213 if (mod_shift_only(event)) {
1215 ret = true;
1216 }
1217 break;
1218 case GDK_KEY_a:
1219 case GDK_KEY_A:
1220 case GDK_KEY_8:
1221 if (mod_shift_only(event)) {
1223 ret = true;
1224 }
1225 break;
1226 case GDK_KEY_r:
1227 case GDK_KEY_R:
1228 case GDK_KEY_9:
1229 if (mod_shift_only(event)) {
1231 ret = true;
1232 }
1233 break;
1234 case GDK_KEY_c:
1235 case GDK_KEY_C:
1236 if (mod_shift_only(event)) {
1238 ret = true;
1239 }
1240 break;
1241 case GDK_KEY_j:
1242 case GDK_KEY_J:
1243 if (mod_shift_only(event)) {
1245 ret = true;
1246 }
1247 break;
1248 case GDK_KEY_b:
1249 case GDK_KEY_B:
1250 if (mod_shift_only(event)) {
1252 ret = true;
1253 }
1254 break;
1255
1256 case GDK_KEY_Up:
1257 case GDK_KEY_KP_Up:
1258 if (!mod_ctrl_only(event)) {
1259 force += 0.05;
1260 if (force > 1.0) {
1261 force = 1.0;
1262 }
1263 _desktop->setToolboxAdjustmentValue("tweak-force", force * 100);
1264 ret = true;
1265 }
1266 break;
1267 case GDK_KEY_Down:
1268 case GDK_KEY_KP_Down:
1269 if (!mod_ctrl_only(event)) {
1270 force -= 0.05;
1271 if (force < 0.0) {
1272 force = 0.0;
1273 }
1274 _desktop->setToolboxAdjustmentValue("tweak-force", force * 100);
1275 ret = true;
1276 }
1277 break;
1278 case GDK_KEY_Right:
1279 case GDK_KEY_KP_Right:
1280 if (!mod_ctrl_only(event)) {
1281 width += 0.01;
1282 if (width > 1.0) {
1283 width = 1.0;
1284 }
1285 _desktop->setToolboxAdjustmentValue ("tweak-width", width * 100); // the same spinbutton is for alt+x
1287 ret = true;
1288 }
1289 break;
1290 case GDK_KEY_Left:
1291 case GDK_KEY_KP_Left:
1292 if (!mod_ctrl_only(event)) {
1293 width -= 0.01;
1294 if (width < 0.01) {
1295 width = 0.01;
1296 }
1297 _desktop->setToolboxAdjustmentValue("tweak-width", width * 100);
1299 ret = true;
1300 }
1301 break;
1302 case GDK_KEY_Home:
1303 case GDK_KEY_KP_Home:
1304 width = 0.01;
1305 _desktop->setToolboxAdjustmentValue("tweak-width", width * 100);
1307 ret = true;
1308 break;
1309 case GDK_KEY_End:
1310 case GDK_KEY_KP_End:
1311 width = 1.0;
1312 _desktop->setToolboxAdjustmentValue("tweak-width", width * 100);
1314 ret = true;
1315 break;
1316 case GDK_KEY_x:
1317 case GDK_KEY_X:
1318 if (mod_alt_only(event)) {
1319 _desktop->setToolboxFocusTo("tweak-width");
1320 ret = true;
1321 }
1322 break;
1323
1324 case GDK_KEY_Shift_L:
1325 case GDK_KEY_Shift_R:
1326 update_cursor(true);
1327 break;
1328
1329 case GDK_KEY_Control_L:
1330 case GDK_KEY_Control_R:
1332 break;
1333 case GDK_KEY_Delete:
1334 case GDK_KEY_KP_Delete:
1335 case GDK_KEY_BackSpace:
1336 ret = deleteSelectedDrag(mod_ctrl_only(event));
1337 break;
1338
1339 default:
1340 break;
1341 }
1342 },
1343 [&] (KeyReleaseEvent const &event) {
1345 switch (get_latin_keyval(event)) {
1346 case GDK_KEY_Shift_L:
1347 case GDK_KEY_Shift_R:
1348 update_cursor(false);
1349 break;
1350 case GDK_KEY_Control_L:
1351 case GDK_KEY_Control_R:
1352 sp_tweak_switch_mode (this, prefs->getInt("/tools/tweak/mode"), mod_shift(event));
1353 message_context->clear();
1354 break;
1355 default:
1356 sp_tweak_switch_mode (this, prefs->getInt("/tools/tweak/mode"), mod_shift(event));
1357 break;
1358 }
1359 },
1360 [&] (CanvasEvent const &event) {}
1361 );
1362
1363 return ret || ToolBase::root_handler(event);
1364}
1365
1366} // namespace Inkscape::UI::Tool
1367
1368/*
1369 Local Variables:
1370 mode:c++
1371 c-file-style:"stroustrup"
1372 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1373 indent-tabs-mode:nil
1374 fill-column:99
1375 End:
1376*/
1377// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
@ fill_oddEven
Definition LivarotDefs.h:68
@ fill_nonZero
Definition LivarotDefs.h:69
@ fill_positive
Definition LivarotDefs.h:70
@ join_straight
Definition LivarotDefs.h:61
TODO: insert short description here.
double distance(Shape const *s, Geom::Point const &p)
Definition Shape.cpp:2136
TODO: insert short description here.
@ tweak_mode_repel
Definition Shape.h:33
@ tweak_mode_grow
Definition Shape.h:31
@ tweak_mode_push
Definition Shape.h:32
@ tweak_mode_roughen
Definition Shape.h:34
double scale
Definition aa.cpp:228
Circle shape.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Affine withoutTranslation() const
Definition affine.h:169
Set of all points at a fixed distance from the center.
Definition circle.h:55
bool contains(CRect const &r) const
Check whether the rectangle includes all points in the given rectangle.
bool intersects(CRect const &r) const
Check whether the rectangles have any common points.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of subpaths.
Definition pathvector.h:122
Sequence of contiguous curves, aka spline.
Definition path.h:353
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
Rotation around the origin.
Definition transforms.h:187
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
std::optional< Color > converted(Color const &other) const
Return a copy of this color converted to the same format as the other color.
Definition color.cpp:189
void average(Color const &other, double pos=0.5, unsigned int pin=0)
Definition color.cpp:544
std::shared_ptr< Space::AnySpace > const & getSpace() const
Definition color.h:39
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
SPDesktop * desktop()
Returns the desktop the selection is bound to.
Definition object-set.h:390
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
bool isEmpty()
Returns true if no items are selected.
Data type representing a typeless value of a preference.
double getDouble(double def=0.0, Glib::ustring const &unit="") const
Interpret the preference as a floating point value.
Glib::ustring getEntryName() const
Get the last component of the preference's path.
bool getBool(bool def=false) const
Interpret the preference as a Boolean value.
int getInt(int def=0) const
Interpret the preference as an integer.
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
void setStyle(Glib::ustring const &pref_path, SPCSSAttr *style)
Set a CSS style.
static Preferences * get()
Access the singleton Preferences object.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void add(XML::Node *repr)
Add an XML node's SPObject to the set of selected objects.
Definition selection.h:107
void remove(XML::Node *repr)
Removes an item from the set of selected objects.
Definition selection.h:131
bool includes(XML::Node *repr, bool anyAncestor=false)
Returns true if the given item is selected.
Definition selection.h:140
Base class for Event processors.
Definition tool-base.h:107
void set_cursor(std::string filename)
Sets the current cursor to the given filename.
SPDesktop * getDesktop() const
Definition tool-base.h:125
std::unique_ptr< MessageContext > message_context
Definition tool-base.h:193
virtual bool root_handler(CanvasEvent const &event)
void enableGrDrag(bool enable=true)
void enableSelectionCue(bool enable=true)
Enables/disables the ToolBase's SelCue.
MessageContext * defaultMessageContext() const
Definition tool-base.h:123
bool deleteSelectedDrag(bool just_one)
Delete a selected GrDrag point.
sigc::scoped_connection style_set_connection
Definition tweak-tool.h:76
bool set_style(SPCSSAttr const *css)
CanvasItemPtr< CanvasItemBpath > dilate_area
Definition tweak-tool.h:69
TweakTool(SPDesktop *desktop)
void set(Preferences::Entry const &val) override
Called by our pref_observer if a preference has been changed.
void update_cursor(bool with_shift)
bool root_handler(CanvasEvent const &event) override
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual unsigned position() const =0
Get the index of this node in parent's child order.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Path and its polyline approximation.
Definition Path.h:93
void SetBackData(bool nVal)
Sets the back variable to the value passed in and clears the polyline approximation.
Definition Path.cpp:232
std::string svg_dump_path() const
Definition Path.cpp:577
void ConvertEvenLines(double treshhold)
Creates a polyline approximation of the path.
std::vector< PathDescr * > descr_cmd
Definition Path.h:108
void Simplify(double treshhold)
Simplify the path.
void Reset()
Clears all stored path commands and resets flags that are used by command functions while adding path...
Definition Path.cpp:45
To do: update description of desktop.
Definition desktop.h:149
double current_zoom() const
Definition desktop.h:335
SPDocument * getDocument() const
Definition desktop.h:189
void setToolboxFocusTo(char const *label)
Definition desktop.cpp:1128
Inkscape::CanvasItemGroup * getCanvasSketch() const
Definition desktop.h:201
Geom::Affine const & dt2doc() const
Definition desktop.cpp:1343
Geom::Point point() const
Returns the mouse point in desktop coordinates; if mouse is outside the canvas, returns the center of...
Definition desktop.cpp:378
SPItem * getItemAtPoint(Geom::Point const &p, bool into_groups, SPItem *upto=nullptr) const
Definition desktop.cpp:352
Gtk::Widget * get_toolbar_by_name(Glib::ustring const &name)
Definition desktop.cpp:1139
Inkscape::Selection * getSelection() const
Definition desktop.h:188
double yaxisdir() const
Definition desktop.h:426
sigc::connection connectSetStyle(F &&slot)
Definition desktop.h:265
void setToolboxAdjustmentValue(char const *id, double val)
Definition desktop.cpp:1134
Geom::Affine const & doc2dt() const
Definition desktop.cpp:1337
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Definition desktop.h:416
Typed SVG document implementation.
Definition document.h:103
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Gradient.
Definition sp-gradient.h:86
Geom::Affine gradientTransform
gradientTransform attribute
Definition sp-gradient.h:99
SPGradientSpread getSpread() const
24 bit data type internal to SPStyle.
unsigned value
Base class for visual SVG elements.
Definition sp-item.h:109
void scale_rel(Geom::Scale const &scale)
Definition sp-item.cpp:1923
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1821
Geom::OptRect documentVisualBounds() const
Get item's visual bbox in document coordinate system.
Definition sp-item.cpp:1018
Geom::OptRect documentGeometricBounds() const
Get item's geometric bbox in document coordinate system.
Definition sp-item.cpp:1013
void rotate_rel(Geom::Rotate const &rotation)
Definition sp-item.cpp:1905
void move_rel(Geom::Translate const &tr)
Definition sp-item.cpp:1951
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1816
std::vector< std::vector< SPMeshNode * > > nodes
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
SPDocument * document
Definition sp-object.h:188
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
ChildrenList children
Definition sp-object.h:907
Gradient stop.
Definition sp-stop.h:31
void setColor(Inkscape::Colors::Color const &color)
Sets the stop color and stop opacity in the style attribute.
Definition sp-stop.cpp:142
Inkscape::Colors::Color getColor() const
Definition sp-stop.cpp:130
An SVG style object.
Definition style.h:45
T< SPAttr::FILL, SPIPaint > fill
fill
Definition style.h:240
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition style.h:247
SPFilter * getFilter()
Definition style.h:335
T< SPAttr::OPACITY, SPIScale24 > opacity
opacity
Definition style.h:216
T< SPAttr::FILTER, SPIFilter > filter
Filter effect.
Definition style.h:275
A class to store/manipulate directed graphs.
Definition Shape.h:65
void ConvertToForme(Path *dest)
Extract contours from a directed graph.
Definition ShapeMisc.cpp:42
int MakeTweak(int mode, Shape *a, double dec, JoinType join, double miter, bool do_profile, Geom::Point c, Geom::Point vector, double radius, Geom::Affine *i2doc)
int ConvertToShape(Shape *a, FillRule directed=fill_nonZero, bool invert=false)
Using a given fill rule, find all intersections in the shape given, create a new intersection free sh...
std::shared_ptr< Css const > css
double c[8][4]
Geom::Point orig
double sp_desktop_get_master_opacity_tool(SPDesktop *desktop, Glib::ustring const &tool, bool *has_opacity)
std::optional< Color > sp_desktop_get_color_tool(SPDesktop *desktop, Glib::ustring const &tool, bool is_fill)
Editable view implementation.
static char const *const parent
Definition dir-util.cpp:70
TODO: insert short description here.
void remove_filter(SPObject *item, bool recursive)
SPFilter * modify_filter_gaussian_blur_from_item(SPDocument *document, SPItem *item, gdouble radius)
Modifies the gaussian blur applied to the item.
SVG Gaussian blur filter effect.
SPGradient * sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector)
Obtain the vector from the gradient.
SPGradient * getGradient(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
Fetches either the fill or the stroke gradient from the given item.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
auto floor(Geom::Rect const &rect)
Definition geom.h:130
Macro for icon names used in Inkscape.
SPItem * item
Interface for locally managing a current status message.
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
static R & release(R &r)
Decrements the reference count of a anchored object.
static void sp_tweak_extinput(TweakTool *tc, ExtendedInput const &ext)
static void tweak_stop_color(guint mode, SPStop *stop, Color const &goal, double force, bool do_h, bool do_s, bool do_l)
static Color tweak_color(guint mode, Color const &color, Color const &goal, double force, bool do_h, bool do_s, bool do_l)
static double tweak_profile(double dist, double radius)
static bool sp_tweak_dilate_recursive(Inkscape::Selection *selection, SPItem *item, Geom::Point p, Geom::Point vector, gint mode, double radius, double force, double fidelity, bool reverse)
static bool is_color_mode(gint mode)
static void tweak_opacity(guint mode, SPIScale24 *style_opacity, double opacity_goal, double force)
static double get_move_force(TweakTool *tc)
void gobble_motion_events(unsigned mask)
Definition tool-base.h:244
static bool is_transform_mode(gint mode)
static void sp_tweak_switch_mode_temporarily(TweakTool *tc, gint mode, bool with_shift)
static void tweak_colors_in_gradient(SPItem *item, Inkscape::PaintTarget fill_or_stroke, Color const &goal, Geom::Point p_w, double radius, double force, guint mode, bool do_h, bool do_s, bool do_l, bool)
unsigned get_latin_keyval(GtkEventControllerKey const *const controller, unsigned const keyval, unsigned const keycode, GdkModifierType const state, unsigned *consumed_modifiers)
Return the keyval corresponding to the event controller key in Latin group.
static double get_dilate_radius(SprayTool *tc)
static bool sp_tweak_color_recursive(guint mode, SPItem *item, SPItem *item_at_point, std::optional< Color > &fill_goal, std::optional< Color > &stroke_goal, float opacity_goal, bool do_opacity, bool do_blur, bool reverse, Geom::Point p, double radius, double force, bool do_h, bool do_s, bool do_l, bool do_o)
static double get_path_force(TweakTool *tc)
static void sp_tweak_update_area(TweakTool *tc)
static bool sp_tweak_dilate(TweakTool *tc, Geom::Point event_p, Geom::Point p, Geom::Point vector, bool reverse)
void sp_event_context_read(ToolBase *tool, char const *key)
Calls virtual set() function of ToolBase.
static void sp_tweak_switch_mode(TweakTool *tc, gint mode, bool with_shift)
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
bool mod_ctrl_only(unsigned modifiers)
bool mod_shift_only(unsigned modifiers)
bool mod_shift(unsigned modifiers)
bool mod_alt_only(unsigned modifiers)
@ ERROR_MESSAGE
Definition message.h:29
@ NORMAL_MESSAGE
Definition message.h:26
bool have_viable_layer(SPDesktop *desktop, MessageContext *message)
Check to see if the current layer is both unhidden and unlocked.
int mode
Inkscape::XML::Node * sp_selected_item_to_curved_repr(SPItem *item, guint32)
bool sp_item_list_to_curves(const std::vector< SPItem * > &items, std::vector< SPItem * > &selected, std::vector< Inkscape::XML::Node * > &to_select, bool skip_all_lpeitems)
std::unique_ptr< Path > Path_for_item(SPItem *item, bool doTransformation, bool transformFull)
Creates a Livarot Path object from an SPItem.
Definition path-util.cpp:32
Path utilities.
Ocnode * child[8]
Definition quantize.cpp:33
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
Definition repr-css.cpp:88
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
GList * items
int num
Definition scribble.cpp:47
TODO: insert short description here.
@ SP_GRADIENT_SPREAD_PAD
@ SP_GRADIENT_SPREAD_REPEAT
@ SP_GRADIENT_SPREAD_REFLECT
TODO: insert short description here.
TODO: insert short description here.
TODO: insert short description here.
TODO: insert short description here.
A mouse button (left/right/middle) is pressed.
A mouse button (left/right/middle) is released.
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
The pointer has entered a widget or item.
Extended input data associated to events generated by graphics tablets.
std::optional< double > pressure
A key has been pressed.
A key has been released.
The pointer has exited a widget or item.
Movement of the mouse pointer.
Interface for XML documents.
Definition document.h:43
@ SP_WIND_RULE_EVENODD
Definition style-enums.h:26
void sp_style_set_property_url(SPObject *item, gchar const *property, SPObject *linked, bool recursive)
Definition style.cpp:1380
SPCSSAttr * sp_css_attr_unset_uris(SPCSSAttr *css)
Unset any properties that contain URI values.
Definition style.cpp:1543
SPStyle - a style object for SPItem objects.
SPDesktop * desktop
double width