Code

now that selection description includes style (filtered, clipped), we need to update...
[inkscape.git] / src / sp-spiral.cpp
index aced4ec53148202bd51086c710d006bb95147d36..624c5ae181ad7513a2200cac5e1c79373270043a 100644 (file)
 
 #include "svg/svg.h"
 #include "attributes.h"
-#include "display/bezier-utils.h"
+#include <2geom/bezier-utils.h>
 #include "display/curve.h"
 #include <glibmm/i18n.h>
 #include "xml/repr.h"
+#include "document.h"
 
 #include "sp-spiral.h"
 
@@ -30,15 +31,17 @@ static void sp_spiral_class_init (SPSpiralClass *klass);
 static void sp_spiral_init (SPSpiral *spiral);
 
 static void sp_spiral_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
-static Inkscape::XML::Node *sp_spiral_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
+static Inkscape::XML::Node *sp_spiral_write (SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
 static void sp_spiral_set (SPObject *object, unsigned int key, const gchar *value);
 static void sp_spiral_update (SPObject *object, SPCtx *ctx, guint flags);
 
 static gchar * sp_spiral_description (SPItem * item);
-static void sp_spiral_snappoints(SPItem const *item, SnapPointsIter p);
+static void sp_spiral_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs);
+
 static void sp_spiral_set_shape (SPShape *shape);
+static void sp_spiral_update_patheffect (SPLPEItem *lpeitem, bool write);
 
-static NR::Point sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t);
+static Geom::Point sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t);
 
 static SPShapeClass *parent_class;
 
@@ -77,11 +80,13 @@ sp_spiral_class_init (SPSpiralClass *klass)
        GObjectClass * gobject_class;
        SPObjectClass * sp_object_class;
        SPItemClass * item_class;
+       SPLPEItemClass * lpe_item_class;
        SPShapeClass *shape_class;
 
        gobject_class = (GObjectClass *) klass;
        sp_object_class = (SPObjectClass *) klass;
        item_class = (SPItemClass *) klass;
+       lpe_item_class = (SPLPEItemClass *) klass;
        shape_class = (SPShapeClass *) klass;
 
        parent_class = (SPShapeClass *)g_type_class_ref (SP_TYPE_SHAPE);
@@ -94,7 +99,9 @@ sp_spiral_class_init (SPSpiralClass *klass)
        item_class->description = sp_spiral_description;
        item_class->snappoints = sp_spiral_snappoints;
 
-       shape_class->set_shape = sp_spiral_set_shape;
+    lpe_item_class->update_patheffect = sp_spiral_update_patheffect;
+
+    shape_class->set_shape = sp_spiral_set_shape;
 }
 
 /**
@@ -134,12 +141,12 @@ sp_spiral_build (SPObject * object, SPDocument * document, Inkscape::XML::Node *
  * Virtual write: write spiral attributes to corresponding repr.
  */
 static Inkscape::XML::Node *
-sp_spiral_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
+sp_spiral_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
 {
        SPSpiral *spiral = SP_SPIRAL (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) {
@@ -156,26 +163,24 @@ sp_spiral_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
                sp_repr_set_svg_double(repr, "sodipodi:t0", spiral->t0);
        }
 
-        //Duplicate the path
-        SPCurve *curve = ((SPShape *) spiral)->curve;
-        //Nulls might be possible if this called iteratively
-        if ( !curve ) {
-                //g_warning("sp_spiral_write(): No path to copy\n");
-                return NULL;
-        }
-        NArtBpath *bpath = curve->bpath;
-        if ( !bpath ) {
-                //g_warning("sp_spiral_write(): No path to copy\n");
-                return NULL;
-        }
-       char *d = sp_svg_write_path ( bpath );
-       repr->setAttribute("d", d);
-       g_free (d);
+     // make sure the curve is rebuilt with all up-to-date parameters
+     sp_spiral_set_shape ((SPShape *) spiral);
+
+    //Duplicate the path
+    SPCurve *curve = ((SPShape *) spiral)->curve;
+    //Nulls might be possible if this called iteratively
+    if ( !curve ) {
+            //g_warning("sp_spiral_write(): No path to copy\n");
+            return NULL;
+    }
+    char *d = sp_svg_write_path ( curve->get_pathvector() );
+    repr->setAttribute("d", d);
+    g_free (d);
 
-       if (((SPObjectClass *) (parent_class))->write)
-               ((SPObjectClass *) (parent_class))->write (object, repr, flags | SP_SHAPE_WRITE_PATH);
+    if (((SPObjectClass *) (parent_class))->write)
+        ((SPObjectClass *) (parent_class))->write (object, xml_doc, repr, flags | SP_SHAPE_WRITE_PATH);
 
-       return repr;
+    return repr;
 }
 
 /**
@@ -190,7 +195,7 @@ sp_spiral_set (SPObject *object, unsigned int key, const gchar *value)
        spiral = SP_SPIRAL (object);
        shape  = SP_SHAPE (object);
 
-       /// \todo fixme: we should really collect updates 
+       /// \todo fixme: we should really collect updates
        switch (key) {
        case SP_ATTR_SODIPODI_CX:
                if (!sp_svg_length_read_computed_absolute (value, &spiral->cx)) {
@@ -206,11 +211,11 @@ sp_spiral_set (SPObject *object, unsigned int key, const gchar *value)
                break;
        case SP_ATTR_SODIPODI_EXPANSION:
                if (value) {
-                       /** \todo 
-                         * FIXME: check that value looks like a (finite) 
-                         * number. Create a routine that uses strtod, and 
+                       /** \todo
+                         * FIXME: check that value looks like a (finite)
+                         * number. Create a routine that uses strtod, and
                          * accepts a default value (if strtod finds an error).
-                         * N.B. atof/sscanf/strtod consider "nan" and "inf" 
+                         * N.B. atof/sscanf/strtod consider "nan" and "inf"
                          * to be valid numbers.
                          */
                        spiral->exp = g_ascii_strtod (value, NULL);
@@ -239,11 +244,11 @@ sp_spiral_set (SPObject *object, unsigned int key, const gchar *value)
                if (value) {
                        spiral->arg = g_ascii_strtod (value, NULL);
                        /** \todo
-                         * FIXME: We still need some bounds on arg, for 
-                         * numerical reasons. E.g., we don't want inf or NaN, 
-                         * nor near-infinite numbers. I'm inclined to take 
-                         * modulo 2*pi.  If so, then change the knot editors, 
-                         * which use atan2 - revo*2*pi, which typically 
+                         * FIXME: We still need some bounds on arg, for
+                         * numerical reasons. E.g., we don't want inf or NaN,
+                         * nor near-infinite numbers. I'm inclined to take
+                         * modulo 2*pi.  If so, then change the knot editors,
+                         * which use atan2 - revo*2*pi, which typically
                          * results in very negative arg.
                          */
                } else {
@@ -257,9 +262,9 @@ sp_spiral_set (SPObject *object, unsigned int key, const gchar *value)
                        spiral->t0 = CLAMP (spiral->t0, 0.0, 0.999);
                        /** \todo
                          * Have shared constants for the allowable bounds for
-                         * attributes. There was a bug here where we used -1.0 
-                         * as the minimum (which leads to NaN via, e.g., 
-                         * pow(-1.0, 0.5); see sp_spiral_get_xy for 
+                         * attributes. There was a bug here where we used -1.0
+                         * as the minimum (which leads to NaN via, e.g.,
+                         * pow(-1.0, 0.5); see sp_spiral_get_xy for
                          * requirements.
                          */
                } else {
@@ -288,6 +293,26 @@ sp_spiral_update (SPObject *object, SPCtx *ctx, guint flags)
                ((SPObjectClass *) parent_class)->update (object, ctx, flags);
 }
 
+static void
+sp_spiral_update_patheffect(SPLPEItem *lpeitem, bool write)
+{
+    SPShape *shape = (SPShape *) lpeitem;
+    sp_spiral_set_shape(shape);
+
+    if (write) {
+        Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
+        if ( shape->curve != NULL ) {
+            gchar *str = sp_svg_write_path(shape->curve->get_pathvector());
+            repr->setAttribute("d", str);
+            g_free(str);
+        } else {
+            repr->setAttribute("d", NULL);
+        }
+    }
+
+    ((SPObject *)shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
 /**
  * Return textual description of spiral.
  */
@@ -300,7 +325,7 @@ sp_spiral_description (SPItem * item)
 }
 
 
-/** 
+/**
  * Fit beziers together to spiral and draw it.
  *
  * \pre dstep \> 0.
@@ -311,9 +336,9 @@ static void
 sp_spiral_fit_and_draw (SPSpiral const *spiral,
                        SPCurve  *c,
                        double dstep,
-                       NR::Point darray[],
-                       NR::Point const &hat1,
-                       NR::Point &hat2,
+                       Geom::Point darray[],
+                       Geom::Point const &hat1,
+                       Geom::Point &hat2,
                        double *t)
 {
 #define BEZIER_SIZE   4
@@ -322,7 +347,7 @@ sp_spiral_fit_and_draw (SPSpiral const *spiral,
        g_assert (dstep > 0);
        g_assert (is_unit_vector (hat1));
 
-       NR::Point bezier[BEZIER_LENGTH];
+       Geom::Point bezier[BEZIER_LENGTH];
        double d;
        int depth, i;
 
@@ -336,20 +361,20 @@ sp_spiral_fit_and_draw (SPSpiral const *spiral,
                    && (d < 1.0)) {
                        i--;
                        d += dstep;
-                       /** We mustn't increase dstep for subsequent values of 
-                         * i: for large spiral.exp values, rate of growth 
-                         * increases very rapidly.  
+                       /** We mustn't increase dstep for subsequent values of
+                         * i: for large spiral.exp values, rate of growth
+                         * increases very rapidly.
                          */
-                        /** \todo 
-                         * Get the function itself to decide what value of d 
-                         * to use next: ensure that we move at least 0.25 * 
-                         * stroke width, for example.  The derivative (as used 
-                         * for get_tangent before normalization) would be 
-                         * useful for estimating the appropriate d value.  Or 
-                         * perhaps just start with a small dstep and scale by 
-                         * some small number until we move >= 0.25 * 
-                         * stroke_width.  Must revert to the original dstep 
-                         * value for next iteration to avoid the problem 
+                        /** \todo
+                         * Get the function itself to decide what value of d
+                         * to use next: ensure that we move at least 0.25 *
+                         * stroke width, for example.  The derivative (as used
+                         * for get_tangent before normalization) would be
+                         * useful for estimating the appropriate d value.  Or
+                         * perhaps just start with a small dstep and scale by
+                         * some small number until we move >= 0.25 *
+                         * stroke_width.  Must revert to the original dstep
+                         * value for next iteration to avoid the problem
                          * mentioned above.
                          */
                }
@@ -363,7 +388,7 @@ sp_spiral_fit_and_draw (SPSpiral const *spiral,
        /** \todo
          * We should use better algorithm to specify maximum error.
          */
-       depth = sp_bezier_fit_cubic_full (bezier, NULL, darray, SAMPLE_SIZE,
+       depth = Geom::bezier_fit_cubic_full (bezier, NULL, darray, SAMPLE_SIZE,
                                          hat1, hat2,
                                          SPIRAL_TOLERANCE*SPIRAL_TOLERANCE,
                                          FITTING_MAX_BEZIERS);
@@ -375,8 +400,7 @@ sp_spiral_fit_and_draw (SPSpiral const *spiral,
 #endif
        if (depth != -1) {
                for (i = 0; i < 4*depth; i += 4) {
-                       sp_curve_curveto (c,
-                                         bezier[i + 1],
+                       c->curveto(bezier[i + 1],
                                          bezier[i + 2],
                                          bezier[i + 3]);
                }
@@ -385,7 +409,7 @@ sp_spiral_fit_and_draw (SPSpiral const *spiral,
                g_print ("cant_fit_cubic: t=%g\n", *t);
 #endif
                for (i = 1; i < SAMPLE_SIZE; i++)
-                       sp_curve_lineto (c, darray[i]);
+                       c->lineto(darray[i]);
        }
        *t = next_t;
        g_assert (is_unit_vector (hat2));
@@ -394,15 +418,15 @@ sp_spiral_fit_and_draw (SPSpiral const *spiral,
 static void
 sp_spiral_set_shape (SPShape *shape)
 {
-       NR::Point darray[SAMPLE_SIZE + 1];
+       Geom::Point darray[SAMPLE_SIZE + 1];
        double t;
 
        SPSpiral *spiral = SP_SPIRAL(shape);
 
        SP_OBJECT (spiral)->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
-       SPCurve *c = sp_curve_new ();
-       
+       SPCurve *c = new SPCurve ();
+
 #ifdef SPIRAL_VERBOSE
        g_print ("cx=%g, cy=%g, exp=%g, revo=%g, rad=%g, arg=%g, t0=%g\n",
                 spiral->cx,
@@ -415,13 +439,13 @@ sp_spiral_set_shape (SPShape *shape)
 #endif
 
        /* Initial moveto. */
-       sp_curve_moveto(c, sp_spiral_get_xy(spiral, spiral->t0));
+       c->moveto(sp_spiral_get_xy(spiral, spiral->t0));
 
        double const tstep = SAMPLE_STEP / spiral->revo;
        double const dstep = tstep / (SAMPLE_SIZE - 1);
 
-       NR::Point hat1 = sp_spiral_get_tangent (spiral, spiral->t0);
-       NR::Point hat2;
+       Geom::Point hat1 = sp_spiral_get_tangent (spiral, spiral->t0);
+       Geom::Point hat2;
        for (t = spiral->t0; t < (1.0 - tstep);) {
                sp_spiral_fit_and_draw (spiral, c, dstep, darray, hat1, hat2, &t);
 
@@ -431,8 +455,18 @@ sp_spiral_set_shape (SPShape *shape)
                sp_spiral_fit_and_draw (spiral, c, (1.0 - t)/(SAMPLE_SIZE - 1.0),
                                        darray, hat1, hat2, &t);
 
-       sp_shape_set_curve_insync ((SPShape *) spiral, c, TRUE);
-       sp_curve_unref (c);
+    /* Reset the shape'scurve to the "original_curve"
+     * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
+    sp_shape_set_curve_insync (shape, c, TRUE);
+    if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(shape)) && sp_lpe_item_path_effects_enabled(SP_LPE_ITEM(shape))) {
+        SPCurve *c_lpe = c->copy();
+        bool success = sp_lpe_item_perform_path_effect(SP_LPE_ITEM (shape), c_lpe);
+        if (success) {
+            sp_shape_set_curve_insync (shape, c_lpe, TRUE);
+        }
+        c_lpe->unref();
+    }
+    c->unref();
 }
 
 /**
@@ -452,7 +486,7 @@ sp_spiral_position_set       (SPSpiral          *spiral,
        g_return_if_fail (SP_IS_SPIRAL (spiral));
 
        /** \todo
-         * Consider applying CLAMP or adding in-bounds assertions for 
+         * Consider applying CLAMP or adding in-bounds assertions for
          * some of these parameters.
          */
        spiral->cx         = cx;
@@ -462,29 +496,47 @@ sp_spiral_position_set       (SPSpiral          *spiral,
        spiral->rad        = MAX (rad, 0.001);
        spiral->arg        = arg;
        spiral->t0         = CLAMP(t0, 0.0, 0.999);
-       
+
        ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 }
 
 /**
  * Virtual snappoints callback.
  */
-static void sp_spiral_snappoints(SPItem const *item, SnapPointsIter p)
+static void sp_spiral_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
 {
+       // We will determine the spiral's midpoint ourselves, instead of trusting on the base class
+       // Therefore setSnapObjectMidpoints() is set to false temporarily
+       Inkscape::SnapPreferences local_snapprefs = *snapprefs;
+       local_snapprefs.setSnapObjectMidpoints(false);
+
        if (((SPItemClass *) parent_class)->snappoints) {
-               ((SPItemClass *) parent_class)->snappoints (item, p);
+               ((SPItemClass *) parent_class)->snappoints (item, p, &local_snapprefs);
+       }
+
+       // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
+       if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
+               return;
+       }
+
+       if (snapprefs->getSnapObjectMidpoints()) {
+               Geom::Matrix const i2d (sp_item_i2d_affine (item));
+               SPSpiral *spiral = SP_SPIRAL(item);
+               *p = Geom::Point(spiral->cx, spiral->cy) * i2d;
+               // This point is the start-point of the spiral, which is also returned when _snap_to_itemnode has been set
+               // in the object snapper. In that case we will get a duplicate!
        }
 }
 
-/** 
+/**
  * Return one of the points on the spiral.
  *
  * \param t specifies how far along the spiral.
- * \pre \a t in [0.0, 2.03].  (It doesn't make sense for t to be much more 
- * than 1.0, though some callers go slightly beyond 1.0 for curve-fitting 
+ * \pre \a t in [0.0, 2.03].  (It doesn't make sense for t to be much more
+ * than 1.0, though some callers go slightly beyond 1.0 for curve-fitting
  * purposes.)
  */
-NR::Point sp_spiral_get_xy (SPSpiral const *spiral, gdouble t)
+Geom::Point sp_spiral_get_xy (SPSpiral const *spiral, gdouble t)
 {
        g_assert (spiral != NULL);
        g_assert (SP_IS_SPIRAL(spiral));
@@ -498,12 +550,12 @@ NR::Point sp_spiral_get_xy (SPSpiral const *spiral, gdouble t)
        double const rad = spiral->rad * pow(t, (double) spiral->exp);
        double const arg = 2.0 * M_PI * spiral->revo * t + spiral->arg;
 
-       return NR::Point(rad * cos (arg) + spiral->cx,
-                        rad * sin (arg) + spiral->cy);
+       return Geom::Point(rad * cos (arg) + spiral->cx,
+                           rad * sin (arg) + spiral->cy);
 }
 
 
-/** 
+/**
  * Returns the derivative of sp_spiral_get_xy with respect to t,
  *  scaled to a unit vector.
  *
@@ -512,10 +564,10 @@ NR::Point sp_spiral_get_xy (SPSpiral const *spiral, gdouble t)
  *  \pre p != NULL.
  *  \post is_unit_vector(*p).
  */
-static NR::Point
+static Geom::Point
 sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t)
 {
-       NR::Point ret(1.0, 0.0);
+       Geom::Point ret(1.0, 0.0);
        g_return_val_if_fail (( ( spiral != NULL )
                                && SP_IS_SPIRAL(spiral) ),
                              ret);
@@ -529,18 +581,18 @@ sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t)
        double const c = cos (arg);
 
        if (spiral->exp == 0.0) {
-               ret = NR::Point(-s, c);
+               ret = Geom::Point(-s, c);
        } else if (t_scaled == 0.0) {
-               ret = NR::Point(c, s);
+               ret = Geom::Point(c, s);
        } else {
-               NR::Point unrotated(spiral->exp, t_scaled);
+               Geom::Point unrotated(spiral->exp, t_scaled);
                double const s_len = L2 (unrotated);
                g_assert (s_len != 0);
-               /** \todo 
-                 * Check that this isn't being too hopeful of the hypot 
-                 * function.  E.g. test with numbers around 2**-1070 
-                 * (denormalized numbers), preferably on a few different 
-                 * platforms.  However, njh says that the usual implementation 
+               /** \todo
+                 * Check that this isn't being too hopeful of the hypot
+                 * function.  E.g. test with numbers around 2**-1070
+                 * (denormalized numbers), preferably on a few different
+                 * platforms.  However, njh says that the usual implementation
                  * does handle both very big and very small numbers.
                  */
                unrotated /= s_len;
@@ -548,16 +600,16 @@ sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t)
                /* ret = spiral->exp * (c, s) + t_scaled * (-s, c);
                   alternatively ret = (spiral->exp, t_scaled) * (( c, s),
                                                                  (-s, c)).*/
-               ret = NR::Point(dot(unrotated, NR::Point(c, -s)),
-                               dot(unrotated, NR::Point(s, c)));
+               ret = Geom::Point(dot(unrotated, Geom::Point(c, -s)),
+                                  dot(unrotated, Geom::Point(s, c)));
                /* ret should already be approximately normalized: the
                   matrix ((c, -s), (s, c)) is orthogonal (it just
                   rotates by arg), and unrotated has been normalized,
                   so ret is already of unit length other than numerical
                   error in the above matrix multiplication. */
 
-               /** \todo 
-                 * I haven't checked how important it is for ret to be very 
+               /** \todo
+                 * I haven't checked how important it is for ret to be very
                  * near unit length; we could get rid of the below.
                  */