From b2f771a144d2b6f9ac6563dd72dff99425b215bc Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Cenoz Date: Thu, 19 Mar 2015 00:52:46 +0100 Subject: Changed two SP_IS_LPE_ITEM calls (bzr r14013) --- src/ui/tool/path-manipulator.cpp | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index c8b986824..dbcde240a 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -1221,10 +1221,13 @@ int PathManipulator::BSplineGetSteps() const { LivePathEffect::LPEBSpline const *lpe_bsp = NULL; - if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()){ - Inkscape::LivePathEffect::Effect const *thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE); - if(thisEffect){ - lpe_bsp = dynamic_cast(thisEffect->getLPEObj()->get_lpe()); + SPLPEItem * path = dynamic_cast(_path); + if (path){ + if(path->hasPathEffect()){ + Inkscape::LivePathEffect::Effect const *thisEffect = path->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE); + if(thisEffect){ + lpe_bsp = dynamic_cast(thisEffect->getLPEObj()->get_lpe()); + } } } int steps = 0; @@ -1338,19 +1341,22 @@ 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? - if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()){ - Inkscape::LivePathEffect::Effect* thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE); - if(thisEffect){ - LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast(thisEffect->getLPEObj()->get_lpe()); - if (lpe_pwr) { - lpe_pwr->adjustForNewPath(pathv); + SPLPEItem * path = dynamic_cast(_path); + if (path){ + if(path->hasPathEffect()){ + Inkscape::LivePathEffect::Effect* thisEffect = path->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE); + if(thisEffect){ + LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast(thisEffect->getLPEObj()->get_lpe()); + if (lpe_pwr) { + lpe_pwr->adjustForNewPath(pathv); + } } - } - thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER); - if(thisEffect){ - LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast(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(thisEffect->getLPEObj()->get_lpe()); + if (lpe_fll) { + lpe_fll->adjustForNewPath(pathv); + } } } } -- cgit v1.2.3 From 6177de61cec9a47677b076ffee4dd56d445f6c13 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Wed, 18 Mar 2015 21:41:26 -0400 Subject: Let's hope the world doesn't end Native 2geom path outliner, still buggy (bzr r14014) --- src/helper/CMakeLists.txt | 2 + src/helper/Makefile_insert | 2 + src/helper/geom-pathstroke.cpp | 505 ++++++++++++++++++++++++++++++++++++++ src/helper/geom-pathstroke.h | 57 +++++ src/live_effects/lpe-jointype.cpp | 56 +++-- src/live_effects/lpe-jointype.h | 4 +- 6 files changed, 597 insertions(+), 29 deletions(-) create mode 100644 src/helper/geom-pathstroke.cpp create mode 100644 src/helper/geom-pathstroke.h (limited to 'src') 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 const &curve, double t, double tol=0.01 ) +{ + D2 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 > unitv = unitVector(dM,tol); + Piecewise dMlength = dot(Piecewise >(dM),unitv); + Piecewise 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(outgoing.initialPoint()); +} + +void round_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, double width) +{ + res.appendNew(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(p); + //} + } + } + res.appendNew(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(&incoming)) || (dynamic_cast(&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(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(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 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 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 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(current)) { + size_t order = b->order(); + switch (order) { + case 1: + res.append(offset_line(static_cast(*current), width)); + break; + case 2: { + Geom::QuadraticBezier const& q = static_cast(*current); + offset_quadratic(res, q, width, tolerance, levels); + break; + } + case 3: { + Geom::CubicBezier const& cb = static_cast(*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/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 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 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 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 LPEJoinType::doEffect_path(std::vector const & path_in) { - return Outline::PathVectorOutline(path_in, line_width, static_cast(linecap_type.get_value()), - static_cast(linejoin_type.get_value()), - (attempt_force_join ? std::numeric_limits::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::max() : miter_limit), + static_cast(linejoin_type.get_value()), + static_cast(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 linecap_type; EnumParam linejoin_type; - ScalarParam start_lean; - ScalarParam end_lean; + //ScalarParam start_lean; + //ScalarParam end_lean; ScalarParam miter_limit; BoolParam attempt_force_join; bool was_initialized; -- cgit v1.2.3 From 8fe962f4de79592f7df62c3ec08f98cf90757208 Mon Sep 17 00:00:00 2001 From: Bryce Harrington Date: Wed, 18 Mar 2015 18:53:00 -0700 Subject: ui/tools: sp. fixes (cosmetic) (bzr r14016) --- src/ui/tools/box3d-tool.h | 2 +- src/ui/tools/connector-tool.cpp | 2 +- src/ui/tools/dynamic-base.h | 2 +- src/ui/tools/gradient-tool.cpp | 2 +- src/ui/tools/node-tool.cpp | 2 +- src/ui/tools/pen-tool.cpp | 4 ++-- src/ui/tools/spray-tool.cpp | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') 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(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(new_obj); // Convertion object->item + item_copied = dynamic_cast(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)); -- cgit v1.2.3 From 2c6f909f381a31d7de1245debcdde92f36e94d30 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Thu, 19 Mar 2015 20:58:14 -0400 Subject: Fix remaining bugs in path outliner (bzr r14017) --- src/helper/geom-pathstroke.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp index f41732a51..1b8f90104 100644 --- a/src/helper/geom-pathstroke.cpp +++ b/src/helper/geom-pathstroke.cpp @@ -66,7 +66,7 @@ void bevel_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, void round_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, double width) { - res.appendNew(width, width, 0, false, width > 0, outgoing.initialPoint()); + res.appendNew(width, width, 0, false, width <= 0, outgoing.initialPoint()); } void miter_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) @@ -318,7 +318,7 @@ void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double wi std::vector 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]); + Geom::CubicBezier cub = Geom::CubicBezier(points[0], b1, b2, points[2]); offset_cubic(p, cub, width, tol, levels); } @@ -378,7 +378,7 @@ Geom::PathVector outline(Geom::Path const& input, double width, double miter, Li if (!input.closed()) { switch (butt) { case BUTT_ROUND: - res.arcTo((-width) / 2., (-width) / 2., 0., true, true, against_dir.initialPoint()); + 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.); @@ -410,7 +410,7 @@ Geom::PathVector outline(Geom::Path const& input, double width, double miter, Li if (!input.closed()) { switch(butt) { case BUTT_ROUND: - res.arcTo((-width) / 2., (-width) / 2., 0., true, true, with_dir.initialPoint()); + res.arcTo(width / 2., width / 2., 0., true, false, with_dir.initialPoint()); break; case BUTT_SQUARE: { Geom::Point end_deriv = -input[0].unitTangentAt(0.); @@ -451,7 +451,7 @@ 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(); + const size_t k = input.size_default(); for (size_t u = 0; u < k; u += 2) { temp = Geom::Path(); @@ -473,8 +473,13 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin } if (input.closed()) { - Geom::Curve const &c1 = res[res.size()-1]; - Geom::Curve const &c2 = res[0]; + 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(); temp.append(c1); Geom::Path temp2; @@ -485,9 +490,9 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin // res.append(temp); + res.close(); } - res.close(); return res; } -- cgit v1.2.3 From 5f452e2604d96a028325493da3b704e7dfe08250 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Thu, 19 Mar 2015 21:25:41 -0400 Subject: (audible sound of head banging off wall) Fix that one thing (bzr r14018) --- src/live_effects/lpe-jointype.cpp | 10 ++---- src/live_effects/lpe-powerstroke.cpp | 33 +++++++++---------- src/live_effects/lpe-taperstroke.cpp | 61 ++++++++++++++++-------------------- 3 files changed, 45 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp index 291337da0..033128d98 100644 --- a/src/live_effects/lpe-jointype.cpp +++ b/src/live_effects/lpe-jointype.cpp @@ -81,7 +81,6 @@ LPEJoinType::~LPEJoinType() void LPEJoinType::doOnApply(SPLPEItem const* lpeitem) { - if (SP_IS_SHAPE(lpeitem)) { SPLPEItem* item = const_cast(lpeitem); double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.; @@ -107,6 +106,7 @@ void LPEJoinType::doOnApply(SPLPEItem const* lpeitem) sp_repr_css_unset_property (css, "fill"); } + sp_repr_css_set_property(css, "fill-rule", "nonzero"); sp_repr_css_set_property(css, "stroke", "none"); sp_desktop_apply_css_recursive(item, css, true); @@ -116,17 +116,13 @@ void LPEJoinType::doOnApply(SPLPEItem const* lpeitem) was_initialized = true; line_width.param_set_value(width); } - } else { - g_warning("LPE Join Type can only be applied to paths (not groups)."); - } } //from LPEPowerStroke -- sets stroke color from existing fill color void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem) { - - if (SP_IS_SHAPE(lpeitem)) { + if (SP_IS_SHAPE(lpeitem)) { // does this check even make sense? SPLPEItem *item = const_cast(lpeitem); SPCSSAttr *css = sp_repr_css_attr_new (); @@ -142,7 +138,7 @@ void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem) } } else if (lpeitem->style->fill.isColor()) { gchar c[64]; - sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); + sp_svg_write_color (c, sizeof(c), lpeitem->style->fill.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->fill_opacity.value))); sp_repr_css_set_property (css, "stroke", c); } else { sp_repr_css_set_property (css, "stroke", "none"); diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index f7fe9592d..5d9d224e8 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -297,7 +297,8 @@ LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem) } else { sp_repr_css_unset_property (css, "fill"); } - + + sp_repr_css_set_property(css, "fill-rule", "nonzero"); sp_repr_css_set_property(css, "stroke", "none"); sp_desktop_apply_css_recursive(item, css, true); @@ -330,25 +331,21 @@ void LPEPowerStroke::doOnRemove(SPLPEItem const* lpeitem) if (SP_IS_SHAPE(lpeitem)) { SPLPEItem *item = const_cast(lpeitem); SPCSSAttr *css = sp_repr_css_attr_new (); - if (true) { - if (lpeitem->style->fill.isPaintserver()) { - SPPaintServer * server = lpeitem->style->getFillPaintServer(); - if (server) { - Glib::ustring str; - str += "url(#"; - str += server->getId(); - str += ")"; - sp_repr_css_set_property (css, "stroke", str.c_str()); - } - } else if (lpeitem->style->fill.isColor()) { - gchar c[64]; - sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); - sp_repr_css_set_property (css, "stroke", c); - } else { - sp_repr_css_set_property (css, "stroke", "none"); + if (lpeitem->style->fill.isPaintserver()) { + SPPaintServer * server = lpeitem->style->getFillPaintServer(); + if (server) { + Glib::ustring str; + str += "url(#"; + str += server->getId(); + str += ")"; + sp_repr_css_set_property (css, "stroke", str.c_str()); } + } else if (lpeitem->style->fill.isColor()) { + char c[64] = {0}; + sp_svg_write_color (c, sizeof(c), lpeitem->style->fill.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->fill_opacity.value))); + sp_repr_css_set_property (css, "stroke", c); } else { - sp_repr_css_unset_property (css, "stroke"); + sp_repr_css_set_property (css, "stroke", "none"); } Inkscape::CSSOStringStream os; diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp index 7a024147c..9feb650af 100644 --- a/src/live_effects/lpe-taperstroke.cpp +++ b/src/live_effects/lpe-taperstroke.cpp @@ -102,27 +102,24 @@ void LPETaperStroke::doOnApply(SPLPEItem const* lpeitem) double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.; SPCSSAttr *css = sp_repr_css_attr_new (); - if (true) { - if (lpeitem->style->stroke.isPaintserver()) { - SPPaintServer * server = lpeitem->style->getStrokePaintServer(); - if (server) { - Glib::ustring str; - str += "url(#"; - str += server->getId(); - str += ")"; - sp_repr_css_set_property (css, "fill", str.c_str()); - } - } else if (lpeitem->style->stroke.isColor()) { - gchar c[64]; - sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); - sp_repr_css_set_property (css, "fill", c); - } else { - sp_repr_css_set_property (css, "fill", "none"); + if (lpeitem->style->stroke.isPaintserver()) { + SPPaintServer * server = lpeitem->style->getStrokePaintServer(); + if (server) { + Glib::ustring str; + str += "url(#"; + str += server->getId(); + str += ")"; + sp_repr_css_set_property (css, "fill", str.c_str()); } + } else if (lpeitem->style->stroke.isColor()) { + gchar c[64]; + sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); + sp_repr_css_set_property (css, "fill", c); } else { - sp_repr_css_unset_property (css, "fill"); + sp_repr_css_set_property (css, "fill", "none"); } + sp_repr_css_set_property(css, "fill-rule", "nonzero"); sp_repr_css_set_property(css, "stroke", "none"); sp_desktop_apply_css_recursive(item, css, true); @@ -142,25 +139,21 @@ void LPETaperStroke::doOnRemove(SPLPEItem const* lpeitem) SPLPEItem *item = const_cast(lpeitem); SPCSSAttr *css = sp_repr_css_attr_new (); - if (true) { - if (lpeitem->style->fill.isPaintserver()) { - SPPaintServer * server = lpeitem->style->getFillPaintServer(); - if (server) { - Glib::ustring str; - str += "url(#"; - str += server->getId(); - str += ")"; - sp_repr_css_set_property (css, "stroke", str.c_str()); - } - } else if (lpeitem->style->fill.isColor()) { - gchar c[64]; - sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); - sp_repr_css_set_property (css, "stroke", c); - } else { - sp_repr_css_set_property (css, "stroke", "none"); + if (lpeitem->style->fill.isPaintserver()) { + SPPaintServer * server = lpeitem->style->getFillPaintServer(); + if (server) { + Glib::ustring str; + str += "url(#"; + str += server->getId(); + str += ")"; + sp_repr_css_set_property (css, "stroke", str.c_str()); } + } else if (lpeitem->style->fill.isColor()) { + gchar c[64]; + sp_svg_write_color (c, sizeof(c), lpeitem->style->fill.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->fill_opacity.value))); + sp_repr_css_set_property (css, "stroke", c); } else { - sp_repr_css_unset_property (css, "stroke"); + sp_repr_css_set_property (css, "stroke", "none"); } Inkscape::CSSOStringStream os; -- cgit v1.2.3 From f711db46d6e23da2b808f555bb446f18662840d9 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Thu, 19 Mar 2015 21:30:33 -0400 Subject: nop (if true etc) (bzr r14019) --- src/live_effects/lpe-jointype.cpp | 68 ++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp index 033128d98..09869822c 100644 --- a/src/live_effects/lpe-jointype.cpp +++ b/src/live_effects/lpe-jointype.cpp @@ -81,29 +81,26 @@ LPEJoinType::~LPEJoinType() void LPEJoinType::doOnApply(SPLPEItem const* lpeitem) { + if (SP_IS_SHAPE(lpeitem)) { SPLPEItem* item = const_cast(lpeitem); double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.; SPCSSAttr *css = sp_repr_css_attr_new (); - if (true) { - if (lpeitem->style->stroke.isPaintserver()) { - SPPaintServer * server = lpeitem->style->getStrokePaintServer(); - if (server) { - Glib::ustring str; - str += "url(#"; - str += server->getId(); - str += ")"; - sp_repr_css_set_property (css, "fill", str.c_str()); - } - } else if (lpeitem->style->stroke.isColor()) { - gchar c[64]; - sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); - sp_repr_css_set_property (css, "fill", c); - } else { - sp_repr_css_set_property (css, "fill", "none"); + if (lpeitem->style->stroke.isPaintserver()) { + SPPaintServer * server = lpeitem->style->getStrokePaintServer(); + if (server) { + Glib::ustring str; + str += "url(#"; + str += server->getId(); + str += ")"; + sp_repr_css_set_property (css, "fill", str.c_str()); } + } else if (lpeitem->style->stroke.isColor()) { + gchar c[64]; + sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); + sp_repr_css_set_property (css, "fill", c); } else { - sp_repr_css_unset_property (css, "fill"); + sp_repr_css_set_property (css, "fill", "none"); } sp_repr_css_set_property(css, "fill-rule", "nonzero"); @@ -111,40 +108,37 @@ void LPEJoinType::doOnApply(SPLPEItem const* lpeitem) sp_desktop_apply_css_recursive(item, css, true); sp_repr_css_attr_unref (css); - if (!was_initialized) - { + + if (!was_initialized) { was_initialized = true; line_width.param_set_value(width); } + } } //from LPEPowerStroke -- sets stroke color from existing fill color void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem) { - if (SP_IS_SHAPE(lpeitem)) { // does this check even make sense? + if (SP_IS_SHAPE(lpeitem)) { SPLPEItem *item = const_cast(lpeitem); SPCSSAttr *css = sp_repr_css_attr_new (); - if (true) { - if (lpeitem->style->fill.isPaintserver()) { - SPPaintServer * server = lpeitem->style->getFillPaintServer(); - if (server) { - Glib::ustring str; - str += "url(#"; - str += server->getId(); - str += ")"; - sp_repr_css_set_property (css, "stroke", str.c_str()); - } - } else if (lpeitem->style->fill.isColor()) { - gchar c[64]; - sp_svg_write_color (c, sizeof(c), lpeitem->style->fill.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->fill_opacity.value))); - sp_repr_css_set_property (css, "stroke", c); - } else { - sp_repr_css_set_property (css, "stroke", "none"); + if (lpeitem->style->fill.isPaintserver()) { + SPPaintServer * server = lpeitem->style->getFillPaintServer(); + if (server) { + Glib::ustring str; + str += "url(#"; + str += server->getId(); + str += ")"; + sp_repr_css_set_property (css, "stroke", str.c_str()); } + } else if (lpeitem->style->fill.isColor()) { + gchar c[64]; + sp_svg_write_color (c, sizeof(c), lpeitem->style->fill.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->fill_opacity.value))); + sp_repr_css_set_property (css, "stroke", c); } else { - sp_repr_css_unset_property (css, "stroke"); + sp_repr_css_set_property (css, "stroke", "none"); } Inkscape::CSSOStringStream os; -- cgit v1.2.3 From 9585f670efe431bb89ccda9a45c322089826fe3d Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Thu, 19 Mar 2015 21:56:01 -0400 Subject: take out the trash (bzr r14020) --- src/live_effects/Makefile_insert | 2 - src/live_effects/lpe-taperstroke.cpp | 43 +- src/live_effects/pathoutlineprovider.cpp | 803 ------------------------------- src/live_effects/pathoutlineprovider.h | 55 --- 4 files changed, 30 insertions(+), 873 deletions(-) delete mode 100644 src/live_effects/pathoutlineprovider.cpp delete mode 100644 src/live_effects/pathoutlineprovider.h (limited to 'src') diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 8f0a3ac57..dace45739 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -109,8 +109,6 @@ ink_common_sources += \ live_effects/lpe-fill-between-many.h \ live_effects/lpe-ellipse_5pts.cpp \ live_effects/lpe-ellipse_5pts.h \ - live_effects/pathoutlineprovider.cpp \ - live_effects/pathoutlineprovider.h \ live_effects/lpe-jointype.cpp \ live_effects/lpe-jointype.h \ live_effects/lpe-taperstroke.cpp \ diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp index 9feb650af..d54b2acc0 100644 --- a/src/live_effects/lpe-taperstroke.cpp +++ b/src/live_effects/lpe-taperstroke.cpp @@ -19,7 +19,8 @@ #include <2geom/circle.h> #include <2geom/sbasis-to-bezier.h> -#include "pathoutlineprovider.h" +#include "helper/geom-nodetype.h" +#include "helper/geom-pathstroke.h" #include "display/curve.h" #include "sp-shape.h" #include "style.h" @@ -60,11 +61,10 @@ namespace TpS { } // TpS static const Util::EnumData JoinType[] = { - {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"}, - {LINEJOIN_ROUND, N_("Rounded"), "round"}, - {LINEJOIN_REFLECTED, N_("Reflected"), "reflected"}, - {LINEJOIN_POINTY, N_("Miter"), "miter"}, - {LINEJOIN_EXTRAPOLATED, N_("Extrapolated"), "extrapolated"} + {JOIN_BEVEL, N_("Beveled"), "bevel"}, + {JOIN_ROUND, N_("Rounded"), "round"}, + {JOIN_MITER, N_("Miter"), "miter"}, + {JOIN_EXTRAPOLATE, N_("Extrapolated"), "extrapolated"}, }; static const Util::EnumDataConverter JoinTypeConverter(JoinType, sizeof (JoinType)/sizeof(*JoinType)); @@ -75,7 +75,7 @@ LPETaperStroke::LPETaperStroke(LivePathEffectObject *lpeobject) : attach_start(_("Start offset:"), _("Taper distance from path start"), "attach_start", &wr, this, 0.2), attach_end(_("End offset:"), _("The ending position of the taper"), "end_offset", &wr, this, 0.2), smoothing(_("Taper smoothing:"), _("Amount of smoothing to apply to the tapers"), "smoothing", &wr, this, 0.5), - join_type(_("Join type:"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED), + join_type(_("Join type:"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, JOIN_EXTRAPOLATE), miter_limit(_("Miter limit:"), _("Limit for miter joins"), "miter_limit", &wr, this, 100.) { show_orig_path = true; @@ -127,7 +127,7 @@ void LPETaperStroke::doOnApply(SPLPEItem const* lpeitem) line_width.param_set_value(width); } else { - printf("WARNING: It only makes sense to apply Join Type to paths (not groups).\n"); + printf("WARNING: It only makes sense to apply Taper stroke to paths (not groups).\n"); } } @@ -172,8 +172,25 @@ using Geom::D2; using Geom::SBasis; // leave Geom::Path -Geom::Path return_at_first_cusp(Geom::Path const & path_in, double /*smooth_tolerance*/ = 0.05) { - return Geom::split_at_cusps(path_in)[0]; +static Geom::Path return_at_first_cusp(Geom::Path const & path_in, double /*smooth_tolerance*/ = 0.05) +{ + Geom::Path temp; + + for (unsigned i = 0; i < path_in.size(); i++) { + temp.append(path_in[i]); + if (Geom::get_nodetype(path_in[i], path_in[i + 1]) != Geom::NODE_SMOOTH ) { + break; + } + } + + return temp; +} + +static Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2 const & sbasis_in) +{ + std::vector temp; + Geom::sbasis_to_bezier(temp, sbasis_in, 4); + return Geom::CubicBezier( temp ); } Piecewise > stretch_along(Piecewise > pwd2_in, Geom::Path pattern, double width); @@ -278,7 +295,7 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) // although this seems obvious, it can probably lead to bugs. if (!metInMiddle) { // append the outside outline of the path (goes with the direction of the path) - throwaway_path = Outline::PathOutsideOutline(pathv_out[1], -fabs(line_width), static_cast(join_type.get_value()), miter_limit); + throwaway_path = half_outline(pathv_out[1], -fabs(line_width)/2., miter_limit, static_cast(join_type.get_value())); if (!zeroStart && real_path.size() >= 1 && throwaway_path.size() >= 1) { if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint())) { real_path.appendNew(throwaway_path.initialPoint()); @@ -310,7 +327,7 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) if (!metInMiddle) { // append the inside outline of the path (against direction) - throwaway_path = Outline::PathOutsideOutline(pathv_out[1].reverse(), -fabs(line_width), static_cast(join_type.get_value()), miter_limit); + throwaway_path = half_outline(pathv_out[1].reverse(), -fabs(line_width)/2., miter_limit, static_cast(join_type.get_value())); if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) { real_path.appendNew(throwaway_path.initialPoint()); @@ -515,7 +532,7 @@ void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_fi val_second = seg_pair.second.duplicate(); } else { // all other cases: - Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(curve_in->toSBasis()); + Geom::CubicBezier cubic = sbasis_to_cubicbezier(curve_in->toSBasis()); std::pair cubic_pair = cubic.subdivide(t); val_first = cubic_pair.first.duplicate(); val_second = cubic_pair.second.duplicate(); diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp deleted file mode 100644 index 21a0fb809..000000000 --- a/src/live_effects/pathoutlineprovider.cpp +++ /dev/null @@ -1,803 +0,0 @@ -/* Author: - * Liam P. White - * - * Copyright (C) 2014 Author - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include <2geom/angle.h> -#include <2geom/path.h> -#include <2geom/circle.h> -#include <2geom/sbasis-to-bezier.h> -#include <2geom/shape.h> -#include <2geom/transforms.h> -#include <2geom/path-sink.h> -#include - -#include "pathoutlineprovider.h" -#include "livarot/path-description.h" -#include "helper/geom-nodetype.h" -#include "svg/svg.h" - -namespace Geom { -/** -* Refer to: Weisstein, Eric W. "Circle-Circle Intersection." - From MathWorld--A Wolfram Web Resource. - http://mathworld.wolfram.com/Circle-CircleIntersection.html -* -* @return 0 if no intersection -* @return 1 if one circle is contained in the other -* @return 2 if intersections are found (they are written to p0 and p1) -*/ -static int circle_circle_intersection(Circle const &circle0, Circle const &circle1, Point & p0, Point & p1) -{ - Point X0 = circle0.center(); - double r0 = circle0.ray(); - Point X1 = circle1.center(); - double r1 = circle1.ray(); - - /* dx and dy are the vertical and horizontal distances between - * the circle centers. - */ - Point D = X1 - X0; - - /* Determine the straight-line distance between the centers. */ - double d = L2(D); - - /* Check for solvability. */ - if (d > (r0 + r1)) { - /* no solution. circles do not intersect. */ - return 0; - } - if (d <= fabs(r0 - r1)) { - /* no solution. one circle is contained in the other */ - return 1; - } - - /* 'point 2' is the point where the line through the circle - * intersection points crosses the line between the circle - * centers. - */ - - /* Determine the distance from point 0 to point 2. */ - double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ; - - /* Determine the coordinates of point 2. */ - Point p2 = X0 + D * (a/d); - - /* Determine the distance from point 2 to either of the - * intersection points. - */ - double h = std::sqrt((r0*r0) - (a*a)); - - /* Now determine the offsets of the intersection points from - * point 2. - */ - Point r = (h/d)*rot90(D); - - /* Determine the absolute intersection points. */ - p0 = p2 + r; - p1 = p2 - r; - - return 2; -} -/** -* 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 const &curve, double t, double tol=0.01 ) -{ - D2 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 > unitv = unitVector(dM,tol); - Piecewise dMlength = dot(Piecewise >(dM),unitv); - Piecewise 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)); -} - -std::vector split_at_cusps(const Geom::Path& in) -{ - PathVector out = PathVector(); - Path temp = Path(); - - for (unsigned i = 0; i < in.size(); i++) { - temp.append(in[i]); - if ( get_nodetype(in[i], in[i + 1]) != Geom::NODE_SMOOTH ) { - out.push_back(temp); - temp = Path(); - } - } - if (temp.size() > 0) { - out.push_back(temp); - } - return out; -} - -Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2 const & sbasis_in) -{ - std::vector temp; - sbasis_to_bezier(temp, sbasis_in, 4); - return Geom::CubicBezier( temp ); -} - -static boost::optional intersection_point(Geom::Point const & origin_a, Geom::Point const & vector_a, Geom::Point const & origin_b, Geom::Point const & vector_b) -{ - Geom::Coord denom = cross(vector_b, vector_a); - if (!Geom::are_near(denom,0.)) { - Geom::Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom; - return origin_a + t * vector_a; - } - return boost::none; -} - -} // namespace Geom - -namespace Outline { - -typedef Geom::D2 D2SB; -typedef Geom::Piecewise PWD2; - -// UTILITY - -unsigned bezierOrder (const Geom::Curve* curve_in) -{ - using namespace Geom; - if ( const BezierCurve* bz = dynamic_cast(curve_in) ) { - return bz->order(); - } - return 0; -} - -/** - * @return true if the angle formed by the curves and their handles is greater than 180 degrees clockwise, otherwise false. - */ -bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) -{ - Geom::Point start_point; - Geom::Point cross_point = cbc1.finalPoint(); - Geom::Point end_point; - - if (cross_point != cbc2.initialPoint()) { - printf("WARNING: Non-contiguous path in Outline::outside_angle()"); - return false; - } - - Geom::CubicBezier cubicBezier = Geom::sbasis_to_cubicbezier(cbc1.toSBasis()); - start_point = cubicBezier [2]; - - /* - * Because the node editor does not yet support true quadratics, paths are converted to - * cubic beziers in the node tool with degenerate handles on one side. - */ - - if (are_near(start_point, cross_point, 0.0000001)) { - start_point = cubicBezier [1]; - } - cubicBezier = Geom::sbasis_to_cubicbezier(cbc2.toSBasis()); - end_point = cubicBezier [1]; - if (are_near(end_point, cross_point, 0.0000001)) { - end_point = cubicBezier [2]; - } - - // got our three points, now let's see what their clockwise angle is - - // Definition of a Graham scan - - /******************************************************************** - # Three points are a counter-clockwise turn if ccw > 0, clockwise if - # ccw < 0, and collinear if ccw = 0 because ccw is a determinant that - # gives the signed area of the triangle formed by p1, p2 and p3. - function ccw(p1, p2, p3): - return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x) - *********************************************************************/ - - double ccw = ( (cross_point.x() - start_point.x()) * (end_point.y() - start_point.y()) ) - - ( (cross_point.y() - start_point.y()) * (end_point.x() - start_point.x()) ); - return ccw > 0; -} - -// LINE JOINS - -typedef Geom::BezierCurveN<1u> BezierLine; - -/** - * Removes the crossings on an interior join. - * @param path_builder Contains the incoming segment; result is appended to this - * @param outgoing The outgoing segment - */ -void joinInside(Geom::Path& path_builder, Geom::Curve const& outgoing) -{ - Geom::Curve const& incoming = path_builder.back(); - - // Using Geom::crossings to find intersections between two curves - Geom::Crossings cross = Geom::crossings(incoming, outgoing); - if (!cross.empty()) { - // Crossings found, create the join - Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(incoming.toSBasis()); - cubic = cubic.subdivide(cross[0].ta).first; - // erase the last segment, as we're going to overwrite it now - path_builder.erase_last(); - path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); - - cubic = Geom::sbasis_to_cubicbezier(outgoing.toSBasis()); - cubic = cubic.subdivide(cross[0].tb).second; - path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); - } else { - // No crossings occurred, or Geom::crossings() failed; default to bevel - if (Geom::are_near(incoming.finalPoint(), outgoing.initialPoint())) { - path_builder.appendNew(outgoing.initialPoint()); - } else { - path_builder.setFinal(outgoing.initialPoint()); - } - } -} - -/** - * Try to create a miter join. Falls back to bevel if no miter can be created. - * @param path_builder Path to append curves to; back() is the incoming curve - * @param outgoing Outgoing curve. - * @param miter_limit When mitering, don't exceed this length - * @param line_width The thickness of the line. - */ -void miter_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) -{ - using namespace Geom; - Curve const& incoming = path_builder.back(); - Point tang1 = unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.); - Point tang2 = unitTangentAt(outgoing.toSBasis(), 0); - - boost::optional p = intersection_point (incoming.finalPoint(), tang1, outgoing.initialPoint(), tang2); - if (p) { - // check size of miter - Point point_on_path = incoming.finalPoint() - rot90(tang1) * line_width; - Coord len = distance(*p, point_on_path); - if (len <= miter_limit) { - // miter OK - path_builder.appendNew(*p); - } - } - path_builder.appendNew(outgoing.initialPoint()); -} - -/** - * Smoothly extrapolate curves along a circular route. Falls back to miter if necessary. - * @param path_builder Path to append curves to; back() is the incoming curve - * @param outgoing Outgoing curve. - * @param miter_limit When mitering, don't exceed this length - * @param line_width The thickness of the line. Used for miter fallback. - */ -void extrapolate_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) -{ - 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(&incoming)) || (dynamic_cast(&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, circle2, 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->toSBasis()); - delete arc0; - arc0 = NULL; - } else { - throw std::exception(); - } - - if (arc1) { - path_builder.append (arc1->toSBasis()); - 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(endPt); - } - } else { - // 1 or no solutions found, default to miter - miter_curves(path_builder, outgoing, miter_limit, line_width); - } - } else { - // Line segments exist - miter_curves(path_builder, outgoing, miter_limit, line_width); - } -} - -/** - * Extrapolate curves by reflecting them along the line that would be given by beveling the join. - * @param path_builder Path to append curves to; back() is the incoming curve - * @param outgoing Outgoing curve. - * @param miter_limit When mitering, don't exceed this length - * @param line_width The thickness of the line. Used for miter fallback. - */ -void reflect_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) -{ - using namespace Geom; - Curve const& incoming = path_builder.back(); - // On the outside, we'll take the incoming curve, the outgoing curve, and - // reflect them over the line formed by taking the unit tangent vector at times - // 0 and 1, respectively, rotated by 90 degrees. - Crossings cross; - - // reflect curves along the line that would be given by beveling the join - Point tang1 = unitTangentAt(reverse(incoming.toSBasis()), 0.); - D2SB newcurve1 = incoming.toSBasis() * reflection(-rot90(tang1), incoming.finalPoint()); - CubicBezier bzr1 = sbasis_to_cubicbezier(reverse(newcurve1)); - - Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0.); - D2SB newcurve2 = outgoing.toSBasis() * reflection(-rot90(tang2), outgoing.initialPoint()); - CubicBezier bzr2 = sbasis_to_cubicbezier(reverse(newcurve2)); - - cross = crossings(bzr1, bzr2); - if (cross.empty()) { - // paths don't cross, fall back to miter - miter_curves(path_builder, outgoing, miter_limit, line_width); - } else { - // reflected join - std::pair sub1 = bzr1.subdivide(cross[0].ta); - std::pair sub2 = bzr2.subdivide(cross[0].tb); - - // TODO it seems as if a bug in 2geom sometimes doesn't catch the first - // crossing of paths, but the second instead; but only sometimes. - path_builder.appendNew (sub1.first[1], sub1.first[2], sub2.second[0]); - path_builder.appendNew (sub2.second[1], sub2.second[2], outgoing.initialPoint()); - } -} - -// Ideal function pointer we want to pass -typedef void JoinFunc(Geom::Path& /*path_builder*/, Geom::Curve const& /*outgoing*/, double /*miter_limit*/, double /*line_width*/); - -/** - * Helper function for repeated logic in outlineHalf. - */ -static void outlineHelper(Geom::Path& path_builder, Geom::PathVector* path_vec, bool outside, double width, double miter, JoinFunc func) -{ - Geom::Curve * cbc2 = path_vec->front()[0].duplicate(); - - if (outside) { - func(path_builder, *cbc2, miter, width); - } else { - joinInside(path_builder, *cbc2); - } - - // store it - Geom::Path temp_path = path_vec->front(); - if (!outside) { - // erase the first segment since the inside join code already appended it - temp_path.erase(temp_path.begin()); - } - - if (temp_path.initialPoint() != path_builder.finalPoint()) { - temp_path.setInitial(path_builder.finalPoint()); - } - - path_builder.append(temp_path); - - delete cbc2; -} - -/** - * Offsets exactly one half of a bezier spline (path). - * @param path_in The input path to use. (To create the other side use path_in.reverse() ) - * @param line_width the line width to use (usually you want to divide this by 2) - * @param miter_limit the miter parameter - * @param func Join function to apply at each join. - */ - -Geom::Path outlineHalf(const Geom::Path& path_in, double line_width, double miter_limit, JoinFunc func) -{ - // NOTE: it is important to notice the distinction between a Geom::Path and a livarot ::Path here! - // if you do not see "Geom::" there is a different function set! - - Geom::PathVector pv = split_at_cusps(path_in); - - ::Path to_outline; - ::Path outlined_result; - - Geom::Path path_builder = Geom::Path(); // the path to store the result in - Geom::PathVector* path_vec; // needed because livarot returns a pointer (TODO make this not a pointer) - - // Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well - const size_t k = pv.size(); - for (size_t u = 0; u < k; u += 2) { - to_outline = Path(); - outlined_result = Path(); - - to_outline.LoadPath(pv[u], Geom::identity(), false, false); - to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); - // now a curve has been outside outlined and loaded into outlined_result - - // get the Geom::Path - path_vec = outlined_result.MakePathVector(); - - // on the first run through, there is no join - if (u == 0) { - path_builder.start(path_vec->front().initialPoint()); - path_builder.append(path_vec->front()); - } else { - outlineHelper(path_builder, path_vec, outside_angle(pv[u-1][pv[u-1].size()-1], pv[u][0]), line_width, miter_limit, func); - } - - // outline the next segment, but don't store it yet - if (path_vec) - delete path_vec; - path_vec = NULL; - - // odd number of paths - if (u < k - 1) { - outlined_result = Path(); - to_outline = Path(); - - to_outline.LoadPath(pv[u+1], Geom::Affine(), false, false); - to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); - - path_vec = outlined_result.MakePathVector(); - outlineHelper(path_builder, path_vec, outside_angle(pv[u][pv[u].size()-1], pv[u+1][0]), line_width, miter_limit, func); - - if (path_vec) - delete path_vec; - path_vec = NULL; - } - } - - if (path_in.closed()) { - Geom::Curve * cbc1; - Geom::Curve * cbc2; - - if ( path_in[path_in.size()].isDegenerate() ) { - // handle case for last segment curved - outlined_result = Path(); - to_outline = Path(); - - Geom::Path oneCurve; oneCurve.append(path_in[0]); - - to_outline.LoadPath(oneCurve, Geom::Affine(), false, false); - to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); - - path_vec = outlined_result.MakePathVector(); - - cbc1 = path_builder[path_builder.size() - 1].duplicate(); - cbc2 = path_vec->front()[0].duplicate(); - - delete path_vec; - } else { - // handle case for last segment straight - // since the path doesn't actually give us access to it, we'll do it ourselves - outlined_result = Path(); - to_outline = Path(); - - Geom::Path oneCurve; oneCurve.append(Geom::LineSegment(path_in.finalPoint(), path_in.initialPoint())); - - to_outline.LoadPath(oneCurve, Geom::Affine(), false, false); - to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); - - path_vec = outlined_result.MakePathVector(); - - cbc1 = path_builder[path_builder.size() - 1].duplicate(); - cbc2 = (*path_vec)[0] [0].duplicate(); - - outlineHelper(path_builder, path_vec, outside_angle(path_in[path_in.size()-1], oneCurve[0]), line_width, miter_limit, func); - - delete cbc1; - cbc1 = cbc2->duplicate(); - delete path_vec; - - oneCurve = Geom::Path(); oneCurve.append(path_in[0]); - - to_outline.LoadPath(oneCurve, Geom::Affine(), false, false); - to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); - - path_vec = outlined_result.MakePathVector(); - delete cbc2; cbc2 = (*path_vec)[0] [0].duplicate(); - delete path_vec; - } - - Geom::Path temporary; - temporary.append(*cbc1); - - Geom::Curve const & prev_curve = path_in[path_in.size()].isDegenerate() ? path_in[path_in.size() - 1] : path_in[path_in.size()]; - Geom::Path isStraight; - isStraight.append(prev_curve); - isStraight.append(path_in[0]); - // does closing path require a join? - if (Geom::split_at_cusps(isStraight).size() > 1) { - bool outside = outside_angle(prev_curve, path_in[0]); - if (outside) { - func(temporary, *cbc2, miter_limit, line_width); - } else { - joinInside(temporary, *cbc2); - path_builder.erase(path_builder.begin()); - } - - // extract the appended curves - path_builder.erase_last(); - if (Geom::are_near(path_builder.finalPoint(), temporary.initialPoint())) { - path_builder.setFinal(temporary.initialPoint()); - } else { - path_builder.appendNew(temporary.initialPoint()); - } - path_builder.append(temporary); - } else { - // closing path does not require a join - path_builder.setFinal(path_builder.initialPoint()); - } - path_builder.close(); - - if (cbc1) delete cbc1; - if (cbc2) delete cbc2; - } - - return path_builder; -} - -Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtTypeMod butt, double miter_lim, bool extrapolate, double start_lean, double end_lean) -{ - Geom::PathVector path_out; - - unsigned pv_size = path_in.size(); - for (unsigned i = 0; i < pv_size; i++) { - - if (path_in[i].size() > 1) { - Geom::Path with_direction; - Geom::Path against_direction; - - with_direction = Outline::outlineHalf(path_in[i], -line_width, miter_lim, extrapolate ? extrapolate_curves : reflect_curves); - against_direction = Outline::outlineHalf(path_in[i].reverse(), -line_width, miter_lim, extrapolate ? extrapolate_curves : reflect_curves); - - Geom::PathBuilder pb; - - pb.moveTo(with_direction.initialPoint()); - pb.append(with_direction); - - //add in our line caps - if (!path_in[i].closed()) { - switch (butt) { - case BUTT_STRAIGHT: - pb.lineTo(against_direction.initialPoint()); - break; - case BUTT_ROUND: - pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() ); - break; - case BUTT_POINTY: { - Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.); - double radius = 0.5 * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint()); - Geom::Point midpoint = 0.5 * (with_direction.finalPoint() + against_direction.initialPoint()) + radius*end_deriv; - pb.lineTo(midpoint); - pb.lineTo(against_direction.initialPoint()); - break; - } - case BUTT_SQUARE: { - Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.); - double radius = 0.5 * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint()); - pb.lineTo(with_direction.finalPoint() + radius*end_deriv); - pb.lineTo(against_direction.initialPoint() + radius*end_deriv); - pb.lineTo(against_direction.initialPoint()); - break; - } - case BUTT_LEANED: { - Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.); - double maxRadius = (end_lean+0.5) * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint()); - double minRadius = ((end_lean*-1)+0.5) * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint()); - pb.lineTo(with_direction.finalPoint() + maxRadius*end_deriv); - pb.lineTo(against_direction.initialPoint() + minRadius*end_deriv); - pb.lineTo(against_direction.initialPoint()); - break; - } - } - } else { - pb.moveTo(against_direction.initialPoint()); - } - - pb.append(against_direction); - - //cap (if necessary) - if (!path_in[i].closed()) { - switch (butt) { - case BUTT_STRAIGHT: - pb.lineTo(with_direction.initialPoint()); - break; - case BUTT_ROUND: - pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() ); - break; - case BUTT_POINTY: { - Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.); - double radius = 0.5 * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint()); - Geom::Point midpoint = 0.5 * (against_direction.finalPoint() + with_direction.initialPoint()) + radius*end_deriv; - pb.lineTo(midpoint); - pb.lineTo(with_direction.initialPoint()); - break; - } - case BUTT_SQUARE: { - Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.); - double radius = 0.5 * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint()); - pb.lineTo(against_direction.finalPoint() + radius*end_deriv); - pb.lineTo(with_direction.initialPoint() + radius*end_deriv); - pb.lineTo(with_direction.initialPoint()); - break; - } - case BUTT_LEANED: { - Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.); - double maxRadius = (start_lean+0.5) * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint()); - double minRadius = ((start_lean*-1)+0.5) * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint()); - pb.lineTo(against_direction.finalPoint() + minRadius*end_deriv); - pb.lineTo(with_direction.initialPoint() + maxRadius*end_deriv); - pb.lineTo(with_direction.initialPoint()); - break; - } - } - } - pb.flush(); - path_out.push_back(pb.peek()[0]); - if (path_in[i].closed()) { - path_out.push_back(pb.peek()[1]); - } - } else { - Path p = Path(); - Path outlinepath = Path(); - ButtType original_butt; - switch (butt) { - case BUTT_STRAIGHT: - original_butt = butt_straight; - break; - case BUTT_ROUND: - original_butt = butt_round; - break; - case butt_pointy: { - original_butt = butt_pointy; - break; - } - case BUTT_SQUARE: { - original_butt = butt_square; - break; - } - case BUTT_LEANED: { - original_butt = butt_straight; - break; - } - } - p.LoadPath(path_in[i], Geom::Affine(), false, false); - p.Outline(&outlinepath, line_width / 2, static_cast(join), original_butt, miter_lim); - Geom::PathVector *pv_p = outlinepath.MakePathVector(); - //somewhat hack-ish - path_out.push_back( (*pv_p)[0].reverse() ); - if (pv_p) delete pv_p; - } - } - return path_out; -} - -#define miter_lim fabs(line_width * miter_limit) - -Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line_width, ButtTypeMod linecap_type, LineJoinType linejoin_type, double miter_limit, double start_lean, double end_lean) -{ - std::vector path_out = std::vector(); - if (path_in.empty()) { - return path_out; - } - Path p = Path(); - Path outlinepath = Path(); - for (unsigned i = 0; i < path_in.size(); i++) { - p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true)); - } - - // magic! - ButtType original_butt; - switch (linecap_type) { - case BUTT_STRAIGHT: - original_butt = butt_straight; - break; - case BUTT_ROUND: - original_butt = butt_round; - break; - case butt_pointy: { - original_butt = butt_pointy; - break; - } - case BUTT_SQUARE: { - original_butt = butt_square; - break; - } - case BUTT_LEANED: { - original_butt = butt_straight; - break; - } - } - if (linejoin_type <= LINEJOIN_POINTY) { - p.Outline(&outlinepath, line_width / 2, static_cast(linejoin_type), - original_butt, miter_lim); - // fix memory leak - std::vector *pv_p = outlinepath.MakePathVector(); - path_out = *pv_p; - delete pv_p; - - } else if (linejoin_type == LINEJOIN_REFLECTED) { - // reflected arc join - path_out = outlinePath(path_in, line_width, static_cast(linejoin_type), - linecap_type , miter_lim, false, start_lean, end_lean); - - } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) { - // extrapolated arc join - path_out = outlinePath(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim, true, start_lean, end_lean); - } - - return path_out; -} - -Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit) -{ - - Geom::Path path_out; - - if (linejoin_type <= LINEJOIN_POINTY || path_in.size() <= 1) { - - Geom::PathVector * pathvec; - - Path path_tangent = Path(); - Path path_outline = Path(); - path_outline.LoadPath(path_in, Geom::Affine(), false, false); - path_outline.OutsideOutline(&path_tangent, line_width / 2, static_cast(linejoin_type), butt_straight, miter_lim); - - pathvec = path_tangent.MakePathVector(); - path_out = pathvec->front(); - delete pathvec; - return path_out; - } else if (linejoin_type == LINEJOIN_REFLECTED) { - path_out = outlineHalf(path_in, line_width, miter_lim, reflect_curves); - return path_out; - } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) { - path_out = outlineHalf(path_in, line_width, miter_lim, extrapolate_curves); - return path_out; - } - return path_out; -} - -} // namespace Outline - -/* - 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/live_effects/pathoutlineprovider.h b/src/live_effects/pathoutlineprovider.h deleted file mode 100644 index c17584be2..000000000 --- a/src/live_effects/pathoutlineprovider.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef SEEN_PATH_OUTLINE_H -#define SEEN_PATH_OUTLINE_H - -/* Author: - * Liam P. White - * - * Copyright (C) 2014 Author - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include -#include - -enum LineJoinType { - LINEJOIN_STRAIGHT, - LINEJOIN_ROUND, - LINEJOIN_POINTY, - LINEJOIN_REFLECTED, - LINEJOIN_EXTRAPOLATED -}; -enum ButtTypeMod { - BUTT_STRAIGHT, - BUTT_ROUND, - BUTT_SQUARE, - BUTT_POINTY, - BUTT_LEANED -}; - -namespace Geom -{ - Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2 const & sbasis_in); - std::vector split_at_cusps(const Geom::Path& in); -} - -namespace Outline -{ - unsigned bezierOrder (const Geom::Curve* curve_in); - std::vector PathVectorOutline(std::vector const & path_in, double line_width, ButtTypeMod linecap_type, - LineJoinType linejoin_type, double miter_limit, double start_lean = 0, double end_lean = 0); - Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit); -} - -#endif // SEEN_PATH_OUTLINE_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 : -- cgit v1.2.3