Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * LPEToolContext: a context for a generic tool composed of subtools that are given by LPEs
4 *
5 * Authors:
6 * Maximilian Albert <maximilian.albert@gmail.com>
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Abhishek Sharma
9 *
10 * Copyright (C) 1998 The Free Software Foundation
11 * Copyright (C) 1999-2005 authors
12 * Copyright (C) 2001-2002 Ximian, Inc.
13 * Copyright (C) 2008 Maximilian Albert
14 *
15 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 */
17
18#include <iomanip>
19
20#include <glibmm/i18n.h>
21#include <gtk/gtk.h>
22
24
25#include "desktop.h"
26#include "document.h"
27#include "message-stack.h"
28#include "selection.h"
29
30#include "display/curve.h"
33
34#include "object/sp-path.h"
35
36#include "util/units.h"
38
40#include "ui/tools/lpe-tool.h"
41#include "ui/shape-editor.h"
43
46
47int const num_subtools = 8;
48
50 // this must be here to account for the "all inactive" action
51 {Inkscape::LivePathEffect::INVALID_LPE, "draw-geometry-inactive"},
52 {Inkscape::LivePathEffect::LINE_SEGMENT, "draw-geometry-line-segment"},
53 {Inkscape::LivePathEffect::CIRCLE_3PTS, "draw-geometry-circle-from-three-points"},
54 {Inkscape::LivePathEffect::CIRCLE_WITH_RADIUS, "draw-geometry-circle-from-radius"},
55 {Inkscape::LivePathEffect::PARALLEL, "draw-geometry-line-parallel"},
56 {Inkscape::LivePathEffect::PERP_BISECTOR, "draw-geometry-line-perpendicular"},
57 {Inkscape::LivePathEffect::ANGLE_BISECTOR, "draw-geometry-angle-bisector"},
58 {Inkscape::LivePathEffect::MIRROR_SYMMETRY, "draw-geometry-mirror"}
59};
60
61namespace Inkscape::UI::Tools {
62
64 : PenTool(desktop, "/tools/lpetool", "geometric.svg")
65{
66 auto const selection = desktop->getSelection();
67 auto const item = selection->singleItem();
68
70
71 shape_editor = std::make_unique<ShapeEditor>(desktop);
72
76
77// TODO temp force:
79
80 if (item) {
81 shape_editor->set_item(item);
82 }
83
84 auto prefs = Preferences::get();
85 if (prefs->getBool("/tools/lpetool/selcue")) {
87 }
88}
89
90LpeTool::~LpeTool() = default;
91
97{
98 shape_editor->unset_item();
99 shape_editor->set_item(selection->singleItem());
100}
101
103{
104 if (val.getEntryName() == "mode") {
105 Preferences::get()->setString("/tools/geometric/mode", "drag");
107 }
108}
109
111{
112 bool ret = false;
113
114 inspect_event(event,
115 [&] (ButtonPressEvent const &event) {
116 if (event.num_press != 1 || event.button != 1) {
117 return;
118 }
119 // select the clicked item but do nothing else
120 auto const selection = _desktop->getSelection();
121 selection->clear();
123 ret = true;
124 },
125 [&] (ButtonReleaseEvent const &event) {
126 // TODO: do we need to catch this or can we pass it on to the parent handler?
127 ret = true;
128 },
129 [&] (CanvasEvent const &event) {}
130 );
131
132 return ret || PenTool::item_handler(item, event);
133}
134
136{
137 if (hasWaitingLPE()) {
138 // quit when we are waiting for a LPE to be applied
139 return PenTool::root_handler(event);
140 }
141
142 auto const selection = _desktop->getSelection();
143
144 bool ret = false;
145
146 inspect_event(event,
147 [&] (ButtonPressEvent const &event) {
148 if (event.num_press == 1 && event.button == 1) {
149 if (mode == LivePathEffect::INVALID_LPE) {
150 // don't do anything for now if we are inactive (except clearing the selection
151 // since this was a click into empty space)
152 selection->clear();
153 _desktop->messageStack()->flash(WARNING_MESSAGE, _("Choose a construction tool from the toolbar."));
154 ret = true;
155 return;
156 }
157
158 saveDragOrigin(event.pos);
159
160 auto prefs = Preferences::get();
161 int mode = prefs->getInt("/tools/lpetool/mode");
162 auto type = lpesubtools[mode].type;
163
165 }
166 },
167
168 [&] (CanvasEvent const &event) {}
169 );
170
171 return ret || PenTool::root_handler(event);
172}
173
174/*
175 * Finds the index in the list of geometric subtools corresponding to the given LPE type.
176 * Returns -1 if no subtool is found.
177 */
179{
180 for (int i = 0; i < num_subtools; ++i) {
181 if (lpesubtools[i].type == type) {
182 return i;
183 }
184 }
185 return -1;
186}
187
188/*
189 * Checks whether an item has a construction applied as LPE and if so returns the index in
190 * lpesubtools of this construction
191 */
193{
194 if (!is<SPLPEItem>(item)) {
195 return -1;
196 }
197
198 auto lpe = cast<SPLPEItem>(item)->getCurrentLPE();
199 if (!lpe) {
200 return -1;
201 }
202
203 return lpetool_mode_to_index(lpe->effectType());
204}
205
206/*
207 * Attempts to perform the construction of the given type (i.e., to apply the corresponding LPE) to
208 * a single selected item. Returns whether we succeeded.
209 */
211{
212 auto const selection = desktop->getSelection();
213 auto const item = selection->singleItem();
214
215 // TODO: should we check whether type represents a valid geometric construction?
216 if (item && is<SPLPEItem>(item) && LivePathEffect::Effect::acceptsNumClicks(type) == 0) {
218 return true;
219 }
220
221 return false;
222}
223
224void LpeTool::switch_mode(LivePathEffect::EffectType const type)
225{
226 int index = lpetool_mode_to_index(type);
227 if (index == -1) {
228 g_warning("Invalid mode selected: %d", type);
229 return;
230 }
231
232 mode = type;
233
234 if (auto tb = dynamic_cast<UI::Toolbar::LPEToolbar*>(getDesktop()->get_toolbar_by_name("LPEToolToolbar"))) {
235 tb->setMode(index);
236 } else {
237 std::cerr << "Could not access LPE toolbar" << std::endl;
238 }
239}
240
241std::pair<Geom::Point, Geom::Point> lpetool_get_limiting_bbox_corners(SPDocument const *document)
242{
243 auto const w = document->getWidth().value("px");
244 auto const h = document->getHeight().value("px");
245
246 auto prefs = Preferences::get();
247 auto const ulx = prefs->getDouble("/tools/lpetool/bbox_upperleftx", 0);
248 auto const uly = prefs->getDouble("/tools/lpetool/bbox_upperlefty", 0);
249 auto const lrx = prefs->getDouble("/tools/lpetool/bbox_lowerrightx", w);
250 auto const lry = prefs->getDouble("/tools/lpetool/bbox_lowerrighty", h);
251
252 return {{ulx, uly}, {lrx, lry}};
253}
254
255/*
256 * Reads the limiting bounding box from preferences and draws it on the screen
257 */
258// TODO: Note that currently the bbox is not user-settable; we simply use the page borders
259void LpeTool::reset_limiting_bbox()
260{
261 canvas_bbox.reset();
262
263 auto prefs = Preferences::get();
264 if (!prefs->getBool("/tools/lpetool/show_bbox", true)) {
265 return;
266 }
267
268 auto const document = _desktop->getDocument();
269
270 auto [A, B] = lpetool_get_limiting_bbox_corners(document);
271 auto const doc2dt = _desktop->doc2dt();
272 A *= doc2dt;
273 B *= doc2dt;
274
275 canvas_bbox = make_canvasitem<CanvasItemRect>(_desktop->getCanvasControls(), Geom::Rect(A, B));
276 canvas_bbox->set_stroke(0x0000ffff);
277 canvas_bbox->set_dashed(true);
278}
279
281 double t, double length)
282{
283 auto const pwd2_reparam = arc_length_parametrization(pwd2, 2, 0.1);
284 auto const t_reparam = pwd2_reparam.cuts.back() * t;
285 auto const pos = pwd2_reparam.valueAt(t_reparam);
286 auto const dir = unit_vector(derivative(pwd2_reparam).valueAt(t_reparam));
287 auto const n = -rot90(dir);
288 auto const angle = Geom::angle_between(dir, Geom::Point(1, 0));
289
290 canvas_text->set_coord(pos + n * length);
291 canvas_text->set_anchor(Geom::Point(std::sin(angle), -std::cos(angle)));
292}
293
294void LpeTool::create_measuring_items(Selection *selection)
295{
296 if (!selection) {
297 selection = _desktop->getSelection();
298 }
299 auto prefs = Preferences::get();
300 bool show = prefs->getBool("/tools/lpetool/show_measuring_info", true);
301
302 auto tmpgrp = _desktop->getCanvasTemp();
303
304 auto const &unit_table = Util::UnitTable::get();
305
306 Util::Unit const *unit = nullptr;
307 if (prefs->getString("/tools/lpetool/unit").compare("")) {
308 unit = unit_table.getUnit(prefs->getString("/tools/lpetool/unit"));
309 } else {
310 unit = unit_table.getUnit("px");
311 }
312
313 for (auto item : selection->items()) {
314 if (auto path = cast<SPPath>(item)) {
315 SPCurve const *curve = path->curve();
316 auto const pwd2 = paths_to_pw(curve->get_pathvector());
317
318 double lengthval = Geom::length(pwd2);
319 lengthval = Util::Quantity::convert(lengthval, "px", unit);
320
321 auto arc_length = Inkscape::ustring::format_classic(std::setprecision(2), std::fixed, lengthval);
322 arc_length += " ";
323 arc_length += unit->abbr;
324
325 auto canvas_text = make_canvasitem<CanvasItemText>(tmpgrp, Geom::Point(0,0), arc_length);
326 set_pos_and_anchor(canvas_text.get(), pwd2, 0.5, 10);
327 if (!show) {
328 canvas_text->set_visible(false);
329 }
330
331 measuring_items[path] = std::move(canvas_text);
332 }
333 }
334}
335
336void LpeTool::delete_measuring_items()
337{
338 measuring_items.clear();
339}
340
341void LpeTool::update_measuring_items()
342{
343 auto prefs = Preferences::get();
344 auto const &unit_table = Util::UnitTable::get();
345
346 Util::Unit const *unit = nullptr;
347 if (prefs->getString("/tools/lpetool/unit").compare("")) {
348 unit = unit_table.getUnit(prefs->getString("/tools/lpetool/unit"));
349 } else {
350 unit = unit_table.getUnit("px");
351 }
352
353 for (auto &i : measuring_items) {
354 SPPath *path = i.first;
355 SPCurve const *curve = path->curve();
356 auto const pwd2 = Geom::paths_to_pw(curve->get_pathvector());
357 double lengthval = Geom::length(pwd2);
358 lengthval = Util::Quantity::convert(lengthval, "px", unit);
359
360 auto arc_length = Inkscape::ustring::format_classic(std::setprecision(2), std::fixed, lengthval);
361 arc_length += " ";
362 arc_length += unit->abbr;
363
364 i.second->set_text(std::move(arc_length));
365 set_pos_and_anchor(i.second.get(), pwd2, 0.5, 10);
366 }
367}
368
369void LpeTool::show_measuring_info(bool show)
370{
371 for (auto &i : measuring_items) {
372 i.second->set_visible(show);
373 }
374}
375
376} // namespace Inkscape::UI::Tools
377
378/*
379 Local Variables:
380 mode:c++
381 c-file-style:"stroustrup"
382 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
383 indent-tabs-mode:nil
384 fill-column:99
385 End:
386*/
387// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double arc_length(double t, void *params)
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
Function defined as discrete pieces.
Definition piecewise.h:71
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
void set_anchor(Geom::Point const &anchor_pt)
Set the anchor point, x and y between 0.0 and 1.0.
void set_coord(Geom::Point const &p)
Set a text position.
static void createAndApply(const char *name, SPDocument *doc, SPItem *item)
Definition effect.cpp:1118
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
void clear()
Unselects all selected objects.
SPItem * singleItem()
Returns a single selected item.
Data type representing a typeless value of a preference.
Glib::ustring getEntryName() const
Get the last component of the preference's path.
static Preferences * get()
Access the singleton Preferences object.
void setString(Glib::ustring const &pref_path, Glib::ustring const &value)
Set an UTF-8 string 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
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
void set(Preferences::Entry const &val) override
Called by our pref_observer if a preference has been changed.
Definition lpe-tool.cpp:102
bool item_handler(SPItem *item, CanvasEvent const &event) override
Handles item specific events.
Definition lpe-tool.cpp:110
void create_measuring_items(Selection *selection=nullptr)
Definition lpe-tool.cpp:294
LivePathEffect::EffectType mode
Definition lpe-tool.h:58
LpeTool(SPDesktop *desktop)
Definition lpe-tool.cpp:63
sigc::scoped_connection sel_changed_connection
Definition lpe-tool.h:73
bool root_handler(CanvasEvent const &event) override
Definition lpe-tool.cpp:135
void selection_changed(Selection *selection)
Callback that processes the "changed" signal on the selection; destroys old and creates new nodepath ...
Definition lpe-tool.cpp:96
void switch_mode(LivePathEffect::EffectType type)
Definition lpe-tool.cpp:224
std::unique_ptr< ShapeEditor > shape_editor
Definition lpe-tool.h:68
PenTool: a context for pen tool events.
Definition pen-tool.h:41
bool root_handler(CanvasEvent const &event) override
Callback to handle all pen events.
Definition pen-tool.cpp:247
bool item_handler(SPItem *item, CanvasEvent const &event) override
Handles item specific events.
Definition pen-tool.cpp:227
void waitForLPEMouseClicks(Inkscape::LivePathEffect::EffectType effect_type, unsigned int num_clicks, bool use_polylines=true)
void saveDragOrigin(Geom::Point const &pos)
void enableSelectionCue(bool enable=true)
Enables/disables the ToolBase's SelCue.
double value(Unit const *u) const
Return the quantity's value in the specified unit.
Definition units.cpp:565
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:588
static UnitTable & get()
Definition units.cpp:441
Glib::ustring abbr
Definition units.h:81
Wrapper around a Geom::PathVector object.
Definition curve.h:28
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Typed SVG document implementation.
Definition document.h:101
Inkscape::Util::Quantity getWidth() const
Definition document.cpp:847
Inkscape::Util::Quantity getHeight() const
Definition document.cpp:886
Base class for visual SVG elements.
Definition sp-item.h:109
SVG <path> implementation.
Definition sp-path.h:29
SPCurve const * curve() const
Return a borrowed pointer to the curve (if any exists) or NULL if there is no curve.
Definition sp-shape.cpp:970
const double w
Definition conic-4.cpp:19
Editable view implementation.
SPItem * item
int const num_subtools
Definition lpe-tool.cpp:47
SubtoolEntry const lpesubtools[]
Definition lpe-tool.cpp:49
LPETool: a generic tool composed of subtools that are given by LPEs.
Raw stack of active status messages.
Coord length(LineSegment const &seg)
Piecewise< D2< SBasis > > paths_to_pw(PathVector const &paths)
Definition path.cpp:1123
double angle_between(Line const &l1, Line const &l2)
Definition line.h:456
std::pair< Geom::Point, Geom::Point > lpetool_get_limiting_bbox_corners(SPDocument const *document)
Definition lpe-tool.cpp:241
int lpetool_item_has_construction(SPItem *item)
Definition lpe-tool.cpp:192
bool lpetool_try_construction(SPDesktop *desktop, LivePathEffect::EffectType const type)
Definition lpe-tool.cpp:210
static void set_pos_and_anchor(CanvasItemText *canvas_text, Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwd2, double t, double length)
Definition lpe-tool.cpp:280
int lpetool_mode_to_index(LivePathEffect::EffectType const type)
Definition lpe-tool.cpp:178
Glib::ustring format_classic(T const &... args)
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
int mode
two-dimensional geometric operators.
Inkscape::ShapeEditor This is a container class which contains a knotholder for shapes.
A mouse button (left/right/middle) is pressed.
A mouse button (left/right/middle) is released.
Abstract base class for events.
Inkscape::LivePathEffect::EffectType type
Definition lpe-tool.h:29
Definition curve.h:24
int index
SPDesktop * desktop