X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fmarker.cpp;h=0ec92a1ff2147b00573ad9703e86d10a7628083c;hb=9dc68827cbd515262ecb8d5ae8547d9e82c72e00;hp=68b630920fa6a56a1f01af9127c66bc0626520f1;hpb=dd69425007680aafc47fdd994e1985625571d252;p=inkscape.git diff --git a/src/marker.cpp b/src/marker.cpp index 68b630920..0ec92a1ff 100644 --- a/src/marker.cpp +++ b/src/marker.cpp @@ -1,34 +1,42 @@ -#define __SP_MARKER_C__ - /* * SVG implementation * * Authors: * Lauris Kaplinski + * Bryce Harrington + * 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 +#include #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 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 :