summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 1113a0e)
raw | patch | inline | side by side (parent: 1113a0e)
author | dvlierop2 <dvlierop2@users.sourceforge.net> | |
Tue, 25 Mar 2008 22:12:45 +0000 (22:12 +0000) | ||
committer | dvlierop2 <dvlierop2@users.sourceforge.net> | |
Tue, 25 Mar 2008 22:12:45 +0000 (22:12 +0000) |
14 files changed:
diff --git a/src/libnr/nr-scale.h b/src/libnr/nr-scale.h
index c9de871dee4a736627534393185b483401ea9b95..b4dbd1fb56a69ebb2b5faae882e587c23941fd6d 100644 (file)
--- a/src/libnr/nr-scale.h
+++ b/src/libnr/nr-scale.h
bool operator!=(scale const &o) const {
return _p != o._p;
}
+
scale inverse() const {
return scale(1/_p[0], 1/_p[1]);
}
+
+ NR::Point point() const {
+ return _p;
+ }
};
} /* namespace NR */
diff --git a/src/mod360.cpp b/src/mod360.cpp
index a30aa65a797d571a2b3c3f1f1e829ad4562978c2..8abda4cf7bba906ad76e6dcf8d2c7f6c5d8e2e73 100644 (file)
--- a/src/mod360.cpp
+++ b/src/mod360.cpp
return ret;
}
+/** Returns \a x wrapped around to between -180 and less than 180,
+ or 0 if \a x isn't finite.
+**/
+double mod360symm(double const x)
+{
+ double m = mod360(x);
+
+ return m < 180.0 ? m : m - 360.0;
+}
+
/*
Local Variables:
mode:c++
diff --git a/src/mod360.h b/src/mod360.h
index 378efab4b2c5775e9ef86e1f3e4adf7182a60b3b..15e006dd700af3455021e34d76f95827e3034aea 100644 (file)
--- a/src/mod360.h
+++ b/src/mod360.h
#define SEEN_MOD360_H
double mod360(double const x);
+double mod360symm (double const x);
#endif /* !SEEN_MOD360_H */
diff --git a/src/object-snapper.h b/src/object-snapper.h
index b9d90deaec045110fd0ebc63dcc2061b23a94cac..924c94e07725925242f884dbcb4d2a567f518fd7 100644 (file)
--- a/src/object-snapper.h
+++ b/src/object-snapper.h
#include "sp-path.h"
#include "splivarot.h"
+
struct SPNamedView;
struct SPItem;
struct SPObject;
std::list<SPItem const *> const &it,
bool const &first_point,
std::vector<NR::Point> &points_to_snap,
- DimensionToSnap const snap_dim) const;
+ DimensionToSnap snap_dim) const;
void _snapNodes(SnappedConstraints &sc,
Inkscape::Snapper::PointType const &t,
diff --git a/src/seltrans.cpp b/src/seltrans.cpp
index 7d09289bc7332247fae97a23f3ea317c0262b067..490ff4644ece120c51aa80b4b1bd737ba3766250 100644 (file)
--- a/src/seltrans.cpp
+++ b/src/seltrans.cpp
/*
* Helper object for transforming selected items
*
- * Author:
+ * Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* bulia byak <buliabyak@users.sf.net>
* Carl Hetherington <inkscape@carlh.net>
+ * Diederik van Lierop <mail@diedenrezi.nl>
*
* Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 1999-2008 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "display/sp-ctrlline.h"
#include "prefs-utils.h"
#include "xml/repr.h"
+#include "mod360.h"
+#include "2geom/angle.h"
#include "isnan.h" //temp fix. make sure included last
_show_handles(true),
_bbox(NR::Nothing()),
_approximate_bbox(NR::Nothing()),
+ _absolute_affine(NR::scale(1,1)),
+ _opposite(NR::Point(0,0)),
+ _opposite_for_specpoints(NR::Point(0,0)),
+ _opposite_for_bboxpoints(NR::Point(0,0)),
+ _origin_for_specpoints(NR::Point(0,0)),
+ _origin_for_bboxpoints(NR::Point(0,0)),
_chandle(NULL),
_stamp_cache(NULL),
- _message_context(desktop->messageStack())
+ _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;
}
_updateVolatileState();
- _current.set_identity();
+ _current_relative_affine.set_identity();
_center_is_set = false; // reread _center from items, or set to bbox midpoint
@@ -243,7 +253,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();
+ _current_relative_affine.set_identity();
_changed = false;
@@ -257,16 +267,18 @@ void Inkscape::SelTrans::grab(NR::Point const &p, gdouble x, gdouble y, bool sho
_items_centers.push_back(std::pair<SPItem *, NR::Point>(it, it->getCenter())); // for content-dragging, we need to remember original centers
}
- _current.set_identity();
-
- _point = p;
-
+ _handle_x = x;
+ _handle_y = y;
+
// 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
+ _geometric_bbox = selection->bounds(SPItem::GEOMETRIC_BBOX);
+ _point = p;
+ _point_geom = _geometric_bbox->min() + _geometric_bbox->dimensions() * NR::scale(x, y);
// Next, get all points to consider for snapping
SnapManager const &m = _desktop->namedview->snap_manager;
@@ -304,11 +316,10 @@ void Inkscape::SelTrans::grab(NR::Point const &p, gdouble x, gdouble y, bool sho
// - 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)
+ // These distinct "opposites" are needed in the snapmanager to avoid bugs such as #sf1540195 (in which
+ // a box is caught between two 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_for_specpoints = snap_points_bbox.min() + snap_points_bbox.dimensions() * NR::scale(1-x, 1-y);
_opposite = _opposite_for_bboxpoints;
}
@@ -367,7 +378,7 @@ void Inkscape::SelTrans::transform(NR::Matrix const &rel_affine, NR::Point const
}
}
- _current = affine;
+ _current_relative_affine = affine;
_changed = true;
_updateHandles();
}
_message_context.clear();
if (!_empty && _changed) {
- sp_selection_apply_affine(selection, _current, (_show == SHOW_OUTLINE)? true : false);
+ sp_selection_apply_affine(selection, _current_relative_affine, (_show == SHOW_OUTLINE)? true : false);
if (_center) {
- *_center *= _current;
+ *_center *= _current_relative_affine;
_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
// the new bboxes). So we need to reset the centers from our saved array.
- if (_show != SHOW_OUTLINE && !_current.is_translation()) {
+ if (_show != SHOW_OUTLINE && !_current_relative_affine.is_translation()) {
for (unsigned i = 0; i < _items_centers.size(); i++) {
SPItem *currentItem = _items_centers[i].first;
if (currentItem->isCenterSet()) { // only if it's already set
- currentItem->setCenter (_items_centers[i].second * _current);
+ currentItem->setCenter (_items_centers[i].second * _current_relative_affine);
SP_OBJECT(currentItem)->updateRepr();
}
}
_items.clear();
_items_centers.clear();
- if (_current.is_translation()) {
+ if (_current_relative_affine.is_translation()) {
sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT,
_("Move"));
- } else if (_current.is_scale()) {
+ } else if (_current_relative_affine.is_scale()) {
sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT,
_("Scale"));
- } else if (_current.is_rotation()) {
+ } else if (_current_relative_affine.is_rotation()) {
sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_SELECT,
_("Rotate"));
} else {
NR::Matrix const *new_affine;
if (_show == SHOW_OUTLINE) {
NR::Matrix const i2d(sp_item_i2d_affine(original_item));
- NR::Matrix const i2dnew( i2d * _current );
+ NR::Matrix const i2dnew( i2d * _current_relative_affine );
sp_item_set_i2d_affine(copy_item, i2dnew);
new_affine = ©_item->transform;
} else {
sp_item_write_transform(copy_item, copy_repr, *new_affine);
if ( copy_item->isCenterSet() && _center ) {
- copy_item->setCenter(*_center * _current);
+ copy_item->setCenter(*_center * _current_relative_affine);
}
Inkscape::GC::release(copy_repr);
@@ -767,6 +778,9 @@ gboolean Inkscape::SelTrans::handleRequest(SPKnot *knot, NR::Point *position, gu
knot->desktop->setPosition(*position);
+ // When holding shift while rotating or skewing, the transformation will be
+ // relative to the point opposite of the handle; otherwise it will be relative
+ // to the center as set for the selection
if ((!(state & GDK_SHIFT_MASK) == !(_state == STATE_ROTATE)) && (&handle != &handle_center)) {
_origin = _opposite;
_origin_for_bboxpoints = _opposite_for_bboxpoints;
//SPItem::APPROXIMATE_BBOX will be replaced by SPItem::VISUAL_BBOX, as soon as the latter is implemented properly
_updateVolatileState();
- _current.set_identity();
+ _current_relative_affine.set_identity();
_center_is_set = false; // center(s) may have changed
_updateHandles();
}
{
if (!_grabbed) {
_updateVolatileState();
- _current.set_identity();
+ _current_relative_affine.set_identity();
// reset internal flag
_changed = false;
gboolean Inkscape::SelTrans::scaleRequest(NR::Point &pt, guint state)
{
- using NR::X;
- using NR::Y;
-
- NR::Point d = _point - _origin;
- NR::scale s(0, 0);
-
- /* Work out the new scale factors `s' */
- for ( unsigned int i = 0 ; i < 2 ; i++ ) {
- if ( fabs(d[i]) > 0.001 ) {
- s[i] = ( pt[i] - _origin[i] ) / d[i];
- if ( fabs(s[i]) < 1e-9 ) {
- s[i] = 1e-9;
- }
- }
- }
-
+
+ // Calculate the scale factors, which can be either visual or geometric
+ // depending on which type of bbox is currently being used (see preferences -> selector tool)
+ NR::scale default_scale = calcScaleFactors(_point, pt, _origin);
+
+ // Find the scale factors for the geometric bbox
+ NR::Point pt_geom = _getGeomHandlePos(pt);
+ NR::scale geom_scale = calcScaleFactors(_point_geom, pt_geom, _origin_for_specpoints);
+
+ _absolute_affine = NR::identity(); //Initialize the scaler
+
if (state & GDK_MOD1_MASK) { // scale by an integer multiplier/divider
+ // We're scaling either the visual or the geometric bbox here (see the comment above)
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)));
+ if (fabs(default_scale[i]) > 1) {
+ default_scale[i] = round(default_scale[i]);
+ } else if (default_scale[i] != 0) {
+ default_scale[i] = 1/round(1/(MIN(default_scale[i], 10)));
+ }
}
- }
-
- SnapManager const &m = _desktop->namedview->snap_manager;
-
- /* Get a STL list of the selected items.
- ** FIXME: this should probably be done by Inkscape::Selection.
- */
- std::list<SPItem const*> it;
- for (GSList const *i = _selection->itemList(); i != NULL; i = i->next) {
- it.push_back(reinterpret_cast<SPItem*>(i->data));
- }
-
- 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].
- //
- // 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]);
+ // Update the knot position
+ pt = _calcAbsAffineDefault(default_scale);
+ // When scaling by an integer, snapping is not needed
+ } else {
+ // In all other cases we should try to snap now
+ SnapManager const &m = _desktop->namedview->snap_manager;
+
+ /* Get a STL list of the selected items.
+ ** FIXME: this should probably be done by Inkscape::Selection.
+ */
+ std::list<SPItem const*> it;
+ for (GSList const *i = _selection->itemList(); i != NULL; i = i->next) {
+ it.push_back(reinterpret_cast<SPItem*>(i->data));
+ }
+
+ std::pair<NR::scale, bool> bb = std::make_pair(NR::scale(1,1), false);
+ std::pair<NR::scale, bool> sn = std::make_pair(NR::scale(1,1), false);
+ NR::Coord bd(NR_HUGE);
+ NR::Coord sd(NR_HUGE);
+
+ 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].
+ //
+ // The aspect-ratio must be locked before snapping
+ if (fabs(default_scale[NR::X]) > fabs(default_scale[NR::Y])) {
+ default_scale[NR::X] = fabs(default_scale[NR::Y]) * sign(default_scale[NR::X]);
+ geom_scale[NR::X] = fabs(geom_scale[NR::Y]) * sign(geom_scale[NR::X]);
} else {
- s[NR::Y] = fabs(s[NR::X]) * sign(s[NR::Y]);
+ default_scale[NR::Y] = fabs(default_scale[NR::X]) * sign(default_scale[NR::Y]);
+ geom_scale[NR::Y] = fabs(geom_scale[NR::X]) * sign(geom_scale[NR::Y]);
}
- // Snap along a suitable constraint vector from the origin.
- std::pair<NR::scale, bool> bb = m.constrainedSnapScale(Snapper::SNAPPOINT_BBOX,
- _bbox_points,
- it,
- s,
- _origin_for_bboxpoints);
-
- std::pair<NR::scale, bool> sn = m.constrainedSnapScale(Snapper::SNAPPOINT_NODE,
- _snap_points,
- it,
- s,
- _origin_for_specpoints);
-
- if (bb.second || sn.second) { // If we snapped to something
+ // Snap along a suitable constraint vector from the origin.
+ bb = m.constrainedSnapScale(Snapper::SNAPPOINT_BBOX, _bbox_points, it, default_scale, _origin_for_bboxpoints);
+ sn = m.constrainedSnapScale(Snapper::SNAPPOINT_NODE, _snap_points, it, geom_scale, _origin_for_specpoints);
+
/* 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 */
-
- std::pair<NR::scale, bool> bb = m.freeSnapScale(Snapper::SNAPPOINT_BBOX,
- _bbox_points,
- it,
- s,
- _origin_for_bboxpoints);
- std::pair<NR::scale, bool> sn = m.freeSnapScale(Snapper::SNAPPOINT_NODE,
- _snap_points,
- it,
- s,
- _origin_for_specpoints);
-
- if (bb.second || sn.second) { // If we snapped to something
+ bd = bb.second ? fabs(bb.first[NR::X] - default_scale[NR::X]) : NR_HUGE;
+ sd = sn.second ? fabs(sn.first[NR::X] - geom_scale[NR::X]) : NR_HUGE;
+ } else {
+ /* Scale aspect ratio is unlocked */
+ bb = m.freeSnapScale(Snapper::SNAPPOINT_BBOX, _bbox_points, it, default_scale, _origin_for_bboxpoints);
+ sn = m.freeSnapScale(Snapper::SNAPPOINT_NODE, _snap_points, it, geom_scale, _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;
+ bd = bb.second ? fabs(NR::L2(bb.first.point()) - NR::L2(default_scale.point())) : NR_HUGE;
+ sd = sn.second ? fabs(NR::L2(sn.first.point()) - NR::L2(geom_scale.point())) : NR_HUGE;
}
- }
-
- /* Update the knot position */
- pt = ( _point - _origin ) * s + _origin;
-
+
+ if (!(bb.second || sn.second)) {
+ // We didn't snap at all! Don't update the handle position, just calculate the new transformation
+ _calcAbsAffineDefault(default_scale);
+ } else if (bd < sd) {
+ // We snapped the bbox (which is either visual or geometric)
+ default_scale = bb.first;
+ // Calculate the new transformation and update the handle position
+ pt = _calcAbsAffineDefault(default_scale);
+ } else {
+ // We snapped the special points (e.g. nodes), which are not at the visual bbox
+ // The handle location however (pt) might however be at the visual bbox, so we
+ // will have to calculate pt taking the stroke width into account
+ geom_scale = sn.first;
+ pt = _calcAbsAffineGeom(geom_scale);
+ }
+ }
+
/* Status text */
_message_context.setF(Inkscape::NORMAL_MESSAGE,
_("<b>Scale</b>: %0.2f%% x %0.2f%%; with <b>Ctrl</b> to lock ratio"),
- 100 * s[NR::X], 100 * s[NR::Y]);
+ 100 * _absolute_affine[0], 100 * _absolute_affine[3]);
return TRUE;
}
gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, NR::Point &pt, guint state)
{
- using NR::X;
- using NR::Y;
-
NR::Dim2 axis, perp;
-
switch (handle.cursor) {
case GDK_TOP_SIDE:
case GDK_BOTTOM_SIDE:
- axis = NR::Y;
- perp = NR::X;
- break;
+ axis = NR::Y;
+ perp = NR::X;
+ break;
case GDK_LEFT_SIDE:
case GDK_RIGHT_SIDE:
- axis = NR::X;
- perp = NR::Y;
- break;
+ axis = NR::X;
+ perp = NR::Y;
+ break;
default:
g_assert_not_reached();
return TRUE;
};
- if ( fabs( _point[axis] - _origin[axis] ) < 1e-15 ) {
- return FALSE;
- }
-
- NR::scale s(1, 1);
- s[axis] = ( ( pt[axis] - _origin[axis] )
- / ( _point[axis] - _origin[axis] ) );
- if ( fabs(s[axis]) < 1e-15 ) {
- 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.
- */
- std::list<SPItem const*> it;
- for (GSList const *i = _selection->itemList(); i != NULL; i = i->next) {
- it.push_back(reinterpret_cast<SPItem*>(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<NR::Coord, bool> const bb = m.constrainedSnapStretch(
- Snapper::SNAPPOINT_BBOX,
- _bbox_points,
- it,
- s[axis],
- _origin_for_bboxpoints,
- axis,
- true);
-
- std::pair<NR::Coord, bool> const sn = m.constrainedSnapStretch(
- Snapper::SNAPPOINT_NODE,
- _snap_points,
- it,
- s[axis],
- _origin_for_specpoints,
- axis,
- true);
-
- 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]);
+ // Calculate the scale factors, which can be either visual or geometric
+ // depending on which type of bbox is currently being used (see preferences -> selector tool)
+ NR::scale default_scale = calcScaleFactors(_point, pt, _origin);
+ default_scale[perp] = 1;
+
+ // Find the scale factors for the geometric bbox
+ NR::Point pt_geom = _getGeomHandlePos(pt);
+ NR::scale geom_scale = calcScaleFactors(_point_geom, pt_geom, _origin_for_specpoints);
+ geom_scale[perp] = 1;
+
+ _absolute_affine = NR::identity(); //Initialize the scaler
+
+ if (state & GDK_MOD1_MASK) { // stretch by an integer multiplier/divider
+ if (fabs(default_scale[axis]) > 1) {
+ default_scale[axis] = round(default_scale[axis]);
+ } else if (default_scale[axis] != 0) {
+ default_scale[axis] = 1/round(1/(MIN(default_scale[axis], 10)));
}
+ // Calculate the new transformation and update the handle position
+ pt = _calcAbsAffineDefault(default_scale);
+ // When stretching by an integer, snapping is not needed
} else {
-
- std::pair<NR::Coord, bool> const bb = m.constrainedSnapStretch(
- Snapper::SNAPPOINT_BBOX,
- _bbox_points,
- it,
- s[axis],
- _origin_for_bboxpoints,
- axis,
- false);
-
- std::pair<NR::Coord, bool> const sn = m.constrainedSnapStretch(
- Snapper::SNAPPOINT_NODE,
- _snap_points,
- it,
- s[axis],
- _origin_for_specpoints,
- axis,
- false);
-
- 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;
+ // In all other cases we should try to snap now
+
+ /* Get a STL list of the selected items.
+ ** FIXME: this should probably be done by Inkscape::Selection.
+ */
+ std::list<SPItem const*> it;
+ for (GSList const *i = _selection->itemList(); i != NULL; i = i->next) {
+ it.push_back(reinterpret_cast<SPItem*>(i->data));
+ }
+
+ SnapManager const &m = _desktop->namedview->snap_manager;
+
+ std::pair<NR::Coord, bool> bb = std::make_pair(NR_HUGE, false);
+ std::pair<NR::Coord, bool> sn = std::make_pair(NR_HUGE, false);
+ NR::Coord bd(NR_HUGE);
+ NR::Coord sd(NR_HUGE);
+
+ bool symmetrical = state & GDK_CONTROL_MASK;
+
+ bb = m.constrainedSnapStretch(Snapper::SNAPPOINT_BBOX, _bbox_points, it, default_scale[axis], _origin_for_bboxpoints, axis, symmetrical);
+ sn = m.constrainedSnapStretch(Snapper::SNAPPOINT_NODE, _snap_points, it, geom_scale[axis], _origin_for_specpoints, axis, symmetrical);
+
+ if (bb.second) {
+ // We snapped the bbox (which is either visual or geometric)
+ bd = fabs(bb.first - default_scale[axis]);
+ default_scale[axis] = bb.first;
+ }
+
+ if (sn.second) {
+ sd = fabs(sn.first - geom_scale[axis]);
+ geom_scale[axis] = sn.first;
+ }
+
+ if (symmetrical) {
+ // on ctrl, apply symmetrical scaling instead of stretching
+ // Preserve aspect ratio, but never flip in the dimension not being edited (by using fabs())
+ default_scale[perp] = fabs(default_scale[axis]);
+ geom_scale[perp] = fabs(geom_scale[axis]);
+ }
+
+ if (!(bb.second || sn.second)) {
+ // We didn't snap at all! Don't update the handle position, just calculate the new transformation
+ _calcAbsAffineDefault(default_scale);
+ } else if (bd < sd) {
+ // Calculate the new transformation and update the handle position
+ pt = _calcAbsAffineDefault(default_scale);
+ } else {
+ // We snapped the special points (e.g. nodes), which are not at the visual bbox
+ // The handle location however (pt) might however be at the visual bbox, so we
+ // will have to calculate pt taking the stroke width into account
+ pt = _calcAbsAffineGeom(geom_scale);
}
}
-
- pt = ( _point - _origin ) * NR::scale(s) + _origin;
- if (isNaN(pt[X] + pt[Y])) {
- g_warning("point=(%g, %g), norm=(%g, %g), s=(%g, %g)\n",
- _point[X], _point[Y], _origin[X], _origin[Y], s[X], s[Y]);
- }
-
+
// status text
_message_context.setF(Inkscape::NORMAL_MESSAGE,
_("<b>Scale</b>: %0.2f%% x %0.2f%%; with <b>Ctrl</b> to lock ratio"),
- 100 * s[NR::X], 100 * s[NR::Y]);
+ 100 * _absolute_affine[0], 100 * _absolute_affine[3]);
return TRUE;
}
gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, NR::Point &pt, guint state)
{
- using NR::X;
- using NR::Y;
-
- if (handle.cursor != GDK_SB_V_DOUBLE_ARROW && handle.cursor != GDK_SB_H_DOUBLE_ARROW) {
- return FALSE;
- }
-
+ /* When skewing (or rotating):
+ * 1) the stroke width will not change. This makes life much easier because we don't have to
+ * account for that (like for scaling or stretching). As a consequence, all points will
+ * have the same origin for the transformation and for the snapping.
+ * 2) When holding shift, the transformation will be relative to the point opposite of
+ * the handle; otherwise it will be relative to the center as set for the selection
+ */
+
NR::Dim2 dim_a;
NR::Dim2 dim_b;
- if (handle.cursor == GDK_SB_V_DOUBLE_ARROW) {
- dim_a = X;
- dim_b = Y;
- } else {
- dim_a = Y;
- dim_b = X;
+
+ switch (handle.cursor) {
+ case GDK_SB_H_DOUBLE_ARROW:
+ dim_a = NR::Y;
+ dim_b = NR::X;
+ break;
+ case GDK_SB_V_DOUBLE_ARROW:
+ dim_a = NR::X;
+ dim_b = NR::Y;
+ break;
+ default:
+ g_assert_not_reached();
+ abort();
+ break;
}
-
- double skew[2];
- double s[2] = { 1.0, 1.0 };
-
- if (fabs(_point[dim_a] - _origin[dim_a]) < NR_EPSILON) {
- return FALSE;
+
+ NR::Point const initial_delta = _point - _origin;
+
+ if (fabs(initial_delta[dim_a]) < 1e-15) {
+ return false;
}
- skew[dim_a] = ( pt[dim_b] - _point[dim_b] ) / ( _point[dim_a] - _origin[dim_a] );
+ // Calculate the scale factors, which can be either visual or geometric
+ // depending on which type of bbox is currently being used (see preferences -> selector tool)
+ NR::scale scale = calcScaleFactors(_point, pt, _origin, false);
+ NR::scale skew = calcScaleFactors(_point, pt, _origin, true);
+ scale[dim_b] = 1;
+ skew[dim_b] = 1;
- s[dim_a] = ( pt[dim_a] - _origin[dim_a] ) / ( _point[dim_a] - _origin[dim_a] );
-
- if ( fabs(s[dim_a]) < 1 ) {
- s[dim_a] = sign(s[dim_a]);
+ if (fabs(scale[dim_a]) < 1) {
+ // Prevent shrinking of the selected object, while allowing mirroring
+ scale[dim_a] = sign(scale[dim_a]);
} else {
- s[dim_a] = floor( s[dim_a] + 0.5 );
+ // Allow expanding of the selected object by integer multiples
+ scale[dim_a] = floor(scale[dim_a] + 0.5);
}
- double radians = atan(skew[dim_a] / s[dim_a]);
-
+ double radians = atan(skew[dim_a] / scale[dim_a]);
+
if (state & GDK_CONTROL_MASK) {
-
+ // Snap to defined angle increments
int snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
-
if (snaps) {
- double sections = floor( radians * snaps / M_PI + .5 );
- if (fabs(sections) >= snaps / 2) sections = sign(sections) * (snaps / 2 - 1);
- radians = ( M_PI / snaps ) * sections;
+ double sections = floor(radians * snaps / M_PI + .5);
+ if (fabs(sections) >= snaps / 2) {
+ sections = sign(sections) * (snaps / 2 - 1);
+ }
+ radians = (M_PI / snaps) * sections;
}
- skew[dim_a] = tan(radians) * s[dim_a];
+ skew[dim_a] = tan(radians) * scale[dim_a];
} else {
+ // Snap to objects, grids, guides
+
/* Get a STL list of the selected items.
** FIXME: this should probably be done by Inkscape::Selection.
*/
@@ -1158,36 +1151,36 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, NR::Poi
SnapManager const &m = _desktop->namedview->snap_manager;
- std::pair<NR::Coord, bool> bb = m.freeSnapSkew(Inkscape::Snapper::SNAPPOINT_BBOX,
- _bbox_points,
- it,
- skew[dim_a],
- _origin_for_bboxpoints,
- dim_b);
-
- std::pair<NR::Coord, bool> sn = m.freeSnapSkew(Inkscape::Snapper::SNAPPOINT_NODE,
- _snap_points,
- it,
- skew[dim_a],
- _origin_for_specpoints,
- dim_b);
+ std::pair<NR::Coord, bool> bb = m.freeSnapSkew(Inkscape::Snapper::SNAPPOINT_BBOX, _bbox_points, it, skew[dim_a], _origin, dim_b);
+ std::pair<NR::Coord, bool> sn = m.freeSnapSkew(Inkscape::Snapper::SNAPPOINT_NODE, _snap_points, it, skew[dim_a], _origin, dim_b);
if (bb.second || sn.second) {
- /* We snapped something, so change the skew to reflect it */
+ // 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];
- pt[dim_a] = ( _point[dim_a] - _origin[dim_a] ) * s[dim_a] + _origin[dim_a];
-
- /* status text */
- double degrees = 180 / M_PI * radians;
- if (degrees > 180) degrees -= 360;
- if (degrees < -180) degrees += 360;
+ // Update the handle position
+ pt[dim_b] = initial_delta[dim_a] * skew[dim_a] + _point[dim_b];
+ pt[dim_a] = initial_delta[dim_a] * scale[dim_a] + _origin[dim_a];
+
+ // Calculate the relative affine
+ _relative_affine = NR::identity();
+ _relative_affine[2*dim_a + dim_a] = (pt[dim_a] - _origin[dim_a]) / initial_delta[dim_a];
+ _relative_affine[2*dim_a + (dim_b)] = (pt[dim_b] - _point[dim_b]) / initial_delta[dim_a];
+ _relative_affine[2*(dim_b) + (dim_a)] = 0;
+ _relative_affine[2*(dim_b) + (dim_b)] = 1;
+ for (int i = 0; i < 2; i++) {
+ if (fabs(_relative_affine[3*i]) < 1e-15) {
+ _relative_affine[3*i] = 1e-15;
+ }
+ }
+
+ // Update the status text
+ double degrees = mod360symm(Geom::rad_to_deg(radians));
_message_context.setF(Inkscape::NORMAL_MESSAGE,
// TRANSLATORS: don't modify the first ";"
// (it will NOT be displayed as ";" - only the second one will be)
@@ -1199,22 +1192,30 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, NR::Poi
gboolean Inkscape::SelTrans::rotateRequest(NR::Point &pt, guint state)
{
+ /* When rotating (or skewing):
+ * 1) the stroke width will not change. This makes life much easier because we don't have to
+ * account for that (like for scaling or stretching). As a consequence, all points will
+ * have the same origin for the transformation and for the snapping.
+ * 2) When holding shift, the transformation will be relative to the point opposite of
+ * the handle; otherwise it will be relative to the center as set for the selection
+ */
+
int snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
// rotate affine in rotate
NR::Point const d1 = _point - _origin;
NR::Point const d2 = pt - _origin;
- NR::Coord const h1 = NR::L2(d1);
+ NR::Coord const h1 = NR::L2(d1); // initial radius
if (h1 < 1e-15) return FALSE;
- NR::Point q1 = d1 / h1;
- NR::Coord const h2 = NR::L2(d2);
+ NR::Point q1 = d1 / h1; // normalized initial vector to handle
+ NR::Coord const h2 = NR::L2(d2); // new radius
if (fabs(h2) < 1e-15) return FALSE;
- NR::Point q2 = d2 / h2;
+ NR::Point q2 = d2 / h2; // normalized new vector to handle
double radians;
if (state & GDK_CONTROL_MASK) {
- /* Have to restrict movement. */
+ // Snap to defined angle increments
double cos_t = NR::dot(q1, q2);
double sin_t = NR::dot(NR::rot90(q1), q2);
radians = atan2(sin_t, cos_t);
NR::rotate const r1(q1);
NR::rotate const r2(q2);
- pt = _point * NR::translate(-_origin) * ( r2 / r1 ) * NR::translate(_origin);
-
- /* status text */
- double degrees = 180 / M_PI * radians;
- if (degrees > 180) degrees -= 360;
- if (degrees < -180) degrees += 360;
-
+
+ // Calculate the relative affine
+ _relative_affine = NR::Matrix(r2/r1);
+
+ // Update the handle position
+ pt = _point * NR::translate(-_origin) * _relative_affine * NR::translate(_origin);
+
+ // Update the status text
+ double degrees = mod360symm(Geom::rad_to_deg(radians));
_message_context.setF(Inkscape::NORMAL_MESSAGE,
// TRANSLATORS: don't modify the first ";"
// (it will NOT be displayed as ";" - only the second one will be)
gboolean Inkscape::SelTrans::centerRequest(NR::Point &pt, guint state)
{
- using NR::X;
- using NR::Y;
-
SnapManager const &m = _desktop->namedview->snap_manager;
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]) ) {
- pt[Y] = _point[Y];
+ if ( fabs(_point[NR::X] - pt[NR::X]) > fabs(_point[NR::Y] - pt[NR::Y]) ) {
+ pt[NR::Y] = _point[NR::Y];
} else {
- pt[X] = _point[X];
+ pt[NR::X] = _point[NR::X];
}
}
}
// status text
- GString *xs = SP_PX_TO_METRIC_STRING(pt[X], _desktop->namedview->getDefaultMetric());
- GString *ys = SP_PX_TO_METRIC_STRING(pt[Y], _desktop->namedview->getDefaultMetric());
+ GString *xs = SP_PX_TO_METRIC_STRING(pt[NR::X], _desktop->namedview->getDefaultMetric());
+ GString *ys = SP_PX_TO_METRIC_STRING(pt[NR::Y], _desktop->namedview->getDefaultMetric());
_message_context.setF(Inkscape::NORMAL_MESSAGE, _("Move <b>center</b> to %s, %s"), xs->str, ys->str);
g_string_free(xs, FALSE);
g_string_free(ys, FALSE);
@@ -1317,151 +1317,22 @@ void sp_sel_trans_rotate(Inkscape::SelTrans *seltrans, SPSelTransHandle const &,
void Inkscape::SelTrans::stretch(SPSelTransHandle const &handle, NR::Point &pt, guint state)
{
- using NR::X;
- using NR::Y;
-
- NR::Dim2 dim;
- switch (handle.cursor) {
- case GDK_LEFT_SIDE:
- case GDK_RIGHT_SIDE:
- dim = X;
- break;
- case GDK_TOP_SIDE:
- case GDK_BOTTOM_SIDE:
- dim = Y;
- break;
- default:
- g_assert_not_reached();
- abort();
- break;
- }
-
- NR::Point const scale_origin(_origin);
- double const offset = _point[dim] - scale_origin[dim];
- if (!( fabs(offset) >= 1e-15 )) {
- return;
- }
- NR::scale s(1, 1);
- s[dim] = ( pt[dim] - scale_origin[dim] ) / offset;
- if (isNaN(s[dim])) {
- g_warning("s[dim]=%g, pt[dim]=%g, scale_origin[dim]=%g, point[dim]=%g\n",
- s[dim], pt[dim], scale_origin[dim], _point[dim]);
- }
- if (!( fabs(s[dim]) >= 1e-15 )) {
- s[dim] = 1e-15;
- }
- if (state & GDK_CONTROL_MASK) {
- /* Preserve aspect ratio, but never flip in the dimension not being edited. */
- s[!dim] = fabs(s[dim]);
- }
-
- 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;
- }
-
- 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
+ transform(_absolute_affine, 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);
- for (int i = NR::X; i <= NR::Y; i++) {
- if (fabs(offset[i]) > 1e-9)
- s[i] = (pt[i] - _origin[i]) / offset[i];
- if (fabs(s[i]) < 1e-9)
- s[i] = 1e-9;
- }
-
- 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
+ transform(_absolute_affine, 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*/)
{
- NR::Point const offset = _point - _origin;
-
- unsigned dim;
- switch (handle.cursor) {
- case GDK_SB_H_DOUBLE_ARROW:
- dim = NR::Y;
- break;
- case GDK_SB_V_DOUBLE_ARROW:
- dim = NR::X;
- break;
- default:
- g_assert_not_reached();
- abort();
- break;
- }
- if (fabs(offset[dim]) < 1e-15) {
- return;
- }
- NR::Matrix skew = NR::identity();
- skew[2*dim + dim] = (pt[dim] - _origin[dim]) / offset[dim];
- skew[2*dim + (1-dim)] = (pt[1-dim] - _point[1-dim]) / offset[dim];
- skew[2*(1-dim) + (dim)] = 0;
- skew[2*(1-dim) + (1-dim)] = 1;
-
- for (int i = 0; i < 2; i++) {
- if (fabs(skew[3*i]) < 1e-15) {
- skew[3*i] = 1e-15;
- }
- }
- transform(skew, _origin);
+ transform(_relative_affine, _origin);
}
void Inkscape::SelTrans::rotate(NR::Point &pt, guint /*state*/)
{
- NR::Point const offset = _point - _origin;
-
- NR::Coord const h1 = NR::L2(offset);
- if (h1 < 1e-15) {
- return;
- }
- NR::Point const q1 = offset / h1;
- NR::Coord const h2 = NR::L2( pt - _origin );
- if (h2 < 1e-15) {
- return;
- }
- NR::Point const q2 = (pt - _origin) / h2;
- NR::rotate const r1(q1);
- NR::rotate const r2(q2);
-
- NR::Matrix rotate( r2 / r1 );
- transform(rotate, _origin);
+ transform(_relative_affine, _origin);
}
void sp_sel_trans_center(Inkscape::SelTrans *seltrans, SPSelTransHandle const &, NR::Point &pt, guint /*state*/)
g_string_free(ys, TRUE);
}
+// Given a location of a handle at the visual bounding box, find the corresponding location at the
+// geometrical bounding box
+NR::Point Inkscape::SelTrans::_getGeomHandlePos(NR::Point const &visual_handle_pos)
+{
+ if ( _snap_bbox_type == SPItem::GEOMETRIC_BBOX) {
+ // When the selector tool is using geometric bboxes, then the handle is already
+ // located at one of the geometric bbox corners
+ return visual_handle_pos;
+ }
+
+ if (!_geometric_bbox) {
+ //_getGeomHandlePos() can only be used after _geometric_bbox has been defined!
+ return visual_handle_pos;
+ }
+
+ // Using the NR::Rect constructor below ensures that "min() < max()", which is important
+ // because this will also hold for _bbox, and which is required for get_scale_transform_with_stroke()
+ NR::Rect new_bbox = NR::Rect(_origin_for_bboxpoints, visual_handle_pos); // new visual bounding box
+ // Please note that the new_bbox might in fact be just a single line, for example when stretching (in
+ // which case the handle and origin will be aligned vertically or horizontally)
+ NR::Point normalized_handle_pos = (visual_handle_pos - new_bbox.min()) * NR::scale(new_bbox.dimensions()).inverse();
+
+ // Calculate the absolute affine while taking into account the scaling of the stroke width
+ int transform_stroke = prefs_get_int_attribute ("options.transform", "stroke", 1);
+ NR::Matrix abs_affine = get_scale_transform_with_stroke (*_bbox, _strokewidth, transform_stroke,
+ new_bbox.min()[NR::X], new_bbox.min()[NR::Y], new_bbox.max()[NR::X], new_bbox.max()[NR::Y]);
+
+ // Calculate the scaled geometrical bbox
+ NR::Rect new_geom_bbox = NR::Rect(_geometric_bbox->min() * abs_affine, _geometric_bbox->max() * abs_affine);
+ // Find the location of the handle on this new geometrical bbox
+ return normalized_handle_pos * NR::scale(new_geom_bbox.dimensions()) + new_geom_bbox.min(); //new position of the geometric handle
+}
+
+NR::scale Inkscape::calcScaleFactors(NR::Point const &initial_point, NR::Point const &new_point, NR::Point const &origin, bool const skew)
+{
+ // Work out the new scale factors for the bbox
+
+ NR::Point const initial_delta = initial_point - origin;
+ NR::Point const new_delta = new_point - origin;
+ NR::Point const offset = new_point - initial_point;
+ NR::scale scale(1, 1);
+
+ for ( unsigned int i = 0 ; i < 2 ; i++ ) {
+ if ( fabs(initial_delta[i]) > 1e-6 ) {
+ if (skew) {
+ scale[i] = offset[1-i] / initial_delta[i];
+ } else {
+ scale[i] = new_delta[i] / initial_delta[i];
+ }
+ }
+ }
+
+ return scale;
+}
+
+// Only for scaling/stretching
+NR::Point Inkscape::SelTrans::_calcAbsAffineDefault(NR::scale const default_scale)
+{
+ NR::Matrix abs_affine = NR::translate(-_origin) * NR::Matrix(default_scale) * NR::translate(_origin);
+ NR::Point new_bbox_min = _approximate_bbox->min() * abs_affine;
+ NR::Point new_bbox_max = _approximate_bbox->max() * abs_affine;
+
+ 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;
+ }
+
+ _absolute_affine = 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]);
+
+ // return the new handle position
+ return ( _point - _origin ) * default_scale + _origin;
+}
+
+// Only for scaling/stretching
+NR::Point Inkscape::SelTrans::_calcAbsAffineGeom(NR::scale const geom_scale)
+{
+ _relative_affine = NR::Matrix(geom_scale);
+ _absolute_affine = NR::translate(-_origin_for_specpoints) * _relative_affine * NR::translate(_origin_for_specpoints);
+
+ bool const transform_stroke = prefs_get_int_attribute ("options.transform", "stroke", 1);
+ NR::Rect visual_bbox = get_visual_bbox(_geometric_bbox, _absolute_affine, _strokewidth, transform_stroke);
+
+ // return the new handle position
+ return visual_bbox.min() + visual_bbox.dimensions() * NR::scale(_handle_x, _handle_y);
+}
+
/*
Local Variables:
diff --git a/src/seltrans.h b/src/seltrans.h
index fec8ab017e415faf463caa569075a03c21cc869d..00d7fb1c494dcd865184cb580385cff42ead8348 100644 (file)
--- a/src/seltrans.h
+++ b/src/seltrans.h
/*
* Helper object for transforming selected items
*
- * Author:
+ * Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* Carl Hetherington <inkscape@carlh.net>
+ * Diederik van Lierop <mail@diedenrezi.nl>
*
* Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
* Copyright (C) 1999-2002 Lauris Kaplinski
namespace Inkscape
{
+NR::scale calcScaleFactors(NR::Point const &initial_point, NR::Point const &new_point, NR::Point const &origin, bool const skew = false);
+
namespace XML
{
class Node;
void _selModified(Inkscape::Selection *selection, guint flags);
void _showHandles(SPKnot *knot[], SPSelTransHandle const handle[], gint num,
gchar const *even_tip, gchar const *odd_tip);
+ NR::Point _getGeomHandlePos(NR::Point const &visual_handle_pos);
+ NR::Point _calcAbsAffineDefault(NR::scale const default_scale);
+ NR::Point _calcAbsAffineGeom(NR::scale const geom_scale);
enum State {
- STATE_SCALE,
- STATE_ROTATE
+ STATE_SCALE, //scale or stretch
+ STATE_ROTATE //rotate or skew
};
SPDesktop *_desktop;
NR::Maybe<NR::Rect> _bbox;
NR::Maybe<NR::Rect> _approximate_bbox;
+ NR::Maybe<NR::Rect> _geometric_bbox;
gdouble _strokewidth;
- NR::Matrix _current;
- NR::Point _opposite; ///< opposite point to where a scale is taking place
-
+
+ NR::Matrix _current_relative_affine;
+ NR::Matrix _absolute_affine;
+ NR::Matrix _relative_affine;
+ /* According to Merriam - Webster's online dictionary
+ * Affine: a transformation (as a translation, a rotation, or a uniform stretching) that carries straight
+ * lines into straight lines and parallel lines into parallel lines but may alter distance between points
+ * and angles between lines <affine geometry>
+ */
+
+ NR::Point _opposite; ///< opposite point to where a scale is taking place
NR::Point _opposite_for_specpoints;
NR::Point _opposite_for_bboxpoints;
NR::Point _origin_for_specpoints;
NR::Point _origin_for_bboxpoints;
+ gdouble _handle_x;
+ gdouble _handle_y;
+
NR::Maybe<NR::Point> _center;
bool _center_is_set; ///< we've already set _center, no need to reread it from items
NR::Point _origin; ///< position of origin for transforms
NR::Point _point; ///< original position of the knot being used for the current transform
+ NR::Point _point_geom; ///< original position of the knot being used for the current transform
Inkscape::MessageContext _message_context;
sigc::connection _sel_changed_connection;
sigc::connection _sel_modified_connection;
diff --git a/src/snap.cpp b/src/snap.cpp
index 093ae10e3d497e00272bfd4bfb818b42d3565f5b..639e704b0a57b10d56c667163058ea6961751cc0 100644 (file)
--- a/src/snap.cpp
+++ b/src/snap.cpp
*
* Copyright (C) 2006-2007 Johan Engelen <johan@shouraizou.nl>
* Copyrigth (C) 2004 Nathan Hurst
- * Copyright (C) 1999-2002 Authors
+ * Copyright (C) 1999-2008 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
NR::Point const &p,
- SPItem const *it) const
+ SPItem const *it,
+ NR::Maybe<NR::Point> point_not_to_snap_to) const
{
std::list<SPItem const *> lit;
diff --git a/src/snap.h b/src/snap.h
index 156703264a413fb2b535e2dad6987673d6ef3ce7..bdaa27ea8be380ad3d67b62b447ed576d4e7f6cf 100644 (file)
--- a/src/snap.h
+++ b/src/snap.h
Inkscape::SnappedPoint freeSnap(Inkscape::Snapper::PointType t,
NR::Point const &p,
- SPItem const *it) const;
+ SPItem const *it,
+ NR::Maybe<NR::Point> point_not_to_snap_to = NR::Nothing()) const;
Inkscape::SnappedPoint freeSnap(Inkscape::Snapper::PointType t,
NR::Point const &p,
diff --git a/src/snapper.cpp b/src/snapper.cpp
index f8f7705f23d5e3963e72f6a2e51426d0255ad4a9..31a96a8716868c21fe052b0718157833b41d942a 100644 (file)
--- a/src/snapper.cpp
+++ b/src/snapper.cpp
* \return Snapped point.
*/
-void Inkscape::Snapper::freeSnap(SnappedConstraints &sc,
-
+void Inkscape::Snapper::freeSnap(SnappedConstraints &sc,
PointType const &t,
NR::Point const &p,
bool const &first_point,
* \return Snapped point.
*/
-void Inkscape::Snapper::constrainedSnap(SnappedConstraints &sc,
-
+void Inkscape::Snapper::constrainedSnap(SnappedConstraints &sc,
PointType const &t,
NR::Point const &p,
bool const &first_point,
- std::vector<NR::Point> &points_to_snap,
+ std::vector<NR::Point> &points_to_snap,
ConstraintLine const &c,
SPItem const *it) const
{
diff --git a/src/snapper.h b/src/snapper.h
index 63f68173a14c73e170c67fc360b00bd1d793fcd3..508ba7b9ad91dee834c396c82465c34bed2f16c6 100644 (file)
--- a/src/snapper.h
+++ b/src/snapper.h
#include <list>
#include "libnr/nr-coord.h"
#include "libnr/nr-point.h"
+#include "libnr/nr-maybe.h"
#include "snapped-point.h"
#include "snapped-line.h"
std::vector<NR::Point> &points_to_snap,
std::list<SPItem const *> const &it,
std::vector<NR::Point> *unselected_nodes) const;
-
+
class ConstraintLine
{
public:
index 93c3f5b7b410a15c83204283d93b9907cb34f57f..08c4d167b2c1672be331110a0df3e0a736618ace 100644 (file)
}
/*
-** Returns the matrix you need to apply to an object with given bbox and strokewidth to
-scale/move it to the new box x0/y0/x1/y1. Takes into account the "scale stroke"
+** Returns the matrix you need to apply to an object with given visual bbox and strokewidth to
+scale/move it to the new visual bbox x0/y0/x1/y1. Takes into account the "scale stroke"
preference value passed to it. Has to solve a quadratic equation to make sure
the goal is met exactly and the stroke scaling is obeyed.
*/
@@ -133,7 +133,7 @@ get_scale_transform_with_stroke (NR::Rect &bbox_param, gdouble strokewidth, bool
// These coefficients are obtained from the assumption that scaling applies to the
// non-stroked "shape proper" and that stroke scale is scaled by the expansion of that
// matrix. We're trying to solve this equation:
- // r1 = r0 * sqrt (((w1-r0)/(w0-r0))*((h1-r1)/(h0-r0)))
+ // r1 = r0 * sqrt (((w1-r0)/(w0-r0))*((h1-r0)/(h0-r0)))
// The operant of the sqrt() must be positive, which is ensured by the fabs() a few lines above
gdouble A = -w0*h0 + r0*(w0 + h0);
gdouble B = -(w1 + h1) * r0*r0;
@@ -165,6 +165,38 @@ get_scale_transform_with_stroke (NR::Rect &bbox_param, gdouble strokewidth, bool
return (p2o * scale * unbudge * o2n);
}
+NR::Rect
+get_visual_bbox (NR::Maybe<NR::Rect> const &initial_geom_bbox, NR::Matrix const &abs_affine, gdouble const initial_strokewidth, bool const transform_stroke)
+{
+
+ g_assert(initial_geom_bbox);
+
+ // Find the new geometric bounding box; Do this by transforming each corner of
+ // the initial geometric bounding box individually and fitting a new boundingbox
+ // around the transformerd corners
+ NR::Point const p0 = initial_geom_bbox->corner(0) * abs_affine;
+ NR::Rect new_geom_bbox = NR::Rect(p0, p0);
+ for (unsigned i = 1 ; i < 4 ; i++) {
+ new_geom_bbox.expandTo(initial_geom_bbox->corner(i) * abs_affine);
+ }
+
+ NR::Rect new_visual_bbox = new_geom_bbox;
+ if (initial_strokewidth > 0 && initial_strokewidth < NR_HUGE) {
+ if (transform_stroke) {
+ // scale stroke by: sqrt (((w1-r0)/(w0-r0))*((h1-r0)/(h0-r0))) (for visual bboxes, see get_scale_transform_with_stroke)
+ // equals scaling by: sqrt ((w1/w0)*(h1/h0)) for geometrical bboxes
+ // equals scaling by: sqrt (area1/area0) for geometrical bboxes
+ gdouble const new_strokewidth = initial_strokewidth * sqrt (new_geom_bbox.area() / initial_geom_bbox->area());
+ new_visual_bbox.growBy(0.5 * new_strokewidth);
+ } else {
+ // Do not transform the stroke
+ new_visual_bbox.growBy(0.5 * initial_strokewidth);
+ }
+ }
+
+ return new_visual_bbox;
+}
+
/*
Local Variables:
mode:c++
index 23d96987f51ec4889b28893a2be27e83373ba38a..df87da84c000f0ddd36d4d6fcf2ec8f3dfab365d 100644 (file)
--- a/src/sp-item-transform.h
+++ b/src/sp-item-transform.h
void sp_item_move_rel(SPItem *item, NR::translate const &tr);
NR::Matrix get_scale_transform_with_stroke (NR::Rect &bbox, gdouble strokewidth, bool transform_stroke, gdouble x0, gdouble y0, gdouble x1, gdouble y1);
+NR::Rect get_visual_bbox (NR::Maybe<NR::Rect> const &initial_geom_bbox, NR::Matrix const &abs_affine, gdouble const initial_strokewidth, bool const transform_stroke);
+
#endif /* !SP_ITEM_TRANSFORM_H */
index acf76f034bb288005fddf96d5c8708502d53c937..e7aca0edc7559a8a4dd57b686dc55235a0f9d8ec 100644 (file)
Gtk::Label *label_i= manage (new Gtk::Label);
label_i->set_markup (_("<b>Snapping to intersections of</b>"));
Gtk::Label *label_m = manage (new Gtk::Label);
- label_m->set_markup (_("<b>Snapping to special nodes</b>"));
+ label_m->set_markup (_("<b>Special points to consider</b>"));
Gtk::Widget *const array[] =
{
diff --git a/src/widgets/ruler.cpp b/src/widgets/ruler.cpp
index 255bdd853ac7e62703c0287eed69253f3f8a7852..6ab7c8394024499582d97732a323df1954f9a06b 100644 (file)
--- a/src/widgets/ruler.cpp
+++ b/src/widgets/ruler.cpp
* Lauris Kaplinski <lauris@kaplinski.com>
* Frank Felfe <innerspace@iname.com>
* bulia byak <buliabyak@users.sf.net>
+ * Diederik van Lierop <mail@diedenrezi.nl>
*
- * Copyright (C) 1999-2005 authors
+ * Copyright (C) 1999-2008 authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/