diff options
Diffstat (limited to 'src/live_effects')
| -rw-r--r-- | src/live_effects/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/live_effects/Makefile_insert | 5 | ||||
| -rw-r--r-- | src/live_effects/bezctx.cpp | 48 | ||||
| -rw-r--r-- | src/live_effects/bezctx.h | 10 | ||||
| -rw-r--r-- | src/live_effects/bezctx_intf.h | 20 | ||||
| -rw-r--r-- | src/live_effects/lpe-powerstroke-interpolators.h | 83 | ||||
| -rw-r--r-- | src/live_effects/lpe-powerstroke.cpp | 193 | ||||
| -rw-r--r-- | src/live_effects/lpe-powerstroke.h | 2 | ||||
| -rw-r--r-- | src/live_effects/lpe-spiro.cpp | 85 | ||||
| -rw-r--r-- | src/live_effects/spiro-converters.cpp | 123 | ||||
| -rw-r--r-- | src/live_effects/spiro-converters.h | 67 | ||||
| -rw-r--r-- | src/live_effects/spiro.cpp | 69 | ||||
| -rw-r--r-- | src/live_effects/spiro.h | 54 |
13 files changed, 450 insertions, 317 deletions
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index e066e7f43..a5f50a69d 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -1,6 +1,5 @@ set(live_effects_SRC - bezctx.cpp effect.cpp lpe-angle_bisector.cpp lpe-bendpath.cpp @@ -41,6 +40,7 @@ set(live_effects_SRC lpeobject-reference.cpp lpeobject.cpp spiro.cpp + spiro-converters.cpp parameter/array.cpp parameter/bool.cpp @@ -58,8 +58,6 @@ set(live_effects_SRC # ------- # Headers - bezctx.h - bezctx_intf.h effect-enum.h effect.h lpe-angle_bisector.h @@ -101,6 +99,8 @@ set(live_effects_SRC lpegroupbbox.h lpeobject-reference.h lpeobject.h + spiro.h + spiro-converters.h parameter/array.h parameter/bool.h @@ -115,7 +115,7 @@ set(live_effects_SRC parameter/text.h parameter/unit.h parameter/vector.h - spiro.h + ) # add_inkscape_lib(live_effects_LIB "${live_effects_SRC}") diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 74356f563..9c3c171f2 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -50,9 +50,8 @@ ink_common_sources += \ live_effects/lpe-perp_bisector.h \ live_effects/spiro.h \ live_effects/spiro.cpp \ - live_effects/bezctx.h \ - live_effects/bezctx_intf.h \ - live_effects/bezctx.cpp \ + live_effects/spiro-converters.h \ + live_effects/spiro-converters.cpp \ live_effects/lpe-circle_with_radius.cpp \ live_effects/lpe-circle_with_radius.h \ live_effects/lpe-perspective_path.cpp \ diff --git a/src/live_effects/bezctx.cpp b/src/live_effects/bezctx.cpp deleted file mode 100644 index 722f5dbaf..000000000 --- a/src/live_effects/bezctx.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* -ppedit - A pattern plate editor for Spiro splines. -Copyright (C) 2007 Raph Levien - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. - -*/ -#include "bezctx.h" - -void bezctx_moveto(bezctx *bc, double x, double y, int is_open) -{ - bc->moveto(bc, x, y, is_open); -} - -void bezctx_lineto(bezctx *bc, double x, double y) -{ - bc->lineto(bc, x, y); -} - -void bezctx_quadto(bezctx *bc, double x1, double y1, double x2, double y2) -{ - bc->quadto(bc, x1, y1, x2, y2); -} - -void bezctx_curveto(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3) -{ - bc->curveto(bc, x1, y1, x2, y2, x3, y3); -} - -void bezctx_mark_knot(bezctx *bc, int knot_idx) -{ - if (bc->mark_knot) - bc->mark_knot(bc, knot_idx); -} diff --git a/src/live_effects/bezctx.h b/src/live_effects/bezctx.h deleted file mode 100644 index 057312e5a..000000000 --- a/src/live_effects/bezctx.h +++ /dev/null @@ -1,10 +0,0 @@ -#include "bezctx_intf.h" - -struct _bezctx { - void (*moveto)(bezctx *bc, double x, double y, int is_open); - void (*lineto)(bezctx *bc, double x, double y); - void (*quadto)(bezctx *bc, double x1, double y1, double x2, double y2); - void (*curveto)(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3); - void (*mark_knot)(bezctx *bc, int knot_idx); -}; diff --git a/src/live_effects/bezctx_intf.h b/src/live_effects/bezctx_intf.h deleted file mode 100644 index a47b8ef5a..000000000 --- a/src/live_effects/bezctx_intf.h +++ /dev/null @@ -1,20 +0,0 @@ -typedef struct _bezctx bezctx; - -bezctx * -new_bezctx(void); - -void -bezctx_moveto(bezctx *bc, double x, double y, int is_open); - -void -bezctx_lineto(bezctx *bc, double x, double y); - -void -bezctx_quadto(bezctx *bc, double x1, double y1, double x2, double y2); - -void -bezctx_curveto(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3); - -void -bezctx_mark_knot(bezctx *bc, int knot_idx); diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h index 7f9cb3ddb..6f5b75af8 100644 --- a/src/live_effects/lpe-powerstroke-interpolators.h +++ b/src/live_effects/lpe-powerstroke-interpolators.h @@ -16,8 +16,6 @@ #include <2geom/bezier-utils.h> #include <2geom/sbasis-to-bezier.h> -#include "live_effects/bezctx.h" -#include "live_effects/bezctx_intf.h" #include "live_effects/spiro.h" @@ -136,7 +134,6 @@ private: }; -#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS class SpiroInterpolator : public Interpolator { public: SpiroInterpolator() {}; @@ -148,8 +145,7 @@ public: Coord scale_y = 100.; guint len = points.size(); - bezctx *bc = new_bezctx_ink(&fit); - spiro_cp *controlpoints = g_new (spiro_cp, len); + Spiro::spiro_cp *controlpoints = g_new (Spiro::spiro_cp, len); for (unsigned int i = 0; i < len; ++i) { controlpoints[i].x = points[i][X]; controlpoints[i].y = points[i][Y] / scale_y; @@ -160,88 +156,13 @@ public: controlpoints[len-2].ty = 'v'; controlpoints[len-1].ty = '}'; - spiro_seg *s = run_spiro(controlpoints, len); - spiro_to_bpath(s, len, bc); - free(s); - free(bc); + Spiro::spiro_run(controlpoints, len, fit); fit *= Scale(1,scale_y); return fit; }; private: - typedef struct { - bezctx base; - Path *path; - int is_open; - } bezctx_ink; - - static void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/) - { - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x) && IS_FINITE(y) ) { - bi->path->start(Point(x, y)); - } - #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("spiro moveto not finite"); - } - #endif - } - - static void bezctx_ink_lineto(bezctx *bc, double x, double y) - { - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x) && IS_FINITE(y) ) { - bi->path->appendNew<LineSegment>( Point(x, y) ); - } - #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("spiro lineto not finite"); - } - #endif - } - - static void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3) - { - bezctx_ink *bi = (bezctx_ink *) bc; - - if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) { - bi->path->appendNew<QuadraticBezier>(Point(xm, ym), Point(x3, y3)); - } - #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("spiro quadto not finite"); - } - #endif - } - - static void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3) - { - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) { - bi->path->appendNew<CubicBezier>(Point(x1, y1), Point(x2, y2), Point(x3, y3)); - } - #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("spiro curveto not finite"); - } - #endif - } - - bezctx * - new_bezctx_ink(Geom::Path *path) const { - bezctx_ink *result = g_new(bezctx_ink, 1); - result->base.moveto = bezctx_ink_moveto; - result->base.lineto = bezctx_ink_lineto; - result->base.quadto = bezctx_ink_quadto; - result->base.curveto = bezctx_ink_curveto; - result->base.mark_knot = NULL; - result->path = path; - return &result->base; - } - SpiroInterpolator(const SpiroInterpolator&); SpiroInterpolator& operator=(const SpiroInterpolator&); }; diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 257dba8ea..d3843dc92 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -5,7 +5,7 @@ /* Authors: * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl> * - * Copyright (C) 2010-2011 Authors + * Copyright (C) 2010-2012 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -26,9 +26,12 @@ #include <2geom/svg-path.h> #include <2geom/path-intersection.h> #include <2geom/crossing.h> +#include <2geom/ellipse.h> -namespace Geom { +#include "spiro.h" +namespace Geom { +// should all be moved to 2geom at some point Point unitTangentAt( D2<SBasis> const & a, Coord t, unsigned n = 3) { std::vector<Point> derivs = a.valueAndDerivatives(t, n); @@ -55,8 +58,48 @@ boost::optional<Point> intersection_point( Point const & origin_a, Point const & return boost::none; } +Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in) +{ + std::vector<Geom::Point> temp; + sbasis_to_bezier(temp, sbasis_in, 4); + return Geom::CubicBezier( temp ); +} + +/** + * document this! + * very quick: this finds the ellipse with minimum eccentricity + passing through point P and Q, with tangent PO at P and QO at Q + http://mathforum.org/kb/message.jspa?messageID=7471596&tstart=0 + */ +static Ellipse find_ellipse(Point P, Point Q, Point O) +{ + Point p = P - O; + Point q = Q - O; + Coord K = 4 * dot(p,q) / (L2sq(p) + L2sq(q)); + + double cross = p[Y]*q[X] - p[X]*q[Y]; + double a = -q[Y]/cross; + double b = q[X]/cross; + double c = (O[X]*q[Y] - O[Y]*q[X])/cross; + + double d = p[Y]/cross; + double e = -p[X]/cross; + double f = (-O[X]*p[Y] + O[Y]*p[X])/cross; + + // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 + double A = (a*d*K+d*d+a*a); + double B = (a*e*K+b*d*K+2*d*e+2*a*b); + double C = (b*e*K+e*e+b*b); + double D = (a*f*K+c*d*K+2*d*f-2*d+2*a*c-2*a); + double E = (b*f*K+c*e*K+2*e*f-2*e+2*b*c-2*b); + double F = c*f*K+f*f-2*f+c*c-2*c+1; + + return Ellipse(A, B, C, D, E, F); } + +} // namespace Geom + namespace Inkscape { namespace LivePathEffect { @@ -84,19 +127,21 @@ static const Util::EnumData<unsigned> LineCapTypeData[] = { }; static const Util::EnumDataConverter<unsigned> LineCapTypeConverter(LineCapTypeData, sizeof(LineCapTypeData)/sizeof(*LineCapTypeData)); -enum LineCuspType { - LINECUSP_BEVEL, - LINECUSP_ROUND, - LINECUSP_EXTRP_MITER, - LINECUSP_MITER +enum LineJoinType { + LINEJOIN_BEVEL, + LINEJOIN_ROUND, + LINEJOIN_EXTRP_MITER, + LINEJOIN_MITER, + LINEJOIN_SPIRO }; -static const Util::EnumData<unsigned> LineCuspTypeData[] = { - {LINECUSP_BEVEL, N_("Beveled"), "bevel"}, - {LINECUSP_ROUND, N_("Rounded"), "round"}, - {LINECUSP_EXTRP_MITER, N_("Extrapolated"), "extrapolated"}, - {LINECUSP_MITER, N_("Miter"), "miter"}, +static const Util::EnumData<unsigned> LineJoinTypeData[] = { + {LINEJOIN_BEVEL, N_("Beveled"), "bevel"}, + {LINEJOIN_ROUND, N_("Rounded"), "round"}, + {LINEJOIN_EXTRP_MITER, N_("Extrapolated"), "extrapolated"}, + {LINEJOIN_MITER, N_("Miter"), "miter"}, + {LINEJOIN_SPIRO, N_("Spiro"), "spiro"}, }; -static const Util::EnumDataConverter<unsigned> LineCuspTypeConverter(LineCuspTypeData, sizeof(LineCuspTypeData)/sizeof(*LineCuspTypeData)); +static const Util::EnumDataConverter<unsigned> LineJoinTypeConverter(LineJoinTypeData, sizeof(LineJoinTypeData)/sizeof(*LineJoinTypeData)); LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : Effect(lpeobject), @@ -105,7 +150,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : interpolator_type(_("Interpolator type"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path."), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN), interpolator_beta(_("Smoothness"), _("Sets the smoothness for the CubicBezierJohan interpolator. 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2), start_linecap_type(_("Start cap"), _("Determines the shape of the path's start."), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND), - cusp_linecap_type(_("Join"), _("Specifies the shape of the path's corners."), "cusp_linecap_type", LineCuspTypeConverter, &wr, this, LINECUSP_ROUND), + linejoin_type(_("Join"), _("Specifies the shape of the path's corners."), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_ROUND), miter_limit(_("Miter limit"), _("Maximum length of the miter (in units of stroke width)"), "miter_limit", &wr, this, 4.), end_linecap_type(_("End cap"), _("Determines the shape of the path's end."), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND) { @@ -121,7 +166,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : registerParameter( dynamic_cast<Parameter *>(&interpolator_type) ); registerParameter( dynamic_cast<Parameter *>(&interpolator_beta) ); registerParameter( dynamic_cast<Parameter *>(&start_linecap_type) ); - registerParameter( dynamic_cast<Parameter *>(&cusp_linecap_type) ); + registerParameter( dynamic_cast<Parameter *>(&linejoin_type) ); registerParameter( dynamic_cast<Parameter *>(&miter_limit) ); registerParameter( dynamic_cast<Parameter *>(&end_linecap_type) ); } @@ -163,9 +208,9 @@ static bool compare_offsets (Geom::Point first, Geom::Point second) // find discontinuities in input path struct discontinuity_data { - Geom::Point der0; // unit derivative of 'left' side of cusp - Geom::Point der1; // unit derivative of 'right' side of cusp - double width; // intended stroke width at cusp + Geom::Point der0; // unit derivative of 'left' side of join + Geom::Point der1; // unit derivative of 'right' side of join + double width; // intended stroke width at join }; std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & der, Geom::Piecewise<Geom::SBasis> const & x, @@ -176,8 +221,16 @@ std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<G for(unsigned i = 1; i < der.size(); i++) { if ( ! are_near(der[i-1].at1(), der[i].at0(), eps) ) { discontinuity_data data; + data.der0 = der[i-1].at1(); data.der1 = der[i].at0(); + if ( Geom::are_near(data.der0.length(), 0) ) { + data.der0 = unitTangentAt(der[i-1], 1, 2); + } + if ( Geom::are_near(data.der1.length(), 0) ) { + data.der1 = unitTangentAt(der[i], 0, 2); + } + double t = der.cuts[i]; std::vector< double > rts = roots (x - t); /// @todo this has multiple solutions for general strokewidth paths (generated by spiro interpolator...), ignore for now if (!rts.empty()) { @@ -194,12 +247,12 @@ std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<G Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & B, std::vector<discontinuity_data> const & cusps, - LineCuspType cusp_linecap, + LineJoinType jointype, double miter_limit, bool forward_direction, double tol=Geom::EPSILON) { -/* per definition, each discontinuity should be fixed with a cusp-ending, as defined by cusp_linecap_type +/* per definition, each discontinuity should be fixed with a join-ending, as defined by linejoin_type */ Geom::PathBuilder pb; if (B.size() == 0) { @@ -223,14 +276,41 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> { // discontinuity found, so fix it :-) discontinuity_data cusp = cusps[cusp_i]; - switch (cusp_linecap) { - case LINECUSP_ROUND: // properly bugged ^_^ - pb.arcTo( abs(cusp.width), abs(cusp.width), - angle_between(cusp.der0, cusp.der1), false, cusp.width < 0, - B[i].at0() ); + bool on_outside = ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0. ); + + switch (jointype) { + case LINEJOIN_ROUND: { + if (on_outside) { + // we are on the outside: round corner + /* for constant width paths, the rounding is a circular arc (rx == ry), + for non-constant width paths, the rounding can be done with an ellipse but is hard and ambiguous. + The elliptical arc should go through the discontinuity's start and end points (of course!) + and also should match the discontinuity tangents at those start and end points. + To resolve the ambiguity, the elliptical arc with minimal eccentricity should be chosen. + A 2Geom method was created to do exactly this :) + */ + + Geom::Point tang1 = unitTangentAt(B[prev_i],1); + Geom::Point tang2 = unitTangentAt(B[i],0); + boost::optional<Geom::Point> O = intersection_point( B[prev_i].at1(), tang1, + B[i].at0(), tang2 ); + if (!O) { + // no center found, i.e. 180 degrees round + pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles + break; + } + + Geom::Ellipse ellipse = find_ellipse(B[prev_i].at1(), B[i].at0(), *O); + pb.arcTo( ellipse.ray(Geom::X), ellipse.ray(Geom::Y), ellipse.rot_angle(), + false, cusp.width < 0, B[i].at0() ); + } else { + // we are on the inside, do a simple bevel to connect the paths + pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles + } break; -/* case LINECUSP_NONE: { - if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) { + } +/* case LINEJOIN_NONE: { + if ( on_outside ) { // we are on the outside Geom::Point der1 = unitTangentAt(B[prev_i],1); Geom::Point point_on_path = B[prev_i].at1() - rot90(der1) * cusp.width; @@ -241,24 +321,18 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles } } */ - case LINECUSP_EXTRP_MITER: { - // first figure out whether we are on the outside or inside of the corner in the path - if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) { + case LINEJOIN_EXTRP_MITER: { + if (on_outside) { // we are on the outside, do something complicated to make it look good ;) Geom::Point der1 = unitTangentAt(B[prev_i],1); Geom::Point der2 = unitTangentAt(B[i],0); Geom::D2<Geom::SBasis> newcurve1 = B[prev_i] * Geom::reflection(rot90(der1), B[prev_i].at1()); - newcurve1 = reverse(newcurve1); - std::vector<Geom::Point> temp; - sbasis_to_bezier(temp, newcurve1, 4); - Geom::CubicBezier bzr1( temp ); + Geom::CubicBezier bzr1 = sbasis_to_cubicbezier( reverse(newcurve1) ); Geom::D2<Geom::SBasis> newcurve2 = B[i] * Geom::reflection(rot90(der2), B[i].at0()); - newcurve2 = reverse(newcurve2); - sbasis_to_bezier(temp, newcurve2, 4); - Geom::CubicBezier bzr2( temp ); + Geom::CubicBezier bzr2 = sbasis_to_cubicbezier( reverse(newcurve2) ); Geom::Crossings cross = crossings(bzr1, bzr2); if (cross.empty()) { @@ -285,9 +359,8 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> } break; } - case LINECUSP_MITER: { - // first figure out whether we are on the outside or inside of the corner in the path - if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) { + case LINEJOIN_MITER: { + if (on_outside) { // we are on the outside, do something complicated to make it look good ;) Geom::Point der1 = unitTangentAt(B[prev_i],1); @@ -310,7 +383,39 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> } break; } - case LINECUSP_BEVEL: + case LINEJOIN_SPIRO: { + if (on_outside) { + Geom::Point tang1 = unitTangentAt(B[prev_i],1); + Geom::Point tang2 = unitTangentAt(B[i],0); + + Geom::Point direction = B[i].at0() - B[prev_i].at1(); + double tang1_sign = dot(direction,tang1); + double tang2_sign = dot(direction,tang2); + + Spiro::spiro_cp *controlpoints = g_new (Spiro::spiro_cp, 4); + controlpoints[0].x = (B[prev_i].at1() - tang1_sign*tang1)[Geom::X]; + controlpoints[0].y = (B[prev_i].at1() - tang1_sign*tang1)[Geom::Y]; + controlpoints[0].ty = '{'; + controlpoints[1].x = B[prev_i].at1()[Geom::X]; + controlpoints[1].y = B[prev_i].at1()[Geom::Y]; + controlpoints[1].ty = ']'; + controlpoints[2].x = B[i].at0()[Geom::X]; + controlpoints[2].y = B[i].at0()[Geom::Y]; + controlpoints[2].ty = '['; + controlpoints[3].x = (B[i].at0() + tang2_sign*tang2)[Geom::X]; + controlpoints[3].y = (B[i].at0() + tang2_sign*tang2)[Geom::Y]; + controlpoints[3].ty = '}'; + + Geom::Path spiro; + Spiro::spiro_run(controlpoints, 4, spiro); + pb.append(spiro.portion(1,spiro.size_open()-1), Geom::Path::STITCH_DISCONTINUOUS); + } else { + // we are on the inside, do a simple bevel to connect the paths + pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles + } + break; + } + case LINEJOIN_BEVEL: default: pb.lineTo(B[i].at0()); break; @@ -388,13 +493,13 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in) } std::vector<discontinuity_data> cusps = find_discontinuities(der, x, y); - LineCuspType cusp_linecap = static_cast<LineCuspType>(cusp_linecap_type.get_value()); + LineJoinType jointype = static_cast<LineJoinType>(linejoin_type.get_value()); Piecewise<D2<SBasis> > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); Piecewise<D2<SBasis> > mirrorpath = reverse(compose(pwd2_in,x) - y*compose(n,x)); - Geom::Path fixed_path = path_from_piecewise_fix_cusps( pwd2_out, cusps, cusp_linecap, miter_limit, true, LPE_CONVERSION_TOLERANCE); - Geom::Path fixed_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, cusps, cusp_linecap, miter_limit, false, LPE_CONVERSION_TOLERANCE); + Geom::Path fixed_path = path_from_piecewise_fix_cusps( pwd2_out, cusps, jointype, miter_limit, true, LPE_CONVERSION_TOLERANCE); + Geom::Path fixed_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, cusps, jointype, miter_limit, false, LPE_CONVERSION_TOLERANCE); if (path_in[0].closed()) { fixed_path.close(true); diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h index a5bb8c836..e6c915234 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -39,7 +39,7 @@ private: EnumParam<unsigned> interpolator_type; ScalarParam interpolator_beta; EnumParam<unsigned> start_linecap_type; - EnumParam<unsigned> cusp_linecap_type; + EnumParam<unsigned> linejoin_type; ScalarParam miter_limit; EnumParam<unsigned> end_linecap_type; diff --git a/src/live_effects/lpe-spiro.cpp b/src/live_effects/lpe-spiro.cpp index 22974fe13..8b4274ab2 100644 --- a/src/live_effects/lpe-spiro.cpp +++ b/src/live_effects/lpe-spiro.cpp @@ -15,8 +15,6 @@ #include "helper/geom-nodetype.h" #include "helper/geom-curves.h" -#include "live_effects/bezctx.h" -#include "live_effects/bezctx_intf.h" #include "live_effects/spiro.h" // For handling un-continuous paths: @@ -24,82 +22,6 @@ #include "inkscape.h" #include "desktop.h" -#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS - -typedef struct { - bezctx base; - SPCurve *curve; - int is_open; -} bezctx_ink; - -void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/) -{ - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x) && IS_FINITE(y) ) { - bi->curve->moveto(x, y); - } -#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("lpe moveto not finite"); - } -#endif -} - -void bezctx_ink_lineto(bezctx *bc, double x, double y) -{ - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x) && IS_FINITE(y) ) { - bi->curve->lineto(x, y); - } -#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("lpe lineto not finite"); - } -#endif -} - -void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3) -{ - bezctx_ink *bi = (bezctx_ink *) bc; - - if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) { - bi->curve->quadto(xm, ym, x3, y3); - } -#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("lpe quadto not finite"); - } -#endif -} - -void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3) -{ - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) { - bi->curve->curveto(x1, y1, x2, y2, x3, y3); - } -#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("lpe curveto not finite"); - } -#endif -} - -bezctx * -new_bezctx_ink(SPCurve *curve) { - bezctx_ink *result = g_new(bezctx_ink, 1); - result->base.moveto = bezctx_ink_moveto; - result->base.lineto = bezctx_ink_lineto; - result->base.quadto = bezctx_ink_quadto; - result->base.curveto = bezctx_ink_curveto; - result->base.mark_knot = NULL; - result->curve = curve; - return &result->base; -} - - - namespace Inkscape { namespace LivePathEffect { @@ -124,8 +46,7 @@ LPESpiro::doEffect(SPCurve * curve) guint len = curve->get_segment_count() + 2; curve->reset(); - bezctx *bc = new_bezctx_ink(curve); - spiro_cp *path = g_new (spiro_cp, len); + Spiro::spiro_cp *path = g_new (Spiro::spiro_cp, len); int ip = 0; for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { @@ -218,9 +139,7 @@ LPESpiro::doEffect(SPCurve * curve) // run subpath through spiro int sp_len = ip; - spiro_seg *s = run_spiro(path, sp_len); - spiro_to_bpath(s, sp_len, bc); - free(s); + Spiro::spiro_run(path, sp_len, *curve); ip = 0; } diff --git a/src/live_effects/spiro-converters.cpp b/src/live_effects/spiro-converters.cpp new file mode 100644 index 000000000..3c7bdf99e --- /dev/null +++ b/src/live_effects/spiro-converters.cpp @@ -0,0 +1,123 @@ +/* Authors:
+ * Johan Engelen
+ *
+ * Copyright (C) 2010-2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "spiro-converters.h"
+#include <2geom/path.h>
+#include "display/curve.h"
+#include <glib.h>
+
+#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS
+#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
+# define SPIRO_G_MESSAGE(x) g_message(x)
+#else
+# define SPIRO_G_MESSAGE(x)
+#endif
+
+namespace Spiro {
+
+void
+ConverterSPCurve::moveto(double x, double y, bool is_open)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _curve.moveto(x, y);
+ if (!is_open) {
+ _curve.closepath();
+ }
+ } else {
+ SPIRO_G_MESSAGE("Spiro: moveto not finite");
+ }
+}
+
+void
+ConverterSPCurve::lineto(double x, double y)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _curve.lineto(x, y);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: lineto not finite");
+ }
+}
+
+void
+ConverterSPCurve::quadto(double xm, double ym, double x3, double y3)
+{
+ if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
+ _curve.quadto(xm, ym, x3, y3);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: quadto not finite");
+ }
+}
+
+void
+ConverterSPCurve::curveto(double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
+ _curve.curveto(x1, y1, x2, y2, x3, y3);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: curveto not finite");
+ }
+}
+
+
+
+
+void
+ConverterPath::moveto(double x, double y, bool is_open)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _path.start(Geom::Point(x, y));
+ _path.close(!is_open);
+ } else {
+ SPIRO_G_MESSAGE("spiro moveto not finite");
+ }
+}
+
+void
+ConverterPath::lineto(double x, double y)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _path.appendNew<Geom::LineSegment>( Geom::Point(x, y) );
+ } else {
+ SPIRO_G_MESSAGE("spiro lineto not finite");
+ }
+}
+
+void
+ConverterPath::quadto(double xm, double ym, double x3, double y3)
+{
+ if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
+ _path.appendNew<Geom::QuadraticBezier>(Geom::Point(xm, ym), Geom::Point(x3, y3));
+ } else {
+ SPIRO_G_MESSAGE("spiro quadto not finite");
+ }
+}
+
+void
+ConverterPath::curveto(double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
+ _path.appendNew<Geom::CubicBezier>(Geom::Point(x1, y1), Geom::Point(x2, y2), Geom::Point(x3, y3));
+ } else {
+ SPIRO_G_MESSAGE("spiro curveto not finite");
+ }
+}
+
+} // namespace Spiro
+
+
+
+/*
+ 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 :
diff --git a/src/live_effects/spiro-converters.h b/src/live_effects/spiro-converters.h new file mode 100644 index 000000000..83f6ebbc3 --- /dev/null +++ b/src/live_effects/spiro-converters.h @@ -0,0 +1,67 @@ +#ifndef INKSCAPE_SPIRO_CONVERTERS_H +#define INKSCAPE_SPIRO_CONVERTERS_H + +#include <2geom/forward.h> +class SPCurve; + +namespace Spiro { + +class ConverterBase { +public: + ConverterBase() {}; + virtual ~ConverterBase() {}; + + virtual void moveto(double x, double y, bool is_open) = 0; + virtual void lineto(double x, double y) = 0; + virtual void quadto(double x1, double y1, double x2, double y2) = 0; + virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3) = 0; +}; + + +/** + * Converts Spiro to Inkscape's SPCurve + */ +class ConverterSPCurve : public ConverterBase { +public: + ConverterSPCurve(SPCurve &curve) + : _curve(curve) + {} ; + + virtual void moveto(double x, double y, bool is_open); + virtual void lineto(double x, double y); + virtual void quadto(double x1, double y1, double x2, double y2); + virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3); + +private: + SPCurve &_curve; + + ConverterSPCurve(const ConverterSPCurve&); + ConverterSPCurve& operator=(const ConverterSPCurve&); +}; + + +/** + * Converts Spiro to 2Geom's Path + */ +class ConverterPath : public ConverterBase { +public: + ConverterPath(Geom::Path &path) + : _path(path) + {} ; + + virtual void moveto(double x, double y, bool is_open); + virtual void lineto(double x, double y); + virtual void quadto(double x1, double y1, double x2, double y2); + virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3); + +private: + Geom::Path &_path; + + ConverterPath(const ConverterPath&); + ConverterPath& operator=(const ConverterPath&); +}; + + +} // namespace Spiro + +#endif diff --git a/src/live_effects/spiro.cpp b/src/live_effects/spiro.cpp index b98e12213..e4b72793c 100644 --- a/src/live_effects/spiro.cpp +++ b/src/live_effects/spiro.cpp @@ -1,6 +1,8 @@ /* -ppedit - A pattern plate editor for Spiro splines. -Copyright (C) 2007 Raph Levien +Copyright (C) 2007-2012 Authors + +Authors: Raph Levien + Johan Engelen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -20,12 +22,39 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA */ /* C implementation of third-order polynomial spirals. */ +#include "spiro.h" + #include <math.h> #include <stdlib.h> #include <string.h> -#include "bezctx_intf.h" -#include "spiro.h" +#include "display/curve.h" +#include <2geom/math-utils.h> + +#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS + +namespace Spiro { + +void spiro_run(const spiro_cp *src, int src_len, SPCurve &curve) +{ + spiro_seg *s = Spiro::run_spiro(src, src_len); + Spiro::ConverterSPCurve bc(curve); + Spiro::spiro_to_otherpath(s, src_len, bc); + free(s); +} + +void spiro_run(const spiro_cp *src, int src_len, Geom::Path &path) +{ + spiro_seg *s = Spiro::run_spiro(src, src_len); + Spiro::ConverterPath bc(path); + Spiro::spiro_to_otherpath(s, src_len, bc); + free(s); +} + + +/************************************ + * Spiro math + */ struct spiro_seg_s { double x; @@ -814,15 +843,15 @@ solve_spiro(spiro_seg *s, int nseg) } static void -spiro_seg_to_bpath(const double ks[4], +spiro_seg_to_otherpath(const double ks[4], double x0, double y0, double x1, double y1, - bezctx *bc, int depth) + ConverterBase &bc, int depth) { double bend = fabs(ks[0]) + fabs(.5 * ks[1]) + fabs(.125 * ks[2]) + fabs((1./48) * ks[3]); if (!bend > 1e-8) { - bezctx_lineto(bc, x1, y1); + bc.lineto(x1, y1); } else { double seg_ch = hypot(x1 - x0, y1 - y0); double seg_th = atan2(y1 - y0, x1 - x0); @@ -845,7 +874,7 @@ spiro_seg_to_bpath(const double ks[4], vl = (scale * (1./3)) * sin(th_even - th_odd); ur = (scale * (1./3)) * cos(th_even + th_odd); vr = (scale * (1./3)) * sin(th_even + th_odd); - bezctx_curveto(bc, x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1); + bc.curveto(x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1); } else { /* subdivide */ double ksub[4]; @@ -864,11 +893,11 @@ spiro_seg_to_bpath(const double ks[4], integrate_spiro(ksub, xysub); xmid = x0 + cth * xysub[0] - sth * xysub[1]; ymid = y0 + cth * xysub[1] + sth * xysub[0]; - spiro_seg_to_bpath(ksub, x0, y0, xmid, ymid, bc, depth + 1); + spiro_seg_to_otherpath(ksub, x0, y0, xmid, ymid, bc, depth + 1); ksub[0] += .25 * ks[1] + (1./384) * ks[3]; ksub[1] += .125 * ks[2]; ksub[2] += (1./16) * ks[3]; - spiro_seg_to_bpath(ksub, xmid, ymid, x1, y1, bc, depth + 1); + spiro_seg_to_otherpath(ksub, xmid, ymid, x1, y1, bc, depth + 1); } } } @@ -890,7 +919,7 @@ free_spiro(spiro_seg *s) } void -spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc) +spiro_to_otherpath(const spiro_seg *s, int n, ConverterBase &bc) { int i; int nsegs = s[n - 1].ty == '}' ? n - 1 : n; @@ -901,10 +930,10 @@ spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc) double x1 = s[i + 1].x; double y1 = s[i + 1].y; - if (i == 0) - bezctx_moveto(bc, x0, y0, s[0].ty == '{'); - bezctx_mark_knot(bc, i); - spiro_seg_to_bpath(s[i].ks, x0, y0, x1, y1, bc, 0); + if (i == 0) { + bc.moveto(x0, y0, s[0].ty == '{'); + } + spiro_seg_to_otherpath(s[i].ks, x0, y0, x1, y1, bc, 0); } } @@ -922,10 +951,20 @@ get_knot_th(const spiro_seg *s, int i) } } + +} // namespace Spiro + +/************************************ + * Unit_test code + */ + + #ifdef UNIT_TEST #include <stdio.h> #include <sys/time.h> /* for gettimeofday */ +using namespace Spiro; + static double get_time (void) { diff --git a/src/live_effects/spiro.h b/src/live_effects/spiro.h index 54de40465..0d85da74b 100644 --- a/src/live_effects/spiro.h +++ b/src/live_effects/spiro.h @@ -1,18 +1,56 @@ +/* +Copyright (C) 2007-2012 Authors + +Authors: Raph Levien + Johan Engelen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +*/ + +#ifndef INKSCAPE_SPIRO_H +#define INKSCAPE_SPIRO_H + +#include "live_effects/spiro-converters.h" + +class SPCurve; +namespace Geom { + class Path; +} + +namespace Spiro { + typedef struct { double x; double y; char ty; } spiro_cp; -typedef struct spiro_seg_s spiro_seg; -spiro_seg * -run_spiro(const spiro_cp *src, int n); +void spiro_run(const spiro_cp *src, int src_len, SPCurve &curve); +void spiro_run(const spiro_cp *src, int src_len, Geom::Path &path); -void -free_spiro(spiro_seg *s); +/* the following methods are only for expert use: */ +typedef struct spiro_seg_s spiro_seg; +spiro_seg * run_spiro(const spiro_cp *src, int n); +void free_spiro(spiro_seg *s); +void spiro_to_otherpath(const spiro_seg *s, int n, ConverterBase &bc); +double get_knot_th(const spiro_seg *s, int i); -void -spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc); -double get_knot_th(const spiro_seg *s, int i); +} // namespace Spiro + +#endif // INKSCAPE_SPIRO_H
\ No newline at end of file |
