X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fseltrans.cpp;h=82632d45693b65d416534dc79f7f545a6a8f2080;hb=5e230ff5b88fa5522aeaf72d6222c38e4470557d;hp=d6628ea7201a957c6e4b354b97984096001d9c45;hpb=d2378b62d2e48da3d7b23df2be3cf42184293299;p=inkscape.git diff --git a/src/seltrans.cpp b/src/seltrans.cpp index d6628ea72..82632d456 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->getSnapPoints(); - _bbox_points = selection->getBBoxPoints(); - - 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) */ @@ -380,6 +458,13 @@ void Inkscape::SelTrans::stamp() { Inkscape::Selection *selection = sp_desktop_selection(_desktop); + bool fixup = !_grabbed; + if ( fixup && _stamp_cache ) { + // TODO - give a proper fix. Simple temproary work-around for the grab() issue + g_slist_free(_stamp_cache); + _stamp_cache = NULL; + } + /* stamping mode */ if (!_empty) { GSList *l; @@ -401,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); @@ -422,14 +507,21 @@ 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 ) { + // TODO - give a proper fix. Simple temproary work-around for the grab() issue + g_slist_free(_stamp_cache); + _stamp_cache = NULL; } } @@ -486,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); } } @@ -503,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) @@ -554,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); @@ -602,11 +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: @@ -663,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); @@ -689,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(); } @@ -699,6 +808,7 @@ void Inkscape::SelTrans::_selModified(Inkscape::Selection *selection, guint flag { if (!_grabbed) { _updateVolatileState(); + _current.set_identity(); // reset internal flag _changed = false; @@ -769,6 +879,8 @@ gboolean Inkscape::SelTrans::scaleRequest(NR::Point &pt, guint state) } } + SnapManager const &m = _desktop->namedview->snap_manager; + /* Get a STL list of the selected items. ** FIXME: this should probably be done by Inkscape::Selection. */ @@ -778,50 +890,76 @@ 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] */ - - NR::Dim2 locked_dim; - - /* Lock aspect ratio, using the smaller of the x and y factors */ + // 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]); - locked_dim = NR::X; + 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] ? aspect_ratio : -aspect_ratio + ); + + std::pair bb = m.constrainedSnapScale(Snapper::BBOX_POINT, + _bbox_points, + it, + Snapper::ConstraintLine(_origin_for_bboxpoints, cv), + s, + _origin_for_bboxpoints); + + std::pair sn = m.constrainedSnapScale(Snapper::SNAP_POINT, + _snap_points, + it, + Snapper::ConstraintLine(_origin_for_specpoints, cv), + s, + _origin_for_specpoints); + + if (bb.second == false && sn.second == false) { + /* We didn't snap, so just keep the locked aspect ratio */ } else { - s[NR::Y] = fabs(s[NR::X]) * sign(s[NR::Y]); - locked_dim = NR::Y; - } - - /* Snap the scale factor */ - std::pair bb = namedview_vector_snap_list(_desktop->namedview, - Snapper::BBOX_POINT, _bbox_points, - _origin, s, it); - std::pair sn = namedview_vector_snap_list(_desktop->namedview, - Snapper::SNAP_POINT, _snap_points, - _origin, s, it); - - double bd = bb.second ? fabs(bb.first - s[locked_dim]) : NR_HUGE; - double sd = sn.second ? fabs(sn.first - s[locked_dim]) : NR_HUGE; - double r = (bd < sd) ? bb.first : sn.first; - - for ( unsigned int i = 0 ; i < 2 ; i++ ) { - s[i] = r * sign(s[i]); + /* Choose the smaller difference in scale. Since s[X] == s[Y] we can + ** just compare difference in s[X]. + */ + double const bd = bb.second ? fabs(bb.first[NR::X] - s[NR::X]) : NR_HUGE; + double const sd = sn.second ? fabs(sn.first[NR::X] - s[NR::X]) : NR_HUGE; + s = (bd < sd) ? bb.first : sn.first; } } else { /* Scale aspect ratio is unlocked */ - for ( unsigned int i = 0 ; i < 2 ; i++ ) { - std::pair bb = namedview_dim_snap_list_scale(_desktop->namedview, - Snapper::BBOX_POINT, _bbox_points, - _origin, s[i], NR::Dim2(i), it); - std::pair sn = namedview_dim_snap_list_scale(_desktop->namedview, - Snapper::SNAP_POINT, _snap_points, - _origin, s[i], NR::Dim2(i), it); - - /* Pick the snap that puts us closest to the original scale */ - NR::Coord bd = bb.second ? fabs(bb.first - s[i]) : NR_HUGE; - NR::Coord sd = sn.second ? fabs(sn.first - s[i]) : NR_HUGE; - s[i] = (bd < sd) ? bb.first : sn.first; - } + + std::pair bb = m.freeSnapScale(Snapper::BBOX_POINT, + _bbox_points, + it, + s, + _origin_for_bboxpoints); + std::pair sn = m.freeSnapScale(Snapper::SNAP_POINT, + _snap_points, + it, + s, + _origin_for_specpoints); + + /* 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]))) + : NR_HUGE; + NR::Coord sd = sn.second ? + fabs(NR::L2(NR::Point(sn.first[NR::X], sn.first[NR::Y])) - + NR::L2(NR::Point(s[NR::X], s[NR::Y]))) + : NR_HUGE; + s = (bd < sd) ? bb.first : sn.first; } /* Update the knot position */ @@ -877,34 +1015,61 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, NR:: it.push_back(reinterpret_cast(i->data)); } + 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 sn = namedview_vector_snap_list(_desktop->namedview, - Snapper::BBOX_POINT, - _bbox_points, _origin, s, it); - std::pair bb = namedview_vector_snap_list(_desktop->namedview, - Snapper::SNAP_POINT, - _snap_points, _origin, s, it); - - double bd = bb.second ? fabs(bb.first - s[axis]) : NR_HUGE; - double sd = sn.second ? fabs(sn.first - s[axis]) : NR_HUGE; - double ratio = (bd < sd) ? bb.first : sn.first; + std::pair const bb = m.freeSnapStretch( + Snapper::BBOX_POINT, + _bbox_points, + it, + s[axis], + _origin_for_bboxpoints, + axis, + true); + + std::pair const sn = m.freeSnapStretch( + Snapper::SNAP_POINT, + _snap_points, + it, + s[axis], + _origin_for_specpoints, + axis, + true); + + NR::Coord const bd = bb.second ? fabs(bb.first - s[axis]) : NR_HUGE; + NR::Coord const sd = sn.second ? fabs(sn.first - s[axis]) : NR_HUGE; + NR::Coord const ratio = (bd < sd) ? bb.first : sn.first; s[axis] = fabs(ratio) * sign(s[axis]); s[perp] = fabs(s[axis]); } else { - std::pair bb = namedview_dim_snap_list_scale(_desktop->namedview, Snapper::BBOX_POINT, - _bbox_points, _origin, - s[axis], axis, it); - std::pair sn = namedview_dim_snap_list_scale(_desktop->namedview, Snapper::SNAP_POINT, - _snap_points, _origin, - s[axis], axis, it); - - /* Pick the snap that puts us closest to the original scale */ - NR::Coord bd = bb.second ? fabs(bb.first - s[axis]) : NR_HUGE; - NR::Coord sd = sn.second ? fabs(sn.first - s[axis]) : NR_HUGE; + + std::pair const bb = m.freeSnapStretch( + Snapper::BBOX_POINT, + _bbox_points, + it, + s[axis], + _origin_for_bboxpoints, + axis, + false); + + std::pair const sn = m.freeSnapStretch( + Snapper::SNAP_POINT, + _snap_points, + it, + s[axis], + _origin_for_specpoints, + axis, + false); + + /* Choose the smaller difference in scale */ + NR::Coord const bd = bb.second ? fabs(bb.first - s[axis]) : NR_HUGE; + NR::Coord const sd = sn.second ? fabs(sn.first - s[axis]) : NR_HUGE; s[axis] = (bd < sd) ? bb.first : sn.first; + s[perp] = 1; } pt = ( _point - _origin ) * NR::scale(s) + _origin; @@ -970,9 +1135,28 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, NR::Poi } skew[dim_a] = tan(radians) * s[dim_a]; } else { - skew[dim_a] = namedview_dim_snap_list_skew(_desktop->namedview, - Snapper::SNAP_POINT, _snap_points, - _origin, skew[dim_a], dim_b); + SnapManager const &m = _desktop->namedview->snap_manager; + + std::pair bb = m.freeSnapSkew(Inkscape::Snapper::BBOX_POINT, + _bbox_points, + std::list(), + skew[dim_a], + _origin_for_bboxpoints, + dim_b); + + std::pair sn = m.freeSnapSkew(Inkscape::Snapper::SNAP_POINT, + _snap_points, + std::list(), + skew[dim_a], + _origin_for_specpoints, + dim_b); + + if (bb.second || sn.second) { + /* We snapped something, so change the skew to reflect it */ + NR::Coord const bd = bb.second ? bb.first : NR_HUGE; + NR::Coord const sd = sn.second ? sn.first : NR_HUGE; + skew[dim_a] = std::min(bd, sd); + } } pt[dim_b] = ( _point[dim_a] - _origin[dim_a] ) * skew[dim_a] + _point[dim_b]; @@ -1056,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]; } } } @@ -1151,18 +1334,38 @@ 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; + } + + //Get two corners of the new bbox + NR::Point p1 = _approximate_bbox->min() * (NR::translate(-scale_origin) * NR::Matrix(s) * NR::translate(scale_origin)); + NR::Point p2 = _approximate_bbox->max() * (NR::translate(-scale_origin) * NR::Matrix(s) * NR::translate(scale_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]); + //Find the ones at the lower-left and upper-right, as required by get_scale_transform_with_stroke + NR::Point new_bbox_min = NR::Point(std::min(p1[NR::X], p2[NR::X]), std::min(p1[NR::Y], p2[NR::Y])); + NR::Point new_bbox_max = NR::Point(std::max(p1[NR::X], p2[NR::X]), std::max(p1[NR::Y], p2[NR::Y])); + + 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 } void Inkscape::SelTrans::scale(NR::Point &pt, guint state) { + if (!_bbox) { + return; + } + NR::Point const offset = _point - _origin; NR::scale s (1, 1); @@ -1172,13 +1375,26 @@ 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]); - + + //Get two corners of the new bbox + NR::Point p1 = _approximate_bbox->min() * (NR::translate(-_origin) * NR::Matrix(s) * NR::translate(_origin)); + NR::Point p2 = _approximate_bbox->max() * (NR::translate(-_origin) * NR::Matrix(s) * NR::translate(_origin)); + + //Find the ones at the lower-left and upper-right, as required by get_scale_transform_with_stroke + NR::Point new_bbox_min = NR::Point(std::min(p1[NR::X], p2[NR::X]), std::min(p1[NR::Y], p2[NR::Y])); + NR::Point new_bbox_max = NR::Point(std::max(p1[NR::X], p2[NR::X]), std::max(p1[NR::Y], p2[NR::Y])); + + 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 } @@ -1287,10 +1503,15 @@ void Inkscape::SelTrans::moveTo(NR::Point const &xy, guint state) for (unsigned int dim = 0; dim < 2; dim++) { s.push_back(m.constrainedSnapTranslation(Inkscape::Snapper::BBOX_POINT, _bbox_points, - component_vectors[dim], it, dxy)); + it, + Inkscape::Snapper::ConstraintLine(component_vectors[dim]), + dxy)); + s.push_back(m.constrainedSnapTranslation(Inkscape::Snapper::SNAP_POINT, _snap_points, - component_vectors[dim], it, dxy)); + it, + Inkscape::Snapper::ConstraintLine(component_vectors[dim]), + dxy)); } } else {