Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / sp-shape.cpp
index 3b3e1108b94be13666afe6cf343ebeb7d27eb871..35364692b29b680a2841d79f42c801c945de3858 100644 (file)
@@ -3,11 +3,13 @@
  *
  * Author:
  *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Abhishek Sharma
  *
  * Copyright (C) 1999-2002 Lauris Kaplinski
  * Copyright (C) 2000-2001 Ximian, Inc.
  * Copyright (C) 2004 John Cliff
  * Copyright (C) 2007-2008 Johan Engelen
+ * Copyright (C) 2010      Jon A. Cruz <jon@joncruz.org>
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
@@ -24,6 +26,7 @@
 #include <2geom/transforms.h>
 #include <2geom/pathvector.h>
 #include <2geom/path-intersection.h>
+#include <2geom/exception.h>
 #include "helper/geom.h"
 #include "helper/geom-nodetype.h"
 
 
 #define noSHAPE_VERBOSE
 
-/*static void sp_shape_class_init (SPShapeClass *klass);
-static void sp_shape_init (SPShape *shape);
-static void sp_shape_finalize (GObject *object);
-
-static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
-static void sp_shape_release (SPObject *object);
-
-static void sp_shape_set(SPObject *object, unsigned key, gchar const *value);
-static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
-static void sp_shape_modified (SPObject *object, unsigned int flags);
-static Inkscape::XML::Node *sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
-
-static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags);*/
 void sp_shape_print (SPItem * item, SPPrintContext * ctx);
-/*static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
-static void sp_shape_hide (SPItem *item, unsigned int key);
-static void sp_shape_snappoints (SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs);
-
-static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
-
-static SPLPEItemClass *parent_class;*/
 
-SPLPEItemClass * SPShapeClass::parent_class=0;
+SPLPEItemClass * SPShapeClass::parent_class = 0;
 
 /**
  * Registers the SPShape class with Gdk and returns its type number.
  */
-GType
-SPShape::getType (void)
+GType SPShape::getType(void)
 {
     static GType type = 0;
     if (!type) {
@@ -105,8 +87,7 @@ SPShape::getType (void)
  * Initializes a SPShapeClass object.  Establishes the function pointers to the class'
  * member routines in the class vtable, and sets pointers to parent classes.
  */
-void
-SPShapeClass::sp_shape_class_init (SPShapeClass *klass)
+void SPShapeClass::sp_shape_class_init(SPShapeClass *klass)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
     SPObjectClass *sp_object_class = SP_OBJECT_CLASS(klass);
@@ -137,8 +118,7 @@ SPShapeClass::sp_shape_class_init (SPShapeClass *klass)
 /**
  * Initializes an SPShape object.
  */
-void
-SPShape::sp_shape_init (SPShape *shape)
+void SPShape::sp_shape_init(SPShape *shape)
 {
     for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
         new (&shape->release_connect[i]) sigc::connection();
@@ -148,8 +128,7 @@ SPShape::sp_shape_init (SPShape *shape)
     shape->curve = NULL;
 }
 
-void
-SPShape::sp_shape_finalize (GObject *object)
+void SPShape::sp_shape_finalize(GObject *object)
 {
     SPShape *shape=(SPShape *)object;
 
@@ -172,8 +151,7 @@ SPShape::sp_shape_finalize (GObject *object)
  *
  * \see sp_object_build()
  */
-void
-SPShape::sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+void SPShape::sp_shape_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
 {
     if (((SPObjectClass *) (SPShapeClass::parent_class))->build) {
        (*((SPObjectClass *) (SPShapeClass::parent_class))->build) (object, document, repr);
@@ -194,8 +172,7 @@ SPShape::sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::
  *
  * \see sp_object_release()
  */
-void
-SPShape::sp_shape_release (SPObject *object)
+void SPShape::sp_shape_release(SPObject *object)
 {
     SPItem *item;
     SPShape *shape;
@@ -226,16 +203,14 @@ SPShape::sp_shape_release (SPObject *object)
 
 
 
-void
-SPShape::sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
+void SPShape::sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
 {
     if (((SPObjectClass *) SPShapeClass::parent_class)->set) {
         ((SPObjectClass *) SPShapeClass::parent_class)->set(object, key, value);
     }
 }
 
-Inkscape::XML::Node *
-SPShape::sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
+Inkscape::XML::Node * SPShape::sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
 {
     if (((SPObjectClass *)(SPShapeClass::parent_class))->write) {
         ((SPObjectClass *)(SPShapeClass::parent_class))->write(object, doc, repr, flags);
@@ -248,8 +223,7 @@ SPShape::sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape
  * Updates the shape when its attributes have changed.  Also establishes
  * marker objects to match the style settings.
  */
-void
-SPShape::sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
+void SPShape::sp_shape_update(SPObject *object, SPCtx *ctx, unsigned int flags)
 {
     SPItem *item = (SPItem *) object;
     SPShape *shape = (SPShape *) object;
@@ -333,8 +307,7 @@ SPShape::sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
  * Reference for behaviour of zero-length segments:
  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
  */
-Geom::Matrix
-sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
+Geom::Matrix sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
 {
     Geom::Point p = c1.pointAt(1);
     Geom::Curve * c1_reverse = c1.reverse();
@@ -360,8 +333,8 @@ sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
 
     return Geom::Rotate(ret_angle) * Geom::Translate(p);
 }
-Geom::Matrix
-sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
+
+Geom::Matrix sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
 {
     Geom::Point p = c.pointAt(0);
     Geom::Matrix ret = Geom::Translate(p);
@@ -377,8 +350,8 @@ sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
 
     return ret;
 }
-Geom::Matrix
-sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
+
+Geom::Matrix sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
 {
     Geom::Point p = c.pointAt(1);
     Geom::Matrix ret = Geom::Translate(p);
@@ -404,15 +377,16 @@ sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
  *
  * @todo figure out what to do when both 'marker' and for instance 'marker-end' are set.
  */
-void
-SPShape::sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
+void SPShape::sp_shape_update_marker_view(SPShape *shape, NRArenaItem *ai)
 {
     SPStyle *style = ((SPObject *) shape)->style;
 
     // position arguments to sp_marker_show_instance, basically counts the amount of markers.
     int counter[4] = {0};
 
+    if (!shape->curve) return;
     Geom::PathVector const & pathv = shape->curve->get_pathvector();
+    if (pathv.empty()) return;
 
     // the first vertex should get a start marker, the last an end marker, and all the others a mid marker
     // see bug 456148
@@ -513,8 +487,7 @@ SPShape::sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
 /**
  * Sets modified flag for all sub-item views.
  */
-void
-SPShape::sp_shape_modified (SPObject *object, unsigned int flags)
+void SPShape::sp_shape_modified(SPObject *object, unsigned int flags)
 {
     SPShape *shape = SP_SHAPE (object);
 
@@ -587,7 +560,7 @@ void SPShape::sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const
                     }
 
                     // Union with bboxes of the markers, if any
-                    if (shape->hasMarkers ()) {
+                    if ( shape->hasMarkers()  && !shape->curve->get_pathvector().empty() ) {
                         /** \todo make code prettier! */
                         Geom::PathVector const & pathv = shape->curve->get_pathvector();
                         // START marker
@@ -769,6 +742,9 @@ sp_shape_print (SPItem *item, SPPrintContext *ctx)
 
     if (!shape->curve) return;
 
+    Geom::PathVector const & pathv = shape->curve->get_pathvector();
+    if (pathv.empty()) return;
+
         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
         gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
         if (add_comments) {
@@ -790,15 +766,14 @@ sp_shape_print (SPItem *item, SPPrintContext *ctx)
     SPStyle* style = SP_OBJECT_STYLE (item);
 
     if (!style->fill.isNone()) {
-        sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
+        sp_print_fill (ctx, pathv, &i2d, style, &pbox, &dbox, &bbox);
     }
 
     if (!style->stroke.isNone()) {
-        sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
+        sp_print_stroke (ctx, pathv, &i2d, style, &pbox, &dbox, &bbox);
     }
 
     /** \todo make code prettier */
-    Geom::PathVector const & pathv = shape->curve->get_pathvector();
     // START marker
     for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START    
         if ( shape->marker[i] ) {
@@ -873,8 +848,7 @@ sp_shape_print (SPItem *item, SPPrintContext *ctx)
 /**
  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
  */
-NRArenaItem *
-SPShape::sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
+NRArenaItem * SPShape::sp_shape_show(SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
 {
     SPObject *object = SP_OBJECT(item);
     SPShape *shape = SP_SHAPE(item);
@@ -922,8 +896,7 @@ SPShape::sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsi
 /**
  * Hides/removes marker views from the shape.
  */
-void
-SPShape::sp_shape_hide (SPItem *item, unsigned int key)
+void SPShape::sp_shape_hide(SPItem *item, unsigned int key)
 {
     SPShape *shape;
     SPItemView *v;
@@ -951,8 +924,7 @@ SPShape::sp_shape_hide (SPItem *item, unsigned int key)
 * \param shape Shape.
 * \return TRUE if the shape has any markers, or FALSE if not.
 */
-int
-SPShape::hasMarkers () const
+int SPShape::hasMarkers() const
 {
     /* Note, we're ignoring 'marker' settings, which technically should apply for
        all three settings.  This should be fixed later such that if 'marker' is
@@ -973,8 +945,7 @@ SPShape::hasMarkers () const
 * \param type Marker type (e.g. SP_MARKER_LOC_START)
 * \return Number of markers that the shape has of this type.
 */
-int
-SPShape::numberOfMarkers (int type)
+int SPShape::numberOfMarkers(int type)
 {
     Geom::PathVector const & pathv = this->curve->get_pathvector();
     if (pathv.size() == 0) {
@@ -1059,7 +1030,7 @@ static void
 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
 {
     /* I think mask does update automagically */
-    /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
+    /* g_warning ("Item %s mask %s modified", item->getId(), mask->getId()); */
 }
 
 /**
@@ -1114,12 +1085,8 @@ sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
 /**
  * Calls any registered handlers for the set_shape action
  */
-void
-SPShape::setShape ()
+void SPShape::setShape()
 {
-    //g_return_if_fail (shape != NULL);
-    //g_return_if_fail (SP_IS_SHAPE (shape));
-
     if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (this))->set_shape) {
       SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (this))->set_shape (this);
     }
@@ -1131,8 +1098,7 @@ SPShape::setShape ()
  * Any existing curve in the shape will be unreferenced first.
  * This routine also triggers a request to update the display.
  */
-void
-SPShape::setCurve (SPCurve *curve, unsigned int owner)
+void SPShape::setCurve(SPCurve *curve, unsigned int owner)
 {
     if (this->curve) {
         this->curve = this->curve->unref();
@@ -1150,8 +1116,7 @@ SPShape::setCurve (SPCurve *curve, unsigned int owner)
 /**
  * Return duplicate of curve (if any exists) or NULL if there is no curve
  */
-SPCurve *
-SPShape::getCurve ()
+SPCurve * SPShape::getCurve()
 {
     if (this->curve) {
         return this->curve->copy();
@@ -1162,8 +1127,7 @@ SPShape::getCurve ()
 /**
  * Same as sp_shape_set_curve but without updating the display
  */
-void
-SPShape::setCurveInsync (SPCurve *curve, unsigned int owner)
+void SPShape::setCurveInsync(SPCurve *curve, unsigned int owner)
 {
     if (this->curve) {
         this->curve = this->curve->unref();
@@ -1210,49 +1174,55 @@ void SPShape::sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::Snap
 
     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
         if (snapprefs->getSnapToItemNode()) {
+            // Add the first point of the path
             p.push_back(Inkscape::SnapCandidatePoint(path_it->initialPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
         }
 
         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
-        while (curve_it2 != path_it->end_closed())
+        while (curve_it1 != path_it->end_default())
         {
-            /* Test whether to add the node between curve_it1 and curve_it2.
-             * Loop to end_closed (so always including closing segment); the last node to be added
-             * is the node between the closing segment and the segment before that, regardless
-             * of the path being closed or not. If the path is closed, the final point was already added by
-             * adding the initial point. */
-
-            Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
-
-            bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
-            bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
-
-            if (c1 || c2) {
-                Inkscape::SnapSourceType sst;
-                Inkscape::SnapTargetType stt;
-                switch (nodetype) {
-                case Geom::NODE_CUSP:
-                    sst = Inkscape::SNAPSOURCE_NODE_CUSP;
-                    stt = Inkscape::SNAPTARGET_NODE_CUSP;
-                    break;
-                case Geom::NODE_SMOOTH:
-                case Geom::NODE_SYMM:
-                    sst = Inkscape::SNAPSOURCE_NODE_SMOOTH;
-                    stt = Inkscape::SNAPTARGET_NODE_SMOOTH;
-                    break;
-                default:
-                    sst = Inkscape::SNAPSOURCE_UNDEFINED;
-                    stt = Inkscape::SNAPTARGET_UNDEFINED;
-                    break;
+            // For each path: consider midpoints of line segments for snapping
+            if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforces strict snapping)
+                if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
+                    p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
                 }
-                p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt));
             }
 
-            // Consider midpoints of line segments for snapping
-            if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping)
-                if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
-                    p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
+            if (curve_it2 == path_it->end_default()) { // Test will only pass for the last iteration of the while loop
+                if (snapprefs->getSnapToItemNode() && !path_it->closed()) {
+                    // Add the last point of the path, but only for open paths
+                    // (for closed paths the first and last point will coincide)
+                    p.push_back(Inkscape::SnapCandidatePoint((*curve_it1).finalPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
+                }
+            } else {
+                /* Test whether to add the node between curve_it1 and curve_it2.
+                 * Loop to end_default (so only iterating through the stroked part); */
+
+                Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
+
+                bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
+                bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
+
+                if (c1 || c2) {
+                    Inkscape::SnapSourceType sst;
+                    Inkscape::SnapTargetType stt;
+                    switch (nodetype) {
+                    case Geom::NODE_CUSP:
+                        sst = Inkscape::SNAPSOURCE_NODE_CUSP;
+                        stt = Inkscape::SNAPTARGET_NODE_CUSP;
+                        break;
+                    case Geom::NODE_SMOOTH:
+                    case Geom::NODE_SYMM:
+                        sst = Inkscape::SNAPSOURCE_NODE_SMOOTH;
+                        stt = Inkscape::SNAPTARGET_NODE_SMOOTH;
+                        break;
+                    default:
+                        sst = Inkscape::SNAPSOURCE_UNDEFINED;
+                        stt = Inkscape::SNAPTARGET_UNDEFINED;
+                        break;
+                    }
+                    p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt));
                 }
             }
 
@@ -1264,13 +1234,19 @@ void SPShape::sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::Snap
         // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes())
         if (snapprefs->getSnapIntersectionCS()) {
             Geom::Crossings cs;
-            cs = self_crossings(*path_it);
-            if (cs.size() > 0) { // There might be multiple intersections...
-                for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
-                    Geom::Point p_ix = (*path_it).pointAt((*i).ta);
-                    p.push_back(Inkscape::SnapCandidatePoint(p_ix * i2d, Inkscape::SNAPSOURCE_PATH_INTERSECTION, Inkscape::SNAPTARGET_PATH_INTERSECTION));
+            try {
+                cs = self_crossings(*path_it);
+                if (cs.size() > 0) { // There might be multiple intersections...
+                    for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
+                        Geom::Point p_ix = (*path_it).pointAt((*i).ta);
+                        p.push_back(Inkscape::SnapCandidatePoint(p_ix * i2d, Inkscape::SNAPSOURCE_PATH_INTERSECTION, Inkscape::SNAPTARGET_PATH_INTERSECTION));
+                    }
                 }
+            } catch (Geom::RangeError &e) {
+                // do nothing
+                // The exception could be Geom::InfiniteSolutions: then no snappoints should be added
             }
+
         }
     }
 
@@ -1285,4 +1261,4 @@ void SPShape::sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::Snap
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :