1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /** @file
4 *
5 * Path related functions for ObjectSet.
6 *
7 * Copyright (C) 2020 Tavmjong Bah
8 *
9 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
10 *
11 * TODO: Move related code from path-chemistry.cpp
12 *
13 */
14
15 #include <glibmm/i18n.h>
16
17 #include "document-undo.h"
18 #include "message-stack.h"
19
20 #include "attribute-rel-util.h"
21
22 #include "object/object-set.h"
23 #include "path/path-outline.h"
24 #include "path/path-simplify.h"
25
26 using Inkscape::ObjectSet;
27
28 bool
strokesToPaths(bool legacy,bool skip_undo)29 ObjectSet::strokesToPaths(bool legacy, bool skip_undo)
30 {
31 if (desktop() && isEmpty()) {
32 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>stroked path(s)</b> to convert stroke to path."));
33 return false;
34 }
35
36 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
37 if (prefs->getBool("/options/pathoperationsunlink/value", true)) {
38 unlinkRecursive(true);
39 }
40
41 // Need to turn on stroke scaling to ensure stroke is scaled when transformed!
42 bool scale_stroke = prefs->getBool("/options/transform/stroke", true);
43 prefs->setBool("/options/transform/stroke", true);
44
45 bool did = false;
46
47 std::vector<SPItem *> my_items(items().begin(), items().end());
48
49 for (auto item : my_items) {
50 // Do not remove the object from the selection here
51 // as we want to keep it selected if the whole operation fails
52 Inkscape::XML::Node *new_node = item_to_paths(item, legacy);
53 if (new_node) {
54 SPObject* new_item = document()->getObjectByRepr(new_node);
55
56 // Markers don't inherit properties from outside the
57 // marker. When converted to paths objects they need to be
58 // protected from inheritance. This is why (probably) the stroke
59 // to path code uses SP_STYLE_FLAG_ALWAYS when defining the
60 // style of the fill and stroke during the conversion. This
61 // means the style contains every possible property. Once we've
62 // finished the stroke to path conversion, we can eliminate
63 // unneeded properties from the style element.
64 sp_attribute_clean_recursive(new_node, SP_ATTRCLEAN_STYLE_REMOVE | SP_ATTRCLEAN_DEFAULT_REMOVE);
65
66 add(new_item); // Add to selection.
67 did = true;
68 }
69 }
70
71 // Reset
72 prefs->setBool("/options/transform/stroke", scale_stroke);
73
74 if (desktop() && !did) {
75 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No stroked paths</b> in the selection."));
76 }
77
78 if (did && !skip_undo) {
79 Inkscape::DocumentUndo::done(document(), SP_VERB_NONE, _("Convert stroke to path"));
80 }
81
82 return did;
83 }
84
85 bool
simplifyPaths(bool skip_undo)86 ObjectSet::simplifyPaths(bool skip_undo)
87 {
88 if (desktop() && isEmpty()) {
89 desktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>path(s)</b> to simplify."));
90 return false;
91 }
92
93 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
94 double threshold = prefs->getDouble("/options/simplifythreshold/value", 0.003);
95 bool justCoalesce = prefs->getBool( "/options/simplifyjustcoalesce/value", false);
96
97 // Keep track of accelerated simplify
98 static gint64 previous_time = 0;
99 static gdouble multiply = 1.0;
100
101 // Get the current time
102 gint64 current_time = g_get_monotonic_time();
103
104 // Was the previous call to this function recent? (<0.5 sec)
105 if (previous_time > 0 && current_time - previous_time < 500000) {
106
107 // add to the threshold 1/2 of its original value
108 multiply += 0.5;
109 threshold *= multiply;
110
111 } else {
112 // reset to the default
113 multiply = 1;
114 }
115
116 // Remember time for next call
117 previous_time = current_time;
118
119 // set "busy" cursor
120 if (desktop()) {
121 desktop()->setWaitingCursor();
122 }
123
124 Geom::OptRect selectionBbox = visualBounds();
125 if (!selectionBbox) {
126 std::cerr << "ObjectSet::: selection has no visual bounding box!" << std::endl;
127 return false;
128 }
129 double size = L2(selectionBbox->dimensions());
130
131 int pathsSimplified = 0;
132 std::vector<SPItem *> my_items(items().begin(), items().end());
133 for (auto item : my_items) {
134 pathsSimplified += path_simplify(item, threshold, justCoalesce, size);
135 }
136
137 if (pathsSimplified > 0 && !skip_undo) {
138 DocumentUndo::done(document(), SP_VERB_SELECTION_SIMPLIFY, _("Simplify"));
139 }
140
141 if (desktop()) {
142 desktop()->clearWaitingCursor();
143 if (pathsSimplified > 0) {
144 desktop()->messageStack()->flashF(Inkscape::NORMAL_MESSAGE, _("<b>%d</b> paths simplified."), pathsSimplified);
145 } else {
146 desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No paths</b> to simplify in the selection."));
147 }
148 }
149
150 return (pathsSimplified > 0);
151 }
152
153 /*
154 Local Variables:
155 mode:c++
156 c-file-style:"stroustrup"
157 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
158 indent-tabs-mode:nil
159 fill-column:99
160 End:
161 */
162 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
163