~ubuntu-branches/ubuntu/oneiric/inkscape/oneiric-updates

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/sp-shape.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Base class for shapes, including <path> element
 
3
 *
 
4
 * Author:
 
5
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
6
 *
 
7
 * Copyright (C) 1999-2002 Lauris Kaplinski
 
8
 * Copyright (C) 2000-2001 Ximian, Inc.
 
9
 * Copyright (C) 2004 John Cliff
 
10
 * Copyright (C) 2007-2008 Johan Engelen
 
11
 *
 
12
 * Released under GNU GPL, read the file 'COPYING' for more information
 
13
 */
 
14
 
 
15
#ifdef HAVE_CONFIG_H
 
16
# include "config.h"
 
17
#endif
 
18
 
 
19
#include <libnr/nr-matrix-fns.h>
 
20
#include <libnr/nr-matrix-ops.h>
 
21
#include <libnr/nr-matrix-translate-ops.h>
 
22
#include <libnr/nr-scale-matrix-ops.h>
 
23
#include <2geom/rect.h>
 
24
#include <2geom/transforms.h>
 
25
#include <2geom/pathvector.h>
 
26
#include <2geom/path-intersection.h>
 
27
#include "helper/geom.h"
 
28
#include "helper/geom-nodetype.h"
 
29
 
 
30
#include <sigc++/functors/ptr_fun.h>
 
31
#include <sigc++/adaptors/bind.h>
 
32
 
 
33
#include "macros.h"
 
34
#include "display/nr-arena-shape.h"
 
35
#include "display/curve.h"
 
36
#include "print.h"
 
37
#include "document.h"
 
38
#include "style.h"
 
39
#include "marker.h"
 
40
#include "sp-path.h"
 
41
#include "preferences.h"
 
42
#include "attributes.h"
 
43
 
 
44
#include "live_effects/lpeobject.h"
 
45
#include "uri.h"
 
46
#include "extract-uri.h"
 
47
#include "uri-references.h"
 
48
#include "bad-uri-exception.h"
 
49
#include "xml/repr.h"
 
50
 
 
51
#include "util/mathfns.h" // for triangle_area()
 
52
 
 
53
#define noSHAPE_VERBOSE
 
54
 
 
55
static void sp_shape_class_init (SPShapeClass *klass);
 
56
static void sp_shape_init (SPShape *shape);
 
57
static void sp_shape_finalize (GObject *object);
 
58
 
 
59
static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
 
60
static void sp_shape_release (SPObject *object);
 
61
 
 
62
static void sp_shape_set(SPObject *object, unsigned key, gchar const *value);
 
63
static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
 
64
static void sp_shape_modified (SPObject *object, unsigned int flags);
 
65
static Inkscape::XML::Node *sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
 
66
 
 
67
static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags);
 
68
void sp_shape_print (SPItem * item, SPPrintContext * ctx);
 
69
static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
 
70
static void sp_shape_hide (SPItem *item, unsigned int key);
 
71
static void sp_shape_snappoints (SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs);
 
72
 
 
73
static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
 
74
 
 
75
static SPLPEItemClass *parent_class;
 
76
 
 
77
/**
 
78
 * Registers the SPShape class with Gdk and returns its type number.
 
79
 */
 
80
GType
 
81
sp_shape_get_type (void)
 
82
{
 
83
    static GType type = 0;
 
84
    if (!type) {
 
85
        GTypeInfo info = {
 
86
            sizeof (SPShapeClass),
 
87
            NULL, NULL,
 
88
            (GClassInitFunc) sp_shape_class_init,
 
89
            NULL, NULL,
 
90
            sizeof (SPShape),
 
91
            16,
 
92
            (GInstanceInitFunc) sp_shape_init,
 
93
            NULL,    /* value_table */
 
94
        };
 
95
        type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0);
 
96
    }
 
97
    return type;
 
98
}
 
99
 
 
100
/**
 
101
 * Initializes a SPShapeClass object.  Establishes the function pointers to the class'
 
102
 * member routines in the class vtable, and sets pointers to parent classes.
 
103
 */
 
104
static void
 
105
sp_shape_class_init (SPShapeClass *klass)
 
106
{
 
107
    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
 
108
    SPObjectClass *sp_object_class = SP_OBJECT_CLASS(klass);
 
109
    SPItemClass * item_class = SP_ITEM_CLASS(klass);
 
110
    SPLPEItemClass * lpe_item_class = SP_LPE_ITEM_CLASS(klass);
 
111
 
 
112
    parent_class = (SPLPEItemClass *)g_type_class_peek_parent (klass);
 
113
 
 
114
    gobject_class->finalize = sp_shape_finalize;
 
115
 
 
116
    sp_object_class->build = sp_shape_build;
 
117
    sp_object_class->release = sp_shape_release;
 
118
    sp_object_class->set = sp_shape_set;
 
119
    sp_object_class->update = sp_shape_update;
 
120
    sp_object_class->modified = sp_shape_modified;
 
121
    sp_object_class->write = sp_shape_write;
 
122
 
 
123
    item_class->bbox = sp_shape_bbox;
 
124
    item_class->print = sp_shape_print;
 
125
    item_class->show = sp_shape_show;
 
126
    item_class->hide = sp_shape_hide;
 
127
    item_class->snappoints = sp_shape_snappoints;
 
128
    lpe_item_class->update_patheffect = NULL;
 
129
 
 
130
    klass->set_shape = NULL;
 
131
}
 
132
 
 
133
/**
 
134
 * Initializes an SPShape object.
 
135
 */
 
136
static void
 
137
sp_shape_init (SPShape *shape)
 
138
{
 
139
    for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
 
140
        new (&shape->release_connect[i]) sigc::connection();
 
141
        new (&shape->modified_connect[i]) sigc::connection();
 
142
        shape->marker[i] = NULL;
 
143
    }
 
144
    shape->curve = NULL;
 
145
}
 
146
 
 
147
static void
 
148
sp_shape_finalize (GObject *object)
 
149
{
 
150
    SPShape *shape=(SPShape *)object;
 
151
 
 
152
    for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
 
153
        shape->release_connect[i].disconnect();
 
154
        shape->release_connect[i].~connection();
 
155
        shape->modified_connect[i].disconnect();
 
156
        shape->modified_connect[i].~connection();
 
157
    }
 
158
 
 
159
    if (((GObjectClass *) (parent_class))->finalize) {
 
160
        (* ((GObjectClass *) (parent_class))->finalize)(object);
 
161
    }
 
162
}
 
163
 
 
164
/**
 
165
 * Virtual build callback for SPMarker.
 
166
 *
 
167
 * This is to be invoked immediately after creation of an SPShape.
 
168
 *
 
169
 * \see sp_object_build()
 
170
 */
 
171
static void
 
172
sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
 
173
{
 
174
    if (((SPObjectClass *) (parent_class))->build) {
 
175
       (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
 
176
    }
 
177
 
 
178
    for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
 
179
        sp_shape_set_marker (object, i, object->style->marker[i].value);
 
180
      }
 
181
}
 
182
 
 
183
/**
 
184
 * Removes, releases and unrefs all children of object
 
185
 *
 
186
 * This is the inverse of sp_shape_build().  It must be invoked as soon
 
187
 * as the shape is removed from the tree, even if it is still referenced
 
188
 * by other objects.  This routine also disconnects/unrefs markers and
 
189
 * curves attached to it.
 
190
 *
 
191
 * \see sp_object_release()
 
192
 */
 
193
static void
 
194
sp_shape_release (SPObject *object)
 
195
{
 
196
    SPItem *item;
 
197
    SPShape *shape;
 
198
    SPItemView *v;
 
199
    int i;
 
200
 
 
201
    item = (SPItem *) object;
 
202
    shape = (SPShape *) object;
 
203
 
 
204
    for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
 
205
        if (shape->marker[i]) {
 
206
            for (v = item->display; v != NULL; v = v->next) {
 
207
              sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
 
208
            }
 
209
            shape->release_connect[i].disconnect();
 
210
            shape->modified_connect[i].disconnect();
 
211
            shape->marker[i] = sp_object_hunref (shape->marker[i], object);
 
212
        }
 
213
    }
 
214
    if (shape->curve) {
 
215
        shape->curve = shape->curve->unref();
 
216
    }
 
217
 
 
218
    if (((SPObjectClass *) parent_class)->release) {
 
219
      ((SPObjectClass *) parent_class)->release (object);
 
220
    }
 
221
}
 
222
 
 
223
 
 
224
 
 
225
static void
 
226
sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
 
227
{
 
228
    if (((SPObjectClass *) parent_class)->set) {
 
229
        ((SPObjectClass *) parent_class)->set(object, key, value);
 
230
    }
 
231
}
 
232
 
 
233
static Inkscape::XML::Node *
 
234
sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
 
235
{
 
236
    if (((SPObjectClass *)(parent_class))->write) {
 
237
        ((SPObjectClass *)(parent_class))->write(object, doc, repr, flags);
 
238
    }
 
239
 
 
240
    return repr;
 
241
}
 
242
 
 
243
/**
 
244
 * Updates the shape when its attributes have changed.  Also establishes
 
245
 * marker objects to match the style settings.
 
246
 */
 
247
static void
 
248
sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
 
249
{
 
250
    SPItem *item = (SPItem *) object;
 
251
    SPShape *shape = (SPShape *) object;
 
252
 
 
253
    if (((SPObjectClass *) (parent_class))->update) {
 
254
        (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
 
255
    }
 
256
 
 
257
    /* This stanza checks that an object's marker style agrees with
 
258
     * the marker objects it has allocated.  sp_shape_set_marker ensures
 
259
     * that the appropriate marker objects are present (or absent) to
 
260
     * match the style.
 
261
     */
 
262
    for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
 
263
        sp_shape_set_marker (object, i, object->style->marker[i].value);
 
264
      }
 
265
 
 
266
    if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
 
267
        SPStyle *style;
 
268
        style = SP_OBJECT_STYLE (object);
 
269
        if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
 
270
            SPItemCtx *ictx = (SPItemCtx *) ctx;
 
271
            double const aw = 1.0 / NR::expansion(ictx->i2vp);
 
272
            style->stroke_width.computed = style->stroke_width.value * aw;
 
273
            for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
 
274
                nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
 
275
            }
 
276
        }
 
277
    }
 
278
 
 
279
    if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
 
280
        /* This is suboptimal, because changing parent style schedules recalculation */
 
281
        /* But on the other hand - how can we know that parent does not tie style and transform */
 
282
        Geom::OptRect paintbox = SP_ITEM(object)->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
 
283
        for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
 
284
            NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
 
285
            if (flags & SP_OBJECT_MODIFIED_FLAG) {
 
286
                nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
 
287
            }
 
288
            if (paintbox) {
 
289
                s->setPaintBox(*paintbox);
 
290
            }
 
291
        }
 
292
    }
 
293
 
 
294
    if (sp_shape_has_markers (shape)) {
 
295
        /* Dimension marker views */
 
296
        for (SPItemView *v = item->display; v != NULL; v = v->next) {
 
297
            if (!v->arenaitem->key) {
 
298
                NR_ARENA_ITEM_SET_KEY (v->arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
 
299
            }
 
300
            for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
 
301
                if (shape->marker[i]) {
 
302
                    sp_marker_show_dimension ((SPMarker *) shape->marker[i],
 
303
                                              NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i,
 
304
                                              sp_shape_number_of_markers (shape, i));
 
305
                }
 
306
            }
 
307
        }
 
308
 
 
309
        /* Update marker views */
 
310
        for (SPItemView *v = item->display; v != NULL; v = v->next) {
 
311
            sp_shape_update_marker_view (shape, v->arenaitem);
 
312
        }
 
313
    }
 
314
}
 
315
 
 
316
/**
 
317
 * Calculate the transform required to get a marker's path object in the
 
318
 * right place for particular path segment on a shape.
 
319
 *
 
320
 * \see sp_shape_marker_update_marker_view.
 
321
 *
 
322
 * From SVG spec:
 
323
 * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
 
324
 * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
 
325
 * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
 
326
 * determined, the slope is assumed to be zero.)
 
327
 *
 
328
 * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
 
329
 * Reference for behaviour of zero-length segments:
 
330
 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
 
331
 */
 
332
Geom::Matrix
 
333
sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
 
334
{
 
335
    Geom::Point p = c1.pointAt(1);
 
336
    Geom::Curve * c1_reverse = c1.reverse();
 
337
    Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
 
338
    delete c1_reverse;
 
339
    Geom::Point tang2 = c2.unitTangentAt(0);
 
340
 
 
341
    double const angle1 = Geom::atan2(tang1);
 
342
    double const angle2 = Geom::atan2(tang2);
 
343
 
 
344
    double ret_angle;
 
345
    ret_angle = .5 * (angle1 + angle2);
 
346
 
 
347
    if ( fabs( angle2 - angle1 ) > M_PI ) {
 
348
        /* ret_angle is in the middle of the larger of the two sectors between angle1 and
 
349
         * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
 
350
         *
 
351
         * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
 
352
         * circle.  Those two rays divide the circle into two sectors.)
 
353
         */
 
354
        ret_angle += M_PI;
 
355
    }
 
356
 
 
357
    return Geom::Rotate(ret_angle) * Geom::Translate(p);
 
358
}
 
359
Geom::Matrix
 
360
sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
 
361
{
 
362
    Geom::Point p = c.pointAt(0);
 
363
    Geom::Matrix ret = Geom::Translate(p);
 
364
 
 
365
    if ( !c.isDegenerate() ) {
 
366
        Geom::Point tang = c.unitTangentAt(0);
 
367
        double const angle = Geom::atan2(tang);
 
368
        ret = Geom::Rotate(angle) * Geom::Translate(p);
 
369
    } else {
 
370
        /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
 
371
         * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
 
372
    }
 
373
 
 
374
    return ret;
 
375
}
 
376
Geom::Matrix
 
377
sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
 
378
{
 
379
    Geom::Point p = c.pointAt(1);
 
380
    Geom::Matrix ret = Geom::Translate(p);
 
381
 
 
382
    if ( !c.isDegenerate() ) {
 
383
        Geom::Curve * c_reverse = c.reverse();
 
384
        Geom::Point tang = - c_reverse->unitTangentAt(0);
 
385
        delete c_reverse;
 
386
        double const angle = Geom::atan2(tang);
 
387
        ret = Geom::Rotate(angle) * Geom::Translate(p);
 
388
    } else {
 
389
        /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
 
390
         * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
 
391
    }
 
392
 
 
393
    return ret;
 
394
}
 
395
 
 
396
/**
 
397
 * Updates the instances (views) of a given marker in a shape.
 
398
 * Marker views have to be scaled already.  The transformation
 
399
 * is retrieved and then shown by calling sp_marker_show_instance.
 
400
 *
 
401
 * @todo figure out what to do when both 'marker' and for instance 'marker-end' are set.
 
402
 */
 
403
static void
 
404
sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
 
405
{
 
406
    SPStyle *style = ((SPObject *) shape)->style;
 
407
 
 
408
    // position arguments to sp_marker_show_instance, basically counts the amount of markers.
 
409
    int counter[4] = {0};
 
410
 
 
411
    Geom::PathVector const & pathv = shape->curve->get_pathvector();
 
412
    for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
 
413
      // START position
 
414
        Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
 
415
        for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START
 
416
            if ( shape->marker[i] ) {
 
417
                sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
 
418
                                         NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
 
419
                                         style->stroke_width.computed);
 
420
                 counter[i]++;
 
421
            }
 
422
        }
 
423
 
 
424
      // MID position
 
425
        if ( (shape->marker[SP_MARKER_LOC_MID] || shape->marker[SP_MARKER_LOC]) && (path_it->size_default() > 1) ) {
 
426
            Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
 
427
            Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
 
428
            while (curve_it2 != path_it->end_default())
 
429
            {
 
430
                /* Put marker between curve_it1 and curve_it2.
 
431
                 * Loop to end_default (so including closing segment), because when a path is closed,
 
432
                 * there should be a midpoint marker between last segment and closing straight line segment
 
433
                 */
 
434
                Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
 
435
                for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
 
436
                    if (shape->marker[i]) {
 
437
                        sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
 
438
                                                 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
 
439
                                                 style->stroke_width.computed);
 
440
                        counter[i]++;
 
441
                    }
 
442
                }
 
443
 
 
444
                ++curve_it1;
 
445
                ++curve_it2;
 
446
            }
 
447
        }
 
448
 
 
449
      // END position
 
450
        if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC] ) {
 
451
            /* Get reference to last curve in the path.
 
452
             * For moveto-only path, this returns the "closing line segment". */
 
453
            unsigned int index = path_it->size_default();
 
454
            if (index > 0) {
 
455
                index--;
 
456
            }
 
457
            Geom::Curve const &lastcurve = (*path_it)[index];
 
458
            Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
 
459
 
 
460
            for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
 
461
                if (shape->marker[i]) {
 
462
                    sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
 
463
                                             NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
 
464
                                             style->stroke_width.computed);
 
465
                    counter[i]++;
 
466
                }
 
467
            }
 
468
        }
 
469
    }
 
470
}
 
471
 
 
472
/**
 
473
 * Sets modified flag for all sub-item views.
 
474
 */
 
475
static void
 
476
sp_shape_modified (SPObject *object, unsigned int flags)
 
477
{
 
478
    SPShape *shape = SP_SHAPE (object);
 
479
 
 
480
    if (((SPObjectClass *) (parent_class))->modified) {
 
481
      (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
 
482
    }
 
483
 
 
484
    if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
 
485
        for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
 
486
            nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
 
487
        }
 
488
    }
 
489
}
 
490
 
 
491
/**
 
492
 * Calculates the bounding box for item, storing it into bbox.
 
493
 * This also includes the bounding boxes of any markers included in the shape.
 
494
 */
 
495
static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
 
496
{
 
497
    SPShape const *shape = SP_SHAPE (item);
 
498
    if (shape->curve) {
 
499
        Geom::OptRect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
 
500
        if (geombbox) {
 
501
            NRRect  cbbox;
 
502
            cbbox.x0 = (*geombbox)[0][0];
 
503
            cbbox.y0 = (*geombbox)[1][0];
 
504
            cbbox.x1 = (*geombbox)[0][1];
 
505
            cbbox.y1 = (*geombbox)[1][1];
 
506
 
 
507
            if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
 
508
 
 
509
                SPStyle* style=SP_OBJECT_STYLE (item);
 
510
                if (!style->stroke.isNone()) {
 
511
                    double const scale = transform.descrim();
 
512
                    if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
 
513
                        double const width = MAX(0.125, style->stroke_width.computed * scale);
 
514
                        if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
 
515
                            cbbox.x0-=0.5*width;
 
516
                            cbbox.x1+=0.5*width;
 
517
                            cbbox.y0-=0.5*width;
 
518
                            cbbox.y1+=0.5*width;
 
519
                        }
 
520
                    }
 
521
                }
 
522
 
 
523
                // Union with bboxes of the markers, if any
 
524
                if (sp_shape_has_markers (shape)) {
 
525
                    /* TODO: make code prettier: lots of variables can be taken out of the loop! */
 
526
                    Geom::PathVector const & pathv = shape->curve->get_pathvector();
 
527
                    for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
 
528
                        for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
 
529
                            if ( shape->marker[i] ) {
 
530
                                SPMarker* marker = SP_MARKER (shape->marker[i]);
 
531
                                SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
 
532
 
 
533
                                if (marker_item) {
 
534
                                                                        Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
 
535
                                                                        if (!marker->orient_auto) {
 
536
                                                                                Geom::Point transl = tr.translation();
 
537
                                                                                tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
 
538
                                                                        }
 
539
                                                                        if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
 
540
                                                                                tr = Geom::Scale(style->stroke_width.computed) * tr;
 
541
                                                                        }
 
542
 
 
543
                                                                        // total marker transform
 
544
                                                                        tr = marker_item->transform * marker->c2p * tr * transform;
 
545
 
 
546
                                                                        // get bbox of the marker with that transform
 
547
                                                                        NRRect marker_bbox;
 
548
                                                                        sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
 
549
                                                                        // union it with the shape bbox
 
550
                                                                        nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
 
551
                                }
 
552
                            }
 
553
                        }
 
554
 
 
555
                        for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
 
556
                            if ( shape->marker[i] && (path_it->size_default() > 1) ) {
 
557
                                Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
 
558
                                Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
 
559
                                while (curve_it2 != path_it->end_default())
 
560
                                {
 
561
                                    /* Put marker between curve_it1 and curve_it2.
 
562
                                     * Loop to end_default (so including closing segment), because when a path is closed,
 
563
                                     * there should be a midpoint marker between last segment and closing straight line segment */
 
564
 
 
565
                                    SPMarker* marker = SP_MARKER (shape->marker[i]);
 
566
                                    SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
 
567
 
 
568
                                    if (marker_item) {
 
569
                                        Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
 
570
                                                                                if (!marker->orient_auto) {
 
571
                                                                                        Geom::Point transl = tr.translation();
 
572
                                                                                        tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
 
573
                                                                                }
 
574
                                                                                if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
 
575
                                                                                        tr = Geom::Scale(style->stroke_width.computed) * tr;
 
576
                                                                                }
 
577
 
 
578
                                                                                // total marker transform
 
579
                                                                                tr = marker_item->transform * marker->c2p * tr * transform;
 
580
 
 
581
                                                                                // get bbox of the marker with that transform
 
582
                                                                                NRRect marker_bbox;
 
583
                                                                                sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
 
584
                                                                                // union it with the shape bbox
 
585
                                                                                nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
 
586
                                    }
 
587
 
 
588
                                    ++curve_it1;
 
589
                                    ++curve_it2;
 
590
                                }
 
591
                            }
 
592
                        }
 
593
 
 
594
                        for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
 
595
                            if ( shape->marker[i] ) {
 
596
                                SPMarker* marker = SP_MARKER (shape->marker[i]);
 
597
                                SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
 
598
 
 
599
                                if (marker_item) {
 
600
                                                                        /* Get reference to last curve in the path.
 
601
                                                                         * For moveto-only path, this returns the "closing line segment". */
 
602
                                                                        unsigned int index = path_it->size_default();
 
603
                                                                        if (index > 0) {
 
604
                                                                                index--;
 
605
                                                                        }
 
606
                                                                        Geom::Curve const &lastcurve = (*path_it)[index];
 
607
 
 
608
                                                                        Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
 
609
                                                                        if (!marker->orient_auto) {
 
610
                                                                                Geom::Point transl = tr.translation();
 
611
                                                                                tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
 
612
                                                                        }
 
613
                                                                        if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
 
614
                                                                                tr = Geom::Scale(style->stroke_width.computed) * tr;
 
615
                                                                        }
 
616
 
 
617
                                                                        // total marker transform
 
618
                                                                        tr = marker_item->transform * marker->c2p * tr * transform;
 
619
 
 
620
                                                                        // get bbox of the marker with that transform
 
621
                                                                        NRRect marker_bbox;
 
622
                                                                        sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
 
623
                                                                        // union it with the shape bbox
 
624
                                                                        nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
 
625
                                }
 
626
                            }
 
627
                        }
 
628
                    }
 
629
                }
 
630
            }
 
631
 
 
632
            // copy our bbox to the variable we're given
 
633
            *bbox = cbbox;
 
634
        }
 
635
    }
 
636
}
 
637
 
 
638
static void
 
639
sp_shape_print_invoke_marker_printing(SPObject* obj, Geom::Matrix tr, SPStyle* style, SPPrintContext *ctx) {
 
640
    SPMarker *marker = SP_MARKER(obj);
 
641
    if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
 
642
        tr = Geom::Scale(style->stroke_width.computed) * tr;
 
643
    }
 
644
 
 
645
    SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
 
646
    tr = marker_item->transform * marker->c2p * tr;
 
647
 
 
648
    Geom::Matrix old_tr = marker_item->transform;
 
649
    marker_item->transform = tr;
 
650
    sp_item_invoke_print (marker_item, ctx);
 
651
    marker_item->transform = old_tr;
 
652
}
 
653
/**
 
654
 * Prepares shape for printing.  Handles printing of comments for printing
 
655
 * debugging, sizes the item to fit into the document width/height,
 
656
 * applies print fill/stroke, sets transforms for markers, and adds
 
657
 * comment labels.
 
658
 */
 
659
void
 
660
sp_shape_print (SPItem *item, SPPrintContext *ctx)
 
661
{
 
662
    NRRect pbox, dbox, bbox;
 
663
 
 
664
    SPShape *shape = SP_SHAPE(item);
 
665
 
 
666
    if (!shape->curve) return;
 
667
 
 
668
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
669
        gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
 
670
        if (add_comments) {
 
671
            gchar * comment = g_strdup_printf("begin '%s'",
 
672
                                              SP_OBJECT(item)->defaultLabel());
 
673
            sp_print_comment(ctx, comment);
 
674
            g_free(comment);
 
675
        }
 
676
 
 
677
    /* fixme: Think (Lauris) */
 
678
    sp_item_invoke_bbox(item, &pbox, Geom::identity(), TRUE);
 
679
    dbox.x0 = 0.0;
 
680
    dbox.y0 = 0.0;
 
681
    dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
 
682
    dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
 
683
    sp_item_bbox_desktop (item, &bbox);
 
684
    Geom::Matrix const i2d(sp_item_i2d_affine(item));
 
685
 
 
686
    SPStyle* style = SP_OBJECT_STYLE (item);
 
687
 
 
688
    if (!style->fill.isNone()) {
 
689
        sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
 
690
    }
 
691
 
 
692
    if (!style->stroke.isNone()) {
 
693
        sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
 
694
    }
 
695
 
 
696
    /* TODO: make code prettier: lots of variables can be taken out of the loop! */
 
697
    Geom::PathVector const & pathv = shape->curve->get_pathvector();
 
698
    for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
 
699
        if ( shape->marker[SP_MARKER_LOC_START] || shape->marker[SP_MARKER_LOC]) {
 
700
            Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
 
701
            if (shape->marker[SP_MARKER_LOC_START]) {
 
702
                sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC_START], tr, style, ctx);
 
703
            }
 
704
            if (shape->marker[SP_MARKER_LOC]) {
 
705
                sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC], tr, style, ctx);
 
706
            }
 
707
        }
 
708
 
 
709
        if ( (shape->marker[SP_MARKER_LOC_MID] || shape->marker[SP_MARKER_LOC]) && (path_it->size_default() > 1) ) {
 
710
            Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
 
711
            Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
 
712
            while (curve_it2 != path_it->end_default())
 
713
            {
 
714
                /* Put marker between curve_it1 and curve_it2.
 
715
                 * Loop to end_default (so including closing segment), because when a path is closed,
 
716
                 * there should be a midpoint marker between last segment and closing straight line segment */
 
717
                Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
 
718
 
 
719
                if (shape->marker[SP_MARKER_LOC_MID]) {
 
720
                    sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC_MID], tr, style, ctx);
 
721
                }
 
722
                if (shape->marker[SP_MARKER_LOC]) {
 
723
                    sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC], tr, style, ctx);
 
724
                }
 
725
 
 
726
                ++curve_it1;
 
727
                ++curve_it2;
 
728
            }
 
729
        }
 
730
 
 
731
        if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC]) {
 
732
            /* Get reference to last curve in the path.
 
733
             * For moveto-only path, this returns the "closing line segment". */
 
734
            unsigned int index = path_it->size_default();
 
735
            if (index > 0) {
 
736
                index--;
 
737
            }
 
738
            Geom::Curve const &lastcurve = (*path_it)[index];
 
739
 
 
740
            Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
 
741
 
 
742
            if (shape->marker[SP_MARKER_LOC_END]) {
 
743
                sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC_END], tr, style, ctx);
 
744
            }
 
745
            if (shape->marker[SP_MARKER_LOC]) {
 
746
                sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC], tr, style, ctx);
 
747
            }
 
748
        }
 
749
    }
 
750
 
 
751
        if (add_comments) {
 
752
            gchar * comment = g_strdup_printf("end '%s'",
 
753
                                              SP_OBJECT(item)->defaultLabel());
 
754
            sp_print_comment(ctx, comment);
 
755
            g_free(comment);
 
756
        }
 
757
}
 
758
 
 
759
/**
 
760
 * Sets style, path, and paintbox.  Updates marker views, including dimensions.
 
761
 */
 
762
static NRArenaItem *
 
763
sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
 
764
{
 
765
    SPObject *object = SP_OBJECT(item);
 
766
    SPShape *shape = SP_SHAPE(item);
 
767
 
 
768
    NRArenaItem *arenaitem = NRArenaShape::create(arena);
 
769
    NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
 
770
    nr_arena_shape_set_style(s, object->style);
 
771
    nr_arena_shape_set_path(s, shape->curve, false);
 
772
    Geom::OptRect paintbox = item->getBounds(Geom::identity());
 
773
    if (paintbox) {
 
774
        s->setPaintBox(*paintbox);
 
775
    }
 
776
 
 
777
    /* This stanza checks that an object's marker style agrees with
 
778
     * the marker objects it has allocated.  sp_shape_set_marker ensures
 
779
     * that the appropriate marker objects are present (or absent) to
 
780
     * match the style.
 
781
     */
 
782
    for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
 
783
        sp_shape_set_marker (object, i, object->style->marker[i].value);
 
784
      }
 
785
 
 
786
    if (sp_shape_has_markers (shape)) {
 
787
 
 
788
        /* provide key and dimension the marker views */
 
789
        if (!arenaitem->key) {
 
790
            NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
 
791
        }
 
792
 
 
793
        for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
 
794
            if (shape->marker[i]) {
 
795
                sp_marker_show_dimension ((SPMarker *) shape->marker[i],
 
796
                                          NR_ARENA_ITEM_GET_KEY (arenaitem) + i,
 
797
                                          sp_shape_number_of_markers (shape, i));
 
798
            }
 
799
        }
 
800
 
 
801
        /* Update marker views */
 
802
        sp_shape_update_marker_view (shape, arenaitem);
 
803
    }
 
804
 
 
805
    return arenaitem;
 
806
}
 
807
 
 
808
/**
 
809
 * Hides/removes marker views from the shape.
 
810
 */
 
811
static void
 
812
sp_shape_hide (SPItem *item, unsigned int key)
 
813
{
 
814
    SPShape *shape;
 
815
    SPItemView *v;
 
816
    int i;
 
817
 
 
818
    shape = (SPShape *) item;
 
819
 
 
820
    for (i=0; i<SP_MARKER_LOC_QTY; i++) {
 
821
      if (shape->marker[i]) {
 
822
        for (v = item->display; v != NULL; v = v->next) {
 
823
                if (key == v->key) {
 
824
          sp_marker_hide ((SPMarker *) shape->marker[i],
 
825
                                    NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
 
826
                }
 
827
        }
 
828
      }
 
829
    }
 
830
 
 
831
    if (((SPItemClass *) parent_class)->hide) {
 
832
      ((SPItemClass *) parent_class)->hide (item, key);
 
833
    }
 
834
}
 
835
 
 
836
/**
 
837
* \param shape Shape.
 
838
* \return TRUE if the shape has any markers, or FALSE if not.
 
839
*/
 
840
int
 
841
sp_shape_has_markers (SPShape const *shape)
 
842
{
 
843
    /* Note, we're ignoring 'marker' settings, which technically should apply for
 
844
       all three settings.  This should be fixed later such that if 'marker' is
 
845
       specified, then all three should appear. */
 
846
 
 
847
    return (
 
848
        shape->curve &&
 
849
        (shape->marker[SP_MARKER_LOC] ||
 
850
         shape->marker[SP_MARKER_LOC_START] ||
 
851
         shape->marker[SP_MARKER_LOC_MID] ||
 
852
         shape->marker[SP_MARKER_LOC_END])
 
853
        );
 
854
}
 
855
 
 
856
 
 
857
/**
 
858
* \param shape Shape.
 
859
* \param type Marker type (e.g. SP_MARKER_LOC_START)
 
860
* \return Number of markers that the shape has of this type.
 
861
*/
 
862
int
 
863
sp_shape_number_of_markers (SPShape *shape, int type)
 
864
{
 
865
    Geom::PathVector const & pathv = shape->curve->get_pathvector();
 
866
 
 
867
    switch(type) {
 
868
        case SP_MARKER_LOC:
 
869
        {
 
870
            if ( shape->marker[SP_MARKER_LOC] ) {
 
871
                guint n = 2*pathv.size();
 
872
                for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
 
873
                    n += path_it->size();
 
874
                    n += path_it->closed() ? 1 : 0;
 
875
                }
 
876
                return n;
 
877
            } else {
 
878
                return 0;
 
879
            }
 
880
        }
 
881
        case SP_MARKER_LOC_START:
 
882
            return shape->marker[SP_MARKER_LOC_START] ? pathv.size() : 0;
 
883
 
 
884
        case SP_MARKER_LOC_MID:
 
885
        {
 
886
            if ( shape->marker[SP_MARKER_LOC_MID] ) {
 
887
            guint n = 0;
 
888
                for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
 
889
                    n += path_it->size();
 
890
                    n += path_it->closed() ? 1 : 0;
 
891
                }
 
892
                return n;
 
893
            } else {
 
894
                return 0;
 
895
            }
 
896
        }
 
897
 
 
898
        case SP_MARKER_LOC_END:
 
899
        {
 
900
            return shape->marker[SP_MARKER_LOC_END] ? pathv.size() : 0;
 
901
        }
 
902
 
 
903
        default:
 
904
            return 0;
 
905
    }
 
906
}
 
907
 
 
908
/**
 
909
 * Checks if the given marker is used in the shape, and if so, it
 
910
 * releases it by calling sp_marker_hide.  Also detaches signals
 
911
 * and unrefs the marker from the shape.
 
912
 */
 
913
static void
 
914
sp_shape_marker_release (SPObject *marker, SPShape *shape)
 
915
{
 
916
    SPItem *item;
 
917
    int i;
 
918
 
 
919
    item = (SPItem *) shape;
 
920
 
 
921
    for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
 
922
        if (marker == shape->marker[i]) {
 
923
            SPItemView *v;
 
924
            /* Hide marker */
 
925
            for (v = item->display; v != NULL; v = v->next) {
 
926
              sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
 
927
              /* fixme: Do we need explicit remove here? (Lauris) */
 
928
              /* nr_arena_item_set_mask (v->arenaitem, NULL); */
 
929
            }
 
930
            /* Detach marker */
 
931
            shape->release_connect[i].disconnect();
 
932
            shape->modified_connect[i].disconnect();
 
933
            shape->marker[i] = sp_object_hunref (shape->marker[i], item);
 
934
        }
 
935
    }
 
936
}
 
937
 
 
938
/**
 
939
 * No-op.  Exists for handling 'modified' messages
 
940
 */
 
941
static void
 
942
sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
 
943
{
 
944
    /* I think mask does update automagically */
 
945
    /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
 
946
}
 
947
 
 
948
/**
 
949
 * Adds a new marker to shape object at the location indicated by key.  value
 
950
 * must be a valid URI reference resolvable from the shape object (i.e., present
 
951
 * in the document <defs>).  If the shape object already has a marker
 
952
 * registered at the given position, it is removed first.  Then the
 
953
 * new marker is hrefed and its signals connected.
 
954
 */
 
955
void
 
956
sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
 
957
{
 
958
    SPItem *item = (SPItem *) object;
 
959
    SPShape *shape = (SPShape *) object;
 
960
 
 
961
    if (key > SP_MARKER_LOC_END) {
 
962
        return;
 
963
    }
 
964
 
 
965
    SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
 
966
    if (mrk != shape->marker[key]) {
 
967
        if (shape->marker[key]) {
 
968
            SPItemView *v;
 
969
 
 
970
            /* Detach marker */
 
971
            shape->release_connect[key].disconnect();
 
972
            shape->modified_connect[key].disconnect();
 
973
 
 
974
            /* Hide marker */
 
975
            for (v = item->display; v != NULL; v = v->next) {
 
976
                sp_marker_hide ((SPMarker *) (shape->marker[key]),
 
977
                                NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
 
978
                /* fixme: Do we need explicit remove here? (Lauris) */
 
979
                /* nr_arena_item_set_mask (v->arenaitem, NULL); */
 
980
            }
 
981
 
 
982
            /* Unref marker */
 
983
            shape->marker[key] = sp_object_hunref (shape->marker[key], object);
 
984
        }
 
985
        if (SP_IS_MARKER (mrk)) {
 
986
            shape->marker[key] = sp_object_href (mrk, object);
 
987
            shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
 
988
            shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
 
989
        }
 
990
    }
 
991
}
 
992
 
 
993
 
 
994
 
 
995
/* Shape section */
 
996
 
 
997
/**
 
998
 * Calls any registered handlers for the set_shape action
 
999
 */
 
1000
void
 
1001
sp_shape_set_shape (SPShape *shape)
 
1002
{
 
1003
    g_return_if_fail (shape != NULL);
 
1004
    g_return_if_fail (SP_IS_SHAPE (shape));
 
1005
 
 
1006
    if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
 
1007
      SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
 
1008
    }
 
1009
}
 
1010
 
 
1011
/**
 
1012
 * Adds a curve to the shape.  If owner is specified, a reference
 
1013
 * will be made, otherwise the curve will be copied into the shape.
 
1014
 * Any existing curve in the shape will be unreferenced first.
 
1015
 * This routine also triggers a request to update the display.
 
1016
 */
 
1017
void
 
1018
sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
 
1019
{
 
1020
    if (shape->curve) {
 
1021
        shape->curve = shape->curve->unref();
 
1022
    }
 
1023
    if (curve) {
 
1024
        if (owner) {
 
1025
            shape->curve = curve->ref();
 
1026
        } else {
 
1027
            shape->curve = curve->copy();
 
1028
        }
 
1029
    }
 
1030
        SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
1031
}
 
1032
 
 
1033
/**
 
1034
 * Return duplicate of curve (if any exists) or NULL if there is no curve
 
1035
 */
 
1036
SPCurve *
 
1037
sp_shape_get_curve (SPShape *shape)
 
1038
{
 
1039
    if (shape->curve) {
 
1040
        return shape->curve->copy();
 
1041
    }
 
1042
    return NULL;
 
1043
}
 
1044
 
 
1045
/**
 
1046
 * Same as sp_shape_set_curve but without updating the display
 
1047
 */
 
1048
void
 
1049
sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
 
1050
{
 
1051
    if (shape->curve) {
 
1052
        shape->curve = shape->curve->unref();
 
1053
    }
 
1054
    if (curve) {
 
1055
        if (owner) {
 
1056
            shape->curve = curve->ref();
 
1057
        } else {
 
1058
            shape->curve = curve->copy();
 
1059
        }
 
1060
    }
 
1061
}
 
1062
 
 
1063
/**
 
1064
 * Return all nodes in a path that are to be considered for snapping
 
1065
 */
 
1066
static void sp_shape_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs)
 
1067
{
 
1068
    g_assert(item != NULL);
 
1069
    g_assert(SP_IS_SHAPE(item));
 
1070
 
 
1071
    SPShape const *shape = SP_SHAPE(item);
 
1072
    if (shape->curve == NULL) {
 
1073
        return;
 
1074
    }
 
1075
 
 
1076
    // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
 
1077
    if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
 
1078
        return;
 
1079
    }
 
1080
 
 
1081
    Geom::PathVector const &pathv = shape->curve->get_pathvector();
 
1082
    if (pathv.empty())
 
1083
        return;
 
1084
 
 
1085
    Geom::Matrix const i2d (sp_item_i2d_affine (item));
 
1086
 
 
1087
    int type;
 
1088
 
 
1089
        if (snapprefs->getSnapObjectMidpoints()) {
 
1090
                Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item));
 
1091
                if (bbox) {
 
1092
                        type = target ? int(Inkscape::SNAPTARGET_OBJECT_MIDPOINT) : int(Inkscape::SNAPSOURCE_OBJECT_MIDPOINT);
 
1093
                        p.push_back(std::make_pair(bbox->midpoint(), type));
 
1094
                }
 
1095
        }
 
1096
 
 
1097
    for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
 
1098
        if (snapprefs->getSnapToItemNode()) {
 
1099
                type = target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP);
 
1100
                p.push_back(std::make_pair(path_it->initialPoint() * i2d, type));
 
1101
        }
 
1102
 
 
1103
        Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
 
1104
        Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
 
1105
        while (curve_it2 != path_it->end_closed())
 
1106
        {
 
1107
            /* Test whether to add the node between curve_it1 and curve_it2.
 
1108
             * Loop to end_closed (so always including closing segment); the last node to be added
 
1109
             * is the node between the closing segment and the segment before that, regardless
 
1110
             * of the path being closed or not. If the path is closed, the final point was already added by
 
1111
             * adding the initial point. */
 
1112
 
 
1113
            Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
 
1114
 
 
1115
            bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
 
1116
            bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
 
1117
 
 
1118
            if (c1 || c2) {
 
1119
                type = target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP);
 
1120
                                p.push_back(std::make_pair(curve_it1->finalPoint() * i2d, type));
 
1121
            }
 
1122
 
 
1123
                        // Consider midpoints of line segments for snapping
 
1124
                        if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping)
 
1125
                                if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
 
1126
                                        type = target ? int(Inkscape::SNAPTARGET_LINE_MIDPOINT) : int(Inkscape::SNAPSOURCE_LINE_MIDPOINT);
 
1127
                                        p.push_back(std::make_pair(Geom::middle_point(*line_segment) * i2d, type));
 
1128
                                }
 
1129
                        }
 
1130
 
 
1131
            ++curve_it1;
 
1132
            ++curve_it2;
 
1133
        }
 
1134
 
 
1135
        // Find the internal intersections of each path and consider these for snapping
 
1136
        // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes())
 
1137
        if (snapprefs->getSnapIntersectionCS()) {
 
1138
            Geom::Crossings cs;
 
1139
            cs = self_crossings(*path_it);
 
1140
            if (cs.size() > 0) { // There might be multiple intersections...
 
1141
                for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
 
1142
                    Geom::Point p_ix = (*path_it).pointAt((*i).ta);
 
1143
                    type = target ? int(Inkscape::SNAPTARGET_PATH_INTERSECTION) : int(Inkscape::SNAPSOURCE_PATH_INTERSECTION);
 
1144
                    p.push_back(std::make_pair(p_ix * i2d, type));
 
1145
                }
 
1146
            }
 
1147
        }
 
1148
    }
 
1149
 
 
1150
}
 
1151
 
 
1152
/*
 
1153
  Local Variables:
 
1154
  mode:c++
 
1155
  c-file-style:"stroustrup"
 
1156
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
1157
  indent-tabs-mode:nil
 
1158
  fill-column:99
 
1159
  End:
 
1160
*/
 
1161
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :