Code

rename, rearrange stroke shapes; add elliptic
[inkscape.git] / src / sp-offset.cpp
index a9730653c247e4ac11f3118113197c9bc2aea9c9..de4933821c3ed46f65e596b5da6950c24476cc92 100644 (file)
@@ -19,6 +19,8 @@
 # include "config.h"
 #endif
 
+#include <cstring>
+#include <string>
 
 #include "svg/svg.h"
 #include "attributes.h"
@@ -37,6 +39,7 @@
 
 #include "libnr/n-art-bpath.h"
 #include <libnr/nr-matrix-fns.h>
+#include <2geom/pathvector.h>
 
 #include "xml/repr.h"
 
@@ -46,24 +49,24 @@ class SPDocument;
 
 /** \note
  * SPOffset is a derivative of SPShape, much like the SPSpiral or SPRect.
- * The goal is to have a source shape (= originalPath), an offset (= radius) 
- * and compute the offset of the source by the radius. To get it to work, 
- * one needs to know what the source is and what the radius is, and how it's 
- * stored in the xml representation. The object itself is a "path" element, 
- * to get lots of shape functionality for free. The source is the easy part: 
- * it's stored in a "inkscape:original" attribute in the path. In case of 
+ * The goal is to have a source shape (= originalPath), an offset (= radius)
+ * and compute the offset of the source by the radius. To get it to work,
+ * one needs to know what the source is and what the radius is, and how it's
+ * stored in the xml representation. The object itself is a "path" element,
+ * to get lots of shape functionality for free. The source is the easy part:
+ * it's stored in a "inkscape:original" attribute in the path. In case of
  * "linked" offset, as they've been dubbed, there is an additional
- * "inkscape:href" that contains the id of an element of the svg. 
- * When built, the object will attach a listener vector to that object and 
- * rebuild the "inkscape:original" whenever the href'd object changes. This 
- * is of course grossly inefficient, and also does not react to changes 
- * to the href'd during context stuff (like changing the shape of a star by 
- * dragging control points) unless the path of that object is changed during 
- * the context (seems to be the case for SPEllipse). The computation of the 
- * offset is done in sp_offset_set_shape(), a function that is called whenever 
+ * "inkscape:href" that contains the id of an element of the svg.
+ * When built, the object will attach a listener vector to that object and
+ * rebuild the "inkscape:original" whenever the href'd object changes. This
+ * is of course grossly inefficient, and also does not react to changes
+ * to the href'd during context stuff (like changing the shape of a star by
+ * dragging control points) unless the path of that object is changed during
+ * the context (seems to be the case for SPEllipse). The computation of the
+ * offset is done in sp_offset_set_shape(), a function that is called whenever
  * a change occurs to the offset (change of source or change of radius).
- * just like the sp-star and other, this path derivative can make control 
- * points, or more precisely one control point, that's enough to define the 
+ * just like the sp-star and other, this path derivative can make control
+ * points, or more precisely one control point, that's enough to define the
  * radius (look in object-edit).
  */
 
@@ -73,7 +76,7 @@ static void sp_offset_finalize(GObject *obj);
 
 static void sp_offset_build (SPObject * object, SPDocument * document,
                              Inkscape::XML::Node * repr);
-static Inkscape::XML::Node *sp_offset_write (SPObject * object, Inkscape::XML::Node * repr,
+static Inkscape::XML::Node *sp_offset_write (SPObject * object, Inkscape::XML::Document *doc, Inkscape::XML::Node * repr,
                                 guint flags);
 static void sp_offset_set (SPObject * object, unsigned int key,
                            const gchar * value);
@@ -84,8 +87,6 @@ static gchar *sp_offset_description (SPItem * item);
 static void sp_offset_snappoints(SPItem const *item, SnapPointsIter p);
 static void sp_offset_set_shape (SPShape * shape);
 
-Path *bpath_to_liv_path (NArtBpath * bpath);
-
 static void refresh_offset_source(SPOffset* offset);
 
 static void sp_offset_start_listening(SPOffset *offset,SPObject* to);
@@ -179,6 +180,7 @@ sp_offset_init(SPOffset *offset)
     offset->sourceHref = NULL;
     offset->sourceRepr = NULL;
     offset->sourceObject = NULL;
+    new (&offset->_modified_connection) sigc::connection();
     new (&offset->_delete_connection) sigc::connection();
     new (&offset->_changed_connection) sigc::connection();
     new (&offset->_transformed_connection) sigc::connection();
@@ -196,8 +198,14 @@ sp_offset_finalize(GObject *obj)
     SPOffset *offset = (SPOffset *) obj;
 
     delete offset->sourceRef;
+
+    offset->_modified_connection.disconnect();
+    offset->_modified_connection.~connection();
+    offset->_delete_connection.disconnect();
     offset->_delete_connection.~connection();
+    offset->_changed_connection.disconnect();
     offset->_changed_connection.~connection();
+    offset->_transformed_connection.disconnect();
     offset->_transformed_connection.~connection();
 }
 
@@ -209,19 +217,19 @@ sp_offset_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *rep
 {
     if (((SPObjectClass *) parent_class)->build)
         ((SPObjectClass *) parent_class)->build (object, document, repr);
-  
+
     if (object->repr->attribute("inkscape:radius")) {
         sp_object_read_attr (object, "inkscape:radius");
     } else {
         gchar const *oldA = object->repr->attribute("sodipodi:radius");
         object->repr->setAttribute("inkscape:radius",oldA);
         object->repr->setAttribute("sodipodi:radius",NULL);
-    
+
         sp_object_read_attr (object, "inkscape:radius");
     }
     if (object->repr->attribute("inkscape:original")) {
         sp_object_read_attr (object, "inkscape:original");
-    } else {    
+    } else {
         gchar const *oldA = object->repr->attribute("sodipodi:original");
         object->repr->setAttribute("inkscape:original",oldA);
         object->repr->setAttribute("sodipodi:original",NULL);
@@ -250,12 +258,12 @@ sp_offset_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *rep
  * Virtual write: write offset attributes to corresponding repr.
  */
 static Inkscape::XML::Node *
-sp_offset_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+sp_offset_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
 {
     SPOffset *offset = SP_OFFSET (object);
 
     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
-        repr = sp_repr_new ("svg:path");
+        repr = xml_doc->createElement("svg:path");
     }
 
     if (flags & SP_OBJECT_WRITE_EXT) {
@@ -277,12 +285,12 @@ sp_offset_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
     }
 
     // write that curve to "d"
-    char *d = sp_svg_write_path (((SPShape *) offset)->curve->bpath);
+    char *d = sp_svg_write_path (((SPShape *) offset)->curve->get_pathvector());
     repr->setAttribute("d", d);
     g_free (d);
 
     if (((SPObjectClass *) (parent_class))->write)
-        ((SPObjectClass *) (parent_class))->write (object, repr,
+        ((SPObjectClass *) (parent_class))->write (object, xml_doc, repr,
                                                    flags | SP_SHAPE_WRITE_PATH);
 
     return repr;
@@ -315,7 +323,7 @@ sp_offset_release(SPObject *object)
 }
 
 /**
- * Set callback: the function that is called whenever a change is made to 
+ * Set callback: the function that is called whenever a change is made to
  * the description of the object.
  */
 static void
@@ -338,16 +346,12 @@ sp_offset_set(SPObject *object, unsigned key, gchar const *value)
                     offset->original = NULL;
                     offset->originalPath = NULL;
                 }
-                NArtBpath *bpath;
-                SPCurve *curve;
 
                 offset->original = strdup (value);
 
-                bpath = sp_svg_read_path (offset->original);
-                curve = sp_curve_new_from_bpath (bpath);       // curve se chargera de detruire bpath
-                g_assert (curve != NULL);
-                offset->originalPath = bpath_to_liv_path (curve->bpath);
-                sp_curve_unref (curve);
+                Geom::PathVector pv = sp_svg_read_pathv(offset->original);
+                offset->originalPath = new Path;
+                reinterpret_cast<Path *>(offset->originalPath)->LoadPathVector(pv);
 
                 offset->knotSet = false;
                 if ( offset->isUpdating == false ) object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
@@ -429,75 +433,6 @@ sp_offset_description(SPItem *item)
     }
 }
 
-/**
- * Converts an NArtBpath (like the one stored in a SPCurve) into a 
- * livarot Path. Duplicate of splivarot.
- */
-Path *
-bpath_to_liv_path(NArtBpath *bpath)
-{
-    if (bpath == NULL)
-        return NULL;
-  
-    Path *dest = new Path;
-    dest->SetBackData (false);
-    {
-        int i;
-        bool closed = false;
-        float lastX = 0.0;
-        float lastY = 0.0;
-
-        for (i = 0; bpath[i].code != NR_END; i++)
-        {
-            switch (bpath[i].code)
-            {
-                case NR_LINETO:
-                    lastX = bpath[i].x3;
-                    lastY = bpath[i].y3;
-                    {
-                        NR::Point  tmp(lastX,lastY);
-                        dest->LineTo (tmp);
-                    }
-                    break;
-
-                case NR_CURVETO:
-                {
-                    NR::Point  tmp(bpath[i].x3, bpath[i].y3);
-                    NR::Point  tms;
-                    tms[0]=3 * (bpath[i].x1 - lastX);
-                    tms[1]=3 * (bpath[i].y1 - lastY);
-                    NR::Point  tme;
-                    tme[0]=3 * (bpath[i].x3 - bpath[i].x2);
-                    tme[1]= 3 * (bpath[i].y3 - bpath[i].y2);
-                    dest->CubicTo (tmp,tms,tme);
-                }
-                lastX = bpath[i].x3;
-                lastY = bpath[i].y3;
-                break;
-
-                case NR_MOVETO_OPEN:
-                case NR_MOVETO:
-                    if (closed)
-                        dest->Close ();
-                    closed = (bpath[i].code == NR_MOVETO);
-                    lastX = bpath[i].x3;
-                    lastY = bpath[i].y3;
-                    {
-                        NR::Point tmp(lastX,lastY);
-                        dest->MoveTo(tmp);
-                    }
-                    break;
-                default:
-                    break;
-            }
-        }
-        if (closed)
-            dest->Close ();
-    }
-
-    return dest;
-}
-
 /**
  * Compute and set shape's offset.
  */
@@ -522,11 +457,11 @@ sp_offset_set_shape(SPShape *shape)
 
         const char *res_d = SP_OBJECT(shape)->repr->attribute("inkscape:original");
         if ( res_d ) {
-            NArtBpath *bpath = sp_svg_read_path (res_d);
-            SPCurve *c = sp_curve_new_from_bpath (bpath);
+            Geom::PathVector pv = sp_svg_read_pathv(res_d);
+            SPCurve *c = new SPCurve(pv);
             g_assert(c != NULL);
             sp_shape_set_curve_insync ((SPShape *) offset, c, TRUE);
-            sp_curve_unref (c);
+            c->unref();
         }
         return;
     }
@@ -545,7 +480,7 @@ sp_offset_set_shape(SPShape *shape)
         Path *originaux[1];
         Path *res = new Path;
         res->SetBackData (false);
-  
+
         // and now: offset
         float o_width;
         if (offset->rad >= 0)
@@ -576,9 +511,9 @@ sp_offset_set_shape(SPShape *shape)
         theRes->ConvertToForme (orig, 1, originaux);
 
         SPItem *item = shape;
-        NR::Rect bbox = sp_item_bbox_desktop (item);
-        if (!bbox.isEmpty()) {
-            gdouble size = L2(bbox.dimensions());
+        NR::Maybe<NR::Rect> bbox = sp_item_bbox_desktop (item);
+        if ( bbox && !bbox->isEmpty() ) {
+            gdouble size = L2(bbox->dimensions());
             gdouble const exp = NR::expansion(item->transform);
             if (exp != 0)
                 size /= exp;
@@ -608,8 +543,8 @@ sp_offset_set_shape(SPShape *shape)
         // version par makeoffset
         Shape *theShape = new Shape;
         Shape *theRes = new Shape;
-    
-    
+
+
         // and now: offset
         float o_width;
         if (offset->rad >= 0)
@@ -620,7 +555,7 @@ sp_offset_set_shape(SPShape *shape)
         {
             o_width = -offset->rad;
         }
-    
+
         // one has to have a measure of the details
         if (o_width >= 1.0)
         {
@@ -771,11 +706,11 @@ sp_offset_set_shape(SPShape *shape)
         }
         delete orig;
 
-        NArtBpath *bpath = sp_svg_read_path (res_d);
-        SPCurve *c = sp_curve_new_from_bpath (bpath);
+        Geom::PathVector pv = sp_svg_read_pathv(res_d);
+        SPCurve *c = new SPCurve(pv);
         g_assert(c != NULL);
         sp_shape_set_curve_insync ((SPShape *) offset, c, TRUE);
-        sp_curve_unref (c);
+        c->unref();
 
         free (res_d);
     }
@@ -795,21 +730,21 @@ static void sp_offset_snappoints(SPItem const *item, SnapPointsIter p)
 // utilitaires pour les poignees
 // used to get the distance to the shape: distance to polygon give the fabs(radius), we still need
 // the sign. for edges, it's easy to determine which side the point is on, for points of the polygon
-// it's trickier: we need to identify which angle the point is in; to that effect, we take each 
+// it's trickier: we need to identify which angle the point is in; to that effect, we take each
 // successive clockwise angle (A,C) and check if the vector B given by the point is in the angle or
 // outside.
-// another method would be to use the Winding() function to test whether the point is inside or outside 
+// another method would be to use the Winding() function to test whether the point is inside or outside
 // the polygon (it would be wiser to do so, in fact, but i like being stupid)
 
-/** 
+/**
  *
  * \todo
  * FIXME: This can be done using linear operations, more stably and
  *  faster.  method: transform A and C into B's space, A should be
  *  negative and B should be positive in the orthogonal component.  I
- *  think this is equivalent to 
- *  dot(A, rot90(B))*dot(C, rot90(B)) == -1.  
- *    -- njh 
+ *  think this is equivalent to
+ *  dot(A, rot90(B))*dot(C, rot90(B)) == -1.
+ *    -- njh
  */
 bool
 vectors_are_clockwise (NR::Point A, NR::Point B, NR::Point C)
@@ -851,12 +786,12 @@ vectors_are_clockwise (NR::Point A, NR::Point B, NR::Point C)
     return false;
 }
 
-/** 
- * Distance to the original path; that function is called from object-edit 
+/**
+ * Distance to the original path; that function is called from object-edit
  * to set the radius when the control knot moves.
  *
- * The sign of the result is the radius we're going to offset the shape with, 
- * so result > 0 ==outset and result < 0 ==inset. thus result<0 means 
+ * The sign of the result is the radius we're going to offset the shape with,
+ * so result > 0 ==outset and result < 0 ==inset. thus result<0 means
  * 'px inside source'.
  */
 double
@@ -868,14 +803,14 @@ sp_offset_distance_to_original (SPOffset * offset, NR::Point px)
     double dist = 1.0;
     Shape *theShape = new Shape;
     Shape *theRes = new Shape;
-  
-    /** \todo 
-     * Awfully damn stupid method: uncross the source path EACH TIME you 
-     * need to compute the distance. The good way to do this would be to 
+
+    /** \todo
+     * Awfully damn stupid method: uncross the source path EACH TIME you
+     * need to compute the distance. The good way to do this would be to
      * store the uncrossed source path somewhere, and delete it when the
-     * context is finished. Hopefully this part is much faster than actually 
-     * computing the offset (which happen just after), so the time spent in 
-     * this function should end up being negligible with respect to the 
+     * context is finished. Hopefully this part is much faster than actually
+     * computing the offset (which happen just after), so the time spent in
+     * this function should end up being negligible with respect to the
      * delay of one context.
      */
     // move
@@ -995,8 +930,8 @@ sp_offset_distance_to_original (SPOffset * offset, NR::Point px)
     return dist;
 }
 
-/** 
- * Computes a point on the offset;  used to set a "seed" position for 
+/**
+ * Computes a point on the offset;  used to set a "seed" position for
  * the control knot.
  *
  * \return the topmost point on the offset.
@@ -1022,14 +957,15 @@ sp_offset_top_point (SPOffset * offset, NR::Point *px)
         if (curve == NULL)
             return;
     }
-
-    Path *finalPath = bpath_to_liv_path (curve->bpath);
-    if (finalPath == NULL)
+    if (curve->is_empty())
     {
-        sp_curve_unref (curve);
+        curve->unref();
         return;
     }
 
+    Path *finalPath = new Path;
+    finalPath->LoadPathVector(curve->get_pathvector());
+
     Shape *theShape = new Shape;
 
     finalPath->Convert (1.0);
@@ -1043,7 +979,7 @@ sp_offset_top_point (SPOffset * offset, NR::Point *px)
 
     delete theShape;
     delete finalPath;
-    sp_curve_unref (curve);
+    curve->unref();
 }
 
 // the listening functions
@@ -1057,7 +993,7 @@ static void sp_offset_start_listening(SPOffset *offset,SPObject* to)
 
     offset->_delete_connection = SP_OBJECT(to)->connectDelete(sigc::bind(sigc::ptr_fun(&sp_offset_delete_self), offset));
     offset->_transformed_connection = SP_ITEM(to)->connectTransformed(sigc::bind(sigc::ptr_fun(&sp_offset_move_compensate), offset));
-    offset->_modified_connection = g_signal_connect (G_OBJECT (to), "modified", G_CALLBACK (sp_offset_source_modified), offset);
+    offset->_modified_connection = SP_OBJECT(to)->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_offset_source_modified), offset));
 }
 
 static void sp_offset_quit_listening(SPOffset *offset)
@@ -1065,7 +1001,7 @@ static void sp_offset_quit_listening(SPOffset *offset)
     if ( offset->sourceObject == NULL )
         return;
 
-    g_signal_handler_disconnect (offset->sourceObject, offset->_modified_connection);
+    offset->_modified_connection.disconnect();
     offset->_delete_connection.disconnect();
     offset->_transformed_connection.disconnect();
 
@@ -1086,7 +1022,7 @@ sp_offset_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, SPOffset *offse
 }
 
 static void
-sp_offset_move_compensate(NR::Matrix const *mp, SPItem *original, SPOffset *self)
+sp_offset_move_compensate(NR::Matrix const *mp, SPItem */*original*/, SPOffset *self)
 {
     guint mode = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_PARALLEL);
     if (mode == SP_CLONE_COMPENSATION_NONE) return;
@@ -1134,7 +1070,7 @@ sp_offset_delete_self(SPObject */*deleted*/, SPOffset *offset)
 }
 
 static void
-sp_offset_source_modified (SPObject *iSource, guint flags, SPItem *item)
+sp_offset_source_modified (SPObject */*iSource*/, guint /*flags*/, SPItem *item)
 {
     SPOffset *offset = SP_OFFSET(item);
     offset->sourceDirty=true;
@@ -1142,12 +1078,11 @@ sp_offset_source_modified (SPObject *iSource, guint flags, SPItem *item)
     sp_shape_set_shape ((SPShape *) offset);
 }
 
-static void 
+static void
 refresh_offset_source(SPOffset* offset)
 {
     if ( offset == NULL ) return;
     offset->sourceDirty=false;
-    Path *orig = NULL;
 
     // le mauvais cas: pas d'attribut d => il faut verifier que c'est une SPShape puis prendre le contour
     // The bad case: no d attribute.  Must check that it's an SPShape and then take the outline.
@@ -1165,10 +1100,11 @@ refresh_offset_source(SPOffset* offset)
     if (SP_IS_TEXT (item)) {
         curve = SP_TEXT (item)->getNormalizedBpath ();
         if (curve == NULL)
-           return;
+        return;
     }
-    orig = bpath_to_liv_path (curve->bpath);
-    sp_curve_unref (curve);
+    Path *orig = new Path;
+    orig->LoadPathVector(curve->get_pathvector());
+    curve->unref();
 
 
     // Finish up.
@@ -1195,21 +1131,21 @@ refresh_offset_source(SPOffset* offset)
         {
             theRes->ConvertToShape (theShape, fill_nonZero);
         }
-    
+
         Path *originaux[1];
         originaux[0] = orig;
         Path *res = new Path;
         theRes->ConvertToForme (res, 1, originaux);
-    
+
         delete theShape;
         delete theRes;
-    
+
         char *res_d = res->svg_dump_path ();
         delete res;
         delete orig;
-    
+
         SP_OBJECT (offset)->repr->setAttribute("inkscape:original", res_d);
-    
+
         free (res_d);
     }
 }