X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fseltrans.cpp;h=a3f34384107f93300bf100471b2dcdb57dc09d0f;hb=d405eeb21d54618e50b98f0483d2663faa13c7d6;hp=bbaaf2f9df1de0957925f6712865ef30f9ff2bd0;hpb=09cab2fed4b3f98c514cbf2ea801be385fb3babd;p=inkscape.git diff --git a/src/seltrans.cpp b/src/seltrans.cpp index bbaaf2f9d..a3f343841 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", "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; @@ -253,23 +259,70 @@ void Inkscape::SelTrans::grab(NR::Point const &p, gdouble x, gdouble y, bool sho _point = p; - _snap_points = selection->getSnapPoints(); - _bbox_points = selection->getBBoxPoints(); + // The selector tool should snap the bbox, special snappoints, and 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 - 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); + // Next, get all points to consider for snapping + SnapManager const &m = _desktop->namedview->snap_manager; + _snap_points = selection->getSnapPoints(m.getIncludeItemCenter()); + std::vector snap_points_hull = selection->getSnapPointsConvexHull(); + 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; + } + + // The lines below are usefull for debugging any snapping issues, as they'll spit out all points that are considered for snapping + + /*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); @@ -281,7 +334,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 +353,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 +373,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 +418,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 +460,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 +488,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 +509,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 +580,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 +597,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 +649,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); @@ -569,7 +666,7 @@ static void sp_sel_trans_handle_grab(SPKnot *knot, guint state, gpointer data) ); } -static void sp_sel_trans_handle_ungrab(SPKnot *knot, guint state, gpointer data) +static void sp_sel_trans_handle_ungrab(SPKnot *knot, guint /*state*/, gpointer /*data*/) { SP_SELECT_CONTEXT(knot->desktop->event_context)->_seltrans->ungrab(); } @@ -595,20 +692,21 @@ static void sp_sel_trans_handle_click(SPKnot *knot, guint state, gpointer data) ); } -void Inkscape::SelTrans::handleClick(SPKnot *knot, guint state, SPSelTransHandle const &handle) +void Inkscape::SelTrans::handleClick(SPKnot */*knot*/, guint state, SPSelTransHandle const &handle) { switch (handle.anchor) { case GTK_ANCHOR_CENTER: 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: @@ -616,7 +714,7 @@ void Inkscape::SelTrans::handleClick(SPKnot *knot, guint state, SPSelTransHandle } } -void Inkscape::SelTrans::handleGrab(SPKnot *knot, guint state, SPSelTransHandle const &handle) +void Inkscape::SelTrans::handleGrab(SPKnot *knot, guint /*state*/, SPSelTransHandle const &handle) { switch (handle.anchor) { case GTK_ANCHOR_CENTER: @@ -665,18 +763,19 @@ 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); @@ -688,19 +787,26 @@ gboolean Inkscape::SelTrans::handleRequest(SPKnot *knot, NR::Point *position, gu } -void Inkscape::SelTrans::_selChanged(Inkscape::Selection *selection) +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", "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(); } } -void Inkscape::SelTrans::_selModified(Inkscape::Selection *selection, guint flags) +void Inkscape::SelTrans::_selModified(Inkscape::Selection */*selection*/, guint /*flags*/) { if (!_grabbed) { _updateVolatileState(); + _current.set_identity(); // reset internal flag _changed = false; @@ -771,6 +877,15 @@ gboolean Inkscape::SelTrans::scaleRequest(NR::Point &pt, guint state) } } + if (state & GDK_MOD1_MASK) { // scale by an integer multiplier/divider + for ( unsigned int i = 0 ; i < 2 ; i++ ) { + if (fabs(s[i]) > 1) + s[i] = round(s[i]); + else + s[i] = 1/round(1/(MIN(s[i], 10))); + } + } + SnapManager const &m = _desktop->namedview->snap_manager; /* Get a STL list of the selected items. @@ -782,40 +897,29 @@ 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. - */ - - NR::Point const cv = NR::Point( - pt[NR::X] > _origin[NR::X] ? 1 : -1, - pt[NR::Y] > _origin[NR::Y] ? 1 : -1 - ); + // 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]); + } - std::pair bb = m.constrainedSnapScale(Snapper::BBOX_POINT, + // Snap along a suitable constraint vector from the origin. + std::pair bb = m.constrainedSnapScale(Snapper::SNAPPOINT_BBOX, _bbox_points, it, - Snapper::ConstraintLine(_origin, cv), s, - _origin); + _origin_for_bboxpoints); - std::pair sn = m.constrainedSnapScale(Snapper::SNAP_POINT, + std::pair sn = m.constrainedSnapScale(Snapper::SNAPPOINT_NODE, _snap_points, it, - Snapper::ConstraintLine(_origin, cv), s, - _origin); - - 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]); - } - - } else { + _origin_for_specpoints); + if (bb.second || sn.second) { // If we snapped to something /* Choose the smaller difference in scale. Since s[X] == s[Y] we can ** just compare difference in s[X]. */ @@ -826,28 +930,30 @@ gboolean Inkscape::SelTrans::scaleRequest(NR::Point &pt, guint state) } else { /* Scale aspect ratio is unlocked */ - - std::pair bb = m.freeSnapScale(Snapper::BBOX_POINT, + + std::pair bb = m.freeSnapScale(Snapper::SNAPPOINT_BBOX, _bbox_points, it, s, - _origin); - std::pair sn = m.freeSnapScale(Snapper::SNAP_POINT, + _origin_for_bboxpoints); + std::pair sn = m.freeSnapScale(Snapper::SNAPPOINT_NODE, _snap_points, it, s, - _origin); - - /* 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; + _origin_for_specpoints); + + if (bb.second || sn.second) { // If we snapped to something + /* 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 */ @@ -895,6 +1001,13 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, NR:: s[axis] = 1e-15; } + if (state & GDK_MOD1_MASK) { // scale by an integer multiplier/divider + if (fabs(s[axis]) > 1) + s[axis] = round(s[axis]); + else + s[axis] = 1/round(1/(MIN(s[axis], 10))); + } + /* Get a STL list of the selected items. ** FIXME: this should probably be done by Inkscape::Selection. */ @@ -906,57 +1019,68 @@ 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( - Snapper::BBOX_POINT, + std::pair const bb = m.constrainedSnapStretch( + Snapper::SNAPPOINT_BBOX, _bbox_points, it, s[axis], - _origin, + _origin_for_bboxpoints, axis, true); - std::pair const sn = m.freeSnapStretch( - Snapper::SNAP_POINT, + std::pair const sn = m.constrainedSnapStretch( + Snapper::SNAPPOINT_NODE, _snap_points, it, s[axis], - _origin, + _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]); + if (bb.second || sn.second) { // If we snapped to something + /* 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; + NR::Coord const ratio = (bd < sd) ? bb.first : sn.first; + + if (fabs(ratio) < NR_HUGE) { + s[axis] = fabs(ratio) * sign(s[axis]); + } + s[perp] = fabs(s[axis]); + } } else { - std::pair const bb = m.freeSnapStretch( - Snapper::BBOX_POINT, + std::pair const bb = m.constrainedSnapStretch( + Snapper::SNAPPOINT_BBOX, _bbox_points, it, s[axis], - _origin, + _origin_for_bboxpoints, axis, false); - std::pair const sn = m.freeSnapStretch( - Snapper::SNAP_POINT, + std::pair const sn = m.constrainedSnapStretch( + Snapper::SNAPPOINT_NODE, _snap_points, it, s[axis], - _origin, + _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; + if (bb.second || sn.second) { // If we snapped to something + /* 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; + NR::Coord const nw = (bd < sd) ? bb.first : sn.first; // new stretch scale + if (fabs(nw) < NR_HUGE) { + s[axis] = nw; + } + s[perp] = 1; + } } pt = ( _point - _origin ) * NR::scale(s) + _origin; @@ -1022,9 +1146,36 @@ 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); + /* Get a STL list of the selected items. + ** FIXME: this should probably be done by Inkscape::Selection. + */ + std::list it; + for (GSList const *i = _selection->itemList(); i != NULL; i = i->next) { + it.push_back(reinterpret_cast(i->data)); + } + + SnapManager const &m = _desktop->namedview->snap_manager; + + std::pair bb = m.freeSnapSkew(Inkscape::Snapper::SNAPPOINT_BBOX, + _bbox_points, + it, + skew[dim_a], + _origin_for_bboxpoints, + dim_b); + + std::pair sn = m.freeSnapSkew(Inkscape::Snapper::SNAPPOINT_NODE, + _snap_points, + it, + 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]; @@ -1098,7 +1249,7 @@ gboolean Inkscape::SelTrans::centerRequest(NR::Point &pt, guint state) using NR::Y; SnapManager const &m = _desktop->namedview->snap_manager; - pt = m.freeSnap(Snapper::SNAP_POINT, pt, NULL).getPoint(); + pt = m.freeSnap(Snapper::SNAPPOINT_NODE, pt, NULL).getPoint(); if (state & GDK_CONTROL_MASK) { if ( fabs(_point[X] - pt[X]) > fabs(_point[Y] - pt[Y]) ) { @@ -1108,22 +1259,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]; } } } @@ -1203,18 +1353,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) +void Inkscape::SelTrans::scale(NR::Point &pt, guint /*state*/) { + if (!_bbox) { + return; + } + NR::Point const offset = _point - _origin; NR::scale s (1, 1); @@ -1224,17 +1389,25 @@ 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 } -void Inkscape::SelTrans::skew(SPSelTransHandle const &handle, NR::Point &pt, guint state) +void Inkscape::SelTrans::skew(SPSelTransHandle const &handle, NR::Point &pt, guint /*state*/) { NR::Point const offset = _point - _origin; @@ -1268,7 +1441,7 @@ void Inkscape::SelTrans::skew(SPSelTransHandle const &handle, NR::Point &pt, gui transform(skew, _origin); } -void Inkscape::SelTrans::rotate(NR::Point &pt, guint state) +void Inkscape::SelTrans::rotate(NR::Point &pt, guint /*state*/) { NR::Point const offset = _point - _origin; @@ -1289,7 +1462,7 @@ void Inkscape::SelTrans::rotate(NR::Point &pt, guint state) transform(rotate, _origin); } -void sp_sel_trans_center(Inkscape::SelTrans *seltrans, SPSelTransHandle const &, NR::Point &pt, guint state) +void sp_sel_trans_center(Inkscape::SelTrans *seltrans, SPSelTransHandle const &, NR::Point &pt, guint /*state*/) { seltrans->setCenter(pt); } @@ -1320,7 +1493,7 @@ void Inkscape::SelTrans::moveTo(NR::Point const &xy, guint state) ** FIXME: this will snap to more than just the grid, nowadays. */ - dxy = m.freeSnap(Snapper::SNAP_POINT, dxy, NULL).getPoint(); + dxy = m.freeSnap(Snapper::SNAPPOINT_NODE, dxy, NULL).getPoint(); } else if (!shift) { @@ -1337,13 +1510,13 @@ void Inkscape::SelTrans::moveTo(NR::Point const &xy, guint state) /* Snap to things, and also constrain to horizontal or vertical movement */ for (unsigned int dim = 0; dim < 2; dim++) { - s.push_back(m.constrainedSnapTranslation(Inkscape::Snapper::BBOX_POINT, + s.push_back(m.constrainedSnapTranslation(Inkscape::Snapper::SNAPPOINT_BBOX, _bbox_points, it, Inkscape::Snapper::ConstraintLine(component_vectors[dim]), dxy)); - - s.push_back(m.constrainedSnapTranslation(Inkscape::Snapper::SNAP_POINT, + + s.push_back(m.constrainedSnapTranslation(Inkscape::Snapper::SNAPPOINT_NODE, _snap_points, it, Inkscape::Snapper::ConstraintLine(component_vectors[dim]), @@ -1352,12 +1525,20 @@ void Inkscape::SelTrans::moveTo(NR::Point const &xy, guint state) } else { - /* Snap to things with no constraint */ + // Let's leave this timer code here for a while. I'll probably need it in the near future (Diederik van Lierop) + /* GTimeVal starttime; + GTimeVal endtime; + g_get_current_time(&starttime); */ - s.push_back(m.freeSnapTranslation(Inkscape::Snapper::BBOX_POINT, + /* Snap to things with no constraint */ + s.push_back(m.freeSnapTranslation(Inkscape::Snapper::SNAPPOINT_BBOX, _bbox_points, it, dxy)); - s.push_back(m.freeSnapTranslation(Inkscape::Snapper::SNAP_POINT, + s.push_back(m.freeSnapTranslation(Inkscape::Snapper::SNAPPOINT_NODE, _snap_points, it, dxy)); + + /*g_get_current_time(&endtime); + double elapsed = ((((double)endtime.tv_sec - starttime.tv_sec) * G_USEC_PER_SEC + (endtime.tv_usec - starttime.tv_usec))) / 1000.0; + std::cout << "Time spent snapping: " << elapsed << std::endl; */ } /* Pick one */