Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-conn-end-pair.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * A class for handling connector endpoint movement and libavoid interaction.
4 *
5 * Authors:
6 * Peter Moulder <pmoulder@mail.csse.monash.edu.au>
7 * Michael Wybrow <mjwybrow@users.sourceforge.net>
8 * Abhishek Sharma
9 *
10 * * Copyright (C) 2004-2005 Monash University
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include "sp-conn-end-pair.h"
16
17#include <cstring>
18#include <string>
19#include <glibmm/stringutils.h>
20
21#include "attributes.h"
22#include "document.h"
23#include "sp-conn-end.h"
24#include "sp-item-group.h"
25#include "sp-path.h"
26#include "sp-use.h"
27#include "uri.h"
28
30#include "display/curve.h"
31#include "xml/node.h"
32
34 : _path(owner)
35 , _connRef(nullptr)
36 , _connType(SP_CONNECTOR_NOAVOID)
37 , _connCurvature(0.0)
38 , _transformed_connection()
39{
40 for (unsigned handle_ix = 0; handle_ix <= 1; ++handle_ix) {
41 this->_connEnd[handle_ix] = new SPConnEnd(owner);
42 this->_connEnd[handle_ix]->_changed_connection
43 = this->_connEnd[handle_ix]->ref.changedSignal()
44 .connect(sigc::bind(sigc::ptr_fun(sp_conn_end_href_changed),
45 this->_connEnd[handle_ix], owner, handle_ix));
46 }
47}
48
50{
51 for (auto & handle_ix : this->_connEnd) {
52 delete handle_ix;
53 handle_ix = nullptr;
54 }
55}
56
58{
59 for (auto & handle_ix : this->_connEnd) {
60 handle_ix->_changed_connection.disconnect();
61 handle_ix->_delete_connection.disconnect();
62 handle_ix->_transformed_connection.disconnect();
63 g_free(handle_ix->href);
64 handle_ix->href = nullptr;
65 handle_ix->ref.detach();
66 }
67
68 // If the document is being destroyed then the router instance
69 // and the ConnRefs will have been destroyed with it.
70 const bool routerInstanceExists = (_path->document->getRouter() != nullptr);
71
72 if (_connRef && routerInstanceExists) {
74 }
75 _connRef = nullptr;
76
77 _transformed_connection.disconnect();
78}
79
81{
82 object->readAttr(SPAttr::CONNECTOR_TYPE);
83 object->readAttr(SPAttr::CONNECTION_START);
84 object->readAttr(SPAttr::CONNECTION_START_POINT);
85 object->readAttr(SPAttr::CONNECTION_END);
86 object->readAttr(SPAttr::CONNECTION_END_POINT);
87 object->readAttr(SPAttr::CONNECTOR_CURVATURE);
88}
89
90
91static void avoid_conn_transformed(Geom::Affine const */*mp*/, SPItem *moved_item)
92{
93 auto path = cast<SPPath>(moved_item);
94 if (path->connEndPair.isAutoRoutingConn()) {
95 path->connEndPair.tellLibavoidNewEndpoints();
96 }
97}
98
99
100void SPConnEndPair::setAttr(const SPAttr key, gchar const *const value)
101{
102 switch (key) {
104 if (value && (strcmp(value, "polyline") == 0 || strcmp(value, "orthogonal") == 0)) {
105 int new_conn_type = strcmp(value, "polyline") ? SP_CONNECTOR_ORTHOGONAL : SP_CONNECTOR_POLYLINE;
106
107 if (!_connRef) {
108 _connType = new_conn_type;
110 _connRef = new Avoid::ConnRef(router);
114 } else if (new_conn_type != _connType) {
115 _connType = new_conn_type;
119 }
120 } else {
122
123 if (_connRef) {
125 _connRef = nullptr;
126 _transformed_connection.disconnect();
127 }
128 }
129 break;
131 if (value) {
132 _connCurvature = g_strtod(value, nullptr);
133 if (_connRef && _connRef->isInitialised()) {
134 // Redraw the connector, but only if it has been initialised.
136 }
137 }
138 break;
140 this->_connEnd[0]->setAttacherHref(value);
141 break;
143 this->_connEnd[0]->setAttacherSubHref(value);
144 break;
146 this->_connEnd[1]->setAttacherHref(value);
147 break;
149 this->_connEnd[1]->setAttacherSubHref(value);
150 break;
151 }
152}
153
155{
156 char const * const attrs[] = {
157 "inkscape:connection-start", "inkscape:connection-end"};
158 char const * const point_attrs[] = {
159 "inkscape:connection-start-point", "inkscape:connection-end-point"};
160 for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) {
161 const Inkscape::URI* U = this->_connEnd[handle_ix]->ref.getURI();
162 if (U) {
163 auto str = U->str();
164 repr->setAttribute(attrs[handle_ix], str);
165 }
166 const Inkscape::URI* P = this->_connEnd[handle_ix]->sub_ref.getURI();
167 if (P) {
168 auto str = P->str();
169 repr->setAttribute(point_attrs[handle_ix], str);
170 }
171 }
173 repr->setAttribute("inkscape:connector-curvature", Glib::Ascii::dtostr(_connCurvature));
174 repr->setAttribute("inkscape:connector-type", _connType == SP_CONNECTOR_POLYLINE ? "polyline" : "orthogonal" );
175 }
176}
177
178void SPConnEndPair::getAttachedItems(SPItem *h2attItem[2]) const {
179 for (unsigned h = 0; h < 2; ++h) {
180 auto obj = this->_connEnd[h]->ref.getObject();
181 auto sub_obj = this->_connEnd[h]->sub_ref.getObject();
182
183 if(sub_obj) {
184 // For sub objects, we have to go fishing for the virtual/shadow
185 // object which has the correct position for this use/symbol
186 auto use = cast<SPUse>(obj);
187 if(use) {
188 auto root = use->root();
189 bool found = false;
190 for (auto& child: root->children) {
191 if(!g_strcmp0(child.getAttribute("id"), sub_obj->getId())) {
192 h2attItem[h] = (SPItem *) &child;
193 found = true;
194 }
195 }
196 if(!found) {
197 g_warning("Couldn't find sub connector point!");
198 }
199 }
200 } else {
201 h2attItem[h] = obj;
202 }
203
204 // Deal with the case of the attached object being an empty group.
205 // A group containing no items does not have a valid bbox, so
206 // causes problems for the auto-routing code. Also, since such a
207 // group no longer has an onscreen representation and can only be
208 // selected through the XML editor, it makes sense just to detach
209 // connectors from them.
210 if (auto group = cast<SPGroup>(h2attItem[h])) {
211 if (group->getItemCount() == 0) {
212 // This group is empty, so detach.
214 h2attItem[h] = nullptr;
215 }
216 }
217 }
218}
219
221{
222 SPCurve const *curve = _path->curveForEdit();
223 SPItem *h2attItem[2] = {nullptr};
224 getAttachedItems(h2attItem);
226
227 for (unsigned h = 0; h < 2; ++h) {
228 if (h2attItem[h]) {
229 endPts[h] = h2attItem[h]->getAvoidRef().getConnectionPointPos();
230 } else if (!curve->is_empty()) {
231 if (h == 0) {
232 endPts[h] = *(curve->first_point()) * i2d;
233 } else {
234 endPts[h] = *(curve->last_point()) * i2d;
235 }
236 }
237 }
238}
239
241{
242 return _connCurvature;
243}
244
249
254
255
256static void redrawConnectorCallback(void *ptr)
257{
258 auto path = static_cast<SPPath *>(ptr);
259 if (path->document == nullptr) {
260 // This can happen when the document is being destroyed.
261 return;
262 }
264}
265
270
271
272// Called from SPPath::update to initialise the endpoints.
274{
276 g_assert(_connRef != nullptr);
277 if (!_connRef->isInitialised()) {
280 }
281 }
282}
283
285{
286 Geom::Point endPt[2];
287 getEndpoints(endPt);
288
289 Avoid::Point src(endPt[0][Geom::X], endPt[0][Geom::Y]);
290 Avoid::Point dst(endPt[1][Geom::X], endPt[1][Geom::Y]);
291
292 _connRef->setEndpoints(src, dst);
293}
294
295
300
302{
303 g_assert(_connRef != nullptr);
304
306}
307
308// Redraws the curve along the recalculated route
309// Straight or curved
311{
312 g_assert(connRef != nullptr);
313
314 bool straight = curvature<1e-3;
315
316 Avoid::PolyLine route = connRef->displayRoute();
317 if (!straight) route = route.curvedPolyline(curvature);
318 connRef->calcRouteDist();
319
321
322 curve.moveto(Geom::Point(route.ps[0].x, route.ps[0].y));
323 int pn = route.size();
324 for (int i = 1; i < pn; ++i) {
325 Geom::Point p(route.ps[i].x, route.ps[i].y);
326 if (straight) {
327 curve.lineto(p);
328 } else {
329 switch (route.ts[i]) {
330 case 'M':
331 curve.moveto(p);
332 break;
333 case 'L':
334 curve.lineto(p);
335 break;
336 case 'C':
337 g_assert(i + 2 < pn);
338 curve.curveto(p, Geom::Point(route.ps[i+1].x, route.ps[i+1].y),
339 Geom::Point(route.ps[i+2].x, route.ps[i+2].y));
340 i+=2;
341 break;
342 }
343 }
344 }
345
346 return curve;
347}
348
349void SPConnEndPair::tellLibavoidNewEndpoints(bool const processTransaction)
350{
351 if (_connRef == nullptr || !isAutoRoutingConn()) {
352 // Do nothing
353 return;
354 }
356
358 if (processTransaction) {
360 }
361 return;
362}
363
365{
366 if (!_connRef || !isAutoRoutingConn()) {
367 // Do nothing
368 return false;
369 }
370
372
373 auto doc2item = _path->i2doc_affine().inverse();
374 curve.transform(doc2item);
375
376 _path->setCurve(std::move(curve));
377
378 return true;
379}
380
381/*
382 Local Variables:
383 mode:c++
384 c-file-style:"stroustrup"
385 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
386 indent-tabs-mode:nil
387 fill-column:99
388 End:
389*/
390// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ CONNECTOR_TYPE
@ CONNECTION_START
@ CONNECTOR_CURVATURE
@ CONNECTION_START_POINT
@ CONNECTION_END_POINT
@ CONNECTION_END
The ConnRef class represents a connector object.
Definition connector.h:132
void makePathInvalid(void)
void calcRouteDist(void)
void setCallback(void(*cb)(void *), void *ptr)
Sets a callback function that will called to indicate that the connector needs rerouting.
bool isInitialised(void) const
PolyLine & displayRoute(void)
Returns a reference to the current display version of the route for the connector.
Router * router(void) const
Returns a pointer to the router scene this connector is in.
void setRoutingType(ConnType type)
Sets the type of routing to be performed for this connector.
void setEndpoints(const ConnEnd &srcPoint, const ConnEnd &dstPoint)
Sets both a new source and destination endpoint for this connector.
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.
Polygon curvedPolyline(const double curve_amount, const bool closed=false) const
Returns a curved approximation of this multi-segment PolyLine, with the corners replaced by smooth Be...
std::vector< char > ts
If used, denotes whether the corresponding point in ps is a move-to operation or a Bezier curve-to.
Definition geomtypes.h:301
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 deleteConnector(ConnRef *connector)
Remove a connector from the router scene.
Definition router.cpp:312
bool processTransaction(void)
Finishes the current transaction and processes all the queued object changes efficiently.
Definition router.cpp:640
3x3 matrix representing an affine transformation.
Definition affine.h:70
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Two-dimensional point that doubles as a vector.
Definition point.h:66
URI const * getURI() const
Returns a pointer to a URI containing the currently attached URI, or NULL if no URI is currently atta...
sigc::signal< void(SPObject *, SPObject *)> changedSignal()
Accessor for the referrent change notification signal; this signal is emitted whenever the URIReferen...
Represents an URI as per RFC 2396.
Definition uri.h:36
std::string str(char const *baseuri=nullptr) const
Return the string representation of this URI.
Definition uri.cpp:281
Interface for refcounted XML nodes.
Definition node.h:80
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
Geom::Point getConnectionPointPos()
Avoid::ConnRef * _connRef
void writeRepr(Inkscape::XML::Node *const repr) const
void setAttr(const SPAttr key, char const *const value)
void getEndpoints(Geom::Point endPts[]) const
SPConnEndPair(SPPath *)
sigc::connection _transformed_connection
SPConnEnd * _connEnd[2]
bool isOrthogonal() const
void tellLibavoidNewEndpoints(bool const processTransaction=false)
SPConnEnd ** getConnEnds()
static SPCurve createCurve(Avoid::ConnRef *connRef, double curvature)
bool isAutoRoutingConn() const
double getCurvature() const
void getAttachedItems(SPItem *[2]) const
sigc::connection _changed_connection
Change of href string (not a modification of the attributes of the referrent).
Definition sp-conn-end.h:36
void setAttacherSubHref(char const *value)
SPUseReference ref
Definition sp-conn-end.h:30
SPUseReference sub_ref
Definition sp-conn-end.h:31
void setAttacherHref(char const *value)
Wrapper around a Geom::PathVector object.
Definition curve.h:26
Avoid::Router * getRouter() const
Definition document.h:199
Base class for visual SVG elements.
Definition sp-item.h:109
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:1823
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
SVG <path> implementation.
Definition sp-path.h:29
SPCurve const * curveForEdit() const
Return a borrowed pointer of the curve for edit.
Definition sp-shape.cpp:986
void setCurve(SPCurve const *)
Definition sp-shape.cpp:926
SPItem * getObject() const
RootCluster root
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
@ ConnType_Orthogonal
The connector path will be a shortest-path orthogonal poly-line (only vertical and horizontal line se...
Definition connector.h:61
@ ConnType_PolyLine
The connector path will be a shortest-path poly-line that routes around obstacles.
Definition connector.h:57
static cairo_user_data_key_t key
Ocnode * child[8]
Definition quantize.cpp:33
Contains the interface for the Router class.
double curvature(Point const &a, Point const &b, Point const &c)
static void avoid_conn_transformed(Geom::Affine const *, SPItem *moved_item)
static void redrawConnectorCallback(void *ptr)
void sp_conn_end_pair_build(SPObject *object)
@ SP_CONNECTOR_POLYLINE
@ SP_CONNECTOR_NOAVOID
@ SP_CONNECTOR_ORTHOGONAL
void sp_conn_reroute_path(SPPath *const path)
void sp_conn_reroute_path_immediate(SPPath *const path)
void sp_conn_end_href_changed(SPObject *, SPObject *, SPConnEnd *connEnd, SPPath *path, unsigned handle_ix)
void sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix)
void sp_conn_redraw_path(SPPath *const path)
TODO: insert short description here.
Definition curve.h:24
Interface for XML nodes.