42
static bool try_get_intersect_point_with_item_recursive(SPCurve *conn_curve, SPItem& item,
43
const Geom::Matrix& item_transform, const bool at_start, double* intersect_pos,
44
unsigned *intersect_index) {
46
double initial_pos = (at_start) ? 0.0 : std::numeric_limits<double>::max();
45
static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_pv, SPItem* item,
46
const Geom::Matrix& item_transform, double& intersect_pos) {
48
double initial_pos = intersect_pos;
48
49
// if this is a group...
49
if (SP_IS_GROUP(&item)) {
50
SPGroup* group = SP_GROUP(&item);
50
if (SP_IS_GROUP(item)) {
51
SPGroup* group = SP_GROUP(item);
52
53
// consider all first-order children
53
double child_pos = initial_pos;
54
double child_pos = 0.0;
55
55
for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) {
56
56
SPItem* child_item = SP_ITEM(i->data);
57
try_get_intersect_point_with_item_recursive(conn_curve, *child_item,
58
item_transform * child_item->transform, at_start, &child_pos, &child_index);
59
if (fabs(initial_pos - child_pos) > fabs(initial_pos - *intersect_pos)) {
60
// It is further away from the initial point than the current intersection
61
// point (i.e. the "outermost" intersection), so use this one.
62
*intersect_pos = child_pos;
63
*intersect_index = child_index;
57
try_get_intersect_point_with_item_recursive(conn_pv, child_item,
58
item_transform * child_item->transform, child_pos);
59
if (intersect_pos < child_pos)
60
intersect_pos = child_pos;
66
return *intersect_pos != initial_pos;
62
return intersect_pos != initial_pos;
69
// if this is a shape...
70
if (!SP_IS_SHAPE(&item)) return false;
65
// if this is not a shape, nothing to be done
66
if (!SP_IS_SHAPE(item)) return false;
72
68
// make sure it has an associated curve
73
SPCurve* item_curve = sp_shape_get_curve(SP_SHAPE(&item));
69
SPCurve* item_curve = sp_shape_get_curve(SP_SHAPE(item));
74
70
if (!item_curve) return false;
76
72
// apply transformations (up to common ancestor)
77
73
item_curve->transform(item_transform);
79
75
const Geom::PathVector& curve_pv = item_curve->get_pathvector();
80
const Geom::PathVector& conn_pv = conn_curve->get_pathvector();
81
76
Geom::CrossingSet cross = crossings(conn_pv, curve_pv);
82
77
// iterate over all Crossings
83
78
for (Geom::CrossingSet::const_iterator i = cross.begin(); i != cross.end(); i++) {
104
95
// and the item given. If the item is a group, then the component items are considered.
105
96
// The transforms given should be to a common ancestor of both the path and item.
107
static bool try_get_intersect_point_with_item(SPPath& conn, SPItem& item,
108
const Geom::Matrix& item_transform, const Geom::Matrix& conn_transform,
109
const bool at_start, double* intersect_pos, unsigned *intersect_index) {
111
// We start with the intersection point either at the beginning or end of the
112
// path, depending on whether we are considering the source or target endpoint.
113
*intersect_pos = (at_start) ? 0.0 : std::numeric_limits<double>::max();
98
static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item,
99
const Geom::Matrix& item_transform, const Geom::Matrix& conn_transform,
100
const bool at_start, double& intersect_pos) {
115
102
// Copy the curve and apply transformations up to common ancestor.
116
SPCurve* conn_curve = conn.curve->copy();
103
SPCurve* conn_curve = conn->curve->copy();
117
104
conn_curve->transform(conn_transform);
106
Geom::PathVector conn_pv = conn_curve->get_pathvector();
108
// If this is not the starting point, use Geom::Path::reverse() to reverse the path
111
// connectors are actually a single path, so consider the first element from a Geom::PathVector
112
conn_pv[0] = conn_pv[0].reverse();
115
// We start with the intersection point at the beginning of the path
119
118
// Find the intersection.
120
bool result = try_get_intersect_point_with_item_recursive(conn_curve, item, item_transform,
121
at_start, intersect_pos, intersect_index);
119
bool result = try_get_intersect_point_with_item_recursive(conn_pv, item, item_transform, intersect_pos);
122
// No intersection point has been found (why?)
123
// just default to connector end
125
// If not at the starting point, recompute position with respect to original path
127
intersect_pos = conn_pv[0].size() - intersect_pos;
123
128
// Free the curve copy.
124
129
conn_curve->unref();
131
sp_conn_end_move_compensate(Geom::Matrix const */*mp*/, SPItem */*moved_item*/,
133
bool const updatePathRepr = true)
136
sp_conn_get_route_and_redraw(SPPath *const path,
137
const bool updatePathRepr = true)
135
// TODO: SPItem::getBounds gives the wrong result for some objects
136
// that have internal representations that are updated later
137
// by the sp_*_update functions, e.g., text.
138
sp_document_ensure_up_to_date(path->document);
140
139
// Get the new route around obstacles.
141
path->connEndPair.reroutePath();
140
bool rerouted = path->connEndPair.reroutePathFromLibavoid();
143
145
SPItem *h2attItem[2];
144
146
path->connEndPair.getAttachedItems(h2attItem);
147
149
SPObject const *const ancestor = get_nearest_common_ancestor(path_item, h2attItem);
148
150
Geom::Matrix const path2anc(i2anc_affine(path_item, ancestor));
150
Geom::Point endPts[2] = { *(path->curve->first_point()), *(path->curve->last_point()) };
152
// Set sensible values incase there the connector ends are not
153
// attached to any shapes.
154
Geom::PathVector conn_pv = path->curve->get_pathvector();
155
double endPos[2] = { 0, conn_pv[0].size() };
157
SPConnEnd** _connEnd = path->connEndPair.getConnEnds();
152
158
for (unsigned h = 0; h < 2; ++h) {
154
// For each attached object, change the corresponding point to be
155
// at the outermost intersection with the object's path.
156
double intersect_pos;
157
unsigned intersect_index;
159
if (h2attItem[h] && _connEnd[h]->type == ConnPointDefault && _connEnd[h]->id == ConnPointPosCC) {
158
160
Geom::Matrix h2i2anc = i2anc_affine(h2attItem[h], ancestor);
159
if ( try_get_intersect_point_with_item(*path, *h2attItem[h], h2i2anc, path2anc,
160
(h == 0), &intersect_pos, &intersect_index) ) {
161
const Geom::PathVector& curve = path->curve->get_pathvector();
162
endPts[h] = curve[intersect_index].pointAt(intersect_pos);
161
try_get_intersect_point_with_item(path, h2attItem[h], h2i2anc, path2anc,
162
(h == 0), endPos[h]);
166
change_endpts(path->curve, endPts);
165
change_endpts(path->curve, endPos);
167
166
if (updatePathRepr) {
168
168
path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
173
// TODO: This triggering of makeInvalidPath could be cleaned up to be
174
// another option passed to move_compensate.
176
sp_conn_end_shape_move_compensate(Geom::Matrix const *mp, SPItem *moved_item,
174
sp_conn_end_shape_move(Geom::Matrix const */*mp*/, SPItem */*moved_item*/,
177
175
SPPath *const path)
179
177
if (path->connEndPair.isAutoRoutingConn()) {
180
path->connEndPair.makePathInvalid();
182
sp_conn_end_move_compensate(mp, moved_item, path);
187
sp_conn_adjust_invalid_path(SPPath *const path)
189
sp_conn_end_move_compensate(NULL, NULL, path);
193
sp_conn_adjust_path(SPPath *const path)
195
if (path->connEndPair.isAutoRoutingConn()) {
196
path->connEndPair.makePathInvalid();
178
path->connEndPair.tellLibavoidNewEndpoints();
184
sp_conn_reroute_path(SPPath *const path)
186
if (path->connEndPair.isAutoRoutingConn()) {
187
path->connEndPair.tellLibavoidNewEndpoints();
193
sp_conn_reroute_path_immediate(SPPath *const path)
195
if (path->connEndPair.isAutoRoutingConn()) {
196
bool processTransaction = true;
197
path->connEndPair.tellLibavoidNewEndpoints(processTransaction);
198
199
// Don't update the path repr or else connector dragging is slowed by
199
200
// constant update of values to the xml editor, and each step is also
200
201
// needlessly remembered by undo/redo.
201
202
bool const updatePathRepr = false;
202
sp_conn_end_move_compensate(NULL, NULL, path, updatePathRepr);
203
sp_conn_get_route_and_redraw(path, updatePathRepr);
206
void sp_conn_redraw_path(SPPath *const path)
208
sp_conn_get_route_and_redraw(path);
207
change_endpts(SPCurve *const curve, Geom::Point const h2endPt[2])
213
change_endpts(SPCurve *const curve, double const endPos[2])
211
curve->moveto(h2endPt[0]);
212
curve->lineto(h2endPt[1]);
214
curve->move_endpoints(h2endPt[0], h2endPt[1]);
215
// Use Geom::Path::portion to cut the curve at the end positions
216
if (endPos[0] > endPos[1])
218
// Path is "negative", reset the curve and return
222
const Geom::Path& old_path = curve->get_pathvector()[0];
223
Geom::PathVector new_path_vector;
224
new_path_vector.push_back(old_path.portion(endPos[0], endPos[1]));
225
curve->set_pathvector(new_path_vector);
221
231
// todo: The first argument is the deleted object, or just NULL if
222
232
// called by sp_conn_end_detach.
223
233
g_return_if_fail(handle_ix < 2);
224
char const *const attr_str[] = {"inkscape:connection-start",
225
"inkscape:connection-end"};
226
SP_OBJECT_REPR(owner)->setAttribute(attr_str[handle_ix], NULL);
234
char const * const attr_strs[] = {"inkscape:connection-start", "inkscape:connection-start-point",
235
"inkscape:connection-end", "inkscape:connection-end-point"};
236
SP_OBJECT_REPR(owner)->setAttribute(attr_strs[2*handle_ix], NULL);
237
SP_OBJECT_REPR(owner)->setAttribute(attr_strs[2*handle_ix+1], NULL);
227
238
/* I believe this will trigger sp_conn_end_href_changed. */
237
SPConnEnd::setAttacherHref(gchar const *value)
248
SPConnEnd::setAttacherHref(gchar const *value, SPPath* /*path*/)
239
250
if ( value && href && ( strcmp(value, href) == 0 ) ) {
240
251
/* No change, do nothing. */
245
// First, set the href field, because sp_conn_end_href_changed will need it.
263
bool validRef = true;
246
264
href = g_strdup(value);
248
265
// Now do the attaching, which emits the changed signal.
250
267
ref.attach(Inkscape::URI(value));
251
268
} catch (Inkscape::BadURIException &e) {
252
269
/* TODO: Proper error handling as per
253
* http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing. (Also needed for
270
* http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing. (Also needed for
255
272
g_warning("%s", e.what());
287
SPConnEnd::setAttacherEndpoint(gchar const *value, SPPath* /*path*/)
290
/* References to the connection points have the following format
291
<t><id>, where t is the type of the point, which
292
can be either "d" for default or "u" for user-defined, and
293
id is the local (inside the item) id of the connection point.
294
In the case of default points id represents the position on the
295
item (i.e. Top-Left, Center-Center, etc.).
298
bool changed = false;
299
ConnPointType newtype = type;
303
// Default to center endpoint
304
type = ConnPointDefault;
312
if ( newtype != ConnPointDefault )
314
newtype = ConnPointDefault;
319
if ( newtype != ConnPointUserDefined)
321
newtype = ConnPointUserDefined;
326
g_warning("Bad reference to a connection point.");
329
int newid = (int) g_ascii_strtod( value+1, 0 );
336
// We have to verify that the reference to the
337
// connection point is a valid one.
342
// Get the item the connector is attached to
343
SPItem* item = ref.getObject();
346
if (!item->avoidRef->isValidConnPointId( newtype, newid ) )
348
g_warning("Bad reference to a connection point.");
355
/* // Update the connector
356
if (path->connEndPair.isAutoRoutingConn()) {
357
path->connEndPair.tellLibavoidNewEndpoints();