diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2015-03-19 18:27:16 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx.marker.es> | 2015-03-19 18:27:16 +0000 |
| commit | 6f389e1771b308f9e2686060f7dc292b7f65daa5 (patch) | |
| tree | 35f5fae6ce272d446359fb2010861f07734e1ab6 /src | |
| parent | fixing a boring bug in path manipulator (diff) | |
| parent | ui/tools: sp. fixes (cosmetic) (diff) | |
| download | inkscape-6f389e1771b308f9e2686060f7dc292b7f65daa5.tar.gz inkscape-6f389e1771b308f9e2686060f7dc292b7f65daa5.zip | |
update to trunk
(bzr r13645.1.44)
Diffstat (limited to 'src')
| -rw-r--r-- | src/2geom/sbasis-to-bezier.cpp | 21 | ||||
| -rw-r--r-- | src/extension/internal/cairo-renderer.cpp | 2 | ||||
| -rw-r--r-- | src/helper/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/helper/Makefile_insert | 2 | ||||
| -rw-r--r-- | src/helper/geom-pathstroke.cpp | 505 | ||||
| -rw-r--r-- | src/helper/geom-pathstroke.h | 57 | ||||
| -rw-r--r-- | src/knotholder.cpp | 3 | ||||
| -rw-r--r-- | src/live_effects/lpe-jointype.cpp | 56 | ||||
| -rw-r--r-- | src/live_effects/lpe-jointype.h | 4 | ||||
| -rw-r--r-- | src/live_effects/parameter/point.cpp | 2 | ||||
| -rw-r--r-- | src/sp-ellipse.cpp | 83 | ||||
| -rw-r--r-- | src/sp-item.cpp | 69 | ||||
| -rw-r--r-- | src/sp-item.h | 1 | ||||
| -rw-r--r-- | src/sp-rect.cpp | 24 | ||||
| -rw-r--r-- | src/ui/object-edit.cpp | 12 | ||||
| -rw-r--r-- | src/ui/tools/box3d-tool.h | 2 | ||||
| -rw-r--r-- | src/ui/tools/connector-tool.cpp | 2 | ||||
| -rw-r--r-- | src/ui/tools/dynamic-base.h | 2 | ||||
| -rw-r--r-- | src/ui/tools/gradient-tool.cpp | 2 | ||||
| -rw-r--r-- | src/ui/tools/node-tool.cpp | 2 | ||||
| -rw-r--r-- | src/ui/tools/pen-tool.cpp | 4 | ||||
| -rw-r--r-- | src/ui/tools/spray-tool.cpp | 2 | ||||
| -rw-r--r-- | src/ui/widget/page-sizer.cpp | 1 |
23 files changed, 725 insertions, 135 deletions
diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp index 0525be04b..a2e4253d2 100644 --- a/src/2geom/sbasis-to-bezier.cpp +++ b/src/2geom/sbasis-to-bezier.cpp @@ -37,6 +37,7 @@ #include <2geom/choose.h> #include <2geom/path-sink.h> #include <2geom/exception.h> +#include <2geom/convex-cover.h> #include <iostream> @@ -203,11 +204,8 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb) THROW_RANGEERROR("size of sb is too small"); } - bz.resize(4, Point(0,0)); - bz[0][X] = sb[X][0][0]; - bz[0][Y] = sb[Y][0][0]; - bz[3][X] = sb[X][0][1]; - bz[3][Y] = sb[Y][0][1]; + sbasis_to_bezier(bz, sb, 4); // zeroth-order estimate + Geom::ConvexHull bezhull(bz); // calculate first derivatives of x and y wrt t @@ -231,17 +229,23 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb) midx += (sb[X][i][0] + sb[X][i][1])/div; div *= 4; } - midx = 8*midx - 4*bz[0][X] - 4*bz[3][X]; div = 2; for (size_t i = 0; i < sb[Y].size(); ++i) { midy += (sb[Y][i][0] + sb[Y][i][1])/div; div *= 4; } - midy = 8*midy - 4*bz[0][Y] - 4*bz[3][Y]; + +// is midpoint in hull: if not, the solution will be ill-conditioned, LP Bug 1428683 + + if (!bezhull.contains_point(Geom::Point(midx, midy))) + return; // calculate Bezier control arms + midx = 8*midx - 4*bz[0][X] - 4*bz[3][X]; // re-define relative to center + midy = 8*midy - 4*bz[0][Y] - 4*bz[3][Y]; + if ((std::abs(xprime[0]) < EPSILON) && (std::abs(yprime[0]) < EPSILON) && ((std::abs(xprime[1]) > EPSILON) || (std::abs(yprime[1]) > EPSILON))) { // degenerate handle at 0 : use distance of closest approach numer = midx*xprime[1] + midy*yprime[1]; @@ -258,7 +262,8 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb) dely[0] = yprime[0]*numer/denom; delx[1] = 0; dely[1] = 0; - } else if (std::abs(xprime[1]*yprime[0] - yprime[1]*xprime[0]) > EPSILON) { // general case : fit mid fxn value + } else if (std::abs(xprime[1]*yprime[0] - yprime[1]*xprime[0]) > // general case : fit mid fxn value + 0.002 * std::abs(xprime[1]*xprime[0] + yprime[1]*yprime[0])) { // approx. 0.1 degree of angle denom = xprime[1]*yprime[0] - yprime[1]*xprime[0]; for (int i = 0; i < 2; ++i) { numer = xprime[1 - i]*midy - yprime[1 - i]*midx; diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 1f48d2097..7fbdc4296 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -517,7 +517,7 @@ static void sp_item_invoke_render(SPItem *item, CairoRenderContext *ctx) SPStyle* style = item->style; if((ctx->getFilterToBitmap() == TRUE) && (style->filter.set != 0)) { - sp_asbitmap_render(item, ctx); + return sp_asbitmap_render(item, ctx); } SPRoot *root = dynamic_cast<SPRoot *>(item); diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index 74ce2c85d..ff4760c24 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -13,6 +13,7 @@ set(helper_SRC action-context.cpp geom.cpp geom-nodetype.cpp + geom-pathstroke.cpp gnome-utils.cpp pixbuf-ops.cpp png-write.cpp @@ -30,6 +31,7 @@ set(helper_SRC action-context.h geom-curves.h geom-nodetype.h + geom-pathstroke.h geom.h gnome-utils.h mathfns.h diff --git a/src/helper/Makefile_insert b/src/helper/Makefile_insert index 5cb4cea8d..e59fcfb70 100644 --- a/src/helper/Makefile_insert +++ b/src/helper/Makefile_insert @@ -12,6 +12,8 @@ ink_common_sources += \ helper/geom-curves.h \ helper/geom-nodetype.cpp \ helper/geom-nodetype.h \ + helper/geom-pathstroke.cpp \ + helper/geom-pathstroke.h \ helper/gnome-utils.cpp \ helper/gnome-utils.h \ helper/mathfns.h \ diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp new file mode 100644 index 000000000..f41732a51 --- /dev/null +++ b/src/helper/geom-pathstroke.cpp @@ -0,0 +1,505 @@ +/* Author: + * Liam P. White + * + * Copyright (C) 2014-2015 Author + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <2geom/path-sink.h> +#include <2geom/point.h> +#include <2geom/bezier-curve.h> +#include <2geom/svg-elliptical-arc.h> +#include <2geom/sbasis-to-bezier.h> // cubicbezierpath_from_sbasis + +#include "helper/geom-pathstroke.h" + +namespace Geom { +// 2geom/circle-circle.cpp, no header +int circle_circle_intersection(Point X0, double r0, Point X1, double r1, Point &p0, Point &p1); + +static Point intersection_point(Point origin_a, Point vector_a, Point origin_b, Point vector_b) +{ + Coord denom = cross(vector_b, vector_a); + if (!are_near(denom,0.)) { + Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom; + return origin_a + vector_a*t; + } + return Point(infinity(), infinity()); +} + +/** +* Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t. +* Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt). +*/ +static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.01 ) +{ + D2<SBasis> dM=derivative(curve); + if ( are_near(L2sq(dM(t)),0.) ) { + dM=derivative(dM); + } + if ( are_near(L2sq(dM(t)),0.) ) { // try second time + dM=derivative(dM); + } + Piecewise<D2<SBasis> > unitv = unitVector(dM,tol); + Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv); + Piecewise<SBasis> k = cross(derivative(unitv),unitv); + k = divide(k,dMlength,tol,3); + double curv = k(t); // note that this value is signed + + Geom::Point normal = unitTangentAt(curve, t).cw(); + double radius = 1/curv; + Geom::Point center = curve(t) + radius*normal; + return Geom::Circle(center, fabs(radius)); +} + +} + +namespace { + +typedef void join_func(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width); + +void bevel_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, double /*width*/) +{ + res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); +} + +void round_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, double width) +{ + res.appendNew<Geom::SVGEllipticalArc>(width, width, 0, false, width > 0, outgoing.initialPoint()); +} + +void miter_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) +{ + Geom::Curve const& incoming = res.back(); + Geom::Point tang1 = Geom::unitTangentAt(reverse(incoming.toSBasis()), 0.); + Geom::Point tang2 = outgoing.unitTangentAt(0); + Geom::Point p = Geom::intersection_point(incoming.finalPoint(), tang1, outgoing.initialPoint(), tang2); + if (p.isFinite()) { + // check size of miter + Geom::Point point_on_path = incoming.finalPoint() - Geom::rot90(tang1)*width; + double len = Geom::distance(p, point_on_path); + if (len <= miter) { + // miter OK, check to see if we can do a relocation + // TODO FIXME + /*if (auto line = cast(const(LineSegment))res.back_open) { + Curve copy = line.duplicate; + copy.setFinal(p); + res.erase_last(); + res.append(copy); + } else {*/ + res.appendNew<Geom::LineSegment>(p); + //} + } + } + res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); +} + +// might need a little reworking +void extrapolate_join(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) +{ + using namespace Geom; + Geom::Curve const& incoming = path_builder.back(); + Geom::Point endPt = outgoing.initialPoint(); + + // The method used when extrapolating curves fails to work when either side of the join to be extrapolated + // is a line segment. When this situation is encountered, fall back to a regular miter join. + bool lineProblem = (dynamic_cast<LineSegment const *>(&incoming)) || (dynamic_cast<LineSegment const*>(&outgoing)); + if (lineProblem == false) { + // Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.); + Geom::Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0); + + Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.); + Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0); + + Geom::Point points[2]; + int solutions = Geom::circle_circle_intersection(circle1.center(), circle1.ray(), + circle2.center(), circle2.ray(), + points[0], points[1]); + if (solutions == 2) { + Geom::Point sol(0,0); + if ( dot(tang2,points[0]-endPt) > 0 ) { + // points[0] is bad, choose points[1] + sol = points[1]; + } else if ( dot(tang2,points[1]-endPt) > 0 ) { // points[0] could be good, now check points[1] + // points[1] is bad, choose points[0] + sol = points[0]; + } else { + // both points are good, choose nearest + sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ? points[0] : points[1]; + } + + Geom::EllipticalArc *arc0 = circle1.arc(incoming.finalPoint(), 0.5*(incoming.finalPoint()+sol), sol, true); + Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); + try { + if (arc0) { + path_builder.append(*arc0); + delete arc0; + arc0 = NULL; + } else { + throw std::exception(); + } + + if (arc1) { + path_builder.append(*arc1); + delete arc1; + arc1 = NULL; + } else { + throw std::exception(); + } + + } catch (std::exception const & ex) { + printf("WARNING: Error extrapolating line join: %s\n", ex.what()); + path_builder.appendNew<Geom::LineSegment>(endPt); + } + } else { + // 1 or no solutions found, default to miter + miter_join(path_builder, outgoing, miter_limit, line_width); + } + } else { + // Line segments exist + miter_join(path_builder, outgoing, miter_limit, line_width); + } +} + +void join_inside(Geom::Path& res, Geom::Curve const& outgoing) +{ + res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); +} + +void outline_helper(Geom::Path& res, Geom::Path const& to_add, double width, double miter, Inkscape::LineJoinType join) +{ + Geom::Point tang1 = -Geom::unitTangentAt(reverse(res.back().toSBasis()), 0.); + //Geom::Point tang2 = to_add[0].unitTangentAt(0); + Geom::Point discontinuity_vec = to_add.initialPoint() - res.finalPoint(); + bool on_outside = (Geom::dot(tang1, discontinuity_vec) >= 0); + + if (on_outside) { + join_func *jf; + switch (join) { + case Inkscape::JOIN_BEVEL: + jf = &bevel_join; + break; + case Inkscape::JOIN_ROUND: + jf = &round_join; + break; + case Inkscape::JOIN_EXTRAPOLATE: + jf = &extrapolate_join; + break; + default: + jf = &miter_join; + } + jf(res, to_add[0], miter, width); + } else { + join_inside(res, to_add[0]); + } + + res.append(to_add); +} + +// Offsetting a line segment is mathematically stable and quick to do +Geom::LineSegment offset_line(Geom::LineSegment const& l, double width) +{ + Geom::Point tang1 = Geom::rot90(l.unitTangentAt(0)); + Geom::Point tang2 = Geom::rot90(unitTangentAt(reverse(l.toSBasis()), 0.)); + + Geom::Point start = l.initialPoint() + tang1 * width; + Geom::Point end = l.finalPoint() - tang2 * width; + + return Geom::LineSegment(start, end); +} + +void get_cubic_data(Geom::CubicBezier const& bez, double time, double& len, double& rad) +{ + // get derivatives + std::vector<Geom::Point> derivs = bez.pointAndDerivatives(time, 3); + + Geom::Point der1 = derivs[1]; // first deriv (tangent vector) + Geom::Point der2 = derivs[2]; // second deriv (tangent's tangent) + double l = Geom::L2(der1); // length + + len = rad = 0; + + // TODO: we might want to consider using Geom::touching_circle to determine the + // curvature radius here. Less code duplication, but slower + + if (Geom::are_near(l, 0, 1e-4)) { + l = Geom::L2(der2); + Geom::Point der3 = derivs.at(3); // try second time + if (Geom::are_near(l, 0, 1e-4)) { + l = Geom::L2(der3); + if (Geom::are_near(l, 0)) { + return; // this isn't a segment... + } + rad = 1e8; + } else { + rad = -l * (Geom::dot(der2, der2) / Geom::cross(der3, der2)); + } + } else { + rad = -l * (Geom::dot(der1, der1) / Geom::cross(der2, der1)); + } + len = l; +} + +void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, double tol, size_t levels) +{ + using Geom::X; + using Geom::Y; + + Geom::Point start_pos = bez.initialPoint(); + Geom::Point end_pos = bez.finalPoint(); + + Geom::Point start_normal = Geom::rot90(bez.unitTangentAt(0)); + Geom::Point end_normal = -Geom::rot90(Geom::unitTangentAt(Geom::reverse(bez.toSBasis()), 0.)); + + // offset the start and end control points out by the width + Geom::Point start_new = start_pos + start_normal*width; + Geom::Point end_new = end_pos + end_normal*width; + + // -------- + double start_rad, end_rad; + double start_len, end_len; // tangent lengths + get_cubic_data(bez, 0, start_len, start_rad); + get_cubic_data(bez, 1, end_len, end_rad); + + double start_off = 1, end_off = 1; + // correction of the lengths of the tangent to the offset + if (!Geom::are_near(start_rad, 0)) + start_off += width / start_rad; + if (!Geom::are_near(end_rad, 0)) + end_off += width / end_rad; + start_off *= start_len; + end_off *= end_len; + // -------- + + Geom::Point mid1_new = start_normal.ccw()*start_off; + mid1_new = Geom::Point(start_new[X] + mid1_new[X]/3., start_new[Y] + mid1_new[Y]/3.); + Geom::Point mid2_new = end_normal.ccw()*end_off; + mid2_new = Geom::Point(end_new[X] - mid2_new[X]/3., end_new[Y] - mid2_new[Y]/3.); + + // create the estimate curve + Geom::CubicBezier c = Geom::CubicBezier(start_new, mid1_new, mid2_new, end_new); + + // reached maximum recursive depth + // don't bother with any more correction + if (levels == 0) { + p.append(c, Geom::Path::STITCH_DISCONTINUOUS); + return; + } + + // check the tolerance for our estimate to be a parallel curve + Geom::Point chk = c.pointAt(.5); + Geom::Point req = bez.pointAt(.5) + Geom::rot90(bez.unitTangentAt(.5))*width; // required accuracy + + Geom::Point const diff = req - chk; + double const err = Geom::dot(diff, diff); + + if (err < tol) { + if (Geom::are_near(start_new, p.finalPoint())) { + p.setFinal(start_new); // if it isn't near, we throw + } + + // we're good, curve is accurate enough + p.append(c); + return; + } else { + // split the curve in two + std::pair<Geom::CubicBezier, Geom::CubicBezier> s = bez.subdivide(.5); + offset_cubic(p, s.first, width, tol, levels - 1); + offset_cubic(p, s.second, width, tol, levels - 1); + } +} + +void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double width, double tol, size_t levels) +{ + // cheat + // it's faster + // seriously + std::vector<Geom::Point> points = bez.points(); + Geom::Point b1 = points[0] + (2./3) * (points[1] - points[0]); + Geom::Point b2 = b1 + (1./3) * (points[2] - points[0]); + Geom::CubicBezier cub = Geom::CubicBezier(points[0], b1, b2, points[3]); + offset_cubic(p, cub, width, tol, levels); +} + +void offset_curve(Geom::Path& res, Geom::Curve const* current, double width) +{ + double const tolerance = 0.0025; + size_t levels = 8; + + // TODO: we can handle SVGEllipticalArc here as well, do that! + + if (Geom::BezierCurve const *b = dynamic_cast<Geom::BezierCurve const*>(current)) { + size_t order = b->order(); + switch (order) { + case 1: + res.append(offset_line(static_cast<Geom::LineSegment const&>(*current), width)); + break; + case 2: { + Geom::QuadraticBezier const& q = static_cast<Geom::QuadraticBezier const&>(*current); + offset_quadratic(res, q, width, tolerance, levels); + break; + } + case 3: { + Geom::CubicBezier const& cb = static_cast<Geom::CubicBezier const&>(*current); + offset_cubic(res, cb, width, tolerance, levels); + break; + } + default: { + Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), 0.1); + for (size_t i = 0; i < sbasis_path.size(); ++i) + offset_curve(res, &sbasis_path[i], width); + break; + } + } + } else { + Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), 0.1); + for (size_t i = 0; i < sbasis_path.size(); ++i) + offset_curve(res, &sbasis_path[i], width); + } +} + +} + +namespace Inkscape { + +Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join, LineCapType butt) +{ + if (input.size() == 0) return Geom::PathVector(); // nope, don't even try + + Geom::PathBuilder res; + Geom::Path with_dir = half_outline(input, width/2., miter, join); + Geom::Path against_dir = half_outline(input.reverse(), width/2., miter, join); + + res.moveTo(with_dir[0].initialPoint()); + res.append(with_dir); + + // glue caps + if (!input.closed()) { + switch (butt) { + case BUTT_ROUND: + res.arcTo((-width) / 2., (-width) / 2., 0., true, true, against_dir.initialPoint()); + break; + case BUTT_SQUARE: { + Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(input[input.size()-1].toSBasis()), 0.); + double radius = 0.5 * Geom::distance(with_dir.finalPoint(), against_dir.initialPoint()); + res.lineTo(with_dir.finalPoint() + end_deriv*radius); + res.lineTo(against_dir.initialPoint() + end_deriv*radius); + res.lineTo(against_dir.initialPoint()); + break; + } + case BUTT_PEAK: { + Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(input[input.size()-1].toSBasis()), 0.); + double radius = 0.5 * Geom::distance(with_dir.finalPoint(), against_dir.initialPoint()); + Geom::Point midpoint = ((with_dir.finalPoint() + against_dir.initialPoint()) * 0.5) + end_deriv*radius; + res.lineTo(midpoint); + res.lineTo(against_dir.initialPoint()); + break; + } + case BUTT_FLAT: + default: + res.lineTo(against_dir.initialPoint()); + break; + } + } else { + res.moveTo(against_dir.initialPoint()); + } + + res.append(against_dir); + + if (!input.closed()) { + switch(butt) { + case BUTT_ROUND: + res.arcTo((-width) / 2., (-width) / 2., 0., true, true, with_dir.initialPoint()); + break; + case BUTT_SQUARE: { + Geom::Point end_deriv = -input[0].unitTangentAt(0.); + double radius = 0.5 * Geom::distance(against_dir.finalPoint(), with_dir.initialPoint()); + res.lineTo(against_dir.finalPoint() + end_deriv*radius); + res.lineTo(with_dir.initialPoint() + end_deriv*radius); + res.lineTo(with_dir.initialPoint()); + break; + } + case BUTT_PEAK: { + Geom::Point end_deriv = -input[0].unitTangentAt(0.); + double radius = 0.5 * Geom::distance(against_dir.finalPoint(), with_dir.initialPoint()); + Geom::Point midpoint = ((against_dir.finalPoint() + with_dir.initialPoint()) * 0.5) + end_deriv*radius; + res.lineTo(midpoint); + res.lineTo(with_dir.initialPoint()); + break; + } + case BUTT_FLAT: + default: + res.lineTo(with_dir.initialPoint()); + } + res.closePath(); + } + + res.flush(); + return res.peek(); +} + +Geom::Path half_outline(Geom::Path const& input, double width, double miter, LineJoinType join) +{ + Geom::Path res; + if (input.size() == 0) return res; + + Geom::Point tang1 = input[0].unitTangentAt(0); + Geom::Point start = input.initialPoint() + tang1 * width; + Geom::Path temp; + + res.start(start); + + // Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well + const size_t k = input.size(); + for (size_t u = 0; u < k; u += 2) { + temp = Geom::Path(); + + offset_curve(temp, &input[u], width); + + // on the first run through, there isn't a join + if (u == 0) { + res.append(temp); + } else { + outline_helper(res, temp, width, miter, join); + } + + // odd number of paths + if (u < k - 1) { + temp = Geom::Path(); + offset_curve(temp, &input[u+1], width); + outline_helper(res, temp, width, miter, join); + } + } + + if (input.closed()) { + Geom::Curve const &c1 = res[res.size()-1]; + Geom::Curve const &c2 = res[0]; + temp = Geom::Path(); + temp.append(c1); + Geom::Path temp2; + temp2.append(c2); + outline_helper(temp, temp2, width, miter, join); + temp.erase_last(); // we already outlined c2 + temp.erase(temp.begin()); // we already outlined c1 + + // + res.append(temp); + } + + res.close(); + return res; +} + +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8 : diff --git a/src/helper/geom-pathstroke.h b/src/helper/geom-pathstroke.h new file mode 100644 index 000000000..fe79e2777 --- /dev/null +++ b/src/helper/geom-pathstroke.h @@ -0,0 +1,57 @@ +#ifndef INKSCAPE_HELPER_PATH_STROKE_H +#define INKSCAPE_HELPER_PATH_STROKE_H + +/* Author: + * Liam P. White + * + * Copyright (C) 2014-2015 Author + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <2geom/path.h> +#include <2geom/pathvector.h> + +namespace Inkscape { + +enum LineJoinType { + JOIN_BEVEL, + JOIN_ROUND, + JOIN_MITER, + JOIN_EXTRAPOLATE, +}; + +enum LineCapType { + BUTT_FLAT, + BUTT_ROUND, + BUTT_SQUARE, + BUTT_PEAK, // ? +}; + +/** + * Offset the input path by @a width. + * Joins may behave oddly if the width is negative. + * + * @param input + * @param width Amount to offset. + * @param miter Miter limit. Only used with JOIN_EXTRAPOLATE and JOIN_MITER. + * @param join + */ +Geom::Path half_outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL); + +Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL, LineCapType cap = BUTT_FLAT); + +} // namespace Inkscape + +#endif // INKSCAPE_HELPER_PATH_STROKE_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : diff --git a/src/knotholder.cpp b/src/knotholder.cpp index f46daa09e..a2d1cf017 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -165,7 +165,8 @@ KnotHolder::knot_clicked_handler(SPKnot *knot, guint state) if (saved_item) { //increasingly aggressive sanity checks if (saved_item->document) { - if (object_verb <= SP_VERB_LAST && object_verb >= SP_VERB_INVALID) { + // enum is unsigned so can't be less than SP_VERB_INVALID + if (object_verb <= SP_VERB_LAST) { DocumentUndo::done(saved_item->document, object_verb, _("Change handle")); } diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp index bf2526986..291337da0 100644 --- a/src/live_effects/lpe-jointype.cpp +++ b/src/live_effects/lpe-jointype.cpp @@ -8,7 +8,7 @@ */ #include "live_effects/parameter/enum.h" -#include "live_effects/pathoutlineprovider.h" +#include "helper/geom-pathstroke.h" #include "sp-shape.h" #include "style.h" @@ -28,19 +28,18 @@ namespace Inkscape { namespace LivePathEffect { static const Util::EnumData<unsigned> JoinTypeData[] = { - {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"}, - {LINEJOIN_ROUND, N_("Rounded"), "round"}, - {LINEJOIN_POINTY, N_("Miter"), "miter"}, - {LINEJOIN_REFLECTED, N_("Reflected"), "extrapolated"}, - {LINEJOIN_EXTRAPOLATED, N_("Extrapolated arc"), "extrp_arc"} + {JOIN_BEVEL, N_("Beveled"), "bevel"}, + {JOIN_ROUND, N_("Rounded"), "round"}, + {JOIN_MITER, N_("Miter"), "miter"}, + {JOIN_EXTRAPOLATE, N_("Extrapolated arc"), "extrp_arc"}, }; static const Util::EnumData<unsigned> CapTypeData[] = { - {BUTT_STRAIGHT, N_("Butt"), "butt"}, + {BUTT_FLAT, N_("Butt"), "butt"}, {BUTT_ROUND, N_("Rounded"), "round"}, {BUTT_SQUARE, N_("Square"), "square"}, - {BUTT_POINTY, N_("Peak"), "peak"}, - {BUTT_LEANED, N_("Leaned"), "leaned"} + {BUTT_PEAK, N_("Peak"), "peak"}, + //{BUTT_LEANED, N_("Leaned"), "leaned"} }; static const Util::EnumDataConverter<unsigned> CapTypeConverter(CapTypeData, sizeof(CapTypeData)/sizeof(*CapTypeData)); @@ -50,9 +49,9 @@ LPEJoinType::LPEJoinType(LivePathEffectObject *lpeobject) : Effect(lpeobject), line_width(_("Line width"), _("Thickness of the stroke"), "line_width", &wr, this, 1.), linecap_type(_("Line cap"), _("The end shape of the stroke"), "linecap_type", CapTypeConverter, &wr, this, butt_straight), - linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED), - start_lean(_("Start path lean"), _("Start path lean"), "start_lean", &wr, this, 0.), - end_lean(_("End path lean"), _("End path lean"), "end_lean", &wr, this, 0.), + linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", JoinTypeConverter, &wr, this, JOIN_EXTRAPOLATE), + //start_lean(_("Start path lean"), _("Start path lean"), "start_lean", &wr, this, 0.), + //end_lean(_("End path lean"), _("End path lean"), "end_lean", &wr, this, 0.), miter_limit(_("Miter limit:"), _("Maximum length of the miter join (in units of stroke width)"), "miter_limit", &wr, this, 100.), attempt_force_join(_("Force miter"), _("Overrides the miter limit and forces a join."), "attempt_force_join", &wr, this, true) { @@ -60,17 +59,17 @@ LPEJoinType::LPEJoinType(LivePathEffectObject *lpeobject) : registerParameter(&linecap_type); registerParameter(&line_width); registerParameter(&linejoin_type); - registerParameter(&start_lean); - registerParameter(&end_lean); + //registerParameter(&start_lean); + //registerParameter(&end_lean); registerParameter(&miter_limit); registerParameter(&attempt_force_join); was_initialized = false; - start_lean.param_set_range(-1,1); - start_lean.param_set_increments(0.1, 0.1); - start_lean.param_set_digits(4); - end_lean.param_set_range(-1,1); - end_lean.param_set_increments(0.1, 0.1); - end_lean.param_set_digits(4); + //start_lean.param_set_range(-1,1); + //start_lean.param_set_increments(0.1, 0.1); + //start_lean.param_set_digits(4); + //end_lean.param_set_range(-1,1); + //end_lean.param_set_increments(0.1, 0.1); + //end_lean.param_set_digits(4); } LPEJoinType::~LPEJoinType() @@ -164,15 +163,18 @@ void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem) } } -// NOTE: I originally had all the outliner functions defined in here, but they were actually useful -// enough for other LPEs so I moved them all into pathoutlineprovider.cpp. The code here is just a -// wrapper around it. std::vector<Geom::Path> LPEJoinType::doEffect_path(std::vector<Geom::Path> const & path_in) { - return Outline::PathVectorOutline(path_in, line_width, static_cast<ButtTypeMod>(linecap_type.get_value()), - static_cast<LineJoinType>(linejoin_type.get_value()), - (attempt_force_join ? std::numeric_limits<double>::max() : miter_limit), - start_lean/2 ,end_lean/2); + Geom::PathVector ret; + for (size_t i = 0; i < path_in.size(); ++i) { + Geom::PathVector tmp = Inkscape::outline(path_in[i], line_width, + (attempt_force_join ? std::numeric_limits<double>::max() : miter_limit), + static_cast<LineJoinType>(linejoin_type.get_value()), + static_cast<LineCapType>(linecap_type.get_value())); + ret.insert(ret.begin(), tmp.begin(), tmp.end()); + } + + return ret; } } // namespace LivePathEffect diff --git a/src/live_effects/lpe-jointype.h b/src/live_effects/lpe-jointype.h index 73705666d..799901eb6 100644 --- a/src/live_effects/lpe-jointype.h +++ b/src/live_effects/lpe-jointype.h @@ -33,8 +33,8 @@ private: ScalarParam line_width;
EnumParam<unsigned> linecap_type;
EnumParam<unsigned> linejoin_type;
- ScalarParam start_lean;
- ScalarParam end_lean;
+ //ScalarParam start_lean;
+ //ScalarParam end_lean;
ScalarParam miter_limit;
BoolParam attempt_force_join;
bool was_initialized;
diff --git a/src/live_effects/parameter/point.cpp b/src/live_effects/parameter/point.cpp index 4564363db..4c4d2cd9c 100644 --- a/src/live_effects/parameter/point.cpp +++ b/src/live_effects/parameter/point.cpp @@ -141,7 +141,7 @@ PointParam::set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint class PointParamKnotHolderEntity : public KnotHolderEntity { public: PointParamKnotHolderEntity(PointParam *p) { this->pparam = p; } - virtual ~PointParamKnotHolderEntity() {} + virtual ~PointParamKnotHolderEntity() { this->pparam->knoth = NULL;} virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); virtual Geom::Point knot_get() const; diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index 885fedafa..932a3a1b7 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -95,6 +95,10 @@ void SPGenericEllipse::build(SPDocument *document, Inkscape::XML::Node *repr) std::cerr << "SPGenericEllipse::build() unknown defined type." << std::endl; } + // std::cout << " cx: " << cx.write() << std::endl; + // std::cout << " cy: " << cy.write() << std::endl; + // std::cout << " rx: " << rx.write() << std::endl; + // std::cout << " ry: " << ry.write() << std::endl; SPShape::build(document, repr); } @@ -103,29 +107,41 @@ void SPGenericEllipse::set(unsigned int key, gchar const *value) // There are multiple ways to set internal cx, cy, rx, and ry (via SVG attributes or Sodipodi // attributes) thus we don't want to unset them if a read fails (e.g., when we explicitly clear // an attribute by setting it to NULL). + + // We must update the SVGLengths immediately or nodes may be misplaced after they are moved. + double const w = viewport.width(); + double const h = viewport.height(); + double const d = hypot(w, h) / sqrt(2); // diagonal + double const em = style->font_size.computed; + double const ex = em * 0.5; + SVGLength t; switch (key) { case SP_ATTR_CX: case SP_ATTR_SODIPODI_CX: if( t.read(value) ) cx = t; + cx.update( em, ex, w ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_CY: case SP_ATTR_SODIPODI_CY: if( t.read(value) ) cy = t; + cy.update( em, ex, h ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_RX: case SP_ATTR_SODIPODI_RX: - if( t.read(value) && t.value > 0.0 ) this->rx = t; + if( t.read(value) && t.value > 0.0 ) rx = t; + rx.update( em, ex, w ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_RY: case SP_ATTR_SODIPODI_RY: - if( t.read(value) && t.value > 0.0 ) this->ry = t; + if( t.read(value) && t.value > 0.0 ) ry = t; + ry.update( em, ex, h ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -133,6 +149,8 @@ void SPGenericEllipse::set(unsigned int key, gchar const *value) if( t.read(value) && t.value > 0.0 ) { this->ry = this->rx = t; } + rx.update( em, ex, d ); + ry.update( em, ex, d ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -196,7 +214,7 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I // << ")" << std::endl; GenericEllipseType new_type = SP_GENERIC_ELLIPSE_UNDEFINED; - if (this->_isSlice() || hasPathEffect() ) { + if (_isSlice() || hasPathEffect() ) { new_type = SP_GENERIC_ELLIPSE_ARC; } else if ( rx.computed == ry.computed ) { new_type = SP_GENERIC_ELLIPSE_CIRCLE; @@ -246,10 +264,10 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I } // std::cout << " type: " << g_quark_to_string( repr->code() ) << std::endl; - // std::cout << " cx: " << cx.computed - // << " cy: " << cy.computed - // << " rx: " << rx.computed - // << " ry: " << ry.computed << std::endl; + // std::cout << " cx: " << cx.write() << " " << cx.computed + // << " cy: " << cy.write() << " " << cy.computed + // << " rx: " << rx.write() << " " << rx.computed + // << " ry: " << ry.write() << " " << ry.computed << std::endl; switch ( type ) { case SP_GENERIC_ELLIPSE_UNDEFINED: @@ -264,17 +282,17 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I if (flags & SP_OBJECT_WRITE_EXT) { repr->setAttribute("sodipodi:type", "arc"); - sp_repr_set_svg_double(repr, "sodipodi:cx", this->cx.computed); - sp_repr_set_svg_double(repr, "sodipodi:cy", this->cy.computed); - sp_repr_set_svg_double(repr, "sodipodi:rx", this->rx.computed); - sp_repr_set_svg_double(repr, "sodipodi:ry", this->ry.computed); + sp_repr_set_svg_length(repr, "sodipodi:cx", cx); + sp_repr_set_svg_length(repr, "sodipodi:cy", cy); + sp_repr_set_svg_length(repr, "sodipodi:rx", rx); + sp_repr_set_svg_length(repr, "sodipodi:ry", ry); // write start and end only if they are non-trivial; otherwise remove - if (this->_isSlice()) { - sp_repr_set_svg_double(repr, "sodipodi:start", this->start); - sp_repr_set_svg_double(repr, "sodipodi:end", this->end); + if (_isSlice()) { + sp_repr_set_svg_double(repr, "sodipodi:start", start); + sp_repr_set_svg_double(repr, "sodipodi:end", end); - repr->setAttribute("sodipodi:open", (!this->_closed) ? "true" : NULL); + repr->setAttribute("sodipodi:open", (!_closed) ? "true" : NULL); } else { repr->setAttribute("sodipodi:end", NULL); repr->setAttribute("sodipodi:start", NULL); @@ -283,13 +301,13 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I } // write d= - this->set_elliptical_path_attribute(repr); + set_elliptical_path_attribute(repr); break; case SP_GENERIC_ELLIPSE_CIRCLE: - sp_repr_set_svg_double(repr, "cx", this->cx.computed); - sp_repr_set_svg_double(repr, "cy", this->cy.computed); - sp_repr_set_svg_double(repr, "r", this->rx.computed); + sp_repr_set_svg_length(repr, "cx", cx); + sp_repr_set_svg_length(repr, "cy", cy); + sp_repr_set_svg_length(repr, "r", rx); repr->setAttribute("rx", NULL ); repr->setAttribute("ry", NULL ); repr->setAttribute("sodipodi:cx", NULL ); @@ -304,10 +322,10 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I break; case SP_GENERIC_ELLIPSE_ELLIPSE: - sp_repr_set_svg_double(repr, "cx", this->cx.computed); - sp_repr_set_svg_double(repr, "cy", this->cy.computed); - sp_repr_set_svg_double(repr, "rx", this->rx.computed); - sp_repr_set_svg_double(repr, "ry", this->ry.computed); + sp_repr_set_svg_length(repr, "cx", cx); + sp_repr_set_svg_length(repr, "cy", cy); + sp_repr_set_svg_length(repr, "rx", rx); + sp_repr_set_svg_length(repr, "ry", ry); repr->setAttribute("r", NULL ); repr->setAttribute("sodipodi:cx", NULL ); repr->setAttribute("sodipodi:cy", NULL ); @@ -324,11 +342,10 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I std::cerr << "SPGenericEllipse::write: unknown type." << std::endl; } - this->set_shape(); // evaluate SPCurve + set_shape(); // evaluate SPCurve SPShape::write(xml_doc, repr, flags); - // std::cout << "SPGenericEllipse::write: Exit: " << g_quark_to_string(repr->code()) << "\n" << std::endl; return repr; } @@ -339,8 +356,8 @@ const char *SPGenericEllipse::displayName() const case SP_GENERIC_ELLIPSE_UNDEFINED: case SP_GENERIC_ELLIPSE_ARC: - if (this->_isSlice()) { - if (this->_closed) { + if (_isSlice()) { + if (_closed) { return _("Segment"); } else { return _("Arc"); @@ -480,11 +497,11 @@ Geom::Affine SPGenericEllipse::set_transform(Geom::Affine const &xform) } if (this->rx._set) { - this->rx = this->rx.computed * sw; + this->rx.scale( sw ); } if (this->ry._set) { - this->ry = this->ry.computed * sh; + this->ry.scale( sh ); } /* Find start in item coords */ @@ -628,10 +645,10 @@ bool SPGenericEllipse::set_elliptical_path_attribute(Inkscape::XML::Node *repr) void SPGenericEllipse::position_set(gdouble x, gdouble y, gdouble rx, gdouble ry) { - this->cx.computed = x; - this->cy.computed = y; - this->rx.computed = rx; - this->ry.computed = ry; + this->cx = x; + this->cy = y; + this->rx = rx; + this->ry = ry; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 5d181b80a..8c99e9bcf 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -75,18 +75,6 @@ static SPItemView* sp_item_view_list_remove(SPItemView *list, SPItem::SPItem() : SPObject() { - this->sensitive = 0; - this->clip_ref = NULL; - this->avoidRef = NULL; - this->_is_evaluated = false; - this->stop_paint = 0; - this->_evaluated_status = StatusUnknown; - this->bbox_valid = 0; - this->freeze_stroke_width = false; - this->transform_center_x = 0; - this->transform_center_y = 0; - this->display = NULL; - this->mask_ref = NULL; sensitive = TRUE; bbox_valid = FALSE; @@ -96,12 +84,13 @@ SPItem::SPItem() : SPObject() { transform_center_x = 0; transform_center_y = 0; + freeze_stroke_width = false; + _is_evaluated = true; _evaluated_status = StatusUnknown; transform = Geom::identity(); - doc_bbox = Geom::OptRect(); - freeze_stroke_width = false; + // doc_bbox = Geom::OptRect(); display = NULL; @@ -670,54 +659,56 @@ void SPItem::stroke_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) } } -void SPItem::update(SPCtx* /*ctx*/, guint flags) { - SPItem *item = this; - SPItem* object = item; +void SPItem::update(SPCtx* ctx, guint flags) { -// SPObject::onUpdate(ctx, flags); + SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx); - // any of the modifications defined in sp-object.h might change bbox, + // Any of the modifications defined in sp-object.h might change bbox, // so we invalidate it unconditionally - item->bbox_valid = FALSE; + bbox_valid = FALSE; + + viewport = ictx->viewport; // Cache viewport - if (flags & (SP_OBJECT_CHILD_MODIFIED_FLAG | SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG)) { + if (flags & (SP_OBJECT_CHILD_MODIFIED_FLAG | + SP_OBJECT_MODIFIED_FLAG | + SP_OBJECT_STYLE_MODIFIED_FLAG) ) { if (flags & SP_OBJECT_MODIFIED_FLAG) { - for (SPItemView *v = item->display; v != NULL; v = v->next) { - v->arenaitem->setTransform(item->transform); + for (SPItemView *v = display; v != NULL; v = v->next) { + v->arenaitem->setTransform(transform); } } - SPClipPath *clip_path = item->clip_ref ? item->clip_ref->getObject() : NULL; - SPMask *mask = item->mask_ref ? item->mask_ref->getObject() : NULL; + SPClipPath *clip_path = clip_ref ? clip_ref->getObject() : NULL; + SPMask *mask = mask_ref ? mask_ref->getObject() : NULL; if ( clip_path || mask ) { - Geom::OptRect bbox = item->geometricBounds(); + Geom::OptRect bbox = geometricBounds(); if (clip_path) { - for (SPItemView *v = item->display; v != NULL; v = v->next) { + for (SPItemView *v = display; v != NULL; v = v->next) { clip_path->setBBox(v->arenaitem->key(), bbox); } } if (mask) { - for (SPItemView *v = item->display; v != NULL; v = v->next) { + for (SPItemView *v = display; v != NULL; v = v->next) { mask->sp_mask_set_bbox(v->arenaitem->key(), bbox); } } } if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { - for (SPItemView *v = item->display; v != NULL; v = v->next) { - v->arenaitem->setOpacity(SP_SCALE24_TO_FLOAT(object->style->opacity.value)); - v->arenaitem->setAntialiasing(object->style->shape_rendering.computed != SP_CSS_SHAPE_RENDERING_CRISPEDGES); - v->arenaitem->setIsolation( object->style->isolation.value ); - v->arenaitem->setBlendMode( object->style->mix_blend_mode.value ); - v->arenaitem->setVisible(!item->isHidden()); + for (SPItemView *v = display; v != NULL; v = v->next) { + v->arenaitem->setOpacity(SP_SCALE24_TO_FLOAT(style->opacity.value)); + v->arenaitem->setAntialiasing(style->shape_rendering.computed != SP_CSS_SHAPE_RENDERING_CRISPEDGES); + v->arenaitem->setIsolation( style->isolation.value ); + v->arenaitem->setBlendMode( style->mix_blend_mode.value ); + v->arenaitem->setVisible(!isHidden()); } } } /* Update bounding box in user space, used for filter and objectBoundingBox units */ - if (item->style->filter.set && item->display) { - Geom::OptRect item_bbox = item->geometricBounds(); - SPItemView *itemview = item->display; + if (style->filter.set && display) { + Geom::OptRect item_bbox = geometricBounds(); + SPItemView *itemview = display; do { if (itemview->arenaitem) itemview->arenaitem->setItemBounds(item_bbox); @@ -725,8 +716,8 @@ void SPItem::update(SPCtx* /*ctx*/, guint flags) { } // Update libavoid with item geometry (for connector routing). - if (item->avoidRef) - item->avoidRef->handleSettingChange(); + if (avoidRef) + avoidRef->handleSettingChange(); } void SPItem::modified(unsigned int /*flags*/) diff --git a/src/sp-item.h b/src/sp-item.h index b9d71c551..bdc6a0ad9 100644 --- a/src/sp-item.h +++ b/src/sp-item.h @@ -145,6 +145,7 @@ public: Geom::Affine transform; mutable Geom::OptRect doc_bbox; + Geom::Rect viewport; // Cache viewport information SPClipPathReference *clip_ref; SPMaskReference *mask_ref; diff --git a/src/sp-rect.cpp b/src/sp-rect.cpp index 0128c248f..e17d7373c 100644 --- a/src/sp-rect.cpp +++ b/src/sp-rect.cpp @@ -52,22 +52,30 @@ void SPRect::build(SPDocument* doc, Inkscape::XML::Node* repr) { void SPRect::set(unsigned key, gchar const *value) { /* fixme: We need real error processing some time */ + // We must update the SVGLengths immediately or nodes may be misplaced after they are moved. + double const w = viewport.width(); + double const h = viewport.height(); + double const em = style->font_size.computed; + double const ex = em * 0.5; + switch (key) { case SP_ATTR_X: - this->x.readOrUnset(value); - this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + this->x.readOrUnset(value); + this->x.update( ex, em, w ); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_Y: - this->y.readOrUnset(value); - this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + this->y.readOrUnset(value); + this->y.update( ex, em, h ); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_WIDTH: if (!this->width.read(value) || this->width.value < 0.0) { this->width.unset(); } - + this->width.update( ex, em, w ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -75,7 +83,7 @@ void SPRect::set(unsigned key, gchar const *value) { if (!this->height.read(value) || this->height.value < 0.0) { this->height.unset(); } - + this->height.update( ex, em, h ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -83,7 +91,7 @@ void SPRect::set(unsigned key, gchar const *value) { if (!this->rx.read(value) || this->rx.value <= 0.0) { this->rx.unset(); } - + this->rx.update( ex, em, w ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -91,7 +99,7 @@ void SPRect::set(unsigned key, gchar const *value) { if (!this->ry.read(value) || this->ry.value <= 0.0) { this->ry.unset(); } - + this->ry.update( ex, em, h ); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; diff --git a/src/ui/object-edit.cpp b/src/ui/object-edit.cpp index 04ee13cef..0a6c792dc 100644 --- a/src/ui/object-edit.cpp +++ b/src/ui/object-edit.cpp @@ -903,10 +903,10 @@ ArcKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origi Geom::Point const s = snap_knot_position(p, state); - ge->rx.computed = fabs( ge->cx.computed - s[Geom::X] ); + ge->rx = fabs( ge->cx.computed - s[Geom::X] ); if ( state & GDK_CONTROL_MASK ) { - ge->ry.computed = ge->rx.computed; + ge->ry = ge->rx.computed; } item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); @@ -928,7 +928,7 @@ ArcKnotHolderEntityRX::knot_click(unsigned int state) g_assert(ge != NULL); if (state & GDK_CONTROL_MASK) { - ge->ry.computed = ge->rx.computed; + ge->ry = ge->rx.computed; ge->updateRepr(); } } @@ -941,10 +941,10 @@ ArcKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origi Geom::Point const s = snap_knot_position(p, state); - ge->ry.computed = fabs( ge->cy.computed - s[Geom::Y] ); + ge->ry = fabs( ge->cy.computed - s[Geom::Y] ); if ( state & GDK_CONTROL_MASK ) { - ge->rx.computed = ge->ry.computed; + ge->rx = ge->ry.computed; } item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); @@ -966,7 +966,7 @@ ArcKnotHolderEntityRY::knot_click(unsigned int state) g_assert(ge != NULL); if (state & GDK_CONTROL_MASK) { - ge->rx.computed = ge->ry.computed; + ge->rx = ge->ry.computed; ge->updateRepr(); } } diff --git a/src/ui/tools/box3d-tool.h b/src/ui/tools/box3d-tool.h index 1dd6bb5f8..33ae6d8e7 100644 --- a/src/ui/tools/box3d-tool.h +++ b/src/ui/tools/box3d-tool.h @@ -80,7 +80,7 @@ private: Proj::Pt3 drag_ptC_proj; bool ctrl_dragged; /* whether we are ctrl-dragging */ - bool extruded; /* whether shift-dragging already occured (i.e. the box is already extruded) */ + bool extruded; /* whether shift-dragging already occurred (i.e. the box is already extruded) */ sigc::connection sel_changed_connection; diff --git a/src/ui/tools/connector-tool.cpp b/src/ui/tools/connector-tool.cpp index d76b0d142..c3ef19507 100644 --- a/src/ui/tools/connector-tool.cpp +++ b/src/ui/tools/connector-tool.cpp @@ -49,7 +49,7 @@ * Much of the way connectors work for user-defined points has been * changed so that it no longer defines special attributes to record * the points. Instead it uses single node paths to define points - * who are then seperate objects that can be fixed on the canvas, + * who are then separate objects that can be fixed on the canvas, * grouped into objects and take full advantage of all transform, snap * and align functionality of all other objects. * diff --git a/src/ui/tools/dynamic-base.h b/src/ui/tools/dynamic-base.h index c948fa286..095af8f88 100644 --- a/src/ui/tools/dynamic-base.h +++ b/src/ui/tools/dynamic-base.h @@ -47,7 +47,7 @@ protected: /** accumulated shape which ultimately goes in svg:path */ SPCurve *accumulated; - /** canvas items for "comitted" segments */ + /** canvas items for "committed" segments */ GSList *segments; /** canvas item for red "leading" segment */ diff --git a/src/ui/tools/gradient-tool.cpp b/src/ui/tools/gradient-tool.cpp index 5be84eb76..b27859ebb 100644 --- a/src/ui/tools/gradient-tool.cpp +++ b/src/ui/tools/gradient-tool.cpp @@ -321,7 +321,7 @@ sp_gradient_context_add_stops_between_selected_stops (GradientTool *rc) if (d->point_type == POINT_RG_FOCUS) { /* * There are 2 draggables at the center (start) of a radial gradient - * To avoid creating 2 seperate stops, ignore this draggable point type + * To avoid creating 2 separate stops, ignore this draggable point type */ continue; } diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp index f8045a029..a4b903960 100644 --- a/src/ui/tools/node-tool.cpp +++ b/src/ui/tools/node-tool.cpp @@ -75,7 +75,7 @@ * - ControlPointSelection: keeps track of node selection and a set of nodes that can potentially * be selected. There can be more than one selection. Performs actions that require no * knowledge about the path, only about the nodes, like dragging and transforms. It is not - * specific to nodes and can accomodate any control point derived from SelectableControlPoint. + * specific to nodes and can accommodate any control point derived from SelectableControlPoint. * Transforms nodes in response to transform handle events. * - TransformHandleSet: displays nodeset transform handles and emits transform events. The aim * is to eventually use a common class for object and control point transforms. diff --git a/src/ui/tools/pen-tool.cpp b/src/ui/tools/pen-tool.cpp index d28b7c27a..d5f501163 100644 --- a/src/ui/tools/pen-tool.cpp +++ b/src/ui/tools/pen-tool.cpp @@ -2143,7 +2143,7 @@ bool PenTool::_undoLastPoint() { this->p[1] = this->p[0]; } - // asign the value in a third of the distance of the last segment. + // assign the value in a third of the distance of the last segment. if (this->bspline){ this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]); } @@ -2165,7 +2165,7 @@ bool PenTool::_undoLastPoint() { this->green_curve->backspace(); } - // assign the value of this->p[1] to the oposite of the green line last segment + // assign the value of this->p[1] to the opposite of the green line last segment if (this->spiro){ Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(this->green_curve->last_segment()); if ( cubic ) { diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp index ec7d10e13..0399b1e55 100644 --- a/src/ui/tools/spray-tool.cpp +++ b/src/ui/tools/spray-tool.cpp @@ -406,7 +406,7 @@ static bool sp_spray_recursive(SPDesktop *desktop, parent->appendChild(copy); SPObject *new_obj = doc->getObjectByRepr(copy); - item_copied = dynamic_cast<SPItem *>(new_obj); // Convertion object->item + item_copied = dynamic_cast<SPItem *>(new_obj); // Conversion object->item Geom::Point center=item->getCenter(); sp_spray_scale_rel(center,desktop, item_copied, Geom::Scale(_scale,_scale)); sp_spray_scale_rel(center,desktop, item_copied, Geom::Scale(scale,scale)); diff --git a/src/ui/widget/page-sizer.cpp b/src/ui/widget/page-sizer.cpp index afece6df2..8e647ebb4 100644 --- a/src/ui/widget/page-sizer.cpp +++ b/src/ui/widget/page-sizer.cpp @@ -784,7 +784,6 @@ PageSizer::updateScaleUI() _scaleLabel.set_text( "Unknown scale" ); } - sleep( 0.1 ); _changeds_connection.unblock(); _called = false; |
