summaryrefslogtreecommitdiffstats
path: root/src/sp-item.cpp
diff options
context:
space:
mode:
authorAndrew Higginson <at.higginson@gmail.com>2011-12-27 21:04:47 +0000
committerAndrew <at.higginson@gmail.com>2011-12-27 21:04:47 +0000
commit80960b623a99aae1402ab651b2974ef544ed3b03 (patch)
treeba49d42c2789e9e11f805e2d5263e10f9fedeef8 /src/sp-item.cpp
parenttry to fix bug (diff)
parentGDL: Cherry-pick upstream patch 73852 (2011-03-23) - Add missing return value. (diff)
downloadinkscape-80960b623a99aae1402ab651b2974ef544ed3b03.tar.gz
inkscape-80960b623a99aae1402ab651b2974ef544ed3b03.zip
merged with trunk so I can build again...
(bzr r10092.1.36)
Diffstat (limited to 'src/sp-item.cpp')
-rw-r--r--src/sp-item.cpp630
1 files changed, 299 insertions, 331 deletions
diff --git a/src/sp-item.cpp b/src/sp-item.cpp
index 40c9b0233..89ff92035 100644
--- a/src/sp-item.cpp
+++ b/src/sp-item.cpp
@@ -25,12 +25,10 @@
# include "config.h"
#endif
-
#include "sp-item.h"
#include "svg/svg.h"
#include "print.h"
-#include "display/nr-arena.h"
-#include "display/nr-arena-item.h"
+#include "display/drawing-item.h"
#include "attributes.h"
#include "document.h"
#include "uri.h"
@@ -61,12 +59,6 @@
#include "sp-title.h"
#include "sp-desc.h"
-#include "libnr/nr-matrix-fns.h"
-#include "libnr/nr-matrix-scale-ops.h"
-#include "libnr/nr-matrix-translate-ops.h"
-#include "libnr/nr-scale-translate-ops.h"
-#include "libnr/nr-translate-scale-ops.h"
-#include "libnr/nr-convert2geom.h"
#include "util/find-last-if.h"
#include "util/reverse-list.h"
#include <2geom/rect.h>
@@ -138,6 +130,7 @@ void SPItem::sp_item_init(SPItem *item)
void SPItem::init() {
sensitive = TRUE;
+ bbox_valid = FALSE;
transform_center_x = 0;
transform_center_y = 0;
@@ -146,18 +139,16 @@ void SPItem::init() {
_evaluated_status = StatusUnknown;
transform = Geom::identity();
+ doc_bbox = Geom::OptRect();
+ freeze_stroke_width = false;
display = NULL;
clip_ref = new SPClipPathReference(this);
- sigc::signal<void, SPObject *, SPObject *> cs1 = clip_ref->changedSignal();
- sigc::slot2<void,SPObject*, SPObject *> sl1 = sigc::bind(sigc::ptr_fun(clip_ref_changed), this);
- _clip_ref_connection = cs1.connect(sl1);
+ clip_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(clip_ref_changed), this));
mask_ref = new SPMaskReference(this);
- sigc::signal<void, SPObject *, SPObject *> cs2 = mask_ref->changedSignal();
- sigc::slot2<void,SPObject*, SPObject *> sl2=sigc::bind(sigc::ptr_fun(mask_ref_changed), this);
- _mask_ref_connection = cs2.connect(sl2);
+ mask_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(mask_ref_changed), this));
avoidRef = new SPAvoidRef(this);
@@ -209,10 +200,10 @@ bool SPItem::isHidden(unsigned display_key) const {
for ( SPItemView *view(display) ; view ; view = view->next ) {
if ( view->key == display_key ) {
g_assert(view->arenaitem != NULL);
- for ( NRArenaItem *arenaitem = view->arenaitem ;
- arenaitem ; arenaitem = arenaitem->parent )
+ for ( Inkscape::DrawingItem *arenaitem = view->arenaitem ;
+ arenaitem ; arenaitem = arenaitem->parent() )
{
- if (!arenaitem->visible) {
+ if (!arenaitem->visible()) {
return true;
}
}
@@ -263,7 +254,7 @@ bool SPItem::isExplicitlyHidden() const
* Sets the display CSS property to `hidden' if \a val is true,
* otherwise makes it unset
*/
-void SPItem::setExplicitlyHidden(bool const val) {
+void SPItem::setExplicitlyHidden(bool val) {
style->display.set = val;
style->display.value = ( val ? SP_CSS_DISPLAY_NONE : SP_CSS_DISPLAY_INLINE );
style->display.computed = style->display.value;
@@ -273,17 +264,17 @@ void SPItem::setExplicitlyHidden(bool const val) {
/**
* Sets the transform_center_x and transform_center_y properties to retain the rotation centre
*/
-void SPItem::setCenter(Geom::Point object_centre) {
- // for getBounds() to work
+void SPItem::setCenter(Geom::Point const &object_centre) {
document->ensureUpToDate();
- Geom::OptRect bbox = getBounds(i2d_affine());
+ // FIXME this is seriously wrong
+ Geom::OptRect bbox = desktopGeometricBounds();
if (bbox) {
transform_center_x = object_centre[Geom::X] - bbox->midpoint()[Geom::X];
- if (fabs(transform_center_x) < 1e-5) // rounding error
+ if (Geom::are_near(transform_center_x, 0)) // rounding error
transform_center_x = 0;
transform_center_y = object_centre[Geom::Y] - bbox->midpoint()[Geom::Y];
- if (fabs(transform_center_y) < 1e-5) // rounding error
+ if (Geom::are_near(transform_center_y, 0)) // rounding error
transform_center_y = 0;
}
}
@@ -299,12 +290,12 @@ bool SPItem::isCenterSet() {
}
Geom::Point SPItem::getCenter() const {
- // for getBounds() to work
document->ensureUpToDate();
- Geom::OptRect bbox = getBounds(i2d_affine());
+ // FIXME this is seriously wrong
+ Geom::OptRect bbox = desktopGeometricBounds();
if (bbox) {
- return to_2geom(bbox->midpoint()) + Geom::Point (transform_center_x, transform_center_y);
+ return bbox->midpoint() + Geom::Point (transform_center_x, transform_center_y);
} else {
return Geom::Point(0, 0); // something's wrong!
}
@@ -399,35 +390,22 @@ void SPItem::sp_item_release(SPObject *object)
{
SPItem *item = (SPItem *) object;
- item->_clip_ref_connection.disconnect();
- item->_mask_ref_connection.disconnect();
-
// Note: do this here before the clip_ref is deleted, since calling
// ensureUpToDate() for triggered routing may reference
// the deleted clip_ref.
- if (item->avoidRef) {
- delete item->avoidRef;
- item->avoidRef = NULL;
- }
+ delete item->avoidRef;
- if (item->clip_ref) {
- item->clip_ref->detach();
- delete item->clip_ref;
- item->clip_ref = NULL;
- }
-
- if (item->mask_ref) {
- item->mask_ref->detach();
- delete item->mask_ref;
- item->mask_ref = NULL;
- }
+ // we do NOT disconnect from the changed signal of those before deletion.
+ // The destructor will call *_ref_changed with NULL as the new value,
+ // which will cause the hide() function to be called.
+ delete item->clip_ref;
+ delete item->mask_ref;
if (((SPObjectClass *) (SPItemClass::static_parent_class))->release) {
((SPObjectClass *) SPItemClass::static_parent_class)->release(object);
}
while (item->display) {
- nr_arena_item_unparent(item->display->arenaitem);
item->display = sp_item_view_list_remove(item->display, item->display);
}
@@ -483,7 +461,7 @@ void SPItem::sp_item_set(SPObject *object, unsigned key, gchar const *value)
case SP_ATTR_SODIPODI_INSENSITIVE:
item->sensitive = !value;
for (SPItemView *v = item->display; v != NULL; v = v->next) {
- nr_arena_item_set_sensitive(v->arenaitem, item->sensitive);
+ v->arenaitem->setSensitive(item->sensitive);
}
break;
case SP_ATTR_CONNECTOR_AVOID:
@@ -534,23 +512,20 @@ void SPItem::clip_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item)
SPItemView *v;
/* Hide clippath */
for (v = item->display; v != NULL; v = v->next) {
- SP_CLIPPATH(old_clip)->hide(NR_ARENA_ITEM_GET_KEY(v->arenaitem));
- nr_arena_item_set_clip(v->arenaitem, NULL);
+ SP_CLIPPATH(old_clip)->hide(v->arenaitem->key());
}
}
if (SP_IS_CLIPPATH(clip)) {
- NRRect bbox;
- item->invoke_bbox( &bbox, Geom::identity(), TRUE);
+ Geom::OptRect bbox = item->geometricBounds();
for (SPItemView *v = item->display; v != NULL; v = v->next) {
- if (!v->arenaitem->key) {
- NR_ARENA_ITEM_SET_KEY(v->arenaitem, SPItem::display_key_new(3));
+ if (!v->arenaitem->key()) {
+ v->arenaitem->setKey(SPItem::display_key_new(3));
}
- NRArenaItem *ai = SP_CLIPPATH(clip)->show(
- NR_ARENA_ITEM_ARENA(v->arenaitem),
- NR_ARENA_ITEM_GET_KEY(v->arenaitem));
- nr_arena_item_set_clip(v->arenaitem, ai);
- nr_arena_item_unref(ai);
- SP_CLIPPATH(clip)->setBBox(NR_ARENA_ITEM_GET_KEY(v->arenaitem), &bbox);
+ Inkscape::DrawingItem *ai = SP_CLIPPATH(clip)->show(
+ v->arenaitem->drawing(),
+ v->arenaitem->key());
+ v->arenaitem->setClip(ai);
+ SP_CLIPPATH(clip)->setBBox(v->arenaitem->key(), bbox);
clip->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
}
@@ -561,23 +536,20 @@ void SPItem::mask_ref_changed(SPObject *old_mask, SPObject *mask, SPItem *item)
if (old_mask) {
/* Hide mask */
for (SPItemView *v = item->display; v != NULL; v = v->next) {
- sp_mask_hide(SP_MASK(old_mask), NR_ARENA_ITEM_GET_KEY(v->arenaitem));
- nr_arena_item_set_mask(v->arenaitem, NULL);
+ sp_mask_hide(SP_MASK(old_mask), v->arenaitem->key());
}
}
if (SP_IS_MASK(mask)) {
- NRRect bbox;
- item->invoke_bbox( &bbox, Geom::identity(), TRUE);
+ Geom::OptRect bbox = item->geometricBounds();
for (SPItemView *v = item->display; v != NULL; v = v->next) {
- if (!v->arenaitem->key) {
- NR_ARENA_ITEM_SET_KEY(v->arenaitem, SPItem::display_key_new(3));
+ if (!v->arenaitem->key()) {
+ v->arenaitem->setKey(SPItem::display_key_new(3));
}
- NRArenaItem *ai = sp_mask_show(SP_MASK(mask),
- NR_ARENA_ITEM_ARENA(v->arenaitem),
- NR_ARENA_ITEM_GET_KEY(v->arenaitem));
- nr_arena_item_set_mask(v->arenaitem, ai);
- nr_arena_item_unref(ai);
- sp_mask_set_bbox(SP_MASK(mask), NR_ARENA_ITEM_GET_KEY(v->arenaitem), &bbox);
+ Inkscape::DrawingItem *ai = sp_mask_show(SP_MASK(mask),
+ v->arenaitem->drawing(),
+ v->arenaitem->key());
+ v->arenaitem->setMask(ai);
+ sp_mask_set_bbox(SP_MASK(mask), v->arenaitem->key(), bbox);
mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
}
@@ -591,10 +563,14 @@ void SPItem::sp_item_update(SPObject *object, SPCtx *ctx, guint flags)
(* ((SPObjectClass *) (SPItemClass::static_parent_class))->update)(object, ctx, flags);
}
+ // any of the modifications defined in sp-object.h might change bbox,
+ // so we invalidate it unconditionally
+ item->bbox_valid = FALSE;
+
if (flags & (SP_OBJECT_CHILD_MODIFIED_FLAG | SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG)) {
if (flags & SP_OBJECT_MODIFIED_FLAG) {
for (SPItemView *v = item->display; v != NULL; v = v->next) {
- nr_arena_item_set_transform(v->arenaitem, item->transform);
+ v->arenaitem->setTransform(item->transform);
}
}
@@ -602,37 +578,35 @@ void SPItem::sp_item_update(SPObject *object, SPCtx *ctx, guint flags)
SPMask *mask = item->mask_ref ? item->mask_ref->getObject() : NULL;
if ( clip_path || mask ) {
- NRRect bbox;
- item->invoke_bbox( &bbox, Geom::identity(), TRUE);
+ Geom::OptRect bbox = item->geometricBounds();
if (clip_path) {
for (SPItemView *v = item->display; v != NULL; v = v->next) {
- clip_path->setBBox(NR_ARENA_ITEM_GET_KEY(v->arenaitem), &bbox);
+ clip_path->setBBox(v->arenaitem->key(), bbox);
}
}
if (mask) {
for (SPItemView *v = item->display; v != NULL; v = v->next) {
- sp_mask_set_bbox(mask, NR_ARENA_ITEM_GET_KEY(v->arenaitem), &bbox);
+ sp_mask_set_bbox(mask, v->arenaitem->key(), bbox);
}
}
}
if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
for (SPItemView *v = item->display; v != NULL; v = v->next) {
- nr_arena_item_set_opacity(v->arenaitem, SP_SCALE24_TO_FLOAT(object->style->opacity.value));
- nr_arena_item_set_visible(v->arenaitem, !item->isHidden());
+ v->arenaitem->setOpacity(SP_SCALE24_TO_FLOAT(object->style->opacity.value));
+ v->arenaitem->setVisible(!item->isHidden());
}
}
}
/* Update bounding box data used by filters */
if (item->style->filter.set && item->display) {
- Geom::OptRect item_bbox;
- item->invoke_bbox( item_bbox, Geom::identity(), TRUE, SPItem::GEOMETRIC_BBOX);
+ Geom::OptRect item_bbox = item->geometricBounds();
SPItemView *itemview = item->display;
do {
if (itemview->arenaitem)
- nr_arena_item_set_item_bbox(itemview->arenaitem, item_bbox);
+ itemview->arenaitem->setItemBounds(item_bbox);
} while ( (itemview = itemview->next) );
}
@@ -643,19 +617,19 @@ void SPItem::sp_item_update(SPObject *object, SPCtx *ctx, guint flags)
Inkscape::XML::Node *SPItem::sp_item_write(SPObject *const object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
{
- SPObject *child;
SPItem *item = SP_ITEM(object);
// in the case of SP_OBJECT_WRITE_BUILD, the item should always be newly created,
// so we need to add any children from the underlying object to the new repr
if (flags & SP_OBJECT_WRITE_BUILD) {
- Inkscape::XML::Node *crepr;
- GSList *l;
- l = NULL;
- for (child = object->firstChild(); child != NULL; child = child->next ) {
- if (!SP_IS_TITLE(child) && !SP_IS_DESC(child)) continue;
- crepr = child->updateRepr(xml_doc, NULL, flags);
- if (crepr) l = g_slist_prepend (l, crepr);
+ GSList *l = NULL;
+ for (SPObject *child = object->firstChild(); child != NULL; child = child->next ) {
+ if (SP_IS_TITLE(child) || SP_IS_DESC(child)) {
+ Inkscape::XML::Node *crepr = child->updateRepr(xml_doc, NULL, flags);
+ if (crepr) {
+ l = g_slist_prepend (l, crepr);
+ }
+ }
}
while (l) {
repr->addChild((Inkscape::XML::Node *) l->data, NULL);
@@ -663,9 +637,10 @@ Inkscape::XML::Node *SPItem::sp_item_write(SPObject *const object, Inkscape::XML
l = g_slist_remove (l, l->data);
}
} else {
- for (child = object->firstChild() ; child != NULL; child = child->next ) {
- if (!SP_IS_TITLE(child) && !SP_IS_DESC(child)) continue;
- child->updateRepr(flags);
+ for (SPObject *child = object->firstChild() ; child != NULL; child = child->next ) {
+ if (SP_IS_TITLE(child) || SP_IS_DESC(child)) {
+ child->updateRepr(flags);
+ }
}
}
@@ -704,168 +679,155 @@ Inkscape::XML::Node *SPItem::sp_item_write(SPObject *const object, Inkscape::XML
}
/**
- * \return There is no guarantee that the return value will contain a rectangle.
- If this item does not have a boundingbox, it might well be empty.
+ * Get item's geometric bounding box in this item's coordinate system.
+ *
+ * The geometric bounding box includes only the path, disregarding all style attributes.
*/
-Geom::OptRect SPItem::getBounds(Geom::Affine const &transform,
- SPItem::BBoxType type,
- unsigned int /*dkey*/) const
+Geom::OptRect SPItem::geometricBounds(Geom::Affine const &transform) const
{
- Geom::OptRect r;
- invoke_bbox_full( r, transform, type, TRUE);
- return r;
-}
-
-void SPItem::invoke_bbox( Geom::OptRect &bbox, Geom::Affine const &transform, unsigned const clear, SPItem::BBoxType type)
-{
- invoke_bbox_full( bbox, transform, type, clear);
-}
-
-// DEPRECATED to phase out the use of NRRect in favor of Geom::OptRect
-void SPItem::invoke_bbox( NRRect *bbox, Geom::Affine const &transform, unsigned const clear, SPItem::BBoxType type)
-{
- invoke_bbox_full( bbox, transform, type, clear);
+ Geom::OptRect bbox;
+ // call the subclass method
+ if (((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox) {
+ bbox = ((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox(this, transform, SPItem::GEOMETRIC_BBOX);
+ }
+ return bbox;
}
-/** Calls \a item's subclass' bounding box method; clips it by the bbox of clippath, if any; and
- * unions the resulting bbox with \a bbox. If \a clear is true, empties \a bbox first. Passes the
- * transform and the flags to the actual bbox methods. Note that many of subclasses (e.g. groups,
- * clones), in turn, call this function in their bbox methods.
- * \retval bbox Note that there is no guarantee that bbox will contain a rectangle when the
- * function returns. If this item does not have a boundingbox, this might well be empty.
+/**
+ * Get item's visual bounding box in this item's coordinate system.
+ *
+ * The visual bounding box includes the stroke and the filter region.
*/
-void SPItem::invoke_bbox_full( Geom::OptRect &bbox, Geom::Affine const &transform, unsigned const flags, unsigned const clear) const
+Geom::OptRect SPItem::visualBounds(Geom::Affine const &transform) const
{
- if (clear) {
- bbox = Geom::OptRect();
- }
-
- // TODO: replace NRRect by Geom::Rect, for all SPItemClasses, and for SP_CLIPPATH
+ using Geom::X;
+ using Geom::Y;
- NRRect temp_bbox;
- temp_bbox.x0 = temp_bbox.y0 = NR_HUGE;
- temp_bbox.x1 = temp_bbox.y1 = -NR_HUGE;
+ Geom::OptRect bbox;
- // call the subclass method
- if (((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox) {
- ((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox(this, &temp_bbox, transform, flags);
- }
-
- // unless this is geometric bbox, extend by filter area and crop the bbox by clip path, if any
- if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
- if ( style && style->filter.href) {
- SPObject *filter = style->getFilter();
- if (filter && SP_IS_FILTER(filter)) {
- // default filer area per the SVG spec:
- double x = -0.1;
- double y = -0.1;
- double w = 1.2;
- double h = 1.2;
-
- // if area is explicitly set, override:
- if (SP_FILTER(filter)->x._set)
- x = SP_FILTER(filter)->x.computed;
- if (SP_FILTER(filter)->y._set)
- y = SP_FILTER(filter)->y.computed;
- if (SP_FILTER(filter)->width._set)
- w = SP_FILTER(filter)->width.computed;
- if (SP_FILTER(filter)->height._set)
- h = SP_FILTER(filter)->height.computed;
-
- double dx0 = 0;
- double dx1 = 0;
- double dy0 = 0;
- double dy1 = 0;
- if (filter_is_single_gaussian_blur(SP_FILTER(filter))) {
- // if this is a single blur, use 2.4*radius
- // which may be smaller than the default area;
- // see set_filter_area for why it's 2.4
- double r = get_single_gaussian_blur_radius (SP_FILTER(filter));
- dx0 = -2.4 * r;
- dx1 = 2.4 * r;
- dy0 = -2.4 * r;
- dy1 = 2.4 * r;
- } else {
- // otherwise, calculate expansion from relative to absolute units:
- dx0 = x * (temp_bbox.x1 - temp_bbox.x0);
- dx1 = (w + x - 1) * (temp_bbox.x1 - temp_bbox.x0);
- dy0 = y * (temp_bbox.y1 - temp_bbox.y0);
- dy1 = (h + y - 1) * (temp_bbox.y1 - temp_bbox.y0);
- }
+ if ( style && style->filter.href && style->getFilter() && SP_IS_FILTER(style->getFilter())) {
+ // call the subclass method
+ if (((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox) {
+ bbox = ((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox(this, Geom::identity(), SPItem::VISUAL_BBOX);
+ }
- // transform the expansions by the item's transform:
- Geom::Affine i2d(i2d_affine ());
- dx0 *= i2d.expansionX();
- dx1 *= i2d.expansionX();
- dy0 *= i2d.expansionY();
- dy1 *= i2d.expansionY();
-
- // expand the bbox
- temp_bbox.x0 += dx0;
- temp_bbox.x1 += dx1;
- temp_bbox.y0 += dy0;
- temp_bbox.y1 += dy1;
- }
+ SPFilter *filter = SP_FILTER(style->getFilter());
+ // default filer area per the SVG spec:
+ SVGLength x, y, w, h;
+ Geom::Point minp, maxp;
+ x.set(SVGLength::PERCENT, -0.10, 0);
+ y.set(SVGLength::PERCENT, -0.10, 0);
+ w.set(SVGLength::PERCENT, 1.20, 0);
+ h.set(SVGLength::PERCENT, 1.20, 0);
+
+ // if area is explicitly set, override:
+ if (filter->x._set)
+ x = filter->x;
+ if (filter->y._set)
+ y = filter->y;
+ if (filter->width._set)
+ w = filter->width;
+ if (filter->height._set)
+ h = filter->height;
+
+ double len_x = bbox ? bbox->width() : 0;
+ double len_y = bbox ? bbox->height() : 0;
+
+ x.update(12, 6, len_x);
+ y.update(12, 6, len_y);
+ w.update(12, 6, len_x);
+ h.update(12, 6, len_y);
+
+ if (filter->filterUnits == SP_FILTER_UNITS_OBJECTBOUNDINGBOX && bbox) {
+ minp[X] = bbox->left() + x.computed * (x.unit == SVGLength::PERCENT ? 1.0 : len_x);
+ maxp[X] = minp[X] + w.computed * (w.unit == SVGLength::PERCENT ? 1.0 : len_x);
+ minp[Y] = bbox->top() + y.computed * (y.unit == SVGLength::PERCENT ? 1.0 : len_y);
+ maxp[Y] = minp[Y] + h.computed * (h.unit == SVGLength::PERCENT ? 1.0 : len_y);
+ } else if (filter->filterUnits == SP_FILTER_UNITS_USERSPACEONUSE) {
+ minp[X] = x.computed;
+ maxp[X] = minp[X] + w.computed;
+ minp[Y] = y.computed;
+ maxp[Y] = minp[Y] + h.computed;
}
- if (clip_ref->getObject()) {
- NRRect b;
- SP_CLIPPATH(clip_ref->getObject())->getBBox(&b, transform, flags);
- nr_rect_d_intersect (&temp_bbox, &temp_bbox, &b);
+ bbox = Geom::OptRect(minp, maxp);
+ *bbox *= transform;
+ } else {
+ // call the subclass method
+ if (((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox) {
+ bbox = ((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox(this, transform, SPItem::VISUAL_BBOX);
}
}
-
- if (temp_bbox.x0 > temp_bbox.x1 || temp_bbox.y0 > temp_bbox.y1) {
- // Either the bbox hasn't been touched by the SPItemClass' bbox method
- // (it still has its initial values, see above: x0 = y0 = NR_HUGE and x1 = y1 = -NR_HUGE)
- // or it has explicitely been set to be like this (e.g. in sp_shape_bbox)
-
- // When x0 > x1 or y0 > y1, the bbox is considered to be "nothing", although it has not been
- // explicitely defined this way for NRRects (as opposed to Geom::OptRect)
- // So union bbox with nothing = do nothing, just return
- return;
+ if (clip_ref->getObject()) {
+ bbox.intersectWith(SP_CLIPPATH(clip_ref->getObject())->geometricBounds(transform));
}
- // Do not use temp_bbox.upgrade() here, because it uses a test that returns an empty Geom::OptRect()
- // for any rectangle with zero area. The geometrical bbox of for example a vertical line
- // would therefore be translated into empty Geom::OptRect() (see bug https://bugs.launchpad.net/inkscape/+bug/168684)
- Geom::OptRect temp_bbox_new = Geom::Rect(Geom::Point(temp_bbox.x0, temp_bbox.y0), Geom::Point(temp_bbox.x1, temp_bbox.y1));
-
- bbox = Geom::unify(bbox, temp_bbox_new);
+ return bbox;
}
-
-// DEPRECATED to phase out the use of NRRect in favor of Geom::OptRect
-/** Calls \a item's subclass' bounding box method; clips it by the bbox of clippath, if any; and
- * unions the resulting bbox with \a bbox. If \a clear is true, empties \a bbox first. Passes the
- * transform and the flags to the actual bbox methods. Note that many of subclasses (e.g. groups,
- * clones), in turn, call this function in their bbox methods. */
-void SPItem::invoke_bbox_full( NRRect *bbox, Geom::Affine const &transform, unsigned const flags, unsigned const clear)
+Geom::OptRect SPItem::bounds(BBoxType type, Geom::Affine const &transform) const
{
- g_assert(bbox != NULL);
-
- if (clear) {
- bbox->x0 = bbox->y0 = 1e18;
- bbox->x1 = bbox->y1 = -1e18;
+ if (type == GEOMETRIC_BBOX) {
+ return geometricBounds(transform);
+ } else {
+ return visualBounds(transform);
}
+}
- NRRect this_bbox;
- this_bbox.x0 = this_bbox.y0 = 1e18;
- this_bbox.x1 = this_bbox.y1 = -1e18;
-
- // call the subclass method
- if (((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox) {
- ((SPItemClass *) G_OBJECT_GET_CLASS(this))->bbox(this, &this_bbox, transform, flags);
+/** Get item's geometric bbox in document coordinate system.
+ * Document coordinates are the default coordinates of the root element:
+ * the origin is at the top left, X grows to the right and Y grows downwards. */
+Geom::OptRect SPItem::documentGeometricBounds() const
+{
+ return geometricBounds(i2doc_affine());
+}
+/// Get item's visual bbox in document coordinate system.
+Geom::OptRect SPItem::documentVisualBounds() const
+{
+ if (!bbox_valid) {
+ doc_bbox = visualBounds(i2doc_affine());
+ bbox_valid = true;
+ }
+ return doc_bbox;
+}
+Geom::OptRect SPItem::documentBounds(BBoxType type) const
+{
+ if (type == GEOMETRIC_BBOX) {
+ return documentGeometricBounds();
+ } else {
+ return documentVisualBounds();
}
+}
+/** Get item's geometric bbox in desktop coordinate system.
+ * Desktop coordinates should be user defined. Currently they are hardcoded:
+ * origin is at bottom left, X grows to the right and Y grows upwards. */
+Geom::OptRect SPItem::desktopGeometricBounds() const
+{
+ return geometricBounds(i2dt_affine());
+}
+/// Get item's visual bbox in desktop coordinate system.
+Geom::OptRect SPItem::desktopVisualBounds() const
+{
+ /// @fixme hardcoded desktop transform
+ Geom::Affine m = Geom::Scale(1, -1) * Geom::Translate(0, document->getHeight());
+ Geom::OptRect ret = documentVisualBounds();
+ if (ret) *ret *= m;
+ return ret;
+}
- // unless this is geometric bbox, crop the bbox by clip path, if any
- if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX && clip_ref->getObject()) {
- NRRect b;
- SP_CLIPPATH(clip_ref->getObject())->getBBox(&b, transform, flags);
- nr_rect_d_intersect (&this_bbox, &this_bbox, &b);
+Geom::OptRect SPItem::desktopPreferredBounds() const
+{
+ if (Inkscape::Preferences::get()->getInt("/tools/bounding_box") == 0) {
+ return desktopBounds(SPItem::VISUAL_BBOX);
+ } else {
+ return desktopBounds(SPItem::GEOMETRIC_BBOX);
}
+}
- // if non-empty (with some tolerance - ?) union this_bbox with the bbox we've got passed
- if ( fabs(this_bbox.x1-this_bbox.x0) > -0.00001 && fabs(this_bbox.y1-this_bbox.y0) > -0.00001 ) {
- nr_rect_d_union (bbox, bbox, &this_bbox);
+Geom::OptRect SPItem::desktopBounds(BBoxType type) const
+{
+ if (type == GEOMETRIC_BBOX) {
+ return desktopGeometricBounds();
+ } else {
+ return desktopVisualBounds();
}
}
@@ -890,41 +852,16 @@ unsigned SPItem::pos_in_parent()
return 0;
}
-void SPItem::getBboxDesktop(NRRect *bbox, SPItem::BBoxType type)
-{
- g_assert(bbox != NULL);
-
- invoke_bbox( bbox, i2d_affine(), TRUE, type);
-}
-
-Geom::OptRect SPItem::getBboxDesktop(SPItem::BBoxType type)
-{
- Geom::OptRect rect = Geom::OptRect();
- invoke_bbox( rect, i2d_affine(), TRUE, type);
- return rect;
-}
-
-void SPItem::sp_item_private_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/)
+void SPItem::sp_item_private_snappoints(SPItem const * /*item*/, std::vector<Inkscape::SnapCandidatePoint> &/*p*/, Inkscape::SnapPreferences const * /*snapprefs*/)
{
/* This will only be called if the derived class doesn't override this.
* see for example sp_genericellipse_snappoints in sp-ellipse.cpp
* We don't know what shape we could be dealing with here, so we'll just
- * return the corners of the bounding box */
-
- Geom::OptRect bbox = item->getBounds(item->i2d_affine());
-
- if (bbox) {
- Geom::Point p1, p2;
- p1 = bbox->min();
- p2 = bbox->max();
- p.push_back(Inkscape::SnapCandidatePoint(p1, Inkscape::SNAPSOURCE_BBOX_CORNER, Inkscape::SNAPTARGET_BBOX_CORNER));
- p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(p1[Geom::X], p2[Geom::Y]), Inkscape::SNAPSOURCE_BBOX_CORNER, Inkscape::SNAPTARGET_BBOX_CORNER));
- p.push_back(Inkscape::SnapCandidatePoint(p2, Inkscape::SNAPSOURCE_BBOX_CORNER, Inkscape::SNAPTARGET_BBOX_CORNER));
- p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(p2[Geom::X], p1[Geom::Y]), Inkscape::SNAPSOURCE_BBOX_CORNER, Inkscape::SNAPTARGET_BBOX_CORNER));
- }
-
+ * do nothing
+ */
}
+
void SPItem::getSnappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const
{
// Get the snappoints of the item
@@ -934,7 +871,7 @@ void SPItem::getSnappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscap
}
// Get the snappoints at the item's center
- if (snapprefs != NULL && snapprefs->getIncludeItemCenter()) {
+ if (snapprefs != NULL && snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_ROTATION_CENTER)) {
p.push_back(Inkscape::SnapCandidatePoint(getCenter(), Inkscape::SNAPSOURCE_ROTATION_CENTER, Inkscape::SNAPTARGET_ROTATION_CENTER));
}
@@ -957,7 +894,7 @@ void SPItem::getSnappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscap
for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator p_orig = p_clip_or_mask.begin(); p_orig != p_clip_or_mask.end(); p_orig++) {
// All snappoints are in desktop coordinates, but the item's transformation is
// in document coordinates. Hence the awkward construction below
- Geom::Point pt = desktop->dt2doc((*p_orig).getPoint()) * i2d_affine();
+ Geom::Point pt = desktop->dt2doc((*p_orig).getPoint()) * i2dt_affine();
p.push_back(Inkscape::SnapCandidatePoint(pt, (*p_orig).getSourceType(), (*p_orig).getTargetType()));
}
}
@@ -1040,64 +977,55 @@ unsigned SPItem::display_key_new(unsigned numkeys)
return dkey - numkeys;
}
-NRArenaItem *SPItem::invoke_show(NRArena *arena, unsigned key, unsigned flags)
+Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned key, unsigned flags)
{
- g_assert(arena != NULL);
- g_assert(NR_IS_ARENA(arena));
-
- NRArenaItem *ai = NULL;
+ Inkscape::DrawingItem *ai = NULL;
if (((SPItemClass *) G_OBJECT_GET_CLASS(this))->show) {
- ai = ((SPItemClass *) G_OBJECT_GET_CLASS(this))->show(this, arena, key, flags);
+ ai = ((SPItemClass *) G_OBJECT_GET_CLASS(this))->show(this, drawing, key, flags);
}
if (ai != NULL) {
+ Geom::OptRect item_bbox = geometricBounds();
+
display = sp_item_view_new_prepend(display, this, flags, key, ai);
- nr_arena_item_set_transform(ai, transform);
- nr_arena_item_set_opacity(ai, SP_SCALE24_TO_FLOAT(style->opacity.value));
- nr_arena_item_set_visible(ai, !isHidden());
- nr_arena_item_set_sensitive(ai, sensitive);
+ ai->setTransform(transform);
+ ai->setOpacity(SP_SCALE24_TO_FLOAT(style->opacity.value));
+ ai->setVisible(!isHidden());
+ ai->setSensitive(sensitive);
if (clip_ref->getObject()) {
SPClipPath *cp = clip_ref->getObject();
- if (!display->arenaitem->key) {
- NR_ARENA_ITEM_SET_KEY(display->arenaitem, display_key_new(3));
+ if (!display->arenaitem->key()) {
+ display->arenaitem->setKey(display_key_new(3));
}
- int clip_key = NR_ARENA_ITEM_GET_KEY(display->arenaitem);
+ int clip_key = display->arenaitem->key();
// Show and set clip
- NRArenaItem *ac = cp->show(arena, clip_key);
- nr_arena_item_set_clip(ai, ac);
- nr_arena_item_unref(ac);
+ Inkscape::DrawingItem *ac = cp->show(drawing, clip_key);
+ ai->setClip(ac);
// Update bbox, in case the clip uses bbox units
- NRRect bbox;
- invoke_bbox( &bbox, Geom::identity(), TRUE);
- SP_CLIPPATH(cp)->setBBox(clip_key, &bbox);
+ SP_CLIPPATH(cp)->setBBox(clip_key, item_bbox);
cp->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
if (mask_ref->getObject()) {
SPMask *mask = mask_ref->getObject();
- if (!display->arenaitem->key) {
- NR_ARENA_ITEM_SET_KEY(display->arenaitem, display_key_new(3));
+ if (!display->arenaitem->key()) {
+ display->arenaitem->setKey(display_key_new(3));
}
- int mask_key = NR_ARENA_ITEM_GET_KEY(display->arenaitem);
+ int mask_key = display->arenaitem->key();
// Show and set mask
- NRArenaItem *ac = sp_mask_show(mask, arena, mask_key);
- nr_arena_item_set_mask(ai, ac);
- nr_arena_item_unref(ac);
+ Inkscape::DrawingItem *ac = sp_mask_show(mask, drawing, mask_key);
+ ai->setMask(ac);
// Update bbox, in case the mask uses bbox units
- NRRect bbox;
- invoke_bbox( &bbox, Geom::identity(), TRUE);
- sp_mask_set_bbox(SP_MASK(mask), mask_key, &bbox);
+ sp_mask_set_bbox(SP_MASK(mask), mask_key, item_bbox);
mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}
- NR_ARENA_ITEM_SET_DATA(ai, this);
- Geom::OptRect item_bbox;
- invoke_bbox( item_bbox, Geom::identity(), TRUE, SPItem::GEOMETRIC_BBOX);
- nr_arena_item_set_item_bbox(ai, item_bbox);
+ ai->setData(this);
+ ai->setItemBounds(item_bbox);
}
return ai;
@@ -1115,20 +1043,19 @@ void SPItem::invoke_hide(unsigned key)
SPItemView *next = v->next;
if (v->key == key) {
if (clip_ref->getObject()) {
- (clip_ref->getObject())->hide(NR_ARENA_ITEM_GET_KEY(v->arenaitem));
- nr_arena_item_set_clip(v->arenaitem, NULL);
+ (clip_ref->getObject())->hide(v->arenaitem->key());
+ v->arenaitem->setClip(NULL);
}
if (mask_ref->getObject()) {
- sp_mask_hide(mask_ref->getObject(), NR_ARENA_ITEM_GET_KEY(v->arenaitem));
- nr_arena_item_set_mask(v->arenaitem, NULL);
+ sp_mask_hide(mask_ref->getObject(), v->arenaitem->key());
+ v->arenaitem->setMask(NULL);
}
if (!ref) {
display = v->next;
} else {
ref->next = v->next;
}
- nr_arena_item_unparent(v->arenaitem);
- nr_arena_item_unref(v->arenaitem);
+ delete v->arenaitem;
g_free(v);
} else {
ref = v;
@@ -1190,7 +1117,13 @@ void SPItem::adjust_gradient( Geom::Affine const &postmul, bool set )
void SPItem::adjust_stroke( gdouble ex )
{
- if ( style && !style->stroke.isNone() && !NR_DF_TEST_CLOSE(ex, 1.0, NR_EPSILON) ) {
+ if (freeze_stroke_width) {
+ return;
+ }
+
+ SPStyle *style = this->style;
+
+ if (style && !style->stroke.isNone() && !Geom::are_near(ex, 1.0, Geom::EPSILON)) {
style->stroke_width.computed *= ex;
style->stroke_width.set = TRUE;
@@ -1240,6 +1173,20 @@ void SPItem::adjust_stroke_width_recursive(double expansion)
}
}
+void SPItem::freeze_stroke_width_recursive(bool freeze)
+{
+ freeze_stroke_width = freeze;
+
+// A clone's child is the ghost of its original - we must not touch it, skip recursion
+ if ( !SP_IS_USE(this) ) {
+ for ( SPObject *o = children; o; o = o->getNext() ) {
+ if (SP_IS_ITEM(o)) {
+ SP_ITEM(o)->freeze_stroke_width_recursive(freeze);
+ }
+ }
+ }
+}
+
/**
* Recursively adjust rx and ry of rects.
*/
@@ -1336,10 +1283,15 @@ void SPItem::doWriteTransform(Inkscape::XML::Node *repr, Geom::Affine const &tra
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
if (compensate) {
- // recursively compensate for stroke scaling, depending on user preference
+ // recursively compensating for stroke scaling will not work, because it can be scaled to zero or infinite
+ // from which we cannot ever recover by applying an inverse scale; therefore we temporarily block any changes
+ // to the strokewidth instead, and unblock these after the transformation
+ // (as reported in https://bugs.launchpad.net/inkscape/+bug/825840/comments/4)
if (!prefs->getBool("/options/transform/stroke", true)) {
- double const expansion = 1. / advertized_transform.descrim();
- adjust_stroke_width_recursive(expansion);
+ freeze_stroke_width_recursive(true);
+ // This will only work if the item has a set_transform method (in this method adjust_stroke() will be called)
+ // We will still have to apply the inverse scaling to other items, not having a set_transform method
+ // such as ellipses and stars
}
// recursively compensate rx/ry of a rect if requested
@@ -1370,13 +1322,25 @@ void SPItem::doWriteTransform(Inkscape::XML::Node *repr, Geom::Affine const &tra
!preserve && // user did not chose to preserve all transforms
!clip_ref->getObject() && // the object does not have a clippath
!mask_ref->getObject() && // the object does not have a mask
- !(!transform.isTranslation() && style && style->getFilter())
- // the object does not have a filter, or the transform is translation (which is supposed to not affect filters)
+ !(!transform.isTranslation() && style && style->getFilter()) // the object does not have a filter, or the transform is translation (which is supposed to not affect filters)
) {
transform_attr = ((SPItemClass *) G_OBJECT_GET_CLASS(this))->set_transform(this, transform);
+ freeze_stroke_width_recursive(false);
+ } else {
+ freeze_stroke_width_recursive(false);
+ if (compensate) {
+ if (!prefs->getBool("/options/transform/stroke", true)) {
+ // Recursively compensate for stroke scaling, depending on user preference
+ // (As to why we need to do this, see the comment a few lines above near the freeze_stroke_width_recursive(true) call)
+ double const expansion = 1. / advertized_transform.descrim();
+ adjust_stroke_width_recursive(expansion);
+ }
+ }
}
set_item_transform(transform_attr);
+
+
// Note: updateRepr comes before emitting the transformed signal since
// it causes clone SPUse's copy of the original object to brought up to
// date with the original. Otherwise, sp_use_bbox returns incorrect
@@ -1402,7 +1366,7 @@ gint SPItem::emitEvent(SPEvent &event)
*/
void SPItem::set_item_transform(Geom::Affine const &transform_matrix)
{
- if (!matrix_equalp(transform_matrix, transform, NR_EPSILON)) {
+ if (!Geom::are_near(transform_matrix, transform, 1e-18)) {
transform = transform_matrix;
/* The SP_OBJECT_USER_MODIFIED_FLAG_B is used to mark the fact that it's only a
transformation. It's apparently not used anywhere else. */
@@ -1467,11 +1431,20 @@ Geom::Affine SPItem::i2doc_affine() const
/**
* Returns the transformation from item to desktop coords
*/
-Geom::Affine SPItem::i2d_affine() const
+Geom::Affine SPItem::i2dt_affine() const
{
- Geom::Affine const ret( i2doc_affine()
- * Geom::Scale(1, -1)
- * Geom::Translate(0, document->getHeight()) );
+ Geom::Affine ret;
+ SPDesktop const *desktop = inkscape_active_desktop();
+ if ( desktop ) {
+ ret = i2doc_affine() * desktop->doc2dt();
+ } else {
+ // TODO temp code to prevent crashing on command-line launch:
+ ret = i2doc_affine()
+ * Geom::Scale(1, -1)
+ * Geom::Translate(0, document->getHeight());
+
+ g_return_val_if_fail(desktop != NULL, ret);
+ }
return ret;
}
@@ -1479,10 +1452,10 @@ void SPItem::set_i2d_affine(Geom::Affine const &i2dt)
{
Geom::Affine dt2p; /* desktop to item parent transform */
if (parent) {
- dt2p = static_cast<SPItem *>(parent)->i2d_affine().inverse();
+ dt2p = static_cast<SPItem *>(parent)->i2dt_affine().inverse();
} else {
- dt2p = ( Geom::Translate(0, -document->getHeight())
- * Geom::Scale(1, -1) );
+ SPDesktop *dt = inkscape_active_desktop();
+ dt2p = dt->dt2doc();
}
Geom::Affine const i2p( i2dt * dt2p );
@@ -1496,32 +1469,32 @@ void SPItem::set_i2d_affine(Geom::Affine const &i2dt)
Geom::Affine SPItem::dt2i_affine() const
{
/* fixme: Implement the right way (Lauris) */
- return i2d_affine().inverse();
+ return i2dt_affine().inverse();
}
/* Item views */
-SPItemView *SPItem::sp_item_view_new_prepend(SPItemView *list, SPItem *item, unsigned flags, unsigned key, NRArenaItem *arenaitem)
+SPItemView *SPItem::sp_item_view_new_prepend(SPItemView *list, SPItem *item, unsigned flags, unsigned key, Inkscape::DrawingItem *drawing_item)
{
g_assert(item != NULL);
g_assert(SP_IS_ITEM(item));
- g_assert(arenaitem != NULL);
- g_assert(NR_IS_ARENA_ITEM(arenaitem));
+ g_assert(drawing_item != NULL);
SPItemView *new_view = g_new(SPItemView, 1);
new_view->next = list;
new_view->flags = flags;
new_view->key = key;
- new_view->arenaitem = arenaitem;
+ new_view->arenaitem = drawing_item;
return new_view;
}
SPItemView *SPItem::sp_item_view_list_remove(SPItemView *list, SPItemView *view)
{
+ SPItemView *ret = list;
if (view == list) {
- list = list->next;
+ ret = list->next;
} else {
SPItemView *prev;
prev = list;
@@ -1529,17 +1502,17 @@ SPItemView *SPItem::sp_item_view_list_remove(SPItemView *list, SPItemView *view)
prev->next = view->next;
}
- nr_arena_item_unref(view->arenaitem);
+ delete view->arenaitem;
g_free(view);
- return list;
+ return ret;
}
/**
* Return the arenaitem corresponding to the given item in the display
* with the given key
*/
-NRArenaItem *SPItem::get_arenaitem(unsigned key)
+Inkscape::DrawingItem *SPItem::get_arenaitem(unsigned key)
{
for ( SPItemView *iv = display ; iv ; iv = iv->next ) {
if ( iv->key == key ) {
@@ -1574,15 +1547,10 @@ SPItem *sp_item_first_item_child(SPObject *obj)
}
void SPItem::convert_to_guides() {
- SPDesktop *dt = inkscape_active_desktop();
- sp_desktop_namedview(dt);
-
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
int prefs_bbox = prefs->getInt("/tools/bounding_box", 0);
- SPItem::BBoxType bbox_type = (prefs_bbox ==0)?
- SPItem::APPROXIMATE_BBOX : SPItem::GEOMETRIC_BBOX;
- Geom::OptRect bbox = getBboxDesktop(bbox_type);
+ Geom::OptRect bbox = (prefs_bbox == 0) ? desktopVisualBounds() : desktopGeometricBounds();
if (!bbox) {
g_warning ("Cannot determine item's bounding box during conversion to guides.\n");
return;
@@ -1600,7 +1568,7 @@ void SPItem::convert_to_guides() {
pts.push_back(std::make_pair(C, D));
pts.push_back(std::make_pair(D, A));
- sp_guide_pt_pairs_to_guides(dt, pts);
+ sp_guide_pt_pairs_to_guides(document, pts);
}
/*