~ubuntu-branches/ubuntu/precise/inkscape/precise-updates

« back to all changes in this revision

Viewing changes to src/sp-conn-end.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alex Valavanis
  • Date: 2010-09-12 19:44:58 UTC
  • mfrom: (1.1.12 upstream) (45.1.3 maverick)
  • Revision ID: james.westby@ubuntu.com-20100912194458-4sjwmbl7dlsrk5dc
Tags: 0.48.0-1ubuntu1
* Merge with Debian unstable (LP: #628048, LP: #401567, LP: #456248, 
  LP: #463602, LP: #591986)
* debian/control: 
  - Ubuntu maintainers
  - Promote python-lxml, python-numpy, python-uniconvertor to Recommends.
  - Demote pstoedit to Suggests (universe package).
  - Suggests ttf-dejavu instead of ttf-bitstream-vera (LP: #513319)
* debian/rules:
  - Run intltool-update on build (Ubuntu-specific).
  - Add translation domain to .desktop files (Ubuntu-specific).
* debian/dirs:
  - Add usr/share/pixmaps.  Allow inkscape.xpm installation
* drop 50-poppler-API.dpatch (now upstream)
* drop 51-paste-in-unwritable-directory.dpatch (now upstream) 

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
#include "2geom/path-intersection.h"
17
17
 
18
18
 
19
 
static void change_endpts(SPCurve *const curve, Geom::Point const h2endPt[2]);
 
19
static void change_endpts(SPCurve *const curve, double const endPos[2]);
20
20
 
21
21
SPConnEnd::SPConnEnd(SPObject *const owner) :
22
22
    ref(owner),
23
23
    href(NULL),
 
24
    // Default to center connection endpoint
 
25
    type(ConnPointDefault),
 
26
    id(4),
24
27
    _changed_connection(),
25
28
    _delete_connection(),
26
29
    _transformed_connection()
39
42
}
40
43
 
41
44
 
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) {
45
 
 
46
 
    double initial_pos = (at_start) ? 0.0 : std::numeric_limits<double>::max();
47
 
 
 
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) {
 
47
 
 
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);
51
 
        
 
50
    if (SP_IS_GROUP(item)) {
 
51
        SPGroup* group = SP_GROUP(item);
 
52
 
52
53
        // consider all first-order children
53
 
        double child_pos = initial_pos;
54
 
        unsigned child_index;
 
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;
64
 
            }
 
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;
65
61
        }
66
 
        return *intersect_pos != initial_pos;
 
62
        return intersect_pos != initial_pos;
67
63
    }
68
64
 
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;
71
67
 
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;
75
71
 
76
72
    // apply transformations (up to common ancestor)
77
73
    item_curve->transform(item_transform);
78
74
 
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++) {
85
80
 
86
81
        for (Geom::Crossings::const_iterator i = cr.begin(); i != cr.end(); i++) {
87
82
            const Geom::Crossing& cr_pt = *i;
88
 
            if (fabs(initial_pos - cr_pt.ta) > fabs(initial_pos - *intersect_pos)) {
89
 
                // It is further away from the initial point than the current intersection
90
 
                // point (i.e. the "outermost" intersection), so use this one.
91
 
                *intersect_pos = cr_pt.ta;
92
 
                *intersect_index = cr_pt.a;
93
 
            }
 
83
            if ( intersect_pos < cr_pt.ta)
 
84
                intersect_pos = cr_pt.ta;
94
85
        }
95
86
    }
96
87
 
97
88
    item_curve->unref();
98
89
 
99
 
    return *intersect_pos != initial_pos;
 
90
    return intersect_pos != initial_pos;
100
91
}
101
92
 
102
93
 
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.
106
97
//
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) {
110
 
 
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) {
114
101
 
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);
118
105
 
 
106
    Geom::PathVector conn_pv = conn_curve->get_pathvector();
 
107
 
 
108
    // If this is not the starting point, use Geom::Path::reverse() to reverse the path
 
109
    if (!at_start)
 
110
    {
 
111
        // connectors are actually a single path, so consider the first element from a Geom::PathVector
 
112
        conn_pv[0] = conn_pv[0].reverse();
 
113
    }
 
114
 
 
115
    // We start with the intersection point at the beginning of the path
 
116
    intersect_pos = 0.0;
 
117
 
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);
122
 
    
 
119
    bool result = try_get_intersect_point_with_item_recursive(conn_pv, item, item_transform, intersect_pos);
 
120
 
 
121
    if (!result)
 
122
        // No intersection point has been found (why?)
 
123
        // just default to connector end
 
124
        intersect_pos = 0;
 
125
    // If not at the starting point, recompute position with respect to original path
 
126
    if (!at_start)
 
127
        intersect_pos = conn_pv[0].size() - intersect_pos;
123
128
    // Free the curve copy.
124
129
    conn_curve->unref();
125
130
 
128
133
 
129
134
 
130
135
static void
131
 
sp_conn_end_move_compensate(Geom::Matrix const */*mp*/, SPItem */*moved_item*/,
132
 
                            SPPath *const path,
133
 
                            bool const updatePathRepr = true)
 
136
sp_conn_get_route_and_redraw(SPPath *const path,
 
137
        const bool updatePathRepr = true)
134
138
{
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);
139
 
 
140
139
    // Get the new route around obstacles.
141
 
    path->connEndPair.reroutePath();
 
140
    bool rerouted = path->connEndPair.reroutePathFromLibavoid();
 
141
    if (!rerouted) {
 
142
        return;
 
143
    }
142
144
 
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));
149
151
 
150
 
    Geom::Point endPts[2] = { *(path->curve->first_point()), *(path->curve->last_point()) };
151
 
 
 
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() };
 
156
 
 
157
    SPConnEnd** _connEnd = path->connEndPair.getConnEnds();
152
158
    for (unsigned h = 0; h < 2; ++h) {
153
 
        if (h2attItem[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);
163
 
            }
 
161
            try_get_intersect_point_with_item(path, h2attItem[h], h2i2anc, path2anc,
 
162
                        (h == 0), endPos[h]);
164
163
        }
165
164
    }
166
 
    change_endpts(path->curve, endPts);
 
165
    change_endpts(path->curve, endPos);
167
166
    if (updatePathRepr) {
 
167
        path->updateRepr();
168
168
        path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
169
 
        path->updateRepr();
170
169
    }
171
170
}
172
171
 
173
 
// TODO: This triggering of makeInvalidPath could be cleaned up to be
174
 
//       another option passed to move_compensate.
 
172
 
175
173
static void
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)
178
176
{
179
177
    if (path->connEndPair.isAutoRoutingConn()) {
180
 
        path->connEndPair.makePathInvalid();
181
 
    }
182
 
    sp_conn_end_move_compensate(mp, moved_item, path);
183
 
}
184
 
 
185
 
 
186
 
void
187
 
sp_conn_adjust_invalid_path(SPPath *const path)
188
 
{
189
 
    sp_conn_end_move_compensate(NULL, NULL, path);
190
 
}
191
 
 
192
 
void
193
 
sp_conn_adjust_path(SPPath *const path)
194
 
{
195
 
    if (path->connEndPair.isAutoRoutingConn()) {
196
 
        path->connEndPair.makePathInvalid();
 
178
        path->connEndPair.tellLibavoidNewEndpoints();
 
179
    }
 
180
}
 
181
 
 
182
 
 
183
void
 
184
sp_conn_reroute_path(SPPath *const path)
 
185
{
 
186
    if (path->connEndPair.isAutoRoutingConn()) {
 
187
        path->connEndPair.tellLibavoidNewEndpoints();
 
188
    }
 
189
}
 
190
 
 
191
 
 
192
void
 
193
sp_conn_reroute_path_immediate(SPPath *const path)
 
194
{
 
195
    if (path->connEndPair.isAutoRoutingConn()) {
 
196
        bool processTransaction = true;
 
197
        path->connEndPair.tellLibavoidNewEndpoints(processTransaction);
197
198
    }
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);
 
204
}
 
205
 
 
206
void sp_conn_redraw_path(SPPath *const path)
 
207
{
 
208
    sp_conn_get_route_and_redraw(path);
203
209
}
204
210
 
205
211
 
206
212
static void
207
 
change_endpts(SPCurve *const curve, Geom::Point const h2endPt[2])
 
213
change_endpts(SPCurve *const curve, double const endPos[2])
208
214
{
209
 
#if 0
210
 
    curve->reset();
211
 
    curve->moveto(h2endPt[0]);
212
 
    curve->lineto(h2endPt[1]);
213
 
#else
214
 
    curve->move_endpoints(h2endPt[0], h2endPt[1]);
215
 
#endif
 
215
    // Use Geom::Path::portion to cut the curve at the end positions
 
216
    if (endPos[0] > endPos[1])
 
217
    {
 
218
        // Path is "negative", reset the curve and return
 
219
        curve->reset();
 
220
        return;
 
221
    }
 
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);
216
226
}
217
227
 
218
228
static void
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. */
228
239
}
229
240
 
234
245
}
235
246
 
236
247
void
237
 
SPConnEnd::setAttacherHref(gchar const *value)
 
248
SPConnEnd::setAttacherHref(gchar const *value, SPPath* /*path*/)
238
249
{
239
250
    if ( value && href && ( strcmp(value, href) == 0 ) ) {
240
251
        /* No change, do nothing. */
241
 
    } else {
242
 
        g_free(href);
243
 
        href = NULL;
244
 
        if (value) {
245
 
            // First, set the href field, because sp_conn_end_href_changed will need it.
 
252
    } 
 
253
    else 
 
254
    {
 
255
        if (!value)
 
256
        {
 
257
            ref.detach();
 
258
            g_free(href);
 
259
            href = NULL;
 
260
        }
 
261
        else
 
262
        {
 
263
            bool validRef = true;
246
264
            href = g_strdup(value);
247
 
 
248
265
            // Now do the attaching, which emits the changed signal.
249
266
            try {
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
254
 
                 * sp-use.) */
 
270
                * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.  (Also needed for
 
271
                * sp-use.) */
255
272
                g_warning("%s", e.what());
 
273
                validRef = false;
 
274
            }
 
275
 
 
276
            if ( !validRef )
 
277
            {
256
278
                ref.detach();
257
 
            }
258
 
        } else {
259
 
            ref.detach();
 
279
                g_free(href);
 
280
                href = NULL;
 
281
            }
 
282
        }
 
283
    }
 
284
}
 
285
 
 
286
void
 
287
SPConnEnd::setAttacherEndpoint(gchar const *value, SPPath* /*path*/)
 
288
{
 
289
    
 
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.).
 
296
    */
 
297
    
 
298
    bool changed = false;
 
299
    ConnPointType newtype = type;
 
300
    
 
301
    if (!value)
 
302
    {
 
303
        // Default to center endpoint
 
304
        type = ConnPointDefault;
 
305
        id = 4;
 
306
    }
 
307
    else
 
308
    {
 
309
        switch (value[0])
 
310
        {
 
311
            case 'd':
 
312
                if ( newtype != ConnPointDefault )
 
313
                {
 
314
                    newtype = ConnPointDefault;
 
315
                    changed = true;
 
316
                }
 
317
                break;
 
318
            case 'u':
 
319
                if ( newtype != ConnPointUserDefined)
 
320
                {
 
321
                    newtype = ConnPointUserDefined;
 
322
                    changed = true;
 
323
                }
 
324
                break;
 
325
            default:
 
326
                g_warning("Bad reference to a connection point.");
 
327
        }
 
328
        
 
329
        int newid = (int) g_ascii_strtod( value+1, 0 );
 
330
        if ( id != newid )
 
331
        {
 
332
            id = newid;
 
333
            changed = true;
 
334
        }
 
335
 
 
336
        // We have to verify that the reference to the
 
337
        // connection point is a valid one.
 
338
        
 
339
        if ( changed )
 
340
        {
 
341
 
 
342
            // Get the item the connector is attached to
 
343
            SPItem* item = ref.getObject();
 
344
            if ( item )
 
345
            {
 
346
                if (!item->avoidRef->isValidConnPointId( newtype, newid ) )
 
347
                {
 
348
                    g_warning("Bad reference to a connection point.");
 
349
                }
 
350
                else
 
351
                {
 
352
                    type = newtype;
 
353
                    id = newid;
 
354
                }
 
355
    /*          // Update the connector
 
356
                if (path->connEndPair.isAutoRoutingConn()) {
 
357
                    path->connEndPair.tellLibavoidNewEndpoints();
 
358
                }
 
359
    */
 
360
            }
260
361
        }
261
362
    }
262
363
}
277
378
                = SP_OBJECT(refobj)->connectDelete(sigc::bind(sigc::ptr_fun(&sp_conn_end_deleted),
278
379
                                                              SP_OBJECT(path), handle_ix));
279
380
            connEnd._transformed_connection
280
 
                = SP_ITEM(refobj)->connectTransformed(sigc::bind(sigc::ptr_fun(&sp_conn_end_shape_move_compensate),
 
381
                = SP_ITEM(refobj)->connectTransformed(sigc::bind(sigc::ptr_fun(&sp_conn_end_shape_move),
281
382
                                                                 path));
282
383
        }
283
384
    }