Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-conn-end.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors: see git history
6 *
7 * Copyright (C) 2018 Authors
8 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9 */
10#include "sp-conn-end.h"
11
13
14#include "sp-item-group.h"
15#include "sp-path.h"
16
17static void change_endpts(SPPath *path, double endPos[2]);
18
20 : ref(owner)
21 , sub_ref(owner)
22 , href(nullptr)
23 , sub_href(nullptr)
24 // Default to center connection endpoint
25 , _changed_connection()
26 , _delete_connection()
27 , _transformed_connection()
28{
29}
30
31static SPObject const *get_nearest_common_ancestor(SPObject const *const obj, SPItem const *const objs[2])
32{
33 SPObject const *anc_sofar = obj;
34 for (unsigned i = 0; i < 2; ++i) {
35 if ( objs[i] != nullptr ) {
36 anc_sofar = anc_sofar->nearestCommonAncestor(objs[i]);
37 }
38 }
39 return anc_sofar;
40}
41
42
44 const Geom::Affine& item_transform, double& intersect_pos)
45{
46 double initial_pos = intersect_pos;
47 // if this is a group...
48 if (is<SPGroup>(item)) {
49 auto group = cast<SPGroup>(item);
50
51 // consider all first-order children
52 double child_pos = 0.0;
53 std::vector<SPItem*> g = group->item_list();
54 for (auto child_item : g) {
56 item_transform * child_item->transform, child_pos);
57 if (intersect_pos < child_pos)
58 intersect_pos = child_pos;
59 }
60 return intersect_pos != initial_pos;
61 }
62
63 // if this is not a shape, nothing to be done
64 auto shape = cast<SPShape>(item);
65 if (!shape)
66 return false;
67
68 // make sure it has an associated curve
69 if (!shape->curve()) return false;
70
71 // apply transformations (up to common ancestor)
72 auto const curve_pv = *shape->curve() * item_transform;
73 Geom::CrossingSet cross = crossings(conn_pv, curve_pv);
74 // iterate over all Crossings
75 //TODO: check correctness of the following code: inner loop uses loop variable
76 // with a name identical to the loop variable of the outer loop. Then rename.
77 for (const auto & cr : cross) {
78 for (const auto & cr_pt : cr) {
79 if ( intersect_pos < cr_pt.ta)
80 intersect_pos = cr_pt.ta;
81 }
82 }
83
84 return intersect_pos != initial_pos;
85}
86
87
88// This function returns the outermost intersection point between the path (a connector)
89// and the item given. If the item is a group, then the component items are considered.
90// The transforms given should be to a common ancestor of both the path and item.
91//
93 const Geom::Affine& item_transform, const Geom::Affine& conn_transform,
94 const bool at_start, double& intersect_pos)
95{
96 // Copy the curve and apply transformations up to common ancestor.
97 auto conn_pv = *conn->curve() * conn_transform;
98
99 // If this is not the starting point, use Geom::Path::reverse() to reverse the path
100 if (!at_start) {
101 // connectors are actually a single path, so consider the first element from a Geom::PathVector
102 conn_pv[0] = conn_pv[0].reversed();
103 }
104
105 // We start with the intersection point at the beginning of the path
106 intersect_pos = 0.0;
107
108 // Find the intersection.
109 bool result = try_get_intersect_point_with_item_recursive(conn_pv, item, item_transform, intersect_pos);
110
111 if (!result) {
112 // No intersection point has been found (why?)
113 // just default to connector end
114 intersect_pos = 0;
115 }
116 // If not at the starting point, recompute position with respect to original path
117 if (!at_start) {
118 intersect_pos = conn_pv[0].size() - intersect_pos;
119 }
120
121 return result;
122}
123
124
125static void sp_conn_get_route_and_redraw(SPPath *const path, const bool updatePathRepr = true)
126{
127 // Get the new route around obstacles.
128 bool rerouted = path->connEndPair.reroutePathFromLibavoid();
129 if (!rerouted) {
130 return;
131 }
132
133 SPItem *h2attItem[2] = {nullptr};
134 path->connEndPair.getAttachedItems(h2attItem);
135
136 SPObject const *const ancestor = get_nearest_common_ancestor(path, h2attItem);
137 Geom::Affine const path2anc(i2anc_affine(path, ancestor));
138
139 // Set sensible values in case there the connector ends are not
140 // attached to any shapes.
141 Geom::PathVector conn_pv = *path->curve();
142 double endPos[2] = { 0.0, static_cast<double>(conn_pv[0].size()) };
143
144 for (unsigned h = 0; h < 2; ++h) {
145 // Assume center point for all
146 if (h2attItem[h]) {
147 Geom::Affine h2i2anc = i2anc_affine(h2attItem[h], ancestor);
148 try_get_intersect_point_with_item(path, h2attItem[h], h2i2anc, path2anc,
149 (h == 0), endPos[h]);
150 }
151 }
152 change_endpts(path, endPos);
153 if (updatePathRepr) {
154 path->updateRepr();
155 path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
156 }
157}
158
160{
161 if (path->connEndPair.isAutoRoutingConn()) {
163 }
164}
165
167{
168 if (path->connEndPair.isAutoRoutingConn()) {
170 }
171}
172
173
175{
176 if (path->connEndPair.isAutoRoutingConn()) {
178 }
179 // Don't update the path repr or else connector dragging is slowed by
180 // constant update of values to the xml editor, and each step is also
181 // needlessly remembered by undo/redo.
182 sp_conn_get_route_and_redraw(path, false);
183}
184
186{
188}
189
190
191static void change_endpts(SPPath *path, double endPos[2])
192{
193 // Use Geom::Path::portion to cut the curve at the end positions
194 if (endPos[0] > endPos[1]) {
195 // Path is "negative", reset the curve and return
196 path->setCurve({});
197 return;
198 }
199 const Geom::Path& old_path = path->curve()->front();
200 Geom::PathVector new_path_vector;
201 new_path_vector.push_back(old_path.portion(endPos[0], endPos[1]));
202 path->setCurve(std::move(new_path_vector));
203}
204
205static void sp_conn_end_deleted(SPObject *, SPObject *const owner, unsigned const handle_ix)
206{
207 char const * const attrs[] = {
208 "inkscape:connection-start", "inkscape:connection-end"};
209 owner->removeAttribute(attrs[handle_ix]);
210
211 char const * const point_attrs[] = {
212 "inkscape:connection-start-point", "inkscape:connection-end-point"};
213 owner->removeAttribute(point_attrs[handle_ix]);
214 /* I believe this will trigger sp_conn_end_href_changed. */
215}
216
217void sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix)
218{
219 sp_conn_end_deleted(nullptr, owner, handle_ix);
220}
221
222void SPConnEnd::setAttacherHref(gchar const *value)
223{
224 if (g_strcmp0(value, href) != 0) {
225 g_free(href);
226 href = g_strdup(value);
227 if (!ref.try_attach(value)) {
228 g_free(href);
229 href = nullptr;
230 }
231 }
232}
233
234void SPConnEnd::setAttacherSubHref(gchar const *value)
235{
236 // TODO This could check the URI object is actually a sub-object
237 // of the set href. It should be done here and in setAttacherHref
238 if (g_strcmp0(value, sub_href) != 0) {
239 g_free(sub_href);
240 sub_href = g_strdup(value);
241 if (!sub_ref.try_attach(value)) {
242 g_free(sub_href);
243 sub_href = nullptr;
244 }
245 }
246}
247
248void sp_conn_end_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, SPConnEnd *connEnd, SPPath *path, unsigned handle_ix)
249{
250 if (!connEnd) return;
251 connEnd->_delete_connection.disconnect();
252 connEnd->_transformed_connection.disconnect();
253
254 if (connEnd->href) {
255 if (auto refobj = connEnd->ref.getObject()) {
256 connEnd->_delete_connection = refobj->connectDelete(sigc::bind(sigc::ptr_fun(&sp_conn_end_deleted), path, handle_ix));
257 connEnd->_transformed_connection = refobj->connectModified([path] (SPObject *, unsigned) {
259 });
260 }
261 }
262}
263
264/*
265 Local Variables:
266 mode:c++
267 c-file-style:"stroustrup"
268 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
269 indent-tabs-mode:nil
270 fill-column:99
271 End:
272*/
273// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
3x3 matrix representing an affine transformation.
Definition affine.h:70
Sequence of subpaths.
Definition pathvector.h:122
size_type size() const
Get the number of paths in the vector.
Definition pathvector.h:147
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
Sequence of contiguous curves, aka spline.
Definition path.h:353
Path portion(Coord f, Coord t) const
Definition path.h:645
bool try_attach(char const *uri)
Try to attach to a URI.
void tellLibavoidNewEndpoints(bool const processTransaction=false)
bool isAutoRoutingConn() const
void getAttachedItems(SPItem *[2]) const
char * sub_href
Definition sp-conn-end.h:33
sigc::connection _transformed_connection
A sigc connection for transformed signal, used to do move compensation.
Definition sp-conn-end.h:42
void setAttacherSubHref(char const *value)
SPUseReference ref
Definition sp-conn-end.h:30
SPUseReference sub_ref
Definition sp-conn-end.h:31
char * href
Definition sp-conn-end.h:32
SPConnEnd(SPObject *owner)
void setAttacherHref(char const *value)
sigc::connection _delete_connection
Called when the attached object gets deleted.
Definition sp-conn-end.h:39
Base class for visual SVG elements.
Definition sp-item.h:109
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void removeAttribute(char const *key)
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
SPObject const * nearestCommonAncestor(SPObject const *object) const
Returns youngest object being parent to this and object.
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
SVG <path> implementation.
Definition sp-path.h:27
SPConnEndPair connEndPair
Definition sp-path.h:35
void setCurve(Geom::PathVector const *)
Definition sp-shape.cpp:923
Geom::PathVector const * curve() const
Return a borrowed pointer to the curve (if any exists) or NULL if there is no curve.
Definition sp-shape.cpp:967
SPItem * getObject() const
Css & result
SPItem * item
std::vector< Crossings > CrossingSet
Definition crossing.h:128
Path intersection.
Ocnode ** ref
Definition quantize.cpp:32
void sp_conn_reroute_path(SPPath *const path)
static void sp_conn_get_route_and_redraw(SPPath *const path, const bool updatePathRepr=true)
static bool try_get_intersect_point_with_item(SPPath *conn, SPItem *item, const Geom::Affine &item_transform, const Geom::Affine &conn_transform, const bool at_start, double &intersect_pos)
void sp_conn_reroute_path_immediate(SPPath *const path)
void sp_conn_end_href_changed(SPObject *, SPObject *, SPConnEnd *connEnd, SPPath *path, unsigned handle_ix)
static void sp_conn_end_deleted(SPObject *, SPObject *const owner, unsigned const handle_ix)
static void change_endpts(SPPath *path, double endPos[2])
void sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix)
static void sp_conn_end_shape_modified(SPPath *path)
static bool try_get_intersect_point_with_item_recursive(Geom::PathVector &conn_pv, SPItem *item, const Geom::Affine &item_transform, double &intersect_pos)
void sp_conn_redraw_path(SPPath *const path)
static SPObject const * get_nearest_common_ancestor(SPObject const *const obj, SPItem const *const objs[2])
TODO: insert short description here.
Geom::Affine i2anc_affine(SPObject const *object, SPObject const *ancestor)
Definition sp-item.cpp:1795