From 9a04c985ec628dc749a8a82d94bcf47d482a4f63 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Thu, 20 Mar 2014 17:09:57 -0400 Subject: Fix a linker error ("static") (hopefully) Resolve compiler errors with GTK3+ (bzr r13090.1.28) --- src/live_effects/pathoutlineprovider.cpp | 840 +++++++++++++++++++++++++++++++ 1 file changed, 840 insertions(+) create mode 100755 src/live_effects/pathoutlineprovider.cpp (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp new file mode 100755 index 000000000..e62f516c9 --- /dev/null +++ b/src/live_effects/pathoutlineprovider.cpp @@ -0,0 +1,840 @@ +#include "pathoutlineprovider.h" + +#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> + +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)); + } + + static std::vector split_at_cusps(const Geom::Path& in) + { + Geom::PathVector out = Geom::PathVector(); + Geom::Path temp = Geom::Path(); + + for (unsigned path_descr = 0; path_descr < in.size(); path_descr++) + { + temp = Geom::Path(); + temp.append(in[path_descr]); + out.push_back(temp); + } + + return out; + } + + static 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 Outline +{ + +typedef Geom::D2 D2SB; +typedef Geom::Piecewise PWD2; + +unsigned bezierOrder (const Geom::Curve* curve_in) +{ + using namespace Geom; + //cast it + const CubicBezier *cbc = dynamic_cast(curve_in); + if (cbc) return 3; + const QuadraticBezier * qbc = dynamic_cast(curve_in); + if (qbc) return 2; + const BezierCurveN<1U> * lbc = dynamic_cast *>(curve_in); + if (lbc) return 1; + return 0; +} + +//returns true if the angle formed by the curves and their handles +//is >180 clockwise, otherwise false. +bool outside_angle (const Geom::Curve* cbc1, const Geom::Curve* cbc2) +{ + Geom::Point start_point = cbc1->initialPoint(); + Geom::Point end_point = cbc2->finalPoint(); + unsigned order = bezierOrder(cbc1); + switch (order) + { + case 3: + start_point = ( dynamic_cast(cbc1) )->operator [] (2); + break; + case 2: + start_point = ( dynamic_cast(cbc1) )->operator [] (1); + break; + } + order = bezierOrder(cbc2); + switch (order) + { + case 3: + end_point = ( dynamic_cast(cbc2) )->operator [] (1); + break; + case 2: + end_point = ( dynamic_cast(cbc2) )->operator[] (1); + break; + } + return false; +} + +void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve*cbc2, Geom::Point endPt, double miter_limit) +{ + + Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2); + if (cross.empty()) + { + Geom::Path pth; + pth.append(*cbc1); + + //Geom::Point tang1 = Geom::unitTangentAt(pth.toPwSb()[0], 1); + + pth = Geom::Path(); + pth.append( *cbc2 ); + Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); + + + Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(cbc1->toSBasis()), 0.); + Geom::Circle circle2 = Geom::touching_circle(cbc2->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(cbc1->finalPoint(), 0.5*(cbc1->finalPoint()+sol), sol, true); + Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); + + if (arc0) + { + path_builder.append (arc0->toSBasis()); + delete arc0; + arc0 = NULL; + } + + if (arc1) + { + path_builder.append (arc1->toSBasis()); + delete arc1; + arc1 = NULL; + } + } + else + { + path_builder.appendNew (endPt); + } + } + else + { + path_builder.appendNew (endPt); + } +} + Geom::Path half_outline_extrp(const Geom::Path& path_in, double line_width, ButtType linecap_type, double miter_limit) + { + Geom::PathVector pv = split_at_cusps(path_in); + unsigned m; + Path path_outline = Path(); + Path path_tangent = Path(); + + Geom::Point initialPoint; + Geom::Point endPoint; + + Geom::Path path_builder = Geom::Path(); + Geom::PathVector * pathvec; + + //load the first portion in before the loop starts + { + path_outline = Path(); + path_outline.LoadPath(pv[0], Geom::Affine(), false, false); + path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10); + //now half of first cusp has been loaded + + pathvec = path_tangent.MakePathVector(); + path_tangent = Path(); + + //instead of array accessing twice, dereferencing used for clarity + initialPoint = (*pathvec)[0].initialPoint(); + + path_builder.start(initialPoint); + path_builder.append( (*pathvec)[0] ); + + path_outline = Path(); + path_outline.LoadPath(pv[1], Geom::Affine(), false, false); + path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10); + + delete pathvec; pathvec = NULL; + pathvec = path_tangent.MakePathVector(); + path_tangent = Path(); + + Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate(); + Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate(); + + extrapolate_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit ); + + path_builder.append( (*pathvec)[0] ); + + //always set pointers null after deleting + delete pathvec; pathvec = NULL; + delete cbc1; delete cbc2; cbc1 = cbc2 = NULL; + } + + for (m = 2; m < pv.size(); m++) + { + path_outline = Path(); + path_outline.LoadPath(pv[m], Geom::Affine(), false, false); + path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10); + + delete pathvec; pathvec = NULL; + pathvec = path_tangent.MakePathVector(); + + Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate(); + Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate(); + + extrapolate_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit ); + path_builder.append( (*pathvec)[0] ); + + delete pathvec; pathvec = NULL; + delete cbc1; delete cbc2; cbc1 = cbc2 = NULL; + } + + return path_builder; + } + + //Create a reflected outline join. + //Note: it is generally recommended to let half_outline do this for you! + //path_builder: the path to append the curves to + //cbc1: the curve before the join + //cbc2: the curve after the join + //endPt: the point to end at + //miter_limit: the miter parameter + void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit) + { + //the most important work for the reflected join is done here + + //determine where we are in the path. If we're on the inside, ignore + //and just lineTo. On the outside, we'll do a little reflection magic :) + Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2); + if (cross.empty()) + { + //probably on the outside of the corner + Geom::Path pth; + pth.append(*cbc1); + + Geom::Point tang1 = Geom::unitTangentAt(pth.toPwSb()[0], 1); + + //reflect curves along the bevel + D2SB newcurve1 = pth.toPwSb()[0] * + Geom::reflection ( -Geom::rot90(tang1) , + cbc1->finalPoint() ); + + Geom::CubicBezier bzr1 = sbasis_to_cubicbezier(Geom::reverse(newcurve1)); + + pth = Geom::Path(); + pth.append( *cbc2 ); + Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); + + D2SB newcurve2 = pth.toPwSb()[0] * + Geom::reflection ( -Geom::rot90(tang2) , + cbc2->initialPoint() ); + Geom::CubicBezier bzr2 = sbasis_to_cubicbezier(Geom::reverse(newcurve2)); + + cross = Geom::crossings(bzr1, bzr2); + if ( cross.empty() ) + { + //std::cout << "Oops, no crossings!" << std::endl; + //curves didn't cross; default to miter + /*boost::optional p = intersection_point (cbc1->finalPoint(), tang1, + cbc2->initialPoint(), tang2); + if (p) + { + path_builder.appendNew (*p); + }*/ + //bevel + path_builder.appendNew( endPt ); + } + else + { + //join + std::pair sub1 = bzr1.subdivide(cross[0].ta); + std::pair sub2 = bzr2.subdivide(cross[0].tb); + + //@TODO joins have a strange tendency to cross themselves twice. Check this. + + //sections commented out are for general stability + path_builder.appendNew (sub1.first[1], sub1.first[2], /*sub1.first[3]*/ sub2.second[0] ); + path_builder.appendNew (sub2.second[1], sub2.second[2], /*sub2.second[3]*/ endPt ); + } + } + else // cross.empty() + { + //probably on the inside of the corner + path_builder.appendNew ( endPt ); + } + } + + /** @brief Converts a path to one half of an outline. + * path_in: The input path to use. (To create the other side use path_in.reverse() ) + * line_width: the line width to use (usually you want to divide this by 2) + * linecap_type: (not used here) the cap to apply. Passed to libvarot. + * miter_limit: the miter parameter + */ + Geom::Path half_outline(const Geom::Path& path_in, double line_width, ButtType linecap_type, double miter_limit) + { + Geom::PathVector pv = split_at_cusps(path_in); + unsigned m; + Path path_outline = Path(); + Path path_tangent = Path(); + //needed for closing the path + Geom::Point initialPoint; + Geom::Point endPoint; + + //some issues prevented me from using a PathBuilder here + //it seems like PathBuilder::peek() gave me a null reference exception + //and I was unable to get a stack trace on Windows, so had to switch to Linux + //to see what the hell was wrong. :( + //I wasted five hours opening it in IDAPro, VS2012, and GDB Windows + + /*Program received signal SIGSEGV, Segmentation fault. + 0x00000000006539ac in get_curves (this=0x0) + at /usr/include/c++/4.6/bits/locale_facets.h:1077 + 1077 { return __c; } + */ + + Geom::Path path_builder = Geom::Path(); + Geom::PathVector * pathvec; + + //load the first portion in before the loop starts + { + path_outline = Path(); + path_outline.LoadPath(pv[0], Geom::Affine(), false, false); + path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10); + //now half of first cusp has been loaded + + pathvec = path_tangent.MakePathVector(); + path_tangent = Path(); + + //instead of array accessing twice, dereferencing used for clarity + initialPoint = (*pathvec)[0].initialPoint(); + + path_builder.start(initialPoint); + path_builder.append( (*pathvec)[0] ); + + path_outline = Path(); + path_outline.LoadPath(pv[1], Geom::Affine(), false, false); + path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10); + + delete pathvec; pathvec = NULL; + pathvec = path_tangent.MakePathVector(); + path_tangent = Path(); + + Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate(); + Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate(); + + reflect_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit ); + + path_builder.append( (*pathvec)[0] ); + + //always set pointers null after deleting + delete pathvec; pathvec = NULL; + delete cbc1; delete cbc2; cbc1 = cbc2 = NULL; + } + + for (m = 2; m < pv.size(); m++) + { + path_outline = Path(); + path_outline.LoadPath(pv[m], Geom::Affine(), false, false); + path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10); + + delete pathvec; pathvec = NULL; + pathvec = path_tangent.MakePathVector(); + + Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate(); + Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate(); + + reflect_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit ); + path_builder.append( (*pathvec)[0] ); + + delete pathvec; pathvec = NULL; + delete cbc1; delete cbc2; cbc1 = cbc2 = NULL; + } + + return path_builder; + } + + Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, JoinType join, ButtType butt, double miter_lim) + { + 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)); + } + + Geom::PathVector path_out; + for (unsigned lmnop = 0; lmnop < path_in.size(); lmnop++) + { + if (path_in[lmnop].size() > 1) + { + Geom::Path p_init; + Geom::Path p_rev; + Geom::PathBuilder pb = Geom::PathBuilder(); + + if ( !path_in[lmnop].closed() ) + { + p_init = Outline::half_outline( path_in[lmnop], -line_width, butt, + miter_lim ); + p_rev = Outline::half_outline( path_in[lmnop].reverse(), -line_width, butt, + miter_lim ); + + pb.moveTo(p_init.initialPoint() ); + pb.append(p_init); + + //cap + if (butt == butt_straight) { + pb.lineTo(p_rev.initialPoint() ); + } else if (butt == butt_round) { + pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_rev.initialPoint() ); + } else if (butt == butt_square) { + //don't know what to do + pb.lineTo(p_rev.initialPoint() ); + } else if (butt == butt_pointy) { + //don't know what to do + pb.lineTo(p_rev.initialPoint() ); + } + + pb.append(p_rev); + + if (butt == butt_straight) { + pb.lineTo(p_init.initialPoint() ); + } else if (butt == butt_round) { + pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_init.initialPoint() ); + } else if (butt == butt_square) { + //don't know what to do + pb.lineTo(p_init.initialPoint() ); + } else if (butt == butt_pointy) { + //don't know what to do + //Geom::Point end_deriv = Geom::unitTangentAt( Geom::reverse(path_in[lmnop].toPwSb()[path_in[lmnop].size()]), 0); + //double radius = 0.5 * Geom::distance(path_in[lmnop].finalPoint(), p_rev.initialPoint()); + + pb.lineTo(p_init.initialPoint() ); + } + } + else + { + //final join + //refer to half_outline for documentation + Geom::Path p_almost = path_in[lmnop]; + p_almost.appendNew ( path_in[lmnop].initialPoint() ); + p_init = Outline::half_outline( p_almost, -line_width, butt, + miter_lim ); + p_rev = Outline::half_outline( p_almost.reverse(), -line_width, butt, + miter_lim ); + p.LoadPath(path_in[lmnop], Geom::Affine(), false, false); + + //this is a kludge, because I can't find how to make this work properly + bool lastIsLinear = ( (Geom::distance(path_in[lmnop].finalPoint(), + path_in[lmnop] [path_in[lmnop].size() - 1].finalPoint())) == + (path_in[lmnop] [path_in[lmnop].size()].length())); + + p_almost = p_init; + if (lastIsLinear) + { + p_almost.erase_last(); p_almost.erase_last(); + } + + //outside test + Geom::Curve* cbc1 = p_almost[p_almost.size() - 1].duplicate(); + Geom::Curve* cbc2 = p_almost[0].duplicate(); + + Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2); + + if (cross.empty()) + { + //this is the outside path + + //reuse the old one + p_init = p_almost; + Outline::reflect_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim ); + pb.moveTo(p_init.initialPoint()); pb.append(p_init); + } + else + { + //inside, carry on :-) + pb.moveTo(p_almost.initialPoint()); pb.append(p_almost); + } + + p_almost = p_rev; + if (lastIsLinear) + { + p_almost.erase(p_almost.begin() ); + p_almost.erase(p_almost.begin() ); + } + + delete cbc1; delete cbc2; cbc1 = cbc2 = NULL; + + cbc1 = p_almost[p_almost.size() - 1].duplicate(); + cbc2 = p_almost[0].duplicate(); + + cross = Geom::crossings(*cbc1, *cbc2); + + if (cross.empty()) + { + //outside path + + p_init = p_almost; + reflect_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim ); + pb.moveTo(p_init.initialPoint()); pb.append(p_init); + } + else + { + //inside + pb.moveTo(p_almost.initialPoint()); pb.append(p_almost); + } + delete cbc1; delete cbc2; cbc1 = cbc2 = NULL; + } + //pb.closePath(); + pb.flush(); + Geom::PathVector pv_np = pb.peek(); + //hack + for (unsigned abcd = 0; abcd < pv_np.size(); abcd++) + { + path_out.push_back( pv_np[abcd] ); + } + } + else + { + p.LoadPath(path_in[lmnop], Geom::Affine(), false, false); + p.Outline(&outlinepath, line_width / 2, join, butt, miter_lim); + std::vector *pv_p = outlinepath.MakePathVector(); + //hack + path_out.push_back( (*pv_p)[0].reverse() ); + delete pv_p; + } + } + return path_out; + } + Geom::PathVector outlinePath_extr(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtType butt, double miter_lim) + { + 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)); + } + + Geom::PathVector path_out; + for (unsigned lmnop = 0; lmnop < path_in.size(); lmnop++) + { + if (path_in[lmnop].size() > 1) + { + Geom::Path p_init; + Geom::Path p_rev; + Geom::PathBuilder pb = Geom::PathBuilder(); + + if ( !path_in[lmnop].closed() ) + { + p_init = Outline::half_outline_extrp( path_in[lmnop], -line_width, butt, + miter_lim ); + p_rev = Outline::half_outline_extrp( path_in[lmnop].reverse(), -line_width, butt, + miter_lim ); + + pb.moveTo(p_init.initialPoint() ); + pb.append(p_init); + + //cap + if (butt == butt_straight) { + pb.lineTo(p_rev.initialPoint() ); + } else if (butt == butt_round) { + pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_rev.initialPoint() ); + } else if (butt == butt_square) { + //don't know what to do + pb.lineTo(p_rev.initialPoint() ); + } else if (butt == butt_pointy) { + //don't know what to do + pb.lineTo(p_rev.initialPoint() ); + } + + pb.append(p_rev); + + if (butt == butt_straight) { + pb.lineTo(p_init.initialPoint() ); + } else if (butt == butt_round) { + pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_init.initialPoint() ); + } else if (butt == butt_square) { + //don't know what to do + pb.lineTo(p_init.initialPoint() ); + } else if (butt == butt_pointy) { + //don't know what to do + //Geom::Point end_deriv = Geom::unitTangentAt( Geom::reverse(path_in[lmnop].toPwSb()[path_in[lmnop].size()]), 0); + //double radius = 0.5 * Geom::distance(path_in[lmnop].finalPoint(), p_rev.initialPoint()); + + pb.lineTo(p_init.initialPoint() ); + } + } + else + { + //final join + //refer to half_outline for documentation + Geom::Path p_almost = path_in[lmnop]; + p_almost.appendNew ( path_in[lmnop].initialPoint() ); + p_init = Outline::half_outline_extrp( p_almost, -line_width, butt, + miter_lim ); + p_rev = Outline::half_outline_extrp( p_almost.reverse(), -line_width, butt, + miter_lim ); + p.LoadPath(path_in[lmnop], Geom::Affine(), false, false); + + //this is a kludge, because I can't find how to make this work properly + bool lastIsLinear = ( (Geom::distance(path_in[lmnop].finalPoint(), + path_in[lmnop] [path_in[lmnop].size() - 1].finalPoint())) == + (path_in[lmnop] [path_in[lmnop].size()].length())); + + p_almost = p_init; + if (lastIsLinear) + { + p_almost.erase_last(); p_almost.erase_last(); + } + + //outside test + Geom::Curve* cbc1 = p_almost[p_almost.size() - 1].duplicate(); + Geom::Curve* cbc2 = p_almost[0].duplicate(); + + Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2); + + if (cross.empty()) + { + //this is the outside path + + //reuse the old one + p_init = p_almost; + Outline::extrapolate_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim ); + pb.moveTo(p_init.initialPoint()); pb.append(p_init); + } + else + { + //inside, carry on :-) + pb.moveTo(p_almost.initialPoint()); pb.append(p_almost); + } + + p_almost = p_rev; + if (lastIsLinear) + { + p_almost.erase(p_almost.begin() ); + p_almost.erase(p_almost.begin() ); + } + + delete cbc1; delete cbc2; cbc1 = cbc2 = NULL; + + cbc1 = p_almost[p_almost.size() - 1].duplicate(); + cbc2 = p_almost[0].duplicate(); + + cross = Geom::crossings(*cbc1, *cbc2); + + if (cross.empty()) + { + //outside path + + p_init = p_almost; + extrapolate_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim ); + pb.moveTo(p_init.initialPoint()); pb.append(p_init); + } + else + { + //inside + pb.moveTo(p_almost.initialPoint()); pb.append(p_almost); + } + delete cbc1; delete cbc2; cbc1 = cbc2 = NULL; + } + //pb.closePath(); + pb.flush(); + Geom::PathVector pv_np = pb.peek(); + //hack + for (unsigned abcd = 0; abcd < pv_np.size(); abcd++) + { + path_out.push_back( pv_np[abcd] ); + } + } + else + { + p.LoadPath(path_in[lmnop], Geom::Affine(), false, false); + p.Outline(&outlinepath, line_width / 2, join_pointy, butt, miter_lim); + std::vector *pv_p = outlinepath.MakePathVector(); + //hack + path_out.push_back( (*pv_p)[0].reverse() ); + delete pv_p; + } + } + return path_out; + } + +Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line_width, ButtType linecap_type, + join_typ linejoin_type, double miter_limit) +{ + 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)); + } + + #define miter_lim fabs(line_width * miter_limit) + + //magic! + if (linejoin_type <= 2) + { + p.Outline(&outlinepath, line_width / 2, linejoin_type, + linecap_type, miter_lim); + //fix memory leak + std::vector *pv_p = outlinepath.MakePathVector(); + path_out = *pv_p; + delete pv_p; + + } else if (linejoin_type == 3) { + //reflected arc join + path_out = outlinePath(path_in, line_width, linejoin_type, + linecap_type , miter_lim); + + } else if (linejoin_type == 4) { + //extrapolated arc join + path_out = outlinePath_extr(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim); + + } + + #undef miter_lim + 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:textwidth=99 : -- cgit v1.2.3 From 7d6a2eac770b159a51183add6917eaec36729d80 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sun, 23 Mar 2014 17:25:49 -0400 Subject: tiny bugfix for Taper Strokes (bzr r13090.1.30) --- src/live_effects/pathoutlineprovider.cpp | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index e62f516c9..302756c7c 100755 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -825,6 +825,39 @@ Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line #undef miter_lim return path_out; } +Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit) +{ + + #define miter_lim fabs(line_width * 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[0]/* deref pointer */[0]/*actual object ref*/; + delete pathvec; + return path_out; + } + else if (linejoin_type == LINEJOIN_REFLECTED) { + //reflected half outline + Geom::PathVector pathvec; pathvec.push_back(path_in); + path_out = half_outline(path_in, line_width, butt_straight, miter_lim); + return path_out; + } + else if (linejoin_type == LINEJOIN_EXTRAPOLATED) { + path_out = half_outline_extrp(path_in, line_width, butt_straight, miter_lim); + return path_out; + } + return path_out; +} } // namespace Outline -- cgit v1.2.3 From 8a082e274bc322596c217bbe7770904a3cb50cc2 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sat, 29 Mar 2014 15:28:44 -0400 Subject: Fix build errors and a few small bugs (bzr r13090.1.36) --- src/live_effects/pathoutlineprovider.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index a5a7a044f..2a0da05b4 100755 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -220,8 +220,8 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit, bool outside = false) { - - if ( outside ) { + bool lineProblem = (dynamic_cast *>(cbc1)) || (dynamic_cast *>(cbc2)); + if ( outside && !lineProblem ) { Geom::Path pth; pth.append(*cbc1); @@ -273,9 +273,26 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } path_builder.appendNew (endPt); } - } else { + } + if ( outside && lineProblem ) { + Geom::Path pth; + pth.append(*cbc1); + Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(pth.toPwSb()[0]), 0.); + pth = Geom::Path(); + pth.append( *cbc2 ); + Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); + + boost::optional p = intersection_point (cbc1->finalPoint(), tang1, + cbc2->initialPoint(), tang2); + if (p) + { + path_builder.appendNew (*p); + } path_builder.appendNew (endPt); - } + } + if ( !outside ) { + path_builder.appendNew (endPt); + } } void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit, bool outside = false) -- cgit v1.2.3 From e667170b1473faccbeabece0cef7d24ebf15bef9 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sat, 29 Mar 2014 20:33:36 -0400 Subject: Minor fixes (bzr r13090.1.37) --- src/live_effects/pathoutlineprovider.cpp | 79 +++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 16 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index 2a0da05b4..6a47285a0 100755 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -1,5 +1,5 @@ #include "pathoutlineprovider.h" - +#include "livarot/path-description.h" #include <2geom/angle.h> #include <2geom/path.h> #include <2geom/circle.h> @@ -7,6 +7,7 @@ #include <2geom/shape.h> #include <2geom/transforms.h> #include <2geom/path-sink.h> +#include namespace Geom { @@ -163,7 +164,10 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) Geom::Point end_point; //assert(cbc1.finalPoint() == cbc2.initialPoint()); + //short circuiting? if (cbc1.finalPoint() != cbc2.initialPoint()) { + printf("There was an issue when asserting that one curve's end is the start of the other. Line %d, File %s\n" + "By default we are going to say that this is an inside join, so we cannot make a line join for it.\n", __LINE__, __FILE__); return false; } switch (order) @@ -218,7 +222,8 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) return false; } -void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit, bool outside = false) +void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, + double miter_limit, double line_width, bool outside = false) { bool lineProblem = (dynamic_cast *>(cbc1)) || (dynamic_cast *>(cbc2)); if ( outside && !lineProblem ) { @@ -269,7 +274,13 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve cbc2->initialPoint(), tang2); if (p) { - path_builder.appendNew (*p); + //check size of miter + Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; + Geom::Coord len = distance(*p, point_on_path); + if (len <= miter_limit) { + // miter OK + path_builder.appendNew (*p); + } } path_builder.appendNew (endPt); } @@ -286,7 +297,13 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve cbc2->initialPoint(), tang2); if (p) { - path_builder.appendNew (*p); + //check size of miter + Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; + Geom::Coord len = distance(*p, point_on_path); + if (len <= miter_limit) { + // miter OK + path_builder.appendNew (*p); + } } path_builder.appendNew (endPt); } @@ -295,7 +312,8 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } } -void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit, bool outside = false) +void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, + double miter_limit, double line_width, bool outside = false) { //the most important work for the reflected join is done here @@ -332,7 +350,13 @@ void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cb cbc2->initialPoint(), tang2); if (p) { - path_builder.appendNew (*p); + //check size of miter + Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; + Geom::Coord len = distance(*p, point_on_path); + if (len <= miter_limit) { + // miter OK + path_builder.appendNew (*p); + } } //bevel path_builder.appendNew( endPt ); @@ -395,10 +419,10 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate(); //do the reflection/extrapolation: - if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, - outside_angle ( pv[u - 1] [pv[u - 1].size()], pv[u] [0] )); } - else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, - outside_angle ( pv[u - 1] [pv[u - 1].size()], pv[u] [0] )); } + if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); } + else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); } } path_builder.append( (*path_vec)[0] ); @@ -420,9 +444,9 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate(); //do the reflection/extrapolation: - if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, + if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); } - else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, + else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); } //Now we can store it. @@ -449,10 +473,33 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, //since you've made it this far, hopefully all this is obvious :P Geom::Path with_direction; Geom::Path against_direction; - - with_direction = Outline::doAdvHalfOutline( path_in[i], -line_width, miter_lim, extrapolate ); - against_direction = Outline::doAdvHalfOutline( path_in[i].reverse(), -line_width, extrapolate ); - + if ( !path_in[i].closed() ) { + with_direction = Outline::doAdvHalfOutline( path_in[i], -line_width, miter_lim, extrapolate ); + against_direction = Outline::doAdvHalfOutline( path_in[i].reverse(), -line_width, miter_lim, extrapolate ); + } else { + //Geom::Path absolutely refuses to do what I want with these + Geom::Path newPath = path_in[i]; + newPath.close(false); + Geom::Piecewise > pwd2 = newPath.toPwSb(); + newPath = Geom::path_from_piecewise(pwd2, 0.01)[0]; + //fuk this + with_direction = Outline::doAdvHalfOutline( newPath, -line_width, miter_lim, extrapolate ); + against_direction = Outline::doAdvHalfOutline( newPath.reverse(), -line_width, miter_lim, extrapolate ); + + /*if (dynamic_cast *>(&newPath[newPath.size()])) { + //delete the 'Z' + newPath.erase_last(); + newPath.append(path_in[i][path_in[i].size() - 1]); + newPath.appendNew(newPath.initialPoint()); + newPath.erase_last(); + } else { + //delete the 'Z' + newPath.erase_last(); + newPath.append(path_in[i][path_in[i].size() - 1]); + newPath.appendNew(newPath.initialPoint()); + newPath.erase_last(); + }*/ + } Geom::PathBuilder pb; //add in the...do I really need to say this? -- cgit v1.2.3 From d20b73d611b6de99e0e0697ee47a760de437ee97 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Wed, 2 Apr 2014 17:18:40 -0400 Subject: Clean up code (bzr r13090.1.42) --- src/live_effects/pathoutlineprovider.cpp | 521 +++++++++++++++---------------- 1 file changed, 258 insertions(+), 263 deletions(-) mode change 100755 => 100644 src/live_effects/pathoutlineprovider.cpp (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp old mode 100755 new mode 100644 index 6a47285a0..ad39a7416 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -9,133 +9,128 @@ #include <2geom/path-sink.h> #include -namespace Geom +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 ) { - /** - * 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)); - } - - static std::vector split_at_cusps(const Geom::Path& in) - { - Geom::PathVector out = Geom::PathVector(); - Geom::Path temp = Geom::Path(); - - for (unsigned path_descr = 0; path_descr < in.size(); path_descr++) - { - temp = Geom::Path(); - temp.append(in[path_descr]); - out.push_back(temp); - } - - return out; - } - - static 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; - } + 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 Outline +static std::vector split_at_cusps(const Geom::Path& in) { + Geom::PathVector out = Geom::PathVector(); + Geom::Path temp = Geom::Path(); + + for (unsigned path_descr = 0; path_descr < in.size(); path_descr++) { + temp = Geom::Path(); + temp.append(in[path_descr]); + out.push_back(temp); + } + + return out; +} + +static 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 Outline { typedef Geom::D2 D2SB; typedef Geom::Piecewise PWD2; @@ -160,72 +155,70 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) unsigned order = bezierOrder(&cbc1); Geom::Point start_point; - Geom::Point cross_point = cbc1.finalPoint(); - Geom::Point end_point; - - //assert(cbc1.finalPoint() == cbc2.initialPoint()); - //short circuiting? - if (cbc1.finalPoint() != cbc2.initialPoint()) { - printf("There was an issue when asserting that one curve's end is the start of the other. Line %d, File %s\n" - "By default we are going to say that this is an inside join, so we cannot make a line join for it.\n", __LINE__, __FILE__); - return false; + Geom::Point cross_point = cbc1.finalPoint(); + Geom::Point end_point; + + //assert(cbc1.finalPoint() == cbc2.initialPoint()); + //short circuiting? + if (cbc1.finalPoint() != cbc2.initialPoint()) { + printf("There was an issue when asserting that one curve's end is the start of the other. Line %d, File %s\n" + "By default we are going to say that this is an inside join, so we cannot make a line join for it.\n", __LINE__, __FILE__); + return false; + } + switch (order) { + case 3: + start_point = (static_cast(&cbc1))->operator[] (2); + //major league b***f***ing + if (are_near(start_point, cross_point, 0.0000001)) { + start_point = (static_cast(&cbc1))->operator[] (1); } - switch (order) - { - case 3: - start_point = (static_cast(&cbc1))->operator[] (2); - //major league b***f***ing - if (are_near(start_point, cross_point, 0.0000001)) { - start_point = (static_cast(&cbc1))->operator[] (1); - } - break; - case 2: - //this never happens - start_point = (static_cast(&cbc1))->operator[] (1); - break; - case 1: - default: - start_point = cbc1.initialPoint(); - } - - order = Outline::bezierOrder(&cbc2); - - switch (order) - { - case 3: - end_point = (static_cast(&cbc2))->operator[] (1); - if (are_near(end_point, cross_point, 0.0000001)) { - end_point = (static_cast(&cbc2))->operator[] (2); - } - break; - case 2: - end_point = (static_cast(&cbc2))->operator[] (1); - break; - case 1: - default: - end_point = cbc2.finalPoint(); - } - //got our three points, now let's see what their clockwise angle is - - //Much credit to Wikipedia for the following ( http://en.wikipedia.org/wiki/Graham_scan ) - /******************************************************************** + break; + case 2: + //this never happens + start_point = (static_cast(&cbc1))->operator[] (1); + break; + case 1: + default: + start_point = cbc1.initialPoint(); + } + + order = Outline::bezierOrder(&cbc2); + + switch (order) { + case 3: + end_point = (static_cast(&cbc2))->operator[] (1); + if (are_near(end_point, cross_point, 0.0000001)) { + end_point = (static_cast(&cbc2))->operator[] (2); + } + break; + case 2: + end_point = (static_cast(&cbc2))->operator[] (1); + break; + case 1: + default: + end_point = cbc2.finalPoint(); + } + //got our three points, now let's see what their clockwise angle is + + //Much credit to Wikipedia for the following ( http://en.wikipedia.org/wiki/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) + 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()) ); if (ccw > 0) return true; return false; } -void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, +void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit, double line_width, bool outside = false) { - bool lineProblem = (dynamic_cast *>(cbc1)) || (dynamic_cast *>(cbc2)); + bool lineProblem = (dynamic_cast *>(cbc1)) || (dynamic_cast *>(cbc2)); if ( outside && !lineProblem ) { Geom::Path pth; pth.append(*cbc1); @@ -271,9 +264,8 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } } else { boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) - { + cbc2->initialPoint(), tang2); + if (p) { //check size of miter Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; Geom::Coord len = distance(*p, point_on_path); @@ -284,19 +276,18 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } path_builder.appendNew (endPt); } - } - if ( outside && lineProblem ) { - Geom::Path pth; + } + if ( outside && lineProblem ) { + Geom::Path pth; pth.append(*cbc1); - Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(pth.toPwSb()[0]), 0.); + Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(pth.toPwSb()[0]), 0.); pth = Geom::Path(); - pth.append( *cbc2 ); - Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); - - boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) - { + pth.append( *cbc2 ); + Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); + + boost::optional p = intersection_point (cbc1->finalPoint(), tang1, + cbc2->initialPoint(), tang2); + if (p) { //check size of miter Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; Geom::Coord len = distance(*p, point_on_path); @@ -306,13 +297,13 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } } path_builder.appendNew (endPt); - } - if ( !outside ) { - path_builder.appendNew (endPt); - } + } + if ( !outside ) { + path_builder.appendNew (endPt); + } } -void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, +void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit, double line_width, bool outside = false) { //the most important work for the reflected join is done here @@ -320,7 +311,7 @@ void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cb //determine where we are in the path. If we're on the inside, ignore //and just lineTo. On the outside, we'll do a little reflection magic :) Geom::Crossings cross; - + if (outside) { Geom::Path pth; pth.append(*cbc1); @@ -347,9 +338,8 @@ void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cb if ( cross.empty() ) { //curves didn't cross; default to miter boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) - { + cbc2->initialPoint(), tang2); + if (p) { //check size of miter Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; Geom::Coord len = distance(*p, point_on_path); @@ -388,27 +378,26 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double // 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 goddamn pointer - + const unsigned k = path_in.size(); - - for (unsigned u = 0; u < k; u+=2) - { + + for (unsigned u = 0; u < k; u+=2) { to_outline = Path(); outlined_result = Path(); - + to_outline.LoadPath(pv[u], Geom::Affine(), 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(); - + //thing to do on the first run through if (u == 0) { //I could use the pv->operator[] (0) notation but that looks terrible @@ -417,58 +406,64 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double //get the curves ready for the operation Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate(); Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate(); - + //do the reflection/extrapolation: - if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); } - else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); } + if (extrapolate) { + extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); + } else { + reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); + } } - + path_builder.append( (*path_vec)[0] ); - + //outline the next segment, but don't store it yet if (path_vec) delete path_vec; - + 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(); - + //get the curves ready for the operation Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate(); Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate(); - + //do the reflection/extrapolation: - if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); } - else { reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); } - + if (extrapolate) { + extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); + } else { + reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, + outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); + } + //Now we can store it. path_builder.append( (*path_vec)[0] ); - + if (cbc1) delete cbc1; if (cbc2) delete cbc2; if (path_vec) delete path_vec; } } - + return path_builder; } -Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, +Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtType butt, double miter_lim, bool extrapolate) { Geom::PathVector path_out; - + unsigned pv_size = path_in.size(); for (unsigned i = 0; i < pv_size; i++) { - + if (path_in[i].size() > 1) { //since you've made it this far, hopefully all this is obvious :P Geom::Path with_direction; @@ -484,8 +479,8 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, newPath = Geom::path_from_piecewise(pwd2, 0.01)[0]; //fuk this with_direction = Outline::doAdvHalfOutline( newPath, -line_width, miter_lim, extrapolate ); - against_direction = Outline::doAdvHalfOutline( newPath.reverse(), -line_width, miter_lim, extrapolate ); - + against_direction = Outline::doAdvHalfOutline( newPath.reverse(), -line_width, miter_lim, extrapolate ); + /*if (dynamic_cast *>(&newPath[newPath.size()])) { //delete the 'Z' newPath.erase_last(); @@ -497,54 +492,54 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, newPath.erase_last(); newPath.append(path_in[i][path_in[i].size() - 1]); newPath.appendNew(newPath.initialPoint()); - newPath.erase_last(); + newPath.erase_last(); }*/ } Geom::PathBuilder pb; - + //add in the...do I really need to say this? 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: - //I have ZERO idea what to do here. - pb.lineTo(against_direction.initialPoint()); - break; - case butt_square: - pb.lineTo(against_direction.initialPoint()); - break; + 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: + //I have ZERO idea what to do here. + pb.lineTo(against_direction.initialPoint()); + break; + case butt_square: + 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: - //I have ZERO idea what to do here. - pb.lineTo(with_direction.initialPoint()); - break; - case butt_square: - pb.lineTo(with_direction.initialPoint()); - break; + 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: + //I have ZERO idea what to do here. + pb.lineTo(with_direction.initialPoint()); + break; + case butt_square: + pb.lineTo(with_direction.initialPoint()); + break; } } pb.flush(); @@ -554,7 +549,7 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, } else { Path p = Path(); Path outlinepath = Path(); - + p.LoadPath(path_in[i], Geom::Affine(), false, false); p.Outline(&outlinepath, line_width / 2, static_cast(join), butt, miter_lim); Geom::PathVector *pv_p = outlinepath.MakePathVector(); -- cgit v1.2.3 From b7c026006efcc5461dcef0f3c9a5e1b87d162d3d Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Wed, 2 Apr 2014 17:33:37 -0400 Subject: Hopefully remove the last of the exception throwing with certain path effects (bzr r13090.1.43) --- src/live_effects/pathoutlineprovider.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index ad39a7416..56f741417 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -250,17 +250,21 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } Geom::EllipticalArc *arc0 = circle1.arc(cbc1->finalPoint(), 0.5*(cbc1->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; + } - if (arc0) { - path_builder.append (arc0->toSBasis()); - delete arc0; - arc0 = NULL; - } - - if (arc1) { - path_builder.append (arc1->toSBasis()); - delete arc1; - arc1 = NULL; + if (arc1) { + path_builder.append (arc1->toSBasis()); + delete arc1; + arc1 = NULL; + } + } catch (std::exception ex) { + printf("Exception occured, probably NaN or infinite valued points: %s\n", ex.what()); + path_builder.appendNew(endPt); } } else { boost::optional p = intersection_point (cbc1->finalPoint(), tang1, -- cgit v1.2.3 From 4007abe02509fc1e312a007fb26f31c5a595df26 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Thu, 3 Apr 2014 21:04:51 -0400 Subject: Begin first stage of resolving issue with duplicate knots (bzr r13090.1.46) --- src/live_effects/pathoutlineprovider.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index 56f741417..a696728d6 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -1,3 +1,5 @@ +#include //g_critical + #include "pathoutlineprovider.h" #include "livarot/path-description.h" #include <2geom/angle.h> @@ -262,7 +264,7 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve delete arc1; arc1 = NULL; } - } catch (std::exception ex) { + } catch (std::exception & ex) { printf("Exception occured, probably NaN or infinite valued points: %s\n", ex.what()); path_builder.appendNew(endPt); } -- cgit v1.2.3 From a354238633c206078acc11a60d02382cee572740 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sat, 12 Apr 2014 00:52:48 -0400 Subject: Wonderful code optimizations (bzr r13090.1.50) --- src/live_effects/pathoutlineprovider.cpp | 79 ++++++++++++-------------------- 1 file changed, 29 insertions(+), 50 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index a696728d6..5a95da75d 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -9,6 +9,7 @@ #include <2geom/shape.h> #include <2geom/transforms.h> #include <2geom/path-sink.h> +#include "helper/geom-nodetype.h" #include namespace Geom { @@ -99,21 +100,25 @@ static Circle touching_circle( D2 const &curve, double t, double tol=0.0 return Geom::Circle(center, fabs(radius)); } -static std::vector split_at_cusps(const Geom::Path& in) +std::vector split_at_cusps(const Geom::Path& in) { - Geom::PathVector out = Geom::PathVector(); - Geom::Path temp = Geom::Path(); - - for (unsigned path_descr = 0; path_descr < in.size(); path_descr++) { - temp = Geom::Path(); - temp.append(in[path_descr]); + 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; } -static Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2 const & sbasis_in) +Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2 const & sbasis_in) { std::vector temp; sbasis_to_bezier(temp, sbasis_in, 4); @@ -140,13 +145,9 @@ typedef Geom::Piecewise PWD2; unsigned bezierOrder (const Geom::Curve* curve_in) { using namespace Geom; - //cast it - const CubicBezier *cbc = dynamic_cast(curve_in); - if (cbc) return 3; - const QuadraticBezier * qbc = dynamic_cast(curve_in); - if (qbc) return 2; - const BezierCurveN<1U> * lbc = dynamic_cast *>(curve_in); - if (lbc) return 1; + if ( const BezierCurve* bz = dynamic_cast(curve_in) ) { + return bz->order(); + } return 0; } @@ -154,8 +155,6 @@ unsigned bezierOrder (const Geom::Curve* curve_in) //is >180 clockwise, otherwise false. bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) { - unsigned order = bezierOrder(&cbc1); - Geom::Point start_point; Geom::Point cross_point = cbc1.finalPoint(); Geom::Point end_point; @@ -167,38 +166,18 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) "By default we are going to say that this is an inside join, so we cannot make a line join for it.\n", __LINE__, __FILE__); return false; } - switch (order) { - case 3: - start_point = (static_cast(&cbc1))->operator[] (2); - //major league b***f***ing - if (are_near(start_point, cross_point, 0.0000001)) { - start_point = (static_cast(&cbc1))->operator[] (1); - } - break; - case 2: - //this never happens - start_point = (static_cast(&cbc1))->operator[] (1); - break; - case 1: - default: - start_point = cbc1.initialPoint(); + + //let's try: + Geom::CubicBezier cubicBezier = Geom::sbasis_to_cubicbezier(cbc1.toSBasis()); + start_point = cubicBezier [2]; + //stupid thing Inkscape does: + if (are_near(start_point, cross_point, 0.0000001)) { + start_point = cubicBezier [1]; } - - order = Outline::bezierOrder(&cbc2); - - switch (order) { - case 3: - end_point = (static_cast(&cbc2))->operator[] (1); - if (are_near(end_point, cross_point, 0.0000001)) { - end_point = (static_cast(&cbc2))->operator[] (2); - } - break; - case 2: - end_point = (static_cast(&cbc2))->operator[] (1); - break; - case 1: - default: - end_point = cbc2.finalPoint(); + 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 @@ -391,7 +370,7 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double Geom::Path path_builder = Geom::Path(); //the path to store the result in Geom::PathVector * path_vec; //needed because livarot returns a goddamn pointer - const unsigned k = path_in.size(); + const unsigned k = pv.size(); for (unsigned u = 0; u < k; u+=2) { to_outline = Path(); -- cgit v1.2.3 From 727826e1fdd2c4142b1687e50c174ed14c178427 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sat, 12 Apr 2014 14:18:31 -0400 Subject: Fix triangles in joins (bzr r13090.1.52) --- src/live_effects/pathoutlineprovider.cpp | 45 +++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index 5a95da75d..080f42c08 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -261,6 +261,7 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } path_builder.appendNew (endPt); } + path_builder.append(*cbc2, Geom::Path::STITCH_DISCONTINUOUS); } if ( outside && lineProblem ) { Geom::Path pth; @@ -282,9 +283,22 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } } path_builder.appendNew (endPt); + path_builder.append(*cbc2, Geom::Path::STITCH_DISCONTINUOUS); } if ( !outside ) { - path_builder.appendNew (endPt); + /*path_builder.appendNew (endPt);*/ + Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2); + if (!cross.empty()) { + path_builder.erase_last(); + Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(cbc1->toSBasis()); + cubic = cubic.subdivide(cross[0].ta).first; + path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); + cubic = Geom::sbasis_to_cubicbezier(cbc2->toSBasis()); + cubic = cubic.subdivide(cross[0].tb).second; + path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); + } else { + path_builder.append(*cbc2, Geom::Path::STITCH_DISCONTINUOUS); + } } } @@ -346,9 +360,22 @@ void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cb path_builder.appendNew (sub1.first[1], sub1.first[2], /*sub1.first[3]*/ sub2.second[0] ); path_builder.appendNew (sub2.second[1], sub2.second[2], /*sub2.second[3]*/ endPt ); } - } else { // cross.empty() + path_builder.append(*cbc2); + } else { //probably on the inside of the corner - path_builder.appendNew ( endPt ); + /*path_builder.appendNew ( endPt );*/ + cross = Geom::crossings(*cbc1, *cbc2); + if (!cross.empty()) { + path_builder.erase_last(); + Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(cbc1->toSBasis()); + cubic = cubic.subdivide(cross[0].ta).first; + path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); + cubic = Geom::sbasis_to_cubicbezier(cbc2->toSBasis()); + cubic = cubic.subdivide(cross[0].tb).second; + path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); + } else { + path_builder.append(*cbc2, Geom::Path::STITCH_DISCONTINUOUS); + } } } @@ -387,6 +414,7 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double if (u == 0) { //I could use the pv->operator[] (0) notation but that looks terrible path_builder.start( (*path_vec)[0].initialPoint() ); + path_builder.append( (*path_vec)[0] ); } else { //get the curves ready for the operation Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate(); @@ -400,10 +428,12 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); } + Geom::Path temp_path = (*path_vec)[0]; + temp_path.erase(temp_path.begin()); + + path_builder.append( temp_path ); } - path_builder.append( (*path_vec)[0] ); - //outline the next segment, but don't store it yet if (path_vec) delete path_vec; @@ -430,7 +460,10 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double } //Now we can store it. - path_builder.append( (*path_vec)[0] ); + Geom::Path temp_path = (*path_vec)[0]; + temp_path.erase(temp_path.begin()); + + path_builder.append( temp_path ); if (cbc1) delete cbc1; if (cbc2) delete cbc2; -- cgit v1.2.3 From 3a4844ef32ce02cbcb9f1102f61d60914bb48d72 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sat, 12 Apr 2014 18:58:48 -0400 Subject: Enable line caps (bzr r13090.1.54) --- src/live_effects/pathoutlineprovider.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index 080f42c08..d7f5362aa 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -528,14 +528,23 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, case butt_round: pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() ); break; - case butt_pointy: - //I have ZERO idea what to do here. + 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: + } + 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; } + } } else { pb.moveTo(against_direction.initialPoint()); } @@ -551,14 +560,23 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, case butt_round: pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() ); break; - case butt_pointy: - //I have ZERO idea what to do here. + 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: + } + 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; } + } } pb.flush(); for (unsigned m = 0; i < pb.peek().size(); i++) { -- cgit v1.2.3 From fd29d6366fe47e17b732ec4cc40d40ae1f272e35 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sun, 13 Apr 2014 12:49:05 -0400 Subject: Nothing special (bzr r13090.1.55) --- src/live_effects/pathoutlineprovider.cpp | 49 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index d7f5362aa..d8e416fea 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -389,7 +389,13 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double { // 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); + Geom::Path temporary(path_in); + if (path_in.closed()) { + //this is a terrible solution + temporary.erase_last(); + temporary.close(false); + } + Geom::PathVector pv = split_at_cusps(temporary); Path to_outline; Path outlined_result; @@ -469,6 +475,12 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double if (cbc2) delete cbc2; if (path_vec) delete path_vec; } + if (path_in.closed()) { + temporary = Geom::Path(); + temporary.append(*path_in.end()); + Geom::Path temporary2 = doAdvHalfOutline(temporary, line_width, miter_limit, extrapolate); + path_builder.append(temporary2/*, Geom::Path::STITCH_DISCONTINUOUS*/); + } } return path_builder; @@ -492,25 +504,19 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, } else { //Geom::Path absolutely refuses to do what I want with these Geom::Path newPath = path_in[i]; - newPath.close(false); - Geom::Piecewise > pwd2 = newPath.toPwSb(); - newPath = Geom::path_from_piecewise(pwd2, 0.01)[0]; - //fuk this + if (Geom::distance(newPath.finalPoint(), newPath.initialPoint()) == newPath[newPath.size()].length()) { + Geom::LineSegment linear(newPath.finalPoint(), newPath.initialPoint()); + Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(linear.toSBasis()); + newPath.appendNew(cubic[1], cubic[2], cubic[3]); + } + //newPath.close(false); with_direction = Outline::doAdvHalfOutline( newPath, -line_width, miter_lim, extrapolate ); against_direction = Outline::doAdvHalfOutline( newPath.reverse(), -line_width, miter_lim, extrapolate ); - - /*if (dynamic_cast *>(&newPath[newPath.size()])) { - //delete the 'Z' - newPath.erase_last(); - newPath.append(path_in[i][path_in[i].size() - 1]); - newPath.appendNew(newPath.initialPoint()); - newPath.erase_last(); - } else { - //delete the 'Z' - newPath.erase_last(); - newPath.append(path_in[i][path_in[i].size() - 1]); - newPath.appendNew(newPath.initialPoint()); - newPath.erase_last(); + /*if (Geom::distance(newPath.finalPoint(), newPath.initialPoint()) != newPath[newPath.size()].length()) { + with_direction.erase_last(); + with_direction.erase_last(); + against_direction.erase(against_direction.begin()); + against_direction.erase(against_direction.begin()); }*/ } Geom::PathBuilder pb; @@ -579,8 +585,9 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, } } pb.flush(); - for (unsigned m = 0; i < pb.peek().size(); i++) { - path_out.push_back(pb.peek()[m]); + path_out.push_back(pb.peek()[0]); + if (path_in[i].closed()) { + path_out.push_back(pb.peek()[1]); } } else { Path p = Path(); @@ -658,8 +665,6 @@ Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, Lin return path_out; } else if (linejoin_type == LINEJOIN_REFLECTED) { //reflected half outline - Geom::PathVector pathvec; - pathvec.push_back(path_in); path_out = doAdvHalfOutline(path_in, line_width, miter_lim, false); return path_out; } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) { -- cgit v1.2.3 From 313863173f0d2f406dd3c36aea90099f22a04531 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Thu, 17 Apr 2014 13:58:57 -0400 Subject: Semi-fix for Join type on closed paths (bzr r13090.1.59) --- src/live_effects/pathoutlineprovider.cpp | 78 ++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 33 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index d8e416fea..4836b0d98 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -389,13 +389,8 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double { // 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::Path temporary(path_in); - if (path_in.closed()) { - //this is a terrible solution - temporary.erase_last(); - temporary.close(false); - } - Geom::PathVector pv = split_at_cusps(temporary); + + Geom::PathVector pv = split_at_cusps(path_in); Path to_outline; Path outlined_result; @@ -434,6 +429,7 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); } + //store it Geom::Path temp_path = (*path_vec)[0]; temp_path.erase(temp_path.begin()); @@ -475,11 +471,44 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double if (cbc2) delete cbc2; if (path_vec) delete path_vec; } - if (path_in.closed()) { - temporary = Geom::Path(); - temporary.append(*path_in.end()); - Geom::Path temporary2 = doAdvHalfOutline(temporary, line_width, miter_limit, extrapolate); - path_builder.append(temporary2/*, Geom::Path::STITCH_DISCONTINUOUS*/); + } + + if (path_in.closed()) { + if ( path_in[path_in.size() - 1].length() != Geom::distance(path_in[path_in.size() - 1].finalPoint(), path_in.initialPoint())) { + //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(); + + Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate();//(*path_vec)[0] [0].duplicate(); + Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate();//path_builder[path_builder.size() - 1].duplicate(); + + Geom::Path temporary; //why do we need this? you'll see in a bit + temporary.append(*cbc1); + + if (extrapolate) { + extrapolate_curves(temporary, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, + outside_angle ( path_in[path_in.size() - 1], path_in [0] )); + } else { + reflect_curves (temporary, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, + outside_angle ( path_in[path_in.size() - 1], path_in [0] )); + } + //extract the appended curves + if (temporary[0].finalPoint() != path_builder[path_builder.size() - 1].finalPoint()) { + path_builder.erase(path_builder.begin()); + } else { + temporary.erase_last(); + } + path_builder.erase_last(); + + path_builder.append(temporary, Geom::Path::STITCH_DISCONTINUOUS); + path_builder.close(); } } @@ -498,27 +527,10 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, //since you've made it this far, hopefully all this is obvious :P Geom::Path with_direction; Geom::Path against_direction; - if ( !path_in[i].closed() ) { - with_direction = Outline::doAdvHalfOutline( path_in[i], -line_width, miter_lim, extrapolate ); - against_direction = Outline::doAdvHalfOutline( path_in[i].reverse(), -line_width, miter_lim, extrapolate ); - } else { - //Geom::Path absolutely refuses to do what I want with these - Geom::Path newPath = path_in[i]; - if (Geom::distance(newPath.finalPoint(), newPath.initialPoint()) == newPath[newPath.size()].length()) { - Geom::LineSegment linear(newPath.finalPoint(), newPath.initialPoint()); - Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(linear.toSBasis()); - newPath.appendNew(cubic[1], cubic[2], cubic[3]); - } - //newPath.close(false); - with_direction = Outline::doAdvHalfOutline( newPath, -line_width, miter_lim, extrapolate ); - against_direction = Outline::doAdvHalfOutline( newPath.reverse(), -line_width, miter_lim, extrapolate ); - /*if (Geom::distance(newPath.finalPoint(), newPath.initialPoint()) != newPath[newPath.size()].length()) { - with_direction.erase_last(); - with_direction.erase_last(); - against_direction.erase(against_direction.begin()); - against_direction.erase(against_direction.begin()); - }*/ - } + + with_direction = Outline::doAdvHalfOutline( path_in[i], -line_width, miter_lim, extrapolate ); + against_direction = Outline::doAdvHalfOutline( path_in[i].reverse(), -line_width, miter_lim, extrapolate ); + Geom::PathBuilder pb; //add in the...do I really need to say this? -- cgit v1.2.3 From 6cf3f3c4fa0309fbe2af3849cbb0c1f3e9d2b2e7 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sat, 19 Apr 2014 21:14:39 -0400 Subject: remove easter eggs (bzr r13090.1.62) --- src/live_effects/pathoutlineprovider.cpp | 58 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 16 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index b5a3258fa..9ff896f84 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -162,8 +162,7 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) //assert(cbc1.finalPoint() == cbc2.initialPoint()); //short circuiting? if (cbc1.finalPoint() != cbc2.initialPoint()) { - printf("There was an issue when asserting that one curve's end is the start of the other. Line %d, File %s\n" - "By default we are going to say that this is an inside join, so we cannot make a line join for it.\n", __LINE__, __FILE__); + printf("erk! Line %d, File %s\n", __LINE__, __FILE__); return false; } @@ -261,7 +260,10 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } path_builder.appendNew (endPt); } - path_builder.append(*cbc2, Geom::Path::STITCH_DISCONTINUOUS); + if (cbc1->finalPoint() != cbc2->initialPoint()) { + path_builder.appendNew(cbc2->initialPoint()); + } + path_builder.append(*cbc2); } if ( outside && lineProblem ) { Geom::Path pth; @@ -283,7 +285,10 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve } } path_builder.appendNew (endPt); - path_builder.append(*cbc2, Geom::Path::STITCH_DISCONTINUOUS); + if (cbc1->finalPoint() != cbc2->initialPoint()) { + path_builder.appendNew(cbc2->initialPoint()); + } + path_builder.append(*cbc2); } if ( !outside ) { /*path_builder.appendNew (endPt);*/ @@ -297,7 +302,12 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve cubic = cubic.subdivide(cross[0].tb).second; path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); } else { - path_builder.append(*cbc2, Geom::Path::STITCH_DISCONTINUOUS); + if (Geom::distance(path_builder.finalPoint(), cbc2->initialPoint()) > 0.0000001) { + path_builder.appendNew(cbc2->initialPoint()); + } else { + path_builder.setFinal(cbc2->initialPoint()); + } + path_builder.append(*cbc2); } } } @@ -360,6 +370,9 @@ void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cb path_builder.appendNew (sub1.first[1], sub1.first[2], /*sub1.first[3]*/ sub2.second[0] ); path_builder.appendNew (sub2.second[1], sub2.second[2], /*sub2.second[3]*/ endPt ); } + if (cbc1->finalPoint() != cbc2->initialPoint()) { + path_builder.appendNew(cbc2->initialPoint()); + } path_builder.append(*cbc2); } else { //probably on the inside of the corner @@ -374,7 +387,12 @@ void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cb cubic = cubic.subdivide(cross[0].tb).second; path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); } else { - path_builder.append(*cbc2, Geom::Path::STITCH_DISCONTINUOUS); + if (Geom::distance(path_builder.finalPoint(), cbc2->initialPoint()) > 0.0000001) { + path_builder.appendNew(cbc2->initialPoint()); + } else { + path_builder.setFinal(cbc2->initialPoint()); + } + path_builder.append(*cbc2); } } } @@ -431,6 +449,7 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double } //store it Geom::Path temp_path = (*path_vec)[0]; + //erase the first segment since the join code already appended it temp_path.erase(temp_path.begin()); path_builder.append( temp_path ); @@ -477,7 +496,7 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double Geom::Curve * cbc1; Geom::Curve * cbc2; - if ( path_in[path_in.size() - 1].length() != Geom::distance(path_in[path_in.size() - 1].finalPoint(), path_in.initialPoint())) { + if ( path_in[path_in.size()].isDegenerate() ) { //handle case for last segment curved outlined_result = Path(); to_outline = Path(); @@ -512,10 +531,10 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double if (extrapolate) { extrapolate_curves(path_builder, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, - outside_angle ( path_in[path_in.size() - 1], path_in [0] )); + outside_angle ( path_in[path_in.size() - 1], oneCurve [0] )); } else { reflect_curves (path_builder, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, - outside_angle ( path_in[path_in.size() - 1], path_in [0] )); + outside_angle ( path_in[path_in.size() - 1], oneCurve [0] )); } delete cbc1; cbc1 = cbc2->duplicate(); @@ -534,22 +553,29 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double Geom::Path temporary; //just an accessory path, we won't need it for long temporary.append(*cbc1); + const Geom::Curve& prev_curve = path_in[path_in.size()].isDegenerate() ? path_in[path_in.size() - 1] : + path_in[path_in.size()]; + if (extrapolate) { extrapolate_curves(temporary, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, - outside_angle ( path_in[path_in.size() - 1], path_in [0] )); + outside_angle ( prev_curve, path_in [0] )); } else { reflect_curves (temporary, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, - outside_angle ( path_in[path_in.size() - 1], path_in [0] )); + outside_angle ( prev_curve, path_in [0] )); } //extract the appended curves - if (temporary[0].finalPoint() != path_builder[path_builder.size() - 1].finalPoint()) { + //if (temporary[temporary.size()].initialPoint() != path_builder[0].initialPoint()) { path_builder.erase(path_builder.begin()); - } else { + /*} else { temporary.erase_last(); - } + }*/ path_builder.erase_last(); - - path_builder.append(temporary, Geom::Path::STITCH_DISCONTINUOUS); + if (Geom::distance(path_builder.finalPoint(), temporary.initialPoint()) > 0.0000001) { + path_builder.appendNew(temporary.initialPoint()); + } else { + path_builder.setFinal(temporary.initialPoint()); + } + path_builder.append(temporary); path_builder.close(); if (cbc1) delete cbc1; -- cgit v1.2.3 From 8e303a547b23758ec6c1accee912185f25718c03 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Mon, 12 May 2014 19:59:28 -0400 Subject: Commit patch for "leaned" cap. Thanks Jabiertxof! (bzr r13090.1.78) --- src/live_effects/pathoutlineprovider.cpp | 92 ++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 17 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index 9ff896f84..d6e0ce7ea 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -586,7 +586,7 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double } Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, - ButtType butt, double miter_lim, bool extrapolate) + ButtTypeMod butt, double miter_lim, bool extrapolate, double start_lean, double end_lean) { Geom::PathVector path_out; @@ -610,13 +610,13 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, //add in our line caps if (!path_in[i].closed()) { switch (butt) { - case butt_straight: + case BUTT_STRAIGHT: pb.lineTo(against_direction.initialPoint()); break; - case butt_round: + case BUTT_ROUND: pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() ); break; - case butt_pointy: { + 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; @@ -624,7 +624,7 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, pb.lineTo(against_direction.initialPoint()); break; } - case butt_square: { + 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); @@ -632,6 +632,15 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, 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()); @@ -642,13 +651,13 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, //cap (if necessary) if (!path_in[i].closed()) { switch (butt) { - case butt_straight: + case BUTT_STRAIGHT: pb.lineTo(with_direction.initialPoint()); break; - case butt_round: + case BUTT_ROUND: pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() ); break; - case butt_pointy: { + 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; @@ -656,7 +665,7 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, pb.lineTo(with_direction.initialPoint()); break; } - case butt_square: { + 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); @@ -664,6 +673,15 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, 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(); @@ -674,9 +692,29 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, } 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), butt, miter_lim); + 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() ); @@ -686,8 +724,8 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, return path_out; } -Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line_width, ButtType linecap_type, - LineJoinType linejoin_type, double 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()) { @@ -702,9 +740,30 @@ Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line #define miter_lim fabs(line_width * miter_limit) //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 <= 2) { p.Outline(&outlinepath, line_width / 2, static_cast(linejoin_type), - linecap_type, miter_lim); + original_butt, miter_lim); //fix memory leak std::vector *pv_p = outlinepath.MakePathVector(); path_out = *pv_p; @@ -713,12 +772,11 @@ Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line } else if (linejoin_type == 3) { //reflected arc join path_out = outlinePath(path_in, line_width, static_cast(linejoin_type), - linecap_type , miter_lim, false); + linecap_type , miter_lim, false, start_lean, end_lean); } else if (linejoin_type == 4) { //extrapolated arc join - path_out = outlinePath(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim, true); - + path_out = outlinePath(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim, true, start_lean, end_lean); } #undef miter_lim -- cgit v1.2.3 From 3a6ed95f3fa5921c78b24f9ae73df7d442827571 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Wed, 23 Jul 2014 16:16:43 -0400 Subject: Refactoring of linejoin code (bzr r13090.1.94) --- src/live_effects/pathoutlineprovider.cpp | 551 +++++++++++++++---------------- 1 file changed, 258 insertions(+), 293 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index d6e0ce7ea..482f1f5e0 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -125,8 +125,7 @@ Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2 const & sbasis_in 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) +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.)) { @@ -135,13 +134,16 @@ static boost::optional intersection_point( Geom::Point const & orig } 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; @@ -151,25 +153,28 @@ unsigned bezierOrder (const Geom::Curve* curve_in) return 0; } -//returns true if the angle formed by the curves and their handles -//is >180 clockwise, otherwise false. +/** + * @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; - //assert(cbc1.finalPoint() == cbc2.initialPoint()); - //short circuiting? - if (cbc1.finalPoint() != cbc2.initialPoint()) { - printf("erk! Line %d, File %s\n", __LINE__, __FILE__); + if (cross_point != cbc2.initialPoint()) { + g_warning("Non-contiguous path in Outline::outside_angle()"); return false; } - //let's try: Geom::CubicBezier cubicBezier = Geom::sbasis_to_cubicbezier(cbc1.toSBasis()); start_point = cubicBezier [2]; - //stupid thing Inkscape does: + + /* + * 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]; } @@ -178,9 +183,11 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) 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 - //Much credit to Wikipedia for the following ( http://en.wikipedia.org/wiki/Graham_scan ) + // 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 @@ -191,27 +198,90 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) 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()) ); - if (ccw > 0) return true; - return false; + return ccw > 0; } -void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, - double miter_limit, double line_width, bool outside = false) -{ - bool lineProblem = (dynamic_cast *>(cbc1)) || (dynamic_cast *>(cbc2)); - if ( outside && !lineProblem ) { - Geom::Path pth; - pth.append(*cbc1); +// LINE JOINS - Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(pth.toPwSb()[0]), 0.); +typedef Geom::BezierCurveN<1u> BezierLine; - pth = Geom::Path(); - pth.append( *cbc2 ); - Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); +/** + * 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()); +} - Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(cbc1->toSBasis()), 0.); - Geom::Circle circle2 = Geom::touching_circle(cbc2->toSBasis(), 0); +/** + * 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]); @@ -225,239 +295,161 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve sol = points[0]; } else { // both points are good, choose nearest - sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ? - points[0] : points[1]; + sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ? points[0] : points[1]; } - Geom::EllipticalArc *arc0 = circle1.arc(cbc1->finalPoint(), 0.5*(cbc1->finalPoint()+sol), sol, true); + + 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 & ex) { - printf("Exception occured, probably NaN or infinite valued points: %s\n", ex.what()); + + } catch (std::exception const & ex) { + g_warning("Error extrapolating line join: %s\n", ex.what()); path_builder.appendNew(endPt); } } else { - boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) { - //check size of miter - Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; - Geom::Coord len = distance(*p, point_on_path); - if (len <= miter_limit) { - // miter OK - path_builder.appendNew (*p); - } - } - path_builder.appendNew (endPt); - } - if (cbc1->finalPoint() != cbc2->initialPoint()) { - path_builder.appendNew(cbc2->initialPoint()); - } - path_builder.append(*cbc2); - } - if ( outside && lineProblem ) { - Geom::Path pth; - pth.append(*cbc1); - Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(pth.toPwSb()[0]), 0.); - pth = Geom::Path(); - pth.append( *cbc2 ); - Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); - - boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) { - //check size of miter - Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; - Geom::Coord len = distance(*p, point_on_path); - if (len <= miter_limit) { - // miter OK - path_builder.appendNew (*p); - } - } - path_builder.appendNew (endPt); - if (cbc1->finalPoint() != cbc2->initialPoint()) { - path_builder.appendNew(cbc2->initialPoint()); - } - path_builder.append(*cbc2); - } - if ( !outside ) { - /*path_builder.appendNew (endPt);*/ - Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2); - if (!cross.empty()) { - path_builder.erase_last(); - Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(cbc1->toSBasis()); - cubic = cubic.subdivide(cross[0].ta).first; - path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); - cubic = Geom::sbasis_to_cubicbezier(cbc2->toSBasis()); - cubic = cubic.subdivide(cross[0].tb).second; - path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); - } else { - if (Geom::distance(path_builder.finalPoint(), cbc2->initialPoint()) > 0.0000001) { - path_builder.appendNew(cbc2->initialPoint()); - } else { - path_builder.setFinal(cbc2->initialPoint()); - } - path_builder.append(*cbc2); + // 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); } } -void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, - double miter_limit, double line_width, bool outside = false) +/** + * 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) { - //the most important work for the reflected join is done here - - //determine where we are in the path. If we're on the inside, ignore - //and just lineTo. On the outside, we'll do a little reflection magic :) - Geom::Crossings cross; + 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()); + } +} - if (outside) { - Geom::Path pth; - pth.append(*cbc1); - - Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(pth.toPwSb()[0]), 0.); - - //reflect curves along the bevel - D2SB newcurve1 = pth.toPwSb()[0] * - Geom::reflection ( -Geom::rot90(tang1) , - cbc1->finalPoint() ); - - Geom::CubicBezier bzr1 = sbasis_to_cubicbezier(Geom::reverse(newcurve1)); - - pth = Geom::Path(); - pth.append( *cbc2 ); - Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0); - - D2SB newcurve2 = pth.toPwSb()[0] * - Geom::reflection ( -Geom::rot90(tang2) , - cbc2->initialPoint() ); - Geom::CubicBezier bzr2 = sbasis_to_cubicbezier(Geom::reverse(newcurve2)); - - cross = Geom::crossings(bzr1, bzr2); - if ( cross.empty() ) { - //curves didn't cross; default to miter - boost::optional p = intersection_point (cbc1->finalPoint(), tang1, - cbc2->initialPoint(), tang2); - if (p) { - //check size of miter - Geom::Point point_on_path = cbc1->finalPoint() - rot90(tang1) * line_width; - Geom::Coord len = distance(*p, point_on_path); - if (len <= miter_limit) { - // miter OK - path_builder.appendNew (*p); - } - } - //bevel - path_builder.appendNew( endPt ); - } else { - //join - std::pair sub1 = bzr1.subdivide(cross[0].ta); - std::pair sub2 = bzr2.subdivide(cross[0].tb); +// Ideal function pointer we want to pass +typedef void JoinFunc(Geom::Path& /*path_builder*/, Geom::Curve const& /*outgoing*/, double /*miter_limit*/, double /*line_width*/); - //@TODO joins have a strange tendency to cross themselves twice. Check this. +/** + * 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(); - //sections commented out are for general stability - path_builder.appendNew (sub1.first[1], sub1.first[2], /*sub1.first[3]*/ sub2.second[0] ); - path_builder.appendNew (sub2.second[1], sub2.second[2], /*sub2.second[3]*/ endPt ); - } - if (cbc1->finalPoint() != cbc2->initialPoint()) { - path_builder.appendNew(cbc2->initialPoint()); - } - path_builder.append(*cbc2); + if (outside) { + func(path_builder, *cbc2, miter, width); } else { - //probably on the inside of the corner - /*path_builder.appendNew ( endPt );*/ - cross = Geom::crossings(*cbc1, *cbc2); - if (!cross.empty()) { - path_builder.erase_last(); - Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(cbc1->toSBasis()); - cubic = cubic.subdivide(cross[0].ta).first; - path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); - cubic = Geom::sbasis_to_cubicbezier(cbc2->toSBasis()); - cubic = cubic.subdivide(cross[0].tb).second; - path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); - } else { - if (Geom::distance(path_builder.finalPoint(), cbc2->initialPoint()) > 0.0000001) { - path_builder.appendNew(cbc2->initialPoint()); - } else { - path_builder.setFinal(cbc2->initialPoint()); - } - path_builder.append(*cbc2); - } + 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; } -/** @brief Converts a path to one half of an outline. -* path_in: The input path to use. (To create the other side use path_in.reverse() ) -* line_width: the line width to use (usually you want to divide this by 2) -* miter_limit: the miter parameter -* extrapolate: whether the join should be extrapolated instead of reflected -*/ -Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double miter_limit, bool extrapolate = false) -{ - // NOTE: it is important to notice the distinction between a Geom::Path and a livarot Path here! +/** + * 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 goddamn pointer + ::Path to_outline; + ::Path outlined_result; - const unsigned k = pv.size(); + 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) - for (unsigned u = 0; u < k; u+=2) { + // 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::Affine(), false, false); + 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 + // now a curve has been outside outlined and loaded into outlined_result - //get the Geom::Path + // get the Geom::Path path_vec = outlined_result.MakePathVector(); - //thing to do on the first run through + // on the first run through, there is no join if (u == 0) { - //I could use the pv->operator[] (0) notation but that looks terrible - path_builder.start( (*path_vec)[0].initialPoint() ); - path_builder.append( (*path_vec)[0] ); + path_builder.start(path_vec->front().initialPoint()); + path_builder.append(path_vec->front()); } else { - //get the curves ready for the operation - Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate(); - Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate(); - - //do the reflection/extrapolation: - if (extrapolate) { - extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); - } else { - reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u - 1] [pv[u - 1].size() - 1], pv[u] [0] )); - } - //store it - Geom::Path temp_path = (*path_vec)[0]; - //erase the first segment since the join code already appended it - temp_path.erase(temp_path.begin()); - - path_builder.append( temp_path ); + 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; + // 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(); @@ -466,29 +458,11 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double 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); - //get the curves ready for the operation - Geom::Curve * cbc1 = path_builder[path_builder.size() - 1].duplicate(); - Geom::Curve * cbc2 = (*path_vec)[0] [0].duplicate(); - - //do the reflection/extrapolation: - if (extrapolate) { - extrapolate_curves(path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); - } else { - reflect_curves (path_builder, cbc1, cbc2, (*path_vec)[0].initialPoint(), miter_limit, line_width, - outside_angle ( pv[u] [pv[u].size()-1], pv[u+1] [0] )); - } - - //Now we can store it. - Geom::Path temp_path = (*path_vec)[0]; - temp_path.erase(temp_path.begin()); - - path_builder.append( temp_path ); - - if (cbc1) delete cbc1; - if (cbc2) delete cbc2; - if (path_vec) delete path_vec; + if (path_vec) + delete path_vec; + path_vec = NULL; } } @@ -497,7 +471,7 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double Geom::Curve * cbc2; if ( path_in[path_in.size()].isDegenerate() ) { - //handle case for last segment curved + // handle case for last segment curved outlined_result = Path(); to_outline = Path(); @@ -509,11 +483,12 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double path_vec = outlined_result.MakePathVector(); cbc1 = path_builder[path_builder.size() - 1].duplicate(); - cbc2 = (*path_vec)[0] [0].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) + // 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(); @@ -527,17 +502,10 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double cbc1 = path_builder[path_builder.size() - 1].duplicate(); cbc2 = (*path_vec)[0] [0].duplicate(); - //append the closing segment - - if (extrapolate) { - extrapolate_curves(path_builder, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, - outside_angle ( path_in[path_in.size() - 1], oneCurve [0] )); - } else { - reflect_curves (path_builder, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, - outside_angle ( path_in[path_in.size() - 1], oneCurve [0] )); - } - - delete cbc1; cbc1 = cbc2->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]); @@ -550,32 +518,35 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double delete path_vec; } - Geom::Path temporary; //just an accessory path, we won't need it for long + Geom::Path temporary; temporary.append(*cbc1); - const Geom::Curve& prev_curve = path_in[path_in.size()].isDegenerate() ? path_in[path_in.size() - 1] : - path_in[path_in.size()]; - - if (extrapolate) { - extrapolate_curves(temporary, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, - outside_angle ( prev_curve, path_in [0] )); - } else { - reflect_curves (temporary, cbc1, cbc2, cbc2->initialPoint(), miter_limit, line_width, - outside_angle ( prev_curve, path_in [0] )); - } - //extract the appended curves - //if (temporary[temporary.size()].initialPoint() != path_builder[0].initialPoint()) { - path_builder.erase(path_builder.begin()); - /*} else { - temporary.erase_last(); - }*/ - path_builder.erase_last(); - if (Geom::distance(path_builder.finalPoint(), temporary.initialPoint()) > 0.0000001) { - path_builder.appendNew(temporary.initialPoint()); + 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 { - path_builder.setFinal(temporary.initialPoint()); + // closing path does not require a join + path_builder.setFinal(path_builder.initialPoint()); } - path_builder.append(temporary); path_builder.close(); if (cbc1) delete cbc1; @@ -585,8 +556,7 @@ Geom::Path doAdvHalfOutline(const Geom::Path& path_in, double line_width, double 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 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; @@ -594,16 +564,14 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, for (unsigned i = 0; i < pv_size; i++) { if (path_in[i].size() > 1) { - //since you've made it this far, hopefully all this is obvious :P Geom::Path with_direction; Geom::Path against_direction; - - with_direction = Outline::doAdvHalfOutline( path_in[i], -line_width, miter_lim, extrapolate ); - against_direction = Outline::doAdvHalfOutline( path_in[i].reverse(), -line_width, miter_lim, extrapolate ); + + 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; - //add in the...do I really need to say this? pb.moveTo(with_direction.initialPoint()); pb.append(with_direction); @@ -724,8 +692,7 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, return path_out; } -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) +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()) { @@ -761,21 +728,21 @@ Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line break; } } - if (linejoin_type <= 2) { + if (linejoin_type <= LINEJOIN_POINTY) { p.Outline(&outlinepath, line_width / 2, static_cast(linejoin_type), original_butt, miter_lim); - //fix memory leak + // fix memory leak std::vector *pv_p = outlinepath.MakePathVector(); path_out = *pv_p; delete pv_p; - } else if (linejoin_type == 3) { - //reflected arc join + } 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 == 4) { - //extrapolated arc join + } 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); } @@ -800,16 +767,14 @@ Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, Lin path_outline.OutsideOutline(&path_tangent, line_width / 2, static_cast(linejoin_type), butt_straight, miter_lim); pathvec = path_tangent.MakePathVector(); - path_out = pathvec[0]/* deref pointer */[0]/*actual object ref*/; + path_out = pathvec->front(); delete pathvec; return path_out; } else if (linejoin_type == LINEJOIN_REFLECTED) { - //reflected half outline - path_out = doAdvHalfOutline(path_in, line_width, miter_lim, false); + path_out = outlineHalf(path_in, line_width, miter_lim, reflect_curves); return path_out; } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) { - //what the hell do you think this is? :P - path_out = doAdvHalfOutline(path_in, line_width, miter_lim, true); + path_out = outlineHalf(path_in, line_width, miter_lim, extrapolate_curves); return path_out; } #undef miter_lim @@ -827,4 +792,4 @@ Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, Lin fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8 : -- cgit v1.2.3 From 92089c17de58981ed91537402a93e17ce2d2ec26 Mon Sep 17 00:00:00 2001 From: "Liam P. White" Date: Sun, 14 Sep 2014 13:55:34 -0400 Subject: More formatting (bzr r13341.1.207) --- src/live_effects/pathoutlineprovider.cpp | 48 +++++++++++++++++++------------- 1 file changed, 28 insertions(+), 20 deletions(-) (limited to 'src/live_effects/pathoutlineprovider.cpp') diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp index 482f1f5e0..21a0fb809 100644 --- a/src/live_effects/pathoutlineprovider.cpp +++ b/src/live_effects/pathoutlineprovider.cpp @@ -1,7 +1,11 @@ -#include //g_critical +/* Author: + * Liam P. White + * + * Copyright (C) 2014 Author + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ -#include "pathoutlineprovider.h" -#include "livarot/path-description.h" #include <2geom/angle.h> #include <2geom/path.h> #include <2geom/circle.h> @@ -9,8 +13,12 @@ #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 +#include "svg/svg.h" namespace Geom { /** @@ -22,8 +30,7 @@ namespace Geom { * @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) +static int circle_circle_intersection(Circle const &circle0, Circle const &circle1, Point & p0, Point & p1) { Point X0 = circle0.center(); double r0 = circle0.ray(); @@ -163,7 +170,7 @@ bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) Geom::Point end_point; if (cross_point != cbc2.initialPoint()) { - g_warning("Non-contiguous path in Outline::outside_angle()"); + printf("WARNING: Non-contiguous path in Outline::outside_angle()"); return false; } @@ -210,7 +217,8 @@ typedef Geom::BezierCurveN<1u> BezierLine; * @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) { +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 @@ -243,7 +251,8 @@ void joinInside(Geom::Path& path_builder, Geom::Curve const& outgoing) { * @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) { +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.); @@ -269,7 +278,8 @@ void miter_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double * @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) { +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(); @@ -318,7 +328,7 @@ void extrapolate_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, d } } catch (std::exception const & ex) { - g_warning("Error extrapolating line join: %s\n", ex.what()); + printf("WARNING: Error extrapolating line join: %s\n", ex.what()); path_builder.appendNew(endPt); } } else { @@ -378,7 +388,8 @@ typedef void JoinFunc(Geom::Path& /*path_builder*/, Geom::Curve const& /*outgoin /** * 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) { +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) { @@ -411,7 +422,8 @@ static void outlineHelper(Geom::Path& path_builder, Geom::PathVector* path_vec, * @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) { +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! @@ -692,6 +704,8 @@ Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, 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(); @@ -704,9 +718,7 @@ Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true)); } -#define miter_lim fabs(line_width * miter_limit) - - //magic! + // magic! ButtType original_butt; switch (linecap_type) { case BUTT_STRAIGHT: @@ -746,15 +758,12 @@ Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line path_out = outlinePath(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim, true, start_lean, end_lean); } -#undef miter_lim return path_out; } Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit) { -#define miter_lim fabs(line_width * miter_limit) - Geom::Path path_out; if (linejoin_type <= LINEJOIN_POINTY || path_in.size() <= 1) { @@ -777,7 +786,6 @@ Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, Lin path_out = outlineHalf(path_in, line_width, miter_lim, extrapolate_curves); return path_out; } -#undef miter_lim return path_out; } -- cgit v1.2.3