From 081a89987d4cb46e707c9dd5648a7a24a01a750d Mon Sep 17 00:00:00 2001 From: jfbarraud Date: Sat, 3 Jan 2009 01:40:34 +0000 Subject: [PATCH] added lpe-dynastroke, but hidden as experimental. --- src/live_effects/CMakeLists.txt | 1 + src/live_effects/effect-enum.h | 1 + src/live_effects/effect.cpp | 6 + src/live_effects/lpe-dynastroke.cpp | 329 ++++++++++++++++++++++++++++ src/live_effects/lpe-dynastroke.h | 67 ++++++ 5 files changed, 404 insertions(+) create mode 100644 src/live_effects/lpe-dynastroke.cpp create mode 100644 src/live_effects/lpe-dynastroke.h diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 72d955586..583386796 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -25,5 +25,6 @@ lpe-spiro.cpp lpe-tangent_to_curve.cpp lpe-test-doEffect-stack.cpp lpe-vonkoch.cpp +lpe-dynastroke.cpp spiro.cpp ) diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h index 6409fecd2..b6a091b0c 100644 --- a/src/live_effects/effect-enum.h +++ b/src/live_effects/effect-enum.h @@ -48,6 +48,7 @@ enum EffectType { LINE_SEGMENT, #ifdef LPE_ENABLE_TEST_EFFECTS DOEFFECTSTACK_TEST, + DYNASTROKE, #endif INVALID_LPE // This must be last }; diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index dc252722a..83e3ccb8d 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -43,6 +43,7 @@ #include "live_effects/lpe-vonkoch.h" #include "live_effects/lpe-knot.h" #include "live_effects/lpe-rough-hatches.h" +#include "live_effects/lpe-dynastroke.h" #include "live_effects/lpe-test-doEffect-stack.h" #include "live_effects/lpe-gears.h" #include "live_effects/lpe-curvestitch.h" @@ -66,6 +67,7 @@ #include "live_effects/lpe-text_label.h" #include "live_effects/lpe-path_length.h" #include "live_effects/lpe-line_segment.h" + // end of includes namespace Inkscape { @@ -76,6 +78,7 @@ const Util::EnumData LPETypeData[] = { // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"} #ifdef LPE_ENABLE_TEST_EFFECTS {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"}, + {DYNASTROKE, N_("Dynamic stroke"), "dynastroke"}, #endif {ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"}, {BEND_PATH, N_("Bend"), "bend_path"}, @@ -218,6 +221,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case DOEFFECTSTACK_TEST: neweffect = static_cast ( new LPEdoEffectStackTest(lpeobj) ); break; + case DYNASTROKE: + neweffect = static_cast ( new LPEDynastroke(lpeobj) ); + break; #endif default: g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); diff --git a/src/live_effects/lpe-dynastroke.cpp b/src/live_effects/lpe-dynastroke.cpp new file mode 100644 index 000000000..77fc86ddf --- /dev/null +++ b/src/live_effects/lpe-dynastroke.cpp @@ -0,0 +1,329 @@ +#define INKSCAPE_LPE_DYNASTROKE_CPP +/** \file + * LPE implementation + */ +/* + * Authors: + * JF Barraud +* +* Copyright (C) JF Barraud 2007 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-dynastroke.h" +#include "display/curve.h" +//# include + +#include <2geom/path.h> +#include <2geom/sbasis.h> +#include <2geom/sbasis-geometric.h> +#include <2geom/bezier-to-sbasis.h> +#include <2geom/sbasis-to-bezier.h> +#include <2geom/d2.h> +#include <2geom/d2-sbasis.h> +#include <2geom/sbasis-math.h> +#include <2geom/piecewise.h> + +namespace Inkscape { +namespace LivePathEffect { + +//---------------------------------------------------------- +//--- TODO: Test this and move to 2Geom -------------------- +//---------------------------------------------------------- + +static +std::vector +find_corners (Geom::Piecewise > const &m){ + using namespace Geom; + std::vector output = std::vector(); + Piecewise > v = derivative(m); + for (unsigned i=0; i DynastrokeMethodData[DSM_END] = { + {DSM_ELLIPTIC_PEN, N_("Ellipitic Pen"), "elliptic_pen"}, + {DSM_THICKTHIN_FAST, N_("Thick-Thin strokes (fast)"), "thickthin_fast"}, + {DSM_THICKTHIN_SLOW, N_("Thick-Thin strokes (slow)"), "thickthin_slow"} +}; +static const Util::EnumDataConverter DSMethodConverter(DynastrokeMethodData, DSM_END); + +static const Util::EnumData DynastrokeCappingTypeData[DSCT_END] = { + {DSCT_SHARP, N_("Sharp"), "sharp"}, + {DSCT_ROUND, N_("Round"), "round"}, +}; +static const Util::EnumDataConverter DSCTConverter(DynastrokeCappingTypeData, DSCT_END); + +LPEDynastroke::LPEDynastroke(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + // initialise your parameters here: + method(_("Method"), _("Choose pen type"), "method", DSMethodConverter, &wr, this, DSM_THICKTHIN_FAST), + width(_("Pen width"), _("Maximal stroke width"), "width", &wr, this, 25), + roundness(_("Pen roundness"), _("Min/Max width ratio"), "roundness", &wr, this, .2), + angle(_("angle"), _("direction of thickest strokes (opposite = thinest)"), "angle", &wr, this, 45), +// modulo_pi(_("modulo pi"), _("Give forward and backward moves in one direction the same thickness "), "modulo_pi", &wr, this, false), + start_cap(_("Start"), _("Choose start capping type"), "start_cap", DSCTConverter, &wr, this, DSCT_SHARP), + end_cap(_("End"), _("Choose end capping type"), "end_cap", DSCTConverter, &wr, this, DSCT_SHARP), + growfor(_("Grow for"), _("Make the stroke thiner near it's start"), "growfor", &wr, this, 100), + fadefor(_("Fade for"), _("Make the stroke thiner near it's end"), "fadefor", &wr, this, 100), + round_ends(_("Round ends"), _("Strokes end with a round end"), "round_ends", &wr, this, false), + capping(_("Capping"), _("left capping"), "capping", &wr, this, "M 100,5 C 50,5 0,0 0,0 0,0 50,-5 100,-5") +{ + + registerParameter( dynamic_cast(& method) ); + registerParameter( dynamic_cast(& width) ); + registerParameter( dynamic_cast(& roundness) ); + registerParameter( dynamic_cast(& angle) ); + //registerParameter( dynamic_cast(& modulo_pi) ); + registerParameter( dynamic_cast(& start_cap) ); + registerParameter( dynamic_cast(& growfor) ); + registerParameter( dynamic_cast(& end_cap) ); + registerParameter( dynamic_cast(& fadefor) ); + registerParameter( dynamic_cast(& round_ends) ); + registerParameter( dynamic_cast(& capping) ); + + width.param_set_range(0, NR_HUGE); + roundness.param_set_range(0.01, 1); + angle.param_set_range(-360, 360); + growfor.param_set_range(0, NR_HUGE); + fadefor.param_set_range(0, NR_HUGE); + + show_orig_path = true; +} + +LPEDynastroke::~LPEDynastroke() +{ + +} + +Geom::Piecewise > +LPEDynastroke::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) +{ + using namespace Geom; + + std::cout<<"do effect: debut\n"; + + Piecewise > output; + Piecewise > m = pwd2_in; + Piecewise > v = derivative(m);; + Piecewise > n = unitVector(v); + n = rot90(n); + Piecewise > n1,n2; + +// for (unsigned i=0; i k = curvature(m); + OptInterval mag = bounds_exact(k); + //TODO test if mag is non empty... + k = (k-mag->min())*width/mag->extent() + (roundness*width); + Piecewise > left = m + k*n; + Piecewise > right = m - k*n; + right = compose(right,Linear(right.cuts.back(),right.cuts.front())); + D2 line; + line[X] = Linear(left.lastValue()[X],right.firstValue()[X]); + line[Y] = Linear(left.lastValue()[Y],right.firstValue()[Y]); + output = left; + output.concat(Piecewise >(line)); + output.concat(right); + line[X] = Linear(right.lastValue()[X],left.firstValue()[X]); + line[Y] = Linear(right.lastValue()[Y],left.firstValue()[Y]); + output.concat(Piecewise >(line)); + return output; +#else + + double angle_rad = angle*M_PI/180.;//TODO: revert orientation?... + Piecewise w; + + std::vector corners = find_corners(m); + + DynastrokeMethod stroke_method = method.get_value(); + if (roundness==1.) { + std::cout<<"round pen.\n"; + n1 = n*double(width); + n2 =-n1; + }else{ + switch(stroke_method) { + case DSM_ELLIPTIC_PEN:{ + std::cout<<"ellptic pen\n"; + //FIXME: roundness=0??? + double c = cos(angle_rad), s = sin(angle_rad); + Matrix rot,slant; + rot = Matrix(c, -s, s, c, 0, 0 ); + slant = Matrix(double(width)*roundness, 0, 0, double(width), 0, 0 ); + Piecewise > nn = unitVector(v * ( rot * slant ) ); + slant = Matrix( 0,-roundness, 1, 0, 0, 0 ); + rot = Matrix(-s, -c, c, -s, 0, 0 ); + nn = nn * (slant * rot ); + + n1 = nn*double(width); + n2 =-n1; + break; + } + case DSM_THICKTHIN_FAST:{ + std::cout<<"fast thick thin pen\n"; + D2 > n_xy = make_cuts_independent(n); + w = n_xy[X]*sin(angle_rad) - n_xy[Y]*cos(angle_rad); + w = w * ((1 - roundness)*width/2.) + ((1 + roundness)*width/2.); + n1 = w*n; + n2 = -n1; + break; + } + case DSM_THICKTHIN_SLOW:{ + std::cout<<"slow thick thin pen\n"; + D2 > n_xy = make_cuts_independent(n); + w = n_xy[X]*cos(angle_rad)+ n_xy[Y]*sin(angle_rad); + w = w * ((1 - roundness)*width/2.) + ((1 + roundness)*width/2.); + //->Slower and less stable, but more accurate . + // General formula: n1 = w*u with ||u||=1 and u.v = -dw/dt + Piecewise dw = derivative(w); + Piecewise ncomp = sqrt(dot(v,v)-dw*dw,.1,3); + //FIXME: is force continuity usefull? compatible with corners? + std::cout<<"ici\n"; + n1 = -dw*v + ncomp*rot90(v); + n1 = w*force_continuity(unitVector(n1),.1); + n2 = -dw*v - ncomp*rot90(v); + n2 = w*force_continuity(unitVector(n2),.1); + std::cout<<"ici2\n"; + break; + } + default:{ + n1 = n*double(width); + n2 = n1*(-.5); + break; + } + }//case + }//if/else + + // + //TODO: insert relevant stitch at each corner!! + // + + Piecewise > left, right; + if ( m.segs.front().at0() == m.segs.back().at1()){ + // if closed: + std::cout<<"closed input.\n"; + left = m + n1;//+ n; + right = m + n2;//- n; + } else { + //if not closed, shape the ends: + //TODO: allow fancy ends... + std::cout<<"shaping the ends\n"; + double grow_length = growfor;// * width; + double fade_length = fadefor;// * width; + Piecewise s = arcLengthSb(m); + double totlength = s.segs.back().at1(); + + //scale factor for a sharp start + SBasis join = SBasis(2,Linear(0,1)); + join[1] = Linear(1,1); + Piecewise factor_in = Piecewise(join); + factor_in.cuts[1]=grow_length; + if (grow_length < totlength){ + factor_in.concat(Piecewise(Linear(1))); + factor_in.cuts[2]=totlength; + } + std::cout<<"shaping the ends ici\n"; + //scale factor for a sharp end + join[0] = Linear(1,0); + join[1] = Linear(1,1); + Piecewise factor_out; + if (fade_length < totlength){ + factor_out = Piecewise(Linear(1)); + factor_out.cuts[1] = totlength-fade_length; + factor_out.concat(Piecewise(join)); + factor_out.cuts[2] = totlength; + }else{ + factor_out = Piecewise(join); + factor_out.setDomain(Interval(totlength-fade_length,totlength)); + } + std::cout<<"shaping the ends ici ici\n"; + + Piecewise factor = factor_in*factor_out; + n1 = compose(factor,s)*n1; + n2 = compose(factor,s)*n2; + + left = m + n1; + right = m + n2; + std::cout<<"shaping the ends ici ici ici\n"; + + if (start_cap.get_value() == DSCT_ROUND){ + std::cout<<"shaping round start\n"; + SBasis tau(2,Linear(0)); + tau[1] = Linear(-1,0); + Piecewise hbump; + hbump.concat(Piecewise(tau*grow_length)); + hbump.concat(Piecewise(Linear(0))); + hbump.cuts[0]=0; + hbump.cuts[1]=fmin(grow_length,totlength*grow_length/(grow_length+fade_length)); + hbump.cuts[2]=totlength; + hbump = compose(hbump,s); + + left += - hbump * rot90(n); + right += - hbump * rot90(n); + } + if (end_cap.get_value() == DSCT_ROUND){ + std::cout<<"shaping round end\n"; + SBasis tau(2,Linear(0)); + tau[1] = Linear(0,1); + Piecewise hbump; + hbump.concat(Piecewise(Linear(0))); + hbump.concat(Piecewise(tau*fade_length)); + hbump.cuts[0]=0; + hbump.cuts[1]=fmax(totlength-fade_length, totlength*grow_length/(grow_length+fade_length)); + hbump.cuts[2]=totlength; + hbump = compose(hbump,s); + + left += - hbump * rot90(n); + right += - hbump * rot90(n); + } + } + + left = force_continuity(left); + right = force_continuity(right); + + std::cout<<"gathering result: left"; + output = left; + std::cout<<" + reverse(right)"; + output.concat(reverse(right)); + std::cout<<". done\n"; + +//----------- + return output; +#endif +} + + +/* ######################## */ + +} //namespace LivePathEffect (setq default-directory "c:/Documents And Settings/jf/Mes Documents/InkscapeSVN") +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/lpe-dynastroke.h b/src/live_effects/lpe-dynastroke.h new file mode 100644 index 000000000..ced376515 --- /dev/null +++ b/src/live_effects/lpe-dynastroke.h @@ -0,0 +1,67 @@ +#ifndef INKSCAPE_LPE_DYNASTROKE_H +#define INKSCAPE_LPE_DYNASTROKE_H + +/** \file + * LPE implementation, see lpe-dynastroke.cpp. + */ + +/* + * Authors: + * JFB, but derived from Johan Engelen! + * + * Copyright (C) JF Barraud 2008 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/effect.h" +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/enum.h" +#include "live_effects/parameter/path.h" +#include "live_effects/parameter/bool.h" + +namespace Inkscape { +namespace LivePathEffect { + +enum DynastrokeMethod { + DSM_ELLIPTIC_PEN = 0, + DSM_THICKTHIN_FAST, + DSM_THICKTHIN_SLOW, + DSM_END // This must be last +}; +enum DynastrokeCappingType { + DSCT_SHARP = 0, + DSCT_ROUND, + //DSCT_CUSTOM, + DSCT_END // This must be last +}; + + +class LPEDynastroke : public Effect { +public: + LPEDynastroke(LivePathEffectObject *lpeobject); + virtual ~LPEDynastroke(); + + virtual Geom::Piecewise > doEffect_pwd2 (Geom::Piecewise > const & pwd2_in); + +private: + EnumParam method; + ScalarParam width; + ScalarParam roundness; + ScalarParam angle; + //BoolParam modulo_pi; + EnumParam start_cap; + EnumParam end_cap; + ScalarParam growfor; + ScalarParam fadefor; + BoolParam round_ends; + PathParam capping; + + LPEDynastroke(const LPEDynastroke&); + LPEDynastroke& operator=(const LPEDynastroke&); +}; + +} //namespace LivePathEffect +} //namespace Inkscape + +#endif -- 2.30.2