Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / conn-avoid-ref.cpp
index c04ad9e49a4cb2d3a0c122653faf16a986bf721e..21ef2deab53c14bd87502c68c47648ea9c14d249 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Authors:
  *   Michael Wybrow <mjwybrow@users.sourceforge.net>
+ *   Abhishek Sharma
  *
  * Copyright (C) 2005 Michael Wybrow
  *
@@ -19,6 +20,7 @@
 #include "2geom/line.h"
 #include "2geom/crossing.h"
 #include "2geom/convex-cover.h"
+#include "helper/geom-curves.h"
 #include "svg/stringstream.h"
 #include "conn-avoid-ref.h"
 #include "connection-points.h"
 #include "libavoid/router.h"
 #include "libavoid/connector.h"
 #include "libavoid/geomtypes.h"
+#include "libavoid/shape.h"
 #include "xml/node.h"
 #include "document.h"
 #include "desktop.h"
 #include "desktop-handles.h"
 #include "sp-namedview.h"
+#include "sp-item-group.h"
 #include "inkscape.h"
 #include <glibmm/i18n.h>
 
-
+using Inkscape::DocumentUndo;
 
 using Avoid::Router;
 
@@ -56,13 +60,13 @@ SPAvoidRef::~SPAvoidRef()
 {
     _transformed_connection.disconnect();
 
-    // If the document is being destroyed then the router instance 
+    // If the document is being destroyed then the router instance
     // and the ShapeRefs will have been destroyed with it.
     const bool routerInstanceExists = (item->document->router != NULL);
 
     if (shapeRef && routerInstanceExists) {
-        Router *router = shapeRef->router();
-        router->removeShape(shapeRef);
+        // Deleting the shapeRef will remove it completely from 
+        // an existing Router instance.
         delete shapeRef;
     }
     shapeRef = NULL;
@@ -103,10 +107,10 @@ void SPAvoidRef::setConnectionPoints(gchar const *value)
            Update the connectors for which
            the endpoint has changed.
         */
-        
+
         gchar ** strarray = g_strsplit(value, "|", 0);
         gchar ** iter = strarray;
-        
+
         while (*iter != NULL) {
             ConnectionPoint cp;
             Inkscape::SVGIStringStream is(*iter);
@@ -167,17 +171,19 @@ void SPAvoidRef::setConnectionPoints(gchar const *value)
     {
         SPPath* path = SP_PATH(i->data);
         SPConnEnd** connEnds = path->connEndPair.getConnEnds();
-        for (int ix=0; ix<2; ++ix)
-            if (connEnds[ix]->type == ConnPointUserDefined)
-                if (updates.find(connEnds[ix]->id) != updates.end())
-                    if (path->connEndPair.isAutoRoutingConn())
+        for (int ix=0; ix<2; ++ix) {
+            if (connEnds[ix]->type == ConnPointUserDefined) {
+                if (updates.find(connEnds[ix]->id) != updates.end()) {
+                    if (path->connEndPair.isAutoRoutingConn()) {
                         path->connEndPair.tellLibavoidNewEndpoints();
-                    else
-                    {
+                    } else {
                     }
-                else
-                    if (deletes.find(connEnds[ix]->id) != deletes.end())
-                        sp_conn_end_detach(path, ix);
+                }
+                else if (deletes.find(connEnds[ix]->id) != deletes.end()) {
+                    sp_conn_end_detach(path, ix);
+                }
+            }
+        }
     }
     g_slist_free(conns);
     // Remove all deleted connection points
@@ -189,11 +195,11 @@ void SPAvoidRef::setConnectionPoints(gchar const *value)
 void SPAvoidRef::setConnectionPointsAttrUndoable(const gchar* value, const gchar* action)
 {
     SPDocument* doc = SP_OBJECT_DOCUMENT(item);
-    
-    sp_object_setAttribute( SP_OBJECT(item), "inkscape:connection-points", value, 0 );
+
+    item->setAttribute( "inkscape:connection-points", value, 0 );
     item->updateRepr();
-    sp_document_ensure_up_to_date(doc);
-    sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, action);
+    doc->ensureUpToDate();
+    DocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR, action);
 }
 
 void SPAvoidRef::addConnectionPoint(ConnectionPoint &cp)
@@ -229,8 +235,8 @@ void SPAvoidRef::addConnectionPoint(ConnectionPoint &cp)
     }
     else
         ostr<<'|'<<cp;
-        
-    this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Added a new connection point") );
+
+    this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Add a new connection point") );
 }
 
 void SPAvoidRef::updateConnectionPoint(ConnectionPoint &cp)
@@ -255,7 +261,7 @@ void SPAvoidRef::updateConnectionPoint(ConnectionPoint &cp)
             else
                 ostr<<'|'<<*to_write;
         }
-        this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Moved a connection point") );
+        this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Move a connection point") );
     }
 }
 
@@ -263,21 +269,19 @@ void SPAvoidRef::deleteConnectionPoint(ConnectionPoint &cp)
 {
     Inkscape::SVGOStringStream ostr;
     IdConnectionPointMap::iterator cp_pos = connection_points.find( cp.id );
-    if ( cp_pos != connection_points.end() )
-    {
+    if ( cp_pos != connection_points.end() ) {
         bool first = true;
-        for (IdConnectionPointMap::iterator it = connection_points.begin(); it != connection_points.end(); ++it)
-        {
-            if ( it != cp_pos )
-                if ( first )
-                {
+        for (IdConnectionPointMap::iterator it = connection_points.begin(); it != connection_points.end(); ++it) {
+            if ( it != cp_pos ) {
+                if ( first ) {
                     first = false;
                     ostr<<it->second;
-                }
-                else
+                } else {
                     ostr<<'|'<<it->second;
+                }
+            }
         }
-        this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Removed a connection point") );
+        this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Remove a connection point") );
     }
 }
 
@@ -292,7 +296,7 @@ void SPAvoidRef::handleSettingChange(void)
         // isn't the same as the document that this item is part of.  This
         // case can happen if a new document is loaded from the file chooser
         // or via the recent file menu.  In this case, we can end up here
-        // as a rersult of a sp_document_ensure_up_to_date performed on a
+        // as a rersult of a ensureUpToDate performed on a
         // document not yet attached to the active desktop.
         return;
     }
@@ -304,7 +308,7 @@ void SPAvoidRef::handleSettingChange(void)
     setting = new_setting;
 
     Router *router = item->document->router;
-    
+
     _transformed_connection.disconnect();
     if (new_setting) {
         Avoid::Polygon poly = avoid_item_poly(item);
@@ -312,22 +316,23 @@ void SPAvoidRef::handleSettingChange(void)
             _transformed_connection = item->connectTransformed(
                     sigc::ptr_fun(&avoid_item_move));
 
-            const char *id = SP_OBJECT_REPR(item)->attribute("id");
+            char const *id = item->getAttribute("id");
             g_assert(id != NULL);
-            
+
             // Get a unique ID for the item.
             GQuark itemID = g_quark_from_string(id);
 
             shapeRef = new Avoid::ShapeRef(router, poly, itemID);
-        
+
             router->addShape(shapeRef);
         }
     }
     else
     {
         g_assert(shapeRef);
-        
-        router->removeShape(shapeRef);
+
+        // Deleting the shapeRef will remove it completely from 
+        // an existing Router instance.
         delete shapeRef;
         shapeRef = NULL;
     }
@@ -339,9 +344,9 @@ GSList *SPAvoidRef::getAttachedShapes(const unsigned int type)
     GSList *list = NULL;
 
     Avoid::IntList shapes;
-    GQuark shapeId = g_quark_from_string(item->id);
+    GQuark shapeId = g_quark_from_string(item->getId());
     item->document->router->attachedShapes(shapes, shapeId, type);
-    
+
     Avoid::IntList::iterator finish = shapes.end();
     for (Avoid::IntList::iterator i = shapes.begin(); i != finish; ++i) {
         const gchar *connId = g_quark_to_string(*i);
@@ -363,9 +368,9 @@ GSList *SPAvoidRef::getAttachedConnectors(const unsigned int type)
     GSList *list = NULL;
 
     Avoid::IntList conns;
-    GQuark shapeId = g_quark_from_string(item->id);
+    GQuark shapeId = g_quark_from_string(item->getId());
     item->document->router->attachedConns(conns, shapeId, type);
-    
+
     Avoid::IntList::iterator finish = conns.end();
     for (Avoid::IntList::iterator i = conns.begin(); i != finish; ++i) {
         const gchar *connId = g_quark_to_string(*i);
@@ -385,13 +390,12 @@ Geom::Point SPAvoidRef::getConnectionPointPos(const int type, const int id)
 {
     g_assert(item);
     Geom::Point pos;
-    const Geom::Matrix& transform = sp_item_i2doc_affine(item);
-    SPDesktop *desktop = inkscape_active_desktop();
+    const Geom::Matrix& transform = item->i2doc_affine();
 
     if ( type == ConnPointDefault )
     {
         // For now, just default to the centre of the item
-        Geom::OptRect bbox = item->getBounds(sp_item_i2doc_affine(item));
+        Geom::OptRect bbox = item->getBounds(item->i2doc_affine());
         pos = (bbox) ? bbox->midpoint() : Geom::Point(0, 0);
     }
     else
@@ -419,146 +423,145 @@ bool SPAvoidRef::isValidConnPointId( const int type, const int id )
         else
             return connection_points.find( id ) != connection_points.end();
     }
-    
+
     return true;
 }
 
-static Avoid::Polygon avoid_item_poly(SPItem const *item)
+static std::vector<Geom::Point> approxCurveWithPoints(SPCurve *curve)
 {
-    SPDesktop *desktop = inkscape_active_desktop();
-    g_assert(desktop != NULL);
-
-    // TODO: The right way to do this is to return the convex hull of
-    //       the object, or an approximation in the case of a rounded
-    //       object.  Specific SPItems will need to have a new
-    //       function that returns points for the convex hull.
-    //       For some objects it is enough to feed the snappoints to
-    //       some convex hull code, though not NR::ConvexHull as this
-    //       only keeps the bounding box of the convex hull currently.
-
-    double spacing = desktop->namedview->connector_spacing;
-
-    // [sommer] If item is a shape, use an approximation of its convex hull
+    // The number of segments to use for not straight curves approximation
+    const unsigned NUM_SEGS = 4;
+    
+    const Geom::PathVector& curve_pv = curve->get_pathvector();
+   
+    // The structure to hold the output
+    std::vector<Geom::Point> poly_points;
+
+    // Iterate over all curves, adding the endpoints for linear curves and
+    // sampling the other curves
+    double seg_size = 1.0 / NUM_SEGS;
+    double at;
+    at = 0;
+    Geom::PathVector::const_iterator pit = curve_pv.begin();
+    while (pit != curve_pv.end())
     {
-        // MJW: Disable this for the moment.  It still has some issues.
-        const bool convex_hull_approximation_enabled = false;
-
-        if ( convex_hull_approximation_enabled && SP_IS_SHAPE (item) ) {
-            // The number of points to use for approximation
-            const unsigned NUM_POINTS = 64; 
-
-//             printf("[sommer] is a shape\n");
-            SPCurve* curve = sp_shape_get_curve (SP_SHAPE (item));
-            if (curve) {
-//                 printf("[sommer] is a curve\n");
-
-                // apply all transformations
-                Geom::Matrix itd_mat = sp_item_i2doc_affine(item);
-                curve->transform(itd_mat);
-
-                // iterate over all paths
-                const Geom::PathVector& curve_pv = curve->get_pathvector();
-                std::vector<Geom::Point> hull_points;
-                for (Geom::PathVector::const_iterator i = curve_pv.begin(); i != curve_pv.end(); i++) {
-                    const Geom::Path& curve_pv_path = *i; 
-//                     printf("[sommer] tracing sub-path\n");
-
-                    // FIXME: enlarge path by "desktop->namedview->connector_spacing" (using sp_selected_path_do_offset)?
-
-                    // use appropriate fraction of points for this path (first one gets any remainder)
-                    unsigned num_points = NUM_POINTS / curve_pv.size();
-                    if (i == curve_pv.begin()) num_points += NUM_POINTS - (num_points * curve_pv.size());
-                    printf("[sommer] using %d points for this path\n", num_points);
-
-                    // sample points along the path for approximation of convex hull
-                    for (unsigned n = 0; n < num_points; n++) {
-                        double at = curve_pv_path.size() / static_cast<double>(num_points) * n;                        
-                        Geom::Point pt = curve_pv_path.pointAt(at);
-                        hull_points.push_back(pt);
-                    }
-                }
-
-                curve->unref();
-
-                // create convex hull from all sampled points
-                Geom::ConvexHull hull(hull_points);
-
-                // store expanded convex hull in Avoid::Polygn
-                unsigned n = 0;
-                Avoid::Polygon poly;
-                const Geom::Point& old_pt = *hull.boundary.begin();
-
-                Geom::Line hull_edge(*hull.boundary.begin(), *(hull.boundary.begin()+1));
-                Geom::Line parallel_hull_edge;
-                parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
-                parallel_hull_edge.versor(hull_edge.versor());
-                Geom::Line bisector = Geom::make_angle_bisector_line( *(hull.boundary.end()), *hull.boundary.begin(),
-                                                                      *(hull.boundary.begin()+1));
-                Geom::OptCrossing int_pt = Geom::intersection(parallel_hull_edge, bisector);
+        Geom::Path::const_iterator cit = pit->begin();
+        while (cit != pit->end())
+        {
+            if (cit == pit->begin())
+            {
+                poly_points.push_back(cit->initialPoint());
+            }
 
-                if (int_pt)
+            if (dynamic_cast<Geom::CubicBezier const*>(&*cit))
+            {
+                at += seg_size;
+                if (at <= 1.0 )
+                    poly_points.push_back(cit->pointAt(at));
+                else
                 {
-                    Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], 
-                                            (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);
-//                     printf("[sommer] %f, %f\n", old_pt[Geom::X], old_pt[Geom::Y]);
-/*                    printf("[sommer] %f, %f\n", (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], 
-                                                (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);*/
-                    poly.ps.push_back(avoid_pt);
-                }
-                for (std::vector<Geom::Point>::const_iterator i = hull.boundary.begin() + 1; i != hull.boundary.end(); i++, n++) {
-                        const Geom::Point& old_pt = *i;
-                        Geom::Line hull_edge(*i, *(i+1));
-                        Geom::Line parallel_hull_edge;
-                        parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
-                        parallel_hull_edge.versor(hull_edge.versor());
-                        Geom::Line bisector = Geom::make_angle_bisector_line( *(i-1), *i, *(i+1));
-                        Geom::OptCrossing intersect_pt = Geom::intersection(parallel_hull_edge, bisector);
-
-                        if (int_pt)
-                        {
-                            Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], 
-                                                  (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);
-/*                            printf("[sommer] %f, %f\n", old_pt[Geom::X], old_pt[Geom::Y]);
-                            printf("[sommer] %f, %f\n", (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], 
-                                                        (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);*/
-                            poly.ps.push_back(avoid_pt);
-                        }
+                    at = 0.0;
+                    ++cit;
                 }
-                
+            }
+            else
+            {
+                poly_points.push_back(cit->finalPoint());
+                ++cit;
+            }
+        }
+        ++pit;
+    }
+    return poly_points;
+}
 
-                return poly;
-            }// else printf("[sommer] is no curve\n");
-        }// else printf("[sommer] is no shape\n");
+static std::vector<Geom::Point> approxItemWithPoints(SPItem const *item, const Geom::Matrix& item_transform)
+{
+    // The structure to hold the output
+    std::vector<Geom::Point> poly_points;
+
+    if (SP_IS_GROUP(item))
+    {
+        SPGroup* group = SP_GROUP(item);
+        // consider all first-order children
+        for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) {
+            SPItem* child_item = SP_ITEM(i->data);
+            std::vector<Geom::Point> child_points = approxItemWithPoints(child_item, item_transform * child_item->transform);
+            poly_points.insert(poly_points.end(), child_points.begin(), child_points.end());
+        }
     }
-    
-    Geom::OptRect rHull = item->getBounds(sp_item_i2doc_affine(item));
-    if (!rHull) {
-        return Avoid::Polygon();
+    else if (SP_IS_SHAPE(item))
+    {
+        SPCurve* item_curve = SP_SHAPE(item)->getCurve();
+        // make sure it has an associated curve
+        if (item_curve)
+        {
+            // apply transformations (up to common ancestor)
+            item_curve->transform(item_transform);
+            std::vector<Geom::Point> curve_points = approxCurveWithPoints(item_curve);
+            poly_points.insert(poly_points.end(), curve_points.begin(), curve_points.end());
+            item_curve->unref();
+        }
     }
 
-    // Add a little buffer around the edge of each object.
-    Geom::Rect rExpandedHull = *rHull;
-    rExpandedHull.expandBy(spacing); 
-    Avoid::Polygon poly(4);
+    return poly_points;
+}
+static Avoid::Polygon avoid_item_poly(SPItem const *item)
+{
+    SPDesktop *desktop = inkscape_active_desktop();
+    g_assert(desktop != NULL);
+    double spacing = desktop->namedview->connector_spacing;
+
+    Geom::Matrix itd_mat = item->i2doc_affine();
+    std::vector<Geom::Point> hull_points;
+    hull_points = approxItemWithPoints(item, itd_mat);
 
-    for (size_t n = 0; n < 4; ++n) {
-        Geom::Point hullPoint = rExpandedHull.corner(n);
-        poly.ps[n].x = hullPoint[Geom::X];
-        poly.ps[n].y = hullPoint[Geom::Y];
-    }
+    // create convex hull from all sampled points
+    Geom::ConvexHull hull(hull_points);
+
+    // enlarge path by "desktop->namedview->connector_spacing"
+    // store expanded convex hull in Avoid::Polygn
+    Avoid::Polygon poly;
 
+    Geom::Line hull_edge(hull[-1], hull[0]);
+    Geom::Line prev_parallel_hull_edge;
+    prev_parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
+    prev_parallel_hull_edge.versor(hull_edge.versor());
+    int hull_size = hull.boundary.size();
+    for (int i = 0; i < hull_size; ++i)
+    {
+        hull_edge.setBy2Points(hull[i], hull[i+1]);
+        Geom::Line parallel_hull_edge;
+        parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
+        parallel_hull_edge.versor(hull_edge.versor());
+        
+        // determine the intersection point
+        
+        Geom::OptCrossing int_pt = Geom::intersection(parallel_hull_edge, prev_parallel_hull_edge);
+        if (int_pt)
+        {
+            Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X],
+                                    (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);
+            poly.ps.push_back(avoid_pt);
+        }
+        else
+        {
+            // something went wrong...
+            std::cout<<"conn-avoid-ref.cpp: avoid_item_poly: Geom:intersection failed."<<std::endl;
+        }
+        prev_parallel_hull_edge = parallel_hull_edge;
+    }
     return poly;
 }
 
 
-GSList *get_avoided_items(GSList *list, SPObject *from, SPDesktop *desktop, 
+GSList *get_avoided_items(GSList *list, SPObject *from, SPDesktop *desktop,
         bool initialised)
 {
-    for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ;
-            child != NULL; child = SP_OBJECT_NEXT(child) ) {
+    for (SPObject *child = from->firstChild() ; child != NULL; child = child->next ) {
         if (SP_IS_ITEM(child) &&
             !desktop->isLayer(SP_ITEM(child)) &&
-            !SP_ITEM(child)->isLocked() && 
+            !SP_ITEM(child)->isLocked() &&
             !desktop->itemIsHidden(SP_ITEM(child)) &&
             (!initialised || SP_ITEM(child)->avoidRef->shapeRef)
             )
@@ -593,9 +596,9 @@ void init_avoided_shape_geometry(SPDesktop *desktop)
     // Don't count this as changes to the document,
     // it is basically just late initialisation.
     SPDocument *document = sp_desktop_document(desktop);
-    bool saved = sp_document_get_undo_sensitive(document);
-    sp_document_set_undo_sensitive(document, false);
-    
+    bool saved = DocumentUndo::getUndoSensitive(document);
+    DocumentUndo::setUndoSensitive(document, false);
+
     bool initialised = false;
     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop,
             initialised);
@@ -608,7 +611,7 @@ void init_avoided_shape_geometry(SPDesktop *desktop)
     if (items) {
         g_slist_free(items);
     }
-    sp_document_set_undo_sensitive(document, saved);
+    DocumentUndo::setUndoSensitive(document, saved);
 }