summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDiederik van Lierop <mail@diedenrezi.nl>2011-07-10 19:40:56 +0000
committerDiederik van Lierop <mail@diedenrezi.nl>2011-07-10 19:40:56 +0000
commitdd5da66df059871c546f4d09b9d2eb92e71b74b7 (patch)
tree28c7ece771468b0703b7dcf7f3c0f3db1db053c7 /src
parentTutorials. New Greek translation of Basic by dimitris Spingos. (diff)
downloadinkscape-dd5da66df059871c546f4d09b9d2eb92e71b74b7.tar.gz
inkscape-dd5da66df059871c546f4d09b9d2eb92e71b74b7.zip
Selector's toolbar: changing the dimensions of the visual bounding box of selection of multiple objects having different stroke widths has been fixed (bug #212768, #190557, ...)
Fixed bugs: - https://launchpad.net/bugs/212768 - https://launchpad.net/bugs/190557 (bzr r10437.1.5)
Diffstat (limited to 'src')
-rw-r--r--src/seltrans.cpp4
-rw-r--r--src/snap-preferences.cpp2
-rw-r--r--src/snap-preferences.h2
-rw-r--r--src/sp-item-transform.cpp273
-rw-r--r--src/sp-item-transform.h3
-rw-r--r--src/widgets/select-toolbar.cpp42
6 files changed, 260 insertions, 66 deletions
diff --git a/src/seltrans.cpp b/src/seltrans.cpp
index f95a204a9..f6a702ed9 100644
--- a/src/seltrans.cpp
+++ b/src/seltrans.cpp
@@ -1566,7 +1566,7 @@ Geom::Point Inkscape::SelTrans::_getGeomHandlePos(Geom::Point const &visual_hand
// Calculate the absolute affine while taking into account the scaling of the stroke width
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
bool transform_stroke = prefs->getBool("/options/transform/stroke", true);
- Geom::Affine abs_affine = get_scale_transform_with_stroke (*_bbox, _strokewidth, transform_stroke,
+ Geom::Affine abs_affine = get_scale_transform_with_uniform_stroke (*_bbox, _strokewidth, transform_stroke,
new_bbox.min()[Geom::X], new_bbox.min()[Geom::Y], new_bbox.max()[Geom::X], new_bbox.max()[Geom::Y]);
// Calculate the scaled geometrical bbox
@@ -1613,7 +1613,7 @@ Geom::Point Inkscape::SelTrans::_calcAbsAffineDefault(Geom::Scale const default_
strokewidth = _strokewidth;
}
- _absolute_affine = get_scale_transform_with_stroke (*_approximate_bbox, strokewidth, transform_stroke,
+ _absolute_affine = get_scale_transform_with_uniform_stroke (*_approximate_bbox, strokewidth, transform_stroke,
new_bbox_min[Geom::X], new_bbox_min[Geom::Y], new_bbox_max[Geom::X], new_bbox_max[Geom::Y]);
// return the new handle position
diff --git a/src/snap-preferences.cpp b/src/snap-preferences.cpp
index 4859b111e..b98726a86 100644
--- a/src/snap-preferences.cpp
+++ b/src/snap-preferences.cpp
@@ -5,7 +5,7 @@
* Authors:
* Diederik van Lierop <mail@diedenrezi.nl>
*
- * Copyright (C) 2008 Authors
+ * Copyright (C) 2008 - 2011 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
diff --git a/src/snap-preferences.h b/src/snap-preferences.h
index 8e8ebc9cf..35d05c40e 100644
--- a/src/snap-preferences.h
+++ b/src/snap-preferences.h
@@ -8,7 +8,7 @@
* Authors:
* Diederik van Lierop <mail@diedenrezi.nl>
*
- * Copyright (C) 2008 - 2010 Authors
+ * Copyright (C) 2008 - 2011 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
diff --git a/src/sp-item-transform.cpp b/src/sp-item-transform.cpp
index ae55a5c50..45d965e44 100644
--- a/src/sp-item-transform.cpp
+++ b/src/sp-item-transform.cpp
@@ -7,8 +7,9 @@
* bulia byak <buliabyak@gmail.com>
* Johan Engelen <goejendaagh@zonnet.nl>
* Abhishek Sharma
+ * Diederik van Lierop <mail@diedenrezi.nl>
*
- * Copyright (C) 1999-2008 authors
+ * Copyright (C) 1999-2011 authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -67,46 +68,76 @@ sp_item_skew_rel (SPItem *item, double skewX, double skewY)
void sp_item_move_rel(SPItem *item, Geom::Translate const &tr)
{
- item->set_i2d_affine(item->i2d_affine() * tr);
+ item->set_i2d_affine(item->i2d_affine() * tr);
- item->doWriteTransform(item->getRepr(), item->transform);
+ item->doWriteTransform(item->getRepr(), item->transform);
}
-/*
-** 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.
+/**
+ * \brief Calculate the affine transformation required to transform one visual bounding box into another, accounting for a uniform strokewidth
+ *
+ * PS: This function will only return accurate results for the visual bounding box of a selection of one of more objects, all having
+ * the same strokewidth. If the stroke width varies from object to object in this selection, then the function
+ * get_scale_transform_with_unequal_stroke() should be called instead
+ *
+ * When scaling or stretching an object using the selector, e.g. by dragging the handles or by entering a value, we will
+ * need to calculate the affine transformation for the old dimensions to the new dimensions. When using a geometric bounding
+ * box this is very straightforward, but when using a visual bounding box this become more tricky as we need to account for
+ * the strokewidth, which is either constant or scales width the area of the object. This function takes care of the calculation
+ * of the affine transformation:
+ * \param bbox_visual Current visual bounding box
+ * \param strokewidth Strokewidth
+ * \param transform_stroke If true then the stroke will be scaled proportional to the square root of the area of the geometric bounding box
+ * \param x0 Coordinate of the target visual bounding box
+ * \param y0 Coordinate of the target visual bounding box
+ * \param x1 Coordinate of the target visual bounding box
+ * \param y1 Coordinate of the target visual bounding box
+ * PS: we have to pass each coordinate individually, to find out if we are mirroring the object; Using a Geom::Rect() instead is
+ not possible here because it will only allow for a positive width and height, and therefore cannot mirror
+ * \return
*/
Geom::Affine
-get_scale_transform_with_stroke (Geom::Rect const &bbox_param, gdouble strokewidth, bool transform_stroke, gdouble x0, gdouble y0, gdouble x1, gdouble y1)
+get_scale_transform_with_uniform_stroke (Geom::Rect const &bbox_visual, gdouble strokewidth, bool transform_stroke, gdouble x0, gdouble y0, gdouble x1, gdouble y1)
{
- Geom::Rect bbox (bbox_param);
-
- Geom::Affine p2o = Geom::Translate (-bbox.min());
+ Geom::Affine p2o = Geom::Translate (-bbox_visual.min());
Geom::Affine o2n = Geom::Translate (x0, y0);
- Geom::Affine scale = Geom::Scale (1, 1); // scale component
- Geom::Affine unbudge = Geom::Translate (0, 0); // move component to compensate for the drift caused by stroke width change
+ Geom::Affine scale = Geom::Scale (1, 1);
+ Geom::Affine unbudge = Geom::Translate (0, 0); // moves the object(s) to compensate for the drift caused by stroke width change
+
+ // 1) We start with a visual bounding box (w0, h0) which we want to transfer into another visual bounding box (w1, h1)
+ // 2) The stroke is r0, equal for all edges
+ // 3) Given this visual bounding box we can calculate the geometric bounding box by subtracting half the stroke from each side;
+ // -> The width and height of the geometric bounding box will therefore be (w0 - 2*0.5*r0) and (h0 - 2*0.5*r0)
- gdouble w0 = bbox[Geom::X].extent(); // will return a value >= 0, as required further down the road
- gdouble h0 = bbox[Geom::Y].extent();
+ gdouble w0 = bbox_visual.width(); // will return a value >= 0, as required further down the road
+ gdouble h0 = bbox_visual.height();
+ gdouble r0 = fabs(strokewidth);
+
+ // We also know the width and height of the new visual bounding box
gdouble w1 = x1 - x0; // can have any sign
gdouble h1 = y1 - y0;
- gdouble r0 = strokewidth;
+ // The new visual bounding box will have a stroke r1
+
+ // We will now try to calculate the affine transformation required to transform the first visual bounding box into
+ // the second one, while accounting for strokewidth
- if (bbox.hasZeroArea()) {
- Geom::Affine move = Geom::Translate(x0 - bbox.min()[Geom::X], y0 - bbox.min()[Geom::Y]);
- return (move); // cannot scale from empty boxes at all, so only translate
+ if (bbox_visual.hasZeroArea()) { // Obviously we cannot scale from empty visual bounding boxes at all, so we will only translate in such a case
+ Geom::Affine move = Geom::Translate(x0 - bbox_visual.min()[Geom::X], y0 - bbox_visual.min()[Geom::Y]);
+ return (move);
}
- Geom::Affine direct = Geom::Scale(w1 / w0, h1 / h0);
+ Geom::Affine direct = Geom::Scale(w1 / w0, h1 / h0); // Scaling of the visual bounding box
+ // Although the area of the visual bounding box is not zero, we can still have a geometric
+ // bounding box with one or both sides having zero length. We can't handle this and will therefore
+ // simply return the scaling of the visual bounding box, without accounting for any stroke scaling
if (fabs(w0 - r0) < 1e-6 || fabs(h0 - r0) < 1e-6 || (!transform_stroke && (fabs(w1 - r0) < 1e-6 || fabs(h1 - r0) < 1e-6))) {
- return (p2o * direct * o2n); // can't solve the equation: one of the dimensions is equal to stroke width, so return the straightforward scaler
+ return (p2o * direct * o2n);
}
+ // Here starts the calculation you've been waiting for; first do some preparation
int flip_x = (w1 > 0) ? 1 : -1;
int flip_y = (h1 > 0) ? 1 : -1;
@@ -115,43 +146,201 @@ get_scale_transform_with_stroke (Geom::Rect const &bbox_param, gdouble strokewid
w1 = fabs(w1);
h1 = fabs(h1);
r0 = fabs(r0);
- // w0 and h0 will always be positive due to the definition extent()
+ // w0 and h0 will always be positive due to the definition of the width() and height() methods.
- gdouble ratio_x = (w1 - r0) / (w0 - r0);
+ gdouble ratio_x = (w1 - r0) / (w0 - r0); // Only valid when the stroke is kept constant, in which case r1 = r0
gdouble ratio_y = (h1 - r0) / (h0 - r0);
-
+
+ // Calculating the scaling of the geometric bounding box if the stroke is kept constant
Geom::Affine direct_constant_r = Geom::Scale(flip_x * ratio_x, flip_y * ratio_y);
- if (transform_stroke && r0 != 0 && r0 != Geom::infinity()) { // there's stroke, and we need to scale it
- // 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-r0)/(h0-r0)))
- // The operant of the sqrt() must be positive, which is ensured by the fabs() a few lines above
+ // If the stroke is not kept constant however, the scaling of the geometric bbox is more difficult to find
+ if (transform_stroke && r0 != 0 && r0 != Geom::infinity()) { // Check if there's stroke, and we need to scale it
+ /* Initial area of the geometric bounding box: A0 = (w0-r0)*(h0-r0)
+ * Desired area of the geometric bounding box: A1 = (w1-r1)*(h1-r1)
+ * This is how the stroke should scale: r1^2 / A1 = r0^2 / A0
+ * So therefore we will need to solve this equation:
+ *
+ * r1^2 * (w0-r0) * (h1-r1) = r0^2 * (w1-r1) * (h0-r0)
+ *
+ * This is a quadratic equation in r1, of which the roots can be found using the ABC formula
+ * */
gdouble A = -w0*h0 + r0*(w0 + h0);
gdouble B = -(w1 + h1) * r0*r0;
gdouble C = w1 * h1 * r0*r0;
if (B*B - 4*A*C > 0) {
+ // Of the two roots, I verified experimentally that this is the one we need
gdouble r1 = fabs((-B - sqrt(B*B - 4*A*C))/(2*A));
- //gdouble r2 = (-B + sqrt (B*B - 4*A*C))/(2*A);
- //std::cout << "r0" << r0 << " r1" << r1 << " r2" << r2 << "\n";
- //
- // If w1 < 0 then the scale will be wrong if we just do
- // gdouble scale_x = (w1 - r1)/(w0 - r0);
- // Here we also need the absolute values of w0, w1, h0, h1, and r1
+ // If w1 < 0 then the scale will be wrong if we just assume that scale_x = (w1 - r1)/(w0 - r0);
+ // Therefore we here need the absolute values of w0, w1, h0, h1, and r0, as taken care of earlier
gdouble scale_x = (w1 - r1)/(w0 - r0);
gdouble scale_y = (h1 - r1)/(h0 - r0);
+ // Now we account for mirroring by flipping if needed
scale *= Geom::Scale(flip_x * scale_x, flip_y * scale_y);
+ // Make sure that the lower-left corner of the visual bounding box stays where it is, even though the stroke width has changed
unbudge *= Geom::Translate (-flip_x * 0.5 * (r0 * scale_x - r1), -flip_y * 0.5 * (r0 * scale_y - r1));
- } else {
+ } else { // Can't find the roots of the quadratic equation. Likely the input parameters are invalid?
scale *= direct;
}
- } else {
- if (r0 == 0 || r0 == Geom::infinity()) { // no stroke to scale
- scale *= direct;
- } else {// nonscaling strokewidth
+ } else { // The stroke should not be scaled, or is zero
+ if (!transform_stroke) { // Nonscaling strokewidth
scale *= direct_constant_r;
unbudge *= Geom::Translate (flip_x * 0.5 * r0 * (1 - ratio_x), flip_y * 0.5 * r0 * (1 - ratio_y));
+ } else { // Strokewidth is zero or infinite
+ scale *= direct;
+ }
+ }
+
+ return (p2o * scale * unbudge * o2n);
+}
+
+/**
+ * \brief Calculate the affine transformation required to transform one visual bounding box into another, accounting for a VARIABLE strokewidth
+ *
+ * Note: Please try to understand get_scale_transform_with_uniform_stroke() first, and read all it's comments carefully. This function
+ * (get_scale_transform_with_unequal_stroke) is a bit different because it will allow for a strokewidth that's different for each
+ * side of the visual bounding box. Such a situation will arise when transforming the visual bounding box of a selection of objects,
+ * each having a different stroke width. In fact this function is a generalized version of get_scale_transform_with_uniform_stroke(), but
+ * will not (yet) replace it because it has not been tested as carefully, and because the old function is can serve as an introduction to
+ * understand the new one.
+ *
+ * When scaling or stretching an object using the selector, e.g. by dragging the handles or by entering a value, we will
+ * need to calculate the affine transformation for the old dimensions to the new dimensions. When using a geometric bounding
+ * box this is very straightforward, but when using a visual bounding box this become more tricky as we need to account for
+ * the strokewidth, which is either constant or scales width the area of the object. This function takes care of the calculation
+ * of the affine transformation:
+ *
+ * \param bbox_visual Current visual bounding box
+ * \param bbox_geometric Current geometric bounding box (allows for calculating the strokewidth of each edge)
+ * \param transform_stroke If true then the stroke will be scaled proportional to the square root of the area of the geometric bounding box
+ * \param x0 Coordinate of the target visual bounding box
+ * \param y0 Coordinate of the target visual bounding box
+ * \param x1 Coordinate of the target visual bounding box
+ * \param y1 Coordinate of the target visual bounding box
+ PS: we have to pass each coordinate individually, to find out if we are mirroring the object; Using a Geom::Rect() instead is
+ not possible here because it will only allow for a positive width and height, and therefore cannot mirror
+ * \return
+*/
+
+Geom::Affine
+get_scale_transform_with_unequal_stroke (Geom::Rect const &bbox_visual, Geom::Rect const &bbox_geom, bool transform_stroke, gdouble x0, gdouble y0, gdouble x1, gdouble y1)
+{
+ Geom::Affine p2o = Geom::Translate (-bbox_visual.min());
+ Geom::Affine o2n = Geom::Translate (x0, y0);
+
+ Geom::Affine scale = Geom::Scale (1, 1);
+ Geom::Affine unbudge = Geom::Translate (0, 0); // moves the object(s) to compensate for the drift caused by stroke width change
+
+ // 1) We start with a visual bounding box (w0, h0) which we want to transfer into another visual bounding box (w1, h1)
+ // 2) We will also know the geometric bounding box, which can be used to calculate the strokewidth. The strokewidth will however
+ // be different for each of the four sides (left/right/top/bottom: r0l, r0r, r0t, r0b)
+
+ gdouble w0 = bbox_visual.width(); // will return a value >= 0, as required further down the road
+ gdouble h0 = bbox_visual.height();
+
+ // We also know the width and height of the new visual bounding box
+ gdouble w1 = x1 - x0; // can have any sign
+ gdouble h1 = y1 - y0;
+ // The new visual bounding box will have strokes r1l, r1r, r1t, and r1b
+
+ // We will now try to calculate the affine transformation required to transform the first visual bounding box into
+ // the second one, while accounting for strokewidth
+ gdouble r0w = w0 - bbox_geom.width(); // r0w is the average strokewidth of the left and right edges, i.e. 0.5*(r0l + r0r)
+ gdouble r0h = h0 - bbox_geom.height(); // r0h is the average strokewidth of the top and bottom edges, i.e. 0.5*(r0t + r0b)
+
+ // Check whether the stroke is not negative; should not be possible, but just in case:
+ g_assert(r0w >= 0);
+ g_assert(r0h >= 0);
+
+ if (bbox_visual.hasZeroArea()) { // Obviously we cannot scale from empty visual bounding boxes at all, so we will only translate in such a case
+ Geom::Affine move = Geom::Translate(x0 - bbox_visual.min()[Geom::X], y0 - bbox_visual.min()[Geom::Y]);
+ return (move);
+ }
+
+ Geom::Affine direct = Geom::Scale(w1 / w0, h1 / h0);
+
+ // Although the area of the visual bounding box is not zero, we can still have a geometric
+ // bounding box with one or both sides having zero length. We can't handle this and will therefore
+ // simply return the scaling of the visual bounding box, without accounting for any stroke scaling
+ if (fabs(w0 - r0w) < 1e-6 || fabs(h0 - r0h) < 1e-6 || (!transform_stroke && (fabs(w1 - r0w) < 1e-6 || fabs(h1 - r0h) < 1e-6))) {
+ return (p2o * direct * o2n);
+ }
+
+ // Here starts the calculation you've been waiting for; first do some preparation
+ int flip_x = (w1 > 0) ? 1 : -1;
+ int flip_y = (h1 > 0) ? 1 : -1;
+
+ // w1 and h1 will be negative when mirroring, but if so then e.g. w1-r0 won't make sense
+ // Therefore we will use the absolute values from this point on
+ w1 = fabs(w1);
+ h1 = fabs(h1);
+ // w0 and h0 will always be positive due to the definition of the width() and height() methods.
+
+ gdouble ratio_x = (w1 - r0w) / (w0 - r0w); // Only valid when the stroke is kept constant, in which case r1 = r0
+ gdouble ratio_y = (h1 - r0h) / (h0 - r0h);
+
+ // Calculating the scaling of the geometric bounding box if the stroke is kept constant
+ Geom::Affine direct_constant_r = Geom::Scale(flip_x * ratio_x, flip_y * ratio_y);
+
+ // The calculation of the new strokewidth will only use the average stroke for each of the dimensions; To find the new stroke for each
+ // of the edges individually though, we will use the boundary condition that the ratio of the left/right strokewidth will not change due to the
+ // scaling. The same holds for the ratio of the top/bottom strokewidth.
+ gdouble stroke_ratio_w = fabs(r0w) < 1e-6 ? 1 : (bbox_geom[Geom::X].min() - bbox_visual[Geom::X].min())/r0w;
+ gdouble stroke_ratio_h = fabs(r0h) < 1e-6 ? 1 : (bbox_geom[Geom::Y].min() - bbox_visual[Geom::Y].min())/r0h;
+
+ // If the stroke is not kept constant however, the scaling of the geometric bbox is more difficult to find
+ if (transform_stroke && r0w != 0 && r0w != Geom::infinity() && r0h != 0 && r0h != Geom::infinity()) { // Check if there's stroke, and we need to scale it
+ /* Initial area of the geometric bounding box: A0 = (w0-r0w)*(h0-r0h)
+ * Desired area of the geometric bounding box: A1 = (w1-r1w)*(h1-r1h)
+ * This is how the stroke should scale: r1w^2 = A1/A0 * r0w^2, AND
+ * r1h^2 = A1/A0 * r0h^2
+ * Now we have to solve this set of two equations and find r1w and r1h; this too complicated to do by hand,
+ * so I used wxMaxima for that (http://wxmaxima.sourceforge.net/). These lines can be copied into Maxima
+ *
+ * A1: (w1-r1w)*(h1-r1h);
+ * s: A1/A0;
+ * expr1a: r1w^2 = s*r0w^2;
+ * expr1b: r1h^2 = s*r0h^2;
+ * sol: solve([expr1a, expr1b], [r1h, r1w]);
+ * sol[1][1]; sol[2][1]; sol[3][1]; sol[4][1];
+ * sol[1][2]; sol[2][2]; sol[3][2]; sol[4][2];
+ *
+ * PS1: The last two lines are only needed for readability of the output, and can be omitted if desired
+ * PS2: A0 is known beforehand and assumed to be constant, instead of using A0 = (w0-r0w)*(h0-r0h). This reduces the
+ * length of the results significantly
+ * PS3: You'll get 8 solutions, 4 for each of the strokewidths r1w and r1h. Some experiments quickly showed which of the solutions
+ * lead to meaningful strokewidths
+ * */
+ gdouble r0h2 = r0h*r0h;
+ gdouble r0h3 = r0h2*r0h;
+ gdouble r0w2 = r0w*r0w;
+ gdouble w12 = w1*w1;
+ gdouble h12 = h1*h1;
+ gdouble A0 = bbox_geom.area();
+ gdouble A02 = A0*A0;
+
+ gdouble operant = 4*h1*w1*A0+r0h2*w12-2*h1*r0h*r0w*w1+h12*r0w2;
+ if (operant >= 0) {
+ // Of the eight roots, I verified experimentally that these are the two we need
+ gdouble r1h= fabs((r0h*sqrt(operant)-r0h2*w1-h1*r0h*r0w)/(2*A0-2*r0h*r0w));
+ gdouble r1w= fabs(-((h1*r0w*A0+r0h2*r0w*w1)*sqrt(operant)+(-3*h1*r0h*r0w*w1-h12*r0w2)*A0-r0h3*r0w*w12+h1*r0h2*r0w2*w1)/((r0h*A0-r0h2*r0w)*sqrt(operant)-2*h1*A02+(3*h1*r0h*r0w-r0h2*w1)*A0+r0h3*r0w*w1-h1*r0h2*r0w2));
+ // If w1 < 0 then the scale will be wrong if we just assume that scale_x = (w1 - r1)/(w0 - r0);
+ // Therefore we here need the absolute values of w0, w1, h0, h1, and r0, as taken care of earlier
+ gdouble scale_x = (w1 - r1w)/(w0 - r0w);
+ gdouble scale_y = (h1 - r1h)/(h0 - r0h);
+ // Now we account for mirroring by flipping if needed
+ scale *= Geom::Scale(flip_x * scale_x, flip_y * scale_y);
+ // Make sure that the lower-left corner of the visual bounding box stays where it is, even though the stroke width has changed
+ unbudge *= Geom::Translate (-flip_x * stroke_ratio_w * (r0w * scale_x - r1w), -flip_y * stroke_ratio_h * (r0h * scale_y - r1h));
+ } else { // Can't find the roots of the quadratic equation. Likely the input parameters are invalid?
+ scale *= direct;
+ }
+ } else { // The stroke should not be scaled, or is zero (or infinite)
+ if (!transform_stroke) {
+ scale *= direct_constant_r;
+ unbudge *= Geom::Translate (flip_x * stroke_ratio_w * r0w * (1 - ratio_x), flip_y * stroke_ratio_h * r0h * (1 - ratio_y));
+ } else { // can't calculate, because apparently strokewidth is zero or infinite
+ scale *= direct;
}
}
diff --git a/src/sp-item-transform.h b/src/sp-item-transform.h
index 552b23e2f..47e0ec0ec 100644
--- a/src/sp-item-transform.h
+++ b/src/sp-item-transform.h
@@ -9,7 +9,8 @@ void sp_item_scale_rel (SPItem *item, Geom::Scale const &scale);
void sp_item_skew_rel (SPItem *item, double skewX, double skewY);
void sp_item_move_rel(SPItem *item, Geom::Translate const &tr);
-Geom::Affine get_scale_transform_with_stroke (Geom::Rect const &bbox, gdouble strokewidth, bool transform_stroke, gdouble x0, gdouble y0, gdouble x1, gdouble y1);
+Geom::Affine get_scale_transform_with_uniform_stroke (Geom::Rect const &bbox_visual, gdouble strokewidth, bool transform_stroke, gdouble x0, gdouble y0, gdouble x1, gdouble y1);
+Geom::Affine get_scale_transform_with_unequal_stroke (Geom::Rect const &bbox_visual, Geom::Rect const &bbox_geom, bool transform_stroke, gdouble x0, gdouble y0, gdouble x1, gdouble y1);
Geom::Rect get_visual_bbox (Geom::OptRect const &initial_geom_bbox, Geom::Affine const &abs_affine, gdouble const initial_strokewidth, bool const transform_stroke);
diff --git a/src/widgets/select-toolbar.cpp b/src/widgets/select-toolbar.cpp
index eb9b2805d..ba32dc321 100644
--- a/src/widgets/select-toolbar.cpp
+++ b/src/widgets/select-toolbar.cpp
@@ -159,12 +159,16 @@ sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw)
document->ensureUpToDate ();
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ Geom::OptRect bbox_vis = selection->bounds(SPItem::APPROXIMATE_BBOX);
+ Geom::OptRect bbox_geom = selection->bounds(SPItem::GEOMETRIC_BBOX);
+
int prefs_bbox = prefs->getInt("/tools/bounding_box");
- SPItem::BBoxType bbox_type = (prefs_bbox ==0)?
+ SPItem::BBoxType bbox_type = (prefs_bbox == 0)?
SPItem::APPROXIMATE_BBOX : SPItem::GEOMETRIC_BBOX;
- Geom::OptRect bbox = selection->bounds(bbox_type);
+ Geom::OptRect bbox_user = selection->bounds(bbox_type);
- if ( !bbox ) {
+ if ( !bbox_user ) {
g_object_set_data(G_OBJECT(spw), "update", GINT_TO_POINTER(FALSE));
return;
}
@@ -186,35 +190,35 @@ sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw)
x0 = sp_units_get_pixels (a_x->value, unit);
y0 = sp_units_get_pixels (a_y->value, unit);
x1 = x0 + sp_units_get_pixels (a_w->value, unit);
- xrel = sp_units_get_pixels (a_w->value, unit) / bbox->dimensions()[Geom::X];
+ xrel = sp_units_get_pixels (a_w->value, unit) / bbox_user->dimensions()[Geom::X];
y1 = y0 + sp_units_get_pixels (a_h->value, unit);
- yrel = sp_units_get_pixels (a_h->value, unit) / bbox->dimensions()[Geom::Y];
+ yrel = sp_units_get_pixels (a_h->value, unit) / bbox_user->dimensions()[Geom::Y];
} else {
double const x0_propn = a_x->value * unit.unittobase;
- x0 = bbox->min()[Geom::X] * x0_propn;
+ x0 = bbox_user->min()[Geom::X] * x0_propn;
double const y0_propn = a_y->value * unit.unittobase;
- y0 = y0_propn * bbox->min()[Geom::Y];
+ y0 = y0_propn * bbox_user->min()[Geom::Y];
xrel = a_w->value * unit.unittobase;
- x1 = x0 + xrel * bbox->dimensions()[Geom::X];
+ x1 = x0 + xrel * bbox_user->dimensions()[Geom::X];
yrel = a_h->value * unit.unittobase;
- y1 = y0 + yrel * bbox->dimensions()[Geom::Y];
+ y1 = y0 + yrel * bbox_user->dimensions()[Geom::Y];
}
// Keep proportions if lock is on
GtkToggleAction *lock = GTK_TOGGLE_ACTION( g_object_get_data(G_OBJECT(spw), "lock") );
if ( gtk_toggle_action_get_active(lock) ) {
if (adj == a_h) {
- x1 = x0 + yrel * bbox->dimensions()[Geom::X];
+ x1 = x0 + yrel * bbox_user->dimensions()[Geom::X];
} else if (adj == a_w) {
- y1 = y0 + xrel * bbox->dimensions()[Geom::Y];
+ y1 = y0 + xrel * bbox_user->dimensions()[Geom::Y];
}
}
// scales and moves, in px
- double mh = fabs(x0 - bbox->min()[Geom::X]);
- double sh = fabs(x1 - bbox->max()[Geom::X]);
- double mv = fabs(y0 - bbox->min()[Geom::Y]);
- double sv = fabs(y1 - bbox->max()[Geom::Y]);
+ double mh = fabs(x0 - bbox_user->min()[Geom::X]);
+ double sh = fabs(x1 - bbox_user->max()[Geom::X]);
+ double mv = fabs(y0 - bbox_user->min()[Geom::Y]);
+ double sv = fabs(y1 - bbox_user->max()[Geom::Y]);
// unless the unit is %, convert the scales and moves to the unit
if (unit.base == SP_UNIT_ABSOLUTE || unit.base == SP_UNIT_DEVICE) {
@@ -244,11 +248,11 @@ sp_object_layout_any_value_changed(GtkAdjustment *adj, SPWidget *spw)
Geom::Affine scaler;
if (bbox_type == SPItem::APPROXIMATE_BBOX) {
- // get_scale_transform_with_stroke() is intended for VISUAL (or APPROXIMATE) bounding boxes, not geometrical ones!
- scaler = get_scale_transform_with_stroke (*bbox, strokewidth, transform_stroke, x0, y0, x1, y1);
+ scaler = get_scale_transform_with_unequal_stroke (*bbox_vis, *bbox_geom, transform_stroke, x0, y0, x1, y1);
} else {
- // we'll trick it into using a geometrical bounding box though, by setting the stroke width to zero
- scaler = get_scale_transform_with_stroke (*bbox, 0, false, x0, y0, x1, y1);
+ // get_scale_transform_with_stroke() is intended for VISUAL (or APPROXIMATE) bounding boxes, not geometrical ones!
+ // we'll trick it into using a geometric bounding box though, by setting the stroke width to zero
+ scaler = get_scale_transform_with_uniform_stroke (*bbox_user, 0, false, x0, y0, x1, y1);
}
sp_selection_apply_affine(selection, scaler);