Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / marker.cpp
index 68b630920fa6a56a1f01af9127c66bc0626520f1..0ec92a1ff2147b00573ad9703e86d10a7628083c 100644 (file)
@@ -1,34 +1,42 @@
-#define __SP_MARKER_C__
-
 /*
  * SVG <marker> implementation
  *
  * Authors:
  *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Bryce Harrington <bryce@bryceharrington.org>
+ *   Abhishek Sharma
  *
  * Copyright (C) 1999-2003 Lauris Kaplinski
+ *               2004-2006 Bryce Harrington
+ *               2008      Johan Engelen
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
+#include <cstring>
+#include <string>
 #include "config.h"
 
 
 #include "libnr/nr-matrix-fns.h"
 #include "libnr/nr-matrix-ops.h"
+#include "libnr/nr-matrix-translate-ops.h"
 #include "libnr/nr-scale-matrix-ops.h"
-#include "libnr/nr-rotate-fns.h"
+#include "libnr/nr-translate-matrix-ops.h"
+#include "libnr/nr-convert2geom.h"
+#include <2geom/matrix.h>
 #include "svg/svg.h"
 #include "display/nr-arena-group.h"
 #include "xml/repr.h"
 #include "attributes.h"
 #include "marker.h"
+#include "document.h"
+#include "document-private.h"
 
 struct SPMarkerView {
        SPMarkerView *next;
        unsigned int key;
-       unsigned int size;
-       NRArenaItem *items[1];
+  std::vector<NRArenaItem *> items;
 };
 
 static void sp_marker_class_init (SPMarkerClass *klass);
@@ -38,11 +46,11 @@ static void sp_marker_build (SPObject *object, SPDocument *document, Inkscape::X
 static void sp_marker_release (SPObject *object);
 static void sp_marker_set (SPObject *object, unsigned int key, const gchar *value);
 static void sp_marker_update (SPObject *object, SPCtx *ctx, guint flags);
-static Inkscape::XML::Node *sp_marker_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
+static Inkscape::XML::Node *sp_marker_write (SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
 
 static NRArenaItem *sp_marker_private_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
 static void sp_marker_private_hide (SPItem *item, unsigned int key);
-static void sp_marker_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
+static void sp_marker_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags);
 static void sp_marker_print (SPItem *item, SPPrintContext *ctx);
 
 static void sp_marker_view_remove (SPMarker *marker, SPMarkerView *view, unsigned int destroyitems);
@@ -73,7 +81,7 @@ sp_marker_get_type (void)
 }
 
 /**
- * Initializes a SPMarkerClass object.  Establishes the function pointers to the class' 
+ * Initializes a SPMarkerClass object.  Establishes the function pointers to the class'
  * member routines in the class vtable, and sets pointers to parent classes.
  */
 static void
@@ -108,9 +116,9 @@ sp_marker_class_init (SPMarkerClass *klass)
 static void
 sp_marker_init (SPMarker *marker)
 {
-       marker->viewBox_set = FALSE;
-
-       nr_matrix_set_identity (&marker->c2p);
+    marker->viewBox = Geom::OptRect();
+    marker->c2p.setIdentity();
+    marker->views = NULL;
 }
 
 /**
@@ -118,7 +126,7 @@ sp_marker_init (SPMarker *marker)
  *
  * This is to be invoked immediately after creation of an SPMarker.  This
  * method fills an SPMarker object with its SVG attributes, and calls the
- * parent class' build routine to attach the object to its document and 
+ * parent class' build routine to attach the object to its document and
  * repr.  The result will be creation of the whole document tree.
  *
  * \see sp_object_build()
@@ -132,14 +140,14 @@ sp_marker_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *re
        group = (SPGroup *) object;
        marker = (SPMarker *) object;
 
-       sp_object_read_attr (object, "markerUnits");
-       sp_object_read_attr (object, "refX");
-       sp_object_read_attr (object, "refY");
-       sp_object_read_attr (object, "markerWidth");
-       sp_object_read_attr (object, "markerHeight");
-       sp_object_read_attr (object, "orient");
-       sp_object_read_attr (object, "viewBox");
-       sp_object_read_attr (object, "preserveAspectRatio");
+       object->readAttr( "markerUnits" );
+       object->readAttr( "refX" );
+       object->readAttr( "refY" );
+       object->readAttr( "markerWidth" );
+       object->readAttr( "markerHeight" );
+       object->readAttr( "orient" );
+       object->readAttr( "viewBox" );
+       object->readAttr( "preserveAspectRatio" );
 
        if (((SPObjectClass *) parent_class)->build)
                ((SPObjectClass *) parent_class)->build (object, document, repr);
@@ -242,7 +250,7 @@ sp_marker_set (SPObject *object, unsigned int key, const gchar *value)
                object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
                break;
        case SP_ATTR_VIEWBOX:
-               marker->viewBox_set = FALSE;
+        marker->viewBox = Geom::OptRect();
                if (value) {
                        double x, y, width, height;
                        char *eptr;
@@ -259,11 +267,8 @@ sp_marker_set (SPObject *object, unsigned int key, const gchar *value)
                        while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
                        if ((width > 0) && (height > 0)) {
                                /* Set viewbox */
-                               marker->viewBox.x0 = x;
-                               marker->viewBox.y0 = y;
-                               marker->viewBox.x1 = x + width;
-                               marker->viewBox.y1 = y + height;
-                               marker->viewBox_set = TRUE;
+                marker->viewBox = Geom::Rect( Geom::Point(x,y),
+                                              Geom::Point(x + width, y + height) );
                        }
                }
                object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
@@ -301,7 +306,7 @@ sp_marker_set (SPObject *object, unsigned int key, const gchar *value)
                                align = SP_ASPECT_XMIN_YMID;
                        } else if (!strcmp (c, "xMidYMid")) {
                                align = SP_ASPECT_XMID_YMID;
-                       } else if (!strcmp (c, "xMaxYMin")) {
+                       } else if (!strcmp (c, "xMaxYMid")) {
                                align = SP_ASPECT_XMAX_YMID;
                        } else if (!strcmp (c, "xMinYMax")) {
                                align = SP_ASPECT_XMIN_YMAX;
@@ -314,7 +319,7 @@ sp_marker_set (SPObject *object, unsigned int key, const gchar *value)
                        }
                        clip = SP_ASPECT_MEET;
                        while (*e && *e == 32) e += 1;
-                       if (e) {
+                       if (*e) {
                                if (!strcmp (e, "meet")) {
                                        clip = SP_ASPECT_MEET;
                                } else if (!strcmp (e, "slice")) {
@@ -345,9 +350,8 @@ sp_marker_update (SPObject *object, SPCtx *ctx, guint flags)
        SPItem *item;
        SPMarker *marker;
        SPItemCtx rctx;
-       NRRect *vb;
+    Geom::Rect vb;
        double x, y, width, height;
-       NRMatrix q;
        SPMarkerView *v;
 
        item = SP_ITEM (object);
@@ -358,8 +362,8 @@ sp_marker_update (SPObject *object, SPCtx *ctx, guint flags)
        /* Copy parent context */
        rctx.ctx = *ctx;
        /* Initialize tranformations */
-       rctx.i2doc = NR::identity();
-       rctx.i2vp = NR::identity();
+       rctx.i2doc = Geom::identity();
+       rctx.i2vp = Geom::identity();
        /* Set up viewport */
        rctx.vp.x0 = 0.0;
        rctx.vp.y0 = 0.0;
@@ -367,13 +371,13 @@ sp_marker_update (SPObject *object, SPCtx *ctx, guint flags)
        rctx.vp.y1 = marker->markerHeight.computed;
 
        /* Start with identity transform */
-       nr_matrix_set_identity (&marker->c2p);
+       marker->c2p.setIdentity();
 
        /* Viewbox is always present, either implicitly or explicitly */
-       if (marker->viewBox_set) {
-               vb = &marker->viewBox;
+    if (marker->viewBox) {
+        vb = *marker->viewBox;
        } else {
-               vb = &rctx.vp;
+        vb = *(rctx.vp.upgrade_2geom());
        }
        /* Now set up viewbox transformation */
        /* Determine actual viewbox in viewport coordinates */
@@ -385,11 +389,11 @@ sp_marker_update (SPObject *object, SPCtx *ctx, guint flags)
        } else {
                double scalex, scaley, scale;
                /* Things are getting interesting */
-               scalex = (rctx.vp.x1 - rctx.vp.x0) / (vb->x1 - vb->x0);
-               scaley = (rctx.vp.y1 - rctx.vp.y0) / (vb->y1 - vb->y0);
+        scalex = (rctx.vp.x1 - rctx.vp.x0) / (vb.width());
+        scaley = (rctx.vp.y1 - rctx.vp.y0) / (vb.height());
                scale = (marker->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley);
-               width = (vb->x1 - vb->x0) * scale;
-               height = (vb->y1 - vb->y0) * scale;
+        width = (vb.width()) * scale;
+        height = (vb.height()) * scale;
                /* Now place viewbox to requested position */
                switch (marker->aspect_align) {
                case SP_ASPECT_XMIN_YMIN:
@@ -434,32 +438,21 @@ sp_marker_update (SPObject *object, SPCtx *ctx, guint flags)
                        break;
                }
        }
-       /* Compose additional transformation from scale and position */
-       q.c[0] = width / (vb->x1 - vb->x0);
-       q.c[1] = 0.0;
-       q.c[2] = 0.0;
-       q.c[3] = height / (vb->y1 - vb->y0);
-       q.c[4] = -vb->x0 * q.c[0] + x;
-       q.c[5] = -vb->y0 * q.c[3] + y;
-       /* Append viewbox transformation */
-       nr_matrix_multiply (&marker->c2p, &q, &marker->c2p);
-
-
-       /* Append reference translation */
-       /* fixme: lala (Lauris) */
-       nr_matrix_set_translate (&q, -marker->refX.computed, -marker->refY.computed);
-       nr_matrix_multiply (&marker->c2p, &q, &marker->c2p);
+
+       // viewbox transformation and reference translation
+       marker->c2p = Geom::Translate(-marker->refX.computed, -marker->refY.computed) *
+               Geom::Scale(width / vb.width(), height / vb.height());
 
        rctx.i2doc = marker->c2p * rctx.i2doc;
 
        /* If viewBox is set reinitialize child viewport */
        /* Otherwise it already correct */
-       if (marker->viewBox_set) {
-               rctx.vp.x0 = marker->viewBox.x0;
-               rctx.vp.y0 = marker->viewBox.y0;
-               rctx.vp.x1 = marker->viewBox.x1;
-               rctx.vp.y1 = marker->viewBox.y1;
-               rctx.i2vp = NR::identity();
+       if (marker->viewBox) {
+            rctx.vp.x0 = marker->viewBox->min()[Geom::X];
+            rctx.vp.y0 = marker->viewBox->min()[Geom::Y];
+            rctx.vp.x1 = marker->viewBox->max()[Geom::X];
+            rctx.vp.y1 = marker->viewBox->max()[Geom::Y];
+            rctx.i2vp = Geom::identity();
        }
 
        /* And invoke parent method */
@@ -468,26 +461,27 @@ sp_marker_update (SPObject *object, SPCtx *ctx, guint flags)
 
        /* As last step set additional transform of arena group */
        for (v = marker->views; v != NULL; v = v->next) {
-               for (unsigned i = 0 ; i < v->size ; i++) {
-                       if (v->items[i]) {
-                               nr_arena_group_set_child_transform(NR_ARENA_GROUP(v->items[i]), &marker->c2p);
-                       }
-               }
+      for (unsigned i = 0 ; i < v->items.size() ; i++) {
+                if (v->items[i]) {
+                    Geom::Matrix tmp = marker->c2p;
+                    nr_arena_group_set_child_transform(NR_ARENA_GROUP(v->items[i]), &tmp);
+                }
+      }
        }
 }
 
-/** 
+/**
  * Writes the object's properties into its repr object.
  */
 static Inkscape::XML::Node *
-sp_marker_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
+sp_marker_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
 {
        SPMarker *marker;
 
        marker = SP_MARKER (object);
 
        if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
-               repr = sp_repr_new ("svg:marker");
+               repr = xml_doc->createElement("svg:marker");
        }
 
        if (marker->markerUnits_set) {
@@ -529,11 +523,13 @@ sp_marker_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
                repr->setAttribute("orient", NULL);
        }
        /* fixme: */
-       repr->setAttribute("viewBox", object->repr->attribute("viewBox"));
-       repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
+       //XML Tree being used directly here while it shouldn't be....
+       repr->setAttribute("viewBox", object->getRepr()->attribute("viewBox"));
+       //XML Tree being used directly here while it shouldn't be....
+       repr->setAttribute("preserveAspectRatio", object->getRepr()->attribute("preserveAspectRatio"));
 
        if (((SPObjectClass *) (parent_class))->write)
-               ((SPObjectClass *) (parent_class))->write (object, repr, flags);
+               ((SPObjectClass *) (parent_class))->write (object, xml_doc, repr, flags);
 
        return repr;
 }
@@ -542,26 +538,26 @@ sp_marker_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
  * This routine is disabled to break propagation.
  */
 static NRArenaItem *
-sp_marker_private_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
+sp_marker_private_show (SPItem */*item*/, NRArena */*arena*/, unsigned int /*key*/, unsigned int /*flags*/)
 {
-       /* Break propagation */
-       return NULL;
+    /* Break propagation */
+    return NULL;
 }
 
 /**
  * This routine is disabled to break propagation.
  */
 static void
-sp_marker_private_hide (SPItem *item, unsigned int key)
+sp_marker_private_hide (SPItem */*item*/, unsigned int /*key*/)
 {
-       /* Break propagation */
+    /* Break propagation */
 }
 
 /**
  * This routine is disabled to break propagation.
  */
 static void
-sp_marker_bbox(SPItem const *, NRRect *, NR::Matrix const &, unsigned const)
+sp_marker_bbox(SPItem const *, NRRect *, Geom::Matrix const &, unsigned const)
 {
        /* Break propagation */
 }
@@ -570,9 +566,9 @@ sp_marker_bbox(SPItem const *, NRRect *, NR::Matrix const &, unsigned const)
  * This routine is disabled to break propagation.
  */
 static void
-sp_marker_print (SPItem *item, SPPrintContext *ctx)
+sp_marker_print (SPItem */*item*/, SPPrintContext */*ctx*/)
 {
-       /* Break propagation */
+    /* Break propagation */
 }
 
 /* fixme: Remove link if zero-sized (Lauris) */
@@ -583,7 +579,7 @@ sp_marker_print (SPItem *item, SPPrintContext *ctx)
  * This is called from sp_shape_update() for shapes that have markers.  It
  * removes the old view of the marker and establishes a new one, registering
  * it with the marker's list of views for future updates.
- * 
+ *
  * \param marker Marker to create views in.
  * \param key Key to give each SPMarkerView.
  * \param size Number of NRArenaItems to put in the SPMarkerView.
@@ -591,75 +587,78 @@ sp_marker_print (SPItem *item, SPPrintContext *ctx)
 void
 sp_marker_show_dimension (SPMarker *marker, unsigned int key, unsigned int size)
 {
-       SPMarkerView *view;
-       unsigned int i;
-
-       for (view = marker->views; view != NULL; view = view->next) {
-               if (view->key == key) break;
-       }
-       if (view && (view->size != size)) {
-               /* Free old view and allocate new */
-               /* Parent class ::hide method */
-               ((SPItemClass *) parent_class)->hide ((SPItem *) marker, key);
-               sp_marker_view_remove (marker, view, TRUE);
-               view = NULL;
-       }
-       if (!view) {
-               view = (SPMarkerView *)g_malloc (sizeof (SPMarkerView) + (size) * sizeof (NRArenaItem *));
-               for (i = 0; i < size; i++) view->items[i] = NULL;
-               view->next = marker->views;
-               marker->views = view;
-               view->key = key;
-               view->size = size;
-       }
+    SPMarkerView *view;
+    unsigned int i;
+
+    for (view = marker->views; view != NULL; view = view->next) {
+        if (view->key == key) break;
+    }
+    if (view && (view->items.size() != size)) {
+        /* Free old view and allocate new */
+        /* Parent class ::hide method */
+        ((SPItemClass *) parent_class)->hide ((SPItem *) marker, key);
+        sp_marker_view_remove (marker, view, TRUE);
+        view = NULL;
+    }
+    if (!view) {
+        view = new SPMarkerView();
+        view->items.clear();
+        for (i = 0; i < size; i++) {
+            view->items.push_back(NULL);
+        }
+        view->next = marker->views;
+        marker->views = view;
+        view->key = key;
+    }
 }
 
-/** 
+/**
  * Shows an instance of a marker.  This is called during sp_shape_update_marker_view()
  * show and transform a child item in the arena for all views with the given key.
  */
 NRArenaItem *
-sp_marker_show_instance (SPMarker *marker, NRArenaItem *parent,
-                        unsigned int key, unsigned int pos,
-                        NR::Matrix const &base, float linewidth)
+sp_marker_show_instance ( SPMarker *marker, NRArenaItem *parent,
+                          unsigned int key, unsigned int pos,
+                          Geom::Matrix const &base, float linewidth)
 {
-       for (SPMarkerView *v = marker->views; v != NULL; v = v->next) {
-               if (v->key == key) {
-                       if (pos >= v->size) {
-                         return NULL;
-                       }
-                       if (!v->items[pos]) {
-                               /* Parent class ::show method */
-                               v->items[pos] = ((SPItemClass *) parent_class)->show ((SPItem *) marker,
-                                                                                     parent->arena, key,
-                                                                                     SP_ITEM_REFERENCE_FLAGS);
-                               if (v->items[pos]) {
-                                       /* fixme: Position (Lauris) */
-                                       nr_arena_item_add_child (parent, v->items[pos], NULL);
-                                       /* nr_arena_item_unref (v->items[pos]); */
-                                       nr_arena_group_set_child_transform((NRArenaGroup *) v->items[pos], &marker->c2p);
-                               }
-                       }
-                       if (v->items[pos]) {
-                               NR::Matrix m;
-                               if (marker->orient_auto) {
-                                       m = base;
-                               } else {
-                                       /* fixme: Orient units (Lauris) */
-                                       m = NR::Matrix(rotate_degrees(marker->orient));
-                                       m *= get_translation(base);
-                               }
-                               if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
-                                       m = NR::scale(linewidth) * m;
-                               }
-
-                               nr_arena_item_set_transform(v->items[pos], m);
-                       }
-                       return v->items[pos];
-               }
-       }
-
-       return NULL;
+    for (SPMarkerView *v = marker->views; v != NULL; v = v->next) {
+        if (v->key == key) {
+            if (pos >= v->items.size()) {
+                return NULL;
+            }
+            if (!v->items[pos]) {
+                /* Parent class ::show method */
+                v->items[pos] = ((SPItemClass *) parent_class)->show ((SPItem *) marker,
+                                                                      parent->arena, key,
+                                                                      SP_ITEM_REFERENCE_FLAGS);
+                if (v->items[pos]) {
+                    /* fixme: Position (Lauris) */
+                    nr_arena_item_add_child (parent, v->items[pos], NULL);
+                    /* nr_arena_item_unref (v->items[pos]); */
+                    Geom::Matrix tmp = marker->c2p;
+                    nr_arena_group_set_child_transform((NRArenaGroup *) v->items[pos], &tmp);
+                }
+            }
+            if (v->items[pos]) {
+                Geom::Matrix m;
+                if (marker->orient_auto) {
+                    m = base;
+                } else {
+                    /* fixme: Orient units (Lauris) */
+                    m = Geom::Rotate::from_degrees(marker->orient);
+                    m *= Geom::Translate(base.translation());
+                }
+                if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
+                    m = Geom::Scale(linewidth) * m;
+                }
+                
+                nr_arena_item_set_transform(v->items[pos], m);
+            }
+            return v->items[pos];
+        }
+    }
+
+    return NULL;
 }
 
 /**
@@ -687,7 +686,7 @@ sp_marker_hide (SPMarker *marker, unsigned int key)
 }
 
 /**
- * Removes a given view.  Also will destroy sub-items in the view if destroyitems 
+ * Removes a given view.  Also will destroy sub-items in the view if destroyitems
  * is set to a non-zero value.
  */
 static void
@@ -702,10 +701,59 @@ sp_marker_view_remove (SPMarker *marker, SPMarkerView *view, unsigned int destro
                v->next = view->next;
        }
        if (destroyitems) {
-               for (i = 0; i < view->size; i++) {
+      for (i = 0; i < view->items.size(); i++) {
                        /* We have to walk through the whole array because there may be hidden items */
                        if (view->items[i]) nr_arena_item_unref (view->items[i]);
                }
        }
-       g_free (view);
+    view->items.clear();
+    delete view;
+}
+
+const gchar *generate_marker(GSList *reprs, Geom::Rect bounds, SPDocument *document, Geom::Matrix /*transform*/, Geom::Matrix move)
+{
+    Inkscape::XML::Document *xml_doc = document->getReprDoc();
+    Inkscape::XML::Node *defsrepr = SP_DOCUMENT_DEFS(document)->getRepr();
+
+    Inkscape::XML::Node *repr = xml_doc->createElement("svg:marker");
+
+    // Uncommenting this will make the marker fixed-size independent of stroke width.
+    // Commented out for consistency with standard markers which scale when you change
+    // stroke width:
+    //repr->setAttribute("markerUnits", "userSpaceOnUse");
+
+    sp_repr_set_svg_double(repr, "markerWidth", bounds.dimensions()[Geom::X]);
+    sp_repr_set_svg_double(repr, "markerHeight", bounds.dimensions()[Geom::Y]);
+
+    repr->setAttribute("orient", "auto");
+
+    defsrepr->appendChild(repr);
+    const gchar *mark_id = repr->attribute("id");
+    SPObject *mark_object = document->getObjectById(mark_id);
+
+    for (GSList *i = reprs; i != NULL; i = i->next) {
+            Inkscape::XML::Node *node = (Inkscape::XML::Node *)(i->data);
+        SPItem *copy = SP_ITEM(mark_object->appendChildRepr(node));
+
+        Geom::Matrix dup_transform;
+        if (!sp_svg_transform_read (node->attribute("transform"), &dup_transform))
+            dup_transform = Geom::identity();
+        dup_transform *= move;
+
+        copy->doWriteTransform(SP_OBJECT_REPR(copy), dup_transform);
+    }
+
+    Inkscape::GC::release(repr);
+    return mark_id;
 }
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :