Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
conn-avoid-ref.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * A class for handling shape interaction with libavoid.
4 *
5 * Authors:
6 * Michael Wybrow <mjwybrow@users.sourceforge.net>
7 * Abhishek Sharma
8 *
9 * Copyright (C) 2005 Michael Wybrow
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14
15#include <cstring>
16#include <string>
17#include <iostream>
18
19#include <2geom/convex-hull.h>
20#include <2geom/line.h>
21
22#include "conn-avoid-ref.h"
23#include "desktop.h"
24#include "document-undo.h"
25#include "document.h"
26#include "inkscape.h"
27#include "layer-manager.h"
28
29#include "display/curve.h"
30
33
34#include "object/sp-namedview.h"
35#include "object/sp-shape.h"
36
37#include "svg/stringstream.h"
38
39#include "xml/node.h"
40
42
43using Avoid::Router;
44
46
47
49 : shapeRef(nullptr)
50 , item(spitem)
51 , setting(false)
52 , new_setting(false)
53 , _transformed_connection()
54{
55}
56
57
59{
60 _transformed_connection.disconnect();
61
62 // If the document is being destroyed then the router instance
63 // and the ShapeRefs will have been destroyed with it.
64 Router *router = item->document->getRouter();
65
66 if (shapeRef && router) {
67 router->deleteShape(shapeRef);
68 }
69 shapeRef = nullptr;
70}
71
72
73void SPAvoidRef::setAvoid(char const *value)
74{
75 // Don't keep avoidance information for cloned objects.
76 if ( !item->cloned ) {
77 new_setting = false;
78 if (value && (strcmp(value, "true") == 0)) {
79 new_setting = true;
80 }
81 }
82}
83
85{
86 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
87 if (desktop == nullptr) {
88 return;
89 }
90 if (desktop->getDocument() != item->document) {
91 // We don't want to go any further if the active desktop's document
92 // isn't the same as the document that this item is part of. This
93 // case can happen if a new document is loaded from the file chooser
94 // or via the recent file menu. In this case, we can end up here
95 // as a result of a ensureUpToDate performed on a
96 // document not yet attached to the active desktop.
97 return;
98 }
99
100 if (new_setting == setting) {
101 // Don't need to make any changes
102 return;
103 }
105
106 Router *router = item->document->getRouter();
107
108 _transformed_connection.disconnect();
109 if (new_setting) {
111 if (poly.size() > 0) {
113 sigc::ptr_fun(&avoid_item_move));
114
115 char const *id = item->getAttribute("id");
116 g_assert(id != nullptr);
117
118 // Get a unique ID for the item.
119 GQuark itemID = g_quark_from_string(id);
120
121 shapeRef = new Avoid::ShapeRef(router, poly, itemID);
122 }
123 }
124 else if (shapeRef)
125 {
126 router->deleteShape(shapeRef);
127 shapeRef = nullptr;
128 }
129}
130
131
132std::vector<SPItem *> SPAvoidRef::getAttachedShapes(const unsigned int type)
133{
134 std::vector<SPItem *> list;
135
136 Avoid::IntList shapes;
137 GQuark shapeId = g_quark_from_string(item->getId());
138 item->document->getRouter()->attachedShapes(shapes, shapeId, type);
139
140 for (auto const shape: shapes) {
141 auto const connId = g_quark_to_string(shape);
142 SPObject *obj = item->document->getObjectById(connId);
143 if (obj == nullptr) {
144 g_warning("getAttachedShapes: Object with id=\"%s\" is not "
145 "found. Skipping.", connId);
146 continue;
147 }
148 auto shapeItem = cast<SPItem>(obj);
149 list.push_back(shapeItem);
150 }
151 return list;
152}
153
154
155std::vector<SPItem *> SPAvoidRef::getAttachedConnectors(const unsigned int type)
156{
157 std::vector<SPItem *> list;
158
159 Avoid::IntList conns;
160 GQuark shapeId = g_quark_from_string(item->getId());
161 item->document->getRouter()->attachedConns(conns, shapeId, type);
162
163 for (auto const conn: conns) {
164 auto const connId = g_quark_to_string(conn);
165 SPObject *obj = item->document->getObjectById(connId);
166 if (obj == nullptr) {
167 g_warning("getAttachedConnectors: Object with id=\"%s\" is not "
168 "found. Skipping.", connId);
169 continue;
170 }
171 auto connItem = cast<SPItem>(obj);
172 list.push_back(connItem);
173 }
174 return list;
175}
176
178{
179 g_assert(item);
180 // the center is all we are interested in now; we used to care
181 // about non-center points, but that's moot.
183 return (bbox) ? bbox->midpoint() : Geom::Point(0, 0);
184}
185
186static std::vector<Geom::Point> approxCurveWithPoints(Geom::PathVector const &curve_pv)
187{
188 // The number of segments to use for not straight curves approximation
189 constexpr unsigned NUM_SEGS = 4;
190
191 // The structure to hold the output
192 std::vector<Geom::Point> poly_points;
193
194 // Iterate over all curves, adding the endpoints for linear curves and
195 // sampling the other curves
196 double seg_size = 1.0 / NUM_SEGS;
197 double at;
198 at = 0;
200 while (pit != curve_pv.end())
201 {
202 Geom::Path::const_iterator cit = pit->begin();
203 while (cit != pit->end())
204 {
205 if (cit == pit->begin())
206 {
207 poly_points.push_back(cit->initialPoint());
208 }
209
210 if (dynamic_cast<Geom::CubicBezier const*>(&*cit))
211 {
212 at += seg_size;
213 if (at <= 1.0 )
214 poly_points.push_back(cit->pointAt(at));
215 else
216 {
217 at = 0.0;
218 ++cit;
219 }
220 }
221 else
222 {
223 poly_points.push_back(cit->finalPoint());
224 ++cit;
225 }
226 }
227 ++pit;
228 }
229 return poly_points;
230}
231
232static std::vector<Geom::Point> approxItemWithPoints(SPItem const *item, const Geom::Affine& item_transform)
233{
234 auto item_mutable = const_cast<SPItem *>(item);
235
236 if (auto group = cast<SPGroup>(item_mutable)) {
237 std::vector<Geom::Point> poly_points;
238 // consider all first-order children
239 std::vector<SPItem*> itemlist = group->item_list();
240 for (auto child_item : itemlist) {
241 std::vector<Geom::Point> child_points = approxItemWithPoints(child_item, item_transform * child_item->transform);
242 poly_points.insert(poly_points.end(), child_points.begin(), child_points.end());
243 }
244 return poly_points;
245 } else if (auto shape = cast<SPShape>(item_mutable)) {
246 shape->set_shape();
247 // make sure it has an associated curve
248 if (shape->curve()) {
249 auto item_pathv = *shape->curve();
250 // apply transformations (up to common ancestor)
251 item_pathv *= item_transform;
252 return approxCurveWithPoints(item_pathv);
253 } else {
254 return {};
255 }
256 } else {
257 if (auto bbox = item->documentPreferredBounds()) {
258 return approxCurveWithPoints(Geom::Path{*bbox});
259 } else {
260 return {};
261 }
262 }
263}
264
266{
267 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
268 g_assert(desktop != nullptr);
269 auto const spacing = desktop->getNamedView()->connector_spacing;
270
271 Geom::Affine itd_mat = item->i2doc_affine();
272 std::vector<Geom::Point> hull_points;
273 hull_points = approxItemWithPoints(item, itd_mat);
274
275 // create convex hull from all sampled points
276 Geom::ConvexHull hull(hull_points);
277
278 // enlarge path by "desktop->namedview->connector_spacing"
279 // store expanded convex hull in Avoid::Polygn
280 Avoid::Polygon poly;
281 if (hull.empty()) {
282 return poly;
283 }
284
285 Geom::Line hull_edge(hull.back(), hull.front());
286 Geom::Line prev_parallel_hull_edge;
287 prev_parallel_hull_edge.setOrigin(hull_edge.origin() + hull_edge.versor().ccw() * spacing);
288 prev_parallel_hull_edge.setVector(hull_edge.versor());
289
290 std::size_t const hull_size = hull.size();
291 for (std::size_t i = 0; i < hull_size; ++i)
292 {
293 if (i + 1 == hull_size) {
294 hull_edge.setPoints(hull.back(), hull.front());
295 } else {
296 hull_edge.setPoints(hull[i], hull[i + 1]);
297 }
298
299 Geom::Line parallel_hull_edge;
300 parallel_hull_edge.setOrigin(hull_edge.origin() + hull_edge.versor().ccw() * spacing);
301 parallel_hull_edge.setVector(hull_edge.versor());
302
303 // determine the intersection point
304 try {
305 Geom::OptCrossing int_pt = Geom::intersection(parallel_hull_edge, prev_parallel_hull_edge);
306 if (int_pt) {
307 Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X],
308 (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);
309 poly.ps.push_back(avoid_pt);
310 } else {
311 // something went wrong...
312 std::cerr<<"conn-avoid-ref.cpp: avoid_item_poly: Geom:intersection failed."<<std::endl;
313 }
314 } catch (Geom::InfiniteSolutions const &e) {
315 // the parallel_hull_edge and prev_parallel_hull_edge lie on top of each other, hence infinite crossings
316 g_message("conn-avoid-ref.cpp: trying to get crossings of identical lines");
317 }
318
319 prev_parallel_hull_edge = parallel_hull_edge;
320 }
321
322 return poly;
323}
324
325static inline void get_avoided_items_rec(std::vector<SPItem *> &list, SPObject *from, SPDesktop *desktop, bool initialised);
326
327std::vector<SPItem *> get_avoided_items(SPObject *from, SPDesktop *desktop, bool initialised)
328{
329 std::vector<SPItem *> list;
330 get_avoided_items_rec(list, from, desktop, initialised);
331 return list;
332}
333
334static inline void get_avoided_items_rec(std::vector<SPItem *> &list, SPObject *from, SPDesktop *desktop, bool initialised)
335{
336 for (auto &child: from->children) {
337 if (is<SPItem>(&child) &&
338 !desktop->layerManager().isLayer(cast<SPItem>(&child)) &&
339 !cast_unsafe<SPItem>(&child)->isLocked() &&
340 !desktop->itemIsHidden(cast<SPItem>(&child)) &&
341 (!initialised || cast<SPItem>(&child)->getAvoidRef().shapeRef)
342 )
343 {
344 list.push_back(cast<SPItem>(&child));
345 }
346
347 if (is<SPItem>(&child) && desktop->layerManager().isLayer(cast<SPItem>(&child))) {
348 get_avoided_items_rec(list, &child, desktop, initialised);
349 }
350 }
351}
352
353
354void avoid_item_move(Geom::Affine const */*mp*/, SPItem *moved_item)
355{
356 Avoid::ShapeRef *shapeRef = moved_item->getAvoidRef().shapeRef;
357 g_assert(shapeRef);
358
359 Router *router = moved_item->document->getRouter();
360 Avoid::Polygon poly = avoid_item_poly(moved_item);
361 if (!poly.empty()) {
362 router->moveShape(shapeRef, poly);
363 }
364}
365
366
368{
369 // Don't count this as changes to the document,
370 // it is basically just late initialisation.
371 SPDocument *document = desktop->getDocument();
372 DocumentUndo::ScopedInsensitive _no_undo(document);
373
374 bool initialised = false;
376
377 for (auto item : items) {
379 }
380}
381
382
383/*
384 Local Variables:
385 mode:c++
386 c-file-style:"stroustrup"
387 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
388 indent-tabs-mode:nil
389 fill-column:99
390 End:
391*/
392// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
The Point class defines a point in the plane.
Definition geomtypes.h:53
A dynamic Polygon, to which points can be easily added and removed.
Definition geomtypes.h:208
size_t size(void) const
Returns the number of points in this polygon.
bool empty(void) const
Returns true if this polygon is empty.
std::vector< Point > ps
A vector of the points that make up the Polygon.
Definition geomtypes.h:285
The Router class represents a libavoid router instance.
Definition router.h:386
void deleteShape(ShapeRef *shape)
Delete a shape from the router scene.
Definition router.cpp:281
void moveShape(ShapeRef *shape, const Polygon &newPoly, const bool first_move=false)
Move or resize an existing shape within the router scene.
Definition router.cpp:360
void attachedShapes(IntList &shapes, const unsigned int shapeId, const unsigned int type)
Definition router.cpp:891
void attachedConns(IntList &conns, const unsigned int shapeId, const unsigned int type)
Definition router.cpp:867
The ShapeRef class represents a shape object.
Definition shape.h:82
3x3 matrix representing an affine transformation.
Definition affine.h:70
Bezier curve with compile-time specified order.
Convex hull based on the Andrew's monotone chain algorithm.
Infinite line on a plane.
Definition line.h:53
void setOrigin(Point const &p)
Set the point at zero time.
Definition line.h:147
Point origin() const
Get the line's origin point.
Definition line.h:128
void setPoints(Point const &a, Point const &b)
Set a line based on two points it should pass through.
Definition line.h:168
void setVector(Point const &v)
Set the speed of the line.
Definition line.h:154
Point versor() const
Get the line's normalized direction vector.
Definition line.h:135
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of subpaths.
Definition pathvector.h:122
Sequence::const_iterator const_iterator
Definition pathvector.h:127
iterator begin()
Definition pathvector.h:151
iterator end()
Definition pathvector.h:152
Sequence of contiguous curves, aka spline.
Definition path.h:353
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Point ccw() const
Return a point like this point but rotated -90 degrees.
Definition point.h:130
SPGroup * currentRoot() const
Returns current root (=bottom) layer.
bool isLayer(SPObject *object) const
True if object is a layer.
std::vector< SPItem * > getAttachedShapes(const unsigned int type)
Geom::Point getConnectionPointPos()
virtual ~SPAvoidRef()
Avoid::ShapeRef * shapeRef
SPAvoidRef(SPItem *spitem)
void handleSettingChange()
void setAvoid(char const *value)
std::vector< SPItem * > getAttachedConnectors(const unsigned int type)
sigc::connection _transformed_connection
SPItem * item
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
bool itemIsHidden(SPItem const *item) const
Definition desktop.cpp:264
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::LayerManager & layerManager()
Definition desktop.h:287
Typed SVG document implementation.
Definition document.h:101
Avoid::Router * getRouter() const
Definition document.h:197
SPObject * getObjectById(std::string const &id) const
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::OptRect documentVisualBounds() const
Get item's visual bbox in document coordinate system.
Definition sp-item.cpp:1026
Geom::OptRect documentPreferredBounds() const
Definition sp-item.cpp:1012
sigc::connection connectTransformed(sigc::slot< void(Geom::Affine const *, SPItem *)> slot)
Definition sp-item.h:234
SPAvoidRef & getAvoidRef()
Definition sp-item.cpp:202
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1824
double connector_spacing
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
SPDocument * document
Definition sp-object.h:188
char const * getId() const
Returns the objects current ID string.
char const * getAttribute(char const *name) const
unsigned int cloned
Definition sp-object.h:180
ChildrenList children
Definition sp-object.h:907
static std::vector< Geom::Point > approxItemWithPoints(SPItem const *item, const Geom::Affine &item_transform)
void init_avoided_shape_geometry(SPDesktop *desktop)
static void get_avoided_items_rec(std::vector< SPItem * > &list, SPObject *from, SPDesktop *desktop, bool initialised)
void avoid_item_move(Geom::Affine const *, SPItem *moved_item)
std::vector< SPItem * > get_avoided_items(SPObject *from, SPDesktop *desktop, bool initialised)
static std::vector< Geom::Point > approxCurveWithPoints(Geom::PathVector const &curve_pv)
static Avoid::Polygon avoid_item_poly(SPItem const *item)
A class for handling shape interaction with libavoid.
void avoid_item_move(Geom::Affine const *mp, SPItem *moved_item)
Convex hull data structures.
Editable view implementation.
TODO: insert short description here.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
Infinite straight line.
std::list< unsigned int > IntList
Definition router.h:58
std::optional< Crossing > OptCrossing
Definition crossing.h:64
OptCrossing intersection(Ray const &r1, Line const &l2)
Definition line.h:545
Ocnode * child[8]
Definition quantize.cpp:33
Contains the interface for the Router class.
GList * items
Contains the interface for the ShapeRef class.
guint32 GQuark
TODO: insert short description here.
SPDesktop * desktop
Interface for XML nodes.