diff options
| author | Tavmjong Bah <tavmjong@free.fr> | 2015-03-29 20:33:48 +0000 |
|---|---|---|
| committer | tavmjong-free <tavmjong@free.fr> | 2015-03-29 20:33:48 +0000 |
| commit | ef9edf66715ddbc498b31e921e2506c6e87116fc (patch) | |
| tree | 2b13d7eb83f24ca9603d7fd6bd5347440bad71be /src/helper/geom-pathstroke.cpp | |
| parent | make_angle_bisector_line(): Return sensible line if three input points are co... (diff) | |
| download | inkscape-ef9edf66715ddbc498b31e921e2506c6e87116fc.tar.gz inkscape-ef9edf66715ddbc498b31e921e2506c6e87116fc.zip | |
Add clipping at miter-length to 'arcs' line join.
(bzr r14036)
Diffstat (limited to 'src/helper/geom-pathstroke.cpp')
| -rw-r--r-- | src/helper/geom-pathstroke.cpp | 152 |
1 files changed, 131 insertions, 21 deletions
diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp index e34546d0e..aaafea98f 100644 --- a/src/helper/geom-pathstroke.cpp +++ b/src/helper/geom-pathstroke.cpp @@ -225,12 +225,14 @@ Geom::Point pick_solution(Geom::Point points[2], Geom::Point tang2, Geom::Point return sol; } -void extrapolate_join(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) +void extrapolate_join(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter, double width) { using namespace Geom; Geom::Curve const& incoming = path_builder.back(); + Geom::Point startPt = incoming.finalPoint(); Geom::Point endPt = outgoing.initialPoint(); - Geom::Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0); + Geom::Point tang1 = Geom::unitTangentAt(reverse(incoming.toSBasis()), 0.); + Geom::Point tang2 = outgoing.unitTangentAt(0); Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.); Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0); @@ -241,52 +243,160 @@ void extrapolate_join(Geom::Path& path_builder, Geom::Curve const& outgoing, dou Geom::Point points[2]; int solutions = 0; - Geom::EllipticalArc *arc0 = NULL; Geom::EllipticalArc *arc1 = NULL; + Geom::EllipticalArc *arc2 = NULL; + Geom::Point sol; + Geom::Point p1; + Geom::Point p2; if (!inc_ls && !out_ls) { + // Two circles solutions = Geom::circle_circle_intersection(circle1.center(), circle1.ray(), circle2.center(), circle2.ray(), points[0], points[1]); if (solutions == 2) { - Geom::Point sol = pick_solution(points, tang2, endPt); - - arc0 = circle1.arc(incoming.finalPoint(), 0.5*(incoming.finalPoint()+sol), sol, true); - arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); + sol = pick_solution(points, tang2, endPt); + arc1 = circle1.arc(startPt, 0.5*(startPt+sol), sol, true); + arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); } } else if (inc_ls && !out_ls) { + // Line and circle solutions = Geom::circle_line_intersection(circle2, incoming.initialPoint(), incoming.finalPoint(), points[0], points[1]); if (solutions == 2) { - Geom::Point sol = pick_solution(points, tang2, endPt); - path_builder.setFinal(sol); - arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); + sol = pick_solution(points, tang2, endPt); + arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); } } else if (!inc_ls && out_ls) { + // Circle and line solutions = Geom::circle_line_intersection(circle1, outgoing.initialPoint(), outgoing.finalPoint(), points[0], points[1]); if (solutions == 2) { - Geom::Point sol = pick_solution(points, tang2, endPt); - arc0 = circle1.arc(incoming.finalPoint(), 0.5*(sol+incoming.finalPoint()), sol, true); + sol = pick_solution(points, tang2, endPt); + arc1 = circle1.arc(startPt, 0.5*(sol+startPt), sol, true); } } if (solutions != 2) // no solutions available, fall back to miter - return miter_join(path_builder, outgoing, miter_limit, line_width); + return miter_clip_join(path_builder, outgoing, miter, width); + + // We have a solution, thus sol is defined. + p1 = sol; + + // See if we need to clip. Miter length is measured along a circular arc that is tangent to the + // bisector of the incoming and out going angles and passes through the end point (sol) of the + // line join. + + // Center of circle is intersection of a line orthogonal to bisector and a line bisecting + // a chord connecting the path end point (point_on_path) and the join end point (sol). + Geom::Point point_on_path = startPt + Geom::rot90(tang1)*width; + Geom::Line bisector = make_angle_bisector_line( startPt, point_on_path, endPt ); + Geom::Line ortho = make_orthogonal_line(point_on_path, bisector); + + Geom::LineSegment chord( point_on_path, sol ); + Geom::Line bisector_chord = make_bisector_line( chord ); + + Geom::Line limit_line; + double miter_limit = 2.0 * width * miter; + bool clipped = false; + + if( are_parallel( bisector_chord, ortho ) ) { + + // No intersection (can happen if curvatures are equal but opposite) + if( Geom::distance( point_on_path, sol ) > miter_limit ) { + clipped = true; + Geom::Point limit_point = point_on_path + miter_limit * bisector.versor(); + limit_line = make_parallel_line( limit_point, ortho ); + } + + } else { + + Geom::Point center = + Geom::intersection_point( bisector_chord.pointAt(0), bisector_chord.versor(), + ortho.pointAt(0), ortho.versor() ); + Geom::Coord radius = distance( center, point_on_path ); + Geom::Circle circle_center( center, radius ); + + double limit_angle = miter_limit / radius; + Geom::Ray start_ray( center, point_on_path ); + Geom::Ray end_ray( center, sol ); + Geom::Line limit_line( center, 0 ); // Angle set below + + if( Geom::cross( start_ray.versor(), end_ray.versor() ) > 0 ) { + limit_line.setAngle( start_ray.angle() - limit_angle ); + } else { + limit_line.setAngle( start_ray.angle() + limit_angle ); + } + + Geom::EllipticalArc* arc_center = circle_center.arc(point_on_path, 0.5*(point_on_path + sol), sol, true); + if( arc_center && arc_center->sweepAngle() > limit_angle ) { + // We need to clip + clipped = true; + + if (!inc_ls ) { + // Incoming circular + solutions = Geom::circle_line_intersection(circle1, limit_line.pointAt(0), limit_line.pointAt(1), points[0], points[1]); + + if (solutions == 2) { + p1 = pick_solution(points, tang2, endPt); + delete arc1; + arc1 = circle1.arc(startPt, 0.5*(p1+startPt), p1, true); + } + } else { + p1 = Geom::intersection_point( startPt, tang1, limit_line.pointAt(0), limit_line.versor() ); + } - if (arc0) - path_builder.append(*arc0); - if (arc1) + if (!out_ls ) { + // Outgoing circular + solutions = Geom::circle_line_intersection(circle2, limit_line.pointAt(0), limit_line.pointAt(1), points[0], points[1]); + + if (solutions == 2) { + p2 = pick_solution(points, tang1, endPt); + delete arc2; + arc2 = circle2.arc(p2, 0.5*(p2+endPt), endPt, true); + } + } else { + p2 = Geom::intersection_point( endPt, tang2, limit_line.pointAt(0), limit_line.versor() ); + } + } + // std::cout << " IFP: " << startPt + // << " POP: " << point_on_path + // << " OIP: " << endPt << std::endl; + // std::cout << " center: " << center << std::endl; + // std::cout << " radius: " << radius << std::endl; + // std::cout << " miter_limit: " << miter_limit << std::endl; + // std::cout << " limit_angle: " << limit_angle << std::endl; + // std::cout << " start_ray: " << Geom::Line( start_ray ) << std::endl; + // std::cout << " limit_line: " << limit_line << std::endl; + // std::cout << " P1 out: " << p1 << std::endl; + // std::cout << " P2 out: " << p2 << std::endl; + } + + // Add initial + if (arc1) { path_builder.append(*arc1); + } else { + // Straight line segment: move last point + path_builder.setFinal(p1); + } - delete arc0; - delete arc1; + if( clipped ) { + path_builder.appendNew<Geom::LineSegment>(p2); + } - if (!inc_ls && out_ls) - path_builder.appendNew<Geom::LineSegment>(outgoing.finalPoint()); - else + // Add outgoing + if (arc2) { + path_builder.append(*arc2); path_builder.append(outgoing); + } else { + // Straight line segment: + path_builder.appendNew<Geom::LineSegment>(outgoing.finalPoint()); + } + + delete arc1; + delete arc2; + } void join_inside(Geom::Path& res, Geom::Curve const& outgoing) |
