X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fseltrans.cpp;h=9c874c242cb666d8d2b07f18c92deb0e7b1846bd;hb=724821145d62dee9f97465c706952582da6e432d;hp=6b61beac49b796eb6d804b691b7f7d59ac2208b6;hpb=46e288e75cd7b4d376ace88c57aec3b47a148a20;p=inkscape.git diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 6b61beac4..9c874c242 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -39,6 +39,7 @@ #include "seltrans.h" #include "selection-chemistry.h" #include "sp-metrics.h" +#include "verbs.h" #include #include "display/sp-ctrlline.h" #include "prefs-utils.h" @@ -87,11 +88,15 @@ Inkscape::SelTrans::SelTrans(SPDesktop *desktop) : _show(SHOW_CONTENT), _grabbed(false), _show_handles(true), - _box(NR::Point(0,0), NR::Point(0,0)), + _bbox(NR::Nothing()), + _approximate_bbox(NR::Nothing()), _chandle(NULL), _stamp_cache(NULL), _message_context(desktop->messageStack()) { + gchar const *prefs_bbox = prefs_get_string_attribute("tools.select", "bounding_box"); + _snap_bbox_type = (prefs_bbox != NULL && strcmp(prefs_bbox, "geometric")==0)? SPItem::GEOMETRIC_BBOX : SPItem::APPROXIMATE_BBOX; + g_return_if_fail(desktop != NULL); for (int i = 0; i < 8; i++) { @@ -100,6 +105,7 @@ Inkscape::SelTrans::SelTrans(SPDesktop *desktop) : } _updateVolatileState(); + _current.set_identity(); _center_is_set = false; // reread _center from items, or set to bbox midpoint @@ -218,11 +224,10 @@ void Inkscape::SelTrans::setCenter(NR::Point const &p) // Write the new center position into all selected items for (GSList const *l = _desktop->selection->itemList(); l; l = l->next) { - SPItem *it = (SPItem*)sp_object_ref(SP_OBJECT(l->data), NULL); + SPItem *it = (SPItem*)SP_OBJECT(l->data); it->setCenter(p); - SP_OBJECT(it)->updateRepr(); + // only set the value; updating repr and document_done will be done once, on ungrab } - sp_document_maybe_done (sp_desktop_document(_desktop), "center::move"); _updateHandles(); } @@ -236,6 +241,7 @@ void Inkscape::SelTrans::grab(NR::Point const &p, gdouble x, gdouble y, bool sho _grabbed = true; _show_handles = show_handles; _updateVolatileState(); + _current.set_identity(); _changed = false; @@ -252,25 +258,70 @@ void Inkscape::SelTrans::grab(NR::Point const &p, gdouble x, gdouble y, bool sho _current.set_identity(); _point = p; - - _snap_points = selection->getSnapPointsConvexHull(); - _bbox_points = selection->getBBoxPointsOuter(); - - gchar const *scale_origin = prefs_get_string_attribute("tools.select", "scale_origin"); - bool const origin_on_bbox = (scale_origin == NULL || !strcmp(scale_origin, "bbox")); - NR::Rect op_box = _box; - if (origin_on_bbox == false && _snap_points.empty() == false) { - std::vector::iterator i = _snap_points.begin(); - op_box = NR::Rect(*i, *i); + + // The selector tool should snap the bbox and the special snappoints, but not path nodes + // (The special points are the handles, center, rotation axis, font baseline, ends of spiral, etc.) + + // First, determine the bounding box for snapping ... + _bbox = selection->bounds(_snap_bbox_type); + _approximate_bbox = selection->bounds(SPItem::APPROXIMATE_BBOX); // Used for correctly scaling the strokewidth + + + // Next, get all special points for snapping + _snap_points = selection->getSnapPoints(); // Excludes path nodes + std::vector snap_points_hull = selection->getSnapPointsConvexHull(); // Includes path nodes + if (_snap_points.size() > 100) { + /* Snapping a huge number of nodes will take way too long, so limit the number of snappable nodes + An average user would rarely ever try to snap such a large number of nodes anyway, because + (s)he could hardly discern which node would be snapping */ + _snap_points = snap_points_hull; + // Unfortunately, by now we will have lost the font-baseline snappoints :-( + } + + // Find bbox hulling all special points, which excludes stroke width. Here we need to include the + // path nodes, for example because a rectangle which has been converted to a path doesn't have + // any other special points + NR::Rect snap_points_bbox; + if ( snap_points_hull.empty() == false ) { + std::vector::iterator i = snap_points_hull.begin(); + snap_points_bbox = NR::Rect(*i, *i); i++; - while (i != _snap_points.end()) { - op_box.expandTo(*i); + while (i != snap_points_hull.end()) { + snap_points_bbox.expandTo(*i); i++; } } - - _opposite = ( op_box.min() + ( op_box.dimensions() * NR::scale(1-x, 1-y) ) ); - + + _bbox_points.clear(); + if (_bbox) { + // ... and add the bbox corners to _bbox_points + for ( unsigned i = 0 ; i < 4 ; i++ ) { + _bbox_points.push_back(_bbox->corner(i)); + } + // There are two separate "opposites" (i.e. opposite w.r.t. the handle being dragged): + // - one for snapping the boundingbox, which can be either visual or geometric + // - one for snapping the special points + // The "opposite" in case of a geometric boundingbox always coincides with the "opposite" for the special points + // These distinct "opposites" are needed in the snapmanager to avoid bugs such as #1540195 (in which + // a box is caught between to guides) + _opposite_for_bboxpoints = _bbox->min() + _bbox->dimensions() * NR::scale(1-x, 1-y); + _opposite_for_specpoints = (snap_points_bbox.min() + (snap_points_bbox.dimensions() * NR::scale(1-x, 1-y) ) ); + // Only a single "opposite" can be used in calculating transformations. + _opposite = _opposite_for_bboxpoints; + } + + /*std::cout << "Number of snap points: " << _snap_points.size() << std::endl; + for (std::vector::const_iterator i = _snap_points.begin(); i != _snap_points.end(); i++) + { + std::cout << " " << *i << std::endl; + } + + std::cout << "Number of bbox points: " << _bbox_points.size() << std::endl; + for (std::vector::const_iterator i = _bbox_points.begin(); i != _bbox_points.end(); i++) + { + std::cout << " " << *i << std::endl; + }*/ + if ((x != -1) && (y != -1)) { sp_canvas_item_show(_norm); sp_canvas_item_show(_grip); @@ -281,7 +332,6 @@ void Inkscape::SelTrans::grab(NR::Point const &p, gdouble x, gdouble y, bool sho sp_canvas_item_show(_l[i]); } - _updateHandles(); g_return_if_fail(_stamp_cache == NULL); } @@ -301,13 +351,15 @@ void Inkscape::SelTrans::transform(NR::Matrix const &rel_affine, NR::Point const sp_item_set_i2d_affine(&item, prev_transform * affine); } } else { - NR::Point p[4]; - /* update the outline */ - for (unsigned i = 0 ; i < 4 ; i++) { - p[i] = _box.corner(i) * affine; - } - for (unsigned i = 0 ; i < 4 ; i++) { - sp_ctrlline_set_coords(SP_CTRLLINE(_l[i]), p[i], p[(i+1)%4]); + if (_bbox) { + NR::Point p[4]; + /* update the outline */ + for (unsigned i = 0 ; i < 4 ; i++) { + p[i] = _bbox->corner(i) * affine; + } + for (unsigned i = 0 ; i < 4 ; i++) { + sp_ctrlline_set_coords(SP_CTRLLINE(_l[i]), p[i], p[(i+1)%4]); + } } } @@ -319,13 +371,37 @@ void Inkscape::SelTrans::transform(NR::Matrix const &rel_affine, NR::Point const void Inkscape::SelTrans::ungrab() { g_return_if_fail(_grabbed); + _grabbed = false; + _show_handles = true; Inkscape::Selection *selection = sp_desktop_selection(_desktop); - bool updh = true; + _updateVolatileState(); + + for (unsigned i = 0; i < _items.size(); i++) { + sp_object_unref(SP_OBJECT(_items[i].first), NULL); + } + + sp_canvas_item_hide(_norm); + sp_canvas_item_hide(_grip); + + if (_show == SHOW_OUTLINE) { + for (int i = 0; i < 4; i++) + sp_canvas_item_hide(_l[i]); + } + + if (_stamp_cache) { + g_slist_free(_stamp_cache); + _stamp_cache = NULL; + } + + _message_context.clear(); + if (!_empty && _changed) { sp_selection_apply_affine(selection, _current, (_show == SHOW_OUTLINE)? true : false); - _center *= _current; - _center_is_set = true; + if (_center) { + *_center *= _current; + _center_is_set = true; + } // If dragging showed content live, sp_selection_apply_affine cannot change the centers // appropriately - it does not know the original positions of the centers (all objects already have @@ -340,37 +416,39 @@ void Inkscape::SelTrans::ungrab() } } - sp_document_done(sp_desktop_document(_desktop)); - updh = false; - } - - for (unsigned i = 0; i < _items.size(); i++) { - sp_object_unref(SP_OBJECT(_items[i].first), NULL); - } - _items.clear(); - _items_centers.clear(); - - _grabbed = false; - _show_handles = true; + _items.clear(); + _items_centers.clear(); + + if (_current.is_translation()) { + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Move")); + } else if (_current.is_scale()) { + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Scale")); + } else if (_current.is_rotation()) { + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Rotate")); + } else { + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Skew")); + } - sp_canvas_item_hide(_norm); - sp_canvas_item_hide(_grip); + } else { - if (_show == SHOW_OUTLINE) { - for (int i = 0; i < 4; i++) - sp_canvas_item_hide(_l[i]); - } + if (_center_is_set) { + // we were dragging center; update reprs and commit undoable action + for (GSList const *l = _desktop->selection->itemList(); l; l = l->next) { + SPItem *it = (SPItem*)SP_OBJECT(l->data); + SP_OBJECT(it)->updateRepr(); + } + sp_document_done (sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Set center")); + } - _updateVolatileState(); - if (updh) { + _items.clear(); + _items_centers.clear(); _updateHandles(); } - if (_stamp_cache) { - g_slist_free(_stamp_cache); - _stamp_cache = NULL; - } - - _message_context.clear(); } /* fixme: This is really bad, as we compare positions for each stamp (Lauris) */ @@ -408,7 +486,7 @@ void Inkscape::SelTrans::stamp() // remember parent Inkscape::XML::Node *parent = sp_repr_parent(original_repr); - Inkscape::XML::Node *copy_repr = original_repr->duplicate(); + Inkscape::XML::Node *copy_repr = original_repr->duplicate(parent->document()); // add the new repr to the parent parent->appendChild(copy_repr); @@ -429,14 +507,15 @@ void Inkscape::SelTrans::stamp() sp_item_write_transform(copy_item, copy_repr, *new_affine); - if (copy_item->isCenterSet()) { - copy_item->setCenter(_center * _current); + if ( copy_item->isCenterSet() && _center ) { + copy_item->setCenter(*_center * _current); } Inkscape::GC::release(copy_repr); l = l->next; } - sp_document_done(sp_desktop_document(_desktop)); + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Stamp")); } if ( fixup && _stamp_cache ) { @@ -499,11 +578,11 @@ void Inkscape::SelTrans::_updateHandles() _center_is_set = true; } - if ( _state == STATE_SCALE ) { + if ( _state == STATE_SCALE || !_center ) { sp_knot_hide(_chandle); } else { sp_knot_show(_chandle); - sp_knot_moveto(_chandle, &_center); + sp_knot_moveto(_chandle, &*_center); } } @@ -516,15 +595,16 @@ void Inkscape::SelTrans::_updateVolatileState() return; } - _box = selection->bounds(); - if (_box.isEmpty()) { + //Update the bboxes + _bbox = selection->bounds(_snap_bbox_type); + _approximate_bbox = selection->bounds(SPItem::APPROXIMATE_BBOX); + + if (!_bbox) { _empty = true; return; } _strokewidth = stroke_average_width (selection->itemList()); - - _current.set_identity(); } static void sp_remove_handles(SPKnot *knot[], gint num) @@ -567,8 +647,10 @@ void Inkscape::SelTrans::_showHandles(SPKnot *knot[], SPSelTransHandle const han sp_knot_show(knot[i]); NR::Point const handle_pt(handle[i].x, handle[i].y); - NR::Point p( _box.min() - + ( _box.dimensions() + // shouldn't have nullary bbox, but knots + g_assert(_bbox); + NR::Point p( _bbox->min() + + ( _bbox->dimensions() * NR::scale(handle_pt) ) ); sp_knot_moveto(knot[i], &p); @@ -615,13 +697,14 @@ void Inkscape::SelTrans::handleClick(SPKnot *knot, guint state, SPSelTransHandle if (state & GDK_SHIFT_MASK) { // Unset the center position for all selected items for (GSList const *l = _desktop->selection->itemList(); l; l = l->next) { - SPItem *it = (SPItem*)sp_object_ref(SP_OBJECT(l->data), NULL); + SPItem *it = (SPItem*)(SP_OBJECT(l->data)); it->unsetCenter(); SP_OBJECT(it)->updateRepr(); _center_is_set = false; // center has changed _updateHandles(); } - sp_document_maybe_done (sp_desktop_document(_desktop), "center::unset"); + sp_document_done (sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT, + _("Reset center")); } break; default: @@ -678,18 +761,23 @@ gboolean Inkscape::SelTrans::handleRequest(SPKnot *knot, NR::Point *position, gu return TRUE; } - knot->desktop->set_coordinate_status(*position); knot->desktop->setPosition(*position); - if (state & GDK_MOD1_MASK) { *position = _point + ( *position - _point ) / 10; } - if (!(state & GDK_SHIFT_MASK) == !(_state == STATE_ROTATE)) { + if ((!(state & GDK_SHIFT_MASK) == !(_state == STATE_ROTATE)) && (&handle != &handle_center)) { _origin = _opposite; + _origin_for_bboxpoints = _opposite_for_bboxpoints; + _origin_for_specpoints = _opposite_for_specpoints; + } else if (_center) { + _origin = *_center; + _origin_for_bboxpoints = *_center; + _origin_for_specpoints = *_center; } else { - _origin = _center; + // FIXME + return TRUE; } if (handle.request(this, handle, *position, state)) { sp_knot_set_position(knot, position, state); @@ -704,7 +792,13 @@ gboolean Inkscape::SelTrans::handleRequest(SPKnot *knot, NR::Point *position, gu void Inkscape::SelTrans::_selChanged(Inkscape::Selection *selection) { if (!_grabbed) { + // reread in case it changed on the fly: + gchar const *prefs_bbox = prefs_get_string_attribute("tools.select", "bounding_box"); + _snap_bbox_type = (prefs_bbox != NULL && strcmp(prefs_bbox, "geometric")==0)? SPItem::GEOMETRIC_BBOX : SPItem::APPROXIMATE_BBOX; + //SPItem::APPROXIMATE_BBOX will be replaced by SPItem::VISUAL_BBOX, as soon as the latter is implemented properly + _updateVolatileState(); + _current.set_identity(); _center_is_set = false; // center(s) may have changed _updateHandles(); } @@ -714,6 +808,7 @@ void Inkscape::SelTrans::_selModified(Inkscape::Selection *selection, guint flag { if (!_grabbed) { _updateVolatileState(); + _current.set_identity(); // reset internal flag _changed = false; @@ -795,40 +890,44 @@ gboolean Inkscape::SelTrans::scaleRequest(NR::Point &pt, guint state) } if ((state & GDK_CONTROL_MASK) || _desktop->isToolboxButtonActive ("lock")) { - /* Scale is locked to a 1:1 aspect ratio, so that s[X] must be made to equal s[Y]. - ** To do this, we snap along a suitable constraint vector from the origin. - */ + // Scale is locked to a 1:1 aspect ratio, so that s[X] must be made to equal s[Y]. + // + // The aspect-ratio must be locked before snapping + if (fabs(s[NR::X]) > fabs(s[NR::Y])) { + s[NR::X] = fabs(s[NR::Y]) * sign(s[NR::X]); + } else { + s[NR::Y] = fabs(s[NR::X]) * sign(s[NR::Y]); + } + + // Snap along a suitable constraint vector from the origin. + // The inclination of the constraint vector is calculated from the aspect ratio + NR::Point bbox_dim = _bbox->dimensions(); + double const aspect_ratio = bbox_dim[1] / bbox_dim[0]; // = height / width + + // Determine direction of the constraint vector NR::Point const cv = NR::Point( pt[NR::X] > _origin[NR::X] ? 1 : -1, - pt[NR::Y] > _origin[NR::Y] ? 1 : -1 + pt[NR::Y] > _origin[NR::Y] ? aspect_ratio : -aspect_ratio ); std::pair bb = m.constrainedSnapScale(Snapper::BBOX_POINT, _bbox_points, it, - Snapper::ConstraintLine(_origin, cv), + Snapper::ConstraintLine(_origin_for_bboxpoints, cv), s, - _origin); + _origin_for_bboxpoints); std::pair sn = m.constrainedSnapScale(Snapper::SNAP_POINT, _snap_points, it, - Snapper::ConstraintLine(_origin, cv), + Snapper::ConstraintLine(_origin_for_specpoints, cv), s, - _origin); + _origin_for_specpoints); if (bb.second == false && sn.second == false) { - - /* We didn't snap, so just lock aspect ratio */ - if (fabs(s[NR::X]) > fabs(s[NR::Y])) { - s[NR::X] = fabs(s[NR::Y]) * sign(s[NR::X]); - } else { - s[NR::Y] = fabs(s[NR::X]) * sign(s[NR::Y]); - } - + /* We didn't snap, so just keep the locked aspect ratio */ } else { - /* Choose the smaller difference in scale. Since s[X] == s[Y] we can ** just compare difference in s[X]. */ @@ -844,14 +943,14 @@ gboolean Inkscape::SelTrans::scaleRequest(NR::Point &pt, guint state) _bbox_points, it, s, - _origin); + _origin_for_bboxpoints); std::pair sn = m.freeSnapScale(Snapper::SNAP_POINT, _snap_points, it, s, - _origin); + _origin_for_specpoints); - /* Pick the snap that puts us closest to the original scale */ + /* Pick the snap that puts us closest to the original scale */ NR::Coord bd = bb.second ? fabs(NR::L2(NR::Point(bb.first[NR::X], bb.first[NR::Y])) - NR::L2(NR::Point(s[NR::X], s[NR::Y]))) @@ -919,6 +1018,7 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, NR:: SnapManager const &m = _desktop->namedview->snap_manager; if ( state & GDK_CONTROL_MASK ) { + // on ctrl, apply symmetrical scaling instead of stretching s[perp] = fabs(s[axis]); std::pair const bb = m.freeSnapStretch( @@ -926,7 +1026,7 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, NR:: _bbox_points, it, s[axis], - _origin, + _origin_for_bboxpoints, axis, true); @@ -935,7 +1035,7 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, NR:: _snap_points, it, s[axis], - _origin, + _origin_for_specpoints, axis, true); @@ -946,13 +1046,13 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, NR:: s[axis] = fabs(ratio) * sign(s[axis]); s[perp] = fabs(s[axis]); } else { - + std::pair const bb = m.freeSnapStretch( Snapper::BBOX_POINT, _bbox_points, it, s[axis], - _origin, + _origin_for_bboxpoints, axis, false); @@ -961,7 +1061,7 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, NR:: _snap_points, it, s[axis], - _origin, + _origin_for_specpoints, axis, false); @@ -1041,15 +1141,15 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, NR::Poi _bbox_points, std::list(), skew[dim_a], - _origin, - dim_a); + _origin_for_bboxpoints, + dim_b); std::pair sn = m.freeSnapSkew(Inkscape::Snapper::SNAP_POINT, _snap_points, std::list(), skew[dim_a], - _origin, - dim_a); + _origin_for_specpoints, + dim_b); if (bb.second || sn.second) { /* We snapped something, so change the skew to reflect it */ @@ -1140,22 +1240,21 @@ gboolean Inkscape::SelTrans::centerRequest(NR::Point &pt, guint state) } } - if (!(state & GDK_SHIFT_MASK)) { + if ( !(state & GDK_SHIFT_MASK) && _bbox ) { // screen pixels to snap center to bbox #define SNAP_DIST 5 // FIXME: take from prefs double snap_dist = SNAP_DIST / _desktop->current_zoom(); for (int i = 0; i < 2; i++) { - - if (fabs(pt[i] - _box.min()[i]) < snap_dist) { - pt[i] = _box.min()[i]; + if (fabs(pt[i] - _bbox->min()[i]) < snap_dist) { + pt[i] = _bbox->min()[i]; } - if (fabs(pt[i] - _box.midpoint()[i]) < snap_dist) { - pt[i] = _box.midpoint()[i]; + if (fabs(pt[i] - _bbox->midpoint()[i]) < snap_dist) { + pt[i] = _bbox->midpoint()[i]; } - if (fabs(pt[i] - _box.max()[i]) < snap_dist) { - pt[i] = _box.max()[i]; + if (fabs(pt[i] - _bbox->max()[i]) < snap_dist) { + pt[i] = _bbox->max()[i]; } } } @@ -1235,18 +1334,33 @@ void Inkscape::SelTrans::stretch(SPSelTransHandle const &handle, NR::Point &pt, s[!dim] = fabs(s[dim]); } - NR::Point new_bbox_min = _box.min() * (NR::translate(-scale_origin) * NR::Matrix(s) * NR::translate(scale_origin)); - NR::Point new_bbox_max = _box.max() * (NR::translate(-scale_origin) * NR::Matrix(s) * NR::translate(scale_origin)); + if (!_bbox) { + return; + } + + NR::Point new_bbox_min = _approximate_bbox->min() * (NR::translate(-scale_origin) * NR::Matrix(s) * NR::translate(scale_origin)); + NR::Point new_bbox_max = _approximate_bbox->max() * (NR::translate(-scale_origin) * NR::Matrix(s) * NR::translate(scale_origin)); + + int transform_stroke = false; + gdouble strokewidth = 0; + + if ( _snap_bbox_type != SPItem::GEOMETRIC_BBOX) { + transform_stroke = prefs_get_int_attribute ("options.transform", "stroke", 1); + strokewidth = _strokewidth; + } - int transform_stroke = prefs_get_int_attribute ("options.transform", "stroke", 1); - NR::Matrix scaler = get_scale_transform_with_stroke (_box, _strokewidth, transform_stroke, - new_bbox_min[NR::X], new_bbox_min[NR::Y], new_bbox_max[NR::X], new_bbox_max[NR::Y]); + NR::Matrix scaler = get_scale_transform_with_stroke (*_approximate_bbox, strokewidth, transform_stroke, + new_bbox_min[NR::X], new_bbox_min[NR::Y], new_bbox_max[NR::X], new_bbox_max[NR::Y]); transform(scaler, NR::Point(0, 0)); // we have already accounted for origin, so pass 0,0 } void Inkscape::SelTrans::scale(NR::Point &pt, guint state) { + if (!_bbox) { + return; + } + NR::Point const offset = _point - _origin; NR::scale s (1, 1); @@ -1256,13 +1370,21 @@ void Inkscape::SelTrans::scale(NR::Point &pt, guint state) if (fabs(s[i]) < 1e-9) s[i] = 1e-9; } - NR::Point new_bbox_min = _box.min() * (NR::translate(-_origin) * NR::Matrix(s) * NR::translate(_origin)); - NR::Point new_bbox_max = _box.max() * (NR::translate(-_origin) * NR::Matrix(s) * NR::translate(_origin)); - - int transform_stroke = prefs_get_int_attribute ("options.transform", "stroke", 1); - NR::Matrix scaler = get_scale_transform_with_stroke (_box, _strokewidth, transform_stroke, - new_bbox_min[NR::X], new_bbox_min[NR::Y], new_bbox_max[NR::X], new_bbox_max[NR::Y]); + + NR::Point new_bbox_min = _approximate_bbox->min() * (NR::translate(-_origin) * NR::Matrix(s) * NR::translate(_origin)); + NR::Point new_bbox_max = _approximate_bbox->max() * (NR::translate(-_origin) * NR::Matrix(s) * NR::translate(_origin)); + + int transform_stroke = false; + gdouble strokewidth = 0; + if ( _snap_bbox_type != SPItem::GEOMETRIC_BBOX) { + transform_stroke = prefs_get_int_attribute ("options.transform", "stroke", 1); + strokewidth = _strokewidth; + } + + NR::Matrix scaler = get_scale_transform_with_stroke (*_approximate_bbox, strokewidth, transform_stroke, + new_bbox_min[NR::X], new_bbox_min[NR::Y], new_bbox_max[NR::X], new_bbox_max[NR::Y]); + transform(scaler, NR::Point(0, 0)); // we have already accounted for origin, so pass 0,0 }