diff options
| author | Jon A. Cruz <jon@joncruz.org> | 2011-07-10 23:53:06 +0000 |
|---|---|---|
| committer | Jon A. Cruz <jon@joncruz.org> | 2011-07-10 23:53:06 +0000 |
| commit | 41d2d7dc84877f7187d260c89241d47b7a29a062 (patch) | |
| tree | d9a44107fe1d685719acb26fbe7bb316ca27c8b8 /src | |
| parent | Update for non-LCMS builds. (diff) | |
| parent | Selector's toolbar: changing the dimensions of the visual bounding box of sel... (diff) | |
| download | inkscape-41d2d7dc84877f7187d260c89241d47b7a29a062.tar.gz inkscape-41d2d7dc84877f7187d260c89241d47b7a29a062.zip | |
Merge from trunk.
(bzr r10439)
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 6 | ||||
| -rwxr-xr-x | src/extension/internal/filter/abc.h | 12 | ||||
| -rw-r--r-- | src/libnrtype/Layout-TNG-Input.cpp | 2 | ||||
| -rw-r--r-- | src/seltrans.cpp | 4 | ||||
| -rw-r--r-- | src/snap-preferences.cpp | 2 | ||||
| -rw-r--r-- | src/snap-preferences.h | 2 | ||||
| -rw-r--r-- | src/sp-item-transform.cpp | 273 | ||||
| -rw-r--r-- | src/sp-item-transform.h | 3 | ||||
| -rw-r--r-- | src/style.cpp | 133 | ||||
| -rw-r--r-- | src/style.h | 11 | ||||
| -rw-r--r-- | src/widgets/select-toolbar.cpp | 42 |
11 files changed, 350 insertions, 140 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 7925dcd7e..5a50eb36f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -208,11 +208,7 @@ libinkscape_a_SOURCES = $(ink_common_sources) inkscape_SOURCES += main.cpp $(win32_sources) inkscape_LDADD = $(all_libs) -if EXPORT_DYNAMIC_DIRECT -inkscape_LDFLAGS = --export-dynamic $(kdeldflags) $(mwindows) -else -inkscape_LDFLAGS = -Wl,--export-dynamic $(kdeldflags) $(mwindows) -endif +inkscape_LDFLAGS = $(kdeldflags) $(mwindows) inkview_SOURCES += inkview.cpp $(win32_sources) inkview_LDADD = $(all_libs) diff --git a/src/extension/internal/filter/abc.h b/src/extension/internal/filter/abc.h index 8368d3f3b..cf2bc8927 100755 --- a/src/extension/internal/filter/abc.h +++ b/src/extension/internal/filter/abc.h @@ -218,7 +218,7 @@ ColorShift::get_filter_text (Inkscape::Extension::Extension * ext) * Smoothness (0.->10., default 6.) -> blur (stdDeviation) * Elevation (0->360, default 25) -> feDistantLight (elevation) * Azimuth (0->360, default 235) -> feDistantLight (azimuth) - * Lightning color (guint, default -1 [white]) -> diffuse (lighting-color) + * Lighting color (guint, default -1 [white]) -> diffuse (lighting-color) */ class DiffuseLight : public Inkscape::Extension::Internal::Filter::Filter { @@ -237,7 +237,7 @@ public: "<param name=\"smooth\" gui-text=\"" N_("Smoothness:") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"10\">6</param>\n" "<param name=\"elevation\" gui-text=\"" N_("Elevation (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">25</param>\n" "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">235</param>\n" - "<param name=\"color\" gui-text=\"" N_("Lightning color") "\" type=\"color\">-1</param>\n" + "<param name=\"color\" gui-text=\"" N_("Lighting color") "\" type=\"color\">-1</param>\n" "<effect>\n" "<object-type>all</object-type>\n" "<effects-menu>\n" @@ -355,7 +355,7 @@ Feather::get_filter_text (Inkscape::Extension::Extension * ext) * Brightness (0.0->5., default .9) -> specular (specularConstant) * Elevation (0->360, default 60) -> feDistantLight (elevation) * Azimuth (0->360, default 225) -> feDistantLight (azimuth) - * Lightning color (guint, default -1 [white]) -> specular (lighting-color) + * Lighting color (guint, default -1 [white]) -> specular (lighting-color) */ class MatteJelly : public Inkscape::Extension::Internal::Filter::Filter { @@ -375,7 +375,7 @@ public: "<param name=\"bright\" gui-text=\"" N_("Brightness:") "\" type=\"float\" appearance=\"full\" precision=\"2\" min=\"0.00\" max=\"5.00\">0.9</param>\n" "<param name=\"elevation\" gui-text=\"" N_("Elevation (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">60</param>\n" "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">225</param>\n" - "<param name=\"color\" gui-text=\"" N_("Lightning color") "\" type=\"color\">-1</param>\n" + "<param name=\"color\" gui-text=\"" N_("Lighting color") "\" type=\"color\">-1</param>\n" "<effect>\n" "<object-type>all</object-type>\n" "<effects-menu>\n" @@ -795,7 +795,7 @@ Silhouette::get_filter_text (Inkscape::Extension::Extension * ext) * Brightness (0.0->5., default 1.) -> specular (specularConstant) * Elevation (0->360, default 45) -> feDistantLight (elevation) * Azimuth (0->360, default 235) -> feDistantLight (azimuth) - * Lightning color (guint, default -1 [white]) -> specular (lighting-color) + * Lighting color (guint, default -1 [white]) -> specular (lighting-color) */ class SpecularLight : public Inkscape::Extension::Internal::Filter::Filter { @@ -815,7 +815,7 @@ public: "<param name=\"bright\" gui-text=\"" N_("Brightness:") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"5\">1</param>\n" "<param name=\"elevation\" gui-text=\"" N_("Elevation (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">45</param>\n" "<param name=\"azimuth\" gui-text=\"" N_("Azimuth (°):") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">235</param>\n" - "<param name=\"color\" gui-text=\"" N_("Lightning color") "\" type=\"color\">-1</param>\n" + "<param name=\"color\" gui-text=\"" N_("Lighting color") "\" type=\"color\">-1</param>\n" "<effect>\n" "<object-type>all</object-type>\n" "<effects-menu>\n" diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp index 45bc0c89b..c5ea3969d 100644 --- a/src/libnrtype/Layout-TNG-Input.cpp +++ b/src/libnrtype/Layout-TNG-Input.cpp @@ -135,7 +135,7 @@ float Layout::InputStreamTextSource::styleComputeFontSize() const if (this_style->font_size.set && !this_style->font_size.inherit) { switch (this_style->font_size.type) { case SP_FONT_SIZE_LITERAL: { - switch(this_style->font_size.value) { // these multipliers are straight out of the CSS spec + switch(this_style->font_size.literal) { // these multipliers are straight out of the CSS spec case SP_CSS_FONT_SIZE_XX_SMALL: return medium_font_size * inherit_multiplier * (3.0/5.0); case SP_CSS_FONT_SIZE_X_SMALL: return medium_font_size * inherit_multiplier * (3.0/4.0); case SP_CSS_FONT_SIZE_SMALL: return medium_font_size * inherit_multiplier * (8.0/9.0); 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/style.cpp b/src/style.cpp index bb25a5f46..699b087dd 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -1343,11 +1343,11 @@ sp_style_merge_font_size_from_parent(SPIFontSize &child, SPIFontSize const &pare * fixme: SVG and CSS do not specify clearly, whether we should use * user or screen coordinates (Lauris) */ - if (child.value < SP_CSS_FONT_SIZE_SMALLER) { - child.computed = font_size_table[child.value]; - } else if (child.value == SP_CSS_FONT_SIZE_SMALLER) { + if (child.literal < SP_CSS_FONT_SIZE_SMALLER) { + child.computed = font_size_table[child.literal]; + } else if (child.literal == SP_CSS_FONT_SIZE_SMALLER) { child.computed = parent.computed / 1.2; - } else if (child.value == SP_CSS_FONT_SIZE_LARGER) { + } else if (child.literal == SP_CSS_FONT_SIZE_LARGER) { child.computed = parent.computed * 1.2; } else { /* Illegal value */ @@ -1355,7 +1355,21 @@ sp_style_merge_font_size_from_parent(SPIFontSize &child, SPIFontSize const &pare } else if (child.type == SP_FONT_SIZE_PERCENTAGE) { /* Unlike most other lengths, percentage for font size is relative to parent computed value * rather than viewport. */ - child.computed = parent.computed * SP_F8_16_TO_FLOAT(child.value); + child.computed = parent.computed * child.value; + } else if (child.type == SP_FONT_SIZE_LENGTH) { + switch (child.unit) { + case SP_CSS_UNIT_EM: + /* Relative to parent font size */ + child.computed = parent.computed * child.value; + break; + case SP_CSS_UNIT_EX: + /* Relative to parent font size */ + child.computed = parent.computed * child.value * 0.5; /* Hack */ + break; + default: + /* No change */ + break; + } } } @@ -1790,7 +1804,7 @@ get_relative_font_size_frac(SPIFontSize const &font_size) { switch (font_size.type) { case SP_FONT_SIZE_LITERAL: { - switch (font_size.value) { + switch (font_size.literal) { case SP_CSS_FONT_SIZE_SMALLER: return 5.0 / 6.0; @@ -1803,10 +1817,20 @@ get_relative_font_size_frac(SPIFontSize const &font_size) } case SP_FONT_SIZE_PERCENTAGE: - return SP_F8_16_TO_FLOAT(font_size.value); + return font_size.value; + + case SP_FONT_SIZE_LENGTH: { + switch (font_size.unit ) { + case SP_CSS_UNIT_EM: + return font_size.value; + + case SP_CSS_UNIT_EX: + return font_size.value * 0.5; - case SP_FONT_SIZE_LENGTH: - g_assert_not_reached(); + default: + g_assert_not_reached(); + } + } } g_assert_not_reached(); } @@ -1853,20 +1877,29 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare { /* font-size. Note that we update the computed font-size of style, to assist in em calculations later in this function. */ + if (parent->font_size.set && !parent->font_size.inherit) { + /* Parent has defined font-size */ + if (!style->font_size.set || style->font_size.inherit) { /* font_size inherits the computed value, so we can use the parent value * verbatim. */ style->font_size = parent->font_size; - } else if ( style->font_size.type == SP_FONT_SIZE_LENGTH ) { + + } else if ( style->font_size.type == SP_FONT_SIZE_LENGTH && + style->font_size.unit != SP_CSS_UNIT_EM && + style->font_size.unit != SP_CSS_UNIT_EX ) { + /* Child already has absolute size (stored in computed value), so do nothing. */ + } else if ( style->font_size.type == SP_FONT_SIZE_LITERAL - && style->font_size.value < SP_CSS_FONT_SIZE_SMALLER ) { + && style->font_size.literal < SP_CSS_FONT_SIZE_SMALLER ) { /* Child already has absolute size, but we ensure that the computed value is up-to-date. */ - unsigned const ix = style->font_size.value; + unsigned const ix = style->font_size.literal; g_assert(ix < G_N_ELEMENTS(font_size_table)); style->font_size.computed = font_size_table[ix]; + } else { /* Child has relative size. */ double const child_frac(get_relative_font_size_frac(style->font_size)); @@ -1875,17 +1908,26 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare style->font_size.computed = parent->font_size.computed * child_frac; if ( ( parent->font_size.type == SP_FONT_SIZE_LITERAL - && parent->font_size.value < SP_CSS_FONT_SIZE_SMALLER ) - || parent->font_size.type == SP_FONT_SIZE_LENGTH ) - { + && parent->font_size.literal < SP_CSS_FONT_SIZE_SMALLER ) || + ( parent->font_size.type == SP_FONT_SIZE_LENGTH && + parent->font_size.unit != SP_CSS_UNIT_EM && + parent->font_size.unit != SP_CSS_UNIT_EX ) ) { + /* Absolute value. */ style->font_size.type = SP_FONT_SIZE_LENGTH; - /* .value is unused for SP_FONT_SIZE_LENGTH. */ + /* .value is unused for non ex/em SP_FONT_SIZE_LENGTH. */ + } else { /* Relative value. */ + double const parent_frac(get_relative_font_size_frac(parent->font_size)); - style->font_size.type = SP_FONT_SIZE_PERCENTAGE; - style->font_size.value = SP_F8_16_FROM_FLOAT(parent_frac * child_frac); + if( style->font_size.type == SP_FONT_SIZE_LENGTH ) { + /* Value in terms of ex/em */ + style->font_size.value *= parent_frac; + } else { + style->font_size.value = parent_frac * child_frac; + style->font_size.type = SP_FONT_SIZE_PERCENTAGE; + } } } } @@ -2677,7 +2719,7 @@ sp_style_clear(SPStyle *style) style->font_size.set = FALSE; style->font_size.type = SP_FONT_SIZE_LITERAL; - style->font_size.value = SP_CSS_FONT_SIZE_MEDIUM; + style->font_size.literal = SP_CSS_FONT_SIZE_MEDIUM; style->font_size.computed = 12.0; style->font_style.set = FALSE; style->font_style.value = style->font_style.computed = SP_CSS_FONT_STYLE_NORMAL; @@ -3008,6 +3050,7 @@ sp_style_read_ienum(SPIEnum *val, gchar const *str, SPStyleEnum const *dict, } } } + return; } @@ -3284,52 +3327,26 @@ sp_style_read_ifontsize(SPIFontSize *val, gchar const *str) val->set = TRUE; val->inherit = FALSE; val->type = SP_FONT_SIZE_LITERAL; - val->value = enum_font_size[i].value; + val->literal = enum_font_size[i].value; return; } } /* Invalid */ return; } else { - gdouble value; - gchar *e; - /* fixme: Move this to standard place (Lauris) */ - value = g_ascii_strtod(str, &e); - if ((gchar const *) e != str) { - if (!*e) { - /* Userspace */ - } else if (!strcmp(e, "px")) { - /* Userspace */ - } else if (!strcmp(e, "pt")) { - /* Userspace * DEVICESCALE */ - value *= PX_PER_PT; - } else if (!strcmp(e, "pc")) { - /* 12pt */ - value *= PX_PER_PT * 12.0; - } else if (!strcmp(e, "mm")) { - value *= PX_PER_MM; - } else if (!strcmp(e, "cm")) { - value *= PX_PER_CM; - } else if (!strcmp(e, "in")) { - value *= PX_PER_IN; - } else if (!strcmp(e, "%")) { - /* Percentage */ - val->set = TRUE; - val->inherit = FALSE; - val->type = SP_FONT_SIZE_PERCENTAGE; - val->value = SP_F8_16_FROM_FLOAT(value / 100.0); - return; - } else { - /* Invalid */ - return; - } - /* Length */ - val->set = TRUE; - val->inherit = FALSE; + SPILength length; + sp_style_read_ilength(&length, str); + val->set = length.set; + val->inherit = length.inherit; + val->unit = length.unit; + val->value = length.value; + val->computed = length.computed; + if( val->unit == SP_CSS_UNIT_PERCENT ) { + val->type = SP_FONT_SIZE_PERCENTAGE; + } else { val->type = SP_FONT_SIZE_LENGTH; - val->computed = value; - return; } + return; } } @@ -3935,7 +3952,7 @@ sp_style_write_ifontsize(gchar *p, gint const len, gchar const *key, return g_strlcpy(p, os.str().c_str(), len); } else if (val->type == SP_FONT_SIZE_PERCENTAGE) { Inkscape::CSSOStringStream os; - os << key << ":" << (SP_F8_16_TO_FLOAT(val->value) * 100.0) << "%;"; + os << key << ":" << (val->value * 100.0) << "%;"; return g_strlcpy(p, os.str().c_str(), len); } } diff --git a/src/style.h b/src/style.h index a12db388a..d82a0dd5e 100644 --- a/src/style.h +++ b/src/style.h @@ -206,21 +206,24 @@ enum { SP_BASELINE_SHIFT_PERCENTAGE }; -#define SP_FONT_SIZE ((1 << 24) - 1) - +/* +Not used anymore, originally for SPIFontSize #define SP_F8_16_TO_FLOAT(v) ((gdouble) (v) / (1 << 16)) #define SP_F8_16_FROM_FLOAT(v) ((int) ((v) * ((1 << 16) + 0.9999))) +*/ #define SP_STYLE_FLAG_IFSET (1 << 0) #define SP_STYLE_FLAG_IFDIFF (1 << 1) #define SP_STYLE_FLAG_ALWAYS (1 << 2) -/// Fontsize type internal to SPStyle. +/// Fontsize type internal to SPStyle (also used by libnrtype/Layout-TNG-Input.cpp). struct SPIFontSize { unsigned set : 1; unsigned inherit : 1; unsigned type : 2; - unsigned value : 24; + unsigned unit : 4; + unsigned literal: 4; + float value; float computed; }; 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); |
