summaryrefslogtreecommitdiffstats
path: root/src/sp-item-transform.cpp
diff options
context:
space:
mode:
authorJabier Arraiza Cenoz <jabier.arraiza@marker.es>2014-01-09 16:47:29 +0000
committerJabiertxof <jtx@jtx.marker.es>2014-01-09 16:47:29 +0000
commit13abf3e6f87b4bc85708bfd8d8fe93deb76facdc (patch)
tree8f5021ba5ae6123d242f5740c45611266a317071 /src/sp-item-transform.cpp
parentFix a bug whith oposite handles on node move,and a little cleanup (diff)
parentFix for bug #1266113 (xcf export only allows 90 dpi). (diff)
downloadinkscape-13abf3e6f87b4bc85708bfd8d8fe93deb76facdc.tar.gz
inkscape-13abf3e6f87b4bc85708bfd8d8fe93deb76facdc.zip
update to trunk
(bzr r11950.1.233)
Diffstat (limited to 'src/sp-item-transform.cpp')
-rw-r--r--src/sp-item-transform.cpp172
1 files changed, 89 insertions, 83 deletions
diff --git a/src/sp-item-transform.cpp b/src/sp-item-transform.cpp
index 2d1dd8193..250713beb 100644
--- a/src/sp-item-transform.cpp
+++ b/src/sp-item-transform.cpp
@@ -226,6 +226,7 @@ Geom::Affine get_scale_transform_for_uniform_stroke(Geom::Rect const &bbox_visua
* @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 preserve If true then the transform element will be preserved in XML, and evaluated after stroke is applied
* @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
@@ -234,7 +235,7 @@ Geom::Affine get_scale_transform_for_uniform_stroke(Geom::Rect const &bbox_visua
* not possible here because it will only allow for a positive width and height, and therefore cannot mirror
* @return
*/
-Geom::Affine get_scale_transform_for_variable_stroke(Geom::Rect const &bbox_visual, Geom::Rect const &bbox_geom, bool transform_stroke, gdouble x0, gdouble y0, gdouble x1, gdouble y1)
+Geom::Affine get_scale_transform_for_variable_stroke(Geom::Rect const &bbox_visual, Geom::Rect const &bbox_geom, bool transform_stroke, bool preserve, gdouble x0, gdouble y0, gdouble x1, gdouble y1)
{
Geom::Affine p2o = Geom::Translate (-bbox_visual.min());
Geom::Affine o2n = Geom::Translate (x0, y0);
@@ -272,79 +273,10 @@ Geom::Affine get_scale_transform_for_variable_stroke(Geom::Rect const &bbox_visu
return Geom::Affine();
}
- Geom::Affine direct;
- gdouble ratio_x = 1;
- gdouble ratio_y = 1;
- gdouble scale_x = 1;
- gdouble scale_y = 1;
- gdouble r1h = r0h;
- gdouble r1w = r0w;
-
- if (fabs(w0 - r0w) < 1e-6) { // We have a vertical line at hand
- direct = Geom::Scale(flip_x, flip_y * h1 / h0);
- ratio_x = 1;
- ratio_y = (h1 - r0h) / (h0 - r0h);
- r1h = transform_stroke ? r0h * sqrt(h1/h0) : r0h;
- scale_x = 1;
- scale_y = (h1 - r1h)/(h0 - r0h);
- } else if (fabs(h0 - r0h) < 1e-6) { // We have a horizontal line at hand
- direct = Geom::Scale(flip_x * w1 / w0, flip_y);
- ratio_x = (w1 - r0w) / (w0 - r0w);
- ratio_y = 1;
- r1w = transform_stroke ? r0w * sqrt(w1/w0) : r0w;
- scale_x = (w1 - r1w)/(w0 - r0w);
- scale_y = 1;
- } else { // We have a true 2D object at hand
- direct = Geom::Scale(flip_x * w1 / w0, flip_y* h1 / h0); // Scaling of the visual bounding box
- ratio_x = (w1 - r0w) / (w0 - r0w); // Only valid when the stroke is kept constant, in which case r1 = r0
- ratio_y = (h1 - r0h) / (h0 - r0h);
- /* 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
- r1h = fabs((r0h*sqrt(operant)-r0h2*w1-h1*r0h*r0w)/(2*A0-2*r0h*r0w));
- 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
- scale_x = (w1 - r1w)/(w0 - r0w);
- scale_y = (h1 - r1h)/(h0 - r0h);
- } else { // Can't find the roots of the quadratic equation. Likely the input parameters are invalid?
- scale_x = w1 / w0;
- scale_y = h1 / h0;
- }
- }
-
// Check whether the stroke is negative; i.e. the geometric bounding box is larger than the visual bounding box, which
// occurs for example for clipped objects (see launchpad bug #811819)
if (r0w < 0 || r0h < 0) {
+ Geom::Affine direct = Geom::Scale(flip_x * w1 / w0, flip_y* h1 / h0); // Scaling of the visual bounding box
// How should we handle the stroke width scaling of clipped object? I don't know if we can/should handle this,
// so for now we simply return the direct scaling
return (p2o * direct * o2n);
@@ -356,21 +288,95 @@ Geom::Affine get_scale_transform_for_variable_stroke(Geom::Rect const &bbox_visu
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
- // 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 { // The stroke should not be scaled, or is zero (or infinite)
- if (r0w == 0 || r0w == Geom::infinity() || r0h == 0 || r0h == Geom::infinity()) { // can't calculate, because apparently strokewidth is zero or infinite
- scale *= direct;
- } else {
- scale *= Geom::Scale(flip_x * ratio_x, flip_y * ratio_y); // Scaling of the geometric bounding box for constant stroke width
- unbudge *= Geom::Translate (flip_x * stroke_ratio_w * r0w * (1 - ratio_x), flip_y * stroke_ratio_h * r0h * (1 - ratio_y));
+ gdouble scale_x = 1;
+ gdouble scale_y = 1;
+ gdouble r1h = r0h;
+ gdouble r1w = r0w;
+
+ if ((fabs(w0 - r0w) < 1e-6) || w1 == 0) { // We have a vertical line at hand
+ r1h = transform_stroke ? r0h * sqrt(h1/h0) : r0h;
+ scale_x = 1;
+ scale_y = preserve ? h1/h0 : (h1 - r1h)/(h0 - r0h);
+ } else if ((fabs(h0 - r0h) < 1e-6) || h1 == 0) { // We have a horizontal line at hand
+ r1w = transform_stroke ? r0w * sqrt(w1/w0) : r0w;
+ scale_x = preserve ? w1/w0 : (w1 - r1w)/(w0 - r0w);
+ scale_y = 1;
+ } else { // We have a true 2D object at hand
+ if (transform_stroke && !preserve) {
+ /* 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) {
+ g_message("variable stroke scaling error : %d, %d, %f, %f, %f, %f, %f, %f", transform_stroke, preserve, r0w, r0h, w0, h0, w1, h1);
+ } else {
+ // Of the eight roots, I verified experimentally that these are the two we need
+ r1h = fabs((r0h*sqrt(operant)-r0h2*w1-h1*r0h*r0w)/(2*A0-2*r0h*r0w));
+ 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
+ scale_x = (w1 - r1w)/(w0 - r0w);
+ scale_y = (h1 - r1h)/(h0 - r0h);
+ // 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 if (!transform_stroke && !preserve) { // scale the geometric bbox with constant stroke
+ scale_x = (w1 - r0w) / (w0 - r0w);
+ scale_y = (h1 - r0h) / (h0 - r0h);
+ unbudge *= Geom::Translate (-flip_x * stroke_ratio_w * r0w * (scale_x - 1), -flip_y * stroke_ratio_h * r0h * (scale_y - 1));
+ } else if (!transform_stroke) { // 'Preserve Transforms' was chosen.
+ // geometric mean of r0w and r0h will be preserved
+ // new_r0w = r0w*sqrt(scale_x/scale_y)
+ // new_r0h = r0h*sqrt(scale_y/scale_x)
+ // scale_x = (w1 - new_r0w)/(w0 - r0w)
+ // scale_y = (h1 - new_r0h)/(h0 - r0h)
+ gdouble A = h1*(w0 - r0w);
+ gdouble B = (h0*r0w - w0*r0h);
+ gdouble C = -w1*(h0 - r0h);
+ gdouble Sx_div_Sy; // Sx_div_Sy = sqrt(scale_x/scale_y)
+ if (B*B - 4*A*C < 0) {
+ g_message("variable stroke scaling error : %d, %d, %f, %f, %f, %f, %f, %f", transform_stroke, preserve, r0w, r0h, w0, h0, w1, h1);
+ } else {
+ Sx_div_Sy = (-B + sqrt(B*B - 4*A*C))/2/A;
+ scale_x = (w1 - r0w*Sx_div_Sy)/(w0 - r0w);
+ scale_y = (h1 - r0h/Sx_div_Sy)/(h0 - r0h);
+ unbudge *= Geom::Translate (-flip_x * stroke_ratio_w * r0w * scale_x * (1.0 - sqrt(1.0/scale_x/scale_y)), -flip_y * stroke_ratio_h * r0h * scale_y * (1.0 - sqrt(1.0/scale_x/scale_y)));
+ }
+ } else { // 'Preserve Transforms' was chosen, and stroke is scaled
+ scale_x = w1 / w0;
+ scale_y = h1 / h0;
}
}
+ // Now we account for mirroring by flipping if needed
+ scale *= Geom::Scale(flip_x * scale_x, flip_y * scale_y);
+
return (p2o * scale * unbudge * o2n);
}