diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2015-03-28 13:57:30 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx.marker.es> | 2015-03-28 13:57:30 +0000 |
| commit | d3117487bcf047050642b80a3f46acd811947105 (patch) | |
| tree | 2e1d204fefe18c4d2e94053f71830a18bc4590e1 /src | |
| parent | adding rotation transform (diff) | |
| parent | Fix calculation of miter limit. Implement 'miter-clip' line join. (diff) | |
| download | inkscape-d3117487bcf047050642b80a3f46acd811947105.tar.gz inkscape-d3117487bcf047050642b80a3f46acd811947105.zip | |
update to trunk
(bzr r13708.1.21)
Diffstat (limited to 'src')
| -rw-r--r-- | src/document.cpp | 11 | ||||
| -rw-r--r-- | src/document.h | 1 | ||||
| -rw-r--r-- | src/helper/geom-pathstroke.cpp | 442 | ||||
| -rw-r--r-- | src/helper/geom-pathstroke.h | 3 | ||||
| -rw-r--r-- | src/live_effects/lpe-bspline.cpp | 44 | ||||
| -rw-r--r-- | src/live_effects/lpe-bspline.h | 3 | ||||
| -rw-r--r-- | src/live_effects/lpe-jointype.cpp | 13 | ||||
| -rw-r--r-- | src/live_effects/lpe-jointype.h | 1 | ||||
| -rw-r--r-- | src/live_effects/lpe-perspective-envelope.cpp | 12 | ||||
| -rw-r--r-- | src/live_effects/lpe-taperstroke.cpp | 116 | ||||
| -rw-r--r-- | src/svg-profile.h | 2 | ||||
| -rw-r--r-- | src/ui/dialog/filter-effects-dialog.cpp | 2 | ||||
| -rw-r--r-- | src/ui/tool/curve-drag-point.cpp | 4 | ||||
| -rw-r--r-- | src/ui/tool/multi-path-manipulator.h | 2 | ||||
| -rw-r--r-- | src/ui/tool/node.cpp | 15 | ||||
| -rw-r--r-- | src/ui/tool/path-manipulator.cpp | 53 | ||||
| -rw-r--r-- | src/ui/tool/path-manipulator.h | 2 | ||||
| -rw-r--r-- | src/ui/widget/page-sizer.cpp | 169 | ||||
| -rw-r--r-- | src/ui/widget/page-sizer.h | 21 | ||||
| -rw-r--r-- | src/uri-references.cpp | 2 |
20 files changed, 561 insertions, 357 deletions
diff --git a/src/document.cpp b/src/document.cpp index 62e2f5b46..1c6bb76de 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -764,6 +764,17 @@ void SPDocument::setHeight(const Inkscape::Util::Quantity &height, bool changeSi root->updateRepr(); } +Geom::Rect SPDocument::getViewBox() const +{ + Geom::Rect viewBox; + if (root->viewBox_set) { + viewBox = root->viewBox; + } else { + viewBox = Geom::Rect::from_xywh( 0, 0, getWidth().value("px"), getHeight().value("px")); + } + return viewBox; +} + void SPDocument::setViewBox(const Geom::Rect &viewBox) { root->viewBox_set = true; diff --git a/src/document.h b/src/document.h index ed19d123b..74dcfa75e 100644 --- a/src/document.h +++ b/src/document.h @@ -247,6 +247,7 @@ public: Geom::Scale getDocumentScale() const; Inkscape::Util::Quantity getWidth() const; Inkscape::Util::Quantity getHeight() const; + Geom::Rect getViewBox() const; Geom::Point getDimensions() const; Geom::OptRect preferredBounds() const; void setWidthAndHeight(const Inkscape::Util::Quantity &width, const Inkscape::Util::Quantity &height, bool changeSize=true); diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp index 1b8f90104..1908f2db7 100644 --- a/src/helper/geom-pathstroke.cpp +++ b/src/helper/geom-pathstroke.cpp @@ -6,11 +6,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <iomanip> #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 <2geom/path-intersection.h> #include "helper/geom-pathstroke.h" @@ -18,6 +20,74 @@ namespace Geom { // 2geom/circle-circle.cpp, no header int circle_circle_intersection(Point X0, double r0, Point X1, double r1, Point &p0, Point &p1); +/** + * Determine the intersection points between a circle C0 and a line defined + * by two points, X0 and X1. + * + * Which intersection point is assigned to p0 or p1 is unspecified, and callers + * should not depend on any particular intersection always being assigned to p0. + * + * Returns: + * If the line and circle do not cross, 0 is returned. + * If solution(s) exist, 2 is returned, and the results are written to p0 and p1. + */ +static int circle_line_intersection(Circle C0, Point X0, Point X1, Point &p0, Point &p1) +{ + /* equation of a circle: (x - h)^2 + (y - k)^2 = r^2 */ + Coord r = C0.ray(); + Coord h = C0.center()[X]; + Coord k = C0.center()[Y]; + + Coord x0, y0; + Coord x1, y1; + + if (are_near(X1[X], X0[X])) { + /* slope is undefined (vertical line) */ + Coord c = X0[X]; + Coord det = r*r - (c-h)*(c-h); + + /* no intersection */ + if (det < 0) + return 0; + + /* solve for y */ + y0 = k + std::sqrt(det); + y1 = k - std::sqrt(det); + + // x == c (always) + x0 = c; + x1 = c; + } else { + /* equation of a line: y = mx + b */ + Coord m = (X1[Y] - X0[Y]) / (X1[X] - X0[X]); + Coord b = X0[Y] - m*X0[X]; + + /* obtain quadratic for x: */ + Coord A = m*m + 1; + Coord B = 2*h - 2*b*m + 2*k*m; + Coord C = b*b + h*h + k*k - r*r - 2*b*k; + + Coord det = B*B - 4*A*C; + + /* no intersection, circle and line do not cross */ + if (det < 0) + return 0; + + /* solve quadratic */ + x0 = (B + std::sqrt(det)) / (2*A); + x1 = (B - std::sqrt(det)) / (2*A); + + /* substitute the calculated x times to determine the y values */ + y0 = m*x0 + b; + y1 = m*x1 + b; + } + + p0 = Point(x0, y0); + p1 = Point(x1, y1); + + return 2; +} + static Point intersection_point(Point origin_a, Point vector_a, Point origin_b, Point vector_b) { Coord denom = cross(vector_b, vector_a); @@ -57,122 +127,221 @@ static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.0 namespace { +// Join functions may: +// - inspect any curve of the current path +// - append any type of curve to the current path +// - inspect the outgoing path +// +// Join functions must: +// - append the outgoing curve +// OR +// - end at outgoing.finalPoint + 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()); + res.append(outgoing); } 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()); + res.append(outgoing); } -void miter_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) +void miter_join_internal(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width, bool clip) { 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); + + bool satisfied = false; + 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) { + Geom::Point point_on_path = incoming.finalPoint() + Geom::rot90(tang1)*width; + satisfied = Geom::distance(p, point_on_path) <= miter * 2.0 * width; + if (satisfied) { // 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 {*/ + bool ls = res.back_open().degreesOfFreedom() <= 4; + if (ls) { + res.setFinal(p); + } else { res.appendNew<Geom::LineSegment>(p); - //} + } + } else if (clip) { + // miter needs clipping, find two points + Geom::Line bisector(point_on_path, p); + Geom::Point point_limit = point_on_path + miter * 2.0 * width * bisector.versor(); + + Geom::Line line_limit = + Geom::Line::from_origin_and_versor( point_limit, bisector.versor().cw() ); + + Geom::Line incoming_line( incoming.finalPoint(), p ); + Geom::Line outgoing_line( p, outgoing.initialPoint() ); + + Geom::OptCrossing i1 = intersection( line_limit, incoming_line ); + Geom::OptCrossing i2 = intersection( line_limit, outgoing_line ); + + // It would be nice to have a simple point returned by intersection! + Geom::Point p1 = line_limit.pointAt( (*i1).ta ); + Geom::Point p2 = line_limit.pointAt( (*i2).ta ); + + bool ls = res.back_open().degreesOfFreedom() <= 4; + if (ls) { + res.setFinal(p1); + } else { + res.appendNew<Geom::LineSegment>(p1); + } + res.appendNew<Geom::LineSegment>(p2); } } + res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); + + // check if we can do another relocation + bool ls = outgoing.degreesOfFreedom() <= 4; + + if ( (satisfied || clip) && ls) { + res.setFinal(outgoing.finalPoint()); + } else { + res.append(outgoing); + } +} + +void miter_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) { + miter_join_internal( res, outgoing, miter, width, false ); +} + +void miter_clip_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) { + miter_join_internal( res, outgoing, miter, width, true ); +} + +Geom::Point pick_solution(Geom::Point points[2], Geom::Point tang2, Geom::Point endPt) +{ + Geom::Point sol; + 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]; + } + return sol; } -// 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(); + 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); - // 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); + bool inc_ls = !circle1.center().isFinite(); + bool out_ls = !circle2.center().isFinite(); - Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.); - Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0); + Geom::Point points[2]; - Geom::Point points[2]; - int solutions = Geom::circle_circle_intersection(circle1.center(), circle1.ray(), - circle2.center(), circle2.ray(), - points[0], points[1]); + int solutions = 0; + Geom::EllipticalArc *arc0 = NULL; + Geom::EllipticalArc *arc1 = NULL; + + if (!inc_ls && !out_ls) { + 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::Point sol = pick_solution(points, tang2, endPt); - 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); + arc0 = circle1.arc(incoming.finalPoint(), 0.5*(incoming.finalPoint()+sol), sol, true); + arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); + } + } else if (inc_ls && !out_ls) { + solutions = Geom::circle_line_intersection(circle2, incoming.initialPoint(), incoming.finalPoint(), points[0], points[1]); + + if (solutions == 2) { + Geom::Point sol = pick_solution(points, tang2, endPt); + path_builder.setFinal(sol); + arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); + } + } else if (!inc_ls && out_ls) { + solutions = Geom::circle_line_intersection(circle1, outgoing.initialPoint(), outgoing.finalPoint(), points[0], points[1]); + + if (solutions == 2) { + Geom::Point sol = pick_solution(points, tang2, endPt); + arc0 = circle1.arc(incoming.finalPoint(), 0.5*(sol+incoming.finalPoint()), sol, true); } - } else { - // Line segments exist - miter_join(path_builder, outgoing, miter_limit, line_width); } + + if (solutions != 2) + // no solutions available, fall back to miter + return miter_join(path_builder, outgoing, miter_limit, line_width); + + if (arc0) + path_builder.append(*arc0); + if (arc1) + path_builder.append(*arc1); + + delete arc0; + delete arc1; + + if (!inc_ls && out_ls) + path_builder.appendNew<Geom::LineSegment>(outgoing.finalPoint()); + else + path_builder.append(outgoing); } void join_inside(Geom::Path& res, Geom::Curve const& outgoing) { - res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); + Geom::Curve const& incoming = res.back_open(); + Geom::Crossings cross = Geom::crossings(incoming, outgoing); + + if (!cross.empty()) { + // yeah if we could avoid allocing that'd be great + Geom::Curve *d1 = incoming.portion(0., cross[0].ta); + res.erase_last(); + res.append(*d1); + delete d1; + + Geom::Curve *d2 = outgoing.portion(cross[0].tb, 1.); + res.setFinal(d2->initialPoint()); + res.append(*d2); + delete d2; + } else { + res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); + res.append(outgoing); + } } 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 (res.size() == 0 || to_add.size() == 0) + return; + + Geom::Curve const& outgoing = to_add[0]; + if (Geom::are_near(res.finalPoint(), outgoing.initialPoint())) { + // if the points are /that/ close, just ignore this one + res.setFinal(outgoing.initialPoint()); + res.append(outgoing); + return; + } + + Geom::Point tang1 = Geom::unitTangentAt(reverse(res.back().toSBasis()), 0.); + Geom::Point tang2 = Geom::unitTangentAt(to_add.front().toSBasis(), 0.); + // Geom::Point discontinuity_vec = to_add.initialPoint() - res.finalPoint(); + bool on_outside = (Geom::cross(tang1, tang2) < 0); + // std::cout << std::fixed << std::setprecision(3) + // << " in: " << tang1 << " out: " << tang2 + // << " side: " << (on_outside?"inside":"outside") << std::endl; if (on_outside) { join_func *jf; @@ -186,15 +355,16 @@ void outline_helper(Geom::Path& res, Geom::Path const& to_add, double width, dou case Inkscape::JOIN_EXTRAPOLATE: jf = &extrapolate_join; break; + case Inkscape::JOIN_MITER_CLIP: + jf = &miter_clip_join; + break; default: jf = &miter_join; } - jf(res, to_add[0], miter, width); + jf(res, outgoing, miter, width); } else { - join_inside(res, to_add[0]); + join_inside(res, outgoing); } - - res.append(to_add); } // Offsetting a line segment is mathematically stable and quick to do @@ -324,9 +494,11 @@ void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double wi void offset_curve(Geom::Path& res, Geom::Curve const* current, double width) { - double const tolerance = 0.0025; + double const tolerance = 0.005; size_t levels = 8; + if (current->isDegenerate()) return; // don't do anything + // TODO: we can handle SVGEllipticalArc here as well, do that! if (Geom::BezierCurve const *b = dynamic_cast<Geom::BezierCurve const*>(current)) { @@ -346,7 +518,7 @@ void offset_curve(Geom::Path& res, Geom::Curve const* current, double width) break; } default: { - Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), 0.1); + Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), tolerance); for (size_t i = 0; i < sbasis_path.size(); ++i) offset_curve(res, &sbasis_path[i], width); break; @@ -359,8 +531,40 @@ void offset_curve(Geom::Path& res, Geom::Curve const* current, double width) } } +typedef void cap_func(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width); + +void flat_cap(Geom::PathBuilder& res, Geom::Path const&, Geom::Path const& against_dir, double) +{ + res.lineTo(against_dir.initialPoint()); +} + +void round_cap(Geom::PathBuilder& res, Geom::Path const&, Geom::Path const& against_dir, double width) +{ + res.arcTo(width / 2., width / 2., 0., true, false, against_dir.initialPoint()); +} + +void square_cap(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width) +{ + width /= 2.; + Geom::Point normal_1 = -Geom::unitTangentAt(Geom::reverse(with_dir.back().toSBasis()), 0.); + Geom::Point normal_2 = -against_dir[0].unitTangentAt(0.); + res.lineTo(with_dir.finalPoint() + normal_1*width); + res.lineTo(against_dir.initialPoint() + normal_2*width); + res.lineTo(against_dir.initialPoint()); +} + +void peak_cap(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path const& against_dir, double width) +{ + width /= 2.; + Geom::Point normal_1 = -Geom::unitTangentAt(Geom::reverse(with_dir.back().toSBasis()), 0.); + Geom::Point normal_2 = -against_dir[0].unitTangentAt(0.); + Geom::Point midpoint = ((with_dir.finalPoint() + normal_1*width) + (against_dir.initialPoint() + normal_2*width)) * 0.5; + res.lineTo(midpoint); + res.lineTo(against_dir.initialPoint()); } +} // namespace + namespace Inkscape { Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join, LineCapType butt) @@ -374,67 +578,36 @@ Geom::PathVector outline(Geom::Path const& input, double width, double miter, Li res.moveTo(with_dir[0].initialPoint()); res.append(with_dir); + cap_func *cf; + switch (butt) { + case BUTT_ROUND: + cf = &round_cap; + break; + case BUTT_SQUARE: + cf = &square_cap; + break; + case BUTT_PEAK: + cf = &peak_cap; + break; + default: + cf = &flat_cap; + } + // glue caps if (!input.closed()) { - switch (butt) { - case BUTT_ROUND: - res.arcTo(width / 2., width / 2., 0., true, false, 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; - } + cf(res, with_dir, against_dir, width); } else { + res.closePath(); 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, false, 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(); + cf(res, against_dir, with_dir, width); } + res.closePath(); res.flush(); return res.peek(); } @@ -451,7 +624,8 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin 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_default(); + const size_t k = (input.back_closed().isDegenerate() && input.closed()) + ?input.size_default()-1:input.size_default(); for (size_t u = 0; u < k; u += 2) { temp = Geom::Path(); @@ -462,6 +636,8 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin res.append(temp); } else { outline_helper(res, temp, width, miter, join); + if (temp.size() > 0) + res.insert(res.end(), ++temp.begin(), temp.end()); } // odd number of paths @@ -469,15 +645,12 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin temp = Geom::Path(); offset_curve(temp, &input[u+1], width); outline_helper(res, temp, width, miter, join); + if (temp.size() > 0) + res.insert(res.end(), ++temp.begin(), temp.end()); } } if (input.closed()) { - if (input.back_closed().isDegenerate()) { - res.erase_last(); - res.erase_last(); // ? - } - Geom::Curve const &c1 = res.back(); Geom::Curve const &c2 = res.front(); temp = Geom::Path(); @@ -485,9 +658,8 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin 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.erase(res.begin()); + res.erase_last(); // res.append(temp); res.close(); diff --git a/src/helper/geom-pathstroke.h b/src/helper/geom-pathstroke.h index fe79e2777..0cfb9f817 100644 --- a/src/helper/geom-pathstroke.h +++ b/src/helper/geom-pathstroke.h @@ -18,6 +18,7 @@ enum LineJoinType { JOIN_BEVEL, JOIN_ROUND, JOIN_MITER, + JOIN_MITER_CLIP, JOIN_EXTRAPOLATE, }; @@ -34,7 +35,7 @@ enum LineCapType { * * @param input * @param width Amount to offset. - * @param miter Miter limit. Only used with JOIN_EXTRAPOLATE and JOIN_MITER. + * @param miter Miter limit. Only used with JOIN_MITER, JOIN_MITER_CLIP, and JOIN_EXTRAPOLATE. * @param join */ Geom::Path half_outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL); diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp index b924d8a23..dba501f42 100644 --- a/src/live_effects/lpe-bspline.cpp +++ b/src/live_effects/lpe-bspline.cpp @@ -1,48 +1,16 @@ /* * Released under GNU GPL, read the file 'COPYING' for more information */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - #include <gtkmm.h> - -#if WITH_GLIBMM_2_32 -# include <glibmm/threads.h> -#endif - -#include <glib.h> -#include <glibmm/i18n.h> - - -#include "display/curve.h" -#include <2geom/bezier-curve.h> -#include <2geom/point.h> -#include "helper/geom-curves.h" #include "live_effects/lpe-bspline.h" -#include "live_effects/lpeobject.h" -#include "live_effects/parameter/parameter.h" #include "ui/widget/scalar.h" -#include "xml/repr.h" -#include "svg/svg.h" +#include "display/curve.h" +#include "helper/geom-curves.h" #include "sp-path.h" -#include "style.h" -#include "document-private.h" -#include "document.h" -#include "document-undo.h" -#include "verbs.h" -#include "sp-lpe-item.h" -#include "sp-namedview.h" -#include "display/sp-canvas.h" -#include <typeinfo> -#include <vector> -#include "util/units.h" -// For handling un-continuous paths: -#include "message-stack.h" -#include "inkscape.h" - -using Inkscape::DocumentUndo; +#include "svg/svg.h" +#include "xml/repr.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-bspline.h b/src/live_effects/lpe-bspline.h index 642562b24..8017e39ef 100644 --- a/src/live_effects/lpe-bspline.h +++ b/src/live_effects/lpe-bspline.h @@ -6,9 +6,8 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ - #include "live_effects/effect.h" -#include "live_effects/parameter/bool.h" + #include <vector> namespace Inkscape { diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp index 09869822c..0111a0f99 100644 --- a/src/live_effects/lpe-jointype.cpp +++ b/src/live_effects/lpe-jointype.cpp @@ -28,9 +28,10 @@ namespace Inkscape { namespace LivePathEffect { static const Util::EnumData<unsigned> JoinTypeData[] = { - {JOIN_BEVEL, N_("Beveled"), "bevel"}, - {JOIN_ROUND, N_("Rounded"), "round"}, - {JOIN_MITER, N_("Miter"), "miter"}, + {JOIN_BEVEL, N_("Beveled"), "bevel"}, + {JOIN_ROUND, N_("Rounded"), "round"}, + {JOIN_MITER, N_("Miter"), "miter"}, + {JOIN_MITER_CLIP, N_("Miter Clip"), "miter-clip"}, {JOIN_EXTRAPOLATE, N_("Extrapolated arc"), "extrp_arc"}, }; @@ -63,7 +64,6 @@ LPEJoinType::LPEJoinType(LivePathEffectObject *lpeobject) : //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); @@ -109,10 +109,7 @@ void LPEJoinType::doOnApply(SPLPEItem const* lpeitem) sp_desktop_apply_css_recursive(item, css, true); sp_repr_css_attr_unref (css); - if (!was_initialized) { - was_initialized = true; - line_width.param_set_value(width); - } + line_width.param_set_value(width); } } diff --git a/src/live_effects/lpe-jointype.h b/src/live_effects/lpe-jointype.h index 799901eb6..bca0961c9 100644 --- a/src/live_effects/lpe-jointype.h +++ b/src/live_effects/lpe-jointype.h @@ -37,7 +37,6 @@ private: //ScalarParam end_lean;
ScalarParam miter_limit;
BoolParam attempt_force_join;
- bool was_initialized;
};
} //namespace LivePathEffect
diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp index d60a13c23..b5885bdb3 100644 --- a/src/live_effects/lpe-perspective-envelope.cpp +++ b/src/live_effects/lpe-perspective-envelope.cpp @@ -48,11 +48,11 @@ LPEPerspectiveEnvelope::LPEPerspectiveEnvelope(LivePathEffectObject *lpeobject) Down_Right_Point(_("Down Right"), _("Down Right - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "Down_Right_Point", &wr, this) { // register all your parameters here, so Inkscape knows which parameters this effect has: - registerParameter( dynamic_cast<Parameter *>(&deform_type)); - registerParameter( dynamic_cast<Parameter *>(&Up_Left_Point) ); - registerParameter( dynamic_cast<Parameter *>(&Up_Right_Point) ); - registerParameter( dynamic_cast<Parameter *>(&Down_Left_Point) ); - registerParameter( dynamic_cast<Parameter *>(&Down_Right_Point) ); + registerParameter(&deform_type); + registerParameter(&Up_Left_Point); + registerParameter(&Up_Right_Point); + registerParameter(&Down_Left_Point); + registerParameter(&Down_Right_Point); } LPEPerspectiveEnvelope::~LPEPerspectiveEnvelope() @@ -340,8 +340,8 @@ LPEPerspectiveEnvelope::resetDefaults(SPItem const* item) { Effect::resetDefaults(item); original_bbox(SP_LPE_ITEM(item)); - resetGrid(); setDefaults(); + resetGrid(); } void diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp index d54b2acc0..bdd36df68 100644 --- a/src/live_effects/lpe-taperstroke.cpp +++ b/src/live_effects/lpe-taperstroke.cpp @@ -186,18 +186,8 @@ static Geom::Path return_at_first_cusp(Geom::Path const & path_in, double /*smoo return temp; } -static Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in) -{ - std::vector<Geom::Point> temp; - Geom::sbasis_to_bezier(temp, sbasis_in, 4); - return Geom::CubicBezier( temp ); -} - Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path pattern, double width); -// references to pointers -void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second); - // actual effect Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) @@ -357,85 +347,18 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) */ Geom::PathVector LPETaperStroke::doEffect_simplePath(Geom::PathVector const & path_in) { - size_t size = path_in[0].size(); - - unsigned loc = (unsigned)attach_start; - Geom::Curve * curve_start = path_in[0] [loc].duplicate(); - - std::vector<Geom::Path> pathv_out; - Geom::Path path_out = Geom::Path(); - - Geom::Path trimmed_start = Geom::Path(); - Geom::Path trimmed_end = Geom::Path(); - - for (size_t i = 0; i < loc; ++i) { - trimmed_start.append(path_in[0] [i]); - } - - Geom::Curve * temp; - subdivideCurve(curve_start, attach_start - loc, temp, curve_start); - trimmed_start.append(*temp); - if (temp) delete temp; temp = 0; - - // special case: path is one segment long - // special case: what if the two knots occupy the same segment? - if ((size == 1) || ( size - unsigned(attach_end) - 1 == loc )) { - - // If you look into it, I don't actually think there is a working way to do this - // with only point math. So we use nearest_point instead. - Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_start); - - // it is just a dumb segment - // we have to do some shifting here because the value changed when we reduced the length - // of the previous segment. - - subdivideCurve(curve_start, t, curve_start, temp); - trimmed_end.append(*temp); - if (temp) delete temp; temp = 0; - - for (size_t j = (size - attach_end) + 1; j < size; ++j) { - trimmed_end.append(path_in[0] [j]); - } - - path_out.append(*curve_start); - pathv_out.push_back(trimmed_start); - pathv_out.push_back(path_out); - pathv_out.push_back(trimmed_end); - return pathv_out; - } - - pathv_out.push_back(trimmed_start); + Geom::Coord endTime = path_in[0].size() - attach_end; - // append almost all of the rest of the path, ignore the curves that the knot is past (we'll get to it in a minute) - path_out.append(*curve_start); - - for (size_t k = loc + 1; k < (size - unsigned(attach_end)) - 1; ++k) { - path_out.append(path_in[0] [k]); - } - - // deal with the last segment in a very similar fashion to the first - loc = size - attach_end; - - Geom::Curve * curve_end = path_in[0] [loc].duplicate(); - - Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end); - - subdivideCurve(curve_end, t, curve_end, temp); - trimmed_end.append(*temp); - if (temp) delete temp; temp = 0; - - for (size_t j = (size - attach_end) + 1; j < size; ++j) { - trimmed_end.append(path_in[0] [j]); - } - - path_out.append(*curve_end); - pathv_out.push_back(path_out); - - pathv_out.push_back(trimmed_end); - - if (curve_end) delete curve_end; - if (curve_start) delete curve_start; - return pathv_out; + Geom::Path p1 = path_in[0].portion(0., attach_start); + Geom::Path p2 = path_in[0].portion(attach_start, endTime); + Geom::Path p3 = path_in[0].portion(endTime, path_in[0].size()); + + Geom::PathVector out; + out.push_back(p1); + out.push_back(p2); + out.push_back(p3); + + return out; } @@ -523,23 +446,6 @@ Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path } } -void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second) -{ - if (Geom::LineSegment* linear = dynamic_cast<Geom::LineSegment*>(curve_in)) { - // special case for line segments - std::pair<Geom::LineSegment, Geom::LineSegment> seg_pair = linear->subdivide(t); - val_first = seg_pair.first.duplicate(); - val_second = seg_pair.second.duplicate(); - } else { - // all other cases: - Geom::CubicBezier cubic = sbasis_to_cubicbezier(curve_in->toSBasis()); - std::pair<Geom::CubicBezier, Geom::CubicBezier> cubic_pair = cubic.subdivide(t); - val_first = cubic_pair.first.duplicate(); - val_second = cubic_pair.second.duplicate(); - } -} - - void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this); diff --git a/src/svg-profile.h b/src/svg-profile.h index 9d221fc76..8945ee313 100644 --- a/src/svg-profile.h +++ b/src/svg-profile.h @@ -74,7 +74,7 @@ public: SVG_PRINT_1_1, /**< */ - PROFILE_UNIQUE_CNT, /**< A marker to seperate between the entires + PROFILE_UNIQUE_CNT, /**< A marker to separate between the entires that are unique, and those which are aggregates of them. */ diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 3da0e0043..077c6f5ca 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -211,7 +211,7 @@ private: ComboBoxEnum<T>* combo; }; -// Contains an arbitrary number of spin buttons that use seperate attributes +// Contains an arbitrary number of spin buttons that use separate attributes class MultiSpinButton : public Gtk::HBox { public: diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp index 49c949107..a90dfb155 100644 --- a/src/ui/tool/curve-drag-point.cpp +++ b/src/ui/tool/curve-drag-point.cpp @@ -54,7 +54,7 @@ bool CurveDragPoint::grabbed(GdkEventMotion */*event*/) // delta is a vector equal 1/3 of distance from first to second Geom::Point delta = (second->position() - first->position()) / 3.0; // only update the nodes if the mode is bspline - if(!_pm.isBSpline(false)){ + if(!_pm.isBSpline()){ first->front()->move(first->front()->position() + delta); second->back()->move(second->back()->position() - delta); } @@ -91,7 +91,7 @@ void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event) Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta; //modified so that, if the trace is bspline, it only acts if the SHIFT key is pressed - if(!_pm.isBSpline(false)){ + if(!_pm.isBSpline()){ first->front()->move(first->front()->position() + offset0); second->back()->move(second->back()->position() + offset1); }else if(weight>=0.8){ diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h index cdbf34e9d..1bbcdd7ec 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -39,7 +39,7 @@ public: virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *event); bool empty() { return _mmap.empty(); } - unsigned size() { return _mmap.empty(); } + unsigned size() { return _mmap.size(); } void setItems(std::set<ShapeRecord> const &); void clear() { _mmap.clear(); } void cleanup(); diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index eeea47e4d..08cc6708d 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -192,7 +192,7 @@ void Handle::move(Geom::Point const &new_pos) setRelativePos(new_delta); //move the handler and its oposite the same proportion - if(_pm().isBSpline()){ + if(_pm().isBSpline()){ setPosition(_pm().BSplineHandleReposition(this,this)); this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this)); } @@ -222,7 +222,6 @@ void Handle::move(Geom::Point const &new_pos) setPosition(_pm().BSplineHandleReposition(this,this)); this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this)); } - } void Handle::setPosition(Geom::Point const &p) @@ -382,7 +381,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) std::vector<Inkscape::SnapCandidatePoint> unselected; //if the snap adjustment is activated and it is not bspline - if (snap && !_pm().isBSpline(false)) { + if (snap && !_pm().isBSpline()) { ControlPointSelection::Set &nodes = _parent->_selection.allPoints(); for (ControlPointSelection::Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { Node *n = static_cast<Node*>(*i); @@ -486,7 +485,7 @@ Glib::ustring Handle::_getTip(unsigned state) const char const *more; // a trick to mark as bspline if the node has no strength, we are going to use it later // to show the appropiate messages. We cannot do it in any different way becasue the function is constant - + Handle *h = const_cast<Handle *>(this); bool isBSpline = _pm().isBSpline(); bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate(); if (can_shift_rotate && !isBSpline) { @@ -550,7 +549,7 @@ Glib::ustring Handle::_getTip(unsigned state) const "<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more); }else{ return format_tip(C_("Path handle tip", - "<b>BSpline node handle</b>: Shift to drag, double click to reset (%s)"), more); + "<b>BSpline node handle</b>: Shift to drag, double click to reset (%s). %g power"),more,_pm().BSplineHandlePosition(h,NULL)); } } } @@ -1435,6 +1434,8 @@ Node *Node::nodeAwayFrom(Handle *h) Glib::ustring Node::_getTip(unsigned state) const { bool isBSpline = _pm().isBSpline(); + Handle *h = const_cast<Handle *>(&_front); + Handle *h2 = const_cast<Handle *>(&_back); if (state_held_shift(state)) { bool can_drag_out = (_next() && _front.isDegenerate()) || (_prev() && _back.isDegenerate()); if (can_drag_out) { @@ -1469,7 +1470,7 @@ Glib::ustring Node::_getTip(unsigned state) const "<b>%s</b>: drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype); }else if(_selection.size() == 1){ return format_tip(C_("Path node tip", - "<b>BSpline node</b>: %g weight, drag to shape the path (more: Shift, Ctrl, Alt)"),noPower/*this->bsplineWeight*/); + "<b>BSpline node</b>: drag to shape the path (more: Shift, Ctrl, Alt). %g power"),_pm().BSplineHandlePosition(h,h2)); } return format_tip(C_("Path node tip", "<b>%s</b>: drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype); @@ -1479,7 +1480,7 @@ Glib::ustring Node::_getTip(unsigned state) const "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); }else{ return format_tip(C_("Path node tip", - "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)")); + "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt). %g power"),_pm().BSplineHandlePosition(h,h2)); } } diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index dbcde240a..ccf75dcde 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -11,6 +11,7 @@ */ #include "live_effects/lpe-powerstroke.h" +#include "live_effects/lpe-bspline.h" #include "live_effects/lpe-fillet-chamfer.h" #include <string> #include <sstream> @@ -43,7 +44,6 @@ #include "ui/tool/multi-path-manipulator.h" #include "xml/node.h" #include "xml/node-observer.h" -#include "live_effects/lpe-bspline.h" namespace Inkscape { namespace UI { @@ -151,7 +151,7 @@ PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path, _createControlPointsFromGeometry(); //Define if the path is BSpline on construction - isBSpline(true); + recalculateIsBSpline(); } PathManipulator::~PathManipulator() @@ -1238,19 +1238,24 @@ int PathManipulator::BSplineGetSteps() const { } // determines if the trace has bspline effect -bool PathManipulator::isBSpline(bool recalculate){ - if(recalculate){ - _is_bspline = this->BSplineGetSteps() > 0; +void PathManipulator::recalculateIsBSpline(){ + if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()) { + Inkscape::LivePathEffect::Effect const *thisEffect = _path->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE); + if(thisEffect){ + _is_bspline = true; + return; + } } - return _is_bspline; + _is_bspline = false; } bool PathManipulator::isBSpline() const { - return BSplineGetSteps() > 0; + return _is_bspline; } // returns the corresponding strength to the position of the handlers -double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){ +double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2) +{ using Geom::X; using Geom::Y; if(h2){ @@ -1275,7 +1280,8 @@ double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){ } // give the location for the handler in the corresponding position -Geom::Point PathManipulator::BSplineHandleReposition(Handle *h, Handle *h2){ +Geom::Point PathManipulator::BSplineHandleReposition(Handle *h, Handle *h2) +{ double pos = this->BSplineHandlePosition(h, h2); return BSplineHandleReposition(h,pos); } @@ -1312,7 +1318,7 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) { Geom::PathBuilder builder; //Refresh if is bspline some times -think on path change selection, this value get lost - isBSpline(true); + recalculateIsBSpline(); for (std::list<SubpathPtr>::iterator spi = _subpaths.begin(); spi != _subpaths.end(); ) { SubpathPtr subpath = *spi; if (subpath->empty()) { @@ -1341,22 +1347,19 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) _spcurve->set_pathvector(pathv); if (alert_LPE) { /// \todo note that _path can be an Inkscape::LivePathEffect::Effect* too, kind of confusing, rework member naming? - SPLPEItem * path = dynamic_cast<SPLPEItem *>(_path); - if (path){ - if(path->hasPathEffect()){ - Inkscape::LivePathEffect::Effect* thisEffect = path->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE); - if(thisEffect){ - LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>(thisEffect->getLPEObj()->get_lpe()); - if (lpe_pwr) { - lpe_pwr->adjustForNewPath(pathv); - } + if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()) { + Inkscape::LivePathEffect::Effect* thisEffect = _path->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE); + if(thisEffect){ + LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>(thisEffect->getLPEObj()->get_lpe()); + if (lpe_pwr) { + lpe_pwr->adjustForNewPath(pathv); } - thisEffect = path->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER); - if(thisEffect){ - LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast<LivePathEffect::LPEFilletChamfer*>(thisEffect->getLPEObj()->get_lpe()); - if (lpe_fll) { - lpe_fll->adjustForNewPath(pathv); - } + } + thisEffect = _path->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER); + if(thisEffect){ + LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast<LivePathEffect::LPEFilletChamfer*>(thisEffect->getLPEObj()->get_lpe()); + if (lpe_fll) { + lpe_fll->adjustForNewPath(pathv); } } } diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h index 4d2bf4300..6dc1c9d09 100644 --- a/src/ui/tool/path-manipulator.h +++ b/src/ui/tool/path-manipulator.h @@ -107,7 +107,7 @@ private: void _createControlPointsFromGeometry(); - bool isBSpline(bool recalculate = false); + void recalculateIsBSpline(); bool isBSpline() const; double BSplineHandlePosition(Handle *h, Handle *h2 = NULL); Geom::Point BSplineHandleReposition(Handle *h, Handle *h2 = NULL); diff --git a/src/ui/widget/page-sizer.cpp b/src/ui/widget/page-sizer.cpp index 8e647ebb4..0a5697661 100644 --- a/src/ui/widget/page-sizer.cpp +++ b/src/ui/widget/page-sizer.cpp @@ -242,7 +242,13 @@ PageSizer::PageSizer(Registry & _wr) _marginBottom( _("Botto_m:"), _("Bottom margin"), "fit-margin-bottom", _wr), _lockMarginUpdate(false), _scaleX(_("Scale _x:"), _("Scale X"), "scale-x", _wr), + _scaleY(_("Scale _y:"), _("Scale Y"), "scale-y", _wr), _lockScaleUpdate(false), + _viewboxX(_("X:"), _("X"), "viewbox-x", _wr), + _viewboxY(_("Y:"), _("Y"), "viewbox-y", _wr), + _viewboxW(_("Width:"), _("Width"), "viewbox-width", _wr), + _viewboxH(_("Height:"), _("Height"), "viewbox-height", _wr), + _lockViewboxUpdate(false), _widgetRegistry(&_wr) { // set precision of scalar entry boxes @@ -254,7 +260,21 @@ PageSizer::PageSizer(Registry & _wr) _marginRight.setDigits(5); _marginBottom.setDigits(5); _scaleX.setDigits(5); + _scaleY.setDigits(5); + _viewboxX.setDigits(2); + _viewboxY.setDigits(2); + _viewboxW.setDigits(2); + _viewboxH.setDigits(2); + _scaleX.setRange( 0.00001, 100000 ); + _scaleY.setRange( 0.00001, 100000 ); + _viewboxX.setRange( -100000, 100000 ); + _viewboxY.setRange( -100000, 100000 ); + _viewboxW.setRange( 0, 200000 ); + _viewboxH.setRange( 0, 200000 ); + + _scaleY.set_sensitive (false); // We only want to display Y scale. + _wr.setUpdating (false); //# Set up the Paper Size combo box @@ -435,21 +455,62 @@ PageSizer::PageSizer(Registry & _wr) _scaleTable.set_row_spacing(4); _scaleTable.set_column_spacing(4); - _dimensionWidth.set_hexpand(); - _dimensionWidth.set_vexpand(); _scaleTable.attach(_scaleX, 0, 0, 1, 1); + _scaleTable.attach(_scaleY, 1, 0, 1, 1); - _dimensionUnits.set_hexpand(); - _dimensionUnits.set_vexpand(); - _scaleTable.attach(_scaleLabel, 1, 0, 1, 1); + _scaleTable.attach(_scaleLabel, 2, 0, 1, 1); + _scaleTable.attach(_scaleWarning, 0, 1, 2, 1); + _viewboxExpander.set_hexpand(); + _viewboxExpander.set_vexpand(); + _scaleTable.attach(_viewboxExpander, 0, 2, 2, 1); #else - _scaleTable.resize(2, 1); + _scaleTable.resize(3, 2); _scaleTable.set_row_spacings(4); _scaleTable.set_col_spacings(4); _scaleTable.attach(_scaleX, 0,1, 0,1); - _scaleTable.attach(_scaleLabel, 1,2, 0,1); + _scaleTable.attach(_scaleY, 1,2, 0,1); + _scaleTable.attach(_scaleLabel, 2,3, 0,1); + _scaleTable.attach(_scaleWarning, 0,3, 1,2, Gtk::FILL); + _scaleTable.attach(_viewboxExpander, 0,3, 2,3); #endif + _scaleWarning.set_label(_("While SVG allows non-uniform scaling it is recommended to use only uniform scaling in Inkscape. To set a non-uniform scaling, set the 'viewBox' directly.")); + _scaleWarning.set_line_wrap( true ); + + _viewboxExpander.set_use_underline(); + _viewboxExpander.set_label(_("_Viewbox...")); + _viewboxExpander.add(_viewboxTable); + +#if WITH_GTKMM_3_0 + _viewboxTable.set_row_spacing(2); + _viewboxTable.set_column_spacing(2); + + _viewboxX.set_hexpand(); + _viewboxX.set_vexpand(); + _viewboxTable.attach(_viewboxX, 0, 0, 1, 1); + + _viewboxY.set_hexpand(); + _viewboxY.set_vexpand(); + _viewboxTable.attach(_viewboxY, 1, 0, 1, 1); + + _viewboxW.set_hexpand(); + _viewboxW.set_vexpand(); + _viewboxTable.attach(_viewboxW, 0, 1, 1, 1); + + _viewboxH.set_hexpand(); + _viewboxH.set_vexpand(); + _viewboxTable.attach(_viewboxH, 1, 1, 1, 1); + +#else + _viewboxTable.set_border_width(4); + _viewboxTable.set_row_spacings(2); + _viewboxTable.set_col_spacings(2); + _viewboxTable.attach(_viewboxX, 0,1, 0,1); + _viewboxTable.attach(_viewboxY, 1,2, 0,1); + _viewboxTable.attach(_viewboxW, 0,1, 1,2); + _viewboxTable.attach(_viewboxH, 1,2, 1,2); +#endif + _wr.setUpdating (true); updateScaleUI(); _wr.setUpdating (false); @@ -478,7 +539,11 @@ PageSizer::init () _changedh_connection = _dimensionHeight.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed)); _changedu_connection = _dimensionUnits.getUnitMenu()->signal_changed().connect (sigc::mem_fun (*this, &PageSizer::on_units_changed)); _fitPageButton.signal_clicked().connect(sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing)); - _changeds_connection = _scaleX.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_scale_changed)); + _changeds_connection = _scaleX.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_scale_changed)); + _changedvx_connection = _viewboxX.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_viewbox_changed)); + _changedvy_connection = _viewboxY.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_viewbox_changed)); + _changedvw_connection = _viewboxW.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_viewbox_changed)); + _changedvh_connection = _viewboxH.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_viewbox_changed)); show_all_children(); } @@ -744,10 +809,6 @@ void PageSizer::updateScaleUI() { - if (_lockScaleUpdate) { - return; - } - static bool _called = false; if (_called) { return; @@ -756,28 +817,56 @@ PageSizer::updateScaleUI() _called = true; _changeds_connection.block(); + _changedvx_connection.block(); + _changedvy_connection.block(); + _changedvw_connection.block(); + _changedvh_connection.block(); SPDesktop *dt = SP_ACTIVE_DESKTOP; if (dt) { SPDocument *doc = dt->getDocument(); + + // Update scale Geom::Scale scale = doc->getDocumentScale(); - SPNamedView *nv = dt->getNamedView(); std::stringstream ss; ss << _("User units per ") << nv->display_units->abbr << "." ; _scaleLabel.set_text( ss.str() ); - double scaleX_inv = - Inkscape::Util::Quantity::convert( scale[Geom::X], "px", nv->display_units ); - if( scaleX_inv > 0 ) { - _scaleX.setValue(1.0/scaleX_inv); - } else { - // Should never happen - std::cerr << "PageSizer::updateScaleUI(): Invalid scale value: " << scaleX_inv << std::endl; - _scaleX.setValue(1.0); + if( !_lockScaleUpdate ) { + + double scaleX_inv = + Inkscape::Util::Quantity::convert( scale[Geom::X], "px", nv->display_units ); + if( scaleX_inv > 0 ) { + _scaleX.setValue(1.0/scaleX_inv); + } else { + // Should never happen + std::cerr << "PageSizer::updateScaleUI(): Invalid scale value: " << scaleX_inv << std::endl; + _scaleX.setValue(1.0); + } + } + + { // Don't need to lock as scaleY widget not linked to callback. + double scaleY_inv = + Inkscape::Util::Quantity::convert( scale[Geom::Y], "px", nv->display_units ); + if( scaleY_inv > 0 ) { + _scaleY.setValue(1.0/scaleY_inv); + } else { + // Should never happen + std::cerr << "PageSizer::updateScaleUI(): Invalid scale value: " << scaleY_inv << std::endl; + _scaleY.setValue(1.0); + } } + if( !_lockViewboxUpdate ) { + Geom::Rect viewBox = doc->getViewBox(); + _viewboxX.setValue( viewBox.min()[Geom::X] ); + _viewboxY.setValue( viewBox.min()[Geom::Y] ); + _viewboxW.setValue( viewBox.width() ); + _viewboxH.setValue( viewBox.height() ); + } + } else { // Should never happen std::cerr << "PageSizer::updateScaleUI(): No active desktop." << std::endl; @@ -785,6 +874,10 @@ PageSizer::updateScaleUI() } _changeds_connection.unblock(); + _changedvx_connection.unblock(); + _changedvy_connection.unblock(); + _changedvw_connection.unblock(); + _changedvh_connection.unblock(); _called = false; } @@ -801,6 +894,7 @@ PageSizer::on_value_changed() setDim (Inkscape::Util::Quantity(_dimensionWidth.getValue(""), _dimensionUnits.getUnit()), Inkscape::Util::Quantity(_dimensionHeight.getValue(""), _dimensionUnits.getUnit())); } + void PageSizer::on_units_changed() { @@ -830,13 +924,44 @@ PageSizer::on_scale_changed() double scaleX_inv = Inkscape::Util::Quantity(1.0/value, nv->display_units ).value("px"); _lockScaleUpdate = true; - doc->setDocumentScale( 1.0/scaleX_inv ); + doc->setDocumentScale( 1.0/scaleX_inv ); + updateScaleUI(); _lockScaleUpdate = false; DocumentUndo::done(doc, SP_VERB_NONE, _("Set page scale")); } } } +/** + * Callback for viewbox widgets + */ +void +PageSizer::on_viewbox_changed() +{ + if (_widgetRegistry->isUpdating()) return; + + double viewboxX = _viewboxX.getValue(); + double viewboxY = _viewboxY.getValue(); + double viewboxW = _viewboxW.getValue(); + double viewboxH = _viewboxH.getValue(); + + if( viewboxW > 0 && viewboxH > 0) { + SPDesktop *dt = SP_ACTIVE_DESKTOP; + if (dt) { + SPDocument *doc = dt->getDocument(); + _lockViewboxUpdate = true; + doc->setViewBox( Geom::Rect::from_xywh( viewboxX, viewboxY, viewboxW, viewboxH ) ); + updateScaleUI(); + _lockViewboxUpdate = false; + DocumentUndo::done(doc, SP_VERB_NONE, _("Set 'viewBox'")); + } + } else { + std::cerr + << "PageSizer::on_viewbox_changed(): width and height must both be greater than zero." + << std::endl; + } +} + } // namespace Widget } // namespace UI } // namespace Inkscape diff --git a/src/ui/widget/page-sizer.h b/src/ui/widget/page-sizer.h index f9a72d9f3..0eea28e61 100644 --- a/src/ui/widget/page-sizer.h +++ b/src/ui/widget/page-sizer.h @@ -264,17 +264,38 @@ protected: #endif Gtk::Label _scaleLabel; + Gtk::Label _scaleWarning; RegisteredScalar _scaleX; + RegisteredScalar _scaleY; bool _lockScaleUpdate; + // Viewbox + Gtk::Expander _viewboxExpander; +#if WITH_GTKMM_3_0 + Gtk::Grid _viewboxTable; +#else + Gtk::Table _viewboxTable; +#endif + + RegisteredScalar _viewboxX; + RegisteredScalar _viewboxY; + RegisteredScalar _viewboxW; + RegisteredScalar _viewboxH; + bool _lockViewboxUpdate; + //callback void on_value_changed(); void on_units_changed(); void on_scale_changed(); + void on_viewbox_changed(); sigc::connection _changedw_connection; sigc::connection _changedh_connection; sigc::connection _changedu_connection; sigc::connection _changeds_connection; + sigc::connection _changedvx_connection; + sigc::connection _changedvy_connection; + sigc::connection _changedvw_connection; + sigc::connection _changedvh_connection; Registry *_widgetRegistry; diff --git a/src/uri-references.cpp b/src/uri-references.cpp index b23bed74a..2518c173e 100644 --- a/src/uri-references.cpp +++ b/src/uri-references.cpp @@ -65,7 +65,7 @@ void URIReference::attach(const URI &uri) throw(BadURIException) skip = true; } - // The path contains references to seperate document files to load. + // The path contains references to separate document files to load. if(document && uri.getPath() && !skip ) { std::string base = document->getBase() ? document->getBase() : ""; std::string path = uri.getFullPath(base); |
