~vaifrax/inkscape/bugfix170049

« back to all changes in this revision

Viewing changes to src/sp-offset.cpp

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_OFFSET_C__
 
2
 
 
3
/** \file
 
4
 * Implementation of <path sodipodi:type="inkscape:offset">.
 
5
 */
 
6
 
 
7
/*
 
8
 * Authors: (of the sp-spiral.c upon which this file was constructed):
 
9
 *   Mitsuru Oka <oka326@parkcity.ne.jp>
 
10
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
11
 *
 
12
 * Copyright (C) 1999-2002 Lauris Kaplinski
 
13
 * Copyright (C) 2000-2001 Ximian, Inc.
 
14
 *
 
15
 * Released under GNU GPL, read the file 'COPYING' for more information
 
16
 */
 
17
 
 
18
#ifdef HAVE_CONFIG_H
 
19
# include "config.h"
 
20
#endif
 
21
 
 
22
 
 
23
#include "svg/svg.h"
 
24
#include "attributes.h"
 
25
#include "display/curve.h"
 
26
#include <glibmm/i18n.h>
 
27
 
 
28
#include "livarot/Path.h"
 
29
#include "livarot/Shape.h"
 
30
 
 
31
#include "enums.h"
 
32
#include "prefs-utils.h"
 
33
#include "sp-text.h"
 
34
#include "sp-offset.h"
 
35
#include "sp-use-reference.h"
 
36
#include "uri.h"
 
37
 
 
38
#include "libnr/n-art-bpath.h"
 
39
#include <libnr/nr-matrix-fns.h>
 
40
 
 
41
#include "xml/repr.h"
 
42
 
 
43
class SPDocument;
 
44
 
 
45
#define noOFFSET_VERBOSE
 
46
 
 
47
/** \note
 
48
 * SPOffset is a derivative of SPShape, much like the SPSpiral or SPRect.
 
49
 * The goal is to have a source shape (= originalPath), an offset (= radius) 
 
50
 * and compute the offset of the source by the radius. To get it to work, 
 
51
 * one needs to know what the source is and what the radius is, and how it's 
 
52
 * stored in the xml representation. The object itself is a "path" element, 
 
53
 * to get lots of shape functionality for free. The source is the easy part: 
 
54
 * it's stored in a "inkscape:original" attribute in the path. In case of 
 
55
 * "linked" offset, as they've been dubbed, there is an additional
 
56
 * "inkscape:href" that contains the id of an element of the svg. 
 
57
 * When built, the object will attach a listener vector to that object and 
 
58
 * rebuild the "inkscape:original" whenever the href'd object changes. This 
 
59
 * is of course grossly inefficient, and also does not react to changes 
 
60
 * to the href'd during context stuff (like changing the shape of a star by 
 
61
 * dragging control points) unless the path of that object is changed during 
 
62
 * the context (seems to be the case for SPEllipse). The computation of the 
 
63
 * offset is done in sp_offset_set_shape(), a function that is called whenever 
 
64
 * a change occurs to the offset (change of source or change of radius).
 
65
 * just like the sp-star and other, this path derivative can make control 
 
66
 * points, or more precisely one control point, that's enough to define the 
 
67
 * radius (look in object-edit).
 
68
 */
 
69
 
 
70
static void sp_offset_class_init (SPOffsetClass * klass);
 
71
static void sp_offset_init (SPOffset * offset);
 
72
static void sp_offset_finalize(GObject *obj);
 
73
 
 
74
static void sp_offset_build (SPObject * object, SPDocument * document,
 
75
                             Inkscape::XML::Node * repr);
 
76
static Inkscape::XML::Node *sp_offset_write (SPObject * object, Inkscape::XML::Node * repr,
 
77
                                guint flags);
 
78
static void sp_offset_set (SPObject * object, unsigned int key,
 
79
                           const gchar * value);
 
80
static void sp_offset_update (SPObject * object, SPCtx * ctx, guint flags);
 
81
static void sp_offset_release (SPObject * object);
 
82
 
 
83
static gchar *sp_offset_description (SPItem * item);
 
84
static void sp_offset_snappoints(SPItem const *item, SnapPointsIter p);
 
85
static void sp_offset_set_shape (SPShape * shape);
 
86
 
 
87
Path *bpath_to_liv_path (NArtBpath * bpath);
 
88
 
 
89
static void refresh_offset_source(SPOffset* offset);
 
90
 
 
91
static void sp_offset_start_listening(SPOffset *offset,SPObject* to);
 
92
static void sp_offset_quit_listening(SPOffset *offset);
 
93
static void sp_offset_href_changed(SPObject *old_ref, SPObject *ref, SPOffset *offset);
 
94
static void sp_offset_move_compensate(NR::Matrix const *mp, SPItem *original, SPOffset *self);
 
95
static void sp_offset_delete_self(SPObject *deleted, SPOffset *self);
 
96
static void sp_offset_source_modified (SPObject *iSource, guint flags, SPItem *item);
 
97
 
 
98
 
 
99
// slow= source path->polygon->offset of polygon->polygon->path
 
100
// fast= source path->offset of source path->polygon->path
 
101
// fast is not mathematically correct, because computing the offset of a single
 
102
// cubic bezier patch is not trivial; in particular, there are problems with holes 
 
103
// reappearing in offset when the radius becomes too large
 
104
static bool   use_slow_but_correct_offset_method=false;
 
105
 
 
106
 
 
107
// nothing special here, same for every class in sodipodi/inkscape
 
108
static SPShapeClass *parent_class;
 
109
 
 
110
/**
 
111
 * Register SPOffset class and return its type number.
 
112
 */
 
113
GType
 
114
sp_offset_get_type (void)
 
115
{
 
116
    static GType offset_type = 0;
 
117
  
 
118
    if (!offset_type)
 
119
    {
 
120
        GTypeInfo offset_info = {
 
121
            sizeof (SPOffsetClass),
 
122
            NULL,                       /* base_init */
 
123
            NULL,                       /* base_finalize */
 
124
            (GClassInitFunc) sp_offset_class_init,
 
125
            NULL,                       /* class_finalize */
 
126
            NULL,                       /* class_data */
 
127
            sizeof (SPOffset),
 
128
            16,                 /* n_preallocs */
 
129
            (GInstanceInitFunc) sp_offset_init,
 
130
            NULL,                       /* value_table */
 
131
        };
 
132
        offset_type =
 
133
            g_type_register_static (SP_TYPE_SHAPE, "SPOffset", &offset_info,
 
134
                                    (GTypeFlags) 0);
 
135
    }
 
136
    return offset_type;
 
137
}
 
138
 
 
139
/**
 
140
 * SPOffset vtable initialization.
 
141
 */
 
142
static void
 
143
sp_offset_class_init(SPOffsetClass *klass)
 
144
{
 
145
    GObjectClass  *gobject_class = (GObjectClass *) klass;
 
146
    SPObjectClass *sp_object_class = (SPObjectClass *) klass;
 
147
    SPItemClass   *item_class = (SPItemClass *) klass;
 
148
    SPShapeClass  *shape_class = (SPShapeClass *) klass;
 
149
  
 
150
    parent_class = (SPShapeClass *) g_type_class_ref (SP_TYPE_SHAPE);
 
151
  
 
152
    gobject_class->finalize = sp_offset_finalize;
 
153
 
 
154
    sp_object_class->build = sp_offset_build;
 
155
    sp_object_class->write = sp_offset_write;
 
156
    sp_object_class->set = sp_offset_set;
 
157
    sp_object_class->update = sp_offset_update;
 
158
    sp_object_class->release = sp_offset_release;
 
159
  
 
160
    item_class->description = sp_offset_description;
 
161
    item_class->snappoints = sp_offset_snappoints;
 
162
  
 
163
    shape_class->set_shape = sp_offset_set_shape;
 
164
}
 
165
 
 
166
/**
 
167
 * Callback for SPOffset object initialization.
 
168
 */
 
169
static void
 
170
sp_offset_init(SPOffset *offset)
 
171
{
 
172
    offset->rad = 1.0;
 
173
    offset->original = NULL;
 
174
    offset->originalPath = NULL;
 
175
    offset->knotSet = false;
 
176
    offset->sourceDirty=false;
 
177
    offset->isUpdating=false;
 
178
    // init various connections
 
179
    offset->sourceHref = NULL;
 
180
    offset->sourceRepr = NULL;
 
181
    offset->sourceObject = NULL;
 
182
    new (&offset->_delete_connection) sigc::connection();
 
183
    new (&offset->_changed_connection) sigc::connection();
 
184
    new (&offset->_transformed_connection) sigc::connection();
 
185
    // set up the uri reference
 
186
    offset->sourceRef = new SPUseReference(SP_OBJECT(offset));
 
187
    offset->_changed_connection = offset->sourceRef->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_offset_href_changed), offset));
 
188
}
 
189
 
 
190
/**
 
191
 * Callback for SPOffset finalization.
 
192
 */
 
193
static void
 
194
sp_offset_finalize(GObject *obj)
 
195
{
 
196
    SPOffset *offset = (SPOffset *) obj;
 
197
        
 
198
    delete offset->sourceRef;
 
199
    offset->_delete_connection.~connection();
 
200
    offset->_changed_connection.~connection();
 
201
    offset->_transformed_connection.~connection();
 
202
}
 
203
 
 
204
/**
 
205
 * Virtual build: set offset attributes from corresponding repr.
 
206
 */
 
207
static void
 
208
sp_offset_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
 
209
{
 
210
    if (((SPObjectClass *) parent_class)->build)
 
211
        ((SPObjectClass *) parent_class)->build (object, document, repr);
 
212
  
 
213
    if (object->repr->attribute("inkscape:radius")) {
 
214
        sp_object_read_attr (object, "inkscape:radius");
 
215
    } else {
 
216
        gchar const *oldA = object->repr->attribute("sodipodi:radius");
 
217
        object->repr->setAttribute("inkscape:radius",oldA);
 
218
        object->repr->setAttribute("sodipodi:radius",NULL);
 
219
    
 
220
        sp_object_read_attr (object, "inkscape:radius");
 
221
    }
 
222
    if (object->repr->attribute("inkscape:original")) {
 
223
        sp_object_read_attr (object, "inkscape:original");
 
224
    } else {    
 
225
        gchar const *oldA = object->repr->attribute("sodipodi:original");
 
226
        object->repr->setAttribute("inkscape:original",oldA);
 
227
        object->repr->setAttribute("sodipodi:original",NULL);
 
228
 
 
229
        sp_object_read_attr (object, "inkscape:original");
 
230
    }
 
231
    if (object->repr->attribute("xlink:href")) {
 
232
        sp_object_read_attr(object, "xlink:href");
 
233
    } else {
 
234
        gchar const *oldA = object->repr->attribute("inkscape:href");
 
235
        if (oldA) {
 
236
            size_t lA = strlen(oldA);
 
237
            char *nA=(char*)malloc((lA+1)*sizeof(char));
 
238
            memcpy(nA+1,oldA,lA*sizeof(char));
 
239
            nA[0]='#';
 
240
            nA[lA+1]=0;
 
241
            object->repr->setAttribute("xlink:href",nA);
 
242
            free(nA);
 
243
            object->repr->setAttribute("inkscape:href",NULL);
 
244
        }
 
245
        sp_object_read_attr (object, "xlink:href");
 
246
    }
 
247
}
 
248
 
 
249
/**
 
250
 * Virtual write: write offset attributes to corresponding repr.
 
251
 */
 
252
static Inkscape::XML::Node *
 
253
sp_offset_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
 
254
{
 
255
    SPOffset *offset = SP_OFFSET (object);
 
256
  
 
257
    if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
 
258
        repr = sp_repr_new ("svg:path");
 
259
    }
 
260
  
 
261
    if (flags & SP_OBJECT_WRITE_EXT) {
 
262
        /** \todo
 
263
         * Fixme: we may replace these attributes by
 
264
         * inkscape:offset="cx cy exp revo rad arg t0"
 
265
         */
 
266
        repr->setAttribute("sodipodi:type", "inkscape:offset");
 
267
        sp_repr_set_svg_double(repr, "inkscape:radius", offset->rad);
 
268
        repr->setAttribute("inkscape:original", offset->original);
 
269
        repr->setAttribute("inkscape:href", offset->sourceHref);
 
270
    }
 
271
 
 
272
 
 
273
    // Make sure the object has curve 
 
274
    SPCurve *curve = sp_shape_get_curve (SP_SHAPE (offset));
 
275
    if (curve == NULL) {
 
276
        sp_offset_set_shape (SP_SHAPE (offset));
 
277
    }
 
278
 
 
279
    // write that curve to "d"
 
280
    char *d = sp_svg_write_path (((SPShape *) offset)->curve->bpath);
 
281
    repr->setAttribute("d", d);
 
282
    g_free (d);
 
283
  
 
284
    if (((SPObjectClass *) (parent_class))->write)
 
285
        ((SPObjectClass *) (parent_class))->write (object, repr,
 
286
                                                   flags | SP_SHAPE_WRITE_PATH);
 
287
  
 
288
    return repr;
 
289
}
 
290
 
 
291
/**
 
292
 * Virtual release callback.
 
293
 */
 
294
static void
 
295
sp_offset_release(SPObject *object)
 
296
{
 
297
    SPOffset *offset = (SPOffset *) object;
 
298
  
 
299
    if (offset->original) free (offset->original);
 
300
    if (offset->originalPath) delete ((Path *) offset->originalPath);
 
301
    offset->original = NULL;
 
302
    offset->originalPath = NULL;
 
303
  
 
304
    sp_offset_quit_listening(offset);
 
305
        
 
306
    offset->_changed_connection.disconnect();
 
307
    g_free(offset->sourceHref);
 
308
    offset->sourceHref = NULL;
 
309
    offset->sourceRef->detach();
 
310
    
 
311
    if (((SPObjectClass *) parent_class)->release) {
 
312
        ((SPObjectClass *) parent_class)->release (object);
 
313
    }
 
314
  
 
315
}
 
316
 
 
317
/**
 
318
 * Set callback: the function that is called whenever a change is made to 
 
319
 * the description of the object.
 
320
 */
 
321
static void
 
322
sp_offset_set(SPObject *object, unsigned key, gchar const *value)
 
323
{
 
324
    SPOffset *offset = SP_OFFSET (object);
 
325
  
 
326
    if ( offset->sourceDirty ) refresh_offset_source(offset);
 
327
  
 
328
    /* fixme: we should really collect updates */
 
329
    switch (key)
 
330
    {
 
331
        case SP_ATTR_INKSCAPE_ORIGINAL:
 
332
        case SP_ATTR_SODIPODI_ORIGINAL:
 
333
            if (value == NULL) {
 
334
            } else {
 
335
                if (offset->original) {
 
336
                    free (offset->original);
 
337
                    delete ((Path *) offset->originalPath);
 
338
                    offset->original = NULL;
 
339
                    offset->originalPath = NULL;
 
340
                }
 
341
                NArtBpath *bpath;
 
342
                SPCurve *curve;
 
343
        
 
344
                offset->original = strdup (value);
 
345
        
 
346
                bpath = sp_svg_read_path (offset->original);
 
347
                curve = sp_curve_new_from_bpath (bpath);        // curve se chargera de detruire bpath
 
348
                g_assert (curve != NULL);
 
349
                offset->originalPath = bpath_to_liv_path (curve->bpath);
 
350
                sp_curve_unref (curve);
 
351
        
 
352
                offset->knotSet = false;
 
353
                if ( offset->isUpdating == false ) object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
354
            }
 
355
            break;
 
356
        case SP_ATTR_INKSCAPE_RADIUS:
 
357
        case SP_ATTR_SODIPODI_RADIUS:
 
358
            if (!sp_svg_length_read_computed_absolute (value, &offset->rad)) {
 
359
                if (fabs (offset->rad) < 0.01)
 
360
                    offset->rad = (offset->rad < 0) ? -0.01 : 0.01;
 
361
                offset->knotSet = false; // knotset=false because it's not set from the context
 
362
            }
 
363
            if ( offset->isUpdating == false ) object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
364
            break;
 
365
        case SP_ATTR_INKSCAPE_HREF:
 
366
        case SP_ATTR_XLINK_HREF:
 
367
            if ( value == NULL ) {
 
368
                sp_offset_quit_listening(offset);
 
369
                if ( offset->sourceHref ) g_free(offset->sourceHref);
 
370
                offset->sourceHref = NULL;
 
371
                offset->sourceRef->detach();
 
372
            } else {
 
373
                if ( offset->sourceHref && ( strcmp(value, offset->sourceHref) == 0 ) ) {
 
374
                } else {
 
375
                    if ( offset->sourceHref ) g_free(offset->sourceHref);
 
376
                    offset->sourceHref = g_strdup(value);
 
377
                    try {
 
378
                        offset->sourceRef->attach(Inkscape::URI(value));
 
379
                    } catch (Inkscape::BadURIException &e) {
 
380
                        g_warning("%s", e.what());
 
381
                        offset->sourceRef->detach();
 
382
                    }
 
383
                }
 
384
            }
 
385
            break;
 
386
        default:
 
387
            if (((SPObjectClass *) parent_class)->set)
 
388
                ((SPObjectClass *) parent_class)->set (object, key, value);
 
389
            break;
 
390
    }
 
391
}
 
392
 
 
393
/**
 
394
 * Update callback: the object has changed, recompute its shape.
 
395
 */
 
396
static void
 
397
sp_offset_update(SPObject *object, SPCtx *ctx, guint flags)
 
398
{
 
399
    SPOffset* offset = SP_OFFSET(object);
 
400
    offset->isUpdating=true; // prevent sp_offset_set from requesting updates
 
401
    if ( offset->sourceDirty ) refresh_offset_source(offset);
 
402
    if (flags &
 
403
        (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
 
404
         SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
 
405
        sp_shape_set_shape ((SPShape *) object);
 
406
    }
 
407
    offset->isUpdating=false;
 
408
  
 
409
    if (((SPObjectClass *) parent_class)->update)
 
410
        ((SPObjectClass *) parent_class)->update (object, ctx, flags);
 
411
}
 
412
 
 
413
/**
 
414
 * Returns a textual description of object.
 
415
 */
 
416
static gchar *
 
417
sp_offset_description(SPItem *item)
 
418
{
 
419
    SPOffset *offset = SP_OFFSET (item);
 
420
 
 
421
    if ( offset->sourceHref ) {
 
422
        // TRANSLATORS COMMENT: %s is either "outset" or "inset" depending on sign
 
423
        return g_strdup_printf(_("<b>Linked offset</b>, %s by %f pt"),
 
424
                               (offset->rad >= 0)? _("outset") : _("inset"), fabs (offset->rad));
 
425
    } else {
 
426
        // TRANSLATORS COMMENT: %s is either "outset" or "inset" depending on sign
 
427
        return g_strdup_printf(_("<b>Dynamic offset</b>, %s by %f pt"),
 
428
                               (offset->rad >= 0)? _("outset") : _("inset"), fabs (offset->rad));
 
429
    }
 
430
}
 
431
 
 
432
/**
 
433
 * Converts an NArtBpath (like the one stored in a SPCurve) into a 
 
434
 * livarot Path. Duplicate of splivarot.
 
435
 */
 
436
Path *
 
437
bpath_to_liv_path(NArtBpath *bpath)
 
438
{
 
439
    if (bpath == NULL)
 
440
        return NULL;
 
441
  
 
442
    Path *dest = new Path;
 
443
    dest->SetBackData (false);
 
444
    {
 
445
        int i;
 
446
        bool closed = false;
 
447
        float lastX = 0.0;
 
448
        float lastY = 0.0;
 
449
    
 
450
        for (i = 0; bpath[i].code != NR_END; i++)
 
451
        {
 
452
            switch (bpath[i].code)
 
453
            {
 
454
                case NR_LINETO:
 
455
                    lastX = bpath[i].x3;
 
456
                    lastY = bpath[i].y3;
 
457
                    {
 
458
                        NR::Point  tmp(lastX,lastY);
 
459
                        dest->LineTo (tmp);
 
460
                    }
 
461
                    break;
 
462
          
 
463
                case NR_CURVETO:
 
464
                {
 
465
                    NR::Point  tmp(bpath[i].x3, bpath[i].y3);
 
466
                    NR::Point  tms;
 
467
                    tms[0]=3 * (bpath[i].x1 - lastX);
 
468
                    tms[1]=3 * (bpath[i].y1 - lastY);
 
469
                    NR::Point  tme;
 
470
                    tme[0]=3 * (bpath[i].x3 - bpath[i].x2);
 
471
                    tme[1]= 3 * (bpath[i].y3 - bpath[i].y2);
 
472
                    dest->CubicTo (tmp,tms,tme);
 
473
                }
 
474
                lastX = bpath[i].x3;
 
475
                lastY = bpath[i].y3;
 
476
                break;
 
477
          
 
478
                case NR_MOVETO_OPEN:
 
479
                case NR_MOVETO:
 
480
                    if (closed)
 
481
                        dest->Close ();
 
482
                    closed = (bpath[i].code == NR_MOVETO);
 
483
                    lastX = bpath[i].x3;
 
484
                    lastY = bpath[i].y3;
 
485
                    {
 
486
                        NR::Point tmp(lastX,lastY);
 
487
                        dest->MoveTo(tmp);
 
488
                    }
 
489
                    break;
 
490
                default:
 
491
                    break;
 
492
            }
 
493
        }
 
494
        if (closed)
 
495
            dest->Close ();
 
496
    }
 
497
  
 
498
    return dest;
 
499
}
 
500
 
 
501
/**
 
502
 * Compute and set shape's offset.
 
503
 */
 
504
static void
 
505
sp_offset_set_shape(SPShape *shape)
 
506
{
 
507
    SPOffset *offset = SP_OFFSET (shape);
 
508
  
 
509
    if ( offset->originalPath == NULL ) {
 
510
        // oops : no path?! (the offset object should do harakiri)
 
511
        return;
 
512
    }
 
513
#ifdef OFFSET_VERBOSE
 
514
    g_print ("rad=%g\n", offset->rad);
 
515
#endif
 
516
    // au boulot
 
517
  
 
518
    if ( fabs(offset->rad) < 0.01 ) {
 
519
        // grosso modo: 0
 
520
        // just put the source shape as the offseted one, no one will notice
 
521
        // it's also useless to compute the offset with a 0 radius
 
522
    
 
523
        const char *res_d = SP_OBJECT(shape)->repr->attribute("inkscape:original");
 
524
        if ( res_d ) {
 
525
            NArtBpath *bpath = sp_svg_read_path (res_d);
 
526
            SPCurve *c = sp_curve_new_from_bpath (bpath);
 
527
            g_assert(c != NULL);
 
528
            sp_shape_set_curve_insync ((SPShape *) offset, c, TRUE);
 
529
            sp_curve_unref (c);
 
530
        }
 
531
        return;
 
532
    }
 
533
  
 
534
    // extra paraniac careful check. the preceding if () should take care of this case
 
535
    if (fabs (offset->rad) < 0.01)
 
536
        offset->rad = (offset->rad < 0) ? -0.01 : 0.01;
 
537
  
 
538
    Path *orig = new Path;
 
539
    orig->Copy ((Path *) offset->originalPath);
 
540
  
 
541
    if ( use_slow_but_correct_offset_method == false ) {
 
542
        // version par outline
 
543
        Shape *theShape = new Shape;
 
544
        Shape *theRes = new Shape;
 
545
        Path *originaux[1];
 
546
        Path *res = new Path;
 
547
        res->SetBackData (false);
 
548
  
 
549
        // and now: offset
 
550
        float o_width;
 
551
        if (offset->rad >= 0)
 
552
        {
 
553
            o_width = offset->rad;
 
554
            orig->OutsideOutline (res, o_width, join_round, butt_straight, 20.0);
 
555
        }
 
556
        else
 
557
        {
 
558
            o_width = -offset->rad;
 
559
            orig->OutsideOutline (res, -o_width, join_round, butt_straight, 20.0);
 
560
        }
 
561
  
 
562
        if (o_width >= 1.0)
 
563
        {
 
564
            //      res->ConvertForOffset (1.0, orig, offset->rad);
 
565
            res->ConvertWithBackData (1.0);
 
566
        }
 
567
        else
 
568
        {
 
569
            //      res->ConvertForOffset (o_width, orig, offset->rad);
 
570
            res->ConvertWithBackData (o_width);
 
571
        }
 
572
        res->Fill (theShape, 0);
 
573
        theRes->ConvertToShape (theShape, fill_positive);
 
574
        originaux[0] = res;
 
575
  
 
576
        theRes->ConvertToForme (orig, 1, originaux);
 
577
 
 
578
        SPItem *item = shape;
 
579
        NR::Rect bbox = sp_item_bbox_desktop (item);
 
580
        if (!bbox.isEmpty()) {
 
581
            gdouble size = L2(bbox.dimensions());
 
582
            gdouble const exp = NR::expansion(item->transform);
 
583
            if (exp != 0) 
 
584
                size /= exp;
 
585
            orig->Coalesce (size * 0.001);
 
586
            //g_print ("coa %g    exp %g    item %p\n", size * 0.001, exp, item);
 
587
        }
 
588
 
 
589
  
 
590
        //  if (o_width >= 1.0)
 
591
        //  {
 
592
        //    orig->Coalesce (0.1);  // small treshhold, since we only want to get rid of small segments
 
593
        // the curve should already be computed by the Outline() function
 
594
        //   orig->ConvertEvenLines (1.0);
 
595
        //   orig->Simplify (0.5);
 
596
        //  }
 
597
        //  else
 
598
        //  {
 
599
        //          orig->Coalesce (0.1*o_width);
 
600
        //   orig->ConvertEvenLines (o_width);
 
601
        //   orig->Simplify (0.5 * o_width);
 
602
        //  }
 
603
  
 
604
        delete theShape;
 
605
        delete theRes;
 
606
        delete res;
 
607
    } else {
 
608
        // version par makeoffset
 
609
        Shape *theShape = new Shape;
 
610
        Shape *theRes = new Shape;
 
611
    
 
612
    
 
613
        // and now: offset
 
614
        float o_width;
 
615
        if (offset->rad >= 0)
 
616
        {
 
617
            o_width = offset->rad;
 
618
        }
 
619
        else
 
620
        {
 
621
            o_width = -offset->rad;
 
622
        }
 
623
    
 
624
        // one has to have a measure of the details
 
625
        if (o_width >= 1.0)
 
626
        {
 
627
            orig->ConvertWithBackData (0.5);
 
628
        }
 
629
        else
 
630
        {
 
631
            orig->ConvertWithBackData (0.5*o_width);
 
632
        }
 
633
        orig->Fill (theShape, 0);
 
634
        theRes->ConvertToShape (theShape, fill_positive);
 
635
        Path *originaux[1];
 
636
        originaux[0]=orig;
 
637
        Path *res = new Path;
 
638
        theRes->ConvertToForme (res, 1, originaux);
 
639
        int    nbPart=0;
 
640
        Path** parts=res->SubPaths(nbPart,true);
 
641
        char   *holes=(char*)malloc(nbPart*sizeof(char));
 
642
        // we offset contours separately, because we can.
 
643
        // this way, we avoid doing a unique big ConvertToShape when dealing with big shapes with lots of holes
 
644
        {
 
645
            Shape* onePart=new Shape;
 
646
            Shape* oneCleanPart=new Shape;
 
647
            theShape->Reset();
 
648
            for (int i=0;i<nbPart;i++) {
 
649
                double partSurf=parts[i]->Surface();
 
650
                parts[i]->Convert(1.0);
 
651
                {
 
652
                    // raffiner si besoin
 
653
                    double  bL,bT,bR,bB;
 
654
                    parts[i]->PolylineBoundingBox(bL,bT,bR,bB);
 
655
                    double  mesure=((bR-bL)+(bB-bT))*0.5;
 
656
                    if ( mesure < 10.0 ) {
 
657
                        parts[i]->Convert(0.02*mesure);
 
658
                    }
 
659
                }
 
660
                if ( partSurf < 0 ) { // inverse par rapport a la realite
 
661
                    // plein
 
662
                    holes[i]=0;
 
663
                    parts[i]->Fill(oneCleanPart,0);
 
664
                    onePart->ConvertToShape(oneCleanPart,fill_positive); // there aren't intersections in that one, but maybe duplicate points and null edges
 
665
                    oneCleanPart->MakeOffset(onePart,offset->rad,join_round,20.0);
 
666
                    onePart->ConvertToShape(oneCleanPart,fill_positive);
 
667
          
 
668
                    onePart->CalcBBox();
 
669
                    double  typicalSize=0.5*((onePart->rightX-onePart->leftX)+(onePart->bottomY-onePart->topY));
 
670
                    if ( typicalSize < 0.05 ) typicalSize=0.05;
 
671
                    typicalSize*=0.01;
 
672
                    if ( typicalSize > 1.0 ) typicalSize=1.0;
 
673
                    onePart->ConvertToForme (parts[i]);
 
674
                    parts[i]->ConvertEvenLines (typicalSize);
 
675
                    parts[i]->Simplify (typicalSize);
 
676
                    double nPartSurf=parts[i]->Surface();
 
677
                    if ( nPartSurf >= 0 ) {
 
678
                        // inversion de la surface -> disparait
 
679
                        delete parts[i];
 
680
                        parts[i]=NULL;
 
681
                    } else {
 
682
                    }
 
683
/*          int  firstP=theShape->nbPt;
 
684
            for (int j=0;j<onePart->nbPt;j++) theShape->AddPoint(onePart->pts[j].x);
 
685
            for (int j=0;j<onePart->nbAr;j++) theShape->AddEdge(firstP+onePart->aretes[j].st,firstP+onePart->aretes[j].en);*/
 
686
                } else {
 
687
                    // trou
 
688
                    holes[i]=1;
 
689
                    parts[i]->Fill(oneCleanPart,0,false,true,true);
 
690
                    onePart->ConvertToShape(oneCleanPart,fill_positive);
 
691
                    oneCleanPart->MakeOffset(onePart,-offset->rad,join_round,20.0);
 
692
                    onePart->ConvertToShape(oneCleanPart,fill_positive);
 
693
//          for (int j=0;j<onePart->nbAr;j++) onePart->Inverse(j); // pas oublier de reinverser
 
694
          
 
695
                    onePart->CalcBBox();
 
696
                    double  typicalSize=0.5*((onePart->rightX-onePart->leftX)+(onePart->bottomY-onePart->topY));
 
697
                    if ( typicalSize < 0.05 ) typicalSize=0.05;
 
698
                    typicalSize*=0.01;
 
699
                    if ( typicalSize > 1.0 ) typicalSize=1.0;
 
700
                    onePart->ConvertToForme (parts[i]);
 
701
                    parts[i]->ConvertEvenLines (typicalSize);
 
702
                    parts[i]->Simplify (typicalSize);
 
703
                    double nPartSurf=parts[i]->Surface();
 
704
                    if ( nPartSurf >= 0 ) {
 
705
                        // inversion de la surface -> disparait
 
706
                        delete parts[i];
 
707
                        parts[i]=NULL;
 
708
                    } else {
 
709
                    }
 
710
          
 
711
                    /*         int  firstP=theShape->nbPt;
 
712
                               for (int j=0;j<onePart->nbPt;j++) theShape->AddPoint(onePart->pts[j].x);
 
713
                               for (int j=0;j<onePart->nbAr;j++) theShape->AddEdge(firstP+onePart->aretes[j].en,firstP+onePart->aretes[j].st);*/
 
714
                }
 
715
//        delete parts[i];
 
716
            }
 
717
//      theShape->MakeOffset(theRes,offset->rad,join_round,20.0);
 
718
            delete onePart;
 
719
            delete oneCleanPart;
 
720
        }
 
721
        if ( nbPart > 1 ) {
 
722
            theShape->Reset();
 
723
            for (int i=0;i<nbPart;i++) {
 
724
                if ( parts[i] ) {
 
725
                    parts[i]->ConvertWithBackData(1.0);
 
726
                    if ( holes[i] ) {
 
727
                        parts[i]->Fill(theShape,i,true,true,true);        
 
728
                    } else {
 
729
                        parts[i]->Fill(theShape,i,true,true,false);        
 
730
                    }
 
731
                }
 
732
            }
 
733
            theRes->ConvertToShape (theShape, fill_positive);
 
734
            theRes->ConvertToForme (orig,nbPart,parts);
 
735
            for (int i=0;i<nbPart;i++) if ( parts[i] ) delete parts[i];
 
736
        } else if ( nbPart == 1 ) {
 
737
            orig->Copy(parts[0]);
 
738
            for (int i=0;i<nbPart;i++) if ( parts[i] ) delete parts[i];
 
739
        } else {
 
740
            orig->Reset();
 
741
        }
 
742
//    theRes->ConvertToShape (theShape, fill_positive);
 
743
//    theRes->ConvertToForme (orig);
 
744
    
 
745
/*    if (o_width >= 1.0) {
 
746
      orig->ConvertEvenLines (1.0);
 
747
      orig->Simplify (1.0);
 
748
      } else {
 
749
      orig->ConvertEvenLines (1.0*o_width);
 
750
      orig->Simplify (1.0 * o_width);
 
751
      }*/
 
752
    
 
753
        if ( parts ) free(parts);
 
754
        if ( holes ) free(holes);
 
755
        delete res;
 
756
        delete theShape;
 
757
        delete theRes;
 
758
    } 
 
759
    {
 
760
        char *res_d = NULL;
 
761
        if (orig->descr_cmd.size() <= 1)
 
762
        {
 
763
            // Aie.... nothing left.
 
764
            res_d = strdup ("M 0 0 L 0 0 z");
 
765
            //printf("%s\n",res_d);
 
766
        }
 
767
        else
 
768
        {
 
769
      
 
770
            res_d = orig->svg_dump_path ();
 
771
        } 
 
772
        delete orig;
 
773
    
 
774
        NArtBpath *bpath = sp_svg_read_path (res_d);
 
775
        SPCurve *c = sp_curve_new_from_bpath (bpath);
 
776
        g_assert(c != NULL);
 
777
        sp_shape_set_curve_insync ((SPShape *) offset, c, TRUE);
 
778
        sp_curve_unref (c);
 
779
    
 
780
        free (res_d);
 
781
    }
 
782
}
 
783
 
 
784
/**
 
785
 * Virtual snappoints function.
 
786
 */
 
787
static void sp_offset_snappoints(SPItem const *item, SnapPointsIter p)
 
788
{
 
789
    if (((SPItemClass *) parent_class)->snappoints) {
 
790
        ((SPItemClass *) parent_class)->snappoints (item, p);
 
791
    }
 
792
}
 
793
 
 
794
 
 
795
// utilitaires pour les poignees
 
796
// used to get the distance to the shape: distance to polygon give the fabs(radius), we still need
 
797
// the sign. for edges, it's easy to determine which side the point is on, for points of the polygon
 
798
// it's trickier: we need to identify which angle the point is in; to that effect, we take each 
 
799
// successive clockwise angle (A,C) and check if the vector B given by the point is in the angle or
 
800
// outside.
 
801
// another method would be to use the Winding() function to test whether the point is inside or outside 
 
802
// the polygon (it would be wiser to do so, in fact, but i like being stupid)
 
803
 
 
804
/** 
 
805
 *
 
806
 * \todo
 
807
 * FIXME: This can be done using linear operations, more stably and
 
808
 *  faster.  method: transform A and C into B's space, A should be
 
809
 *  negative and B should be positive in the orthogonal component.  I
 
810
 *  think this is equivalent to 
 
811
 *  dot(A, rot90(B))*dot(C, rot90(B)) == -1.  
 
812
 *    -- njh 
 
813
 */
 
814
bool
 
815
vectors_are_clockwise (NR::Point A, NR::Point B, NR::Point C)
 
816
{
 
817
    using NR::rot90;
 
818
    double ab_s = dot(A, rot90(B));
 
819
    double ab_c = dot(A, B);
 
820
    double bc_s = dot(B, rot90(C));
 
821
    double bc_c = dot(B, C);
 
822
    double ca_s = dot(C, rot90(A));
 
823
    double ca_c = dot(C, A);
 
824
  
 
825
    double ab_a = acos (ab_c);
 
826
    if (ab_c <= -1.0)
 
827
        ab_a = M_PI;
 
828
    if (ab_c >= 1.0)
 
829
        ab_a = 0;
 
830
    if (ab_s < 0)
 
831
        ab_a = 2 * M_PI - ab_a;
 
832
    double bc_a = acos (bc_c);
 
833
    if (bc_c <= -1.0)
 
834
        bc_a = M_PI;
 
835
    if (bc_c >= 1.0)
 
836
        bc_a = 0;
 
837
    if (bc_s < 0)
 
838
        bc_a = 2 * M_PI - bc_a;
 
839
    double ca_a = acos (ca_c);
 
840
    if (ca_c <= -1.0)
 
841
        ca_a = M_PI;
 
842
    if (ca_c >= 1.0)
 
843
        ca_a = 0;
 
844
    if (ca_s < 0)
 
845
        ca_a = 2 * M_PI - ca_a;
 
846
  
 
847
    double lim = 2 * M_PI - ca_a;
 
848
  
 
849
    if (ab_a < lim)
 
850
        return true;
 
851
    return false;
 
852
}
 
853
 
 
854
/** 
 
855
 * Distance to the original path; that function is called from object-edit 
 
856
 * to set the radius when the control knot moves.
 
857
 *
 
858
 * The sign of the result is the radius we're going to offset the shape with, 
 
859
 * so result > 0 ==outset and result < 0 ==inset. thus result<0 means 
 
860
 * 'px inside source'.
 
861
 */
 
862
double
 
863
sp_offset_distance_to_original (SPOffset * offset, NR::Point px)
 
864
{
 
865
    if (offset == NULL || offset->originalPath == NULL
 
866
        || ((Path *) offset->originalPath)->descr_cmd.size() <= 1)
 
867
        return 1.0;
 
868
    double dist = 1.0;
 
869
    Shape *theShape = new Shape;
 
870
    Shape *theRes = new Shape;
 
871
  
 
872
    /** \todo 
 
873
     * Awfully damn stupid method: uncross the source path EACH TIME you 
 
874
     * need to compute the distance. The good way to do this would be to 
 
875
     * store the uncrossed source path somewhere, and delete it when the
 
876
     * context is finished. Hopefully this part is much faster than actually 
 
877
     * computing the offset (which happen just after), so the time spent in 
 
878
     * this function should end up being negligible with respect to the 
 
879
     * delay of one context.
 
880
     */
 
881
    // move
 
882
    ((Path *) offset->originalPath)->Convert (1.0);
 
883
    ((Path *) offset->originalPath)->Fill (theShape, 0);
 
884
    theRes->ConvertToShape (theShape, fill_oddEven);
 
885
  
 
886
    if (theRes->numberOfEdges() <= 1)
 
887
    {
 
888
    
 
889
    }
 
890
    else
 
891
    {
 
892
        double ptDist = -1.0;
 
893
        bool ptSet = false;
 
894
        double arDist = -1.0;
 
895
        bool arSet = false;
 
896
        // first get the minimum distance to the points
 
897
        for (int i = 0; i < theRes->numberOfPoints(); i++)
 
898
        {
 
899
            if (theRes->getPoint(i).totalDegree() > 0)
 
900
            {
 
901
                NR::Point nx = theRes->getPoint(i).x;
 
902
                NR::Point nxpx = px-nx;
 
903
                double ndist = sqrt (dot(nxpx,nxpx));
 
904
                if (ptSet == false || fabs (ndist) < fabs (ptDist))
 
905
                {
 
906
                    // we have a new minimum distance
 
907
                    // now we need to wheck if px is inside or outside (for the sign)
 
908
                    nx = px - theRes->getPoint(i).x;
 
909
                    double nlen = sqrt (dot(nx , nx));
 
910
                    nx /= nlen;
 
911
                    int pb, cb, fb;
 
912
                    fb = theRes->getPoint(i).incidentEdge[LAST];
 
913
                    pb = theRes->getPoint(i).incidentEdge[LAST];
 
914
                    cb = theRes->getPoint(i).incidentEdge[FIRST];
 
915
                    do
 
916
                    {
 
917
                        // one angle
 
918
                        NR::Point prx, nex;
 
919
                        prx = theRes->getEdge(pb).dx;
 
920
                        nlen = sqrt (dot(prx, prx));
 
921
                        prx /= nlen;
 
922
                        nex = theRes->getEdge(cb).dx;
 
923
                        nlen = sqrt (dot(nex , nex));
 
924
                        nex /= nlen;
 
925
                        if (theRes->getEdge(pb).en == i)
 
926
                        {
 
927
                            prx = -prx;
 
928
                        }
 
929
                        if (theRes->getEdge(cb).en == i)
 
930
                        {
 
931
                            nex = -nex;
 
932
                        }
 
933
            
 
934
                        if (vectors_are_clockwise (nex, nx, prx))
 
935
                        {
 
936
                            // we're in that angle. set the sign, and exit that loop
 
937
                            if (theRes->getEdge(cb).st == i)
 
938
                            {
 
939
                                ptDist = -ndist;
 
940
                                ptSet = true;
 
941
                            }
 
942
                            else
 
943
                            {
 
944
                                ptDist = ndist;
 
945
                                ptSet = true;
 
946
                            }
 
947
                            break;
 
948
                        }
 
949
                        pb = cb;
 
950
                        cb = theRes->NextAt (i, cb);
 
951
                    }
 
952
                    while (cb >= 0 && pb >= 0 && pb != fb);
 
953
                }
 
954
            }
 
955
        }
 
956
        // loop over the edges to try to improve the distance
 
957
        for (int i = 0; i < theRes->numberOfEdges(); i++)
 
958
        {
 
959
            NR::Point sx = theRes->getPoint(theRes->getEdge(i).st).x;
 
960
            NR::Point ex = theRes->getPoint(theRes->getEdge(i).en).x;
 
961
            NR::Point nx = ex - sx;
 
962
            double len = sqrt (dot(nx,nx));
 
963
            if (len > 0.0001)
 
964
            {
 
965
                NR::Point   pxsx=px-sx;
 
966
                double ab = dot(nx,pxsx);
 
967
                if (ab > 0 && ab < len * len)
 
968
                {
 
969
                    // we're in the zone of influence of the segment
 
970
                    double ndist = (cross(pxsx,nx)) / len;
 
971
                    if (arSet == false || fabs (ndist) < fabs (arDist))
 
972
                    {
 
973
                        arDist = ndist;
 
974
                        arSet = true;
 
975
                    }
 
976
                }
 
977
            }
 
978
        }
 
979
        if (arSet || ptSet)
 
980
        {
 
981
            if (arSet == false)
 
982
                arDist = ptDist;
 
983
            if (ptSet == false)
 
984
                ptDist = arDist;
 
985
            if (fabs (ptDist) < fabs (arDist))
 
986
                dist = ptDist;
 
987
            else
 
988
                dist = arDist;
 
989
        }
 
990
    }
 
991
  
 
992
    delete theShape;
 
993
    delete theRes;
 
994
  
 
995
    return dist;
 
996
}
 
997
 
 
998
/** 
 
999
 * Computes a point on the offset;  used to set a "seed" position for 
 
1000
 * the control knot.
 
1001
 *
 
1002
 * \return the topmost point on the offset.
 
1003
 */
 
1004
void
 
1005
sp_offset_top_point (SPOffset * offset, NR::Point *px)
 
1006
{
 
1007
    (*px) = NR::Point(0, 0);
 
1008
    if (offset == NULL)
 
1009
        return;
 
1010
  
 
1011
    if (offset->knotSet)
 
1012
    {
 
1013
        (*px) = offset->knot;
 
1014
        return;
 
1015
    }
 
1016
  
 
1017
    SPCurve *curve = sp_shape_get_curve (SP_SHAPE (offset));
 
1018
    if (curve == NULL)
 
1019
    {
 
1020
        sp_offset_set_shape (SP_SHAPE (offset));
 
1021
        curve = sp_shape_get_curve (SP_SHAPE (offset));
 
1022
        if (curve == NULL)
 
1023
            return;
 
1024
    }
 
1025
  
 
1026
    Path *finalPath = bpath_to_liv_path (curve->bpath);
 
1027
    if (finalPath == NULL)
 
1028
    {
 
1029
        sp_curve_unref (curve);
 
1030
        return;
 
1031
    }
 
1032
  
 
1033
    Shape *theShape = new Shape;
 
1034
  
 
1035
    finalPath->Convert (1.0);
 
1036
    finalPath->Fill (theShape, 0);
 
1037
  
 
1038
    if (theShape->hasPoints())
 
1039
    {
 
1040
        theShape->SortPoints ();
 
1041
        *px = theShape->getPoint(0).x;
 
1042
    }
 
1043
  
 
1044
    delete theShape;
 
1045
    delete finalPath;
 
1046
    sp_curve_unref (curve);
 
1047
}
 
1048
 
 
1049
// the listening functions
 
1050
static void sp_offset_start_listening(SPOffset *offset,SPObject* to)
 
1051
{
 
1052
    if ( to == NULL ) 
 
1053
        return;
 
1054
 
 
1055
    offset->sourceObject = to;
 
1056
    offset->sourceRepr = SP_OBJECT_REPR(to);
 
1057
 
 
1058
    offset->_delete_connection = SP_OBJECT(to)->connectDelete(sigc::bind(sigc::ptr_fun(&sp_offset_delete_self), offset));
 
1059
    offset->_transformed_connection = SP_ITEM(to)->connectTransformed(sigc::bind(sigc::ptr_fun(&sp_offset_move_compensate), offset));
 
1060
    offset->_modified_connection = g_signal_connect (G_OBJECT (to), "modified", G_CALLBACK (sp_offset_source_modified), offset);
 
1061
}
 
1062
 
 
1063
static void sp_offset_quit_listening(SPOffset *offset)
 
1064
{
 
1065
    if ( offset->sourceObject == NULL )  
 
1066
        return;
 
1067
 
 
1068
    g_signal_handler_disconnect (offset->sourceObject, offset->_modified_connection);
 
1069
    offset->_delete_connection.disconnect();
 
1070
    offset->_transformed_connection.disconnect();
 
1071
 
 
1072
    offset->sourceRepr = NULL;
 
1073
    offset->sourceObject = NULL;
 
1074
}
 
1075
 
 
1076
static void
 
1077
sp_offset_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, SPOffset *offset)
 
1078
{
 
1079
    sp_offset_quit_listening(offset);
 
1080
    if (offset->sourceRef) {
 
1081
        SPItem *refobj = offset->sourceRef->getObject();
 
1082
        if (refobj) sp_offset_start_listening(offset,refobj);
 
1083
        offset->sourceDirty=true;
 
1084
        SP_OBJECT(offset)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
1085
    }
 
1086
}
 
1087
 
 
1088
static void
 
1089
sp_offset_move_compensate(NR::Matrix const *mp, SPItem *original, SPOffset *self)
 
1090
{       
 
1091
    guint mode = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_PARALLEL);
 
1092
    if (mode == SP_CLONE_COMPENSATION_NONE) return;
 
1093
 
 
1094
    NR::Matrix m(*mp);
 
1095
    if (!(m.is_translation())) return;
 
1096
 
 
1097
    // calculate the compensation matrix and the advertized movement matrix
 
1098
    SPItem *item = SP_ITEM(self);
 
1099
 
 
1100
    NR::Matrix compensate;
 
1101
    NR::Matrix advertized_move;
 
1102
 
 
1103
    if (mode == SP_CLONE_COMPENSATION_UNMOVED) {
 
1104
        compensate = NR::identity();
 
1105
        advertized_move.set_identity();
 
1106
    } else if (mode == SP_CLONE_COMPENSATION_PARALLEL) {
 
1107
        compensate = m;
 
1108
        advertized_move = m;
 
1109
    } else {
 
1110
        g_assert_not_reached();
 
1111
    }
 
1112
 
 
1113
    item->transform *= compensate;
 
1114
 
 
1115
    // commit the compensation
 
1116
    sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform, &advertized_move);
 
1117
    SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
1118
}
 
1119
 
 
1120
static void
 
1121
sp_offset_delete_self(SPObject */*deleted*/, SPOffset *offset)
 
1122
{
 
1123
    guint const mode = prefs_get_int_attribute("options.cloneorphans", "value", SP_CLONE_ORPHANS_UNLINK);
 
1124
        
 
1125
    if (mode == SP_CLONE_ORPHANS_UNLINK) {
 
1126
        // leave it be. just forget about the source
 
1127
        sp_offset_quit_listening(offset);
 
1128
        if ( offset->sourceHref ) g_free(offset->sourceHref);
 
1129
        offset->sourceHref = NULL;
 
1130
        offset->sourceRef->detach();
 
1131
    } else if (mode == SP_CLONE_ORPHANS_DELETE) {
 
1132
        SP_OBJECT(offset)->deleteObject();
 
1133
    }
 
1134
}
 
1135
 
 
1136
static void
 
1137
sp_offset_source_modified (SPObject *iSource, guint flags, SPItem *item)
 
1138
{
 
1139
    SPOffset *offset = SP_OFFSET(item);
 
1140
    offset->sourceDirty=true;
 
1141
    refresh_offset_source(offset);
 
1142
    sp_shape_set_shape ((SPShape *) offset);
 
1143
}
 
1144
 
 
1145
static void 
 
1146
refresh_offset_source(SPOffset* offset)
 
1147
{
 
1148
    if ( offset == NULL ) return;
 
1149
    offset->sourceDirty=false;
 
1150
    Path *orig = NULL;
 
1151
  
 
1152
    // le mauvais cas: pas d'attribut d => il faut verifier que c'est une SPShape puis prendre le contour
 
1153
    // The bad case: no d attribute.  Must check that it's an SPShape and then take the outline.
 
1154
    SPObject *refobj=offset->sourceObject;
 
1155
    if ( refobj == NULL ) return;
 
1156
    SPItem *item = SP_ITEM (refobj);
 
1157
  
 
1158
    SPCurve *curve=NULL;
 
1159
    if (!SP_IS_SHAPE (item) && !SP_IS_TEXT (item)) return;
 
1160
    if (SP_IS_SHAPE (item)) {
 
1161
        curve = sp_shape_get_curve (SP_SHAPE (item));
 
1162
        if (curve == NULL)
 
1163
            return;
 
1164
    }
 
1165
    if (SP_IS_TEXT (item)) {
 
1166
        curve = SP_TEXT (item)->getNormalizedBpath ();
 
1167
        if (curve == NULL)
 
1168
            return;
 
1169
    }
 
1170
    orig = bpath_to_liv_path (curve->bpath);
 
1171
    sp_curve_unref (curve);
 
1172
  
 
1173
  
 
1174
    // Finish up.
 
1175
    {
 
1176
        SPCSSAttr *css;
 
1177
        const gchar *val;
 
1178
        Shape *theShape = new Shape;
 
1179
        Shape *theRes = new Shape;
 
1180
    
 
1181
        orig->ConvertWithBackData (1.0);
 
1182
        orig->Fill (theShape, 0);
 
1183
    
 
1184
        css = sp_repr_css_attr (offset->sourceRepr , "style");
 
1185
        val = sp_repr_css_property (css, "fill-rule", NULL);
 
1186
        if (val && strcmp (val, "nonzero") == 0)
 
1187
        {
 
1188
            theRes->ConvertToShape (theShape, fill_nonZero);
 
1189
        }
 
1190
        else if (val && strcmp (val, "evenodd") == 0)
 
1191
        {
 
1192
            theRes->ConvertToShape (theShape, fill_oddEven);
 
1193
        }
 
1194
        else
 
1195
        {
 
1196
            theRes->ConvertToShape (theShape, fill_nonZero);
 
1197
        }
 
1198
    
 
1199
        Path *originaux[1];
 
1200
        originaux[0] = orig;
 
1201
        Path *res = new Path;
 
1202
        theRes->ConvertToForme (res, 1, originaux);
 
1203
    
 
1204
        delete theShape;
 
1205
        delete theRes;
 
1206
    
 
1207
        char *res_d = res->svg_dump_path ();
 
1208
        delete res;
 
1209
        delete orig;
 
1210
    
 
1211
        SP_OBJECT (offset)->repr->setAttribute("inkscape:original", res_d);
 
1212
    
 
1213
        free (res_d);
 
1214
    }
 
1215
}
 
1216
 
 
1217
SPItem *
 
1218
sp_offset_get_source (SPOffset *offset)
 
1219
{
 
1220
    if (offset && offset->sourceRef) {
 
1221
        SPItem *refobj = offset->sourceRef->getObject();
 
1222
        if (SP_IS_ITEM (refobj))
 
1223
            return (SPItem *) refobj;
 
1224
    }
 
1225
    return NULL;
 
1226
}
 
1227
 
 
1228
 
 
1229
/*
 
1230
  Local Variables:
 
1231
  mode:c++
 
1232
  c-file-style:"stroustrup"
 
1233
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
1234
  indent-tabs-mode:nil
 
1235
  fill-column:99
 
1236
  End:
 
1237
*/
 
1238
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :